diff options
| author | Ophestra <cat@gensokyo.uk> | 2026-03-16 22:09:12 +0900 |
|---|---|---|
| committer | Ophestra <cat@gensokyo.uk> | 2026-03-16 23:07:51 +0900 |
| commit | 72bd3fb05ef796e04e3b61015f6ffac3c1a937e3 (patch) | |
| tree | 1c842839af26daf68e7aa68d17b492835900594a /internal/netlink/rtnl.go | |
| parent | 59c66747dfb6647b28610ee13b122b1a927de08d (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.go | 132 |
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) +} |
