]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
edp: steal IP addresses from the received chassis instead of copying them
[thirdparty/lldpd.git] / src / lldpd.c
CommitLineData
43c02e7b
VB
1/*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
51434125 4 * Permission to use, copy, modify, and/or distribute this software for any
43c02e7b
VB
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>
43c02e7b 25#include <time.h>
a2993d83 26#include <libgen.h>
e6b36c87 27#include <assert.h>
43c02e7b
VB
28#include <sys/utsname.h>
29#include <sys/types.h>
c036b15d 30#include <sys/wait.h>
43c02e7b
VB
31#include <sys/socket.h>
32#include <sys/select.h>
33#include <sys/time.h>
34#include <sys/ioctl.h>
35#include <arpa/inet.h>
43c02e7b 36#include <net/if_arp.h>
43c02e7b 37
bc598c23
VB
38#if LLDPD_FD_SETSIZE != FD_SETSIZE
39# warning "FD_SETSIZE is set to an inconsistent value."
40#endif
41
43c02e7b
VB
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 struct protocol protos[] =
43c02e7b 52{
0c877af0 53 { LLDPD_MODE_LLDP, 1, "LLDP", 'l', lldp_send, lldp_decode, NULL,
f2dcb180 54 LLDP_MULTICAST_ADDR },
4bad1937 55#ifdef ENABLE_CDP
43c02e7b 56 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
f2dcb180 57 CDP_MULTICAST_ADDR },
43c02e7b 58 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
f2dcb180 59 CDP_MULTICAST_ADDR },
4bad1937
VB
60#endif
61#ifdef ENABLE_SONMP
43c02e7b 62 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
f2dcb180 63 SONMP_MULTICAST_ADDR },
4bad1937
VB
64#endif
65#ifdef ENABLE_EDP
43c02e7b 66 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
f2dcb180 67 EDP_MULTICAST_ADDR },
4bad1937
VB
68#endif
69#ifdef ENABLE_FDP
031118c4 70 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
f2dcb180 71 FDP_MULTICAST_ADDR },
4bad1937 72#endif
43c02e7b 73 { 0, 0, "any", ' ', NULL, NULL, NULL,
f2dcb180 74 {0,0,0,0,0,0} }
43c02e7b
VB
75};
76
8888d191 77static char **saved_argv;
6bb9c4e0
VB
78#ifdef HAVE___PROGNAME
79extern const char *__progname;
80#else
81# define __progname "lldpd"
82#endif
43c02e7b 83
8888d191 84static void
43c02e7b
VB
85usage(void)
86{
4593c0dc
VB
87 fprintf(stderr, "Usage: %s [OPTIONS ...]\n", __progname);
88 fprintf(stderr, "Version: %s\n", PACKAGE_STRING);
53132616
VB
89
90 fprintf(stderr, "\n");
91
92 fprintf(stderr, "-d Do not daemonize.\n");
5e64a80e 93 fprintf(stderr, "-r Receive-only mode\n");
53132616
VB
94 fprintf(stderr, "-i Disable LLDP-MED inventory TLV transmission.\n");
95 fprintf(stderr, "-k Disable advertising of kernel release, version, machine.\n");
fde7a7ce 96 fprintf(stderr, "-S descr Override the default system description.\n");
d4afb919 97 fprintf(stderr, "-P name Override the default hardware platform.\n");
e6b36c87
JV
98 fprintf(stderr, "-4 IP Specify the IPv4 management address of this system.\n");
99 fprintf(stderr, "-m IP Same as '-4', for backward compatibility.\n");
100 fprintf(stderr, "-6 IP Specify the IPv6 management address of this system.\n");
42b39485 101 fprintf(stderr, "-H mode Specify the behaviour when detecting multiple neighbors.\n");
fbda1f9f 102 fprintf(stderr, "-I iface Limit interfaces to use.\n");
8347587d 103#ifdef ENABLE_LLDPMED
53132616
VB
104 fprintf(stderr, "-M class Enable emission of LLDP-MED frame. 'class' should be one of:\n");
105 fprintf(stderr, " 1 Generic Endpoint (Class I)\n");
106 fprintf(stderr, " 2 Media Endpoint (Class II)\n");
107 fprintf(stderr, " 3 Communication Device Endpoints (Class III)\n");
108 fprintf(stderr, " 4 Network Connectivity Device\n");
8347587d
VB
109#endif
110#ifdef USE_SNMP
53132616 111 fprintf(stderr, "-x Enable SNMP subagent.\n");
53132616
VB
112#endif
113 fprintf(stderr, "\n");
114
8347587d
VB
115#if defined ENABLE_CDP || defined ENABLE_EDP || defined ENABLE_FDP || defined ENABLE_SONMP
116 fprintf(stderr, "Additional protocol support.\n");
117#ifdef ENABLE_CDP
53132616 118 fprintf(stderr, "-c Enable the support of CDP protocol. (Cisco)\n");
8347587d
VB
119#endif
120#ifdef ENABLE_EDP
53132616 121 fprintf(stderr, "-e Enable the support of EDP protocol. (Extreme)\n");
8347587d
VB
122#endif
123#ifdef ENABLE_FDP
53132616 124 fprintf(stderr, "-f Enable the support of FDP protocol. (Foundry)\n");
8347587d
VB
125#endif
126#ifdef ENABLE_SONMP
53132616 127 fprintf(stderr, "-s Enable the support of SONMP protocol. (Nortel)\n");
8347587d 128#endif
53132616
VB
129
130 fprintf(stderr, "\n");
8347587d 131#endif
53132616 132
e809a587 133 fprintf(stderr, "see manual page lldpd(8) for more information\n");
43c02e7b
VB
134 exit(1);
135}
136
6e75df87 137struct lldpd_hardware *
44002d66 138lldpd_get_hardware(struct lldpd *cfg, char *name, int index, struct lldpd_ops *ops)
43c02e7b 139{
6e75df87
VB
140 struct lldpd_hardware *hardware;
141 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
849954d7 142 if ((strcmp(hardware->h_ifname, name) == 0) &&
44002d66 143 (hardware->h_ifindex == index) &&
849954d7 144 ((!ops) || (ops == hardware->h_ops)))
6e75df87 145 break;
43c02e7b 146 }
6e75df87 147 return hardware;
43c02e7b
VB
148}
149
6e75df87
VB
150struct lldpd_hardware *
151lldpd_alloc_hardware(struct lldpd *cfg, char *name)
43c02e7b 152{
6e75df87 153 struct lldpd_hardware *hardware;
43c02e7b 154
6e75df87
VB
155 if ((hardware = (struct lldpd_hardware *)
156 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
157 return NULL;
43c02e7b 158
6e75df87
VB
159 strlcpy(hardware->h_ifname, name, sizeof(hardware->h_ifname));
160 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
9df5ec3d 161 hardware->h_lport.p_chassis->c_refcount++;
6e75df87 162 TAILQ_INIT(&hardware->h_rports);
43c02e7b 163
6e75df87
VB
164#ifdef ENABLE_LLDPMED
165 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
166 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
167 if (!cfg->g_noinventory)
168 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
169 }
170#endif
171#ifdef ENABLE_DOT1
172 TAILQ_INIT(&hardware->h_lport.p_vlans);
9757bfbc
SK
173 TAILQ_INIT(&hardware->h_lport.p_ppvids);
174 TAILQ_INIT(&hardware->h_lport.p_pids);
6e75df87
VB
175#endif
176 return hardware;
43c02e7b
VB
177}
178
a1347cd8 179#ifdef ENABLE_DOT1
43c02e7b
VB
180void
181lldpd_vlan_cleanup(struct lldpd_port *port)
182{
183 struct lldpd_vlan *vlan, *vlan_next;
184 for (vlan = TAILQ_FIRST(&port->p_vlans);
185 vlan != NULL;
186 vlan = vlan_next) {
187 free(vlan->v_name);
188 vlan_next = TAILQ_NEXT(vlan, v_entries);
189 TAILQ_REMOVE(&port->p_vlans, vlan, v_entries);
190 free(vlan);
191 }
192}
9757bfbc
SK
193
194void
195lldpd_ppvid_cleanup(struct lldpd_port *port)
196{
197 struct lldpd_ppvid *ppvid, *ppvid_next;
198 for (ppvid = TAILQ_FIRST(&port->p_ppvids);
199 ppvid != NULL;
200 ppvid = ppvid_next) {
201 ppvid_next = TAILQ_NEXT(ppvid, p_entries);
202 TAILQ_REMOVE(&port->p_ppvids, ppvid, p_entries);
203 free(ppvid);
204 }
205}
206
207void
208lldpd_pi_cleanup(struct lldpd_port *port)
209{
210 struct lldpd_pi *pi, *pi_next;
211 for (pi = TAILQ_FIRST(&port->p_pids);
212 pi != NULL;
213 pi = pi_next) {
214 free(pi->p_pi);
215 pi_next = TAILQ_NEXT(pi, p_entries);
216 TAILQ_REMOVE(&port->p_pids, pi, p_entries);
217 free(pi);
218 }
219}
a1347cd8 220#endif
43c02e7b 221
a0edeaf8 222/* If `all' is true, clear all information, including information that
4e624dc2 223 are not refreshed periodically. Port should be freed manually. */
43c02e7b 224void
9898ac07 225lldpd_port_cleanup(struct lldpd *cfg, struct lldpd_port *port, int all)
43c02e7b 226{
740593ff
VB
227#ifdef ENABLE_LLDPMED
228 int i;
a0edeaf8
VB
229 if (all)
230 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
231 free(port->p_med_location[i].data);
740593ff 232#endif
a1347cd8 233#ifdef ENABLE_DOT1
43c02e7b 234 lldpd_vlan_cleanup(port);
9757bfbc
SK
235 lldpd_ppvid_cleanup(port);
236 lldpd_pi_cleanup(port);
a1347cd8 237#endif
43c02e7b
VB
238 free(port->p_id);
239 free(port->p_descr);
77507b69
VB
240 if (all) {
241 free(port->p_lastframe);
9898ac07 242 if (port->p_chassis) { /* chassis may not have been attributed, yet */
77507b69 243 port->p_chassis->c_refcount--;
9898ac07
V
244 port->p_chassis = NULL;
245 }
77507b69 246 }
43c02e7b
VB
247}
248
e6b36c87
JV
249struct lldpd_mgmt *
250lldpd_alloc_mgmt(int family, void *addrptr, size_t addrsize, u_int32_t iface)
251{
252 struct lldpd_mgmt *mgmt;
253
254 if (family <= LLDPD_AF_UNSPEC || family >= LLDPD_AF_LAST) {
255 errno = EAFNOSUPPORT;
256 return NULL;
257 }
258 if (addrsize > LLDPD_MGMT_MAXADDRSIZE) {
259 errno = EOVERFLOW;
260 return NULL;
261 }
262 mgmt = calloc(1, sizeof(struct lldpd_mgmt));
263 if (mgmt == NULL) {
264 errno = ENOMEM;
265 return NULL;
266 }
267 mgmt->m_family = family;
268 assert(addrsize <= LLDPD_MGMT_MAXADDRSIZE);
269 memcpy(&mgmt->m_addr, addrptr, addrsize);
270 mgmt->m_addrsize = addrsize;
271 mgmt->m_iface = iface;
272 return mgmt;
273}
274
43c02e7b 275void
77507b69 276lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
43c02e7b 277{
89840df0
VB
278#ifdef ENABLE_LLDPMED
279 free(chassis->c_med_hw);
517d524b 280 free(chassis->c_med_sw);
89840df0
VB
281 free(chassis->c_med_fw);
282 free(chassis->c_med_sn);
283 free(chassis->c_med_manuf);
284 free(chassis->c_med_model);
285 free(chassis->c_med_asset);
286#endif
43c02e7b
VB
287 free(chassis->c_id);
288 free(chassis->c_name);
289 free(chassis->c_descr);
77507b69
VB
290 if (all)
291 free(chassis);
43c02e7b
VB
292}
293
294void
77507b69 295lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
43c02e7b 296{
77507b69
VB
297 struct lldpd_port *port, *port_next;
298 int del;
299 for (port = TAILQ_FIRST(&hardware->h_rports);
300 port != NULL;
301 port = port_next) {
302 port_next = TAILQ_NEXT(port, p_entries);
303 del = all;
304 if (!del &&
305 (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
306 hardware->h_rx_ageout_cnt++;
307 del = 1;
308 }
309 if (del) {
310 TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
9898ac07 311 lldpd_port_cleanup(cfg, port, 1);
4e624dc2 312 free(port);
77507b69 313 }
43c02e7b 314 }
43c02e7b
VB
315}
316
d9be8ea0 317void
6e75df87 318lldpd_hardware_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware)
d9be8ea0 319{
6e75df87 320 int i;
9898ac07 321 lldpd_port_cleanup(cfg, &hardware->h_lport, 1);
6e75df87
VB
322 /* If we have a dedicated cleanup function, use it. Otherwise,
323 we just free the hardware-dependent data and close all FD
324 in h_recvfds and h_sendfd. */
325 if (hardware->h_ops->cleanup)
326 hardware->h_ops->cleanup(cfg, hardware);
327 else {
328 free(hardware->h_data);
bc598c23 329 for (i=0; i < LLDPD_FD_SETSIZE; i++)
6e75df87
VB
330 if (FD_ISSET(i, &hardware->h_recvfds))
331 close(i);
332 if (hardware->h_sendfd) close(hardware->h_sendfd);
333 }
d9be8ea0
VB
334 free(hardware);
335}
336
6e75df87 337static void
43c02e7b
VB
338lldpd_cleanup(struct lldpd *cfg)
339{
340 struct lldpd_hardware *hardware, *hardware_next;
aadc9936 341 struct lldpd_chassis *chassis, *chassis_next;
43c02e7b
VB
342
343 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
344 hardware = hardware_next) {
345 hardware_next = TAILQ_NEXT(hardware, h_entries);
6e75df87 346 if (!hardware->h_flags) {
43c02e7b 347 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
77507b69 348 lldpd_remote_cleanup(cfg, hardware, 1);
6e75df87 349 lldpd_hardware_cleanup(cfg, hardware);
77507b69
VB
350 } else
351 lldpd_remote_cleanup(cfg, hardware, 0);
43c02e7b 352 }
aadc9936
VB
353
354 for (chassis = TAILQ_FIRST(&cfg->g_chassis); chassis;
355 chassis = chassis_next) {
356 chassis_next = TAILQ_NEXT(chassis, c_entries);
357 if (chassis->c_refcount == 0) {
358 TAILQ_REMOVE(&cfg->g_chassis, chassis, c_entries);
359 lldpd_chassis_cleanup(chassis, 1);
360 }
361 }
43c02e7b
VB
362}
363
566c635d
VB
364/* Update chassis `ochassis' with values from `chassis'. */
365static void
366lldpd_update_chassis(struct lldpd_chassis *ochassis,
367 const struct lldpd_chassis *chassis) {
368 TAILQ_ENTRY(lldpd_chassis) entries;
369 /* We want to keep refcount, index and list stuff from the current
370 * chassis */
371 int refcount = ochassis->c_refcount;
372 int index = ochassis->c_index;
373 memcpy(&entries, &ochassis->c_entries,
374 sizeof(entries));
375 /* Make the copy */
376 lldpd_chassis_cleanup(ochassis, 0);
377 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
378 /* Restore saved values */
379 ochassis->c_refcount = refcount;
380 ochassis->c_index = index;
381 memcpy(&ochassis->c_entries, &entries, sizeof(entries));
382}
383
8888d191 384static int
43c02e7b
VB
385lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
386{
387 int i;
388 if (s < ETH_ALEN)
389 return -1;
390 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
391 if (!cfg->g_protocols[i].enabled)
392 continue;
393 if (cfg->g_protocols[i].guess == NULL) {
394 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
395 return cfg->g_protocols[i].mode;
396 } else {
397 if (cfg->g_protocols[i].guess(frame, s))
398 return cfg->g_protocols[i].mode;
399 }
400 }
401 return -1;
402}
403
8888d191 404static void
43c02e7b 405lldpd_decode(struct lldpd *cfg, char *frame, int s,
0bc32943 406 struct lldpd_hardware *hardware)
43c02e7b 407{
77507b69
VB
408 int i, result;
409 struct lldpd_chassis *chassis, *ochassis = NULL;
410 struct lldpd_port *port, *oport = NULL;
43c02e7b
VB
411 int guess = LLDPD_MODE_LLDP;
412
49697208
VB
413 if (s < sizeof(struct ethhdr) + 4)
414 /* Too short, just discard it */
50a89ca7 415 return;
49697208
VB
416 /* Decapsulate VLAN frames */
417 if (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)) {
418 /* VLAN decapsulation means to shift 4 bytes left the frame from
419 * offset 2*ETH_ALEN */
420 memmove(frame + 2*ETH_ALEN, frame + 2*ETH_ALEN + 4, s - 2*ETH_ALEN);
421 s -= 4;
422 }
50a89ca7 423
77507b69
VB
424 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
425 if ((oport->p_lastframe != NULL) &&
426 (oport->p_lastframe->size == s) &&
427 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
428 /* Already received the same frame */
429 oport->p_lastupdate = time(NULL);
430 return;
431 }
43c02e7b
VB
432 }
433
f2dcb180
VB
434 guess = lldpd_guess_type(cfg, frame, s);
435 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
436 if (!cfg->g_protocols[i].enabled)
437 continue;
438 if (cfg->g_protocols[i].mode == guess) {
439 if ((result = cfg->g_protocols[i].decode(cfg, frame,
440 s, hardware, &chassis, &port)) == -1)
441 return;
77507b69
VB
442 chassis->c_protocol = port->p_protocol =
443 cfg->g_protocols[i].mode;
f2dcb180 444 break;
43c02e7b 445 }
f2dcb180
VB
446 }
447 if (cfg->g_protocols[i].mode == 0) {
ba5116b5
VB
448 LLOG_INFO("unable to guess frame type on %s",
449 hardware->h_ifname);
43c02e7b 450 return;
f2dcb180 451 }
43c02e7b 452
77507b69
VB
453 /* Do we already have the same MSAP somewhere? */
454 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
455 if ((port->p_protocol == oport->p_protocol) &&
456 (port->p_id_subtype == oport->p_id_subtype) &&
457 (port->p_id_len == oport->p_id_len) &&
458 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
459 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
460 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
461 (memcmp(chassis->c_id, oport->p_chassis->c_id,
462 chassis->c_id_len) == 0)) {
463 ochassis = oport->p_chassis;
464 break;
465 }
43c02e7b 466 }
77507b69
VB
467 /* No, but do we already know the system? */
468 if (!oport) {
469 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
470 if ((chassis->c_protocol == ochassis->c_protocol) &&
471 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
472 (chassis->c_id_len == ochassis->c_id_len) &&
473 (memcmp(chassis->c_id, ochassis->c_id,
474 chassis->c_id_len) == 0))
475 break;
43c02e7b 476 }
43c02e7b 477 }
43c02e7b 478
77507b69
VB
479 if (oport) {
480 /* The port is known, remove it before adding it back */
481 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
9898ac07 482 lldpd_port_cleanup(cfg, oport, 1);
4e624dc2 483 free(oport);
77507b69
VB
484 }
485 if (ochassis) {
16f910e1 486 lldpd_update_chassis(ochassis, chassis);
77507b69
VB
487 free(chassis);
488 chassis = ochassis;
489 } else {
490 /* Chassis not known, add it */
491 chassis->c_index = ++cfg->g_lastrid;
77507b69
VB
492 chassis->c_refcount = 0;
493 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
f4c43902
VB
494 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
495 LLOG_DEBUG("Currently, we know %d different systems", i);
77507b69
VB
496 }
497 /* Add port */
498 port->p_lastchange = port->p_lastupdate = time(NULL);
499 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
500 sizeof(int))) != NULL) {
501 port->p_lastframe->size = s;
502 memcpy(port->p_lastframe->frame, frame, s);
503 }
504 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
505 port->p_chassis = chassis;
506 port->p_chassis->c_refcount++;
9df5ec3d
VB
507 /* Several cases are possible :
508 1. chassis is new, its refcount was 0. It is now attached
509 to this port, its refcount is 1.
510 2. chassis already exists and was attached to another
511 port, we increase its refcount accordingly.
512 3. chassis already exists and was attached to the same
513 port, its refcount was decreased with
514 lldpd_port_cleanup() and is now increased again.
515
516 In all cases, if the port already existed, it has been
517 freed with lldpd_port_cleanup() and therefore, the refcount
518 of the chassis that was attached to it is decreased.
519 */
42b39485
VB
520 i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries)
521 i++;
522 LLOG_DEBUG("Currently, %s knows %d neighbors",
f4c43902 523 hardware->h_ifname, i);
43c02e7b
VB
524 return;
525}
526
c036b15d
VB
527/* Get the output of lsb_release -s -d. This is a slow function. It should be
528 called once. It return NULL if any problem happens. Otherwise, this is a
529 statically allocated buffer. The result includes the trailing \n */
530static char *
531lldpd_get_lsb_release() {
532 static char release[1024];
533 char *const command[] = { "lsb_release", "-s", "-d", NULL };
534 int pid, status, devnull, count;
535 int pipefd[2];
536
537 if (pipe(pipefd)) {
538 LLOG_WARN("unable to get a pair of pipes");
539 return NULL;
540 }
541
542 if ((pid = fork()) < 0) {
543 LLOG_WARN("unable to fork");
544 return NULL;
545 }
546 switch (pid) {
547 case 0:
548 /* Child, exec lsb_release */
549 close(pipefd[0]);
550 if ((devnull = open("/dev/null", O_RDWR, 0)) != -1) {
551 dup2(devnull, STDIN_FILENO);
552 dup2(devnull, STDERR_FILENO);
553 dup2(pipefd[1], STDOUT_FILENO);
554 if (devnull > 2) close(devnull);
555 if (pipefd[1] > 2) close(pipefd[1]);
556 execvp("lsb_release", command);
557 }
558 exit(127);
559 break;
560 default:
561 /* Father, read the output from the children */
562 close(pipefd[1]);
563 count = 0;
564 do {
565 status = read(pipefd[0], release+count, sizeof(release)-count);
566 if ((status == -1) && (errno == EINTR)) continue;
567 if (status > 0)
568 count += status;
569 } while (count < sizeof(release) && (status > 0));
570 if (status < 0) {
571 LLOG_WARN("unable to read from lsb_release");
572 close(pipefd[0]);
573 waitpid(pid, &status, 0);
574 return NULL;
575 }
576 close(pipefd[0]);
577 if (count >= sizeof(release)) {
578 LLOG_INFO("output of lsb_release is too large");
579 waitpid(pid, &status, 0);
580 return NULL;
581 }
582 status = -1;
583 if (waitpid(pid, &status, 0) != pid)
584 return NULL;
585 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
586 LLOG_INFO("lsb_release information not available");
587 return NULL;
588 }
589 if (!count) {
590 LLOG_INFO("lsb_release returned an empty string");
591 return NULL;
592 }
593 release[count] = '\0';
594 return release;
595 }
596 /* Should not be here */
597 return NULL;
598}
599
ae87586a
MT
600/* Same like lldpd_get_lsb_release but reads /etc/os-release for PRETTY_NAME=. */
601static char *
602lldpd_get_os_release() {
603 static char release[1024];
9757bfbc
SK
604 char line[1024];
605 char *key, *val;
606 char *ptr1 = release;
607 char *ptr2 = release;
ae87586a
MT
608
609 FILE *fp = fopen("/etc/os-release", "r");
610 if (!fp) {
e02afca4 611 LLOG_WARN("could not open /etc/os-release");
ae87586a
MT
612 return NULL;
613 }
614
ae87586a
MT
615 while ((fgets(line, 1024, fp) != NULL)) {
616 key = strtok(line, "=");
617 val = strtok(NULL, "=");
618
619 if (strncmp(key, "PRETTY_NAME", 1024) == 0) {
620 strncpy(release, val, 1024);
621 break;
622 }
623 }
624 fclose(fp);
625
626 /* Remove trailing newline and all " in the string. */
ae87586a
MT
627 while (*ptr1 != 0) {
628 if ((*ptr1 == '"') || (*ptr1 == '\n')) {
629 ++ptr1;
630 } else {
631 *ptr2++ = *ptr1++;
632 }
633 }
634 *ptr2 = 0;
635
636 return release;
637}
638
77d7090e
VB
639int
640lldpd_callback_add(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG), void *data)
641{
642 struct lldpd_callback *callback;
643 if ((callback = (struct lldpd_callback *)
644 malloc(sizeof(struct lldpd_callback))) == NULL)
645 return -1;
646 callback->fd = fd;
647 callback->function = fn;
648 callback->data = data;
649 TAILQ_INSERT_TAIL(&cfg->g_callbacks, callback, next);
650 return 0;
651}
652
653void
654lldpd_callback_del(struct lldpd *cfg, int fd, void(*fn)(CALLBACK_SIG))
655{
656 struct lldpd_callback *callback, *callback_next;
657 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
658 callback;
659 callback = callback_next) {
660 callback_next = TAILQ_NEXT(callback, next);
661 if ((callback->fd == fd) &&
662 (callback->function = fn)) {
663 free(callback->data);
664 TAILQ_REMOVE(&cfg->g_callbacks, callback, next);
665 free(callback);
666 }
667 }
668}
16f910e1 669
8482abe9
VB
670static void
671lldpd_hide_ports(struct lldpd *cfg, struct lldpd_hardware *hardware, int mask) {
42b39485
VB
672 struct lldpd_port *port;
673 int protocols[LLDPD_MODE_MAX+1];
8482abe9
VB
674 char buffer[256];
675 int i, j, k, found;
42b39485
VB
676 unsigned int min;
677
8482abe9
VB
678 /* Compute the number of occurrences of each protocol */
679 for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
680 TAILQ_FOREACH(port, &hardware->h_rports, p_entries)
681 protocols[port->p_protocol]++;
682
683 /* Turn the protocols[] array into an array of
684 enabled/disabled protocols. 1 means enabled, 0
685 means disabled. */
686 min = (unsigned int)-1;
687 for (i = 0; i <= LLDPD_MODE_MAX; i++)
688 if (protocols[i] && (protocols[i] < min))
689 min = protocols[i];
690 found = 0;
691 for (i = 0; i <= LLDPD_MODE_MAX; i++)
692 if ((protocols[i] == min) && !found) {
693 /* If we need a tie breaker, we take
694 the first protocol only */
695 if (cfg->g_smart & mask &
696 (SMART_OUTGOING_ONE_PROTO | SMART_INCOMING_ONE_PROTO))
697 found = 1;
698 protocols[i] = 1;
699 } else protocols[i] = 0;
700
701 /* We set the p_hidden flag to 1 if the protocol is disabled */
702 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
703 if (mask == SMART_OUTGOING)
704 port->p_hidden_out = protocols[port->p_protocol]?0:1;
705 else
706 port->p_hidden_in = protocols[port->p_protocol]?0:1;
707 }
708
709 /* If we want only one neighbor, we take the first one */
710 if (cfg->g_smart & mask &
711 (SMART_OUTGOING_ONE_NEIGH | SMART_INCOMING_ONE_NEIGH)) {
42b39485 712 found = 0;
8482abe9
VB
713 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
714 if (mask == SMART_OUTGOING) {
715 if (found) port->p_hidden_out = 1;
716 if (!port->p_hidden_out)
717 found = 1;
718 }
719 if (mask == SMART_INCOMING) {
720 if (found) port->p_hidden_in = 1;
721 if (!port->p_hidden_in)
42b39485 722 found = 1;
42b39485
VB
723 }
724 }
8482abe9 725 }
42b39485 726
8482abe9
VB
727 /* Print a debug message summarizing the operation */
728 for (i = 0; i <= LLDPD_MODE_MAX; i++) protocols[i] = 0;
729 k = j = 0;
730 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
731 if (!(((mask == SMART_OUTGOING) && port->p_hidden_out) ||
732 ((mask == SMART_INCOMING) && port->p_hidden_in))) {
733 k++;
734 protocols[port->p_protocol] = 1;
42b39485 735 }
8482abe9
VB
736 j++;
737 }
738 buffer[0] = '\0';
739 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
740 if (cfg->g_protocols[i].enabled && protocols[cfg->g_protocols[i].mode]) {
741 if (strlen(buffer) +
742 strlen(cfg->g_protocols[i].name) + 3 > sizeof(buffer)) {
743 /* Unlikely, our buffer is too small */
744 memcpy(buffer + sizeof(buffer) - 4, "...", 4);
745 break;
42b39485 746 }
8482abe9
VB
747 if (buffer[0])
748 strcat(buffer, ", ");
749 strcat(buffer, cfg->g_protocols[i].name);
42b39485
VB
750 }
751 }
8482abe9
VB
752 LLOG_DEBUG("[%s] %s: %d visible neigh / %d. Protocols: %s.",
753 (mask == SMART_OUTGOING)?"out filter":"in filter",
754 hardware->h_ifname, k, j, buffer[0]?buffer:"(none)");
42b39485
VB
755}
756
566c635d
VB
757/* Hide unwanted ports depending on smart mode set by the user */
758static void
759lldpd_hide_all(struct lldpd *cfg)
760{
761 struct lldpd_hardware *hardware;
762
763 if (!cfg->g_smart)
764 return;
765 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
766 if (cfg->g_smart & SMART_INCOMING_FILTER)
767 lldpd_hide_ports(cfg, hardware, SMART_INCOMING);
768 if (cfg->g_smart & SMART_OUTGOING_FILTER)
769 lldpd_hide_ports(cfg, hardware, SMART_OUTGOING);
770 }
771}
772
8888d191 773static void
43c02e7b
VB
774lldpd_recv_all(struct lldpd *cfg)
775{
776 struct lldpd_hardware *hardware;
77d7090e 777 struct lldpd_callback *callback, *callback_next;
43c02e7b
VB
778 fd_set rfds;
779 struct timeval tv;
43c02e7b 780#ifdef USE_SNMP
630b4134
VB
781 struct timeval snmptv;
782 int snmpblock = 0;
43c02e7b 783#endif
0bc32943 784 int rc, nfds, n;
43c02e7b
VB
785 char *buffer;
786
787 do {
788 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
789 if (tv.tv_sec < 0)
c520cb14
VB
790 /* We did not send packets in a long time,
791 just give up receive for now. */
792 break;
43c02e7b
VB
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 */
6e75df87 802 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b 803 continue;
6e75df87
VB
804 /* This is quite expensive but we don't rely on internal
805 * structure of fd_set. */
bc598c23 806 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
6e75df87
VB
807 if (FD_ISSET(n, &hardware->h_recvfds)) {
808 FD_SET(n, &rfds);
809 if (nfds < n)
810 nfds = n;
811 }
43c02e7b 812 }
77d7090e
VB
813 TAILQ_FOREACH(callback, &cfg->g_callbacks, next) {
814 FD_SET(callback->fd, &rfds);
815 if (nfds < callback->fd)
816 nfds = callback->fd;
43c02e7b 817 }
43c02e7b
VB
818
819#ifdef USE_SNMP
630b4134
VB
820 if (cfg->g_snmp) {
821 snmpblock = 0;
822 memcpy(&snmptv, &tv, sizeof(struct timeval));
823 snmp_select_info(&nfds, &rfds, &snmptv, &snmpblock);
824 if (snmpblock == 0)
825 memcpy(&tv, &snmptv, sizeof(struct timeval));
826 }
43c02e7b
VB
827#endif /* USE_SNMP */
828 if (nfds == -1) {
829 sleep(cfg->g_delay);
830 return;
831 }
832
833 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
834 if (rc == -1) {
835 if (errno == EINTR)
836 continue;
837 LLOG_WARN("failure on select");
838 break;
839 }
840#ifdef USE_SNMP
841 if (cfg->g_snmp) {
842 if (rc > 0)
843 snmp_read(&rfds);
844 else if (rc == 0)
845 snmp_timeout();
846 }
847#endif /* USE_SNMP */
848 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
bc598c23 849 for (n = 0; n < LLDPD_FD_SETSIZE; n++)
6e75df87
VB
850 if ((FD_ISSET(n, &hardware->h_recvfds)) &&
851 (FD_ISSET(n, &rfds))) break;
bc598c23 852 if (n == LLDPD_FD_SETSIZE) continue;
6e75df87
VB
853 if ((buffer = (char *)malloc(
854 hardware->h_mtu)) == NULL) {
855 LLOG_WARN("failed to alloc reception buffer");
856 continue;
857 }
858 if ((n = hardware->h_ops->recv(cfg, hardware,
859 n, buffer, hardware->h_mtu)) == -1) {
43c02e7b 860 free(buffer);
6e75df87 861 continue;
43c02e7b 862 }
6e75df87
VB
863 hardware->h_rx_cnt++;
864 lldpd_decode(cfg, buffer, n, hardware);
6b41bbd3 865 lldpd_hide_all(cfg); /* Immediatly hide */
6e75df87
VB
866 free(buffer);
867 break;
43c02e7b 868 }
77d7090e
VB
869 for (callback = TAILQ_FIRST(&cfg->g_callbacks);
870 callback;
871 callback = callback_next) {
872 /* Callback function can use TAILQ_REMOVE */
873 callback_next = TAILQ_NEXT(callback, next);
874 if (FD_ISSET(callback->fd, &rfds))
875 callback->function(cfg, callback);
43c02e7b
VB
876 }
877
878#ifdef USE_SNMP
879 if (cfg->g_snmp) {
880 run_alarms();
881 netsnmp_check_outstanding_agent_requests();
882 }
883#endif /* USE_SNMP */
884 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
885}
886
8888d191 887static void
43c02e7b
VB
888lldpd_send_all(struct lldpd *cfg)
889{
890 struct lldpd_hardware *hardware;
77507b69 891 struct lldpd_port *port;
0d86e62f 892 int i, sent;
f7db0dd8 893
43c02e7b 894 cfg->g_lastsent = time(NULL);
c520cb14 895 if (cfg->g_receiveonly) return;
43c02e7b
VB
896 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
897 /* Ignore if interface is down */
6e75df87 898 if ((hardware->h_flags & IFF_RUNNING) == 0)
43c02e7b
VB
899 continue;
900
0d86e62f 901 sent = 0;
43c02e7b
VB
902 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
903 if (!cfg->g_protocols[i].enabled)
904 continue;
77507b69 905 /* We send only if we have at least one remote system
0c877af0
VB
906 * speaking this protocol or if the protocol is forced */
907 if (cfg->g_protocols[i].enabled > 1) {
908 cfg->g_protocols[i].send(cfg, hardware);
909 sent++;
910 continue;
911 }
77507b69 912 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
42b39485
VB
913 /* If this remote port is disabled, we don't
914 * consider it */
4d1a5b39 915 if (port->p_hidden_out)
42b39485 916 continue;
77507b69
VB
917 if (port->p_protocol ==
918 cfg->g_protocols[i].mode) {
919 cfg->g_protocols[i].send(cfg,
920 hardware);
0c877af0 921 sent++;
77507b69
VB
922 break;
923 }
924 }
43c02e7b 925 }
77507b69
VB
926
927 if (!sent)
928 /* Nothing was sent for this port, let's speak LLDP */
929 cfg->g_protocols[0].send(cfg,
930 hardware);
43c02e7b
VB
931 }
932}
933
89840df0 934#ifdef ENABLE_LLDPMED
8888d191 935static void
89840df0
VB
936lldpd_med(struct lldpd_chassis *chassis)
937{
0265b1e5 938#if __i386__ || __amd64__
55606d4b
VB
939 static short int once = 0;
940 if (!once) {
941 chassis->c_med_hw = dmi_hw();
942 chassis->c_med_fw = dmi_fw();
943 chassis->c_med_sn = dmi_sn();
944 chassis->c_med_manuf = dmi_manuf();
945 chassis->c_med_model = dmi_model();
946 chassis->c_med_asset = dmi_asset();
947 once = 1;
948 }
0265b1e5 949#endif
89840df0
VB
950}
951#endif
952
8888d191 953static void
6e75df87 954lldpd_update_localchassis(struct lldpd *cfg)
43c02e7b 955{
6e75df87
VB
956 struct utsname un;
957 char *hp;
43c02e7b
VB
958 int f;
959 char status;
43c02e7b
VB
960
961 /* Set system name and description */
6e75df87 962 if (uname(&un) != 0)
43c02e7b 963 fatal("failed to get system information");
b5562b23 964 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 965 fatal("failed to get system name");
77507b69
VB
966 free(LOCAL_CHASSIS(cfg)->c_name);
967 free(LOCAL_CHASSIS(cfg)->c_descr);
968 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 969 fatal(NULL);
40ce835b
ST
970 if (cfg->g_descr_override) {
971 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
972 cfg->g_descr_override) == -1)
de1b1b3a 973 fatal("failed to set full system description");
40ce835b
ST
974 } else {
975 if (cfg->g_advertise_version) {
a9db9b44 976 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s %s",
c036b15d 977 cfg->g_lsb_release?cfg->g_lsb_release:"",
a9db9b44 978 un.sysname, un.release, un.version, un.machine)
40ce835b
ST
979 == -1)
980 fatal("failed to set full system description");
981 } else {
982 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s",
c036b15d 983 cfg->g_lsb_release?cfg->g_lsb_release:un.sysname) == -1)
40ce835b
ST
984 fatal("failed to set minimal system description");
985 }
986 }
43c02e7b
VB
987
988 /* Check forwarding */
b5562b23 989 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
e5c5ae41
VB
990 if ((read(f, &status, 1) == 1) && (status == '1'))
991 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_ROUTER;
992 else
993 LOCAL_CHASSIS(cfg)->c_cap_enabled &= ~LLDP_CAP_ROUTER;
43c02e7b
VB
994 close(f);
995 }
89840df0 996#ifdef ENABLE_LLDPMED
77507b69
VB
997 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
998 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
999 lldpd_med(LOCAL_CHASSIS(cfg));
1000 free(LOCAL_CHASSIS(cfg)->c_med_sw);
de1b1b3a
VB
1001 if (cfg->g_advertise_version)
1002 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un.release);
1003 else
1004 LOCAL_CHASSIS(cfg)->c_med_sw = strdup("Unknown");
89840df0 1005#endif
43c02e7b 1006
b4ac8083
VB
1007 /* Set chassis ID if needed. This is only done if chassis ID
1008 has not been set previously (with the MAC address of an
1009 interface for example)
1010 */
1011 if (LOCAL_CHASSIS(cfg)->c_id == NULL) {
1012 if (!(LOCAL_CHASSIS(cfg)->c_id = strdup(LOCAL_CHASSIS(cfg)->c_name)))
6e75df87 1013 fatal(NULL);
b4ac8083
VB
1014 LOCAL_CHASSIS(cfg)->c_id_len = strlen(LOCAL_CHASSIS(cfg)->c_name);
1015 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
6e75df87
VB
1016 }
1017}
1018
1019static void
1020lldpd_update_localports(struct lldpd *cfg)
1021{
1022 struct ifaddrs *ifap;
1023 struct lldpd_hardware *hardware;
1024 lldpd_ifhandlers ifhs[] = {
ba85f9f4 1025 lldpd_ifh_whitelist, /* Is the interface whitelisted? */
849954d7 1026 lldpd_ifh_bond, /* Handle bond */
6e75df87 1027 lldpd_ifh_eth, /* Handle classic ethernet interfaces */
5994b27d 1028#ifdef ENABLE_DOT1
6e75df87 1029 lldpd_ifh_vlan, /* Handle VLAN */
5994b27d 1030#endif
6e75df87 1031 lldpd_ifh_mgmt, /* Handle management address (if not already handled) */
b4ac8083 1032 lldpd_ifh_chassis, /* Handle chassis ID (if not already handled) */
6e75df87
VB
1033 NULL
1034 };
1035 lldpd_ifhandlers *ifh;
1036
1037 /* h_flags is set to 0 for each port. If the port is updated, h_flags
1038 * will be set to a non-zero value. This will allow us to clean up any
1039 * non up-to-date port */
43c02e7b
VB
1040 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
1041 hardware->h_flags = 0;
1042
6e75df87
VB
1043 if (getifaddrs(&ifap) != 0)
1044 fatal("lldpd_update_localports: failed to get interface list");
1045
1046 /* We will run the list of interfaces through a list of interface
1047 * handlers. Each handler will create or update some hardware port (and
1048 * will set h_flags to a non zero value. The handler can use the list of
1049 * interfaces but this is not mandatory. If the interface handler
1050 * handles an interface from the list, it should set ifa_flags to 0 to
1051 * let know the other handlers that it took care of this interface. This
1052 * means that more specific handlers should be before less specific
1053 * ones. */
1054 for (ifh = ifhs; *ifh != NULL; ifh++)
1055 (*ifh)(cfg, ifap);
43c02e7b 1056 freeifaddrs(ifap);
6e75df87 1057}
43c02e7b 1058
6e75df87
VB
1059static void
1060lldpd_loop(struct lldpd *cfg)
1061{
1062 /* Main loop.
1063
1064 1. Update local ports information
1065 2. Clean unwanted (removed) local ports
1066 3. Update local chassis information
1067 4. Send packets
1068 5. Receive packets
42b39485 1069 6. Update smart mode
6e75df87 1070 */
849954d7 1071 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
6e75df87 1072 lldpd_update_localports(cfg);
43c02e7b 1073 lldpd_cleanup(cfg);
6e75df87 1074 lldpd_update_localchassis(cfg);
c520cb14 1075 lldpd_send_all(cfg);
43c02e7b
VB
1076 lldpd_recv_all(cfg);
1077}
1078
8888d191 1079static void
43c02e7b
VB
1080lldpd_shutdown(int sig)
1081{
1082 LLOG_INFO("signal received, exiting");
1083 exit(0);
1084}
1085
1086/* For signal handling */
8888d191 1087static struct lldpd *gcfg = NULL;
43c02e7b 1088
8888d191 1089static void
43c02e7b
VB
1090lldpd_exit()
1091{
6e75df87 1092 struct lldpd_hardware *hardware, *hardware_next;
b5562b23
VB
1093 close(gcfg->g_ctl);
1094 priv_ctl_cleanup();
6e75df87
VB
1095 for (hardware = TAILQ_FIRST(&gcfg->g_hardware); hardware != NULL;
1096 hardware = hardware_next) {
1097 hardware_next = TAILQ_NEXT(hardware, h_entries);
1098 lldpd_hardware_cleanup(gcfg, hardware);
43c02e7b
VB
1099 }
1100#ifdef USE_SNMP
1101 if (gcfg->g_snmp)
1102 agent_shutdown();
1103#endif /* USE_SNMP */
1104}
1105
8482abe9
VB
1106struct intint { int a; int b; };
1107static const struct intint filters[] = {
1108 { 0, 0 },
1109 { 1, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1110 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1111 { 2, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO },
1112 { 3, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1113 { 4, SMART_INCOMING_FILTER | SMART_OUTGOING_FILTER },
1114 { 5, SMART_INCOMING_FILTER },
1115 { 6, SMART_OUTGOING_FILTER },
1116 { 7, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1117 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1118 { 8, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH },
1119 { 9, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1120 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1121 { 10, SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1122 { 11, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH },
1123 { 12, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1124 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1125 { 13, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_NEIGH |
1126 SMART_OUTGOING_FILTER },
1127 { 14, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1128 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1129 { 15, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO |
1130 SMART_OUTGOING_FILTER },
1131 { 16, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1132 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1133 { 17, SMART_INCOMING_FILTER | SMART_INCOMING_ONE_PROTO | SMART_INCOMING_ONE_NEIGH |
1134 SMART_OUTGOING_FILTER },
1135 { 18, SMART_INCOMING_FILTER |
1136 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_NEIGH },
1137 { 19, SMART_INCOMING_FILTER |
1138 SMART_OUTGOING_FILTER | SMART_OUTGOING_ONE_PROTO },
1139 { -1, 0 }
1140};
1141
43c02e7b 1142int
2acc1418 1143lldpd_main(int argc, char *argv[])
43c02e7b
VB
1144{
1145 struct lldpd *cfg;
77507b69 1146 struct lldpd_chassis *lchassis;
e809a587
VB
1147 int ch, debug = 0;
1148#ifdef USE_SNMP
1149 int snmp = 0;
bbea66e1 1150 char *agentx = NULL; /* AgentX socket */
e809a587 1151#endif
e6b36c87 1152 char *mgmtp = NULL, *mgmtp6 = NULL;
5339e725 1153 char *cidp = NULL;
ba85f9f4 1154 char *interfaces = NULL;
993a6e50 1155 char *popt, opts[] =
e6b36c87 1156 "H:hkrdxX:m:4:6:I:C:p:M:P:S:i@ ";
de1b1b3a 1157 int i, found, advertise_version = 1;
89840df0 1158#ifdef ENABLE_LLDPMED
e809a587 1159 int lldpmed = 0, noinventory = 0;
89840df0 1160#endif
d4afb919
VB
1161 char *descr_override = NULL;
1162 char *platform_override = NULL;
c036b15d 1163 char *lsb_release = NULL;
8482abe9 1164 int smart = 15;
537a8043 1165 int receiveonly = 0;
43c02e7b
VB
1166
1167 saved_argv = argv;
1168
1169 /*
1170 * Get and parse command line options
1171 */
f0bd3505 1172 popt = strchr(opts, '@');
0c877af0 1173 for (i=0; protos[i].mode != 0; i++)
43c02e7b 1174 *(popt++) = protos[i].arg;
43c02e7b
VB
1175 *popt = '\0';
1176 while ((ch = getopt(argc, argv, opts)) != -1) {
1177 switch (ch) {
b162b740
VB
1178 case 'h':
1179 usage();
1180 break;
43c02e7b
VB
1181 case 'd':
1182 debug++;
1183 break;
537a8043
VB
1184 case 'r':
1185 receiveonly = 1;
1186 break;
e6b36c87
JV
1187 case 'm': /* fall through */
1188 case '4':
43c02e7b
VB
1189 mgmtp = optarg;
1190 break;
e6b36c87
JV
1191 case '6':
1192 mgmtp6 = optarg;
ba85f9f4
VB
1193 case 'I':
1194 interfaces = optarg;
1195 break;
5339e725
VB
1196 case 'C':
1197 cidp = optarg;
1198 break;
de1b1b3a
VB
1199 case 'k':
1200 advertise_version = 0;
1201 break;
e809a587 1202#ifdef ENABLE_LLDPMED
115ff55c 1203 case 'M':
89840df0 1204 lldpmed = atoi(optarg);
e809a587
VB
1205 if ((lldpmed < 1) || (lldpmed > 4)) {
1206 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1207 usage();
e809a587 1208 }
89840df0 1209 break;
e809a587 1210 case 'i':
e809a587 1211 noinventory = 1;
115ff55c 1212 break;
e809a587 1213#else
115ff55c
VB
1214 case 'M':
1215 case 'i':
e809a587
VB
1216 fprintf(stderr, "LLDP-MED support is not built-in\n");
1217 usage();
e809a587 1218 break;
115ff55c 1219#endif
e809a587 1220#ifdef USE_SNMP
bbea66e1
V
1221 case 'x':
1222 snmp = 1;
1223 break;
1224 case 'X':
43c02e7b 1225 snmp = 1;
bbea66e1
V
1226 agentx = optarg;
1227 break;
e809a587 1228#else
bbea66e1
V
1229 case 'x':
1230 case 'X':
e809a587
VB
1231 fprintf(stderr, "SNMP support is not built-in\n");
1232 usage();
1233#endif
43c02e7b 1234 break;
40ce835b
ST
1235 case 'S':
1236 descr_override = strdup(optarg);
1237 break;
d4afb919
VB
1238 case 'P':
1239 platform_override = strdup(optarg);
1240 break;
42b39485 1241 case 'H':
8482abe9 1242 smart = atoi(optarg);
42b39485 1243 break;
43c02e7b
VB
1244 default:
1245 found = 0;
1246 for (i=0; protos[i].mode != 0; i++) {
43c02e7b 1247 if (ch == protos[i].arg) {
0c877af0
VB
1248 protos[i].enabled++;
1249 /* When an argument enable
1250 several protocols, only the
1251 first one can be forced. */
1252 if (found && protos[i].enabled > 1)
1253 protos[i].enabled = 1;
43c02e7b
VB
1254 found = 1;
1255 }
1256 }
1257 if (!found)
1258 usage();
1259 }
1260 }
8482abe9
VB
1261
1262 /* Set correct smart mode */
1263 for (i=0; (filters[i].a != -1) && (filters[i].a != smart); i++);
1264 if (filters[i].a == -1) {
1265 fprintf(stderr, "Incorrect mode for -H\n");
1266 usage();
1267 }
1268 smart = filters[i].b;
115ff55c 1269
6bb9c4e0 1270 log_init(debug, __progname);
4b9a5a23 1271 tzset(); /* Get timezone info before chroot */
a2993d83 1272
eac2f38a
VB
1273 if (!debug) {
1274 int pid;
1275 char *spid;
1276 if (daemon(0, 0) != 0)
1277 fatal("failed to detach daemon");
1278 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1279 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
1280 fatal("unable to open pid file " LLDPD_PID_FILE);
1281 if (asprintf(&spid, "%d\n", getpid()) == -1)
1282 fatal("unable to create pid file " LLDPD_PID_FILE);
1283 if (write(pid, spid, strlen(spid)) == -1)
1284 fatal("unable to write pid file " LLDPD_PID_FILE);
1285 free(spid);
1286 close(pid);
1287 }
1288
ae87586a
MT
1289 /* Try to read system information from /etc/os-release if possible.
1290 Fall back to lsb_release for compatibility. */
1291 lsb_release = lldpd_get_os_release();
1292 if (!lsb_release) {
1293 lsb_release = lldpd_get_lsb_release();
1294 }
c036b15d 1295
a2993d83 1296 priv_init(PRIVSEP_CHROOT);
43c02e7b 1297
43c02e7b
VB
1298 if ((cfg = (struct lldpd *)
1299 calloc(1, sizeof(struct lldpd))) == NULL)
1300 fatal(NULL);
1301
766f32b3 1302 cfg->g_mgmt_pattern = mgmtp;
e6b36c87 1303 cfg->g_mgmt_pattern6 = mgmtp6;
5339e725 1304 cfg->g_cid_pattern = cidp;
ba85f9f4 1305 cfg->g_interfaces = interfaces;
42b39485 1306 cfg->g_smart = smart;
537a8043 1307 cfg->g_receiveonly = receiveonly;
43c02e7b
VB
1308
1309 /* Get ioctl socket */
1310 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
1311 fatal("failed to get ioctl socket");
1312 cfg->g_delay = LLDPD_TX_DELAY;
1313
c036b15d 1314 /* Description */
bf89e7e0 1315 if (!(cfg->g_advertise_version = advertise_version) && lsb_release)
c036b15d
VB
1316 /* Remove the \n */
1317 lsb_release[strlen(lsb_release) - 1] = '\0';
1318 cfg->g_lsb_release = lsb_release;
40ce835b
ST
1319 if (descr_override)
1320 cfg->g_descr_override = descr_override;
1321
d4afb919
VB
1322 if (platform_override)
1323 cfg->g_platform_override = platform_override;
1324
43c02e7b 1325 /* Set system capabilities */
77507b69
VB
1326 if ((lchassis = (struct lldpd_chassis*)
1327 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
1328 fatal(NULL);
1329 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 1330 LLDP_CAP_ROUTER;
e6b36c87 1331 TAILQ_INIT(&lchassis->c_mgmt);
89840df0
VB
1332#ifdef ENABLE_LLDPMED
1333 if (lldpmed > 0) {
1334 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
1335 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1336 lchassis->c_med_type = lldpmed;
1337 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
4c0d2715
VB
1338 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION |
1339 LLDPMED_CAP_POLICY | LLDPMED_CAP_MDI_PSE | LLDPMED_CAP_MDI_PD;
740593ff 1340 cfg->g_noinventory = noinventory;
42bddd41
VB
1341 } else
1342 cfg->g_noinventory = 1;
89840df0 1343#endif
43c02e7b
VB
1344
1345 /* Set TTL */
77507b69 1346 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
1347
1348 cfg->g_protocols = protos;
43c02e7b 1349 for (i=0; protos[i].mode != 0; i++)
0c877af0
VB
1350 if (protos[i].enabled > 1)
1351 LLOG_INFO("protocol %s enabled and forced", protos[i].name);
1352 else if (protos[i].enabled)
43c02e7b 1353 LLOG_INFO("protocol %s enabled", protos[i].name);
0c877af0 1354 else
43c02e7b 1355 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
1356
1357 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
1358 TAILQ_INIT(&cfg->g_chassis);
1359 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
9898ac07 1360 lchassis->c_refcount++; /* We should always keep a reference to local chassis */
43c02e7b 1361
77d7090e
VB
1362 TAILQ_INIT(&cfg->g_callbacks);
1363
43c02e7b
VB
1364#ifdef USE_SNMP
1365 if (snmp) {
1366 cfg->g_snmp = 1;
bbea66e1 1367 agent_init(cfg, agentx, debug);
43c02e7b
VB
1368 }
1369#endif /* USE_SNMP */
1370
1371 /* Create socket */
6f1046d1 1372 if ((cfg->g_ctl = priv_ctl_create()) == -1)
b5562b23 1373 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
77d7090e
VB
1374 if (lldpd_callback_add(cfg, cfg->g_ctl, ctl_accept, NULL) != 0)
1375 fatalx("unable to add callback for control socket");
43c02e7b 1376
43c02e7b
VB
1377 gcfg = cfg;
1378 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1379 close(cfg->g_ctl);
1380 priv_ctl_cleanup();
43c02e7b
VB
1381 fatal("unable to set exit function");
1382 }
43c02e7b
VB
1383
1384 /* Signal handling */
b5562b23 1385 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1386 signal(SIGINT, lldpd_shutdown);
1387 signal(SIGTERM, lldpd_shutdown);
1388
1389 for (;;)
1390 lldpd_loop(cfg);
1391
1392 return (0);
1393}