Source file
src/syscall/netlink_linux.go
1
2
3
4
5
6
7 package syscall
8
9 import (
10 "sync"
11 "unsafe"
12 )
13
14
15 func nlmAlignOf(msglen int) int {
16 return (msglen + NLMSG_ALIGNTO - 1) & ^(NLMSG_ALIGNTO - 1)
17 }
18
19
20
21 func rtaAlignOf(attrlen int) int {
22 return (attrlen + RTA_ALIGNTO - 1) & ^(RTA_ALIGNTO - 1)
23 }
24
25
26
27 type NetlinkRouteRequest struct {
28 Header NlMsghdr
29 Data RtGenmsg
30 }
31
32 func (rr *NetlinkRouteRequest) toWireFormat() []byte {
33 b := make([]byte, rr.Header.Len)
34 *(*uint32)(unsafe.Pointer(&b[0:4][0])) = rr.Header.Len
35 *(*uint16)(unsafe.Pointer(&b[4:6][0])) = rr.Header.Type
36 *(*uint16)(unsafe.Pointer(&b[6:8][0])) = rr.Header.Flags
37 *(*uint32)(unsafe.Pointer(&b[8:12][0])) = rr.Header.Seq
38 *(*uint32)(unsafe.Pointer(&b[12:16][0])) = rr.Header.Pid
39 b[16] = rr.Data.Family
40 return b
41 }
42
43 func newNetlinkRouteRequest(proto, seq, family int) []byte {
44 rr := &NetlinkRouteRequest{}
45 rr.Header.Len = uint32(NLMSG_HDRLEN + SizeofRtGenmsg)
46 rr.Header.Type = uint16(proto)
47 rr.Header.Flags = NLM_F_DUMP | NLM_F_REQUEST
48 rr.Header.Seq = uint32(seq)
49 rr.Data.Family = uint8(family)
50 return rr.toWireFormat()
51 }
52
53 var pageBufPool = &sync.Pool{New: func() any {
54 b := make([]byte, Getpagesize())
55 return &b
56 }}
57
58
59
60 func NetlinkRIB(proto, family int) ([]byte, error) {
61 s, err := Socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC, NETLINK_ROUTE)
62 if err != nil {
63 return nil, err
64 }
65 defer Close(s)
66 sa := &SockaddrNetlink{Family: AF_NETLINK}
67 if err := Bind(s, sa); err != nil {
68 return nil, err
69 }
70 wb := newNetlinkRouteRequest(proto, 1, family)
71 if err := Sendto(s, wb, 0, sa); err != nil {
72 return nil, err
73 }
74 lsa, err := Getsockname(s)
75 if err != nil {
76 return nil, err
77 }
78 lsanl, ok := lsa.(*SockaddrNetlink)
79 if !ok {
80 return nil, EINVAL
81 }
82 var tab []byte
83
84 rbNew := pageBufPool.Get().(*[]byte)
85 defer pageBufPool.Put(rbNew)
86 done:
87 for {
88 rb := *rbNew
89 nr, _, err := Recvfrom(s, rb, 0)
90 if err != nil {
91 return nil, err
92 }
93 if nr < NLMSG_HDRLEN {
94 return nil, EINVAL
95 }
96 rb = rb[:nr]
97 tab = append(tab, rb...)
98 msgs, err := ParseNetlinkMessage(rb)
99 if err != nil {
100 return nil, err
101 }
102 for _, m := range msgs {
103 if m.Header.Seq != 1 || m.Header.Pid != lsanl.Pid {
104 return nil, EINVAL
105 }
106 if m.Header.Type == NLMSG_DONE {
107 break done
108 }
109 if m.Header.Type == NLMSG_ERROR {
110 return nil, EINVAL
111 }
112 }
113 }
114 return tab, nil
115 }
116
117
118 type NetlinkMessage struct {
119 Header NlMsghdr
120 Data []byte
121 }
122
123
124
125 func ParseNetlinkMessage(b []byte) ([]NetlinkMessage, error) {
126 var msgs []NetlinkMessage
127 for len(b) >= NLMSG_HDRLEN {
128 h, dbuf, dlen, err := netlinkMessageHeaderAndData(b)
129 if err != nil {
130 return nil, err
131 }
132 m := NetlinkMessage{Header: *h, Data: dbuf[:int(h.Len)-NLMSG_HDRLEN]}
133 msgs = append(msgs, m)
134 b = b[dlen:]
135 }
136 return msgs, nil
137 }
138
139 func netlinkMessageHeaderAndData(b []byte) (*NlMsghdr, []byte, int, error) {
140 h := (*NlMsghdr)(unsafe.Pointer(&b[0]))
141 l := nlmAlignOf(int(h.Len))
142 if int(h.Len) < NLMSG_HDRLEN || l > len(b) {
143 return nil, nil, 0, EINVAL
144 }
145 return h, b[NLMSG_HDRLEN:], l, nil
146 }
147
148
149 type NetlinkRouteAttr struct {
150 Attr RtAttr
151 Value []byte
152 }
153
154
155
156
157 func ParseNetlinkRouteAttr(m *NetlinkMessage) ([]NetlinkRouteAttr, error) {
158 var b []byte
159 switch m.Header.Type {
160 case RTM_NEWLINK, RTM_DELLINK:
161 b = m.Data[SizeofIfInfomsg:]
162 case RTM_NEWADDR, RTM_DELADDR:
163 b = m.Data[SizeofIfAddrmsg:]
164 case RTM_NEWROUTE, RTM_DELROUTE:
165 b = m.Data[SizeofRtMsg:]
166 default:
167 return nil, EINVAL
168 }
169 var attrs []NetlinkRouteAttr
170 for len(b) >= SizeofRtAttr {
171 a, vbuf, alen, err := netlinkRouteAttrAndValue(b)
172 if err != nil {
173 return nil, err
174 }
175 ra := NetlinkRouteAttr{Attr: *a, Value: vbuf[:int(a.Len)-SizeofRtAttr]}
176 attrs = append(attrs, ra)
177 b = b[alen:]
178 }
179 return attrs, nil
180 }
181
182 func netlinkRouteAttrAndValue(b []byte) (*RtAttr, []byte, int, error) {
183 a := (*RtAttr)(unsafe.Pointer(&b[0]))
184 if int(a.Len) < SizeofRtAttr || int(a.Len) > len(b) {
185 return nil, nil, 0, EINVAL
186 }
187 return a, b[SizeofRtAttr:], rtaAlignOf(int(a.Len)), nil
188 }
189
View as plain text