]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/interfaces.c
Remove "listen on VLAN" feature.
[thirdparty/lldpd.git] / src / interfaces.c
CommitLineData
6e75df87
VB
1/*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#define INCLUDE_LINUX_IF_H
18#include "lldpd.h"
19
20#include <stdio.h>
06db3608 21#include <stddef.h>
6e75df87
VB
22#include <unistd.h>
23#include <errno.h>
24#include <sys/ioctl.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <fcntl.h>
28#include <fnmatch.h>
29#include <arpa/inet.h>
6e75df87
VB
30#include <net/if_arp.h>
31#include <linux/if_vlan.h>
32#include <linux/if_bonding.h>
33#include <linux/if_bridge.h>
34#include <linux/wireless.h>
35#include <linux/sockios.h>
36#include <linux/filter.h>
6e75df87
VB
37#include <linux/if_packet.h>
38
39#define SYSFS_PATH_MAX 256
40#define MAX_PORTS 1024
41#define MAX_BRIDGES 1024
42
43/* BPF filter to get revelant information from interfaces */
44/* LLDP: "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
45/* FDP: "ether dst 01:e0:52:cc:cc:cc" */
46/* CDP: "ether dst 01:00:0c:cc:cc:cc" */
47/* SONMP: "ether dst 01:00:81:00:01:00" */
48/* EDP: "ether dst 00:e0:2b:00:00:00" */
49#define LLDPD_FILTER_F \
50 { 0x28, 0, 0, 0x0000000c }, \
51 { 0x15, 0, 4, 0x000088cc }, \
52 { 0x20, 0, 0, 0x00000002 }, \
53 { 0x15, 0, 2, 0xc200000e }, \
54 { 0x28, 0, 0, 0x00000000 }, \
55 { 0x15, 11, 12, 0x00000180 }, \
56 { 0x20, 0, 0, 0x00000002 }, \
57 { 0x15, 0, 2, 0x2b000000 }, \
58 { 0x28, 0, 0, 0x00000000 }, \
59 { 0x15, 7, 8, 0x000000e0 }, \
60 { 0x15, 1, 0, 0x0ccccccc }, \
61 { 0x15, 0, 2, 0x81000100 }, \
62 { 0x28, 0, 0, 0x00000000 }, \
63 { 0x15, 3, 4, 0x00000100 }, \
64 { 0x15, 0, 3, 0x52cccccc }, \
65 { 0x28, 0, 0, 0x00000000 }, \
66 { 0x15, 0, 1, 0x000001e0 }, \
67 { 0x6, 0, 0, 0x0000ffff }, \
68 { 0x6, 0, 0, 0x00000000 },
69static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
70
71/* net/if.h */
72extern unsigned int if_nametoindex (__const char *__ifname) __THROW;
73extern char *if_indextoname (unsigned int __ifindex, char *__ifname) __THROW;
74
75static int iface_is_bridge(struct lldpd *, const char *);
7a008075 76#ifdef ENABLE_DOT1
6e75df87
VB
77static int iface_is_bridged_to(struct lldpd *,
78 const char *, const char *);
7a008075 79#endif
6e75df87
VB
80static int iface_is_wireless(struct lldpd *, const char *);
81static int iface_is_vlan(struct lldpd *, const char *);
82static int iface_is_bond(struct lldpd *, const char *);
83static int iface_is_bond_slave(struct lldpd *,
84 const char *, const char *, int *);
85static int iface_is_enslaved(struct lldpd *, const char *);
6e75df87 86static void iface_get_permanent_mac(struct lldpd *, struct lldpd_hardware *);
849954d7 87static int iface_minimal_checks(struct lldpd *, struct ifaddrs *);
6a2fa591 88static int iface_set_filter(const char *, int);
6e75df87 89
36bd79e3 90static void iface_port_name_desc(struct lldpd_hardware *);
6e75df87
VB
91static void iface_macphy(struct lldpd_hardware *);
92static void iface_mtu(struct lldpd *, struct lldpd_hardware *);
93static void iface_multicast(struct lldpd *, const char *, int);
94static int iface_eth_init(struct lldpd *, struct lldpd_hardware *);
849954d7 95static int iface_bond_init(struct lldpd *, struct lldpd_hardware *);
6a2fa591 96static void iface_fds_close(struct lldpd *, struct lldpd_hardware *);
5994b27d
VB
97#ifdef ENABLE_DOT1
98static void iface_append_vlan(struct lldpd *,
99 struct lldpd_hardware *, struct ifaddrs *);
100#endif
6e75df87
VB
101
102static int iface_eth_send(struct lldpd *, struct lldpd_hardware*, char *, size_t);
103static int iface_eth_recv(struct lldpd *, struct lldpd_hardware*, int, char*, size_t);
104static int iface_eth_close(struct lldpd *, struct lldpd_hardware *);
105struct lldpd_ops eth_ops = {
106 .send = iface_eth_send,
107 .recv = iface_eth_recv,
108 .cleanup = iface_eth_close,
109};
849954d7
VB
110static int iface_bond_send(struct lldpd *, struct lldpd_hardware*, char *, size_t);
111static int iface_bond_recv(struct lldpd *, struct lldpd_hardware*, int, char*, size_t);
112static int iface_bond_close(struct lldpd *, struct lldpd_hardware *);
113struct lldpd_ops bond_ops = {
114 .send = iface_bond_send,
115 .recv = iface_bond_recv,
116 .cleanup = iface_bond_close,
117};
6e75df87
VB
118
119static int
120old_iface_is_bridge(struct lldpd *cfg, const char *name)
121{
122 int ifindices[MAX_BRIDGES];
123 char ifname[IFNAMSIZ];
124 int num, i;
125 unsigned long args[3] = { BRCTL_GET_BRIDGES,
126 (unsigned long)ifindices, MAX_BRIDGES };
b9f4c120
VB
127 if ((num = ioctl(cfg->g_sock, SIOCGIFBR, args)) < 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 for (i = 0; i < num; i++) {
133 if (if_indextoname(ifindices[i], ifname) == NULL)
134 LLOG_INFO("unable to get name of interface %d",
135 ifindices[i]);
136 else if (strncmp(name, ifname, IFNAMSIZ) == 0)
137 return 1;
138 }
139 return 0;
140}
141
142static int
143iface_is_bridge(struct lldpd *cfg, const char *name)
144{
f4ed5af1 145#ifdef SYSFS_BRIDGE_FDB
6e75df87
VB
146 char path[SYSFS_PATH_MAX];
147 int f;
148
149 if ((snprintf(path, SYSFS_PATH_MAX,
150 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX)
151 LLOG_WARNX("path truncated");
152 if ((f = priv_open(path)) < 0) {
153 return old_iface_is_bridge(cfg, name);
154 }
155 close(f);
156 return 1;
f4ed5af1
VB
157#else
158 return old_iface_is_bridge(cfg, name);
159#endif
6e75df87
VB
160}
161
7a008075 162#ifdef ENABLE_DOT1
6e75df87
VB
163static int
164old_iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
165{
166 int j, index = if_nametoindex(slave);
167 int ifptindices[MAX_PORTS];
168 unsigned long args2[4] = { BRCTL_GET_PORT_LIST,
169 (unsigned long)ifptindices, MAX_PORTS, 0 };
170 struct ifreq ifr;
171
172 strncpy(ifr.ifr_name, master, IFNAMSIZ);
173 memset(ifptindices, 0, sizeof(ifptindices));
174 ifr.ifr_data = (char *)&args2;
175
b9f4c120
VB
176 if (ioctl(cfg->g_sock, SIOCDEVPRIVATE, &ifr) < 0)
177 /* This can happen with a 64bit kernel and 32bit
178 userland, don't output anything about this to avoid
179 to fill logs. */
6e75df87 180 return 0;
6e75df87
VB
181
182 for (j = 0; j < MAX_PORTS; j++) {
183 if (ifptindices[j] == index)
184 return 1;
185 }
186
187 return 0;
188}
189
190static int
191iface_is_bridged_to(struct lldpd *cfg, const char *slave, const char *master)
192{
f4ed5af1 193#ifdef SYSFS_BRIDGE_PORT_SUBDIR
6e75df87
VB
194 char path[SYSFS_PATH_MAX];
195 int f;
196
197 /* Master should be a bridge, first */
198 if (!iface_is_bridge(cfg, master)) return 0;
199
200 if (snprintf(path, SYSFS_PATH_MAX,
201 SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_SUBDIR "/%s/port_no",
202 master, slave) >= SYSFS_PATH_MAX)
203 LLOG_WARNX("path truncated");
204 if ((f = priv_open(path)) < 0) {
205 return old_iface_is_bridged_to(cfg, slave, master);
206 }
207 close(f);
208 return 1;
f4ed5af1
VB
209#else
210 return old_iface_is_bridged_to(cfg, slave, master);
211#endif
6e75df87 212}
7a008075 213#endif
6e75df87
VB
214
215static int
216iface_is_vlan(struct lldpd *cfg, const char *name)
217{
218 struct vlan_ioctl_args ifv;
219 memset(&ifv, 0, sizeof(ifv));
220 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
221 if ((strlcpy(ifv.device1, name, sizeof(ifv.device1))) >=
222 sizeof(ifv.device1))
223 LLOG_WARNX("device name truncated");
224 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0)
225 return 1;
226 return 0;
227}
228
229static int
230iface_is_wireless(struct lldpd *cfg, const char *name)
231{
232 struct iwreq iwr;
233 strlcpy(iwr.ifr_name, name, IFNAMSIZ);
234 if (ioctl(cfg->g_sock, SIOCGIWNAME, &iwr) >= 0)
235 return 1;
236 return 0;
237}
238
239static int
240iface_is_bond(struct lldpd *cfg, const char *name)
241{
242 struct ifreq ifr;
243 struct ifbond ifb;
244 memset(&ifr, 0, sizeof(ifr));
245 memset(&ifb, 0, sizeof(ifb));
246 strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
8f88ff70 247 ifr.ifr_data = (char *)&ifb;
6e75df87
VB
248 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0)
249 return 1;
250 return 0;
251}
252
253static int
254iface_is_bond_slave(struct lldpd *cfg, const char *slave, const char *master,
255 int *active)
256{
257 struct ifreq ifr;
258 struct ifbond ifb;
259 struct ifslave ifs;
260 memset(&ifr, 0, sizeof(ifr));
261 memset(&ifb, 0, sizeof(ifb));
262 strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name));
8f88ff70 263 ifr.ifr_data = (char *)&ifb;
6e75df87
VB
264 if (ioctl(cfg->g_sock, SIOCBONDINFOQUERY, &ifr) >= 0) {
265 while (ifb.num_slaves--) {
266 memset(&ifr, 0, sizeof(ifr));
267 memset(&ifs, 0, sizeof(ifs));
268 strlcpy(ifr.ifr_name, master, sizeof(ifr.ifr_name));
8f88ff70 269 ifr.ifr_data = (char *)&ifs;
6e75df87
VB
270 ifs.slave_id = ifb.num_slaves;
271 if ((ioctl(cfg->g_sock, SIOCBONDSLAVEINFOQUERY, &ifr) >= 0) &&
272 (strncmp(ifs.slave_name, slave, sizeof(ifs.slave_name)) == 0)) {
273 if (active)
274 *active = ifs.state;
275 return 1;
276 }
277 }
278 }
279 return 0;
280}
281
282static int
283iface_is_enslaved(struct lldpd *cfg, const char *name)
284{
285 struct ifaddrs *ifap, *ifa;
286 int master;
287
288 if (getifaddrs(&ifap) != 0) {
289 LLOG_WARN("unable to get interface list");
290 return -1;
291 }
292 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
293 if (iface_is_bond_slave(cfg, name, ifa->ifa_name, NULL)) {
294 master = if_nametoindex(ifa->ifa_name);
295 freeifaddrs(ifap);
296 return master;
297 }
298 }
299 freeifaddrs(ifap);
300 return -1;
301}
302
6e75df87
VB
303static void
304iface_get_permanent_mac(struct lldpd *cfg, struct lldpd_hardware *hardware)
305{
306 int master, f, state = 0;
307 FILE *netbond;
308 const char *slaveif = "Slave Interface: ";
309 const char *hwaddr = "Permanent HW addr: ";
310 u_int8_t mac[ETHER_ADDR_LEN];
311 char bond[IFNAMSIZ];
312 char path[SYSFS_PATH_MAX];
313 char line[100];
314 if ((master = iface_is_enslaved(cfg, hardware->h_ifname)) == -1)
315 return;
316 /* We have a bond, we need to query it to get real MAC addresses */
317 if ((if_indextoname(master, bond)) == NULL) {
318 LLOG_WARNX("unable to get bond name");
319 return;
320 }
321
322 if (snprintf(path, SYSFS_PATH_MAX, "/proc/net/bonding/%s",
323 bond) >= SYSFS_PATH_MAX) {
324 LLOG_WARNX("path truncated");
325 return;
326 }
327 if ((f = priv_open(path)) < 0) {
328 if (snprintf(path, SYSFS_PATH_MAX, "/proc/self/net/bonding/%s",
329 bond) >= SYSFS_PATH_MAX) {
330 LLOG_WARNX("path truncated");
331 return;
332 }
333 f = priv_open(path);
334 }
335 if (f < 0) {
336 LLOG_WARNX("unable to find %s in /proc/net/bonding or /proc/self/net/bonding",
337 bond);
338 return;
339 }
340 if ((netbond = fdopen(f, "r")) == NULL) {
341 LLOG_WARN("unable to read stream from %s", path);
342 close(f);
343 return;
344 }
345 /* State 0:
346 We parse the file to search "Slave Interface: ". If found, go to
347 state 1.
348 State 1:
349 We parse the file to search "Permanent HW addr: ". If found, we get
350 the mac.
351 */
352 while (fgets(line, sizeof(line), netbond)) {
353 switch (state) {
354 case 0:
355 if (strncmp(line, slaveif, strlen(slaveif)) == 0) {
356 if (line[strlen(line)-1] == '\n')
357 line[strlen(line)-1] = '\0';
358 if (strncmp(hardware->h_ifname,
359 line + strlen(slaveif),
360 sizeof(hardware->h_ifname)) == 0)
361 state++;
362 }
363 break;
364 case 1:
365 if (strncmp(line, hwaddr, strlen(hwaddr)) == 0) {
366 if (line[strlen(line)-1] == '\n')
367 line[strlen(line)-1] = '\0';
368 if (sscanf(line + strlen(hwaddr),
369 "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
370 &mac[0], &mac[1], &mac[2],
371 &mac[3], &mac[4], &mac[5]) !=
372 ETHER_ADDR_LEN) {
373 LLOG_WARN("unable to parse %s",
374 line + strlen(hwaddr));
375 fclose(netbond);
376 return;
377 }
378 memcpy(hardware->h_lladdr, mac,
379 ETHER_ADDR_LEN);
380 fclose(netbond);
381 return;
382 }
383 break;
384 }
385 }
386 LLOG_WARNX("unable to find real mac address for %s",
387 bond);
388 fclose(netbond);
389}
390
849954d7
VB
391/* Generic minimal checks to handle a given interface. */
392static int
393iface_minimal_checks(struct lldpd *cfg, struct ifaddrs *ifa)
394{
06db3608 395 struct sockaddr_ll sdl;
58fe6128 396 struct ifreq ifr;
a0aa43e9 397 int is_bridge = iface_is_bridge(cfg, ifa->ifa_name);
849954d7
VB
398
399 if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_BRIDGE) &&
a0aa43e9 400 is_bridge) {
849954d7
VB
401 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
402 return 0;
403 }
404
405 if (!(LOCAL_CHASSIS(cfg)->c_cap_enabled & LLDP_CAP_WLAN) &&
406 iface_is_wireless(cfg, ifa->ifa_name))
407 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
408
409 /* First, check if this interface has already been handled */
410 if (!ifa->ifa_flags)
411 return 0;
412
413 if (ifa->ifa_addr == NULL ||
414 ifa->ifa_addr->sa_family != PF_PACKET)
415 return 0;
416
06db3608
VB
417 memcpy(&sdl, ifa->ifa_addr, sizeof(struct sockaddr_ll));
418 if (sdl.sll_hatype != ARPHRD_ETHER || !sdl.sll_halen)
849954d7
VB
419 return 0;
420
421 /* We request that the interface is able to do either multicast
422 * or broadcast to be able to send discovery frames. */
423 if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
424 return 0;
425
58fe6128
VB
426 /* Check queue len. If no queue, this usually means that this
427 is not a "real" interface. */
428 memset(&ifr, 0, sizeof(ifr));
429 strcpy(ifr.ifr_name, ifa->ifa_name);
430 if ((ioctl(cfg->g_sock, SIOCGIFTXQLEN, &ifr) < 0) || !ifr.ifr_qlen)
431 return 0;
432
a0aa43e9 433 /* Don't handle bond and VLAN, nor bridge */
849954d7 434 if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
a0aa43e9
V
435 (iface_is_bond(cfg, ifa->ifa_name)) ||
436 is_bridge)
849954d7
VB
437 return 0;
438
439 return 1;
440}
441
442static int
6a2fa591 443iface_set_filter(const char *name, int fd)
849954d7
VB
444{
445 const struct sock_fprog prog = {
446 .filter = lldpd_filter_f,
447 .len = sizeof(lldpd_filter_f) / sizeof(struct sock_filter)
448 };
449 if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER,
450 &prog, sizeof(prog)) < 0) {
6a2fa591 451 LLOG_WARN("unable to change filter for %s", name);
849954d7
VB
452 return ENETDOWN;
453 }
454 return 0;
455}
456
f8cb50fa 457/* Fill up port name and description */
849954d7 458static void
36bd79e3 459iface_port_name_desc(struct lldpd_hardware *hardware)
849954d7
VB
460{
461 struct lldpd_port *port = &hardware->h_lport;
36bd79e3
VB
462 char buffer[256]; /* 256 = IFALIASZ */
463 char path[SYSFS_PATH_MAX];
464 int f;
465
466 /* There are two cases:
467
468 1. We have a kernel recent enough to support ifAlias
469 _and_ a non empty ifAlias, then we will use it for
470 description and use ifname for port ID.
471
472 2. Otherwise, we will use the MAC address as ID and the
473 port name in description.
474 */
475
476 if ((snprintf(path, SYSFS_PATH_MAX,
477 SYSFS_CLASS_NET "%s/ifalias", hardware->h_ifname)) >= SYSFS_PATH_MAX)
478 LLOG_WARNX("path truncated");
479 memset(buffer, 0, sizeof(buffer));
480 if (((f = priv_open(path)) < 0) || (read(f, buffer, sizeof(buffer)-1) < 1)) {
481 /* Case 2: MAC address and port name */
482 close(f);
483 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
484 if ((port->p_id =
485 calloc(1, sizeof(hardware->h_lladdr))) == NULL)
486 fatal(NULL);
487 memcpy(port->p_id, hardware->h_lladdr,
488 sizeof(hardware->h_lladdr));
489 port->p_id_len = sizeof(hardware->h_lladdr);
490 port->p_descr = strdup(hardware->h_ifname);
491 return;
492 }
493 /* Case 1: port name and port description */
494 close(f);
495 port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
496 port->p_id_len = strlen(hardware->h_ifname);
497 if ((port->p_id =
498 calloc(1, port->p_id_len)) == NULL)
849954d7 499 fatal(NULL);
36bd79e3
VB
500 memcpy(port->p_id, hardware->h_ifname, port->p_id_len);
501 if (buffer[strlen(buffer) - 1] == '\n')
502 buffer[strlen(buffer) - 1] = '\0';
503 port->p_descr = strdup(buffer);
849954d7
VB
504}
505
6e75df87
VB
506/* Fill up MAC/PHY for a given hardware port */
507static void
508iface_macphy(struct lldpd_hardware *hardware)
509{
510#ifdef ENABLE_DOT3
511 struct ethtool_cmd ethc;
512 struct lldpd_port *port = &hardware->h_lport;
513 int j;
514 int advertised_ethtool_to_rfc3636[][2] = {
515 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
516 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
517 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
518 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
519 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
520 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
521 {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
522 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
523 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
524 {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
525 {0,0}};
526
527 if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
528 port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
529 port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
530 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
531 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
532 port->p_autoneg_advertised |=
533 advertised_ethtool_to_rfc3636[j][1];
534 }
535 switch (ethc.speed) {
536 case SPEED_10:
537 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
538 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
539 if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2;
540 if (ethc.port == PORT_FIBRE)
541 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
542 LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD;
543 break;
544 case SPEED_100:
545 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
546 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
547 if (ethc.port == PORT_BNC)
548 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
549 LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD;
550 if (ethc.port == PORT_FIBRE)
551 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
552 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
553 break;
554 case SPEED_1000:
555 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
556 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
557 if (ethc.port == PORT_FIBRE)
558 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
559 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
560 break;
561 case SPEED_10000:
562 port->p_mau_type = (ethc.port == PORT_FIBRE) ? \
563 LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
564 break;
565 }
566 if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
567 }
568#endif
569}
570
571static void
572iface_mtu(struct lldpd *cfg, struct lldpd_hardware *hardware)
573{
574 struct ifreq ifr;
575
576 /* get MTU */
577 memset(&ifr, 0, sizeof(ifr));
578 strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
579 if (ioctl(cfg->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) {
580 LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname);
581 hardware->h_mtu = 1500;
582 } else
77c4d9d0 583 hardware->h_mtu = ifr.ifr_mtu;
6e75df87
VB
584}
585
586static void
587iface_multicast(struct lldpd *cfg, const char *name, int remove)
588{
589 int i, rc;
590
591 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
592 if (!cfg->g_protocols[i].enabled) continue;
593 if ((rc = priv_iface_multicast(name,
594 cfg->g_protocols[i].mac, !remove)) != 0) {
595 errno = rc;
596 if (errno != ENOENT)
597 LLOG_INFO("unable to %s %s address to multicast filter for %s",
598 (remove)?"delete":"add",
599 cfg->g_protocols[i].name,
600 name);
601 }
602 }
603}
604
6a2fa591
VB
605static void
606iface_fds_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
607{
608 int i;
bc598c23 609 for (i=0; i < LLDPD_FD_SETSIZE; i++)
6a2fa591
VB
610 if (FD_ISSET(i, &hardware->h_recvfds)) {
611 FD_CLR(i, &hardware->h_recvfds);
612 close(i);
613 }
614}
615
616
6e75df87
VB
617static int
618iface_eth_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
619{
849954d7 620 int fd, status;
6e75df87 621
849954d7
VB
622 if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
623 return -1;
624 hardware->h_sendfd = fd;
6e75df87
VB
625
626 /* fd to receive is the same as fd to send */
849954d7 627 FD_SET(fd, &hardware->h_recvfds);
6e75df87
VB
628
629 /* Set filter */
6a2fa591 630 if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
849954d7
VB
631 close(fd);
632 return status;
6e75df87
VB
633 }
634
635 iface_multicast(cfg, hardware->h_ifname, 0);
636
637 LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
849954d7 638 fd);
6e75df87
VB
639 return 0;
640}
641
642static int
643iface_eth_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
644 char *buffer, size_t size)
645{
646 return write(hardware->h_sendfd,
647 buffer, size);
648}
649
650static int
651iface_eth_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
652 int fd, char *buffer, size_t size)
653{
654 int n;
655 struct sockaddr_ll from;
656 socklen_t fromlen;
657
658 fromlen = sizeof(from);
659 if ((n = recvfrom(fd,
660 buffer,
661 size, 0,
662 (struct sockaddr *)&from,
663 &fromlen)) == -1) {
664 LLOG_WARN("error while receiving frame on %s",
665 hardware->h_ifname);
666 hardware->h_rx_discarded_cnt++;
667 return -1;
668 }
669 if (from.sll_pkttype == PACKET_OUTGOING)
670 return -1;
671 return n;
672}
673
674static int
675iface_eth_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
676{
677 close(hardware->h_sendfd);
678 iface_multicast(cfg, hardware->h_ifname, 1);
679 return 0;
680}
681
682void
683lldpd_ifh_eth(struct lldpd *cfg, struct ifaddrs *ifap)
684{
685 struct ifaddrs *ifa;
6e75df87
VB
686 struct lldpd_hardware *hardware;
687 struct lldpd_port *port;
6e75df87
VB
688
689 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
849954d7 690 if (!iface_minimal_checks(cfg, ifa))
6e75df87
VB
691 continue;
692
44002d66
VB
693 if ((hardware = lldpd_get_hardware(cfg,
694 ifa->ifa_name,
695 if_nametoindex(ifa->ifa_name),
696 &eth_ops)) == NULL) {
6e75df87
VB
697 if ((hardware = lldpd_alloc_hardware(cfg,
698 ifa->ifa_name)) == NULL) {
699 LLOG_WARNX("Unable to allocate space for %s",
700 ifa->ifa_name);
701 continue;
702 }
703 if (iface_eth_init(cfg, hardware) != 0) {
704 LLOG_WARN("unable to initialize %s", hardware->h_ifname);
705 lldpd_hardware_cleanup(cfg, hardware);
706 continue;
707 }
708 hardware->h_ops = &eth_ops;
44002d66 709 hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
6e75df87 710 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
cfe00f7f
VB
711 } else {
712 if (hardware->h_flags) continue; /* Already seen this time */
9898ac07 713 lldpd_port_cleanup(cfg, &hardware->h_lport, 0);
cfe00f7f 714 }
6e75df87
VB
715
716 port = &hardware->h_lport;
717 hardware->h_flags = ifa->ifa_flags; /* Should be non-zero */
718 ifa->ifa_flags = 0; /* Future handlers
719 don't have to
720 care about this
721 interface. */
722
723 /* Get local address */
06db3608
VB
724 memcpy(&hardware->h_lladdr,
725 (u_int8_t*)((u_int8_t*)ifa->ifa_addr +
726 offsetof(struct sockaddr_ll, sll_addr)),
727 sizeof(hardware->h_lladdr));
6e75df87 728
36bd79e3
VB
729 /* Fill information about port */
730 iface_port_name_desc(hardware);
6e75df87
VB
731
732 /* Fill additional info */
733 iface_macphy(hardware);
734 iface_mtu(cfg, hardware);
735 }
736}
737
849954d7
VB
738static int
739iface_bond_init(struct lldpd *cfg, struct lldpd_hardware *hardware)
740{
741 char *mastername = (char *)hardware->h_data; /* Master name */
742 int fd, status;
743 int un = 1;
744
745 if (!mastername) return -1;
746
747 /* First, we get a socket to the raw physical interface */
748 if ((fd = priv_iface_init(hardware->h_ifname)) == -1)
749 return -1;
750 hardware->h_sendfd = fd;
751 FD_SET(fd, &hardware->h_recvfds);
6a2fa591 752 if ((status = iface_set_filter(hardware->h_ifname, fd)) != 0) {
849954d7
VB
753 close(fd);
754 return status;
755 }
756 iface_multicast(cfg, hardware->h_ifname, 0);
757
758 /* Then, we open a raw interface for the master */
759 if ((fd = priv_iface_init(mastername)) == -1) {
760 close(hardware->h_sendfd);
761 return -1;
762 }
763 FD_SET(fd, &hardware->h_recvfds);
6a2fa591 764 if ((status = iface_set_filter(mastername, fd)) != 0) {
849954d7
VB
765 close(hardware->h_sendfd);
766 close(fd);
767 return status;
768 }
769 /* With bonding and older kernels (< 2.6.27) we need to listen
770 * to bond device. We use setsockopt() PACKET_ORIGDEV to get
771 * physical device instead of bond device (works with >=
772 * 2.6.24). */
773 if (setsockopt(fd, SOL_PACKET,
774 PACKET_ORIGDEV, &un, sizeof(un)) == -1) {
775 LLOG_DEBUG("[priv]: unable to setsockopt for master bonding device of %s. "
776 "You will get inaccurate results",
777 hardware->h_ifname);
778 }
779 iface_multicast(cfg, mastername, 0);
780
781 LLOG_DEBUG("interface %s initialized (fd=%d,master=%s[%d])",
782 hardware->h_ifname,
783 hardware->h_sendfd,
784 mastername, fd);
785 return 0;
786}
787
788static int
789iface_bond_send(struct lldpd *cfg, struct lldpd_hardware *hardware,
790 char *buffer, size_t size)
791{
792 /* With bonds, we have duplicate MAC address on different physical
793 * interfaces. We need to alter the source MAC address when we send on
794 * an inactive slave. */
795 char *master = (char*)hardware->h_data;
796 int active;
797 if (!iface_is_bond_slave(cfg, hardware->h_ifname, master, &active)) {
798 LLOG_WARNX("%s seems to not be enslaved anymore?",
799 hardware->h_ifname);
800 return 0;
801 }
802 if (active) {
803 /* We need to modify the source MAC address */
804 if (size < 2 * ETH_ALEN) {
805 LLOG_WARNX("packet to send on %s is too small!",
806 hardware->h_ifname);
807 return 0;
808 }
809 memset(buffer + ETH_ALEN, 0, ETH_ALEN);
810 }
811 return write(hardware->h_sendfd,
812 buffer, size);
813}
814
815static int
816iface_bond_recv(struct lldpd *cfg, struct lldpd_hardware *hardware,
817 int fd, char *buffer, size_t size)
818{
819 int n;
820 struct sockaddr_ll from;
821 socklen_t fromlen;
822
823 fromlen = sizeof(from);
824 if ((n = recvfrom(fd, buffer, size, 0,
825 (struct sockaddr *)&from,
826 &fromlen)) == -1) {
827 LLOG_WARN("error while receiving frame on %s",
828 hardware->h_ifname);
829 hardware->h_rx_discarded_cnt++;
830 return -1;
831 }
832 if (from.sll_pkttype == PACKET_OUTGOING)
833 return -1;
834 if (fd == hardware->h_sendfd)
835 /* We received this on the physical interface. */
836 return n;
837 /* We received this on the bonding interface. Is it really for us? */
44002d66 838 if (from.sll_ifindex == hardware->h_ifindex)
849954d7
VB
839 /* This is for us */
840 return n;
841 if (from.sll_ifindex == if_nametoindex((char*)hardware->h_data))
842 /* We don't know from which physical interface it comes (kernel
843 * < 2.6.24). In doubt, this is for us. */
844 return n;
845 return -1; /* Not for us */
846}
847
848static int
849iface_bond_close(struct lldpd *cfg, struct lldpd_hardware *hardware)
850{
6a2fa591 851 iface_fds_close(cfg, hardware); /* h_sendfd is here too */
849954d7
VB
852 iface_multicast(cfg, hardware->h_ifname, 1);
853 iface_multicast(cfg, (char*)hardware->h_data, 1);
854 free(hardware->h_data);
855 return 0;
856}
857
858void
859lldpd_ifh_bond(struct lldpd *cfg, struct ifaddrs *ifap)
860{
861 struct ifaddrs *ifa;
862 struct lldpd_hardware *hardware;
863 struct lldpd_port *port;
864 int master;
865 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
866 if (!iface_minimal_checks(cfg, ifa))
867 continue;
868 if ((master = iface_is_enslaved(cfg, ifa->ifa_name)) == -1)
869 continue;
870
44002d66
VB
871 if ((hardware = lldpd_get_hardware(cfg,
872 ifa->ifa_name,
873 if_nametoindex(ifa->ifa_name),
874 &bond_ops)) == NULL) {
849954d7
VB
875 if ((hardware = lldpd_alloc_hardware(cfg,
876 ifa->ifa_name)) == NULL) {
877 LLOG_WARNX("Unable to allocate space for %s",
878 ifa->ifa_name);
879 continue;
880 }
881 hardware->h_data = (char *)calloc(1, IFNAMSIZ);
882 if_indextoname(master, hardware->h_data);
883 if (iface_bond_init(cfg, hardware) != 0) {
884 LLOG_WARN("unable to initialize %s",
885 hardware->h_ifname);
886 lldpd_hardware_cleanup(cfg, hardware);
887 continue;
888 }
889 hardware->h_ops = &bond_ops;
44002d66 890 hardware->h_ifindex = if_nametoindex(ifa->ifa_name);
849954d7
VB
891 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
892 } else {
cfe00f7f 893 if (hardware->h_flags) continue; /* Already seen this time */
849954d7
VB
894 memset(hardware->h_data, 0, IFNAMSIZ);
895 if_indextoname(master, hardware->h_data);
9898ac07 896 lldpd_port_cleanup(cfg, &hardware->h_lport, 0);
849954d7
VB
897 }
898
899 port = &hardware->h_lport;
900 hardware->h_flags = ifa->ifa_flags;
901 ifa->ifa_flags = 0;
902
903 /* Get local address */
904 iface_get_permanent_mac(cfg, hardware);
905
36bd79e3
VB
906 /* Fill information about port */
907 iface_port_name_desc(hardware);
849954d7
VB
908
909 /* Fill additional info */
7a008075 910#ifdef ENABLE_DOT3
849954d7 911 port->p_aggregid = master;
7a008075 912#endif
849954d7
VB
913 iface_macphy(hardware);
914 iface_mtu(cfg, hardware);
915 }
916}
917
5994b27d
VB
918#ifdef ENABLE_DOT1
919static void
920iface_append_vlan(struct lldpd *cfg,
921 struct lldpd_hardware *hardware, struct ifaddrs *ifa)
922{
923 struct lldpd_port *port = &hardware->h_lport;
924 struct lldpd_vlan *vlan;
925 struct vlan_ioctl_args ifv;
926
cfe00f7f
VB
927 /* Check if the VLAN is already here. */
928 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries)
929 if (strncmp(ifa->ifa_name, vlan->v_name, IFNAMSIZ) == 0)
930 return;
5994b27d
VB
931 if ((vlan = (struct lldpd_vlan *)
932 calloc(1, sizeof(struct lldpd_vlan))) == NULL)
933 return;
934 if ((vlan->v_name = strdup(ifa->ifa_name)) == NULL) {
935 free(vlan);
936 return;
937 }
938 memset(&ifv, 0, sizeof(ifv));
939 ifv.cmd = GET_VLAN_VID_CMD;
940 strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
941 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
942 /* Dunno what happened */
943 free(vlan->v_name);
944 free(vlan);
6a2fa591 945 return;
5994b27d 946 }
6a2fa591
VB
947 vlan->v_vid = ifv.u.VID;
948 TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
5994b27d
VB
949}
950
6e75df87
VB
951void
952lldpd_ifh_vlan(struct lldpd *cfg, struct ifaddrs *ifap)
953{
6e75df87 954 struct ifaddrs *ifa;
6e75df87
VB
955 struct vlan_ioctl_args ifv;
956 struct lldpd_hardware *hardware;
6a2fa591 957
6e75df87
VB
958 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
959 if (!ifa->ifa_flags)
960 continue;
961 if (!iface_is_vlan(cfg, ifa->ifa_name))
962 continue;
963
964 /* We need to find the physical interfaces of this
965 vlan, through bonds and bridges. */
966 memset(&ifv, 0, sizeof(ifv));
967 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
968 strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1));
969 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) {
970 /* Three cases:
971 1. we get a real device
972 2. we get a bond
973 3. we get a bridge
974 */
975 if ((hardware = lldpd_get_hardware(cfg,
44002d66
VB
976 ifv.u.device2,
977 if_nametoindex(ifv.u.device2),
978 NULL)) == NULL) {
6e75df87
VB
979 if (iface_is_bond(cfg, ifv.u.device2)) {
980 TAILQ_FOREACH(hardware, &cfg->g_hardware,
981 h_entries)
982 if (iface_is_bond_slave(cfg,
983 hardware->h_ifname,
984 ifv.u.device2, NULL))
5994b27d
VB
985 iface_append_vlan(cfg,
986 hardware, ifa);
6e75df87
VB
987 } else if (iface_is_bridge(cfg, ifv.u.device2)) {
988 TAILQ_FOREACH(hardware, &cfg->g_hardware,
989 h_entries)
990 if (iface_is_bridged_to(cfg,
991 hardware->h_ifname,
992 ifv.u.device2))
5994b27d
VB
993 iface_append_vlan(cfg,
994 hardware, ifa);
6e75df87 995 }
5994b27d
VB
996 } else iface_append_vlan(cfg,
997 hardware, ifa);
6e75df87
VB
998 }
999 }
6e75df87 1000}
5994b27d 1001#endif
6e75df87
VB
1002
1003/* Find a management address in all available interfaces, even those that were
1004 already handled. This is a special interface handler because it does not
1005 really handle interface related information (management address is attached
1006 to the local chassis). */
1007void
1008lldpd_ifh_mgmt(struct lldpd *cfg, struct ifaddrs *ifap)
1009{
1010 struct ifaddrs *ifa;
06db3608 1011 struct sockaddr_in sa;
6e75df87
VB
1012
1013 if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr != INADDR_ANY)
1014 return; /* We already have one */
1015
1016 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1017 if ((ifa->ifa_addr != NULL) &&
1018 (ifa->ifa_addr->sa_family == AF_INET)) {
1019 /* We have an IPv4 address (IPv6 not handled yet) */
06db3608
VB
1020 memcpy(&sa, ifa->ifa_addr, sizeof(struct sockaddr_in));
1021 if ((ntohl(*(u_int32_t*)&sa.sin_addr) != INADDR_LOOPBACK) &&
6e75df87
VB
1022 (cfg->g_mgmt_pattern == NULL)) {
1023 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
06db3608 1024 &sa.sin_addr,
6e75df87
VB
1025 sizeof(struct in_addr));
1026 LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name);
1027 } else if (cfg->g_mgmt_pattern != NULL) {
1028 char *ip;
06db3608 1029 ip = inet_ntoa(sa.sin_addr);
6e75df87
VB
1030 if (fnmatch(cfg->g_mgmt_pattern,
1031 ip, 0) == 0) {
1032 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
06db3608 1033 &sa.sin_addr,
6e75df87
VB
1034 sizeof(struct in_addr));
1035 LOCAL_CHASSIS(cfg)->c_mgmt_if =
1036 if_nametoindex(ifa->ifa_name);
1037 }
1038 }
1039 }
1040 }
1041}