go.ligato.io/vpp-agent/v3@v3.5.0/plugins/linux/ifplugin/descriptor/interface_veth.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  	"hash/fnv"
    20  
    21  	"github.com/pkg/errors"
    22  
    23  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/linuxcalls"
    24  
    25  	"go.ligato.io/vpp-agent/v3/plugins/linux/ifplugin/ifaceidx"
    26  	nslinuxcalls "go.ligato.io/vpp-agent/v3/plugins/linux/nsplugin/linuxcalls"
    27  	interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces"
    28  )
    29  
    30  // createVETH creates a new VETH pair if neither of VETH-ends are configured, or just
    31  // applies configuration to the unfinished VETH-end with a temporary host name.
    32  func (d *InterfaceDescriptor) createVETH(
    33  	nsCtx nslinuxcalls.NamespaceMgmtCtx, key string, linuxIf *interfaces.Interface,
    34  ) (md *ifaceidx.LinuxIfMetadata, err error) {
    35  	// determine host/logical/temporary interface names
    36  	hostName := getHostIfName(linuxIf)
    37  	peerName := linuxIf.GetVeth().GetPeerIfName()
    38  	tempHostName := getVethTemporaryHostName(linuxIf.GetName())
    39  	tempPeerHostName := getVethTemporaryHostName(peerName)
    40  
    41  	// context
    42  	agentPrefix := d.serviceLabel.GetAgentPrefix()
    43  
    44  	// check if this VETH-end was already created by the other end
    45  	_, peerExists := d.intfIndex.LookupByName(peerName)
    46  	if !peerExists {
    47  		// delete obsolete/invalid unfinished VETH (ignore errors)
    48  		_ = d.ifHandler.DeleteInterface(tempHostName)
    49  		_ = d.ifHandler.DeleteInterface(tempPeerHostName)
    50  
    51  		// create a new VETH pair
    52  		err = d.ifHandler.AddVethInterfacePair(tempHostName, tempPeerHostName)
    53  		if err != nil {
    54  			return nil, errors.WithMessagef(err, "error adding veth interface pair %s, %s", tempHostName, tempPeerHostName)
    55  		}
    56  
    57  		// add alias to both VETH ends
    58  		err = d.ifHandler.SetInterfaceAlias(tempHostName, agentPrefix+linuxcalls.GetVethAlias(linuxIf.Name, peerName))
    59  		if err != nil {
    60  			return nil, errors.WithMessagef(err, "error setting interface %s alias", tempHostName)
    61  		}
    62  		err = d.ifHandler.SetInterfaceAlias(tempPeerHostName, agentPrefix+linuxcalls.GetVethAlias(peerName, linuxIf.Name))
    63  		if err != nil {
    64  			return nil, errors.WithMessagef(err, "error setting peer interface %s alias", tempPeerHostName)
    65  		}
    66  	}
    67  
    68  	// move the VETH-end to the right namespace
    69  	err = d.setInterfaceNamespace(nsCtx, tempHostName, linuxIf.Namespace)
    70  	if err != nil {
    71  		return nil, errors.WithMessagef(err, "error setting interface %s to namespace %v", tempHostName, linuxIf.Namespace)
    72  	}
    73  
    74  	// move to the namespace with the interface
    75  	revert, err := d.nsPlugin.SwitchToNamespace(nsCtx, linuxIf.Namespace)
    76  	if err != nil {
    77  		return nil, errors.WithMessagef(err, "error switching to namespace %v", linuxIf.Namespace)
    78  	}
    79  	defer revert()
    80  
    81  	// rename from the temporary host name to the requested host name
    82  	if err = d.ifHandler.RenameInterface(tempHostName, hostName); err != nil {
    83  		return nil, errors.WithMessagef(err, "error renaming %s to %s", tempHostName, hostName)
    84  	}
    85  
    86  	// build metadata
    87  	link, err := d.ifHandler.GetLinkByName(hostName)
    88  	if err != nil {
    89  		return nil, errors.WithMessagef(err, "error getting link %s", hostName)
    90  	}
    91  
    92  	return &ifaceidx.LinuxIfMetadata{
    93  		Namespace:    linuxIf.Namespace,
    94  		LinuxIfIndex: link.Attrs().Index,
    95  	}, nil
    96  }
    97  
    98  // deleteVETH either un-configures one VETH-end if the other end is still configured, or
    99  // removes the entire VETH pair.
   100  func (d *InterfaceDescriptor) deleteVETH(nsCtx nslinuxcalls.NamespaceMgmtCtx, key string, linuxIf *interfaces.Interface, metadata *ifaceidx.LinuxIfMetadata) error {
   101  	// determine host/logical/temporary interface names
   102  	hostName := getHostIfName(linuxIf)
   103  	peerName := linuxIf.GetVeth().GetPeerIfName()
   104  	tempHostName := getVethTemporaryHostName(linuxIf.Name)
   105  	tempPeerHostName := getVethTemporaryHostName(peerName)
   106  
   107  	// check if the other end is still configured
   108  	_, peerExists := d.intfIndex.LookupByName(peerName)
   109  	if peerExists {
   110  		// just un-configure this VETH-end, but do not delete the pair
   111  
   112  		// rename to the temporary host name
   113  		err := d.ifHandler.RenameInterface(hostName, tempHostName)
   114  		if err != nil {
   115  			d.log.Error(err)
   116  			return err
   117  		}
   118  
   119  		// move this VETH-end to the default namespace
   120  		err = d.setInterfaceNamespace(nsCtx, tempHostName, nil)
   121  		if err != nil {
   122  			d.log.Error(err)
   123  			return err
   124  		}
   125  	} else {
   126  		// remove the VETH pair completely now
   127  		err := d.ifHandler.DeleteInterface(hostName)
   128  		if err != nil {
   129  			d.log.Error(err)
   130  			return err
   131  		}
   132  		if tempPeerHostName != "" {
   133  			// peer should be automatically removed as well, but just in case...
   134  			_ = d.ifHandler.DeleteInterface(tempPeerHostName) // ignore errors
   135  		}
   136  	}
   137  
   138  	return nil
   139  }
   140  
   141  // getVethTemporaryHostName (deterministically) generates a temporary host name
   142  // for a VETH interface.
   143  func getVethTemporaryHostName(vethName string) string {
   144  	if vethName == "" {
   145  		return ""
   146  	}
   147  	return fmt.Sprintf("veth-%d", fnvHash(vethName))
   148  }
   149  
   150  // fnvHash hashes string using fnv32a algorithm.
   151  func fnvHash(s string) uint32 {
   152  	h := fnv.New32a()
   153  	h.Write([]byte(s))
   154  	return h.Sum32()
   155  }