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
);
232 log_debug("netlink", "attribute IFLA_LINK for %s: %d",
233 iff
->name
? iff
->name
: "(unknown)", iff
->lower_idx
);
235 case IFLA_LINK_NETNSID
:
236 /* Is the lower interface into another namesapce? */
238 log_debug("netlink", "attribute IFLA_LINK_NETNSID received for %s",
239 iff
->name
? iff
->name
: "(unknown)");
242 /* Index of master interface */
243 iff
->upper_idx
= *(int*)RTA_DATA(attribute
);
246 /* Maximum Transmission Unit */
247 iff
->mtu
= *(int*)RTA_DATA(attribute
);
250 netlink_parse_linkinfo(iff
, RTA_DATA(attribute
), RTA_PAYLOAD(attribute
));
253 log_debug("netlink", "unhandled link attribute type %d for iface %s",
254 attribute
->rta_type
, iff
->name
? iff
->name
: "(unknown)");
258 if (!iff
->name
|| !iff
->address
) {
259 log_info("netlink", "interface %d does not have a name or an address, skip",
264 log_debug("netlink", "parsed link %d (%s, flags: %d)",
265 iff
->index
, iff
->name
, iff
->flags
);
270 * Parse a `address` netlink message.
272 * @param msg message to be parsed
273 * @param ifa where to put the result
274 * return 0 if the address is worth it, -1 otherwise
277 netlink_parse_address(struct nlmsghdr
*msg
,
278 struct interfaces_address
*ifa
)
280 struct ifaddrmsg
*ifi
;
281 struct rtattr
*attribute
;
283 ifi
= NLMSG_DATA(msg
);
284 len
= msg
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ifaddrmsg
));
286 ifa
->index
= ifi
->ifa_index
;
287 ifa
->flags
= ifi
->ifa_flags
;
288 switch (ifi
->ifa_family
) {
290 case AF_INET6
: break;
292 log_debug("netlink", "got a non IP address on if %d (family: %d)",
293 ifa
->index
, ifi
->ifa_family
);
297 for (attribute
= IFA_RTA(ifi
);
298 RTA_OK(attribute
, len
);
299 attribute
= RTA_NEXT(attribute
, len
)) {
300 switch(attribute
->rta_type
) {
303 if (ifi
->ifa_family
== AF_INET
) {
304 struct sockaddr_in ip
;
305 memset(&ip
, 0, sizeof(struct sockaddr_in
));
306 ip
.sin_family
= AF_INET
;
307 memcpy(&ip
.sin_addr
, RTA_DATA(attribute
),
308 sizeof(struct in_addr
));
309 memcpy(&ifa
->address
, &ip
, sizeof(struct sockaddr_in
));
311 struct sockaddr_in6 ip6
;
312 memset(&ip6
, 0, sizeof(struct sockaddr_in6
));
313 ip6
.sin6_family
= AF_INET6
;
314 memcpy(&ip6
.sin6_addr
, RTA_DATA(attribute
),
315 sizeof(struct in6_addr
));
316 memcpy(&ifa
->address
, &ip6
, sizeof(struct sockaddr_in6
));
320 log_debug("netlink", "unhandled address attribute type %d for iface %d",
321 attribute
->rta_type
, ifa
->index
);
325 if (ifa
->address
.ss_family
== AF_UNSPEC
) {
326 log_debug("netlink", "no IP for interface %d",
334 * Merge an old interface with a new one.
336 * Some properties may be absent in the new interface that should be copied over
340 netlink_merge(struct interfaces_device
*old
, struct interfaces_device
*new)
342 if (new->alias
== NULL
) {
343 new->alias
= old
->alias
;
346 if (new->address
== NULL
) {
347 new->address
= old
->address
;
353 new->type
= old
->type
;
354 if (new->vlanid
== 0)
355 new->vlanid
= old
->vlanid
;
357 /* It's not possible for lower link to change */
358 new->lower_idx
= old
->lower_idx
;
362 * Receive netlink answer from the kernel.
364 * @param s the netlink socket
365 * @param ifs list to store interface list or NULL if we don't
366 * @param ifas list to store address list or NULL if we don't
367 * @return 0 on success, -1 on error
371 struct interfaces_device_list
*ifs
,
372 struct interfaces_address_list
*ifas
)
374 int end
= 0, ret
= 0;
375 int flags
= MSG_PEEK
| MSG_TRUNC
;
379 struct interfaces_device
*ifdold
;
380 struct interfaces_device
*ifdnew
;
381 struct interfaces_address
*ifaold
;
382 struct interfaces_address
*ifanew
;
383 char addr
[INET6_ADDRSTRLEN
+ 1];
385 iov
.iov_len
= NETLINK_BUFFER
;
386 iov
.iov_base
= malloc(iov
.iov_len
);
388 log_warn("netlink", "not enough memory");
394 struct nlmsghdr
*msg
;
395 struct sockaddr_nl peer
= { .nl_family
= AF_NETLINK
};
396 struct msghdr rtnl_reply
= {
400 .msg_namelen
= sizeof(struct sockaddr_nl
)
404 len
= recvmsg(s
, &rtnl_reply
, flags
);
406 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
407 log_debug("netlink", "should have received something, but didn't");
411 log_warn("netlink", "unable to receive netlink answer");
420 if (iov
.iov_len
< len
|| (rtnl_reply
.msg_flags
& MSG_TRUNC
)) {
423 /* Provided buffer is not large enough, enlarge it
424 * to size of len (which should be total length of the message)
427 tmp
= realloc(iov
.iov_base
, iov
.iov_len
);
429 log_warn("netlink", "not enough memory");
433 log_debug("netlink", "enlarge message size to %zu bytes", len
);
440 /* Buffer is big enough, do the actual reading */
445 for (msg
= (struct nlmsghdr
*)(void*)(iov
.iov_base
);
447 msg
= NLMSG_NEXT(msg
, len
)) {
448 if (!(msg
->nlmsg_flags
& NLM_F_MULTI
))
450 switch (msg
->nlmsg_type
) {
452 log_debug("netlink", "received done message");
458 log_debug("netlink", "received link information");
459 ifdnew
= calloc(1, sizeof(struct interfaces_device
));
460 if (ifdnew
== NULL
) {
461 log_warn("netlink", "not enough memory for another interface, give up what we have");
464 if (netlink_parse_link(msg
, ifdnew
) == 0) {
465 /* We need to find if we already have this interface */
466 TAILQ_FOREACH(ifdold
, ifs
, next
) {
467 if (ifdold
->index
== ifdnew
->index
) break;
469 if (msg
->nlmsg_type
== RTM_NEWLINK
) {
470 if (ifdold
== NULL
) {
471 log_debug("netlink", "interface %s is new",
473 TAILQ_INSERT_TAIL(ifs
, ifdnew
, next
);
475 log_debug("netlink", "interface %s/%s is updated",
476 ifdold
->name
, ifdnew
->name
);
477 netlink_merge(ifdold
, ifdnew
);
478 TAILQ_INSERT_AFTER(ifs
, ifdold
, ifdnew
, next
);
479 TAILQ_REMOVE(ifs
, ifdold
, next
);
480 interfaces_free_device(ifdold
);
483 if (ifdold
== NULL
) {
485 "removal request for %s, but no knowledge of it",
488 log_debug("netlink", "interface %s is to be removed",
490 TAILQ_REMOVE(ifs
, ifdold
, next
);
491 interfaces_free_device(ifdold
);
493 interfaces_free_device(ifdnew
);
497 interfaces_free_device(ifdnew
);
503 log_debug("netlink", "received address information");
504 ifanew
= calloc(1, sizeof(struct interfaces_address
));
505 if (ifanew
== NULL
) {
506 log_warn("netlink", "not enough memory for another address, give what we have");
509 if (netlink_parse_address(msg
, ifanew
) == 0) {
510 TAILQ_FOREACH(ifaold
, ifas
, next
) {
511 if ((ifaold
->index
== ifanew
->index
) &&
512 !memcmp(&ifaold
->address
, &ifanew
->address
,
513 sizeof(ifaold
->address
))) continue;
515 if (getnameinfo((struct sockaddr
*)&ifanew
->address
,
516 sizeof(ifanew
->address
),
518 NULL
, 0, NI_NUMERICHOST
) != 0) {
519 strlcpy(addr
, "(unknown)", sizeof(addr
));
522 if (msg
->nlmsg_type
== RTM_NEWADDR
) {
523 if (ifaold
== NULL
) {
524 log_debug("netlink", "new address %s%%%d",
525 addr
, ifanew
->index
);
526 TAILQ_INSERT_TAIL(ifas
, ifanew
, next
);
528 log_debug("netlink", "updated address %s%%%d",
529 addr
, ifaold
->index
);
530 TAILQ_INSERT_AFTER(ifas
, ifaold
, ifanew
, next
);
531 TAILQ_REMOVE(ifas
, ifaold
, next
);
532 interfaces_free_address(ifaold
);
535 if (ifaold
== NULL
) {
537 "removal request for address of %s%%%d, but no knowledge of it",
538 addr
, ifanew
->index
);
540 log_debug("netlink", "address %s%%%d is to be removed",
541 addr
, ifaold
->index
);
542 TAILQ_REMOVE(ifas
, ifaold
, next
);
543 interfaces_free_address(ifaold
);
545 interfaces_free_address(ifanew
);
548 interfaces_free_address(ifanew
);
553 "received unhandled message type %d (len: %d)",
554 msg
->nlmsg_type
, msg
->nlmsg_len
);
557 flags
= MSG_PEEK
| MSG_TRUNC
;
561 /* Fill out lower/upper */
562 struct interfaces_device
*iface1
, *iface2
;
563 TAILQ_FOREACH(iface1
, ifs
, next
) {
564 if (iface1
->upper_idx
!= -1 && iface1
->upper_idx
!= iface1
->index
) {
565 TAILQ_FOREACH(iface2
, ifs
, next
) {
566 if (iface1
->upper_idx
== iface2
->index
) {
568 "upper interface for %s is %s",
569 iface1
->name
, iface2
->name
);
570 iface1
->upper
= iface2
;
575 iface1
->upper
= NULL
;
577 iface1
->upper
= NULL
;
579 if (iface1
->lower_idx
!= -1 && iface1
->lower_idx
!= iface1
->index
) {
580 TAILQ_FOREACH(iface2
, ifs
, next
) {
581 if (iface1
->lower_idx
== iface2
->index
) {
582 /* Workaround a bug introduced
583 * in Linux 4.1: a pair of veth
584 * will be lower interface of
585 * each other. Do not modify
586 * index as if one of them is
587 * updated, we will loose the
588 * information about the
590 if (iface2
->lower_idx
== iface1
->index
) {
591 iface1
->lower
= NULL
;
593 "link loop detected between %s and %s",
594 iface1
->name
, iface2
->name
);
597 "lower interface for %s is %s",
598 iface1
->name
, iface2
->name
);
599 iface1
->lower
= iface2
;
604 iface1
->lower
= NULL
;
607 iface1
->lower
= NULL
;
618 netlink_group_mask(int group
)
620 return group
? (1 << (group
- 1)) : 0;
624 * Subscribe to link changes.
626 * @return The socket we should listen to for changes.
629 netlink_subscribe_changes()
633 log_debug("netlink", "listening on interface changes");
635 groups
= netlink_group_mask(RTNLGRP_LINK
) |
636 netlink_group_mask(RTNLGRP_IPV4_IFADDR
) |
637 netlink_group_mask(RTNLGRP_IPV6_IFADDR
);
639 return netlink_connect(NETLINK_ROUTE
, groups
);
643 * Receive changes from netlink */
645 netlink_change_cb(struct lldpd
*cfg
)
647 if (cfg
->g_netlink
== NULL
)
649 netlink_recv(cfg
->g_netlink
->nl_socket
,
650 cfg
->g_netlink
->devices
,
651 cfg
->g_netlink
->addresses
);
655 * Initialize netlink subsystem.
657 * This can be called several times but will have effect only the first time.
659 * @return 0 on success, -1 otherwise
662 netlink_initialize(struct lldpd
*cfg
)
664 if (cfg
->g_netlink
) return 0;
666 log_debug("netlink", "initialize netlink subsystem");
667 if ((cfg
->g_netlink
= calloc(sizeof(struct lldpd_netlink
), 1)) == NULL
) {
668 log_warn("netlink", "unable to allocate memory for netlink subsystem");
672 /* Connect to netlink (by requesting to get notified on updates) and
673 * request updated information right now */
674 int s
= cfg
->g_netlink
->nl_socket
= netlink_subscribe_changes();
676 struct interfaces_address_list
*ifaddrs
= cfg
->g_netlink
->addresses
=
677 malloc(sizeof(struct interfaces_address_list
));
678 if (ifaddrs
== NULL
) {
679 log_warn("netlink", "not enough memory for address list");
684 struct interfaces_device_list
*ifs
= cfg
->g_netlink
->devices
=
685 malloc(sizeof(struct interfaces_device_list
));
687 log_warn("netlink", "not enough memory for interface list");
692 if (netlink_send(s
, RTM_GETADDR
, AF_UNSPEC
, 1) == -1)
694 netlink_recv(s
, NULL
, ifaddrs
);
695 if (netlink_send(s
, RTM_GETLINK
, AF_PACKET
, 2) == -1)
697 netlink_recv(s
, ifs
, NULL
);
699 /* Listen to any future change */
700 cfg
->g_iface_cb
= netlink_change_cb
;
701 if (levent_iface_subscribe(cfg
, s
) == -1) {
707 netlink_cleanup(cfg
);
712 * Cleanup netlink subsystem.
715 netlink_cleanup(struct lldpd
*cfg
)
717 if (cfg
->g_netlink
== NULL
) return;
718 if (cfg
->g_netlink
->nl_socket
!= -1)
719 close(cfg
->g_netlink
->nl_socket
);
720 interfaces_free_devices(cfg
->g_netlink
->devices
);
721 interfaces_free_addresses(cfg
->g_netlink
->addresses
);
723 free(cfg
->g_netlink
);
724 cfg
->g_netlink
= NULL
;
728 * Receive the list of interfaces.
730 * @return a list of interfaces.
732 struct interfaces_device_list
*
733 netlink_get_interfaces(struct lldpd
*cfg
)
735 if (netlink_initialize(cfg
) == -1) return NULL
;
736 struct interfaces_device
*ifd
;
737 TAILQ_FOREACH(ifd
, cfg
->g_netlink
->devices
, next
) {
740 return cfg
->g_netlink
->devices
;
744 * Receive the list of addresses.
746 * @return a list of addresses.
748 struct interfaces_address_list
*
749 netlink_get_addresses(struct lldpd
*cfg
)
751 if (netlink_initialize(cfg
) == -1) return NULL
;
752 return cfg
->g_netlink
->addresses
;