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