1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2008 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.
23 #include <sys/ioctl.h>
24 #if defined(__clang__)
25 #pragma clang diagnostic push
26 #pragma clang diagnostic ignored "-Wdocumentation"
28 #include <linux/if_vlan.h>
29 #include <linux/if_bonding.h>
30 #include <linux/if_bridge.h>
31 #include <linux/wireless.h>
32 #include <linux/sockios.h>
33 #include <linux/if_packet.h>
34 #include <linux/ethtool.h>
35 #if defined(__clang__)
36 #pragma clang diagnostic pop
39 #define SYSFS_PATH_MAX 256
40 #define MAX_PORTS 1024
41 #define MAX_BRIDGES 1024
44 iflinux_eth_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
48 log_debug("interfaces", "initialize ethernet device %s",
50 if ((fd
= priv_iface_init(hardware
->h_ifindex
, hardware
->h_ifname
)) == -1)
52 hardware
->h_sendfd
= fd
; /* Send */
54 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
56 levent_hardware_add_fd(hardware
, fd
); /* Receive */
57 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware
->h_ifname
,
62 /* Generic ethernet send/receive */
64 iflinux_eth_send(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
65 char *buffer
, size_t size
)
67 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
68 hardware
->h_ifname
, hardware
->h_sendfd
);
69 return write(hardware
->h_sendfd
,
74 iflinux_error_recv(struct lldpd_hardware
*hardware
, int fd
)
81 .msg_controllen
= sizeof(buf
)
83 if ((n
= recvmsg(fd
, &msg
, MSG_ERRQUEUE
)) <= 0) {
86 struct cmsghdr
*cmsg
= CMSG_FIRSTHDR(&msg
);
88 log_warnx("interfaces", "received unknown error on %s",
91 log_warnx("interfaces", "received error (level=%d/type=%d) on %s",
92 cmsg
->cmsg_level
, cmsg
->cmsg_type
, hardware
->h_ifname
);
97 iflinux_generic_recv(struct lldpd_hardware
*hardware
,
98 int fd
, char *buffer
, size_t size
,
99 struct sockaddr_ll
*from
)
105 fromlen
= sizeof(*from
);
106 memset(from
, 0, fromlen
);
107 if ((n
= recvfrom(fd
, buffer
, size
, 0,
108 (struct sockaddr
*)from
,
110 if (errno
== EAGAIN
&& retry
== 0) {
111 /* There may be an error queued in the socket. Clear it and retry. */
112 iflinux_error_recv(hardware
, fd
);
116 if (errno
== ENETDOWN
) {
117 log_debug("interfaces", "error while receiving frame on %s (network down)",
120 log_warn("interfaces", "error while receiving frame on %s (retry: %d)",
121 hardware
->h_ifname
, retry
);
122 hardware
->h_rx_discarded_cnt
++;
126 if (from
->sll_pkttype
== PACKET_OUTGOING
)
132 iflinux_eth_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
133 int fd
, char *buffer
, size_t size
)
136 struct sockaddr_ll from
;
138 log_debug("interfaces", "receive PDU from ethernet device %s",
140 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
146 iflinux_eth_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
148 log_debug("interfaces", "close ethernet device %s",
150 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
154 static struct lldpd_ops eth_ops
= {
155 .send
= iflinux_eth_send
,
156 .recv
= iflinux_eth_recv
,
157 .cleanup
= iflinux_eth_close
,
161 iflinux_is_bridge(struct lldpd
*cfg
,
162 struct interfaces_device_list
*interfaces
,
163 struct interfaces_device
*iface
)
166 struct interfaces_device
*port
;
167 char path
[SYSFS_PATH_MAX
];
170 if ((snprintf(path
, SYSFS_PATH_MAX
,
171 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_FDB
,
172 iface
->name
)) >= SYSFS_PATH_MAX
)
173 log_warnx("interfaces", "path truncated");
174 if ((f
= priv_open(path
)) < 0)
178 /* Also grab all ports */
179 TAILQ_FOREACH(port
, interfaces
, next
) {
180 if (port
->upper
) continue;
181 if (snprintf(path
, SYSFS_PATH_MAX
,
182 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_PORT_SUBDIR
"/%s/port_no",
183 iface
->name
, port
->name
) >= SYSFS_PATH_MAX
)
184 log_warnx("interfaces", "path truncated");
185 if ((f
= priv_open(path
)) < 0)
187 log_debug("interfaces",
188 "port %s is bridged to %s",
189 port
->name
, iface
->name
);
201 iflinux_is_vlan(struct lldpd
*cfg
,
202 struct interfaces_device_list
*interfaces
,
203 struct interfaces_device
*iface
)
206 struct vlan_ioctl_args ifv
= {};
207 ifv
.cmd
= GET_VLAN_REALDEV_NAME_CMD
;
208 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
209 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) >= 0) {
210 /* This is a VLAN, get the lower interface and the VID */
211 struct interfaces_device
*lower
=
212 interfaces_nametointerface(interfaces
, ifv
.u
.device2
);
214 log_debug("interfaces",
215 "unable to find lower interface for VLAN %s",
220 memset(&ifv
, 0, sizeof(ifv
));
221 ifv
.cmd
= GET_VLAN_VID_CMD
;
222 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
223 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) < 0) {
224 log_debug("interfaces",
225 "unable to find VID for VLAN %s",
230 iface
->lower
= lower
;
231 iface
->vlanid
= ifv
.u
.VID
;
239 iflinux_is_bond(struct lldpd
*cfg
,
240 struct interfaces_device_list
*interfaces
,
241 struct interfaces_device
*master
)
244 /* Shortcut if we detect the new team driver. Upper and lower links
245 * should already be set with netlink in this case. */
246 if (master
->driver
&& !strcmp(master
->driver
, "team")) {
250 struct ifreq ifr
= {};
251 struct ifbond ifb
= {};
252 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
253 ifr
.ifr_data
= (char *)&ifb
;
254 if (ioctl(cfg
->g_sock
, SIOCBONDINFOQUERY
, &ifr
) >= 0) {
255 while (ifb
.num_slaves
--) {
257 memset(&ifr
, 0, sizeof(ifr
));
258 memset(&ifs
, 0, sizeof(ifs
));
259 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
260 ifr
.ifr_data
= (char *)&ifs
;
261 ifs
.slave_id
= ifb
.num_slaves
;
262 if (ioctl(cfg
->g_sock
, SIOCBONDSLAVEINFOQUERY
, &ifr
) >= 0) {
263 struct interfaces_device
*slave
=
264 interfaces_nametointerface(interfaces
,
266 if (slave
== NULL
) continue;
267 if (slave
->upper
) continue;
268 log_debug("interfaces",
269 "interface %s is enslaved to %s",
270 slave
->name
, master
->name
);
271 slave
->upper
= master
;
281 * Get permanent MAC from ethtool.
283 * Return 0 on success, -1 on error.
286 iflinux_get_permanent_mac_ethtool(struct lldpd
*cfg
,
287 struct interfaces_device_list
*interfaces
,
288 struct interfaces_device
*iface
)
290 struct ifreq ifr
= {};
292 struct ethtool_perm_addr addr
;
293 /* cppcheck-suppress unusedStructMember */
294 char u8
[sizeof(struct ethtool_perm_addr
) + ETHER_ADDR_LEN
];
297 strlcpy(ifr
.ifr_name
, iface
->name
, sizeof(ifr
.ifr_name
));
298 epaddr
.addr
.cmd
= ETHTOOL_GPERMADDR
;
299 epaddr
.addr
.size
= ETHER_ADDR_LEN
;
300 ifr
.ifr_data
= (caddr_t
)&epaddr
.addr
;
301 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == -1) {
303 if (errno
== EPERM
&& !once
) {
304 log_warn("interfaces",
305 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
311 log_warnx("interfaces", "cannot get permanent MAC address for %s",
315 if (epaddr
.addr
.data
[0] != 0 ||
316 epaddr
.addr
.data
[1] != 0 ||
317 epaddr
.addr
.data
[2] != 0 ||
318 epaddr
.addr
.data
[3] != 0 ||
319 epaddr
.addr
.data
[4] != 0 ||
320 epaddr
.addr
.data
[5] != 0 ||
321 epaddr
.addr
.data
[6] != 0) {
322 memcpy(iface
->address
, epaddr
.addr
.data
, ETHER_ADDR_LEN
);
325 log_debug("interfaces", "cannot get permanent MAC for %s", iface
->name
);
330 * Get permanent MAC address for a bond device.
333 iflinux_get_permanent_mac_bond(struct lldpd
*cfg
,
334 struct interfaces_device_list
*interfaces
,
335 struct interfaces_device
*iface
)
337 struct interfaces_device
*master
= iface
->upper
;
340 const char *slaveif
= "Slave Interface: ";
341 const char *hwaddr
= "Permanent HW addr: ";
342 u_int8_t mac
[ETHER_ADDR_LEN
];
343 char path
[SYSFS_PATH_MAX
];
346 /* We have a bond, we need to query it to get real MAC addresses */
347 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/net/bonding/%s",
348 master
->name
) >= SYSFS_PATH_MAX
) {
349 log_warnx("interfaces", "path truncated");
352 if ((f
= priv_open(path
)) < 0) {
353 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/self/net/bonding/%s",
354 master
->name
) >= SYSFS_PATH_MAX
) {
355 log_warnx("interfaces", "path truncated");
361 log_warnx("interfaces",
362 "unable to get permanent MAC address for %s",
366 if ((netbond
= fdopen(f
, "r")) == NULL
) {
367 log_warn("interfaces", "unable to read stream from %s", path
);
372 We parse the file to search "Slave Interface: ". If found, go to
375 We parse the file to search "Permanent HW addr: ". If found, we get
378 while (fgets(line
, sizeof(line
), netbond
)) {
381 if (strncmp(line
, slaveif
, strlen(slaveif
)) == 0) {
382 if (line
[strlen(line
)-1] == '\n')
383 line
[strlen(line
)-1] = '\0';
384 if (strcmp(iface
->name
,
385 line
+ strlen(slaveif
)) == 0)
390 if (strncmp(line
, hwaddr
, strlen(hwaddr
)) == 0) {
391 if (line
[strlen(line
)-1] == '\n')
392 line
[strlen(line
)-1] = '\0';
393 if (sscanf(line
+ strlen(hwaddr
),
394 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
395 &mac
[0], &mac
[1], &mac
[2],
396 &mac
[3], &mac
[4], &mac
[5]) !=
398 log_warn("interfaces", "unable to parse %s",
399 line
+ strlen(hwaddr
));
403 memcpy(iface
->address
, mac
,
411 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
420 iflinux_get_permanent_mac(struct lldpd
*cfg
,
421 struct interfaces_device_list
*interfaces
,
422 struct interfaces_device
*iface
)
424 struct interfaces_device
*master
= iface
->upper
;
426 if (master
== NULL
|| master
->type
!= IFACE_BOND_T
)
428 if (iflinux_get_permanent_mac_ethtool(cfg
, interfaces
, iface
) == -1 &&
429 (master
->driver
== NULL
|| !strcmp(master
->driver
, "bonding")))
430 /* Fallback to old method for a bond */
431 iflinux_get_permanent_mac_bond(cfg
, interfaces
, iface
);
435 iflinux_ethtool_link_mode_test_bit(unsigned int nr
, const uint32_t *mask
)
437 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
)
439 return !!(mask
[nr
/ 32] & (1 << (nr
% 32)));
442 iflinux_ethtool_link_mode_unset_bit(unsigned int nr
, uint32_t *mask
)
444 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
)
446 mask
[nr
/ 32] &= ~(1 << (nr
% 32));
449 iflinux_ethtool_link_mode_is_empty(const uint32_t *mask
)
451 for (unsigned int i
= 0;
452 i
< ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
;
462 /* Fill up MAC/PHY for a given hardware port */
464 iflinux_macphy(struct lldpd_hardware
*hardware
)
467 struct ethtool_link_usettings uset
;
468 struct lldpd_port
*port
= &hardware
->h_lport
;
470 int advertised_ethtool_to_rfc3636
[][2] = {
471 {ETHTOOL_LINK_MODE_10baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_10BASE_T
},
472 {ETHTOOL_LINK_MODE_10baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
473 {ETHTOOL_LINK_MODE_100baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_100BASE_TX
},
474 {ETHTOOL_LINK_MODE_100baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD
},
475 {ETHTOOL_LINK_MODE_1000baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_T
},
476 {ETHTOOL_LINK_MODE_1000baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD
},
477 {ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
478 {ETHTOOL_LINK_MODE_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE
},
479 {ETHTOOL_LINK_MODE_Asym_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE
},
482 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
484 if (priv_ethtool(hardware
->h_ifname
, &uset
) == 0) {
485 port
->p_macphy
.autoneg_support
= iflinux_ethtool_link_mode_test_bit(
486 ETHTOOL_LINK_MODE_Autoneg_BIT
, uset
.link_modes
.supported
);
487 port
->p_macphy
.autoneg_enabled
= (uset
.base
.autoneg
== AUTONEG_DISABLE
) ? 0 : 1;
488 for (j
=0; advertised_ethtool_to_rfc3636
[j
][0] >= 0; j
++) {
489 if (iflinux_ethtool_link_mode_test_bit(
490 advertised_ethtool_to_rfc3636
[j
][0],
491 uset
.link_modes
.advertising
)) {
492 port
->p_macphy
.autoneg_advertised
|=
493 advertised_ethtool_to_rfc3636
[j
][1];
494 iflinux_ethtool_link_mode_unset_bit(
495 advertised_ethtool_to_rfc3636
[j
][0],
496 uset
.link_modes
.advertising
);
499 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
, uset
.link_modes
.advertising
);
500 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT
, uset
.link_modes
.advertising
);
501 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT
, uset
.link_modes
.advertising
);
502 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT
, uset
.link_modes
.advertising
);
503 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, uset
.link_modes
.advertising
);
504 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT
, uset
.link_modes
.advertising
);
505 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT
, uset
.link_modes
.advertising
);
506 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT
, uset
.link_modes
.advertising
);
507 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT
, uset
.link_modes
.advertising
);
508 if (!iflinux_ethtool_link_mode_is_empty(uset
.link_modes
.advertising
)) {
509 port
->p_macphy
.autoneg_advertised
|= LLDP_DOT3_LINK_AUTONEG_OTHER
;
511 switch (uset
.base
.speed
) {
513 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
514 LLDP_DOT3_MAU_10BASETFD
: LLDP_DOT3_MAU_10BASETHD
;
515 if (uset
.base
.port
== PORT_BNC
) port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10BASE2
;
516 if (uset
.base
.port
== PORT_FIBRE
)
517 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
518 LLDP_DOT3_MAU_10BASEFLFD
: LLDP_DOT3_MAU_10BASEFLHD
;
521 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
522 LLDP_DOT3_MAU_100BASETXFD
: LLDP_DOT3_MAU_100BASETXHD
;
523 if (uset
.base
.port
== PORT_BNC
)
524 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
525 LLDP_DOT3_MAU_100BASET2FD
: LLDP_DOT3_MAU_100BASET2HD
;
526 if (uset
.base
.port
== PORT_FIBRE
)
527 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
528 LLDP_DOT3_MAU_100BASEFXFD
: LLDP_DOT3_MAU_100BASEFXHD
;
531 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
532 LLDP_DOT3_MAU_1000BASETFD
: LLDP_DOT3_MAU_1000BASETHD
;
533 if (uset
.base
.port
== PORT_FIBRE
)
534 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
535 LLDP_DOT3_MAU_1000BASEXFD
: LLDP_DOT3_MAU_1000BASEXHD
;
538 // For fiber, we tell 10GIGBASEX and for others,
539 // 10GIGBASER. It's not unusual to have 10GIGBASER on
540 // fiber either but we don't have 10GIGBASET for
541 // copper. No good solution.
542 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
543 LLDP_DOT3_MAU_10GIGBASELR
: LLDP_DOT3_MAU_10GIGBASECX4
;
546 // Same kind of approximation.
547 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
548 LLDP_DOT3_MAU_40GBASELR4
: LLDP_DOT3_MAU_40GBASECR4
;
552 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
553 LLDP_DOT3_MAU_100GBASELR4
: LLDP_DOT3_MAU_100GBASECR10
;
556 if (uset
.base
.port
== PORT_AUI
) port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_AUI
;
567 iface_bond_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
569 struct bond_master
*master
= hardware
->h_data
;
573 if (!master
) return -1;
575 log_debug("interfaces", "initialize enslaved device %s",
578 /* First, we get a socket to the raw physical interface */
579 if ((fd
= priv_iface_init(hardware
->h_ifindex
,
580 hardware
->h_ifname
)) == -1)
582 hardware
->h_sendfd
= fd
;
583 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
585 /* Then, we open a raw interface for the master */
586 log_debug("interfaces", "enslaved device %s has master %s(%d)",
587 hardware
->h_ifname
, master
->name
, master
->index
);
588 if ((fd
= priv_iface_init(master
->index
, master
->name
)) == -1) {
589 close(hardware
->h_sendfd
);
592 /* With bonding and older kernels (< 2.6.27) we need to listen
593 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
594 * physical device instead of bond device (works with >=
596 if (setsockopt(fd
, SOL_PACKET
,
597 PACKET_ORIGDEV
, &un
, sizeof(un
)) == -1) {
598 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
599 "You will get inaccurate results",
602 interfaces_setup_multicast(cfg
, master
->name
, 0);
604 levent_hardware_add_fd(hardware
, hardware
->h_sendfd
);
605 levent_hardware_add_fd(hardware
, fd
);
606 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
614 iface_bond_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
615 int fd
, char *buffer
, size_t size
)
618 struct sockaddr_ll from
;
619 struct bond_master
*master
= hardware
->h_data
;
621 log_debug("interfaces", "receive PDU from enslaved device %s",
623 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
625 if (fd
== hardware
->h_sendfd
)
626 /* We received this on the physical interface. */
628 /* We received this on the bonding interface. Is it really for us? */
629 if (from
.sll_ifindex
== hardware
->h_ifindex
)
632 if (from
.sll_ifindex
== master
->index
)
633 /* We don't know from which physical interface it comes (kernel
634 * < 2.6.24). In doubt, this is for us. */
636 return -1; /* Not for us */
640 iface_bond_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
642 struct bond_master
*master
= hardware
->h_data
;
643 log_debug("interfaces", "closing enslaved device %s",
645 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
646 interfaces_setup_multicast(cfg
, master
->name
, 1);
647 free(hardware
->h_data
); hardware
->h_data
= NULL
;
651 struct lldpd_ops bond_ops
= {
652 .send
= iflinux_eth_send
,
653 .recv
= iface_bond_recv
,
654 .cleanup
= iface_bond_close
,
658 iflinux_handle_bond(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
660 struct interfaces_device
*iface
;
661 struct interfaces_device
*master
;
662 struct lldpd_hardware
*hardware
;
663 struct bond_master
*bmaster
;
665 TAILQ_FOREACH(iface
, interfaces
, next
) {
666 if (!(iface
->type
& IFACE_PHYSICAL_T
)) continue;
667 if (iface
->ignore
) continue;
668 if (!iface
->upper
|| !(iface
->upper
->type
& IFACE_BOND_T
)) continue;
670 master
= iface
->upper
;
671 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
672 iface
->name
, master
->name
);
674 if ((hardware
= lldpd_get_hardware(cfg
,
676 iface
->index
)) == NULL
) {
677 if ((hardware
= lldpd_alloc_hardware(cfg
,
679 iface
->index
)) == NULL
) {
680 log_warnx("interfaces", "Unable to allocate space for %s",
686 if (hardware
->h_flags
) continue;
687 if (hardware
->h_ops
!= &bond_ops
) {
689 log_debug("interfaces",
690 "bond %s is converted from another type of interface",
692 if (hardware
->h_ops
&& hardware
->h_ops
->cleanup
)
693 hardware
->h_ops
->cleanup(cfg
, hardware
);
694 levent_hardware_release(hardware
);
695 levent_hardware_init(hardware
);
697 bmaster
= hardware
->h_data
= calloc(1, sizeof(struct bond_master
));
699 log_warn("interfaces", "not enough memory");
700 lldpd_hardware_cleanup(cfg
, hardware
);
703 } else bmaster
= hardware
->h_data
;
704 bmaster
->index
= master
->index
;
705 strlcpy(bmaster
->name
, master
->name
, IFNAMSIZ
);
706 if (hardware
->h_ops
!= &bond_ops
) {
707 if (iface_bond_init(cfg
, hardware
) != 0) {
708 log_warn("interfaces", "unable to initialize %s",
710 lldpd_hardware_cleanup(cfg
, hardware
);
713 hardware
->h_ops
= &bond_ops
;
714 hardware
->h_mangle
= 1;
717 interfaces_helper_add_hardware(cfg
, hardware
);
719 lldpd_port_cleanup(&hardware
->h_lport
, 0);
721 hardware
->h_flags
= iface
->flags
;
724 /* Get local address */
725 memcpy(&hardware
->h_lladdr
, iface
->address
, ETHER_ADDR_LEN
);
727 /* Fill information about port */
728 interfaces_helper_port_name_desc(cfg
, hardware
, iface
);
730 /* Fill additional info */
732 hardware
->h_lport
.p_aggregid
= master
->index
;
734 hardware
->h_mtu
= iface
->mtu
? iface
->mtu
: 1500;
738 /* Query each interface to get the appropriate driver */
740 iflinux_add_driver(struct lldpd
*cfg
,
741 struct interfaces_device_list
*interfaces
)
743 struct interfaces_device
*iface
;
744 TAILQ_FOREACH(iface
, interfaces
, next
) {
745 struct ethtool_drvinfo ethc
= {
746 .cmd
= ETHTOOL_GDRVINFO
749 .ifr_data
= (caddr_t
)ðc
751 if (iface
->driver
) continue;
753 strlcpy(ifr
.ifr_name
, iface
->name
, IFNAMSIZ
);
754 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == 0) {
755 iface
->driver
= strdup(ethc
.driver
);
756 log_debug("interfaces", "driver for %s is `%s`",
757 iface
->name
, iface
->driver
);
762 /* Query each interface to see if it is a wireless one */
764 iflinux_add_wireless(struct lldpd
*cfg
,
765 struct interfaces_device_list
*interfaces
)
767 struct interfaces_device
*iface
;
768 TAILQ_FOREACH(iface
, interfaces
, next
) {
769 struct iwreq iwr
= {};
770 strlcpy(iwr
.ifr_name
, iface
->name
, IFNAMSIZ
);
771 if (ioctl(cfg
->g_sock
, SIOCGIWNAME
, &iwr
) >= 0) {
772 log_debug("interfaces", "%s is wireless",
774 iface
->type
|= IFACE_WIRELESS_T
| IFACE_PHYSICAL_T
;
779 /* Query each interface to see if it is a bridge */
781 iflinux_add_bridge(struct lldpd
*cfg
,
782 struct interfaces_device_list
*interfaces
)
784 struct interfaces_device
*iface
;
786 TAILQ_FOREACH(iface
, interfaces
, next
) {
787 if (iface
->type
& (IFACE_PHYSICAL_T
|
788 IFACE_VLAN_T
|IFACE_BOND_T
|IFACE_BRIDGE_T
))
790 if (iflinux_is_bridge(cfg
, interfaces
, iface
)) {
791 log_debug("interfaces",
792 "interface %s is a bridge",
794 iface
->type
|= IFACE_BRIDGE_T
;
799 /* Query each interface to see if it is a bond */
801 iflinux_add_bond(struct lldpd
*cfg
,
802 struct interfaces_device_list
*interfaces
)
804 struct interfaces_device
*iface
;
806 TAILQ_FOREACH(iface
, interfaces
, next
) {
807 if (iface
->type
& (IFACE_PHYSICAL_T
|IFACE_VLAN_T
|
808 IFACE_BOND_T
|IFACE_BRIDGE_T
))
810 if (iflinux_is_bond(cfg
, interfaces
, iface
)) {
811 log_debug("interfaces",
812 "interface %s is a bond",
814 iface
->type
|= IFACE_BOND_T
;
819 /* Query each interface to see if it is a vlan */
821 iflinux_add_vlan(struct lldpd
*cfg
,
822 struct interfaces_device_list
*interfaces
)
824 struct interfaces_device
*iface
;
826 TAILQ_FOREACH(iface
, interfaces
, next
) {
827 if (iface
->type
& (IFACE_PHYSICAL_T
|IFACE_VLAN_T
|
828 IFACE_BOND_T
|IFACE_BRIDGE_T
))
830 if (iflinux_is_vlan(cfg
, interfaces
, iface
)) {
831 log_debug("interfaces",
832 "interface %s is a VLAN",
834 iface
->type
|= IFACE_VLAN_T
;
840 iflinux_add_physical(struct lldpd
*cfg
,
841 struct interfaces_device_list
*interfaces
)
843 struct interfaces_device
*iface
;
844 /* Blacklist some drivers */
845 const char * const *rif
;
846 const char * const blacklisted_drivers
[] = {
852 TAILQ_FOREACH(iface
, interfaces
, next
) {
853 if (iface
->type
& (IFACE_VLAN_T
|
854 IFACE_BOND_T
|IFACE_BRIDGE_T
))
857 iface
->type
&= ~IFACE_PHYSICAL_T
;
859 /* We request that the interface is able to do either multicast
860 * or broadcast to be able to send discovery frames. */
861 if (!(iface
->flags
& (IFF_MULTICAST
|IFF_BROADCAST
))) {
862 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
867 /* Check if the driver is not blacklisted */
870 for (rif
= blacklisted_drivers
; *rif
; rif
++) {
871 if (strcmp(iface
->driver
, *rif
) == 0) {
872 log_debug("interfaces", "skip %s: blacklisted driver",
881 /* If the interface is linked to another one, skip it too. */
882 if (iface
->lower
&& (!iface
->driver
|| strcmp(iface
->driver
, "veth"))) {
883 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
884 iface
->name
, iface
->lower
->name
);
888 /* Get the real MAC address (for example, if the interface is enslaved) */
889 iflinux_get_permanent_mac(cfg
, interfaces
, iface
);
891 log_debug("interfaces",
892 "%s is a physical interface",
894 iface
->type
|= IFACE_PHYSICAL_T
;
899 interfaces_update(struct lldpd
*cfg
)
901 struct lldpd_hardware
*hardware
;
902 struct interfaces_device_list
*interfaces
;
903 struct interfaces_address_list
*addresses
;
904 interfaces
= netlink_get_interfaces(cfg
);
905 addresses
= netlink_get_addresses(cfg
);
906 if (interfaces
== NULL
|| addresses
== NULL
) {
907 log_warnx("interfaces", "cannot update the list of local interfaces");
911 /* Add missing bits to list of interfaces */
912 iflinux_add_driver(cfg
, interfaces
);
913 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_WLAN
)
914 iflinux_add_wireless(cfg
, interfaces
);
915 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_BRIDGE
)
916 iflinux_add_bridge(cfg
, interfaces
);
917 iflinux_add_bond(cfg
, interfaces
);
918 iflinux_add_vlan(cfg
, interfaces
);
919 iflinux_add_physical(cfg
, interfaces
);
921 interfaces_helper_whitelist(cfg
, interfaces
);
922 iflinux_handle_bond(cfg
, interfaces
);
923 interfaces_helper_physical(cfg
, interfaces
,
927 interfaces_helper_vlan(cfg
, interfaces
);
929 interfaces_helper_mgmt(cfg
, addresses
);
930 interfaces_helper_chassis(cfg
, interfaces
);
933 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
934 if (!hardware
->h_flags
) continue;
935 iflinux_macphy(hardware
);
936 interfaces_helper_promisc(cfg
, hardware
);
941 interfaces_cleanup(struct lldpd
*cfg
)
943 netlink_cleanup(cfg
);