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