]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/interfaces-linux.c
interfaces: handle the case where the provided MAC address is incorrect
[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
6e75df87 234static void
adbb6e54
VB
235iflinux_get_permanent_mac(struct lldpd *cfg,
236 struct interfaces_device_list *interfaces,
237 struct interfaces_device *iface)
6e75df87 238{
adbb6e54 239 struct interfaces_device *master;
4e5f34c5 240 u_int8_t mac[ETHER_ADDR_LEN];
6f8925be 241
d535fe05
VB
242 /* We could do that for any interface, but let's do that only for
243 * aggregates. */
12baf781 244 if ((master = iface->upper) == NULL || master->type != IFACE_BOND_T)
c557a63b
VB
245 return;
246
6f8925be 247 log_debug("interfaces", "get MAC address for %s",
adbb6e54 248 iface->name);
6f8925be 249
d535fe05
VB
250 if (priv_iface_mac(iface->name, mac, ETHER_ADDR_LEN) != 0) {
251 log_warnx("interfaces", "unable to get permanent MAC address for %s",
252 iface->name);
6e75df87
VB
253 return;
254 }
5ac66d33
VB
255 size_t i;
256 for (i = 0; i < ETHER_ADDR_LEN; i++)
257 if (mac[i] != 0) break;
258 if (i == ETHER_ADDR_LEN) {
259 log_warnx("interfaces", "driver for %s do not provide a permanent MAC address",
260 iface->name);
261 return;
262 }
d535fe05
VB
263 memcpy(iface->address, mac,
264 ETHER_ADDR_LEN);
6e75df87
VB
265}
266
267/* Fill up MAC/PHY for a given hardware port */
268static void
adbb6e54 269iflinux_macphy(struct lldpd_hardware *hardware)
6e75df87
VB
270{
271#ifdef ENABLE_DOT3
272 struct ethtool_cmd ethc;
273 struct lldpd_port *port = &hardware->h_lport;
274 int j;
275 int advertised_ethtool_to_rfc3636[][2] = {
276 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
277 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
278 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
279 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
280 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
281 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
282 {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
283 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
284 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
285 {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
286 {0,0}};
287
6f8925be
VB
288 log_debug("interfaces", "ask ethtool for the appropriate MAC/PHY for %s",
289 hardware->h_ifname);
e12c2365 290 if (priv_ethtool(hardware->h_ifname, &ethc, sizeof(struct ethtool_cmd)) == 0) {
3fd015c0
VB
291 port->p_macphy.autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
292 port->p_macphy.autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
6e75df87
VB
293 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
294 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
3fd015c0 295 port->p_macphy.autoneg_advertised |=
6e75df87
VB
296 advertised_ethtool_to_rfc3636[j][1];
297 }
298 switch (ethc.speed) {
299 case SPEED_10:
3fd015c0 300 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87 301 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
3fd015c0 302 if (ethc.port == PORT_BNC) port->p_macphy.mau_type = LLDP_DOT3_MAU_10BASE2;
6e75df87 303 if (ethc.port == PORT_FIBRE)
3fd015c0 304 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
fda729fd 305 LLDP_DOT3_MAU_10BASEFLFD : LLDP_DOT3_MAU_10BASEFLHD;
6e75df87
VB
306 break;
307 case SPEED_100:
3fd015c0 308 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
309 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
310 if (ethc.port == PORT_BNC)
3fd015c0 311 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
5fa8eaa5 312 LLDP_DOT3_MAU_100BASET2FD : LLDP_DOT3_MAU_100BASET2HD;
6e75df87 313 if (ethc.port == PORT_FIBRE)
3fd015c0 314 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
315 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
316 break;
317 case SPEED_1000:
3fd015c0 318 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
319 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
320 if (ethc.port == PORT_FIBRE)
3fd015c0 321 port->p_macphy.mau_type = (ethc.duplex == DUPLEX_FULL) ? \
6e75df87
VB
322 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
323 break;
324 case SPEED_10000:
3fd015c0 325 port->p_macphy.mau_type = (ethc.port == PORT_FIBRE) ? \
6e75df87
VB
326 LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
327 break;
328 }
3fd015c0 329 if (ethc.port == PORT_AUI) port->p_macphy.mau_type = LLDP_DOT3_MAU_AUI;
6e75df87
VB
330 }
331#endif
332}
333
e12c2365
VB
334struct bond_master {
335 char name[IFNAMSIZ];
336 int index;
337};
338
849954d7
VB
339static int
340iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
341{
e12c2365 342 struct bond_master *master = hardware->h_data;
e735a319 343 int fd;
849954d7
VB
344 int un = 1;
345
e12c2365 346 if (!master) return -1;
849954d7 347
6f8925be
VB
348 log_debug("interfaces", "initialize bonded device %s",
349 hardware->h_ifname);
350
849954d7 351 /* First, we get a socket to the raw physical interface */
e735a319
VB
352 if ((fd = priv_iface_init(hardware->h_ifindex,
353 hardware->h_ifname)) == -1)
849954d7
VB
354 return -1;
355 hardware->h_sendfd = fd;
adbb6e54 356 interfaces_setup_multicast(cfg, hardware->h_ifname, 0);
849954d7
VB
357
358 /* Then, we open a raw interface for the master */
148a1efe
VB
359 log_debug("interfaces", "bonded device %s has master %s(%d)",
360 hardware->h_ifname, master->name, master->index);
e735a319 361 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
849954d7
VB
362 close(hardware->h_sendfd);
363 return -1;
364 }
849954d7
VB
365 /* With bonding and older kernels (< 2.6.27) we need to listen
366 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
367 * physical device instead of bond device (works with >=
368 * 2.6.24). */
369 if (setsockopt(fd, SOL_PACKET,
e12c2365
VB
370 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
371 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
372 "You will get inaccurate results",
373 hardware->h_ifname);
849954d7 374 }
adbb6e54 375 interfaces_setup_multicast(cfg, master->name, 0);
849954d7 376
ba93c521 377 levent_hardware_add_fd(hardware, hardware->h_sendfd);
d6e889b6 378 levent_hardware_add_fd(hardware, fd);
6f8925be 379 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
849954d7
VB
380 hardware->h_ifname,
381 hardware->h_sendfd,
e12c2365 382 master->name, fd);
849954d7
VB
383 return 0;
384}
385
849954d7
VB
386static int
387iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
388 int fd, char *buffer, size_t size)
389{
390 int n;
aa015c26 391 struct sockaddr_ll from = {};
849954d7 392 socklen_t fromlen;
e12c2365 393 struct bond_master *master = hardware->h_data;
849954d7 394
6f8925be
VB
395 log_debug("interfaces", "receive PDU from bonded device %s",
396 hardware->h_ifname);
849954d7
VB
397 fromlen = sizeof(from);
398 if ((n = recvfrom(fd, buffer, size, 0,
399 (struct sockaddr *)&from,
400 &fromlen)) == -1) {
6f8925be 401 log_warn("interfaces", "error while receiving frame on %s",
849954d7
VB
402 hardware->h_ifname);
403 hardware->h_rx_discarded_cnt++;
404 return -1;
405 }
406 if (from.sll_pkttype == PACKET_OUTGOING)
407 return -1;
408 if (fd == hardware->h_sendfd)
409 /* We received this on the physical interface. */
410 return n;
411 /* We received this on the bonding interface. Is it really for us? */
44002d66 412 if (from.sll_ifindex == hardware->h_ifindex)
849954d7
VB
413 /* This is for us */
414 return n;
e12c2365 415 if (from.sll_ifindex == master->index)
849954d7
VB
416 /* We don't know from which physical interface it comes (kernel
417 * < 2.6.24). In doubt, this is for us. */
418 return n;
419 return -1; /* Not for us */
420}
421
422static int
423iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
424{
e12c2365 425 struct bond_master *master = hardware->h_data;
6f8925be
VB
426 log_debug("interfaces", "closing bonded device %s",
427 hardware->h_ifname);
adbb6e54
VB
428 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
429 interfaces_setup_multicast(cfg, master->name, 1);
849954d7
VB
430 free(hardware->h_data);
431 return 0;
432}
433
adbb6e54 434struct lldpd_ops bond_ops = {
5347914e 435 .send = iflinux_eth_send,
adbb6e54
VB
436 .recv = iface_bond_recv,
437 .cleanup = iface_bond_close,
438};
439
e12c2365 440static void
adbb6e54 441iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
849954d7 442{
adbb6e54
VB
443 struct interfaces_device *iface;
444 struct interfaces_device *master;
849954d7 445 struct lldpd_hardware *hardware;
c557a63b 446 struct bond_master *bmaster;
e12c2365 447 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
448 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
449 if (!iface->flags) continue;
450 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
849954d7 451
adbb6e54 452 master = iface->upper;
c557a63b
VB
453 log_debug("interfaces", "%s is an acceptable bonded device (master=%s)",
454 iface->name, master->name);
44002d66 455 if ((hardware = lldpd_get_hardware(cfg,
e12c2365
VB
456 iface->name,
457 iface->index,
44002d66 458 &bond_ops)) == NULL) {
849954d7 459 if ((hardware = lldpd_alloc_hardware(cfg,
e12c2365
VB
460 iface->name,
461 iface->index)) == NULL) {
6f8925be 462 log_warnx("interfaces", "Unable to allocate space for %s",
e12c2365
VB
463 iface->name);
464 continue;
465 }
08ced6b4
VB
466 bmaster = hardware->h_data = calloc(1, sizeof(struct bond_master));
467 if (!bmaster) {
e12c2365
VB
468 log_warn("interfaces", "not enough memory");
469 lldpd_hardware_cleanup(cfg, hardware);
470 continue;
471 }
08ced6b4
VB
472 bmaster->index = master->index;
473 strlcpy(bmaster->name, master->name, IFNAMSIZ);
849954d7 474 if (iface_bond_init(cfg, hardware) != 0) {
6f8925be 475 log_warn("interfaces", "unable to initialize %s",
849954d7
VB
476 hardware->h_ifname);
477 lldpd_hardware_cleanup(cfg, hardware);
478 continue;
479 }
480 hardware->h_ops = &bond_ops;
5347914e 481 hardware->h_mangle = 1;
bdfe4193 482 interfaces_helper_add_hardware(cfg, hardware);
849954d7 483 } else {
cfe00f7f 484 if (hardware->h_flags) continue; /* Already seen this time */
c557a63b 485 bmaster = hardware->h_data;
a56f4137 486 memset(bmaster, 0, sizeof(struct bond_master));
c557a63b 487 bmaster->index = master->index;
e2b09091 488 strlcpy(bmaster->name, master->name, IFNAMSIZ);
4b292b55 489 lldpd_port_cleanup(&hardware->h_lport, 0);
849954d7 490 }
e12c2365
VB
491
492 hardware->h_flags = iface->flags;
493 iface->flags = 0;
849954d7
VB
494
495 /* Get local address */
4e5f34c5 496 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
e12c2365 497
36bd79e3 498 /* Fill information about port */
8fbd3195 499 interfaces_helper_port_name_desc(cfg, hardware, iface);
e12c2365 500
849954d7 501 /* Fill additional info */
7a008075 502#ifdef ENABLE_DOT3
e12c2365 503 hardware->h_lport.p_aggregid = master->index;
7a008075 504#endif
e12c2365 505 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
849954d7
VB
506 }
507}
508
adbb6e54 509/* Query each interface to get the appropriate driver */
5994b27d 510static void
adbb6e54
VB
511iflinux_add_driver(struct lldpd *cfg,
512 struct interfaces_device_list *interfaces)
5994b27d 513{
adbb6e54
VB
514 struct interfaces_device *iface;
515 TAILQ_FOREACH(iface, interfaces, next) {
516 struct ethtool_drvinfo ethc = {
517 .cmd = ETHTOOL_GDRVINFO
518 };
519 struct ifreq ifr = {
520 .ifr_data = (caddr_t)&ethc
521 };
522 if (iface->driver) continue;
523
524 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
525 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
526 iface->driver = strdup(ethc.driver);
527 log_debug("interfaces", "driver for %s is `%s`",
528 iface->name, iface->driver);
529 }
5994b27d
VB
530 }
531}
532
adbb6e54 533/* Query each interface to see if it is a wireless one */
e12c2365 534static void
adbb6e54
VB
535iflinux_add_wireless(struct lldpd *cfg,
536 struct interfaces_device_list *interfaces)
f6c4ca4b 537{
adbb6e54
VB
538 struct interfaces_device *iface;
539 TAILQ_FOREACH(iface, interfaces, next) {
b0cb07f7 540 struct iwreq iwr = {};
adbb6e54
VB
541 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
542 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
543 log_debug("interfaces", "%s is wireless",
544 iface->name);
545 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
f6c4ca4b 546 }
f6c4ca4b 547 }
adbb6e54 548}
f6c4ca4b 549
adbb6e54
VB
550/* Query each interface to see if it is a bridge */
551static void
552iflinux_add_bridge(struct lldpd *cfg,
553 struct interfaces_device_list *interfaces)
554{
555 struct interfaces_device *iface;
f6c4ca4b 556
adbb6e54
VB
557 TAILQ_FOREACH(iface, interfaces, next) {
558 if (iface->type & (IFACE_PHYSICAL_T|
559 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
560 continue;
561 if (iflinux_is_bridge(cfg, interfaces, iface)) {
562 log_debug("interfaces",
563 "interface %s is a bridge",
564 iface->name);
565 iface->type |= IFACE_BRIDGE_T;
f6c4ca4b 566 }
f6c4ca4b 567 }
f6c4ca4b
VB
568}
569
adbb6e54 570/* Query each interface to see if it is a bond */
f6c4ca4b 571static void
adbb6e54
VB
572iflinux_add_bond(struct lldpd *cfg,
573 struct interfaces_device_list *interfaces)
6e75df87 574{
adbb6e54 575 struct interfaces_device *iface;
e12c2365
VB
576
577 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
578 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
579 IFACE_BOND_T|IFACE_BRIDGE_T))
6e75df87 580 continue;
adbb6e54
VB
581 if (iflinux_is_bond(cfg, interfaces, iface)) {
582 log_debug("interfaces",
583 "interface %s is a bond",
584 iface->name);
585 iface->type |= IFACE_BOND_T;
adbb6e54 586 }
6e75df87 587 }
6e75df87
VB
588}
589
adbb6e54 590/* Query each interface to see if it is a vlan */
e12c2365 591static void
adbb6e54
VB
592iflinux_add_vlan(struct lldpd *cfg,
593 struct interfaces_device_list *interfaces)
6e75df87 594{
adbb6e54 595 struct interfaces_device *iface;
d4e4c804 596
adbb6e54
VB
597 TAILQ_FOREACH(iface, interfaces, next) {
598 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
599 IFACE_BOND_T|IFACE_BRIDGE_T))
600 continue;
601 if (iflinux_is_vlan(cfg, interfaces, iface)) {
602 log_debug("interfaces",
603 "interface %s is a VLAN",
604 iface->name);
605 iface->type |= IFACE_VLAN_T;
6e75df87
VB
606 }
607 }
608}
31ee070d 609
e12c2365 610static void
adbb6e54
VB
611iflinux_add_physical(struct lldpd *cfg,
612 struct interfaces_device_list *interfaces)
b4ac8083 613{
adbb6e54
VB
614 struct interfaces_device *iface;
615 /* White-list some drivers */
616 const char * const *rif;
617 const char * const regular_interfaces[] = {
618 "dsa",
619 "veth",
620 NULL
621 };
b4ac8083 622
e12c2365 623 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
624 if (iface->type & (IFACE_VLAN_T|
625 IFACE_BOND_T|IFACE_BRIDGE_T))
626 continue;
b4ac8083 627
adbb6e54
VB
628 iface->type &= ~IFACE_PHYSICAL_T;
629
630 /* We request that the interface is able to do either multicast
631 * or broadcast to be able to send discovery frames. */
632 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
633 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
634 iface->name);
b4ac8083 635 continue;
adbb6e54 636 }
b4ac8083 637
adbb6e54
VB
638 /* Check if the driver is whitelisted */
639 if (iface->driver) {
640 for (rif = regular_interfaces; *rif; rif++) {
641 if (strcmp(iface->driver, *rif) == 0) {
642 /* White listed! */
643 log_debug("interfaces", "accept %s: whitelisted",
644 iface->name);
645 iface->type |= IFACE_PHYSICAL_T;
646 continue;
647 }
648 }
649 }
650
981ee4fe
VB
651 /* If the interface is linked to another one, skip it too. */
652 if (iface->lower) {
653 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
654 iface->name, iface->lower->name);
655 continue;
656 }
657
adbb6e54
VB
658 /* Check queue len. If no queue, this usually means that this
659 is not a "real" interface. */
660 if (iface->txqueue == 0) {
661 log_debug("interfaces", "skip %s: no queue",
662 iface->name);
663 continue;
664 }
665
12baf781
VB
666 /* Get the real MAC address (for example, if the interface is enslaved) */
667 iflinux_get_permanent_mac(cfg, interfaces, iface);
668
adbb6e54
VB
669 log_debug("interfaces",
670 "%s is a physical interface",
671 iface->name);
672 iface->type |= IFACE_PHYSICAL_T;
b4ac8083
VB
673 }
674}
675
e12c2365
VB
676void
677interfaces_update(struct lldpd *cfg)
678{
adbb6e54 679 struct lldpd_hardware *hardware;
281a5cd4
VB
680 struct interfaces_device_list *interfaces;
681 struct interfaces_address_list *addresses;
13181ede
VB
682 interfaces = netlink_get_interfaces(cfg);
683 addresses = netlink_get_addresses(cfg);
e12c2365
VB
684 if (interfaces == NULL || addresses == NULL) {
685 log_warnx("interfaces", "cannot update the list of local interfaces");
686 goto end;
687 }
688
adbb6e54
VB
689 /* Add missing bits to list of interfaces */
690 iflinux_add_driver(cfg, interfaces);
a2b113ef 691 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_WLAN)
692 iflinux_add_wireless(cfg, interfaces);
693 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_BRIDGE)
694 iflinux_add_bridge(cfg, interfaces);
adbb6e54
VB
695 iflinux_add_bond(cfg, interfaces);
696 iflinux_add_vlan(cfg, interfaces);
697 iflinux_add_physical(cfg, interfaces);
698
699 interfaces_helper_whitelist(cfg, interfaces);
700 iflinux_handle_bond(cfg, interfaces);
88bc404f 701 interfaces_helper_physical(cfg, interfaces,
22e8cd65 702 &eth_ops,
88bc404f 703 iflinux_eth_init);
e12c2365 704#ifdef ENABLE_DOT1
adbb6e54 705 interfaces_helper_vlan(cfg, interfaces);
e12c2365 706#endif
adbb6e54
VB
707 interfaces_helper_mgmt(cfg, addresses);
708 interfaces_helper_chassis(cfg, interfaces);
709
710 /* Mac/PHY */
711 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
712 if (!hardware->h_flags) continue;
713 iflinux_macphy(hardware);
f84199dd 714 interfaces_helper_promisc(cfg, hardware);
adbb6e54 715 }
e12c2365
VB
716
717end:
adbb6e54
VB
718 interfaces_free_devices(interfaces);
719 interfaces_free_addresses(addresses);
e12c2365 720}
13181ede
VB
721
722void
723interfaces_cleanup(struct lldpd *cfg)
724{
725 netlink_cleanup(cfg);
726}