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_statistics {
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_statistics statistics;
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->statistics.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->statistics.stats_frames_discarded_total ++;
343 lldp->statistics.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->statistics.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 *temp_path = NULL;
433 _cleanup_fclose_ FILE *f = NULL;
434 uint8_t *mac, *port_id, type;
435 lldp_neighbour_port *p;
436 uint16_t data = 0, length = 0;
446 r = fopen_temporary(lldp_file, &f, &temp_path);
450 fchmod(fileno(f), 0644);
452 HASHMAP_FOREACH(c, lldp->neighbour_mib, i) {
453 LIST_FOREACH(port, p, c->ports) {
454 _cleanup_free_ char *s = NULL;
457 r = lldp_read_chassis_id(p->packet, &type, &length, &mac);
461 sprintf(buf, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
462 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
468 r = lldp_read_port_id(p->packet, &type, &length, &port_id);
472 if (type != LLDP_PORT_SUBTYPE_MAC_ADDRESS) {
473 k = strndup((char *) port_id, length -1);
477 sprintf(buf, "'_Port=%s' '_PType=%d' ", k , type);
481 sprintf(buf, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
482 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], type);
485 k = strappend(s, buf);
492 time = now(CLOCK_BOOTTIME);
494 /* Don't write expired packets */
495 if (time - p->until <= 0)
498 sprintf(buf, "'_TTL="USEC_FMT"' ", p->until);
500 k = strappend(s, buf);
507 r = lldp_read_system_name(p->packet, &length, &k);
509 k = strappend(s, "'_NAME=N/A' ");
511 t = strndup(k, length);
515 k = strjoin(s, "'_NAME=", t, "' ", NULL);
525 (void)lldp_read_system_capability(p->packet, &data);
527 sprintf(buf, "'_CAP=%x'", data);
529 k = strappend(s, buf);
536 fprintf(f, "%s\n", s);
543 if (ferror(f) || rename(temp_path, lldp_file) < 0) {
551 log_error("Failed to save lldp data %s: %s", lldp_file, strerror(-r));
556 int sd_lldp_start(sd_lldp *lldp) {
559 assert_return(lldp, -EINVAL);
560 assert_return(lldp->port, -EINVAL);
562 lldp->port->status = LLDP_PORT_STATUS_ENABLED;
564 lldp_set_state(lldp, LLDP_AGENT_RX_LLDP_INITIALIZE);
566 r = lldp_port_start(lldp->port);
568 log_lldp("Failed to start Port : %s , %s",
572 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL);
577 lldp_set_state(lldp, LLDP_AGENT_RX_WAIT_FOR_FRAME);
582 int sd_lldp_stop(sd_lldp *lldp) {
585 assert_return(lldp, -EINVAL);
586 assert_return(lldp->port, -EINVAL);
588 lldp->port->status = LLDP_PORT_STATUS_DISABLED;
590 r = lldp_port_stop(lldp->port);
594 lldp_mib_objects_flush(lldp);
599 int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
602 assert_return(lldp, -EINVAL);
603 assert_return(!lldp->port->event, -EBUSY);
606 lldp->port->event = sd_event_ref(event);
608 r = sd_event_default(&lldp->port->event);
613 lldp->port->event_priority = priority;
618 int sd_lldp_detach_event(sd_lldp *lldp) {
620 assert_return(lldp, -EINVAL);
622 lldp->port->event = sd_event_unref(lldp->port->event);
627 int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_cb_t cb, void *userdata) {
628 assert_return(lldp, -EINVAL);
631 lldp->userdata = userdata;
636 void sd_lldp_free(sd_lldp *lldp) {
641 /* Drop all packets */
642 lldp_mib_objects_flush(lldp);
644 lldp_port_free(lldp->port);
646 hashmap_free(lldp->neighbour_mib);
647 prioq_free(lldp->by_expiry);
652 int sd_lldp_new(int ifindex,
654 const struct ether_addr *mac,
656 _cleanup_lldp_free_ sd_lldp *lldp = NULL;
659 assert_return(ret, -EINVAL);
660 assert_return(ifindex > 0, -EINVAL);
661 assert_return(ifname, -EINVAL);
662 assert_return(mac, -EINVAL);
664 lldp = new0(sd_lldp, 1);
668 r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
672 lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
673 if (!lldp->neighbour_mib)
676 r = prioq_ensure_allocated(&lldp->by_expiry,
677 ttl_expiry_item_prioq_compare_func);
681 lldp->rx_state = LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL;