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 }