blob: cb67a2011b235099f2f5be8240e21fce068f4112 [file] [log] [blame]
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +00001/*
2 * libnetlink.c RTnetlink service routines.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10 *
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <unistd.h>
16#include <syslog.h>
17#include <fcntl.h>
18#include <net/if_arp.h>
19#include <sys/socket.h>
20#include <netinet/in.h>
21#include <string.h>
22#include <errno.h>
23#include <time.h>
24#include <sys/uio.h>
25
26#include "libnetlink.h"
27
28void rtnl_close(struct rtnl_handle *rth)
29{
30 close(rth->fd);
31}
32
net[shemminger]!shemmingerc7699872004-07-07 17:05:56 +000033int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol)
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000034{
35 int addr_len;
36
37 memset(rth, 0, sizeof(rth));
38
net[shemminger]!shemmingerc7699872004-07-07 17:05:56 +000039 rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000040 if (rth->fd < 0) {
41 perror("Cannot open netlink socket");
42 return -1;
43 }
44
45 memset(&rth->local, 0, sizeof(rth->local));
46 rth->local.nl_family = AF_NETLINK;
47 rth->local.nl_groups = subscriptions;
48
49 if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
50 perror("Cannot bind netlink socket");
51 return -1;
52 }
53 addr_len = sizeof(rth->local);
54 if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
55 perror("Cannot getsockname");
56 return -1;
57 }
58 if (addr_len != sizeof(rth->local)) {
59 fprintf(stderr, "Wrong address length %d\n", addr_len);
60 return -1;
61 }
62 if (rth->local.nl_family != AF_NETLINK) {
63 fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
64 return -1;
65 }
66 rth->seq = time(NULL);
67 return 0;
68}
69
net[shemminger]!shemmingerc7699872004-07-07 17:05:56 +000070int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
71{
72 return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
73}
74
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +000075int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
76{
77 struct {
78 struct nlmsghdr nlh;
79 struct rtgenmsg g;
80 } req;
81 struct sockaddr_nl nladdr;
82
83 memset(&nladdr, 0, sizeof(nladdr));
84 nladdr.nl_family = AF_NETLINK;
85
86 req.nlh.nlmsg_len = sizeof(req);
87 req.nlh.nlmsg_type = type;
88 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
89 req.nlh.nlmsg_pid = 0;
90 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
91 req.g.rtgen_family = family;
92
93 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
94}
95
96int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
97{
98 struct sockaddr_nl nladdr;
99
100 memset(&nladdr, 0, sizeof(nladdr));
101 nladdr.nl_family = AF_NETLINK;
102
103 return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
104}
105
106int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
107{
108 struct nlmsghdr nlh;
109 struct sockaddr_nl nladdr;
110 struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
111 struct msghdr msg = {
112 (void*)&nladdr, sizeof(nladdr),
113 iov, 2,
114 NULL, 0,
115 0
116 };
117
118 memset(&nladdr, 0, sizeof(nladdr));
119 nladdr.nl_family = AF_NETLINK;
120
121 nlh.nlmsg_len = NLMSG_LENGTH(len);
122 nlh.nlmsg_type = type;
123 nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
124 nlh.nlmsg_pid = 0;
125 nlh.nlmsg_seq = rth->dump = ++rth->seq;
126
127 return sendmsg(rth->fd, &msg, 0);
128}
129
130int rtnl_dump_filter(struct rtnl_handle *rth,
131 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
132 void *arg1,
133 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
134 void *arg2)
135{
136 char buf[8192];
137 struct sockaddr_nl nladdr;
138 struct iovec iov = { buf, sizeof(buf) };
139
140 while (1) {
141 int status;
142 struct nlmsghdr *h;
143
144 struct msghdr msg = {
145 (void*)&nladdr, sizeof(nladdr),
146 &iov, 1,
147 NULL, 0,
148 0
149 };
150
151 status = recvmsg(rth->fd, &msg, 0);
152
153 if (status < 0) {
154 if (errno == EINTR)
155 continue;
156 perror("OVERRUN");
157 continue;
158 }
159 if (status == 0) {
160 fprintf(stderr, "EOF on netlink\n");
161 return -1;
162 }
163 if (msg.msg_namelen != sizeof(nladdr)) {
164 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
165 exit(1);
166 }
167
168 h = (struct nlmsghdr*)buf;
169 while (NLMSG_OK(h, status)) {
170 int err;
171
org[shemminger]!shemminger10f57ef2004-06-07 22:04:04 +0000172 if (nladdr.nl_pid != 0 ||
173 h->nlmsg_pid != rth->local.nl_pid ||
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000174 h->nlmsg_seq != rth->dump) {
175 if (junk) {
176 err = junk(&nladdr, h, arg2);
177 if (err < 0)
178 return err;
179 }
180 goto skip_it;
181 }
182
183 if (h->nlmsg_type == NLMSG_DONE)
184 return 0;
185 if (h->nlmsg_type == NLMSG_ERROR) {
186 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
187 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
188 fprintf(stderr, "ERROR truncated\n");
189 } else {
190 errno = -err->error;
191 perror("RTNETLINK answers");
192 }
193 return -1;
194 }
195 err = filter(&nladdr, h, arg1);
196 if (err < 0)
197 return err;
198
199skip_it:
200 h = NLMSG_NEXT(h, status);
201 }
202 if (msg.msg_flags & MSG_TRUNC) {
203 fprintf(stderr, "Message truncated\n");
204 continue;
205 }
206 if (status) {
207 fprintf(stderr, "!!!Remnant of size %d\n", status);
208 exit(1);
209 }
210 }
211}
212
213int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
214 unsigned groups, struct nlmsghdr *answer,
215 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
216 void *jarg)
217{
218 int status;
219 unsigned seq;
220 struct nlmsghdr *h;
221 struct sockaddr_nl nladdr;
222 struct iovec iov = { (void*)n, n->nlmsg_len };
223 char buf[8192];
224 struct msghdr msg = {
225 (void*)&nladdr, sizeof(nladdr),
226 &iov, 1,
227 NULL, 0,
228 0
229 };
230
231 memset(&nladdr, 0, sizeof(nladdr));
232 nladdr.nl_family = AF_NETLINK;
233 nladdr.nl_pid = peer;
234 nladdr.nl_groups = groups;
235
236 n->nlmsg_seq = seq = ++rtnl->seq;
237 if (answer == NULL)
238 n->nlmsg_flags |= NLM_F_ACK;
239
240 status = sendmsg(rtnl->fd, &msg, 0);
241
242 if (status < 0) {
243 perror("Cannot talk to rtnetlink");
244 return -1;
245 }
246
247 iov.iov_base = buf;
248
249 while (1) {
250 iov.iov_len = sizeof(buf);
251 status = recvmsg(rtnl->fd, &msg, 0);
252
253 if (status < 0) {
254 if (errno == EINTR)
255 continue;
256 perror("OVERRUN");
257 continue;
258 }
259 if (status == 0) {
260 fprintf(stderr, "EOF on netlink\n");
261 return -1;
262 }
263 if (msg.msg_namelen != sizeof(nladdr)) {
264 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
265 exit(1);
266 }
267 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
268 int err;
269 int len = h->nlmsg_len;
270 int l = len - sizeof(*h);
271
272 if (l<0 || len>status) {
273 if (msg.msg_flags & MSG_TRUNC) {
274 fprintf(stderr, "Truncated message\n");
275 return -1;
276 }
277 fprintf(stderr, "!!!malformed message: len=%d\n", len);
278 exit(1);
279 }
280
org[shemminger]!shemminger10f57ef2004-06-07 22:04:04 +0000281 if (nladdr.nl_pid != peer ||
282 h->nlmsg_pid != rtnl->local.nl_pid ||
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000283 h->nlmsg_seq != seq) {
284 if (junk) {
285 err = junk(&nladdr, h, jarg);
286 if (err < 0)
287 return err;
288 }
289 continue;
290 }
291
292 if (h->nlmsg_type == NLMSG_ERROR) {
293 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
294 if (l < sizeof(struct nlmsgerr)) {
295 fprintf(stderr, "ERROR truncated\n");
296 } else {
297 errno = -err->error;
298 if (errno == 0) {
299 if (answer)
300 memcpy(answer, h, h->nlmsg_len);
301 return 0;
302 }
303 perror("RTNETLINK answers");
304 }
305 return -1;
306 }
307 if (answer) {
308 memcpy(answer, h, h->nlmsg_len);
309 return 0;
310 }
311
312 fprintf(stderr, "Unexpected reply!!!\n");
313
314 status -= NLMSG_ALIGN(len);
315 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
316 }
317 if (msg.msg_flags & MSG_TRUNC) {
318 fprintf(stderr, "Message truncated\n");
319 continue;
320 }
321 if (status) {
322 fprintf(stderr, "!!!Remnant of size %d\n", status);
323 exit(1);
324 }
325 }
326}
327
328int rtnl_listen(struct rtnl_handle *rtnl,
329 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
330 void *jarg)
331{
332 int status;
333 struct nlmsghdr *h;
334 struct sockaddr_nl nladdr;
335 struct iovec iov;
336 char buf[8192];
337 struct msghdr msg = {
338 (void*)&nladdr, sizeof(nladdr),
339 &iov, 1,
340 NULL, 0,
341 0
342 };
343
344 memset(&nladdr, 0, sizeof(nladdr));
345 nladdr.nl_family = AF_NETLINK;
346 nladdr.nl_pid = 0;
347 nladdr.nl_groups = 0;
348
349
350 iov.iov_base = buf;
351
352 while (1) {
353 iov.iov_len = sizeof(buf);
354 status = recvmsg(rtnl->fd, &msg, 0);
355
356 if (status < 0) {
357 if (errno == EINTR)
358 continue;
359 perror("OVERRUN");
360 continue;
361 }
362 if (status == 0) {
363 fprintf(stderr, "EOF on netlink\n");
364 return -1;
365 }
366 if (msg.msg_namelen != sizeof(nladdr)) {
367 fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
368 exit(1);
369 }
370 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
371 int err;
372 int len = h->nlmsg_len;
373 int l = len - sizeof(*h);
374
375 if (l<0 || len>status) {
376 if (msg.msg_flags & MSG_TRUNC) {
377 fprintf(stderr, "Truncated message\n");
378 return -1;
379 }
380 fprintf(stderr, "!!!malformed message: len=%d\n", len);
381 exit(1);
382 }
383
384 err = handler(&nladdr, h, jarg);
385 if (err < 0)
386 return err;
387
388 status -= NLMSG_ALIGN(len);
389 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
390 }
391 if (msg.msg_flags & MSG_TRUNC) {
392 fprintf(stderr, "Message truncated\n");
393 continue;
394 }
395 if (status) {
396 fprintf(stderr, "!!!Remnant of size %d\n", status);
397 exit(1);
398 }
399 }
400}
401
402int rtnl_from_file(FILE *rtnl,
403 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
404 void *jarg)
405{
406 int status;
407 struct sockaddr_nl nladdr;
408 char buf[8192];
409 struct nlmsghdr *h = (void*)buf;
410
411 memset(&nladdr, 0, sizeof(nladdr));
412 nladdr.nl_family = AF_NETLINK;
413 nladdr.nl_pid = 0;
414 nladdr.nl_groups = 0;
415
416 while (1) {
417 int err, len, type;
418 int l;
419
420 status = fread(&buf, 1, sizeof(*h), rtnl);
421
422 if (status < 0) {
423 if (errno == EINTR)
424 continue;
425 perror("rtnl_from_file: fread");
426 return -1;
427 }
428 if (status == 0)
429 return 0;
430
431 len = h->nlmsg_len;
432 type= h->nlmsg_type;
433 l = len - sizeof(*h);
434
435 if (l<0 || len>sizeof(buf)) {
436 fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
437 len, ftell(rtnl));
438 return -1;
439 }
440
441 status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
442
443 if (status < 0) {
444 perror("rtnl_from_file: fread");
445 return -1;
446 }
447 if (status < l) {
448 fprintf(stderr, "rtnl-from_file: truncated message\n");
449 return -1;
450 }
451
452 err = handler(&nladdr, h, jarg);
453 if (err < 0)
454 return err;
455 }
456}
457
458int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
459{
460 int len = RTA_LENGTH(4);
461 struct rtattr *rta;
462 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
463 return -1;
464 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
465 rta->rta_type = type;
466 rta->rta_len = len;
467 memcpy(RTA_DATA(rta), &data, 4);
468 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
469 return 0;
470}
471
472int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
473{
474 int len = RTA_LENGTH(alen);
475 struct rtattr *rta;
476
477 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
478 return -1;
479 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
480 rta->rta_type = type;
481 rta->rta_len = len;
482 memcpy(RTA_DATA(rta), data, alen);
483 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
484 return 0;
485}
486
487int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
488{
489 int len = RTA_LENGTH(4);
490 struct rtattr *subrta;
491
492 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
493 return -1;
494 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
495 subrta->rta_type = type;
496 subrta->rta_len = len;
497 memcpy(RTA_DATA(subrta), &data, 4);
498 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
499 return 0;
500}
501
502int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
503{
504 struct rtattr *subrta;
505 int len = RTA_LENGTH(alen);
506
507 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
508 return -1;
509 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
510 subrta->rta_type = type;
511 subrta->rta_len = len;
512 memcpy(RTA_DATA(subrta), data, alen);
513 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
514 return 0;
515}
516
517
518int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
519{
520 while (RTA_OK(rta, len)) {
521 if (rta->rta_type <= max)
522 tb[rta->rta_type] = rta;
523 rta = RTA_NEXT(rta,len);
524 }
525 if (len)
526 fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
527 return 0;
528}
net[shemminger]!shemmingerc7699872004-07-07 17:05:56 +0000529
530int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
531{
532 int i = 0;
533 while (RTA_OK(rta, len)) {
534 if (rta->rta_type <= max)
535 tb[i++] = rta;
536 rta = RTA_NEXT(rta,len);
537 }
538 if (len)
539 fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
540 return i;
541}