]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/interfaces-linux.c
56931bf60672bf8aa2aaef817d50517f7a9df5ba
[thirdparty/lldpd.git] / src / daemon / interfaces-linux.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
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.
8 *
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.
16 */
17
18 #include "lldpd.h"
19
20 #include <stdio.h>
21 #include <unistd.h>
22 #include <inttypes.h>
23 #include <errno.h>
24 #include <sys/ioctl.h>
25 #if defined(__clang__)
26 #pragma clang diagnostic push
27 #pragma clang diagnostic ignored "-Wdocumentation"
28 #endif
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
38 #endif
39
40 #define SYSFS_PATH_MAX 256
41 #define MAX_PORTS 1024
42 #define MAX_BRIDGES 1024
43
44 static int
45 only_lldp(struct lldpd *cfg)
46 {
47 int lldp_enabled = 0;
48 int other_enabled = 0;
49 size_t i;
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;
54 }
55 return lldp_enabled && !other_enabled;
56
57 }
58
59 static int
60 iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
61 {
62 int fd;
63
64 log_debug("interfaces", "initialize ethernet device %s",
65 hardware->h_ifname);
66 if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname,
67 only_lldp(cfg)?ETH_P_LLDP:ETH_P_ALL)) == -1)
68 return -1;
69 hardware->h_sendfd = fd; /* Send */
70
71 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
72
73 levent_hardware_add_fd(hardware, fd); /* Receive */
74 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
75 fd);
76 return 0;
77 }
78
79 /* Generic ethernet send/receive */
80 static int
81 iflinux_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
82 char *buffer, size_t size)
83 {
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,
87 buffer, size);
88 }
89
90 static int
91 iflinux_generic_recv(struct lldpd_hardware *hardware,
92 int fd, char *buffer, size_t size,
93 struct sockaddr_ll *from)
94 {
95 int n, retry = 0;
96 socklen_t fromlen;
97
98 retry:
99 fromlen = sizeof(*from);
100 memset(from, 0, fromlen);
101 if ((n = recvfrom(fd, buffer, size, 0,
102 (struct sockaddr *)from,
103 &fromlen)) == -1) {
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);
107 retry++;
108 goto retry;
109 }
110 if (errno == ENETDOWN) {
111 log_debug("interfaces", "error while receiving frame on %s (network down)",
112 hardware->h_ifname);
113 } else {
114 log_warn("interfaces", "error while receiving frame on %s (retry: %d)",
115 hardware->h_ifname, retry);
116 hardware->h_rx_discarded_cnt++;
117 }
118 return -1;
119 }
120 if (from->sll_pkttype == PACKET_OUTGOING)
121 return -1;
122 return n;
123 }
124
125 static int
126 iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
127 int fd, char *buffer, size_t size)
128 {
129 int n;
130 struct sockaddr_ll from;
131
132 log_debug("interfaces", "receive PDU from ethernet device %s",
133 hardware->h_ifname);
134 if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
135 return -1;
136 return n;
137 }
138
139 static int
140 iflinux_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
141 {
142 log_debug("interfaces", "close ethernet device %s",
143 hardware->h_ifname);
144 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
145 return 0;
146 }
147
148 static struct lldpd_ops eth_ops = {
149 .send = iflinux_eth_send,
150 .recv = iflinux_eth_recv,
151 .cleanup = iflinux_eth_close,
152 };
153
154 static int
155 iflinux_is_bridge(struct lldpd *cfg,
156 struct interfaces_device_list *interfaces,
157 struct interfaces_device *iface)
158 {
159 #ifdef ENABLE_OLDIES
160 struct interfaces_device *port;
161 char path[SYSFS_PATH_MAX];
162 int f;
163
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)
169 return 0;
170 close(f);
171
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)
180 continue;
181 log_debug("interfaces",
182 "port %s is bridged to %s",
183 port->name, iface->name);
184 port->upper = iface;
185 close(f);
186 }
187
188 return 1;
189 #else
190 return 0;
191 #endif
192 }
193
194 static int
195 iflinux_is_vlan(struct lldpd *cfg,
196 struct interfaces_device_list *interfaces,
197 struct interfaces_device *iface)
198 {
199 #ifdef ENABLE_OLDIES
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);
207 if (!lower) {
208 log_debug("interfaces",
209 "unable to find lower interface for VLAN %s",
210 iface->name);
211 return 0;
212 }
213
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",
220 iface->name);
221 return 0;
222 }
223
224 iface->lower = lower;
225 bitmap_set(iface->vlan_bmap, ifv.u.VID);
226 return 1;
227 }
228 #endif
229 return 0;
230 }
231
232 static int
233 iflinux_is_bond(struct lldpd *cfg,
234 struct interfaces_device_list *interfaces,
235 struct interfaces_device *master)
236 {
237 #ifdef ENABLE_OLDIES
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")) {
241 return 1;
242 }
243
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--) {
250 struct ifslave ifs;
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,
259 ifs.slave_name);
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;
266 }
267 }
268 return 1;
269 }
270 #endif
271 return 0;
272 }
273
274 /**
275 * Get permanent MAC from ethtool.
276 *
277 * Return 0 on success, -1 on error.
278 */
279 static int
280 iflinux_get_permanent_mac_ethtool(struct lldpd *cfg,
281 struct interfaces_device_list *interfaces,
282 struct interfaces_device *iface)
283 {
284 int ret = -1;
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;
288
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) {
294 static int once = 0;
295 if (errno == EPERM && !once) {
296 log_warnx("interfaces",
297 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
298 iface->name);
299 once = 1;
300 goto end;
301 }
302 if (errno != EPERM)
303 log_warn("interfaces", "cannot get permanent MAC address for %s",
304 iface->name);
305 goto end;
306 }
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);
314 ret = 0;
315 goto end;
316 }
317 log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface->name);
318 end:
319 free(epaddr);
320 return ret;
321 }
322
323 /**
324 * Get permanent MAC address for a bond device.
325 */
326 static void
327 iflinux_get_permanent_mac_bond(struct lldpd *cfg,
328 struct interfaces_device_list *interfaces,
329 struct interfaces_device *iface)
330 {
331 struct interfaces_device *master = iface->upper;
332 int f, state = 0;
333 FILE *netbond;
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];
338 char line[100];
339
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");
344 return;
345 }
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");
350 return;
351 }
352 f = priv_open(path);
353 }
354 if (f < 0) {
355 log_warnx("interfaces",
356 "unable to get permanent MAC address for %s",
357 iface->name);
358 return;
359 }
360 if ((netbond = fdopen(f, "r")) == NULL) {
361 log_warn("interfaces", "unable to read stream from %s", path);
362 close(f);
363 return;
364 }
365 /* State 0:
366 We parse the file to search "Slave Interface: ". If found, go to
367 state 1.
368 State 1:
369 We parse the file to search "Permanent HW addr: ". If found, we get
370 the mac.
371 */
372 while (fgets(line, sizeof(line), netbond)) {
373 switch (state) {
374 case 0:
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)
380 state++;
381 }
382 break;
383 case 1:
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]) !=
391 ETHER_ADDR_LEN) {
392 log_warn("interfaces", "unable to parse %s",
393 line + strlen(hwaddr));
394 fclose(netbond);
395 return;
396 }
397 memcpy(iface->address, mac,
398 ETHER_ADDR_LEN);
399 fclose(netbond);
400 return;
401 }
402 break;
403 }
404 }
405 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
406 iface->name);
407 fclose(netbond);
408 }
409
410 /**
411 * Get permanent MAC.
412 */
413 static void
414 iflinux_get_permanent_mac(struct lldpd *cfg,
415 struct interfaces_device_list *interfaces,
416 struct interfaces_device *iface)
417 {
418 struct interfaces_device *master = iface->upper;
419
420 if (master == NULL || master->type != IFACE_BOND_T)
421 return;
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);
426 }
427
428 #ifdef ENABLE_DOT3
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]
432
433 struct ethtool_link_usettings {
434 struct ethtool_link_settings base;
435 struct {
436 ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
437 ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
438 ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
439 } link_modes;
440 };
441
442 static int
443 iflinux_ethtool_link_mode_test_bit(unsigned int nr, const uint32_t *mask)
444 {
445 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
446 return 0;
447 return !!(mask[nr / 32] & (1 << (nr % 32)));
448 }
449 static void
450 iflinux_ethtool_link_mode_unset_bit(unsigned int nr, uint32_t *mask)
451 {
452 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
453 return;
454 mask[nr / 32] &= ~(1 << (nr % 32));
455 }
456 static int
457 iflinux_ethtool_link_mode_is_empty(const uint32_t *mask)
458 {
459 for (unsigned int i = 0;
460 i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32;
461 ++i) {
462 if (mask[i] != 0)
463 return 0;
464 }
465
466 return 1;
467 }
468
469 static int
470 iflinux_ethtool_glink(struct lldpd *cfg, const char *ifname, struct ethtool_link_usettings *uset) {
471 int rc;
472
473 /* Try with ETHTOOL_GLINKSETTINGS first */
474 struct {
475 struct ethtool_link_settings req;
476 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
477 } ecmd;
478 static int8_t nwords = 0;
479 struct ifreq ifr = {};
480 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
481
482 if (nwords == 0) {
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);
488 if (rc == 0) {
489 nwords = -ecmd.req.link_mode_masks_nwords;
490 log_debug("interfaces", "glinksettings nwords is %" PRId8, nwords);
491 } else {
492 static int once = 0;
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");
498 once = 1;
499 }
500 nwords = -1;
501 }
502 }
503 if (nwords > 0) {
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);
509 if (rc == 0) {
510 log_debug("interfaces", "got ethtool results for %s with GLINKSETTINGS",
511 ifname);
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);
525 goto end;
526 }
527 }
528
529 /* Try with ETHTOOL_GSET */
530 struct ethtool_cmd ethc;
531 memset(&ethc, 0, sizeof(ethc));
532 ethc.cmd = ETHTOOL_GSET;
533 ifr.ifr_data = (caddr_t)&ethc;
534 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
535 if (rc == 0) {
536 /* Do a partial copy (only what we need) */
537 log_debug("interfaces", "got ethtool results for %s with GSET",
538 ifname);
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;
549 } else {
550 static int once = 0;
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");
556 once = 1;
557 }
558 }
559 end:
560 return rc;
561 }
562
563 /* Fill up MAC/PHY for a given hardware port */
564 static void
565 iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
566 {
567 struct ethtool_link_usettings uset;
568 struct lldpd_port *port = &hardware->h_lport;
569 int j;
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},
580 {-1, 0}};
581
582 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
583 hardware->h_ifname);
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);
597 }
598 }
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;
610 }
611 switch (uset.base.speed) {
612 case SPEED_10:
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;
619 break;
620 case SPEED_100:
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;
629 break;
630 case SPEED_1000:
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;
636 break;
637 case SPEED_10000:
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;
644 break;
645 case SPEED_40000:
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;
649 break;
650 case SPEED_100000:
651 // Ditto
652 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
653 LLDP_DOT3_MAU_100GBASELR4 : LLDP_DOT3_MAU_100GBASECR10;
654 break;
655 }
656 if (uset.base.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
657 }
658 }
659 #else /* ENABLE_DOT3 */
660 static void
661 iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
662 {
663 }
664 #endif /* ENABLE_DOT3 */
665
666
667 #ifdef ENABLE_OLDIES
668 struct bond_master {
669 char name[IFNAMSIZ];
670 int index;
671 };
672
673 static int
674 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
675 {
676 struct bond_master *master = hardware->h_data;
677 int fd;
678 int un = 1;
679 int proto;
680
681 if (!master) return -1;
682
683 log_debug("interfaces", "initialize enslaved device %s",
684 hardware->h_ifname);
685
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)
690 return -1;
691 hardware->h_sendfd = fd;
692 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
693
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);
699 return -1;
700 }
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 >=
704 * 2.6.24). */
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",
709 hardware->h_ifname);
710 }
711 interfaces_setup_multicast(cfg, master->name, 0);
712
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])",
716 hardware->h_ifname,
717 hardware->h_sendfd,
718 master->name, fd);
719 return 0;
720 }
721
722 static int
723 iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
724 int fd, char *buffer, size_t size)
725 {
726 int n;
727 struct sockaddr_ll from;
728 struct bond_master *master = hardware->h_data;
729
730 log_debug("interfaces", "receive PDU from enslaved device %s",
731 hardware->h_ifname);
732 if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
733 return -1;
734 if (fd == hardware->h_sendfd)
735 /* We received this on the physical interface. */
736 return n;
737 /* We received this on the bonding interface. Is it really for us? */
738 if (from.sll_ifindex == hardware->h_ifindex)
739 /* This is for us */
740 return n;
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. */
744 return n;
745 return -1; /* Not for us */
746 }
747
748 static int
749 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
750 {
751 struct bond_master *master = hardware->h_data;
752 log_debug("interfaces", "closing enslaved device %s",
753 hardware->h_ifname);
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;
757 return 0;
758 }
759
760 struct lldpd_ops bond_ops = {
761 .send = iflinux_eth_send,
762 .recv = iface_bond_recv,
763 .cleanup = iface_bond_close,
764 };
765
766 static void
767 iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
768 {
769 struct interfaces_device *iface;
770 struct interfaces_device *master;
771 struct lldpd_hardware *hardware;
772 struct bond_master *bmaster;
773 int created;
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;
778
779 master = iface->upper;
780 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
781 iface->name, master->name);
782 created = 0;
783 if ((hardware = lldpd_get_hardware(cfg,
784 iface->name,
785 iface->index)) == NULL) {
786 if ((hardware = lldpd_alloc_hardware(cfg,
787 iface->name,
788 iface->index)) == NULL) {
789 log_warnx("interfaces", "Unable to allocate space for %s",
790 iface->name);
791 continue;
792 }
793 created = 1;
794 }
795 if (hardware->h_flags) continue;
796 if (hardware->h_ops != &bond_ops) {
797 if (!created) {
798 log_debug("interfaces",
799 "bond %s is converted from another type of interface",
800 hardware->h_ifname);
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);
805 }
806 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
807 if (!bmaster) {
808 log_warn("interfaces", "not enough memory");
809 lldpd_hardware_cleanup(cfg, hardware);
810 continue;
811 }
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",
818 hardware->h_ifname);
819 lldpd_hardware_cleanup(cfg, hardware);
820 continue;
821 }
822 hardware->h_ops = &bond_ops;
823 hardware->h_mangle = 1;
824 }
825 if (created)
826 interfaces_helper_add_hardware(cfg, hardware);
827 else
828 lldpd_port_cleanup(&hardware->h_lport, 0);
829
830 hardware->h_flags = iface->flags;
831 iface->ignore = 1;
832
833 /* Get local address */
834 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
835
836 /* Fill information about port */
837 interfaces_helper_port_name_desc(cfg, hardware, iface);
838
839 /* Fill additional info */
840 #ifdef ENABLE_DOT3
841 hardware->h_lport.p_aggregid = master->index;
842 #endif
843 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
844 }
845 }
846 #endif
847
848 /* Query each interface to get the appropriate driver */
849 static void
850 iflinux_add_driver(struct lldpd *cfg,
851 struct interfaces_device_list *interfaces)
852 {
853 struct interfaces_device *iface;
854 TAILQ_FOREACH(iface, interfaces, next) {
855 struct ethtool_drvinfo ethc = {
856 .cmd = ETHTOOL_GDRVINFO
857 };
858 struct ifreq ifr = {
859 .ifr_data = (caddr_t)&ethc
860 };
861 if (iface->driver) continue;
862
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);
868 }
869 }
870 }
871
872 /* Query each interface to see if it is a wireless one */
873 static void
874 iflinux_add_wireless(struct lldpd *cfg,
875 struct interfaces_device_list *interfaces)
876 {
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",
883 iface->name);
884 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
885 }
886 }
887 }
888
889 /* Query each interface to see if it is a bridge */
890 static void
891 iflinux_add_bridge(struct lldpd *cfg,
892 struct interfaces_device_list *interfaces)
893 {
894 struct interfaces_device *iface;
895
896 TAILQ_FOREACH(iface, interfaces, next) {
897 if (iface->type & (IFACE_PHYSICAL_T|
898 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
899 continue;
900 if (iflinux_is_bridge(cfg, interfaces, iface)) {
901 log_debug("interfaces",
902 "interface %s is a bridge",
903 iface->name);
904 iface->type |= IFACE_BRIDGE_T;
905 }
906 }
907 }
908
909 /* Query each interface to see if it is a bond */
910 static void
911 iflinux_add_bond(struct lldpd *cfg,
912 struct interfaces_device_list *interfaces)
913 {
914 struct interfaces_device *iface;
915
916 TAILQ_FOREACH(iface, interfaces, next) {
917 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
918 IFACE_BOND_T|IFACE_BRIDGE_T))
919 continue;
920 if (iflinux_is_bond(cfg, interfaces, iface)) {
921 log_debug("interfaces",
922 "interface %s is a bond",
923 iface->name);
924 iface->type |= IFACE_BOND_T;
925 }
926 }
927 }
928
929 /* Query each interface to see if it is a vlan */
930 static void
931 iflinux_add_vlan(struct lldpd *cfg,
932 struct interfaces_device_list *interfaces)
933 {
934 struct interfaces_device *iface;
935
936 TAILQ_FOREACH(iface, interfaces, next) {
937 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
938 IFACE_BOND_T|IFACE_BRIDGE_T))
939 continue;
940 if (iflinux_is_vlan(cfg, interfaces, iface)) {
941 log_debug("interfaces",
942 "interface %s is a VLAN",
943 iface->name);
944 iface->type |= IFACE_VLAN_T;
945 }
946 }
947 }
948
949 static void
950 iflinux_add_physical(struct lldpd *cfg,
951 struct interfaces_device_list *interfaces)
952 {
953 struct interfaces_device *iface;
954 /* Blacklist some drivers */
955 const char * const *rif;
956 const char * const blacklisted_drivers[] = {
957 "cdc_mbim",
958 "vxlan",
959 NULL
960 };
961
962 TAILQ_FOREACH(iface, interfaces, next) {
963 if (iface->type & (IFACE_VLAN_T|
964 IFACE_BOND_T|IFACE_BRIDGE_T))
965 continue;
966
967 iface->type &= ~IFACE_PHYSICAL_T;
968
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",
973 iface->name);
974 continue;
975 }
976
977 /* Check if the driver is not blacklisted */
978 if (iface->driver) {
979 int skip = 0;
980 for (rif = blacklisted_drivers; *rif; rif++) {
981 if (strcmp(iface->driver, *rif) == 0) {
982 log_debug("interfaces", "skip %s: blacklisted driver",
983 iface->name);
984 skip = 1;
985 break;
986 }
987 }
988 if (skip) continue;
989 }
990
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);
995 continue;
996 }
997
998 /* Get the real MAC address (for example, if the interface is enslaved) */
999 iflinux_get_permanent_mac(cfg, interfaces, iface);
1000
1001 log_debug("interfaces",
1002 "%s is a physical interface",
1003 iface->name);
1004 iface->type |= IFACE_PHYSICAL_T;
1005 }
1006 }
1007
1008 void
1009 interfaces_update(struct lldpd *cfg)
1010 {
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");
1018 return;
1019 }
1020
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);
1030
1031 interfaces_helper_whitelist(cfg, interfaces);
1032 #ifdef ENABLE_OLDIES
1033 iflinux_handle_bond(cfg, interfaces);
1034 #endif
1035 interfaces_helper_physical(cfg, interfaces,
1036 &eth_ops,
1037 iflinux_eth_init);
1038 #ifdef ENABLE_DOT1
1039 interfaces_helper_vlan(cfg, interfaces);
1040 #endif
1041 interfaces_helper_mgmt(cfg, addresses, interfaces);
1042 interfaces_helper_chassis(cfg, interfaces);
1043
1044 /* Mac/PHY */
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);
1049 }
1050 }
1051
1052 void
1053 interfaces_cleanup(struct lldpd *cfg)
1054 {
1055 netlink_cleanup(cfg);
1056 }