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

removed subshelling from veth between containers #123

Closed
wants to merge 6 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 126 additions & 43 deletions clab/netlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import (
"fmt"
"net"
"os"
"os/exec"
"runtime"
"strings"
"sync"

"github.com/google/uuid"
log "github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)

func (c *cLab) InitVirtualWiring() {
Expand Down Expand Up @@ -51,23 +52,30 @@ func (c *cLab) createAToBveth(l *Link) error {
interfaceA := fmt.Sprintf("clab-%s", genIfName())
interfaceB := fmt.Sprintf("clab-%s", genIfName())

cmd := exec.Command("sudo", "ip", "link", "add", interfaceA, "type", "veth", "peer", "name", interfaceB)
err := runCmd(cmd)
nllA := &netlink.Veth{PeerName: interfaceB, LinkAttrs: netlink.LinkAttrs{Name: interfaceA}}

err := netlink.LinkAdd(nllA)
if err != nil {
return err
}

wg := new(sync.WaitGroup)
wg.Add(2)
go func() {
defer wg.Done()
err := c.configVeth(interfaceA, l.A.EndpointName, l.A.Node.LongName)
la := c.newLinkAttributes()
la.Name = l.A.EndpointName
err = c.configVeth(interfaceA, la, l.A.Node.LongName)
if err != nil {
log.Fatalf("failed to config interface '%s' in container %s: %v", l.A.EndpointName, l.A.Node.LongName, err)
}
}()
go func() {
defer wg.Done()
err = c.configVeth(interfaceB, l.B.EndpointName, l.B.Node.LongName)

la := c.newLinkAttributes()
la.Name = l.B.EndpointName
err = c.configVeth(interfaceB, la, l.B.Node.LongName)
if err != nil {
log.Fatalf("failed to config interface '%s' in container %s: %v", l.B.EndpointName, l.B.Node.LongName, err)
}
Expand All @@ -76,36 +84,128 @@ func (c *cLab) createAToBveth(l *Link) error {
return nil
}

func (c *cLab) configVeth(dummyInterface, endpointName, ns string) error {
var cmd *exec.Cmd
// LinkAttributes Type for the adjustment of the Link Attributes
type LinkAttributes struct {
Name string
MTU int
LinkUp bool
// Master is just used for interfaces meant to be attached to bridges
Master string
}

// Initialize and instantiate a new LinkAttributes with default values
func (c *cLab) newLinkAttributes() LinkAttributes {
return LinkAttributes{Name: "", MTU: 1500, LinkUp: true, Master: ""}
}

func (c *cLab) configVeth(dummyInterface string, la LinkAttributes, ns string) error {

netNS, err := netns.GetFromName(ns)
if err != nil {
return err
}

log.Debugf("Disabling TX checksum offloading for the %s interface...", dummyInterface)
err := EthtoolTXOff(dummyInterface)
err = EthtoolTXOff(dummyInterface)
if err != nil {
return err
}

netLink, err := netlink.LinkByName(dummyInterface)
if err != nil {
return err
}

log.Debugf("map dummy interface '%s' to container %s", dummyInterface, ns)
cmd = exec.Command("sudo", "ip", "link", "set", dummyInterface, "netns", ns)
err = runCmd(cmd)
err = netlink.LinkSetNsFd(netLink, int(netNS))
if err != nil {
return err
}
log.Debugf("rename interface %s to %s", dummyInterface, endpointName)
cmd = exec.Command("sudo", "ip", "netns", "exec", ns, "ip", "link", "set", dummyInterface, "name", endpointName)
err = runCmd(cmd)

err = c.setLinkAttributes(ns, netNS, dummyInterface, la)
if err != nil {
return err
}
log.Debugf("set interface %s state to up in NS %s", endpointName, ns)
cmd = exec.Command("sudo", "ip", "netns", "exec", ns, "ip", "link", "set", endpointName, "up")
err = runCmd(cmd)
return nil

}

func (c *cLab) setLinkAttributes(namespaceName string, cnamespace netns.NsHandle, oldLinkName string, la LinkAttributes) error {
hostNetNs, _ := netns.Get()
netns.Set(cnamespace)
runtime.LockOSThread()

link, err := netlink.LinkByName(oldLinkName)
if err != nil {
return err
}
if la.Name != "" {
log.Debugf("rename interface %s to %s", oldLinkName, la.Name)
err = netlink.LinkSetName(link, la.Name)
if err != nil {
return err
}
}
if la.LinkUp {
log.Debugf("set interface %s state to up in NS %s", la.Name, namespaceName)
err = netlink.LinkSetUp(link)
if err != nil {
return err
}
}
if la.Master != "" {
// if interface should be attached to a bridge, the Master is set to bridge name
log.Debugf("attache interface %s to bridge %s", la.Name, la.Master)

parentIndex := link.Attrs().ParentIndex

runtime.UnlockOSThread()
netns.Set(hostNetNs)
runtime.LockOSThread()

// get handleto master link
rootNSvethLink, err := netlink.LinkByIndex(parentIndex)
if err != nil {
return err
}

bridgeIf, err := netlink.LinkByName(la.Master)
if err != nil {
return err
}

// assigne master for link
err = netlink.LinkSetMaster(rootNSvethLink, bridgeIf)
if err != nil {
return err
}

// Finally set rootNSvethLink interface up
err = netlink.LinkSetUp(rootNSvethLink)
if err != nil {
return err
}

log.Debugf("setting root-ns interface %s MTU to %d", la.Name, la.MTU)
netlink.LinkSetMTU(rootNSvethLink, la.MTU)
if err != nil {
return err
}
runtime.UnlockOSThread()
netns.Set(cnamespace)
runtime.LockOSThread()
}
log.Debugf("setting interface %s MTU to %d", la.Name, la.MTU)
netlink.LinkSetMTU(link, la.MTU)
if err != nil {
return err
}
runtime.UnlockOSThread()
netns.Set(hostNetNs)
return nil
}

func (c *cLab) createvethToBridge(l *Link) error {
var cmd *exec.Cmd
var err error
log.Debugf("Create veth to bridge wire: %s <--> %s", l.A.EndpointName, l.B.EndpointName)
dummyIface := fmt.Sprintf("clab-%s", genIfName())
Expand All @@ -125,27 +225,21 @@ func (c *cLab) createvethToBridge(l *Link) error {
}

log.Debugf("create dummy veth pair '%s'<-->'%s'", dummyIface, bridgeIfname)
cmd = exec.Command("sudo", "ip", "link", "add", dummyIface, "type", "veth", "peer", "name", bridgeIfname)
err = runCmd(cmd)
if err != nil {
return err
}
err = c.configVeth(dummyIface, containerIfName, containerNS)
if err != nil {
return err
}
log.Debugf("map veth pair %s to bridge %s", bridgeIfname, bridgeName)
cmd = exec.Command("sudo", "ip", "link", "set", bridgeIfname, "master", bridgeName)
err = runCmd(cmd)
nllA := &netlink.Veth{PeerName: bridgeIfname, LinkAttrs: netlink.LinkAttrs{Name: dummyIface, OperState: netlink.OperUp}}

err = netlink.LinkAdd(nllA)
if err != nil {
return err
}
log.Debugf("set interface '%s' state to up", bridgeIfname)
cmd = exec.Command("sudo", "ip", "link", "set", bridgeIfname, "up")
err = runCmd(cmd)

la := c.newLinkAttributes()
la.Name = containerIfName
la.Master = bridgeName
err = c.configVeth(dummyIface, la, containerNS)
if err != nil {
return err
}

log.Debugf("Disabling TX checksum offloading for the %s interface...", bridgeIfname)
err = EthtoolTXOff(bridgeIfname)
if err != nil {
Expand All @@ -169,17 +263,6 @@ func (c *cLab) DeleteNetnsSymlinks() (err error) {
return nil
}

func runCmd(cmd *exec.Cmd) error {
b, err := cmd.CombinedOutput()
if err != nil {
log.Debugf("'%s' failed with: %v", cmd.String(), err)
log.Debugf("'%s' failed output: %v", cmd.String(), string(b))
return err
}
log.Debugf("'%s' output: %v", cmd.String(), string(b))
return nil
}

func genIfName() string {
s, _ := uuid.New().MarshalText() // .MarshalText() always return a nil error
return string(s[:8])
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ require (
github.com/spf13/cobra v1.0.0
github.com/spf13/pflag v1.0.5 // indirect
github.com/vishvananda/netlink v1.1.0
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
gopkg.in/yaml.v2 v2.3.0
)