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