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 }