Quellcode durchsuchen

Merge pull request #1238 from matthiasng/improve-windows-support

Improve windows support
Rajat Chopra vor 4 Jahren
Ursprung
Commit
80fd09edd9

+ 21 - 16
backend/hostgw/hostgw_windows.go

@@ -16,7 +16,7 @@ package hostgw
 
 import (
 	"fmt"
-	"strconv"
+	"github.com/coreos/flannel/pkg/routing"
 	"sync"
 	"time"
 
@@ -26,12 +26,9 @@ import (
 	"github.com/coreos/flannel/subnet"
 	log "github.com/golang/glog"
 	"github.com/juju/errors"
-	"github.com/rakelkar/gonetsh/netroute"
-	"github.com/rakelkar/gonetsh/netsh"
 	"golang.org/x/net/context"
 	"k8s.io/apimachinery/pkg/util/json"
 	"k8s.io/apimachinery/pkg/util/wait"
-	utilexec "k8s.io/utils/exec"
 )
 
 func init() {
@@ -81,11 +78,11 @@ func (be *HostgwBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup,
 		Mtu:         be.extIface.Iface.MTU,
 		LinkIndex:   be.extIface.Iface.Index,
 	}
-	n.GetRoute = func(lease *subnet.Lease) *netroute.Route {
-		return &netroute.Route{
+	n.GetRoute = func(lease *subnet.Lease) *routing.Route {
+		return &routing.Route{
 			DestinationSubnet: lease.Subnet.ToIPNet(),
 			GatewayAddress:    lease.Attrs.PublicIP.ToIP(),
-			LinkIndex:         n.LinkIndex,
+			InterfaceIndex:    n.LinkIndex,
 		}
 	}
 
@@ -108,7 +105,6 @@ func (be *HostgwBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup,
 	}
 
 	// 3. Check if the network exists and has the expected settings
-	netshHelper := netsh.New(utilexec.New())
 	createNewNetwork := true
 	expectedSubnet := n.SubnetLease.Subnet
 	expectedAddressPrefix := expectedSubnet.String()
@@ -172,8 +168,13 @@ func (be *HostgwBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup,
 
 		// Wait for the interface with the management IP
 		log.Infof("Waiting to get net interface for HNSNetwork %s (%s)", networkName, newNetwork.ManagementIP)
+		managementIP, err := ip.ParseIP4(newNetwork.ManagementIP)
+		if err != nil {
+			return nil, errors.Annotatef(err, "Failed to parse management ip (%s)", newNetwork.ManagementIP)
+		}
+
 		waitErr = wait.Poll(500*time.Millisecond, 5*time.Second, func() (done bool, err error) {
-			_, lastErr = netshHelper.GetInterfaceByIP(newNetwork.ManagementIP)
+			_, lastErr = ip.GetInterfaceByIP(managementIP.ToIP())
 			return lastErr == nil, nil
 		})
 		if waitErr == wait.ErrWaitTimeout {
@@ -230,23 +231,27 @@ func (be *HostgwBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup,
 
 	// 7. Enable forwarding on the host interface and endpoint
 	for _, interfaceIpAddress := range []string{expectedNetwork.ManagementIP, expectedBridgeEndpoint.IPAddress.String()} {
-		netInterface, err := netshHelper.GetInterfaceByIP(interfaceIpAddress)
+		ipv4, err := ip.ParseIP4(interfaceIpAddress)
+		if err != nil {
+			return nil, errors.Annotatef(err, "Failed to parse expected net interface ip (%s)", interfaceIpAddress)
+		}
+
+		netInterface, err := ip.GetInterfaceByIP(ipv4.ToIP())
 		if err != nil {
 			return nil, errors.Annotatef(err, "failed to find interface for IP Address %s", interfaceIpAddress)
 		}
 		log.Infof("Found %+v interface with IP %s", netInterface, interfaceIpAddress)
 
 		// When a new hns network is created, the interface is modified, esp the name, index
-		if expectedNetwork.ManagementIP == netInterface.IpAddress {
-			n.LinkIndex = netInterface.Idx
+		if expectedNetwork.ManagementIP == ipv4.String() {
+			n.LinkIndex = netInterface.Index
 			n.Name = netInterface.Name
 		}
 
-		interfaceIdx := strconv.Itoa(netInterface.Idx)
-		if err := netshHelper.EnableForwarding(interfaceIdx); err != nil {
-			return nil, errors.Annotatef(err, "failed to enable forwarding on %s index %s", netInterface.Name, interfaceIdx)
+		if err := ip.EnableForwardingForInterface(netInterface); err != nil {
+			return nil, errors.Annotatef(err, "failed to enable forwarding on %s index %d", netInterface.Name, netInterface.Index)
 		}
-		log.Infof("Enabled forwarding on %s index %s", netInterface.Name, interfaceIdx)
+		log.Infof("Enabled forwarding on %s index %d", netInterface.Name, netInterface.Index)
 	}
 
 	return n, nil

+ 17 - 17
backend/route_network_windows.go

@@ -19,9 +19,9 @@ import (
 	"time"
 
 	log "github.com/golang/glog"
-	"github.com/rakelkar/gonetsh/netroute"
 	"golang.org/x/net/context"
 
+	"github.com/coreos/flannel/pkg/routing"
 	"github.com/coreos/flannel/subnet"
 	"strings"
 )
@@ -35,10 +35,10 @@ type RouteNetwork struct {
 	Name        string
 	BackendType string
 	SM          subnet.Manager
-	GetRoute    func(lease *subnet.Lease) *netroute.Route
+	GetRoute    func(lease *subnet.Lease) *routing.Route
 	Mtu         int
 	LinkIndex   int
-	routes      []netroute.Route
+	routes      []routing.Route
 }
 
 func (n *RouteNetwork) MTU() int {
@@ -56,7 +56,7 @@ func (n *RouteNetwork) Run(ctx context.Context) {
 		wg.Done()
 	}()
 
-	n.routes = make([]netroute.Route, 0, 10)
+	n.routes = make([]routing.Route, 0, 10)
 	wg.Add(1)
 	go func() {
 		n.routeCheck(ctx)
@@ -77,7 +77,7 @@ func (n *RouteNetwork) Run(ctx context.Context) {
 }
 
 func (n *RouteNetwork) handleSubnetEvents(batch []subnet.Event) {
-	netrouteHelper := netroute.New()
+	router := routing.RouterWindows{}
 
 	for _, evt := range batch {
 		leaseSubnet := evt.Lease.Subnet
@@ -93,7 +93,7 @@ func (n *RouteNetwork) handleSubnetEvents(batch []subnet.Event) {
 		case subnet.EventAdded:
 			log.Infof("Subnet added: %v via %v", leaseSubnet, leaseAttrs.PublicIP)
 
-			existingRoutes, _ := netrouteHelper.GetNetRoutes(expectedRoute.LinkIndex, expectedRoute.DestinationSubnet)
+			existingRoutes, _ := router.GetRoutesFromInterfaceToSubnet(expectedRoute.InterfaceIndex, expectedRoute.DestinationSubnet)
 			if len(existingRoutes) > 0 {
 				existingRoute := existingRoutes[0]
 				if existingRoute.Equal(*expectedRoute) {
@@ -101,14 +101,14 @@ func (n *RouteNetwork) handleSubnetEvents(batch []subnet.Event) {
 				}
 
 				log.Warningf("Replacing existing route %v via %v with %v via %v", leaseSubnet, existingRoute.GatewayAddress, leaseSubnet, leaseAttrs.PublicIP)
-				err := netrouteHelper.RemoveNetRoute(existingRoute.LinkIndex, existingRoute.DestinationSubnet, existingRoute.GatewayAddress)
+				err := router.DeleteRoute(existingRoute.InterfaceIndex, existingRoute.DestinationSubnet, existingRoute.GatewayAddress)
 				if err != nil {
 					log.Errorf("Error removing route: %v", err)
 					continue
 				}
 			}
 
-			err := netrouteHelper.NewNetRoute(expectedRoute.LinkIndex, expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress)
+			err := router.CreateRoute(expectedRoute.InterfaceIndex, expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress)
 			if err != nil {
 				log.Errorf("Error creating route: %v", err)
 				continue
@@ -119,13 +119,13 @@ func (n *RouteNetwork) handleSubnetEvents(batch []subnet.Event) {
 		case subnet.EventRemoved:
 			log.Infof("Subnet removed: %v", leaseSubnet)
 
-			existingRoutes, _ := netrouteHelper.GetNetRoutes(expectedRoute.LinkIndex, expectedRoute.DestinationSubnet)
+			existingRoutes, _ := router.GetRoutesFromInterfaceToSubnet(expectedRoute.InterfaceIndex, expectedRoute.DestinationSubnet)
 			if len(existingRoutes) > 0 {
 				existingRoute := existingRoutes[0]
 				if existingRoute.Equal(*expectedRoute) {
 					log.Infof("Removing existing route %v via %v", leaseSubnet, existingRoute.GatewayAddress)
 
-					err := netrouteHelper.RemoveNetRoute(existingRoute.LinkIndex, existingRoute.DestinationSubnet, existingRoute.GatewayAddress)
+					err := router.DeleteRoute(existingRoute.InterfaceIndex, existingRoute.DestinationSubnet, existingRoute.GatewayAddress)
 					if err != nil {
 						log.Warningf("Error removing route: %v", err)
 					}
@@ -140,7 +140,7 @@ func (n *RouteNetwork) handleSubnetEvents(batch []subnet.Event) {
 	}
 }
 
-func (n *RouteNetwork) addToRouteList(newRoute *netroute.Route) {
+func (n *RouteNetwork) addToRouteList(newRoute *routing.Route) {
 	for _, route := range n.routes {
 		if route.Equal(*newRoute) {
 			return
@@ -150,7 +150,7 @@ func (n *RouteNetwork) addToRouteList(newRoute *netroute.Route) {
 	n.routes = append(n.routes, *newRoute)
 }
 
-func (n *RouteNetwork) removeFromRouteList(oldRoute *netroute.Route) {
+func (n *RouteNetwork) removeFromRouteList(oldRoute *routing.Route) {
 	for index, route := range n.routes {
 		if route.Equal(*oldRoute) {
 			n.routes = append(n.routes[:index], n.routes[index+1:]...)
@@ -171,9 +171,9 @@ func (n *RouteNetwork) routeCheck(ctx context.Context) {
 }
 
 func (n *RouteNetwork) checkSubnetExistInRoutes() {
-	netrouteHelper := netroute.New()
+	router := routing.RouterWindows{}
 
-	existingRoutes, err := netrouteHelper.GetNetRoutesAll()
+	existingRoutes, err := router.GetAllRoutes()
 	if err != nil {
 		log.Errorf("Error enumerating routes: %v", err)
 		return
@@ -188,12 +188,12 @@ func (n *RouteNetwork) checkSubnetExistInRoutes() {
 		}
 
 		if !exist {
-			err := netrouteHelper.NewNetRoute(expectedRoute.LinkIndex, expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress)
+			err := router.CreateRoute(expectedRoute.InterfaceIndex, expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress)
 			if err != nil {
-				log.Warningf("Error recovering route to %v via %v on %v (%v).", expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress, expectedRoute.LinkIndex, err)
+				log.Warningf("Error recovering route to %v via %v on %v (%v).", expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress, expectedRoute.InterfaceIndex, err)
 				continue
 			}
-			log.Infof("Recovered route to %v via %v on %v.", expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress, expectedRoute.LinkIndex)
+			log.Infof("Recovered route to %v via %v on %v.", expectedRoute.DestinationSubnet, expectedRoute.GatewayAddress, expectedRoute.InterfaceIndex)
 		}
 	}
 }

+ 7 - 4
backend/vxlan/device_windows.go

@@ -11,6 +11,7 @@
 // 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.
+// +build windows
 
 package vxlan
 
@@ -20,9 +21,7 @@ import (
 	"github.com/coreos/flannel/pkg/ip"
 	log "github.com/golang/glog"
 	"github.com/juju/errors"
-	"github.com/rakelkar/gonetsh/netsh"
 	"k8s.io/apimachinery/pkg/util/wait"
-	utilexec "k8s.io/utils/exec"
 	"time"
 )
 
@@ -132,10 +131,14 @@ func ensureNetwork(expectedNetwork *hcn.HostComputeNetwork, expectedAddressPrefi
 
 		managementIP := getManagementIP(newNetwork)
 		// Wait for the interface with the management IP
-		netshHelper := netsh.New(utilexec.New())
 		log.Infof("Waiting to get net interface for HostComputeNetwork %s (%s)", networkName, managementIP)
+		managementIPv4, err := ip.ParseIP4(managementIP)
+		if err != nil {
+			return nil, errors.Annotatef(err, "Failed to parse management ip (%s)", managementIP)
+		}
+
 		waitErr = wait.Poll(500*time.Millisecond, 5*time.Second, func() (done bool, err error) {
-			_, lastErr = netshHelper.GetInterfaceByIP(managementIP)
+			_, lastErr = ip.GetInterfaceByIP(managementIPv4.ToIP())
 			return lastErr == nil, nil
 		})
 		if waitErr == wait.ErrWaitTimeout {

+ 4 - 4
main.go

@@ -471,7 +471,7 @@ func LookupExtIface(ifname string, ifregex string) (*backend.ExternalInterface,
 
 		// Check IP
 		for _, ifaceToMatch := range ifaces {
-			ifaceIP, err := ip.GetIfaceIP4Addr(&ifaceToMatch)
+			ifaceIP, err := ip.GetInterfaceIP4Addr(&ifaceToMatch)
 			if err != nil {
 				// Skip if there is no IPv4 address
 				continue
@@ -508,7 +508,7 @@ func LookupExtIface(ifname string, ifregex string) (*backend.ExternalInterface,
 		if iface == nil {
 			var availableFaces []string
 			for _, f := range ifaces {
-				ip, _ := ip.GetIfaceIP4Addr(&f) // We can safely ignore errors. We just won't log any ip
+				ip, _ := ip.GetInterfaceIP4Addr(&f) // We can safely ignore errors. We just won't log any ip
 				availableFaces = append(availableFaces, fmt.Sprintf("%s:%s", f.Name, ip))
 			}
 
@@ -516,13 +516,13 @@ func LookupExtIface(ifname string, ifregex string) (*backend.ExternalInterface,
 		}
 	} else {
 		log.Info("Determining IP address of default interface")
-		if iface, err = ip.GetDefaultGatewayIface(); err != nil {
+		if iface, err = ip.GetDefaultGatewayInterface(); err != nil {
 			return nil, fmt.Errorf("failed to get default interface: %s", err)
 		}
 	}
 
 	if ifaceAddr == nil {
-		ifaceAddr, err = ip.GetIfaceIP4Addr(iface)
+		ifaceAddr, err = ip.GetInterfaceIP4Addr(iface)
 		if err != nil {
 			return nil, fmt.Errorf("failed to find IPv4 address for interface %s", iface.Name)
 		}

+ 4 - 5
pkg/ip/iface.go

@@ -13,7 +13,6 @@
 // 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.
-// +build !windows
 
 package ip
 
@@ -36,7 +35,7 @@ func getIfaceAddrs(iface *net.Interface) ([]netlink.Addr, error) {
 	return netlink.AddrList(link, syscall.AF_INET)
 }
 
-func GetIfaceIP4Addr(iface *net.Interface) (net.IP, error) {
+func GetInterfaceIP4Addr(iface *net.Interface) (net.IP, error) {
 	addrs, err := getIfaceAddrs(iface)
 	if err != nil {
 		return nil, err
@@ -67,7 +66,7 @@ func GetIfaceIP4Addr(iface *net.Interface) (net.IP, error) {
 	return nil, errors.New("No IPv4 address found for given interface")
 }
 
-func GetIfaceIP4AddrMatch(iface *net.Interface, matchAddr net.IP) error {
+func GetInterfaceIP4AddrMatch(iface *net.Interface, matchAddr net.IP) error {
 	addrs, err := getIfaceAddrs(iface)
 	if err != nil {
 		return err
@@ -86,7 +85,7 @@ func GetIfaceIP4AddrMatch(iface *net.Interface, matchAddr net.IP) error {
 	return errors.New("No IPv4 address found for given interface")
 }
 
-func GetDefaultGatewayIface() (*net.Interface, error) {
+func GetDefaultGatewayInterface() (*net.Interface, error) {
 	routes, err := netlink.RouteList(nil, syscall.AF_INET)
 	if err != nil {
 		return nil, err
@@ -111,7 +110,7 @@ func GetInterfaceByIP(ip net.IP) (*net.Interface, error) {
 	}
 
 	for _, iface := range ifaces {
-		err := GetIfaceIP4AddrMatch(&iface, ip)
+		err := GetInterfaceIP4AddrMatch(&iface, ip)
 		if err == nil {
 			return &iface, nil
 		}

+ 100 - 21
pkg/ip/iface_windows.go

@@ -1,3 +1,5 @@
+// +build windows
+
 // Copyright 2015 flannel authors
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,52 +17,129 @@
 package ip
 
 import (
-	netsh "github.com/rakelkar/gonetsh/netsh"
+	"errors"
+	"fmt"
+	"github.com/coreos/flannel/pkg/powershell"
 	"net"
 )
 
-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)
+// GetInterfaceIP4Addr returns the IPv4 address for the given network interface
+func GetInterfaceIP4Addr(iface *net.Interface) (net.IP, error) {
+	addrs, err := iface.Addrs()
 	if err != nil {
 		return nil, err
 	}
 
-	ifAddr := net.ParseIP(ifaceDetails.IpAddress)
+	for _, addr := range addrs {
+		var ip net.IP
+		switch v := addr.(type) {
+		case *net.IPAddr:
+			ip = v.IP
+		case *net.IPNet:
+			ip = v.IP
+		}
 
-	return ifAddr, nil
-}
+		if ip != nil && ip.To4() != nil {
+			return ip, nil
+		}
+	}
 
-func GetDefaultGatewayIface() (*net.Interface, error) {
-	netHelper := netsh.New(nil)
+	return nil, errors.New("no IPv4 address found for given interface")
+}
 
-	defaultIfaceName, err := netHelper.GetDefaultGatewayIfaceName()
+// GetDefaultGatewayInterface returns the first network interface found with a default gateway set
+func GetDefaultGatewayInterface() (*net.Interface, error) {
+	index, err := getDefaultGatewayInterfaceIndex()
 	if err != nil {
 		return nil, err
 	}
 
-	iface, err := net.InterfaceByName(defaultIfaceName)
+	return net.InterfaceByIndex(index)
+}
+
+func getDefaultGatewayInterfaceIndex() (int, error) {
+	powerShellJsonData := struct {
+		IfIndex int `json:"ifIndex"`
+	}{-1}
+
+	err := powershell.RunCommandWithJsonResult("Get-NetRoute | Where { $_.DestinationPrefix -eq '0.0.0.0/0' } | Select-Object -Property ifIndex", &powerShellJsonData)
+	if err != nil {
+		return -1, err
+	}
+
+	if powerShellJsonData.IfIndex < 0 {
+		return -1, errors.New("unable to find default gateway interface index")
+	}
+
+	return powerShellJsonData.IfIndex, nil
+}
+
+// GetInterfaceByIP tries to get the network interface with the given ip address
+func GetInterfaceByIP(search net.IP) (*net.Interface, error) {
+	ifaces, err := net.Interfaces()
 	if err != nil {
 		return nil, err
 	}
 
-	return iface, nil
+	for _, i := range ifaces {
+		addrs, err := i.Addrs()
+		if err != nil {
+			return nil, err
+		}
+
+		for _, addr := range addrs {
+			var ip net.IP
+			switch v := addr.(type) {
+			case *net.IPNet:
+				ip = v.IP
+			case *net.IPAddr:
+				ip = v.IP
+			}
+
+			if ip != nil && ip.Equal(search) {
+				return &i, nil
+			}
+		}
+	}
+
+	return nil, errors.New("no interface with given IP found")
+}
+
+// EnableForwardingForInterface enables forwarding for given interface.
+// The process must run with elevated rights. Otherwise the function will fail with an "Access Denied" error.
+func EnableForwardingForInterface(iface *net.Interface) error {
+	return setForwardingForInterface(iface, true)
+}
+
+// DisableForwardingForInterface disables forwarding for given interface.
+// The process must run with elevated rights. Otherwise the function will fail with an "Access Denied" error.
+func DisableForwardingForInterface(iface *net.Interface) error {
+	return setForwardingForInterface(iface, false)
 }
 
-func GetInterfaceByIP(ip net.IP) (*net.Interface, error) {
-	netHelper := netsh.New(nil)
+func setForwardingForInterface(iface *net.Interface, forwarding bool) error {
+	value := "Enabled"
+	if !forwarding {
+		value = "Disabled"
+	}
 
-	ifaceDetails, err := netHelper.GetInterfaceByIP(ip.String())
+	_, err := powershell.RunCommandf("Set-NetIPInterface -ifIndex %d -AddressFamily IPv4 -Forwarding %s", iface.Index, value)
 	if err != nil {
-		return nil, err
+		return err
 	}
 
-	iface, err := net.InterfaceByName(ifaceDetails.Name)
+	return nil
+}
+
+func IsForwardingEnabledForInterface(iface *net.Interface) (bool, error) {
+	powerShellJsonData := struct {
+		Forwarding int `json:"Forwarding"`
+	}{0}
+
+	err := powershell.RunCommandWithJsonResult(fmt.Sprintf("Get-NetIPInterface -ifIndex %d -AddressFamily IPv4 | Select-Object -Property Forwarding", iface.Index), &powerShellJsonData)
 	if err != nil {
-		return nil, err
+		return false, err
 	}
 
-	return iface, nil
+	return powerShellJsonData.Forwarding == 1, nil
 }

+ 83 - 0
pkg/ip/iface_windows_test.go

@@ -0,0 +1,83 @@
+// +build windows
+
+// Copyright 2017 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 (
+	"testing"
+)
+
+func TestGetInterfaceIP4Addr(t *testing.T) {
+	iface, err := GetDefaultGatewayInterface()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, err = GetInterfaceIP4Addr(iface)
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestGetDefaultGatewayInterface(t *testing.T) {
+	_, err := GetDefaultGatewayInterface()
+	if err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestGetInterfaceByIP(t *testing.T) {
+	defaultIface, err := GetDefaultGatewayInterface() // use default gateway interface for test
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	defaultIpv4Addr, err := GetInterfaceIP4Addr(defaultIface)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	iface, err := GetInterfaceByIP(defaultIpv4Addr)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if iface.Index != defaultIface.Index {
+		t.Fatalf("iface.Index(%d) != defaultIface.Index(%d)", iface.Index, defaultIface.Index)
+	}
+}
+
+func TestEnableForwardingForInterface(t *testing.T) {
+	iface, err := GetDefaultGatewayInterface() // use default gateway interface for test
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	err = EnableForwardingForInterface(iface)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() { _ = DisableForwardingForInterface(iface) }() // try to reset forwarding
+
+	enabled, err := IsForwardingEnabledForInterface(iface)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !enabled {
+		t.Fatal("enabled == false")
+	}
+}

+ 7 - 1
pkg/ip/ipnet.go

@@ -31,7 +31,13 @@ func FromBytes(ip []byte) IP4 {
 }
 
 func FromIP(ip net.IP) IP4 {
-	return FromBytes(ip.To4())
+	ipv4 := ip.To4()
+
+	if ipv4 == nil {
+		panic("Address is not an IPv4 address")
+	}
+
+	return FromBytes(ipv4)
 }
 
 func ParseIP4(s string) (IP4, error) {

+ 77 - 0
pkg/powershell/powershell.go

@@ -0,0 +1,77 @@
+// +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 powershell
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os/exec"
+	"strings"
+)
+
+//commandWrapper ensures that exceptions are written to stdout and the powershell process exit code is -1
+const commandWrapper = `$ErrorActionPreference="Stop";try { %s } catch { Write-Host $_; os.Exit(-1) }`
+
+// RunCommand executes a given powershell command.
+//
+// When the command throws a powershell exception, RunCommand will return the exception message as error.
+func RunCommand(command string) ([]byte, error) {
+	cmd := exec.Command("powershell.exe", "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", fmt.Sprintf(commandWrapper, command))
+
+	stdout, err := cmd.Output()
+	if err != nil {
+		if cmd.ProcessState.ExitCode() != 0 {
+			message := strings.TrimSpace(string(stdout))
+			return []byte{}, errors.New(message)
+		}
+
+		return []byte{}, err
+	}
+
+	return stdout, nil
+}
+
+// RunCommandf executes a given powershell command. Command argument formats according to a format specifier (See fmt.Sprintf).
+//
+// When the command throws a powershell exception, RunCommandf will return the exception message as error.
+func RunCommandf(command string, a ...interface{}) ([]byte, error) {
+	return RunCommand(fmt.Sprintf(command, a...))
+}
+
+// RunCommandWithJsonResult executes a given powershell command.
+// The command will be wrapped with ConvertTo-Json.
+//
+// You can Wrap your command with @(<cmd>) to ensure that the returned json is an array
+//
+// When the command throws a powershell exception, RunCommandf will return the exception message as error.
+func RunCommandWithJsonResult(command string, v interface{}) error {
+	wrappedCommand := fmt.Sprintf(commandWrapper, "ConvertTo-Json (%s)")
+	wrappedCommand = fmt.Sprintf(wrappedCommand, command)
+
+	stdout, err := RunCommandf(wrappedCommand)
+	if err != nil {
+		return err
+	}
+
+	err = json.Unmarshal(stdout, v)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 46 - 0
pkg/routing/router.go

@@ -0,0 +1,46 @@
+// 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 routing
+
+import (
+	"bytes"
+	"net"
+)
+
+// Router manages network routes
+type Router interface {
+	// GetAllRoutes returns all existing routes
+	GetAllRoutes() ([]Route, error)
+
+	// GetRoutesFromInterfaceToSubnet returns all routes from the given Interface to the given subnet
+	GetRoutesFromInterfaceToSubnet(interfaceIndex int, destinationSubnet *net.IPNet) ([]Route, error)
+
+	// CreateRoute creates a new route
+	CreateRoute(interfaceIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error
+
+	// DeleteRoute removes an existing route
+	DeleteRoute(interfaceIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error
+}
+
+// Route present a specific route
+type Route struct {
+	InterfaceIndex    int
+	DestinationSubnet *net.IPNet
+	GatewayAddress    net.IP
+}
+
+func (r *Route) Equal(other Route) bool {
+	return r.DestinationSubnet.IP.Equal(other.DestinationSubnet.IP) && bytes.Equal(r.DestinationSubnet.Mask, other.DestinationSubnet.Mask) && r.GatewayAddress.Equal(other.GatewayAddress)
+}

+ 83 - 0
pkg/routing/router_windows.go

@@ -0,0 +1,83 @@
+// +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 routing
+
+import (
+	"fmt"
+	"github.com/coreos/flannel/pkg/powershell"
+	"net"
+)
+
+// Router manages network routes on Windows OS using MSFT_NetRoute
+// See also https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/hh872448(v%3Dvs.85)
+type RouterWindows struct{}
+
+func (r RouterWindows) GetAllRoutes() ([]Route, error) {
+	return parseNetRoutes("@(Get-NetRoute | Select-Object -Property IfIndex,DestinationPrefix,NextHop)")
+}
+
+func (r RouterWindows) GetRoutesFromInterfaceToSubnet(interfaceIndex int, destinationSubnet *net.IPNet) ([]Route, error) {
+	return parseNetRoutes(fmt.Sprintf("@(Get-NetRoute -InterfaceIndex %d -DestinationPrefix %s | Select-Object -Property IfIndex,DestinationPrefix,NextHop)", interfaceIndex, destinationSubnet.String()))
+}
+
+func (r RouterWindows) CreateRoute(interfaceIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error {
+	_, err := powershell.RunCommandf("New-NetRoute -InterfaceIndex %d -DestinationPrefix %s -NextHop  %s", interfaceIndex, destinationSubnet.String(), gatewayAddress.String())
+	return err
+}
+
+func (r RouterWindows) DeleteRoute(interfaceIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error {
+	_, err := powershell.RunCommandf("Remove-NetRoute -InterfaceIndex %d -DestinationPrefix %s -NextHop %s -Verbose -Confirm:$false", interfaceIndex, destinationSubnet.String(), gatewayAddress.String())
+	return err
+}
+
+type winNetRoute struct {
+	IfIndex           int
+	DestinationPrefix string
+	NextHop           string
+}
+
+func parseNetRoutes(cmd string) ([]Route, error) {
+	powerShellJsonData := make([]winNetRoute, 0)
+
+	err := powershell.RunCommandWithJsonResult(cmd, &powerShellJsonData)
+	if err != nil {
+		return nil, err
+	}
+
+	routes := make([]Route, 0)
+	for _, r := range powerShellJsonData {
+		route := Route{
+			InterfaceIndex: r.IfIndex,
+		}
+
+		_, destinationSubnet, err := net.ParseCIDR(r.DestinationPrefix)
+		if err != nil {
+			continue
+		}
+		route.DestinationSubnet = destinationSubnet
+
+		gatewayAddress := net.ParseIP(r.NextHop)
+		if gatewayAddress == nil {
+			continue
+		}
+		route.GatewayAddress = gatewayAddress
+
+		routes = append(routes, route)
+	}
+
+	return routes, nil
+}

+ 95 - 0
pkg/routing/router_windows_test.go

@@ -0,0 +1,95 @@
+// +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 routing
+
+import (
+	"net"
+	"testing"
+
+	"github.com/coreos/flannel/pkg/ip"
+)
+
+func TestGetAllRoutes(t *testing.T) {
+	router := RouterWindows{}
+	routes, err := router.GetAllRoutes()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(routes) == 0 {
+		t.Fatal("len(routes) == 0")
+	}
+}
+
+func TestCreateAndRemoveRoute(t *testing.T) {
+	defaultInterface, err := ip.GetDefaultGatewayInterface()
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	router := RouterWindows{}
+	allRoutes, err := router.GetAllRoutes()
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(allRoutes) == 0 {
+		t.Fatal("len(routes) == 0")
+	}
+
+	destinationSubnet := allRoutes[0].DestinationSubnet
+	gatewayAddress := net.ParseIP("192.168.199.123")
+
+	routes, err := router.GetRoutesFromInterfaceToSubnet(defaultInterface.Index, destinationSubnet)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	firstRouteLen := len(routes)
+	if len(routes) == 0 {
+		t.Fatal("len(routes) == 0")
+	}
+
+	err = router.CreateRoute(defaultInterface.Index, destinationSubnet, gatewayAddress)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	routes, err = router.GetRoutesFromInterfaceToSubnet(defaultInterface.Index, destinationSubnet)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	expectedLen := firstRouteLen + 1
+	givenLen := len(routes)
+	if givenLen != expectedLen {
+		t.Fatalf("givenLen:%d != expectedLen:%d", givenLen, expectedLen)
+	}
+
+	err = router.DeleteRoute(defaultInterface.Index, destinationSubnet, gatewayAddress)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	routes, err = router.GetRoutesFromInterfaceToSubnet(defaultInterface.Index, destinationSubnet)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if len(routes) != firstRouteLen {
+		t.Fatal("len(routes) != startRouteLen")
+	}
+}

+ 0 - 21
vendor/github.com/bhendo/go-powershell/LICENSE

@@ -1,21 +0,0 @@
-Copyright (c) 2017, Gorillalabs
-
-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.
-
-http://www.opensource.org/licenses/MIT

+ 0 - 38
vendor/github.com/bhendo/go-powershell/backend/local.go

@@ -1,38 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package backend
-
-import (
-	"io"
-	"os/exec"
-
-	"github.com/juju/errors"
-)
-
-type Local struct{}
-
-func (b *Local) StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error) {
-	command := exec.Command(cmd, args...)
-
-	stdin, err := command.StdinPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stdin stream")
-	}
-
-	stdout, err := command.StdoutPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stdout stream")
-	}
-
-	stderr, err := command.StderrPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the PowerShell's stderr stream")
-	}
-
-	err = command.Start()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not spawn PowerShell process")
-	}
-
-	return command, stdin, stdout, stderr, nil
-}

+ 0 - 69
vendor/github.com/bhendo/go-powershell/backend/ssh.go

@@ -1,69 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package backend
-
-import (
-	"fmt"
-	"io"
-	"regexp"
-	"strings"
-
-	"github.com/juju/errors"
-)
-
-// sshSession exists so we don't create a hard dependency on crypto/ssh.
-type sshSession interface {
-	Waiter
-
-	StdinPipe() (io.WriteCloser, error)
-	StdoutPipe() (io.Reader, error)
-	StderrPipe() (io.Reader, error)
-	Start(string) error
-}
-
-type SSH struct {
-	Session sshSession
-}
-
-func (b *SSH) StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error) {
-	stdin, err := b.Session.StdinPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stdin stream")
-	}
-
-	stdout, err := b.Session.StdoutPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stdout stream")
-	}
-
-	stderr, err := b.Session.StderrPipe()
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not get hold of the SSH session's stderr stream")
-	}
-
-	err = b.Session.Start(b.createCmd(cmd, args))
-	if err != nil {
-		return nil, nil, nil, nil, errors.Annotate(err, "Could not spawn process via SSH")
-	}
-
-	return b.Session, stdin, stdout, stderr, nil
-}
-
-func (b *SSH) createCmd(cmd string, args []string) string {
-	parts := []string{cmd}
-	simple := regexp.MustCompile(`^[a-z0-9_/.~+-]+$`)
-
-	for _, arg := range args {
-		if !simple.MatchString(arg) {
-			arg = b.quote(arg)
-		}
-
-		parts = append(parts, arg)
-	}
-
-	return strings.Join(parts, " ")
-}
-
-func (b *SSH) quote(s string) string {
-	return fmt.Sprintf(`"%s"`, s)
-}

+ 0 - 13
vendor/github.com/bhendo/go-powershell/backend/types.go

@@ -1,13 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package backend
-
-import "io"
-
-type Waiter interface {
-	Wait() error
-}
-
-type Starter interface {
-	StartProcess(cmd string, args ...string) (Waiter, io.Writer, io.Reader, io.Reader, error)
-}

+ 0 - 120
vendor/github.com/bhendo/go-powershell/shell.go

@@ -1,120 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package powershell
-
-import (
-	"fmt"
-	"io"
-	"strings"
-	"sync"
-
-	"github.com/bhendo/go-powershell/backend"
-	"github.com/bhendo/go-powershell/utils"
-	"github.com/juju/errors"
-)
-
-const newline = "\r\n"
-
-type Shell interface {
-	Execute(cmd string) (string, string, error)
-	Exit()
-}
-
-type shell struct {
-	handle backend.Waiter
-	stdin  io.Writer
-	stdout io.Reader
-	stderr io.Reader
-}
-
-func New(backend backend.Starter) (Shell, error) {
-	handle, stdin, stdout, stderr, err := backend.StartProcess("powershell.exe", "-NoExit", "-Command", "-")
-	if err != nil {
-		return nil, err
-	}
-
-	return &shell{handle, stdin, stdout, stderr}, nil
-}
-
-func (s *shell) Execute(cmd string) (string, string, error) {
-	if s.handle == nil {
-		return "", "", errors.Annotate(errors.New(cmd), "Cannot execute commands on closed shells.")
-	}
-
-	outBoundary := createBoundary()
-	errBoundary := createBoundary()
-
-	// wrap the command in special markers so we know when to stop reading from the pipes
-	full := fmt.Sprintf("%s; echo '%s'; [Console]::Error.WriteLine('%s')%s", cmd, outBoundary, errBoundary, newline)
-
-	_, err := s.stdin.Write([]byte(full))
-	if err != nil {
-		return "", "", errors.Annotate(errors.Annotate(err, cmd), "Could not send PowerShell command")
-	}
-
-	// read stdout and stderr
-	sout := ""
-	serr := ""
-
-	waiter := &sync.WaitGroup{}
-	waiter.Add(2)
-
-	go streamReader(s.stdout, outBoundary, &sout, waiter)
-	go streamReader(s.stderr, errBoundary, &serr, waiter)
-
-	waiter.Wait()
-
-	if len(serr) > 0 {
-		return sout, serr, errors.Annotate(errors.New(cmd), serr)
-	}
-
-	return sout, serr, nil
-}
-
-func (s *shell) Exit() {
-	s.stdin.Write([]byte("exit" + newline))
-
-	// if it's possible to close stdin, do so (some backends, like the local one,
-	// do support it)
-	closer, ok := s.stdin.(io.Closer)
-	if ok {
-		closer.Close()
-	}
-
-	s.handle.Wait()
-
-	s.handle = nil
-	s.stdin = nil
-	s.stdout = nil
-	s.stderr = nil
-}
-
-func streamReader(stream io.Reader, boundary string, buffer *string, signal *sync.WaitGroup) error {
-	// read all output until we have found our boundary token
-	output := ""
-	bufsize := 64
-	marker := boundary + newline
-
-	for {
-		buf := make([]byte, bufsize)
-		read, err := stream.Read(buf)
-		if err != nil {
-			return err
-		}
-
-		output = output + string(buf[:read])
-
-		if strings.HasSuffix(output, marker) {
-			break
-		}
-	}
-
-	*buffer = strings.TrimSuffix(output, marker)
-	signal.Done()
-
-	return nil
-}
-
-func createBoundary() string {
-	return "$gorilla" + utils.CreateRandomString(12) + "$"
-}

+ 0 - 9
vendor/github.com/bhendo/go-powershell/utils/quote.go

@@ -1,9 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package utils
-
-import "strings"
-
-func QuoteArg(s string) string {
-	return "'" + strings.Replace(s, "'", "\"", -1) + "'"
-}

+ 0 - 20
vendor/github.com/bhendo/go-powershell/utils/rand.go

@@ -1,20 +0,0 @@
-// Copyright (c) 2017 Gorillalabs. All rights reserved.
-
-package utils
-
-import (
-	"crypto/rand"
-	"encoding/hex"
-)
-
-func CreateRandomString(bytes int) string {
-	c := bytes
-	b := make([]byte, c)
-
-	_, err := rand.Read(b)
-	if err != nil {
-		panic(err)
-	}
-
-	return hex.EncodeToString(b)
-}

+ 0 - 201
vendor/github.com/rakelkar/gonetsh/LICENSE

@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright 2019 Rakesh Kelkar
-
-   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.

+ 0 - 162
vendor/github.com/rakelkar/gonetsh/netroute/netroute.go

@@ -1,162 +0,0 @@
-package netroute
-
-import (
-	"regexp"
-	"net"
-	"strconv"
-	"strings"
-	"bufio"
-	"bytes"
-	ps "github.com/bhendo/go-powershell"
-	psbe "github.com/bhendo/go-powershell/backend"
-
-	"fmt"
-	"math/big"
-)
-
-// Interface is an injectable interface for running MSFT_NetRoute commands. Implementations must be goroutine-safe.
-type Interface interface {
-	// Get all net routes on the host
-	GetNetRoutesAll() ([]Route, error)
-
-	// Get net routes by link and destination subnet
-	GetNetRoutes(linkIndex int, destinationSubnet *net.IPNet) ([]Route, error)
-
-	// Create a new route
-	NewNetRoute(linkIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error
-
-	// Remove an existing route
-	RemoveNetRoute(linkIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error
-
-	// exit the shell
-	Exit()
-}
-
-type Route struct {
-	LinkIndex         int
-	DestinationSubnet *net.IPNet
-	GatewayAddress    net.IP
-	RouteMetric       int
-	IfMetric          int
-}
-
-type shell struct {
-	shellInstance ps.Shell
-}
-
-func New() Interface {
-
-	s, _ := ps.New(&psbe.Local{})
-
-	runner := &shell{
-		shellInstance: s,
-	}
-
-	return runner
-}
-
-func (shell *shell) Exit() {
-	shell.shellInstance.Exit()
-	shell.shellInstance = nil
-}
-
-func (shell *shell) GetNetRoutesAll() ([]Route, error) {
-	getRouteCmdLine := "get-netroute -erroraction Ignore"
-	stdout, err := shell.runScript(getRouteCmdLine)
-	if err != nil {
-		return nil, err
-	}
-	return parseRoutesList(stdout), nil
-}
-func (shell *shell) GetNetRoutes(linkIndex int, destinationSubnet *net.IPNet) ([]Route, error) {
-	getRouteCmdLine := fmt.Sprintf("get-netroute -InterfaceIndex %v -DestinationPrefix %v -erroraction Ignore", linkIndex, destinationSubnet.String())
-	stdout, err := shell.runScript(getRouteCmdLine)
-	if err != nil {
-		return nil, err
-	}
-	return parseRoutesList(stdout), nil
-}
-
-func (shell *shell) RemoveNetRoute(linkIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error {
-	removeRouteCmdLine := fmt.Sprintf("remove-netroute -InterfaceIndex %v -DestinationPrefix %v -NextHop  %v -Verbose -Confirm:$false", linkIndex, destinationSubnet.String(), gatewayAddress.String())
-	_, err := shell.runScript(removeRouteCmdLine)
-
-	return err
-}
-
-func (shell *shell) NewNetRoute(linkIndex int, destinationSubnet *net.IPNet, gatewayAddress net.IP) error {
-	newRouteCmdLine := fmt.Sprintf("new-netroute -InterfaceIndex %v -DestinationPrefix %v -NextHop  %v -Verbose", linkIndex, destinationSubnet.String(), gatewayAddress.String())
-	_, err := shell.runScript(newRouteCmdLine)
-
-	return err
-}
-
-func parseRoutesList(stdout string) []Route {
-	internalWhitespaceRegEx := regexp.MustCompile(`[\s\p{Zs}]{2,}`)
-	scanner := bufio.NewScanner(strings.NewReader(stdout))
-	var routes []Route
-	for scanner.Scan() {
-		line := internalWhitespaceRegEx.ReplaceAllString(scanner.Text(), "|")
-		if strings.HasPrefix(line, "ifIndex") || strings.HasPrefix(line, "----") {
-			continue
-		}
-
-		parts := strings.Split(line, "|")
-		if len(parts) != 5 {
-			continue
-		}
-
-		linkIndex, err := strconv.Atoi(parts[0])
-		if err != nil {
-			continue
-		}
-
-		gatewayAddress := net.ParseIP(parts[2])
-		if gatewayAddress == nil {
-			continue
-		}
-
-		_, destinationSubnet, err := net.ParseCIDR(parts[1])
-		if err != nil {
-			continue
-		}
-		route := Route{
-			DestinationSubnet: destinationSubnet,
-			GatewayAddress:    gatewayAddress,
-			LinkIndex:         linkIndex,
-		}
-
-		routes = append(routes, route)
-	}
-
-	return routes
-}
-
-func (r *Route) Equal(route Route) bool {
-	if r.DestinationSubnet.IP.Equal(route.DestinationSubnet.IP) && r.GatewayAddress.Equal(route.GatewayAddress) && bytes.Equal(r.DestinationSubnet.Mask, route.DestinationSubnet.Mask) {
-		return true
-	}
-
-	return false
-}
-
-func (shell *shell) runScript(cmdLine string) (string, error) {
-
-	stdout, _, err := shell.shellInstance.Execute(cmdLine)
-	if err != nil {
-		return "", err
-	}
-
-	return stdout, nil
-}
-
-func IpToInt(ip net.IP) *big.Int {
-	if v := ip.To4(); v != nil {
-		return big.NewInt(0).SetBytes(v)
-	}
-	return big.NewInt(0).SetBytes(ip.To16())
-}
-
-func IntToIP(i *big.Int) net.IP {
-	return net.IP(i.Bytes())
-}

+ 0 - 335
vendor/github.com/rakelkar/gonetsh/netsh/netsh.go

@@ -1,335 +0,0 @@
-package netsh
-
-import (
-	"fmt"
-	"regexp"
-	"strconv"
-	"strings"
-	"sync"
-
-	"errors"
-
-	utilexec "k8s.io/utils/exec"
-)
-
-// Interface is an injectable interface for running netsh commands.  Implementations must be goroutine-safe.
-type Interface interface {
-	// EnsurePortProxyRule checks if the specified redirect exists, if not creates it
-	EnsurePortProxyRule(args []string) (bool, error)
-	// DeletePortProxyRule deletes the specified portproxy rule.  If the rule did not exist, return error.
-	DeletePortProxyRule(args []string) error
-	// DeleteIPAddress checks if the specified IP address is present and, if so, deletes it.
-	DeleteIPAddress(args []string) error
-	// Restore runs `netsh exec` to restore portproxy or addresses using a file.
-	// TODO Check if this is required, most likely not
-	Restore(args []string) error
-	// Get the interface name that has the default gateway
-	GetDefaultGatewayIfaceName() (string, error)
-	// Get a list of interfaces and addresses
-	GetInterfaces() ([]Ipv4Interface, error)
-	// Gets an interface by name
-	GetInterfaceByName(name string) (Ipv4Interface, error)
-	// Gets an interface by ip address in the format a.b.c.d
-	GetInterfaceByIP(ipAddr string) (Ipv4Interface, error)
-	// Enable forwarding on the interface (name or index)
-	EnableForwarding(iface string) error
-}
-
-const (
-	cmdNetsh string = "netsh"
-)
-
-// runner implements Interface in terms of exec("netsh").
-type runner struct {
-	mu   sync.Mutex
-	exec utilexec.Interface
-}
-
-// Ipv4Interface models IPv4 interface output from: netsh interface ipv4 show addresses
-type Ipv4Interface struct {
-	Idx                   int
-	Name                  string
-	InterfaceMetric       int
-	DhcpEnabled           bool
-	IpAddress             string
-	SubnetPrefix          int
-	GatewayMetric         int
-	DefaultGatewayAddress string
-}
-
-// New returns a new Interface which will exec netsh.
-func New(exec utilexec.Interface) Interface {
-
-	if exec == nil {
-		exec = utilexec.New()
-	}
-
-	runner := &runner{
-		exec: exec,
-	}
-	return runner
-}
-
-func (runner *runner) GetInterfaces() ([]Ipv4Interface, error) {
-	interfaces, interfaceError := runner.getIpAddressConfigurations()
-
-	if interfaceError != nil {
-		return nil, interfaceError
-	}
-
-	indexMap, indexError := runner.getNetworkInterfaceParameters()
-
-	if indexError != nil {
-		return nil, indexError
-	}
-
-	// zip them up
-	for i := 0; i < len(interfaces); i++ {
-		name := interfaces[i].Name
-
-		if val, ok := indexMap[name]; ok {
-			interfaces[i].Idx = val
-		} else {
-			return nil, fmt.Errorf("no index found for interface \"%v\"", name)
-		}
-	}
-
-	return interfaces, nil
-}
-
-// GetInterfaces uses the show addresses command and returns a formatted structure
-func (runner *runner) getIpAddressConfigurations() ([]Ipv4Interface, error) {
-	args := []string{
-		"interface", "ipv4", "show", "addresses",
-	}
-
-	output, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
-	if err != nil {
-		return nil, err
-	}
-	interfacesString := string(output[:])
-
-	outputLines := strings.Split(interfacesString, "\n")
-	var interfaces []Ipv4Interface
-	var currentInterface Ipv4Interface
-	quotedPattern := regexp.MustCompile("\\\"(.*?)\\\"")
-	cidrPattern := regexp.MustCompile("\\/(.*?)\\ ")
-
-	if err != nil {
-		return nil, err
-	}
-
-	for _, outputLine := range outputLines {
-		if strings.Contains(outputLine, "Configuration for interface") {
-			if currentInterface != (Ipv4Interface{}) {
-				interfaces = append(interfaces, currentInterface)
-			}
-			match := quotedPattern.FindStringSubmatch(outputLine)
-			currentInterface = Ipv4Interface{
-				Name: match[1],
-			}
-		} else {
-			parts := strings.SplitN(outputLine, ":", 2)
-			if len(parts) != 2 {
-				continue
-			}
-			key := strings.TrimSpace(parts[0])
-			value := strings.TrimSpace(parts[1])
-			if strings.HasPrefix(key, "DHCP enabled") {
-				if value == "Yes" {
-					currentInterface.DhcpEnabled = true
-				}
-			} else if strings.HasPrefix(key, "InterfaceMetric") {
-				if val, err := strconv.Atoi(value); err == nil {
-					currentInterface.InterfaceMetric = val
-				}
-			} else if strings.HasPrefix(key, "Gateway Metric") {
-				if val, err := strconv.Atoi(value); err == nil {
-					currentInterface.GatewayMetric = val
-				}
-			} else if strings.HasPrefix(key, "Subnet Prefix") {
-				match := cidrPattern.FindStringSubmatch(value)
-				if val, err := strconv.Atoi(match[1]); err == nil {
-					currentInterface.SubnetPrefix = val
-				}
-			} else if strings.HasPrefix(key, "IP Address") {
-				currentInterface.IpAddress = value
-			} else if strings.HasPrefix(key, "Default Gateway") {
-				currentInterface.DefaultGatewayAddress = value
-			}
-		}
-	}
-
-	// add the last one
-	if currentInterface != (Ipv4Interface{}) {
-		interfaces = append(interfaces, currentInterface)
-	}
-
-	if len(interfaces) == 0 {
-		return nil, fmt.Errorf("no interfaces found in netsh output: %v", interfacesString)
-	}
-
-	return interfaces, nil
-}
-
-func (runner *runner) getNetworkInterfaceParameters() (map[string]int, error) {
-	args := []string{
-		"interface", "ipv4", "show", "interfaces",
-	}
-
-	output, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
-
-	if err != nil {
-		return nil, err
-	}
-
-	// Split output by line
-	outputString := string(output[:])
-	outputString = strings.TrimSpace(outputString)
-	var outputLines = strings.Split(outputString, "\n")
-
-	if len(outputLines) < 3 {
-		return nil, errors.New("unexpected netsh output:\n" + outputString)
-	}
-
-	// Remove first two lines of header text
-	outputLines = outputLines[2:]
-
-	indexMap := make(map[string]int)
-
-	reg := regexp.MustCompile("\\s{2,}")
-
-	for _, line := range outputLines {
-
-		line = strings.TrimSpace(line)
-
-		// Split the line by two or more whitespace characters, returning all substrings (n < 0)
-		splitLine := reg.Split(line, -1)
-
-		name := splitLine[4]
-		if idx, err := strconv.Atoi(splitLine[0]); err == nil {
-			indexMap[name] = idx
-		}
-
-	}
-
-	return indexMap, nil
-}
-
-// Enable forwarding on the interface (name or index)
-func (runner *runner) EnableForwarding(iface string) error {
-	args := []string{
-		"int", "ipv4", "set", "int", strconv.Quote(iface), "for=en",
-	}
-	cmd := strings.Join(args, " ")
-	if stdout, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput(); err != nil {
-		return fmt.Errorf("failed to enable forwarding on [%v], error: %v. cmd: %v. stdout: %v", iface, err.Error(), cmd, string(stdout))
-	}
-
-	return nil
-}
-
-// EnsurePortProxyRule checks if the specified redirect exists, if not creates it.
-func (runner *runner) EnsurePortProxyRule(args []string) (bool, error) {
-	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
-
-	if err == nil {
-		return true, nil
-	}
-	if ee, ok := err.(utilexec.ExitError); ok {
-		// netsh uses exit(0) to indicate a success of the operation,
-		// as compared to a malformed commandline, for example.
-		if ee.Exited() && ee.ExitStatus() != 0 {
-			return false, nil
-		}
-	}
-	return false, fmt.Errorf("error checking portproxy rule: %v: %s", err, out)
-
-}
-
-// DeletePortProxyRule deletes the specified portproxy rule.  If the rule did not exist, return error.
-func (runner *runner) DeletePortProxyRule(args []string) error {
-	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
-
-	if err == nil {
-		return nil
-	}
-	if ee, ok := err.(utilexec.ExitError); ok {
-		// netsh uses exit(0) to indicate a success of the operation,
-		// as compared to a malformed commandline, for example.
-		if ee.Exited() && ee.ExitStatus() == 0 {
-			return nil
-		}
-	}
-	return fmt.Errorf("error deleting portproxy rule: %v: %s", err, out)
-}
-
-// DeleteIPAddress checks if the specified IP address is present and, if so, deletes it.
-func (runner *runner) DeleteIPAddress(args []string) error {
-	out, err := runner.exec.Command(cmdNetsh, args...).CombinedOutput()
-
-	if err == nil {
-		return nil
-	}
-	if ee, ok := err.(utilexec.ExitError); ok {
-		// netsh uses exit(0) to indicate a success of the operation,
-		// as compared to a malformed commandline, for example.
-		if ee.Exited() && ee.ExitStatus() == 0 {
-			return nil
-		}
-	}
-	return fmt.Errorf("error deleting ipv4 address: %v: %s", err, out)
-}
-
-func (runner *runner) GetDefaultGatewayIfaceName() (string, error) {
-	interfaces, err := runner.GetInterfaces()
-	if err != nil {
-		return "", err
-	}
-
-	for _, iface := range interfaces {
-		if iface.DefaultGatewayAddress != "" {
-			return iface.Name, nil
-		}
-	}
-
-	// return "not found"
-	return "", fmt.Errorf("Default interface not found")
-}
-
-func (runner *runner) GetInterfaceByName(name string) (Ipv4Interface, error) {
-	interfaces, err := runner.GetInterfaces()
-	if err != nil {
-		return Ipv4Interface{}, err
-	}
-
-	for _, iface := range interfaces {
-		if iface.Name == name {
-			return iface, nil
-		}
-	}
-
-	// return "not found"
-	return Ipv4Interface{}, fmt.Errorf("Interface not found: %v", name)
-}
-
-func (runner *runner) GetInterfaceByIP(ipAddr string) (Ipv4Interface, error) {
-	interfaces, err := runner.GetInterfaces()
-	if err != nil {
-		return Ipv4Interface{}, err
-	}
-
-	for _, iface := range interfaces {
-		if iface.IpAddress == ipAddr {
-			return iface, nil
-		}
-	}
-
-	// return "not found"
-	return Ipv4Interface{}, fmt.Errorf("Interface not found: %v", ipAddr)
-}
-
-// Restore is part of Interface.
-func (runner *runner) Restore(args []string) error {
-	return nil
-}