From d31b0dc85a5a15bda5e606acbbbb9f7089441a87 Mon Sep 17 00:00:00 2001 From: matthiasng Date: Fri, 20 Dec 2019 22:26:45 +0100 Subject: [PATCH] windows: replace old netsh (rakelkar/gonetsh) with powershell commands --- backend/hostgw/hostgw_windows.go | 37 +- backend/route_network_windows.go | 34 +- backend/vxlan/device_windows.go | 11 +- main.go | 8 +- pkg/ip/iface.go | 9 +- pkg/ip/iface_windows.go | 121 +++++-- pkg/ip/iface_windows_test.go | 83 +++++ pkg/ip/ipnet.go | 8 +- pkg/powershell/powershell.go | 77 ++++ pkg/routing/router.go | 46 +++ pkg/routing/router_windows.go | 83 +++++ pkg/routing/router_windows_test.go | 95 +++++ .../github.com/bhendo/go-powershell/LICENSE | 21 -- .../bhendo/go-powershell/backend/local.go | 38 -- .../bhendo/go-powershell/backend/ssh.go | 69 ---- .../bhendo/go-powershell/backend/types.go | 13 - .../github.com/bhendo/go-powershell/shell.go | 120 ------- .../bhendo/go-powershell/utils/quote.go | 9 - .../bhendo/go-powershell/utils/rand.go | 20 -- vendor/github.com/rakelkar/gonetsh/LICENSE | 201 ----------- .../rakelkar/gonetsh/netroute/netroute.go | 162 --------- .../rakelkar/gonetsh/netsh/netsh.go | 335 ------------------ 22 files changed, 544 insertions(+), 1056 deletions(-) create mode 100644 pkg/ip/iface_windows_test.go create mode 100644 pkg/powershell/powershell.go create mode 100644 pkg/routing/router.go create mode 100644 pkg/routing/router_windows.go create mode 100644 pkg/routing/router_windows_test.go delete mode 100644 vendor/github.com/bhendo/go-powershell/LICENSE delete mode 100644 vendor/github.com/bhendo/go-powershell/backend/local.go delete mode 100644 vendor/github.com/bhendo/go-powershell/backend/ssh.go delete mode 100644 vendor/github.com/bhendo/go-powershell/backend/types.go delete mode 100644 vendor/github.com/bhendo/go-powershell/shell.go delete mode 100644 vendor/github.com/bhendo/go-powershell/utils/quote.go delete mode 100644 vendor/github.com/bhendo/go-powershell/utils/rand.go delete mode 100644 vendor/github.com/rakelkar/gonetsh/LICENSE delete mode 100644 vendor/github.com/rakelkar/gonetsh/netroute/netroute.go delete mode 100644 vendor/github.com/rakelkar/gonetsh/netsh/netsh.go diff --git a/backend/hostgw/hostgw_windows.go b/backend/hostgw/hostgw_windows.go index 9741f189f7..d65a4a822b 100644 --- a/backend/hostgw/hostgw_windows.go +++ b/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 diff --git a/backend/route_network_windows.go b/backend/route_network_windows.go index 653fb7bf5a..e767a812fa 100644 --- a/backend/route_network_windows.go +++ b/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) } } } diff --git a/backend/vxlan/device_windows.go b/backend/vxlan/device_windows.go index f350521442..aa0d7a5897 100644 --- a/backend/vxlan/device_windows.go +++ b/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 { diff --git a/main.go b/main.go index 363457c59f..840d3ea4ba 100644 --- a/main.go +++ b/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) } diff --git a/pkg/ip/iface.go b/pkg/ip/iface.go index c4ade549e3..0f02cf55f3 100644 --- a/pkg/ip/iface.go +++ b/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 } diff --git a/pkg/ip/iface_windows.go b/pkg/ip/iface_windows.go index 9eb0038971..bb8fcd10cf 100644 --- a/pkg/ip/iface_windows.go +++ b/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 } diff --git a/pkg/ip/iface_windows_test.go b/pkg/ip/iface_windows_test.go new file mode 100644 index 0000000000..8228956edd --- /dev/null +++ b/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") + } +} diff --git a/pkg/ip/ipnet.go b/pkg/ip/ipnet.go index 36c792f223..9fceb5c643 100644 --- a/pkg/ip/ipnet.go +++ b/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) { diff --git a/pkg/powershell/powershell.go b/pkg/powershell/powershell.go new file mode 100644 index 0000000000..8ee522f8e0 --- /dev/null +++ b/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 @() 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 +} diff --git a/pkg/routing/router.go b/pkg/routing/router.go new file mode 100644 index 0000000000..a0ea5af213 --- /dev/null +++ b/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) +} diff --git a/pkg/routing/router_windows.go b/pkg/routing/router_windows.go new file mode 100644 index 0000000000..a629e9958a --- /dev/null +++ b/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 +} diff --git a/pkg/routing/router_windows_test.go b/pkg/routing/router_windows_test.go new file mode 100644 index 0000000000..51220ae9bf --- /dev/null +++ b/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") + } +} diff --git a/vendor/github.com/bhendo/go-powershell/LICENSE b/vendor/github.com/bhendo/go-powershell/LICENSE deleted file mode 100644 index 0e06ff19df..0000000000 --- a/vendor/github.com/bhendo/go-powershell/LICENSE +++ /dev/null @@ -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 diff --git a/vendor/github.com/bhendo/go-powershell/backend/local.go b/vendor/github.com/bhendo/go-powershell/backend/local.go deleted file mode 100644 index 429b1fe245..0000000000 --- a/vendor/github.com/bhendo/go-powershell/backend/local.go +++ /dev/null @@ -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 -} diff --git a/vendor/github.com/bhendo/go-powershell/backend/ssh.go b/vendor/github.com/bhendo/go-powershell/backend/ssh.go deleted file mode 100644 index 68c918280d..0000000000 --- a/vendor/github.com/bhendo/go-powershell/backend/ssh.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/bhendo/go-powershell/backend/types.go b/vendor/github.com/bhendo/go-powershell/backend/types.go deleted file mode 100644 index 505471c128..0000000000 --- a/vendor/github.com/bhendo/go-powershell/backend/types.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/bhendo/go-powershell/shell.go b/vendor/github.com/bhendo/go-powershell/shell.go deleted file mode 100644 index aca8f7a3df..0000000000 --- a/vendor/github.com/bhendo/go-powershell/shell.go +++ /dev/null @@ -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) + "$" -} diff --git a/vendor/github.com/bhendo/go-powershell/utils/quote.go b/vendor/github.com/bhendo/go-powershell/utils/quote.go deleted file mode 100644 index 84dd1d38c1..0000000000 --- a/vendor/github.com/bhendo/go-powershell/utils/quote.go +++ /dev/null @@ -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) + "'" -} diff --git a/vendor/github.com/bhendo/go-powershell/utils/rand.go b/vendor/github.com/bhendo/go-powershell/utils/rand.go deleted file mode 100644 index 245243e5a3..0000000000 --- a/vendor/github.com/bhendo/go-powershell/utils/rand.go +++ /dev/null @@ -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) -} diff --git a/vendor/github.com/rakelkar/gonetsh/LICENSE b/vendor/github.com/rakelkar/gonetsh/LICENSE deleted file mode 100644 index 8f4d556819..0000000000 --- a/vendor/github.com/rakelkar/gonetsh/LICENSE +++ /dev/null @@ -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. diff --git a/vendor/github.com/rakelkar/gonetsh/netroute/netroute.go b/vendor/github.com/rakelkar/gonetsh/netroute/netroute.go deleted file mode 100644 index 4de0902e3a..0000000000 --- a/vendor/github.com/rakelkar/gonetsh/netroute/netroute.go +++ /dev/null @@ -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()) -} diff --git a/vendor/github.com/rakelkar/gonetsh/netsh/netsh.go b/vendor/github.com/rakelkar/gonetsh/netsh/netsh.go deleted file mode 100644 index 713734f0b5..0000000000 --- a/vendor/github.com/rakelkar/gonetsh/netsh/netsh.go +++ /dev/null @@ -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 -}