1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Tom Gundersen
7 Copyright (C) 2014 Susant Sahani
9 systemd is free software; you can redistribute it and/or modify it
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
12 (at your option) any later version.
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
19 You should have received a copy of the GNU Lesser General Public License
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
23 #include <arpa/inet.h>
25 #include "siphash24.h"
27 #include "event-util.h"
30 #include "lldp-port.h"
34 #include "lldp-internal.h"
35 #include "lldp-util.h"
36 #include "ether-addr-util.h"
38 typedef enum LLDPAgentRXState {
39 LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL = 4,
40 LLDP_AGENT_RX_DELETE_AGED_INFO,
41 LLDP_AGENT_RX_LLDP_INITIALIZE,
42 LLDP_AGENT_RX_WAIT_FOR_FRAME,
43 LLDP_AGENT_RX_RX_FRAME,
44 LLDP_AGENT_RX_DELETE_INFO,
45 LLDP_AGENT_RX_UPDATE_INFO,
46 _LLDP_AGENT_RX_STATE_MAX,
47 _LLDP_AGENT_RX_INVALID = -1,
50 /* Section 10.5.2.2 Reception counters */
51 struct lldp_agent_statitics {
52 uint64_t stats_ageouts_total;
53 uint64_t stats_frames_discarded_total;
54 uint64_t stats_frames_in_errors_total;
55 uint64_t stats_frames_in_total;
56 uint64_t stats_tlvs_discarded_total;
57 uint64_t stats_tlvs_unrecognized_total;
64 Hashmap *neighbour_mib;
70 LLDPAgentRXState rx_state;
71 lldp_agent_statitics statitics;
74 static unsigned long chassis_id_hash_func(const void *p,
75 const uint8_t hash_key[HASH_KEY_SIZE]) {
77 const lldp_chassis_id *id = p;
81 siphash24((uint8_t *) &u, id->data, id->length, hash_key);
83 return (unsigned long) u;
86 static int chassis_id_compare_func(const void *_a, const void *_b) {
87 const lldp_chassis_id *a, *b;
92 assert(!a->length || a->data);
93 assert(!b->length || b->data);
95 if (a->type != b->type)
98 if (a->length != b->length)
99 return a->length < b->length ? -1 : 1;
101 return memcmp(a->data, b->data, a->length);
104 static const struct hash_ops chassis_id_hash_ops = {
105 .hash = chassis_id_hash_func,
106 .compare = chassis_id_compare_func
109 static void lldp_mib_delete_objects(sd_lldp *lldp);
110 static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state);
111 static void lldp_run_state_machine(sd_lldp *ll);
113 static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
119 /* Remove expired packets */
120 if (prioq_size(lldp->by_expiry) > 0) {
122 lldp_set_state(lldp, LLDP_AGENT_RX_DELETE_INFO);
124 lldp_mib_delete_objects(lldp);
127 r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
131 lldp_set_state(lldp, LLDP_AGENT_RX_UPDATE_INFO);
133 log_lldp("Packet added. MIB size: %d , PQ size: %d",
134 hashmap_size(lldp->neighbour_mib),
135 prioq_size(lldp->by_expiry));
137 lldp->statitics.stats_frames_in_total ++;
143 log_lldp("Receive frame failed: %s", strerror(-r));
145 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
150 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
151 int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
152 uint16_t type, len, i, l, t;
153 bool chassis_id = false;
154 bool malformed = false;
155 bool port_id = false;
166 port = (lldp_port *) tlv->userdata;
167 lldp = (sd_lldp *) port->userdata;
169 if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
170 log_lldp("Port is disabled : %s . Dropping ...",
175 lldp_set_state(lldp, LLDP_AGENT_RX_RX_FRAME);
178 p += sizeof(struct ether_header);
180 for (i = 1, l = 0; l <= length; i++) {
182 memcpy(&t, p, sizeof(uint16_t));
184 type = ntohs(t) >> 9;
185 len = ntohs(t) & 0x01ff;
187 if (type == LLDP_TYPE_END) {
189 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
199 } else if (type >=_LLDP_TYPE_MAX) {
200 log_lldp("TLV type not recognized %d . Dropping ...",
207 /* skip type and lengh encoding */
216 log_lldp("TLV missing or out of order. Dropping ...");
224 case LLDP_TYPE_CHASSIS_ID:
227 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
235 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
241 /* Look what subtype it has */
242 if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
243 *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
244 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
255 case LLDP_TYPE_PORT_ID:
258 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
266 log_lldp("Duplicate Port ID TLV found. Dropping ...");
272 /* Look what subtype it has */
273 if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
274 *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
275 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
290 "Received invalid lenth: %d TTL TLV. Dropping ...",
298 log_lldp("Duplicate TTL TLV found. Dropping ...");
310 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
320 if(!chassis_id || !port_id || !ttl || !end) {
321 log_lldp( "One or more mandotory TLV missing . Dropping ...");
328 r = tlv_packet_parse_pdu(tlv, length);
330 log_lldp( "Failed to parse the TLV. Dropping ...");
336 return lldp_receive_frame(lldp, tlv);
339 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
342 lldp->statitics.stats_frames_discarded_total ++;
343 lldp->statitics.stats_frames_in_errors_total ++;
346 tlv_packet_free(tlv);
351 static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
352 const lldp_neighbour_port *p = a, *q = b;
354 if (p->until < q->until)
357 if (p->until > q->until)
363 static void lldp_set_state(sd_lldp *lldp, LLDPAgentRXState state) {
366 assert(state < _LLDP_AGENT_RX_STATE_MAX);
368 lldp->rx_state = state;
370 lldp_run_state_machine(lldp);
373 static void lldp_run_state_machine(sd_lldp *lldp) {
375 if (lldp->rx_state == LLDP_AGENT_RX_UPDATE_INFO)
377 lldp->cb(lldp, LLDP_AGENT_RX_UPDATE_INFO, lldp->userdata);
380 /* 10.5.5.2.1 mibDeleteObjects ()
381 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
382 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
383 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
385 static void lldp_mib_delete_objects(sd_lldp *lldp) {
386 lldp_neighbour_port *p;
389 /* Remove all entries that are past their TTL */
392 if (prioq_size(lldp->by_expiry) <= 0)
395 p = prioq_peek(lldp->by_expiry);
400 t = now(CLOCK_BOOTTIME);
405 lldp_neighbour_port_remove_and_free(p);
407 lldp->statitics.stats_ageouts_total ++;
411 static void lldp_mib_objects_flush(sd_lldp *lldp) {
412 lldp_neighbour_port *p, *q;
416 assert(lldp->neighbour_mib);
417 assert(lldp->by_expiry);
419 /* Drop all packets */
420 while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
422 LIST_FOREACH_SAFE(port, p, q, c->ports) {
423 lldp_neighbour_port_remove_and_free(p);
427 assert(hashmap_size(lldp->neighbour_mib) == 0);
428 assert(prioq_size(lldp->by_expiry) == 0);
431 int sd_lldp_save(sd_lldp *lldp, const char *lldp_file) {
432 _cleanup_free_ char *s = NULL, *t = NULL, *k = NULL;
433 _cleanup_free_ char *temp_path = NULL;
434 _cleanup_fclose_ FILE *f = NULL;
435 uint8_t *mac, *port_id, type;
436 lldp_neighbour_port *p;
437 uint16_t data = 0, length = 0;
447 r = fopen_temporary(lldp_file, &f, &temp_path);
451 fchmod(fileno(f), 0644);
453 HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
454 LIST_FOREACH(port, p, c->ports) {
456 r = lldp_read_chassis_id(p->packet, &type, &length, &mac);
460 memzero(buf, LINE_MAX);
462 sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
463 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
469 r = lldp_read_port_id(p->packet, &type, &length, &port_id);
475 memzero(buf, LINE_MAX);
476 if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
478 k = strndup((char *) port_id, length -1);
482 sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
484 t = strappend(s, buf);
492 sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
493 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
495 t = strappend(s, buf);
504 time = now(CLOCK_BOOTTIME);
506 /* Don't write expired packets */
507 if(time - p->until <= 0) {
512 memzero(buf, LINE_MAX);
513 sprintf(buf, "'_TTL=%lu' ", p->until);
515 t = strappend(s, buf);
522 r = lldp_read_system_name(p->packet, &length, &k);
524 k = strappend(s, "'_NAME=N/A' ");
526 t = strndup(k, length);
530 k = strjoin(s, "'_NAME=", t, "' ", NULL);
539 memzero(buf, LINE_MAX);
541 (void)lldp_read_system_capability(p->packet, &data);
543 sprintf(buf, "'_CAP=%x'", data);
545 t = strappend(s, buf);
549 fprintf(f, "%s\n", t);
563 if (ferror(f) || rename(temp_path, lldp_file) < 0) {
571 log_error("Failed to save lldp data %s: %s", lldp_file, strerror(-r));
576 int sd_lldp_start(sd_lldp *lldp) {
579 assert_return(lldp, -EINVAL);
580 assert_return(lldp->port, -EINVAL);
582 lldp->port->status = LLDP_PORT_STATUS_ENABLED;
584 lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
586 r = lldp_port_start(lldp->port);
588 log_lldp("Failed to start Port : %s , %s",
592 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
597 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
602 int sd_lldp_stop(sd_lldp *lldp) {
605 assert_return(lldp, -EINVAL);
606 assert_return(lldp->port, -EINVAL);
608 lldp->port->status = LLDP_PORT_STATUS_DISABLED;
610 r = lldp_port_stop(lldp->port);
614 lldp_mib_objects_flush(lldp);
619 int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
622 assert_return(lldp, -EINVAL);
623 assert_return(!lldp->port->event, -EBUSY);
626 lldp->port->event = sd_event_ref(event);
628 r = sd_event_default(&lldp->port->event);
633 lldp->port->event_priority = priority;
638 int sd_lldp_detach_event(sd_lldp *lldp) {
640 assert_return(lldp, -EINVAL);
642 lldp->port->event = sd_event_unref(lldp->port->event);
647 int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
648 assert_return(lldp, -EINVAL);
651 lldp->userdata = userdata;
656 void sd_lldp_free(sd_lldp *lldp) {
661 /* Drop all packets */
662 lldp_mib_objects_flush(lldp);
664 lldp_port_free(lldp->port);
666 hashmap_free(lldp->neighbour_mib);
667 prioq_free(lldp->by_expiry);
672 int sd_lldp_new(int ifindex,
674 const struct ether_addr *mac,
676 _cleanup_lldp_free_ sd_lldp *lldp = NULL;
679 assert_return(ret, -EINVAL);
680 assert_return(ifindex > 0, -EINVAL);
681 assert_return(ifname, -EINVAL);
682 assert_return(mac, -EINVAL);
684 lldp = new0(sd_lldp, 1);
688 r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
692 lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
693 if (!lldp->neighbour_mib)
696 r = prioq_ensure_allocated(&lldp->by_expiry,
697 ttl_expiry_item_prioq_compare_func);
701 lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;