aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/netlink/rtnl.go
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-03-16 22:09:12 +0900
committerOphestra <cat@gensokyo.uk>2026-03-16 23:07:51 +0900
commit72bd3fb05ef796e04e3b61015f6ffac3c1a937e3 (patch)
tree1c842839af26daf68e7aa68d17b492835900594a /internal/netlink/rtnl.go
parent59c66747dfb6647b28610ee13b122b1a927de08d (diff)
internal/netlink: generalise implementation from container
This is useful for uevent implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/netlink/rtnl.go')
-rw-r--r--internal/netlink/rtnl.go132
1 files changed, 132 insertions, 0 deletions
diff --git a/internal/netlink/rtnl.go b/internal/netlink/rtnl.go
new file mode 100644
index 00000000..1ed463b6
--- /dev/null
+++ b/internal/netlink/rtnl.go
@@ -0,0 +1,132 @@
+package netlink
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+// RouteConn represents a NETLINK_ROUTE socket.
+type RouteConn struct{ *conn }
+
+// DialRoute returns the address of a newly connected [RouteConn].
+func DialRoute() (*RouteConn, error) {
+ c, err := dial(syscall.NETLINK_ROUTE)
+ if err != nil {
+ return nil, err
+ }
+ return &RouteConn{c}, nil
+}
+
+// rtnlConsume consumes a message from rtnetlink.
+func rtnlConsume(msg *syscall.NetlinkMessage) error {
+ switch msg.Header.Type {
+ case syscall.NLMSG_DONE:
+ return Complete{}
+
+ case syscall.NLMSG_ERROR:
+ if e := As[syscall.NlMsgerr](msg.Data); e != nil {
+ if e.Error == 0 {
+ return Complete{}
+ }
+ return syscall.Errno(-e.Error)
+ }
+ return syscall.EBADE
+
+ default:
+ return nil
+ }
+}
+
+// InAddr is equivalent to struct in_addr.
+type InAddr [4]byte
+
+// RtAttrMsg holds syscall.RtAttr alongside its payload.
+type RtAttrMsg[D any] struct {
+ syscall.RtAttr
+ Data D
+}
+
+// populate populates the Len field of the embedded syscall.RtAttr.
+func (attr *RtAttrMsg[M]) populate() {
+ attr.Len = syscall.SizeofRtAttr + uint16(unsafe.Sizeof(attr.Data))
+}
+
+// writeIfAddrmsg writes an ifaddrmsg structure to conn.
+func (c *RouteConn) writeIfAddrmsg(
+ typ, flags uint16,
+ msg *syscall.IfAddrmsg,
+ attrs ...RtAttrMsg[InAddr],
+) bool {
+ c.typ, c.flags = typ, syscall.NLM_F_REQUEST|syscall.NLM_F_ACK|flags
+ if !add(c.conn, msg) {
+ return false
+ }
+ for _, attr := range attrs {
+ attr.populate()
+ if !add(c.conn, &attr) {
+ return false
+ }
+ }
+ return true
+}
+
+// SendIfAddrmsg sends an ifaddrmsg structure to rtnetlink.
+func (c *RouteConn) SendIfAddrmsg(
+ typ, flags uint16,
+ msg *syscall.IfAddrmsg,
+ attrs ...RtAttrMsg[InAddr],
+) error {
+ if !c.writeIfAddrmsg(typ, flags, msg, attrs...) {
+ return syscall.ENOMEM
+ }
+ return c.Roundtrip(rtnlConsume)
+}
+
+// writeNewaddrLo writes a RTM_NEWADDR message for the loopback address.
+func (c *RouteConn) writeNewaddrLo(lo uint32) bool {
+ return c.writeIfAddrmsg(
+ syscall.RTM_NEWADDR,
+ syscall.NLM_F_CREATE|syscall.NLM_F_EXCL,
+ &syscall.IfAddrmsg{
+ Family: syscall.AF_INET,
+ Prefixlen: 8,
+ Flags: syscall.IFA_F_PERMANENT,
+ Scope: syscall.RT_SCOPE_HOST,
+ Index: lo,
+ },
+ RtAttrMsg[InAddr]{syscall.RtAttr{
+ Type: syscall.IFA_LOCAL,
+ }, InAddr{127, 0, 0, 1}},
+ RtAttrMsg[InAddr]{syscall.RtAttr{
+ Type: syscall.IFA_ADDRESS,
+ }, InAddr{127, 0, 0, 1}},
+ )
+}
+
+// SendNewaddrLo sends a RTM_NEWADDR message for the loopback address to the kernel.
+func (c *RouteConn) SendNewaddrLo(lo uint32) error {
+ if !c.writeNewaddrLo(lo) {
+ return syscall.ENOMEM
+ }
+ return c.Roundtrip(rtnlConsume)
+}
+
+// writeIfInfomsg writes an ifinfomsg structure to conn.
+func (c *RouteConn) writeIfInfomsg(
+ typ, flags uint16,
+ msg *syscall.IfInfomsg,
+) bool {
+ c.typ, c.flags = typ, syscall.NLM_F_REQUEST|syscall.NLM_F_ACK|flags
+ return add(c.conn, msg)
+}
+
+// SendIfInfomsg sends an ifinfomsg structure to rtnetlink.
+func (c *RouteConn) SendIfInfomsg(
+ typ, flags uint16,
+ msg *syscall.IfInfomsg,
+) error {
+ if !c.writeIfInfomsg(typ, flags, msg) {
+ return syscall.ENOMEM
+ }
+ return c.Roundtrip(rtnlConsume)
+}