]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Adapt lldpctl to display received systems for each port.
[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
77507b69 110struct lldpd_hardware *lldpd_hardware_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);
77507b69
VB
242 if (all) {
243 free(port->p_lastframe);
244 if (port->p_chassis) /* chassis may not have been attributed, yet */
245 port->p_chassis->c_refcount--;
a0edeaf8 246 free(port);
77507b69 247 }
43c02e7b
VB
248}
249
250void
77507b69 251lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
43c02e7b 252{
89840df0
VB
253#ifdef ENABLE_LLDPMED
254 free(chassis->c_med_hw);
517d524b 255 free(chassis->c_med_sw);
89840df0
VB
256 free(chassis->c_med_fw);
257 free(chassis->c_med_sn);
258 free(chassis->c_med_manuf);
259 free(chassis->c_med_model);
260 free(chassis->c_med_asset);
261#endif
43c02e7b
VB
262 free(chassis->c_id);
263 free(chassis->c_name);
264 free(chassis->c_descr);
77507b69
VB
265 if (all)
266 free(chassis);
43c02e7b
VB
267}
268
269void
77507b69 270lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
43c02e7b 271{
77507b69
VB
272 struct lldpd_port *port, *port_next;
273 int del;
274 for (port = TAILQ_FIRST(&hardware->h_rports);
275 port != NULL;
276 port = port_next) {
277 port_next = TAILQ_NEXT(port, p_entries);
278 del = all;
279 if (!del &&
280 (time(NULL) - port->p_lastupdate > port->p_chassis->c_ttl)) {
281 hardware->h_rx_ageout_cnt++;
282 del = 1;
283 }
284 if (del) {
285 TAILQ_REMOVE(&hardware->h_rports, port, p_entries);
286 lldpd_port_cleanup(port, 1);
287 }
43c02e7b 288 }
43c02e7b
VB
289}
290
d9be8ea0
VB
291void
292lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
293{
a0edeaf8 294 lldpd_port_cleanup(&hardware->h_lport, 1);
d9be8ea0
VB
295 free(hardware);
296}
297
43c02e7b
VB
298void
299lldpd_cleanup(struct lldpd *cfg)
300{
301 struct lldpd_hardware *hardware, *hardware_next;
302
303 for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL;
304 hardware = hardware_next) {
305 hardware_next = TAILQ_NEXT(hardware, h_entries);
306 if (hardware->h_flags == 0) {
307 TAILQ_REMOVE(&cfg->g_hardware, hardware, h_entries);
308 lldpd_iface_close(cfg, hardware);
77507b69 309 lldpd_remote_cleanup(cfg, hardware, 1);
d9be8ea0 310 lldpd_hardware_cleanup(hardware);
77507b69
VB
311 } else
312 lldpd_remote_cleanup(cfg, hardware, 0);
43c02e7b
VB
313 }
314}
315
8888d191 316static struct lldpd_hardware *
77507b69 317lldpd_hardware_add(struct lldpd *cfg, struct ifaddrs *ifa)
43c02e7b 318{
a1347cd8 319#if defined (ENABLE_DOT1) || defined (ENABLE_DOT3)
43c02e7b 320 struct ifaddrs *oifap, *oifa;
a1347cd8 321#endif
43c02e7b
VB
322 struct lldpd_hardware *hardware;
323 struct lldpd_port *port;
a1347cd8 324#ifdef ENABLE_DOT1
43c02e7b 325 struct lldpd_vlan *vlan;
43c02e7b 326 struct vlan_ioctl_args ifv;
a1347cd8
VB
327#endif
328#ifdef ENABLE_DOT3
43c02e7b 329 struct ethtool_cmd ethc;
a1347cd8 330#endif
43c02e7b
VB
331 u_int8_t *lladdr;
332
333 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
334 if (strcmp(hardware->h_ifname, ifa->ifa_name) == 0)
335 break;
336 }
337
338 if (hardware == NULL) {
339 if ((hardware = (struct lldpd_hardware *)
340 calloc(1, sizeof(struct lldpd_hardware))) == NULL)
341 return (NULL);
342 hardware->h_raw = -1;
77507b69
VB
343 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
344 TAILQ_INIT(&hardware->h_rports);
740593ff 345#ifdef ENABLE_LLDPMED
77507b69 346 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
42bddd41
VB
347 hardware->h_lport.p_med_cap_enabled = LLDPMED_CAP_CAP;
348 if (!cfg->g_noinventory)
349 hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_IV;
350 }
740593ff 351#endif
a1347cd8 352#ifdef ENABLE_DOT1
43c02e7b
VB
353 TAILQ_INIT(&hardware->h_lport.p_vlans);
354 } else {
a0edeaf8 355 lldpd_port_cleanup(&hardware->h_lport, 0);
a1347cd8 356#endif
43c02e7b
VB
357 }
358
359 port = &hardware->h_lport;
360 hardware->h_flags = ifa->ifa_flags;
361
362 strlcpy(hardware->h_ifname, ifa->ifa_name, sizeof(hardware->h_ifname));
363 lladdr = (u_int8_t*)(((struct sockaddr_ll *)ifa->ifa_addr)->sll_addr);
364 memcpy(&hardware->h_lladdr, lladdr, sizeof(hardware->h_lladdr));
1d291522 365 iface_get_permanent_mac(cfg, hardware);
43c02e7b 366 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
4395f9c1
VB
367 if ((port->p_id = calloc(1, sizeof(hardware->h_lladdr))) == NULL)
368 fatal(NULL);
369 memcpy(port->p_id, hardware->h_lladdr, sizeof(hardware->h_lladdr));
43c02e7b 370 port->p_id_len = sizeof(hardware->h_lladdr);
4f758bc0 371 port->p_descr = strdup(hardware->h_ifname);
43c02e7b 372
77507b69 373 if (LOCAL_CHASSIS(cfg)->c_id == NULL) {
43c02e7b 374 /* Use the first port's l2 addr as the chassis ID */
77507b69
VB
375 if ((LOCAL_CHASSIS(cfg)->c_id =
376 malloc(sizeof(hardware->h_lladdr))) == NULL)
43c02e7b 377 fatal(NULL);
77507b69
VB
378 LOCAL_CHASSIS(cfg)->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
379 LOCAL_CHASSIS(cfg)->c_id_len = sizeof(hardware->h_lladdr);
380 memcpy(LOCAL_CHASSIS(cfg)->c_id,
43c02e7b
VB
381 hardware->h_lladdr, sizeof(hardware->h_lladdr));
382 }
383
384 /* Get VLANS and aggregation status */
a1347cd8 385#if defined (ENABLE_DOT3) || defined (ENABLE_DOT1)
43c02e7b 386 if (getifaddrs(&oifap) != 0)
77507b69 387 fatal("lldpd_hardware_add: failed to get interface list");
43c02e7b 388 for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
a1347cd8 389#ifdef ENABLE_DOT1
43c02e7b
VB
390 /* Check if we already have checked this one */
391 int skip = 0;
392 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
bac91719 393 if (strcmp(vlan->v_name, oifa->ifa_name) == 0) {
43c02e7b 394 skip = 1;
bac91719
VB
395 break;
396 }
43c02e7b
VB
397 }
398 if (skip) continue;
a1347cd8 399#endif
43c02e7b
VB
400
401 /* Aggregation check */
a1347cd8 402#ifdef ENABLE_DOT3
beeaefa3 403 if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL))
43c02e7b 404 port->p_aggregid = if_nametoindex(oifa->ifa_name);
a1347cd8
VB
405#endif
406
407#ifdef ENABLE_DOT1
43c02e7b
VB
408 /* VLAN check */
409 memset(&ifv, 0, sizeof(ifv));
410 ifv.cmd = GET_VLAN_REALDEV_NAME_CMD;
411 strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
412 if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) &&
beeaefa3 413 ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) ||
bac91719 414 (iface_is_bridged_to(cfg, hardware->h_ifname, ifv.u.device2)) ||
43c02e7b
VB
415 (strncmp(hardware->h_ifname, ifv.u.device2, sizeof(ifv.u.device2)) == 0))) {
416 if ((vlan = (struct lldpd_vlan *)
417 calloc(1, sizeof(struct lldpd_vlan))) == NULL)
418 continue;
249644a4 419 if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) {
43c02e7b
VB
420 free(vlan);
421 continue;
422 }
423 memset(&ifv, 0, sizeof(ifv));
424 ifv.cmd = GET_VLAN_VID_CMD;
425 strlcpy(ifv.device1, oifa->ifa_name, sizeof(ifv.device1));
426 if (ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) < 0) {
427 /* Dunno what happened */
428 free(vlan->v_name);
429 free(vlan);
430 } else {
431 vlan->v_vid = ifv.u.VID;
432 TAILQ_INSERT_TAIL(&port->p_vlans, vlan, v_entries);
433 }
434 }
a1347cd8 435#endif
43c02e7b
VB
436 }
437 freeifaddrs(oifap);
a1347cd8 438#endif
43c02e7b 439
a1347cd8 440#ifdef ENABLE_DOT3
43c02e7b 441 /* MAC/PHY */
4afe659e 442 if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
43c02e7b
VB
443 int j;
444 int advertised_ethtool_to_rfc3636[][2] = {
445 {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T},
446 {ADVERTISED_10baseT_Full, LLDP_DOT3_LINK_AUTONEG_10BASET_FD},
447 {ADVERTISED_100baseT_Half, LLDP_DOT3_LINK_AUTONEG_100BASE_TX},
448 {ADVERTISED_100baseT_Full, LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD},
449 {ADVERTISED_1000baseT_Half, LLDP_DOT3_LINK_AUTONEG_1000BASE_T},
450 {ADVERTISED_1000baseT_Full, LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD},
451 {ADVERTISED_10000baseT_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
452 {ADVERTISED_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_PAUSE},
453 {ADVERTISED_Asym_Pause, LLDP_DOT3_LINK_AUTONEG_FDX_APAUSE},
454 {ADVERTISED_2500baseX_Full, LLDP_DOT3_LINK_AUTONEG_OTHER},
455 {0,0}};
456
457 port->p_autoneg_support = (ethc.supported & SUPPORTED_Autoneg) ? 1 : 0;
458 port->p_autoneg_enabled = (ethc.autoneg == AUTONEG_DISABLE) ? 0 : 1;
459 for (j=0; advertised_ethtool_to_rfc3636[j][0]; j++) {
460 if (ethc.advertising & advertised_ethtool_to_rfc3636[j][0])
461 port->p_autoneg_advertised |= advertised_ethtool_to_rfc3636[j][1];
462 }
463 switch (ethc.speed) {
464 case SPEED_10:
465 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
466 LLDP_DOT3_MAU_10BASETFD : LLDP_DOT3_MAU_10BASETHD;
467 if (ethc.port == PORT_BNC) port->p_mau_type = LLDP_DOT3_MAU_10BASE2;
468 if (ethc.port == PORT_FIBRE)
469 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
470 LLDP_DOT3_MAU_10BASEFLDF : LLDP_DOT3_MAU_10BASEFLHD;
471 break;
472 case SPEED_100:
473 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
474 LLDP_DOT3_MAU_100BASETXFD : LLDP_DOT3_MAU_100BASETXHD;
475 if (ethc.port == PORT_BNC)
476 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
477 LLDP_DOT3_MAU_100BASET2DF : LLDP_DOT3_MAU_100BASET2HD;
478 if (ethc.port == PORT_FIBRE)
479 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
480 LLDP_DOT3_MAU_100BASEFXFD : LLDP_DOT3_MAU_100BASEFXHD;
481 break;
482 case SPEED_1000:
483 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
484 LLDP_DOT3_MAU_1000BASETFD : LLDP_DOT3_MAU_1000BASETHD;
485 if (ethc.port == PORT_FIBRE)
486 port->p_mau_type = (ethc.duplex == DUPLEX_FULL) ? \
487 LLDP_DOT3_MAU_1000BASEXFD : LLDP_DOT3_MAU_1000BASEXHD;
488 break;
489 case SPEED_10000:
490 port->p_mau_type = (ethc.port == PORT_FIBRE) ? \
491 LLDP_DOT3_MAU_10GIGBASEX : LLDP_DOT3_MAU_10GIGBASER;
492 break;
493 }
494 if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
b5562b23 495 } else
28aca6a0 496 LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname);
a1347cd8 497#endif
43c02e7b
VB
498
499 if (!INTERFACE_OPENED(hardware)) {
500
501 if (lldpd_iface_init(cfg, hardware) != 0) {
b5562b23 502 LLOG_WARN("unable to initialize %s", hardware->h_ifname);
d9be8ea0 503 lldpd_hardware_cleanup(hardware);
43c02e7b
VB
504 return (NULL);
505 }
506
507 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
508 }
509
510 return (hardware);
511}
512
8888d191 513static int
43c02e7b
VB
514lldpd_guess_type(struct lldpd *cfg, char *frame, int s)
515{
516 int i;
517 if (s < ETH_ALEN)
518 return -1;
519 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
520 if (!cfg->g_protocols[i].enabled)
521 continue;
522 if (cfg->g_protocols[i].guess == NULL) {
523 if (memcmp(frame, cfg->g_protocols[i].mac, ETH_ALEN) == 0)
524 return cfg->g_protocols[i].mode;
525 } else {
526 if (cfg->g_protocols[i].guess(frame, s))
527 return cfg->g_protocols[i].mode;
528 }
529 }
530 return -1;
531}
532
8888d191 533static void
43c02e7b 534lldpd_decode(struct lldpd *cfg, char *frame, int s,
0bc32943 535 struct lldpd_hardware *hardware)
43c02e7b 536{
77507b69
VB
537 int i, result;
538 struct lldpd_chassis *chassis, *ochassis = NULL;
539 struct lldpd_port *port, *oport = NULL;
43c02e7b
VB
540 int guess = LLDPD_MODE_LLDP;
541
50a89ca7 542 /* Discard VLAN frames */
a8105c1b
VB
543 if ((s >= sizeof(struct ethhdr)) &&
544 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
50a89ca7
VB
545 return;
546
77507b69
VB
547 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
548 if ((oport->p_lastframe != NULL) &&
549 (oport->p_lastframe->size == s) &&
550 (memcmp(oport->p_lastframe->frame, frame, s) == 0)) {
551 /* Already received the same frame */
552 oport->p_lastupdate = time(NULL);
553 return;
554 }
43c02e7b
VB
555 }
556
f2dcb180
VB
557 guess = lldpd_guess_type(cfg, frame, s);
558 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
559 if (!cfg->g_protocols[i].enabled)
560 continue;
561 if (cfg->g_protocols[i].mode == guess) {
562 if ((result = cfg->g_protocols[i].decode(cfg, frame,
563 s, hardware, &chassis, &port)) == -1)
564 return;
77507b69
VB
565 chassis->c_protocol = port->p_protocol =
566 cfg->g_protocols[i].mode;
f2dcb180 567 break;
43c02e7b 568 }
f2dcb180
VB
569 }
570 if (cfg->g_protocols[i].mode == 0) {
571 LLOG_INFO("unable to guess frame type");
43c02e7b 572 return;
f2dcb180 573 }
43c02e7b 574
77507b69
VB
575 /* Do we already have the same MSAP somewhere? */
576 TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) {
577 if ((port->p_protocol == oport->p_protocol) &&
578 (port->p_id_subtype == oport->p_id_subtype) &&
579 (port->p_id_len == oport->p_id_len) &&
580 (memcmp(port->p_id, oport->p_id, port->p_id_len) == 0) &&
581 (chassis->c_id_subtype == oport->p_chassis->c_id_subtype) &&
582 (chassis->c_id_len == oport->p_chassis->c_id_len) &&
583 (memcmp(chassis->c_id, oport->p_chassis->c_id,
584 chassis->c_id_len) == 0)) {
585 ochassis = oport->p_chassis;
586 break;
587 }
43c02e7b 588 }
77507b69
VB
589 /* No, but do we already know the system? */
590 if (!oport) {
591 TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) {
592 if ((chassis->c_protocol == ochassis->c_protocol) &&
593 (chassis->c_id_subtype == ochassis->c_id_subtype) &&
594 (chassis->c_id_len == ochassis->c_id_len) &&
595 (memcmp(chassis->c_id, ochassis->c_id,
596 chassis->c_id_len) == 0))
597 break;
43c02e7b 598 }
43c02e7b 599 }
43c02e7b 600
77507b69
VB
601 if (oport) {
602 /* The port is known, remove it before adding it back */
603 TAILQ_REMOVE(&hardware->h_rports, oport, p_entries);
604 lldpd_port_cleanup(oport, 1);
605 }
606 if (ochassis) {
607 /* Chassis is known, replace values. Hackish */
608 chassis->c_refcount = ochassis->c_refcount;
609 chassis->c_index = ochassis->c_index;
610 memcpy(&chassis->c_entries, &ochassis->c_entries,
611 sizeof(chassis->c_entries));
612 lldpd_chassis_cleanup(ochassis, 0);
613 memcpy(ochassis, chassis, sizeof(struct lldpd_chassis));
614 free(chassis);
615 chassis = ochassis;
616 } else {
617 /* Chassis not known, add it */
618 chassis->c_index = ++cfg->g_lastrid;
619 port->p_chassis = chassis;
620 chassis->c_refcount = 0;
621 TAILQ_INSERT_TAIL(&cfg->g_chassis, chassis, c_entries);
622 }
623 /* Add port */
624 port->p_lastchange = port->p_lastupdate = time(NULL);
625 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
626 sizeof(int))) != NULL) {
627 port->p_lastframe->size = s;
628 memcpy(port->p_lastframe->frame, frame, s);
629 }
630 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
631 port->p_chassis = chassis;
632 port->p_chassis->c_refcount++;
43c02e7b
VB
633 return;
634}
635
8888d191 636static void
43c02e7b
VB
637lldpd_recv_all(struct lldpd *cfg)
638{
639 struct lldpd_hardware *hardware;
640 struct lldpd_client *client, *client_next;
641 fd_set rfds;
642 struct timeval tv;
643 struct sockaddr_ll from;
644 socklen_t fromlen;
43c02e7b
VB
645#ifdef USE_SNMP
646 int fakeblock = 0;
647 struct timeval *tvp = &tv;
648#endif
0bc32943 649 int rc, nfds, n;
43c02e7b
VB
650 char *buffer;
651
652 do {
653 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
654 if (tv.tv_sec < 0)
655 tv.tv_sec = LLDPD_TX_DELAY;
656 if (tv.tv_sec >= cfg->g_delay)
657 tv.tv_sec = cfg->g_delay;
658 tv.tv_usec = 0;
659
660 FD_ZERO(&rfds);
661 nfds = -1;
662
663 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
664 /* Ignore if interface is down */
e70b76f9
VB
665 if (((hardware->h_flags & IFF_UP) == 0) ||
666 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
667 continue;
668 FD_SET(hardware->h_raw, &rfds);
669 if (nfds < hardware->h_raw)
670 nfds = hardware->h_raw;
43c02e7b
VB
671 }
672 TAILQ_FOREACH(client, &cfg->g_clients, next) {
673 FD_SET(client->fd, &rfds);
674 if (nfds < client->fd)
675 nfds = client->fd;
676 }
677 FD_SET(cfg->g_ctl, &rfds);
678 if (nfds < cfg->g_ctl)
679 nfds = cfg->g_ctl;
680
681#ifdef USE_SNMP
682 if (cfg->g_snmp)
683 snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
684#endif /* USE_SNMP */
685 if (nfds == -1) {
686 sleep(cfg->g_delay);
687 return;
688 }
689
690 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
691 if (rc == -1) {
692 if (errno == EINTR)
693 continue;
694 LLOG_WARN("failure on select");
695 break;
696 }
697#ifdef USE_SNMP
698 if (cfg->g_snmp) {
699 if (rc > 0)
700 snmp_read(&rfds);
701 else if (rc == 0)
702 snmp_timeout();
703 }
704#endif /* USE_SNMP */
705 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
706 /* We could have received something on _real_
707 * interface. However, even in this case, this could be
708 * just an outgoing packet. We will try to handle both
709 * cases, but maybe not in the same select. */
0bc32943 710 if (FD_ISSET(hardware->h_raw, &rfds)) {
43c02e7b
VB
711 if ((buffer = (char *)malloc(
712 hardware->h_mtu)) == NULL) {
713 LLOG_WARN("failed to alloc reception buffer");
714 continue;
715 }
716 fromlen = sizeof(from);
717 if ((n = recvfrom(
0bc32943 718 hardware->h_raw,
43c02e7b
VB
719 buffer,
720 hardware->h_mtu, 0,
721 (struct sockaddr *)&from,
722 &fromlen)) == -1) {
723 LLOG_WARN("error while receiving frame on %s",
724 hardware->h_ifname);
725 hardware->h_rx_discarded_cnt++;
726 free(buffer);
727 continue;
728 }
729 if (from.sll_pkttype == PACKET_OUTGOING) {
730 free(buffer);
731 continue;
732 }
43c02e7b 733 hardware->h_rx_cnt++;
0bc32943 734 lldpd_decode(cfg, buffer, n, hardware);
43c02e7b
VB
735 free(buffer);
736 }
737
738 }
739 if (FD_ISSET(cfg->g_ctl, &rfds)) {
740 if (ctl_accept(cfg, cfg->g_ctl) == -1)
741 LLOG_WARN("unable to accept new client");
742 }
743 for (client = TAILQ_FIRST(&cfg->g_clients);
744 client != NULL;
745 client = client_next) {
746 client_next = TAILQ_NEXT(client, next);
747 if (FD_ISSET(client->fd, &rfds)) {
748 /* Got a message */
749 if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
750 NULL) {
751 LLOG_WARN("failed to alloc reception buffer");
752 continue;
753 }
754 if ((n = recv(client->fd, buffer,
755 MAX_HMSGSIZE, 0)) == -1) {
756 LLOG_WARN("error while receiving message");
757 free(buffer);
758 continue;
759 }
760 if (n > 0)
a552a72e 761 client_handle_client(cfg, client, buffer, n);
43c02e7b
VB
762 else
763 ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
764 free(buffer);
765 }
766 }
767
768#ifdef USE_SNMP
769 if (cfg->g_snmp) {
770 run_alarms();
771 netsnmp_check_outstanding_agent_requests();
772 }
773#endif /* USE_SNMP */
774 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
775}
776
8888d191 777static void
43c02e7b
VB
778lldpd_send_all(struct lldpd *cfg)
779{
780 struct lldpd_hardware *hardware;
77507b69
VB
781 struct lldpd_port *port;
782 int i, sent = 0;
f7db0dd8 783
43c02e7b
VB
784 cfg->g_lastsent = time(NULL);
785 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
786 /* Ignore if interface is down */
e70b76f9
VB
787 if (((hardware->h_flags & IFF_UP) == 0) ||
788 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
789 continue;
790
791 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
792 if (!cfg->g_protocols[i].enabled)
793 continue;
77507b69
VB
794 /* We send only if we have at least one remote system
795 * speaking this protocol */
796 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
797 if (port->p_protocol ==
798 cfg->g_protocols[i].mode) {
799 cfg->g_protocols[i].send(cfg,
800 hardware);
801 sent = 1;
802 break;
803 }
804 }
43c02e7b 805 }
77507b69
VB
806
807 if (!sent)
808 /* Nothing was sent for this port, let's speak LLDP */
809 cfg->g_protocols[0].send(cfg,
810 hardware);
43c02e7b
VB
811 }
812}
813
89840df0 814#ifdef ENABLE_LLDPMED
8888d191 815static void
89840df0
VB
816lldpd_med(struct lldpd_chassis *chassis)
817{
818 free(chassis->c_med_hw);
819 free(chassis->c_med_fw);
820 free(chassis->c_med_sn);
821 free(chassis->c_med_manuf);
822 free(chassis->c_med_model);
823 free(chassis->c_med_asset);
824 chassis->c_med_hw = dmi_hw();
825 chassis->c_med_fw = dmi_fw();
826 chassis->c_med_sn = dmi_sn();
827 chassis->c_med_manuf = dmi_manuf();
828 chassis->c_med_model = dmi_model();
829 chassis->c_med_asset = dmi_asset();
830}
831#endif
832
8888d191 833static void
43c02e7b
VB
834lldpd_loop(struct lldpd *cfg)
835{
836 struct ifaddrs *ifap, *ifa;
837 struct sockaddr_ll *sdl;
838 struct lldpd_hardware *hardware;
839 int f;
840 char status;
841 struct utsname *un;
b5562b23 842 char *hp;
43c02e7b
VB
843
844 /* Set system name and description */
845 if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL)
846 fatal(NULL);
847 if (uname(un) != 0)
848 fatal("failed to get system information");
b5562b23 849 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 850 fatal("failed to get system name");
77507b69
VB
851 free(LOCAL_CHASSIS(cfg)->c_name);
852 free(LOCAL_CHASSIS(cfg)->c_descr);
853 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 854 fatal(NULL);
77507b69 855 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
43c02e7b
VB
856 un->sysname, un->release, un->version, un->machine) == -1)
857 fatal("failed to set system description");
43c02e7b
VB
858
859 /* Check forwarding */
77507b69 860 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
b5562b23 861 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 862 if ((read(f, &status, 1) == 1) && (status == '1')) {
77507b69 863 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 864 }
43c02e7b
VB
865 close(f);
866 }
89840df0 867#ifdef ENABLE_LLDPMED
77507b69
VB
868 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
869 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
870 lldpd_med(LOCAL_CHASSIS(cfg));
871 free(LOCAL_CHASSIS(cfg)->c_med_sw);
872 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un->release);
89840df0
VB
873#endif
874 free(un);
43c02e7b
VB
875
876 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
877 hardware->h_flags = 0;
878
879 if (getifaddrs(&ifap) != 0)
880 fatal("lldpd_loop: failed to get interface list");
881
77507b69 882 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
43c02e7b 883 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
77507b69 884 if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr == INADDR_ANY)
43c02e7b
VB
885 /* Get management address, if available */
886 if ((ifa->ifa_addr != NULL) &&
887 (ifa->ifa_addr->sa_family == AF_INET)) {
888 struct sockaddr_in *sa;
889 sa = (struct sockaddr_in *)ifa->ifa_addr;
890 if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) &&
891 (cfg->g_mgmt_pattern == NULL)) {
77507b69 892 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
43c02e7b
VB
893 &sa->sin_addr,
894 sizeof(struct in_addr));
77507b69 895 LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name);
43c02e7b
VB
896 }
897 else if (cfg->g_mgmt_pattern != NULL) {
898 char *ip;
899 ip = inet_ntoa(sa->sin_addr);
900 if (fnmatch(cfg->g_mgmt_pattern,
901 ip, 0) == 0) {
77507b69 902 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
43c02e7b
VB
903 &sa->sin_addr,
904 sizeof(struct in_addr));
77507b69 905 LOCAL_CHASSIS(cfg)->c_mgmt_if =
43c02e7b
VB
906 if_nametoindex(ifa->ifa_name);
907 }
908 }
909 }
910
a5e5458f
VB
911 if (ifa->ifa_addr == NULL ||
912 ifa->ifa_addr->sa_family != PF_PACKET)
913 continue;
914
99f85c00
VB
915 sdl = (struct sockaddr_ll *)ifa->ifa_addr;
916 if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
917 continue;
918
43c02e7b 919 if (iface_is_bridge(cfg, ifa->ifa_name)) {
77507b69 920 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
43c02e7b
VB
921 continue;
922 }
923
50a89ca7 924 if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
43c02e7b
VB
925 (iface_is_bond(cfg, ifa->ifa_name)))
926 continue;
927
b29c23cc 928 if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
43c02e7b
VB
929 continue;
930
43c02e7b 931 if (iface_is_wireless(cfg, ifa->ifa_name))
77507b69 932 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
43c02e7b 933
77507b69 934 if (lldpd_hardware_add(cfg, ifa) == NULL)
43c02e7b
VB
935 LLOG_WARNX("failed to allocate port %s, skip it",
936 ifa->ifa_name);
937 }
938
939 freeifaddrs(ifap);
940
941 lldpd_cleanup(cfg);
942
943 lldpd_send_all(cfg);
944 lldpd_recv_all(cfg);
945}
946
8888d191 947static void
43c02e7b
VB
948lldpd_shutdown(int sig)
949{
950 LLOG_INFO("signal received, exiting");
951 exit(0);
952}
953
954/* For signal handling */
8888d191 955static struct lldpd *gcfg = NULL;
43c02e7b 956
8888d191 957static void
43c02e7b
VB
958lldpd_exit()
959{
960 struct lldpd_hardware *hardware;
b5562b23
VB
961 close(gcfg->g_ctl);
962 priv_ctl_cleanup();
43c02e7b
VB
963 TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
964 if (INTERFACE_OPENED(hardware))
965 lldpd_iface_close(gcfg, hardware);
966 }
967#ifdef USE_SNMP
968 if (gcfg->g_snmp)
969 agent_shutdown();
970#endif /* USE_SNMP */
971}
972
973int
974main(int argc, char *argv[])
975{
976 struct lldpd *cfg;
77507b69 977 struct lldpd_chassis *lchassis;
e809a587
VB
978 int ch, debug = 0;
979#ifdef USE_SNMP
980 int snmp = 0;
981#endif
43c02e7b 982 char *mgmtp = NULL;
71936c67 983 char *popt, opts[] = "dxm:p:M:i@ ";
f2dcb180 984 int i, found;
89840df0 985#ifdef ENABLE_LLDPMED
e809a587 986 int lldpmed = 0, noinventory = 0;
89840df0 987#endif
43c02e7b
VB
988
989 saved_argv = argv;
990
991 /*
992 * Get and parse command line options
993 */
994 popt = index(opts, '@');
995 for (i=0; protos[i].mode != 0; i++) {
996 if (protos[i].enabled == 1) continue;
997 *(popt++) = protos[i].arg;
998 }
999 *popt = '\0';
1000 while ((ch = getopt(argc, argv, opts)) != -1) {
1001 switch (ch) {
1002 case 'd':
1003 debug++;
1004 break;
1005 case 'm':
1006 mgmtp = optarg;
1007 break;
e809a587 1008#ifdef ENABLE_LLDPMED
115ff55c 1009 case 'M':
89840df0 1010 lldpmed = atoi(optarg);
e809a587
VB
1011 if ((lldpmed < 1) || (lldpmed > 4)) {
1012 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1013 usage();
e809a587 1014 }
89840df0 1015 break;
e809a587 1016 case 'i':
e809a587 1017 noinventory = 1;
115ff55c 1018 break;
e809a587 1019#else
115ff55c
VB
1020 case 'M':
1021 case 'i':
115ff55c 1022 case 'P':
e809a587
VB
1023 fprintf(stderr, "LLDP-MED support is not built-in\n");
1024 usage();
e809a587 1025 break;
115ff55c 1026#endif
43c02e7b 1027 case 'x':
e809a587 1028#ifdef USE_SNMP
43c02e7b 1029 snmp = 1;
e809a587
VB
1030#else
1031 fprintf(stderr, "SNMP support is not built-in\n");
1032 usage();
1033#endif
43c02e7b
VB
1034 break;
1035 default:
1036 found = 0;
1037 for (i=0; protos[i].mode != 0; i++) {
1038 if (protos[i].enabled) continue;
1039 if (ch == protos[i].arg) {
1040 protos[i].enabled = 1;
1041 found = 1;
1042 }
1043 }
1044 if (!found)
1045 usage();
1046 }
1047 }
115ff55c 1048
43c02e7b 1049 log_init(debug);
a2993d83 1050
eac2f38a
VB
1051 if (!debug) {
1052 int pid;
1053 char *spid;
1054 if (daemon(0, 0) != 0)
1055 fatal("failed to detach daemon");
1056 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1057 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
1058 fatal("unable to open pid file " LLDPD_PID_FILE);
1059 if (asprintf(&spid, "%d\n", getpid()) == -1)
1060 fatal("unable to create pid file " LLDPD_PID_FILE);
1061 if (write(pid, spid, strlen(spid)) == -1)
1062 fatal("unable to write pid file " LLDPD_PID_FILE);
1063 free(spid);
1064 close(pid);
1065 }
1066
a2993d83 1067 priv_init(PRIVSEP_CHROOT);
43c02e7b 1068
43c02e7b
VB
1069 if ((cfg = (struct lldpd *)
1070 calloc(1, sizeof(struct lldpd))) == NULL)
1071 fatal(NULL);
1072
766f32b3 1073 cfg->g_mgmt_pattern = mgmtp;
43c02e7b
VB
1074
1075 /* Get ioctl socket */
1076 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
1077 fatal("failed to get ioctl socket");
1078 cfg->g_delay = LLDPD_TX_DELAY;
1079
1080 /* Set system capabilities */
77507b69
VB
1081 if ((lchassis = (struct lldpd_chassis*)
1082 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
1083 fatal(NULL);
1084 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 1085 LLDP_CAP_ROUTER;
89840df0
VB
1086#ifdef ENABLE_LLDPMED
1087 if (lldpmed > 0) {
1088 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
1089 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1090 lchassis->c_med_type = lldpmed;
1091 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
115ff55c 1092 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
740593ff 1093 cfg->g_noinventory = noinventory;
42bddd41
VB
1094 } else
1095 cfg->g_noinventory = 1;
89840df0 1096#endif
43c02e7b
VB
1097
1098 /* Set TTL */
77507b69 1099 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
1100
1101 cfg->g_protocols = protos;
43c02e7b
VB
1102 for (i=0; protos[i].mode != 0; i++)
1103 if (protos[i].enabled) {
43c02e7b
VB
1104 LLOG_INFO("protocol %s enabled", protos[i].name);
1105 } else
1106 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
1107
1108 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
1109 TAILQ_INIT(&cfg->g_chassis);
1110 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
1111 lchassis->c_refcount++;
43c02e7b
VB
1112
1113#ifdef USE_SNMP
1114 if (snmp) {
1115 cfg->g_snmp = 1;
1116 agent_init(cfg, debug);
1117 }
1118#endif /* USE_SNMP */
1119
1120 /* Create socket */
b5562b23
VB
1121 if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
1122 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
1123 TAILQ_INIT(&cfg->g_clients);
43c02e7b 1124
43c02e7b
VB
1125 gcfg = cfg;
1126 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1127 close(cfg->g_ctl);
1128 priv_ctl_cleanup();
43c02e7b
VB
1129 fatal("unable to set exit function");
1130 }
43c02e7b
VB
1131
1132 /* Signal handling */
b5562b23 1133 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1134 signal(SIGINT, lldpd_shutdown);
1135 signal(SIGTERM, lldpd_shutdown);
1136
1137 for (;;)
1138 lldpd_loop(cfg);
1139
1140 return (0);
1141}