]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/interfaces-linux.c
interfaces: remove specific handling for bonds except with --enable-oldies
[thirdparty/lldpd.git] / src / daemon / interfaces-linux.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
6e75df87
VB
2/*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
51434125 5 * Permission to use, copy, modify, and/or distribute this software for any
6e75df87
VB
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
6e75df87
VB
18#include "lldpd.h"
19
20#include <stdio.h>
21#include <unistd.h>
78c9dfde 22#include <inttypes.h>
6e75df87
VB
23#include <errno.h>
24#include <sys/ioctl.h>
d7460a6f
ALG
25#if defined(__clang__)
26#pragma clang diagnostic push
27#pragma clang diagnostic ignored "-Wdocumentation"
28#endif
6e75df87
VB
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>
6e75df87 34#include <linux/if_packet.h>
e12c2365 35#include <linux/ethtool.h>
d7460a6f
ALG
36#if defined(__clang__)
37#pragma clang diagnostic pop
38#endif
6e75df87
VB
39
40#define SYSFS_PATH_MAX 256
41#define MAX_PORTS 1024
42#define MAX_BRIDGES 1024
43
88bc404f
VB
44static int
45iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
46{
e735a319 47 int fd;
88bc404f
VB
48
49 log_debug("interfaces", "initialize ethernet device %s",
50 hardware->h_ifname);
e735a319 51 if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
88bc404f
VB
52 return -1;
53 hardware->h_sendfd = fd; /* Send */
54
88bc404f
VB
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
22e8cd65
VB
63/* Generic ethernet send/receive */
64static int
65iflinux_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
54cccdd4
VB
74static void
75iflinux_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
22e8cd65 97static int
462ef2ce
VB
98iflinux_generic_recv(struct lldpd_hardware *hardware,
99 int fd, char *buffer, size_t size,
100 struct sockaddr_ll *from)
22e8cd65 101{
54cccdd4
VB
102 int n, retry = 0;
103 socklen_t fromlen;
22e8cd65 104
54cccdd4
VB
105retry:
106 fromlen = sizeof(*from);
107 memset(from, 0, fromlen);
462ef2ce
VB
108 if ((n = recvfrom(fd, buffer, size, 0,
109 (struct sockaddr *)from,
22e8cd65 110 &fromlen)) == -1) {
54cccdd4
VB
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 }
afd49b83
VB
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 }
22e8cd65
VB
125 return -1;
126 }
462ef2ce
VB
127 if (from->sll_pkttype == PACKET_OUTGOING)
128 return -1;
129 return n;
130}
131
132static int
133iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
134 int fd, char *buffer, size_t size)
135{
136 int n;
54cccdd4 137 struct sockaddr_ll from;
462ef2ce
VB
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)
22e8cd65
VB
142 return -1;
143 return n;
144}
145
146static int
147iflinux_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
155static struct lldpd_ops eth_ops = {
156 .send = iflinux_eth_send,
157 .recv = iflinux_eth_recv,
158 .cleanup = iflinux_eth_close,
159};
160
6e75df87 161static int
adbb6e54
VB
162iflinux_is_bridge(struct lldpd *cfg,
163 struct interfaces_device_list *interfaces,
164 struct interfaces_device *iface)
6e75df87 165{
16eacc5b 166#ifdef ENABLE_OLDIES
adbb6e54 167 struct interfaces_device *port;
6e75df87
VB
168 char path[SYSFS_PATH_MAX];
169 int f;
170
adbb6e54
VB
171 if ((snprintf(path, SYSFS_PATH_MAX,
172 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
173 iface->name)) >= SYSFS_PATH_MAX)
6f8925be 174 log_warnx("interfaces", "path truncated");
adbb6e54 175 if ((f = priv_open(path)) < 0)
31c9173a 176 return 0;
6e75df87 177 close(f);
adbb6e54
VB
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
6e75df87 195 return 1;
16eacc5b
VB
196#else
197 return 0;
198#endif
6e75df87
VB
199}
200
201static int
adbb6e54
VB
202iflinux_is_vlan(struct lldpd *cfg,
203 struct interfaces_device_list *interfaces,
204 struct interfaces_device *iface)
6e75df87 205{
16eacc5b 206#ifdef ENABLE_OLDIES
b0cb07f7 207 struct vlan_ioctl_args ifv = {};
6e75df87 208 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
adbb6e54
VB
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 }
6e75df87 220
adbb6e54
VB
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 }
6e75df87 230
adbb6e54
VB
231 iface->lower = lower;
232 iface->vlanid = ifv.u.VID;
6e75df87 233 return 1;
adbb6e54 234 }
16eacc5b 235#endif
6e75df87
VB
236 return 0;
237}
238
239static int
adbb6e54
VB
240iflinux_is_bond(struct lldpd *cfg,
241 struct interfaces_device_list *interfaces,
242 struct interfaces_device *master)
6e75df87 243{
16eacc5b 244#ifdef ENABLE_OLDIES
f5a0a15e
VB
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
b0cb07f7
VB
251 struct ifreq ifr = {};
252 struct ifbond ifb = {};
c557a63b 253 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
8f88ff70 254 ifr.ifr_data = (char *)&ifb;
6e75df87
VB
255 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
256 while (ifb.num_slaves--) {
adbb6e54 257 struct ifslave ifs;
6e75df87
VB
258 memset(&ifr, 0, sizeof(ifr));
259 memset(&ifs, 0, sizeof(ifs));
c557a63b 260 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
8f88ff70 261 ifr.ifr_data = (char *)&ifs;
6e75df87 262 ifs.slave_id = ifb.num_slaves;
adbb6e54
VB
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;
6e75df87
VB
273 }
274 }
adbb6e54 275 return 1;
6e75df87 276 }
16eacc5b 277#endif
6e75df87
VB
278 return 0;
279}
280
2d8bd087
VB
281/**
282 * Get permanent MAC from ethtool.
283 *
284 * Return 0 on success, -1 on error.
285 */
286static int
287iflinux_get_permanent_mac_ethtool(struct lldpd *cfg,
288 struct interfaces_device_list *interfaces,
289 struct interfaces_device *iface)
290{
8505c295 291 int ret = -1;
2d8bd087 292 struct ifreq ifr = {};
8505c295
VB
293 struct ethtool_perm_addr *epaddr = calloc(sizeof(struct ethtool_perm_addr) + ETHER_ADDR_LEN, 1);
294 if (epaddr == NULL) goto end;
2d8bd087
VB
295
296 strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
8505c295
VB
297 epaddr->cmd = ETHTOOL_GPERMADDR;
298 epaddr->size = ETHER_ADDR_LEN;
299 ifr.ifr_data = (caddr_t)epaddr;
2d8bd087
VB
300 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == -1) {
301 static int once = 0;
302 if (errno == EPERM && !once) {
822804ac 303 log_warnx("interfaces",
2d8bd087
VB
304 "no permission to get permanent MAC address for %s (requires 2.6.19+)",
305 iface->name);
306 once = 1;
8505c295 307 goto end;
2d8bd087
VB
308 }
309 if (errno != EPERM)
c4c9f15a 310 log_warn("interfaces", "cannot get permanent MAC address for %s",
2d8bd087 311 iface->name);
8505c295 312 goto end;
2d8bd087 313 }
8505c295
VB
314 if (epaddr->data[0] != 0 ||
315 epaddr->data[1] != 0 ||
316 epaddr->data[2] != 0 ||
317 epaddr->data[3] != 0 ||
318 epaddr->data[4] != 0 ||
319 epaddr->data[5] != 0) {
320 memcpy(iface->address, epaddr->data, ETHER_ADDR_LEN);
321 ret = 0;
322 goto end;
2d8bd087 323 }
c4c9f15a 324 log_debug("interfaces", "cannot get permanent MAC for %s (all 0)", iface->name);
8505c295
VB
325 end:
326 free(epaddr);
327 return ret;
2d8bd087
VB
328}
329
330/**
331 * Get permanent MAC address for a bond device.
332 */
1a3ec373 333static void
2d8bd087 334iflinux_get_permanent_mac_bond(struct lldpd *cfg,
01293dc4
VB
335 struct interfaces_device_list *interfaces,
336 struct interfaces_device *iface)
337{
2d8bd087 338 struct interfaces_device *master = iface->upper;
01293dc4
VB
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
01293dc4
VB
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) {
0b7f8847
VB
362 log_warnx("interfaces",
363 "unable to get permanent MAC address for %s",
2d8bd087 364 iface->name);
01293dc4
VB
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 }
2d8bd087 412 log_warnx("interfaces", "unable to find real MAC address for enslaved %s",
01293dc4
VB
413 iface->name);
414 fclose(netbond);
415}
416
2d8bd087
VB
417/**
418 * Get permanent MAC.
419 */
420static void
421iflinux_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
dd55e649 435#ifdef ENABLE_DOT3
78c9dfde
VB
436#define ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32 (SCHAR_MAX)
437#define ETHTOOL_DECLARE_LINK_MODE_MASK(name) \
438 uint32_t name[ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32]
439
440struct ethtool_link_usettings {
441 struct ethtool_link_settings base;
442 struct {
443 ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
444 ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
445 ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
446 } link_modes;
447};
448
e86777ab 449static int
38cbcf2b
VB
450iflinux_ethtool_link_mode_test_bit(unsigned int nr, const uint32_t *mask)
451{
452 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
453 return 0;
454 return !!(mask[nr / 32] & (1 << (nr % 32)));
455}
e86777ab 456static void
38cbcf2b
VB
457iflinux_ethtool_link_mode_unset_bit(unsigned int nr, uint32_t *mask)
458{
459 if (nr >= 32 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32)
460 return;
461 mask[nr / 32] &= ~(1 << (nr % 32));
462}
e86777ab 463static int
38cbcf2b
VB
464iflinux_ethtool_link_mode_is_empty(const uint32_t *mask)
465{
466 for (unsigned int i = 0;
467 i < ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32;
468 ++i) {
469 if (mask[i] != 0)
470 return 0;
471 }
472
473 return 1;
474}
475
78c9dfde
VB
476static int
477iflinux_ethtool_glink(struct lldpd *cfg, const char *ifname, struct ethtool_link_usettings *uset) {
478 int rc;
479
480 /* Try with ETHTOOL_GLINKSETTINGS first */
481 struct {
482 struct ethtool_link_settings req;
483 uint32_t link_mode_data[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32];
484 } ecmd;
485 static int8_t nwords = 0;
486 struct ifreq ifr = {};
487 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
488
489 if (nwords == 0) {
490 /* Do a handshake first. We assume that this is device-independant. */
491 memset(&ecmd, 0, sizeof(ecmd));
492 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
493 ifr.ifr_data = (caddr_t)&ecmd;
494 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
495 if (rc == 0) {
496 nwords = -ecmd.req.link_mode_masks_nwords;
497 log_debug("interfaces", "glinksettings nwords is %" PRId8, nwords);
68caeec4
VB
498 } else {
499 static int once = 0;
500 if (errno == EPERM && !once) {
822804ac 501 log_warnx("interfaces",
68caeec4 502 "cannot get ethtool link information "
822804ac
VB
503 "with GLINKSETTINGS (requires 4.9+). "
504 "25G+ speeds may be missing in MAC/PHY TLVs");
68caeec4
VB
505 once = 1;
506 }
507 nwords = -1;
508 }
78c9dfde
VB
509 }
510 if (nwords > 0) {
511 memset(&ecmd, 0, sizeof(ecmd));
512 ecmd.req.cmd = ETHTOOL_GLINKSETTINGS;
513 ecmd.req.link_mode_masks_nwords = nwords;
514 ifr.ifr_data = (caddr_t)&ecmd;
515 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
516 if (rc == 0) {
517 log_debug("interfaces", "got ethtool results for %s with GLINKSETTINGS",
518 ifname);
519 memcpy(&uset->base, &ecmd.req, sizeof(uset->base));
520 unsigned int u32_offs = 0;
521 memcpy(uset->link_modes.supported,
522 &ecmd.link_mode_data[u32_offs],
523 4 * ecmd.req.link_mode_masks_nwords);
524 u32_offs += ecmd.req.link_mode_masks_nwords;
525 memcpy(uset->link_modes.advertising,
526 &ecmd.link_mode_data[u32_offs],
527 4 * ecmd.req.link_mode_masks_nwords);
528 u32_offs += ecmd.req.link_mode_masks_nwords;
529 memcpy(uset->link_modes.lp_advertising,
530 &ecmd.link_mode_data[u32_offs],
531 4 * ecmd.req.link_mode_masks_nwords);
532 goto end;
533 }
534 }
535
536 /* Try with ETHTOOL_GSET */
537 struct ethtool_cmd ethc;
538 memset(&ethc, 0, sizeof(ethc));
539 ethc.cmd = ETHTOOL_GSET;
540 ifr.ifr_data = (caddr_t)&ethc;
541 rc = ioctl(cfg->g_sock, SIOCETHTOOL, &ifr);
542 if (rc == 0) {
543 /* Do a partial copy (only what we need) */
544 log_debug("interfaces", "got ethtool results for %s with GSET",
545 ifname);
546 memset(uset, 0, sizeof(*uset));
547 uset->base.cmd = ETHTOOL_GSET;
548 uset->base.link_mode_masks_nwords = 1;
549 uset->link_modes.supported[0] = ethc.supported;
550 uset->link_modes.advertising[0] = ethc.advertising;
551 uset->link_modes.lp_advertising[0] = ethc.lp_advertising;
552 uset->base.speed = (ethc.speed_hi << 16) | ethc.speed;
553 uset->base.duplex = ethc.duplex;
554 uset->base.port = ethc.port;
555 uset->base.autoneg = ethc.autoneg;
68caeec4
VB
556 } else {
557 static int once = 0;
558 if (errno == EPERM && !once) {
822804ac 559 log_warnx("interfaces",
68caeec4 560 "cannot get ethtool link information "
822804ac
VB
561 "with GSET (requires 2.6.19+). "
562 "MAC/PHY TLV will be unavailable");
68caeec4
VB
563 once = 1;
564 }
78c9dfde
VB
565 }
566end:
567 return rc;
568}
38cbcf2b 569
6e75df87
VB
570/* Fill up MAC/PHY for a given hardware port */
571static void
78c9dfde 572iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
6e75df87 573{
38cbcf2b 574 struct ethtool_link_usettings uset;
6e75df87
VB
575 struct lldpd_port *port = &hardware->h_lport;
576 int j;
577 int advertised_ethtool_to_rfc3636[][2] = {
38cbcf2b
VB
578 {ETHTOOL_LINK_MODE_10baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
579 {ETHTOOL_LINK_MODE_10baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
580 {ETHTOOL_LINK_MODE_100baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
581 {ETHTOOL_LINK_MODE_100baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
582 {ETHTOOL_LINK_MODE_1000baseT_Half_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
583 {ETHTOOL_LINK_MODE_1000baseT_Full_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
584 {ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD},
585 {ETHTOOL_LINK_MODE_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
586 {ETHTOOL_LINK_MODE_Asym_Pause_BIT, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
587 {-1, 0}};
6e75df87 588
6f8925be
VB
589 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
590 hardware->h_ifname);
78c9dfde 591 if (iflinux_ethtool_glink(cfg, hardware->h_ifname, &uset) == 0) {
38cbcf2b
VB
592 port->p_macphy.autoneg_support = iflinux_ethtool_link_mode_test_bit(
593 ETHTOOL_LINK_MODE_Autoneg_BIT, uset.link_modes.supported);
594 port->p_macphy.autoneg_enabled = (uset.base.autoneg == AUTONEG_DISABLE) ? 0 : 1;
595 for (j=0; advertised_ethtool_to_rfc3636[j][0] >= 0; j++) {
596 if (iflinux_ethtool_link_mode_test_bit(
597 advertised_ethtool_to_rfc3636[j][0],
598 uset.link_modes.advertising)) {
a97e3dfe 599 port->p_macphy.autoneg_advertised |=
6e75df87 600 advertised_ethtool_to_rfc3636[j][1];
38cbcf2b
VB
601 iflinux_ethtool_link_mode_unset_bit(
602 advertised_ethtool_to_rfc3636[j][0],
603 uset.link_modes.advertising);
a97e3dfe 604 }
6e75df87 605 }
38cbcf2b
VB
606 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, uset.link_modes.advertising);
607 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_TP_BIT, uset.link_modes.advertising);
608 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_AUI_BIT, uset.link_modes.advertising);
609 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_MII_BIT, uset.link_modes.advertising);
610 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, uset.link_modes.advertising);
611 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_BNC_BIT, uset.link_modes.advertising);
612 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Pause_BIT, uset.link_modes.advertising);
613 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, uset.link_modes.advertising);
614 iflinux_ethtool_link_mode_unset_bit(ETHTOOL_LINK_MODE_Backplane_BIT, uset.link_modes.advertising);
615 if (!iflinux_ethtool_link_mode_is_empty(uset.link_modes.advertising)) {
a97e3dfe 616 port->p_macphy.autoneg_advertised |= LLDP_DOT3_LINK_AUTONEG_OTHER;
38cbcf2b
VB
617 }
618 switch (uset.base.speed) {
6e75df87 619 case SPEED_10:
38cbcf2b 620 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
6e75df87 621 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
38cbcf2b
VB
622 if (uset.base.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
623 if (uset.base.port == PORT_FIBRE)
624 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
fda729fd 625 LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD;
6e75df87
VB
626 break;
627 case SPEED_100:
38cbcf2b 628 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
6e75df87 629 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
38cbcf2b
VB
630 if (uset.base.port == PORT_BNC)
631 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
5fa8eaa5 632 LLDP_DOT3_MAU_100BASET2FD : LLDP_DOT3_MAU_100BASET2HD;
38cbcf2b
VB
633 if (uset.base.port == PORT_FIBRE)
634 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
6e75df87
VB
635 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
636 break;
637 case SPEED_1000:
38cbcf2b 638 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
6e75df87 639 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
38cbcf2b
VB
640 if (uset.base.port == PORT_FIBRE)
641 port->p_macphy.mau_type = (uset.base.duplex == DUPLEX_FULL) ? \
6e75df87
VB
642 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
643 break;
644 case SPEED_10000:
a97e3dfe
VB
645 // For fiber, we tell 10GIGBASEX and for others,
646 // 10GIGBASER. It's not unusual to have 10GIGBASER on
647 // fiber either but we don't have 10GIGBASET for
648 // copper. No good solution.
38cbcf2b
VB
649 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
650 LLDP_DOT3_MAU_10GIGBASELR : LLDP_DOT3_MAU_10GIGBASECX4;
6e75df87 651 break;
38cbcf2b
VB
652 case SPEED_40000:
653 // Same kind of approximation.
654 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
655 LLDP_DOT3_MAU_40GBASELR4 : LLDP_DOT3_MAU_40GBASECR4;
3a59626a 656 break;
38cbcf2b
VB
657 case SPEED_100000:
658 // Ditto
659 port->p_macphy.mau_type = (uset.base.port == PORT_FIBRE) ? \
660 LLDP_DOT3_MAU_100GBASELR4 : LLDP_DOT3_MAU_100GBASECR10;
3a59626a 661 break;
6e75df87 662 }
38cbcf2b 663 if (uset.base.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
6e75df87 664 }
6e75df87 665}
dd55e649
VB
666#else /* ENABLE_DOT3 */
667static void
668iflinux_macphy(struct lldpd *cfg, struct lldpd_hardware *hardware)
669{
670}
671#endif /* ENABLE_DOT3 */
672
6e75df87 673
2f4ff307 674#ifdef ENABLE_OLDIES
e12c2365
VB
675struct bond_master {
676 char name[IFNAMSIZ];
677 int index;
678};
679
849954d7
VB
680static int
681iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
682{
e12c2365 683 struct bond_master *master = hardware->h_data;
e735a319 684 int fd;
849954d7
VB
685 int un = 1;
686
e12c2365 687 if (!master) return -1;
849954d7 688
0da01fd6 689 log_debug("interfaces", "initialize enslaved device %s",
6f8925be
VB
690 hardware->h_ifname);
691
849954d7 692 /* First, we get a socket to the raw physical interface */
e735a319
VB
693 if ((fd = priv_iface_init(hardware->h_ifindex,
694 hardware->h_ifname)) == -1)
849954d7
VB
695 return -1;
696 hardware->h_sendfd = fd;
adbb6e54 697 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
849954d7
VB
698
699 /* Then, we open a raw interface for the master */
0da01fd6 700 log_debug("interfaces", "enslaved device %s has master %s(%d)",
148a1efe 701 hardware->h_ifname, master->name, master->index);
e735a319 702 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
849954d7
VB
703 close(hardware->h_sendfd);
704 return -1;
705 }
849954d7
VB
706 /* With bonding and older kernels (< 2.6.27) we need to listen
707 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
708 * physical device instead of bond device (works with >=
709 * 2.6.24). */
710 if (setsockopt(fd, SOL_PACKET,
e12c2365
VB
711 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
712 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
713 "You will get inaccurate results",
714 hardware->h_ifname);
849954d7 715 }
adbb6e54 716 interfaces_setup_multicast(cfg, master->name, 0);
849954d7 717
ba93c521 718 levent_hardware_add_fd(hardware, hardware->h_sendfd);
d6e889b6 719 levent_hardware_add_fd(hardware, fd);
6f8925be 720 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
849954d7
VB
721 hardware->h_ifname,
722 hardware->h_sendfd,
e12c2365 723 master->name, fd);
849954d7
VB
724 return 0;
725}
726
849954d7
VB
727static int
728iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
729 int fd, char *buffer, size_t size)
730{
731 int n;
54cccdd4 732 struct sockaddr_ll from;
e12c2365 733 struct bond_master *master = hardware->h_data;
849954d7 734
0da01fd6 735 log_debug("interfaces", "receive PDU from enslaved device %s",
6f8925be 736 hardware->h_ifname);
462ef2ce 737 if ((n = iflinux_generic_recv(hardware, fd, buffer, size, &from)) == -1)
849954d7
VB
738 return -1;
739 if (fd == hardware->h_sendfd)
740 /* We received this on the physical interface. */
741 return n;
742 /* We received this on the bonding interface. Is it really for us? */
44002d66 743 if (from.sll_ifindex == hardware->h_ifindex)
849954d7
VB
744 /* This is for us */
745 return n;
e12c2365 746 if (from.sll_ifindex == master->index)
849954d7
VB
747 /* We don't know from which physical interface it comes (kernel
748 * < 2.6.24). In doubt, this is for us. */
749 return n;
750 return -1; /* Not for us */
751}
752
753static int
754iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
755{
e12c2365 756 struct bond_master *master = hardware->h_data;
0da01fd6 757 log_debug("interfaces", "closing enslaved device %s",
6f8925be 758 hardware->h_ifname);
adbb6e54
VB
759 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
760 interfaces_setup_multicast(cfg, master->name, 1);
32945d6a 761 free(hardware->h_data); hardware->h_data = NULL;
849954d7
VB
762 return 0;
763}
764
adbb6e54 765struct lldpd_ops bond_ops = {
5347914e 766 .send = iflinux_eth_send,
adbb6e54
VB
767 .recv = iface_bond_recv,
768 .cleanup = iface_bond_close,
769};
770
e12c2365 771static void
adbb6e54 772iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
849954d7 773{
adbb6e54
VB
774 struct interfaces_device *iface;
775 struct interfaces_device *master;
849954d7 776 struct lldpd_hardware *hardware;
c557a63b 777 struct bond_master *bmaster;
32945d6a 778 int created;
e12c2365 779 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54 780 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
0fa2254b 781 if (iface->ignore) continue;
adbb6e54 782 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
849954d7 783
adbb6e54 784 master = iface->upper;
0da01fd6 785 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
c557a63b 786 iface->name, master->name);
32945d6a 787 created = 0;
44002d66 788 if ((hardware = lldpd_get_hardware(cfg,
e12c2365 789 iface->name,
32945d6a 790 iface->index)) == NULL) {
849954d7 791 if ((hardware = lldpd_alloc_hardware(cfg,
e12c2365
VB
792 iface->name,
793 iface->index)) == NULL) {
6f8925be 794 log_warnx("interfaces", "Unable to allocate space for %s",
e12c2365
VB
795 iface->name);
796 continue;
797 }
32945d6a
VB
798 created = 1;
799 }
800 if (hardware->h_flags) continue;
801 if (hardware->h_ops != &bond_ops) {
802 if (!created) {
803 log_debug("interfaces",
804 "bond %s is converted from another type of interface",
805 hardware->h_ifname);
806 if (hardware->h_ops && hardware->h_ops->cleanup)
807 hardware->h_ops->cleanup(cfg, hardware);
808 levent_hardware_release(hardware);
809 levent_hardware_init(hardware);
810 }
08ced6b4
VB
811 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
812 if (!bmaster) {
e12c2365
VB
813 log_warn("interfaces", "not enough memory");
814 lldpd_hardware_cleanup(cfg, hardware);
815 continue;
816 }
32945d6a
VB
817 } else bmaster = hardware->h_data;
818 bmaster->index = master->index;
819 strlcpy(bmaster->name, master->name, IFNAMSIZ);
820 if (hardware->h_ops != &bond_ops) {
849954d7 821 if (iface_bond_init(cfg, hardware) != 0) {
6f8925be 822 log_warn("interfaces", "unable to initialize %s",
849954d7
VB
823 hardware->h_ifname);
824 lldpd_hardware_cleanup(cfg, hardware);
825 continue;
826 }
827 hardware->h_ops = &bond_ops;
5347914e 828 hardware->h_mangle = 1;
32945d6a
VB
829 }
830 if (created)
bdfe4193 831 interfaces_helper_add_hardware(cfg, hardware);
32945d6a 832 else
4b292b55 833 lldpd_port_cleanup(&hardware->h_lport, 0);
e12c2365
VB
834
835 hardware->h_flags = iface->flags;
0fa2254b 836 iface->ignore = 1;
849954d7
VB
837
838 /* Get local address */
4e5f34c5 839 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
e12c2365 840
36bd79e3 841 /* Fill information about port */
8fbd3195 842 interfaces_helper_port_name_desc(cfg, hardware, iface);
e12c2365 843
849954d7 844 /* Fill additional info */
7a008075 845#ifdef ENABLE_DOT3
e12c2365 846 hardware->h_lport.p_aggregid = master->index;
7a008075 847#endif
e12c2365 848 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
849954d7
VB
849 }
850}
2f4ff307 851#endif
849954d7 852
adbb6e54 853/* Query each interface to get the appropriate driver */
5994b27d 854static void
adbb6e54
VB
855iflinux_add_driver(struct lldpd *cfg,
856 struct interfaces_device_list *interfaces)
5994b27d 857{
adbb6e54
VB
858 struct interfaces_device *iface;
859 TAILQ_FOREACH(iface, interfaces, next) {
860 struct ethtool_drvinfo ethc = {
861 .cmd = ETHTOOL_GDRVINFO
862 };
863 struct ifreq ifr = {
864 .ifr_data = (caddr_t)&ethc
865 };
866 if (iface->driver) continue;
867
868 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
869 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
870 iface->driver = strdup(ethc.driver);
871 log_debug("interfaces", "driver for %s is `%s`",
872 iface->name, iface->driver);
873 }
5994b27d
VB
874 }
875}
876
adbb6e54 877/* Query each interface to see if it is a wireless one */
e12c2365 878static void
adbb6e54
VB
879iflinux_add_wireless(struct lldpd *cfg,
880 struct interfaces_device_list *interfaces)
f6c4ca4b 881{
adbb6e54
VB
882 struct interfaces_device *iface;
883 TAILQ_FOREACH(iface, interfaces, next) {
b0cb07f7 884 struct iwreq iwr = {};
adbb6e54
VB
885 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
886 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
887 log_debug("interfaces", "%s is wireless",
888 iface->name);
889 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
f6c4ca4b 890 }
f6c4ca4b 891 }
adbb6e54 892}
f6c4ca4b 893
adbb6e54
VB
894/* Query each interface to see if it is a bridge */
895static void
896iflinux_add_bridge(struct lldpd *cfg,
897 struct interfaces_device_list *interfaces)
898{
899 struct interfaces_device *iface;
f6c4ca4b 900
adbb6e54
VB
901 TAILQ_FOREACH(iface, interfaces, next) {
902 if (iface->type & (IFACE_PHYSICAL_T|
903 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
904 continue;
905 if (iflinux_is_bridge(cfg, interfaces, iface)) {
906 log_debug("interfaces",
907 "interface %s is a bridge",
908 iface->name);
909 iface->type |= IFACE_BRIDGE_T;
f6c4ca4b 910 }
f6c4ca4b 911 }
f6c4ca4b
VB
912}
913
adbb6e54 914/* Query each interface to see if it is a bond */
f6c4ca4b 915static void
adbb6e54
VB
916iflinux_add_bond(struct lldpd *cfg,
917 struct interfaces_device_list *interfaces)
6e75df87 918{
adbb6e54 919 struct interfaces_device *iface;
e12c2365
VB
920
921 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
922 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
923 IFACE_BOND_T|IFACE_BRIDGE_T))
6e75df87 924 continue;
adbb6e54
VB
925 if (iflinux_is_bond(cfg, interfaces, iface)) {
926 log_debug("interfaces",
927 "interface %s is a bond",
928 iface->name);
929 iface->type |= IFACE_BOND_T;
adbb6e54 930 }
6e75df87 931 }
6e75df87
VB
932}
933
adbb6e54 934/* Query each interface to see if it is a vlan */
e12c2365 935static void
adbb6e54
VB
936iflinux_add_vlan(struct lldpd *cfg,
937 struct interfaces_device_list *interfaces)
6e75df87 938{
adbb6e54 939 struct interfaces_device *iface;
d4e4c804 940
adbb6e54
VB
941 TAILQ_FOREACH(iface, interfaces, next) {
942 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
943 IFACE_BOND_T|IFACE_BRIDGE_T))
944 continue;
945 if (iflinux_is_vlan(cfg, interfaces, iface)) {
946 log_debug("interfaces",
947 "interface %s is a VLAN",
948 iface->name);
949 iface->type |= IFACE_VLAN_T;
6e75df87
VB
950 }
951 }
952}
31ee070d 953
e12c2365 954static void
adbb6e54
VB
955iflinux_add_physical(struct lldpd *cfg,
956 struct interfaces_device_list *interfaces)
b4ac8083 957{
adbb6e54 958 struct interfaces_device *iface;
45369c0a
VB
959 /* Blacklist some drivers */
960 const char * const *rif;
961 const char * const blacklisted_drivers[] = {
962 "cdc_mbim",
aed57880 963 "vxlan",
45369c0a
VB
964 NULL
965 };
b4ac8083 966
e12c2365 967 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
968 if (iface->type & (IFACE_VLAN_T|
969 IFACE_BOND_T|IFACE_BRIDGE_T))
970 continue;
b4ac8083 971
adbb6e54
VB
972 iface->type &= ~IFACE_PHYSICAL_T;
973
974 /* We request that the interface is able to do either multicast
975 * or broadcast to be able to send discovery frames. */
976 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
977 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
978 iface->name);
b4ac8083 979 continue;
adbb6e54 980 }
b4ac8083 981
45369c0a
VB
982 /* Check if the driver is not blacklisted */
983 if (iface->driver) {
984 int skip = 0;
985 for (rif = blacklisted_drivers; *rif; rif++) {
986 if (strcmp(iface->driver, *rif) == 0) {
987 log_debug("interfaces", "skip %s: blacklisted driver",
988 iface->name);
989 skip = 1;
990 break;
991 }
992 }
993 if (skip) continue;
994 }
995
981ee4fe 996 /* If the interface is linked to another one, skip it too. */
2958b9d4 997 if (iface->lower && (!iface->driver || strcmp(iface->driver, "veth"))) {
981ee4fe
VB
998 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
999 iface->name, iface->lower->name);
1000 continue;
1001 }
1002
12baf781
VB
1003 /* Get the real MAC address (for example, if the interface is enslaved) */
1004 iflinux_get_permanent_mac(cfg, interfaces, iface);
1005
adbb6e54
VB
1006 log_debug("interfaces",
1007 "%s is a physical interface",
1008 iface->name);
1009 iface->type |= IFACE_PHYSICAL_T;
b4ac8083
VB
1010 }
1011}
1012
e12c2365
VB
1013void
1014interfaces_update(struct lldpd *cfg)
1015{
adbb6e54 1016 struct lldpd_hardware *hardware;
281a5cd4
VB
1017 struct interfaces_device_list *interfaces;
1018 struct interfaces_address_list *addresses;
13181ede
VB
1019 interfaces = netlink_get_interfaces(cfg);
1020 addresses = netlink_get_addresses(cfg);
e12c2365
VB
1021 if (interfaces == NULL || addresses == NULL) {
1022 log_warnx("interfaces", "cannot update the list of local interfaces");
0fa2254b 1023 return;
e12c2365
VB
1024 }
1025
adbb6e54
VB
1026 /* Add missing bits to list of interfaces */
1027 iflinux_add_driver(cfg, interfaces);
a2b113ef 1028 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
1029 iflinux_add_wireless(cfg, interfaces);
1030 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
1031 iflinux_add_bridge(cfg, interfaces);
adbb6e54
VB
1032 iflinux_add_bond(cfg, interfaces);
1033 iflinux_add_vlan(cfg, interfaces);
1034 iflinux_add_physical(cfg, interfaces);
1035
1036 interfaces_helper_whitelist(cfg, interfaces);
2f4ff307 1037#ifdef ENABLE_OLDIES
adbb6e54 1038 iflinux_handle_bond(cfg, interfaces);
2f4ff307 1039#endif
88bc404f 1040 interfaces_helper_physical(cfg, interfaces,
22e8cd65 1041 &eth_ops,
88bc404f 1042 iflinux_eth_init);
e12c2365 1043#ifdef ENABLE_DOT1
adbb6e54 1044 interfaces_helper_vlan(cfg, interfaces);
e12c2365 1045#endif
adbb6e54
VB
1046 interfaces_helper_mgmt(cfg, addresses);
1047 interfaces_helper_chassis(cfg, interfaces);
1048
1049 /* Mac/PHY */
1050 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
1051 if (!hardware->h_flags) continue;
78c9dfde 1052 iflinux_macphy(cfg, hardware);
f84199dd 1053 interfaces_helper_promisc(cfg, hardware);
adbb6e54 1054 }
e12c2365 1055}
13181ede
VB
1056
1057void
1058interfaces_cleanup(struct lldpd *cfg)
1059{
1060 netlink_cleanup(cfg);
1061}