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 <linux/if_vlan.h>
30 #include <linux/if_bonding.h>
31 #include <linux/if_bridge.h>
32 #include <linux/wireless.h>
33 #include <linux/sockios.h>
34 #include <linux/if_packet.h>
35 #include <linux/ethtool.h>
36 #if defined(__clang__)
37 #pragma clang diagnostic pop
40 #define SYSFS_PATH_MAX 256
41 #define MAX_PORTS 1024
42 #define MAX_BRIDGES 1024
45 only_lldp(struct lldpd
*cfg
)
48 int other_enabled
= 0;
50 for (i
=0; cfg
->g_protocols
[i
].mode
!= 0; i
++) {
51 if (cfg
->g_protocols
[i
].mode
== LLDPD_MODE_LLDP
)
52 lldp_enabled
= cfg
->g_protocols
[i
].enabled
;
53 else other_enabled
= other_enabled
|| cfg
->g_protocols
[i
].enabled
;
55 return lldp_enabled
&& !other_enabled
;
60 iflinux_eth_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
64 log_debug("interfaces", "initialize ethernet device %s",
66 if ((fd
= priv_iface_init(hardware
->h_ifindex
, hardware
->h_ifname
,
67 only_lldp(cfg
)?ETH_P_LLDP
:ETH_P_ALL
)) == -1)
69 hardware
->h_sendfd
= fd
; /* Send */
71 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
73 levent_hardware_add_fd(hardware
, fd
); /* Receive */
74 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware
->h_ifname
,
79 /* Generic ethernet send/receive */
81 iflinux_eth_send(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
82 char *buffer
, size_t size
)
84 log_debug("interfaces", "send PDU to ethernet device %s (fd=%d)",
85 hardware
->h_ifname
, hardware
->h_sendfd
);
86 return write(hardware
->h_sendfd
,
91 iflinux_generic_recv(struct lldpd_hardware
*hardware
,
92 int fd
, char *buffer
, size_t size
,
93 struct sockaddr_ll
*from
)
99 fromlen
= sizeof(*from
);
100 memset(from
, 0, fromlen
);
101 if ((n
= recvfrom(fd
, buffer
, size
, 0,
102 (struct sockaddr
*)from
,
104 if (errno
== EAGAIN
&& retry
== 0) {
105 /* There may be an error queued in the socket. Clear it and retry. */
106 levent_recv_error(fd
, hardware
->h_ifname
);
110 if (errno
== ENETDOWN
) {
111 log_debug("interfaces", "error while receiving frame on %s (network down)",
114 log_warn("interfaces", "error while receiving frame on %s (retry: %d)",
115 hardware
->h_ifname
, retry
);
116 hardware
->h_rx_discarded_cnt
++;
120 if (from
->sll_pkttype
== PACKET_OUTGOING
)
126 iflinux_eth_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
127 int fd
, char *buffer
, size_t size
)
130 struct sockaddr_ll from
;
132 log_debug("interfaces", "receive PDU from ethernet device %s",
134 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
140 iflinux_eth_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
142 log_debug("interfaces", "close ethernet device %s",
144 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
148 static struct lldpd_ops eth_ops
= {
149 .send
= iflinux_eth_send
,
150 .recv
= iflinux_eth_recv
,
151 .cleanup
= iflinux_eth_close
,
155 iflinux_is_bridge(struct lldpd
*cfg
,
156 struct interfaces_device_list
*interfaces
,
157 struct interfaces_device
*iface
)
160 struct interfaces_device
*port
;
161 char path
[SYSFS_PATH_MAX
];
164 if ((snprintf(path
, SYSFS_PATH_MAX
,
165 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_FDB
,
166 iface
->name
)) >= SYSFS_PATH_MAX
)
167 log_warnx("interfaces", "path truncated");
168 if ((f
= priv_open(path
)) < 0)
172 /* Also grab all ports */
173 TAILQ_FOREACH(port
, interfaces
, next
) {
174 if (port
->upper
) continue;
175 if (snprintf(path
, SYSFS_PATH_MAX
,
176 SYSFS_CLASS_NET
"%s/" SYSFS_BRIDGE_PORT_SUBDIR
"/%s/port_no",
177 iface
->name
, port
->name
) >= SYSFS_PATH_MAX
)
178 log_warnx("interfaces", "path truncated");
179 if ((f
= priv_open(path
)) < 0)
181 log_debug("interfaces",
182 "port %s is bridged to %s",
183 port
->name
, iface
->name
);
195 iflinux_is_vlan(struct lldpd
*cfg
,
196 struct interfaces_device_list
*interfaces
,
197 struct interfaces_device
*iface
)
200 struct vlan_ioctl_args ifv
= {};
201 ifv
.cmd
= GET_VLAN_REALDEV_NAME_CMD
;
202 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
203 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) >= 0) {
204 /* This is a VLAN, get the lower interface and the VID */
205 struct interfaces_device
*lower
=
206 interfaces_nametointerface(interfaces
, ifv
.u
.device2
);
208 log_debug("interfaces",
209 "unable to find lower interface for VLAN %s",
214 memset(&ifv
, 0, sizeof(ifv
));
215 ifv
.cmd
= GET_VLAN_VID_CMD
;
216 strlcpy(ifv
.device1
, iface
->name
, sizeof(ifv
.device1
));
217 if (ioctl(cfg
->g_sock
, SIOCGIFVLAN
, &ifv
) < 0) {
218 log_debug("interfaces",
219 "unable to find VID for VLAN %s",
224 iface
->lower
= lower
;
225 bitmap_set(iface
->vlan_bmap
, ifv
.u
.VID
);
233 iflinux_is_bond(struct lldpd
*cfg
,
234 struct interfaces_device_list
*interfaces
,
235 struct interfaces_device
*master
)
238 /* Shortcut if we detect the new team driver. Upper and lower links
239 * should already be set with netlink in this case. */
240 if (master
->driver
&& !strcmp(master
->driver
, "team")) {
244 struct ifreq ifr
= {};
245 struct ifbond ifb
= {};
246 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
247 ifr
.ifr_data
= (char *)&ifb
;
248 if (ioctl(cfg
->g_sock
, SIOCBONDINFOQUERY
, &ifr
) >= 0) {
249 while (ifb
.num_slaves
--) {
251 memset(&ifr
, 0, sizeof(ifr
));
252 memset(&ifs
, 0, sizeof(ifs
));
253 strlcpy(ifr
.ifr_name
, master
->name
, sizeof(ifr
.ifr_name
));
254 ifr
.ifr_data
= (char *)&ifs
;
255 ifs
.slave_id
= ifb
.num_slaves
;
256 if (ioctl(cfg
->g_sock
, SIOCBONDSLAVEINFOQUERY
, &ifr
) >= 0) {
257 struct interfaces_device
*slave
=
258 interfaces_nametointerface(interfaces
,
260 if (slave
== NULL
) continue;
261 if (slave
->upper
) continue;
262 log_debug("interfaces",
263 "interface %s is enslaved to %s",
264 slave
->name
, master
->name
);
265 slave
->upper
= master
;
275 * Get permanent MAC from ethtool.
277 * Return 0 on success, -1 on error.
280 iflinux_get_permanent_mac_ethtool(struct lldpd
*cfg
,
281 struct interfaces_device_list
*interfaces
,
282 struct interfaces_device
*iface
)
285 struct ifreq ifr
= {};
286 struct ethtool_perm_addr
*epaddr
= calloc(sizeof(struct ethtool_perm_addr
) + ETHER_ADDR_LEN
, 1);
287 if (epaddr
== NULL
) goto end
;
289 strlcpy(ifr
.ifr_name
, iface
->name
, sizeof(ifr
.ifr_name
));
290 epaddr
->cmd
= ETHTOOL_GPERMADDR
;
291 epaddr
->size
= ETHER_ADDR_LEN
;
292 ifr
.ifr_data
= (caddr_t
)epaddr
;
293 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == -1) {
295 if (errno
== EPERM
&& !once
) {
296 log_warnx("interfaces",
297 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
303 log_warn("interfaces", "cannot get permanent MAC address for %s",
307 if (epaddr
->data
[0] != 0 ||
308 epaddr
->data
[1] != 0 ||
309 epaddr
->data
[2] != 0 ||
310 epaddr
->data
[3] != 0 ||
311 epaddr
->data
[4] != 0 ||
312 epaddr
->data
[5] != 0) {
313 memcpy(iface
->address
, epaddr
->data
, ETHER_ADDR_LEN
);
317 log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface
->name
);
324 * Get permanent MAC address for a bond device.
327 iflinux_get_permanent_mac_bond(struct lldpd
*cfg
,
328 struct interfaces_device_list
*interfaces
,
329 struct interfaces_device
*iface
)
331 struct interfaces_device
*master
= iface
->upper
;
334 const char *slaveif
= "Slave Interface: ";
335 const char *hwaddr
= "Permanent HW addr: ";
336 u_int8_t mac
[ETHER_ADDR_LEN
];
337 char path
[SYSFS_PATH_MAX
];
340 /* We have a bond, we need to query it to get real MAC addresses */
341 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/net/bonding/%s",
342 master
->name
) >= SYSFS_PATH_MAX
) {
343 log_warnx("interfaces", "path truncated");
346 if ((f
= priv_open(path
)) < 0) {
347 if (snprintf(path
, SYSFS_PATH_MAX
, "/proc/self/net/bonding/%s",
348 master
->name
) >= SYSFS_PATH_MAX
) {
349 log_warnx("interfaces", "path truncated");
355 log_warnx("interfaces",
356 "unable to get permanent MAC address for %s",
360 if ((netbond
= fdopen(f
, "r")) == NULL
) {
361 log_warn("interfaces", "unable to read stream from %s", path
);
366 We parse the file to search "Slave Interface: ". If found, go to
369 We parse the file to search "Permanent HW addr: ". If found, we get
372 while (fgets(line
, sizeof(line
), netbond
)) {
375 if (strncmp(line
, slaveif
, strlen(slaveif
)) == 0) {
376 if (line
[strlen(line
)-1] == '\n')
377 line
[strlen(line
)-1] = '\0';
378 if (strcmp(iface
->name
,
379 line
+ strlen(slaveif
)) == 0)
384 if (strncmp(line
, hwaddr
, strlen(hwaddr
)) == 0) {
385 if (line
[strlen(line
)-1] == '\n')
386 line
[strlen(line
)-1] = '\0';
387 if (sscanf(line
+ strlen(hwaddr
),
388 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
389 &mac
[0], &mac
[1], &mac
[2],
390 &mac
[3], &mac
[4], &mac
[5]) !=
392 log_warn("interfaces", "unable to parse %s",
393 line
+ strlen(hwaddr
));
397 memcpy(iface
->address
, mac
,
405 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
414 iflinux_get_permanent_mac(struct lldpd
*cfg
,
415 struct interfaces_device_list
*interfaces
,
416 struct interfaces_device
*iface
)
418 struct interfaces_device
*master
= iface
->upper
;
420 if (master
== NULL
|| master
->type
!= IFACE_BOND_T
)
422 if (iflinux_get_permanent_mac_ethtool(cfg
, interfaces
, iface
) == -1 &&
423 (master
->driver
== NULL
|| !strcmp(master
->driver
, "bonding")))
424 /* Fallback to old method for a bond */
425 iflinux_get_permanent_mac_bond(cfg
, interfaces
, iface
);
429 #define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
430 #define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
431 uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
433 struct ethtool_link_usettings
{
434 struct ethtool_link_settings base
;
436 ETHTOOL_DECLARE_LINK_MODE_MASK(supported
);
437 ETHTOOL_DECLARE_LINK_MODE_MASK(advertising
);
438 ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising
);
443 iflinux_ethtool_link_mode_test_bit(unsigned int nr
, const uint32_t *mask
)
445 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
)
447 return !!(mask
[nr
/ 32] & (1 << (nr
% 32)));
450 iflinux_ethtool_link_mode_unset_bit(unsigned int nr
, uint32_t *mask
)
452 if (nr
>= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
)
454 mask
[nr
/ 32] &= ~(1 << (nr
% 32));
457 iflinux_ethtool_link_mode_is_empty(const uint32_t *mask
)
459 for (unsigned int i
= 0;
460 i
< ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
;
470 iflinux_ethtool_glink(struct lldpd
*cfg
, const char *ifname
, struct ethtool_link_usettings
*uset
) {
473 /* Try with ETHTOOL_GLINKSETTINGS first */
475 struct ethtool_link_settings req
;
476 uint32_t link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
478 static int8_t nwords
= 0;
479 struct ifreq ifr
= {};
480 strlcpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
));
483 /* Do a handshake first. We assume that this is device-independant. */
484 memset(&ecmd
, 0, sizeof(ecmd
));
485 ecmd
.req
.cmd
= ETHTOOL_GLINKSETTINGS
;
486 ifr
.ifr_data
= (caddr_t
)&ecmd
;
487 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
489 nwords
= -ecmd
.req
.link_mode_masks_nwords
;
490 log_debug("interfaces", "glinksettings nwords is %" PRId8
, nwords
);
493 if (errno
== EPERM
&& !once
) {
494 log_warnx("interfaces",
495 "cannot get ethtool link information "
496 "with GLINKSETTINGS (requires 4.9+). "
497 "25G+ speeds may be missing in MAC/PHY TLVs");
504 memset(&ecmd
, 0, sizeof(ecmd
));
505 ecmd
.req
.cmd
= ETHTOOL_GLINKSETTINGS
;
506 ecmd
.req
.link_mode_masks_nwords
= nwords
;
507 ifr
.ifr_data
= (caddr_t
)&ecmd
;
508 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
510 log_debug("interfaces", "got ethtool results for %s with GLINKSETTINGS",
512 memcpy(&uset
->base
, &ecmd
.req
, sizeof(uset
->base
));
513 unsigned int u32_offs
= 0;
514 memcpy(uset
->link_modes
.supported
,
515 &ecmd
.link_mode_data
[u32_offs
],
516 4 * ecmd
.req
.link_mode_masks_nwords
);
517 u32_offs
+= ecmd
.req
.link_mode_masks_nwords
;
518 memcpy(uset
->link_modes
.advertising
,
519 &ecmd
.link_mode_data
[u32_offs
],
520 4 * ecmd
.req
.link_mode_masks_nwords
);
521 u32_offs
+= ecmd
.req
.link_mode_masks_nwords
;
522 memcpy(uset
->link_modes
.lp_advertising
,
523 &ecmd
.link_mode_data
[u32_offs
],
524 4 * ecmd
.req
.link_mode_masks_nwords
);
529 /* Try with ETHTOOL_GSET */
530 struct ethtool_cmd ethc
;
531 memset(ðc
, 0, sizeof(ethc
));
532 ethc
.cmd
= ETHTOOL_GSET
;
533 ifr
.ifr_data
= (caddr_t
)ðc
;
534 rc
= ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
);
536 /* Do a partial copy (only what we need) */
537 log_debug("interfaces", "got ethtool results for %s with GSET",
539 memset(uset
, 0, sizeof(*uset
));
540 uset
->base
.cmd
= ETHTOOL_GSET
;
541 uset
->base
.link_mode_masks_nwords
= 1;
542 uset
->link_modes
.supported
[0] = ethc
.supported
;
543 uset
->link_modes
.advertising
[0] = ethc
.advertising
;
544 uset
->link_modes
.lp_advertising
[0] = ethc
.lp_advertising
;
545 uset
->base
.speed
= (ethc
.speed_hi
<< 16) | ethc
.speed
;
546 uset
->base
.duplex
= ethc
.duplex
;
547 uset
->base
.port
= ethc
.port
;
548 uset
->base
.autoneg
= ethc
.autoneg
;
551 if (errno
== EPERM
&& !once
) {
552 log_warnx("interfaces",
553 "cannot get ethtool link information "
554 "with GSET (requires 2.6.19+). "
555 "MAC/PHY TLV will be unavailable");
563 /* Fill up MAC/PHY for a given hardware port */
565 iflinux_macphy(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
567 struct ethtool_link_usettings uset
;
568 struct lldpd_port
*port
= &hardware
->h_lport
;
570 int advertised_ethtool_to_rfc3636
[][2] = {
571 {ETHTOOL_LINK_MODE_10baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_10BASE_T
},
572 {ETHTOOL_LINK_MODE_10baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_10BASET_FD
},
573 {ETHTOOL_LINK_MODE_100baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_100BASE_TX
},
574 {ETHTOOL_LINK_MODE_100baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD
},
575 {ETHTOOL_LINK_MODE_1000baseT_Half_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_T
},
576 {ETHTOOL_LINK_MODE_1000baseT_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD
},
577 {ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
, LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD
},
578 {ETHTOOL_LINK_MODE_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE
},
579 {ETHTOOL_LINK_MODE_Asym_Pause_BIT
, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE
},
582 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
584 if (iflinux_ethtool_glink(cfg
, hardware
->h_ifname
, &uset
) == 0) {
585 port
->p_macphy
.autoneg_support
= iflinux_ethtool_link_mode_test_bit(
586 ETHTOOL_LINK_MODE_Autoneg_BIT
, uset
.link_modes
.supported
);
587 port
->p_macphy
.autoneg_enabled
= (uset
.base
.autoneg
== AUTONEG_DISABLE
) ? 0 : 1;
588 for (j
=0; advertised_ethtool_to_rfc3636
[j
][0] >= 0; j
++) {
589 if (iflinux_ethtool_link_mode_test_bit(
590 advertised_ethtool_to_rfc3636
[j
][0],
591 uset
.link_modes
.advertising
)) {
592 port
->p_macphy
.autoneg_advertised
|=
593 advertised_ethtool_to_rfc3636
[j
][1];
594 iflinux_ethtool_link_mode_unset_bit(
595 advertised_ethtool_to_rfc3636
[j
][0],
596 uset
.link_modes
.advertising
);
599 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT
, uset
.link_modes
.advertising
);
600 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT
, uset
.link_modes
.advertising
);
601 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT
, uset
.link_modes
.advertising
);
602 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT
, uset
.link_modes
.advertising
);
603 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT
, uset
.link_modes
.advertising
);
604 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT
, uset
.link_modes
.advertising
);
605 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT
, uset
.link_modes
.advertising
);
606 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT
, uset
.link_modes
.advertising
);
607 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT
, uset
.link_modes
.advertising
);
608 if (!iflinux_ethtool_link_mode_is_empty(uset
.link_modes
.advertising
)) {
609 port
->p_macphy
.autoneg_advertised
|= LLDP_DOT3_LINK_AUTONEG_OTHER
;
611 switch (uset
.base
.speed
) {
613 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
614 LLDP_DOT3_MAU_10BASETFD
: LLDP_DOT3_MAU_10BASETHD
;
615 if (uset
.base
.port
== PORT_BNC
) port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_10BASE2
;
616 if (uset
.base
.port
== PORT_FIBRE
)
617 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
618 LLDP_DOT3_MAU_10BASEFLFD
: LLDP_DOT3_MAU_10BASEFLHD
;
621 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
622 LLDP_DOT3_MAU_100BASETXFD
: LLDP_DOT3_MAU_100BASETXHD
;
623 if (uset
.base
.port
== PORT_BNC
)
624 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
625 LLDP_DOT3_MAU_100BASET2FD
: LLDP_DOT3_MAU_100BASET2HD
;
626 if (uset
.base
.port
== PORT_FIBRE
)
627 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
628 LLDP_DOT3_MAU_100BASEFXFD
: LLDP_DOT3_MAU_100BASEFXHD
;
631 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
632 LLDP_DOT3_MAU_1000BASETFD
: LLDP_DOT3_MAU_1000BASETHD
;
633 if (uset
.base
.port
== PORT_FIBRE
)
634 port
->p_macphy
.mau_type
= (uset
.base
.duplex
== DUPLEX_FULL
) ? \
635 LLDP_DOT3_MAU_1000BASEXFD
: LLDP_DOT3_MAU_1000BASEXHD
;
638 // For fiber, we tell 10GIGBASEX and for others,
639 // 10GIGBASER. It's not unusual to have 10GIGBASER on
640 // fiber either but we don't have 10GIGBASET for
641 // copper. No good solution.
642 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
643 LLDP_DOT3_MAU_10GIGBASELR
: LLDP_DOT3_MAU_10GIGBASECX4
;
646 // Same kind of approximation.
647 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
648 LLDP_DOT3_MAU_40GBASELR4
: LLDP_DOT3_MAU_40GBASECR4
;
652 port
->p_macphy
.mau_type
= (uset
.base
.port
== PORT_FIBRE
) ? \
653 LLDP_DOT3_MAU_100GBASELR4
: LLDP_DOT3_MAU_100GBASECR10
;
656 if (uset
.base
.port
== PORT_AUI
) port
->p_macphy
.mau_type
= LLDP_DOT3_MAU_AUI
;
659 #else /* ENABLE_DOT3 */
661 iflinux_macphy(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
664 #endif /* ENABLE_DOT3 */
674 iface_bond_init(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
676 struct bond_master
*master
= hardware
->h_data
;
681 if (!master
) return -1;
683 log_debug("interfaces", "initialize enslaved device %s",
686 /* First, we get a socket to the raw physical interface */
687 proto
= only_lldp(cfg
)?ETH_P_LLDP
:ETH_P_ALL
;
688 if ((fd
= priv_iface_init(hardware
->h_ifindex
,
689 hardware
->h_ifname
, proto
)) == -1)
691 hardware
->h_sendfd
= fd
;
692 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 0);
694 /* Then, we open a raw interface for the master */
695 log_debug("interfaces", "enslaved device %s has master %s(%d)",
696 hardware
->h_ifname
, master
->name
, master
->index
);
697 if ((fd
= priv_iface_init(master
->index
, master
->name
, proto
)) == -1) {
698 close(hardware
->h_sendfd
);
701 /* With bonding and older kernels (< 2.6.27) we need to listen
702 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
703 * physical device instead of bond device (works with >=
705 if (setsockopt(fd
, SOL_PACKET
,
706 PACKET_ORIGDEV
, &un
, sizeof(un
)) == -1) {
707 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
708 "You will get inaccurate results",
711 interfaces_setup_multicast(cfg
, master
->name
, 0);
713 levent_hardware_add_fd(hardware
, hardware
->h_sendfd
);
714 levent_hardware_add_fd(hardware
, fd
);
715 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
723 iface_bond_recv(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
,
724 int fd
, char *buffer
, size_t size
)
727 struct sockaddr_ll from
;
728 struct bond_master
*master
= hardware
->h_data
;
730 log_debug("interfaces", "receive PDU from enslaved device %s",
732 if ((n
= iflinux_generic_recv(hardware
, fd
, buffer
, size
, &from
)) == -1)
734 if (fd
== hardware
->h_sendfd
)
735 /* We received this on the physical interface. */
737 /* We received this on the bonding interface. Is it really for us? */
738 if (from
.sll_ifindex
== hardware
->h_ifindex
)
741 if (from
.sll_ifindex
== master
->index
)
742 /* We don't know from which physical interface it comes (kernel
743 * < 2.6.24). In doubt, this is for us. */
745 return -1; /* Not for us */
749 iface_bond_close(struct lldpd
*cfg
, struct lldpd_hardware
*hardware
)
751 struct bond_master
*master
= hardware
->h_data
;
752 log_debug("interfaces", "closing enslaved device %s",
754 interfaces_setup_multicast(cfg
, hardware
->h_ifname
, 1);
755 interfaces_setup_multicast(cfg
, master
->name
, 1);
756 free(hardware
->h_data
); hardware
->h_data
= NULL
;
760 struct lldpd_ops bond_ops
= {
761 .send
= iflinux_eth_send
,
762 .recv
= iface_bond_recv
,
763 .cleanup
= iface_bond_close
,
767 iflinux_handle_bond(struct lldpd
*cfg
, struct interfaces_device_list
*interfaces
)
769 struct interfaces_device
*iface
;
770 struct interfaces_device
*master
;
771 struct lldpd_hardware
*hardware
;
772 struct bond_master
*bmaster
;
774 TAILQ_FOREACH(iface
, interfaces
, next
) {
775 if (!(iface
->type
& IFACE_PHYSICAL_T
)) continue;
776 if (iface
->ignore
) continue;
777 if (!iface
->upper
|| !(iface
->upper
->type
& IFACE_BOND_T
)) continue;
779 master
= iface
->upper
;
780 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
781 iface
->name
, master
->name
);
783 if ((hardware
= lldpd_get_hardware(cfg
,
785 iface
->index
)) == NULL
) {
786 if ((hardware
= lldpd_alloc_hardware(cfg
,
788 iface
->index
)) == NULL
) {
789 log_warnx("interfaces", "Unable to allocate space for %s",
795 if (hardware
->h_flags
) continue;
796 if (hardware
->h_ops
!= &bond_ops
) {
798 log_debug("interfaces",
799 "bond %s is converted from another type of interface",
801 if (hardware
->h_ops
&& hardware
->h_ops
->cleanup
)
802 hardware
->h_ops
->cleanup(cfg
, hardware
);
803 levent_hardware_release(hardware
);
804 levent_hardware_init(hardware
);
806 bmaster
= hardware
->h_data
= calloc(1, sizeof(struct bond_master
));
808 log_warn("interfaces", "not enough memory");
809 lldpd_hardware_cleanup(cfg
, hardware
);
812 } else bmaster
= hardware
->h_data
;
813 bmaster
->index
= master
->index
;
814 strlcpy(bmaster
->name
, master
->name
, IFNAMSIZ
);
815 if (hardware
->h_ops
!= &bond_ops
) {
816 if (iface_bond_init(cfg
, hardware
) != 0) {
817 log_warn("interfaces", "unable to initialize %s",
819 lldpd_hardware_cleanup(cfg
, hardware
);
822 hardware
->h_ops
= &bond_ops
;
823 hardware
->h_mangle
= 1;
826 interfaces_helper_add_hardware(cfg
, hardware
);
828 lldpd_port_cleanup(&hardware
->h_lport
, 0);
830 hardware
->h_flags
= iface
->flags
;
833 /* Get local address */
834 memcpy(&hardware
->h_lladdr
, iface
->address
, ETHER_ADDR_LEN
);
836 /* Fill information about port */
837 interfaces_helper_port_name_desc(cfg
, hardware
, iface
);
839 /* Fill additional info */
841 hardware
->h_lport
.p_aggregid
= master
->index
;
843 hardware
->h_mtu
= iface
->mtu
? iface
->mtu
: 1500;
848 /* Query each interface to get the appropriate driver */
850 iflinux_add_driver(struct lldpd
*cfg
,
851 struct interfaces_device_list
*interfaces
)
853 struct interfaces_device
*iface
;
854 TAILQ_FOREACH(iface
, interfaces
, next
) {
855 struct ethtool_drvinfo ethc
= {
856 .cmd
= ETHTOOL_GDRVINFO
859 .ifr_data
= (caddr_t
)ðc
861 if (iface
->driver
) continue;
863 strlcpy(ifr
.ifr_name
, iface
->name
, IFNAMSIZ
);
864 if (ioctl(cfg
->g_sock
, SIOCETHTOOL
, &ifr
) == 0) {
865 iface
->driver
= strdup(ethc
.driver
);
866 log_debug("interfaces", "driver for %s is `%s`",
867 iface
->name
, iface
->driver
);
872 /* Query each interface to see if it is a wireless one */
874 iflinux_add_wireless(struct lldpd
*cfg
,
875 struct interfaces_device_list
*interfaces
)
877 struct interfaces_device
*iface
;
878 TAILQ_FOREACH(iface
, interfaces
, next
) {
879 struct iwreq iwr
= {};
880 strlcpy(iwr
.ifr_name
, iface
->name
, IFNAMSIZ
);
881 if (ioctl(cfg
->g_sock
, SIOCGIWNAME
, &iwr
) >= 0) {
882 log_debug("interfaces", "%s is wireless",
884 iface
->type
|= IFACE_WIRELESS_T
| IFACE_PHYSICAL_T
;
889 /* Query each interface to see if it is a bridge */
891 iflinux_add_bridge(struct lldpd
*cfg
,
892 struct interfaces_device_list
*interfaces
)
894 struct interfaces_device
*iface
;
896 TAILQ_FOREACH(iface
, interfaces
, next
) {
897 if (iface
->type
& (IFACE_PHYSICAL_T
|
898 IFACE_VLAN_T
|IFACE_BOND_T
|IFACE_BRIDGE_T
))
900 if (iflinux_is_bridge(cfg
, interfaces
, iface
)) {
901 log_debug("interfaces",
902 "interface %s is a bridge",
904 iface
->type
|= IFACE_BRIDGE_T
;
909 /* Query each interface to see if it is a bond */
911 iflinux_add_bond(struct lldpd
*cfg
,
912 struct interfaces_device_list
*interfaces
)
914 struct interfaces_device
*iface
;
916 TAILQ_FOREACH(iface
, interfaces
, next
) {
917 if (iface
->type
& (IFACE_PHYSICAL_T
|IFACE_VLAN_T
|
918 IFACE_BOND_T
|IFACE_BRIDGE_T
))
920 if (iflinux_is_bond(cfg
, interfaces
, iface
)) {
921 log_debug("interfaces",
922 "interface %s is a bond",
924 iface
->type
|= IFACE_BOND_T
;
929 /* Query each interface to see if it is a vlan */
931 iflinux_add_vlan(struct lldpd
*cfg
,
932 struct interfaces_device_list
*interfaces
)
934 struct interfaces_device
*iface
;
936 TAILQ_FOREACH(iface
, interfaces
, next
) {
937 if (iface
->type
& (IFACE_PHYSICAL_T
|IFACE_VLAN_T
|
938 IFACE_BOND_T
|IFACE_BRIDGE_T
))
940 if (iflinux_is_vlan(cfg
, interfaces
, iface
)) {
941 log_debug("interfaces",
942 "interface %s is a VLAN",
944 iface
->type
|= IFACE_VLAN_T
;
950 iflinux_add_physical(struct lldpd
*cfg
,
951 struct interfaces_device_list
*interfaces
)
953 struct interfaces_device
*iface
;
954 /* Blacklist some drivers */
955 const char * const *rif
;
956 const char * const blacklisted_drivers
[] = {
962 TAILQ_FOREACH(iface
, interfaces
, next
) {
963 if (iface
->type
& (IFACE_VLAN_T
|
964 IFACE_BOND_T
|IFACE_BRIDGE_T
))
967 iface
->type
&= ~IFACE_PHYSICAL_T
;
969 /* We request that the interface is able to do either multicast
970 * or broadcast to be able to send discovery frames. */
971 if (!(iface
->flags
& (IFF_MULTICAST
|IFF_BROADCAST
))) {
972 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
977 /* Check if the driver is not blacklisted */
980 for (rif
= blacklisted_drivers
; *rif
; rif
++) {
981 if (strcmp(iface
->driver
, *rif
) == 0) {
982 log_debug("interfaces", "skip %s: blacklisted driver",
991 /* If the interface is linked to another one, skip it too. */
992 if (iface
->lower
&& (!iface
->driver
|| (strcmp(iface
->driver
, "veth") && strcmp(iface
->driver
, "dsa")))) {
993 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
994 iface
->name
, iface
->lower
->name
);
998 /* Get the real MAC address (for example, if the interface is enslaved) */
999 iflinux_get_permanent_mac(cfg
, interfaces
, iface
);
1001 log_debug("interfaces",
1002 "%s is a physical interface",
1004 iface
->type
|= IFACE_PHYSICAL_T
;
1009 interfaces_update(struct lldpd
*cfg
)
1011 struct lldpd_hardware
*hardware
;
1012 struct interfaces_device_list
*interfaces
;
1013 struct interfaces_address_list
*addresses
;
1014 interfaces
= netlink_get_interfaces(cfg
);
1015 addresses
= netlink_get_addresses(cfg
);
1016 if (interfaces
== NULL
|| addresses
== NULL
) {
1017 log_warnx("interfaces", "cannot update the list of local interfaces");
1021 /* Add missing bits to list of interfaces */
1022 iflinux_add_driver(cfg
, interfaces
);
1023 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_WLAN
)
1024 iflinux_add_wireless(cfg
, interfaces
);
1025 if (LOCAL_CHASSIS(cfg
)->c_cap_available
& LLDP_CAP_BRIDGE
)
1026 iflinux_add_bridge(cfg
, interfaces
);
1027 iflinux_add_bond(cfg
, interfaces
);
1028 iflinux_add_vlan(cfg
, interfaces
);
1029 iflinux_add_physical(cfg
, interfaces
);
1031 interfaces_helper_whitelist(cfg
, interfaces
);
1032 #ifdef ENABLE_OLDIES
1033 iflinux_handle_bond(cfg
, interfaces
);
1035 interfaces_helper_physical(cfg
, interfaces
,
1039 interfaces_helper_vlan(cfg
, interfaces
);
1041 interfaces_helper_mgmt(cfg
, addresses
, interfaces
);
1042 interfaces_helper_chassis(cfg
, interfaces
);
1045 TAILQ_FOREACH(hardware
, &cfg
->g_hardware
, h_entries
) {
1046 if (!hardware
->h_flags
) continue;
1047 iflinux_macphy(cfg
, hardware
);
1048 interfaces_helper_promisc(cfg
, hardware
);
1053 interfaces_cleanup(struct lldpd
*cfg
)
1055 netlink_cleanup(cfg
);