]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/lldpd.c
Fix SNMP support to handle multiple systems on the same port.
[thirdparty/lldpd.git] / src / lldpd.c
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>
27 #include <libgen.h>
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>
35 #include <ifaddrs.h>
36 #include <net/if_arp.h>
37 #include <linux/filter.h>
38 #include <linux/if_vlan.h>
39 #include <linux/if_packet.h>
40 #include <linux/sockios.h>
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
49 static void usage(void);
50
51 static int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *);
52 static void lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *);
53 static int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *);
54 static void lldpd_iface_multicast(struct lldpd *, const char *, int);
55
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 \
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 }, \
67 { 0x15, 11, 12, 0x00000180 }, \
68 { 0x20, 0, 0, 0x00000002 }, \
69 { 0x15, 0, 2, 0x2b000000 }, \
70 { 0x28, 0, 0, 0x00000000 }, \
71 { 0x15, 7, 8, 0x000000e0 }, \
72 { 0x15, 1, 0, 0x0ccccccc }, \
73 { 0x15, 0, 2, 0x81000100 }, \
74 { 0x28, 0, 0, 0x00000000 }, \
75 { 0x15, 3, 4, 0x00000100 }, \
76 { 0x15, 0, 3, 0x52cccccc }, \
77 { 0x28, 0, 0, 0x00000000 }, \
78 { 0x15, 0, 1, 0x000001e0 }, \
79 { 0x6, 0, 0, 0x0000ffff }, \
80 { 0x6, 0, 0, 0x00000000 },
81 static struct sock_filter lldpd_filter_f[] = { LLDPD_FILTER_F };
82
83 static struct protocol protos[] =
84 {
85 { LLDPD_MODE_LLDP, 1, "LLDP", ' ', lldp_send, lldp_decode, NULL,
86 LLDP_MULTICAST_ADDR },
87 #ifdef ENABLE_CDP
88 { LLDPD_MODE_CDPV1, 0, "CDPv1", 'c', cdpv1_send, cdp_decode, cdpv1_guess,
89 CDP_MULTICAST_ADDR },
90 { LLDPD_MODE_CDPV2, 0, "CDPv2", 'c', cdpv2_send, cdp_decode, cdpv2_guess,
91 CDP_MULTICAST_ADDR },
92 #endif
93 #ifdef ENABLE_SONMP
94 { LLDPD_MODE_SONMP, 0, "SONMP", 's', sonmp_send, sonmp_decode, NULL,
95 SONMP_MULTICAST_ADDR },
96 #endif
97 #ifdef ENABLE_EDP
98 { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL,
99 EDP_MULTICAST_ADDR },
100 #endif
101 #ifdef ENABLE_FDP
102 { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL,
103 FDP_MULTICAST_ADDR },
104 #endif
105 { 0, 0, "any", ' ', NULL, NULL, NULL,
106 {0,0,0,0,0,0} }
107 };
108
109 static
110 struct lldpd_hardware *lldpd_hardware_add(struct lldpd *, struct ifaddrs *);
111 static void lldpd_loop(struct lldpd *);
112 static void lldpd_shutdown(int);
113 static void lldpd_exit();
114 static void lldpd_send_all(struct lldpd *);
115 static void lldpd_recv_all(struct lldpd *);
116 static int lldpd_guess_type(struct lldpd *, char *, int);
117 static void lldpd_decode(struct lldpd *, char *, int,
118 struct lldpd_hardware *);
119 #ifdef ENABLE_LLDPMED
120 static void lldpd_med(struct lldpd_chassis *);
121 #endif
122
123 static char **saved_argv;
124
125 static void
126 usage(void)
127 {
128 extern const char *__progname;
129 fprintf(stderr, "usage: %s [options]\n", __progname);
130 fprintf(stderr, "see manual page lldpd(8) for more information\n");
131 exit(1);
132 }
133
134 static void
135 lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware)
136 {
137 struct ifreq ifr;
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
146 hardware->h_mtu = hardware->h_lport.p_mfs = ifr.ifr_mtu;
147 }
148
149 static int
150 lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware)
151 {
152 int status;
153 struct sock_fprog prog;
154
155 lldpd_iface_init_mtu(global, hardware);
156 status = priv_iface_init(hardware, -1);
157 if (status != 0)
158 return status;
159
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);
166 return ENETDOWN;
167 }
168
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
176 static void
177 lldpd_iface_multicast(struct lldpd *global, const char *name, int remove)
178 {
179 int i, rc;
180
181 for (i=0; global->g_protocols[i].mode != 0; i++) {
182 if (!global->g_protocols[i].enabled) continue;
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);
191 }
192 }
193 }
194
195 static int
196 lldpd_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
203 memcpy(listen, hardware->h_ifname, IFNAMSIZ);
204 lldpd_iface_multicast(global, listen, 1);
205
206 return 0;
207 }
208
209 #ifdef ENABLE_DOT1
210 void
211 lldpd_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 }
223 #endif
224
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. */
228 void
229 lldpd_port_cleanup(struct lldpd_port *port, int all)
230 {
231 #ifdef ENABLE_LLDPMED
232 int i;
233 if (all)
234 for (i=0; i < LLDPMED_LOCFORMAT_LAST; i++)
235 free(port->p_med_location[i].data);
236 #endif
237 #ifdef ENABLE_DOT1
238 lldpd_vlan_cleanup(port);
239 #endif
240 free(port->p_id);
241 free(port->p_descr);
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--;
246 free(port);
247 }
248 }
249
250 void
251 lldpd_chassis_cleanup(struct lldpd_chassis *chassis, int all)
252 {
253 #ifdef ENABLE_LLDPMED
254 free(chassis->c_med_hw);
255 free(chassis->c_med_sw);
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
262 free(chassis->c_id);
263 free(chassis->c_name);
264 free(chassis->c_descr);
265 if (all)
266 free(chassis);
267 }
268
269 void
270 lldpd_remote_cleanup(struct lldpd *cfg, struct lldpd_hardware *hardware, int all)
271 {
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 }
288 }
289 }
290
291 void
292 lldpd_hardware_cleanup(struct lldpd_hardware *hardware)
293 {
294 lldpd_port_cleanup(&hardware->h_lport, 1);
295 free(hardware);
296 }
297
298 void
299 lldpd_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);
309 lldpd_remote_cleanup(cfg, hardware, 1);
310 lldpd_hardware_cleanup(hardware);
311 } else
312 lldpd_remote_cleanup(cfg, hardware, 0);
313 }
314 }
315
316 static struct lldpd_hardware *
317 lldpd_hardware_add(struct lldpd *cfg, struct ifaddrs *ifa)
318 {
319 #if defined (ENABLE_DOT1) || defined (ENABLE_DOT3)
320 struct ifaddrs *oifap, *oifa;
321 #endif
322 struct lldpd_hardware *hardware;
323 struct lldpd_port *port;
324 #ifdef ENABLE_DOT1
325 struct lldpd_vlan *vlan;
326 struct vlan_ioctl_args ifv;
327 #endif
328 #ifdef ENABLE_DOT3
329 struct ethtool_cmd ethc;
330 #endif
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;
343 hardware->h_lport.p_chassis = LOCAL_CHASSIS(cfg);
344 TAILQ_INIT(&hardware->h_rports);
345 #ifdef ENABLE_LLDPMED
346 if (LOCAL_CHASSIS(cfg)->c_med_cap_available) {
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 }
351 #endif
352 #ifdef ENABLE_DOT1
353 TAILQ_INIT(&hardware->h_lport.p_vlans);
354 } else {
355 lldpd_port_cleanup(&hardware->h_lport, 0);
356 #endif
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));
365 iface_get_permanent_mac(cfg, hardware);
366 port->p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
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));
370 port->p_id_len = sizeof(hardware->h_lladdr);
371 port->p_descr = strdup(hardware->h_ifname);
372
373 if (LOCAL_CHASSIS(cfg)->c_id == NULL) {
374 /* Use the first port's l2 addr as the chassis ID */
375 if ((LOCAL_CHASSIS(cfg)->c_id =
376 malloc(sizeof(hardware->h_lladdr))) == NULL)
377 fatal(NULL);
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,
381 hardware->h_lladdr, sizeof(hardware->h_lladdr));
382 }
383
384 /* Get VLANS and aggregation status */
385 #if defined (ENABLE_DOT3) || defined (ENABLE_DOT1)
386 if (getifaddrs(&oifap) != 0)
387 fatal("lldpd_hardware_add: failed to get interface list");
388 for (oifa = oifap; oifa != NULL; oifa = oifa->ifa_next) {
389 #ifdef ENABLE_DOT1
390 /* Check if we already have checked this one */
391 int skip = 0;
392 TAILQ_FOREACH(vlan, &port->p_vlans, v_entries) {
393 if (strcmp(vlan->v_name, oifa->ifa_name) == 0) {
394 skip = 1;
395 break;
396 }
397 }
398 if (skip) continue;
399 #endif
400
401 /* Aggregation check */
402 #ifdef ENABLE_DOT3
403 if (iface_is_bond_slave(cfg, hardware->h_ifname, oifa->ifa_name, NULL))
404 port->p_aggregid = if_nametoindex(oifa->ifa_name);
405 #endif
406
407 #ifdef ENABLE_DOT1
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) &&
413 ((iface_is_bond_slave(cfg, hardware->h_ifname, ifv.u.device2, NULL)) ||
414 (iface_is_bridged_to(cfg, hardware->h_ifname, ifv.u.device2)) ||
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;
419 if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) {
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 }
435 #endif
436 }
437 freeifaddrs(oifap);
438 #endif
439
440 #ifdef ENABLE_DOT3
441 /* MAC/PHY */
442 if (priv_ethtool(hardware->h_ifname, &ethc) == 0) {
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;
495 } else
496 LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname);
497 #endif
498
499 if (!INTERFACE_OPENED(hardware)) {
500
501 if (lldpd_iface_init(cfg, hardware) != 0) {
502 LLOG_WARN("unable to initialize %s", hardware->h_ifname);
503 lldpd_hardware_cleanup(hardware);
504 return (NULL);
505 }
506
507 TAILQ_INSERT_TAIL(&cfg->g_hardware, hardware, h_entries);
508 }
509
510 return (hardware);
511 }
512
513 static int
514 lldpd_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
533 static void
534 lldpd_decode(struct lldpd *cfg, char *frame, int s,
535 struct lldpd_hardware *hardware)
536 {
537 int i, result;
538 struct lldpd_chassis *chassis, *ochassis = NULL;
539 struct lldpd_port *port, *oport = NULL;
540 int guess = LLDPD_MODE_LLDP;
541
542 /* Discard VLAN frames */
543 if ((s >= sizeof(struct ethhdr)) &&
544 (((struct ethhdr*)frame)->h_proto == htons(ETHERTYPE_VLAN)))
545 return;
546
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 }
555 }
556
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;
565 chassis->c_protocol = port->p_protocol =
566 cfg->g_protocols[i].mode;
567 break;
568 }
569 }
570 if (cfg->g_protocols[i].mode == 0) {
571 LLOG_INFO("unable to guess frame type");
572 return;
573 }
574
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 }
588 }
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;
598 }
599 }
600
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 i = 0; TAILQ_FOREACH(ochassis, &cfg->g_chassis, c_entries) i++;
623 LLOG_DEBUG("Currently, we know %d different systems", i);
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++;
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);
638 return;
639 }
640
641 static void
642 lldpd_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;
650 #ifdef USE_SNMP
651 int fakeblock = 0;
652 struct timeval *tvp = &tv;
653 #endif
654 int rc, nfds, n;
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 */
670 if (((hardware->h_flags & IFF_UP) == 0) ||
671 ((hardware->h_flags & IFF_RUNNING) == 0))
672 continue;
673 FD_SET(hardware->h_raw, &rfds);
674 if (nfds < hardware->h_raw)
675 nfds = hardware->h_raw;
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. */
715 if (FD_ISSET(hardware->h_raw, &rfds)) {
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(
723 hardware->h_raw,
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 }
738 hardware->h_rx_cnt++;
739 lldpd_decode(cfg, buffer, n, hardware);
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)
766 client_handle_client(cfg, client, buffer, n);
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
782 static void
783 lldpd_send_all(struct lldpd *cfg)
784 {
785 struct lldpd_hardware *hardware;
786 struct lldpd_port *port;
787 int i, sent = 0;
788
789 cfg->g_lastsent = time(NULL);
790 TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
791 /* Ignore if interface is down */
792 if (((hardware->h_flags & IFF_UP) == 0) ||
793 ((hardware->h_flags & IFF_RUNNING) == 0))
794 continue;
795
796 for (i=0; cfg->g_protocols[i].mode != 0; i++) {
797 if (!cfg->g_protocols[i].enabled)
798 continue;
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 }
810 }
811
812 if (!sent)
813 /* Nothing was sent for this port, let's speak LLDP */
814 cfg->g_protocols[0].send(cfg,
815 hardware);
816 }
817 }
818
819 #ifdef ENABLE_LLDPMED
820 static void
821 lldpd_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
838 static void
839 lldpd_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;
847 char *hp;
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");
854 if ((hp = priv_gethostbyname()) == NULL)
855 fatal("failed to get system name");
856 free(LOCAL_CHASSIS(cfg)->c_name);
857 free(LOCAL_CHASSIS(cfg)->c_descr);
858 if ((LOCAL_CHASSIS(cfg)->c_name = strdup(hp)) == NULL)
859 fatal(NULL);
860 if (asprintf(&LOCAL_CHASSIS(cfg)->c_descr, "%s %s %s %s",
861 un->sysname, un->release, un->version, un->machine) == -1)
862 fatal("failed to set system description");
863
864 /* Check forwarding */
865 LOCAL_CHASSIS(cfg)->c_cap_enabled = 0;
866 if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
867 if ((read(f, &status, 1) == 1) && (status == '1')) {
868 LOCAL_CHASSIS(cfg)->c_cap_enabled = LLDP_CAP_ROUTER;
869 }
870 close(f);
871 }
872 #ifdef ENABLE_LLDPMED
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);
878 #endif
879 free(un);
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
887 LOCAL_CHASSIS(cfg)->c_mgmt.s_addr = INADDR_ANY;
888 for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
889 if (LOCAL_CHASSIS(cfg)->c_mgmt.s_addr == INADDR_ANY)
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)) {
897 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
898 &sa->sin_addr,
899 sizeof(struct in_addr));
900 LOCAL_CHASSIS(cfg)->c_mgmt_if = if_nametoindex(ifa->ifa_name);
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) {
907 memcpy(&LOCAL_CHASSIS(cfg)->c_mgmt,
908 &sa->sin_addr,
909 sizeof(struct in_addr));
910 LOCAL_CHASSIS(cfg)->c_mgmt_if =
911 if_nametoindex(ifa->ifa_name);
912 }
913 }
914 }
915
916 if (ifa->ifa_addr == NULL ||
917 ifa->ifa_addr->sa_family != PF_PACKET)
918 continue;
919
920 sdl = (struct sockaddr_ll *)ifa->ifa_addr;
921 if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen)
922 continue;
923
924 if (iface_is_bridge(cfg, ifa->ifa_name)) {
925 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_BRIDGE;
926 continue;
927 }
928
929 if ((iface_is_vlan(cfg, ifa->ifa_name)) ||
930 (iface_is_bond(cfg, ifa->ifa_name)))
931 continue;
932
933 if (!(ifa->ifa_flags & (IFF_MULTICAST|IFF_BROADCAST)))
934 continue;
935
936 if (iface_is_wireless(cfg, ifa->ifa_name))
937 LOCAL_CHASSIS(cfg)->c_cap_enabled |= LLDP_CAP_WLAN;
938
939 if (lldpd_hardware_add(cfg, ifa) == NULL)
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
952 static void
953 lldpd_shutdown(int sig)
954 {
955 LLOG_INFO("signal received, exiting");
956 exit(0);
957 }
958
959 /* For signal handling */
960 static struct lldpd *gcfg = NULL;
961
962 static void
963 lldpd_exit()
964 {
965 struct lldpd_hardware *hardware;
966 close(gcfg->g_ctl);
967 priv_ctl_cleanup();
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
978 int
979 main(int argc, char *argv[])
980 {
981 struct lldpd *cfg;
982 struct lldpd_chassis *lchassis;
983 int ch, debug = 0;
984 #ifdef USE_SNMP
985 int snmp = 0;
986 #endif
987 char *mgmtp = NULL;
988 char *popt, opts[] = "dxm:p:M:i@ ";
989 int i, found;
990 #ifdef ENABLE_LLDPMED
991 int lldpmed = 0, noinventory = 0;
992 #endif
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;
1013 #ifdef ENABLE_LLDPMED
1014 case 'M':
1015 lldpmed = atoi(optarg);
1016 if ((lldpmed < 1) || (lldpmed > 4)) {
1017 fprintf(stderr, "-M requires an argument between 1 and 4\n");
1018 usage();
1019 }
1020 break;
1021 case 'i':
1022 noinventory = 1;
1023 break;
1024 #else
1025 case 'M':
1026 case 'i':
1027 case 'P':
1028 fprintf(stderr, "LLDP-MED support is not built-in\n");
1029 usage();
1030 break;
1031 #endif
1032 case 'x':
1033 #ifdef USE_SNMP
1034 snmp = 1;
1035 #else
1036 fprintf(stderr, "SNMP support is not built-in\n");
1037 usage();
1038 #endif
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 }
1053
1054 log_init(debug);
1055
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,
1062 O_TRUNC | O_CREAT | O_WRONLY, 0644)) == -1)
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
1072 priv_init(PRIVSEP_CHROOT);
1073
1074 if ((cfg = (struct lldpd *)
1075 calloc(1, sizeof(struct lldpd))) == NULL)
1076 fatal(NULL);
1077
1078 cfg->g_mgmt_pattern = mgmtp;
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 */
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 |
1090 LLDP_CAP_ROUTER;
1091 #ifdef ENABLE_LLDPMED
1092 if (lldpmed > 0) {
1093 if (lldpmed == LLDPMED_CLASS_III)
1094 lchassis->c_cap_available |= LLDP_CAP_TELEPHONE;
1095 lchassis->c_med_type = lldpmed;
1096 lchassis->c_med_cap_available = LLDPMED_CAP_CAP |
1097 LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION;
1098 cfg->g_noinventory = noinventory;
1099 } else
1100 cfg->g_noinventory = 1;
1101 #endif
1102
1103 /* Set TTL */
1104 lchassis->c_ttl = LLDPD_TTL;
1105
1106 cfg->g_protocols = protos;
1107 for (i=0; protos[i].mode != 0; i++)
1108 if (protos[i].enabled) {
1109 LLOG_INFO("protocol %s enabled", protos[i].name);
1110 } else
1111 LLOG_INFO("protocol %s disabled", protos[i].name);
1112
1113 TAILQ_INIT(&cfg->g_hardware);
1114 TAILQ_INIT(&cfg->g_chassis);
1115 TAILQ_INSERT_TAIL(&cfg->g_chassis, lchassis, c_entries);
1116 lchassis->c_refcount++;
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 */
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);
1129
1130 gcfg = cfg;
1131 if (atexit(lldpd_exit) != 0) {
1132 close(cfg->g_ctl);
1133 priv_ctl_cleanup();
1134 fatal("unable to set exit function");
1135 }
1136
1137 /* Signal handling */
1138 signal(SIGHUP, lldpd_shutdown);
1139 signal(SIGINT, lldpd_shutdown);
1140 signal(SIGTERM, lldpd_shutdown);
1141
1142 for (;;)
1143 lldpd_loop(cfg);
1144
1145 return (0);
1146 }