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