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