Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/contributors.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ users:
IrvingMg:
name: Irving Mondragón
email: mirvingr@gmail.com
sidneychang:
name: Xuedong Zhang
email: 2190206983@qq.com
94 changes: 63 additions & 31 deletions pkg/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ import (
)

const (
DefaultInterface = "eth0" // FIXME: Discover the veth endpoint name instead of using default "eth0". See: https://github.com/urunc-dev/urunc/issues/14
DefaultTap = "tapX_urunc"
DefaultTap = "tapX_urunc"
)

var netlog = logrus.WithField("subsystem", "network")
Expand Down Expand Up @@ -120,21 +119,6 @@ func createTapDevice(name string, mtu int, ownerUID, ownerGID uint32) (netlink.L
return tapLink, nil
}

// ensureEth0Exists checks all network interfaces in current netns and returns
// nil if eth0 is present or ErrEth0NotFound if not
func ensureEth0Exists() error {
ifaces, err := net.Interfaces()
if err != nil {
return err
}
for _, iface := range ifaces {
if iface.Name == DefaultInterface {
return nil
}
}
return errors.New("eth0 device not found")
}

func getInterfaceInfo(iface string) (Interface, error) {
ief, err := net.InterfaceByName(iface)
if err != nil {
Expand Down Expand Up @@ -163,7 +147,7 @@ func getInterfaceInfo(iface string) (Interface, error) {
}
}
if mask == "" {
return Interface{}, fmt.Errorf("failed to find mask for %q", DefaultInterface)
return Interface{}, fmt.Errorf("failed to find mask for %q", iface)
}
// convert to decimal notation
decimalParts := make([]string, len(netMask))
Expand All @@ -172,7 +156,7 @@ func getInterfaceInfo(iface string) (Interface, error) {
}
mask = strings.Join(decimalParts, ".")
if ipAddress == "" {
return Interface{}, fmt.Errorf("failed to find IPv4 address for %q", DefaultInterface)
return Interface{}, fmt.Errorf("failed to find IPv4 address for %q", iface)
}
gateway, err := gateway.DiscoverGateway()
if err != nil {
Expand All @@ -182,7 +166,7 @@ func getInterfaceInfo(iface string) (Interface, error) {
IP: ipAddress,
DefaultGateway: gateway.String(),
Mask: mask,
Interface: DefaultInterface,
Interface: iface,
MAC: IfMAC,
}, nil
}
Expand Down Expand Up @@ -219,14 +203,6 @@ func addRedirectFilter(source netlink.Link, target netlink.Link) error {
func networkSetup(tapName string, ipAddress string, redirectLink netlink.Link, addTCRules bool, uid uint32, gid uint32) (netlink.Link, error) {
netlog.Debugf("starting for tapName=%s ipAddress=%s redirectLink=%s addTCRules=%v",
tapName, ipAddress, redirectLink.Attrs().Name, addTCRules)

// Sanity check: ensure eth0 exists in this namespace
err := ensureEth0Exists()
if err != nil {
netlog.Warnf("eth0 interface not found in namespace (unikernel may have been spawned using ctr): %v", err)
return nil, nil
}

// Create TAP
netlog.Debugf("creating tap device %s (mtu=%d)", tapName, redirectLink.Attrs().MTU)
newTapDevice, err := createTapDevice(tapName, redirectLink.Attrs().MTU, uid, gid)
Expand Down Expand Up @@ -333,12 +309,69 @@ func deleteIngressQdisc(link netlink.Link) error {
return nil
}

// discoverContainerIface discovers the container's network interface by
// scanning all links in the current network namespace and returning the first
// non-loopback link that has an IP address and a default route.
func discoverContainerIface() (netlink.Link, error) {
handle, err := netlink.NewHandle()
if err != nil {
return nil, err
}
defer handle.Close()
links, err := handle.LinkList()
if err != nil {
return nil, err
}
for _, link := range links {
attrs := link.Attrs()
if attrs == nil {
netlog.Debug("skipping link with nil attributes")
continue
}
// skip loopback
if (attrs.Flags & net.FlagLoopback) != 0 {
netlog.Debugf("skipping loopback interface %s", attrs.Name)
continue
}
// must have addresses configured
addrs, err := handle.AddrList(link, netlink.FAMILY_ALL)
if err != nil {
netlog.Debugf("skipping interface %s: failed to list addresses: %v", attrs.Name, err)
continue
}
if len(addrs) == 0 {
netlog.Debugf("skipping interface %s: no addresses configured", attrs.Name)
continue
}
// look for a default route on this interface
routes, err := handle.RouteList(link, netlink.FAMILY_ALL)
if err != nil {
netlog.Debugf("skipping interface %s: failed to list routes: %v", attrs.Name, err)
continue
}
for _, r := range routes {
// default route: Dst == nil OR represented as 0.0.0.0/0 or ::/0
if r.Dst == nil {
return link, nil
}
if r.Dst != nil {
dstStr := r.Dst.String()
if dstStr == "0.0.0.0/0" || dstStr == "::/0" {
return link, nil
}
}
}
netlog.Debugf("skipping interface %s: no default route found", attrs.Name)
}
return nil, errors.New("no suitable network interface found in namespace")
}

func deleteAllQDiscs(device netlink.Link) error {
err := deleteIngressQdisc(device)
if err != nil {
return err
}
device, err = netlink.LinkByName(DefaultInterface)
device, err = discoverContainerIface()
if err != nil {
return err
}
Expand All @@ -357,8 +390,7 @@ func deleteAllTCFilters(device netlink.Link) error {
return nil
}
allFilters = append(allFilters, tapFilters...)

device, err = netlink.LinkByName(DefaultInterface)
device, err = discoverContainerIface()
if err != nil {
return err
}
Expand Down
13 changes: 5 additions & 8 deletions pkg/network/network_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import (
"fmt"
"strconv"
"strings"

"github.com/vishvananda/netlink"
)

type DynamicNetwork struct {
Expand All @@ -43,10 +41,9 @@ func (n DynamicNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkI
return nil, fmt.Errorf("unsupported operation: can't spawn multiple unikernels in the same network namespace")
}

netlog.Debugf("trying LinkByName(%s)", DefaultInterface)
redirectLink, err := netlink.LinkByName(DefaultInterface)
redirectLink, err := discoverContainerIface()
if err != nil {
return nil, fmt.Errorf("failed to find interface %s: %w", DefaultInterface, err)
return nil, fmt.Errorf("failed to find container interface, (unikernel may have been spawned using ctr): %w", err)
}
netlog.Debugf("found interface %s (index=%d)", redirectLink.Attrs().Name, redirectLink.Attrs().Index)

Expand All @@ -59,10 +56,10 @@ func (n DynamicNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkI
}
netlog.Debugf("tap device created: %s", newTapDevice.Attrs().Name)

netlog.Debugf("fetching info for %s", DefaultInterface)
ifInfo, err := getInterfaceInfo(DefaultInterface)
netlog.Debugf("fetching info for %s", redirectLink.Attrs().Name)
ifInfo, err := getInterfaceInfo(redirectLink.Attrs().Name)
if err != nil {
return nil, fmt.Errorf("getInterfaceInfo(%s) failed: %w", DefaultInterface, err)
return nil, fmt.Errorf("getInterfaceInfo(%s) failed: %w", redirectLink.Attrs().Name, err)
}

return &UnikernelNetworkInfo{
Expand Down
9 changes: 4 additions & 5 deletions pkg/network/network_static.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"strings"

"github.com/urunc-dev/urunc/internal/constants"
"github.com/vishvananda/netlink"
)

var StaticIPAddr = fmt.Sprintf("%s/24", constants.StaticNetworkTapIP)
Expand Down Expand Up @@ -92,16 +91,16 @@ func setNATRule(iface string, sourceIP string) error {
func (n StaticNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkInfo, error) {
newTapName := strings.ReplaceAll(DefaultTap, "X", "0")
addTCRules := false
redirectLink, err := netlink.LinkByName(DefaultInterface)
redirectLink, err := discoverContainerIface()
if err != nil {
netlog.Errorf("failed to find %s interface", DefaultInterface)
netlog.Errorf("failed to find container interface, (unikernel may have been spawned using ctr): %v", err)
return nil, err
}
newTapDevice, err := networkSetup(newTapName, StaticIPAddr, redirectLink, addTCRules, uid, gid)
if err != nil {
return nil, err
}
err = setNATRule(DefaultInterface, StaticIPAddr)
err = setNATRule(redirectLink.Attrs().Name, StaticIPAddr)
if err != nil {
return nil, err
}
Expand All @@ -111,7 +110,7 @@ func (n StaticNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkIn
IP: constants.StaticNetworkUnikernelIP,
DefaultGateway: constants.StaticNetworkTapIP,
Mask: "255.255.255.0",
Interface: DefaultInterface, // or tap0_urunc?
Interface: redirectLink.Attrs().Name, // or tap0_urunc?
MAC: redirectLink.Attrs().HardwareAddr.String(),
},
}, nil
Expand Down
Loading