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