Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugins/main/ptp: support for creating tap devices #271

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 13 additions & 10 deletions libcni/api.go
Expand Up @@ -15,17 +15,19 @@
package libcni

import (
"strconv"
"strings"

"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
)

type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
ContainerID string
UsesTapDevice bool
NetNS string
IfName string
Args [][2]string
}

type NetworkConfig struct {
Expand Down Expand Up @@ -63,11 +65,12 @@ func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error {
// =====
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
return &invoke.Args{
Command: action,
ContainerID: rt.ContainerID,
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, ":"),
Command: action,
ContainerID: rt.ContainerID,
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, ":"),
UsesTapDevice: strconv.FormatBool(rt.UsesTapDevice),
}
}
4 changes: 3 additions & 1 deletion pkg/invoke/args.go
Expand Up @@ -45,6 +45,7 @@ type Args struct {
PluginArgsStr string
IfName string
Path string
UsesTapDevice string
}

func (args *Args) AsEnv() []string {
Expand All @@ -60,7 +61,8 @@ func (args *Args) AsEnv() []string {
"CNI_NETNS="+args.NetNS,
"CNI_ARGS="+pluginArgsStr,
"CNI_IFNAME="+args.IfName,
"CNI_PATH="+args.Path)
"CNI_PATH="+args.Path,
"CNI_USE_TAP="+args.UsesTapDevice)
return env
}

Expand Down
37 changes: 37 additions & 0 deletions pkg/ip/link.go
Expand Up @@ -130,6 +130,43 @@ func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVet
return
}

func SetupTap(mtu int) (tap netlink.Link, err error) {
tapName, err := RandomTapName()
if err != nil {
return nil, fmt.Errorf("failed to generate random tap name: %v", err)
}
la := netlink.NewLinkAttrs()
la.Name = tapName
la.MTU = mtu
mode := netlink.TUNTAP_MODE_TAP
flags := netlink.TUNTAP_NO_PI | netlink.TUNTAP_VNET_HDR
tunDesc := &netlink.Tuntap{la, mode, flags}
if err := netlink.LinkAdd(tunDesc); err != nil {
return nil, fmt.Errorf("%v", err)
}

link, err := netlink.LinkByName(tunDesc.Name)
if err != nil {
return nil, fmt.Errorf("cannot find link %v %v", tunDesc.Name, err)
}

if err := netlink.LinkSetUp(link); err != nil {
return nil, fmt.Errorf("cannot set link up %q", tapName)
}

return link, nil
}

func RandomTapName() (string, error) {
entropy := make([]byte, 4)
_, err := rand.Reader.Read(entropy)
if err != nil {
return "", fmt.Errorf("failed to generate random veth name: %v", err)
}

return fmt.Sprintf("tap%x", entropy), nil
}

