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