Skip to content

Instantly share code, notes, and snippets.

@thediveo
Last active April 29, 2023 20:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save thediveo/bc22cd3dd96edf12ea1daafeeacf9bad to your computer and use it in GitHub Desktop.
Save thediveo/bc22cd3dd96edf12ea1daafeeacf9bad to your computer and use it in GitHub Desktop.
Discovering the network namespace an (RT)NETLINK socket connects to
module github.com/thediveo/netlinknetns
go 1.20
require (
github.com/vishvananda/netlink v1.2.1-beta.2.0.20230316163032-ced5aaba43e3
golang.org/x/sys v0.7.0
)
require github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae // indirect
// see also: https://stackoverflow.com/questions/69773272/how-to-discover-the-network-namespace-an-rtnetlink-socket-connects-to
package main
import (
"os"
"reflect"
"runtime"
"strconv"
"strings"
"syscall"
"time"
"unsafe"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netlink/nl"
"golang.org/x/sys/unix"
)
// creator creates a new network namespace, enters it and passed back a
// reference to it, then waits to be signalled to exit the network namespace so
// it might collapse if everything goes to plan. (which plan???)
//
// This function must be called on its own goroutine.
func creator(done <-chan struct{}, handle chan<- *netlink.Handle) {
println("creator thread:", syscall.Gettid())
// Make sure to lock this goroutine to its current OS-level thread, as the
// unshare syscall will affect only the thread it is called from. Switching
// threads would be ... slightly bad. We don't unlock, so upon return and
// fallout off the edge of our disk world this OS thread will be killed and
// never reused.
runtime.LockOSThread()
println("creator: locked OS-level thread")
err := syscall.Unshare(syscall.CLONE_NEWNET)
if err != nil {
panic("cannot create and enter new network namespace: " + err.Error())
}
netnslnk, err := os.Stat("/proc/thread-self/ns/net")
if err != nil {
panic("cannot determine task's new network namespace: " + err.Error())
}
println("creator: in new network namespace net:[" +
strconv.FormatUint(netnslnk.Sys().(*syscall.Stat_t).Ino, 10) + "]")
nlHandle, err := netlink.NewHandle(unix.NETLINK_ROUTE)
if err != nil {
panic("cannot open RTNETLINK connection: " + err.Error())
}
println("creator: sending RTNETLINK handle")
handle <- nlHandle
println("creator: handle sent")
<-done // wait for channel to be closed.
println("creator: falling off")
// ...simply fall off the edge.
}
func main() {
runtime.LockOSThread()
println("main thread:", syscall.Gettid())
done := make(chan struct{})
handle := make(chan *netlink.Handle)
go creator(done, handle)
nlHandle := <-handle
println("received RTNETLINK handle")
netnslnk, err := os.Stat("/proc/thread-self/ns/net")
if err != nil {
panic("cannot determine main task's network namespace: " + err.Error())
}
println("main is still in network namespace net:[" +
strconv.FormatUint(netnslnk.Sys().(*syscall.Stat_t).Ino, 10) + "]")
rNlhandle := reflect.ValueOf(nlHandle).Elem().FieldByName("sockets")
rNlhandle = reflect.NewAt(rNlhandle.Type(), unsafe.Pointer(rNlhandle.UnsafeAddr())).Elem()
for _, sock := range rNlhandle.Interface().(map[int]*nl.SocketHandle) {
rSocket := reflect.ValueOf(sock.Socket).Elem().FieldByName("fd")
rSocket = reflect.NewAt(rSocket.Type(), unsafe.Pointer(rSocket.UnsafeAddr())).Elem()
fd := rSocket.Interface().(int32)
netnsfd, err := unix.IoctlRetInt(int(fd), unix.SIOCGSKNS)
if err != nil {
panic("cannot query netns fd of RTNETLINK fd: " + err.Error())
}
var stat unix.Stat_t
if err := unix.Fstat(netnsfd, &stat); err != nil {
panic("cannot stat netns fd: " + err.Error())
}
println("received netlink socket is connected to net:[" +
strconv.FormatUint(stat.Ino, 10) + "]")
}
println("telling creator to stop")
close(done)
for {
links, err := nlHandle.LinkList()
if err != nil {
panic("RTNETLINK failed: " + err.Error())
}
var names []string
for _, link := range links {
names = append(names, link.Attrs().Name)
}
println("nifs found:", strings.Join(names, ", "))
time.Sleep(30 * time.Second)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment