github.com/kata-containers/runtime@v0.0.0-20210505125100-04f29832a923/virtcontainers/physical_endpoint.go (about)

     1  // Copyright (c) 2018 Intel Corporation
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"path/filepath"
    13  	"strings"
    14  
    15  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    16  	"github.com/kata-containers/runtime/virtcontainers/device/drivers"
    17  	persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
    18  	"github.com/kata-containers/runtime/virtcontainers/pkg/cgroups"
    19  	vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
    20  	"github.com/safchain/ethtool"
    21  )
    22  
    23  // PhysicalEndpoint gathers a physical network interface and its properties
    24  type PhysicalEndpoint struct {
    25  	IfaceName          string
    26  	HardAddr           string
    27  	EndpointProperties NetworkInfo
    28  	EndpointType       EndpointType
    29  	BDF                string
    30  	Driver             string
    31  	VendorDeviceID     string
    32  	PCIPath            vcTypes.PciPath
    33  }
    34  
    35  // Properties returns the properties of the physical interface.
    36  func (endpoint *PhysicalEndpoint) Properties() NetworkInfo {
    37  	return endpoint.EndpointProperties
    38  }
    39  
    40  // HardwareAddr returns the mac address of the physical network interface.
    41  func (endpoint *PhysicalEndpoint) HardwareAddr() string {
    42  	return endpoint.HardAddr
    43  }
    44  
    45  // Name returns name of the physical interface.
    46  func (endpoint *PhysicalEndpoint) Name() string {
    47  	return endpoint.IfaceName
    48  }
    49  
    50  // Type indentifies the endpoint as a physical endpoint.
    51  func (endpoint *PhysicalEndpoint) Type() EndpointType {
    52  	return endpoint.EndpointType
    53  }
    54  
    55  // PciPath returns the PCI path of the endpoint.
    56  func (endpoint *PhysicalEndpoint) PciPath() vcTypes.PciPath {
    57  	return endpoint.PCIPath
    58  }
    59  
    60  // SetPciPath sets the PCI path of the endpoint.
    61  func (endpoint *PhysicalEndpoint) SetPciPath(pciPath vcTypes.PciPath) {
    62  	endpoint.PCIPath = pciPath
    63  }
    64  
    65  // SetProperties sets the properties of the physical endpoint.
    66  func (endpoint *PhysicalEndpoint) SetProperties(properties NetworkInfo) {
    67  	endpoint.EndpointProperties = properties
    68  }
    69  
    70  // NetworkPair returns the network pair of the endpoint.
    71  func (endpoint *PhysicalEndpoint) NetworkPair() *NetworkInterfacePair {
    72  	return nil
    73  }
    74  
    75  // Attach for physical endpoint binds the physical network interface to
    76  // vfio-pci and adds device to the hypervisor with vfio-passthrough.
    77  func (endpoint *PhysicalEndpoint) Attach(s *Sandbox) error {
    78  	// Unbind physical interface from host driver and bind to vfio
    79  	// so that it can be passed to qemu.
    80  	vfioPath, err := bindNICToVFIO(endpoint)
    81  	if err != nil {
    82  		return err
    83  	}
    84  
    85  	c, err := cgroups.DeviceToCgroupDevice(vfioPath)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	d := config.DeviceInfo{
    91  		ContainerPath: c.Path,
    92  		DevType:       string(c.Type),
    93  		Major:         c.Major,
    94  		Minor:         c.Minor,
    95  		ColdPlug:      true,
    96  	}
    97  
    98  	_, err = s.AddDevice(d)
    99  	return err
   100  }
   101  
   102  // Detach for physical endpoint unbinds the physical network interface from vfio-pci
   103  // and binds it back to the saved host driver.
   104  func (endpoint *PhysicalEndpoint) Detach(netNsCreated bool, netNsPath string) error {
   105  	// Bind back the physical network interface to host.
   106  	// We need to do this even if a new network namespace has not
   107  	// been created by virtcontainers.
   108  
   109  	// We do not need to enter the network namespace to bind back the
   110  	// physical interface to host driver.
   111  	return bindNICToHost(endpoint)
   112  }
   113  
   114  // HotAttach for physical endpoint not supported yet
   115  func (endpoint *PhysicalEndpoint) HotAttach(h hypervisor) error {
   116  	return fmt.Errorf("PhysicalEndpoint does not support Hot attach")
   117  }
   118  
   119  // HotDetach for physical endpoint not supported yet
   120  func (endpoint *PhysicalEndpoint) HotDetach(h hypervisor, netNsCreated bool, netNsPath string) error {
   121  	return fmt.Errorf("PhysicalEndpoint does not support Hot detach")
   122  }
   123  
   124  // isPhysicalIface checks if an interface is a physical device.
   125  // We use ethtool here to not rely on device sysfs inside the network namespace.
   126  func isPhysicalIface(ifaceName string) (bool, error) {
   127  	if ifaceName == "lo" {
   128  		return false, nil
   129  	}
   130  
   131  	ethHandle, err := ethtool.NewEthtool()
   132  	if err != nil {
   133  		return false, err
   134  	}
   135  	defer ethHandle.Close()
   136  
   137  	bus, err := ethHandle.BusInfo(ifaceName)
   138  	if err != nil {
   139  		return false, nil
   140  	}
   141  
   142  	// Check for a pci bus format
   143  	tokens := strings.Split(bus, ":")
   144  	if len(tokens) != 3 {
   145  		return false, nil
   146  	}
   147  
   148  	return true, nil
   149  }
   150  
   151  var sysPCIDevicesPath = "/sys/bus/pci/devices"
   152  
   153  func createPhysicalEndpoint(netInfo NetworkInfo) (*PhysicalEndpoint, error) {
   154  	// Get ethtool handle to derive driver and bus
   155  	ethHandle, err := ethtool.NewEthtool()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  	defer ethHandle.Close()
   160  
   161  	// Get BDF
   162  	bdf, err := ethHandle.BusInfo(netInfo.Iface.Name)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  
   167  	// Get driver by following symlink /sys/bus/pci/devices/$bdf/driver
   168  	driverPath := filepath.Join(sysPCIDevicesPath, bdf, "driver")
   169  	link, err := os.Readlink(driverPath)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	driver := filepath.Base(link)
   175  
   176  	// Get vendor and device id from pci space (sys/bus/pci/devices/$bdf)
   177  
   178  	ifaceDevicePath := filepath.Join(sysPCIDevicesPath, bdf, "device")
   179  	contents, err := ioutil.ReadFile(ifaceDevicePath)
   180  	if err != nil {
   181  		return nil, err
   182  	}
   183  
   184  	deviceID := strings.TrimSpace(string(contents))
   185  
   186  	// Vendor id
   187  	ifaceVendorPath := filepath.Join(sysPCIDevicesPath, bdf, "vendor")
   188  	contents, err = ioutil.ReadFile(ifaceVendorPath)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  
   193  	vendorID := strings.TrimSpace(string(contents))
   194  	vendorDeviceID := fmt.Sprintf("%s %s", vendorID, deviceID)
   195  	vendorDeviceID = strings.TrimSpace(vendorDeviceID)
   196  
   197  	physicalEndpoint := &PhysicalEndpoint{
   198  		IfaceName:      netInfo.Iface.Name,
   199  		HardAddr:       netInfo.Iface.HardwareAddr.String(),
   200  		VendorDeviceID: vendorDeviceID,
   201  		EndpointType:   PhysicalEndpointType,
   202  		Driver:         driver,
   203  		BDF:            bdf,
   204  	}
   205  
   206  	return physicalEndpoint, nil
   207  }
   208  
   209  func bindNICToVFIO(endpoint *PhysicalEndpoint) (string, error) {
   210  	return drivers.BindDevicetoVFIO(endpoint.BDF, endpoint.Driver, endpoint.VendorDeviceID)
   211  }
   212  
   213  func bindNICToHost(endpoint *PhysicalEndpoint) error {
   214  	return drivers.BindDevicetoHost(endpoint.BDF, endpoint.Driver, endpoint.VendorDeviceID)
   215  }
   216  
   217  func (endpoint *PhysicalEndpoint) save() persistapi.NetworkEndpoint {
   218  	return persistapi.NetworkEndpoint{
   219  		Type: string(endpoint.Type()),
   220  
   221  		Physical: &persistapi.PhysicalEndpoint{
   222  			BDF:            endpoint.BDF,
   223  			Driver:         endpoint.Driver,
   224  			VendorDeviceID: endpoint.VendorDeviceID,
   225  		},
   226  	}
   227  }
   228  
   229  func (endpoint *PhysicalEndpoint) load(s persistapi.NetworkEndpoint) {
   230  	endpoint.EndpointType = PhysicalEndpointType
   231  
   232  	if s.Physical != nil {
   233  		endpoint.BDF = s.Physical.BDF
   234  		endpoint.Driver = s.Physical.Driver
   235  		endpoint.VendorDeviceID = s.Physical.VendorDeviceID
   236  	}
   237  }