Files
kata-containers/virtcontainers/tuntap_endpoint.go
Gabi Beyer e93bf967d2 network: Add tuntap device
The tuntap network device is for tuntap interfaces to connect
to the container. A specific use case is the slirp4netns tap
interface for rootless kata-runtime.

Fixes: #1878

Signed-off-by: Gabi Beyer <gabrielle.n.beyer@intel.com>
2019-09-26 16:17:16 +02:00

215 lines
6.5 KiB
Go

// Copyright (c) 2018 Huawei Corporation
// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"fmt"
"net"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
persistapi "github.com/kata-containers/runtime/virtcontainers/persist/api"
)
// TuntapEndpoint represents just a tap endpoint
type TuntapEndpoint struct {
NetPair NetworkInterfacePair
TuntapInterface TuntapInterface
EndpointProperties NetworkInfo
EndpointType EndpointType
PCIAddr string
}
// Properties returns the properties of the tap interface.
func (endpoint *TuntapEndpoint) Properties() NetworkInfo {
return endpoint.EndpointProperties
}
// Name returns name of the tap interface in the network pair.
func (endpoint *TuntapEndpoint) Name() string {
return endpoint.TuntapInterface.Name
}
// HardwareAddr returns the mac address that is assigned to the tap interface
func (endpoint *TuntapEndpoint) HardwareAddr() string {
return endpoint.TuntapInterface.TAPIface.HardAddr
}
// Type identifies the endpoint as a tap endpoint.
func (endpoint *TuntapEndpoint) Type() EndpointType {
return endpoint.EndpointType
}
// PciAddr returns the PCI address of the endpoint.
func (endpoint *TuntapEndpoint) PciAddr() string {
return endpoint.PCIAddr
}
// SetPciAddr sets the PCI address of the endpoint.
func (endpoint *TuntapEndpoint) SetPciAddr(pciAddr string) {
endpoint.PCIAddr = pciAddr
}
// NetworkPair returns the network pair of the endpoint.
func (endpoint *TuntapEndpoint) NetworkPair() *NetworkInterfacePair {
return &endpoint.NetPair
}
// SetProperties sets the properties for the endpoint.
func (endpoint *TuntapEndpoint) SetProperties(properties NetworkInfo) {
endpoint.EndpointProperties = properties
}
// Attach for tap endpoint adds the tap interface to the hypervisor.
func (endpoint *TuntapEndpoint) Attach(h hypervisor) error {
if err := xConnectVMNetwork(endpoint, h); err != nil {
networkLogger().WithError(err).Error("Error bridging virtual endpoint")
return err
}
return h.addDevice(endpoint, netDev)
}
// Detach for the tap endpoint tears down the tap
func (endpoint *TuntapEndpoint) Detach(netNsCreated bool, netNsPath string) error {
if !netNsCreated && netNsPath != "" {
return nil
}
networkLogger().WithField("endpoint-type", TuntapEndpointType).Info("Detaching endpoint")
return doNetNS(netNsPath, func(_ ns.NetNS) error {
return unTuntapNetwork(endpoint.TuntapInterface.TAPIface.Name)
})
}
// HotAttach for the tap endpoint uses hot plug device
func (endpoint *TuntapEndpoint) HotAttach(h hypervisor) error {
networkLogger().Info("Hot attaching tap endpoint")
if err := tuntapNetwork(endpoint, h.hypervisorConfig().NumVCPUs, h.hypervisorConfig().DisableVhostNet); err != nil {
networkLogger().WithError(err).Error("Error bridging tap ep")
return err
}
if _, err := h.hotplugAddDevice(endpoint, netDev); err != nil {
networkLogger().WithError(err).Error("Error attach tap ep")
return err
}
return nil
}
// HotDetach for the tap endpoint uses hot pull device
func (endpoint *TuntapEndpoint) HotDetach(h hypervisor, netNsCreated bool, netNsPath string) error {
networkLogger().Info("Hot detaching tap endpoint")
if err := doNetNS(netNsPath, func(_ ns.NetNS) error {
return unTuntapNetwork(endpoint.TuntapInterface.TAPIface.Name)
}); err != nil {
networkLogger().WithError(err).Warn("Error un-bridging tap ep")
}
if _, err := h.hotplugRemoveDevice(endpoint, netDev); err != nil {
networkLogger().WithError(err).Error("Error detach tap ep")
return err
}
return nil
}
func createTuntapNetworkEndpoint(idx int, ifName string, hwName net.HardwareAddr, internetworkingModel NetInterworkingModel) (*TuntapEndpoint, error) {
if idx < 0 {
return &TuntapEndpoint{}, fmt.Errorf("invalid network endpoint index: %d", idx)
}
netPair, err := createNetworkInterfacePair(idx, ifName, internetworkingModel)
if err != nil {
return nil, err
}
endpoint := &TuntapEndpoint{
NetPair: netPair,
TuntapInterface: TuntapInterface{
Name: fmt.Sprintf("eth%d", idx),
TAPIface: NetworkInterface{
Name: fmt.Sprintf("tap%d_kata", idx),
HardAddr: fmt.Sprintf("%s", hwName), //nolint:gosimple
},
},
EndpointType: TuntapEndpointType,
}
if ifName != "" {
endpoint.TuntapInterface.Name = ifName
}
return endpoint, nil
}
func tuntapNetwork(endpoint *TuntapEndpoint, numCPUs uint32, disableVhostNet bool) error {
netHandle, err := netlink.NewHandle()
if err != nil {
return err
}
defer netHandle.Delete()
tapLink, _, err := createLink(netHandle, endpoint.TuntapInterface.TAPIface.Name, &netlink.Tuntap{}, int(numCPUs))
if err != nil {
return fmt.Errorf("Could not create TAP interface: %s", err)
}
linkAttrs := endpoint.Properties().Iface.LinkAttrs
// Save the MAC address to the TAP so that it can later be used
// to build the QMP command line. This MAC address has to be
// the one inside the VM in order to avoid any firewall issues. The
// bridge created by the network plugin on the host actually expects
// to see traffic from this MAC address and not another one.
endpoint.TuntapInterface.TAPIface.HardAddr = linkAttrs.HardwareAddr.String()
if err := netHandle.LinkSetMTU(tapLink, linkAttrs.MTU); err != nil {
return fmt.Errorf("Could not set TAP MTU %d: %s", linkAttrs.MTU, err)
}
if err := netHandle.LinkSetUp(tapLink); err != nil {
return fmt.Errorf("Could not enable TAP %s: %s", endpoint.TuntapInterface.Name, err)
}
return nil
}
func unTuntapNetwork(name string) error {
netHandle, err := netlink.NewHandle()
if err != nil {
return err
}
defer netHandle.Delete()
tapLink, err := getLinkByName(netHandle, name, &netlink.Tuntap{})
if err != nil {
return fmt.Errorf("Could not get TAP interface: %s", err)
}
if err := netHandle.LinkSetDown(tapLink); err != nil {
return fmt.Errorf("Could not disable TAP %s: %s", name, err)
}
if err := netHandle.LinkDel(tapLink); err != nil {
return fmt.Errorf("Could not remove TAP %s: %s", name, err)
}
return nil
}
func (endpoint *TuntapEndpoint) save() persistapi.NetworkEndpoint {
tuntapif := saveTuntapIf(&endpoint.TuntapInterface)
return persistapi.NetworkEndpoint{
Type: string(endpoint.Type()),
Tuntap: &persistapi.TuntapEndpoint{
TuntapInterface: *tuntapif,
},
}
}
func (endpoint *TuntapEndpoint) load(s persistapi.NetworkEndpoint) {
endpoint.EndpointType = TuntapEndpointType
if s.Tuntap != nil {
tuntapif := loadTuntapIf(&s.Tuntap.TuntapInterface)
endpoint.TuntapInterface = *tuntapif
}
}