forked from rosa/hakurei
internal/netlink: generalise implementation from container
This is useful for uevent implementation. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
132
internal/netlink/rtnl.go
Normal file
132
internal/netlink/rtnl.go
Normal file
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user