*: add FAST_FUNC to function ptrs where it makes sense
[platform/upstream/busybox.git] / networking / libiproute / libnetlink.c
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 <sys/socket.h>
15 #include <sys/uio.h>
16
17 #include "libbb.h"
18 #include "libnetlink.h"
19
20 void FAST_FUNC rtnl_close(struct rtnl_handle *rth)
21 {
22         close(rth->fd);
23 }
24
25 int FAST_FUNC xrtnl_open(struct rtnl_handle *rth/*, unsigned subscriptions*/)
26 {
27         socklen_t addr_len;
28
29         memset(rth, 0, sizeof(*rth));
30         rth->fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
31         rth->local.nl_family = AF_NETLINK;
32         /*rth->local.nl_groups = subscriptions;*/
33
34         xbind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local));
35         addr_len = sizeof(rth->local);
36         getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len);
37
38 /* too much paranoia
39         if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0)
40                 bb_perror_msg_and_die("getsockname");
41         if (addr_len != sizeof(rth->local))
42                 bb_error_msg_and_die("wrong address length %d", addr_len);
43         if (rth->local.nl_family != AF_NETLINK)
44                 bb_error_msg_and_die("wrong address family %d", rth->local.nl_family);
45 */
46         rth->seq = time(NULL);
47         return 0;
48 }
49
50 int FAST_FUNC xrtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
51 {
52         struct {
53                 struct nlmsghdr nlh;
54                 struct rtgenmsg g;
55         } req;
56         struct sockaddr_nl nladdr;
57
58         memset(&nladdr, 0, sizeof(nladdr));
59         nladdr.nl_family = AF_NETLINK;
60
61         req.nlh.nlmsg_len = sizeof(req);
62         req.nlh.nlmsg_type = type;
63         req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
64         req.nlh.nlmsg_pid = 0;
65         req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
66         req.g.rtgen_family = family;
67
68         return xsendto(rth->fd, (void*)&req, sizeof(req),
69                                  (struct sockaddr*)&nladdr, sizeof(nladdr));
70 }
71
72 int FAST_FUNC rtnl_send(struct rtnl_handle *rth, char *buf, int len)
73 {
74         struct sockaddr_nl nladdr;
75
76         memset(&nladdr, 0, sizeof(nladdr));
77         nladdr.nl_family = AF_NETLINK;
78
79         return xsendto(rth->fd, buf, len, (struct sockaddr*)&nladdr, sizeof(nladdr));
80 }
81
82 int FAST_FUNC rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
83 {
84         struct nlmsghdr nlh;
85         struct sockaddr_nl nladdr;
86         struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
87         struct msghdr msg = {
88                 (void*)&nladdr, sizeof(nladdr),
89                 iov,    2,
90                 NULL,   0,
91                 0
92         };
93
94         memset(&nladdr, 0, sizeof(nladdr));
95         nladdr.nl_family = AF_NETLINK;
96
97         nlh.nlmsg_len = NLMSG_LENGTH(len);
98         nlh.nlmsg_type = type;
99         nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
100         nlh.nlmsg_pid = 0;
101         nlh.nlmsg_seq = rth->dump = ++rth->seq;
102
103         return sendmsg(rth->fd, &msg, 0);
104 }
105
106 static int rtnl_dump_filter(struct rtnl_handle *rth,
107                 int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *n, void *) FAST_FUNC,
108                 void *arg1/*,
109                 int (*junk)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
110                 void *arg2*/)
111 {
112         int retval = -1;
113         char *buf = xmalloc(8*1024); /* avoid big stack buffer */
114         struct sockaddr_nl nladdr;
115         struct iovec iov = { buf, 8*1024 };
116
117         while (1) {
118                 int status;
119                 struct nlmsghdr *h;
120
121                 struct msghdr msg = {
122                         (void*)&nladdr, sizeof(nladdr),
123                         &iov,   1,
124                         NULL,   0,
125                         0
126                 };
127
128                 status = recvmsg(rth->fd, &msg, 0);
129
130                 if (status < 0) {
131                         if (errno == EINTR)
132                                 continue;
133                         bb_perror_msg("OVERRUN");
134                         continue;
135                 }
136                 if (status == 0) {
137                         bb_error_msg("EOF on netlink");
138                         goto ret;
139                 }
140                 if (msg.msg_namelen != sizeof(nladdr)) {
141                         bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
142                 }
143
144                 h = (struct nlmsghdr*)buf;
145                 while (NLMSG_OK(h, status)) {
146                         int err;
147
148                         if (nladdr.nl_pid != 0 ||
149                             h->nlmsg_pid != rth->local.nl_pid ||
150                             h->nlmsg_seq != rth->dump) {
151 //                              if (junk) {
152 //                                      err = junk(&nladdr, h, arg2);
153 //                                      if (err < 0) {
154 //                                              retval = err;
155 //                                              goto ret;
156 //                                      }
157 //                              }
158                                 goto skip_it;
159                         }
160
161                         if (h->nlmsg_type == NLMSG_DONE) {
162                                 goto ret_0;
163                         }
164                         if (h->nlmsg_type == NLMSG_ERROR) {
165                                 struct nlmsgerr *l_err = (struct nlmsgerr*)NLMSG_DATA(h);
166                                 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
167                                         bb_error_msg("ERROR truncated");
168                                 } else {
169                                         errno = -l_err->error;
170                                         bb_perror_msg("RTNETLINK answers");
171                                 }
172                                 goto ret;
173                         }
174                         err = filter(&nladdr, h, arg1);
175                         if (err < 0) {
176                                 retval = err;
177                                 goto ret;
178                         }
179
180  skip_it:
181                         h = NLMSG_NEXT(h, status);
182                 }
183                 if (msg.msg_flags & MSG_TRUNC) {
184                         bb_error_msg("message truncated");
185                         continue;
186                 }
187                 if (status) {
188                         bb_error_msg_and_die("remnant of size %d!", status);
189                 }
190         } /* while (1) */
191  ret_0:
192         retval++; /* = 0 */
193  ret:
194         free(buf);
195         return retval;
196 }
197
198 int FAST_FUNC xrtnl_dump_filter(struct rtnl_handle *rth,
199                 int (*filter)(const struct sockaddr_nl *, struct nlmsghdr *, void *) FAST_FUNC,
200                 void *arg1)
201 {
202         int ret = rtnl_dump_filter(rth, filter, arg1/*, NULL, NULL*/);
203         if (ret < 0)
204                 bb_error_msg_and_die("dump terminated");
205         return ret;
206 }
207
208 int FAST_FUNC rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n,
209                 pid_t peer, unsigned groups,
210                 struct nlmsghdr *answer,
211                 int (*junk)(struct sockaddr_nl *, struct nlmsghdr *, void *),
212                 void *jarg)
213 {
214 /* bbox doesn't use parameters no. 3, 4, 6, 7, they are stubbed out */
215 #define peer   0
216 #define groups 0
217 #define junk   NULL
218 #define jarg   NULL
219         int retval = -1;
220         int status;
221         unsigned seq;
222         struct nlmsghdr *h;
223         struct sockaddr_nl nladdr;
224         struct iovec iov = { (void*)n, n->nlmsg_len };
225         char   *buf = xmalloc(8*1024); /* avoid big stack buffer */
226         struct msghdr msg = {
227                 (void*)&nladdr, sizeof(nladdr),
228                 &iov,   1,
229                 NULL,   0,
230                 0
231         };
232
233         memset(&nladdr, 0, sizeof(nladdr));
234         nladdr.nl_family = AF_NETLINK;
235 //      nladdr.nl_pid = peer;
236 //      nladdr.nl_groups = groups;
237
238         n->nlmsg_seq = seq = ++rtnl->seq;
239         if (answer == NULL) {
240                 n->nlmsg_flags |= NLM_F_ACK;
241         }
242         status = sendmsg(rtnl->fd, &msg, 0);
243
244         if (status < 0) {
245                 bb_perror_msg("cannot talk to rtnetlink");
246                 goto ret;
247         }
248
249         iov.iov_base = buf;
250
251         while (1) {
252                 iov.iov_len = 8*1024;
253                 status = recvmsg(rtnl->fd, &msg, 0);
254
255                 if (status < 0) {
256                         if (errno == EINTR) {
257                                 continue;
258                         }
259                         bb_perror_msg("OVERRUN");
260                         continue;
261                 }
262                 if (status == 0) {
263                         bb_error_msg("EOF on netlink");
264                         goto ret;
265                 }
266                 if (msg.msg_namelen != sizeof(nladdr)) {
267                         bb_error_msg_and_die("sender address length == %d", msg.msg_namelen);
268                 }
269                 for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
270 //                      int l_err;
271                         int len = h->nlmsg_len;
272                         int l = len - sizeof(*h);
273
274                         if (l < 0 || len > status) {
275                                 if (msg.msg_flags & MSG_TRUNC) {
276                                         bb_error_msg("truncated message");
277                                         goto ret;
278                                 }
279                                 bb_error_msg_and_die("malformed message: len=%d!", len);
280                         }
281
282                         if (nladdr.nl_pid != peer ||
283                             h->nlmsg_pid != rtnl->local.nl_pid ||
284                             h->nlmsg_seq != seq) {
285 //                              if (junk) {
286 //                                      l_err = junk(&nladdr, h, jarg);
287 //                                      if (l_err < 0) {
288 //                                              retval = l_err;
289 //                                              goto ret;
290 //                                      }
291 //                              }
292                                 continue;
293                         }
294
295                         if (h->nlmsg_type == NLMSG_ERROR) {
296                                 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
297                                 if (l < (int)sizeof(struct nlmsgerr)) {
298                                         bb_error_msg("ERROR truncated");
299                                 } else {
300                                         errno = - err->error;
301                                         if (errno == 0) {
302                                                 if (answer) {
303                                                         memcpy(answer, h, h->nlmsg_len);
304                                                 }
305                                                 goto ret_0;
306                                         }
307                                         bb_perror_msg("RTNETLINK answers");
308                                 }
309                                 goto ret;
310                         }
311                         if (answer) {
312                                 memcpy(answer, h, h->nlmsg_len);
313                                 goto ret_0;
314                         }
315
316                         bb_error_msg("unexpected reply!");
317
318                         status -= NLMSG_ALIGN(len);
319                         h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
320                 }
321                 if (msg.msg_flags & MSG_TRUNC) {
322                         bb_error_msg("message truncated");
323                         continue;
324                 }
325                 if (status) {
326                         bb_error_msg_and_die("remnant of size %d!", status);
327                 }
328         } /* while (1) */
329  ret_0:
330         retval++; /* = 0 */
331  ret:
332         free(buf);
333         return retval;
334 }
335
336 int FAST_FUNC addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data)
337 {
338         int len = RTA_LENGTH(4);
339         struct rtattr *rta;
340         if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
341                 return -1;
342         rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
343         rta->rta_type = type;
344         rta->rta_len = len;
345         move_to_unaligned32(RTA_DATA(rta), data);
346         n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
347         return 0;
348 }
349
350 int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
351 {
352         int len = RTA_LENGTH(alen);
353         struct rtattr *rta;
354
355         if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen)
356                 return -1;
357         rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
358         rta->rta_type = type;
359         rta->rta_len = len;
360         memcpy(RTA_DATA(rta), data, alen);
361         n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
362         return 0;
363 }
364
365 int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data)
366 {
367         int len = RTA_LENGTH(4);
368         struct rtattr *subrta;
369
370         if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
371                 return -1;
372         }
373         subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
374         subrta->rta_type = type;
375         subrta->rta_len = len;
376         move_to_unaligned32(RTA_DATA(subrta), data);
377         rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
378         return 0;
379 }
380
381 int FAST_FUNC rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
382 {
383         struct rtattr *subrta;
384         int len = RTA_LENGTH(alen);
385
386         if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
387                 return -1;
388         }
389         subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
390         subrta->rta_type = type;
391         subrta->rta_len = len;
392         memcpy(RTA_DATA(subrta), data, alen);
393         rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
394         return 0;
395 }
396
397
398 int FAST_FUNC parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
399 {
400         while (RTA_OK(rta, len)) {
401                 if (rta->rta_type <= max) {
402                         tb[rta->rta_type] = rta;
403                 }
404                 rta = RTA_NEXT(rta,len);
405         }
406         if (len) {
407                 bb_error_msg("deficit %d, rta_len=%d!", len, rta->rta_len);
408         }
409         return 0;
410 }