go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/ifplugin/descriptor/interface.go (about)

     1  // Copyright (c) 2018 Cisco and/or its affiliates.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at:
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package descriptor
    16  
    17  import (
    18  	"fmt"
    19  	"net"
    20  	"os"
    21  	"path/filepath"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"github.com/vishvananda/netlink"
    26  	"golang.org/x/sys/unix"
    27  
    28  	"go.ligato.io/vpp-agent/v3/pkg/models"
    29  
    30  	"github.com/pkg/errors"
    31  	"google.golang.org/protobuf/proto"
    32  	"google.golang.org/protobuf/types/known/emptypb"
    33  
    34  	"go.ligato.io/cn-infra/v2/idxmap"
    35  	"go.ligato.io/cn-infra/v2/logging"
    36  	"go.ligato.io/cn-infra/v2/logging/logrus"
    37  	"go.ligato.io/cn-infra/v2/servicelabel"
    38  
    39  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    40  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/descriptor/adapter"
    41  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
    42  	iflinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
    43  	"go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin"
    44  	nsdescriptor "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/descriptor"
    45  	nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
    46  	"go.ligato.io/vpp-agent/v3/plugins/netalloc"
    47  	netalloc_descr "go.ligato.io/vpp-agent/v3/plugins/netalloc/descriptor"
    48  	vpp_ifaceidx "go.ligato.io/vpp-agent/v3/plugins/vpp/ifplugin/ifaceidx"
    49  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    50  	namespace "go.ligato.io/vpp-agent/v3/proto/ligato/linux/namespace"
    51  	netalloc_api "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc"
    52  	vpp_intf "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces"
    53  )
    54  
    55  const (
    56  	// InterfaceDescriptorName is the name of the descriptor for Linux interfaces.
    57  	InterfaceDescriptorName = "linux-interface"
    58  
    59  	// DefaultVrfDevLegacyMTU is ETH_MAX_MTU value used as the default for the linux VRF Dev
    60  	DefaultVrfDevLegacyMTU = 65536
    61  	// DefaultVrfDevMTU is the ETH_MAX_MTU increased by the size of the IPv6 header, used
    62  	// as the default for VRF Dev in latest linux kernels
    63  	DefaultVrfDevMTU = 65575
    64  
    65  	// default MTU - expected when MTU is not specified in the config.
    66  	defaultEthernetMTU = 1500
    67  	defaultLoopbackMTU = 65536
    68  
    69  	// dependency labels
    70  	existingHostInterfaceDep = "host-interface-exists"
    71  	tapInterfaceDep          = "vpp-tap-interface-exists"
    72  	vethPeerDep              = "veth-peer-exists"
    73  	microserviceDep          = "microservice-available"
    74  
    75  	// suffix attached to logical names of duplicate VETH interfaces
    76  	vethDuplicateSuffix = "-DUPLICATE"
    77  
    78  	// suffix attached to logical names of VETH interfaces with peers not found by Retrieve
    79  	vethMissingPeerSuffix = "-MISSING_PEER"
    80  )
    81  
    82  // A list of non-retriable errors:
    83  var (
    84  	// ErrUnsupportedLinuxInterfaceType is returned for Linux interfaces of unknown type.
    85  	ErrUnsupportedLinuxInterfaceType = errors.New("unsupported Linux interface type")
    86  
    87  	// ErrInterfaceWithoutName is returned when Linux interface configuration has undefined
    88  	// Name attribute.
    89  	ErrInterfaceWithoutName = errors.New("Linux interface defined without logical name")
    90  
    91  	// ErrInterfaceWithoutType is returned when Linux interface configuration has undefined
    92  	// Type attribute.
    93  	ErrInterfaceWithoutType = errors.New("Linux interface defined without type")
    94  
    95  	// ErrNamespaceWithoutReference is returned when namespace is missing reference.
    96  	ErrInterfaceReferenceMismatch = errors.New("Linux interface reference does not match the interface type")
    97  
    98  	// ErrVETHWithoutPeer is returned when VETH interface is missing peer interface
    99  	// reference.
   100  	ErrVETHWithoutPeer = errors.New("VETH interface defined without peer reference")
   101  
   102  	// ErrTAPWithoutVPPReference is returned when TAP_TO_VPP interface is missing reference to VPP TAP.
   103  	ErrTAPWithoutVPPReference = errors.New("TAP_TO_VPP interface defined without reference to VPP TAP")
   104  
   105  	// ErrTAPRequiresVPPIfPlugin is returned when TAP_TO_VPP is supposed to be configured but VPP ifplugin
   106  	// is not loaded.
   107  	ErrTAPRequiresVPPIfPlugin = errors.New("TAP_TO_VPP interface requires VPP interface plugin to be loaded")
   108  
   109  	// ErrNamespaceWithoutReference is returned when namespace is missing reference.
   110  	ErrNamespaceWithoutReference = errors.New("namespace defined without name")
   111  
   112  	// ErrExistingWithNamespace is returned when namespace is specified for
   113  	// EXISTING interface.
   114  	ErrExistingWithNamespace = errors.New("EXISTING interface defined with namespace")
   115  
   116  	// ErrExistingIpWithNetalloc is returned when netalloc and EXISTING-IP features are combined,
   117  	// which is currently not supported.
   118  	ErrExistingIpWithNetalloc = errors.New("it is not supported to reference EXISTING-IP via netalloc")
   119  
   120  	// ErrInvalidIPWithMask is returned when address is invalid or mask is missing
   121  	ErrInvalidIPWithMask = errors.New("IP with mask is not valid")
   122  
   123  	// ErrLoopbackAlreadyConfigured is returned when multiple logical NB interfaces tries to configure the same loopback
   124  	ErrLoopbackAlreadyConfigured = errors.New("loopback already configured")
   125  
   126  	// ErrLoopbackNotFound is returned if loopback interface can not be found
   127  	ErrLoopbackNotFound = errors.New("loopback not found")
   128  
   129  	// ErrVRFDevWithMACAddr is returned when VRF device is configured with a MAC address.
   130  	ErrVRFDevWithMACAddr = errors.New("it is unsupported to set MAC address to a VRF device")
   131  
   132  	// ErrVRFDevInsideVrf is returned when VRF device is configured to be inside another VRF.
   133  	ErrVRFDevInsideVrf = errors.New("VRF device cannot be inside another VRF")
   134  )
   135  
   136  // InterfaceDescriptor teaches KVScheduler how to configure Linux interfaces.
   137  type InterfaceDescriptor struct {
   138  	log          logging.Logger
   139  	serviceLabel servicelabel.ReaderAPI
   140  	ifHandler    iflinuxcalls.NetlinkAPI
   141  	nsPlugin     nsplugin.API
   142  	vppIfPlugin  VPPIfPluginAPI
   143  	addrAlloc    netalloc.AddressAllocator
   144  
   145  	// runtime
   146  	intfIndex ifaceidx.LinuxIfMetadataIndex
   147  }
   148  
   149  // VPPIfPluginAPI is defined here to avoid import cycles.
   150  type VPPIfPluginAPI interface {
   151  	// GetInterfaceIndex gives read-only access to map with metadata of all configured
   152  	// VPP interfaces.
   153  	GetInterfaceIndex() vpp_ifaceidx.IfaceMetadataIndex
   154  }
   155  
   156  // NewInterfaceDescriptor creates a new instance of the Interface descriptor.
   157  func NewInterfaceDescriptor(
   158  	serviceLabel servicelabel.ReaderAPI, nsPlugin nsplugin.API, vppIfPlugin VPPIfPluginAPI,
   159  	addrAlloc netalloc.AddressAllocator, log logging.PluginLogger) (descr *kvs.KVDescriptor,
   160  	ctx *InterfaceDescriptor) {
   161  
   162  	// descriptor context
   163  	ctx = &InterfaceDescriptor{
   164  		nsPlugin:     nsPlugin,
   165  		vppIfPlugin:  vppIfPlugin,
   166  		addrAlloc:    addrAlloc,
   167  		serviceLabel: serviceLabel,
   168  		log:          log.NewLogger("if-descriptor"),
   169  	}
   170  
   171  	typedDescr := &adapter.InterfaceDescriptor{
   172  		Name:               InterfaceDescriptorName,
   173  		NBKeyPrefix:        interfaces.ModelInterface.KeyPrefix(),
   174  		ValueTypeName:      interfaces.ModelInterface.ProtoName(),
   175  		KeySelector:        interfaces.ModelInterface.IsKeyValid,
   176  		KeyLabel:           interfaces.ModelInterface.StripKeyPrefix,
   177  		ValueComparator:    ctx.EquivalentInterfaces,
   178  		WithMetadata:       true,
   179  		MetadataMapFactory: ctx.MetadataFactory,
   180  		Validate:           ctx.Validate,
   181  		Create:             ctx.Create,
   182  		Delete:             ctx.Delete,
   183  		Update:             ctx.Update,
   184  		UpdateWithRecreate: ctx.UpdateWithRecreate,
   185  		Retrieve:           ctx.Retrieve,
   186  		IsRetriableFailure: ctx.IsRetriableFailure,
   187  		DerivedValues:      ctx.DerivedValues,
   188  		Dependencies:       ctx.Dependencies,
   189  		RetrieveDependencies: []string{
   190  			// refresh the pool of allocated IP addresses first
   191  			netalloc_descr.IPAllocDescriptorName,
   192  			nsdescriptor.MicroserviceDescriptorName},
   193  	}
   194  	descr = adapter.NewInterfaceDescriptor(typedDescr)
   195  	return
   196  }
   197  
   198  // SetInterfaceIndex should be used to provide interface index immediately after
   199  // the descriptor registration.
   200  func (d *InterfaceDescriptor) SetInterfaceIndex(intfIndex ifaceidx.LinuxIfMetadataIndex) {
   201  	d.intfIndex = intfIndex
   202  }
   203  
   204  // SetInterfaceHandler provides interface handler to the descriptor immediately after
   205  // the registration.
   206  func (d *InterfaceDescriptor) SetInterfaceHandler(ifHandler iflinuxcalls.NetlinkAPI) {
   207  	d.ifHandler = ifHandler
   208  }
   209  
   210  // EquivalentInterfaces is case-insensitive comparison function for interfaces.LinuxInterface.
   211  func (d *InterfaceDescriptor) EquivalentInterfaces(key string, oldIntf, newIntf *interfaces.Interface) bool {
   212  	// attributes compared as usually:
   213  	if oldIntf.Name != newIntf.Name ||
   214  		oldIntf.Type != newIntf.Type ||
   215  		oldIntf.Enabled != newIntf.Enabled ||
   216  		oldIntf.LinkOnly != newIntf.LinkOnly ||
   217  		getHostIfName(oldIntf) != getHostIfName(newIntf) {
   218  		return false
   219  	}
   220  	switch oldIntf.Type {
   221  	case interfaces.Interface_VETH:
   222  		if oldIntf.GetVeth().GetPeerIfName() != newIntf.GetVeth().GetPeerIfName() {
   223  			return false
   224  		}
   225  		// handle default config for checksum offloading
   226  		if getRxChksmOffloading(oldIntf) != getRxChksmOffloading(newIntf) ||
   227  			getTxChksmOffloading(oldIntf) != getTxChksmOffloading(newIntf) {
   228  			return false
   229  		}
   230  	case interfaces.Interface_TAP_TO_VPP:
   231  		if oldIntf.GetTap().GetVppTapIfName() != newIntf.GetTap().GetVppTapIfName() {
   232  			return false
   233  		}
   234  	case interfaces.Interface_VRF_DEVICE:
   235  		if oldIntf.GetVrfDev().GetRoutingTable() != newIntf.GetVrfDev().GetRoutingTable() {
   236  			return false
   237  		}
   238  	}
   239  
   240  	if !proto.Equal(oldIntf.Namespace, newIntf.Namespace) {
   241  		return false
   242  	}
   243  
   244  	// for existing interfaces all the other parameters are not managed by the agent
   245  	if oldIntf.Type == interfaces.Interface_EXISTING {
   246  		return true
   247  	}
   248  
   249  	// handle default MTU
   250  	if oldIntf.Type == interfaces.Interface_VRF_DEVICE {
   251  		if (oldIntf.Mtu == DefaultVrfDevLegacyMTU || oldIntf.Mtu == DefaultVrfDevMTU) && newIntf.Mtu == 0 {
   252  			// If VRF contains default or legacy default mtu with no intent to change it, left it as it is
   253  		} else if getInterfaceMTU(oldIntf) != getInterfaceMTU(newIntf) {
   254  			return false
   255  		}
   256  	} else if getInterfaceMTU(oldIntf) != getInterfaceMTU(newIntf) {
   257  		return false
   258  	}
   259  
   260  	// for link-only everything else is ignored
   261  	if oldIntf.LinkOnly {
   262  		return true
   263  	}
   264  
   265  	// compare MAC addresses case-insensitively (also handle unspecified MAC address)
   266  	if newIntf.PhysAddress != "" && !strings.EqualFold(oldIntf.PhysAddress, newIntf.PhysAddress) {
   267  		return false
   268  	}
   269  
   270  	// IP addresses and VRFs are derived out and therefore not compared here
   271  
   272  	return true
   273  }
   274  
   275  // MetadataFactory is a factory for index-map customized for Linux interfaces.
   276  func (d *InterfaceDescriptor) MetadataFactory() idxmap.NamedMappingRW {
   277  	return ifaceidx.NewLinuxIfIndex(logrus.DefaultLogger(), "linux-interface-index")
   278  }
   279  
   280  // Validate validates Linux interface configuration.
   281  func (d *InterfaceDescriptor) Validate(key string, linuxIf *interfaces.Interface) error {
   282  	// validate name (this should never happen, since key is derived from name)
   283  	if linuxIf.GetName() == "" {
   284  		return kvs.NewInvalidValueError(ErrInterfaceWithoutName, "name")
   285  	}
   286  
   287  	// validate namespace
   288  	if ns := linuxIf.GetNamespace(); ns != nil {
   289  		if ns.GetType() == namespace.NetNamespace_UNDEFINED || ns.GetReference() == "" {
   290  			return kvs.NewInvalidValueError(ErrNamespaceWithoutReference, "namespace")
   291  		}
   292  	}
   293  
   294  	// validate type
   295  	switch linuxIf.GetType() {
   296  	case interfaces.Interface_EXISTING:
   297  		if linuxIf.GetLink() != nil {
   298  			return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
   299  		}
   300  		// For now support only the same namespace as the agent.
   301  		if linuxIf.GetNamespace() != nil {
   302  			return kvs.NewInvalidValueError(ErrExistingWithNamespace, "namespace")
   303  		}
   304  		// Currently it is not supported to combine netalloc with existing IP.
   305  		if linuxIf.GetLinkOnly() {
   306  			for i, ipAddr := range linuxIf.GetIpAddresses() {
   307  				_, hasAllocDep := d.addrAlloc.GetAddressAllocDep(ipAddr, linuxIf.Name, "")
   308  				if hasAllocDep {
   309  					return kvs.NewInvalidValueError(ErrExistingIpWithNetalloc,
   310  						"type", "link_only", fmt.Sprintf("ip_addresses[%d]", i))
   311  				}
   312  			}
   313  		}
   314  	case interfaces.Interface_LOOPBACK:
   315  		if linuxIf.GetLink() != nil {
   316  			return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
   317  		}
   318  	case interfaces.Interface_TAP_TO_VPP:
   319  		if d.vppIfPlugin == nil {
   320  			return ErrTAPRequiresVPPIfPlugin
   321  		}
   322  	case interfaces.Interface_VRF_DEVICE:
   323  		if linuxIf.GetPhysAddress() != "" {
   324  			return kvs.NewInvalidValueError(ErrVRFDevWithMACAddr, "type", "phys_address")
   325  		}
   326  		if linuxIf.GetVrfMasterInterface() != "" {
   327  			return kvs.NewInvalidValueError(ErrVRFDevInsideVrf, "type", "vrf")
   328  		}
   329  	case interfaces.Interface_UNDEFINED:
   330  		return kvs.NewInvalidValueError(ErrInterfaceWithoutType, "type")
   331  	}
   332  
   333  	// validate link
   334  	switch linuxIf.GetLink().(type) {
   335  	case *interfaces.Interface_Tap:
   336  		if linuxIf.GetType() != interfaces.Interface_TAP_TO_VPP {
   337  			return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
   338  		}
   339  		if linuxIf.GetTap().GetVppTapIfName() == "" {
   340  			return kvs.NewInvalidValueError(ErrTAPWithoutVPPReference, "vpp_tap_if_name")
   341  		}
   342  	case *interfaces.Interface_Veth:
   343  		if linuxIf.GetType() != interfaces.Interface_VETH {
   344  			return kvs.NewInvalidValueError(ErrInterfaceReferenceMismatch, "link")
   345  		}
   346  		if linuxIf.GetVeth().GetPeerIfName() == "" {
   347  			return kvs.NewInvalidValueError(ErrVETHWithoutPeer, "peer_if_name")
   348  		}
   349  	}
   350  
   351  	return nil
   352  }
   353  
   354  // Create creates Linux interface.
   355  func (d *InterfaceDescriptor) Create(key string, linuxIf *interfaces.Interface) (metadata *ifaceidx.LinuxIfMetadata, err error) {
   356  	// move to the default namespace
   357  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   358  	revert1, err := d.nsPlugin.SwitchToNamespace(nsCtx, nil)
   359  	if err != nil {
   360  		d.log.Error(err)
   361  		return nil, err
   362  	}
   363  	defer revert1()
   364  
   365  	// create interface based on its type
   366  	switch linuxIf.Type {
   367  	case interfaces.Interface_VETH:
   368  		metadata, err = d.createVETH(nsCtx, key, linuxIf)
   369  	case interfaces.Interface_TAP_TO_VPP:
   370  		metadata, err = d.createTAPToVPP(nsCtx, key, linuxIf)
   371  	case interfaces.Interface_LOOPBACK:
   372  		metadata, err = d.createLoopback(nsCtx, linuxIf)
   373  	case interfaces.Interface_EXISTING:
   374  		// We expect that the interface already exists, therefore nothing needs to be done.
   375  		// We just get the metadata for the interface.
   376  		link, err := d.ifHandler.GetLinkByName(getHostIfName(linuxIf))
   377  		if err != nil {
   378  			d.log.Error(err)
   379  			return nil, err
   380  		}
   381  		metadata = &ifaceidx.LinuxIfMetadata{
   382  			Namespace:    linuxIf.GetNamespace(),
   383  			HostIfName:   link.Attrs().Name,
   384  			LinuxIfIndex: link.Attrs().Index,
   385  			VrfMasterIf:  linuxIf.VrfMasterInterface,
   386  		}
   387  		if vrfDev, isVrf := link.(*netlink.Vrf); isVrf {
   388  			metadata.VrfDevRT = vrfDev.Table
   389  		}
   390  		return metadata, nil
   391  	case interfaces.Interface_VRF_DEVICE:
   392  		metadata, err = d.createVRF(nsCtx, linuxIf)
   393  	case interfaces.Interface_DUMMY:
   394  		metadata, err = d.createDummyIf(nsCtx, linuxIf)
   395  	default:
   396  		return nil, ErrUnsupportedLinuxInterfaceType
   397  	}
   398  	if err != nil {
   399  		d.log.Errorf("creating %v interface failed: %+v", linuxIf.GetType(), err)
   400  		return nil, err
   401  	}
   402  
   403  	metadata.HostIfName = getHostIfName(linuxIf)
   404  	metadata.VrfMasterIf = linuxIf.VrfMasterInterface
   405  
   406  	// move to the namespace with the interface
   407  	revert2, err := d.nsPlugin.SwitchToNamespace(nsCtx, linuxIf.Namespace)
   408  	if err != nil {
   409  		d.log.Error(err)
   410  		return nil, err
   411  	}
   412  	defer revert2()
   413  
   414  	// set interface up
   415  	hostName := getHostIfName(linuxIf)
   416  	if linuxIf.Enabled {
   417  		err = d.ifHandler.SetInterfaceUp(hostName)
   418  		if nil != err {
   419  			err = errors.Errorf("failed to set linux interface %s up: %v", linuxIf.Name, err)
   420  			d.log.Error(err)
   421  			return nil, err
   422  		}
   423  	}
   424  
   425  	// set checksum offloading
   426  	if linuxIf.Type == interfaces.Interface_VETH {
   427  		rxOn := getRxChksmOffloading(linuxIf)
   428  		txOn := getTxChksmOffloading(linuxIf)
   429  		err = d.ifHandler.SetChecksumOffloading(hostName, rxOn, txOn)
   430  		if err != nil {
   431  			err = errors.Errorf("failed to configure checksum offloading (rx=%t,tx=%t) for linux interface %s: %v",
   432  				rxOn, txOn, linuxIf.Name, err)
   433  			d.log.Error(err)
   434  			return nil, err
   435  		}
   436  	}
   437  
   438  	// set interface MTU
   439  	if linuxIf.Mtu != 0 {
   440  		mtu := int(linuxIf.Mtu)
   441  		err = d.ifHandler.SetInterfaceMTU(hostName, mtu)
   442  		if err != nil {
   443  			err = errors.Errorf("failed to set MTU %d to linux interface %s: %v",
   444  				mtu, linuxIf.Name, err)
   445  			d.log.Error(err)
   446  			return nil, err
   447  		}
   448  	}
   449  
   450  	if linuxIf.GetLinkOnly() {
   451  		// addresses are configured externally
   452  		return metadata, nil
   453  	}
   454  
   455  	// set interface MAC address
   456  	if linuxIf.PhysAddress != "" {
   457  		err = d.ifHandler.SetInterfaceMac(hostName, linuxIf.PhysAddress)
   458  		if err != nil {
   459  			err = errors.Errorf("failed to set MAC address %s to linux interface %s: %v",
   460  				linuxIf.PhysAddress, linuxIf.Name, err)
   461  			d.log.Error(err)
   462  			return nil, err
   463  		}
   464  	}
   465  
   466  	return metadata, nil
   467  }
   468  
   469  // Delete removes Linux interface.
   470  func (d *InterfaceDescriptor) Delete(key string, linuxIf *interfaces.Interface, metadata *ifaceidx.LinuxIfMetadata) error {
   471  	// move to the namespace with the interface
   472  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   473  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, linuxIf.Namespace)
   474  	if err != nil {
   475  		d.log.Error("switch to namespace failed:", err)
   476  		return err
   477  	}
   478  	defer revert()
   479  
   480  	switch linuxIf.Type {
   481  	case interfaces.Interface_VETH:
   482  		return d.deleteVETH(nsCtx, key, linuxIf, metadata)
   483  	case interfaces.Interface_TAP_TO_VPP:
   484  		return d.deleteAutoTAP(nsCtx, key, linuxIf, metadata)
   485  	case interfaces.Interface_LOOPBACK:
   486  		return d.deleteLoopback(nsCtx, linuxIf)
   487  	case interfaces.Interface_EXISTING:
   488  		// We only need to unconfigure the interface.
   489  		// Nothing else needs to be done.
   490  		return nil
   491  	case interfaces.Interface_VRF_DEVICE:
   492  		return d.deleteVRF(linuxIf)
   493  	case interfaces.Interface_DUMMY:
   494  		return d.deleteDummyIf(linuxIf)
   495  	}
   496  
   497  	err = ErrUnsupportedLinuxInterfaceType
   498  	d.log.Error(err)
   499  	return err
   500  }
   501  
   502  // Update is able to change Type-unspecific attributes.
   503  func (d *InterfaceDescriptor) Update(key string, oldLinuxIf, newLinuxIf *interfaces.Interface, oldMetadata *ifaceidx.LinuxIfMetadata) (newMetadata *ifaceidx.LinuxIfMetadata, err error) {
   504  	oldHostName := getHostIfName(oldLinuxIf)
   505  	newHostName := getHostIfName(newLinuxIf)
   506  
   507  	if oldLinuxIf.Type == interfaces.Interface_EXISTING {
   508  		// with existing interface only metadata needs to be updated
   509  		link, err := d.ifHandler.GetLinkByName(newHostName)
   510  		if err != nil {
   511  			d.log.Error(err)
   512  			return nil, err
   513  		}
   514  		oldMetadata.LinuxIfIndex = link.Attrs().Index
   515  		oldMetadata.HostIfName = newHostName
   516  		oldMetadata.VrfMasterIf = newLinuxIf.VrfMasterInterface
   517  		return oldMetadata, nil
   518  	}
   519  
   520  	// move to the namespace with the interface
   521  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   522  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, oldLinuxIf.Namespace)
   523  	if err != nil {
   524  		d.log.Error(err)
   525  		return nil, err
   526  	}
   527  	defer revert()
   528  
   529  	// update host name
   530  	if oldHostName != newHostName {
   531  		if err := d.ifHandler.RenameInterface(oldHostName, newHostName); err != nil {
   532  			d.log.Error("renaming interface failed:", err)
   533  			return nil, err
   534  		}
   535  	}
   536  
   537  	// update admin status
   538  	if oldLinuxIf.Enabled != newLinuxIf.Enabled {
   539  		if newLinuxIf.Enabled {
   540  			err = d.ifHandler.SetInterfaceUp(newHostName)
   541  			if nil != err {
   542  				err = errors.Errorf("failed to set linux interface %s UP: %v", newHostName, err)
   543  				d.log.Error(err)
   544  				return nil, err
   545  			}
   546  		} else {
   547  			err = d.ifHandler.SetInterfaceDown(newHostName)
   548  			if nil != err {
   549  				err = errors.Errorf("failed to set linux interface %s DOWN: %v", newHostName, err)
   550  				d.log.Error(err)
   551  				return nil, err
   552  			}
   553  		}
   554  	}
   555  
   556  	// update MAC address
   557  	if !newLinuxIf.GetLinkOnly() {
   558  		if newLinuxIf.PhysAddress != "" && newLinuxIf.PhysAddress != oldLinuxIf.PhysAddress {
   559  			err := d.ifHandler.SetInterfaceMac(newHostName, newLinuxIf.PhysAddress)
   560  			if err != nil {
   561  				err = errors.Errorf("failed to reconfigure MAC address for linux interface %s: %v",
   562  					newLinuxIf.Name, err)
   563  				d.log.Error(err)
   564  				return nil, err
   565  			}
   566  		}
   567  	}
   568  
   569  	// MTU
   570  	if getInterfaceMTU(newLinuxIf) != getInterfaceMTU(oldLinuxIf) {
   571  		mtu := getInterfaceMTU(newLinuxIf)
   572  		err := d.ifHandler.SetInterfaceMTU(newHostName, mtu)
   573  		if nil != err {
   574  			err = errors.Errorf("failed to reconfigure MTU for the linux interface %s: %v",
   575  				newLinuxIf.Name, err)
   576  			d.log.Error(err)
   577  			return nil, err
   578  		}
   579  	}
   580  
   581  	// update checksum offloading
   582  	if newLinuxIf.Type == interfaces.Interface_VETH {
   583  		rxOn := getRxChksmOffloading(newLinuxIf)
   584  		txOn := getTxChksmOffloading(newLinuxIf)
   585  		if rxOn != getRxChksmOffloading(oldLinuxIf) || txOn != getTxChksmOffloading(oldLinuxIf) {
   586  			err = d.ifHandler.SetChecksumOffloading(newHostName, rxOn, txOn)
   587  			if err != nil {
   588  				err = errors.Errorf("failed to reconfigure checksum offloading (rx=%t,tx=%t) for linux interface %s: %v",
   589  					rxOn, txOn, newLinuxIf.Name, err)
   590  				d.log.Error(err)
   591  				return nil, err
   592  			}
   593  		}
   594  	}
   595  
   596  	// update metadata
   597  	oldMetadata.HostIfName = newHostName
   598  	oldMetadata.VrfMasterIf = newLinuxIf.VrfMasterInterface
   599  	return oldMetadata, nil
   600  }
   601  
   602  // UpdateWithRecreate returns true if Type or Type-specific attributes are different.
   603  func (d *InterfaceDescriptor) UpdateWithRecreate(key string, oldLinuxIf, newLinuxIf *interfaces.Interface, metadata *ifaceidx.LinuxIfMetadata) bool {
   604  	if oldLinuxIf.Type != newLinuxIf.Type {
   605  		return true
   606  	}
   607  	if oldLinuxIf.LinkOnly != newLinuxIf.LinkOnly {
   608  		return true
   609  	}
   610  	if !proto.Equal(oldLinuxIf.Namespace, newLinuxIf.Namespace) {
   611  		// anything attached to the interface (ARPs, routes, ...) will be re-created as well
   612  		return true
   613  	}
   614  	switch oldLinuxIf.Type {
   615  	case interfaces.Interface_VETH:
   616  		return oldLinuxIf.GetVeth().GetPeerIfName() != newLinuxIf.GetVeth().GetPeerIfName()
   617  	case interfaces.Interface_TAP_TO_VPP:
   618  		return oldLinuxIf.GetTap().GetVppTapIfName() != newLinuxIf.GetTap().GetVppTapIfName()
   619  	case interfaces.Interface_VRF_DEVICE:
   620  		return oldLinuxIf.GetVrfDev().GetRoutingTable() != newLinuxIf.GetVrfDev().GetRoutingTable()
   621  	}
   622  	return false
   623  }
   624  
   625  // Dependencies lists dependencies for a Linux interface.
   626  func (d *InterfaceDescriptor) Dependencies(key string, linuxIf *interfaces.Interface) []kvs.Dependency {
   627  	var dependencies []kvs.Dependency
   628  
   629  	// EXISTING depends on a referenced Linux interface in the default namespace
   630  	if linuxIf.Type == interfaces.Interface_EXISTING {
   631  		dependencies = append(dependencies, kvs.Dependency{
   632  			Label: existingHostInterfaceDep,
   633  			Key:   interfaces.InterfaceHostNameKey(getHostIfName(linuxIf)),
   634  		})
   635  	}
   636  	if linuxIf.Type == interfaces.Interface_TAP_TO_VPP {
   637  		// dependency on VPP TAP
   638  		dependencies = append(dependencies, kvs.Dependency{
   639  			Label: tapInterfaceDep,
   640  			Key:   vpp_intf.InterfaceKey(linuxIf.GetTap().GetVppTapIfName()),
   641  		})
   642  	}
   643  
   644  	// circular dependency between VETH ends
   645  	if linuxIf.Type == interfaces.Interface_VETH {
   646  		peerName := linuxIf.GetVeth().GetPeerIfName()
   647  		if peerName != "" {
   648  			dependencies = append(dependencies, kvs.Dependency{
   649  				Label: vethPeerDep,
   650  				Key:   interfaces.InterfaceKey(peerName),
   651  			})
   652  		}
   653  	}
   654  
   655  	if linuxIf.GetNamespace().GetType() == namespace.NetNamespace_MICROSERVICE {
   656  		dependencies = append(dependencies, kvs.Dependency{
   657  			Label: microserviceDep,
   658  			Key:   namespace.MicroserviceKey(linuxIf.Namespace.Reference),
   659  		})
   660  	}
   661  
   662  	return dependencies
   663  }
   664  
   665  // DerivedValues derives:
   666  //   - one empty value to represent interface state
   667  //   - one empty value to represent assignment of the interface to a (non-default) VRF
   668  //   - one empty value for every IP address assigned to the interface.
   669  func (d *InterfaceDescriptor) DerivedValues(key string, linuxIf *interfaces.Interface) (derValues []kvs.KeyValuePair) {
   670  	// interface state
   671  	derValues = append(derValues, kvs.KeyValuePair{
   672  		Key:   interfaces.InterfaceStateKey(linuxIf.Name, linuxIf.Enabled),
   673  		Value: &emptypb.Empty{},
   674  	})
   675  	if linuxIf.GetVrfMasterInterface() != "" {
   676  		derValues = append(derValues, kvs.KeyValuePair{
   677  			Key: interfaces.InterfaceVrfKey(linuxIf.Name, linuxIf.VrfMasterInterface),
   678  			// only fields accessed by VRF descriptor are included in the derived value
   679  			// FIXME: we can get rid of this hack once we add Context to descriptor methods
   680  			Value: &interfaces.Interface{
   681  				Name:               linuxIf.Name,
   682  				Type:               linuxIf.Type,
   683  				HostIfName:         linuxIf.HostIfName,
   684  				VrfMasterInterface: linuxIf.VrfMasterInterface,
   685  			},
   686  		})
   687  	}
   688  	if !linuxIf.GetLinkOnly() || linuxIf.GetType() == interfaces.Interface_EXISTING {
   689  		var ipSource netalloc_api.IPAddressSource
   690  		if linuxIf.GetLinkOnly() { // interface type = EXISTING
   691  			ipSource = netalloc_api.IPAddressSource_EXISTING
   692  		} else {
   693  			ipSource = netalloc_api.IPAddressSource_STATIC
   694  		}
   695  		// IP addresses
   696  		for _, ipAddr := range linuxIf.IpAddresses {
   697  			derValues = append(derValues, kvs.KeyValuePair{
   698  				Key: interfaces.InterfaceAddressKey(
   699  					linuxIf.Name, ipAddr, ipSource),
   700  				// only fields accessed by address descriptor are included in the derived value
   701  				// FIXME: we can get rid of this hack once we add Context to descriptor methods
   702  				Value: &interfaces.Interface{
   703  					Name:               linuxIf.Name,
   704  					Type:               linuxIf.Type,
   705  					HostIfName:         linuxIf.HostIfName,
   706  					VrfMasterInterface: linuxIf.VrfMasterInterface,
   707  					IpAddresses:        []string{ipAddr},
   708  				},
   709  			})
   710  		}
   711  	}
   712  	return derValues
   713  }
   714  
   715  func (d *InterfaceDescriptor) IsRetriableFailure(err error) bool {
   716  	return err != ErrLoopbackAlreadyConfigured
   717  }
   718  
   719  // Retrieve returns all Linux interfaces managed by this agent, attached to the default namespace
   720  // or to one of the configured non-default namespaces.
   721  func (d *InterfaceDescriptor) Retrieve(correlate []adapter.InterfaceKVWithMetadata) ([]adapter.InterfaceKVWithMetadata, error) {
   722  	var retrieved []adapter.InterfaceKVWithMetadata
   723  	nsList := []*namespace.NetNamespace{nil}              // nil = default namespace, which always should be listed for interfaces
   724  	ifCfg := make(map[string]*interfaces.Interface)       // interface logical name -> interface config (as expected by correlate)
   725  	expExisting := make(map[string]*interfaces.Interface) // EXISTING interface host name -> expected interface config
   726  
   727  	// process interfaces for correlation to get:
   728  	//  - the set of namespaces to list for interfaces
   729  	//  - mapping between interface name and the configuration for correlation
   730  	// beware: the same namespace can have multiple different references (e.g. integration of Contiv with SFC)
   731  	for _, kv := range correlate {
   732  		nsListed := false
   733  		for _, ns := range nsList {
   734  			if proto.Equal(ns, kv.Value.Namespace) {
   735  				nsListed = true
   736  				break
   737  			}
   738  		}
   739  		if !nsListed {
   740  			nsList = append(nsList, kv.Value.Namespace)
   741  		}
   742  		ifCfg[kv.Value.Name] = kv.Value
   743  		if kv.Value.Type == interfaces.Interface_EXISTING {
   744  			expExisting[getHostIfName(kv.Value)] = kv.Value
   745  		}
   746  	}
   747  
   748  	// retrieve EXISTING interfaces first
   749  	existingIfaces, err := d.retrieveExistingInterfaces(expExisting)
   750  	if err != nil {
   751  		return nil, err
   752  	}
   753  	for _, kv := range existingIfaces {
   754  		retrieved = append(retrieved, kv)
   755  	}
   756  
   757  	// Obtain interface details - all interfaces with metadata
   758  	ifDetails, err := d.ifHandler.DumpInterfacesFromNamespaces(nsList)
   759  	if err != nil {
   760  		return nil, errors.Errorf("Failed to retrieve linux interfaces: %v", err)
   761  	}
   762  	// interface logical name -> interface data
   763  	ifaces := make(map[string]adapter.InterfaceKVWithMetadata)
   764  	// already retrieved interfaces by their Linux indexes
   765  	indexes := make(map[int]struct{})
   766  
   767  	for _, ifDetail := range ifDetails {
   768  		// Transform linux interface details to the type-safe value with metadata
   769  		var vrfDevRT uint32
   770  		if ifDetail.Interface.Type == interfaces.Interface_VRF_DEVICE {
   771  			vrfDevRT = ifDetail.Interface.GetVrfDev().GetRoutingTable()
   772  		}
   773  		// Handle reference to existing (i.e. not managed) VRF from managed interface
   774  		if ifDetail.Interface.Namespace == nil {
   775  			if vrf, existingVrf := existingIfaces[ifDetail.Meta.MasterIndex]; existingVrf {
   776  				ifDetail.Interface.VrfMasterInterface = vrf.Value.Name
   777  			}
   778  		}
   779  		kv := adapter.InterfaceKVWithMetadata{
   780  			Origin: kvs.FromNB,
   781  			Value:  ifDetail.Interface,
   782  			Metadata: &ifaceidx.LinuxIfMetadata{
   783  				LinuxIfIndex: ifDetail.Meta.LinuxIfIndex,
   784  				Namespace:    ifDetail.Interface.GetNamespace(),
   785  				VPPTapName:   ifDetail.Interface.GetTap().GetVppTapIfName(),
   786  				HostIfName:   ifDetail.Interface.HostIfName,
   787  				VrfMasterIf:  ifDetail.Interface.VrfMasterInterface,
   788  				VrfDevRT:     vrfDevRT,
   789  			},
   790  			Key: interfaces.InterfaceKey(ifDetail.Interface.Name),
   791  		}
   792  
   793  		// skip if this interface was already retrieved and this is not the expected
   794  		// namespace from correlation - remember, the same namespace may have
   795  		// multiple different references
   796  		var rewrite bool
   797  		if _, alreadyRetrieved := indexes[kv.Metadata.LinuxIfIndex]; alreadyRetrieved {
   798  			if expCfg, hasExpCfg := ifCfg[kv.Value.Name]; hasExpCfg {
   799  				if proto.Equal(expCfg.Namespace, kv.Value.Namespace) {
   800  					rewrite = true
   801  				}
   802  			}
   803  			if !rewrite {
   804  				continue
   805  			}
   806  		}
   807  		indexes[kv.Metadata.LinuxIfIndex] = struct{}{}
   808  
   809  		// test for duplicity of VETH logical names
   810  		if kv.Value.Type == interfaces.Interface_VETH {
   811  			if _, duplicate := ifaces[kv.Value.Name]; duplicate && !rewrite {
   812  				// add suffix to the duplicate to make its logical name unique
   813  				// (and not configured by NB so that it will get removed)
   814  				dupIndex := 1
   815  				for intf2 := range ifaces {
   816  					if strings.HasPrefix(intf2, kv.Value.Name+vethDuplicateSuffix) {
   817  						dupIndex++
   818  					}
   819  				}
   820  				kv.Value.Name = kv.Value.Name + vethDuplicateSuffix + strconv.Itoa(dupIndex)
   821  				kv.Key = interfaces.InterfaceKey(kv.Value.Name)
   822  			}
   823  		}
   824  		// correlate link_only attribute
   825  		if expCfg, hasExpCfg := ifCfg[kv.Value.Name]; hasExpCfg {
   826  			kv.Value.LinkOnly = expCfg.GetLinkOnly()
   827  		}
   828  		ifaces[kv.Value.Name] = kv
   829  	}
   830  
   831  	// collect VETHs with duplicate logical names
   832  	for ifName, kv := range ifaces {
   833  		if kv.Value.Type == interfaces.Interface_VETH {
   834  			isDuplicate := strings.Contains(ifName, vethDuplicateSuffix)
   835  			// first interface retrieved from the set of duplicate VETHs still
   836  			// does not have the vethDuplicateSuffix appended to the name
   837  			_, hasDuplicate := ifaces[ifName+vethDuplicateSuffix+"1"]
   838  			if hasDuplicate {
   839  				kv.Value.Name = ifName + vethDuplicateSuffix + "0"
   840  				kv.Key = interfaces.InterfaceKey(kv.Value.Name)
   841  			}
   842  			if isDuplicate || hasDuplicate {
   843  				// clear peer reference so that Delete removes the VETH-end
   844  				// as standalone
   845  				kv.Value.Link = &interfaces.Interface_Veth{}
   846  				delete(ifaces, ifName)
   847  				retrieved = append(retrieved, kv)
   848  			}
   849  		}
   850  	}
   851  
   852  	// next collect VETHs with missing peer
   853  	for ifName, kv := range ifaces {
   854  		if kv.Value.Type == interfaces.Interface_VETH {
   855  			peer, known := ifaces[kv.Value.GetVeth().GetPeerIfName()]
   856  			if !known || peer.Value.GetVeth().GetPeerIfName() != kv.Value.Name {
   857  				// append vethMissingPeerSuffix to the logical name so that VETH
   858  				// will get removed during resync
   859  				kv.Value.Name = ifName + vethMissingPeerSuffix
   860  				kv.Key = interfaces.InterfaceKey(kv.Value.Name)
   861  				// clear peer reference so that Delete removes the VETH-end
   862  				// as standalone
   863  				kv.Value.Link = &interfaces.Interface_Veth{}
   864  				delete(ifaces, ifName)
   865  				retrieved = append(retrieved, kv)
   866  			}
   867  		}
   868  	}
   869  
   870  	// collect AUTO-TAPs and valid VETHs
   871  	for _, kv := range ifaces {
   872  		retrieved = append(retrieved, kv)
   873  	}
   874  
   875  	// correlate IP addresses with netalloc references from the expected config
   876  	for _, kv := range retrieved {
   877  		if expCfg, hasExpCfg := ifCfg[kv.Value.Name]; hasExpCfg {
   878  			kv.Value.IpAddresses = d.addrAlloc.CorrelateRetrievedIPs(
   879  				expCfg.IpAddresses, kv.Value.IpAddresses,
   880  				kv.Value.Name, netalloc_api.IPAddressForm_ADDR_WITH_MASK)
   881  		}
   882  	}
   883  
   884  	return retrieved, nil
   885  }
   886  
   887  // retrieveExistingInterfaces retrieves already created Linux interface - i.e. not created
   888  // by this agent = type EXISTING.
   889  func (d *InterfaceDescriptor) retrieveExistingInterfaces(expected map[string]*interfaces.Interface) (
   890  	existing map[int]adapter.InterfaceKVWithMetadata, err error) {
   891  	existing = make(map[int]adapter.InterfaceKVWithMetadata)
   892  
   893  	// get all links in the default namespace
   894  	links, err := d.ifHandler.GetLinkList()
   895  	if err != nil {
   896  		d.log.Error("Failed to get link list:", err)
   897  		return nil, err
   898  	}
   899  	for _, link := range links {
   900  		expCfg, isExp := expected[link.Attrs().Name]
   901  		if !isExp {
   902  			// do not touch existing interfaces which are not configured by the agent
   903  			continue
   904  		}
   905  		iface := &interfaces.Interface{
   906  			Name:        expCfg.GetName(),
   907  			Type:        interfaces.Interface_EXISTING,
   908  			HostIfName:  link.Attrs().Name,
   909  			PhysAddress: link.Attrs().HardwareAddr.String(),
   910  			Mtu:         uint32(link.Attrs().MTU),
   911  			LinkOnly:    expCfg.LinkOnly,
   912  		}
   913  
   914  		// retrieve VRF config
   915  		var vrfDevRT uint32
   916  		if vrfDev, isVrf := link.(*netlink.Vrf); isVrf {
   917  			vrfDevRT = vrfDev.Table
   918  		}
   919  		master := link.Attrs().MasterIndex
   920  		if master != 0 {
   921  			if masterLink, err := d.ifHandler.GetLinkByIndex(master); err == nil {
   922  				if vrfExpCfg, isVrfExp := expected[masterLink.Attrs().Name]; isVrfExp {
   923  					iface.VrfMasterInterface = vrfExpCfg.GetName()
   924  				}
   925  			}
   926  		}
   927  
   928  		// retrieve addresses, MTU, etc.
   929  		d.retrieveLinkDetails(link, iface, nil)
   930  
   931  		// build key-value pair for the retrieved interface
   932  		existing[link.Attrs().Index] = adapter.InterfaceKVWithMetadata{
   933  			Key:    models.Key(iface),
   934  			Value:  iface,
   935  			Origin: kvs.FromNB,
   936  			Metadata: &ifaceidx.LinuxIfMetadata{
   937  				LinuxIfIndex: link.Attrs().Index,
   938  				HostIfName:   link.Attrs().Name,
   939  				VrfMasterIf:  iface.VrfMasterInterface,
   940  				VrfDevRT:     vrfDevRT,
   941  			},
   942  		}
   943  	}
   944  	return existing, nil
   945  }
   946  
   947  // retrieveLinkDetails retrieves link details common to all interface types (e.g. addresses).
   948  func (d *InterfaceDescriptor) retrieveLinkDetails(link netlink.Link, iface *interfaces.Interface, nsRef *namespace.NetNamespace) {
   949  	var err error
   950  	// read interface status
   951  	iface.Enabled, err = d.ifHandler.IsInterfaceUp(link.Attrs().Name)
   952  	if err != nil {
   953  		d.log.WithFields(logging.Fields{
   954  			"if-host-name": link.Attrs().Name,
   955  			"namespace":    nsRef,
   956  		}).Warn("Failed to read interface status:", err)
   957  	}
   958  
   959  	// read assigned IP addresses
   960  	addressList, err := d.ifHandler.GetAddressList(link.Attrs().Name)
   961  	if err != nil {
   962  		d.log.WithFields(logging.Fields{
   963  			"if-host-name": link.Attrs().Name,
   964  			"namespace":    nsRef,
   965  		}).Warn("Failed to read address list:", err)
   966  	}
   967  	for _, address := range addressList {
   968  		if address.Scope == unix.RT_SCOPE_LINK {
   969  			// ignore link-local IPv6 addresses
   970  			continue
   971  		}
   972  		mask, _ := address.Mask.Size()
   973  		addrStr := address.IP.String() + "/" + strconv.Itoa(mask)
   974  		iface.IpAddresses = append(iface.IpAddresses, addrStr)
   975  	}
   976  
   977  	// read checksum offloading
   978  	if iface.Type == interfaces.Interface_VETH {
   979  		rxOn, txOn, err := d.ifHandler.GetChecksumOffloading(link.Attrs().Name)
   980  		if err != nil {
   981  			d.log.WithFields(logging.Fields{
   982  				"if-host-name": link.Attrs().Name,
   983  				"namespace":    nsRef,
   984  			}).Warn("Failed to read checksum offloading:", err)
   985  		} else {
   986  			if !rxOn {
   987  				iface.GetVeth().RxChecksumOffloading = interfaces.VethLink_CHKSM_OFFLOAD_DISABLED
   988  			}
   989  			if !txOn {
   990  				iface.GetVeth().TxChecksumOffloading = interfaces.VethLink_CHKSM_OFFLOAD_DISABLED
   991  			}
   992  		}
   993  	}
   994  }
   995  
   996  // setInterfaceNamespace moves linux interface from the current to the desired
   997  // namespace.
   998  func (d *InterfaceDescriptor) setInterfaceNamespace(ctx nslinuxcalls.NamespaceMgmtCtx, ifName string, namespace *namespace.NetNamespace) error {
   999  	// Get namespace handle.
  1000  	ns, err := d.nsPlugin.GetNamespaceHandle(ctx, namespace)
  1001  	if err != nil {
  1002  		return err
  1003  	}
  1004  	defer ns.Close()
  1005  
  1006  	// Get the interface link handle.
  1007  	link, err := d.ifHandler.GetLinkByName(ifName)
  1008  	if err != nil {
  1009  		return errors.Errorf("failed to get link for interface %s: %v", ifName, err)
  1010  	}
  1011  
  1012  	// When interface moves from one namespace to another, it loses all its IP addresses, admin status
  1013  	// and MTU configuration -- we need to remember the interface configuration before the move
  1014  	// and re-configure the interface in the new namespace.
  1015  	addresses, isIPv6, err := d.getInterfaceAddresses(link.Attrs().Name)
  1016  	if err != nil {
  1017  		return errors.Errorf("failed to get IP address list from interface %s: %v", link.Attrs().Name, err)
  1018  	}
  1019  	enabled, err := d.ifHandler.IsInterfaceUp(ifName)
  1020  	if err != nil {
  1021  		return errors.Errorf("failed to get admin status of the interface %s: %v", link.Attrs().Name, err)
  1022  	}
  1023  
  1024  	// Move the interface into the namespace.
  1025  	if err := d.ifHandler.SetLinkNamespace(link, ns); err != nil {
  1026  		return errors.Errorf("failed to set interface %s file descriptor: %v", link.Attrs().Name, err)
  1027  	}
  1028  
  1029  	// Re-configure interface in its new namespace
  1030  	revertNs, err := d.nsPlugin.SwitchToNamespace(ctx, namespace)
  1031  	if err != nil {
  1032  		return errors.Errorf("failed to switch namespace: %v", err)
  1033  	}
  1034  	defer revertNs()
  1035  
  1036  	if enabled {
  1037  		// Re-enable interface
  1038  		err = d.ifHandler.SetInterfaceUp(ifName)
  1039  		if nil != err {
  1040  			return errors.Errorf("failed to re-enable Linux interface `%s`: %v", ifName, err)
  1041  		}
  1042  	}
  1043  
  1044  	// Re-add IP addresses
  1045  	for _, address := range addresses {
  1046  		// Skip IPv6 link local address if there is no other IPv6 address
  1047  		if !isIPv6 && address.IP.IsLinkLocalUnicast() {
  1048  			continue
  1049  		}
  1050  		if err := d.ifHandler.AddInterfaceIP(ifName, address); err != nil {
  1051  			if err.Error() == "file exists" {
  1052  				continue
  1053  			}
  1054  			return errors.Errorf("failed to re-assign IP address to a Linux interface `%s`: %v", ifName, err)
  1055  		}
  1056  	}
  1057  
  1058  	// Revert back the MTU config
  1059  	err = d.ifHandler.SetInterfaceMTU(ifName, link.Attrs().MTU)
  1060  	if nil != err {
  1061  		return errors.Errorf("failed to re-assign MTU of a Linux interface `%s`: %v", ifName, err)
  1062  	}
  1063  
  1064  	return nil
  1065  }
  1066  
  1067  // getInterfaceAddresses returns a list of IP addresses assigned to the given linux interface.
  1068  // <hasIPv6> is returned as true if a non link-local IPv6 address is among them.
  1069  func (d *InterfaceDescriptor) getInterfaceAddresses(ifName string) (addresses []*net.IPNet, hasIPv6 bool, err error) {
  1070  	// get all assigned IP addresses
  1071  	ipAddrs, err := d.ifHandler.GetAddressList(ifName)
  1072  	if err != nil {
  1073  		return nil, false, err
  1074  	}
  1075  
  1076  	// iterate over IP addresses to see if there is IPv6 among them
  1077  	for _, ipAddr := range ipAddrs {
  1078  		if ipAddr.IP.To4() == nil && !ipAddr.IP.IsLinkLocalUnicast() {
  1079  			// IP address is version 6 and not a link local address
  1080  			hasIPv6 = true
  1081  		}
  1082  		addresses = append(addresses, ipAddr.IPNet)
  1083  	}
  1084  	return addresses, hasIPv6, nil
  1085  }
  1086  
  1087  // getHostIfName returns the interface host name.
  1088  func getHostIfName(linuxIf *interfaces.Interface) string {
  1089  	if linuxIf.Type == interfaces.Interface_LOOPBACK {
  1090  		return iflinuxcalls.DefaultLoopbackName
  1091  	}
  1092  	hostIfName := linuxIf.HostIfName
  1093  	if hostIfName == "" {
  1094  		hostIfName = linuxIf.Name
  1095  	}
  1096  	return hostIfName
  1097  }
  1098  
  1099  // getInterfaceMTU returns the interface MTU.
  1100  func getInterfaceMTU(linuxIntf *interfaces.Interface) int {
  1101  	mtu := int(linuxIntf.Mtu)
  1102  	if mtu == 0 {
  1103  		switch linuxIntf.Type {
  1104  		case interfaces.Interface_LOOPBACK:
  1105  			return defaultLoopbackMTU
  1106  		case interfaces.Interface_VRF_DEVICE:
  1107  			return DefaultVrfDevMTU
  1108  		}
  1109  		return defaultEthernetMTU
  1110  	}
  1111  	return mtu
  1112  }
  1113  
  1114  func getRxChksmOffloading(linuxIntf *interfaces.Interface) (rxOn bool) {
  1115  	return isChksmOffloadingOn(linuxIntf.GetVeth().GetRxChecksumOffloading())
  1116  }
  1117  
  1118  func getTxChksmOffloading(linuxIntf *interfaces.Interface) (txOn bool) {
  1119  	return isChksmOffloadingOn(linuxIntf.GetVeth().GetTxChecksumOffloading())
  1120  }
  1121  
  1122  func isChksmOffloadingOn(offloading interfaces.VethLink_ChecksumOffloading) bool {
  1123  	switch offloading {
  1124  	case interfaces.VethLink_CHKSM_OFFLOAD_DEFAULT:
  1125  		return true // enabled by default
  1126  	case interfaces.VethLink_CHKSM_OFFLOAD_ENABLED:
  1127  		return true
  1128  	case interfaces.VethLink_CHKSM_OFFLOAD_DISABLED:
  1129  		return false
  1130  	}
  1131  	return true
  1132  }
  1133  
  1134  func getSysctl(name string) (string, error) {
  1135  	fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
  1136  	fullName = filepath.Clean(fullName)
  1137  	data, err := os.ReadFile(fullName)
  1138  	if err != nil {
  1139  		return "", err
  1140  	}
  1141  	return string(data[:len(data)-1]), nil
  1142  }
  1143  
  1144  func setSysctl(name, value string) (string, error) {
  1145  	fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
  1146  	fullName = filepath.Clean(fullName)
  1147  	if err := os.WriteFile(fullName, []byte(value), 0644); err != nil {
  1148  		return "", err
  1149  	}
  1150  	return getSysctl(name)
  1151  }