Skip to content
Open
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
54 changes: 54 additions & 0 deletions link_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -2113,6 +2113,60 @@ func (h *Handle) LinkByIndex(index int) (Link, error) {
return execGetLink(req)
}

// LinkByIndexAndNsid finds a link by index in a different namespace and returns a pointer to the object
func LinkByIndexAndNsid(index int, nsid int) (Link, error) {
return pkgHandle.LinkByIndexAndNsid(index, nsid)
}

// LinkByIndexAndNsid finds a link by index in a different namespace and returns a pointer to the object
func (h *Handle) LinkByIndexAndNsid(index int, nsid int) (Link, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_ACK)

msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
msg.Index = int32(index)
req.AddData(msg)

attr := nl.NewRtAttr(unix.IFLA_TARGET_NETNSID, nl.Uint32Attr(uint32(nsid)))
req.AddData(attr)

if h.options.collectVFInfo {
attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF))
req.AddData(attr)
}

return execGetLink(req)
}

// LinkByNameAndNsid finds a link by name in a different namespace and returns a pointer to the object
func LinkByNameAndNsid(name string, nsid int) (Link, error) {
return pkgHandle.LinkByNameAndNsid(name, nsid)
}

// LinkByNameAndNsid finds a link by name in a different namespace and returns a pointer to the object
func (h *Handle) LinkByNameAndNsid(name string, nsid int) (Link, error) {
req := h.newNetlinkRequest(unix.RTM_GETLINK, unix.NLM_F_ACK)

msg := nl.NewIfInfomsg(unix.AF_UNSPEC)
req.AddData(msg)

attr := nl.NewRtAttr(unix.IFLA_TARGET_NETNSID, nl.Uint32Attr(uint32(nsid)))
req.AddData(attr)

if h.options.collectVFInfo {
attr := nl.NewRtAttr(unix.IFLA_EXT_MASK, nl.Uint32Attr(nl.RTEXT_FILTER_VF))
req.AddData(attr)
}

nameData := nl.NewRtAttr(unix.IFLA_IFNAME, nl.ZeroTerminated(name))
if len(name) > 15 {
nameData = nl.NewRtAttr(unix.IFLA_ALT_IFNAME, nl.ZeroTerminated(name))
}
req.AddData(nameData)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
link, err := execGetLink(req)
return link, err
}

func execGetLink(req *nl.NetlinkRequest) (Link, error) {
msgs, err := req.Execute(unix.NETLINK_ROUTE, 0)
if err != nil {
Expand Down
32 changes: 32 additions & 0 deletions link_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2220,6 +2220,38 @@ func TestLinkByIndex(t *testing.T) {
}
}

func TestLinkByIndexAndNsid(t *testing.T) {
minKernelRequired(t, 4, 15)
t.Cleanup(setUpNetlinkTest(t))
nsId, linkIdx, linkName, cleanup := setUpNamespaceWithLink(t)
t.Cleanup(cleanup)

link, err := LinkByIndexAndNsid(linkIdx, nsId)
if err != nil {
t.Fatal(err)
}

if link.Attrs().Name != linkName {
t.Fatal("did not get the correct link")
}
}

func TestLinkByNameAndNsid(t *testing.T) {
minKernelRequired(t, 4, 15)
t.Cleanup(setUpNetlinkTest(t))
nsId, linkIdx, linkName, cleanup := setUpNamespaceWithLink(t)
t.Cleanup(cleanup)

link, err := LinkByNameAndNsid(linkName, nsId)
if err != nil {
t.Fatal(err)
}

if link.Attrs().Index != linkIdx {
t.Fatal("did not get the correct link")
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

func TestLinkSet(t *testing.T) {
t.Cleanup(setUpNetlinkTest(t))

Expand Down
77 changes: 77 additions & 0 deletions netlink_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"log"
Expand Down Expand Up @@ -264,6 +265,82 @@ func setUpNamedNetlinkTestWithKModule(t *testing.T, moduleNames ...string) (stri
return setUpNamedNetlinkTest(t)
}

func setUpNamespaceWithLink(t *testing.T) (nsId int, linkId int, linkName string, teardown tearDownNetlinkTest) {
skipUnlessRoot(t)
nsName, cleanupNs := setUpNamedNetlinkTest(t)

ok := false
defer func() {
if !ok {
cleanupNs()
}
}()

cmd := exec.Command("ip", "netns", "set", nsName, "auto")
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if strings.Contains(stderr.String(), "Invalid") {
t.Skip("iproute2 version is too old")
}

t.Fatalf("Failed to run: ip netns set %s auto", nsName)
}

// create a random name
rnd := make([]byte, 4)
if _, err := rand.Read(rnd); err != nil {
t.Fatal("failed creating random link name")
}
name := hex.EncodeToString(rnd)

if err := exec.Command("ip", "netns", "exec", nsName, "ip", "link", "add", name, "type", "dummy").Run(); err != nil {
t.Fatalf("Failed to run: ip netns exec %s ip link add %s type dummy", nsName, name)
}

cmd = exec.Command("ip", "netns", "exec", nsName, "ip", "--json", "link", "show", name)
var stdout bytes.Buffer
stderr.Reset()
cmd.Stdout = &stdout
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
if strings.Contains(stderr.String(), "unknown") {
t.Skip("iproute2 version is too old")
}

t.Fatalf("Failed to run: %s", cmd)
}

var ipOutput []struct {
Index int `json:"ifindex"`
}

err := json.Unmarshal(stdout.Bytes(), &ipOutput)
if err != nil {
t.Fatalf("Failed to parse json")
}

if len(ipOutput) != 1 {
t.Fatalf("failed to get created link")
}

ns, err := netns.GetFromName(nsName)
if err != nil {
t.Fatalf("getting ns: %v", err)
}

defer ns.Close()

nsid, err := GetNetNsIdByFd(int(ns))
if err != nil {
t.Fatalf("getting nsid: %v", err)
}

ok = true

return nsid, ipOutput[0].Index, name, cleanupNs
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

func remountSysfs() error {
if err := unix.Mount("", "/", "none", unix.MS_SLAVE|unix.MS_REC, ""); err != nil {
return err
Expand Down