Skip to content
This repository has been archived by the owner on May 12, 2021. It is now read-only.

Commit

Permalink
network: Add tuntap device
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Gabi Beyer committed Jul 16, 2019
1 parent 8eb7ce4 commit 1a5fa52
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 2 deletions.
8 changes: 8 additions & 0 deletions virtcontainers/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ const (
// TapEndpointType is tap network interface.
TapEndpointType EndpointType = "tap"

// TuntapEndpointType is a tap network interface.
TuntapEndpointType EndpointType = "tuntap"

// IPVlanEndpointType is ipvlan network interface.
IPVlanEndpointType EndpointType = "ipvlan"
)
Expand All @@ -73,6 +76,9 @@ func (endpointType *EndpointType) Set(value string) error {
case "tap":
*endpointType = TapEndpointType
return nil
case "tuntap":
*endpointType = TuntapEndpointType
return nil
case "ipvlan":
*endpointType = IPVlanEndpointType
return nil
Expand All @@ -96,6 +102,8 @@ func (endpointType *EndpointType) String() string {
return string(MacvtapEndpointType)
case TapEndpointType:
return string(TapEndpointType)
case TuntapEndpointType:
return string(TuntapEndpointType)
case IPVlanEndpointType:
return string(IPVlanEndpointType)
default:
Expand Down
23 changes: 21 additions & 2 deletions virtcontainers/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ type TapInterface struct {
VhostFds []*os.File
}

// TuntapInterface defines a tap interface
type TuntapInterface struct {
Name string
TAPIface NetworkInterface
}

// NetworkInterfacePair defines a pair between VM and virtual network interfaces.
type NetworkInterfacePair struct {
TapInterface
Expand Down Expand Up @@ -262,6 +268,10 @@ func generateEndpoints(typedEndpoints []TypedJSONEndpoint) ([]Endpoint, error) {
var endpoint IPVlanEndpoint
endpointInf = &endpoint

case TuntapEndpointType:
var endpoint TuntapEndpoint
endpointInf = &endpoint

default:
networkLogger().WithField("endpoint-type", e.Type).Error("Ignoring unknown endpoint type")
}
Expand Down Expand Up @@ -373,6 +383,8 @@ func getLinkForEndpoint(endpoint Endpoint, netHandle *netlink.Handle) (netlink.L
link = &netlink.Macvlan{}
case *IPVlanEndpoint:
link = &netlink.IPVlan{}
case *TuntapEndpoint:
link = &netlink.Tuntap{}
default:
return nil, fmt.Errorf("Unexpected endpointType %s", ep.Type())
}
Expand All @@ -392,7 +404,7 @@ func getLinkByName(netHandle *netlink.Handle, name string, expectedLink netlink.
return l, nil
}
case (&netlink.Tuntap{}).Type():
if l, ok := link.(*netlink.GenericLink); ok {
if l, ok := link.(*netlink.Tuntap); ok {
return l, nil
}
case (&netlink.Veth{}).Type():
Expand Down Expand Up @@ -1233,6 +1245,10 @@ func createNetworkInterfacePair(idx int, ifName string, interworkingModel NetInt
NetInterworkingModel: interworkingModel,
}

if ifName != "" {
netPair.VirtIface.Name = ifName
}

return netPair, nil
}

Expand Down Expand Up @@ -1378,12 +1394,15 @@ func createEndpoint(netInfo NetworkInfo, idx int, model NetInterworkingModel) (E
} else if netInfo.Iface.Type == "tap" {
networkLogger().Info("tap interface found")
endpoint, err = createTapNetworkEndpoint(idx, netInfo.Iface.Name)
} else if netInfo.Iface.Type == "tuntap" {
networkLogger().Info("tuntap interface found")
endpoint, err = createTuntapNetworkEndpoint(idx, netInfo.Iface.Name, netInfo.Iface.HardwareAddr, model)
} else if netInfo.Iface.Type == "veth" {
endpoint, err = createVethNetworkEndpoint(idx, netInfo.Iface.Name, model)
} else if netInfo.Iface.Type == "ipvlan" {
endpoint, err = createIPVlanNetworkEndpoint(idx, netInfo.Iface.Name)
} else {
return nil, fmt.Errorf("Unsupported network interface")
return nil, fmt.Errorf("Unsupported network interface: %s", netInfo.Iface.Type)
}
}

Expand Down
18 changes: 18 additions & 0 deletions virtcontainers/qemu_arch_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,24 @@ func (q *qemuArchBase) appendNetwork(devices []govmmQemu.Device, endpoint Endpoi
},
)
q.networkIndex++
case *TuntapEndpoint:
netPair := ep.NetworkPair()
devices = append(devices,
govmmQemu.NetDevice{
Type: govmmQemu.NetDeviceType("tap"),
Driver: govmmQemu.VirtioNet,
ID: fmt.Sprintf("network-%d", q.networkIndex),
IFName: netPair.TAPIface.Name,
MACAddress: netPair.TAPIface.HardAddr,
DownScript: "no",
Script: "no",
VHost: q.vhost,
DisableModern: q.nestedRun,
FDs: netPair.VMFds,
VhostFDs: netPair.VhostFds,
},
)
q.networkIndex++

}

Expand Down
191 changes: 191 additions & 0 deletions virtcontainers/tuntap_endpoint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// Copyright (c) 2018 Huawei Corporation
//
// SPDX-License-Identifier: Apache-2.0
//

package virtcontainers

import (
"fmt"
"net"

"github.com/containernetworking/plugins/pkg/ns"
"github.com/vishvananda/netlink"
)

// 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
}

0 comments on commit 1a5fa52

Please sign in to comment.