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

     1  // Copyright (c) 2019 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  	"syscall"
    20  
    21  	"github.com/pkg/errors"
    22  	"go.ligato.io/cn-infra/v2/logging"
    23  
    24  	kvs "go.ligato.io/vpp-agent/v3/plugins/kvscheduler/api"
    25  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/descriptor/adapter"
    26  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
    27  	iflinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
    28  	"go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin"
    29  	nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
    30  	"go.ligato.io/vpp-agent/v3/plugins/netalloc"
    31  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    32  	netalloc_api "go.ligato.io/vpp-agent/v3/proto/ligato/netalloc"
    33  )
    34  
    35  const (
    36  	// InterfaceAddressDescriptorName is the name of the descriptor for assigning
    37  	// IP addresses to Linux interfaces.
    38  	InterfaceAddressDescriptorName = "linux-interface-address"
    39  
    40  	// DisableIPv6SysctlTemplate is used to enable ipv6 via sysctl.
    41  	DisableIPv6SysctlTemplate = "net.ipv6.conf.%s.disable_ipv6"
    42  
    43  	// dependency labels
    44  	interfaceVrfDep  = "interface-assigned-to-vrf"
    45  	interfaceAddrDep = "address-assigned-to-interface"
    46  )
    47  
    48  // InterfaceAddressDescriptor (un)assigns IP address to/from Linux interface.
    49  type InterfaceAddressDescriptor struct {
    50  	log       logging.Logger
    51  	ifHandler iflinuxcalls.NetlinkAPI
    52  	nsPlugin  nsplugin.API
    53  	addrAlloc netalloc.AddressAllocator
    54  	intfIndex ifaceidx.LinuxIfMetadataIndex
    55  }
    56  
    57  // NewInterfaceAddressDescriptor creates a new instance of InterfaceAddressDescriptor.
    58  func NewInterfaceAddressDescriptor(nsPlugin nsplugin.API, addrAlloc netalloc.AddressAllocator,
    59  	ifHandler iflinuxcalls.NetlinkAPI, log logging.PluginLogger) (descr *kvs.KVDescriptor, ctx *InterfaceAddressDescriptor) {
    60  
    61  	ctx = &InterfaceAddressDescriptor{
    62  		ifHandler: ifHandler,
    63  		nsPlugin:  nsPlugin,
    64  		addrAlloc: addrAlloc,
    65  		log:       log.NewLogger("interface-address-descriptor"),
    66  	}
    67  	typedDescr := &adapter.InterfaceAddressDescriptor{
    68  		Name:        InterfaceAddressDescriptorName,
    69  		KeySelector: ctx.IsInterfaceAddressKey,
    70  		ValueComparator: func(_ string, _, _ *interfaces.Interface) bool {
    71  			// compare addresses based on keys, not values that contain also other interface attributes
    72  			// needed by the descriptor
    73  			// FIXME: we can get rid of this hack once we add Context to descriptor methods
    74  			return true
    75  		},
    76  		Validate:     ctx.Validate,
    77  		Create:       ctx.Create,
    78  		Delete:       ctx.Delete,
    79  		Dependencies: ctx.Dependencies,
    80  	}
    81  	descr = adapter.NewInterfaceAddressDescriptor(typedDescr)
    82  	return
    83  }
    84  
    85  // SetInterfaceIndex should be used to provide interface index immediately after
    86  // the descriptor registration.
    87  func (d *InterfaceAddressDescriptor) SetInterfaceIndex(intfIndex ifaceidx.LinuxIfMetadataIndex) {
    88  	d.intfIndex = intfIndex
    89  }
    90  
    91  // IsInterfaceAddressKey returns true if the key represents assignment of an IP address
    92  // to a Linux interface (that needs to be applied or is expected to exist).
    93  // KVs representing addresses already allocated from netalloc plugin are excluded.
    94  func (d *InterfaceAddressDescriptor) IsInterfaceAddressKey(key string) bool {
    95  	_, _, source, _, isAddrKey := interfaces.ParseInterfaceAddressKey(key)
    96  	return isAddrKey &&
    97  		(source == netalloc_api.IPAddressSource_STATIC ||
    98  			source == netalloc_api.IPAddressSource_ALLOC_REF ||
    99  			source == netalloc_api.IPAddressSource_EXISTING)
   100  }
   101  
   102  // Validate validates IP address to be assigned to an interface.
   103  func (d *InterfaceAddressDescriptor) Validate(key string, _ *interfaces.Interface) (err error) {
   104  	iface, addr, _, invalidKey, _ := interfaces.ParseInterfaceAddressKey(key)
   105  	if invalidKey {
   106  		return errors.New("invalid key")
   107  	}
   108  
   109  	return d.addrAlloc.ValidateIPAddress(addr, iface, "ip_addresses", netalloc.GwRefUnexpected)
   110  }
   111  
   112  // Create assigns IP address to an interface.
   113  func (d *InterfaceAddressDescriptor) Create(key string, _ *interfaces.Interface) (metadata interface{}, err error) {
   114  	iface, addr, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
   115  	if source == netalloc_api.IPAddressSource_EXISTING {
   116  		// already exists, nothing to do
   117  		return nil, nil
   118  	}
   119  
   120  	ifMeta, found := d.intfIndex.LookupByName(iface)
   121  	if !found {
   122  		err = errors.Errorf("failed to find interface %s", iface)
   123  		d.log.Error(err)
   124  		return nil, err
   125  	}
   126  
   127  	ipAddr, err := d.addrAlloc.GetOrParseIPAddress(addr, iface, netalloc_api.IPAddressForm_ADDR_WITH_MASK)
   128  	if err != nil {
   129  		d.log.Error(err)
   130  		return nil, err
   131  	}
   132  
   133  	// switch to the namespace with the interface
   134  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   135  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace)
   136  	if err != nil {
   137  		d.log.Error(err)
   138  		return nil, err
   139  	}
   140  	defer revert()
   141  
   142  	if ipAddr.IP.To4() == nil {
   143  		// Enable IPv6 for loopback "lo" and the interface being configured
   144  		for _, iface := range [2]string{"lo", ifMeta.HostIfName} {
   145  			ipv6SysctlValueName := fmt.Sprintf(DisableIPv6SysctlTemplate, iface)
   146  
   147  			// Read current sysctl value
   148  			value, err := getSysctl(ipv6SysctlValueName)
   149  			if err != nil || value == "0" {
   150  				if err != nil {
   151  					d.log.Warnf("could not read sysctl value for %v: %v",
   152  						ifMeta.HostIfName, err)
   153  				}
   154  				continue
   155  			}
   156  
   157  			// Write sysctl to enable IPv6
   158  			_, err = setSysctl(ipv6SysctlValueName, "0")
   159  			if err != nil {
   160  				return nil, fmt.Errorf("failed to enable IPv6 (%s=%s): %v",
   161  					ipv6SysctlValueName, value, err)
   162  			}
   163  		}
   164  	}
   165  
   166  	err = d.ifHandler.AddInterfaceIP(ifMeta.HostIfName, ipAddr)
   167  
   168  	// an attempt to add already assigned IP is not considered as error
   169  	if errors.Cause(err) == syscall.EEXIST {
   170  		err = nil
   171  	}
   172  	return nil, err
   173  }
   174  
   175  // Delete unassigns IP address from an interface.
   176  func (d *InterfaceAddressDescriptor) Delete(key string, _ *interfaces.Interface, metadata interface{}) (err error) {
   177  	iface, addr, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
   178  	if source == netalloc_api.IPAddressSource_EXISTING {
   179  		// already existed before Create, nothing to do
   180  		return nil
   181  	}
   182  
   183  	ifMeta, found := d.intfIndex.LookupByName(iface)
   184  	if !found {
   185  		err = errors.Errorf("failed to find interface %s", iface)
   186  		d.log.Error(err)
   187  		return err
   188  	}
   189  
   190  	ipAddr, err := d.addrAlloc.GetOrParseIPAddress(addr, iface, netalloc_api.IPAddressForm_ADDR_WITH_MASK)
   191  	if err != nil {
   192  		d.log.Error(err)
   193  		return err
   194  	}
   195  
   196  	// switch to the namespace with the interface
   197  	nsCtx := nslinuxcalls.NewNamespaceMgmtCtx()
   198  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, ifMeta.Namespace)
   199  	if err != nil {
   200  		if _, ok := err.(*nsplugin.UnavailableMicroserviceErr); ok {
   201  			// Assume that the delete was called by scheduler because the namespace
   202  			// was removed. Do not return error in this case.
   203  			d.log.Debugf("Interface %s IP address %s assumed deleted, required namespace %+v does not exist",
   204  				iface, ipAddr, ifMeta.Namespace)
   205  			return nil
   206  		}
   207  		d.log.Error(err)
   208  		return err
   209  	}
   210  	defer revert()
   211  
   212  	err = d.ifHandler.DelInterfaceIP(ifMeta.HostIfName, ipAddr)
   213  	return err
   214  }
   215  
   216  // Dependencies mentions (non-default) VRF and a potential allocation of the IP address as dependencies.
   217  func (d *InterfaceAddressDescriptor) Dependencies(key string, iface *interfaces.Interface) (deps []kvs.Dependency) {
   218  	ifaceName, addr, source, _, _ := interfaces.ParseInterfaceAddressKey(key)
   219  	if iface.VrfMasterInterface != "" {
   220  		deps = append(deps, kvs.Dependency{
   221  			Label: interfaceVrfDep,
   222  			Key:   interfaces.InterfaceVrfKey(ifaceName, iface.VrfMasterInterface),
   223  		})
   224  	}
   225  	if source == netalloc_api.IPAddressSource_EXISTING {
   226  		deps = append(deps, kvs.Dependency{
   227  			Label: interfaceAddrDep,
   228  			Key:   interfaces.InterfaceHostNameWithAddrKey(getHostIfName(iface), addr),
   229  		})
   230  	}
   231  	allocDep, hasAllocDep := d.addrAlloc.GetAddressAllocDep(addr, ifaceName, "")
   232  	if hasAllocDep {
   233  		deps = append(deps, allocDep)
   234  	}
   235  	return deps
   236  }