]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Suppress bond handling.
[thirdparty/lldpd.git] / src / lldpd.c
CommitLineData
43c02e7b
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#include "lldpd.h"
18
19#include <stdio.h>
20#include <unistd.h>
21#include <errno.h>
22#include <signal.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#include <fnmatch.h>
26#include <time.h>
a2993d83 27#include <libgen.h>
43c02e7b
VB
28#include <sys/utsname.h>
29#include <sys/types.h>
30#include <sys/socket.h>
31#include <sys/select.h>
32#include <sys/time.h>
33#include <sys/ioctl.h>
34#include <arpa/inet.h>
43c02e7b
VB
35#include <ifaddrs.h>
36#include <net/if_arp.h>
37#include <linux/filter.h>
38#include <linux/if_vlan.h>
4de7bd54 39#include <linux/if_packet.h>
43c02e7b 40#include <linux/sockios.h>
43c02e7b
VB
41
42#ifdef USE_SNMP
43#include <net-snmp/net-snmp-config.h>
44#include <net-snmp/net-snmp-includes.h>
45#include <net-snmp/agent/net-snmp-agent-includes.h>
46#include <net-snmp/agent/snmp_vars.h>
47#endif /* USE_SNMP */
48
8888d191 49static void usage(void);
43c02e7b 50
8888d191 51static int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
8888d191
VB
52static void lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *);
53static int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
54static void lldpd_iface_multicast(struct lldpd *, const char *, int);
43c02e7b
VB
55
56/* "ether proto 0x88cc and ether dst 01:80:c2:00:00:0e" */
57#define LLDPD_FILTER_LLDP_F \
58 { 0x28, 0, 0, 0x0000000c }, \
59 { 0x15, 0, 5, 0x000088cc }, \
60 { 0x20, 0, 0, 0x00000002 }, \
61 { 0x15, 0, 3, 0xc200000e }, \
62 { 0x28, 0, 0, 0x00000000 }, \
63 { 0x15, 0, 1, 0x00000180 }, \
64 { 0x6, 0, 0, 0x0000ffff }, \
65 { 0x6, 0, 0, 0x00000000 },
8888d191 66static struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F };
4bad1937 67#ifdef ENABLE_FDP
031118c4
VB
68/* "ether dst 01:e0:52:cc:cc:cc" */
69#define LLDPD_FILTER_FDP_F \
70 { 0x20, 0, 0, 0x00000002 }, \
71 { 0x15, 0, 3, 0x52cccccc }, \
72 { 0x28, 0, 0, 0x00000000 }, \
73 { 0x15, 0, 1, 0x000001e0 }, \
74 { 0x6, 0, 0, 0x0000ffff }, \
75 { 0x6, 0, 0, 0x00000000 },
8888d191 76static struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F };
4bad1937
VB
77#endif /* ENABLE_FDP */
78#ifdef ENABLE_CDP
348f6df6 79/* "ether dst 01:00:0c:cc:cc:cc" */
43c02e7b 80#define LLDPD_FILTER_CDP_F \
348f6df6
VB
81 { 0x20, 0, 0, 0x00000002 }, \
82 { 0x15, 0, 3, 0x0ccccccc }, \
83 { 0x28, 0, 0, 0x00000000 }, \
84 { 0x15, 0, 1, 0x00000100 }, \
85 { 0x6, 0, 0, 0x0000ffff }, \
86 { 0x6, 0, 0, 0x00000000 },
8888d191 87static struct sock_filter lldpd_filter_cdp_f[] = { LLDPD_FILTER_CDP_F };
4bad1937
VB
88#endif /* ENABLE_CDP */
89#ifdef ENABLE_SONMP
43c02e7b
VB
90/* "ether dst 01:00:81:00:01:00" */
91#define LLDPD_FILTER_SONMP_F \
92 { 0x20, 0, 0, 0x00000002 }, \
93 { 0x15, 0, 3, 0x81000100 }, \
94 { 0x28, 0, 0, 0x00000000 }, \
95 { 0x15, 0, 1, 0x00000100 }, \
96 { 0x6, 0, 0, 0x0000ffff }, \
97 { 0x6, 0, 0, 0x00000000 },
8888d191 98static struct sock_filter lldpd_filter_sonmp_f[] = { LLDPD_FILTER_SONMP_F };
4bad1937
VB
99#endif /* ENABLE_SONMP */
100#ifdef ENABLE_EDP
43c02e7b
VB
101/* "ether dst 00:e0:2b:00:00:00" */
102#define LLDPD_FILTER_EDP_F \
103 { 0x20, 0, 0, 0x00000002 }, \
104 { 0x15, 0, 3, 0x2b000000 }, \
105 { 0x28, 0, 0, 0x00000000 }, \
106 { 0x15, 0, 1, 0x000000e0 }, \
107 { 0x6, 0, 0, 0x0000ffff }, \
108 { 0x6, 0, 0, 0x00000000 },
8888d191 109static struct sock_filter lldpd_filter_edp_f[] = { LLDPD_FILTER_EDP_F };
d2012fb3 110#endif /* ENABLE_EDP */
43c02e7b
VB
111#define LLDPD_FILTER_ANY_F \
112 { 0x28, 0, 0, 0x0000000c }, \
113 { 0x15, 0, 4, 0x000088cc }, \
114 { 0x20, 0, 0, 0x00000002 }, \
115 { 0x15, 0, 2, 0xc200000e }, \
116 { 0x28, 0, 0, 0x00000000 }, \
031118c4 117 { 0x15, 11, 12, 0x00000180 }, \
43c02e7b
VB
118 { 0x20, 0, 0, 0x00000002 }, \
119 { 0x15, 0, 2, 0x2b000000 }, \
120 { 0x28, 0, 0, 0x00000000 }, \
031118c4 121 { 0x15, 7, 8, 0x000000e0 }, \
43c02e7b 122 { 0x15, 1, 0, 0x0ccccccc }, \
031118c4
VB
123 { 0x15, 0, 2, 0x81000100 }, \
124 { 0x28, 0, 0, 0x00000000 }, \
125 { 0x15, 3, 4, 0x00000100 }, \
126 { 0x15, 0, 3, 0x52cccccc }, \
43c02e7b 127 { 0x28, 0, 0, 0x00000000 }, \
031118c4 128 { 0x15, 0, 1, 0x000001e0 }, \
43c02e7b
VB
129 { 0x6, 0, 0, 0x0000ffff }, \
130 { 0x6, 0, 0, 0x00000000 },
8888d191 131static struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F };
43c02e7b 132
8888d191 133static struct protocol protos[] =
43c02e7b
VB
134{
135 { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
136 LLDP_MULTICAST_ADDR, lldpd_filter_lldp_f, sizeof(lldpd_filter_lldp_f) },
4bad1937 137#ifdef ENABLE_CDP
43c02e7b
VB
138 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
139 CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
140 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
141 CDP_MULTICAST_ADDR, lldpd_filter_cdp_f, sizeof(lldpd_filter_cdp_f) },
4bad1937
VB
142#endif
143#ifdef ENABLE_SONMP
43c02e7b
VB
144 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
145 SONMP_MULTICAST_ADDR, lldpd_filter_sonmp_f, sizeof(lldpd_filter_sonmp_f) },
4bad1937
VB
146#endif
147#ifdef ENABLE_EDP
43c02e7b
VB
148 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
149 EDP_MULTICAST_ADDR, lldpd_filter_edp_f, sizeof(lldpd_filter_edp_f) },
4bad1937
VB
150#endif
151#ifdef ENABLE_FDP
031118c4
VB
152 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
153 FDP_MULTICAST_ADDR, lldpd_filter_fdp_f, sizeof(lldpd_filter_fdp_f) },
4bad1937 154#endif
43c02e7b
VB
155 { 0, 0, "any", ' ', NULL, NULL, NULL,
156 {0,0,0,0,0,0}, lldpd_filter_any_f, sizeof(lldpd_filter_any_f) }
157};
158
8888d191 159static int lldpd_iface_switchto(struct lldpd *, short int,
43c02e7b 160 struct lldpd_hardware *);
8888d191 161static
43c02e7b 162struct lldpd_hardware *lldpd_port_add(struct lldpd *, struct ifaddrs *);
8888d191
VB
163static void lldpd_loop(struct lldpd *);
164static void lldpd_shutdown(int);
165static void lldpd_exit();
166static void lldpd_send_all(struct lldpd *);
167static void lldpd_recv_all(struct lldpd *);
168static int lldpd_guess_type(struct lldpd *, char *, int);
169static void lldpd_decode(struct lldpd *, char *, int,
0bc32943 170 struct lldpd_hardware *);
89840df0 171#ifdef ENABLE_LLDPMED
8888d191 172static void lldpd_med(struct lldpd_chassis *);
89840df0 173#endif
43c02e7b 174
8888d191 175static char **saved_argv;
43c02e7b 176
8888d191 177static void
43c02e7b
VB
178usage(void)
179{
180 extern const char *__progname;
e809a587
VB
181 fprintf(stderr, "usage: %s [options]\n", __progname);
182 fprintf(stderr, "see manual page lldpd(8) for more information\n");
43c02e7b
VB
183 exit(1);
184}
185
8888d191 186static void
50a89ca7 187lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware)
43c02e7b 188{
43c02e7b 189 struct ifreq ifr;
43c02e7b
VB
190
191 /* get MTU */
192 memset(&ifr, 0, sizeof(ifr));
193 strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name));
194 if (ioctl(global->g_sock, SIOCGIFMTU, (char*)&ifr) == -1) {
195 LLOG_WARN("unable to get MTU of %s, using 1500", hardware->h_ifname);
196 hardware->h_mtu = 1500;
197 } else
548109b2 198 hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu;
50a89ca7
VB
199}
200
8888d191 201static int
50a89ca7
VB
202lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
203{
50a89ca7
VB
204 int status;
205 short int filter;
206
207 lldpd_iface_init_mtu(global, hardware);
a7502371 208 status = priv_iface_init(hardware, -1);
50a89ca7
VB
209 if (status != 0)
210 return status;
211
43c02e7b
VB
212 if (global->g_multi)
213 filter = LLDPD_MODE_ANY;
214 else
215 filter = LLDPD_MODE_LLDP;
216 if (lldpd_iface_switchto(global, filter, hardware) == -1) {
217 LLOG_WARNX("unable to apply filter");
218 return ENETDOWN;
219 }
220
43c02e7b
VB
221 lldpd_iface_multicast(global, hardware->h_ifname, 0);
222
223 LLOG_DEBUG("interface %s initialized (fd=%d)", hardware->h_ifname,
224 hardware->h_raw);
225 return 0;
226}
227
8888d191 228static void
43c02e7b
VB
229lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
230{
a7502371 231 int i, rc;
509861fe 232
43c02e7b
VB
233 for (i=0; global->g_protocols[i].mode != 0; i++) {
234 if (!global->g_protocols[i].enabled) continue;
a7502371
VB
235 if ((rc = priv_iface_multicast(name,
236 global->g_protocols[i].mac, !remove)) != 0) {
237 errno = rc;
238 if (errno != ENOENT)
239 LLOG_INFO("unable to %s %s address to multicast filter for %s",
240 (remove)?"delete":"add",
241 global->g_protocols[i].name,
242 name);
43c02e7b
VB
243 }
244 }
245}
246
8888d191 247static int
43c02e7b
VB
248lldpd_iface_close(struct lldpd *global, struct lldpd_hardware *hardware)
249{
250 char listen[IFNAMSIZ];
251
252 close(hardware->h_raw);
253 hardware->h_raw = -1;
254
b7c1b7c2 255 memcpy(listen, hardware->h_ifname, IFNAMSIZ);
43c02e7b
VB
256 lldpd_iface_multicast(global, listen, 1);
257
43c02e7b
VB
258 return 0;
259}
260
8888d191 261static int
43c02e7b
VB
262lldpd_iface_switchto(struct lldpd *cfg, short int filter, struct lldpd_hardware *hardware)
263{
264 struct sock_fprog prog;
265 int i;
266
267 memset(&prog, 0, sizeof(prog));
268 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
269 if (!cfg->g_protocols[i].enabled) continue;
270 if (cfg->g_protocols[i].mode == filter)
271 break;
272 }
273 prog.filter = cfg->g_protocols[i].filter;
274 prog.len = cfg->g_protocols[i].filterlen / sizeof(struct sock_filter);
275 if (setsockopt(hardware->h_raw, SOL_SOCKET, SO_ATTACH_FILTER,
276 &prog, sizeof(prog)) < 0) {
277 LLOG_WARN("unable to change filter for %s", hardware->h_ifname);
278 return -1;
279 }
43c02e7b
VB
280 return 0;
281}
282
a1347cd8 283#ifdef ENABLE_DOT1
43c02e7b
VB
284void
285lldpd_vlan_cleanup(struct lldpd_port *port)
286{
287 struct lldpd_vlan *vlan, *vlan_next;
288 for (vlan = TAILQ_FIRST(&port->p_vlans);
289 vlan != NULL;
290 vlan = vlan_next) {
291 free(vlan->v_name);
292 vlan_next = TAILQ_NEXT(vlan, v_entries);
293 TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
294 free(vlan);
295 }
296}
a1347cd8 297#endif
43c02e7b 298
a0edeaf8
VB
299/* If `all' is true, clear all information, including information that
300 are not refreshed periodically. If `all' is true, also free the
301 port. */
43c02e7b 302void
a0edeaf8 303lldpd_port_cleanup(struct lldpd_port *port, int all)
43c02e7b 304{
740593ff
VB
305#ifdef ENABLE_LLDPMED
306 int i;
a0edeaf8
VB
307 if (all)
308 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
309 free(port->p_med_location[i].data);
740593ff 310#endif
a1347cd8 311#ifdef ENABLE_DOT1
43c02e7b 312 lldpd_vlan_cleanup(port);
a1347cd8 313#endif
43c02e7b
VB
314 free(port->p_id);
315 free(port->p_descr);
a0edeaf8
VB
316 if (all)
317 free(port);
43c02e7b
VB
318}
319
320void
321lldpd_chassis_cleanup(struct lldpd_chassis *chassis)
322{
89840df0
VB
323#ifdef ENABLE_LLDPMED
324 free(chassis->c_med_hw);
517d524b 325 free(chassis->c_med_sw);
89840df0
VB
326 free(chassis->c_med_fw);
327 free(chassis->c_med_sn);
328 free(chassis->c_med_manuf);
329 free(chassis->c_med_model);
330 free(chassis->c_med_asset);
331#endif
43c02e7b
VB
332 free(chassis->c_id);
333 free(chassis->c_name);
334 free(chassis->c_descr);
335 free(chassis);
336}
337
338void
339lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int reset)
340{
341 if (hardware->h_rport != NULL) {
a0edeaf8 342 lldpd_port_cleanup(hardware->h_rport, 1);
43c02e7b
VB
343 hardware->h_rport = NULL;
344 }
345 if (hardware->h_rchassis != NULL) {
346 lldpd_chassis_cleanup(hardware->h_rchassis);
347 hardware->h_rchassis = NULL;
348 }
349 hardware->h_rlastchange = hardware->h_rlastupdate = 0;
350 free(hardware->h_rlastframe);
351 hardware->h_rlastframe = NULL;
352 if (reset && cfg->g_multi) {
353 hardware->h_mode = LLDPD_MODE_ANY;
354 memset(hardware->h_proto_macs, 0, ETH_ALEN*(cfg->g_multi+1));
355 hardware->h_start_probe = 0;
356 lldpd_iface_switchto(cfg, LLDPD_MODE_ANY, hardware);
357 }
358}
359
d9be8ea0
VB
360void
361lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
362{
a0edeaf8 363 lldpd_port_cleanup(&hardware->h_lport, 1);
d9be8ea0
VB
364 free(hardware->h_proto_macs);
365 free(hardware->h_llastframe);
366 free(hardware);
367}
368
43c02e7b
VB
369void
370lldpd_cleanup(struct lldpd *cfg)
371{
372 struct lldpd_hardware *hardware, *hardware_next;
373
374 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
375 hardware = hardware_next) {
376 hardware_next = TAILQ_NEXT(hardware, h_entries);
377 if (hardware->h_flags == 0) {
378 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
379 lldpd_iface_close(cfg, hardware);
55d63368 380 lldpd_remote_cleanup(cfg, hardware, 0);
d9be8ea0 381 lldpd_hardware_cleanup(hardware);
43c02e7b
VB
382 } else if (hardware->h_rchassis != NULL) {
383 if (time(NULL) - hardware->h_rlastupdate >
384 hardware->h_rchassis->c_ttl) {
385 lldpd_remote_cleanup(cfg, hardware, 1);
386 hardware->h_rx_ageout_cnt++;
387 }
388 }
389 }
390}
391
8888d191 392static struct lldpd_hardware *
43c02e7b
VB
393lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
394{
a1347cd8 395#if defined (ENABLE_DOT1) || defined (ENABLE_DOT3)
43c02e7b 396 struct ifaddrs *oifap, *oifa;
a1347cd8 397#endif
43c02e7b
VB
398 struct lldpd_hardware *hardware;
399 struct lldpd_port *port;
a1347cd8 400#ifdef ENABLE_DOT1
43c02e7b 401 struct lldpd_vlan *vlan;
43c02e7b 402 struct vlan_ioctl_args ifv;
a1347cd8
VB
403#endif
404#ifdef ENABLE_DOT3
43c02e7b 405 struct ethtool_cmd ethc;
a1347cd8 406#endif
43c02e7b
VB
407 u_int8_t *lladdr;
408
409 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
410 if (strcmp(hardware->h_ifname, ifa->ifa_name) == 0)
411 break;
412 }
413
414 if (hardware == NULL) {
415 if ((hardware = (struct lldpd_hardware *)
416 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
417 return (NULL);
418 hardware->h_raw = -1;
43c02e7b
VB
419 hardware->h_start_probe = 0;
420 hardware->h_proto_macs = (u_int8_t*)calloc(cfg->g_multi+1, ETH_ALEN);
740593ff 421#ifdef ENABLE_LLDPMED
42bddd41
VB
422 if (cfg->g_lchassis.c_med_cap_available) {
423 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
424 if (!cfg->g_noinventory)
425 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
426 }
740593ff 427#endif
a1347cd8 428#ifdef ENABLE_DOT1
43c02e7b
VB
429 TAILQ_INIT(&hardware->h_lport.p_vlans);
430 } else {
a0edeaf8 431 lldpd_port_cleanup(&hardware->h_lport, 0);
a1347cd8 432#endif
43c02e7b
VB
433 }
434
435 port = &hardware->h_lport;
436 hardware->h_flags = ifa->ifa_flags;
437
438 strlcpy(hardware->h_ifname, ifa->ifa_name, sizeof(hardware->h_ifname));
439 lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr);
440 memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr));
1d291522 441 iface_get_permanent_mac(cfg, hardware);
43c02e7b 442 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
4395f9c1
VB
443 if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
444 fatal(NULL);
445 memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
43c02e7b 446 port->p_id_len = sizeof(hardware->h_lladdr);
4f758bc0 447 port->p_descr = strdup(hardware->h_ifname);
43c02e7b
VB
448
449 if (cfg->g_lchassis.c_id == NULL) {
450 /* Use the first port's l2 addr as the chassis ID */
451 if ((cfg->g_lchassis.c_id = malloc(sizeof(hardware->h_lladdr))) == NULL)
452 fatal(NULL);
453 cfg->g_lchassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
454 cfg->g_lchassis.c_id_len = sizeof(hardware->h_lladdr);
455 memcpy(cfg->g_lchassis.c_id,
456 hardware->h_lladdr, sizeof(hardware->h_lladdr));
457 }
458
459 /* Get VLANS and aggregation status */
a1347cd8 460#if defined (ENABLE_DOT3) || defined (ENABLE_DOT1)
43c02e7b
VB
461 if (getifaddrs(&oifap) != 0)
462 fatal("lldpd_port_add: failed to get interface list");
463 for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
a1347cd8 464#ifdef ENABLE_DOT1
43c02e7b
VB
465 /* Check if we already have checked this one */
466 int skip = 0;
467 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
bac91719 468 if (strcmp(vlan->v_name, oifa->ifa_name) == 0) {
43c02e7b 469 skip = 1;
bac91719
VB
470 break;
471 }
43c02e7b
VB
472 }
473 if (skip) continue;
a1347cd8 474#endif
43c02e7b
VB
475
476 /* Aggregation check */
a1347cd8 477#ifdef ENABLE_DOT3
beeaefa3 478 if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL))
43c02e7b 479 port->p_aggregid = if_nametoindex(oifa->ifa_name);
a1347cd8
VB
480#endif
481
482#ifdef ENABLE_DOT1
43c02e7b
VB
483 /* VLAN check */
484 memset(&ifv, 0, sizeof(ifv));
485 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
486 strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
487 if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) &&
beeaefa3 488 ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) ||
bac91719 489 (iface_is_bridged_to(cfg, hardware->h_ifname, ifv.u.device2)) ||
43c02e7b
VB
490 (strncmp(hardware->h_ifname, ifv.u.device2, sizeof(ifv.u.device2)) == 0))) {
491 if ((vlan = (struct lldpd_vlan *)
492 calloc(1, sizeof(struct lldpd_vlan))) == NULL)
493 continue;
249644a4 494 if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) {
43c02e7b
VB
495 free(vlan);
496 continue;
497 }
498 memset(&ifv, 0, sizeof(ifv));
499 ifv.cmd = GET_VLAN_VID_CMD;
500 strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
501 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
502 /* Dunno what happened */
503 free(vlan->v_name);
504 free(vlan);
505 } else {
506 vlan->v_vid = ifv.u.VID;
507 TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
508 }
509 }
a1347cd8 510#endif
43c02e7b
VB
511 }
512 freeifaddrs(oifap);
a1347cd8 513#endif
43c02e7b 514
a1347cd8 515#ifdef ENABLE_DOT3
43c02e7b 516 /* MAC/PHY */
4afe659e 517 if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
43c02e7b
VB
518 int j;
519 int advertised_ethtool_to_rfc3636[][2] = {
520 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
521 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
522 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
523 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
524 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
525 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
526 {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
527 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
528 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
529 {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
530 {0,0}};
531
532 port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
533 port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
534 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
535 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
536 port->p_autoneg_advertised |= advertised_ethtool_to_rfc3636[j][1];
537 }
538 switch (ethc.speed) {
539 case SPEED_10:
540 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
541 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
542 if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2;
543 if (ethc.port == PORT_FIBRE)
544 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
545 LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD;
546 break;
547 case SPEED_100:
548 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
549 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
550 if (ethc.port == PORT_BNC)
551 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
552 LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD;
553 if (ethc.port == PORT_FIBRE)
554 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
555 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
556 break;
557 case SPEED_1000:
558 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
559 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
560 if (ethc.port == PORT_FIBRE)
561 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
562 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
563 break;
564 case SPEED_10000:
565 port->p_mau_type = (ethc.port == PORT_FIBRE) ? \
566 LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
567 break;
568 }
569 if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
b5562b23 570 } else
28aca6a0 571 LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname);
a1347cd8 572#endif
43c02e7b
VB
573
574 if (!INTERFACE_OPENED(hardware)) {
575
576 if (lldpd_iface_init(cfg, hardware) != 0) {
b5562b23 577 LLOG_WARN("unable to initialize %s", hardware->h_ifname);
d9be8ea0 578 lldpd_hardware_cleanup(hardware);
43c02e7b
VB
579 return (NULL);
580 }
581
582 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
583 }
584
585 return (hardware);
586}
587
8888d191 588static int
43c02e7b
VB
589lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
590{
591 int i;
592 if (s < ETH_ALEN)
593 return -1;
594 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
595 if (!cfg->g_protocols[i].enabled)
596 continue;
597 if (cfg->g_protocols[i].guess == NULL) {
598 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
599 return cfg->g_protocols[i].mode;
600 } else {
601 if (cfg->g_protocols[i].guess(frame, s))
602 return cfg->g_protocols[i].mode;
603 }
604 }
605 return -1;
606}
607
8888d191 608static void
43c02e7b 609lldpd_decode(struct lldpd *cfg, char *frame, int s,
0bc32943 610 struct lldpd_hardware *hardware)
43c02e7b 611{
0bc32943 612 int result = 0, i, j;
43c02e7b
VB
613 u_int8_t nullmac[ETH_ALEN] = {0,0,0,0,0,0};
614 u_int8_t broadcastmac[ETH_ALEN] = {0xff,0xff,0xff,0xff,0xff,0xff};
615 struct lldpd_chassis *chassis;
616 struct lldpd_port *port;
0bc32943 617 struct lldpd_hardware *ohardware;
43c02e7b
VB
618 int guess = LLDPD_MODE_LLDP;
619
50a89ca7 620 /* Discard VLAN frames */
a8105c1b
VB
621 if ((s >= sizeof(struct ethhdr)) &&
622 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
50a89ca7
VB
623 return;
624
43c02e7b
VB
625 if ((hardware->h_rlastframe != NULL) &&
626 (hardware->h_rlastframe->size == s) &&
627 (memcmp(hardware->h_rlastframe->frame, frame, s) == 0)) {
628 /* Already received the same frame */
629 hardware->h_rlastupdate = time(NULL);
630 return;
631 }
632
633 if (cfg->g_multi) {
634 if (hardware->h_mode == LLDPD_MODE_ANY)
635 guess = lldpd_guess_type(cfg, frame, s);
636 else
637 guess = hardware->h_mode;
638 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
639 if (!cfg->g_protocols[i].enabled)
640 continue;
641 if (cfg->g_protocols[i].mode == guess) {
642 if ((result = cfg->g_protocols[i].decode(cfg, frame,
643 s, hardware, &chassis, &port)) == -1)
644 return;
645 break;
646 }
647 }
648 if (cfg->g_protocols[i].mode == 0) {
649 LLOG_INFO("unable to guess frame type");
650 return;
651 }
652 } else if (cfg->g_protocols[0].decode(cfg, frame, s, hardware,
653 &chassis, &port) == -1)
654 /* Nothing has been received */
655 return;
656
43c02e7b
VB
657 if (cfg->g_multi &&
658 (hardware->h_mode == LLDPD_MODE_ANY)) {
659 u_int8_t *mac;
660 char *modename;
661 int filter;
662
663 for (i=j=0; cfg->g_protocols[i].mode != 0; i++) {
664 if (!cfg->g_protocols[i].enabled)
665 continue;
666 if (cfg->g_protocols[i].mode == guess) {
667 mac = hardware->h_proto_macs + ETH_ALEN*j;
668 modename = cfg->g_protocols[i].name;
669 filter = cfg->g_protocols[i].mode;
670 break;
671 }
672 j++;
673 }
674 if (cfg->g_protocols[i].mode == 0) {
675 LLOG_WARNX("should not be there");
676 goto cleanup;
677 }
678
679 if (hardware->h_start_probe == 0)
680 hardware->h_start_probe = time(NULL) - 1;
681 /* Handle switching respecting probe time */
682 if ((memcmp(mac, frame + ETH_ALEN, ETH_ALEN) == 0) &&
683 ((time(NULL) - hardware->h_start_probe) > cfg->g_probe_time) &&
684 /* Don't switch to this protocol if not LLDP and LLDP is
685 * a valid candidate */
686 ((filter == LLDPD_MODE_LLDP) ||
687 (memcmp(hardware->h_proto_macs,
688 broadcastmac, ETH_ALEN) == 0) ||
689 (memcmp(hardware->h_proto_macs,
690 nullmac, ETH_ALEN) == 0))) {
691 LLOG_INFO("switching to %s on port %s", modename,
692 hardware->h_ifname);
693 hardware->h_mode = guess;
694 lldpd_iface_switchto(cfg, filter, hardware);
695 } else {
696 /* Wait twice probe time to be able to receive packets of all kind */
697 if ((time(NULL) - hardware->h_start_probe) > cfg->g_probe_time * 2) {
698 LLOG_DEBUG("probe expired on %s, retry", hardware->h_ifname);
699 hardware->h_start_probe = 0;
700 memset(hardware->h_proto_macs, 0, ETH_ALEN*(cfg->g_multi+1));
701 goto cleanup;
702 }
703 if (memcmp(mac, broadcastmac, ETH_ALEN) == 0)
704 goto cleanup;
705 LLOG_INFO("received a %s frame on %s but wait for %d sec",
706 modename, hardware->h_ifname, cfg->g_probe_time - time(NULL) +
707 hardware->h_start_probe);
708 if (memcmp(mac, frame + ETH_ALEN, ETH_ALEN) == 0)
709 goto cleanup;
710 if (memcmp(mac, nullmac, ETH_ALEN) == 0) {
711 memcpy(mac, frame + ETH_ALEN, ETH_ALEN);
712 goto cleanup;
713 }
714 LLOG_INFO("several MAC for %s on %s, discarding %s for this interface",
715 modename, hardware->h_ifname, modename);
716 memcpy(mac, broadcastmac, ETH_ALEN);
717 goto cleanup;
718 }
719 }
720
721 result = 0;
722 if ((hardware->h_rchassis == NULL) ||
723 (chassis->c_id_subtype != hardware->h_rchassis->c_id_subtype) ||
724 (chassis->c_id_len != hardware->h_rchassis->c_id_len) ||
725 (memcmp(chassis->c_id, hardware->h_rchassis->c_id,
726 chassis->c_id_len) != 0))
727 result = 1;
728
729 /* We have our new frame */
730 lldpd_remote_cleanup(cfg, hardware, 0);
731 hardware->h_rport = port;
732 hardware->h_rchassis = chassis;
733 hardware->h_rlastchange = hardware->h_rlastupdate = time(NULL);
734
735 /* We remember this frame */
736 free(hardware->h_rlastframe);
737 if ((hardware->h_rlastframe = (struct lldpd_frame *)malloc(s +
738 sizeof(int))) != NULL) {
739 hardware->h_rlastframe->size = s;
740 memcpy(hardware->h_rlastframe->frame, frame, s);
741 }
742
743 if (result) {
744 /* This is a new remote system */
745 LLOG_DEBUG("we discovered a new remote system on %s",
746 hardware->h_ifname);
747 /* Do we already know this remote system? */
748 TAILQ_FOREACH(ohardware, &cfg->g_hardware, h_entries) {
749 if ((ohardware->h_ifname != hardware->h_ifname) &&
750 (ohardware->h_rchassis != NULL) &&
751 (ohardware->h_rchassis->c_id_subtype ==
752 chassis->c_id_subtype) &&
753 (ohardware->h_rchassis->c_id_len ==
754 chassis->c_id_len) &&
755 (memcmp(ohardware->h_rchassis->c_id,
756 chassis->c_id, chassis->c_id_len) == 0)) {
757 LLOG_DEBUG("but it was already on %s",
758 ohardware->h_ifname);
759 hardware->h_rid = ohardware->h_rid;
760 return;
761 }
762 }
763 hardware->h_rid = ++cfg->g_lastrid;
764 }
765 return;
766
767cleanup:
768 lldpd_chassis_cleanup(chassis);
a0edeaf8 769 lldpd_port_cleanup(port, 1);
43c02e7b
VB
770 return;
771}
772
8888d191 773static void
43c02e7b
VB
774lldpd_recv_all(struct lldpd *cfg)
775{
776 struct lldpd_hardware *hardware;
777 struct lldpd_client *client, *client_next;
778 fd_set rfds;
779 struct timeval tv;
780 struct sockaddr_ll from;
781 socklen_t fromlen;
43c02e7b
VB
782#ifdef USE_SNMP
783 int fakeblock = 0;
784 struct timeval *tvp = &tv;
785#endif
0bc32943 786 int rc, nfds, n;
43c02e7b
VB
787 char *buffer;
788
789 do {
790 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
791 if (tv.tv_sec < 0)
792 tv.tv_sec = LLDPD_TX_DELAY;
793 if (tv.tv_sec >= cfg->g_delay)
794 tv.tv_sec = cfg->g_delay;
795 tv.tv_usec = 0;
796
797 FD_ZERO(&rfds);
798 nfds = -1;
799
800 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
801 /* Ignore if interface is down */
e70b76f9
VB
802 if (((hardware->h_flags & IFF_UP) == 0) ||
803 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
804 continue;
805 FD_SET(hardware->h_raw, &rfds);
806 if (nfds < hardware->h_raw)
807 nfds = hardware->h_raw;
43c02e7b
VB
808 }
809 TAILQ_FOREACH(client, &cfg->g_clients, next) {
810 FD_SET(client->fd, &rfds);
811 if (nfds < client->fd)
812 nfds = client->fd;
813 }
814 FD_SET(cfg->g_ctl, &rfds);
815 if (nfds < cfg->g_ctl)
816 nfds = cfg->g_ctl;
817
818#ifdef USE_SNMP
819 if (cfg->g_snmp)
820 snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
821#endif /* USE_SNMP */
822 if (nfds == -1) {
823 sleep(cfg->g_delay);
824 return;
825 }
826
827 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
828 if (rc == -1) {
829 if (errno == EINTR)
830 continue;
831 LLOG_WARN("failure on select");
832 break;
833 }
834#ifdef USE_SNMP
835 if (cfg->g_snmp) {
836 if (rc > 0)
837 snmp_read(&rfds);
838 else if (rc == 0)
839 snmp_timeout();
840 }
841#endif /* USE_SNMP */
842 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
843 /* We could have received something on _real_
844 * interface. However, even in this case, this could be
845 * just an outgoing packet. We will try to handle both
846 * cases, but maybe not in the same select. */
0bc32943 847 if (FD_ISSET(hardware->h_raw, &rfds)) {
43c02e7b
VB
848 if ((buffer = (char *)malloc(
849 hardware->h_mtu)) == NULL) {
850 LLOG_WARN("failed to alloc reception buffer");
851 continue;
852 }
853 fromlen = sizeof(from);
854 if ((n = recvfrom(
0bc32943 855 hardware->h_raw,
43c02e7b
VB
856 buffer,
857 hardware->h_mtu, 0,
858 (struct sockaddr *)&from,
859 &fromlen)) == -1) {
860 LLOG_WARN("error while receiving frame on %s",
861 hardware->h_ifname);
862 hardware->h_rx_discarded_cnt++;
863 free(buffer);
864 continue;
865 }
866 if (from.sll_pkttype == PACKET_OUTGOING) {
867 free(buffer);
868 continue;
869 }
43c02e7b 870 hardware->h_rx_cnt++;
0bc32943 871 lldpd_decode(cfg, buffer, n, hardware);
43c02e7b
VB
872 free(buffer);
873 }
874
875 }
876 if (FD_ISSET(cfg->g_ctl, &rfds)) {
877 if (ctl_accept(cfg, cfg->g_ctl) == -1)
878 LLOG_WARN("unable to accept new client");
879 }
880 for (client = TAILQ_FIRST(&cfg->g_clients);
881 client != NULL;
882 client = client_next) {
883 client_next = TAILQ_NEXT(client, next);
884 if (FD_ISSET(client->fd, &rfds)) {
885 /* Got a message */
886 if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
887 NULL) {
888 LLOG_WARN("failed to alloc reception buffer");
889 continue;
890 }
891 if ((n = recv(client->fd, buffer,
892 MAX_HMSGSIZE, 0)) == -1) {
893 LLOG_WARN("error while receiving message");
894 free(buffer);
895 continue;
896 }
897 if (n > 0)
a552a72e 898 client_handle_client(cfg, client, buffer, n);
43c02e7b
VB
899 else
900 ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
901 free(buffer);
902 }
903 }
904
905#ifdef USE_SNMP
906 if (cfg->g_snmp) {
907 run_alarms();
908 netsnmp_check_outstanding_agent_requests();
909 }
910#endif /* USE_SNMP */
911 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
912}
913
8888d191 914static void
43c02e7b
VB
915lldpd_send_all(struct lldpd *cfg)
916{
917 struct lldpd_hardware *hardware;
0bc32943 918 int i;
f7db0dd8 919
43c02e7b
VB
920 cfg->g_lastsent = time(NULL);
921 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
922 /* Ignore if interface is down */
e70b76f9
VB
923 if (((hardware->h_flags & IFF_UP) == 0) ||
924 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
925 continue;
926
927 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
928 if (!cfg->g_protocols[i].enabled)
929 continue;
930 if ((hardware->h_mode == cfg->g_protocols[i].mode) ||
931 (cfg->g_protocols[i].mode == LLDPD_MODE_LLDP))
932 cfg->g_protocols[i].send(cfg, &cfg->g_lchassis, hardware);
933 }
934 }
935}
936
89840df0 937#ifdef ENABLE_LLDPMED
8888d191 938static void
89840df0
VB
939lldpd_med(struct lldpd_chassis *chassis)
940{
941 free(chassis->c_med_hw);
942 free(chassis->c_med_fw);
943 free(chassis->c_med_sn);
944 free(chassis->c_med_manuf);
945 free(chassis->c_med_model);
946 free(chassis->c_med_asset);
947 chassis->c_med_hw = dmi_hw();
948 chassis->c_med_fw = dmi_fw();
949 chassis->c_med_sn = dmi_sn();
950 chassis->c_med_manuf = dmi_manuf();
951 chassis->c_med_model = dmi_model();
952 chassis->c_med_asset = dmi_asset();
953}
954#endif
955
8888d191 956static void
43c02e7b
VB
957lldpd_loop(struct lldpd *cfg)
958{
959 struct ifaddrs *ifap, *ifa;
960 struct sockaddr_ll *sdl;
961 struct lldpd_hardware *hardware;
962 int f;
963 char status;
964 struct utsname *un;
b5562b23 965 char *hp;
43c02e7b
VB
966
967 /* Set system name and description */
968 if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL)
969 fatal(NULL);
970 if (uname(un) != 0)
971 fatal("failed to get system information");
b5562b23 972 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b
VB
973 fatal("failed to get system name");
974 free(cfg->g_lchassis.c_name);
975 free(cfg->g_lchassis.c_descr);
249644a4
VB
976 if ((cfg->g_lchassis.c_name = strdup(hp)) == NULL)
977 fatal(NULL);
43c02e7b
VB
978 if (asprintf(&cfg->g_lchassis.c_descr, "%s %s %s %s",
979 un->sysname, un->release, un->version, un->machine) == -1)
980 fatal("failed to set system description");
43c02e7b
VB
981
982 /* Check forwarding */
983 cfg->g_lchassis.c_cap_enabled = 0;
b5562b23 984 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 985 if ((read(f, &status, 1) == 1) && (status == '1')) {
43c02e7b 986 cfg->g_lchassis.c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 987 }
43c02e7b
VB
988 close(f);
989 }
89840df0
VB
990#ifdef ENABLE_LLDPMED
991 if (cfg->g_lchassis.c_cap_available & LLDP_CAP_TELEPHONE)
992 cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_TELEPHONE;
993 lldpd_med(&cfg->g_lchassis);
994 free(cfg->g_lchassis.c_med_sw);
995 cfg->g_lchassis.c_med_sw = strdup(un->release);
996#endif
997 free(un);
43c02e7b
VB
998
999 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
1000 hardware->h_flags = 0;
1001
1002 if (getifaddrs(&ifap) != 0)
1003 fatal("lldpd_loop: failed to get interface list");
1004
1005 cfg->g_lchassis.c_mgmt.s_addr = INADDR_ANY;
1006 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
1007 if (cfg->g_lchassis.c_mgmt.s_addr == INADDR_ANY)
1008 /* Get management address, if available */
1009 if ((ifa->ifa_addr != NULL) &&
1010 (ifa->ifa_addr->sa_family == AF_INET)) {
1011 struct sockaddr_in *sa;
1012 sa = (struct sockaddr_in *)ifa->ifa_addr;
1013 if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) &&
1014 (cfg->g_mgmt_pattern == NULL)) {
1015 memcpy(&cfg->g_lchassis.c_mgmt,
1016 &sa->sin_addr,
1017 sizeof(struct in_addr));
1018 cfg->g_lchassis.c_mgmt_if = if_nametoindex(ifa->ifa_name);
1019 }
1020 else if (cfg->g_mgmt_pattern != NULL) {
1021 char *ip;
1022 ip = inet_ntoa(sa->sin_addr);
1023 if (fnmatch(cfg->g_mgmt_pattern,
1024 ip, 0) == 0) {
1025 memcpy(&cfg->g_lchassis.c_mgmt,
1026 &sa->sin_addr,
1027 sizeof(struct in_addr));
1028 cfg->g_lchassis.c_mgmt_if =
1029 if_nametoindex(ifa->ifa_name);
1030 }
1031 }
1032 }
1033
a5e5458f
VB
1034 if (ifa->ifa_addr == NULL ||
1035 ifa->ifa_addr->sa_family != PF_PACKET)
1036 continue;
1037
99f85c00
VB
1038 sdl = (struct sockaddr_ll *)ifa->ifa_addr;
1039 if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
1040 continue;
1041
43c02e7b
VB
1042 if (iface_is_bridge(cfg, ifa->ifa_name)) {
1043 cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_BRIDGE;
1044 continue;
1045 }
1046
50a89ca7 1047 if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
43c02e7b
VB
1048 (iface_is_bond(cfg, ifa->ifa_name)))
1049 continue;
1050
b29c23cc 1051 if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
43c02e7b
VB
1052 continue;
1053
43c02e7b
VB
1054 if (iface_is_wireless(cfg, ifa->ifa_name))
1055 cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_WLAN;
1056
43c02e7b
VB
1057 if (lldpd_port_add(cfg, ifa) == NULL)
1058 LLOG_WARNX("failed to allocate port %s, skip it",
1059 ifa->ifa_name);
1060 }
1061
1062 freeifaddrs(ifap);
1063
1064 lldpd_cleanup(cfg);
1065
1066 lldpd_send_all(cfg);
1067 lldpd_recv_all(cfg);
1068}
1069
8888d191 1070static void
43c02e7b
VB
1071lldpd_shutdown(int sig)
1072{
1073 LLOG_INFO("signal received, exiting");
1074 exit(0);
1075}
1076
1077/* For signal handling */
8888d191 1078static struct lldpd *gcfg = NULL;
43c02e7b 1079
8888d191 1080static void
43c02e7b
VB
1081lldpd_exit()
1082{
1083 struct lldpd_hardware *hardware;
b5562b23
VB
1084 close(gcfg->g_ctl);
1085 priv_ctl_cleanup();
43c02e7b
VB
1086 TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
1087 if (INTERFACE_OPENED(hardware))
1088 lldpd_iface_close(gcfg, hardware);
1089 }
1090#ifdef USE_SNMP
1091 if (gcfg->g_snmp)
1092 agent_shutdown();
1093#endif /* USE_SNMP */
1094}
1095
1096int
1097main(int argc, char *argv[])
1098{
1099 struct lldpd *cfg;
e809a587
VB
1100 int ch, debug = 0;
1101#ifdef USE_SNMP
1102 int snmp = 0;
1103#endif
43c02e7b 1104 char *mgmtp = NULL;
71936c67
VB
1105 char *popt, opts[] = "dxm:p:M:i@ ";
1106 int probe = 0, i, found;
89840df0 1107#ifdef ENABLE_LLDPMED
e809a587 1108 int lldpmed = 0, noinventory = 0;
89840df0 1109#endif
43c02e7b
VB
1110
1111 saved_argv = argv;
1112
1113 /*
1114 * Get and parse command line options
1115 */
1116 popt = index(opts, '@');
1117 for (i=0; protos[i].mode != 0; i++) {
1118 if (protos[i].enabled == 1) continue;
1119 *(popt++) = protos[i].arg;
1120 }
1121 *popt = '\0';
1122 while ((ch = getopt(argc, argv, opts)) != -1) {
1123 switch (ch) {
1124 case 'd':
1125 debug++;
1126 break;
1127 case 'm':
1128 mgmtp = optarg;
1129 break;
e809a587 1130#ifdef ENABLE_LLDPMED
115ff55c 1131 case 'M':
89840df0 1132 lldpmed = atoi(optarg);
e809a587
VB
1133 if ((lldpmed < 1) || (lldpmed > 4)) {
1134 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1135 usage();
e809a587 1136 }
89840df0 1137 break;
e809a587 1138 case 'i':
e809a587 1139 noinventory = 1;
115ff55c 1140 break;
e809a587 1141#else
115ff55c
VB
1142 case 'M':
1143 case 'i':
115ff55c 1144 case 'P':
e809a587
VB
1145 fprintf(stderr, "LLDP-MED support is not built-in\n");
1146 usage();
e809a587 1147 break;
115ff55c 1148#endif
43c02e7b
VB
1149 case 'p':
1150 probe = atoi(optarg);
1151 break;
1152 case 'x':
e809a587 1153#ifdef USE_SNMP
43c02e7b 1154 snmp = 1;
e809a587
VB
1155#else
1156 fprintf(stderr, "SNMP support is not built-in\n");
1157 usage();
1158#endif
43c02e7b
VB
1159 break;
1160 default:
1161 found = 0;
1162 for (i=0; protos[i].mode != 0; i++) {
1163 if (protos[i].enabled) continue;
1164 if (ch == protos[i].arg) {
1165 protos[i].enabled = 1;
1166 found = 1;
1167 }
1168 }
1169 if (!found)
1170 usage();
1171 }
1172 }
115ff55c 1173
43c02e7b 1174 log_init(debug);
a2993d83 1175
eac2f38a
VB
1176 if (!debug) {
1177 int pid;
1178 char *spid;
1179 if (daemon(0, 0) != 0)
1180 fatal("failed to detach daemon");
1181 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1182 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
1183 fatal("unable to open pid file " LLDPD_PID_FILE);
1184 if (asprintf(&spid, "%d\n", getpid()) == -1)
1185 fatal("unable to create pid file " LLDPD_PID_FILE);
1186 if (write(pid, spid, strlen(spid)) == -1)
1187 fatal("unable to write pid file " LLDPD_PID_FILE);
1188 free(spid);
1189 close(pid);
1190 }
1191
a2993d83 1192 priv_init(PRIVSEP_CHROOT);
43c02e7b
VB
1193
1194 if (probe == 0) probe = LLDPD_TTL;
1195
1196 if ((cfg = (struct lldpd *)
1197 calloc(1, sizeof(struct lldpd))) == NULL)
1198 fatal(NULL);
1199
766f32b3 1200 cfg->g_mgmt_pattern = mgmtp;
43c02e7b
VB
1201
1202 /* Get ioctl socket */
1203 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
1204 fatal("failed to get ioctl socket");
1205 cfg->g_delay = LLDPD_TX_DELAY;
1206
1207 /* Set system capabilities */
1208 cfg->g_lchassis.c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
1209 LLDP_CAP_ROUTER;
89840df0
VB
1210#ifdef ENABLE_LLDPMED
1211 if (lldpmed > 0) {
1212 if (lldpmed == LLDPMED_CLASS_III)
1213 cfg->g_lchassis.c_cap_available |= LLDP_CAP_TELEPHONE;
1214 cfg->g_lchassis.c_med_type = lldpmed;
40ecae87 1215 cfg->g_lchassis.c_med_cap_available = LLDPMED_CAP_CAP |
115ff55c 1216 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
740593ff 1217 cfg->g_noinventory = noinventory;
42bddd41
VB
1218 } else
1219 cfg->g_noinventory = 1;
89840df0 1220#endif
43c02e7b
VB
1221
1222 /* Set TTL */
1223 cfg->g_lchassis.c_ttl = LLDPD_TTL;
1224
1225 cfg->g_protocols = protos;
1226 cfg->g_probe_time = probe;
1227 for (i=0; protos[i].mode != 0; i++)
1228 if (protos[i].enabled) {
1229 cfg->g_multi++;
1230 LLOG_INFO("protocol %s enabled", protos[i].name);
1231 } else
1232 LLOG_INFO("protocol %s disabled", protos[i].name);
1233 cfg->g_multi--;
1234
1235 TAILQ_INIT(&cfg->g_hardware);
1236
1237#ifdef USE_SNMP
1238 if (snmp) {
1239 cfg->g_snmp = 1;
1240 agent_init(cfg, debug);
1241 }
1242#endif /* USE_SNMP */
1243
1244 /* Create socket */
b5562b23
VB
1245 if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
1246 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
1247 TAILQ_INIT(&cfg->g_clients);
43c02e7b 1248
43c02e7b
VB
1249 gcfg = cfg;
1250 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1251 close(cfg->g_ctl);
1252 priv_ctl_cleanup();
43c02e7b
VB
1253 fatal("unable to set exit function");
1254 }
43c02e7b
VB
1255
1256 /* Signal handling */
b5562b23 1257 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1258 signal(SIGINT, lldpd_shutdown);
1259 signal(SIGTERM, lldpd_shutdown);
1260
1261 for (;;)
1262 lldpd_loop(cfg);
1263
1264 return (0);
1265}