]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/interfaces-linux.c
9032cad2bcb9851c6370b689c32bc233cdcdcdfd
[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 iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
46 {
47 int fd;
48
49 log_debug("interfaces", "initialize ethernet device %s",
50 hardware->h_ifname);
51 if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
52 return -1;
53 hardware->h_sendfd = fd; /* Send */
54
55 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
56
57 levent_hardware_add_fd(hardware, fd); /* Receive */
58 log_debug("interfaces", "interface %s initialized (fd=%d)", hardware->h_ifname,
59 fd);
60 return 0;
61 }
62
63 /* Generic ethernet send/receive */
64 static int
65 iflinux_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
66 char *buffer, size_t size)
67 {
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,
71 buffer, size);
72 }
73
74 static void
75 iflinux_error_recv(struct lldpd_hardware *hardware, int fd)
76 {
77 do {
78 ssize_t n;
79 char buf[1024] = {};
80 struct msghdr msg = {
81 .msg_control = buf,
82 .msg_controllen = sizeof(buf)
83 };
84 if ((n = recvmsg(fd, &msg, MSG_ERRQUEUE)) <= 0) {
85 return;
86 }
87 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
88 if (cmsg == NULL)
89 log_warnx("interfaces", "received unknown error on %s",
90 hardware->h_ifname);
91 else
92 log_warnx("interfaces", "received error (level=%d/type=%d) on %s",
93 cmsg->cmsg_level, cmsg->cmsg_type, hardware->h_ifname);
94 } while (1);
95 }
96
97 static int
98 iflinux_generic_recv(struct lldpd_hardware *hardware,
99 int fd, char *buffer, size_t size,
100 struct sockaddr_ll *from)
101 {
102 int n, retry = 0;
103 socklen_t fromlen;
104
105 retry:
106 fromlen = sizeof(*from);
107 memset(from, 0, fromlen);
108 if ((n = recvfrom(fd, buffer, size, 0,
109 (struct sockaddr *)from,
110 &fromlen)) == -1) {
111 if (errno == EAGAIN && retry == 0) {
112 /* There may be an error queued in the socket. Clear it and retry. */
113 iflinux_error_recv(hardware, fd);
114 retry++;
115 goto retry;
116 }
117 if (errno == ENETDOWN) {
118 log_debug("interfaces", "error while receiving frame on %s (network down)",
119 hardware->h_ifname);
120 } else {
121 log_warn("interfaces", "error while receiving frame on %s (retry: %d)",
122 hardware->h_ifname, retry);
123 hardware->h_rx_discarded_cnt++;
124 }
125 return -1;
126 }
127 if (from->sll_pkttype == PACKET_OUTGOING)
128 return -1;
129 return n;
130 }
131
132 static int
133 iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
134 int fd, char *buffer, size_t size)
135 {
136 int n;
137 struct sockaddr_ll from;
138
139 log_debug("interfaces", "receive PDU from ethernet device %s",
140 hardware->h_ifname);
141 if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
142 return -1;
143 return n;
144 }
145
146 static int
147 iflinux_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
148 {
149 log_debug("interfaces", "close ethernet device %s",
150 hardware->h_ifname);
151 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
152 return 0;
153 }
154
155 static struct lldpd_ops eth_ops = {
156 .send = iflinux_eth_send,
157 .recv = iflinux_eth_recv,
158 .cleanup = iflinux_eth_close,
159 };
160
161 static int
162 iflinux_is_bridge(struct lldpd *cfg,
163 struct interfaces_device_list *interfaces,
164 struct interfaces_device *iface)
165 {
166 #ifdef ENABLE_OLDIES
167 struct interfaces_device *port;
168 char path[SYSFS_PATH_MAX];
169 int f;
170
171 if ((snprintf(path, SYSFS_PATH_MAX,
172 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
173 iface->name)) >= SYSFS_PATH_MAX)
174 log_warnx("interfaces", "path truncated");
175 if ((f = priv_open(path)) < 0)
176 return 0;
177 close(f);
178
179 /* Also grab all ports */
180 TAILQ_FOREACH(port, interfaces, next) {
181 if (port->upper) continue;
182 if (snprintf(path, SYSFS_PATH_MAX,
183 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
184 iface->name, port->name) >= SYSFS_PATH_MAX)
185 log_warnx("interfaces", "path truncated");
186 if ((f = priv_open(path)) < 0)
187 continue;
188 log_debug("interfaces",
189 "port %s is bridged to %s",
190 port->name, iface->name);
191 port->upper = iface;
192 close(f);
193 }
194
195 return 1;
196 #else
197 return 0;
198 #endif
199 }
200
201 static int
202 iflinux_is_vlan(struct lldpd *cfg,
203 struct interfaces_device_list *interfaces,
204 struct interfaces_device *iface)
205 {
206 #ifdef ENABLE_OLDIES
207 struct vlan_ioctl_args ifv = {};
208 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
209 strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
210 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
211 /* This is a VLAN, get the lower interface and the VID */
212 struct interfaces_device *lower =
213 interfaces_nametointerface(interfaces, ifv.u.device2);
214 if (!lower) {
215 log_debug("interfaces",
216 "unable to find lower interface for VLAN %s",
217 iface->name);
218 return 0;
219 }
220
221 memset(&ifv, 0, sizeof(ifv));
222 ifv.cmd = GET_VLAN_VID_CMD;
223 strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
224 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
225 log_debug("interfaces",
226 "unable to find VID for VLAN %s",
227 iface->name);
228 return 0;
229 }
230
231 iface->lower = lower;
232 iface->vlanid = ifv.u.VID;
233 return 1;
234 }
235 #endif
236 return 0;
237 }
238
239 static int
240 iflinux_is_bond(struct lldpd *cfg,
241 struct interfaces_device_list *interfaces,
242 struct interfaces_device *master)
243 {
244 #ifdef ENABLE_OLDIES
245 /* Shortcut if we detect the new team driver. Upper and lower links
246 * should already be set with netlink in this case. */
247 if (master->driver && !strcmp(master->driver, "team")) {
248 return 1;
249 }
250
251 struct ifreq ifr = {};
252 struct ifbond ifb = {};
253 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
254 ifr.ifr_data = (char *)&ifb;
255 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
256 while (ifb.num_slaves--) {
257 struct ifslave ifs;
258 memset(&ifr, 0, sizeof(ifr));
259 memset(&ifs, 0, sizeof(ifs));
260 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
261 ifr.ifr_data = (char *)&ifs;
262 ifs.slave_id = ifb.num_slaves;
263 if (ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) {
264 struct interfaces_device *slave =
265 interfaces_nametointerface(interfaces,
266 ifs.slave_name);
267 if (slave == NULL) continue;
268 if (slave->upper) continue;
269 log_debug("interfaces",
270 "interface %s is enslaved to %s",
271 slave->name, master->name);
272 slave->upper = master;
273 }
274 }
275 return 1;
276 }
277 #endif
278 return 0;
279 }
280
281 /**
282 * Get permanent MAC from ethtool.
283 *
284 * Return 0 on success, -1 on error.
285 */
286 static int
287 iflinux_get_permanent_mac_ethtool(struct lldpd *cfg,
288 struct interfaces_device_list *interfaces,
289 struct interfaces_device *iface)
290 {
291 struct ifreq ifr = {};
292 union {
293 struct ethtool_perm_addr addr;
294 /* cppcheck-suppress unusedStructMember */
295 char u8[sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN];
296 } epaddr;
297
298 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
299 epaddr.addr.cmd = ETHTOOL_GPERMADDR;
300 epaddr.addr.size = ETHER_ADDR_LEN;
301 ifr.ifr_data = (caddr_t)&epaddr.addr;
302 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == -1) {
303 static int once = 0;
304 if (errno == EPERM && !once) {
305 log_warn("interfaces",
306 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
307 iface->name);
308 once = 1;
309 return -1;
310 }
311 if (errno != EPERM)
312 log_warnx("interfaces", "cannot get permanent MAC address for %s",
313 iface->name);
314 return -1;
315 }
316 if (epaddr.addr.data[0] != 0 ||
317 epaddr.addr.data[1] != 0 ||
318 epaddr.addr.data[2] != 0 ||
319 epaddr.addr.data[3] != 0 ||
320 epaddr.addr.data[4] != 0 ||
321 epaddr.addr.data[5] != 0 ||
322 epaddr.addr.data[6] != 0) {
323 memcpy(iface->address, epaddr.addr.data, ETHER_ADDR_LEN);
324 return 0;
325 }
326 log_debug("interfaces", "cannot get permanent MAC for %s", iface->name);
327 return -1;
328 }
329
330 /**
331 * Get permanent MAC address for a bond device.
332 */
333 static void
334 iflinux_get_permanent_mac_bond(struct lldpd *cfg,
335 struct interfaces_device_list *interfaces,
336 struct interfaces_device *iface)
337 {
338 struct interfaces_device *master = iface->upper;
339 int f, state = 0;
340 FILE *netbond;
341 const char *slaveif = "Slave Interface: ";
342 const char *hwaddr = "Permanent HW addr: ";
343 u_int8_t mac[ETHER_ADDR_LEN];
344 char path[SYSFS_PATH_MAX];
345 char line[100];
346
347 /* We have a bond, we need to query it to get real MAC addresses */
348 if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
349 master->name) >= SYSFS_PATH_MAX) {
350 log_warnx("interfaces", "path truncated");
351 return;
352 }
353 if ((f = priv_open(path)) < 0) {
354 if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
355 master->name) >= SYSFS_PATH_MAX) {
356 log_warnx("interfaces", "path truncated");
357 return;
358 }
359 f = priv_open(path);
360 }
361 if (f < 0) {
362 log_warnx("interfaces",
363 "unable to get permanent MAC address for %s",
364 iface->name);
365 return;
366 }
367 if ((netbond = fdopen(f, "r")) == NULL) {
368 log_warn("interfaces", "unable to read stream from %s", path);
369 close(f);
370 return;
371 }
372 /* State 0:
373 We parse the file to search "Slave Interface: ". If found, go to
374 state 1.
375 State 1:
376 We parse the file to search "Permanent HW addr: ". If found, we get
377 the mac.
378 */
379 while (fgets(line, sizeof(line), netbond)) {
380 switch (state) {
381 case 0:
382 if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
383 if (line[strlen(line)-1] == '\n')
384 line[strlen(line)-1] = '\0';
385 if (strcmp(iface->name,
386 line + strlen(slaveif)) == 0)
387 state++;
388 }
389 break;
390 case 1:
391 if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
392 if (line[strlen(line)-1] == '\n')
393 line[strlen(line)-1] = '\0';
394 if (sscanf(line + strlen(hwaddr),
395 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
396 &mac[0], &mac[1], &mac[2],
397 &mac[3], &mac[4], &mac[5]) !=
398 ETHER_ADDR_LEN) {
399 log_warn("interfaces", "unable to parse %s",
400 line + strlen(hwaddr));
401 fclose(netbond);
402 return;
403 }
404 memcpy(iface->address, mac,
405 ETHER_ADDR_LEN);
406 fclose(netbond);
407 return;
408 }
409 break;
410 }
411 }
412 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
413 iface->name);
414 fclose(netbond);
415 }
416
417 /**
418 * Get permanent MAC.
419 */
420 static void
421 iflinux_get_permanent_mac(struct lldpd *cfg,
422 struct interfaces_device_list *interfaces,
423 struct interfaces_device *iface)
424 {
425 struct interfaces_device *master = iface->upper;
426
427 if (master == NULL || master->type != IFACE_BOND_T)
428 return;
429 if (iflinux_get_permanent_mac_ethtool(cfg, interfaces, iface) == -1 &&
430 (master->driver == NULL || !strcmp(master->driver, "bonding")))
431 /* Fallback to old method for a bond */
432 iflinux_get_permanent_mac_bond(cfg, interfaces, iface);
433 }
434
435 #define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
436 #define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
437 uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
438
439 struct ethtool_link_usettings {
440 struct ethtool_link_settings base;
441 struct {
442 ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
443 ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
444 ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
445 } link_modes;
446 };
447
448 static inline int
449 iflinux_ethtool_link_mode_test_bit(unsigned int nr, const uint32_t *mask)
450 {
451 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
452 return 0;
453 return !!(mask[nr / 32] & (1 << (nr % 32)));
454 }
455 static inline void
456 iflinux_ethtool_link_mode_unset_bit(unsigned int nr, uint32_t *mask)
457 {
458 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
459 return;
460 mask[nr / 32] &= ~(1 << (nr % 32));
461 }
462 static inline int
463 iflinux_ethtool_link_mode_is_empty(const uint32_t *mask)
464 {
465 for (unsigned int i = 0;
466 i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32;
467 ++i) {
468 if (mask[i] != 0)
469 return 0;
470 }
471
472 return 1;
473 }
474
475 static int
476 iflinux_ethtool_glink(struct lldpd *cfg, const char *ifname, struct ethtool_link_usettings *uset) {
477 int rc;
478
479 /* Try with ETHTOOL_GLINKSETTINGS first */
480 struct {
481 struct ethtool_link_settings req;
482 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
483 } ecmd;
484 static int8_t nwords = 0;
485 struct ifreq ifr = {};
486 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
487
488 if (nwords == 0) {
489 /* Do a handshake first. We assume that this is device-independant. */
490 memset(&ecmd, 0, sizeof(ecmd));
491 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
492 ifr.ifr_data = (caddr_t)&ecmd;
493 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
494 if (rc == 0) {
495 nwords = -ecmd.req.link_mode_masks_nwords;
496 log_debug("interfaces", "glinksettings nwords is %" PRId8, nwords);
497 }
498 }
499 if (nwords > 0) {
500 memset(&ecmd, 0, sizeof(ecmd));
501 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
502 ecmd.req.link_mode_masks_nwords = nwords;
503 ifr.ifr_data = (caddr_t)&ecmd;
504 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
505 if (rc == 0) {
506 log_debug("interfaces", "got ethtool results for %s with GLINKSETTINGS",
507 ifname);
508 memcpy(&uset->base, &ecmd.req, sizeof(uset->base));
509 unsigned int u32_offs = 0;
510 memcpy(uset->link_modes.supported,
511 &ecmd.link_mode_data[u32_offs],
512 4 * ecmd.req.link_mode_masks_nwords);
513 u32_offs += ecmd.req.link_mode_masks_nwords;
514 memcpy(uset->link_modes.advertising,
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.lp_advertising,
519 &ecmd.link_mode_data[u32_offs],
520 4 * ecmd.req.link_mode_masks_nwords);
521 goto end;
522 }
523 }
524
525 /* Try with ETHTOOL_GSET */
526 struct ethtool_cmd ethc;
527 memset(&ethc, 0, sizeof(ethc));
528 ethc.cmd = ETHTOOL_GSET;
529 ifr.ifr_data = (caddr_t)&ethc;
530 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
531 if (rc == 0) {
532 /* Do a partial copy (only what we need) */
533 log_debug("interfaces", "got ethtool results for %s with GSET",
534 ifname);
535 memset(uset, 0, sizeof(*uset));
536 uset->base.cmd = ETHTOOL_GSET;
537 uset->base.link_mode_masks_nwords = 1;
538 uset->link_modes.supported[0] = ethc.supported;
539 uset->link_modes.advertising[0] = ethc.advertising;
540 uset->link_modes.lp_advertising[0] = ethc.lp_advertising;
541 uset->base.speed = (ethc.speed_hi << 16) | ethc.speed;
542 uset->base.duplex = ethc.duplex;
543 uset->base.port = ethc.port;
544 uset->base.autoneg = ethc.autoneg;
545 }
546 end:
547 return rc;
548 }
549
550 /* Fill up MAC/PHY for a given hardware port */
551 static void
552 iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
553 {
554 #ifdef ENABLE_DOT3
555 struct ethtool_link_usettings uset;
556 struct lldpd_port *port = &hardware->h_lport;
557 int j;
558 int advertised_ethtool_to_rfc3636[][2] = {
559 {ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
560 {ETHTOOL_LINK_MODE_10baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
561 {ETHTOOL_LINK_MODE_100baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
562 {ETHTOOL_LINK_MODE_100baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
563 {ETHTOOL_LINK_MODE_1000baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
564 {ETHTOOL_LINK_MODE_1000baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
565 {ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD},
566 {ETHTOOL_LINK_MODE_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
567 {ETHTOOL_LINK_MODE_Asym_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
568 {-1, 0}};
569
570 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
571 hardware->h_ifname);
572 if (iflinux_ethtool_glink(cfg, hardware->h_ifname, &uset) == 0) {
573 port->p_macphy.autoneg_support = iflinux_ethtool_link_mode_test_bit(
574 ETHTOOL_LINK_MODE_Autoneg_BIT, uset.link_modes.supported);
575 port->p_macphy.autoneg_enabled = (uset.base.autoneg == AUTONEG_DISABLE) ? 0 : 1;
576 for (j=0; advertised_ethtool_to_rfc3636[j][0] >= 0; j++) {
577 if (iflinux_ethtool_link_mode_test_bit(
578 advertised_ethtool_to_rfc3636[j][0],
579 uset.link_modes.advertising)) {
580 port->p_macphy.autoneg_advertised |=
581 advertised_ethtool_to_rfc3636[j][1];
582 iflinux_ethtool_link_mode_unset_bit(
583 advertised_ethtool_to_rfc3636[j][0],
584 uset.link_modes.advertising);
585 }
586 }
587 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, uset.link_modes.advertising);
588 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT, uset.link_modes.advertising);
589 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT, uset.link_modes.advertising);
590 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT, uset.link_modes.advertising);
591 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, uset.link_modes.advertising);
592 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT, uset.link_modes.advertising);
593 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT, uset.link_modes.advertising);
594 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, uset.link_modes.advertising);
595 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT, uset.link_modes.advertising);
596 if (!iflinux_ethtool_link_mode_is_empty(uset.link_modes.advertising)) {
597 port->p_macphy.autoneg_advertised |= LLDP_DOT3_LINK_AUTONEG_OTHER;
598 }
599 switch (uset.base.speed) {
600 case SPEED_10:
601 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
602 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
603 if (uset.base.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
604 if (uset.base.port == PORT_FIBRE)
605 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
606 LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD;
607 break;
608 case SPEED_100:
609 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
610 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
611 if (uset.base.port == PORT_BNC)
612 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
613 LLDP_DOT3_MAU_100BASET2FD : LLDP_DOT3_MAU_100BASET2HD;
614 if (uset.base.port == PORT_FIBRE)
615 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
616 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
617 break;
618 case SPEED_1000:
619 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
620 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
621 if (uset.base.port == PORT_FIBRE)
622 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
623 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
624 break;
625 case SPEED_10000:
626 // For fiber, we tell 10GIGBASEX and for others,
627 // 10GIGBASER. It's not unusual to have 10GIGBASER on
628 // fiber either but we don't have 10GIGBASET for
629 // copper. No good solution.
630 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
631 LLDP_DOT3_MAU_10GIGBASELR : LLDP_DOT3_MAU_10GIGBASECX4;
632 break;
633 case SPEED_40000:
634 // Same kind of approximation.
635 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
636 LLDP_DOT3_MAU_40GBASELR4 : LLDP_DOT3_MAU_40GBASECR4;
637 break;
638 case SPEED_100000:
639 // Ditto
640 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
641 LLDP_DOT3_MAU_100GBASELR4 : LLDP_DOT3_MAU_100GBASECR10;
642 break;
643 }
644 if (uset.base.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
645 }
646 #endif
647 }
648
649 struct bond_master {
650 char name[IFNAMSIZ];
651 int index;
652 };
653
654 static int
655 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
656 {
657 struct bond_master *master = hardware->h_data;
658 int fd;
659 int un = 1;
660
661 if (!master) return -1;
662
663 log_debug("interfaces", "initialize enslaved device %s",
664 hardware->h_ifname);
665
666 /* First, we get a socket to the raw physical interface */
667 if ((fd = priv_iface_init(hardware->h_ifindex,
668 hardware->h_ifname)) == -1)
669 return -1;
670 hardware->h_sendfd = fd;
671 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
672
673 /* Then, we open a raw interface for the master */
674 log_debug("interfaces", "enslaved device %s has master %s(%d)",
675 hardware->h_ifname, master->name, master->index);
676 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
677 close(hardware->h_sendfd);
678 return -1;
679 }
680 /* With bonding and older kernels (< 2.6.27) we need to listen
681 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
682 * physical device instead of bond device (works with >=
683 * 2.6.24). */
684 if (setsockopt(fd, SOL_PACKET,
685 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
686 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
687 "You will get inaccurate results",
688 hardware->h_ifname);
689 }
690 interfaces_setup_multicast(cfg, master->name, 0);
691
692 levent_hardware_add_fd(hardware, hardware->h_sendfd);
693 levent_hardware_add_fd(hardware, fd);
694 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
695 hardware->h_ifname,
696 hardware->h_sendfd,
697 master->name, fd);
698 return 0;
699 }
700
701 static int
702 iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
703 int fd, char *buffer, size_t size)
704 {
705 int n;
706 struct sockaddr_ll from;
707 struct bond_master *master = hardware->h_data;
708
709 log_debug("interfaces", "receive PDU from enslaved device %s",
710 hardware->h_ifname);
711 if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
712 return -1;
713 if (fd == hardware->h_sendfd)
714 /* We received this on the physical interface. */
715 return n;
716 /* We received this on the bonding interface. Is it really for us? */
717 if (from.sll_ifindex == hardware->h_ifindex)
718 /* This is for us */
719 return n;
720 if (from.sll_ifindex == master->index)
721 /* We don't know from which physical interface it comes (kernel
722 * < 2.6.24). In doubt, this is for us. */
723 return n;
724 return -1; /* Not for us */
725 }
726
727 static int
728 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
729 {
730 struct bond_master *master = hardware->h_data;
731 log_debug("interfaces", "closing enslaved device %s",
732 hardware->h_ifname);
733 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
734 interfaces_setup_multicast(cfg, master->name, 1);
735 free(hardware->h_data); hardware->h_data = NULL;
736 return 0;
737 }
738
739 struct lldpd_ops bond_ops = {
740 .send = iflinux_eth_send,
741 .recv = iface_bond_recv,
742 .cleanup = iface_bond_close,
743 };
744
745 static void
746 iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
747 {
748 struct interfaces_device *iface;
749 struct interfaces_device *master;
750 struct lldpd_hardware *hardware;
751 struct bond_master *bmaster;
752 int created;
753 TAILQ_FOREACH(iface, interfaces, next) {
754 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
755 if (iface->ignore) continue;
756 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
757
758 master = iface->upper;
759 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
760 iface->name, master->name);
761 created = 0;
762 if ((hardware = lldpd_get_hardware(cfg,
763 iface->name,
764 iface->index)) == NULL) {
765 if ((hardware = lldpd_alloc_hardware(cfg,
766 iface->name,
767 iface->index)) == NULL) {
768 log_warnx("interfaces", "Unable to allocate space for %s",
769 iface->name);
770 continue;
771 }
772 created = 1;
773 }
774 if (hardware->h_flags) continue;
775 if (hardware->h_ops != &bond_ops) {
776 if (!created) {
777 log_debug("interfaces",
778 "bond %s is converted from another type of interface",
779 hardware->h_ifname);
780 if (hardware->h_ops && hardware->h_ops->cleanup)
781 hardware->h_ops->cleanup(cfg, hardware);
782 levent_hardware_release(hardware);
783 levent_hardware_init(hardware);
784 }
785 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
786 if (!bmaster) {
787 log_warn("interfaces", "not enough memory");
788 lldpd_hardware_cleanup(cfg, hardware);
789 continue;
790 }
791 } else bmaster = hardware->h_data;
792 bmaster->index = master->index;
793 strlcpy(bmaster->name, master->name, IFNAMSIZ);
794 if (hardware->h_ops != &bond_ops) {
795 if (iface_bond_init(cfg, hardware) != 0) {
796 log_warn("interfaces", "unable to initialize %s",
797 hardware->h_ifname);
798 lldpd_hardware_cleanup(cfg, hardware);
799 continue;
800 }
801 hardware->h_ops = &bond_ops;
802 hardware->h_mangle = 1;
803 }
804 if (created)
805 interfaces_helper_add_hardware(cfg, hardware);
806 else
807 lldpd_port_cleanup(&hardware->h_lport, 0);
808
809 hardware->h_flags = iface->flags;
810 iface->ignore = 1;
811
812 /* Get local address */
813 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
814
815 /* Fill information about port */
816 interfaces_helper_port_name_desc(cfg, hardware, iface);
817
818 /* Fill additional info */
819 #ifdef ENABLE_DOT3
820 hardware->h_lport.p_aggregid = master->index;
821 #endif
822 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
823 }
824 }
825
826 /* Query each interface to get the appropriate driver */
827 static void
828 iflinux_add_driver(struct lldpd *cfg,
829 struct interfaces_device_list *interfaces)
830 {
831 struct interfaces_device *iface;
832 TAILQ_FOREACH(iface, interfaces, next) {
833 struct ethtool_drvinfo ethc = {
834 .cmd = ETHTOOL_GDRVINFO
835 };
836 struct ifreq ifr = {
837 .ifr_data = (caddr_t)&ethc
838 };
839 if (iface->driver) continue;
840
841 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
842 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
843 iface->driver = strdup(ethc.driver);
844 log_debug("interfaces", "driver for %s is `%s`",
845 iface->name, iface->driver);
846 }
847 }
848 }
849
850 /* Query each interface to see if it is a wireless one */
851 static void
852 iflinux_add_wireless(struct lldpd *cfg,
853 struct interfaces_device_list *interfaces)
854 {
855 struct interfaces_device *iface;
856 TAILQ_FOREACH(iface, interfaces, next) {
857 struct iwreq iwr = {};
858 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
859 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
860 log_debug("interfaces", "%s is wireless",
861 iface->name);
862 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
863 }
864 }
865 }
866
867 /* Query each interface to see if it is a bridge */
868 static void
869 iflinux_add_bridge(struct lldpd *cfg,
870 struct interfaces_device_list *interfaces)
871 {
872 struct interfaces_device *iface;
873
874 TAILQ_FOREACH(iface, interfaces, next) {
875 if (iface->type & (IFACE_PHYSICAL_T|
876 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
877 continue;
878 if (iflinux_is_bridge(cfg, interfaces, iface)) {
879 log_debug("interfaces",
880 "interface %s is a bridge",
881 iface->name);
882 iface->type |= IFACE_BRIDGE_T;
883 }
884 }
885 }
886
887 /* Query each interface to see if it is a bond */
888 static void
889 iflinux_add_bond(struct lldpd *cfg,
890 struct interfaces_device_list *interfaces)
891 {
892 struct interfaces_device *iface;
893
894 TAILQ_FOREACH(iface, interfaces, next) {
895 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
896 IFACE_BOND_T|IFACE_BRIDGE_T))
897 continue;
898 if (iflinux_is_bond(cfg, interfaces, iface)) {
899 log_debug("interfaces",
900 "interface %s is a bond",
901 iface->name);
902 iface->type |= IFACE_BOND_T;
903 }
904 }
905 }
906
907 /* Query each interface to see if it is a vlan */
908 static void
909 iflinux_add_vlan(struct lldpd *cfg,
910 struct interfaces_device_list *interfaces)
911 {
912 struct interfaces_device *iface;
913
914 TAILQ_FOREACH(iface, interfaces, next) {
915 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
916 IFACE_BOND_T|IFACE_BRIDGE_T))
917 continue;
918 if (iflinux_is_vlan(cfg, interfaces, iface)) {
919 log_debug("interfaces",
920 "interface %s is a VLAN",
921 iface->name);
922 iface->type |= IFACE_VLAN_T;
923 }
924 }
925 }
926
927 static void
928 iflinux_add_physical(struct lldpd *cfg,
929 struct interfaces_device_list *interfaces)
930 {
931 struct interfaces_device *iface;
932 /* Blacklist some drivers */
933 const char * const *rif;
934 const char * const blacklisted_drivers[] = {
935 "cdc_mbim",
936 "vxlan",
937 NULL
938 };
939
940 TAILQ_FOREACH(iface, interfaces, next) {
941 if (iface->type & (IFACE_VLAN_T|
942 IFACE_BOND_T|IFACE_BRIDGE_T))
943 continue;
944
945 iface->type &= ~IFACE_PHYSICAL_T;
946
947 /* We request that the interface is able to do either multicast
948 * or broadcast to be able to send discovery frames. */
949 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
950 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
951 iface->name);
952 continue;
953 }
954
955 /* Check if the driver is not blacklisted */
956 if (iface->driver) {
957 int skip = 0;
958 for (rif = blacklisted_drivers; *rif; rif++) {
959 if (strcmp(iface->driver, *rif) == 0) {
960 log_debug("interfaces", "skip %s: blacklisted driver",
961 iface->name);
962 skip = 1;
963 break;
964 }
965 }
966 if (skip) continue;
967 }
968
969 /* If the interface is linked to another one, skip it too. */
970 if (iface->lower && (!iface->driver || strcmp(iface->driver, "veth"))) {
971 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
972 iface->name, iface->lower->name);
973 continue;
974 }
975
976 /* Get the real MAC address (for example, if the interface is enslaved) */
977 iflinux_get_permanent_mac(cfg, interfaces, iface);
978
979 log_debug("interfaces",
980 "%s is a physical interface",
981 iface->name);
982 iface->type |= IFACE_PHYSICAL_T;
983 }
984 }
985
986 void
987 interfaces_update(struct lldpd *cfg)
988 {
989 struct lldpd_hardware *hardware;
990 struct interfaces_device_list *interfaces;
991 struct interfaces_address_list *addresses;
992 interfaces = netlink_get_interfaces(cfg);
993 addresses = netlink_get_addresses(cfg);
994 if (interfaces == NULL || addresses == NULL) {
995 log_warnx("interfaces", "cannot update the list of local interfaces");
996 return;
997 }
998
999 /* Add missing bits to list of interfaces */
1000 iflinux_add_driver(cfg, interfaces);
1001 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
1002 iflinux_add_wireless(cfg, interfaces);
1003 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
1004 iflinux_add_bridge(cfg, interfaces);
1005 iflinux_add_bond(cfg, interfaces);
1006 iflinux_add_vlan(cfg, interfaces);
1007 iflinux_add_physical(cfg, interfaces);
1008
1009 interfaces_helper_whitelist(cfg, interfaces);
1010 iflinux_handle_bond(cfg, interfaces);
1011 interfaces_helper_physical(cfg, interfaces,
1012 &eth_ops,
1013 iflinux_eth_init);
1014 #ifdef ENABLE_DOT1
1015 interfaces_helper_vlan(cfg, interfaces);
1016 #endif
1017 interfaces_helper_mgmt(cfg, addresses);
1018 interfaces_helper_chassis(cfg, interfaces);
1019
1020 /* Mac/PHY */
1021 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
1022 if (!hardware->h_flags) continue;
1023 iflinux_macphy(cfg, hardware);
1024 interfaces_helper_promisc(cfg, hardware);
1025 }
1026 }
1027
1028 void
1029 interfaces_cleanup(struct lldpd *cfg)
1030 {
1031 netlink_cleanup(cfg);
1032 }