blob: 4beeca652b2d7525471ff07934615b499d10123f [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
33int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
34{
35 int addr_len;
36
37 memset(rth, 0, sizeof(rth));
38
39 rth->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
40 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
70int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
71{
72 struct {
73 struct nlmsghdr nlh;
74 struct rtgenmsg g;
75 } req;
76 struct sockaddr_nl nladdr;
77
78 memset(&nladdr, 0, sizeof(nladdr));
79 nladdr.nl_family = AF_NETLINK;
80
81 req.nlh.nlmsg_len = sizeof(req);
82 req.nlh.nlmsg_type = type;
83 req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
84 req.nlh.nlmsg_pid = 0;
85 req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
86 req.g.rtgen_family = family;
87
88 return sendto(rth->fd, (void*)&req, sizeof(req), 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
89}
90
91int rtnl_send(struct rtnl_handle *rth, char *buf, int len)
92{
93 struct sockaddr_nl nladdr;
94
95 memset(&nladdr, 0, sizeof(nladdr));
96 nladdr.nl_family = AF_NETLINK;
97
98 return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
99}
100
101int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
102{
103 struct nlmsghdr nlh;
104 struct sockaddr_nl nladdr;
105 struct iovec iov[2] = { { &nlh, sizeof(nlh) }, { req, len } };
106 struct msghdr msg = {
107 (void*)&nladdr, sizeof(nladdr),
108 iov, 2,
109 NULL, 0,
110 0
111 };
112
113 memset(&nladdr, 0, sizeof(nladdr));
114 nladdr.nl_family = AF_NETLINK;
115
116 nlh.nlmsg_len = NLMSG_LENGTH(len);
117 nlh.nlmsg_type = type;
118 nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
119 nlh.nlmsg_pid = 0;
120 nlh.nlmsg_seq = rth->dump = ++rth->seq;
121
122 return sendmsg(rth->fd, &msg, 0);
123}
124
125int rtnl_dump_filter(struct rtnl_handle *rth,
126 int (*filter)(struct sockaddr_nl *, struct nlmsghdr *n, void *),
127 void *arg1,
128 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
129 void *arg2)
130{
131 char buf[8192];
132 struct sockaddr_nl nladdr;
133 struct iovec iov = { buf, sizeof(buf) };
134
135 while (1) {
136 int status;
137 struct nlmsghdr *h;
138
139 struct msghdr msg = {
140 (void*)&nladdr, sizeof(nladdr),
141 &iov, 1,
142 NULL, 0,
143 0
144 };
145
146 status = recvmsg(rth->fd, &msg, 0);
147
148 if (status < 0) {
149 if (errno == EINTR)
150 continue;
151 perror("OVERRUN");
152 continue;
153 }
154 if (status == 0) {
155 fprintf(stderr, "EOF on netlink\n");
156 return -1;
157 }
158 if (msg.msg_namelen != sizeof(nladdr)) {
159 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
160 exit(1);
161 }
162
163 h = (struct nlmsghdr*)buf;
164 while (NLMSG_OK(h, status)) {
165 int err;
166
org[shemminger]!shemminger10f57ef2004-06-07 22:04:04 +0000167 if (nladdr.nl_pid != 0 ||
168 h->nlmsg_pid != rth->local.nl_pid ||
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000169 h->nlmsg_seq != rth->dump) {
170 if (junk) {
171 err = junk(&nladdr, h, arg2);
172 if (err < 0)
173 return err;
174 }
175 goto skip_it;
176 }
177
178 if (h->nlmsg_type == NLMSG_DONE)
179 return 0;
180 if (h->nlmsg_type == NLMSG_ERROR) {
181 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
182 if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
183 fprintf(stderr, "ERROR truncated\n");
184 } else {
185 errno = -err->error;
186 perror("RTNETLINK answers");
187 }
188 return -1;
189 }
190 err = filter(&nladdr, h, arg1);
191 if (err < 0)
192 return err;
193
194skip_it:
195 h = NLMSG_NEXT(h, status);
196 }
197 if (msg.msg_flags & MSG_TRUNC) {
198 fprintf(stderr, "Message truncated\n");
199 continue;
200 }
201 if (status) {
202 fprintf(stderr, "!!!Remnant of size %d\n", status);
203 exit(1);
204 }
205 }
206}
207
208int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
209 unsigned groups, struct nlmsghdr *answer,
210 int (*junk)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
211 void *jarg)
212{
213 int status;
214 unsigned seq;
215 struct nlmsghdr *h;
216 struct sockaddr_nl nladdr;
217 struct iovec iov = { (void*)n, n->nlmsg_len };
218 char buf[8192];
219 struct msghdr msg = {
220 (void*)&nladdr, sizeof(nladdr),
221 &iov, 1,
222 NULL, 0,
223 0
224 };
225
226 memset(&nladdr, 0, sizeof(nladdr));
227 nladdr.nl_family = AF_NETLINK;
228 nladdr.nl_pid = peer;
229 nladdr.nl_groups = groups;
230
231 n->nlmsg_seq = seq = ++rtnl->seq;
232 if (answer == NULL)
233 n->nlmsg_flags |= NLM_F_ACK;
234
235 status = sendmsg(rtnl->fd, &msg, 0);
236
237 if (status < 0) {
238 perror("Cannot talk to rtnetlink");
239 return -1;
240 }
241
242 iov.iov_base = buf;
243
244 while (1) {
245 iov.iov_len = sizeof(buf);
246 status = recvmsg(rtnl->fd, &msg, 0);
247
248 if (status < 0) {
249 if (errno == EINTR)
250 continue;
251 perror("OVERRUN");
252 continue;
253 }
254 if (status == 0) {
255 fprintf(stderr, "EOF on netlink\n");
256 return -1;
257 }
258 if (msg.msg_namelen != sizeof(nladdr)) {
259 fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
260 exit(1);
261 }
262 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
263 int err;
264 int len = h->nlmsg_len;
265 int l = len - sizeof(*h);
266
267 if (l<0 || len>status) {
268 if (msg.msg_flags & MSG_TRUNC) {
269 fprintf(stderr, "Truncated message\n");
270 return -1;
271 }
272 fprintf(stderr, "!!!malformed message: len=%d\n", len);
273 exit(1);
274 }
275
org[shemminger]!shemminger10f57ef2004-06-07 22:04:04 +0000276 if (nladdr.nl_pid != peer ||
277 h->nlmsg_pid != rtnl->local.nl_pid ||
osdl.org!shemmingeraba5acd2004-04-15 20:56:59 +0000278 h->nlmsg_seq != seq) {
279 if (junk) {
280 err = junk(&nladdr, h, jarg);
281 if (err < 0)
282 return err;
283 }
284 continue;
285 }
286
287 if (h->nlmsg_type == NLMSG_ERROR) {
288 struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
289 if (l < sizeof(struct nlmsgerr)) {
290 fprintf(stderr, "ERROR truncated\n");
291 } else {
292 errno = -err->error;
293 if (errno == 0) {
294 if (answer)
295 memcpy(answer, h, h->nlmsg_len);
296 return 0;
297 }
298 perror("RTNETLINK answers");
299 }
300 return -1;
301 }
302 if (answer) {
303 memcpy(answer, h, h->nlmsg_len);
304 return 0;
305 }
306
307 fprintf(stderr, "Unexpected reply!!!\n");
308
309 status -= NLMSG_ALIGN(len);
310 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
311 }
312 if (msg.msg_flags & MSG_TRUNC) {
313 fprintf(stderr, "Message truncated\n");
314 continue;
315 }
316 if (status) {
317 fprintf(stderr, "!!!Remnant of size %d\n", status);
318 exit(1);
319 }
320 }
321}
322
323int rtnl_listen(struct rtnl_handle *rtnl,
324 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
325 void *jarg)
326{
327 int status;
328 struct nlmsghdr *h;
329 struct sockaddr_nl nladdr;
330 struct iovec iov;
331 char buf[8192];
332 struct msghdr msg = {
333 (void*)&nladdr, sizeof(nladdr),
334 &iov, 1,
335 NULL, 0,
336 0
337 };
338
339 memset(&nladdr, 0, sizeof(nladdr));
340 nladdr.nl_family = AF_NETLINK;
341 nladdr.nl_pid = 0;
342 nladdr.nl_groups = 0;
343
344
345 iov.iov_base = buf;
346
347 while (1) {
348 iov.iov_len = sizeof(buf);
349 status = recvmsg(rtnl->fd, &msg, 0);
350
351 if (status < 0) {
352 if (errno == EINTR)
353 continue;
354 perror("OVERRUN");
355 continue;
356 }
357 if (status == 0) {
358 fprintf(stderr, "EOF on netlink\n");
359 return -1;
360 }
361 if (msg.msg_namelen != sizeof(nladdr)) {
362 fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
363 exit(1);
364 }
365 for (h = (struct nlmsghdr*)buf; status >= sizeof(*h); ) {
366 int err;
367 int len = h->nlmsg_len;
368 int l = len - sizeof(*h);
369
370 if (l<0 || len>status) {
371 if (msg.msg_flags & MSG_TRUNC) {
372 fprintf(stderr, "Truncated message\n");
373 return -1;
374 }
375 fprintf(stderr, "!!!malformed message: len=%d\n", len);
376 exit(1);
377 }
378
379 err = handler(&nladdr, h, jarg);
380 if (err < 0)
381 return err;
382
383 status -= NLMSG_ALIGN(len);
384 h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
385 }
386 if (msg.msg_flags & MSG_TRUNC) {
387 fprintf(stderr, "Message truncated\n");
388 continue;
389 }
390 if (status) {
391 fprintf(stderr, "!!!Remnant of size %d\n", status);
392 exit(1);
393 }
394 }
395}
396
397int rtnl_from_file(FILE *rtnl,
398 int (*handler)(struct sockaddr_nl *,struct nlmsghdr *n, void *),
399 void *jarg)
400{
401 int status;
402 struct sockaddr_nl nladdr;
403 char buf[8192];
404 struct nlmsghdr *h = (void*)buf;
405
406 memset(&nladdr, 0, sizeof(nladdr));
407 nladdr.nl_family = AF_NETLINK;
408 nladdr.nl_pid = 0;
409 nladdr.nl_groups = 0;
410
411 while (1) {
412 int err, len, type;
413 int l;
414
415 status = fread(&buf, 1, sizeof(*h), rtnl);
416
417 if (status < 0) {
418 if (errno == EINTR)
419 continue;
420 perror("rtnl_from_file: fread");
421 return -1;
422 }
423 if (status == 0)
424 return 0;
425
426 len = h->nlmsg_len;
427 type= h->nlmsg_type;
428 l = len - sizeof(*h);
429
430 if (l<0 || len>sizeof(buf)) {
431 fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
432 len, ftell(rtnl));
433 return -1;
434 }
435
436 status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
437
438 if (status < 0) {
439 perror("rtnl_from_file: fread");
440 return -1;
441 }
442 if (status < l) {
443 fprintf(stderr, "rtnl-from_file: truncated message\n");
444 return -1;
445 }
446
447 err = handler(&nladdr, h, jarg);
448 if (err < 0)
449 return err;
450 }
451}
452
453int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
454{
455 int len = RTA_LENGTH(4);
456 struct rtattr *rta;
457 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
458 return -1;
459 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
460 rta->rta_type = type;
461 rta->rta_len = len;
462 memcpy(RTA_DATA(rta), &data, 4);
463 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
464 return 0;
465}
466
467int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
468{
469 int len = RTA_LENGTH(alen);
470 struct rtattr *rta;
471
472 if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
473 return -1;
474 rta = (struct rtattr*)(((char*)n) + NLMSG_ALIGN(n->nlmsg_len));
475 rta->rta_type = type;
476 rta->rta_len = len;
477 memcpy(RTA_DATA(rta), data, alen);
478 n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
479 return 0;
480}
481
482int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
483{
484 int len = RTA_LENGTH(4);
485 struct rtattr *subrta;
486
487 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
488 return -1;
489 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
490 subrta->rta_type = type;
491 subrta->rta_len = len;
492 memcpy(RTA_DATA(subrta), &data, 4);
493 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
494 return 0;
495}
496
497int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen)
498{
499 struct rtattr *subrta;
500 int len = RTA_LENGTH(alen);
501
502 if (RTA_ALIGN(rta->rta_len) + len > maxlen)
503 return -1;
504 subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
505 subrta->rta_type = type;
506 subrta->rta_len = len;
507 memcpy(RTA_DATA(subrta), data, alen);
508 rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
509 return 0;
510}
511
512
513int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
514{
515 while (RTA_OK(rta, len)) {
516 if (rta->rta_type <= max)
517 tb[rta->rta_type] = rta;
518 rta = RTA_NEXT(rta,len);
519 }
520 if (len)
521 fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
522 return 0;
523}