]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/interfaces-linux.c
interfaces/linux: make veth special
[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 int
74 iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
75 int fd, char *buffer, size_t size)
76 {
77 int n;
78 struct sockaddr_ll from = {};
79 socklen_t fromlen;
80
81 log_debug("interfaces", "receive PDU from ethernet device %s",
82 hardware->h_ifname);
83 fromlen = sizeof(from);
84 if ((n = recvfrom(fd,
85 buffer,
86 size, 0,
87 (struct sockaddr *)&from,
88 &fromlen)) == -1) {
89 log_warn("interfaces", "error while receiving frame on %s",
90 hardware->h_ifname);
91 hardware->h_rx_discarded_cnt++;
92 return -1;
93 }
94 if (from.sll_pkttype == PACKET_OUTGOING)
95 return -1;
96 return n;
97 }
98
99 static int
100 iflinux_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
101 {
102 log_debug("interfaces", "close ethernet device %s",
103 hardware->h_ifname);
104 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
105 return 0;
106 }
107
108 static struct lldpd_ops eth_ops = {
109 .send = iflinux_eth_send,
110 .recv = iflinux_eth_recv,
111 .cleanup = iflinux_eth_close,
112 };
113
114 static int
115 iflinux_is_bridge(struct lldpd *cfg,
116 struct interfaces_device_list *interfaces,
117 struct interfaces_device *iface)
118 {
119 #ifdef ENABLE_OLDIES
120 struct interfaces_device *port;
121 char path[SYSFS_PATH_MAX];
122 int f;
123
124 if ((snprintf(path, SYSFS_PATH_MAX,
125 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
126 iface->name)) >= SYSFS_PATH_MAX)
127 log_warnx("interfaces", "path truncated");
128 if ((f = priv_open(path)) < 0)
129 return 0;
130 close(f);
131
132 /* Also grab all ports */
133 TAILQ_FOREACH(port, interfaces, next) {
134 if (port->upper) continue;
135 if (snprintf(path, SYSFS_PATH_MAX,
136 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
137 iface->name, port->name) >= SYSFS_PATH_MAX)
138 log_warnx("interfaces", "path truncated");
139 if ((f = priv_open(path)) < 0)
140 continue;
141 log_debug("interfaces",
142 "port %s is bridged to %s",
143 port->name, iface->name);
144 port->upper = iface;
145 close(f);
146 }
147
148 return 1;
149 #else
150 return 0;
151 #endif
152 }
153
154 static int
155 iflinux_is_vlan(struct lldpd *cfg,
156 struct interfaces_device_list *interfaces,
157 struct interfaces_device *iface)
158 {
159 #ifdef ENABLE_OLDIES
160 struct vlan_ioctl_args ifv = {};
161 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
162 strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
163 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
164 /* This is a VLAN, get the lower interface and the VID */
165 struct interfaces_device *lower =
166 interfaces_nametointerface(interfaces, ifv.u.device2);
167 if (!lower) {
168 log_debug("interfaces",
169 "unable to find lower interface for VLAN %s",
170 iface->name);
171 return 0;
172 }
173
174 memset(&ifv, 0, sizeof(ifv));
175 ifv.cmd = GET_VLAN_VID_CMD;
176 strlcpy(ifv.device1, iface->name, sizeof(ifv.device1));
177 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
178 log_debug("interfaces",
179 "unable to find VID for VLAN %s",
180 iface->name);
181 return 0;
182 }
183
184 iface->lower = lower;
185 iface->vlanid = ifv.u.VID;
186 return 1;
187 }
188 #endif
189 return 0;
190 }
191
192 static int
193 iflinux_is_bond(struct lldpd *cfg,
194 struct interfaces_device_list *interfaces,
195 struct interfaces_device *master)
196 {
197 #ifdef ENABLE_OLDIES
198 /* Shortcut if we detect the new team driver. Upper and lower links
199 * should already be set with netlink in this case. */
200 if (master->driver && !strcmp(master->driver, "team")) {
201 return 1;
202 }
203
204 struct ifreq ifr = {};
205 struct ifbond ifb = {};
206 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
207 ifr.ifr_data = (char *)&ifb;
208 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
209 while (ifb.num_slaves--) {
210 struct ifslave ifs;
211 memset(&ifr, 0, sizeof(ifr));
212 memset(&ifs, 0, sizeof(ifs));
213 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
214 ifr.ifr_data = (char *)&ifs;
215 ifs.slave_id = ifb.num_slaves;
216 if (ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) {
217 struct interfaces_device *slave =
218 interfaces_nametointerface(interfaces,
219 ifs.slave_name);
220 if (slave == NULL) continue;
221 if (slave->upper) continue;
222 log_debug("interfaces",
223 "interface %s is enslaved to %s",
224 slave->name, master->name);
225 slave->upper = master;
226 }
227 }
228 return 1;
229 }
230 #endif
231 return 0;
232 }
233
234 /* Get the permanent MAC address using ethtool as a fallback There is a slight
235 * difference with /proc/net/bonding method. The /proc/net/bonding method will
236 * retrieve the MAC address of the interface before it was added to the
237 * bond. The ethtool method will retrieve the real permanent MAC address. For
238 * some devices, there is no such address (for example, virtual devices like
239 * veth). */
240 static void
241 iflinux_get_permanent_mac_ethtool(struct lldpd *cfg,
242 struct interfaces_device_list *interfaces,
243 struct interfaces_device *iface)
244 {
245 struct interfaces_device *master;
246 u_int8_t mac[ETHER_ADDR_LEN];
247
248 /* We could do that for any interface, but let's do that only for
249 * aggregates. */
250 if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T)
251 return;
252
253 log_debug("interfaces", "get MAC address for %s",
254 iface->name);
255
256 if (priv_iface_mac(iface->name, mac, ETHER_ADDR_LEN) != 0) {
257 log_warnx("interfaces",
258 "unable to get permanent MAC address for %s",
259 master->name);
260 return;
261 }
262 size_t i;
263 for (i = 0; i < ETHER_ADDR_LEN; i++)
264 if (mac[i] != 0) break;
265 if (i == ETHER_ADDR_LEN) {
266 log_warnx("interfaces",
267 "no permanent MAC address found for %s",
268 master->name);
269 return;
270 }
271 memcpy(iface->address, mac,
272 ETHER_ADDR_LEN);
273 }
274
275 static void
276 iflinux_get_permanent_mac(struct lldpd *cfg,
277 struct interfaces_device_list *interfaces,
278 struct interfaces_device *iface)
279 {
280 struct interfaces_device *master;
281 int f, state = 0;
282 FILE *netbond;
283 const char *slaveif = "Slave Interface: ";
284 const char *hwaddr = "Permanent HW addr: ";
285 u_int8_t mac[ETHER_ADDR_LEN];
286 char path[SYSFS_PATH_MAX];
287 char line[100];
288
289 if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T)
290 return;
291
292 /* We have a bond, we need to query it to get real MAC addresses */
293 if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
294 master->name) >= SYSFS_PATH_MAX) {
295 log_warnx("interfaces", "path truncated");
296 return;
297 }
298 if ((f = priv_open(path)) < 0) {
299 if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
300 master->name) >= SYSFS_PATH_MAX) {
301 log_warnx("interfaces", "path truncated");
302 return;
303 }
304 f = priv_open(path);
305 }
306 if (f < 0) {
307 iflinux_get_permanent_mac_ethtool(cfg, interfaces, iface);
308 return;
309 }
310 if ((netbond = fdopen(f, "r")) == NULL) {
311 log_warn("interfaces", "unable to read stream from %s", path);
312 close(f);
313 return;
314 }
315 /* State 0:
316 We parse the file to search "Slave Interface: ". If found, go to
317 state 1.
318 State 1:
319 We parse the file to search "Permanent HW addr: ". If found, we get
320 the mac.
321 */
322 while (fgets(line, sizeof(line), netbond)) {
323 switch (state) {
324 case 0:
325 if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
326 if (line[strlen(line)-1] == '\n')
327 line[strlen(line)-1] = '\0';
328 if (strcmp(iface->name,
329 line + strlen(slaveif)) == 0)
330 state++;
331 }
332 break;
333 case 1:
334 if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
335 if (line[strlen(line)-1] == '\n')
336 line[strlen(line)-1] = '\0';
337 if (sscanf(line + strlen(hwaddr),
338 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
339 &mac[0], &mac[1], &mac[2],
340 &mac[3], &mac[4], &mac[5]) !=
341 ETHER_ADDR_LEN) {
342 log_warn("interfaces", "unable to parse %s",
343 line + strlen(hwaddr));
344 fclose(netbond);
345 return;
346 }
347 memcpy(iface->address, mac,
348 ETHER_ADDR_LEN);
349 fclose(netbond);
350 return;
351 }
352 break;
353 }
354 }
355 log_warnx("interfaces", "unable to find real mac address for %s",
356 iface->name);
357 fclose(netbond);
358 }
359
360 /* Fill up MAC/PHY for a given hardware port */
361 static void
362 iflinux_macphy(struct lldpd_hardware *hardware)
363 {
364 #ifdef ENABLE_DOT3
365 struct ethtool_cmd ethc;
366 struct lldpd_port *port = &hardware->h_lport;
367 int j;
368 int advertised_ethtool_to_rfc3636[][2] = {
369 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
370 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
371 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
372 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
373 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
374 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
375 {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
376 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
377 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
378 {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
379 {0,0}};
380
381 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
382 hardware->h_ifname);
383 if (priv_ethtool(hardware->h_ifname, &ethc, sizeof(struct ethtool_cmd)) == 0) {
384 port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
385 port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
386 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
387 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
388 port->p_macphy.autoneg_advertised |=
389 advertised_ethtool_to_rfc3636[j][1];
390 }
391 switch (ethc.speed) {
392 case SPEED_10:
393 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
394 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
395 if (ethc.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
396 if (ethc.port == PORT_FIBRE)
397 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
398 LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD;
399 break;
400 case SPEED_100:
401 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
402 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
403 if (ethc.port == PORT_BNC)
404 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
405 LLDP_DOT3_MAU_100BASET2FD : LLDP_DOT3_MAU_100BASET2HD;
406 if (ethc.port == PORT_FIBRE)
407 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
408 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
409 break;
410 case SPEED_1000:
411 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
412 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
413 if (ethc.port == PORT_FIBRE)
414 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
415 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
416 break;
417 case SPEED_10000:
418 port->p_macphy.mau_type = (ethc.port == PORT_FIBRE) ? \
419 LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
420 break;
421 }
422 if (ethc.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
423 }
424 #endif
425 }
426
427 struct bond_master {
428 char name[IFNAMSIZ];
429 int index;
430 };
431
432 static int
433 iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
434 {
435 struct bond_master *master = hardware->h_data;
436 int fd;
437 int un = 1;
438
439 if (!master) return -1;
440
441 log_debug("interfaces", "initialize enslaved device %s",
442 hardware->h_ifname);
443
444 /* First, we get a socket to the raw physical interface */
445 if ((fd = priv_iface_init(hardware->h_ifindex,
446 hardware->h_ifname)) == -1)
447 return -1;
448 hardware->h_sendfd = fd;
449 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
450
451 /* Then, we open a raw interface for the master */
452 log_debug("interfaces", "enslaved device %s has master %s(%d)",
453 hardware->h_ifname, master->name, master->index);
454 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
455 close(hardware->h_sendfd);
456 return -1;
457 }
458 /* With bonding and older kernels (< 2.6.27) we need to listen
459 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
460 * physical device instead of bond device (works with >=
461 * 2.6.24). */
462 if (setsockopt(fd, SOL_PACKET,
463 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
464 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
465 "You will get inaccurate results",
466 hardware->h_ifname);
467 }
468 interfaces_setup_multicast(cfg, master->name, 0);
469
470 levent_hardware_add_fd(hardware, hardware->h_sendfd);
471 levent_hardware_add_fd(hardware, fd);
472 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
473 hardware->h_ifname,
474 hardware->h_sendfd,
475 master->name, fd);
476 return 0;
477 }
478
479 static int
480 iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
481 int fd, char *buffer, size_t size)
482 {
483 int n;
484 struct sockaddr_ll from = {};
485 socklen_t fromlen;
486 struct bond_master *master = hardware->h_data;
487
488 log_debug("interfaces", "receive PDU from enslaved device %s",
489 hardware->h_ifname);
490 fromlen = sizeof(from);
491 if ((n = recvfrom(fd, buffer, size, 0,
492 (struct sockaddr *)&from,
493 &fromlen)) == -1) {
494 log_warn("interfaces", "error while receiving frame on %s",
495 hardware->h_ifname);
496 hardware->h_rx_discarded_cnt++;
497 return -1;
498 }
499 if (from.sll_pkttype == PACKET_OUTGOING)
500 return -1;
501 if (fd == hardware->h_sendfd)
502 /* We received this on the physical interface. */
503 return n;
504 /* We received this on the bonding interface. Is it really for us? */
505 if (from.sll_ifindex == hardware->h_ifindex)
506 /* This is for us */
507 return n;
508 if (from.sll_ifindex == master->index)
509 /* We don't know from which physical interface it comes (kernel
510 * < 2.6.24). In doubt, this is for us. */
511 return n;
512 return -1; /* Not for us */
513 }
514
515 static int
516 iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
517 {
518 struct bond_master *master = hardware->h_data;
519 log_debug("interfaces", "closing enslaved device %s",
520 hardware->h_ifname);
521 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
522 interfaces_setup_multicast(cfg, master->name, 1);
523 free(hardware->h_data); hardware->h_data = NULL;
524 return 0;
525 }
526
527 struct lldpd_ops bond_ops = {
528 .send = iflinux_eth_send,
529 .recv = iface_bond_recv,
530 .cleanup = iface_bond_close,
531 };
532
533 static void
534 iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
535 {
536 struct interfaces_device *iface;
537 struct interfaces_device *master;
538 struct lldpd_hardware *hardware;
539 struct bond_master *bmaster;
540 int created;
541 TAILQ_FOREACH(iface, interfaces, next) {
542 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
543 if (iface->ignore) continue;
544 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
545
546 master = iface->upper;
547 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
548 iface->name, master->name);
549 created = 0;
550 if ((hardware = lldpd_get_hardware(cfg,
551 iface->name,
552 iface->index)) == NULL) {
553 if ((hardware = lldpd_alloc_hardware(cfg,
554 iface->name,
555 iface->index)) == NULL) {
556 log_warnx("interfaces", "Unable to allocate space for %s",
557 iface->name);
558 continue;
559 }
560 created = 1;
561 }
562 if (hardware->h_flags) continue;
563 if (hardware->h_ops != &bond_ops) {
564 if (!created) {
565 log_debug("interfaces",
566 "bond %s is converted from another type of interface",
567 hardware->h_ifname);
568 if (hardware->h_ops && hardware->h_ops->cleanup)
569 hardware->h_ops->cleanup(cfg, hardware);
570 levent_hardware_release(hardware);
571 levent_hardware_init(hardware);
572 }
573 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
574 if (!bmaster) {
575 log_warn("interfaces", "not enough memory");
576 lldpd_hardware_cleanup(cfg, hardware);
577 continue;
578 }
579 } else bmaster = hardware->h_data;
580 bmaster->index = master->index;
581 strlcpy(bmaster->name, master->name, IFNAMSIZ);
582 if (hardware->h_ops != &bond_ops) {
583 if (iface_bond_init(cfg, hardware) != 0) {
584 log_warn("interfaces", "unable to initialize %s",
585 hardware->h_ifname);
586 lldpd_hardware_cleanup(cfg, hardware);
587 continue;
588 }
589 hardware->h_ops = &bond_ops;
590 hardware->h_mangle = 1;
591 }
592 if (created)
593 interfaces_helper_add_hardware(cfg, hardware);
594 else
595 lldpd_port_cleanup(&hardware->h_lport, 0);
596
597 hardware->h_flags = iface->flags;
598 iface->ignore = 1;
599
600 /* Get local address */
601 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
602
603 /* Fill information about port */
604 interfaces_helper_port_name_desc(cfg, hardware, iface);
605
606 /* Fill additional info */
607 #ifdef ENABLE_DOT3
608 hardware->h_lport.p_aggregid = master->index;
609 #endif
610 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
611 }
612 }
613
614 /* Query each interface to get the appropriate driver */
615 static void
616 iflinux_add_driver(struct lldpd *cfg,
617 struct interfaces_device_list *interfaces)
618 {
619 struct interfaces_device *iface;
620 TAILQ_FOREACH(iface, interfaces, next) {
621 struct ethtool_drvinfo ethc = {
622 .cmd = ETHTOOL_GDRVINFO
623 };
624 struct ifreq ifr = {
625 .ifr_data = (caddr_t)&ethc
626 };
627 if (iface->driver) continue;
628
629 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
630 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
631 iface->driver = strdup(ethc.driver);
632 log_debug("interfaces", "driver for %s is `%s`",
633 iface->name, iface->driver);
634 }
635 }
636 }
637
638 /* Query each interface to see if it is a wireless one */
639 static void
640 iflinux_add_wireless(struct lldpd *cfg,
641 struct interfaces_device_list *interfaces)
642 {
643 struct interfaces_device *iface;
644 TAILQ_FOREACH(iface, interfaces, next) {
645 struct iwreq iwr = {};
646 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
647 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
648 log_debug("interfaces", "%s is wireless",
649 iface->name);
650 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
651 }
652 }
653 }
654
655 /* Query each interface to see if it is a bridge */
656 static void
657 iflinux_add_bridge(struct lldpd *cfg,
658 struct interfaces_device_list *interfaces)
659 {
660 struct interfaces_device *iface;
661
662 TAILQ_FOREACH(iface, interfaces, next) {
663 if (iface->type & (IFACE_PHYSICAL_T|
664 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
665 continue;
666 if (iflinux_is_bridge(cfg, interfaces, iface)) {
667 log_debug("interfaces",
668 "interface %s is a bridge",
669 iface->name);
670 iface->type |= IFACE_BRIDGE_T;
671 }
672 }
673 }
674
675 /* Query each interface to see if it is a bond */
676 static void
677 iflinux_add_bond(struct lldpd *cfg,
678 struct interfaces_device_list *interfaces)
679 {
680 struct interfaces_device *iface;
681
682 TAILQ_FOREACH(iface, interfaces, next) {
683 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
684 IFACE_BOND_T|IFACE_BRIDGE_T))
685 continue;
686 if (iflinux_is_bond(cfg, interfaces, iface)) {
687 log_debug("interfaces",
688 "interface %s is a bond",
689 iface->name);
690 iface->type |= IFACE_BOND_T;
691 }
692 }
693 }
694
695 /* Query each interface to see if it is a vlan */
696 static void
697 iflinux_add_vlan(struct lldpd *cfg,
698 struct interfaces_device_list *interfaces)
699 {
700 struct interfaces_device *iface;
701
702 TAILQ_FOREACH(iface, interfaces, next) {
703 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
704 IFACE_BOND_T|IFACE_BRIDGE_T))
705 continue;
706 if (iflinux_is_vlan(cfg, interfaces, iface)) {
707 log_debug("interfaces",
708 "interface %s is a VLAN",
709 iface->name);
710 iface->type |= IFACE_VLAN_T;
711 }
712 }
713 }
714
715 static void
716 iflinux_add_physical(struct lldpd *cfg,
717 struct interfaces_device_list *interfaces)
718 {
719 struct interfaces_device *iface;
720
721 TAILQ_FOREACH(iface, interfaces, next) {
722 if (iface->type & (IFACE_VLAN_T|
723 IFACE_BOND_T|IFACE_BRIDGE_T))
724 continue;
725
726 iface->type &= ~IFACE_PHYSICAL_T;
727
728 /* We request that the interface is able to do either multicast
729 * or broadcast to be able to send discovery frames. */
730 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
731 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
732 iface->name);
733 continue;
734 }
735
736 /* If the interface is linked to another one, skip it too. */
737 if (iface->lower && (!iface->driver || strcmp(iface->driver, "veth"))) {
738 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
739 iface->name, iface->lower->name);
740 continue;
741 }
742
743 /* Get the real MAC address (for example, if the interface is enslaved) */
744 iflinux_get_permanent_mac(cfg, interfaces, iface);
745
746 log_debug("interfaces",
747 "%s is a physical interface",
748 iface->name);
749 iface->type |= IFACE_PHYSICAL_T;
750 }
751 }
752
753 void
754 interfaces_update(struct lldpd *cfg)
755 {
756 struct lldpd_hardware *hardware;
757 struct interfaces_device_list *interfaces;
758 struct interfaces_address_list *addresses;
759 interfaces = netlink_get_interfaces(cfg);
760 addresses = netlink_get_addresses(cfg);
761 if (interfaces == NULL || addresses == NULL) {
762 log_warnx("interfaces", "cannot update the list of local interfaces");
763 return;
764 }
765
766 /* Add missing bits to list of interfaces */
767 iflinux_add_driver(cfg, interfaces);
768 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
769 iflinux_add_wireless(cfg, interfaces);
770 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
771 iflinux_add_bridge(cfg, interfaces);
772 iflinux_add_bond(cfg, interfaces);
773 iflinux_add_vlan(cfg, interfaces);
774 iflinux_add_physical(cfg, interfaces);
775
776 interfaces_helper_whitelist(cfg, interfaces);
777 iflinux_handle_bond(cfg, interfaces);
778 interfaces_helper_physical(cfg, interfaces,
779 &eth_ops,
780 iflinux_eth_init);
781 #ifdef ENABLE_DOT1
782 interfaces_helper_vlan(cfg, interfaces);
783 #endif
784 interfaces_helper_mgmt(cfg, addresses);
785 interfaces_helper_chassis(cfg, interfaces);
786
787 /* Mac/PHY */
788 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
789 if (!hardware->h_flags) continue;
790 iflinux_macphy(hardware);
791 interfaces_helper_promisc(cfg, hardware);
792 }
793 }
794
795 void
796 interfaces_cleanup(struct lldpd *cfg)
797 {
798 netlink_cleanup(cfg);
799 }