1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
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.
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.
18 /* Grabbing interfaces information with netlink only. */
23 #include <sys/socket.h>
25 #include <net/if_arp.h>
26 #include <linux/netlink.h>
27 #include <linux/rtnetlink.h>
29 #define NETLINK_BUFFER 4096
36 struct lldpd_netlink
{
39 struct interfaces_device_list
*devices
;
40 struct interfaces_address_list
*addresses
;
46 * Open a Netlink socket and connect to it.
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.
53 netlink_connect(int protocol
, unsigned groups
)
56 struct sockaddr_nl local
= {
57 .nl_family
= AF_NETLINK
,
62 /* Open Netlink socket */
63 log_debug("netlink", "opening netlink socket");
64 s
= socket(AF_NETLINK
, SOCK_RAW
, protocol
);
66 log_warn("netlink", "unable to open netlink socket");
69 if (groups
&& bind(s
, (struct sockaddr
*)&local
, sizeof(struct sockaddr_nl
)) < 0) {
70 log_warn("netlink", "unable to bind netlink socket");
78 * Send a netlink message.
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.
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
89 netlink_send(int s
, int type
, int family
, int seq
)
91 struct netlink_req req
= {
93 .nlmsg_len
= NLMSG_LENGTH(sizeof(struct rtgenmsg
)),
95 .nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
,
97 .nlmsg_pid
= getpid() },
98 .gen
= { .rtgen_family
= family
}
102 .iov_len
= req
.hdr
.nlmsg_len
104 struct sockaddr_nl peer
= { .nl_family
= AF_NETLINK
};
105 struct msghdr rtnl_msg
= {
109 .msg_namelen
= sizeof(struct sockaddr_nl
)
112 /* Send netlink message. This is synchronous but we are guaranteed
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");
124 netlink_parse_rtattr(struct rtattr
*tb
[], int max
, struct rtattr
*rta
, int len
)
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
);
134 * Parse a `linkinfo` attributes.
136 * @param iff where to put the result
137 * @param rta linkinfo attribute
138 * @param len length of attributes
141 netlink_parse_linkinfo(struct interfaces_device
*iff
, struct rtattr
*rta
, int len
)
143 struct rtattr
*link_info_attrs
[IFLA_INFO_MAX
+1] = {};
146 netlink_parse_rtattr(link_info_attrs
, IFLA_INFO_MAX
, rta
, len
);
148 if (link_info_attrs
[IFLA_INFO_KIND
]) {
149 kind
= strdup(RTA_DATA(link_info_attrs
[IFLA_INFO_KIND
]));
151 if (!strcmp(kind
, "vlan")) {
152 log_debug("netlink", "interface %s is a VLAN",
154 iff
->type
|= IFACE_VLAN_T
;
155 } else if (!strcmp(kind
, "bridge")) {
156 log_debug("netlink", "interface %s is a bridge",
158 iff
->type
|= IFACE_BRIDGE_T
;
159 } else if (!strcmp(kind
, "bond")) {
160 log_debug("netlink", "interface %s is a bond",
162 iff
->type
|= IFACE_BOND_T
;
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
]));
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
);
184 * Parse a `link` netlink message.
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
191 netlink_parse_link(struct nlmsghdr
*msg
,
192 struct interfaces_device
*iff
)
194 struct ifinfomsg
*ifi
;
195 struct rtattr
*attribute
;
197 ifi
= NLMSG_DATA(msg
);
198 len
= msg
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ifinfomsg
));
200 if (ifi
->ifi_type
!= ARPHRD_ETHER
) {
201 log_debug("netlink", "skip non Ethernet interface at index %d",
206 iff
->index
= ifi
->ifi_index
;
207 iff
->flags
= ifi
->ifi_flags
;
211 for (attribute
= IFLA_RTA(ifi
);
212 RTA_OK(attribute
, len
);
213 attribute
= RTA_NEXT(attribute
, len
)) {
214 switch(attribute
->rta_type
) {
217 iff
->name
= strdup(RTA_DATA(attribute
));
220 /* Interface alias */
221 iff
->alias
= strdup(RTA_DATA(attribute
));
224 /* Interface MAC address */
225 iff
->address
= malloc(RTA_PAYLOAD(attribute
));
227 memcpy(iff
->address
, RTA_DATA(attribute
), RTA_PAYLOAD(attribute
));
230 /* Index of "lower" interface */
231 iff
->lower_idx
= *(int*)RTA_DATA(attribute
);
234 /* Index of master interface */
235 iff
->upper_idx
= *(int*)RTA_DATA(attribute
);
238 /* Maximum Transmission Unit */
239 iff
->mtu
= *(int*)RTA_DATA(attribute
);
242 netlink_parse_linkinfo(iff
, RTA_DATA(attribute
), RTA_PAYLOAD(attribute
));
245 log_debug("netlink", "unhandled link attribute type %d for iface %s",
246 attribute
->rta_type
, iff
->name
? iff
->name
: "(unknown)");
250 if (!iff
->name
|| !iff
->address
) {
251 log_info("netlink", "interface %d does not have a name or an address, skip",
256 log_debug("netlink", "parsed link %d (%s, flags: %d)",
257 iff
->index
, iff
->name
, iff
->flags
);
262 * Parse a `address` netlink message.
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
269 netlink_parse_address(struct nlmsghdr
*msg
,
270 struct interfaces_address
*ifa
)
272 struct ifaddrmsg
*ifi
;
273 struct rtattr
*attribute
;
275 ifi
= NLMSG_DATA(msg
);
276 len
= msg
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ifaddrmsg
));
278 ifa
->index
= ifi
->ifa_index
;
279 ifa
->flags
= ifi
->ifa_flags
;
280 switch (ifi
->ifa_family
) {
282 case AF_INET6
: break;
284 log_debug("netlink", "got a non IP address on if %d (family: %d)",
285 ifa
->index
, ifi
->ifa_family
);
289 for (attribute
= IFA_RTA(ifi
);
290 RTA_OK(attribute
, len
);
291 attribute
= RTA_NEXT(attribute
, len
)) {
292 switch(attribute
->rta_type
) {
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
));
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
));
312 log_debug("netlink", "unhandled address attribute type %d for iface %d",
313 attribute
->rta_type
, ifa
->index
);
317 if (ifa
->address
.ss_family
== AF_UNSPEC
) {
318 log_debug("netlink", "no IP for interface %d",
326 * Merge an old interface with a new one.
328 * Some properties may be absent in the new interface that should be copied over
332 netlink_merge(struct interfaces_device
*old
, struct interfaces_device
*new)
334 if (new->alias
== NULL
) {
335 new->alias
= old
->alias
;
338 if (new->address
== NULL
) {
339 new->address
= old
->address
;
345 new->type
= old
->type
;
346 if (new->vlanid
== 0)
347 new->vlanid
= old
->vlanid
;
351 * Receive netlink answer from the kernel.
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
360 struct interfaces_device_list
*ifs
,
361 struct interfaces_address_list
*ifas
)
363 char reply
[NETLINK_BUFFER
] __attribute__ ((aligned
));
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];
375 struct nlmsghdr
*msg
;
378 .iov_len
= NETLINK_BUFFER
380 struct sockaddr_nl peer
= { .nl_family
= AF_NETLINK
};
381 struct msghdr rtnl_reply
= {
385 .msg_namelen
= sizeof(struct sockaddr_nl
)
388 len
= recvmsg(s
, &rtnl_reply
, 0);
390 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
391 log_debug("netlink", "should have received something, but didn't");
394 log_warnx("netlink", "unable to receive netlink answer");
398 for (msg
= (struct nlmsghdr
*)(void*)reply
;
400 msg
= NLMSG_NEXT(msg
, len
)) {
401 if (!(msg
->nlmsg_flags
& NLM_F_MULTI
))
403 switch (msg
->nlmsg_type
) {
405 log_debug("netlink", "received done message");
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");
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;
422 if (msg
->nlmsg_type
== RTM_NEWLINK
) {
423 if (ifdold
== NULL
) {
424 log_debug("netlink", "interface %s is new",
426 TAILQ_INSERT_TAIL(ifs
, ifdnew
, next
);
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
);
436 if (ifdold
== NULL
) {
438 "removal request for %s, but no knowledge of it",
441 log_debug("netlink", "interface %s is to be removed",
443 TAILQ_REMOVE(ifs
, ifdold
, next
);
444 interfaces_free_device(ifdold
);
446 interfaces_free_device(ifdnew
);
450 interfaces_free_device(ifdnew
);
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");
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;
468 if (getnameinfo((struct sockaddr
*)&ifanew
->address
,
469 sizeof(ifanew
->address
),
471 NULL
, 0, NI_NUMERICHOST
) != 0) {
472 strlcpy(addr
, "(unknown)", sizeof(addr
));
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
);
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
);
488 if (ifaold
== NULL
) {
490 "removal request for address of %s%%%d, but no knowledge of it",
491 addr
, ifanew
->index
);
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
);
498 interfaces_free_address(ifanew
);
501 interfaces_free_address(ifanew
);
506 "received unhandled message type %d (len: %d)",
507 msg
->nlmsg_type
, msg
->nlmsg_len
);
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
;
524 iface1
->upper
= NULL
;
526 iface1
->upper
= NULL
;
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
539 if (iface2
->lower_idx
== iface1
->index
) {
540 iface1
->lower
= NULL
;
541 } else iface1
->lower
= iface2
;
545 iface1
->lower
= NULL
;
548 iface1
->lower
= NULL
;
556 netlink_group_mask(int group
)
558 return group
? (1 << (group
- 1)) : 0;
562 * Subscribe to link changes.
564 * @return The socket we should listen to for changes.
567 netlink_subscribe_changes()
571 log_debug("netlink", "listening on interface changes");
573 groups
= netlink_group_mask(RTNLGRP_LINK
) |
574 netlink_group_mask(RTNLGRP_IPV4_IFADDR
) |
575 netlink_group_mask(RTNLGRP_IPV6_IFADDR
);
577 return netlink_connect(NETLINK_ROUTE
, groups
);
581 * Receive changes from netlink */
583 netlink_change_cb(struct lldpd
*cfg
)
585 if (cfg
->g_netlink
== NULL
)
587 netlink_recv(cfg
->g_netlink
->nl_socket
,
588 cfg
->g_netlink
->devices
,
589 cfg
->g_netlink
->addresses
);
593 * Initialize netlink subsystem.
595 * This can be called several times but will have effect only the first time.
597 * @return 0 on success, -1 otherwise
600 netlink_initialize(struct lldpd
*cfg
)
602 if (cfg
->g_netlink
) return 0;
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");
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();
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");
622 struct interfaces_device_list
*ifs
= cfg
->g_netlink
->devices
=
623 malloc(sizeof(struct interfaces_device_list
));
625 log_warn("netlink", "not enough memory for interface list");
630 if (netlink_send(s
, RTM_GETADDR
, AF_UNSPEC
, 1) == -1)
632 netlink_recv(s
, NULL
, ifaddrs
);
633 if (netlink_send(s
, RTM_GETLINK
, AF_PACKET
, 2) == -1)
635 netlink_recv(s
, ifs
, NULL
);
637 /* Listen to any future change */
638 cfg
->g_iface_cb
= netlink_change_cb
;
639 if (levent_iface_subscribe(cfg
, s
) == -1) {
645 netlink_cleanup(cfg
);
650 * Cleanup netlink subsystem.
653 netlink_cleanup(struct lldpd
*cfg
)
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
);
661 free(cfg
->g_netlink
);
662 cfg
->g_netlink
= NULL
;
666 * Receive the list of interfaces.
668 * @return a list of interfaces.
670 struct interfaces_device_list
*
671 netlink_get_interfaces(struct lldpd
*cfg
)
673 if (netlink_initialize(cfg
) == -1) return NULL
;
674 struct interfaces_device
*ifd
;
675 TAILQ_FOREACH(ifd
, cfg
->g_netlink
->devices
, next
) {
678 return cfg
->g_netlink
->devices
;
682 * Receive the list of addresses.
684 * @return a list of addresses.
686 struct interfaces_address_list
*
687 netlink_get_addresses(struct lldpd
*cfg
)
689 if (netlink_initialize(cfg
) == -1) return NULL
;
690 return cfg
->g_netlink
->addresses
;