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
{
38 int nl_socket_recv_size
;
40 struct interfaces_device_list
*devices
;
41 struct interfaces_address_list
*addresses
;
46 * Set netlink socket buffer size.
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).
53 netlink_socket_set_buffer_size(int s
, int optname
, const char *optname_str
, int bufsize
)
55 socklen_t size
= sizeof(int);
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
);
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.
68 if (getsockopt(s
, SOL_SOCKET
, optname
, &got
, &size
) < 0) {
69 log_warn("netlink", "unable to get %s", optname_str
);
72 if (bufsize
> 0 && got
< bufsize
) {
73 log_warnx("netlink", "tried to set %s to '%d' "
74 "but got '%d'", optname_str
, bufsize
, got
);
84 * Open a Netlink socket and connect to it.
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
91 netlink_connect(struct lldpd
*cfg
, int protocol
, unsigned groups
)
94 struct sockaddr_nl local
= {
95 .nl_family
= AF_NETLINK
,
100 /* Open Netlink socket */
101 log_debug("netlink", "opening netlink socket");
102 s
= socket(AF_NETLINK
, SOCK_RAW
, protocol
);
104 log_warn("netlink", "unable to open netlink socket");
107 if (NETLINK_SEND_BUFSIZE
&&
108 netlink_socket_set_buffer_size(s
,
109 SO_SNDBUF
, "SO_SNDBUF", NETLINK_SEND_BUFSIZE
) == -1)
112 int rc
= netlink_socket_set_buffer_size(s
,
113 SO_RCVBUF
, "SO_RCVBUF", NETLINK_RECEIVE_BUFSIZE
);
116 case -2: cfg
->g_netlink
->nl_socket_recv_size
= 0; break;
117 default: cfg
->g_netlink
->nl_socket_recv_size
= rc
; break;
119 if (groups
&& bind(s
, (struct sockaddr
*)&local
, sizeof(struct sockaddr_nl
)) < 0) {
120 log_warn("netlink", "unable to bind netlink socket");
124 cfg
->g_netlink
->nl_socket
= s
;
129 * Send a netlink message.
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.
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
140 netlink_send(int s
, int type
, int family
, int seq
)
142 struct netlink_req req
= {
144 .nlmsg_len
= NLMSG_LENGTH(sizeof(struct rtgenmsg
)),
146 .nlmsg_flags
= NLM_F_REQUEST
| NLM_F_DUMP
,
148 .nlmsg_pid
= getpid() },
149 .gen
= { .rtgen_family
= family
}
153 .iov_len
= req
.hdr
.nlmsg_len
155 struct sockaddr_nl peer
= { .nl_family
= AF_NETLINK
};
156 struct msghdr rtnl_msg
= {
160 .msg_namelen
= sizeof(struct sockaddr_nl
)
163 /* Send netlink message. This is synchronous but we are guaranteed
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");
175 netlink_parse_rtattr(struct rtattr
*tb
[], int max
, struct rtattr
*rta
, int len
)
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
);
185 * Parse a `linkinfo` attributes.
187 * @param iff where to put the result
188 * @param rta linkinfo attribute
189 * @param len length of attributes
192 netlink_parse_linkinfo(struct interfaces_device
*iff
, struct rtattr
*rta
, int len
)
194 struct rtattr
*link_info_attrs
[IFLA_INFO_MAX
+1] = {};
197 netlink_parse_rtattr(link_info_attrs
, IFLA_INFO_MAX
, rta
, len
);
199 if (link_info_attrs
[IFLA_INFO_KIND
]) {
200 kind
= strdup(RTA_DATA(link_info_attrs
[IFLA_INFO_KIND
]));
202 if (!strcmp(kind
, "vlan")) {
203 log_debug("netlink", "interface %s is a VLAN",
205 iff
->type
|= IFACE_VLAN_T
;
206 } else if (!strcmp(kind
, "bridge")) {
207 log_debug("netlink", "interface %s is a bridge",
209 iff
->type
|= IFACE_BRIDGE_T
;
210 } else if (!strcmp(kind
, "bond")) {
211 log_debug("netlink", "interface %s is a bond",
213 iff
->type
|= IFACE_BOND_T
;
218 if (kind
&& !strcmp(kind
, "vlan") && link_info_attrs
[IFLA_INFO_DATA
]) {
219 struct rtattr
*vlan_link_info_data_attrs
[IFLA_VLAN_MAX
+1] = {};
220 netlink_parse_rtattr(vlan_link_info_data_attrs
, IFLA_VLAN_MAX
,
221 RTA_DATA(link_info_attrs
[IFLA_INFO_DATA
]),
222 RTA_PAYLOAD(link_info_attrs
[IFLA_INFO_DATA
]));
224 if (vlan_link_info_data_attrs
[IFLA_VLAN_ID
]) {
225 iff
->vlanid
= *(uint16_t *)RTA_DATA(vlan_link_info_data_attrs
[IFLA_VLAN_ID
]);
226 log_debug("netlink", "VLAN ID for interface %s is %d",
227 iff
->name
, iff
->vlanid
);
235 * Parse a `link` netlink message.
237 * @param msg message to be parsed
238 * @param iff where to put the result
239 * return 0 if the interface is worth it, -1 otherwise
242 netlink_parse_link(struct nlmsghdr
*msg
,
243 struct interfaces_device
*iff
)
245 struct ifinfomsg
*ifi
;
246 struct rtattr
*attribute
;
248 ifi
= NLMSG_DATA(msg
);
249 len
= msg
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ifinfomsg
));
251 if (ifi
->ifi_type
!= ARPHRD_ETHER
) {
252 log_debug("netlink", "skip non Ethernet interface at index %d",
257 iff
->index
= ifi
->ifi_index
;
258 iff
->flags
= ifi
->ifi_flags
;
262 for (attribute
= IFLA_RTA(ifi
);
263 RTA_OK(attribute
, len
);
264 attribute
= RTA_NEXT(attribute
, len
)) {
265 switch(attribute
->rta_type
) {
268 iff
->name
= strdup(RTA_DATA(attribute
));
271 /* Interface alias */
272 iff
->alias
= strdup(RTA_DATA(attribute
));
275 /* Interface MAC address */
276 iff
->address
= malloc(RTA_PAYLOAD(attribute
));
278 memcpy(iff
->address
, RTA_DATA(attribute
), RTA_PAYLOAD(attribute
));
281 /* Index of "lower" interface */
282 iff
->lower_idx
= *(int*)RTA_DATA(attribute
);
283 log_debug("netlink", "attribute IFLA_LINK for %s: %d",
284 iff
->name
? iff
->name
: "(unknown)", iff
->lower_idx
);
286 case IFLA_LINK_NETNSID
:
287 /* Is the lower interface into another namesapce? */
289 log_debug("netlink", "attribute IFLA_LINK_NETNSID received for %s",
290 iff
->name
? iff
->name
: "(unknown)");
293 /* Index of master interface */
294 iff
->upper_idx
= *(int*)RTA_DATA(attribute
);
297 /* Maximum Transmission Unit */
298 iff
->mtu
= *(int*)RTA_DATA(attribute
);
301 netlink_parse_linkinfo(iff
, RTA_DATA(attribute
), RTA_PAYLOAD(attribute
));
304 log_debug("netlink", "unhandled link attribute type %d for iface %s",
305 attribute
->rta_type
, iff
->name
? iff
->name
: "(unknown)");
309 if (!iff
->name
|| !iff
->address
) {
310 log_info("netlink", "interface %d does not have a name or an address, skip",
314 if (iff
->upper_idx
== -1) {
315 /* No upper interface, we cannot be enslaved. We need to clear
316 * the flag because the appropriate information may come later
317 * and we don't want to miss it. */
318 iff
->flags
&= ~IFF_SLAVE
;
321 log_debug("netlink", "parsed link %d (%s, flags: %d)",
322 iff
->index
, iff
->name
, iff
->flags
);
327 * Parse a `address` netlink message.
329 * @param msg message to be parsed
330 * @param ifa where to put the result
331 * return 0 if the address is worth it, -1 otherwise
334 netlink_parse_address(struct nlmsghdr
*msg
,
335 struct interfaces_address
*ifa
)
337 struct ifaddrmsg
*ifi
;
338 struct rtattr
*attribute
;
340 ifi
= NLMSG_DATA(msg
);
341 len
= msg
->nlmsg_len
- NLMSG_LENGTH(sizeof(struct ifaddrmsg
));
343 ifa
->index
= ifi
->ifa_index
;
344 ifa
->flags
= ifi
->ifa_flags
;
345 switch (ifi
->ifa_family
) {
347 case AF_INET6
: break;
349 log_debug("netlink", "got a non IP address on if %d (family: %d)",
350 ifa
->index
, ifi
->ifa_family
);
354 for (attribute
= IFA_RTA(ifi
);
355 RTA_OK(attribute
, len
);
356 attribute
= RTA_NEXT(attribute
, len
)) {
357 switch(attribute
->rta_type
) {
360 if (ifi
->ifa_family
== AF_INET
) {
361 struct sockaddr_in ip
;
362 memset(&ip
, 0, sizeof(struct sockaddr_in
));
363 ip
.sin_family
= AF_INET
;
364 memcpy(&ip
.sin_addr
, RTA_DATA(attribute
),
365 sizeof(struct in_addr
));
366 memcpy(&ifa
->address
, &ip
, sizeof(struct sockaddr_in
));
368 struct sockaddr_in6 ip6
;
369 memset(&ip6
, 0, sizeof(struct sockaddr_in6
));
370 ip6
.sin6_family
= AF_INET6
;
371 memcpy(&ip6
.sin6_addr
, RTA_DATA(attribute
),
372 sizeof(struct in6_addr
));
373 memcpy(&ifa
->address
, &ip6
, sizeof(struct sockaddr_in6
));
377 log_debug("netlink", "unhandled address attribute type %d for iface %d",
378 attribute
->rta_type
, ifa
->index
);
382 if (ifa
->address
.ss_family
== AF_UNSPEC
) {
383 log_debug("netlink", "no IP for interface %d",
391 * Merge an old interface with a new one.
393 * Some properties may be absent in the new interface that should be copied over
397 netlink_merge(struct interfaces_device
*old
, struct interfaces_device
*new)
399 if (new->alias
== NULL
) {
400 new->alias
= old
->alias
;
403 if (new->address
== NULL
) {
404 new->address
= old
->address
;
410 new->type
= old
->type
;
411 if (new->vlanid
== 0)
412 new->vlanid
= old
->vlanid
;
414 /* It's not possible for lower link to change */
415 new->lower_idx
= old
->lower_idx
;
419 * Receive netlink answer from the kernel.
421 * @param ifs list to store interface list or NULL if we don't
422 * @param ifas list to store address list or NULL if we don't
423 * @return 0 on success, -1 on error
426 netlink_recv(struct lldpd
*cfg
,
427 struct interfaces_device_list
*ifs
,
428 struct interfaces_address_list
*ifas
)
430 int end
= 0, ret
= 0;
431 int flags
= MSG_PEEK
| MSG_TRUNC
;
434 int s
= cfg
->g_netlink
->nl_socket
;
436 struct interfaces_device
*ifdold
;
437 struct interfaces_device
*ifdnew
;
438 struct interfaces_address
*ifaold
;
439 struct interfaces_address
*ifanew
;
440 char addr
[INET6_ADDRSTRLEN
+ 1];
442 iov
.iov_len
= NETLINK_BUFFER
;
443 iov
.iov_base
= malloc(iov
.iov_len
);
445 log_warn("netlink", "not enough memory");
451 struct nlmsghdr
*msg
;
452 struct sockaddr_nl peer
= { .nl_family
= AF_NETLINK
};
453 struct msghdr rtnl_reply
= {
457 .msg_namelen
= sizeof(struct sockaddr_nl
)
461 len
= recvmsg(s
, &rtnl_reply
, flags
);
463 if (errno
== EAGAIN
|| errno
== EWOULDBLOCK
) {
464 log_debug("netlink", "should have received something, but didn't");
468 int rsize
= cfg
->g_netlink
->nl_socket_recv_size
;
469 if (errno
== ENOBUFS
&&
470 rsize
> 0 && rsize
< NETLINK_MAX_RECEIVE_BUFSIZE
) {
471 /* Try to increase buffer size */
473 if (rsize
> NETLINK_MAX_RECEIVE_BUFSIZE
) {
474 rsize
= NETLINK_MAX_RECEIVE_BUFSIZE
;
476 int rc
= netlink_socket_set_buffer_size(s
,
477 SO_RCVBUF
, "SO_RCVBUF",
480 cfg
->g_netlink
->nl_socket_recv_size
= 0;
482 cfg
->g_netlink
->nl_socket_recv_size
= rsize
;
483 if (rc
> 0 || rc
== -2) {
485 "netlink receive buffer too small, retry with larger one (%d)",
491 log_warn("netlink", "unable to receive netlink answer");
500 if (iov
.iov_len
< len
|| (rtnl_reply
.msg_flags
& MSG_TRUNC
)) {
503 /* Provided buffer is not large enough, enlarge it
504 * to size of len (which should be total length of the message)
507 tmp
= realloc(iov
.iov_base
, iov
.iov_len
);
509 log_warn("netlink", "not enough memory");
513 log_debug("netlink", "enlarge message size to %zu bytes", len
);
520 /* Buffer is big enough, do the actual reading */
525 for (msg
= (struct nlmsghdr
*)(void*)(iov
.iov_base
);
527 msg
= NLMSG_NEXT(msg
, len
)) {
528 if (!(msg
->nlmsg_flags
& NLM_F_MULTI
))
530 switch (msg
->nlmsg_type
) {
532 log_debug("netlink", "received done message");
538 log_debug("netlink", "received link information");
539 ifdnew
= calloc(1, sizeof(struct interfaces_device
));
540 if (ifdnew
== NULL
) {
541 log_warn("netlink", "not enough memory for another interface, give up what we have");
544 if (netlink_parse_link(msg
, ifdnew
) == 0) {
545 /* We need to find if we already have this interface */
546 TAILQ_FOREACH(ifdold
, ifs
, next
) {
547 if (ifdold
->index
== ifdnew
->index
) break;
549 if (msg
->nlmsg_type
== RTM_NEWLINK
) {
550 if (ifdold
== NULL
) {
551 log_debug("netlink", "interface %s is new",
553 TAILQ_INSERT_TAIL(ifs
, ifdnew
, next
);
555 log_debug("netlink", "interface %s/%s is updated",
556 ifdold
->name
, ifdnew
->name
);
557 netlink_merge(ifdold
, ifdnew
);
558 TAILQ_INSERT_AFTER(ifs
, ifdold
, ifdnew
, next
);
559 TAILQ_REMOVE(ifs
, ifdold
, next
);
560 interfaces_free_device(ifdold
);
563 if (ifdold
== NULL
) {
565 "removal request for %s, but no knowledge of it",
568 log_debug("netlink", "interface %s is to be removed",
570 TAILQ_REMOVE(ifs
, ifdold
, next
);
571 interfaces_free_device(ifdold
);
573 interfaces_free_device(ifdnew
);
577 interfaces_free_device(ifdnew
);
583 log_debug("netlink", "received address information");
584 ifanew
= calloc(1, sizeof(struct interfaces_address
));
585 if (ifanew
== NULL
) {
586 log_warn("netlink", "not enough memory for another address, give what we have");
589 if (netlink_parse_address(msg
, ifanew
) == 0) {
590 TAILQ_FOREACH(ifaold
, ifas
, next
) {
591 if ((ifaold
->index
== ifanew
->index
) &&
592 !memcmp(&ifaold
->address
, &ifanew
->address
,
593 sizeof(ifaold
->address
))) continue;
595 if (getnameinfo((struct sockaddr
*)&ifanew
->address
,
596 sizeof(ifanew
->address
),
598 NULL
, 0, NI_NUMERICHOST
) != 0) {
599 strlcpy(addr
, "(unknown)", sizeof(addr
));
602 if (msg
->nlmsg_type
== RTM_NEWADDR
) {
603 if (ifaold
== NULL
) {
604 log_debug("netlink", "new address %s%%%d",
605 addr
, ifanew
->index
);
606 TAILQ_INSERT_TAIL(ifas
, ifanew
, next
);
608 log_debug("netlink", "updated address %s%%%d",
609 addr
, ifaold
->index
);
610 TAILQ_INSERT_AFTER(ifas
, ifaold
, ifanew
, next
);
611 TAILQ_REMOVE(ifas
, ifaold
, next
);
612 interfaces_free_address(ifaold
);
615 if (ifaold
== NULL
) {
617 "removal request for address of %s%%%d, but no knowledge of it",
618 addr
, ifanew
->index
);
620 log_debug("netlink", "address %s%%%d is to be removed",
621 addr
, ifaold
->index
);
622 TAILQ_REMOVE(ifas
, ifaold
, next
);
623 interfaces_free_address(ifaold
);
625 interfaces_free_address(ifanew
);
628 interfaces_free_address(ifanew
);
633 "received unhandled message type %d (len: %d)",
634 msg
->nlmsg_type
, msg
->nlmsg_len
);
637 flags
= MSG_PEEK
| MSG_TRUNC
;
641 /* Fill out lower/upper */
642 struct interfaces_device
*iface1
, *iface2
;
643 TAILQ_FOREACH(iface1
, ifs
, next
) {
644 if (iface1
->upper_idx
!= -1 && iface1
->upper_idx
!= iface1
->index
) {
645 TAILQ_FOREACH(iface2
, ifs
, next
) {
646 if (iface1
->upper_idx
== iface2
->index
) {
648 "upper interface for %s is %s",
649 iface1
->name
, iface2
->name
);
650 iface1
->upper
= iface2
;
655 iface1
->upper
= NULL
;
657 iface1
->upper
= NULL
;
659 if (iface1
->lower_idx
!= -1 && iface1
->lower_idx
!= iface1
->index
) {
660 TAILQ_FOREACH(iface2
, ifs
, next
) {
661 if (iface1
->lower_idx
== iface2
->index
) {
662 /* Workaround a bug introduced
663 * in Linux 4.1: a pair of veth
664 * will be lower interface of
665 * each other. Do not modify
666 * index as if one of them is
667 * updated, we will loose the
668 * information about the
670 if (iface2
->lower_idx
== iface1
->index
) {
671 iface1
->lower
= NULL
;
673 "link loop detected between %s and %s",
674 iface1
->name
, iface2
->name
);
677 "lower interface for %s is %s",
678 iface1
->name
, iface2
->name
);
679 iface1
->lower
= iface2
;
684 iface1
->lower
= NULL
;
687 iface1
->lower
= NULL
;
698 netlink_group_mask(int group
)
700 return group
? (1 << (group
- 1)) : 0;
704 * Subscribe to link changes.
706 * @return 0 on success, -1 otherwise
709 netlink_subscribe_changes(struct lldpd
*cfg
)
713 log_debug("netlink", "listening on interface changes");
715 groups
= netlink_group_mask(RTNLGRP_LINK
) |
716 netlink_group_mask(RTNLGRP_IPV4_IFADDR
) |
717 netlink_group_mask(RTNLGRP_IPV6_IFADDR
);
719 return netlink_connect(cfg
, NETLINK_ROUTE
, groups
);
723 * Receive changes from netlink */
725 netlink_change_cb(struct lldpd
*cfg
)
727 if (cfg
->g_netlink
== NULL
)
730 cfg
->g_netlink
->devices
,
731 cfg
->g_netlink
->addresses
);
735 * Initialize netlink subsystem.
737 * This can be called several times but will have effect only the first time.
739 * @return 0 on success, -1 otherwise
742 netlink_initialize(struct lldpd
*cfg
)
744 if (cfg
->g_netlink
) return 0;
746 log_debug("netlink", "initialize netlink subsystem");
747 if ((cfg
->g_netlink
= calloc(sizeof(struct lldpd_netlink
), 1)) == NULL
) {
748 log_warn("netlink", "unable to allocate memory for netlink subsystem");
752 /* Connect to netlink (by requesting to get notified on updates) and
753 * request updated information right now */
754 if (netlink_subscribe_changes(cfg
) == -1)
757 struct interfaces_address_list
*ifaddrs
= cfg
->g_netlink
->addresses
=
758 malloc(sizeof(struct interfaces_address_list
));
759 if (ifaddrs
== NULL
) {
760 log_warn("netlink", "not enough memory for address list");
765 struct interfaces_device_list
*ifs
= cfg
->g_netlink
->devices
=
766 malloc(sizeof(struct interfaces_device_list
));
768 log_warn("netlink", "not enough memory for interface list");
773 if (netlink_send(cfg
->g_netlink
->nl_socket
, RTM_GETADDR
, AF_UNSPEC
, 1) == -1)
775 netlink_recv(cfg
, NULL
, ifaddrs
);
776 if (netlink_send(cfg
->g_netlink
->nl_socket
, RTM_GETLINK
, AF_PACKET
, 2) == -1)
778 netlink_recv(cfg
, ifs
, NULL
);
780 /* Listen to any future change */
781 cfg
->g_iface_cb
= netlink_change_cb
;
782 if (levent_iface_subscribe(cfg
, cfg
->g_netlink
->nl_socket
) == -1) {
788 netlink_cleanup(cfg
);
793 * Cleanup netlink subsystem.
796 netlink_cleanup(struct lldpd
*cfg
)
798 if (cfg
->g_netlink
== NULL
) return;
799 if (cfg
->g_netlink
->nl_socket
!= -1)
800 close(cfg
->g_netlink
->nl_socket
);
801 interfaces_free_devices(cfg
->g_netlink
->devices
);
802 interfaces_free_addresses(cfg
->g_netlink
->addresses
);
804 free(cfg
->g_netlink
);
805 cfg
->g_netlink
= NULL
;
809 * Receive the list of interfaces.
811 * @return a list of interfaces.
813 struct interfaces_device_list
*
814 netlink_get_interfaces(struct lldpd
*cfg
)
816 if (netlink_initialize(cfg
) == -1) return NULL
;
817 struct interfaces_device
*ifd
;
818 TAILQ_FOREACH(ifd
, cfg
->g_netlink
->devices
, next
) {
821 return cfg
->g_netlink
->devices
;
825 * Receive the list of addresses.
827 * @return a list of addresses.
829 struct interfaces_address_list
*
830 netlink_get_addresses(struct lldpd
*cfg
)
832 if (netlink_initialize(cfg
) == -1) return NULL
;
833 return cfg
->g_netlink
->addresses
;