forked from vishvananda/netlink
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add network namespace ID management.
Adds the ability to set and retrieve network namespace IDs. This is useful, for example, for determining the "other side" of a veth pair.
- Loading branch information
1 parent
d68dce4
commit 3ac69fd
Showing
4 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package netlink | ||
|
||
// Network namespace ID functions | ||
// | ||
// The kernel has a weird concept called the network namespace ID. | ||
// This is different from the file reference in proc (and any bind-mounted | ||
// namespaces, etc.) | ||
// | ||
// Instead, namespaces can be assigned a numeric ID at any time. Once set, | ||
// the ID is fixed. The ID can either be set manually by the user, or | ||
// automatically, triggered by certain kernel actions. The most common kernel | ||
// action that triggers namespace ID creation is moving one end of a veth pair | ||
// in to that namespace. | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/vishvananda/netlink/nl" | ||
"golang.org/x/sys/unix" | ||
) | ||
|
||
// These can be replaced by the values from sys/unix when it is next released. | ||
const ( | ||
_ = iota | ||
NETNSA_NSID | ||
NETNSA_PID | ||
NETNSA_FD | ||
) | ||
|
||
// GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id). | ||
// Returns -1 if the namespace does not have an ID set. | ||
func (h *Handle) GetNetNsIdByPid(pid int) (int, error) { | ||
return h.getNetNsId(NETNSA_PID, uint32(pid)) | ||
} | ||
|
||
// GetNetNsIdByPid looks up the network namespace ID for a given pid (really thread id). | ||
// Returns -1 if the namespace does not have an ID set. | ||
func GetNetNsIdByPid(pid int) (int, error) { | ||
return pkgHandle.GetNetNsIdByPid(pid) | ||
} | ||
|
||
// SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id). | ||
// The ID can only be set for namespaces without an ID already set. | ||
func (h *Handle) SetNetNsIdByPid(pid, nsid int) error { | ||
return h.setNetNsId(NETNSA_PID, uint32(pid), uint32(nsid)) | ||
} | ||
|
||
// SetNetNSIdByPid sets the ID of the network namespace for a given pid (really thread id). | ||
// The ID can only be set for namespaces without an ID already set. | ||
func SetNetNsIdByPid(pid, nsid int) error { | ||
return pkgHandle.SetNetNsIdByPid(pid, nsid) | ||
} | ||
|
||
// GetNetNsIdByPid looks up the network namespace ID for a given fd. | ||
// fd must be an open file descriptor to a namespace file. | ||
// Returns -1 if the namespace does not have an ID set. | ||
func (h *Handle) GetNetNsIdByFd(fd int) (int, error) { | ||
return h.getNetNsId(NETNSA_FD, uint32(fd)) | ||
} | ||
|
||
// GetNetNsIdByPid looks up the network namespace ID for a given fd. | ||
// fd must be an open file descriptor to a namespace file. | ||
// Returns -1 if the namespace does not have an ID set. | ||
func GetNetNsIdByFd(fd int) (int, error) { | ||
return pkgHandle.GetNetNsIdByFd(fd) | ||
} | ||
|
||
// SetNetNSIdByFd sets the ID of the network namespace for a given fd. | ||
// fd must be an open file descriptor to a namespace file. | ||
// The ID can only be set for namespaces without an ID already set. | ||
func (h *Handle) SetNetNsIdByFd(fd, nsid int) error { | ||
return h.setNetNsId(NETNSA_FD, uint32(fd), uint32(nsid)) | ||
} | ||
|
||
// SetNetNSIdByFd sets the ID of the network namespace for a given fd. | ||
// fd must be an open file descriptor to a namespace file. | ||
// The ID can only be set for namespaces without an ID already set. | ||
func SetNetNsIdByFd(fd, nsid int) error { | ||
return pkgHandle.SetNetNsIdByFd(fd, nsid) | ||
} | ||
|
||
// getNetNsId requests the netnsid for a given type-val pair | ||
// type should be either NETNSA_PID or NETNSA_FD | ||
func (h *Handle) getNetNsId(attrType int, val uint32) (int, error) { | ||
req := h.newNetlinkRequest(unix.RTM_GETNSID, unix.NLM_F_REQUEST) | ||
|
||
rtgen := nl.NewRtGenMsg() | ||
req.AddData(rtgen) | ||
|
||
b := make([]byte, 4, 4) | ||
native.PutUint32(b, val) | ||
attr := nl.NewRtAttr(attrType, b) | ||
req.AddData(attr) | ||
|
||
msgs, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID) | ||
|
||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
for _, m := range msgs { | ||
msg := nl.DeserializeRtGenMsg(m) | ||
|
||
attrs, err := nl.ParseRouteAttr(m[msg.Len():]) | ||
if err != nil { | ||
return 0, err | ||
} | ||
|
||
for _, attr := range attrs { | ||
switch attr.Attr.Type { | ||
case NETNSA_NSID: | ||
return int(int32(native.Uint32(attr.Value))), nil | ||
} | ||
} | ||
} | ||
|
||
return 0, fmt.Errorf("unexpected empty result") | ||
} | ||
|
||
// setNetNsId sets the netnsid for a given type-val pair | ||
// type should be either NETNSA_PID or NETNSA_FD | ||
// The ID can only be set for namespaces without an ID already set | ||
func (h *Handle) setNetNsId(attrType int, val uint32, newnsid uint32) error { | ||
req := h.newNetlinkRequest(unix.RTM_NEWNSID, unix.NLM_F_REQUEST|unix.NLM_F_ACK) | ||
|
||
rtgen := nl.NewRtGenMsg() | ||
req.AddData(rtgen) | ||
|
||
b := make([]byte, 4, 4) | ||
native.PutUint32(b, val) | ||
attr := nl.NewRtAttr(attrType, b) | ||
req.AddData(attr) | ||
|
||
b1 := make([]byte, 4, 4) | ||
native.PutUint32(b1, newnsid) | ||
attr1 := nl.NewRtAttr(NETNSA_NSID, b1) | ||
req.AddData(attr1) | ||
|
||
_, err := req.Execute(unix.NETLINK_ROUTE, unix.RTM_NEWNSID) | ||
return err | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// +build linux | ||
|
||
package netlink | ||
|
||
import ( | ||
"os" | ||
"runtime" | ||
"syscall" | ||
"testing" | ||
|
||
"github.com/vishvananda/netns" | ||
) | ||
|
||
// TestNetNsIdByFd tests setting and getting the network namespace ID | ||
// by file descriptor. It opens a namespace fd, sets it to a random id, | ||
// then retrieves the ID. | ||
// This does not do any namespace switching. | ||
func TestNetNsIdByFd(t *testing.T) { | ||
// create a network namespace | ||
ns, err := netns.New() | ||
CheckErrorFail(t, err) | ||
|
||
// set its ID | ||
// In an attempt to avoid namespace id collisions, set this to something | ||
// insanely high. When the kernel assigns IDs, it does so starting from 0 | ||
// So, just use our pid shifted up 16 bits | ||
wantID := os.Getpid() << 16 | ||
|
||
h, err := NewHandle() | ||
CheckErrorFail(t, err) | ||
err = h.SetNetNsIdByFd(int(ns), wantID) | ||
CheckErrorFail(t, err) | ||
|
||
// Get the ID back, make sure it matches | ||
haveID, err := h.GetNetNsIdByFd(int(ns)) | ||
if haveID != wantID { | ||
t.Errorf("GetNetNsIdByFd returned %d, want %d", haveID, wantID) | ||
} | ||
|
||
ns.Close() | ||
} | ||
|
||
// TestNetNsIdByPid tests manipulating namespace IDs by pid (really, task / thread id) | ||
// Does the same as TestNetNsIdByFd, but we need to change namespaces so we | ||
// actually have a pid in that namespace | ||
func TestNetNsIdByPid(t *testing.T) { | ||
runtime.LockOSThread() // we need a constant OS thread | ||
origNs, _ := netns.Get() | ||
|
||
// create and enter a new netns | ||
ns, err := netns.New() | ||
CheckErrorFail(t, err) | ||
err = netns.Set(ns) | ||
CheckErrorFail(t, err) | ||
// make sure we go back to the original namespace when done | ||
defer func() { | ||
err := netns.Set(origNs) | ||
if err != nil { | ||
panic("failed to restore network ns, bailing!") | ||
} | ||
runtime.UnlockOSThread() | ||
}() | ||
|
||
// As above, we'll pick a crazy large netnsid to avoid collisions | ||
wantID := syscall.Gettid() << 16 | ||
|
||
h, err := NewHandle() | ||
CheckErrorFail(t, err) | ||
err = h.SetNetNsIdByPid(syscall.Gettid(), wantID) | ||
CheckErrorFail(t, err) | ||
|
||
//Get the ID and see if it worked | ||
haveID, err := h.GetNetNsIdByPid(syscall.Gettid()) | ||
if haveID != wantID { | ||
t.Errorf("GetNetNsIdByPid returned %d, want %d", haveID, wantID) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// +build !linux | ||
|
||
package netlink | ||
|
||
func GetNetNsIdByPid(pid int) (int, error) { | ||
return 0, ErrNotImplemented | ||
} | ||
|
||
func SetNetNsIdByPid(pid, nsid int) error { | ||
return ErrNotImplemented | ||
} | ||
|
||
func GetNetNsIdByFd(fd int) (int, error) { | ||
return 0, ErrNotImplemented | ||
} | ||
|
||
func SetNetNsIdByFd(fd, nsid int) error { | ||
return ErrNotImplemented | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters