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

replace macvlan with veth pairs #2414

Merged
merged 8 commits into from
Oct 15, 2024
9 changes: 6 additions & 3 deletions pkg/network/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,14 @@ func Attach(link netlink.Link, bridge *netlink.Bridge, vlan *uint16, name ...str
n = name[0]
}
//we need to create an veth pair to wire 2 bridges.
veth, err := ifaceutil.MakeVethPair(n, bridge.Name, 1500)
if err != nil {
return err
if err := ifaceutil.MakeVethPair(n, bridge.Name, 1500, nil); err != nil {
return fmt.Errorf("failed to create veth pair from link %q to bridge %q: %w", n, bridge.Name, err)
}

veth, err := netlink.LinkByName(n)
if err != nil {
return fmt.Errorf("no link found with name %q: %w", n, err)
}
return attachNic(veth, linkBr, vlan)
}

Expand Down
89 changes: 67 additions & 22 deletions pkg/network/ifaceutil/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ns"
"github.com/pkg/errors"

Expand Down Expand Up @@ -133,25 +134,43 @@ func RandomName(prefix string) (string, error) {
return fmt.Sprintf("%s%x", prefix, b), nil
}

// MakeVethPair creates a veth pair
func MakeVethPair(name, master string, mtu int) (netlink.Link, error) {
masterLink, err := netlink.LinkByName(master)
if err != nil {
return nil, err
// MakeVethPair attach the given interface to the given bridge with veth cable
// in case of providing a namespace, the interface will be moved there
//
// - name: name of the veth interface
// - master: name of the master bridge to attach the veth peer to
// - mtu: mtu
// - netNs: network namespace to move the veth interface into. (could be nil)
func MakeVethPair(name, master string, mtu int, netNs ns.NetNS) error {
var err error

// 1. Create a Veth link
ifName := name
if netNs != nil {
// due to kernel bug we have to create with temp name or it might
// collide with the name on the host and error out
// temp name will be renamed after moving it to the namespace
ifName, err = ip.RandomVethName()
if err != nil {
return err
}
}

peerName := fmt.Sprintf("p-%s", name)
if _, err = netlink.LinkByName(peerName); err == nil {
return fmt.Errorf("peer already exists %q", peerName)
}

peer := fmt.Sprintf("%s-p", name)
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{
Name: name,
Name: ifName,
Flags: net.FlagUp,
MTU: mtu,
},
PeerName: peer,
PeerName: peerName,
}

if err = netlink.LinkAdd(veth); err != nil {
return nil, err
if err := netlink.LinkAdd(veth); err != nil {
return fmt.Errorf("failed to add the veth: %w", err)
}

defer func() {
Expand All @@ -160,23 +179,49 @@ func MakeVethPair(name, master string, mtu int) (netlink.Link, error) {
}
}()

peerLink, err := netlink.LinkByName(peer)
if err = netlink.LinkSetMaster(peerLink, masterLink); err != nil {
return nil, err
// 2. attach the link to the master
masterLink, err := netlink.LinkByName(master)
if err != nil {
return fmt.Errorf("master link: %s not found: %v", master, err)
}

// make sure the lowerhalf is up, this automatically sets the upperhalf UP
if err = netlink.LinkSetUp(peerLink); err != nil {
return nil, errors.Wrap(err, "could not set veth peer up")
peerLink, err := netlink.LinkByName(peerName)
if err != nil {
return fmt.Errorf("failed to get peer link %q: %v", peerName, err)
}

// Re-fetch the link to get its creation-time parameters, e.g. index and mac
veth2, err := netlink.LinkByName(name)
if err != nil {
return nil, err
if err := netlink.LinkSetMaster(peerLink, masterLink); err != nil {
return fmt.Errorf("failed to set master for peer link %q: %w", peerName, err)
}

// 3. move to the namespace and rename
if netNs != nil {
if err = moveLinkToNamespace(veth, ifName, name, netNs); err != nil {
return err
}
}

// 4. make sure the lowerhalf is up, this automatically sets the upperhalf UP
if err := netlink.LinkSetUp(peerLink); err != nil {
return fmt.Errorf("failed to set veth peer up: %w", err)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this function should return anything because the veth can be inside a namespace. So returning a link that is inside a namespace does not make any sense. Normally to work with links that lives inside a namespace you have to do

ns.Run(func() {
  // load the nic here
  // work with the nic
}

return veth2, nil
return nil
}

// moveLinkToNamespace moves the veth link to the specified namespace and renames it
func moveLinkToNamespace(veth netlink.Link, name, renameTo string, netNs ns.NetNS) error {
if err := netlink.LinkSetNsFd(veth, int(netNs.Fd())); err != nil {
return fmt.Errorf("failed to move link: %s to namespace:%s : %w", renameTo, netNs.Path(), err)
}

return netNs.Do(func(nn ns.NetNS) error {
if err := ip.RenameLink(name, renameTo); err != nil {
_ = netlink.LinkDel(veth)
return fmt.Errorf("failed to rename veth to %q: %v", renameTo, err)
}
return nil
})
}

// VethByName loads one end of a veth pair given its name
Expand Down
Loading
Loading