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