]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/interfaces-linux.c
interface: use 10GBASE-CX4 for copper
[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>
22#include <errno.h>
23#include <sys/ioctl.h>
d7460a6f
ALG
24#if defined(__clang__)
25#pragma clang diagnostic push
26#pragma clang diagnostic ignored "-Wdocumentation"
27#endif
6e75df87
VB
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>
6e75df87 33#include <linux/if_packet.h>
e12c2365 34#include <linux/ethtool.h>
d7460a6f
ALG
35#if defined(__clang__)
36#pragma clang diagnostic pop
37#endif
6e75df87
VB
38
39#define SYSFS_PATH_MAX 256
40#define MAX_PORTS 1024
41#define MAX_BRIDGES 1024
42
88bc404f
VB
43static int
44iflinux_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
45{
e735a319 46 int fd;
88bc404f
VB
47
48 log_debug("interfaces", "initialize ethernet device %s",
49 hardware->h_ifname);
e735a319 50 if ((fd = priv_iface_init(hardware->h_ifindex, hardware->h_ifname)) == -1)
88bc404f
VB
51 return -1;
52 hardware->h_sendfd = fd; /* Send */
53
88bc404f
VB
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
22e8cd65
VB
62/* Generic ethernet send/receive */
63static int
64iflinux_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
73static int
74iflinux_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
75 int fd, char *buffer, size_t size)
76{
77 int n;
aa015c26 78 struct sockaddr_ll from = {};
22e8cd65
VB
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
99static int
100iflinux_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
108static struct lldpd_ops eth_ops = {
109 .send = iflinux_eth_send,
110 .recv = iflinux_eth_recv,
111 .cleanup = iflinux_eth_close,
112};
113
6e75df87 114static int
adbb6e54
VB
115iflinux_is_bridge(struct lldpd *cfg,
116 struct interfaces_device_list *interfaces,
117 struct interfaces_device *iface)
6e75df87 118{
16eacc5b 119#ifdef ENABLE_OLDIES
adbb6e54 120 struct interfaces_device *port;
6e75df87
VB
121 char path[SYSFS_PATH_MAX];
122 int f;
123
adbb6e54
VB
124 if ((snprintf(path, SYSFS_PATH_MAX,
125 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB,
126 iface->name)) >= SYSFS_PATH_MAX)
6f8925be 127 log_warnx("interfaces", "path truncated");
adbb6e54 128 if ((f = priv_open(path)) < 0)
31c9173a 129 return 0;
6e75df87 130 close(f);
adbb6e54
VB
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
6e75df87 148 return 1;
16eacc5b
VB
149#else
150 return 0;
151#endif
6e75df87
VB
152}
153
154static int
adbb6e54
VB
155iflinux_is_vlan(struct lldpd *cfg,
156 struct interfaces_device_list *interfaces,
157 struct interfaces_device *iface)
6e75df87 158{
16eacc5b 159#ifdef ENABLE_OLDIES
b0cb07f7 160 struct vlan_ioctl_args ifv = {};
6e75df87 161 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
adbb6e54
VB
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 }
6e75df87 173
adbb6e54
VB
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 }
6e75df87 183
adbb6e54
VB
184 iface->lower = lower;
185 iface->vlanid = ifv.u.VID;
6e75df87 186 return 1;
adbb6e54 187 }
16eacc5b 188#endif
6e75df87
VB
189 return 0;
190}
191
192static int
adbb6e54
VB
193iflinux_is_bond(struct lldpd *cfg,
194 struct interfaces_device_list *interfaces,
195 struct interfaces_device *master)
6e75df87 196{
16eacc5b 197#ifdef ENABLE_OLDIES
f5a0a15e
VB
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
b0cb07f7
VB
204 struct ifreq ifr = {};
205 struct ifbond ifb = {};
c557a63b 206 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
8f88ff70 207 ifr.ifr_data = (char *)&ifb;
6e75df87
VB
208 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
209 while (ifb.num_slaves--) {
adbb6e54 210 struct ifslave ifs;
6e75df87
VB
211 memset(&ifr, 0, sizeof(ifr));
212 memset(&ifs, 0, sizeof(ifs));
c557a63b 213 strlcpy(ifr.ifr_name, master->name, sizeof(ifr.ifr_name));
8f88ff70 214 ifr.ifr_data = (char *)&ifs;
6e75df87 215 ifs.slave_id = ifb.num_slaves;
adbb6e54
VB
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;
6e75df87
VB
226 }
227 }
adbb6e54 228 return 1;
6e75df87 229 }
16eacc5b 230#endif
6e75df87
VB
231 return 0;
232}
233
1a3ec373
VB
234static void
235iflinux_get_permanent_mac(struct lldpd *cfg,
01293dc4
VB
236 struct interfaces_device_list *interfaces,
237 struct interfaces_device *iface)
238{
239 struct interfaces_device *master;
240 int f, state = 0;
241 FILE *netbond;
242 const char *slaveif = "Slave Interface: ";
243 const char *hwaddr = "Permanent HW addr: ";
244 u_int8_t mac[ETHER_ADDR_LEN];
245 char path[SYSFS_PATH_MAX];
246 char line[100];
247
248 if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T)
249 return;
250
251 /* We have a bond, we need to query it to get real MAC addresses */
252 if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
253 master->name) >= SYSFS_PATH_MAX) {
254 log_warnx("interfaces", "path truncated");
255 return;
256 }
257 if ((f = priv_open(path)) < 0) {
258 if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
259 master->name) >= SYSFS_PATH_MAX) {
260 log_warnx("interfaces", "path truncated");
261 return;
262 }
263 f = priv_open(path);
264 }
265 if (f < 0) {
0b7f8847
VB
266 log_warnx("interfaces",
267 "unable to get permanent MAC address for %s",
268 master->name);
01293dc4
VB
269 return;
270 }
271 if ((netbond = fdopen(f, "r")) == NULL) {
272 log_warn("interfaces", "unable to read stream from %s", path);
273 close(f);
274 return;
275 }
276 /* State 0:
277 We parse the file to search "Slave Interface: ". If found, go to
278 state 1.
279 State 1:
280 We parse the file to search "Permanent HW addr: ". If found, we get
281 the mac.
282 */
283 while (fgets(line, sizeof(line), netbond)) {
284 switch (state) {
285 case 0:
286 if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
287 if (line[strlen(line)-1] == '\n')
288 line[strlen(line)-1] = '\0';
289 if (strcmp(iface->name,
290 line + strlen(slaveif)) == 0)
291 state++;
292 }
293 break;
294 case 1:
295 if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
296 if (line[strlen(line)-1] == '\n')
297 line[strlen(line)-1] = '\0';
298 if (sscanf(line + strlen(hwaddr),
299 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
300 &mac[0], &mac[1], &mac[2],
301 &mac[3], &mac[4], &mac[5]) !=
302 ETHER_ADDR_LEN) {
303 log_warn("interfaces", "unable to parse %s",
304 line + strlen(hwaddr));
305 fclose(netbond);
306 return;
307 }
308 memcpy(iface->address, mac,
309 ETHER_ADDR_LEN);
310 fclose(netbond);
311 return;
312 }
313 break;
314 }
315 }
316 log_warnx("interfaces", "unable to find real mac address for %s",
317 iface->name);
318 fclose(netbond);
319}
320
6e75df87
VB
321/* Fill up MAC/PHY for a given hardware port */
322static void
adbb6e54 323iflinux_macphy(struct lldpd_hardware *hardware)
6e75df87
VB
324{
325#ifdef ENABLE_DOT3
326 struct ethtool_cmd ethc;
327 struct lldpd_port *port = &hardware->h_lport;
328 int j;
329 int advertised_ethtool_to_rfc3636[][2] = {
330 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
331 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
332 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
333 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
334 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
335 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
a97e3dfe 336 {ADVERTISED_1000baseKX_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_XFD},
6e75df87
VB
337 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
338 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
6e75df87
VB
339 {0,0}};
340
6f8925be
VB
341 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
342 hardware->h_ifname);
e12c2365 343 if (priv_ethtool(hardware->h_ifname, &ethc, sizeof(struct ethtool_cmd)) == 0) {
3fd015c0
VB
344 port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
345 port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
6e75df87 346 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
a97e3dfe
VB
347 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0]) {
348 port->p_macphy.autoneg_advertised |=
6e75df87 349 advertised_ethtool_to_rfc3636[j][1];
a97e3dfe
VB
350 ethc.advertising &= ~advertised_ethtool_to_rfc3636[j][0];
351 }
6e75df87 352 }
a97e3dfe
VB
353 if (ethc.advertising)
354 port->p_macphy.autoneg_advertised |= LLDP_DOT3_LINK_AUTONEG_OTHER;
6e75df87
VB
355 switch (ethc.speed) {
356 case SPEED_10:
3fd015c0 357 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87 358 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
3fd015c0 359 if (ethc.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
6e75df87 360 if (ethc.port == PORT_FIBRE)
3fd015c0 361 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
fda729fd 362 LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD;
6e75df87
VB
363 break;
364 case SPEED_100:
3fd015c0 365 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
366 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
367 if (ethc.port == PORT_BNC)
3fd015c0 368 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
5fa8eaa5 369 LLDP_DOT3_MAU_100BASET2FD : LLDP_DOT3_MAU_100BASET2HD;
6e75df87 370 if (ethc.port == PORT_FIBRE)
3fd015c0 371 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
372 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
373 break;
374 case SPEED_1000:
3fd015c0 375 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
376 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
377 if (ethc.port == PORT_FIBRE)
3fd015c0 378 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
379 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
380 break;
381 case SPEED_10000:
a97e3dfe
VB
382 // For fiber, we tell 10GIGBASEX and for others,
383 // 10GIGBASER. It's not unusual to have 10GIGBASER on
384 // fiber either but we don't have 10GIGBASET for
385 // copper. No good solution.
3fd015c0 386 port->p_macphy.mau_type = (ethc.port == PORT_FIBRE) ? \
2fb135c8 387 LLDP_DOT3_MAU_10GIGBASELR : LLDP_DOT3_MAU_10GIGBASECX4;
6e75df87
VB
388 break;
389 }
3fd015c0 390 if (ethc.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
6e75df87
VB
391 }
392#endif
393}
394
e12c2365
VB
395struct bond_master {
396 char name[IFNAMSIZ];
397 int index;
398};
399
849954d7
VB
400static int
401iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
402{
e12c2365 403 struct bond_master *master = hardware->h_data;
e735a319 404 int fd;
849954d7
VB
405 int un = 1;
406
e12c2365 407 if (!master) return -1;
849954d7 408
0da01fd6 409 log_debug("interfaces", "initialize enslaved device %s",
6f8925be
VB
410 hardware->h_ifname);
411
849954d7 412 /* First, we get a socket to the raw physical interface */
e735a319
VB
413 if ((fd = priv_iface_init(hardware->h_ifindex,
414 hardware->h_ifname)) == -1)
849954d7
VB
415 return -1;
416 hardware->h_sendfd = fd;
adbb6e54 417 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
849954d7
VB
418
419 /* Then, we open a raw interface for the master */
0da01fd6 420 log_debug("interfaces", "enslaved device %s has master %s(%d)",
148a1efe 421 hardware->h_ifname, master->name, master->index);
e735a319 422 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
849954d7
VB
423 close(hardware->h_sendfd);
424 return -1;
425 }
849954d7
VB
426 /* With bonding and older kernels (< 2.6.27) we need to listen
427 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
428 * physical device instead of bond device (works with >=
429 * 2.6.24). */
430 if (setsockopt(fd, SOL_PACKET,
e12c2365
VB
431 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
432 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
433 "You will get inaccurate results",
434 hardware->h_ifname);
849954d7 435 }
adbb6e54 436 interfaces_setup_multicast(cfg, master->name, 0);
849954d7 437
ba93c521 438 levent_hardware_add_fd(hardware, hardware->h_sendfd);
d6e889b6 439 levent_hardware_add_fd(hardware, fd);
6f8925be 440 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
849954d7
VB
441 hardware->h_ifname,
442 hardware->h_sendfd,
e12c2365 443 master->name, fd);
849954d7
VB
444 return 0;
445}
446
849954d7
VB
447static int
448iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
449 int fd, char *buffer, size_t size)
450{
451 int n;
aa015c26 452 struct sockaddr_ll from = {};
849954d7 453 socklen_t fromlen;
e12c2365 454 struct bond_master *master = hardware->h_data;
849954d7 455
0da01fd6 456 log_debug("interfaces", "receive PDU from enslaved device %s",
6f8925be 457 hardware->h_ifname);
849954d7
VB
458 fromlen = sizeof(from);
459 if ((n = recvfrom(fd, buffer, size, 0,
460 (struct sockaddr *)&from,
461 &fromlen)) == -1) {
6f8925be 462 log_warn("interfaces", "error while receiving frame on %s",
849954d7
VB
463 hardware->h_ifname);
464 hardware->h_rx_discarded_cnt++;
465 return -1;
466 }
467 if (from.sll_pkttype == PACKET_OUTGOING)
468 return -1;
469 if (fd == hardware->h_sendfd)
470 /* We received this on the physical interface. */
471 return n;
472 /* We received this on the bonding interface. Is it really for us? */
44002d66 473 if (from.sll_ifindex == hardware->h_ifindex)
849954d7
VB
474 /* This is for us */
475 return n;
e12c2365 476 if (from.sll_ifindex == master->index)
849954d7
VB
477 /* We don't know from which physical interface it comes (kernel
478 * < 2.6.24). In doubt, this is for us. */
479 return n;
480 return -1; /* Not for us */
481}
482
483static int
484iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
485{
e12c2365 486 struct bond_master *master = hardware->h_data;
0da01fd6 487 log_debug("interfaces", "closing enslaved device %s",
6f8925be 488 hardware->h_ifname);
adbb6e54
VB
489 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
490 interfaces_setup_multicast(cfg, master->name, 1);
32945d6a 491 free(hardware->h_data); hardware->h_data = NULL;
849954d7
VB
492 return 0;
493}
494
adbb6e54 495struct lldpd_ops bond_ops = {
5347914e 496 .send = iflinux_eth_send,
adbb6e54
VB
497 .recv = iface_bond_recv,
498 .cleanup = iface_bond_close,
499};
500
e12c2365 501static void
adbb6e54 502iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
849954d7 503{
adbb6e54
VB
504 struct interfaces_device *iface;
505 struct interfaces_device *master;
849954d7 506 struct lldpd_hardware *hardware;
c557a63b 507 struct bond_master *bmaster;
32945d6a 508 int created;
e12c2365 509 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54 510 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
0fa2254b 511 if (iface->ignore) continue;
adbb6e54 512 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
849954d7 513
adbb6e54 514 master = iface->upper;
0da01fd6 515 log_debug("interfaces", "%s is an acceptable enslaved device (master=%s)",
c557a63b 516 iface->name, master->name);
32945d6a 517 created = 0;
44002d66 518 if ((hardware = lldpd_get_hardware(cfg,
e12c2365 519 iface->name,
32945d6a 520 iface->index)) == NULL) {
849954d7 521 if ((hardware = lldpd_alloc_hardware(cfg,
e12c2365
VB
522 iface->name,
523 iface->index)) == NULL) {
6f8925be 524 log_warnx("interfaces", "Unable to allocate space for %s",
e12c2365
VB
525 iface->name);
526 continue;
527 }
32945d6a
VB
528 created = 1;
529 }
530 if (hardware->h_flags) continue;
531 if (hardware->h_ops != &bond_ops) {
532 if (!created) {
533 log_debug("interfaces",
534 "bond %s is converted from another type of interface",
535 hardware->h_ifname);
536 if (hardware->h_ops && hardware->h_ops->cleanup)
537 hardware->h_ops->cleanup(cfg, hardware);
538 levent_hardware_release(hardware);
539 levent_hardware_init(hardware);
540 }
08ced6b4
VB
541 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
542 if (!bmaster) {
e12c2365
VB
543 log_warn("interfaces", "not enough memory");
544 lldpd_hardware_cleanup(cfg, hardware);
545 continue;
546 }
32945d6a
VB
547 } else bmaster = hardware->h_data;
548 bmaster->index = master->index;
549 strlcpy(bmaster->name, master->name, IFNAMSIZ);
550 if (hardware->h_ops != &bond_ops) {
849954d7 551 if (iface_bond_init(cfg, hardware) != 0) {
6f8925be 552 log_warn("interfaces", "unable to initialize %s",
849954d7
VB
553 hardware->h_ifname);
554 lldpd_hardware_cleanup(cfg, hardware);
555 continue;
556 }
557 hardware->h_ops = &bond_ops;
5347914e 558 hardware->h_mangle = 1;
32945d6a
VB
559 }
560 if (created)
bdfe4193 561 interfaces_helper_add_hardware(cfg, hardware);
32945d6a 562 else
4b292b55 563 lldpd_port_cleanup(&hardware->h_lport, 0);
e12c2365
VB
564
565 hardware->h_flags = iface->flags;
0fa2254b 566 iface->ignore = 1;
849954d7
VB
567
568 /* Get local address */
4e5f34c5 569 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
e12c2365 570
36bd79e3 571 /* Fill information about port */
8fbd3195 572 interfaces_helper_port_name_desc(cfg, hardware, iface);
e12c2365 573
849954d7 574 /* Fill additional info */
7a008075 575#ifdef ENABLE_DOT3
e12c2365 576 hardware->h_lport.p_aggregid = master->index;
7a008075 577#endif
e12c2365 578 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
849954d7
VB
579 }
580}
581
adbb6e54 582/* Query each interface to get the appropriate driver */
5994b27d 583static void
adbb6e54
VB
584iflinux_add_driver(struct lldpd *cfg,
585 struct interfaces_device_list *interfaces)
5994b27d 586{
adbb6e54
VB
587 struct interfaces_device *iface;
588 TAILQ_FOREACH(iface, interfaces, next) {
589 struct ethtool_drvinfo ethc = {
590 .cmd = ETHTOOL_GDRVINFO
591 };
592 struct ifreq ifr = {
593 .ifr_data = (caddr_t)&ethc
594 };
595 if (iface->driver) continue;
596
597 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
598 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
599 iface->driver = strdup(ethc.driver);
600 log_debug("interfaces", "driver for %s is `%s`",
601 iface->name, iface->driver);
602 }
5994b27d
VB
603 }
604}
605
adbb6e54 606/* Query each interface to see if it is a wireless one */
e12c2365 607static void
adbb6e54
VB
608iflinux_add_wireless(struct lldpd *cfg,
609 struct interfaces_device_list *interfaces)
f6c4ca4b 610{
adbb6e54
VB
611 struct interfaces_device *iface;
612 TAILQ_FOREACH(iface, interfaces, next) {
b0cb07f7 613 struct iwreq iwr = {};
adbb6e54
VB
614 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
615 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
616 log_debug("interfaces", "%s is wireless",
617 iface->name);
618 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
f6c4ca4b 619 }
f6c4ca4b 620 }
adbb6e54 621}
f6c4ca4b 622
adbb6e54
VB
623/* Query each interface to see if it is a bridge */
624static void
625iflinux_add_bridge(struct lldpd *cfg,
626 struct interfaces_device_list *interfaces)
627{
628 struct interfaces_device *iface;
f6c4ca4b 629
adbb6e54
VB
630 TAILQ_FOREACH(iface, interfaces, next) {
631 if (iface->type & (IFACE_PHYSICAL_T|
632 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
633 continue;
634 if (iflinux_is_bridge(cfg, interfaces, iface)) {
635 log_debug("interfaces",
636 "interface %s is a bridge",
637 iface->name);
638 iface->type |= IFACE_BRIDGE_T;
f6c4ca4b 639 }
f6c4ca4b 640 }
f6c4ca4b
VB
641}
642
adbb6e54 643/* Query each interface to see if it is a bond */
f6c4ca4b 644static void
adbb6e54
VB
645iflinux_add_bond(struct lldpd *cfg,
646 struct interfaces_device_list *interfaces)
6e75df87 647{
adbb6e54 648 struct interfaces_device *iface;
e12c2365
VB
649
650 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
651 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
652 IFACE_BOND_T|IFACE_BRIDGE_T))
6e75df87 653 continue;
adbb6e54
VB
654 if (iflinux_is_bond(cfg, interfaces, iface)) {
655 log_debug("interfaces",
656 "interface %s is a bond",
657 iface->name);
658 iface->type |= IFACE_BOND_T;
adbb6e54 659 }
6e75df87 660 }
6e75df87
VB
661}
662
adbb6e54 663/* Query each interface to see if it is a vlan */
e12c2365 664static void
adbb6e54
VB
665iflinux_add_vlan(struct lldpd *cfg,
666 struct interfaces_device_list *interfaces)
6e75df87 667{
adbb6e54 668 struct interfaces_device *iface;
d4e4c804 669
adbb6e54
VB
670 TAILQ_FOREACH(iface, interfaces, next) {
671 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
672 IFACE_BOND_T|IFACE_BRIDGE_T))
673 continue;
674 if (iflinux_is_vlan(cfg, interfaces, iface)) {
675 log_debug("interfaces",
676 "interface %s is a VLAN",
677 iface->name);
678 iface->type |= IFACE_VLAN_T;
6e75df87
VB
679 }
680 }
681}
31ee070d 682
e12c2365 683static void
adbb6e54
VB
684iflinux_add_physical(struct lldpd *cfg,
685 struct interfaces_device_list *interfaces)
b4ac8083 686{
adbb6e54 687 struct interfaces_device *iface;
45369c0a
VB
688 /* Blacklist some drivers */
689 const char * const *rif;
690 const char * const blacklisted_drivers[] = {
691 "cdc_mbim",
692 NULL
693 };
b4ac8083 694
e12c2365 695 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
696 if (iface->type & (IFACE_VLAN_T|
697 IFACE_BOND_T|IFACE_BRIDGE_T))
698 continue;
b4ac8083 699
adbb6e54
VB
700 iface->type &= ~IFACE_PHYSICAL_T;
701
702 /* We request that the interface is able to do either multicast
703 * or broadcast to be able to send discovery frames. */
704 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
705 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
706 iface->name);
b4ac8083 707 continue;
adbb6e54 708 }
b4ac8083 709
45369c0a
VB
710 /* Check if the driver is not blacklisted */
711 if (iface->driver) {
712 int skip = 0;
713 for (rif = blacklisted_drivers; *rif; rif++) {
714 if (strcmp(iface->driver, *rif) == 0) {
715 log_debug("interfaces", "skip %s: blacklisted driver",
716 iface->name);
717 skip = 1;
718 break;
719 }
720 }
721 if (skip) continue;
722 }
723
981ee4fe 724 /* If the interface is linked to another one, skip it too. */
2958b9d4 725 if (iface->lower && (!iface->driver || strcmp(iface->driver, "veth"))) {
981ee4fe
VB
726 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
727 iface->name, iface->lower->name);
728 continue;
729 }
730
12baf781
VB
731 /* Get the real MAC address (for example, if the interface is enslaved) */
732 iflinux_get_permanent_mac(cfg, interfaces, iface);
733
adbb6e54
VB
734 log_debug("interfaces",
735 "%s is a physical interface",
736 iface->name);
737 iface->type |= IFACE_PHYSICAL_T;
b4ac8083
VB
738 }
739}
740
e12c2365
VB
741void
742interfaces_update(struct lldpd *cfg)
743{
adbb6e54 744 struct lldpd_hardware *hardware;
281a5cd4
VB
745 struct interfaces_device_list *interfaces;
746 struct interfaces_address_list *addresses;
13181ede
VB
747 interfaces = netlink_get_interfaces(cfg);
748 addresses = netlink_get_addresses(cfg);
e12c2365
VB
749 if (interfaces == NULL || addresses == NULL) {
750 log_warnx("interfaces", "cannot update the list of local interfaces");
0fa2254b 751 return;
e12c2365
VB
752 }
753
adbb6e54
VB
754 /* Add missing bits to list of interfaces */
755 iflinux_add_driver(cfg, interfaces);
a2b113ef 756 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
757 iflinux_add_wireless(cfg, interfaces);
758 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
759 iflinux_add_bridge(cfg, interfaces);
adbb6e54
VB
760 iflinux_add_bond(cfg, interfaces);
761 iflinux_add_vlan(cfg, interfaces);
762 iflinux_add_physical(cfg, interfaces);
763
764 interfaces_helper_whitelist(cfg, interfaces);
765 iflinux_handle_bond(cfg, interfaces);
88bc404f 766 interfaces_helper_physical(cfg, interfaces,
22e8cd65 767 &eth_ops,
88bc404f 768 iflinux_eth_init);
e12c2365 769#ifdef ENABLE_DOT1
adbb6e54 770 interfaces_helper_vlan(cfg, interfaces);
e12c2365 771#endif
adbb6e54
VB
772 interfaces_helper_mgmt(cfg, addresses);
773 interfaces_helper_chassis(cfg, interfaces);
774
775 /* Mac/PHY */
776 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
777 if (!hardware->h_flags) continue;
778 iflinux_macphy(hardware);
f84199dd 779 interfaces_helper_promisc(cfg, hardware);
adbb6e54 780 }
e12c2365 781}
13181ede
VB
782
783void
784interfaces_cleanup(struct lldpd *cfg)
785{
786 netlink_cleanup(cfg);
787}