]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/daemon/interfaces-linux.c
README: point to the website for install instructions
[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 */
e735a319 449 if ((fd = priv_iface_init(master->index, master->name)) == -1) {
849954d7
VB
450 close(hardware->h_sendfd);
451 return -1;
452 }
849954d7
VB
453 /* With bonding and older kernels (< 2.6.27) we need to listen
454 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
455 * physical device instead of bond device (works with >=
456 * 2.6.24). */
457 if (setsockopt(fd, SOL_PACKET,
e12c2365
VB
458 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
459 log_info("interfaces", "unable to setsockopt for master bonding device of %s. "
460 "You will get inaccurate results",
461 hardware->h_ifname);
849954d7 462 }
adbb6e54 463 interfaces_setup_multicast(cfg, master->name, 0);
849954d7 464
ba93c521 465 levent_hardware_add_fd(hardware, hardware->h_sendfd);
d6e889b6 466 levent_hardware_add_fd(hardware, fd);
6f8925be 467 log_debug("interfaces", "interface %s initialized (fd=%d,master=%s[%d])",
849954d7
VB
468 hardware->h_ifname,
469 hardware->h_sendfd,
e12c2365 470 master->name, fd);
849954d7
VB
471 return 0;
472}
473
849954d7
VB
474static int
475iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
476 int fd, char *buffer, size_t size)
477{
478 int n;
aa015c26 479 struct sockaddr_ll from = {};
849954d7 480 socklen_t fromlen;
e12c2365 481 struct bond_master *master = hardware->h_data;
849954d7 482
6f8925be
VB
483 log_debug("interfaces", "receive PDU from bonded device %s",
484 hardware->h_ifname);
849954d7
VB
485 fromlen = sizeof(from);
486 if ((n = recvfrom(fd, buffer, size, 0,
487 (struct sockaddr *)&from,
488 &fromlen)) == -1) {
6f8925be 489 log_warn("interfaces", "error while receiving frame on %s",
849954d7
VB
490 hardware->h_ifname);
491 hardware->h_rx_discarded_cnt++;
492 return -1;
493 }
494 if (from.sll_pkttype == PACKET_OUTGOING)
495 return -1;
496 if (fd == hardware->h_sendfd)
497 /* We received this on the physical interface. */
498 return n;
499 /* We received this on the bonding interface. Is it really for us? */
44002d66 500 if (from.sll_ifindex == hardware->h_ifindex)
849954d7
VB
501 /* This is for us */
502 return n;
e12c2365 503 if (from.sll_ifindex == master->index)
849954d7
VB
504 /* We don't know from which physical interface it comes (kernel
505 * < 2.6.24). In doubt, this is for us. */
506 return n;
507 return -1; /* Not for us */
508}
509
510static int
511iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
512{
e12c2365 513 struct bond_master *master = hardware->h_data;
6f8925be
VB
514 log_debug("interfaces", "closing bonded device %s",
515 hardware->h_ifname);
adbb6e54
VB
516 interfaces_setup_multicast(cfg, hardware->h_ifname, 1);
517 interfaces_setup_multicast(cfg, master->name, 1);
849954d7
VB
518 free(hardware->h_data);
519 return 0;
520}
521
adbb6e54 522struct lldpd_ops bond_ops = {
5347914e 523 .send = iflinux_eth_send,
adbb6e54
VB
524 .recv = iface_bond_recv,
525 .cleanup = iface_bond_close,
526};
527
e12c2365 528static void
adbb6e54 529iflinux_handle_bond(struct lldpd *cfg, struct interfaces_device_list *interfaces)
849954d7 530{
adbb6e54
VB
531 struct interfaces_device *iface;
532 struct interfaces_device *master;
849954d7 533 struct lldpd_hardware *hardware;
c557a63b 534 struct bond_master *bmaster;
e12c2365 535 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
536 if (!(iface->type & IFACE_PHYSICAL_T)) continue;
537 if (!iface->flags) continue;
538 if (!iface->upper || !(iface->upper->type & IFACE_BOND_T)) continue;
849954d7 539
adbb6e54 540 master = iface->upper;
c557a63b
VB
541 log_debug("interfaces", "%s is an acceptable bonded device (master=%s)",
542 iface->name, master->name);
44002d66 543 if ((hardware = lldpd_get_hardware(cfg,
e12c2365
VB
544 iface->name,
545 iface->index,
44002d66 546 &bond_ops)) == NULL) {
849954d7 547 if ((hardware = lldpd_alloc_hardware(cfg,
e12c2365
VB
548 iface->name,
549 iface->index)) == NULL) {
6f8925be 550 log_warnx("interfaces", "Unable to allocate space for %s",
e12c2365
VB
551 iface->name);
552 continue;
553 }
a56f4137 554 hardware->h_data = calloc(1, sizeof(struct bond_master));
e12c2365
VB
555 if (!hardware->h_data) {
556 log_warn("interfaces", "not enough memory");
557 lldpd_hardware_cleanup(cfg, hardware);
558 continue;
559 }
849954d7 560 if (iface_bond_init(cfg, hardware) != 0) {
6f8925be 561 log_warn("interfaces", "unable to initialize %s",
849954d7
VB
562 hardware->h_ifname);
563 lldpd_hardware_cleanup(cfg, hardware);
564 continue;
565 }
566 hardware->h_ops = &bond_ops;
5347914e 567 hardware->h_mangle = 1;
bdfe4193 568 interfaces_helper_add_hardware(cfg, hardware);
849954d7 569 } else {
cfe00f7f 570 if (hardware->h_flags) continue; /* Already seen this time */
c557a63b 571 bmaster = hardware->h_data;
a56f4137 572 memset(bmaster, 0, sizeof(struct bond_master));
c557a63b 573 bmaster->index = master->index;
e2b09091 574 strlcpy(bmaster->name, master->name, IFNAMSIZ);
4b292b55 575 lldpd_port_cleanup(&hardware->h_lport, 0);
849954d7 576 }
e12c2365
VB
577
578 hardware->h_flags = iface->flags;
579 iface->flags = 0;
849954d7
VB
580
581 /* Get local address */
4e5f34c5 582 memcpy(&hardware->h_lladdr, iface->address, ETHER_ADDR_LEN);
e12c2365 583
36bd79e3 584 /* Fill information about port */
adbb6e54 585 interfaces_helper_port_name_desc(hardware, iface);
e12c2365 586
849954d7 587 /* Fill additional info */
7a008075 588#ifdef ENABLE_DOT3
e12c2365 589 hardware->h_lport.p_aggregid = master->index;
7a008075 590#endif
e12c2365 591 hardware->h_mtu = iface->mtu ? iface->mtu : 1500;
849954d7
VB
592 }
593}
594
adbb6e54 595/* Query each interface to get the appropriate driver */
5994b27d 596static void
adbb6e54
VB
597iflinux_add_driver(struct lldpd *cfg,
598 struct interfaces_device_list *interfaces)
5994b27d 599{
adbb6e54
VB
600 struct interfaces_device *iface;
601 TAILQ_FOREACH(iface, interfaces, next) {
602 struct ethtool_drvinfo ethc = {
603 .cmd = ETHTOOL_GDRVINFO
604 };
605 struct ifreq ifr = {
606 .ifr_data = (caddr_t)&ethc
607 };
608 if (iface->driver) continue;
609
610 strlcpy(ifr.ifr_name, iface->name, IFNAMSIZ);
611 if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) {
612 iface->driver = strdup(ethc.driver);
613 log_debug("interfaces", "driver for %s is `%s`",
614 iface->name, iface->driver);
615 }
5994b27d
VB
616 }
617}
618
adbb6e54 619/* Query each interface to see if it is a wireless one */
e12c2365 620static void
adbb6e54
VB
621iflinux_add_wireless(struct lldpd *cfg,
622 struct interfaces_device_list *interfaces)
f6c4ca4b 623{
adbb6e54
VB
624 struct interfaces_device *iface;
625 TAILQ_FOREACH(iface, interfaces, next) {
b0cb07f7 626 struct iwreq iwr = {};
adbb6e54
VB
627 strlcpy(iwr.ifr_name, iface->name, IFNAMSIZ);
628 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0) {
629 log_debug("interfaces", "%s is wireless",
630 iface->name);
631 iface->type |= IFACE_WIRELESS_T | IFACE_PHYSICAL_T;
f6c4ca4b 632 }
f6c4ca4b 633 }
adbb6e54 634}
f6c4ca4b 635
adbb6e54
VB
636/* Query each interface to see if it is a bridge */
637static void
638iflinux_add_bridge(struct lldpd *cfg,
639 struct interfaces_device_list *interfaces)
640{
641 struct interfaces_device *iface;
f6c4ca4b 642
adbb6e54
VB
643 TAILQ_FOREACH(iface, interfaces, next) {
644 if (iface->type & (IFACE_PHYSICAL_T|
645 IFACE_VLAN_T|IFACE_BOND_T|IFACE_BRIDGE_T))
646 continue;
647 if (iflinux_is_bridge(cfg, interfaces, iface)) {
648 log_debug("interfaces",
649 "interface %s is a bridge",
650 iface->name);
651 iface->type |= IFACE_BRIDGE_T;
f6c4ca4b 652 }
f6c4ca4b 653 }
f6c4ca4b
VB
654}
655
adbb6e54 656/* Query each interface to see if it is a bond */
f6c4ca4b 657static void
adbb6e54
VB
658iflinux_add_bond(struct lldpd *cfg,
659 struct interfaces_device_list *interfaces)
6e75df87 660{
adbb6e54 661 struct interfaces_device *iface;
e12c2365
VB
662
663 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
664 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
665 IFACE_BOND_T|IFACE_BRIDGE_T))
6e75df87 666 continue;
adbb6e54
VB
667 if (iflinux_is_bond(cfg, interfaces, iface)) {
668 log_debug("interfaces",
669 "interface %s is a bond",
670 iface->name);
671 iface->type |= IFACE_BOND_T;
adbb6e54 672 }
6e75df87 673 }
6e75df87
VB
674}
675
adbb6e54 676/* Query each interface to see if it is a vlan */
e12c2365 677static void
adbb6e54
VB
678iflinux_add_vlan(struct lldpd *cfg,
679 struct interfaces_device_list *interfaces)
6e75df87 680{
adbb6e54 681 struct interfaces_device *iface;
d4e4c804 682
adbb6e54
VB
683 TAILQ_FOREACH(iface, interfaces, next) {
684 if (iface->type & (IFACE_PHYSICAL_T|IFACE_VLAN_T|
685 IFACE_BOND_T|IFACE_BRIDGE_T))
686 continue;
687 if (iflinux_is_vlan(cfg, interfaces, iface)) {
688 log_debug("interfaces",
689 "interface %s is a VLAN",
690 iface->name);
691 iface->type |= IFACE_VLAN_T;
6e75df87
VB
692 }
693 }
694}
31ee070d 695
e12c2365 696static void
adbb6e54
VB
697iflinux_add_physical(struct lldpd *cfg,
698 struct interfaces_device_list *interfaces)
b4ac8083 699{
adbb6e54
VB
700 struct interfaces_device *iface;
701 /* White-list some drivers */
702 const char * const *rif;
703 const char * const regular_interfaces[] = {
704 "dsa",
705 "veth",
706 NULL
707 };
b4ac8083 708
e12c2365 709 TAILQ_FOREACH(iface, interfaces, next) {
adbb6e54
VB
710 if (iface->type & (IFACE_VLAN_T|
711 IFACE_BOND_T|IFACE_BRIDGE_T))
712 continue;
b4ac8083 713
adbb6e54
VB
714 iface->type &= ~IFACE_PHYSICAL_T;
715
716 /* We request that the interface is able to do either multicast
717 * or broadcast to be able to send discovery frames. */
718 if (!(iface->flags & (IFF_MULTICAST|IFF_BROADCAST))) {
719 log_debug("interfaces", "skip %s: not able to do multicast nor broadcast",
720 iface->name);
b4ac8083 721 continue;
adbb6e54 722 }
b4ac8083 723
adbb6e54
VB
724 /* Check if the driver is whitelisted */
725 if (iface->driver) {
726 for (rif = regular_interfaces; *rif; rif++) {
727 if (strcmp(iface->driver, *rif) == 0) {
728 /* White listed! */
729 log_debug("interfaces", "accept %s: whitelisted",
730 iface->name);
731 iface->type |= IFACE_PHYSICAL_T;
732 continue;
733 }
734 }
735 }
736
981ee4fe
VB
737 /* If the interface is linked to another one, skip it too. */
738 if (iface->lower) {
739 log_debug("interfaces", "skip %s: there is a lower interface (%s)",
740 iface->name, iface->lower->name);
741 continue;
742 }
743
adbb6e54
VB
744 /* Check queue len. If no queue, this usually means that this
745 is not a "real" interface. */
746 if (iface->txqueue == 0) {
747 log_debug("interfaces", "skip %s: no queue",
748 iface->name);
749 continue;
750 }
751
12baf781
VB
752 /* Get the real MAC address (for example, if the interface is enslaved) */
753 iflinux_get_permanent_mac(cfg, interfaces, iface);
754
adbb6e54
VB
755 log_debug("interfaces",
756 "%s is a physical interface",
757 iface->name);
758 iface->type |= IFACE_PHYSICAL_T;
b4ac8083
VB
759 }
760}
761
e12c2365
VB
762void
763interfaces_update(struct lldpd *cfg)
764{
adbb6e54 765 struct lldpd_hardware *hardware;
281a5cd4
VB
766 struct interfaces_device_list *interfaces;
767 struct interfaces_address_list *addresses;
e12c2365
VB
768 interfaces = netlink_get_interfaces();
769 addresses = netlink_get_addresses();
770 if (interfaces == NULL || addresses == NULL) {
771 log_warnx("interfaces", "cannot update the list of local interfaces");
772 goto end;
773 }
774
adbb6e54
VB
775 /* Add missing bits to list of interfaces */
776 iflinux_add_driver(cfg, interfaces);
777 iflinux_add_wireless(cfg, interfaces);
778 iflinux_add_bridge(cfg, interfaces);
779 iflinux_add_bond(cfg, interfaces);
780 iflinux_add_vlan(cfg, interfaces);
781 iflinux_add_physical(cfg, interfaces);
782
783 interfaces_helper_whitelist(cfg, interfaces);
784 iflinux_handle_bond(cfg, interfaces);
88bc404f 785 interfaces_helper_physical(cfg, interfaces,
22e8cd65 786 &eth_ops,
88bc404f 787 iflinux_eth_init);
e12c2365 788#ifdef ENABLE_DOT1
adbb6e54 789 interfaces_helper_vlan(cfg, interfaces);
e12c2365 790#endif
adbb6e54
VB
791 interfaces_helper_mgmt(cfg, addresses);
792 interfaces_helper_chassis(cfg, interfaces);
793
794 /* Mac/PHY */
795 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
796 if (!hardware->h_flags) continue;
797 iflinux_macphy(hardware);
798 }
e12c2365 799
0484f180
VB
800 if (cfg->g_iface_event == NULL) {
801 int s;
802 log_debug("interfaces", "subscribe to netlink notifications");
803 s = netlink_subscribe_changes();
804 if (s == -1) {
805 log_warnx("interfaces", "unable to subscribe to netlink notifications");
806 goto end;
807 }
aa313f2a
VB
808 if (levent_iface_subscribe(cfg, s) == -1)
809 close(s);
87dfd175
VB
810 /* coverity[leaked_handle]
811 s has been saved by levent_iface_subscribe */
0484f180
VB
812 }
813
e12c2365 814end:
adbb6e54
VB
815 interfaces_free_devices(interfaces);
816 interfaces_free_addresses(addresses);
e12c2365 817}