]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/lldpd.c
Fix SNMP support to handle multiple systems on the same 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);
f4c43902
VB
622 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
623 LLOG_DEBUG("Currently, we know %d different systems", i);
77507b69
VB
624 }
625 /* Add port */
626 port->p_lastchange = port->p_lastupdate = time(NULL);
627 if ((port->p_lastframe = (struct lldpd_frame *)malloc(s +
628 sizeof(int))) != NULL) {
629 port->p_lastframe->size = s;
630 memcpy(port->p_lastframe->frame, frame, s);
631 }
632 TAILQ_INSERT_TAIL(&hardware->h_rports, port, p_entries);
633 port->p_chassis = chassis;
634 port->p_chassis->c_refcount++;
f4c43902
VB
635 i = 0; TAILQ_FOREACH(oport, &hardware->h_rports, p_entries) i++;
636 LLOG_DEBUG("Currently, %s known %d neighbors",
637 hardware->h_ifname, i);
43c02e7b
VB
638 return;
639}
640
8888d191 641static void
43c02e7b
VB
642lldpd_recv_all(struct lldpd *cfg)
643{
644 struct lldpd_hardware *hardware;
645 struct lldpd_client *client, *client_next;
646 fd_set rfds;
647 struct timeval tv;
648 struct sockaddr_ll from;
649 socklen_t fromlen;
43c02e7b
VB
650#ifdef USE_SNMP
651 int fakeblock = 0;
652 struct timeval *tvp = &tv;
653#endif
0bc32943 654 int rc, nfds, n;
43c02e7b
VB
655 char *buffer;
656
657 do {
658 tv.tv_sec = cfg->g_delay - (time(NULL) - cfg->g_lastsent);
659 if (tv.tv_sec < 0)
660 tv.tv_sec = LLDPD_TX_DELAY;
661 if (tv.tv_sec >= cfg->g_delay)
662 tv.tv_sec = cfg->g_delay;
663 tv.tv_usec = 0;
664
665 FD_ZERO(&rfds);
666 nfds = -1;
667
668 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
669 /* Ignore if interface is down */
e70b76f9
VB
670 if (((hardware->h_flags & IFF_UP) == 0) ||
671 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
672 continue;
673 FD_SET(hardware->h_raw, &rfds);
674 if (nfds < hardware->h_raw)
675 nfds = hardware->h_raw;
43c02e7b
VB
676 }
677 TAILQ_FOREACH(client, &cfg->g_clients, next) {
678 FD_SET(client->fd, &rfds);
679 if (nfds < client->fd)
680 nfds = client->fd;
681 }
682 FD_SET(cfg->g_ctl, &rfds);
683 if (nfds < cfg->g_ctl)
684 nfds = cfg->g_ctl;
685
686#ifdef USE_SNMP
687 if (cfg->g_snmp)
688 snmp_select_info(&nfds, &rfds, tvp, &fakeblock);
689#endif /* USE_SNMP */
690 if (nfds == -1) {
691 sleep(cfg->g_delay);
692 return;
693 }
694
695 rc = select(nfds + 1, &rfds, NULL, NULL, &tv);
696 if (rc == -1) {
697 if (errno == EINTR)
698 continue;
699 LLOG_WARN("failure on select");
700 break;
701 }
702#ifdef USE_SNMP
703 if (cfg->g_snmp) {
704 if (rc > 0)
705 snmp_read(&rfds);
706 else if (rc == 0)
707 snmp_timeout();
708 }
709#endif /* USE_SNMP */
710 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
711 /* We could have received something on _real_
712 * interface. However, even in this case, this could be
713 * just an outgoing packet. We will try to handle both
714 * cases, but maybe not in the same select. */
0bc32943 715 if (FD_ISSET(hardware->h_raw, &rfds)) {
43c02e7b
VB
716 if ((buffer = (char *)malloc(
717 hardware->h_mtu)) == NULL) {
718 LLOG_WARN("failed to alloc reception buffer");
719 continue;
720 }
721 fromlen = sizeof(from);
722 if ((n = recvfrom(
0bc32943 723 hardware->h_raw,
43c02e7b
VB
724 buffer,
725 hardware->h_mtu, 0,
726 (struct sockaddr *)&from,
727 &fromlen)) == -1) {
728 LLOG_WARN("error while receiving frame on %s",
729 hardware->h_ifname);
730 hardware->h_rx_discarded_cnt++;
731 free(buffer);
732 continue;
733 }
734 if (from.sll_pkttype == PACKET_OUTGOING) {
735 free(buffer);
736 continue;
737 }
43c02e7b 738 hardware->h_rx_cnt++;
0bc32943 739 lldpd_decode(cfg, buffer, n, hardware);
43c02e7b
VB
740 free(buffer);
741 }
742
743 }
744 if (FD_ISSET(cfg->g_ctl, &rfds)) {
745 if (ctl_accept(cfg, cfg->g_ctl) == -1)
746 LLOG_WARN("unable to accept new client");
747 }
748 for (client = TAILQ_FIRST(&cfg->g_clients);
749 client != NULL;
750 client = client_next) {
751 client_next = TAILQ_NEXT(client, next);
752 if (FD_ISSET(client->fd, &rfds)) {
753 /* Got a message */
754 if ((buffer = (char *)malloc(MAX_HMSGSIZE)) ==
755 NULL) {
756 LLOG_WARN("failed to alloc reception buffer");
757 continue;
758 }
759 if ((n = recv(client->fd, buffer,
760 MAX_HMSGSIZE, 0)) == -1) {
761 LLOG_WARN("error while receiving message");
762 free(buffer);
763 continue;
764 }
765 if (n > 0)
a552a72e 766 client_handle_client(cfg, client, buffer, n);
43c02e7b
VB
767 else
768 ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */
769 free(buffer);
770 }
771 }
772
773#ifdef USE_SNMP
774 if (cfg->g_snmp) {
775 run_alarms();
776 netsnmp_check_outstanding_agent_requests();
777 }
778#endif /* USE_SNMP */
779 } while ((rc != 0) || (time(NULL) - cfg->g_lastsent < cfg->g_delay));
780}
781
8888d191 782static void
43c02e7b
VB
783lldpd_send_all(struct lldpd *cfg)
784{
785 struct lldpd_hardware *hardware;
77507b69
VB
786 struct lldpd_port *port;
787 int i, sent = 0;
f7db0dd8 788
43c02e7b
VB
789 cfg->g_lastsent = time(NULL);
790 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
791 /* Ignore if interface is down */
e70b76f9
VB
792 if (((hardware->h_flags & IFF_UP) == 0) ||
793 ((hardware->h_flags & IFF_RUNNING) == 0))
43c02e7b
VB
794 continue;
795
796 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
797 if (!cfg->g_protocols[i].enabled)
798 continue;
77507b69
VB
799 /* We send only if we have at least one remote system
800 * speaking this protocol */
801 TAILQ_FOREACH(port, &hardware->h_rports, p_entries) {
802 if (port->p_protocol ==
803 cfg->g_protocols[i].mode) {
804 cfg->g_protocols[i].send(cfg,
805 hardware);
806 sent = 1;
807 break;
808 }
809 }
43c02e7b 810 }
77507b69
VB
811
812 if (!sent)
813 /* Nothing was sent for this port, let's speak LLDP */
814 cfg->g_protocols[0].send(cfg,
815 hardware);
43c02e7b
VB
816 }
817}
818
89840df0 819#ifdef ENABLE_LLDPMED
8888d191 820static void
89840df0
VB
821lldpd_med(struct lldpd_chassis *chassis)
822{
823 free(chassis->c_med_hw);
824 free(chassis->c_med_fw);
825 free(chassis->c_med_sn);
826 free(chassis->c_med_manuf);
827 free(chassis->c_med_model);
828 free(chassis->c_med_asset);
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}
836#endif
837
8888d191 838static void
43c02e7b
VB
839lldpd_loop(struct lldpd *cfg)
840{
841 struct ifaddrs *ifap, *ifa;
842 struct sockaddr_ll *sdl;
843 struct lldpd_hardware *hardware;
844 int f;
845 char status;
846 struct utsname *un;
b5562b23 847 char *hp;
43c02e7b
VB
848
849 /* Set system name and description */
850 if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL)
851 fatal(NULL);
852 if (uname(un) != 0)
853 fatal("failed to get system information");
b5562b23 854 if ((hp = priv_gethostbyname()) == NULL)
43c02e7b 855 fatal("failed to get system name");
77507b69
VB
856 free(LOCAL_CHASSIS(cfg)->c_name);
857 free(LOCAL_CHASSIS(cfg)->c_descr);
858 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
249644a4 859 fatal(NULL);
77507b69 860 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
43c02e7b
VB
861 un->sysname, un->release, un->version, un->machine) == -1)
862 fatal("failed to set system description");
43c02e7b
VB
863
864 /* Check forwarding */
77507b69 865 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
b5562b23 866 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
4afe659e 867 if ((read(f, &status, 1) == 1) && (status == '1')) {
77507b69 868 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
4afe659e 869 }
43c02e7b
VB
870 close(f);
871 }
89840df0 872#ifdef ENABLE_LLDPMED
77507b69
VB
873 if (LOCAL_CHASSIS(cfg)->c_cap_available & LLDP_CAP_TELEPHONE)
874 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_TELEPHONE;
875 lldpd_med(LOCAL_CHASSIS(cfg));
876 free(LOCAL_CHASSIS(cfg)->c_med_sw);
877 LOCAL_CHASSIS(cfg)->c_med_sw = strdup(un->release);
89840df0
VB
878#endif
879 free(un);
43c02e7b
VB
880
881 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries)
882 hardware->h_flags = 0;
883
884 if (getifaddrs(&ifap) != 0)
885 fatal("lldpd_loop: failed to get interface list");
886
77507b69 887 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
43c02e7b 888 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
77507b69 889 if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr == INADDR_ANY)
43c02e7b
VB
890 /* Get management address, if available */
891 if ((ifa->ifa_addr != NULL) &&
892 (ifa->ifa_addr->sa_family == AF_INET)) {
893 struct sockaddr_in *sa;
894 sa = (struct sockaddr_in *)ifa->ifa_addr;
895 if ((ntohl(*(u_int32_t*)&sa->sin_addr) != INADDR_LOOPBACK) &&
896 (cfg->g_mgmt_pattern == NULL)) {
77507b69 897 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
43c02e7b
VB
898 &sa->sin_addr,
899 sizeof(struct in_addr));
77507b69 900 LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name);
43c02e7b
VB
901 }
902 else if (cfg->g_mgmt_pattern != NULL) {
903 char *ip;
904 ip = inet_ntoa(sa->sin_addr);
905 if (fnmatch(cfg->g_mgmt_pattern,
906 ip, 0) == 0) {
77507b69 907 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
43c02e7b
VB
908 &sa->sin_addr,
909 sizeof(struct in_addr));
77507b69 910 LOCAL_CHASSIS(cfg)->c_mgmt_if =
43c02e7b
VB
911 if_nametoindex(ifa->ifa_name);
912 }
913 }
914 }
915
a5e5458f
VB
916 if (ifa->ifa_addr == NULL ||
917 ifa->ifa_addr->sa_family != PF_PACKET)
918 continue;
919
99f85c00
VB
920 sdl = (struct sockaddr_ll *)ifa->ifa_addr;
921 if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
922 continue;
923
43c02e7b 924 if (iface_is_bridge(cfg, ifa->ifa_name)) {
77507b69 925 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
43c02e7b
VB
926 continue;
927 }
928
50a89ca7 929 if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
43c02e7b
VB
930 (iface_is_bond(cfg, ifa->ifa_name)))
931 continue;
932
b29c23cc 933 if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
43c02e7b
VB
934 continue;
935
43c02e7b 936 if (iface_is_wireless(cfg, ifa->ifa_name))
77507b69 937 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
43c02e7b 938
77507b69 939 if (lldpd_hardware_add(cfg, ifa) == NULL)
43c02e7b
VB
940 LLOG_WARNX("failed to allocate port %s, skip it",
941 ifa->ifa_name);
942 }
943
944 freeifaddrs(ifap);
945
946 lldpd_cleanup(cfg);
947
948 lldpd_send_all(cfg);
949 lldpd_recv_all(cfg);
950}
951
8888d191 952static void
43c02e7b
VB
953lldpd_shutdown(int sig)
954{
955 LLOG_INFO("signal received, exiting");
956 exit(0);
957}
958
959/* For signal handling */
8888d191 960static struct lldpd *gcfg = NULL;
43c02e7b 961
8888d191 962static void
43c02e7b
VB
963lldpd_exit()
964{
965 struct lldpd_hardware *hardware;
b5562b23
VB
966 close(gcfg->g_ctl);
967 priv_ctl_cleanup();
43c02e7b
VB
968 TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
969 if (INTERFACE_OPENED(hardware))
970 lldpd_iface_close(gcfg, hardware);
971 }
972#ifdef USE_SNMP
973 if (gcfg->g_snmp)
974 agent_shutdown();
975#endif /* USE_SNMP */
976}
977
978int
979main(int argc, char *argv[])
980{
981 struct lldpd *cfg;
77507b69 982 struct lldpd_chassis *lchassis;
e809a587
VB
983 int ch, debug = 0;
984#ifdef USE_SNMP
985 int snmp = 0;
986#endif
43c02e7b 987 char *mgmtp = NULL;
71936c67 988 char *popt, opts[] = "dxm:p:M:i@ ";
f2dcb180 989 int i, found;
89840df0 990#ifdef ENABLE_LLDPMED
e809a587 991 int lldpmed = 0, noinventory = 0;
89840df0 992#endif
43c02e7b
VB
993
994 saved_argv = argv;
995
996 /*
997 * Get and parse command line options
998 */
999 popt = index(opts, '@');
1000 for (i=0; protos[i].mode != 0; i++) {
1001 if (protos[i].enabled == 1) continue;
1002 *(popt++) = protos[i].arg;
1003 }
1004 *popt = '\0';
1005 while ((ch = getopt(argc, argv, opts)) != -1) {
1006 switch (ch) {
1007 case 'd':
1008 debug++;
1009 break;
1010 case 'm':
1011 mgmtp = optarg;
1012 break;
e809a587 1013#ifdef ENABLE_LLDPMED
115ff55c 1014 case 'M':
89840df0 1015 lldpmed = atoi(optarg);
e809a587
VB
1016 if ((lldpmed < 1) || (lldpmed > 4)) {
1017 fprintf(stderr, "-M requires an argument between 1 and 4\n");
89840df0 1018 usage();
e809a587 1019 }
89840df0 1020 break;
e809a587 1021 case 'i':
e809a587 1022 noinventory = 1;
115ff55c 1023 break;
e809a587 1024#else
115ff55c
VB
1025 case 'M':
1026 case 'i':
115ff55c 1027 case 'P':
e809a587
VB
1028 fprintf(stderr, "LLDP-MED support is not built-in\n");
1029 usage();
e809a587 1030 break;
115ff55c 1031#endif
43c02e7b 1032 case 'x':
e809a587 1033#ifdef USE_SNMP
43c02e7b 1034 snmp = 1;
e809a587
VB
1035#else
1036 fprintf(stderr, "SNMP support is not built-in\n");
1037 usage();
1038#endif
43c02e7b
VB
1039 break;
1040 default:
1041 found = 0;
1042 for (i=0; protos[i].mode != 0; i++) {
1043 if (protos[i].enabled) continue;
1044 if (ch == protos[i].arg) {
1045 protos[i].enabled = 1;
1046 found = 1;
1047 }
1048 }
1049 if (!found)
1050 usage();
1051 }
1052 }
115ff55c 1053
43c02e7b 1054 log_init(debug);
a2993d83 1055
eac2f38a
VB
1056 if (!debug) {
1057 int pid;
1058 char *spid;
1059 if (daemon(0, 0) != 0)
1060 fatal("failed to detach daemon");
1061 if ((pid = open(LLDPD_PID_FILE,
0aa5f676 1062 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
eac2f38a
VB
1063 fatal("unable to open pid file " LLDPD_PID_FILE);
1064 if (asprintf(&spid, "%d\n", getpid()) == -1)
1065 fatal("unable to create pid file " LLDPD_PID_FILE);
1066 if (write(pid, spid, strlen(spid)) == -1)
1067 fatal("unable to write pid file " LLDPD_PID_FILE);
1068 free(spid);
1069 close(pid);
1070 }
1071
a2993d83 1072 priv_init(PRIVSEP_CHROOT);
43c02e7b 1073
43c02e7b
VB
1074 if ((cfg = (struct lldpd *)
1075 calloc(1, sizeof(struct lldpd))) == NULL)
1076 fatal(NULL);
1077
766f32b3 1078 cfg->g_mgmt_pattern = mgmtp;
43c02e7b
VB
1079
1080 /* Get ioctl socket */
1081 if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
1082 fatal("failed to get ioctl socket");
1083 cfg->g_delay = LLDPD_TX_DELAY;
1084
1085 /* Set system capabilities */
77507b69
VB
1086 if ((lchassis = (struct lldpd_chassis*)
1087 calloc(1, sizeof(struct lldpd_chassis))) == NULL)
1088 fatal(NULL);
1089 lchassis->c_cap_available = LLDP_CAP_BRIDGE | LLDP_CAP_WLAN |
43c02e7b 1090 LLDP_CAP_ROUTER;
89840df0
VB
1091#ifdef ENABLE_LLDPMED
1092 if (lldpmed > 0) {
1093 if (lldpmed == LLDPMED_CLASS_III)
77507b69
VB
1094 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1095 lchassis->c_med_type = lldpmed;
1096 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
115ff55c 1097 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
740593ff 1098 cfg->g_noinventory = noinventory;
42bddd41
VB
1099 } else
1100 cfg->g_noinventory = 1;
89840df0 1101#endif
43c02e7b
VB
1102
1103 /* Set TTL */
77507b69 1104 lchassis->c_ttl = LLDPD_TTL;
43c02e7b
VB
1105
1106 cfg->g_protocols = protos;
43c02e7b
VB
1107 for (i=0; protos[i].mode != 0; i++)
1108 if (protos[i].enabled) {
43c02e7b
VB
1109 LLOG_INFO("protocol %s enabled", protos[i].name);
1110 } else
1111 LLOG_INFO("protocol %s disabled", protos[i].name);
43c02e7b
VB
1112
1113 TAILQ_INIT(&cfg->g_hardware);
77507b69
VB
1114 TAILQ_INIT(&cfg->g_chassis);
1115 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
1116 lchassis->c_refcount++;
43c02e7b
VB
1117
1118#ifdef USE_SNMP
1119 if (snmp) {
1120 cfg->g_snmp = 1;
1121 agent_init(cfg, debug);
1122 }
1123#endif /* USE_SNMP */
1124
1125 /* Create socket */
b5562b23
VB
1126 if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
1127 fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
1128 TAILQ_INIT(&cfg->g_clients);
43c02e7b 1129
43c02e7b
VB
1130 gcfg = cfg;
1131 if (atexit(lldpd_exit) != 0) {
b5562b23
VB
1132 close(cfg->g_ctl);
1133 priv_ctl_cleanup();
43c02e7b
VB
1134 fatal("unable to set exit function");
1135 }
43c02e7b
VB
1136
1137 /* Signal handling */
b5562b23 1138 signal(SIGHUP, lldpd_shutdown);
43c02e7b
VB
1139 signal(SIGINT, lldpd_shutdown);
1140 signal(SIGTERM, lldpd_shutdown);
1141
1142 for (;;)
1143 lldpd_loop(cfg);
1144
1145 return (0);
1146}