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