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