Contents of /trunk/mkinitrd-magellan/busybox/networking/libiproute/libnetlink.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 9112 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 9112 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * libnetlink.c RTnetlink service routines. |
4 | * |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License |
7 | * as published by the Free Software Foundation; either version |
8 | * 2 of the License, or (at your option) any later version. |
9 | * |
10 | * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
11 | * |
12 | */ |
13 | |
14 | #include "libbb.h" |
15 | #include <sys/socket.h> |
16 | |
17 | #include <errno.h> |
18 | #include <string.h> |
19 | #include <time.h> |
20 | #include <unistd.h> |
21 | |
22 | #include <sys/uio.h> |
23 | |
24 | #include "libnetlink.h" |
25 | |
26 | void rtnl_close(struct rtnl_handle *rth) |
27 | { |
28 | close(rth->fd); |
29 | } |
30 | |
31 | int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) |
32 | { |
33 | socklen_t addr_len; |
34 | |
35 | memset(rth, 0, sizeof(rth)); |
36 | |
37 | rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); |
38 | if (rth->fd < 0) { |
39 | bb_perror_msg("cannot open netlink socket"); |
40 | return -1; |
41 | } |
42 | |
43 | memset(&rth->local, 0, sizeof(rth->local)); |
44 | rth->local.nl_family = AF_NETLINK; |
45 | rth->local.nl_groups = subscriptions; |
46 | |
47 | if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) { |
48 | bb_perror_msg("cannot bind netlink socket"); |
49 | return -1; |
50 | } |
51 | addr_len = sizeof(rth->local); |
52 | if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) { |
53 | bb_perror_msg("cannot getsockname"); |
54 | return -1; |
55 | } |
56 | if (addr_len != sizeof(rth->local)) { |
57 | bb_error_msg("wrong address length %d", addr_len); |
58 | return -1; |
59 | } |
60 | if (rth->local.nl_family != AF_NETLINK) { |
61 | bb_error_msg("wrong address family %d", rth->local.nl_family); |
62 | return -1; |
63 | } |
64 | rth->seq = time(NULL); |
65 | return 0; |
66 | } |
67 | |
68 | int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) |
69 | { |
70 | struct { |
71 | struct nlmsghdr nlh; |
72 | struct rtgenmsg g; |
73 | } req; |
74 | struct sockaddr_nl nladdr; |
75 | |
76 | memset(&nladdr, 0, sizeof(nladdr)); |
77 | nladdr.nl_family = AF_NETLINK; |
78 | |
79 | req.nlh.nlmsg_len = sizeof(req); |
80 | req.nlh.nlmsg_type = type; |
81 | req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
82 | req.nlh.nlmsg_pid = 0; |
83 | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; |
84 | req.g.rtgen_family = family; |
85 | |
86 | return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
87 | } |
88 | |
89 | int rtnl_send(struct rtnl_handle *rth, char *buf, int len) |
90 | { |
91 | struct sockaddr_nl nladdr; |
92 | |
93 | memset(&nladdr, 0, sizeof(nladdr)); |
94 | nladdr.nl_family = AF_NETLINK; |
95 | |
96 | return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr)); |
97 | } |
98 | |
99 | int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) |
100 | { |
101 | struct nlmsghdr nlh; |
102 | struct sockaddr_nl nladdr; |
103 | struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } }; |
104 | struct msghdr msg = { |
105 | (void*)&nladdr, sizeof(nladdr), |
106 | iov, 2, |
107 | NULL, 0, |
108 | 0 |
109 | }; |
110 | |
111 | memset(&nladdr, 0, sizeof(nladdr)); |
112 | nladdr.nl_family = AF_NETLINK; |
113 | |
114 | nlh.nlmsg_len = NLMSG_LENGTH(len); |
115 | nlh.nlmsg_type = type; |
116 | nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; |
117 | nlh.nlmsg_pid = 0; |
118 | nlh.nlmsg_seq = rth->dump = ++rth->seq; |
119 | |
120 | return sendmsg(rth->fd, &msg, 0); |
121 | } |
122 | |
123 | int rtnl_dump_filter(struct rtnl_handle *rth, |
124 | int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *), |
125 | void *arg1, |
126 | int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *), |
127 | void *arg2) |
128 | { |
129 | char buf[8192]; |
130 | struct sockaddr_nl nladdr; |
131 | struct iovec iov = { buf, sizeof(buf) }; |
132 | |
133 | while (1) { |
134 | int status; |
135 | struct nlmsghdr *h; |
136 | |
137 | struct msghdr msg = { |
138 | (void*)&nladdr, sizeof(nladdr), |
139 | &iov, 1, |
140 | NULL, 0, |
141 | 0 |
142 | }; |
143 | |
144 | status = recvmsg(rth->fd, &msg, 0); |
145 | |
146 | if (status < 0) { |
147 | if (errno == EINTR) |
148 | continue; |
149 | bb_perror_msg("OVERRUN"); |
150 | continue; |
151 | } |
152 | if (status == 0) { |
153 | bb_error_msg("EOF on netlink"); |
154 | return -1; |
155 | } |
156 | if (msg.msg_namelen != sizeof(nladdr)) { |
157 | bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); |
158 | } |
159 | |
160 | h = (struct nlmsghdr*)buf; |
161 | while (NLMSG_OK(h, status)) { |
162 | int err; |
163 | |
164 | if (nladdr.nl_pid != 0 || |
165 | h->nlmsg_pid != rth->local.nl_pid || |
166 | h->nlmsg_seq != rth->dump) { |
167 | if (junk) { |
168 | err = junk(&nladdr, h, arg2); |
169 | if (err < 0) { |
170 | return err; |
171 | } |
172 | } |
173 | goto skip_it; |
174 | } |
175 | |
176 | if (h->nlmsg_type == NLMSG_DONE) { |
177 | return 0; |
178 | } |
179 | if (h->nlmsg_type == NLMSG_ERROR) { |
180 | struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h); |
181 | if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { |
182 | bb_error_msg("ERROR truncated"); |
183 | } else { |
184 | errno = -l_err->error; |
185 | bb_perror_msg("RTNETLINK answers"); |
186 | } |
187 | return -1; |
188 | } |
189 | err = filter(&nladdr, h, arg1); |
190 | if (err < 0) { |
191 | return err; |
192 | } |
193 | |
194 | skip_it: |
195 | h = NLMSG_NEXT(h, status); |
196 | } |
197 | if (msg.msg_flags & MSG_TRUNC) { |
198 | bb_error_msg("message truncated"); |
199 | continue; |
200 | } |
201 | if (status) { |
202 | bb_error_msg_and_die("remnant of size %d!", status); |
203 | } |
204 | } |
205 | } |
206 | |
207 | int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, |
208 | unsigned groups, struct nlmsghdr *answer, |
209 | int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *), |
210 | void *jarg) |
211 | { |
212 | int status; |
213 | unsigned seq; |
214 | struct nlmsghdr *h; |
215 | struct sockaddr_nl nladdr; |
216 | struct iovec iov = { (void*)n, n->nlmsg_len }; |
217 | char buf[8192]; |
218 | struct msghdr msg = { |
219 | (void*)&nladdr, sizeof(nladdr), |
220 | &iov, 1, |
221 | NULL, 0, |
222 | 0 |
223 | }; |
224 | |
225 | memset(&nladdr, 0, sizeof(nladdr)); |
226 | nladdr.nl_family = AF_NETLINK; |
227 | nladdr.nl_pid = peer; |
228 | nladdr.nl_groups = groups; |
229 | |
230 | n->nlmsg_seq = seq = ++rtnl->seq; |
231 | if (answer == NULL) { |
232 | n->nlmsg_flags |= NLM_F_ACK; |
233 | } |
234 | status = sendmsg(rtnl->fd, &msg, 0); |
235 | |
236 | if (status < 0) { |
237 | bb_perror_msg("cannot talk to rtnetlink"); |
238 | return -1; |
239 | } |
240 | |
241 | iov.iov_base = buf; |
242 | |
243 | while (1) { |
244 | iov.iov_len = sizeof(buf); |
245 | status = recvmsg(rtnl->fd, &msg, 0); |
246 | |
247 | if (status < 0) { |
248 | if (errno == EINTR) { |
249 | continue; |
250 | } |
251 | bb_perror_msg("OVERRUN"); |
252 | continue; |
253 | } |
254 | if (status == 0) { |
255 | bb_error_msg("EOF on netlink"); |
256 | return -1; |
257 | } |
258 | if (msg.msg_namelen != sizeof(nladdr)) { |
259 | bb_error_msg_and_die("sender address length == %d", msg.msg_namelen); |
260 | } |
261 | for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) { |
262 | int l_err; |
263 | int len = h->nlmsg_len; |
264 | int l = len - sizeof(*h); |
265 | |
266 | if (l<0 || len>status) { |
267 | if (msg.msg_flags & MSG_TRUNC) { |
268 | bb_error_msg("truncated message"); |
269 | return -1; |
270 | } |
271 | bb_error_msg_and_die("malformed message: len=%d!", len); |
272 | } |
273 | |
274 | if (nladdr.nl_pid != peer || |
275 | h->nlmsg_pid != rtnl->local.nl_pid || |
276 | h->nlmsg_seq != seq) { |
277 | if (junk) { |
278 | l_err = junk(&nladdr, h, jarg); |
279 | if (l_err < 0) { |
280 | return l_err; |
281 | } |
282 | } |
283 | continue; |
284 | } |
285 | |
286 | if (h->nlmsg_type == NLMSG_ERROR) { |
287 | struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h); |
288 | if (l < sizeof(struct nlmsgerr)) { |
289 | bb_error_msg("ERROR truncated"); |
290 | } else { |
291 | errno = -err->error; |
292 | if (errno == 0) { |
293 | if (answer) { |
294 | memcpy(answer, h, h->nlmsg_len); |
295 | } |
296 | return 0; |
297 | } |
298 | bb_perror_msg("RTNETLINK answers"); |
299 | } |
300 | return -1; |
301 | } |
302 | if (answer) { |
303 | memcpy(answer, h, h->nlmsg_len); |
304 | return 0; |
305 | } |
306 | |
307 | bb_error_msg("unexpected reply!"); |
308 | |
309 | status -= NLMSG_ALIGN(len); |
310 | h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len)); |
311 | } |
312 | if (msg.msg_flags & MSG_TRUNC) { |
313 | bb_error_msg("message truncated"); |
314 | continue; |
315 | } |
316 | if (status) { |
317 | bb_error_msg_and_die("remnant of size %d!", status); |
318 | } |
319 | } |
320 | } |
321 | |
322 | int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) |
323 | { |
324 | int len = RTA_LENGTH(4); |
325 | struct rtattr *rta; |
326 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) |
327 | return -1; |
328 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
329 | rta->rta_type = type; |
330 | rta->rta_len = len; |
331 | memcpy(RTA_DATA(rta), &data, 4); |
332 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; |
333 | return 0; |
334 | } |
335 | |
336 | int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) |
337 | { |
338 | int len = RTA_LENGTH(alen); |
339 | struct rtattr *rta; |
340 | |
341 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) |
342 | return -1; |
343 | rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len)); |
344 | rta->rta_type = type; |
345 | rta->rta_len = len; |
346 | memcpy(RTA_DATA(rta), data, alen); |
347 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; |
348 | return 0; |
349 | } |
350 | |
351 | int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) |
352 | { |
353 | int len = RTA_LENGTH(4); |
354 | struct rtattr *subrta; |
355 | |
356 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { |
357 | return -1; |
358 | } |
359 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
360 | subrta->rta_type = type; |
361 | subrta->rta_len = len; |
362 | memcpy(RTA_DATA(subrta), &data, 4); |
363 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; |
364 | return 0; |
365 | } |
366 | |
367 | int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) |
368 | { |
369 | struct rtattr *subrta; |
370 | int len = RTA_LENGTH(alen); |
371 | |
372 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { |
373 | return -1; |
374 | } |
375 | subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len)); |
376 | subrta->rta_type = type; |
377 | subrta->rta_len = len; |
378 | memcpy(RTA_DATA(subrta), data, alen); |
379 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; |
380 | return 0; |
381 | } |
382 | |
383 | |
384 | int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) |
385 | { |
386 | while (RTA_OK(rta, len)) { |
387 | if (rta->rta_type <= max) { |
388 | tb[rta->rta_type] = rta; |
389 | } |
390 | rta = RTA_NEXT(rta,len); |
391 | } |
392 | if (len) { |
393 | bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len); |
394 | } |
395 | return 0; |
396 | } |