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