Last active
April 29, 2023 20:47
-
-
Save thediveo/bc22cd3dd96edf12ea1daafeeacf9bad to your computer and use it in GitHub Desktop.
Discovering the network namespace an (RT)NETLINK socket connects to
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
gist title |
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
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 |
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
// 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