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) }