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