feat: Add native linux endpoint client to prep removing OVS (#1471)

* Native Endpoint Client Add Endpoints

* AddEndpointRules, ConfigureContainerInterfacesAndRoutes

* Changed interface names, log statements

nw.extIf.Name > eth0 (eth0)
eth0.vlanid > eth0.X (eth0.1)
%s%s hostIfName > vnet (A1veth0)
%s%s-2 contIfName > container (B1veth0)

* Renaming, using lib to set ns

* Namespace "path" is /var/run/netns/<NS>

* Loopback set up, Remove auto kernel subnet route

* Cannot set link to up if it's in another NS

* Multiple containers on same VNET NS

* Delete Endpoint routes on Delete

* Minimizing netns usage

* Moving NS Exec Code

* Further minimized netns.Set usage

* Moved helper methods down, drafted tests

* Removed DevName from Route Info, more tests

* Test existing vnet ns, delete endpoint

* NetNS interface for testing

* Separated tests by namespace

* Endpoints delete if they cannot be moved into NS

* Namespace netns tests

* Added Native Client to deleteEndpointImpl

* Deletion of Endpoints Impl and Tests

* Cleaned code (Tests ok)

* Moved mock/netns to package (Tests ok)

* Fixing Netns (wip)

Moved netnsinterface to consumer package (network).
Removed "Netns" from "NewNetns" and "NewMockNetns" as it is unambiguous.
Changed uintptr to int and casted the int to uintptr when needed later.

* Using errors.Wrap for error context (wip)

* Removed sentence case (wip)

* Removing variable predeclaration

* Removed NewNativeEndpointClient

Directly instantiating struct because nothing special happens in NewNativeEndpointClient

* Removed generics from ExecuteInNS

* Removed uintptr from mocknetns, tests compile

Forgot to remove uintptr from mocknetns

* Fix tests, lint

* Fixes from linter

Works on VMSS

* Replacing references to ethX with vlan veth

* Removed unnecessary log

* Removed unnecessary mac, fix tests

* Mockns method name enum

* Unable to use GetNetworkInterfaceByName due to NS

If I use GetNetworkInterface, I need to be in the vnet NS, but that means I will need to call ExecuteInNS, which causes tests to fail.

* Fixes from linter

* Assume if NS exists, vlan veth exists

Tests ok

* Fixes for Linter

* Fix delete tests

* Fix delete tests bug

* Go mod tidy for linting

Hopefully this fixes the windows lint error

* No lint on vishvananda netns

Maybe this will fix the windows linter?

* Build linux only for netns package

Maybe this fixes the linter error?

* Remove nolint to see if linter fails

* Moved netns interface to caller, generalized tests

Tests ok, Native ok

* Typos

* Reordered if statement, unwrapped arp

Tests ok, ping ok, wget ok

* Renamed veth, fixed logs

* Made deleteEndpoints logic clearer, renamed error

* Renamed eth0 to primaryHostIfName, vlanEth to vlanIf
This commit is contained in:
QxBytes 2022-08-02 14:54:10 -07:00 коммит произвёл GitHub
Родитель 7640ebc35f
Коммит d57e24ee25
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
11 изменённых файлов: 1181 добавлений и 22 удалений

3
go.mod
Просмотреть файл

@ -104,7 +104,8 @@ require (
github.com/subosito/gotenv v1.3.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect
github.com/vishvananda/netlink v1.2.1-beta.2
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect

7
go.sum
Просмотреть файл

@ -830,13 +830,14 @@ github.com/valyala/fasttemplate v1.2.1 h1:TVEnxayobAdVkhQfrfes2IzOB6o+z4roRkPF52
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852 h1:cPXZWzzG0NllBLdjWoD1nDfaqu98YMv+OneaKc8sPOA=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs=
github.com/vishvananda/netlink v1.2.1-beta.2/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=
github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=

38
netns/netns.go Normal file
Просмотреть файл

@ -0,0 +1,38 @@
//go:build linux
// +build linux
package netns
import (
"github.com/pkg/errors"
"github.com/vishvananda/netns"
)
type Netns struct{}
func New() *Netns {
return &Netns{}
}
func (f *Netns) Get() (int, error) {
nsHandle, err := netns.Get()
return int(nsHandle), errors.Wrap(err, "netns impl")
}
func (f *Netns) GetFromName(name string) (int, error) {
nsHandle, err := netns.GetFromName(name)
return int(nsHandle), errors.Wrap(err, "netns impl")
}
func (f *Netns) Set(fileDescriptor int) error {
return errors.Wrap(netns.Set(netns.NsHandle(fileDescriptor)), "netns impl")
}
func (f *Netns) NewNamed(name string) (int, error) {
nsHandle, err := netns.NewNamed(name)
return int(nsHandle), errors.Wrap(err, "netns impl")
}
func (f *Netns) DeleteNamed(name string) error {
return errors.Wrap(netns.DeleteNamed(name), "netns impl")
}

Просмотреть файл

@ -13,6 +13,7 @@ import (
"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/netns"
"github.com/Azure/azure-container-networking/network/networkutils"
"github.com/Azure/azure-container-networking/ovsctl"
"github.com/Azure/azure-container-networking/platform"
@ -89,21 +90,42 @@ func (nw *network) newEndpointImpl(_ apipaClient, nl netlink.NetlinkInterface, p
}
if vlanid != 0 {
log.Printf("OVS client")
if _, ok := epInfo.Data[SnatBridgeIPKey]; ok {
nw.SnatBridgeIP = epInfo.Data[SnatBridgeIPKey].(string)
}
if nw.Mode == opModeNative {
log.Printf("Native client")
vlanVethName := fmt.Sprintf("%s.%d", nw.extIf.Name, vlanid)
vnetNSName := fmt.Sprintf("az_ns_%d", vlanid)
epClient = NewOVSEndpointClient(
nw,
epInfo,
hostIfName,
contIfName,
vlanid,
localIP,
nl,
ovsctl.NewOvsctl(),
plc)
epClient = &NativeEndpointClient{
primaryHostIfName: nw.extIf.Name,
vlanIfName: vlanVethName,
vnetVethName: hostIfName,
containerVethName: contIfName,
vnetNSName: vnetNSName,
nw: nw,
vlanID: vlanid,
netnsClient: netns.New(),
netlink: nl,
netioshim: &netio.NetIO{},
plClient: plc,
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
}
} else {
log.Printf("OVS client")
if _, ok := epInfo.Data[SnatBridgeIPKey]; ok {
nw.SnatBridgeIP = epInfo.Data[SnatBridgeIPKey].(string)
}
epClient = NewOVSEndpointClient(
nw,
epInfo,
hostIfName,
contIfName,
vlanid,
localIP,
nl,
ovsctl.NewOvsctl(),
plc)
}
} else if nw.Mode != opModeTransparent {
log.Printf("Bridge client")
epClient = NewLinuxBridgeEndpointClient(nw.extIf, hostIfName, contIfName, nw.Mode, nl, plc)
@ -239,7 +261,28 @@ func (nw *network) deleteEndpointImpl(nl netlink.NetlinkInterface, plc platform.
// entering the container netns and hence works both for CNI and CNM.
if ep.VlanID != 0 {
epInfo := ep.getInfo()
epClient = NewOVSEndpointClient(nw, epInfo, ep.HostIfName, "", ep.VlanID, ep.LocalIP, nl, ovsctl.NewOvsctl(), plc)
if nw.Mode == opModeNative {
log.Printf("Native client")
vlanVethName := fmt.Sprintf("%s.%d", nw.extIf.Name, ep.VlanID)
vnetNSName := fmt.Sprintf("az_ns_%d", ep.VlanID)
epClient = &NativeEndpointClient{
primaryHostIfName: nw.extIf.Name,
vlanIfName: vlanVethName,
vnetVethName: ep.HostIfName,
containerVethName: "",
vnetNSName: vnetNSName,
nw: nw,
vlanID: ep.VlanID,
netnsClient: netns.New(),
netlink: nl,
netioshim: &netio.NetIO{},
plClient: plc,
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
}
} else {
epClient = NewOVSEndpointClient(nw, epInfo, ep.HostIfName, "", ep.VlanID, ep.LocalIP, nl, ovsctl.NewOvsctl(), plc)
}
} else if nw.Mode != opModeTransparent {
epClient = NewLinuxBridgeEndpointClient(nw.extIf, ep.HostIfName, "", nw.Mode, nl, plc)
} else {

Просмотреть файл

@ -1,5 +1,5 @@
//go:build windows
//+build windows
// +build windows
package hnswrapper

Просмотреть файл

@ -19,5 +19,5 @@ type HnsV1WrapperInterface interface {
GetHNSEndpointByID(endpointID string) (*hcsshim.HNSEndpoint, error)
HotAttachEndpoint(containerID string, endpointID string) error
IsAttached(hnsep *hcsshim.HNSEndpoint, containerID string) (bool, error)
GetHNSGlobals() (*hcsshim.HNSGlobals, error)
GetHNSGlobals() (*hcsshim.HNSGlobals, error)
}

Просмотреть файл

@ -42,7 +42,7 @@ func NewHnsv2wrapperFake() *Hnsv2wrapperFake {
}
}
func delayHnsCall(delay time.Duration){
func delayHnsCall(delay time.Duration) {
time.Sleep(delay)
}

Просмотреть файл

@ -0,0 +1,407 @@
package network
import (
"fmt"
"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/networkutils"
"github.com/Azure/azure-container-networking/platform"
"github.com/pkg/errors"
vishnetlink "github.com/vishvananda/netlink"
)
const (
azureMac = "12:34:56:78:9a:bc" // Packets leaving the VM should have this MAC
loopbackIf = "lo" // The name of the loopback interface
numDefaultRoutes = 2 // VNET NS, when no containers use it, has this many routes
)
type netnsClient interface {
Get() (fileDescriptor int, err error)
GetFromName(name string) (fileDescriptor int, err error)
Set(fileDescriptor int) (err error)
NewNamed(name string) (fileDescriptor int, err error)
DeleteNamed(name string) (err error)
}
type NativeEndpointClient struct {
primaryHostIfName string // So like eth0
vlanIfName string // So like eth0.1
vnetVethName string // Peer is containerVethName
containerVethName string // Peer is vnetVethName
vnetMac net.HardwareAddr
containerMac net.HardwareAddr
vnetNSName string
vnetNSFileDescriptor int
nw *network
vlanID int
netnsClient netnsClient
netlink netlink.NetlinkInterface
netioshim netio.NetIOInterface
plClient platform.ExecClient
netUtilsClient networkutils.NetworkUtils
}
// Adds interfaces to the vnet (created if not existing) and vm namespace
func (client *NativeEndpointClient) AddEndpoints(epInfo *EndpointInfo) error {
// VM Namespace
err := client.PopulateVM(epInfo)
if err != nil {
return err
}
// VNET Namespace
return ExecuteInNS(client.vnetNSName, func() error {
return client.PopulateVnet(epInfo)
})
}
// Called from AddEndpoints, Namespace: VM
func (client *NativeEndpointClient) PopulateVM(epInfo *EndpointInfo) error {
vmNS, err := client.netnsClient.Get()
if err != nil {
return errors.Wrap(err, "failed to get vm ns handle")
}
log.Printf("[native] Checking if NS exists...")
vnetNS, existingErr := client.netnsClient.GetFromName(client.vnetNSName)
// If the ns does not exist, the below code will trigger to create it
// This will also (we assume) mean the vlan veth does not exist
if existingErr != nil {
// We assume the only possible error is that the namespace doesn't exist
log.Printf("[native] No existing NS detected. Creating the vnet namespace and switching to it")
vnetNS, err = client.netnsClient.NewNamed(client.vnetNSName)
if err != nil {
return errors.Wrap(err, "failed to create vnet ns")
}
client.vnetNSFileDescriptor = vnetNS
deleteNSIfNotNilErr := client.netnsClient.Set(vmNS)
// Any failure will trigger removing the namespace created
defer func() {
if deleteNSIfNotNilErr != nil {
log.Logf("[native] Removing vnet ns due to failure...")
err = client.netnsClient.DeleteNamed(client.vnetNSName)
if err != nil {
log.Errorf("failed to cleanup/delete ns after failing to create vlan veth")
}
}
}()
if deleteNSIfNotNilErr != nil {
return errors.Wrap(deleteNSIfNotNilErr, "failed to set current ns to vm")
}
// Now create vlan veth
log.Printf("[native] Create the host vlan link after getting eth0: %s", client.primaryHostIfName)
// Get parent interface index. Index is consistent across libraries.
eth0, deleteNSIfNotNilErr := client.netioshim.GetNetworkInterfaceByName(client.primaryHostIfName)
if deleteNSIfNotNilErr != nil {
return errors.Wrap(deleteNSIfNotNilErr, "failed to get eth0 interface")
}
linkAttrs := vishnetlink.NewLinkAttrs()
linkAttrs.Name = client.vlanIfName
// Set the peer
linkAttrs.ParentIndex = eth0.Index
link := &vishnetlink.Vlan{
LinkAttrs: linkAttrs,
VlanId: client.vlanID,
}
log.Printf("[native] Attempting to create %s link in VM NS", client.vlanIfName)
// Create vlan veth
deleteNSIfNotNilErr = vishnetlink.LinkAdd(link)
if deleteNSIfNotNilErr != nil {
// Any failure to add the link should error (auto delete NS)
return errors.Wrap(deleteNSIfNotNilErr, "failed to create vlan vnet link after making new ns")
}
// vlan veth was created successfully, so move the vlan veth you created
log.Printf("[native] Move vlan link (%s) to vnet NS: %d", client.vlanIfName, uintptr(client.vnetNSFileDescriptor))
deleteNSIfNotNilErr = client.netlink.SetLinkNetNs(client.vlanIfName, uintptr(client.vnetNSFileDescriptor))
if deleteNSIfNotNilErr != nil {
if delErr := client.netlink.DeleteLink(client.vlanIfName); delErr != nil {
log.Errorf("deleting vlan veth failed on addendpoint failure")
}
return errors.Wrap(deleteNSIfNotNilErr, "deleting vlan veth in vm ns due to addendpoint failure")
}
} else {
log.Printf("[native] Existing NS (%s) detected. Assuming %s exists too", client.vnetNSName, client.vlanIfName)
}
client.vnetNSFileDescriptor = vnetNS
if err = client.netUtilsClient.CreateEndpoint(client.vnetVethName, client.containerVethName); err != nil {
return errors.Wrap(err, "failed to create veth pair")
}
if err = client.netlink.SetLinkNetNs(client.vnetVethName, uintptr(client.vnetNSFileDescriptor)); err != nil {
if delErr := client.netlink.DeleteLink(client.vnetVethName); delErr != nil {
log.Errorf("Deleting vnet veth failed on addendpoint failure:%v", delErr)
}
return errors.Wrap(err, "failed to move vnetVethName into vnet ns, deleting")
}
containerIf, err := client.netioshim.GetNetworkInterfaceByName(client.containerVethName)
if err != nil {
return errors.Wrap(err, "container veth does not exist")
}
client.containerMac = containerIf.HardwareAddr
return nil
}
// Called from AddEndpoints, Namespace: Vnet
func (client *NativeEndpointClient) PopulateVnet(epInfo *EndpointInfo) error {
_, err := client.netioshim.GetNetworkInterfaceByName(client.vlanIfName)
if err != nil {
return errors.Wrap(err, "vlan veth doesn't exist")
}
vnetVethIf, err := client.netioshim.GetNetworkInterfaceByName(client.vnetVethName)
if err != nil {
return errors.Wrap(err, "vnet veth doesn't exist")
}
client.vnetMac = vnetVethIf.HardwareAddr
return nil
}
func (client *NativeEndpointClient) AddEndpointRules(epInfo *EndpointInfo) error {
// There are no rules to add here
// Described as rules on ip addresses on the container interface
return nil
}
func (client *NativeEndpointClient) DeleteEndpointRules(ep *endpoint) {
// Never added any endpoint rules
}
func (client *NativeEndpointClient) MoveEndpointsToContainerNS(epInfo *EndpointInfo, nsID uintptr) error {
if err := client.netlink.SetLinkNetNs(client.containerVethName, nsID); err != nil {
return errors.Wrap(err, "failed to move endpoint to container ns")
}
return nil
}
func (client *NativeEndpointClient) SetupContainerInterfaces(epInfo *EndpointInfo) error {
if err := client.netUtilsClient.SetupContainerInterface(client.containerVethName, epInfo.IfName); err != nil {
return errors.Wrap(err, "failed to setup container interface")
}
client.containerVethName = epInfo.IfName
return nil
}
// Adds routes, arp entries, etc. to the vnet and container namespaces
func (client *NativeEndpointClient) ConfigureContainerInterfacesAndRoutes(epInfo *EndpointInfo) error {
// Container NS
err := client.ConfigureContainerInterfacesAndRoutesImpl(epInfo)
if err != nil {
return err
}
// Switch to vnet NS and call ConfigureVnetInterfacesAndRoutes
return ExecuteInNS(client.vnetNSName, func() error {
return client.ConfigureVnetInterfacesAndRoutesImpl(epInfo)
})
}
// Called from ConfigureContainerInterfacesAndRoutes, Namespace: Container
func (client *NativeEndpointClient) ConfigureContainerInterfacesAndRoutesImpl(epInfo *EndpointInfo) error {
if err := client.netUtilsClient.AssignIPToInterface(client.containerVethName, epInfo.IPAddresses); err != nil {
return errors.Wrap(err, "failed to assign ips to container veth interface")
}
// kernel subnet route auto added by above call must be removed
for _, ipAddr := range epInfo.IPAddresses {
_, ipnet, _ := net.ParseCIDR(ipAddr.String())
routeInfo := RouteInfo{
Dst: *ipnet,
Scope: netlink.RT_SCOPE_LINK,
Protocol: netlink.RTPROT_KERNEL,
}
if err := deleteRoutes(client.netlink, client.netioshim, client.containerVethName, []RouteInfo{routeInfo}); err != nil {
return errors.Wrap(err, "failed to remove kernel subnet route")
}
}
if err := client.AddDefaultRoutes(client.containerVethName); err != nil {
return errors.Wrap(err, "failed container ns add default routes")
}
if err := client.AddDefaultArp(client.containerVethName, client.vnetMac.String()); err != nil {
return errors.Wrap(err, "failed container ns add default arp")
}
return nil
}
// Called from ConfigureContainerInterfacesAndRoutes, Namespace: Vnet
func (client *NativeEndpointClient) ConfigureVnetInterfacesAndRoutesImpl(epInfo *EndpointInfo) error {
err := client.netlink.SetLinkState(loopbackIf, true)
if err != nil {
return errors.Wrap(err, "failed to set loopback link state to up")
}
// Add route specifying which device the pod ip(s) are on
routeInfoList := client.GetVnetRoutes(epInfo.IPAddresses)
if err = client.AddDefaultRoutes(client.vlanIfName); err != nil {
return errors.Wrap(err, "failed vnet ns add default/gateway routes (idempotent)")
}
if err = client.AddDefaultArp(client.vlanIfName, azureMac); err != nil {
return errors.Wrap(err, "failed vnet ns add default arp entry (idempotent)")
}
if err = addRoutes(client.netlink, client.netioshim, client.vnetVethName, routeInfoList); err != nil {
return errors.Wrap(err, "failed adding routes to vnet specific to this container")
}
// Return to ConfigureContainerInterfacesAndRoutes
return err
}
// Helper that gets the routes in the vnet NS for a particular list of IP addresses
// Example: 192.168.0.4 dev <device which connects to NS with that IP> proto static
func (client *NativeEndpointClient) GetVnetRoutes(ipAddresses []net.IPNet) []RouteInfo {
routeInfoList := make([]RouteInfo, 0, len(ipAddresses))
// Add route specifying which device the pod ip(s) are on
for _, ipAddr := range ipAddresses {
var (
routeInfo RouteInfo
ipNet net.IPNet
)
if ipAddr.IP.To4() != nil {
ipNet = net.IPNet{IP: ipAddr.IP, Mask: net.CIDRMask(ipv4FullMask, ipv4Bits)}
} else {
ipNet = net.IPNet{IP: ipAddr.IP, Mask: net.CIDRMask(ipv6FullMask, ipv6Bits)}
}
log.Printf("[net] Native client adding route for the ip %v", ipNet.String())
routeInfo.Dst = ipNet
routeInfoList = append(routeInfoList, routeInfo)
}
return routeInfoList
}
// Helper that creates routing rules for the current NS which direct packets
// to the virtual gateway ip on linkToName device interface
// Route 1: 169.254.1.1 dev <linkToName>
// Route 2: default via 169.254.1.1 dev <linkToName>
func (client *NativeEndpointClient) AddDefaultRoutes(linkToName string) error {
// Add route for virtualgwip (ip route add 169.254.1.1/32 dev eth0)
virtualGwIP, virtualGwNet, _ := net.ParseCIDR(virtualGwIPString)
routeInfo := RouteInfo{
Dst: *virtualGwNet,
Scope: netlink.RT_SCOPE_LINK,
}
// Difference between interface name in addRoutes and DevName: in RouteInfo?
if err := addRoutes(client.netlink, client.netioshim, linkToName, []RouteInfo{routeInfo}); err != nil {
return err
}
// Add default route (ip route add default via 169.254.1.1 dev eth0)
_, defaultIPNet, _ := net.ParseCIDR(defaultGwCidr)
dstIP := net.IPNet{IP: net.ParseIP(defaultGw), Mask: defaultIPNet.Mask}
routeInfo = RouteInfo{
Dst: dstIP,
Gw: virtualGwIP,
}
if err := addRoutes(client.netlink, client.netioshim, linkToName, []RouteInfo{routeInfo}); err != nil {
return err
}
return nil
}
// Helper that creates arp entry for the current NS which maps the virtual
// gateway (169.254.1.1) to destMac on a particular interfaceName
// Example: (169.254.1.1) at 12:34:56:78:9a:bc [ether] PERM on <interfaceName>
func (client *NativeEndpointClient) AddDefaultArp(interfaceName, destMac string) error {
_, virtualGwNet, _ := net.ParseCIDR(virtualGwIPString)
log.Printf("[net] Adding static arp for IP address %v and MAC %v in namespace",
virtualGwNet.String(), destMac)
hardwareAddr, err := net.ParseMAC(destMac)
if err != nil {
return errors.Wrap(err, "unable to parse mac")
}
if err := client.netlink.AddOrRemoveStaticArp(netlink.ADD, interfaceName, virtualGwNet.IP, hardwareAddr, false); err != nil {
return fmt.Errorf("adding arp entry failed: %w", err)
}
return nil
}
func (client *NativeEndpointClient) DeleteEndpoints(ep *endpoint) error {
return ExecuteInNS(client.vnetNSName, func() error {
// Passing in functionality to get number of routes after deletion
getNumRoutesLeft := func() (int, error) {
routes, err := vishnetlink.RouteList(nil, vishnetlink.FAMILY_V4)
if err != nil {
return 0, errors.Wrap(err, "failed to get num routes left")
}
return len(routes), nil
}
return client.DeleteEndpointsImpl(ep, getNumRoutesLeft)
})
}
// getNumRoutesLeft is a function which gets the current number of routes in the namespace. Namespace: Vnet
func (client *NativeEndpointClient) DeleteEndpointsImpl(ep *endpoint, getNumRoutesLeft func() (int, error)) error {
routeInfoList := client.GetVnetRoutes(ep.IPAddresses)
if err := deleteRoutes(client.netlink, client.netioshim, client.vnetVethName, routeInfoList); err != nil {
return errors.Wrap(err, "failed to remove routes")
}
routesLeft, err := getNumRoutesLeft()
if err != nil {
return err
}
log.Printf("[native] There are %d routes remaining after deletion", routesLeft)
if routesLeft <= numDefaultRoutes {
// Deletes default arp, default routes, vlan veth; there are two default routes
// so when we have <= numDefaultRoutes routes left, no containers use this namespace
log.Printf("[native] Deleting namespace %s as no containers occupy it", client.vnetNSName)
delErr := client.netnsClient.DeleteNamed(client.vnetNSName)
if delErr != nil {
return errors.Wrap(delErr, "failed to delete namespace")
}
}
return nil
}
// Helper function that allows executing a function in a VM namespace
// Does not work for process namespaces
func ExecuteInNS(nsName string, f func() error) error {
// Current namespace
returnedTo, err := GetCurrentThreadNamespace()
if err != nil {
log.Errorf("[ExecuteInNS] Could not get NS we are in: %v", err)
} else {
log.Printf("[ExecuteInNS] In NS before switch: %s", returnedTo.file.Name())
}
// Open the network namespace
log.Printf("[ExecuteInNS] Opening ns %v.", fmt.Sprintf("/var/run/netns/%s", nsName))
ns, err := OpenNamespace(fmt.Sprintf("/var/run/netns/%s", nsName))
if err != nil {
return err
}
defer ns.Close()
// Enter the network namespace
log.Printf("[ExecuteInNS] Entering ns %s.", ns.file.Name())
if err := ns.Enter(); err != nil {
return err
}
// Exit network namespace
defer func() {
log.Printf("[ExecuteInNS] Exiting ns %s.", ns.file.Name())
if err := ns.Exit(); err != nil {
log.Errorf("[ExecuteInNS] Could not exit ns, err:%v.", err)
}
returnedTo, err := GetCurrentThreadNamespace()
if err != nil {
log.Errorf("[ExecuteInNS] Could not get NS we returned to: %v", err)
} else {
log.Printf("[ExecuteInNS] Returned to NS: %s", returnedTo.file.Name())
}
}()
return f()
}

Просмотреть файл

@ -0,0 +1,665 @@
//go:build linux
// +build linux
package network
import (
"net"
"testing"
"github.com/Azure/azure-container-networking/netio"
"github.com/Azure/azure-container-networking/netlink"
"github.com/Azure/azure-container-networking/network/networkutils"
"github.com/Azure/azure-container-networking/platform"
"github.com/pkg/errors"
"github.com/stretchr/testify/require"
)
var errNetnsMock = errors.New("mock netns error")
func newNetnsErrorMock(errStr string) error {
return errors.Wrap(errNetnsMock, errStr)
}
type mockNetns struct {
get func() (fileDescriptor int, err error)
getFromName func(name string) (fileDescriptor int, err error)
set func(fileDescriptor int) (err error)
newNamed func(name string) (fileDescriptor int, err error)
deleteNamed func(name string) (err error)
}
func (netns *mockNetns) Get() (fileDescriptor int, err error) {
return netns.get()
}
func (netns *mockNetns) GetFromName(name string) (fileDescriptor int, err error) {
return netns.getFromName(name)
}
func (netns *mockNetns) Set(fileDescriptor int) (err error) {
return netns.set(fileDescriptor)
}
func (netns *mockNetns) NewNamed(name string) (fileDescriptor int, err error) {
return netns.newNamed(name)
}
func (netns *mockNetns) DeleteNamed(name string) (err error) {
return netns.deleteNamed(name)
}
func defaultGet() (int, error) {
return 1, nil
}
func defaultGetFromName(name string) (int, error) {
return 1, nil
}
func defaultSet(handle int) error {
return nil
}
func defaultNewNamed(name string) (int, error) {
return 1, nil
}
func defaultDeleteNamed(name string) error {
return nil
}
func TestNativeAddEndpoints(t *testing.T) {
nl := netlink.NewMockNetlink(false, "")
plc := platform.NewMockExecClient(false)
tests := []struct {
name string
client *NativeEndpointClient
epInfo *EndpointInfo
wantErr bool
wantErrMsg string
}{
// Populating VM with data and creating interfaces/links
{
name: "Add endpoints create vnet ns failure",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: func(name string) (fileDescriptor int, err error) {
return 0, newNetnsErrorMock("netns failure")
},
newNamed: func(name string) (fileDescriptor int, err error) {
return 0, newNetnsErrorMock("netns failure")
},
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "failed to create vnet ns: netns failure: " + errNetnsMock.Error(),
},
{
name: "Add endpoints with existing vnet ns",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: defaultGetFromName,
newNamed: defaultNewNamed,
set: defaultSet,
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: false,
},
{
name: "Add endpoints netlink fail",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: defaultGetFromName,
newNamed: defaultNewNamed,
set: defaultSet,
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(true, "netlink fail"),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "failed to move vnetVethName into vnet ns, deleting: " + netlink.ErrorMockNetlink.Error() + " : netlink fail",
},
{
name: "Add endpoints get interface fail for primary interface (eth0)",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: func(name string) (fileDescriptor int, err error) {
return 0, newNetnsErrorMock("netns failure")
},
newNamed: defaultNewNamed,
set: defaultSet,
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 1),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "failed to get eth0 interface: " + netio.ErrMockNetIOFail.Error() + ":eth0",
},
{
name: "Add endpoints get interface fail for getting container veth",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: defaultGetFromName,
newNamed: defaultNewNamed,
set: defaultSet,
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 1),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "container veth does not exist: " + netio.ErrMockNetIOFail.Error() + ":B1veth0",
},
{
name: "Add endpoints NetNS Get fail",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: func() (fileDescriptor int, err error) {
return 0, newNetnsErrorMock("netns failure")
},
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "failed to get vm ns handle: netns failure: " + errNetnsMock.Error(),
},
{
name: "Add endpoints NetNS Set fail",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
get: defaultGet,
getFromName: func(name string) (fileDescriptor int, err error) {
return 0, newNetnsErrorMock("do not fail on this error")
},
newNamed: defaultNewNamed,
set: func(fileDescriptor int) (err error) {
return newNetnsErrorMock("netns failure")
},
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "failed to set current ns to vm: netns failure: " + errNetnsMock.Error(),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.client.PopulateVM(tt.epInfo)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErrMsg, "Expected:%v actual:%v", tt.wantErrMsg, err.Error())
} else {
require.NoError(t, err)
}
})
}
tests = []struct {
name string
client *NativeEndpointClient
epInfo *EndpointInfo
wantErr bool
wantErrMsg string
}{
// Populate the client with information from the vnet and set up vnet
{
name: "Add endpoints get vnet veth mac address",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{},
wantErr: false,
},
{
name: "Add endpoints fail check vlan veth exists",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 1),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "vlan veth doesn't exist: " + netio.ErrMockNetIOFail.Error() + ":eth0.1",
},
{
name: "Add endpoints fail check vnet veth exists",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 2),
},
epInfo: &EndpointInfo{},
wantErr: true,
wantErrMsg: "vnet veth doesn't exist: " + netio.ErrMockNetIOFail.Error() + ":A1veth0",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.client.PopulateVnet(tt.epInfo)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErrMsg, "Expected:%v actual:%v", tt.wantErrMsg, err.Error())
} else {
require.NoError(t, err)
}
})
}
}
func TestNativeDeleteEndpoints(t *testing.T) {
nl := netlink.NewMockNetlink(false, "")
plc := platform.NewMockExecClient(false)
IPAddresses := []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
{
IP: net.ParseIP("192.168.0.6"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
}
tests := []struct {
name string
client *NativeEndpointClient
ep *endpoint
wantErr bool
wantErrMsg string
routesLeft func() (int, error)
}{
{
name: "Delete endpoint delete vnet ns",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
deleteNamed: defaultDeleteNamed,
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
ep: &endpoint{
IPAddresses: IPAddresses,
},
routesLeft: func() (int, error) {
return numDefaultRoutes, nil
},
wantErr: false,
},
{
name: "Delete endpoint do not delete vnet ns it is still in use",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
deleteNamed: func(name string) (err error) {
return newNetnsErrorMock("netns failure")
},
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
ep: &endpoint{
IPAddresses: IPAddresses,
},
routesLeft: func() (int, error) {
return numDefaultRoutes + 1, nil
},
wantErr: false,
},
{
name: "Delete endpoint fail to delete namespace",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
netnsClient: &mockNetns{
deleteNamed: func(name string) (err error) {
return newNetnsErrorMock("netns failure")
},
},
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
ep: &endpoint{
IPAddresses: IPAddresses,
},
routesLeft: func() (int, error) {
return numDefaultRoutes, nil
},
wantErr: true,
wantErrMsg: "failed to delete namespace: netns failure: " + errNetnsMock.Error(),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.client.DeleteEndpointsImpl(tt.ep, tt.routesLeft)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErrMsg, "Expected:%v actual:%v", tt.wantErrMsg, err.Error())
} else {
require.NoError(t, err)
}
})
}
}
func TestNativeConfigureContainerInterfacesAndRoutes(t *testing.T) {
nl := netlink.NewMockNetlink(false, "")
plc := platform.NewMockExecClient(false)
vnetMac, _ := net.ParseMAC("ab:cd:ef:12:34:56")
tests := []struct {
name string
client *NativeEndpointClient
epInfo *EndpointInfo
wantErr bool
wantErrMsg string
}{
{
name: "Configure interface and routes good path for container",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: false,
},
{
name: "Configure interface and routes multiple IPs",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
{
IP: net.ParseIP("192.168.0.6"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
{
IP: net.ParseIP("192.168.0.8"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: false,
},
{
name: "Configure interface and routes assign ip fail",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(true, "netlink fail"),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: true,
wantErrMsg: "netlink fail",
},
{
name: "Configure interface and routes container 2nd default route added fail",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 3),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: true,
wantErrMsg: "failed container ns add default routes: addRoutes failed: " + netio.ErrMockNetIOFail.Error() + ":B1veth0",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.client.ConfigureContainerInterfacesAndRoutesImpl(tt.epInfo)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErrMsg, "Expected:%v actual:%v", tt.wantErrMsg, err.Error())
} else {
require.NoError(t, err)
}
})
}
tests = []struct {
name string
client *NativeEndpointClient
epInfo *EndpointInfo
wantErr bool
wantErrMsg string
}{
{
name: "Configure interface and routes good path for vnet",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(false, 0),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: false,
},
{
// fail route that tells which device container ip is on for vnet
name: "Configure interface and routes fail final routes for vnet",
client: &NativeEndpointClient{
primaryHostIfName: "eth0",
vlanIfName: "eth0.1",
vnetVethName: "A1veth0",
containerVethName: "B1veth0",
vnetNSName: "az_ns_1",
vnetMac: vnetMac,
netlink: netlink.NewMockNetlink(false, ""),
plClient: platform.NewMockExecClient(false),
netUtilsClient: networkutils.NewNetworkUtils(nl, plc),
netioshim: netio.NewMockNetIO(true, 3),
},
epInfo: &EndpointInfo{
IPAddresses: []net.IPNet{
{
IP: net.ParseIP("192.168.0.4"),
Mask: net.CIDRMask(subnetv4Mask, ipv4Bits),
},
},
},
wantErr: true,
wantErrMsg: "failed adding routes to vnet specific to this container: addRoutes failed: " + netio.ErrMockNetIOFail.Error() + ":A1veth0",
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
err := tt.client.ConfigureVnetInterfacesAndRoutesImpl(tt.epInfo)
if tt.wantErr {
require.Error(t, err)
require.Contains(t, err.Error(), tt.wantErrMsg, "Expected:%v actual:%v", tt.wantErrMsg, err.Error())
} else {
require.NoError(t, err)
}
})
}
}

Просмотреть файл

@ -18,6 +18,7 @@ const (
opModeBridge = "bridge"
opModeTunnel = "tunnel"
opModeTransparent = "transparent"
opModeNative = "native"
opModeDefault = opModeTunnel
)

Просмотреть файл

@ -88,6 +88,9 @@ func (nm *networkManager) newNetworkImpl(nwInfo *NetworkInfo, extIf *externalInt
return nil, fmt.Errorf("Ipv6 forwarding failed: %w", err)
}
}
case opModeNative:
log.Printf("Native mode")
ifName = extIf.Name
default:
return nil, errNetworkModeInvalid
}