diff --git a/netio/mocknetio.go b/netio/mocknetio.go new file mode 100644 index 0000000000..dd3c077692 --- /dev/null +++ b/netio/mocknetio.go @@ -0,0 +1,46 @@ +package netio + +import ( + "errors" + "fmt" + "net" +) + +type MockNetIO struct { + fail bool + failAttempt int + numTimesCalled int +} + +// ErrMockNetIOFail - mock netio error +var ErrMockNetIOFail = errors.New("netio fail") + +func NewMockNetIO(fail bool, failAttempt int) *MockNetIO { + return &MockNetIO{ + fail: fail, + failAttempt: failAttempt, + } +} + +func (netshim *MockNetIO) GetNetworkInterfaceByName(name string) (*net.Interface, error) { + netshim.numTimesCalled++ + + if netshim.fail && netshim.failAttempt == netshim.numTimesCalled { + return nil, fmt.Errorf("%w:%s", ErrMockNetIOFail, name) + } + + hwAddr, _ := net.ParseMAC("ab:cd:ef:12:34:56") + + return &net.Interface{ + //nolint:gomnd // Dummy MTU + MTU: 1000, + Name: name, + HardwareAddr: hwAddr, + //nolint:gomnd // Dummy interface index + Index: 2, + }, nil +} + +func (netshim *MockNetIO) GetNetworkInterfaceAddrs(iface *net.Interface) ([]net.Addr, error) { + return []net.Addr{}, nil +} diff --git a/netio/netio.go b/netio/netio.go new file mode 100644 index 0000000000..0f2bf03274 --- /dev/null +++ b/netio/netio.go @@ -0,0 +1,32 @@ +package netio + +import ( + "net" + + "github.com/pkg/errors" +) + +//nolint:revive // keeping NetIOInterface makes sense +type NetIOInterface interface { + GetNetworkInterfaceByName(name string) (*net.Interface, error) + GetNetworkInterfaceAddrs(iface *net.Interface) ([]net.Addr, error) +} + +// ErrInterfaceNil - errors out when interface is nil +var ErrInterfaceNil = errors.New("Interface is nil") + +type NetIO struct{} + +func (ns *NetIO) GetNetworkInterfaceByName(name string) (*net.Interface, error) { + iface, err := net.InterfaceByName(name) + return iface, errors.Wrap(err, "GetNetworkInterfaceByName failed") +} + +func (ns *NetIO) GetNetworkInterfaceAddrs(iface *net.Interface) ([]net.Addr, error) { + if iface == nil { + return []net.Addr{}, ErrInterfaceNil + } + + addrs, err := iface.Addrs() + return addrs, errors.Wrap(err, "GetNetworkInterfaceAddrs failed") +} diff --git a/netlink/link.go b/netlink/link.go index d7ffada9b7..fa1d8c0248 100644 --- a/netlink/link.go +++ b/netlink/link.go @@ -11,6 +11,7 @@ import ( "net" "github.com/Azure/azure-container-networking/log" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -151,6 +152,29 @@ func (Netlink) AddLink(link Link) error { return s.sendAndWaitForAck(req) } +func (Netlink) SetLinkMTU(name string, mtu int) error { + iface, err := net.InterfaceByName(name) + if err != nil { + log.Printf("[net] Interface not found. returning error") + return errors.Wrap(err, "SetLinkMTU:InterfaceByName failed") + } + + s, err := getSocket() + if err != nil { + return err + } + + req := newRequest(unix.RTM_SETLINK, unix.NLM_F_ACK) + ifInfo := newIfInfoMsg() + ifInfo.Index = int32(iface.Index) + req.addPayload(ifInfo) + + mtuData := newAttributeUint32(unix.IFLA_MTU, uint32(mtu)) + req.addPayload(mtuData) + + return s.sendAndWaitForAck(req) +} + // DeleteLink deletes a network interface. func (Netlink) DeleteLink(name string) error { if name == "" { diff --git a/netlink/mocknetlink.go b/netlink/mocknetlink.go index 17cc6d2c46..49fa8afadc 100644 --- a/netlink/mocknetlink.go +++ b/netlink/mocknetlink.go @@ -6,10 +6,11 @@ import ( "net" ) -var errorMockNetlink = errors.New("Mock Netlink Error") +// ErrorMockNetlink - netlink mock error +var ErrorMockNetlink = errors.New("Mock Netlink Error") func newErrorMockNetlink(errStr string) error { - return fmt.Errorf("%w : %s", errorMockNetlink, errStr) + return fmt.Errorf("%w : %s", ErrorMockNetlink, errStr) } type MockNetlink struct { @@ -17,8 +18,8 @@ type MockNetlink struct { errorString string } -func NewMockNetlink(returnError bool, errorString string) MockNetlink { - return MockNetlink{ +func NewMockNetlink(returnError bool, errorString string) *MockNetlink { + return &MockNetlink{ returnError: returnError, errorString: errorString, } @@ -31,11 +32,15 @@ func (f *MockNetlink) error() error { return nil } -func (f *MockNetlink) AddLink(Link) error { +func (f *MockNetlink) AddLink(l Link) error { return f.error() } -func (f *MockNetlink) DeleteLink(string) error { +func (f *MockNetlink) SetLinkMTU(name string, mtu int) error { + return f.error() +} + +func (f *MockNetlink) DeleteLink(name string) error { return f.error() } diff --git a/netlink/netlink_test.go b/netlink/netlink_test.go index fd9aad9ff6..369154c445 100644 --- a/netlink/netlink_test.go +++ b/netlink/netlink_test.go @@ -9,6 +9,8 @@ package netlink import ( "net" "testing" + + "github.com/stretchr/testify/require" ) const ( @@ -99,6 +101,37 @@ func TestAddDeleteVEth(t *testing.T) { } } +// TestSetMTU tests if MTU can be sent on link +func TestSetMTU(t *testing.T) { + link := VEthLink{ + LinkInfo: LinkInfo{ + Type: LINK_TYPE_VETH, + Name: ifName, + }, + PeerName: ifName2, + } + nl := NewNetlink() + + err := nl.AddLink(&link) + if err != nil { + t.Errorf("AddLink failed: %+v", err) + } + + //nolint:errcheck // not testing deletelink here + defer nl.DeleteLink(ifName) + + if err = nl.SetLinkMTU(ifName, 1028); err != nil { + t.Errorf("SetMTU failed: %+v", err) + } + + iface, err := net.InterfaceByName(ifName) + if err != nil { + t.Errorf("InterfaceByName err:%v", err) + } + + require.Equal(t, 1028, iface.MTU, "Expected mtu:1024 but got %d", iface.MTU) +} + // TestAddDeleteIPVlan tests adding and deleting an IPVLAN interface. func TestAddDeleteIPVlan(t *testing.T) { dummy, err := addDummyInterface(dummyName) diff --git a/netlink/netlink_windows.go b/netlink/netlink_windows.go index 31c0e67c16..4ea66903d2 100644 --- a/netlink/netlink_windows.go +++ b/netlink/netlink_windows.go @@ -26,6 +26,10 @@ func (Netlink) AddLink(link Link) error { return nil } +func (Netlink) SetLinkMTU(name string, mtu int) error { + return nil +} + func (Netlink) DeleteLink(name string) error { return nil } diff --git a/network/netlinkinterface/netlinkinterface.go b/netlink/netlinkinterface.go similarity index 74% rename from network/netlinkinterface/netlinkinterface.go rename to netlink/netlinkinterface.go index 6f9b5d58f1..729f566996 100644 --- a/network/netlinkinterface/netlinkinterface.go +++ b/netlink/netlinkinterface.go @@ -1,19 +1,18 @@ // Copyright 2021 Microsoft. All rights reserved. // MIT License -package netlinkinterface +package netlink import ( "net" - - "github.com/Azure/azure-container-networking/netlink" ) type NetlinkInterface interface { - AddLink(link netlink.Link) error + AddLink(link Link) error DeleteLink(name string) error SetLinkName(name string, newName string) error SetLinkState(name string, up bool) error + SetLinkMTU(name string, mtu int) error SetLinkMaster(name string, master string) error SetLinkNetNs(name string, fd uintptr) error SetLinkAddress(ifName string, hwAddress net.HardwareAddr) error @@ -22,7 +21,7 @@ type NetlinkInterface interface { AddOrRemoveStaticArp(mode int, name string, ipaddr net.IP, mac net.HardwareAddr, isProxy bool) error AddIPAddress(ifName string, ipAddress net.IP, ipNet *net.IPNet) error DeleteIPAddress(ifName string, ipAddress net.IP, ipNet *net.IPNet) error - GetIPRoute(filter *netlink.Route) ([]*netlink.Route, error) - AddIPRoute(route *netlink.Route) error - DeleteIPRoute(route *netlink.Route) error + GetIPRoute(filter *Route) ([]*Route, error) + AddIPRoute(route *Route) error + DeleteIPRoute(route *Route) error } diff --git a/network/bridge_endpointclient_linux.go b/network/bridge_endpointclient_linux.go index d4b13d1f68..a068f91fab 100644 --- a/network/bridge_endpointclient_linux.go +++ b/network/bridge_endpointclient_linux.go @@ -8,7 +8,6 @@ import ( "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" ) const ( @@ -26,7 +25,7 @@ type LinuxBridgeEndpointClient struct { containerMac net.HardwareAddr hostIPAddresses []*net.IPNet mode string - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface } func NewLinuxBridgeEndpointClient( @@ -34,7 +33,7 @@ func NewLinuxBridgeEndpointClient( hostVethName string, containerVethName string, mode string, - nl netlinkinterface.NetlinkInterface, + nl netlink.NetlinkInterface, ) *LinuxBridgeEndpointClient { client := &LinuxBridgeEndpointClient{ diff --git a/network/bridge_networkclient_linux.go b/network/bridge_networkclient_linux.go index aa10ff1ca8..3ffadbf2e2 100644 --- a/network/bridge_networkclient_linux.go +++ b/network/bridge_networkclient_linux.go @@ -9,7 +9,6 @@ import ( "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" ) const ( @@ -26,14 +25,14 @@ type LinuxBridgeClient struct { bridgeName string hostInterfaceName string nwInfo NetworkInfo - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface } func NewLinuxBridgeClient( bridgeName string, hostInterfaceName string, nwInfo NetworkInfo, - nl netlinkinterface.NetlinkInterface, + nl netlink.NetlinkInterface, ) *LinuxBridgeClient { client := &LinuxBridgeClient{ bridgeName: bridgeName, diff --git a/network/endpoint.go b/network/endpoint.go index 28a92d9a3b..740f80e611 100644 --- a/network/endpoint.go +++ b/network/endpoint.go @@ -9,7 +9,7 @@ import ( "strings" "github.com/Azure/azure-container-networking/log" - "github.com/Azure/azure-container-networking/network/netlinkinterface" + "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/policy" ) @@ -98,7 +98,7 @@ type apipaClient interface { } // NewEndpoint creates a new endpoint in the network. -func (nw *network) newEndpoint(cli apipaClient, nl netlinkinterface.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { +func (nw *network) newEndpoint(cli apipaClient, nl netlink.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { var ep *endpoint var err error @@ -122,7 +122,7 @@ func (nw *network) newEndpoint(cli apipaClient, nl netlinkinterface.NetlinkInter } // DeleteEndpoint deletes an existing endpoint from the network. -func (nw *network) deleteEndpoint(cli apipaClient, nl netlinkinterface.NetlinkInterface, endpointID string) error { +func (nw *network) deleteEndpoint(cli apipaClient, nl netlink.NetlinkInterface, endpointID string) error { var err error log.Printf("[net] Deleting endpoint %v from network %v.", endpointID, nw.Id) diff --git a/network/endpoint_linux.go b/network/endpoint_linux.go index 731af5a91b..49f9fd88e4 100644 --- a/network/endpoint_linux.go +++ b/network/endpoint_linux.go @@ -12,7 +12,6 @@ import ( "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/ovsctl" ) @@ -46,7 +45,7 @@ func ConstructEndpointID(containerID string, _ string, ifName string) (string, s } // newEndpointImpl creates a new endpoint in the network. -func (nw *network) newEndpointImpl(_ apipaClient, nl netlinkinterface.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { +func (nw *network) newEndpointImpl(_ apipaClient, nl netlink.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { var containerIf *net.Interface var ns *Namespace var ep *endpoint @@ -219,7 +218,7 @@ func (nw *network) newEndpointImpl(_ apipaClient, nl netlinkinterface.NetlinkInt } // deleteEndpointImpl deletes an existing endpoint from the network. -func (nw *network) deleteEndpointImpl(_ apipaClient, nl netlinkinterface.NetlinkInterface, ep *endpoint) error { +func (nw *network) deleteEndpointImpl(_ apipaClient, nl netlink.NetlinkInterface, ep *endpoint) error { var epClient EndpointClient // Delete the veth pair by deleting one of the peer interfaces. @@ -244,7 +243,7 @@ func (nw *network) deleteEndpointImpl(_ apipaClient, nl netlinkinterface.Netlink func (ep *endpoint) getInfoImpl(epInfo *EndpointInfo) { } -func addRoutes(nl netlinkinterface.NetlinkInterface, interfaceName string, routes []RouteInfo) error { +func addRoutes(nl netlink.NetlinkInterface, interfaceName string, routes []RouteInfo) error { ifIndex := 0 interfaceIf, _ := net.InterfaceByName(interfaceName) @@ -285,7 +284,7 @@ func addRoutes(nl netlinkinterface.NetlinkInterface, interfaceName string, route return nil } -func deleteRoutes(nl netlinkinterface.NetlinkInterface, interfaceName string, routes []RouteInfo) error { +func deleteRoutes(nl netlink.NetlinkInterface, interfaceName string, routes []RouteInfo) error { ifIndex := 0 interfaceIf, _ := net.InterfaceByName(interfaceName) diff --git a/network/endpoint_windows.go b/network/endpoint_windows.go index f004c9afb2..09caddde16 100644 --- a/network/endpoint_windows.go +++ b/network/endpoint_windows.go @@ -9,16 +9,16 @@ import ( "fmt" "net" "strings" - "github.com/Azure/azure-container-networking/network/hnswrapper" + "github.com/Azure/azure-container-networking/log" - "github.com/Azure/azure-container-networking/network/netlinkinterface" + "github.com/Azure/azure-container-networking/netlink" + "github.com/Azure/azure-container-networking/network/hnswrapper" "github.com/Azure/azure-container-networking/network/policy" "github.com/Azure/azure-container-networking/platform" "github.com/Microsoft/hcsshim" "github.com/Microsoft/hcsshim/hcn" ) - // this hnsv2 variable is package level variable in network // we do this to avoid passing around os specific objects in platform agnostic code var hnsv2 hnswrapper.HnsV2WrapperInterface = hnswrapper.Hnsv2wrapper{} @@ -93,7 +93,7 @@ func ConstructEndpointID(containerID string, netNsPath string, ifName string) (s } // newEndpointImpl creates a new endpoint in the network. -func (nw *network) newEndpointImpl(cli apipaClient, _ netlinkinterface.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { +func (nw *network) newEndpointImpl(cli apipaClient, _ netlink.NetlinkInterface, epInfo *EndpointInfo) (*endpoint, error) { if useHnsV2, err := UseHnsV2(epInfo.NetNsPath); useHnsV2 { if err != nil { return nil, err @@ -415,7 +415,7 @@ func (nw *network) newEndpointImplHnsV2(cli apipaClient, epInfo *EndpointInfo) ( } // deleteEndpointImpl deletes an existing endpoint from the network. -func (nw *network) deleteEndpointImpl(cli apipaClient, _ netlinkinterface.NetlinkInterface, ep *endpoint) error { +func (nw *network) deleteEndpointImpl(cli apipaClient, _ netlink.NetlinkInterface, ep *endpoint) error { if useHnsV2, err := UseHnsV2(ep.NetNs); useHnsV2 { if err != nil { return err diff --git a/network/epcommon/endpoint_common.go b/network/epcommon/endpoint_common.go index 341555e2d6..af9cd403d0 100644 --- a/network/epcommon/endpoint_common.go +++ b/network/epcommon/endpoint_common.go @@ -11,7 +11,6 @@ import ( "github.com/Azure/azure-container-networking/iptables" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/platform" ) @@ -45,10 +44,10 @@ func newErrorEPCommon(errStr string) error { } type EPCommon struct { - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface } -func NewEPCommon(nl netlinkinterface.NetlinkInterface) EPCommon { +func NewEPCommon(nl netlink.NetlinkInterface) EPCommon { return EPCommon{ netlink: nl, } diff --git a/network/manager.go b/network/manager.go index bc6089b67a..c7a954365c 100644 --- a/network/manager.go +++ b/network/manager.go @@ -11,7 +11,7 @@ import ( cnms "github.com/Azure/azure-container-networking/cnms/cnmspackage" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" - "github.com/Azure/azure-container-networking/network/netlinkinterface" + "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/platform" "github.com/Azure/azure-container-networking/store" ) @@ -57,7 +57,7 @@ type networkManager struct { TimeStamp time.Time ExternalInterfaces map[string]*externalInterface store store.KeyValueStore - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface sync.Mutex } @@ -85,7 +85,7 @@ type NetworkManager interface { } // Creates a new network manager. -func NewNetworkManager(nl netlinkinterface.NetlinkInterface) (NetworkManager, error) { +func NewNetworkManager(nl netlink.NetlinkInterface) (NetworkManager, error) { nm := &networkManager{ ExternalInterfaces: make(map[string]*externalInterface), netlink: nl, diff --git a/network/network_linux.go b/network/network_linux.go index dcc80af15d..d3fdeb0b2c 100644 --- a/network/network_linux.go +++ b/network/network_linux.go @@ -14,7 +14,6 @@ import ( "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/ovsctl" "github.com/Azure/azure-container-networking/platform" "golang.org/x/sys/unix" @@ -594,7 +593,7 @@ func (nm *networkManager) addBridgeRoutes(bridgeName string, routes []RouteInfo) } // Add ipv6 nat gateway IP on bridge -func addIpv6NatGateway(nl netlinkinterface.NetlinkInterface, nwInfo *NetworkInfo) error { +func addIpv6NatGateway(nl netlink.NetlinkInterface, nwInfo *NetworkInfo) error { log.Printf("[net] Adding ipv6 nat gateway on azure bridge") for _, subnetInfo := range nwInfo.Subnets { if subnetInfo.Family == platform.AfINET6 { @@ -636,7 +635,7 @@ func getNetworkInfoImpl(nwInfo *NetworkInfo, nw *network) { } // AddStaticRoute adds a static route to the interface. -func AddStaticRoute(nl netlinkinterface.NetlinkInterface, ip string, interfaceName string) error { +func AddStaticRoute(nl netlink.NetlinkInterface, ip, interfaceName string) error { log.Printf("[ovs] Adding %v static route", ip) var routes []RouteInfo _, ipNet, _ := net.ParseCIDR(ip) diff --git a/network/ovs_endpointclient_linux.go b/network/ovs_endpointclient_linux.go index f0680039df..4653fc73f0 100644 --- a/network/ovs_endpointclient_linux.go +++ b/network/ovs_endpointclient_linux.go @@ -7,8 +7,8 @@ import ( "net" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/network/ovsinfravnet" "github.com/Azure/azure-container-networking/network/ovssnat" "github.com/Azure/azure-container-networking/ovsctl" @@ -29,7 +29,7 @@ type OVSEndpointClient struct { allowInboundFromHostToNC bool allowInboundFromNCToHost bool enableSnatForDns bool - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface ovsctlClient ovsctl.OvsInterface } @@ -45,7 +45,7 @@ func NewOVSEndpointClient( containerVethName string, vlanid int, localIP string, - nl netlinkinterface.NetlinkInterface, + nl netlink.NetlinkInterface, ovs ovsctl.OvsInterface) *OVSEndpointClient { client := &OVSEndpointClient{ diff --git a/network/ovsinfravnet/infravnet.go b/network/ovsinfravnet/infravnet.go index ba6d1a0086..5a9df203f0 100644 --- a/network/ovsinfravnet/infravnet.go +++ b/network/ovsinfravnet/infravnet.go @@ -9,9 +9,8 @@ import ( "net" "github.com/Azure/azure-container-networking/log" - + "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/ovsctl" ) @@ -29,10 +28,10 @@ type OVSInfraVnetClient struct { hostInfraVethName string ContainerInfraVethName string containerInfraMac string - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface } -func NewInfraVnetClient(hostIfName string, contIfName string, nl netlinkinterface.NetlinkInterface) OVSInfraVnetClient { +func NewInfraVnetClient(hostIfName, contIfName string, nl netlink.NetlinkInterface) OVSInfraVnetClient { infraVnetClient := OVSInfraVnetClient{ hostInfraVethName: hostIfName, ContainerInfraVethName: contIfName, diff --git a/network/ovssnat/ovssnat.go b/network/ovssnat/ovssnat.go index 0fd165294f..e9df1a7f63 100644 --- a/network/ovssnat/ovssnat.go +++ b/network/ovssnat/ovssnat.go @@ -14,7 +14,6 @@ import ( "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/ovsctl" "github.com/Azure/azure-container-networking/platform" ) @@ -44,7 +43,7 @@ type OVSSnatClient struct { localIP string snatBridgeIP string SkipAddressesFromBlock []string - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface ovsctlClient ovsctl.OvsInterface } @@ -54,7 +53,7 @@ func NewSnatClient(hostIfName string, snatBridgeIP string, hostPrimaryMac string, skipAddressesFromBlock []string, - nl netlinkinterface.NetlinkInterface, + nl netlink.NetlinkInterface, ovsctlClient ovsctl.OvsInterface, ) OVSSnatClient { log.Printf("Initialize new snat client") diff --git a/network/transparent_endpoint_linux_test.go b/network/transparent_endpoint_linux_test.go new file mode 100644 index 0000000000..c36548975a --- /dev/null +++ b/network/transparent_endpoint_linux_test.go @@ -0,0 +1,111 @@ +//+build linux + +package network + +import ( + "testing" + + "github.com/Azure/azure-container-networking/netio" + "github.com/Azure/azure-container-networking/netlink" + "github.com/stretchr/testify/require" +) + +func TestAddEndpoints(t *testing.T) { + tests := []struct { + name string + client *TransparentEndpointClient + epInfo *EndpointInfo + wantErr bool + wantErrMsg string + }{ + { + name: "Add endpoints", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(false, ""), + netioshim: netio.NewMockNetIO(false, 0), + }, + epInfo: &EndpointInfo{}, + wantErr: false, + }, + { + name: "Add endpoints netlink fail", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(true, "netlink fail"), + netioshim: netio.NewMockNetIO(false, 0), + }, + epInfo: &EndpointInfo{}, + wantErr: true, + wantErrMsg: "TransparentEndpointClient Error : " + netlink.ErrorMockNetlink.Error() + " : netlink fail", + }, + { + name: "Add endpoints get interface fail for old veth", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(false, ""), + netioshim: netio.NewMockNetIO(true, 1), + }, + epInfo: &EndpointInfo{}, + wantErr: false, + }, + { + name: "Add endpoints get interface fail for primary interface", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(false, ""), + netioshim: netio.NewMockNetIO(true, 2), + }, + epInfo: &EndpointInfo{}, + wantErr: true, + wantErrMsg: "TransparentEndpointClient Error : " + netio.ErrMockNetIOFail.Error() + ":eth0", + }, + { + name: "Add endpoints get interface fail for host veth", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(false, ""), + netioshim: netio.NewMockNetIO(true, 3), + }, + epInfo: &EndpointInfo{}, + wantErr: true, + wantErrMsg: "TransparentEndpointClient Error : " + netio.ErrMockNetIOFail.Error() + ":azvcontainer", + }, + { + name: "get interface fail for container veth", + client: &TransparentEndpointClient{ + hostPrimaryIfName: "eth0", + hostVethName: "azvhost", + containerVethName: "azvcontainer", + netlink: netlink.NewMockNetlink(false, ""), + netioshim: netio.NewMockNetIO(true, 4), + }, + epInfo: &EndpointInfo{}, + wantErr: true, + wantErrMsg: "TransparentEndpointClient Error : " + netio.ErrMockNetIOFail.Error() + ":azvhost", + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + err := tt.client.AddEndpoints(tt.epInfo) + if tt.wantErr { + require.Error(t, err) + require.Contains(t, tt.wantErrMsg, err.Error(), "Expected:%v actual:%v", tt.wantErrMsg, err.Error()) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/network/transparent_endpointclient_linux.go b/network/transparent_endpointclient_linux.go index 7f21d7893e..7f9af6641b 100644 --- a/network/transparent_endpointclient_linux.go +++ b/network/transparent_endpointclient_linux.go @@ -6,9 +6,9 @@ import ( "net" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/netio" "github.com/Azure/azure-container-networking/netlink" "github.com/Azure/azure-container-networking/network/epcommon" - "github.com/Azure/azure-container-networking/network/netlinkinterface" "github.com/Azure/azure-container-networking/platform" ) @@ -33,7 +33,8 @@ type TransparentEndpointClient struct { containerMac net.HardwareAddr hostVethMac net.HardwareAddr mode string - netlink netlinkinterface.NetlinkInterface + netlink netlink.NetlinkInterface + netioshim netio.NetIOInterface } func NewTransparentEndpointClient( @@ -41,7 +42,7 @@ func NewTransparentEndpointClient( hostVethName string, containerVethName string, mode string, - nl netlinkinterface.NetlinkInterface, + nl netlink.NetlinkInterface, ) *TransparentEndpointClient { client := &TransparentEndpointClient{ @@ -52,6 +53,7 @@ func NewTransparentEndpointClient( hostPrimaryMac: extIf.MacAddress, mode: mode, netlink: nl, + netioshim: &netio.NetIO{}, } return client @@ -64,7 +66,7 @@ func setArpProxy(ifName string) error { } func (client *TransparentEndpointClient) AddEndpoints(epInfo *EndpointInfo) error { - if _, err := net.InterfaceByName(client.hostVethName); err == nil { + if _, err := client.netioshim.GetNetworkInterfaceByName(client.hostVethName); err == nil { log.Printf("Deleting old host veth %v", client.hostVethName) if err = client.netlink.DeleteLink(client.hostVethName); err != nil { log.Printf("[net] Failed to delete old hostveth %v: %v.", client.hostVethName, err) @@ -72,25 +74,47 @@ func (client *TransparentEndpointClient) AddEndpoints(epInfo *EndpointInfo) erro } } + primaryIf, err := client.netioshim.GetNetworkInterfaceByName(client.hostPrimaryIfName) + if err != nil { + return newErrorTransparentEndpointClient(err.Error()) + } + epc := epcommon.NewEPCommon(client.netlink) if err := epc.CreateEndpoint(client.hostVethName, client.containerVethName); err != nil { return newErrorTransparentEndpointClient(err.Error()) } - containerIf, err := net.InterfaceByName(client.containerVethName) + defer func() { + if err != nil { + if delErr := client.netlink.DeleteLink(client.hostVethName); delErr != nil { + log.Errorf("Deleting veth failed on addendpoint failure:%v", delErr) + } + } + }() + + containerIf, err := client.netioshim.GetNetworkInterfaceByName(client.containerVethName) if err != nil { - return err + return newErrorTransparentEndpointClient(err.Error()) } client.containerMac = containerIf.HardwareAddr - hostVethIf, err := net.InterfaceByName(client.hostVethName) + hostVethIf, err := client.netioshim.GetNetworkInterfaceByName(client.hostVethName) if err != nil { - return err + return newErrorTransparentEndpointClient(err.Error()) } client.hostVethMac = hostVethIf.HardwareAddr + log.Printf("Setting mtu %d on veth interface %s", primaryIf.MTU, client.hostVethName) + if err := client.netlink.SetLinkMTU(client.hostVethName, primaryIf.MTU); err != nil { + log.Errorf("Setting mtu failed for hostveth %s:%v", client.hostVethName, err) + } + + if err := client.netlink.SetLinkMTU(client.containerVethName, primaryIf.MTU); err != nil { + log.Errorf("Setting mtu failed for containerveth %s:%v", client.containerVethName, err) + } + return nil }