]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/netlink.c
netlink: make change of buffer size optional
[thirdparty/lldpd.git] / src / daemon / netlink.c
CommitLineData
e12c2365
VB
1/* -*- mode: c; c-file-style: "openbsd" -*- */
2/*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
adbb6e54
VB
18/* Grabbing interfaces information with netlink only. */
19
e12c2365
VB
20#include "lldpd.h"
21
0fa2254b 22#include <errno.h>
e12c2365 23#include <sys/socket.h>
0fa2254b 24#include <netdb.h>
13181ede 25#include <net/if_arp.h>
0fa2254b
VB
26#include <linux/netlink.h>
27#include <linux/rtnetlink.h>
28
29#define NETLINK_BUFFER 4096
02c91f0a 30
0fa2254b
VB
31struct netlink_req {
32 struct nlmsghdr hdr;
33 struct rtgenmsg gen;
34};
13181ede
VB
35
36struct lldpd_netlink {
0fa2254b
VB
37 int nl_socket;
38 /* Cache */
39 struct interfaces_device_list *devices;
40 struct interfaces_address_list *addresses;
e12c2365
VB
41};
42
c9f0ee58
AA
43
44static int
45netlink_socket_set_buffer_sizes(int s)
46{
568a0d73
VB
47 int sndbuf = NETLINK_SEND_BUFSIZE;
48 int rcvbuf = NETLINK_RECEIVE_BUFSIZE;
c9f0ee58
AA
49 socklen_t size = 0;
50 int got = 0;
51
93051bc2 52 if (sndbuf > 0 && setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) < 0) {
c9f0ee58
AA
53 log_warn("netlink", "unable to set SO_SNDBUF to '%d'", sndbuf);
54 return -1;
55 }
56
93051bc2 57 if (rcvbuf > 0 && setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) < 0) {
c9f0ee58
AA
58 log_warn("netlink", "unable to set SO_RCVBUF to '%d'", rcvbuf);
59 return -1;
60 }
61
62 /* Now read them back from kernel.
63 * SO_SNDBUF & SO_RCVBUF are cap-ed at sysctl `net.core.rmem_max` &
64 * `net.core.wmem_max`. This it the easiest [probably sanest too]
65 * to validate that our socket buffers were set properly.
66 */
93051bc2
VB
67 if (sndbuf > 0) {
68 if (getsockopt(s, SOL_SOCKET, SO_SNDBUF, &got, &size) < 0) {
69 log_warn("netlink", "unable to get SO_SNDBUF");
70 } else {
71 if (size != sizeof(sndbuf))
72 log_warn("netlink", "size mismatch for SO_SNDBUF got '%u' "
73 "should be '%zu'", size, sizeof(sndbuf));
74 if (got != sndbuf)
75 log_warn("netlink", "tried to set SO_SNDBUF to '%d' "
76 "but got '%d'", sndbuf, got);
77 }
c9f0ee58
AA
78 }
79
93051bc2
VB
80 if (rcvbuf > 0) {
81 if (getsockopt(s, SOL_SOCKET, SO_RCVBUF, &got, &size) < 0) {
82 log_warn("netlink", "unable to get SO_RCVBUF");
83 } else {
84 if (size != sizeof(rcvbuf))
85 log_warn("netlink", "size mismatch for SO_RCVBUF got '%u' "
86 "should be '%zu'", size, sizeof(rcvbuf));
87 if (got != rcvbuf)
88 log_warn("netlink", "tried to set SO_RCVBUF to '%d' "
89 "but got '%d'", rcvbuf, got);
90 }
c9f0ee58
AA
91 }
92
93 return 0;
94}
95
e12c2365 96/**
0fa2254b
VB
97 * Connect to netlink.
98 *
99 * Open a Netlink socket and connect to it.
100 *
101 * @param protocol Which protocol to use (eg NETLINK_ROUTE).
102 * @param groups Which groups we want to subscribe to
103 * @return The opened socket or -1 on error.
e12c2365 104 */
0fa2254b
VB
105static int
106netlink_connect(int protocol, unsigned groups)
e12c2365 107{
0fa2254b
VB
108 int s;
109 struct sockaddr_nl local = {
110 .nl_family = AF_NETLINK,
111 .nl_pid = getpid(),
112 .nl_groups = groups
113 };
114
115 /* Open Netlink socket */
116 log_debug("netlink", "opening netlink socket");
117 s = socket(AF_NETLINK, SOCK_RAW, protocol);
118 if (s == -1) {
119 log_warn("netlink", "unable to open netlink socket");
120 return -1;
13181ede 121 }
c9f0ee58
AA
122 if (netlink_socket_set_buffer_sizes(s) < 0)
123 return -1;
0fa2254b
VB
124 if (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
125 log_warn("netlink", "unable to bind netlink socket");
126 close(s);
127 return -1;
128 }
129 return s;
e12c2365
VB
130}
131
132/**
0fa2254b 133 * Send a netlink message.
e12c2365 134 *
0fa2254b
VB
135 * The type of the message can be chosen as well the route family. The
136 * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
e12c2365 137 *
0fa2254b
VB
138 * @param s the netlink socket
139 * @param type the request type (eg RTM_GETLINK)
140 * @param family the rt family (eg AF_PACKET)
e12c2365
VB
141 * @return 0 on success, -1 otherwise
142 */
143static int
0fa2254b 144netlink_send(int s, int type, int family, int seq)
e12c2365 145{
0fa2254b
VB
146 struct netlink_req req = {
147 .hdr = {
148 .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
149 .nlmsg_type = type,
150 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
151 .nlmsg_seq = seq,
152 .nlmsg_pid = getpid() },
153 .gen = { .rtgen_family = family }
154 };
155 struct iovec iov = {
156 .iov_base = &req,
157 .iov_len = req.hdr.nlmsg_len
158 };
159 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
160 struct msghdr rtnl_msg = {
161 .msg_iov = &iov,
162 .msg_iovlen = 1,
163 .msg_name = &peer,
164 .msg_namelen = sizeof(struct sockaddr_nl)
165 };
166
167 /* Send netlink message. This is synchronous but we are guaranteed
168 * to not block. */
169 log_debug("netlink", "sending netlink message");
170 if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
171 log_warn("netlink", "unable to send netlink message");
172 return -1;
16eacc5b
VB
173 }
174
0fa2254b
VB
175 return 0;
176}
16eacc5b 177
0fa2254b
VB
178static void
179netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
180{
181 while (RTA_OK(rta, len)) {
182 if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
183 tb[rta->rta_type] = rta;
184 rta = RTA_NEXT(rta,len);
16eacc5b 185 }
16eacc5b
VB
186}
187
e12c2365 188/**
0fa2254b
VB
189 * Parse a `linkinfo` attributes.
190 *
191 * @param iff where to put the result
192 * @param rta linkinfo attribute
193 * @param len length of attributes
e12c2365 194 */
0fa2254b
VB
195static void
196netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
e12c2365 197{
0fa2254b
VB
198 struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {};
199 char *kind = NULL;
200
201 netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
202
203 if (link_info_attrs[IFLA_INFO_KIND]) {
204 kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
205 if (kind) {
206 if (!strcmp(kind, "vlan")) {
207 log_debug("netlink", "interface %s is a VLAN",
208 iff->name);
209 iff->type |= IFACE_VLAN_T;
210 } else if (!strcmp(kind, "bridge")) {
211 log_debug("netlink", "interface %s is a bridge",
212 iff->name);
213 iff->type |= IFACE_BRIDGE_T;
214 } else if (!strcmp(kind, "bond")) {
215 log_debug("netlink", "interface %s is a bond",
216 iff->name);
217 iff->type |= IFACE_BOND_T;
218 }
219 }
220 }
13181ede 221
0fa2254b
VB
222 if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) {
223 struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX+1] = {};
224 netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX,
225 RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
226 RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
227
228 if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) {
229 iff->vlanid = *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs[IFLA_VLAN_ID]);
230 log_debug("netlink", "VLAN ID for interface %s is %d",
231 iff->name, iff->vlanid);
232 }
233 }
234
235 free(kind);
e12c2365
VB
236}
237
238/**
13181ede 239 * Parse a `link` netlink message.
e12c2365 240 *
0fa2254b
VB
241 * @param msg message to be parsed
242 * @param iff where to put the result
243 * return 0 if the interface is worth it, -1 otherwise
e12c2365 244 */
0fa2254b
VB
245static int
246netlink_parse_link(struct nlmsghdr *msg,
247 struct interfaces_device *iff)
e12c2365 248{
0fa2254b
VB
249 struct ifinfomsg *ifi;
250 struct rtattr *attribute;
251 int len;
252 ifi = NLMSG_DATA(msg);
253 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
254
255 if (ifi->ifi_type != ARPHRD_ETHER) {
256 log_debug("netlink", "skip non Ethernet interface at index %d",
257 ifi->ifi_index);
258 return -1;
13181ede
VB
259 }
260
0fa2254b
VB
261 iff->index = ifi->ifi_index;
262 iff->flags = ifi->ifi_flags;
263 iff->lower_idx = -1;
264 iff->upper_idx = -1;
265
266 for (attribute = IFLA_RTA(ifi);
267 RTA_OK(attribute, len);
268 attribute = RTA_NEXT(attribute, len)) {
269 switch(attribute->rta_type) {
270 case IFLA_IFNAME:
271 /* Interface name */
272 iff->name = strdup(RTA_DATA(attribute));
273 break;
274 case IFLA_IFALIAS:
275 /* Interface alias */
276 iff->alias = strdup(RTA_DATA(attribute));
277 break;
278 case IFLA_ADDRESS:
279 /* Interface MAC address */
280 iff->address = malloc(RTA_PAYLOAD(attribute));
281 if (iff->address)
282 memcpy(iff->address, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
283 break;
284 case IFLA_LINK:
285 /* Index of "lower" interface */
286 iff->lower_idx = *(int*)RTA_DATA(attribute);
d2e6f750
VB
287 log_debug("netlink", "attribute IFLA_LINK for %s: %d",
288 iff->name ? iff->name : "(unknown)", iff->lower_idx);
0fa2254b 289 break;
c04fafa7
VB
290 case IFLA_LINK_NETNSID:
291 /* Is the lower interface into another namesapce? */
292 iff->lower_idx = -1;
d2e6f750
VB
293 log_debug("netlink", "attribute IFLA_LINK_NETNSID received for %s",
294 iff->name ? iff->name : "(unknown)");
c04fafa7 295 break;
0fa2254b
VB
296 case IFLA_MASTER:
297 /* Index of master interface */
298 iff->upper_idx = *(int*)RTA_DATA(attribute);
299 break;
0fa2254b
VB
300 case IFLA_MTU:
301 /* Maximum Transmission Unit */
302 iff->mtu = *(int*)RTA_DATA(attribute);
303 break;
304 case IFLA_LINKINFO:
305 netlink_parse_linkinfo(iff, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
306 break;
307 default:
308 log_debug("netlink", "unhandled link attribute type %d for iface %s",
309 attribute->rta_type, iff->name ? iff->name : "(unknown)");
310 break;
311 }
13181ede 312 }
0fa2254b
VB
313 if (!iff->name || !iff->address) {
314 log_info("netlink", "interface %d does not have a name or an address, skip",
13181ede 315 iff->index);
0fa2254b 316 return -1;
13181ede
VB
317 }
318
0fa2254b
VB
319 log_debug("netlink", "parsed link %d (%s, flags: %d)",
320 iff->index, iff->name, iff->flags);
321 return 0;
e12c2365
VB
322}
323
324/**
13181ede 325 * Parse a `address` netlink message.
e12c2365 326 *
0fa2254b
VB
327 * @param msg message to be parsed
328 * @param ifa where to put the result
329 * return 0 if the address is worth it, -1 otherwise
e12c2365 330 */
0fa2254b
VB
331static int
332netlink_parse_address(struct nlmsghdr *msg,
333 struct interfaces_address *ifa)
e12c2365 334{
0fa2254b
VB
335 struct ifaddrmsg *ifi;
336 struct rtattr *attribute;
337 int len;
338 ifi = NLMSG_DATA(msg);
339 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
340
341 ifa->index = ifi->ifa_index;
342 ifa->flags = ifi->ifa_flags;
343 switch (ifi->ifa_family) {
13181ede
VB
344 case AF_INET:
345 case AF_INET6: break;
346 default:
347 log_debug("netlink", "got a non IP address on if %d (family: %d)",
0fa2254b
VB
348 ifa->index, ifi->ifa_family);
349 return -1;
13181ede
VB
350 }
351
0fa2254b
VB
352 for (attribute = IFA_RTA(ifi);
353 RTA_OK(attribute, len);
354 attribute = RTA_NEXT(attribute, len)) {
355 switch(attribute->rta_type) {
356 case IFA_ADDRESS:
357 /* Address */
358 if (ifi->ifa_family == AF_INET) {
359 struct sockaddr_in ip;
360 memset(&ip, 0, sizeof(struct sockaddr_in));
361 ip.sin_family = AF_INET;
362 memcpy(&ip.sin_addr, RTA_DATA(attribute),
363 sizeof(struct in_addr));
364 memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
365 } else {
366 struct sockaddr_in6 ip6;
367 memset(&ip6, 0, sizeof(struct sockaddr_in6));
368 ip6.sin6_family = AF_INET6;
369 memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
370 sizeof(struct in6_addr));
371 memcpy(&ifa->address, &ip6, sizeof(struct sockaddr_in6));
372 }
373 break;
374 default:
375 log_debug("netlink", "unhandled address attribute type %d for iface %d",
376 attribute->rta_type, ifa->index);
377 break;
378 }
13181ede 379 }
0fa2254b 380 if (ifa->address.ss_family == AF_UNSPEC) {
13181ede
VB
381 log_debug("netlink", "no IP for interface %d",
382 ifa->index);
0fa2254b 383 return -1;
13181ede 384 }
0fa2254b 385 return 0;
e12c2365
VB
386}
387
58e30b5a
VB
388/**
389 * Merge an old interface with a new one.
390 *
391 * Some properties may be absent in the new interface that should be copied over
392 * from the old one.
393 */
394void
395netlink_merge(struct interfaces_device *old, struct interfaces_device *new)
396{
397 if (new->alias == NULL) {
398 new->alias = old->alias;
399 old->alias = NULL;
400 }
401 if (new->address == NULL) {
402 new->address = old->address;
403 old->address = NULL;
404 }
405 if (new->mtu == 0)
406 new->mtu = old->mtu;
407 if (new->type == 0)
408 new->type = old->type;
409 if (new->vlanid == 0)
410 new->vlanid = old->vlanid;
d2e6f750
VB
411
412 /* It's not possible for lower link to change */
413 new->lower_idx = old->lower_idx;
58e30b5a
VB
414}
415
e12c2365 416/**
0fa2254b 417 * Receive netlink answer from the kernel.
e12c2365 418 *
0fa2254b
VB
419 * @param s the netlink socket
420 * @param ifs list to store interface list or NULL if we don't
421 * @param ifas list to store address list or NULL if we don't
422 * @return 0 on success, -1 on error
e12c2365 423 */
0fa2254b
VB
424static int
425netlink_recv(int s,
426 struct interfaces_device_list *ifs,
427 struct interfaces_address_list *ifas)
e12c2365 428{
85b72fe0
DM
429 int end = 0, ret = 0;
430 int flags = MSG_PEEK | MSG_TRUNC;
431 struct iovec iov;
0fa2254b
VB
432 int link_update = 0;
433
434 struct interfaces_device *ifdold;
435 struct interfaces_device *ifdnew;
436 struct interfaces_address *ifaold;
437 struct interfaces_address *ifanew;
438 char addr[INET6_ADDRSTRLEN + 1];
439
85b72fe0
DM
440 iov.iov_len = NETLINK_BUFFER;
441 iov.iov_base = malloc(iov.iov_len);
442 if (!iov.iov_base) {
443 log_warn("netlink", "not enough memory");
444 return -1;
445 }
446
0fa2254b
VB
447 while (!end) {
448 ssize_t len;
449 struct nlmsghdr *msg;
0fa2254b
VB
450 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
451 struct msghdr rtnl_reply = {
452 .msg_iov = &iov,
453 .msg_iovlen = 1,
454 .msg_name = &peer,
455 .msg_namelen = sizeof(struct sockaddr_nl)
456 };
457
85b72fe0
DM
458retry:
459 len = recvmsg(s, &rtnl_reply, flags);
0fa2254b
VB
460 if (len == -1) {
461 if (errno == EAGAIN || errno == EWOULDBLOCK) {
462 log_debug("netlink", "should have received something, but didn't");
85b72fe0
DM
463 ret = 0;
464 goto out;
0fa2254b 465 }
644cd42d 466 log_warn("netlink", "unable to receive netlink answer");
85b72fe0
DM
467 ret = -1;
468 goto out;
0fa2254b 469 }
85b72fe0
DM
470 if (!len) {
471 ret = 0;
472 goto out;
473 }
474
475 if (iov.iov_len < len || (rtnl_reply.msg_flags & MSG_TRUNC)) {
476 void *tmp;
477
478 /* Provided buffer is not large enough, enlarge it
479 * to size of len (which should be total length of the message)
480 * and try again. */
481 iov.iov_len = len;
482 tmp = realloc(iov.iov_base, iov.iov_len);
483 if (!tmp) {
484 log_warn("netlink", "not enough memory");
485 ret = -1;
486 goto out;
487 }
488 log_debug("netlink", "enlarge message size to %zu bytes", len);
489 iov.iov_base = tmp;
490 flags = 0;
491 goto retry;
492 }
493
494 if (flags != 0) {
495 /* Buffer is big enough, do the actual reading */
496 flags = 0;
497 goto retry;
498 }
499
500 for (msg = (struct nlmsghdr*)(void*)(iov.iov_base);
0fa2254b
VB
501 NLMSG_OK(msg, len);
502 msg = NLMSG_NEXT(msg, len)) {
503 if (!(msg->nlmsg_flags & NLM_F_MULTI))
504 end = 1;
505 switch (msg->nlmsg_type) {
506 case NLMSG_DONE:
507 log_debug("netlink", "received done message");
508 end = 1;
509 break;
510 case RTM_NEWLINK:
511 case RTM_DELLINK:
512 if (!ifs) break;
513 log_debug("netlink", "received link information");
514 ifdnew = calloc(1, sizeof(struct interfaces_device));
515 if (ifdnew == NULL) {
516 log_warn("netlink", "not enough memory for another interface, give up what we have");
517 goto end;
518 }
519 if (netlink_parse_link(msg, ifdnew) == 0) {
520 /* We need to find if we already have this interface */
521 TAILQ_FOREACH(ifdold, ifs, next) {
522 if (ifdold->index == ifdnew->index) break;
523 }
524 if (msg->nlmsg_type == RTM_NEWLINK) {
525 if (ifdold == NULL) {
526 log_debug("netlink", "interface %s is new",
527 ifdnew->name);
528 TAILQ_INSERT_TAIL(ifs, ifdnew, next);
529 } else {
530 log_debug("netlink", "interface %s/%s is updated",
531 ifdold->name, ifdnew->name);
58e30b5a 532 netlink_merge(ifdold, ifdnew);
0fa2254b
VB
533 TAILQ_INSERT_AFTER(ifs, ifdold, ifdnew, next);
534 TAILQ_REMOVE(ifs, ifdold, next);
535 interfaces_free_device(ifdold);
536 }
537 } else {
538 if (ifdold == NULL) {
539 log_warnx("netlink",
540 "removal request for %s, but no knowledge of it",
541 ifdnew->name);
542 } else {
543 log_debug("netlink", "interface %s is to be removed",
544 ifdold->name);
545 TAILQ_REMOVE(ifs, ifdold, next);
546 interfaces_free_device(ifdold);
547 }
548 interfaces_free_device(ifdnew);
549 }
550 link_update = 1;
551 } else {
552 interfaces_free_device(ifdnew);
553 }
554 break;
555 case RTM_NEWADDR:
556 case RTM_DELADDR:
557 if (!ifas) break;
558 log_debug("netlink", "received address information");
559 ifanew = calloc(1, sizeof(struct interfaces_address));
560 if (ifanew == NULL) {
561 log_warn("netlink", "not enough memory for another address, give what we have");
562 goto end;
563 }
564 if (netlink_parse_address(msg, ifanew) == 0) {
565 TAILQ_FOREACH(ifaold, ifas, next) {
566 if ((ifaold->index == ifanew->index) &&
567 !memcmp(&ifaold->address, &ifanew->address,
568 sizeof(ifaold->address))) continue;
569 }
570 if (getnameinfo((struct sockaddr *)&ifanew->address,
571 sizeof(ifanew->address),
572 addr, sizeof(addr),
573 NULL, 0, NI_NUMERICHOST) != 0) {
574 strlcpy(addr, "(unknown)", sizeof(addr));
575 }
13181ede 576
0fa2254b
VB
577 if (msg->nlmsg_type == RTM_NEWADDR) {
578 if (ifaold == NULL) {
579 log_debug("netlink", "new address %s%%%d",
580 addr, ifanew->index);
581 TAILQ_INSERT_TAIL(ifas, ifanew, next);
582 } else {
583 log_debug("netlink", "updated address %s%%%d",
584 addr, ifaold->index);
585 TAILQ_INSERT_AFTER(ifas, ifaold, ifanew, next);
586 TAILQ_REMOVE(ifas, ifaold, next);
587 interfaces_free_address(ifaold);
588 }
589 } else {
590 if (ifaold == NULL) {
8027d907 591 log_info("netlink",
0fa2254b
VB
592 "removal request for address of %s%%%d, but no knowledge of it",
593 addr, ifanew->index);
594 } else {
595 log_debug("netlink", "address %s%%%d is to be removed",
596 addr, ifaold->index);
597 TAILQ_REMOVE(ifas, ifaold, next);
598 interfaces_free_address(ifaold);
599 }
600 interfaces_free_address(ifanew);
601 }
602 } else {
603 interfaces_free_address(ifanew);
604 }
605 break;
606 default:
607 log_debug("netlink",
608 "received unhandled message type %d (len: %d)",
609 msg->nlmsg_type, msg->nlmsg_len);
610 }
611 }
85b72fe0 612 flags = MSG_PEEK | MSG_TRUNC;
13181ede 613 }
0fa2254b
VB
614end:
615 if (link_update) {
616 /* Fill out lower/upper */
617 struct interfaces_device *iface1, *iface2;
618 TAILQ_FOREACH(iface1, ifs, next) {
619 if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index) {
620 TAILQ_FOREACH(iface2, ifs, next) {
621 if (iface1->upper_idx == iface2->index) {
c04fafa7
VB
622 log_debug("netlink",
623 "upper interface for %s is %s",
624 iface1->name, iface2->name);
0fa2254b
VB
625 iface1->upper = iface2;
626 break;
627 }
13181ede 628 }
100266c1
VB
629 if (iface2 == NULL)
630 iface1->upper = NULL;
0fa2254b
VB
631 } else {
632 iface1->upper = NULL;
13181ede 633 }
0fa2254b
VB
634 if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index) {
635 TAILQ_FOREACH(iface2, ifs, next) {
636 if (iface1->lower_idx == iface2->index) {
100266c1
VB
637 /* Workaround a bug introduced
638 * in Linux 4.1: a pair of veth
639 * will be lower interface of
640 * each other. Do not modify
641 * index as if one of them is
642 * updated, we will loose the
643 * information about the
644 * loop. */
0fa2254b 645 if (iface2->lower_idx == iface1->index) {
100266c1 646 iface1->lower = NULL;
c04fafa7
VB
647 log_debug("netlink",
648 "link loop detected between %s and %s",
649 iface1->name, iface2->name);
650 } else {
651 log_debug("netlink",
652 "lower interface for %s is %s",
653 iface1->name, iface2->name);
654 iface1->lower = iface2;
655 }
0fa2254b 656 break;
7d0a4975 657 }
100266c1
VB
658 if (iface2 == NULL)
659 iface1->lower = NULL;
13181ede 660 }
0fa2254b
VB
661 } else {
662 iface1->lower = NULL;
13181ede 663 }
0fa2254b 664 }
13181ede 665 }
85b72fe0
DM
666
667out:
668 free(iov.iov_base);
669 return ret;
0fa2254b 670}
13181ede 671
0fa2254b
VB
672static int
673netlink_group_mask(int group)
674{
675 return group ? (1 << (group - 1)) : 0;
e12c2365
VB
676}
677
678/**
0fa2254b 679 * Subscribe to link changes.
e12c2365 680 *
0fa2254b 681 * @return The socket we should listen to for changes.
e12c2365 682 */
0fa2254b
VB
683int
684netlink_subscribe_changes()
e12c2365 685{
0fa2254b
VB
686 unsigned int groups;
687
688 log_debug("netlink", "listening on interface changes");
689
690 groups = netlink_group_mask(RTNLGRP_LINK) |
691 netlink_group_mask(RTNLGRP_IPV4_IFADDR) |
692 netlink_group_mask(RTNLGRP_IPV6_IFADDR);
693
694 return netlink_connect(NETLINK_ROUTE, groups);
695}
696
697/**
698 * Receive changes from netlink */
699static void
700netlink_change_cb(struct lldpd *cfg)
701{
702 if (cfg->g_netlink == NULL)
703 return;
704 netlink_recv(cfg->g_netlink->nl_socket,
705 cfg->g_netlink->devices,
706 cfg->g_netlink->addresses);
707}
0484f180 708
0fa2254b
VB
709/**
710 * Initialize netlink subsystem.
711 *
712 * This can be called several times but will have effect only the first time.
713 *
714 * @return 0 on success, -1 otherwise
715 */
716static int
717netlink_initialize(struct lldpd *cfg)
718{
719 if (cfg->g_netlink) return 0;
aca48e4b 720
0fa2254b
VB
721 log_debug("netlink", "initialize netlink subsystem");
722 if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) {
723 log_warn("netlink", "unable to allocate memory for netlink subsystem");
724 goto end;
725 }
726
727 /* Connect to netlink (by requesting to get notified on updates) and
728 * request updated information right now */
729 int s = cfg->g_netlink->nl_socket = netlink_subscribe_changes();
730
731 struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses =
732 malloc(sizeof(struct interfaces_address_list));
13181ede
VB
733 if (ifaddrs == NULL) {
734 log_warn("netlink", "not enough memory for address list");
0fa2254b 735 goto end;
13181ede
VB
736 }
737 TAILQ_INIT(ifaddrs);
738
0fa2254b
VB
739 struct interfaces_device_list *ifs = cfg->g_netlink->devices =
740 malloc(sizeof(struct interfaces_device_list));
741 if (ifs == NULL) {
742 log_warn("netlink", "not enough memory for interface list");
743 goto end;
744 }
745 TAILQ_INIT(ifs);
746
747 if (netlink_send(s, RTM_GETADDR, AF_UNSPEC, 1) == -1)
748 goto end;
749 netlink_recv(s, NULL, ifaddrs);
750 if (netlink_send(s, RTM_GETLINK, AF_PACKET, 2) == -1)
751 goto end;
752 netlink_recv(s, ifs, NULL);
753
754 /* Listen to any future change */
755 cfg->g_iface_cb = netlink_change_cb;
756 if (levent_iface_subscribe(cfg, s) == -1) {
757 goto end;
758 }
759
760 return 0;
761end:
762 netlink_cleanup(cfg);
763 return -1;
764}
765
766/**
767 * Cleanup netlink subsystem.
768 */
769void
770netlink_cleanup(struct lldpd *cfg)
771{
772 if (cfg->g_netlink == NULL) return;
773 if (cfg->g_netlink->nl_socket != -1)
774 close(cfg->g_netlink->nl_socket);
775 interfaces_free_devices(cfg->g_netlink->devices);
776 interfaces_free_addresses(cfg->g_netlink->addresses);
777
778 free(cfg->g_netlink);
779 cfg->g_netlink = NULL;
780}
781
782/**
783 * Receive the list of interfaces.
784 *
785 * @return a list of interfaces.
786 */
787struct interfaces_device_list*
788netlink_get_interfaces(struct lldpd *cfg)
789{
790 if (netlink_initialize(cfg) == -1) return NULL;
791 struct interfaces_device *ifd;
792 TAILQ_FOREACH(ifd, cfg->g_netlink->devices, next) {
793 ifd->ignore = 0;
13181ede 794 }
0fa2254b
VB
795 return cfg->g_netlink->devices;
796}
aca48e4b 797
0fa2254b
VB
798/**
799 * Receive the list of addresses.
800 *
801 * @return a list of addresses.
802 */
803struct interfaces_address_list*
804netlink_get_addresses(struct lldpd *cfg)
805{
806 if (netlink_initialize(cfg) == -1) return NULL;
807 return cfg->g_netlink->addresses;
0484f180 808}