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.
24 #include <sys/ioctl.h>
25 #if defined(__clang__)
26 # pragma clang diagnostic push
27 # pragma clang diagnostic ignored "-Wdocumentation"
29 #include <netinet/in.h>
30 #include <linux/if_vlan.h>
31 #include <linux/if_bonding.h>
32 #include <linux/if_bridge.h>
33 #include <linux/wireless.h>
34 #include <linux/sockios.h>
35 #include <linux/if_packet.h>
36 #include <linux/ethtool.h>
37 #if defined(__clang__)
38 # pragma clang diagnostic pop
41 #define SYSFS_PATH_MAX 256
42 #define MAX_PORTS 1024
43 #define MAX_BRIDGES 1024
46 iflinux_eth_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
50 log_debug("interfaces", "initialize ethernet device %s", hardware
->h_ifname
);
51 if ((fd
= priv_iface_init(hardware
->h_ifindex
, hardware
->h_ifname
)) == -1)
53 hardware
->h_sendfd
= fd
; /* Send */
55 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
57 levent_hardware_add_fd(hardware
, fd
); /* Receive */
58 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware
->h_ifname
,
63 /* Generic ethernet send/receive */
65 iflinux_eth_send(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
, char *buffer
,
68 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
69 hardware
->h_ifname
, hardware
->h_sendfd
);
70 return write(hardware
->h_sendfd
, buffer
, size
);
74 iflinux_generic_recv(struct lldpd_hardware
*hardware
, int fd
, char *buffer
, size_t size
,
75 struct sockaddr_ll
*from
)
81 fromlen
= sizeof(*from
);
82 memset(from
, 0, fromlen
);
83 if ((n
= recvfrom(fd
, buffer
, size
, 0, (struct sockaddr
*)from
, &fromlen
)) ==
85 if (errno
== EAGAIN
&& retry
== 0) {
86 /* There may be an error queued in the socket. Clear it and
88 levent_recv_error(fd
, hardware
->h_ifname
);
92 if (errno
== ENETDOWN
) {
93 log_debug("interfaces",
94 "error while receiving frame on %s (network down)",
97 log_warn("interfaces",
98 "error while receiving frame on %s (retry: %d)",
99 hardware
->h_ifname
, retry
);
100 hardware
->h_rx_discarded_cnt
++;
104 if (from
->sll_pkttype
== PACKET_OUTGOING
) return -1;
109 iflinux_eth_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
, int fd
,
110 char *buffer
, size_t size
)
113 struct sockaddr_ll from
;
115 log_debug("interfaces", "receive PDU from ethernet device %s",
117 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
123 iflinux_eth_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
125 log_debug("interfaces", "close ethernet device %s", hardware
->h_ifname
);
126 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
130 static struct lldpd_ops eth_ops
= {
131 .send
= iflinux_eth_send
,
132 .recv
= iflinux_eth_recv
,
133 .cleanup
= iflinux_eth_close
,
137 iflinux_is_bridge(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
,
138 struct interfaces_device
*iface
)
141 struct interfaces_device
*port
;
142 char path
[SYSFS_PATH_MAX
];
145 if ((snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_FDB
,
146 iface
->name
)) >= SYSFS_PATH_MAX
)
147 log_warnx("interfaces", "path truncated");
148 if ((f
= priv_open(path
)) < 0) return 0;
151 /* Also grab all ports */
152 TAILQ_FOREACH (port
, interfaces
, next
) {
153 if (port
->upper
) continue;
154 if (snprintf(path
, SYSFS_PATH_MAX
,
155 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_PORT_SUBDIR
"/%s/port_no",
156 iface
->name
, port
->name
) >= SYSFS_PATH_MAX
)
157 log_warnx("interfaces", "path truncated");
158 if ((f
= priv_open(path
)) < 0) continue;
159 log_debug("interfaces", "port %s is bridged to %s", port
->name
,
172 iflinux_is_vlan(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
,
173 struct interfaces_device
*iface
)
176 struct vlan_ioctl_args ifv
= {};
177 ifv
.cmd
= GET_VLAN_REALDEV_NAME_CMD
;
178 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
179 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) >= 0) {
180 /* This is a VLAN, get the lower interface and the VID */
181 struct interfaces_device
*lower
=
182 interfaces_nametointerface(interfaces
, ifv
.u
.device2
);
184 log_debug("interfaces",
185 "unable to find lower interface for VLAN %s", iface
->name
);
189 memset(&ifv
, 0, sizeof(ifv
));
190 ifv
.cmd
= GET_VLAN_VID_CMD
;
191 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
192 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) < 0) {
193 log_debug("interfaces", "unable to find VID for VLAN %s",
198 iface
->lower
= lower
;
199 bitmap_set(iface
->vlan_bmap
, ifv
.u
.VID
);
207 iflinux_is_bond(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
,
208 struct interfaces_device
*master
)
211 /* Shortcut if we detect the new team driver. Upper and lower links
212 * should already be set with netlink in this case. */
213 if (master
->driver
&& !strcmp(master
->driver
, "team")) {
217 struct ifreq ifr
= {};
218 struct ifbond ifb
= {};
219 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
220 ifr
.ifr_data
= (char *)&ifb
;
221 if (ioctl(cfg
->g_sock
, SIOCBONDINFOQUERY
, &ifr
) >= 0) {
222 while (ifb
.num_slaves
--) {
224 memset(&ifr
, 0, sizeof(ifr
));
225 memset(&ifs
, 0, sizeof(ifs
));
226 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
227 ifr
.ifr_data
= (char *)&ifs
;
228 ifs
.slave_id
= ifb
.num_slaves
;
229 if (ioctl(cfg
->g_sock
, SIOCBONDSLAVEINFOQUERY
, &ifr
) >= 0) {
230 struct interfaces_device
*slave
=
231 interfaces_nametointerface(interfaces
,
233 if (slave
== NULL
) continue;
234 if (slave
->upper
) continue;
235 log_debug("interfaces",
236 "interface %s is enslaved to %s", slave
->name
,
238 slave
->upper
= master
;
248 * Get permanent MAC from ethtool.
250 * Return 0 on success, -1 on error.
253 iflinux_get_permanent_mac_ethtool(struct lldpd
*cfg
,
254 struct interfaces_device_list
*interfaces
, struct interfaces_device
*iface
)
257 struct ifreq ifr
= {};
258 struct ethtool_perm_addr
*epaddr
=
259 calloc(1, sizeof(struct ethtool_perm_addr
) + ETHER_ADDR_LEN
);
260 if (epaddr
== NULL
) goto end
;
262 strlcpy(ifr
.ifr_name
, iface
->name
, sizeof(ifr
.ifr_name
));
263 epaddr
->cmd
= ETHTOOL_GPERMADDR
;
264 epaddr
->size
= ETHER_ADDR_LEN
;
265 ifr
.ifr_data
= (caddr_t
)epaddr
;
266 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == -1) {
268 if (errno
== EPERM
&& !once
) {
269 log_warnx("interfaces",
270 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
276 log_warn("interfaces",
277 "cannot get permanent MAC address for %s", iface
->name
);
280 if (epaddr
->data
[0] != 0 || epaddr
->data
[1] != 0 || epaddr
->data
[2] != 0 ||
281 epaddr
->data
[3] != 0 || epaddr
->data
[4] != 0 || epaddr
->data
[5] != 0) {
282 memcpy(iface
->address
, epaddr
->data
, ETHER_ADDR_LEN
);
286 log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface
->name
);
293 * Get permanent MAC address for a bond device.
296 iflinux_get_permanent_mac_bond(struct lldpd
*cfg
,
297 struct interfaces_device_list
*interfaces
, struct interfaces_device
*iface
)
299 struct interfaces_device
*master
= iface
->upper
;
302 const char *slaveif
= "Slave Interface: ";
303 const char *hwaddr
= "Permanent HW addr: ";
304 u_int8_t mac
[ETHER_ADDR_LEN
];
305 char path
[SYSFS_PATH_MAX
];
308 /* We have a bond, we need to query it to get real MAC addresses */
309 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/net/bonding/%s", master
->name
) >=
311 log_warnx("interfaces", "path truncated");
314 if ((f
= priv_open(path
)) < 0) {
315 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/self/net/bonding/%s",
316 master
->name
) >= SYSFS_PATH_MAX
) {
317 log_warnx("interfaces", "path truncated");
323 log_warnx("interfaces", "unable to get permanent MAC address for %s",
327 if ((netbond
= fdopen(f
, "r")) == NULL
) {
328 log_warn("interfaces", "unable to read stream from %s", path
);
333 We parse the file to search "Slave Interface: ". If found, go to
336 We parse the file to search "Permanent HW addr: ". If found, we get
339 while (fgets(line
, sizeof(line
), netbond
)) {
342 if (strncmp(line
, slaveif
, strlen(slaveif
)) == 0) {
343 if (line
[strlen(line
) - 1] == '\n')
344 line
[strlen(line
) - 1] = '\0';
345 if (strcmp(iface
->name
, line
+ strlen(slaveif
)) == 0)
350 if (strncmp(line
, hwaddr
, strlen(hwaddr
)) == 0) {
351 if (line
[strlen(line
) - 1] == '\n')
352 line
[strlen(line
) - 1] = '\0';
353 if (sscanf(line
+ strlen(hwaddr
),
354 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
355 &mac
[0], &mac
[1], &mac
[2], &mac
[3], &mac
[4],
356 &mac
[5]) != ETHER_ADDR_LEN
) {
357 log_warn("interfaces", "unable to parse %s",
358 line
+ strlen(hwaddr
));
362 memcpy(iface
->address
, mac
, ETHER_ADDR_LEN
);
369 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
378 iflinux_get_permanent_mac(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
,
379 struct interfaces_device
*iface
)
381 struct interfaces_device
*master
= iface
->upper
;
383 if (master
== NULL
|| master
->type
!= IFACE_BOND_T
) return;
384 if (iflinux_get_permanent_mac_ethtool(cfg
, interfaces
, iface
) == -1 &&
385 (master
->driver
== NULL
|| !strcmp(master
->driver
, "bonding")))
386 /* Fallback to old method for a bond */
387 iflinux_get_permanent_mac_bond(cfg
, interfaces
, iface
);
391 # define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
392 # define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
393 uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
395 struct ethtool_link_usettings
{
396 struct ethtool_link_settings base
;
398 ETHTOOL_DECLARE_LINK_MODE_MASK(supported
);
399 ETHTOOL_DECLARE_LINK_MODE_MASK(advertising
);
400 ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising
);
405 iflinux_ethtool_link_mode_test_bit(unsigned int nr
, const uint32_t *mask
)
407 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
) return 0;
408 return !!(mask
[nr
/ 32] & (1 << (nr
% 32)));
411 iflinux_ethtool_link_mode_unset_bit(unsigned int nr
, uint32_t *mask
)
413 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
) return;
414 mask
[nr
/ 32] &= ~(1 << (nr
% 32));
417 iflinux_ethtool_link_mode_is_empty(const uint32_t *mask
)
419 for (unsigned int i
= 0; i
< ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
; ++i
) {
420 if (mask
[i
] != 0) return 0;
427 iflinux_ethtool_glink(struct lldpd
*cfg
, const char *ifname
,
428 struct ethtool_link_usettings
*uset
)
432 /* Try with ETHTOOL_GLINKSETTINGS first */
434 struct ethtool_link_settings req
;
435 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
437 static int8_t nwords
= 0;
438 struct ifreq ifr
= {};
439 strlcpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
));
442 /* Do a handshake first. We assume that this is device-independant. */
443 memset(&ecmd
, 0, sizeof(ecmd
));
444 ecmd
.req
.cmd
= ETHTOOL_GLINKSETTINGS
;
445 ifr
.ifr_data
= (caddr_t
)&ecmd
;
446 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
448 nwords
= -ecmd
.req
.link_mode_masks_nwords
;
449 log_debug("interfaces", "glinksettings nwords is %" PRId8
,
453 if (errno
== EPERM
&& !once
) {
454 log_warnx("interfaces",
455 "cannot get ethtool link information "
456 "with GLINKSETTINGS (requires 4.9+). "
457 "25G+ speeds may be missing in MAC/PHY TLVs");
464 memset(&ecmd
, 0, sizeof(ecmd
));
465 ecmd
.req
.cmd
= ETHTOOL_GLINKSETTINGS
;
466 ecmd
.req
.link_mode_masks_nwords
= nwords
;
467 ifr
.ifr_data
= (caddr_t
)&ecmd
;
468 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
470 log_debug("interfaces",
471 "got ethtool results for %s with GLINKSETTINGS", ifname
);
472 memcpy(&uset
->base
, &ecmd
.req
, sizeof(uset
->base
));
473 unsigned int u32_offs
= 0;
474 memcpy(uset
->link_modes
.supported
,
475 &ecmd
.link_mode_data
[u32_offs
],
476 4 * ecmd
.req
.link_mode_masks_nwords
);
477 u32_offs
+= ecmd
.req
.link_mode_masks_nwords
;
478 memcpy(uset
->link_modes
.advertising
,
479 &ecmd
.link_mode_data
[u32_offs
],
480 4 * ecmd
.req
.link_mode_masks_nwords
);
481 u32_offs
+= ecmd
.req
.link_mode_masks_nwords
;
482 memcpy(uset
->link_modes
.lp_advertising
,
483 &ecmd
.link_mode_data
[u32_offs
],
484 4 * ecmd
.req
.link_mode_masks_nwords
);
489 /* Try with ETHTOOL_GSET */
490 struct ethtool_cmd ethc
;
491 memset(ðc
, 0, sizeof(ethc
));
492 ethc
.cmd
= ETHTOOL_GSET
;
493 ifr
.ifr_data
= (caddr_t
)ðc
;
494 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
496 /* Do a partial copy (only what we need) */
497 log_debug("interfaces", "got ethtool results for %s with GSET", ifname
);
498 memset(uset
, 0, sizeof(*uset
));
499 uset
->base
.cmd
= ETHTOOL_GSET
;
500 uset
->base
.link_mode_masks_nwords
= 1;
501 uset
->link_modes
.supported
[0] = ethc
.supported
;
502 uset
->link_modes
.advertising
[0] = ethc
.advertising
;
503 uset
->link_modes
.lp_advertising
[0] = ethc
.lp_advertising
;
504 uset
->base
.speed
= (ethc
.speed_hi
<< 16) | ethc
.speed
;
505 uset
->base
.duplex
= ethc
.duplex
;
506 uset
->base
.port
= ethc
.port
;
507 uset
->base
.autoneg
= ethc
.autoneg
;
510 if (errno
== EPERM
&& !once
) {
511 log_warnx("interfaces",
512 "cannot get ethtool link information "
513 "with GSET (requires 2.6.19+). "
514 "MAC/PHY TLV will be unavailable");
522 /* Fill up MAC/PHY for a given hardware port */
524 iflinux_macphy(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
526 struct ethtool_link_usettings uset
= {};
527 struct lldpd_port
*port
= &hardware
->h_lport
;
529 int advertised_ethtool_to_rfc3636
[][2] = {
530 { ETHTOOL_LINK_MODE_10baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_10BASE_T
},
531 { ETHTOOL_LINK_MODE_10baseT_Full_BIT
,
532 LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
533 { ETHTOOL_LINK_MODE_100baseT_Half_BIT
,
534 LLDP_DOT3_LINK_AUTONEG_100BASE_TX
},
535 { ETHTOOL_LINK_MODE_100baseT_Full_BIT
,
536 LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD
},
537 { ETHTOOL_LINK_MODE_1000baseT_Half_BIT
,
538 LLDP_DOT3_LINK_AUTONEG_1000BASE_T
},
539 { ETHTOOL_LINK_MODE_1000baseT_Full_BIT
,
540 LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD
},
541 { ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
,
542 LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
543 { ETHTOOL_LINK_MODE_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE
},
544 { ETHTOOL_LINK_MODE_Asym_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE
},
548 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
550 if (iflinux_ethtool_glink(cfg
, hardware
->h_ifname
, &uset
) == 0) {
551 port
->p_macphy
.autoneg_support
=
552 iflinux_ethtool_link_mode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
,
553 uset
.link_modes
.supported
);
554 port
->p_macphy
.autoneg_enabled
=
555 (uset
.base
.autoneg
== AUTONEG_DISABLE
) ? 0 : 1;
556 for (j
= 0; advertised_ethtool_to_rfc3636
[j
][0] >= 0; j
++) {
557 if (iflinux_ethtool_link_mode_test_bit(
558 advertised_ethtool_to_rfc3636
[j
][0],
559 uset
.link_modes
.advertising
)) {
560 port
->p_macphy
.autoneg_advertised
|=
561 advertised_ethtool_to_rfc3636
[j
][1];
562 iflinux_ethtool_link_mode_unset_bit(
563 advertised_ethtool_to_rfc3636
[j
][0],
564 uset
.link_modes
.advertising
);
567 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
,
568 uset
.link_modes
.advertising
);
569 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT
,
570 uset
.link_modes
.advertising
);
571 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT
,
572 uset
.link_modes
.advertising
);
573 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT
,
574 uset
.link_modes
.advertising
);
575 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
,
576 uset
.link_modes
.advertising
);
577 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT
,
578 uset
.link_modes
.advertising
);
579 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT
,
580 uset
.link_modes
.advertising
);
581 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT
,
582 uset
.link_modes
.advertising
);
583 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT
,
584 uset
.link_modes
.advertising
);
585 if (!iflinux_ethtool_link_mode_is_empty(uset
.link_modes
.advertising
)) {
586 port
->p_macphy
.autoneg_advertised
|=
587 LLDP_DOT3_LINK_AUTONEG_OTHER
;
589 switch (uset
.base
.speed
) {
591 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ?
592 LLDP_DOT3_MAU_10BASETFD
:
593 LLDP_DOT3_MAU_10BASETHD
;
594 if (uset
.base
.port
== PORT_BNC
)
595 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10BASE2
;
596 if (uset
.base
.port
== PORT_FIBRE
)
597 port
->p_macphy
.mau_type
=
598 (uset
.base
.duplex
== DUPLEX_FULL
) ?
599 LLDP_DOT3_MAU_10BASEFLFD
:
600 LLDP_DOT3_MAU_10BASEFLHD
;
603 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ?
604 LLDP_DOT3_MAU_100BASETXFD
:
605 LLDP_DOT3_MAU_100BASETXHD
;
606 if (uset
.base
.port
== PORT_BNC
)
607 port
->p_macphy
.mau_type
=
608 (uset
.base
.duplex
== DUPLEX_FULL
) ?
609 LLDP_DOT3_MAU_100BASET2FD
:
610 LLDP_DOT3_MAU_100BASET2HD
;
611 if (uset
.base
.port
== PORT_FIBRE
)
612 port
->p_macphy
.mau_type
=
613 (uset
.base
.duplex
== DUPLEX_FULL
) ?
614 LLDP_DOT3_MAU_100BASEFXFD
:
615 LLDP_DOT3_MAU_100BASEFXHD
;
618 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ?
619 LLDP_DOT3_MAU_1000BASETFD
:
620 LLDP_DOT3_MAU_1000BASETHD
;
621 if (uset
.base
.port
== PORT_FIBRE
)
622 port
->p_macphy
.mau_type
=
623 (uset
.base
.duplex
== DUPLEX_FULL
) ?
624 LLDP_DOT3_MAU_1000BASEXFD
:
625 LLDP_DOT3_MAU_1000BASEXHD
;
628 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_2P5GIGT
;
631 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_5GIGT
;
634 // Distinguish between RJ45 BaseT, DAC BaseCX4, or Fibre BaseLR
635 if (uset
.base
.port
== PORT_TP
) {
636 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10GBASET
;
637 } else if (uset
.base
.port
== PORT_FIBRE
) {
638 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10GIGBASELR
;
639 } else if (uset
.base
.port
== PORT_DA
) {
640 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10GIGBASECX4
;
644 // Distinguish between RJ45 BaseT, DAC BaseCR, or Fibre BaseLR
645 if (uset
.base
.port
== PORT_TP
) {
646 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_25GBASET
;
647 } else if (uset
.base
.port
== PORT_FIBRE
) {
648 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_25GBASELR
;
649 } else if (uset
.base
.port
== PORT_DA
) {
650 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_25GBASECR
;
654 // Same kind of approximation.
655 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ?
656 LLDP_DOT3_MAU_40GBASELR4
:
657 LLDP_DOT3_MAU_40GBASECR4
;
660 // Same kind of approximation.
661 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ?
662 LLDP_DOT3_MAU_50GBASELR
:
663 LLDP_DOT3_MAU_50GBASECR
;
667 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ?
668 LLDP_DOT3_MAU_100GBASELR4
:
669 LLDP_DOT3_MAU_100GBASECR4
;
672 if (uset
.base
.port
== PORT_AUI
)
673 port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_AUI
;
676 #else /* ENABLE_DOT3 */
678 iflinux_macphy(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
681 #endif /* ENABLE_DOT3 */
690 iface_bond_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
692 struct bond_master
*master
= hardware
->h_data
;
696 if (!master
) return -1;
698 log_debug("interfaces", "initialize enslaved device %s", hardware
->h_ifname
);
700 /* First, we get a socket to the raw physical interface */
701 if ((fd
= priv_iface_init(hardware
->h_ifindex
, hardware
->h_ifname
)) == -1)
703 hardware
->h_sendfd
= fd
;
704 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
706 /* Then, we open a raw interface for the master */
707 log_debug("interfaces", "enslaved device %s has master %s(%d)",
708 hardware
->h_ifname
, master
->name
, master
->index
);
709 if ((fd
= priv_iface_init(master
->index
, master
->name
)) == -1) {
710 close(hardware
->h_sendfd
);
713 /* With bonding and older kernels (< 2.6.27) we need to listen
714 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
715 * physical device instead of bond device (works with >=
717 if (setsockopt(fd
, SOL_PACKET
, PACKET_ORIGDEV
, &un
, sizeof(un
)) == -1) {
718 log_info("interfaces",
719 "unable to setsockopt for master bonding device of %s. "
720 "You will get inaccurate results",
723 interfaces_setup_multicast(cfg
, master
->name
, 0);
725 levent_hardware_add_fd(hardware
, hardware
->h_sendfd
);
726 levent_hardware_add_fd(hardware
, fd
);
727 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
728 hardware
->h_ifname
, hardware
->h_sendfd
, master
->name
, fd
);
733 iface_bond_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
, int fd
,
734 char *buffer
, size_t size
)
737 struct sockaddr_ll from
;
738 struct bond_master
*master
= hardware
->h_data
;
740 log_debug("interfaces", "receive PDU from enslaved device %s",
742 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
744 if (fd
== hardware
->h_sendfd
) /* We received this on the physical interface. */
746 /* We received this on the bonding interface. Is it really for us? */
747 if (from
.sll_ifindex
== hardware
->h_ifindex
) /* This is for us */
749 if (from
.sll_ifindex
== master
->index
)
750 /* We don't know from which physical interface it comes (kernel
751 * < 2.6.24). In doubt, this is for us. */
753 return -1; /* Not for us */
757 iface_bond_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
759 struct bond_master
*master
= hardware
->h_data
;
760 log_debug("interfaces", "closing enslaved device %s", hardware
->h_ifname
);
761 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
762 interfaces_setup_multicast(cfg
, master
->name
, 1);
763 free(hardware
->h_data
);
764 hardware
->h_data
= NULL
;
768 struct lldpd_ops bond_ops
= {
769 .send
= iflinux_eth_send
,
770 .recv
= iface_bond_recv
,
771 .cleanup
= iface_bond_close
,
775 iflinux_handle_bond(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
777 struct interfaces_device
*iface
;
778 struct interfaces_device
*master
;
779 struct lldpd_hardware
*hardware
;
780 struct bond_master
*bmaster
;
782 TAILQ_FOREACH (iface
, interfaces
, next
) {
783 if (!(iface
->type
& IFACE_PHYSICAL_T
)) continue;
784 if (iface
->ignore
) continue;
785 if (!iface
->upper
|| !(iface
->upper
->type
& IFACE_BOND_T
)) continue;
787 master
= iface
->upper
;
788 log_debug("interfaces",
789 "%s is an acceptable enslaved device (master=%s)", iface
->name
,
792 if ((hardware
= lldpd_get_hardware(cfg
, iface
->name
, iface
->index
)) ==
794 if ((hardware
= lldpd_alloc_hardware(cfg
, iface
->name
,
795 iface
->index
)) == NULL
) {
796 log_warnx("interfaces",
797 "Unable to allocate space for %s", iface
->name
);
802 if (hardware
->h_flags
) continue;
803 if (hardware
->h_ops
!= &bond_ops
|| hardware
->h_ifindex_changed
) {
805 log_debug("interfaces",
806 "bond %s is converted from another type of interface",
808 if (hardware
->h_ops
&& hardware
->h_ops
->cleanup
)
809 hardware
->h_ops
->cleanup(cfg
, hardware
);
810 levent_hardware_release(hardware
);
811 levent_hardware_init(hardware
);
813 bmaster
= hardware
->h_data
=
814 calloc(1, sizeof(struct bond_master
));
816 log_warn("interfaces", "not enough memory");
817 lldpd_hardware_cleanup(cfg
, hardware
);
821 bmaster
= hardware
->h_data
;
822 bmaster
->index
= master
->index
;
823 strlcpy(bmaster
->name
, master
->name
, IFNAMSIZ
);
824 if (hardware
->h_ops
!= &bond_ops
|| hardware
->h_ifindex_changed
) {
825 if (iface_bond_init(cfg
, hardware
) != 0) {
826 log_warn("interfaces", "unable to initialize %s",
828 lldpd_hardware_cleanup(cfg
, hardware
);
831 hardware
->h_ops
= &bond_ops
;
832 hardware
->h_mangle
= 1;
835 interfaces_helper_add_hardware(cfg
, hardware
);
837 lldpd_port_cleanup(&hardware
->h_lport
, 0);
839 hardware
->h_flags
= iface
->flags
;
842 /* Get local address */
843 memcpy(&hardware
->h_lladdr
, iface
->address
, ETHER_ADDR_LEN
);
845 /* Fill information about port */
846 interfaces_helper_port_name_desc(cfg
, hardware
, iface
);
848 /* Fill additional info */
850 hardware
->h_lport
.p_aggregid
= master
->index
;
852 hardware
->h_mtu
= iface
->mtu
? iface
->mtu
: 1500;
857 /* Query each interface to get the appropriate driver */
859 iflinux_add_driver(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
861 struct interfaces_device
*iface
;
862 TAILQ_FOREACH (iface
, interfaces
, next
) {
863 struct ethtool_drvinfo ethc
= { .cmd
= ETHTOOL_GDRVINFO
};
864 struct ifreq ifr
= { .ifr_data
= (caddr_t
)ðc
};
865 if (iface
->driver
) continue;
867 strlcpy(ifr
.ifr_name
, iface
->name
, IFNAMSIZ
);
868 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == 0) {
869 iface
->driver
= strdup(ethc
.driver
);
870 log_debug("interfaces", "driver for %s is `%s`", iface
->name
,
876 /* Query each interface to see if it is a wireless one */
878 iflinux_add_wireless(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
881 struct interfaces_device
*iface
;
882 TAILQ_FOREACH (iface
, interfaces
, next
) {
884 (IFACE_VLAN_T
| IFACE_BOND_T
| IFACE_BRIDGE_T
| IFACE_WIRELESS_T
))
886 struct iwreq iwr
= {};
887 strlcpy(iwr
.ifr_name
, iface
->name
, IFNAMSIZ
);
888 if (ioctl(cfg
->g_sock
, SIOCGIWNAME
, &iwr
) >= 0) {
889 log_debug("interfaces", "%s is wireless", iface
->name
);
890 iface
->type
|= IFACE_WIRELESS_T
| IFACE_PHYSICAL_T
;
896 /* Query each interface to see if it is a bridge */
898 iflinux_add_bridge(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
900 struct interfaces_device
*iface
;
902 TAILQ_FOREACH (iface
, interfaces
, next
) {
904 (IFACE_PHYSICAL_T
| IFACE_VLAN_T
| IFACE_BOND_T
| IFACE_BRIDGE_T
))
906 if (iflinux_is_bridge(cfg
, interfaces
, iface
)) {
907 log_debug("interfaces", "interface %s is a bridge",
909 iface
->type
|= IFACE_BRIDGE_T
;
914 /* Query each interface to see if it is a bond */
916 iflinux_add_bond(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
918 struct interfaces_device
*iface
;
920 TAILQ_FOREACH (iface
, interfaces
, next
) {
922 (IFACE_PHYSICAL_T
| IFACE_VLAN_T
| IFACE_BOND_T
| IFACE_BRIDGE_T
))
924 if (iflinux_is_bond(cfg
, interfaces
, iface
)) {
925 log_debug("interfaces", "interface %s is a bond", iface
->name
);
926 iface
->type
|= IFACE_BOND_T
;
931 /* Query each interface to see if it is a vlan */
933 iflinux_add_vlan(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
935 struct interfaces_device
*iface
;
937 TAILQ_FOREACH (iface
, interfaces
, next
) {
939 (IFACE_PHYSICAL_T
| IFACE_VLAN_T
| IFACE_BOND_T
| IFACE_BRIDGE_T
))
941 if (iflinux_is_vlan(cfg
, interfaces
, iface
)) {
942 log_debug("interfaces", "interface %s is a VLAN", iface
->name
);
943 iface
->type
|= IFACE_VLAN_T
;
949 iflinux_add_physical(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
951 struct interfaces_device
*iface
;
952 /* Deny some drivers */
953 const char *const *rif
;
954 const char *const denied_drivers
[] = { "cdc_mbim", "vxlan", NULL
};
956 TAILQ_FOREACH (iface
, interfaces
, next
) {
957 if (iface
->type
& (IFACE_VLAN_T
| IFACE_BOND_T
| IFACE_BRIDGE_T
))
960 iface
->type
&= ~IFACE_PHYSICAL_T
;
962 /* We request that the interface is able to do either multicast
963 * or broadcast to be able to send discovery frames. */
964 if (!(iface
->flags
& (IFF_MULTICAST
| IFF_BROADCAST
))) {
965 log_debug("interfaces",
966 "skip %s: not able to do multicast nor broadcast",
971 /* Check if the driver is not denied */
974 for (rif
= denied_drivers
; *rif
; rif
++) {
975 if (strcmp(iface
->driver
, *rif
) == 0) {
976 log_debug("interfaces",
977 "skip %s: denied driver", iface
->name
);
985 /* If the interface is linked to another one, skip it too. */
988 (strcmp(iface
->driver
, "veth") &&
989 strcmp(iface
->driver
, "dsa")))) {
990 log_debug("interfaces",
991 "skip %s: there is a lower interface (%s)", iface
->name
,
996 /* Get the real MAC address (for example, if the interface is enslaved)
998 iflinux_get_permanent_mac(cfg
, interfaces
, iface
);
1000 log_debug("interfaces", "%s is a physical interface", iface
->name
);
1001 iface
->type
|= IFACE_PHYSICAL_T
;
1006 interfaces_update(struct lldpd
*cfg
)
1008 struct lldpd_hardware
*hardware
;
1009 struct interfaces_device_list
*interfaces
;
1010 struct interfaces_address_list
*addresses
;
1011 interfaces
= netlink_get_interfaces(cfg
);
1012 addresses
= netlink_get_addresses(cfg
);
1013 if (interfaces
== NULL
|| addresses
== NULL
) {
1014 log_warnx("interfaces", "cannot update the list of local interfaces");
1018 /* Add missing bits to list of interfaces */
1019 iflinux_add_driver(cfg
, interfaces
);
1020 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_WLAN
)
1021 iflinux_add_wireless(cfg
, interfaces
);
1022 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_BRIDGE
)
1023 iflinux_add_bridge(cfg
, interfaces
);
1024 iflinux_add_bond(cfg
, interfaces
);
1025 iflinux_add_vlan(cfg
, interfaces
);
1026 iflinux_add_physical(cfg
, interfaces
);
1028 interfaces_helper_allowlist(cfg
, interfaces
);
1029 #ifdef ENABLE_OLDIES
1030 iflinux_handle_bond(cfg
, interfaces
);
1032 interfaces_helper_physical(cfg
, interfaces
, ð_ops
, iflinux_eth_init
);
1034 interfaces_helper_vlan(cfg
, interfaces
);
1036 interfaces_helper_mgmt(cfg
, addresses
, interfaces
);
1037 interfaces_helper_chassis(cfg
, interfaces
);
1040 TAILQ_FOREACH (hardware
, &cfg
->g_hardware
, h_entries
) {
1041 if (!(hardware
->h_flags
& IFF_RUNNING
)) continue;
1042 iflinux_macphy(cfg
, hardware
);
1043 interfaces_helper_promisc(cfg
, hardware
);
1048 interfaces_cleanup(struct lldpd
*cfg
)
1050 netlink_cleanup(cfg
);