diff --git a/.gitignore b/.gitignore index 075c26a47..c9d349441 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ dist/*.docker cover.out .editorconfig .idea/ -bash_unit +default.etcd/ +flannel.exe +bash_unit \ No newline at end of file diff --git a/backend/alivpc/alivpc.go b/backend/alivpc/alivpc.go index 876738f42..9311563f0 100644 --- a/backend/alivpc/alivpc.go +++ b/backend/alivpc/alivpc.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/alivpc/alivpc_unspecified.go b/backend/alivpc/alivpc_unspecified.go new file mode 100644 index 000000000..d669ab05a --- /dev/null +++ b/backend/alivpc/alivpc_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package alivpc + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("AliVpc is not supported on this platform") +} diff --git a/backend/awsvpc/awsvpc.go b/backend/awsvpc/awsvpc.go index 13bbe4f74..d15a3290a 100644 --- a/backend/awsvpc/awsvpc.go +++ b/backend/awsvpc/awsvpc.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/awsvpc/awsvpc_unspecified.go b/backend/awsvpc/awsvpc_unspecified.go new file mode 100644 index 000000000..387d70cd6 --- /dev/null +++ b/backend/awsvpc/awsvpc_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package awsvpc + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("AWS VPC is not supported on this platform") +} diff --git a/backend/awsvpc/filter.go b/backend/awsvpc/filter.go index 79ba0abf3..be99cfb13 100644 --- a/backend/awsvpc/filter.go +++ b/backend/awsvpc/filter.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/gce/api.go b/backend/gce/api.go index e9389f647..0ca3d8e4e 100644 --- a/backend/gce/api.go +++ b/backend/gce/api.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/gce/gce.go b/backend/gce/gce.go index 5390110fd..d49b2a913 100644 --- a/backend/gce/gce.go +++ b/backend/gce/gce.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/gce/gce_unspecified.go b/backend/gce/gce_unspecified.go new file mode 100644 index 000000000..ba487b8b9 --- /dev/null +++ b/backend/gce/gce_unspecified.go @@ -0,0 +1,48 @@ +// +build !linux + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This work borrows from the https://github.com/kelseyhightower/flannel-route-manager +// project which has the following license agreement. + +// Copyright (c) 2014 Kelsey Hightower + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +package gce + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("GCE is not supported on this platform") +} diff --git a/backend/gce/metadata.go b/backend/gce/metadata.go index 180337cb5..43498d69e 100644 --- a/backend/gce/metadata.go +++ b/backend/gce/metadata.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/hostgw/hostgw.go b/backend/hostgw/hostgw.go index 01b6659d1..ae7b34230 100644 --- a/backend/hostgw/hostgw.go +++ b/backend/hostgw/hostgw.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/hostgw/hostgw_network.go b/backend/hostgw/hostgw_network.go index 73a2efd95..5776872b5 100644 --- a/backend/hostgw/hostgw_network.go +++ b/backend/hostgw/hostgw_network.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/hostgw/hostgw_network_windows.go b/backend/hostgw/hostgw_network_windows.go new file mode 100644 index 000000000..83655dab3 --- /dev/null +++ b/backend/hostgw/hostgw_network_windows.go @@ -0,0 +1,208 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostgw + +import ( + "sync" + "time" + + log "github.com/golang/glog" + "golang.org/x/net/context" + + "github.com/coreos/flannel/backend" + "github.com/coreos/flannel/subnet" + + netroute "github.com/rakelkar/gonetsh/netroute" +) + +type network struct { + name string + extIface *backend.ExternalInterface + linkIndex int + rl []netroute.Route + lease *subnet.Lease + sm subnet.Manager +} + +func (n *network) Lease() *subnet.Lease { + return n.lease +} + +func (n *network) MTU() int { + return n.extIface.Iface.MTU +} + +func (n *network) Run(ctx context.Context) { + wg := sync.WaitGroup{} + + log.Info("Watching for new subnet leases") + evts := make(chan []subnet.Event) + wg.Add(1) + go func() { + subnet.WatchLeases(ctx, n.sm, n.lease, evts) + wg.Done() + }() + + n.rl = make([]netroute.Route, 0, 10) + wg.Add(1) + go func() { + n.routeCheck(ctx) + wg.Done() + }() + + defer wg.Wait() + + for { + select { + case evtBatch := <-evts: + n.handleSubnetEvents(evtBatch) + + case <-ctx.Done(): + return + } + } +} +func (n *network) handleSubnetEvents(batch []subnet.Event) { + nr := netroute.New() + defer nr.Exit() + + for _, evt := range batch { + switch evt.Type { + case subnet.EventAdded: + log.Infof("Subnet added: %v via %v", evt.Lease.Subnet, evt.Lease.Attrs.PublicIP) + + if evt.Lease.Attrs.BackendType != "host-gw" { + log.Warningf("Ignoring non-host-gw subnet: type=%v", evt.Lease.Attrs.BackendType) + continue + } + + route := netroute.Route{ + DestinationSubnet: evt.Lease.Subnet.ToIPNet(), + GatewayAddress: evt.Lease.Attrs.PublicIP.ToIP(), + LinkIndex: n.linkIndex, + } + + existingRoutes, _ := nr.GetNetRoutes(route.LinkIndex, route.DestinationSubnet) + + if existingRoutes != nil && len(existingRoutes) > 0 { + if existingRoutes[0].Equal(route) { + continue + } + + log.Warningf("Replacing existing route to %v via %v with %v via %v.", evt.Lease.Subnet, existingRoutes[0].GatewayAddress, evt.Lease.Subnet, evt.Lease.Attrs.PublicIP) + err := nr.RemoveNetRoute(route.LinkIndex, route.DestinationSubnet, existingRoutes[0].GatewayAddress) + if err != nil { + log.Errorf("Error removing route: %v", err) + continue + } + } + + err := nr.NewNetRoute(route.LinkIndex, route.DestinationSubnet, route.GatewayAddress) + if err != nil { + log.Errorf("Error creating route: %v", err) + } + + n.addToRouteList(route) + + case subnet.EventRemoved: + log.Info("Subnet removed: ", evt.Lease.Subnet) + + if evt.Lease.Attrs.BackendType != "host-gw" { + log.Warningf("Ignoring non-host-gw subnet: type=%v", evt.Lease.Attrs.BackendType) + continue + } + + route := netroute.Route{ + DestinationSubnet: evt.Lease.Subnet.ToIPNet(), + GatewayAddress: evt.Lease.Attrs.PublicIP.ToIP(), + LinkIndex: n.linkIndex, + } + + existingRoutes, _ := nr.GetNetRoutes(route.LinkIndex, route.DestinationSubnet) + + if existingRoutes != nil { + err := nr.RemoveNetRoute(route.LinkIndex, route.DestinationSubnet, route.GatewayAddress) + if err != nil { + log.Errorf("Error removing route: %v", err) + } + } + + n.removeFromRouteList(route) + + default: + log.Error("Internal error: unknown event type: ", int(evt.Type)) + } + } +} + +func (n *network) addToRouteList(route netroute.Route) { + for _, r := range n.rl { + if r.Equal(route) { + return + } + } + n.rl = append(n.rl, route) +} + +func (n *network) removeFromRouteList(route netroute.Route) { + for index, r := range n.rl { + if r.Equal(route) { + n.rl = append(n.rl[:index], n.rl[index+1:]...) + return + } + } +} + +func (n *network) routeCheck(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + case <-time.After(routeCheckRetries * time.Second): + n.checkSubnetExistInRoutes() + } + } +} + +func (n *network) checkSubnetExistInRoutes() { + nr := netroute.New() + defer nr.Exit() + + currentRoutes, err := nr.GetNetRoutesAll() + if err != nil { + log.Errorf("Error enumerating routes", err) + return + } + for _, r := range n.rl { + exist := false + for _, currentRoute := range currentRoutes { + if r.Equal(currentRoute) { + exist = true + break + } + } + + if !exist { + err := nr.NewNetRoute(r.LinkIndex, r.DestinationSubnet, r.GatewayAddress) + if err != nil { + log.Errorf("Error recovering route to %v via %v on %v (%v).", r.DestinationSubnet, r.GatewayAddress, r.LinkIndex, err) + continue + } + log.Errorf("Recovered route to %v via %v on %v.", r.DestinationSubnet, r.GatewayAddress, r.LinkIndex) + } + } +} diff --git a/backend/hostgw/hostgw_unspecified.go b/backend/hostgw/hostgw_unspecified.go new file mode 100644 index 000000000..0fc9ab48f --- /dev/null +++ b/backend/hostgw/hostgw_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux,!windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostgw + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("hostgw is not supported on this platform") +} diff --git a/backend/hostgw/hostgw_windows.go b/backend/hostgw/hostgw_windows.go new file mode 100644 index 000000000..dd7e2cd2f --- /dev/null +++ b/backend/hostgw/hostgw_windows.go @@ -0,0 +1,212 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package hostgw + +import ( + "fmt" + + "github.com/Microsoft/hcsshim" + "github.com/coreos/flannel/backend" + "github.com/coreos/flannel/pkg/ip" + "github.com/coreos/flannel/subnet" + "github.com/golang/glog" + netsh "github.com/rakelkar/gonetsh/netsh" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/util/json" +) + +func init() { + backend.Register("host-gw", New) +} + +const ( + routeCheckRetries = 10 +) + +type HostgwBackend struct { + sm subnet.Manager + extIface *backend.ExternalInterface + networks map[string]*network +} + +func New(sm subnet.Manager, extIface *backend.ExternalInterface) (backend.Backend, error) { + if !extIface.ExtAddr.Equal(extIface.IfaceAddr) { + return nil, fmt.Errorf("your PublicIP differs from interface IP, meaning that probably you're on a NAT, which is not supported by host-gw backend") + } + + be := &HostgwBackend{ + sm: sm, + extIface: extIface, + networks: make(map[string]*network), + } + + return be, nil +} + +func (be *HostgwBackend) RegisterNetwork(ctx context.Context, config *subnet.Config) (backend.Network, error) { + n := &network{ + extIface: be.extIface, + sm: be.sm, + name: be.extIface.Iface.Name, + linkIndex: be.extIface.Iface.Index, + } + + attrs := subnet.LeaseAttrs{ + PublicIP: ip.FromIP(be.extIface.ExtAddr), + BackendType: "host-gw", + } + + l, err := be.sm.AcquireLease(ctx, &attrs) + switch err { + case nil: + n.lease = l + + case context.Canceled, context.DeadlineExceeded: + return nil, err + + default: + return nil, fmt.Errorf("failed to acquire lease: %v", err) + } + + backendConfig := struct { + networkName string + dnsServerList string + }{ + networkName: "cbr0", + } + + if len(config.Backend) > 0 { + if err := json.Unmarshal(config.Backend, &backendConfig); err != nil { + return nil, fmt.Errorf("error decoding windows HOST-GW backend config: %v", err) + } + } + + if backendConfig.networkName == "" { + return nil, fmt.Errorf("Backend.networkName is required e.g. cbr0") + } + + // check if the network exists and has the expected settings? + var networkId string + createNetwork := true + addressPrefix := n.lease.Subnet.String() + networkGatewayAddress := n.lease.Subnet.IP + 1 + podGatewayAddress := n.lease.Subnet.IP + 2 + hnsNetwork, err := hcsshim.GetHNSNetworkByName(backendConfig.networkName) + if err == nil && hnsNetwork.DNSServerList == backendConfig.dnsServerList { + for _, subnet := range hnsNetwork.Subnets { + if subnet.AddressPrefix == addressPrefix && subnet.GatewayAddress == networkGatewayAddress.String() { + networkId = hnsNetwork.Id + createNetwork = false + glog.Infof("Found existing HNS network [%+v]", hnsNetwork) + break + } + } + } + + if createNetwork { + // create, but a network with the same name exists? + if hnsNetwork != nil { + if _, err := hnsNetwork.Delete(); err != nil { + return nil, fmt.Errorf("unable to delete existing network [%v], error: %v", hnsNetwork.Name, err) + } + glog.Infof("Deleted stale HNS network [%v]") + } + + // create the underlying windows HNS network + request := map[string]interface{}{ + "Name": backendConfig.networkName, + "Type": "l2bridge", + "Subnets": []interface{}{ + map[string]interface{}{ + "AddressPrefix": addressPrefix, + "GatewayAddress": networkGatewayAddress, + }, + }, + "DNSServerList": backendConfig.dnsServerList, + } + + jsonRequest, err := json.Marshal(request) + if err != nil { + return nil, err + } + + glog.Infof("Attempting to create HNS network, request: %v", string(jsonRequest)) + hnsNetwork, err := hcsshim.HNSNetworkRequest("POST", "", string(jsonRequest)) + if err != nil { + return nil, fmt.Errorf("unable to create network [%v], error: %v", backendConfig.networkName, err) + } + networkId = hnsNetwork.Id + glog.Infof("Created HNS network [%v] as %+v", backendConfig.networkName, hnsNetwork) + } + + // now ensure there is a 1.2 endpoint on this network in the host compartment + var endpointToAttach *hcsshim.HNSEndpoint + bridgeEndpointName := backendConfig.networkName + "_ep" + createEndpoint := true + hnsEndpoint, err := hcsshim.GetHNSEndpointByName(bridgeEndpointName) + if err == nil && hnsEndpoint.IPAddress.String() == podGatewayAddress.String() { + glog.Infof("Found existing HNS bridge endpoint [%+v]", hnsEndpoint) + endpointToAttach = hnsEndpoint + createEndpoint = false + } + + if createEndpoint { + if hnsEndpoint != nil { + if _, err = hnsEndpoint.Delete(); err != nil { + return nil, fmt.Errorf("unable to delete existing bridge endpoint [%v], error: %v", bridgeEndpointName, err) + } + glog.Infof("Deleted stale HNS endpoint [%v]") + } + + hnsEndpoint = &hcsshim.HNSEndpoint{ + Id: "", + Name: bridgeEndpointName, + IPAddress: podGatewayAddress.ToIP(), + VirtualNetwork: networkId, + } + + glog.Infof("Attempting to create HNS endpoint [%+v]", hnsEndpoint) + hnsEndpoint, err = hnsEndpoint.Create() + if err != nil { + return nil, fmt.Errorf("unable to create bridge endpoint [%v], error: %v", bridgeEndpointName, err) + } + endpointToAttach = hnsEndpoint + glog.Infof("Created bridge endpoint [%v] as %+v", bridgeEndpointName, hnsEndpoint) + } + + if err = endpointToAttach.HostAttach(1); err != nil { + return nil, fmt.Errorf("unable to hot attach bridge endpoint [%v] to host compartment, error: %v", bridgeEndpointName, err) + } + glog.Infof("Attached bridge endpoint [%v] to host", bridgeEndpointName) + + // enable forwarding on the host interface and endpoint + netHelper := netsh.New(nil) + for _, interfaceIpAddress := range []string{hnsNetwork.ManagementIP, hnsEndpoint.IPAddress.String()} { + netInterface, err := netHelper.GetInterfaceByIP(interfaceIpAddress) + if err != nil { + return nil, fmt.Errorf("unable to find interface for IP Addess [%v], error: %v", interfaceIpAddress, err) + } + + interfaceName := netInterface.Name + if err := netHelper.EnableForwarding(interfaceName); err != nil { + return nil, fmt.Errorf("unable to enable forwarding on [%v], error: %v", interfaceName, err) + } + glog.Infof("Enabled forwarding on [%v]", interfaceName) + } + + return n, nil +} diff --git a/backend/udp/cproxy.go b/backend/udp/cproxy.go index bf65afd5f..ad6d4f6da 100644 --- a/backend/udp/cproxy.go +++ b/backend/udp/cproxy.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/udp/proxy.c b/backend/udp/proxy.c index 4a0416987..2f8f0bf7e 100644 --- a/backend/udp/proxy.c +++ b/backend/udp/proxy.c @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/udp/proxy.h b/backend/udp/proxy.h index ddf6c3c04..2aca6a910 100644 --- a/backend/udp/proxy.h +++ b/backend/udp/proxy.h @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 CoreOS, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/udp/udp.go b/backend/udp/udp.go index cca3040b2..d192151f7 100644 --- a/backend/udp/udp.go +++ b/backend/udp/udp.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/udp/udp_network.go b/backend/udp/udp_network.go index c15dcb929..8bc3a0d02 100644 --- a/backend/udp/udp_network.go +++ b/backend/udp/udp_network.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/udp/udp_unspecified.go b/backend/udp/udp_unspecified.go new file mode 100644 index 000000000..2fa3c0ad2 --- /dev/null +++ b/backend/udp/udp_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package udp + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("udp is not supported on this platform") +} diff --git a/backend/vxlan/device.go b/backend/vxlan/device.go index c0a8c3124..9b66130e5 100644 --- a/backend/vxlan/device.go +++ b/backend/vxlan/device.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/vxlan/vxlan.go b/backend/vxlan/vxlan.go index 6dd1da1c3..85e3d640b 100644 --- a/backend/vxlan/vxlan.go +++ b/backend/vxlan/vxlan.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/vxlan/vxlan_network.go b/backend/vxlan/vxlan_network.go index 4a45c5335..be366b825 100644 --- a/backend/vxlan/vxlan_network.go +++ b/backend/vxlan/vxlan_network.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/backend/vxlan/vxlan_network_windows.go b/backend/vxlan/vxlan_network_windows.go new file mode 100644 index 000000000..34a517f69 --- /dev/null +++ b/backend/vxlan/vxlan_network_windows.go @@ -0,0 +1,195 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vxlan + +import ( + "sync" + + log "github.com/golang/glog" + "golang.org/x/net/context" + + "github.com/coreos/flannel/backend" + "github.com/coreos/flannel/subnet" + + "encoding/json" + "fmt" + "github.com/Microsoft/hcsshim" + "github.com/coreos/flannel/pkg/ip" + "net" + "time" +) + +type network struct { + name string + networkId string + macPrefix string + extIface *backend.ExternalInterface + lease *subnet.Lease + sm subnet.Manager +} + +func (n *network) Lease() *subnet.Lease { + return n.lease +} + +func (n *network) MTU() int { + return n.extIface.Iface.MTU +} + +func (n *network) Run(ctx context.Context) { + wg := sync.WaitGroup{} + + log.Info("Watching for new subnet leases") + evts := make(chan []subnet.Event) + wg.Add(1) + go func() { + subnet.WatchLeases(ctx, n.sm, n.lease, evts) + wg.Done() + }() + + defer wg.Wait() + + for { + select { + case evtBatch := <-evts: + n.handleSubnetEvents(evtBatch) + + case <-ctx.Done(): + return + } + } +} + +func conjureMac(macPrefix string, ip ip.IP4) string { + a, b, c, d := ip.Octets() + return fmt.Sprintf("%v-%02x-%02x-%02x-%02x", macPrefix, a, b, c, d) +} + +func (n *network) handleSubnetEvents(batch []subnet.Event) { + for _, evt := range batch { + if evt.Lease.Attrs.BackendType != "vxlan" { + log.Warningf("Ignoring non-vxlan subnet: type=%v", evt.Lease.Attrs.BackendType) + continue + } + + if evt.Type != subnet.EventAdded && evt.Type != subnet.EventRemoved { + log.Error("Internal error: unknown event type: ", int(evt.Type)) + continue + } + + // add or delete all possible remote IPs (excluding gateway & bcast) as remote endpoints + managementIp := evt.Lease.Attrs.PublicIP.String() + lastIP := evt.Lease.Subnet.Next().IP - 1 + + start := time.Now() + for remoteIp := evt.Lease.Subnet.IP + 2; remoteIp < lastIP; remoteIp++ { + remoteMac := conjureMac(n.macPrefix, remoteIp) + remoteEndpointName := fmt.Sprintf("remote_%v", remoteIp.String()) + + if evt.Type == subnet.EventAdded { + if err := createRemoteEndpoint(remoteEndpointName, remoteIp, remoteMac, managementIp, n.networkId); err != nil { + log.Errorf("failed to create remote endpoint [%v], error: %v", remoteEndpointName, err) + } + } else { + if hnsEndpoint, err := hcsshim.GetHNSEndpointByName(remoteEndpointName); err != nil { + if _, err := hnsEndpoint.Delete(); err != nil { + log.Errorf("unable to delete existing remote endpoint [%v], error: %v", remoteEndpointName, err) + } + } + } + } + + t := time.Now() + elapsed := t.Sub(start) + + message := "Subnet removed" + if evt.Type == subnet.EventAdded { + message = "Subnet added" + } + log.Infof("%v: %v [%v ns]", message, evt.Lease.Subnet, elapsed.Nanoseconds()) + } +} + +func checkPAAddress(hnsEndpoint *hcsshim.HNSEndpoint, managementAddress string) bool { + if hnsEndpoint.Policies == nil { + return false + } + + for _, policyJson := range hnsEndpoint.Policies { + var policy map[string]interface{} + if json.Unmarshal(policyJson, &policy) != nil { + return false + } + + if valType, ok := policy["Type"]; ok && valType.(string) == "PA" { + if val, ok := policy["PA"]; ok { + if val.(string) == managementAddress { + return true + } + } + } + } + + return false +} + +func createRemoteEndpoint(remoteEndpointName string, remoteIp ip.IP4, remoteMac string, managementAddress string, networkId string) error { + + // find existing + hnsEndpoint, err := hcsshim.GetHNSEndpointByName(remoteEndpointName) + if err == nil && hnsEndpoint.VirtualNetwork == networkId && checkPAAddress(hnsEndpoint, managementAddress) { + return nil + } + + // create or replace endpoint + if hnsEndpoint != nil { + if _, err = hnsEndpoint.Delete(); err != nil { + log.Errorf("unable to delete existing remote endpoint [%v], error: %v", remoteEndpointName, err) + return err + } + } + + paPolicy := struct { + Type string + PA string + }{ + Type: "PA", + PA: managementAddress, + } + + policyBytes, _ := json.Marshal(&paPolicy) + + hnsEndpoint = &hcsshim.HNSEndpoint{ + Id: "", + Name: remoteEndpointName, + IPAddress: net.IPv4(remoteIp.Octets()), + MacAddress: remoteMac, + VirtualNetwork: networkId, + IsRemoteEndpoint: true, + Policies: []json.RawMessage{ + policyBytes, + }, + } + + hnsEndpoint, err = hnsEndpoint.Create() + if err != nil { + log.Errorf("unable to create remote endpoint [%v], error: %v", remoteEndpointName, err) + return err + } + + return nil +} diff --git a/backend/vxlan/vxlan_network_windows_test.go b/backend/vxlan/vxlan_network_windows_test.go new file mode 100644 index 000000000..c3553af6a --- /dev/null +++ b/backend/vxlan/vxlan_network_windows_test.go @@ -0,0 +1,94 @@ +// +build windows + +// Copyright 2015 CNI authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package vxlan + +import ( + "encoding/json" + "github.com/Microsoft/hcsshim" + "github.com/stretchr/testify/assert" + "testing" +) + +const ( + remoteEndpointJson = ` + { + "ActivityId": "193b6f8d-b760-4940-9abb-66a91e5af49a", + "EncapOverhead": 50, + "ID": "8f109901-9970-4fda-bb2b-e9882fb3e8fb", + "IPAddress": "10.244.2.141", + "IsRemoteEndpoint": true, + "MacAddress": "0E-2A-0a-f4-02-8d", + "Name": "remote_10.244.2.141", + "Policies": [ + { + "PA": "10.123.74.74", + "Type": "PA" + } + ], + "PrefixLength": 16, + "Resources": { + "AllocationOrder": 1, + "Allocators": [ + { + "AllocationOrder": 0, + "CA": "10.244.2.141", + "ID": "c11177da-9d65-4f81-bd76-b11d1a055b38", + "IsLocal": false, + "IsPolicy": true, + "PA": "10.123.74.74", + "Tag": "VNET Policy", + "Type": 3 + } + ], + "ID": "193b6f8d-b760-4940-9abb-66a91e5af49a", + "PortOperationTime": 0, + "State": 1, + "SwitchOperationTime": 0, + "VfpOperationTime": 0, + "parentId": "e469c3a9-20ee-4de5-8fc2-d8ac99a49b18" + }, + "SharedContainers": [ + + ], + "State": 1, + "Type": "Overlay", + "Version": 21474836481, + "VirtualNetwork": "9d59c0df-b1e2-4eee-9fde-b7ea829fc6a1", + "VirtualNetworkName": "vxlan0" + } + ` +) + +func TestCheckPAAddressMatch(t *testing.T) { + endpointJson := []byte(remoteEndpointJson) + var hnsEndpoint hcsshim.HNSEndpoint + err := json.Unmarshal(endpointJson, &hnsEndpoint) + assert.Nil(t, err) + assert.True(t, checkPAAddress(&hnsEndpoint, "10.123.74.74")) +} + +func TestCheckPAAddressNoMatch(t *testing.T) { + endpointJson := []byte(remoteEndpointJson) + var hnsEndpoint hcsshim.HNSEndpoint + err := json.Unmarshal(endpointJson, &hnsEndpoint) + assert.Nil(t, err) + assert.False(t, checkPAAddress(&hnsEndpoint, "someOtherAddress")) +} + +func TestCheckPAAddressNoMatchIfNil(t *testing.T) { + var hnsEndpoint hcsshim.HNSEndpoint + assert.False(t, checkPAAddress(&hnsEndpoint, "someOtherAddress")) +} diff --git a/backend/vxlan/vxlan_unspecified.go b/backend/vxlan/vxlan_unspecified.go new file mode 100644 index 000000000..de9658e48 --- /dev/null +++ b/backend/vxlan/vxlan_unspecified.go @@ -0,0 +1,25 @@ +// +build !linux,!windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vxlan + +import ( + log "github.com/golang/glog" +) + +func init() { + log.Infof("vxlan is not supported on this platform") +} diff --git a/backend/vxlan/vxlan_windows.go b/backend/vxlan/vxlan_windows.go new file mode 100644 index 000000000..ccb5b1ba9 --- /dev/null +++ b/backend/vxlan/vxlan_windows.go @@ -0,0 +1,191 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package vxlan + +// Some design notes: +// VXLAN encapsulates L2 packets (though flannel is L3 only so don't expect to be able to send L2 packets across hosts) +// Windows overlay decap works at L2 and so it needs the correct destination MAC for the remote host to work. +// Windows does not expose an L3Miss interface so for now all possible remote IP/MAC pairs have to be configured upfront. +// +// In this scheme the scaling of table entries (per host) is: +// - 1 network entry for the overlay network +// - 1 endpoint per local container +// - N remote endpoints remote node (total endpoints = +import ( + "encoding/json" + "fmt" + + log "github.com/golang/glog" + + "golang.org/x/net/context" + + "errors" + "github.com/Microsoft/hcsshim" + "github.com/coreos/flannel/backend" + "github.com/coreos/flannel/pkg/ip" + "github.com/coreos/flannel/subnet" +) + +func init() { + backend.Register("vxlan", New) +} + +const ( + minimumVNI = 4096 + vxlanPort = 4789 +) + +type VXLANBackend struct { + sm subnet.Manager + extIface *backend.ExternalInterface + networks map[string]*network +} + +func New(sm subnet.Manager, extIface *backend.ExternalInterface) (backend.Backend, error) { + + be := &VXLANBackend{ + sm: sm, + extIface: extIface, + networks: make(map[string]*network), + } + + return be, nil +} + +func (be *VXLANBackend) RegisterNetwork(ctx context.Context, config *subnet.Config) (backend.Network, error) { + // TODO: are these used? how to pass to HNS? + cfg := struct { + name string + macPrefix string + VNI int + Port int + GBP bool + DirectRouting bool + }{ + name: "vxlan0", + VNI: minimumVNI, + Port: vxlanPort, + macPrefix: "0E-2A", + } + + if len(config.Backend) > 0 { + if err := json.Unmarshal(config.Backend, &cfg); err != nil { + return nil, fmt.Errorf("error decoding VXLAN backend config: %v", err) + } + } + + if cfg.VNI < minimumVNI { + return nil, fmt.Errorf("invalid VXLAN backend config. VNI [%v] must be greater than or equal to %v on Windows", cfg.VNI, minimumVNI) + } + + if cfg.Port != vxlanPort { + return nil, fmt.Errorf("invalid VXLAN backend config. Port [%v] is not supported on Windows. Omit the setting to default to port %v", cfg.Port, vxlanPort) + } + + if cfg.DirectRouting == true { + return nil, errors.New("invalid VXLAN backend config. DirectRouting is not supported on Windows") + } + + if cfg.GBP == true { + return nil, errors.New("invalid VXLAN backend config. GBP is not supported on Windows") + } + + if cfg.macPrefix == "" || len(cfg.macPrefix) != 5 || cfg.macPrefix[2] != '-' { + return nil, fmt.Errorf("invalid VXLAN backend config. macPrefix [%v] is invalid, prefix must be of the format xx-xx e.g. 0E-2A", cfg.macPrefix) + } + + log.Infof("VXLAN config: %+v", cfg) + + n := &network{ + extIface: be.extIface, + sm: be.sm, + name: be.extIface.Iface.Name, + macPrefix: cfg.macPrefix, + } + + attrs := subnet.LeaseAttrs{ + PublicIP: ip.FromIP(be.extIface.ExtAddr), + BackendType: "vxlan", + } + + l, err := be.sm.AcquireLease(ctx, &attrs) + switch err { + case nil: + n.lease = l + + case context.Canceled, context.DeadlineExceeded: + return nil, err + + default: + return nil, fmt.Errorf("failed to acquire lease: %v", err) + } + + // check if the network exists and has the expected settings? + networkName := cfg.name + createNetwork := true + addressPrefix := config.Network + networkGatewayAddress := config.Network.IP + 1 + hnsNetwork, err := hcsshim.GetHNSNetworkByName(networkName) + if err == nil { + log.Infof("Found existing HNS network [%+v]", hnsNetwork) + n.networkId = hnsNetwork.Id + createNetwork = false + } + + if createNetwork { + // create, but a network with the same name exists? + if hnsNetwork != nil { + if _, err := hnsNetwork.Delete(); err != nil { + return nil, fmt.Errorf("unable to delete existing network [%v], error: %v", hnsNetwork.Name, err) + } + log.Infof("Deleted stale HNS network [%v]", networkName) + } + + // create the underlying windows HNS network + request := map[string]interface{}{ + "Name": networkName, + "Type": "Overlay", + "Subnets": []interface{}{ + map[string]interface{}{ + "AddressPrefix": addressPrefix, + "GatewayAddress": networkGatewayAddress, + "Policies": []interface{}{ + map[string]interface{}{ + "Type": "VSID", + "VSID": cfg.VNI, + }, + }, + }, + }, + } + + jsonRequest, err := json.Marshal(request) + if err != nil { + return nil, err + } + + log.Infof("Attempting to create HNS network, request: %v", string(jsonRequest)) + hnsNetwork, err := hcsshim.HNSNetworkRequest("POST", "", string(jsonRequest)) + if err != nil { + return nil, fmt.Errorf("unable to create network [%v], error: %v", networkName, err) + } + log.Infof("Created HNS network [%v] as %+v", networkName, hnsNetwork) + n.networkId = hnsNetwork.Id + } + + return n, nil +} diff --git a/main.go b/main.go index de158ac2a..24cccf1ca 100644 --- a/main.go +++ b/main.go @@ -28,7 +28,6 @@ import ( "strings" "syscall" - "github.com/coreos/go-iptables/iptables" "github.com/coreos/pkg/flagutil" log "github.com/golang/glog" "golang.org/x/net/context" @@ -285,7 +284,7 @@ func main() { // Set up ipMasq if needed if opts.ipMasq { - go setupIPMasq(config, bn) + go network.SetupAndEnsureIPMasq(config.Network, bn.Lease()) } if err := WriteSubnetFile(opts.subnetFile, config.Network, opts.ipMasq, bn); err != nil { @@ -553,26 +552,6 @@ func mustRunHealthz() { } } -func setupIPMasq(config *subnet.Config, bn backend.Network) { - ipt, err := iptables.New() - if err != nil { - // if we can't find iptables, give up and return - log.Errorf("Failed to set up IP Masquerade. iptables was not found: %v", err) - return - } - defer func() { - network.TeardownIPMasq(ipt, config.Network, bn.Lease()) - }() - for { - // Ensure that all the rules exist every 5 seconds - if err := network.EnsureIPMasq(ipt, config.Network, bn.Lease()); err != nil { - log.Errorf("Failed to ensure IP Masquerade: %v", err) - } - time.Sleep(5 * time.Second) - } - -} - func ReadSubnetFromSubnetFile(path string) ip.IP4Net { var prevSubnet ip.IP4Net if _, err := os.Stat(path); !os.IsNotExist(err) { diff --git a/network/ipmasq.go b/network/ipmasq.go index bcbf3315d..e2c2c9bcb 100644 --- a/network/ipmasq.go +++ b/network/ipmasq.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +24,7 @@ import ( "github.com/coreos/flannel/pkg/ip" "github.com/coreos/flannel/subnet" + "github.com/coreos/go-iptables/iptables" ) type IPTablesRules interface { @@ -60,6 +63,24 @@ func ipMasqRulesExist(ipt IPTablesRules, ipn ip.IP4Net, lease *subnet.Lease) (bo return true, nil } +func SetupAndEnsureIPMasq(network ip.IP4Net, lease *subnet.Lease) { + ipt, err := iptables.New() + if err != nil { + // if we can't find iptables, give up and return + log.Errorf("Failed to set up IP Masquerade. iptables was not found: %v", err) + return + } + defer func() { + TeardownIPMasq(ipt, network, lease) + }() + for { + // Ensure that all the rules exist every 5 seconds + if err := EnsureIPMasq(ipt, network, lease); err != nil { + log.Errorf("Failed to ensure IP Masquerade: %v", err) + } + time.Sleep(5 * time.Second) + } +} func EnsureIPMasq(ipt IPTablesRules, ipn ip.IP4Net, lease *subnet.Lease) error { exists, err := ipMasqRulesExist(ipt, ipn, lease) diff --git a/network/ipmasq_unspecified.go b/network/ipmasq_unspecified.go new file mode 100644 index 000000000..7701921b2 --- /dev/null +++ b/network/ipmasq_unspecified.go @@ -0,0 +1,32 @@ +// +build !linux,!windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "errors" + + "github.com/coreos/flannel/pkg/ip" + "github.com/coreos/flannel/subnet" +) + +func SetupIPMasq(ipn ip.IP4Net, lease *subnet.Lease) error { + return errors.New("SetupIPMasq not implemented for this platform") +} + +func TeardownIPMasq(ipn ip.IP4Net, lease *subnet.Lease) error { + return errors.New("TeardownIPMasq not implemented for this platform") +} diff --git a/network/ipmasq_windows.go b/network/ipmasq_windows.go new file mode 100644 index 000000000..df2d7c59d --- /dev/null +++ b/network/ipmasq_windows.go @@ -0,0 +1,34 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package network + +import ( + "github.com/coreos/flannel/pkg/ip" + "github.com/coreos/flannel/subnet" +) + +func SetupIPMasq(ipn ip.IP4Net, lease *subnet.Lease) error { + return nil +} + +func TeardownIPMasq(ipn ip.IP4Net, lease *subnet.Lease) error { + return nil +} + +func SetupAndEnsureIPMasq(network ip.IP4Net, lease *subnet.Lease) { + +} \ No newline at end of file diff --git a/pkg/ip/iface.go b/pkg/ip/iface.go index a5e2da31f..b8ec1b397 100644 --- a/pkg/ip/iface.go +++ b/pkg/ip/iface.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/pkg/ip/iface_unspecified.go b/pkg/ip/iface_unspecified.go new file mode 100644 index 000000000..76740e493 --- /dev/null +++ b/pkg/ip/iface_unspecified.go @@ -0,0 +1,38 @@ +// +build !linux,!windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "errors" + "net" +) + +func GetIfaceIP4Addr(iface *net.Interface) (net.IP, error) { + return nil, errors.New("GetIfaceIP4Addr not implemented for this platform") +} + +func GetIfaceIP4AddrMatch(iface *net.Interface, matchAddr net.IP) error { + return errors.New("GetIfaceIP4AddrMatch not implemented for this platform") +} + +func GetDefaultGatewayIface() (*net.Interface, error) { + return nil, errors.New("GetDefaultGatewayIface not implemented for this platform") +} + +func GetInterfaceByIP(ip net.IP) (*net.Interface, error) { + return nil, errors.New("GetInterfaceByIP not implemented for this platform") +} diff --git a/pkg/ip/iface_windows.go b/pkg/ip/iface_windows.go new file mode 100644 index 000000000..ae547da96 --- /dev/null +++ b/pkg/ip/iface_windows.go @@ -0,0 +1,68 @@ +// +build windows + +// Copyright 2015 flannel authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package ip + +import ( + "net" + netsh "github.com/rakelkar/gonetsh/netsh" +) + +func GetIfaceIP4Addr(iface *net.Interface) (net.IP, error) { + // get ip address for the interface + // prefer global unicast to link local addresses + netHelper := netsh.New(nil) + ifaceDetails, err := netHelper.GetInterfaceByName(iface.Name) + if err != nil { + return nil, err + } + + ifAddr := net.ParseIP(ifaceDetails.IpAddress) + + return ifAddr, nil +} + +func GetDefaultGatewayIface() (*net.Interface, error) { + netHelper := netsh.New(nil) + + defaultIfaceName, err := netHelper.GetDefaultGatewayIfaceName() + if err != nil { + return nil, err + } + + iface, err := net.InterfaceByName(defaultIfaceName) + if err != nil { + return nil, err + } + + return iface, nil +} + +func GetInterfaceByIP(ip net.IP) (*net.Interface, error) { + netHelper := netsh.New(nil) + + ifaceDetails, err := netHelper.GetInterfaceByIP(ip.String()) + if err != nil { + return nil, err + } + + iface, err := net.InterfaceByName(ifaceDetails.Name) + if err != nil { + return nil, err + } + + return iface, nil +} diff --git a/pkg/ip/tun.go b/pkg/ip/tun.go index 82ae54006..9feb89d9e 100644 --- a/pkg/ip/tun.go +++ b/pkg/ip/tun.go @@ -1,3 +1,5 @@ +// +build linux + // Copyright 2015 flannel authors // // Licensed under the Apache License, Version 2.0 (the "License");