]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/netlink.c
netlink: merge old attributes with new ones
[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 /* Cache */
39 struct interfaces_device_list *devices;
40 struct interfaces_address_list *addresses;
41 };
42
43 /**
44 * Connect to netlink.
45 *
46 * Open a Netlink socket and connect to it.
47 *
48 * @param protocol Which protocol to use (eg NETLINK_ROUTE).
49 * @param groups Which groups we want to subscribe to
50 * @return The opened socket or -1 on error.
51 */
52 static int
53 netlink_connect(int protocol, unsigned groups)
54 {
55 int s;
56 struct sockaddr_nl local = {
57 .nl_family = AF_NETLINK,
58 .nl_pid = getpid(),
59 .nl_groups = groups
60 };
61
62 /* Open Netlink socket */
63 log_debug("netlink", "opening netlink socket");
64 s = socket(AF_NETLINK, SOCK_RAW, protocol);
65 if (s == -1) {
66 log_warn("netlink", "unable to open netlink socket");
67 return -1;
68 }
69 if (groups && bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_nl)) < 0) {
70 log_warn("netlink", "unable to bind netlink socket");
71 close(s);
72 return -1;
73 }
74 return s;
75 }
76
77 /**
78 * Send a netlink message.
79 *
80 * The type of the message can be chosen as well the route family. The
81 * mesage will always be NLM_F_REQUEST | NLM_F_DUMP.
82 *
83 * @param s the netlink socket
84 * @param type the request type (eg RTM_GETLINK)
85 * @param family the rt family (eg AF_PACKET)
86 * @return 0 on success, -1 otherwise
87 */
88 static int
89 netlink_send(int s, int type, int family, int seq)
90 {
91 struct netlink_req req = {
92 .hdr = {
93 .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
94 .nlmsg_type = type,
95 .nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
96 .nlmsg_seq = seq,
97 .nlmsg_pid = getpid() },
98 .gen = { .rtgen_family = family }
99 };
100 struct iovec iov = {
101 .iov_base = &req,
102 .iov_len = req.hdr.nlmsg_len
103 };
104 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
105 struct msghdr rtnl_msg = {
106 .msg_iov = &iov,
107 .msg_iovlen = 1,
108 .msg_name = &peer,
109 .msg_namelen = sizeof(struct sockaddr_nl)
110 };
111
112 /* Send netlink message. This is synchronous but we are guaranteed
113 * to not block. */
114 log_debug("netlink", "sending netlink message");
115 if (sendmsg(s, (struct msghdr *)&rtnl_msg, 0) == -1) {
116 log_warn("netlink", "unable to send netlink message");
117 return -1;
118 }
119
120 return 0;
121 }
122
123 static void
124 netlink_parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
125 {
126 while (RTA_OK(rta, len)) {
127 if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
128 tb[rta->rta_type] = rta;
129 rta = RTA_NEXT(rta,len);
130 }
131 }
132
133 /**
134 * Parse a `linkinfo` attributes.
135 *
136 * @param iff where to put the result
137 * @param rta linkinfo attribute
138 * @param len length of attributes
139 */
140 static void
141 netlink_parse_linkinfo(struct interfaces_device *iff, struct rtattr *rta, int len)
142 {
143 struct rtattr *link_info_attrs[IFLA_INFO_MAX+1] = {};
144 char *kind = NULL;
145
146 netlink_parse_rtattr(link_info_attrs, IFLA_INFO_MAX, rta, len);
147
148 if (link_info_attrs[IFLA_INFO_KIND]) {
149 kind = strdup(RTA_DATA(link_info_attrs[IFLA_INFO_KIND]));
150 if (kind) {
151 if (!strcmp(kind, "vlan")) {
152 log_debug("netlink", "interface %s is a VLAN",
153 iff->name);
154 iff->type |= IFACE_VLAN_T;
155 } else if (!strcmp(kind, "bridge")) {
156 log_debug("netlink", "interface %s is a bridge",
157 iff->name);
158 iff->type |= IFACE_BRIDGE_T;
159 } else if (!strcmp(kind, "bond")) {
160 log_debug("netlink", "interface %s is a bond",
161 iff->name);
162 iff->type |= IFACE_BOND_T;
163 }
164 }
165 }
166
167 if (kind && !strcmp(kind, "vlan") && link_info_attrs[IFLA_INFO_DATA]) {
168 struct rtattr *vlan_link_info_data_attrs[IFLA_VLAN_MAX+1] = {};
169 netlink_parse_rtattr(vlan_link_info_data_attrs, IFLA_VLAN_MAX,
170 RTA_DATA(link_info_attrs[IFLA_INFO_DATA]),
171 RTA_PAYLOAD(link_info_attrs[IFLA_INFO_DATA]));
172
173 if (vlan_link_info_data_attrs[IFLA_VLAN_ID]) {
174 iff->vlanid = *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs[IFLA_VLAN_ID]);
175 log_debug("netlink", "VLAN ID for interface %s is %d",
176 iff->name, iff->vlanid);
177 }
178 }
179
180 free(kind);
181 }
182
183 /**
184 * Parse a `link` netlink message.
185 *
186 * @param msg message to be parsed
187 * @param iff where to put the result
188 * return 0 if the interface is worth it, -1 otherwise
189 */
190 static int
191 netlink_parse_link(struct nlmsghdr *msg,
192 struct interfaces_device *iff)
193 {
194 struct ifinfomsg *ifi;
195 struct rtattr *attribute;
196 int len;
197 ifi = NLMSG_DATA(msg);
198 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifinfomsg));
199
200 if (ifi->ifi_type != ARPHRD_ETHER) {
201 log_debug("netlink", "skip non Ethernet interface at index %d",
202 ifi->ifi_index);
203 return -1;
204 }
205
206 iff->index = ifi->ifi_index;
207 iff->flags = ifi->ifi_flags;
208 iff->lower_idx = -1;
209 iff->upper_idx = -1;
210
211 for (attribute = IFLA_RTA(ifi);
212 RTA_OK(attribute, len);
213 attribute = RTA_NEXT(attribute, len)) {
214 switch(attribute->rta_type) {
215 case IFLA_IFNAME:
216 /* Interface name */
217 iff->name = strdup(RTA_DATA(attribute));
218 break;
219 case IFLA_IFALIAS:
220 /* Interface alias */
221 iff->alias = strdup(RTA_DATA(attribute));
222 break;
223 case IFLA_ADDRESS:
224 /* Interface MAC address */
225 iff->address = malloc(RTA_PAYLOAD(attribute));
226 if (iff->address)
227 memcpy(iff->address, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
228 break;
229 case IFLA_LINK:
230 /* Index of "lower" interface */
231 iff->lower_idx = *(int*)RTA_DATA(attribute);
232 break;
233 case IFLA_MASTER:
234 /* Index of master interface */
235 iff->upper_idx = *(int*)RTA_DATA(attribute);
236 break;
237 case IFLA_MTU:
238 /* Maximum Transmission Unit */
239 iff->mtu = *(int*)RTA_DATA(attribute);
240 break;
241 case IFLA_LINKINFO:
242 netlink_parse_linkinfo(iff, RTA_DATA(attribute), RTA_PAYLOAD(attribute));
243 break;
244 default:
245 log_debug("netlink", "unhandled link attribute type %d for iface %s",
246 attribute->rta_type, iff->name ? iff->name : "(unknown)");
247 break;
248 }
249 }
250 if (!iff->name || !iff->address) {
251 log_info("netlink", "interface %d does not have a name or an address, skip",
252 iff->index);
253 return -1;
254 }
255
256 log_debug("netlink", "parsed link %d (%s, flags: %d)",
257 iff->index, iff->name, iff->flags);
258 return 0;
259 }
260
261 /**
262 * Parse a `address` netlink message.
263 *
264 * @param msg message to be parsed
265 * @param ifa where to put the result
266 * return 0 if the address is worth it, -1 otherwise
267 */
268 static int
269 netlink_parse_address(struct nlmsghdr *msg,
270 struct interfaces_address *ifa)
271 {
272 struct ifaddrmsg *ifi;
273 struct rtattr *attribute;
274 int len;
275 ifi = NLMSG_DATA(msg);
276 len = msg->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg));
277
278 ifa->index = ifi->ifa_index;
279 ifa->flags = ifi->ifa_flags;
280 switch (ifi->ifa_family) {
281 case AF_INET:
282 case AF_INET6: break;
283 default:
284 log_debug("netlink", "got a non IP address on if %d (family: %d)",
285 ifa->index, ifi->ifa_family);
286 return -1;
287 }
288
289 for (attribute = IFA_RTA(ifi);
290 RTA_OK(attribute, len);
291 attribute = RTA_NEXT(attribute, len)) {
292 switch(attribute->rta_type) {
293 case IFA_ADDRESS:
294 /* Address */
295 if (ifi->ifa_family == AF_INET) {
296 struct sockaddr_in ip;
297 memset(&ip, 0, sizeof(struct sockaddr_in));
298 ip.sin_family = AF_INET;
299 memcpy(&ip.sin_addr, RTA_DATA(attribute),
300 sizeof(struct in_addr));
301 memcpy(&ifa->address, &ip, sizeof(struct sockaddr_in));
302 } else {
303 struct sockaddr_in6 ip6;
304 memset(&ip6, 0, sizeof(struct sockaddr_in6));
305 ip6.sin6_family = AF_INET6;
306 memcpy(&ip6.sin6_addr, RTA_DATA(attribute),
307 sizeof(struct in6_addr));
308 memcpy(&ifa->address, &ip6, sizeof(struct sockaddr_in6));
309 }
310 break;
311 default:
312 log_debug("netlink", "unhandled address attribute type %d for iface %d",
313 attribute->rta_type, ifa->index);
314 break;
315 }
316 }
317 if (ifa->address.ss_family == AF_UNSPEC) {
318 log_debug("netlink", "no IP for interface %d",
319 ifa->index);
320 return -1;
321 }
322 return 0;
323 }
324
325 /**
326 * Merge an old interface with a new one.
327 *
328 * Some properties may be absent in the new interface that should be copied over
329 * from the old one.
330 */
331 void
332 netlink_merge(struct interfaces_device *old, struct interfaces_device *new)
333 {
334 if (new->alias == NULL) {
335 new->alias = old->alias;
336 old->alias = NULL;
337 }
338 if (new->address == NULL) {
339 new->address = old->address;
340 old->address = NULL;
341 }
342 if (new->mtu == 0)
343 new->mtu = old->mtu;
344 if (new->type == 0)
345 new->type = old->type;
346 if (new->vlanid == 0)
347 new->vlanid = old->vlanid;
348 }
349
350 /**
351 * Receive netlink answer from the kernel.
352 *
353 * @param s the netlink socket
354 * @param ifs list to store interface list or NULL if we don't
355 * @param ifas list to store address list or NULL if we don't
356 * @return 0 on success, -1 on error
357 */
358 static int
359 netlink_recv(int s,
360 struct interfaces_device_list *ifs,
361 struct interfaces_address_list *ifas)
362 {
363 char reply[NETLINK_BUFFER] __attribute__ ((aligned));
364 int end = 0;
365 int link_update = 0;
366
367 struct interfaces_device *ifdold;
368 struct interfaces_device *ifdnew;
369 struct interfaces_address *ifaold;
370 struct interfaces_address *ifanew;
371 char addr[INET6_ADDRSTRLEN + 1];
372
373 while (!end) {
374 ssize_t len;
375 struct nlmsghdr *msg;
376 struct iovec iov = {
377 .iov_base = reply,
378 .iov_len = NETLINK_BUFFER
379 };
380 struct sockaddr_nl peer = { .nl_family = AF_NETLINK };
381 struct msghdr rtnl_reply = {
382 .msg_iov = &iov,
383 .msg_iovlen = 1,
384 .msg_name = &peer,
385 .msg_namelen = sizeof(struct sockaddr_nl)
386 };
387
388 len = recvmsg(s, &rtnl_reply, 0);
389 if (len == -1) {
390 if (errno == EAGAIN || errno == EWOULDBLOCK) {
391 log_debug("netlink", "should have received something, but didn't");
392 return 0;
393 }
394 log_warnx("netlink", "unable to receive netlink answer");
395 return -1;
396 }
397 if (!len) return 0;
398 for (msg = (struct nlmsghdr*)(void*)reply;
399 NLMSG_OK(msg, len);
400 msg = NLMSG_NEXT(msg, len)) {
401 if (!(msg->nlmsg_flags & NLM_F_MULTI))
402 end = 1;
403 switch (msg->nlmsg_type) {
404 case NLMSG_DONE:
405 log_debug("netlink", "received done message");
406 end = 1;
407 break;
408 case RTM_NEWLINK:
409 case RTM_DELLINK:
410 if (!ifs) break;
411 log_debug("netlink", "received link information");
412 ifdnew = calloc(1, sizeof(struct interfaces_device));
413 if (ifdnew == NULL) {
414 log_warn("netlink", "not enough memory for another interface, give up what we have");
415 goto end;
416 }
417 if (netlink_parse_link(msg, ifdnew) == 0) {
418 /* We need to find if we already have this interface */
419 TAILQ_FOREACH(ifdold, ifs, next) {
420 if (ifdold->index == ifdnew->index) break;
421 }
422 if (msg->nlmsg_type == RTM_NEWLINK) {
423 if (ifdold == NULL) {
424 log_debug("netlink", "interface %s is new",
425 ifdnew->name);
426 TAILQ_INSERT_TAIL(ifs, ifdnew, next);
427 } else {
428 log_debug("netlink", "interface %s/%s is updated",
429 ifdold->name, ifdnew->name);
430 netlink_merge(ifdold, ifdnew);
431 TAILQ_INSERT_AFTER(ifs, ifdold, ifdnew, next);
432 TAILQ_REMOVE(ifs, ifdold, next);
433 interfaces_free_device(ifdold);
434 }
435 } else {
436 if (ifdold == NULL) {
437 log_warnx("netlink",
438 "removal request for %s, but no knowledge of it",
439 ifdnew->name);
440 } else {
441 log_debug("netlink", "interface %s is to be removed",
442 ifdold->name);
443 TAILQ_REMOVE(ifs, ifdold, next);
444 interfaces_free_device(ifdold);
445 }
446 interfaces_free_device(ifdnew);
447 }
448 link_update = 1;
449 } else {
450 interfaces_free_device(ifdnew);
451 }
452 break;
453 case RTM_NEWADDR:
454 case RTM_DELADDR:
455 if (!ifas) break;
456 log_debug("netlink", "received address information");
457 ifanew = calloc(1, sizeof(struct interfaces_address));
458 if (ifanew == NULL) {
459 log_warn("netlink", "not enough memory for another address, give what we have");
460 goto end;
461 }
462 if (netlink_parse_address(msg, ifanew) == 0) {
463 TAILQ_FOREACH(ifaold, ifas, next) {
464 if ((ifaold->index == ifanew->index) &&
465 !memcmp(&ifaold->address, &ifanew->address,
466 sizeof(ifaold->address))) continue;
467 }
468 if (getnameinfo((struct sockaddr *)&ifanew->address,
469 sizeof(ifanew->address),
470 addr, sizeof(addr),
471 NULL, 0, NI_NUMERICHOST) != 0) {
472 strlcpy(addr, "(unknown)", sizeof(addr));
473 }
474
475 if (msg->nlmsg_type == RTM_NEWADDR) {
476 if (ifaold == NULL) {
477 log_debug("netlink", "new address %s%%%d",
478 addr, ifanew->index);
479 TAILQ_INSERT_TAIL(ifas, ifanew, next);
480 } else {
481 log_debug("netlink", "updated address %s%%%d",
482 addr, ifaold->index);
483 TAILQ_INSERT_AFTER(ifas, ifaold, ifanew, next);
484 TAILQ_REMOVE(ifas, ifaold, next);
485 interfaces_free_address(ifaold);
486 }
487 } else {
488 if (ifaold == NULL) {
489 log_warnx("netlink",
490 "removal request for address of %s%%%d, but no knowledge of it",
491 addr, ifanew->index);
492 } else {
493 log_debug("netlink", "address %s%%%d is to be removed",
494 addr, ifaold->index);
495 TAILQ_REMOVE(ifas, ifaold, next);
496 interfaces_free_address(ifaold);
497 }
498 interfaces_free_address(ifanew);
499 }
500 } else {
501 interfaces_free_address(ifanew);
502 }
503 break;
504 default:
505 log_debug("netlink",
506 "received unhandled message type %d (len: %d)",
507 msg->nlmsg_type, msg->nlmsg_len);
508 }
509 }
510 }
511 end:
512 if (link_update) {
513 /* Fill out lower/upper */
514 struct interfaces_device *iface1, *iface2;
515 TAILQ_FOREACH(iface1, ifs, next) {
516 if (iface1->upper_idx != -1 && iface1->upper_idx != iface1->index) {
517 TAILQ_FOREACH(iface2, ifs, next) {
518 if (iface1->upper_idx == iface2->index) {
519 iface1->upper = iface2;
520 break;
521 }
522 }
523 if (iface2 == NULL)
524 iface1->upper = NULL;
525 } else {
526 iface1->upper = NULL;
527 }
528 if (iface1->lower_idx != -1 && iface1->lower_idx != iface1->index) {
529 TAILQ_FOREACH(iface2, ifs, next) {
530 if (iface1->lower_idx == iface2->index) {
531 /* Workaround a bug introduced
532 * in Linux 4.1: a pair of veth
533 * will be lower interface of
534 * each other. Do not modify
535 * index as if one of them is
536 * updated, we will loose the
537 * information about the
538 * loop. */
539 if (iface2->lower_idx == iface1->index) {
540 iface1->lower = NULL;
541 } else iface1->lower = iface2;
542 break;
543 }
544 if (iface2 == NULL)
545 iface1->lower = NULL;
546 }
547 } else {
548 iface1->lower = NULL;
549 }
550 }
551 }
552 return 0;
553 }
554
555 static int
556 netlink_group_mask(int group)
557 {
558 return group ? (1 << (group - 1)) : 0;
559 }
560
561 /**
562 * Subscribe to link changes.
563 *
564 * @return The socket we should listen to for changes.
565 */
566 int
567 netlink_subscribe_changes()
568 {
569 unsigned int groups;
570
571 log_debug("netlink", "listening on interface changes");
572
573 groups = netlink_group_mask(RTNLGRP_LINK) |
574 netlink_group_mask(RTNLGRP_IPV4_IFADDR) |
575 netlink_group_mask(RTNLGRP_IPV6_IFADDR);
576
577 return netlink_connect(NETLINK_ROUTE, groups);
578 }
579
580 /**
581 * Receive changes from netlink */
582 static void
583 netlink_change_cb(struct lldpd *cfg)
584 {
585 if (cfg->g_netlink == NULL)
586 return;
587 netlink_recv(cfg->g_netlink->nl_socket,
588 cfg->g_netlink->devices,
589 cfg->g_netlink->addresses);
590 }
591
592 /**
593 * Initialize netlink subsystem.
594 *
595 * This can be called several times but will have effect only the first time.
596 *
597 * @return 0 on success, -1 otherwise
598 */
599 static int
600 netlink_initialize(struct lldpd *cfg)
601 {
602 if (cfg->g_netlink) return 0;
603
604 log_debug("netlink", "initialize netlink subsystem");
605 if ((cfg->g_netlink = calloc(sizeof(struct lldpd_netlink), 1)) == NULL) {
606 log_warn("netlink", "unable to allocate memory for netlink subsystem");
607 goto end;
608 }
609
610 /* Connect to netlink (by requesting to get notified on updates) and
611 * request updated information right now */
612 int s = cfg->g_netlink->nl_socket = netlink_subscribe_changes();
613
614 struct interfaces_address_list *ifaddrs = cfg->g_netlink->addresses =
615 malloc(sizeof(struct interfaces_address_list));
616 if (ifaddrs == NULL) {
617 log_warn("netlink", "not enough memory for address list");
618 goto end;
619 }
620 TAILQ_INIT(ifaddrs);
621
622 struct interfaces_device_list *ifs = cfg->g_netlink->devices =
623 malloc(sizeof(struct interfaces_device_list));
624 if (ifs == NULL) {
625 log_warn("netlink", "not enough memory for interface list");
626 goto end;
627 }
628 TAILQ_INIT(ifs);
629
630 if (netlink_send(s, RTM_GETADDR, AF_UNSPEC, 1) == -1)
631 goto end;
632 netlink_recv(s, NULL, ifaddrs);
633 if (netlink_send(s, RTM_GETLINK, AF_PACKET, 2) == -1)
634 goto end;
635 netlink_recv(s, ifs, NULL);
636
637 /* Listen to any future change */
638 cfg->g_iface_cb = netlink_change_cb;
639 if (levent_iface_subscribe(cfg, s) == -1) {
640 goto end;
641 }
642
643 return 0;
644 end:
645 netlink_cleanup(cfg);
646 return -1;
647 }
648
649 /**
650 * Cleanup netlink subsystem.
651 */
652 void
653 netlink_cleanup(struct lldpd *cfg)
654 {
655 if (cfg->g_netlink == NULL) return;
656 if (cfg->g_netlink->nl_socket != -1)
657 close(cfg->g_netlink->nl_socket);
658 interfaces_free_devices(cfg->g_netlink->devices);
659 interfaces_free_addresses(cfg->g_netlink->addresses);
660
661 free(cfg->g_netlink);
662 cfg->g_netlink = NULL;
663 }
664
665 /**
666 * Receive the list of interfaces.
667 *
668 * @return a list of interfaces.
669 */
670 struct interfaces_device_list*
671 netlink_get_interfaces(struct lldpd *cfg)
672 {
673 if (netlink_initialize(cfg) == -1) return NULL;
674 struct interfaces_device *ifd;
675 TAILQ_FOREACH(ifd, cfg->g_netlink->devices, next) {
676 ifd->ignore = 0;
677 }
678 return cfg->g_netlink->devices;
679 }
680
681 /**
682 * Receive the list of addresses.
683 *
684 * @return a list of addresses.
685 */
686 struct interfaces_address_list*
687 netlink_get_addresses(struct lldpd *cfg)
688 {
689 if (netlink_initialize(cfg) == -1) return NULL;
690 return cfg->g_netlink->addresses;
691 }