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