Skip to content
Draft
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
73 changes: 60 additions & 13 deletions pkg/network/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ 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)

bridgeName := "br_urunc"
br, created, err := createBridge(bridgeName)
if err != nil {
return nil, fmt.Errorf("createBridge(%s) failed: %w", bridgeName, err)
}
if err = netlink.LinkSetUp(br); err != nil {
return nil, fmt.Errorf("LinkSetUp(%s) failed: %w", bridgeName, err)
}

// Create TAP
netlog.Debugf("creating tap device %s (mtu=%d)", tapName, redirectLink.Attrs().MTU)
newTapDevice, err := createTapDevice(tapName, redirectLink.Attrs().MTU, uid, gid)
Expand All @@ -233,6 +243,10 @@ func networkSetup(tapName string, ipAddress string, redirectLink netlink.Link, a
}
netlog.Debugf("created tap device %s (index=%d)", newTapDevice.Attrs().Name, newTapDevice.Attrs().Index)

if err = netlink.LinkSetMaster(newTapDevice, br); err != nil {
return nil, fmt.Errorf("LinkSetMaster(%s, %s) failed: %w", tapName, bridgeName, err)
}

// Bring TAP up before qdisc
if err = netlink.LinkSetUp(newTapDevice); err != nil {
return nil, fmt.Errorf("LinkSetUp(%s) failed: %w", newTapDevice.Attrs().Name, err)
Expand All @@ -246,24 +260,20 @@ func networkSetup(tapName string, ipAddress string, redirectLink netlink.Link, a
netlog.Debugf("redirectLink %s is UP", redirectLink.Attrs().Name)

// Add qdisc + redirect filters
if addTCRules {
netlog.Debug("adding tc ingress qdisc + redirect filters")
if addTCRules && created {
netlog.Debug("adding tc ingress qdisc + redirect filters between redirectLink and bridge")

if err = addIngressQdisc(newTapDevice); err != nil {
return nil, fmt.Errorf("addIngressQdisc(tap=%s) failed: %w",
newTapDevice.Attrs().Name, err)
if err = addIngressQdisc(br); err != nil {
return nil, fmt.Errorf("addIngressQdisc(br=%s) failed: %w", br.Attrs().Name, err)
}
if err = addIngressQdisc(redirectLink); err != nil {
return nil, fmt.Errorf("addIngressQdisc(redirect=%s) failed: %w",
redirectLink.Attrs().Name, err)
return nil, fmt.Errorf("addIngressQdisc(redirect=%s) failed: %w", redirectLink.Attrs().Name, err)
}
if err = addRedirectFilter(newTapDevice, redirectLink); err != nil {
return nil, fmt.Errorf("addRedirectFilter(%s->%s) failed: %w",
newTapDevice.Attrs().Name, redirectLink.Attrs().Name, err)
if err = addRedirectFilter(br, redirectLink); err != nil {
return nil, fmt.Errorf("addRedirectFilter(%s->%s) failed: %w", br.Attrs().Name, redirectLink.Attrs().Name, err)
}
if err = addRedirectFilter(redirectLink, newTapDevice); err != nil {
return nil, fmt.Errorf("addRedirectFilter(%s->%s) failed: %w",
redirectLink.Attrs().Name, newTapDevice.Attrs().Name, err)
if err = addRedirectFilter(redirectLink, br); err != nil {
return nil, fmt.Errorf("addRedirectFilter(%s->%s) failed: %w", redirectLink.Attrs().Name, br.Attrs().Name, err)
}
}

Expand Down Expand Up @@ -300,12 +310,17 @@ func CleanupAllUruncTaps() error {

var retErr error
tapRe := regexp.MustCompile(`^tap\d+_urunc$`)
var brLink netlink.Link
for _, link := range links {
attrs := link.Attrs()
if attrs == nil {
continue
}
name := attrs.Name
if name == "br_urunc" {
brLink = link
continue
}
if !tapRe.MatchString(name) {
continue
}
Expand All @@ -330,6 +345,22 @@ func CleanupAllUruncTaps() error {
retErr = errors.Join(retErr, devErr)
}

if brLink != nil {
netlog.Debugf("cleaning up bridge br_urunc")
if err := deleteAllTCFilters(brLink); err != nil {
netlog.Errorf("failed to delete TC filters for br_urunc: %v", err)
retErr = errors.Join(retErr, err)
}
if err := deleteAllQDiscs(brLink); err != nil {
netlog.Errorf("failed to delete qdiscs for br_urunc: %v", err)
retErr = errors.Join(retErr, err)
}
if err := netlink.LinkDel(brLink); err != nil {
netlog.Errorf("failed to delete bridge br_urunc: %v", err)
retErr = errors.Join(retErr, err)
}
}

return retErr
}

Expand Down Expand Up @@ -461,3 +492,19 @@ func deleteTapDevice(device netlink.Link) error {
}
return nil
}

func createBridge(bridgeName string) (netlink.Link, bool, error) {
link, err := netlink.LinkByName(bridgeName)
if err == nil {
return link, false, nil
}
br := &netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: bridgeName}}
if err := netlink.LinkAdd(br); err != nil {
return nil, false, fmt.Errorf("could not add bridge %s: %w", bridgeName, err)
}
link, err = netlink.LinkByName(bridgeName)
if err != nil {
return nil, false, fmt.Errorf("could not get bridge %s: %w", bridgeName, err)
}
return link, true, nil
}
7 changes: 1 addition & 6 deletions pkg/network/network_dynamic.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,12 @@ type DynamicNetwork struct {
// If no TAP devices are available in the current netns, it creates a new tap device and
// sets TC rules between the veth interface and the tap device inside the namespace.
//
// FIXME: CUrrently only one tap device per netns can provide functional networking. We need to find a proper way to handle networking
// for multiple unikernels in the same pod/network namespace.
// See: https://github.com/urunc-dev/urunc/issues/13
// sets TC rules between the veth interface and the tap device inside the namespace.
func (n DynamicNetwork) NetworkSetup(uid uint32, gid uint32) (*UnikernelNetworkInfo, error) {
tapIndex, err := getTapIndex()
if err != nil {
return nil, fmt.Errorf("getTapIndex failed: %w", err)
}
if tapIndex > 0 {
return nil, fmt.Errorf("unsupported operation: can't spawn multiple unikernels in the same network namespace")
}

redirectLink, err := discoverContainerIface()
if err != nil {
Expand Down
107 changes: 95 additions & 12 deletions pkg/unikontainers/hypervisors/hedge.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,24 @@ const (
ConsoleEndpoint = "/proc/vmcons"
)

type Hedge struct{}
type Hedge struct {
binary string
binaryPath string
}

func (h *Hedge) Ok() error {
return fmt.Errorf("hedge not implemented yet")
if err := hedge.Status(); err != nil {
return ErrVMMNotInstalled
}
return nil
}

func (h *Hedge) Signal(_ int, _ unix.Signal) error {
return fmt.Errorf("hedge not implemented yet")
func (h *Hedge) Signal(pid int, signal unix.Signal) error {
return unix.Kill(pid, signal)
}

func (h *Hedge) Stop(_ int) error {
return fmt.Errorf("hedge not implemented yet")
func (h *Hedge) Stop(pid int) error {
return killProcess(pid)
}

func (h *Hedge) UsesKVM() bool {
Expand All @@ -52,16 +58,93 @@ func (h *Hedge) SupportsSharedfs(_ string) bool {
}

func (h *Hedge) Path() string {
return ""
if h.binaryPath != "" {
return h.binaryPath
}
return hedge.MONITOR_ENDPOINT
}

func (h *Hedge) BuildExecCmd(_ types.ExecArgs, _ types.Unikernel) ([]string, error) {
return nil, fmt.Errorf("hedge not implemented yet")
func (h *Hedge) BuildExecCmd(args types.ExecArgs, ukernel types.Unikernel) ([]string, error) {
memMB := int(bytesToMB(args.MemSizeB))
if memMB <= 0 {
memMB = int(DefaultMemory)
}

netDev := ""
if args.Net.TapDev != "" {
netDev = ukernel.MonitorNetCli(args.Net.TapDev, args.Net.MAC)
}

blkDev := ""
bArgs := ukernel.MonitorBlockCli()
for _, blockArg := range bArgs {
if blkDev != "" {
blkDev += " "
}
blkDev += "--block:" + blockArg.ID + "=" + blockArg.Path
}

conf := hedge.VMConfig{
Name: args.ContainerID,
Binary: args.UnikernelPath,
CPU: int(args.VCPUs),
Mem: memMB,
Blk: blkDev,
Net: netDev,
CmdLine: args.Command,
}

if err := conf.Validate(); err != nil {
return nil, fmt.Errorf("hedge vm config validation failed: %w", err)
}

execCmd := []string{
"hedge",
"--name", conf.Name,
"--binary", conf.Binary,
"--cpu", fmt.Sprintf("%d", conf.CPU),
"--mem", fmt.Sprintf("%d", conf.Mem),
"--cmdline", conf.CmdLine,
}
if conf.Net != "" {
execCmd = append(execCmd, "--net", conf.Net)
}
if conf.Blk != "" {
execCmd = append(execCmd, "--blk", conf.Blk)
}

return execCmd, nil
}

// PreExec performs pre-execution setup. Hedge is not fully implemented.
func (h *Hedge) PreExec(_ types.ExecArgs) error {
return fmt.Errorf("hedge not implemented yet")
// PreExec performs pre-execution setup for Hedge using hedge_api.
func (h *Hedge) PreExec(args types.ExecArgs) error {
memMB := int(bytesToMB(args.MemSizeB))
if memMB <= 0 {
memMB = int(DefaultMemory)
}

netDev := ""
if args.Net.TapDev != "" {
netDev = args.Net.TapDev
}

blkDev := ""

conf := hedge.VMConfig{
Name: args.ContainerID,
Binary: args.UnikernelPath,
CPU: int(args.VCPUs),
Mem: memMB,
Blk: blkDev,
Net: netDev,
CmdLine: args.Command,
}

if err := conf.Validate(); err != nil {
return fmt.Errorf("hedge vm config validation failed: %w", err)
}

return hedge.StartVM(conf)
}

func (h *Hedge) VMState(name string) string {
Expand Down