// DelLinkByName removes an interface link.
func DelLinkByName(ifName string) error {
iface, err := netlink.LinkByName(ifName)
Expand Down
47 changes: 34 additions & 13 deletions pkg/skel/skel.go
Expand Up @@ -22,6 +22,7 @@ import (
"io/ioutil"
"log"
"os"
"strconv"

"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
Expand All @@ -30,12 +31,13 @@ import (
// CmdArgs captures all the arguments passed in to the plugin
// via both env vars and stdin
type CmdArgs struct {
ContainerID string
Netns string
IfName string
Args string
Path string
StdinData []byte
ContainerID string
Netns string
IfName string
Args string
Path string
UsesTapDevice bool
StdinData []byte
}

type dispatcher struct {
Expand All @@ -49,7 +51,7 @@ type dispatcher struct {
type reqForCmdEntry map[string]bool

func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
var cmd, contID, netns, ifName, args, path string
var cmd, contID, netns, ifName, args, path, usesTapDevice string

vars := []struct {
name string
Expand Down Expand Up @@ -104,6 +106,14 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
"DEL": true,
},
},
{
"CNI_USE_TAP",
&usesTapDevice,
reqForCmdEntry{
"ADD": false,
"DEL": false,
},
},
}

argsMissing := false
Expand All @@ -126,13 +136,24 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
}

var useTap bool
if usesTapDevice == "" {
useTap = false
} else {
useTap, err = strconv.ParseBool(usesTapDevice)
if err != nil {
return "", nil, fmt.Errorf("CNI_USE_TAP should only contain 'true' or 'false'")
}
}

cmdArgs := &CmdArgs{
ContainerID: contID,
Netns: netns,
IfName: ifName,
Args: args,
Path: path,
StdinData: stdinData,
ContainerID: contID,
Netns: netns,
IfName: ifName,
Args: args,
Path: path,
UsesTapDevice: useTap,
StdinData: stdinData,
}
return cmd, cmdArgs, nil
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/types/types.go
Expand Up @@ -94,6 +94,7 @@ func (r *Result) String() string {
type IPConfig struct {
IP net.IPNet
Gateway net.IP
Iface string
Routes []Route
}

Expand Down Expand Up @@ -132,6 +133,7 @@ type ipConfig struct {
IP IPNet `json:"ip"`
Gateway net.IP `json:"gateway,omitempty"`
Routes []Route `json:"routes,omitempty"`
Iface string `json:"iface,omitempty"`
}

type route struct {
Expand All @@ -144,6 +146,7 @@ func (c *IPConfig) MarshalJSON() ([]byte, error) {
IP: IPNet(c.IP),
Gateway: c.Gateway,
Routes: c.Routes,
Iface: c.Iface,
}

return json.Marshal(ipc)
Expand All @@ -158,6 +161,7 @@ func (c *IPConfig) UnmarshalJSON(data []byte) error {
c.IP = net.IPNet(ipc.IP)
c.Gateway = ipc.Gateway
c.Routes = ipc.Routes
c.Iface = ipc.Iface
return nil
}

Expand Down
83 changes: 67 additions & 16 deletions plugins/main/ptp/ptp.go
Expand Up @@ -160,6 +160,39 @@ func setupHostVeth(vethName string, ipConf *types.IPConfig) error {
return nil
}

// setupTapDevice creates persistent tap device
// and returns a newly created netlink.Link structure
func setupTapDevice(mtu int, pr *types.Result) error {
// network device names are limited to 16 characters
// the suffix %d will be replaced by the kernel with a suitable number
link, err := ip.SetupTap(mtu)
if err != nil {
return err
}

ipn := &net.IPNet{
IP: pr.IP4.Gateway,
Mask: net.CIDRMask(32, 32),
}
addr := &netlink.Addr{IPNet: ipn, Label: ""}
if err = netlink.AddrAdd(link, addr); err != nil {
return fmt.Errorf("failed to add IP addr (%#v) to tap: %v", ipn, err)
}

ipn = &net.IPNet{
IP: pr.IP4.IP.IP,
Mask: net.CIDRMask(32, 32),
}

if err = ip.AddHostRoute(ipn, nil, link); err != nil && !os.IsExist(err) {
return fmt.Errorf("failed to add route on host: %v", err)
}

pr.IP4.Iface = link.Attrs().Name

return nil
}

func cmdAdd(args *skel.CmdArgs) error {
conf := NetConf{}
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
Expand All @@ -179,13 +212,23 @@ func cmdAdd(args *skel.CmdArgs) error {
return errors.New("IPAM plugin returned missing IPv4 config")
}

hostVethName, err := setupContainerVeth(args.Netns, args.IfName, conf.MTU, result)
if err != nil {
return err
}
if !args.UsesTapDevice {
// veth pair
// regular network configuration for containers
hostVethName, err := setupContainerVeth(args.Netns, args.IfName, conf.MTU, result)
if err != nil {
return err
}

if err = setupHostVeth(hostVethName, result.IP4); err != nil {
return err
if err = setupHostVeth(hostVethName, result.IP4); err != nil {
return err
}
} else {
// tap device
// for vm based containers
if err := setupTapDevice(conf.MTU, result); err != nil {
return err
}
}

if conf.IPMasq {
Expand All @@ -210,24 +253,32 @@ func cmdDel(args *skel.CmdArgs) error {
return err
}

if args.Netns == "" {
return nil
}

var ipn *net.IPNet
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
if !args.UsesTapDevice {
if args.Netns == "" {
return nil
}

err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
var err error
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
return err
})
if err != nil {
return err
}
} else {
var err error
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
return err
})
if err != nil {
return err
if err != nil {
return err
}
}

if conf.IPMasq {
chain := utils.FormatChainName(conf.Name, args.ContainerID)
comment := utils.FormatComment(conf.Name, args.ContainerID)
if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil {
if err := ip.TeardownIPMasq(ipn, chain, comment); err != nil {
return err
}
}
Expand Down