]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/netlink.c
Allow linux kernel to autoprobe correct socket_nl.nl_pid address
[thirdparty/lldpd.git] / src / daemon / netlink.c
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
18 /* Grabbing interfaces information with netlink only. */
19
20 #include "lldpd.h"
21
22 #include <errno.h>
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <net/if_arp.h>
26 #include <linux/netlink.h>
27 #include <linux/rtnetlink.h>
28
29 #define NETLINK_BUFFER 4096
30
31 struct netlink_req {
32 struct nlmsghdr hdr;
33 struct rtgenmsg gen;
34 };
35
36 struct lldpd_netlink {
37 int nl_socket;
38 int nl_socket_recv_size;
39 /* Cache */
40 struct interfaces_device_list *devices;
41 struct interfaces_address_list *addresses;
42 };
43
44
45 /**
46 * Set netlink socket buffer size.
47 *
48 * This returns the effective size on success. If the provided value is 0, this
49 * returns the current size instead. It returns -1 on system errors and -2 if
50 * the size was not changed appropriately (when reaching the max).
51 */
52 static int
53 netlink_socket_set_buffer_size(int s, int optname, const char *optname_str, int bufsize)
54 {
55 socklen_t size = sizeof(int);
56 int got = 0;
57
58 if (bufsize > 0 && setsockopt(s, SOL_SOCKET, optname, &bufsize, sizeof(bufsize)) < 0) {
59 log_warn("netlink", "unable to set %s to '%d'", optname_str, bufsize);
60 return -1;
61 }
62
63 /* Now read them back from kernel.
64 * SO_SNDBUF & SO_RCVBUF are cap-ed at sysctl `net.core.rmem_max` &
65 * `net.core.wmem_max`. This it the easiest [probably sanest too]
66 * to validate that our socket buffers were set properly.
67 */
68 if (getsockopt(s, SOL_SOCKET, optname, &got, &size) < 0) {
69 log_warn("netlink", "unable to get %s", optname_str);
70 return -1;
71 }
72 if (bufsize > 0 && got < bufsize) {
73 log_warnx("netlink", "tried to set %s to '%d' "
74 "but got '%d'", optname_str, bufsize, got);
75 return -2;
76 }
77
78 return got;
79 }
80
81 /**
82 * Connect to netlink.
83 *
84 * Open a Netlink socket and connect to it.
85 *
86 * @param protocol Which protocol to use (eg NETLINK_ROUTE).
87 * @param groups Which groups we want to subscribe to
88 * @return 0 on success, -1 otherwise
89 */
90 static int
91 netlink_connect(struct lldpd *cfg, int protocol, unsigned groups)
92 {
93 int s;
94 struct sockaddr_nl local = {
95 .nl_family = AF_NETLINK,
96 .nl_pid = 0,
97 .nl_groups = groups
98 };
99
100 /* Open Netlink socket */
101 log_debug("netlink", "opening netlink socket");
102 s = socket(AF_NETLINK, SOCK_RAW, protocol);
103 if (s == -1) {
104 log_warn("netlink", "unable to open netlink socket");
105 return -1;
106 }
107 if (NETLINK_SEND_BUFSIZE &&
108 netlink_socket_set_buffer_size(s,
109 SO_SNDBUF, "SO_SNDBUF", NETLINK_SEND_BUFSIZE) == -1)
110 return -1;
111
112 int rc = netlink_socket_set_buffer_size(s,
113 SO_RCVBUF, "SO_RCVBUF", NETLINK_RECEIVE_BUFSIZE);
114 switch (rc) {
115 case -1: return -1;
116 case -2: cfg->g_netlink->nl_socket_recv_size = 0; break;
117 default: cfg->g_netlink->nl_socket_recv_size = rc; break;
118 }
119 if (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
120 log_warn("netlink", "unable to bind netlink socket");
121 close(s);
122 return -1;
123 }
124 cfg->g_netlink->nl_socket = s;
125 return 0;
126 }
127
128 /**
129 * Send a netlink message.
130 *
131 * The type of the message can be chosen as well the route family. The
132 * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
133 *
134 * @param s the netlink socket
135 * @param type the request type (eg RTM_GETLINK)
136 * @param family the rt family (eg AF_PACKET)
137 * @return 0 on success, -1 otherwise
138 */
139 static int
140 netlink_send(int s, int type, int family, int seq)
141 {
142 struct netlink_req req = {
143 .hdr = {
144 .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
145 .nlmsg_type = type,
146 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
147 .nlmsg_seq = seq,
148 .nlmsg_pid = getpid() },
149 .gen = { .rtgen_family = family }
150 };
151 struct iovec iov = {
152 .iov_base = &req,
153 .iov_len = req.hdr.nlmsg_len
154 };
155 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
156 struct msghdr rtnl_msg = {
157 .msg_iov = &iov,
158 .msg_iovlen = 1,
159 .msg_name = &peer,
160 .msg_namelen = sizeof(struct sockaddr_nl)
161 };
162
163 /* Send netlink message. This is synchronous but we are guaranteed
164 * to not block. */
165 log_debug("netlink", "sending netlink message");
166 if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
167 log_warn("netlink", "unable to send netlink message");
168 return -1;
169 }
170
171 return 0;
172 }
173
174 static void
175 netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
176 {
177 while (RTA_OK(rta, len)) {
178 if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
179 tb[rta->rta_type] = rta;
180 rta = RTA_NEXT(rta,len);
181 }
182 }
183
184 /**
185 * Parse a `linkinfo` attributes.
186 *
187 * @param iff where to put the result
188 * @param rta linkinfo attribute
189 * @param len length of attributes
190 */
191 static void
192 netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
193 {
194 struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {};
195 char *kind = NULL;
196
197 netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
198
199 if (link_info_attrs[IFLA_INFO_KIND]) {
200 kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
201 if (kind) {
202 if (!strcmp(kind, "vlan")) {
203 log_debug("netlink", "interface %s is a VLAN",
204 iff->name);
205 iff->type |= IFACE_VLAN_T;
206 } else if (!strcmp(kind, "bridge")) {
207 log_debug("netlink", "interface %s is a bridge",
208 iff->name);
209 iff->type |= IFACE_BRIDGE_T;
210 } else if (!strcmp(kind, "bond")) {
211 log_debug("netlink", "interface %s is a bond",
212 iff->name);
213 iff->type |= IFACE_BOND_T;
214 } else if (!strcmp(kind, "team")) {
215 log_debug("netlink", "interface %s is a team",
216 iff->name);
217 iff->type |= IFACE_BOND_T;
218 }
219 }
220 }
221
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);
236 }
237
238 /**
239 * Parse a `link` netlink message.
240 *
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
244 */
245 static int
246 netlink_parse_link(struct nlmsghdr *msg,
247 struct interfaces_device *iff)
248 {
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;
259 }
260
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);
287 log_debug("netlink", "attribute IFLA_LINK for %s: %d",
288 iff->name ? iff->name : "(unknown)", iff->lower_idx);
289 break;
290 case IFLA_LINK_NETNSID:
291 /* Is the lower interface into another namesapce? */
292 iff->lower_idx = -1;
293 log_debug("netlink", "attribute IFLA_LINK_NETNSID received for %s",
294 iff->name ? iff->name : "(unknown)");
295 break;
296 case IFLA_MASTER:
297 /* Index of master interface */
298 iff->upper_idx = *(int*)RTA_DATA(attribute);
299 break;
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 }
312 }
313 if (!iff->name || !iff->address) {
314 log_info("netlink", "interface %d does not have a name or an address, skip",
315 iff->index);
316 return -1;
317 }
318 if (iff->upper_idx == -1) {
319 /* No upper interface, we cannot be enslaved. We need to clear
320 * the flag because the appropriate information may come later
321 * and we don't want to miss it. */
322 iff->flags &= ~IFF_SLAVE;
323 }
324
325 if (ifi->ifi_family == AF_BRIDGE && msg->nlmsg_type == RTM_DELLINK && iff->upper_idx != -1) {
326 log_debug("netlink", "removal of %s from bridge %d",
327 iff->name, iff->upper_idx);
328 msg->nlmsg_type = RTM_NEWLINK;
329 iff->upper_idx = -1;
330 } else if (ifi->ifi_family != 0) {
331 log_debug("netlink", "skip non-generic message update %d at index %d",
332 ifi->ifi_type, ifi->ifi_index);
333 return -1;
334 }
335
336 log_debug("netlink", "parsed link %d (%s, flags: %d)",
337 iff->index, iff->name, iff->flags);
338 return 0;
339 }
340
341 /**
342 * Parse a `address` netlink message.
343 *
344 * @param msg message to be parsed
345 * @param ifa where to put the result
346 * return 0 if the address is worth it, -1 otherwise
347 */
348 static int
349 netlink_parse_address(struct nlmsghdr *msg,
350 struct interfaces_address *ifa)
351 {
352 struct ifaddrmsg *ifi;
353 struct rtattr *attribute;
354 int len;
355 ifi = NLMSG_DATA(msg);
356 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
357
358 ifa->index = ifi->ifa_index;
359 ifa->flags = ifi->ifa_flags;
360 switch (ifi->ifa_family) {
361 case AF_INET:
362 case AF_INET6: break;
363 default:
364 log_debug("netlink", "got a non IP address on if %d (family: %d)",
365 ifa->index, ifi->ifa_family);
366 return -1;
367 }
368
369 for (attribute = IFA_RTA(ifi);
370 RTA_OK(attribute, len);
371 attribute = RTA_NEXT(attribute, len)) {
372 switch(attribute->rta_type) {
373 case IFA_ADDRESS:
374 /* Address */
375 if (ifi->ifa_family == AF_INET) {
376 struct sockaddr_in ip;
377 memset(&ip, 0, sizeof(struct sockaddr_in));
378 ip.sin_family = AF_INET;
379 memcpy(&ip.sin_addr, RTA_DATA(attribute),
380 sizeof(struct in_addr));
381 memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
382 } else {
383 struct sockaddr_in6 ip6;
384 memset(&ip6, 0, sizeof(struct sockaddr_in6));
385 ip6.sin6_family = AF_INET6;
386 memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
387 sizeof(struct in6_addr));
388 memcpy(&ifa->address, &ip6, sizeof(struct sockaddr_in6));
389 }
390 break;
391 default:
392 log_debug("netlink", "unhandled address attribute type %d for iface %d",
393 attribute->rta_type, ifa->index);
394 break;
395 }
396 }
397 if (ifa->address.ss_family == AF_UNSPEC) {
398 log_debug("netlink", "no IP for interface %d",
399 ifa->index);
400 return -1;
401 }
402 return 0;
403 }
404
405 /**
406 * Merge an old interface with a new one.
407 *
408 * Some properties may be absent in the new interface that should be copied over
409 * from the old one.
410 */
411 void
412 netlink_merge(struct interfaces_device *old, struct interfaces_device *new)
413 {
414 if (new->alias == NULL) {
415 new->alias = old->alias;
416 old->alias = NULL;
417 }
418 if (new->address == NULL) {
419 new->address = old->address;
420 old->address = NULL;
421 }
422 if (new->mtu == 0)
423 new->mtu = old->mtu;
424 if (new->type == 0)
425 new->type = old->type;
426 if (new->vlanid == 0)
427 new->vlanid = old->vlanid;
428
429 /* It's not possible for lower link to change */
430 new->lower_idx = old->lower_idx;
431 }
432
433 /**
434 * Receive netlink answer from the kernel.
435 *
436 * @param ifs list to store interface list or NULL if we don't
437 * @param ifas list to store address list or NULL if we don't
438 * @return 0 on success, -1 on error
439 */
440 static int
441 netlink_recv(struct lldpd *cfg,
442 struct interfaces_device_list *ifs,
443 struct interfaces_address_list *ifas)
444 {
445 int end = 0, ret = 0;
446 int flags = MSG_PEEK | MSG_TRUNC;
447 struct iovec iov;
448 int link_update = 0;
449 int s = cfg->g_netlink->nl_socket;
450
451 struct interfaces_device *ifdold;
452 struct interfaces_device *ifdnew;
453 struct interfaces_address *ifaold;
454 struct interfaces_address *ifanew;
455 char addr[INET6_ADDRSTRLEN + 1];
456
457 iov.iov_len = NETLINK_BUFFER;
458 iov.iov_base = malloc(iov.iov_len);
459 if (!iov.iov_base) {
460 log_warn("netlink", "not enough memory");
461 return -1;
462 }
463
464 while (!end) {
465 ssize_t len;
466 struct nlmsghdr *msg;
467 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
468 struct msghdr rtnl_reply = {
469 .msg_iov = &iov,
470 .msg_iovlen = 1,
471 .msg_name = &peer,
472 .msg_namelen = sizeof(struct sockaddr_nl)
473 };
474
475 retry:
476 len = recvmsg(s, &rtnl_reply, flags);
477 if (len == -1) {
478 if (errno == EAGAIN || errno == EWOULDBLOCK) {
479 log_debug("netlink", "should have received something, but didn't");
480 ret = 0;
481 goto out;
482 }
483 int rsize = cfg->g_netlink->nl_socket_recv_size;
484 if (errno == ENOBUFS &&
485 rsize > 0 && rsize < NETLINK_MAX_RECEIVE_BUFSIZE) {
486 /* Try to increase buffer size */
487 rsize *= 2;
488 if (rsize > NETLINK_MAX_RECEIVE_BUFSIZE) {
489 rsize = NETLINK_MAX_RECEIVE_BUFSIZE;
490 }
491 int rc = netlink_socket_set_buffer_size(s,
492 SO_RCVBUF, "SO_RCVBUF",
493 rsize);
494 if (rc < 0)
495 cfg->g_netlink->nl_socket_recv_size = 0;
496 else
497 cfg->g_netlink->nl_socket_recv_size = rsize;
498 if (rc > 0 || rc == -2) {
499 log_info("netlink",
500 "netlink receive buffer too small, retry with larger one (%d)",
501 rsize);
502 flags = 0;
503 goto retry;
504 }
505 }
506 log_warn("netlink", "unable to receive netlink answer");
507 ret = -1;
508 goto out;
509 }
510 if (!len) {
511 ret = 0;
512 goto out;
513 }
514
515 if (iov.iov_len < len || (rtnl_reply.msg_flags & MSG_TRUNC)) {
516 void *tmp;
517
518 /* Provided buffer is not large enough, enlarge it
519 * to size of len (which should be total length of the message)
520 * and try again. */
521 iov.iov_len = len;
522 tmp = realloc(iov.iov_base, iov.iov_len);
523 if (!tmp) {
524 log_warn("netlink", "not enough memory");
525 ret = -1;
526 goto out;
527 }
528 log_debug("netlink", "enlarge message size to %zu bytes", len);
529 iov.iov_base = tmp;
530 flags = 0;
531 goto retry;
532 }
533
534 if (flags != 0) {
535 /* Buffer is big enough, do the actual reading */
536 flags = 0;
537 goto retry;
538 }
539
540 for (msg = (struct nlmsghdr*)(void*)(iov.iov_base);
541 NLMSG_OK(msg, len);
542 msg = NLMSG_NEXT(msg, len)) {
543 if (!(msg->nlmsg_flags & NLM_F_MULTI))
544 end = 1;
545 switch (msg->nlmsg_type) {
546 case NLMSG_DONE:
547 log_debug("netlink", "received done message");
548 end = 1;
549 break;
550 case RTM_NEWLINK:
551 case RTM_DELLINK:
552 if (!ifs) break;
553 log_debug("netlink", "received link information");
554 ifdnew = calloc(1, sizeof(struct interfaces_device));
555 if (ifdnew == NULL) {
556 log_warn("netlink", "not enough memory for another interface, give up what we have");
557 goto end;
558 }
559 if (netlink_parse_link(msg, ifdnew) == 0) {
560 /* We need to find if we already have this interface */
561 TAILQ_FOREACH(ifdold, ifs, next) {
562 if (ifdold->index == ifdnew->index) break;
563 }
564
565 if (msg->nlmsg_type == RTM_NEWLINK) {
566 if (ifdold == NULL) {
567 log_debug("netlink", "interface %s is new",
568 ifdnew->name);
569 TAILQ_INSERT_TAIL(ifs, ifdnew, next);
570 } else {
571 log_debug("netlink", "interface %s/%s is updated",
572 ifdold->name, ifdnew->name);
573 netlink_merge(ifdold, ifdnew);
574 TAILQ_INSERT_AFTER(ifs, ifdold, ifdnew, next);
575 TAILQ_REMOVE(ifs, ifdold, next);
576 interfaces_free_device(ifdold);
577 }
578 } else {
579 if (ifdold == NULL) {
580 log_warnx("netlink",
581 "removal request for %s, but no knowledge of it",
582 ifdnew->name);
583 } else {
584 log_debug("netlink", "interface %s is to be removed",
585 ifdold->name);
586 TAILQ_REMOVE(ifs, ifdold, next);
587 interfaces_free_device(ifdold);
588 }
589 interfaces_free_device(ifdnew);
590 }
591 link_update = 1;
592 } else {
593 interfaces_free_device(ifdnew);
594 }
595 break;
596 case RTM_NEWADDR:
597 case RTM_DELADDR:
598 if (!ifas) break;
599 log_debug("netlink", "received address information");
600 ifanew = calloc(1, sizeof(struct interfaces_address));
601 if (ifanew == NULL) {
602 log_warn("netlink", "not enough memory for another address, give what we have");
603 goto end;
604 }
605 if (netlink_parse_address(msg, ifanew) == 0) {
606 TAILQ_FOREACH(ifaold, ifas, next) {
607 if ((ifaold->index == ifanew->index) &&
608 !memcmp(&ifaold->address, &ifanew->address,
609 sizeof(ifaold->address))) break;
610 }
611 if (getnameinfo((struct sockaddr *)&ifanew->address,
612 sizeof(ifanew->address),
613 addr, sizeof(addr),
614 NULL, 0, NI_NUMERICHOST) != 0) {
615 strlcpy(addr, "(unknown)", sizeof(addr));
616 }
617
618 if (msg->nlmsg_type == RTM_NEWADDR) {
619 if (ifaold == NULL) {
620 log_debug("netlink", "new address %s%%%d",
621 addr, ifanew->index);
622 TAILQ_INSERT_TAIL(ifas, ifanew, next);
623 } else {
624 log_debug("netlink", "updated address %s%%%d",
625 addr, ifaold->index);
626 TAILQ_INSERT_AFTER(ifas, ifaold, ifanew, next);
627 TAILQ_REMOVE(ifas, ifaold, next);
628 interfaces_free_address(ifaold);
629 }
630 } else {
631 if (ifaold == NULL) {
632 log_info("netlink",
633 "removal request for address of %s%%%d, but no knowledge of it",
634 addr, ifanew->index);
635 } else {
636 log_debug("netlink", "address %s%%%d is to be removed",
637 addr, ifaold->index);
638 TAILQ_REMOVE(ifas, ifaold, next);
639 interfaces_free_address(ifaold);
640 }
641 interfaces_free_address(ifanew);
642 }
643 } else {
644 interfaces_free_address(ifanew);
645 }
646 break;
647 default:
648 log_debug("netlink",
649 "received unhandled message type %d (len: %d)",
650 msg->nlmsg_type, msg->nlmsg_len);
651 }
652 }
653 flags = MSG_PEEK | MSG_TRUNC;
654 }
655 end:
656 if (link_update) {
657 /* Fill out lower/upper */
658 struct interfaces_device *iface1, *iface2;
659 TAILQ_FOREACH(iface1, ifs, next) {
660 if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index) {
661 TAILQ_FOREACH(iface2, ifs, next) {
662 if (iface1->upper_idx == iface2->index) {
663 log_debug("netlink",
664 "upper interface for %s is %s",
665 iface1->name, iface2->name);
666 iface1->upper = iface2;
667 break;
668 }
669 }
670 if (iface2 == NULL)
671 iface1->upper = NULL;
672 } else {
673 iface1->upper = NULL;
674 }
675 if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index) {
676 TAILQ_FOREACH(iface2, ifs, next) {
677 if (iface1->lower_idx == iface2->index) {
678 /* Workaround a bug introduced
679 * in Linux 4.1: a pair of veth
680 * will be lower interface of
681 * each other. Do not modify
682 * index as if one of them is
683 * updated, we will loose the
684 * information about the
685 * loop. */
686 if (iface2->lower_idx == iface1->index) {
687 iface1->lower = NULL;
688 log_debug("netlink",
689 "link loop detected between %s and %s",
690 iface1->name, iface2->name);
691 } else {
692 log_debug("netlink",
693 "lower interface for %s is %s",
694 iface1->name, iface2->name);
695 iface1->lower = iface2;
696 }
697 break;
698 }
699 if (iface2 == NULL)
700 iface1->lower = NULL;
701 }
702 } else {
703 iface1->lower = NULL;
704 }
705 }
706 }
707
708 out:
709 free(iov.iov_base);
710 return ret;
711 }
712
713 static int
714 netlink_group_mask(int group)
715 {
716 return group ? (1 << (group - 1)) : 0;
717 }
718
719 /**
720 * Subscribe to link changes.
721 *
722 * @return 0 on success, -1 otherwise
723 */
724 static int
725 netlink_subscribe_changes(struct lldpd *cfg)
726 {
727 unsigned int groups;
728
729 log_debug("netlink", "listening on interface changes");
730
731 groups = netlink_group_mask(RTNLGRP_LINK) |
732 netlink_group_mask(RTNLGRP_IPV4_IFADDR) |
733 netlink_group_mask(RTNLGRP_IPV6_IFADDR);
734
735 return netlink_connect(cfg, NETLINK_ROUTE, groups);
736 }
737
738 /**
739 * Receive changes from netlink */
740 static void
741 netlink_change_cb(struct lldpd *cfg)
742 {
743 if (cfg->g_netlink == NULL)
744 return;
745 netlink_recv(cfg,
746 cfg->g_netlink->devices,
747 cfg->g_netlink->addresses);
748 }
749
750 /**
751 * Initialize netlink subsystem.
752 *
753 * This can be called several times but will have effect only the first time.
754 *
755 * @return 0 on success, -1 otherwise
756 */
757 static int
758 netlink_initialize(struct lldpd *cfg)
759 {
760 if (cfg->g_netlink) return 0;
761
762 log_debug("netlink", "initialize netlink subsystem");
763 if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) {
764 log_warn("netlink", "unable to allocate memory for netlink subsystem");
765 goto end;
766 }
767
768 /* Connect to netlink (by requesting to get notified on updates) and
769 * request updated information right now */
770 if (netlink_subscribe_changes(cfg) == -1)
771 goto end;
772
773 struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses =
774 malloc(sizeof(struct interfaces_address_list));
775 if (ifaddrs == NULL) {
776 log_warn("netlink", "not enough memory for address list");
777 goto end;
778 }
779 TAILQ_INIT(ifaddrs);
780
781 struct interfaces_device_list *ifs = cfg->g_netlink->devices =
782 malloc(sizeof(struct interfaces_device_list));
783 if (ifs == NULL) {
784 log_warn("netlink", "not enough memory for interface list");
785 goto end;
786 }
787 TAILQ_INIT(ifs);
788
789 if (netlink_send(cfg->g_netlink->nl_socket, RTM_GETADDR, AF_UNSPEC, 1) == -1)
790 goto end;
791 netlink_recv(cfg, NULL, ifaddrs);
792 if (netlink_send(cfg->g_netlink->nl_socket, RTM_GETLINK, AF_PACKET, 2) == -1)
793 goto end;
794 netlink_recv(cfg, ifs, NULL);
795
796 /* Listen to any future change */
797 cfg->g_iface_cb = netlink_change_cb;
798 if (levent_iface_subscribe(cfg, cfg->g_netlink->nl_socket) == -1) {
799 goto end;
800 }
801
802 return 0;
803 end:
804 netlink_cleanup(cfg);
805 return -1;
806 }
807
808 /**
809 * Cleanup netlink subsystem.
810 */
811 void
812 netlink_cleanup(struct lldpd *cfg)
813 {
814 if (cfg->g_netlink == NULL) return;
815 if (cfg->g_netlink->nl_socket != -1)
816 close(cfg->g_netlink->nl_socket);
817 interfaces_free_devices(cfg->g_netlink->devices);
818 interfaces_free_addresses(cfg->g_netlink->addresses);
819
820 free(cfg->g_netlink);
821 cfg->g_netlink = NULL;
822 }
823
824 /**
825 * Receive the list of interfaces.
826 *
827 * @return a list of interfaces.
828 */
829 struct interfaces_device_list*
830 netlink_get_interfaces(struct lldpd *cfg)
831 {
832 if (netlink_initialize(cfg) == -1) return NULL;
833 struct interfaces_device *ifd;
834 TAILQ_FOREACH(ifd, cfg->g_netlink->devices, next) {
835 ifd->ignore = 0;
836 }
837 return cfg->g_netlink->devices;
838 }
839
840 /**
841 * Receive the list of addresses.
842 *
843 * @return a list of addresses.
844 */
845 struct interfaces_address_list*
846 netlink_get_addresses(struct lldpd *cfg)
847 {
848 if (netlink_initialize(cfg) == -1) return NULL;
849 return cfg->g_netlink->addresses;
850 }