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"
33 #include "lldp-internal.h"
35 /* Section 10.5.2.2 Reception counters */
36 struct lldp_agent_statitics {
37 uint64_t stats_ageouts_total;
38 uint64_t stats_frames_discarded_total;
39 uint64_t stats_frames_in_errors_total;
40 uint64_t stats_frames_in_total;
41 uint64_t stats_tlvs_discarded_total;
42 uint64_t stats_tlvs_unrecognized_total;
49 Hashmap *neighbour_mib;
51 lldp_agent_statitics statitics;
54 static unsigned long chassis_id_hash_func(const void *p,
55 const uint8_t hash_key[HASH_KEY_SIZE]) {
57 const lldp_chassis_id *id = p;
61 siphash24((uint8_t *) &u, id->data, id->length, hash_key);
63 return (unsigned long) u;
66 static int chassis_id_compare_func(const void *_a, const void *_b) {
67 const lldp_chassis_id *a, *b;
72 assert(!a->length || a->data);
73 assert(!b->length || b->data);
75 if (a->type != b->type)
78 if (a->length != b->length)
79 return a->length < b->length ? -1 : 1;
81 return memcmp(a->data, b->data, a->length);
84 static const struct hash_ops chassis_id_hash_ops = {
85 .hash = chassis_id_hash_func,
86 .compare = chassis_id_compare_func
89 static void lldp_mib_delete_objects(sd_lldp *lldp);
91 static int lldp_receive_frame(sd_lldp *lldp, tlv_packet *tlv) {
97 /* Remove expired packets */
98 if (prioq_size(lldp->by_expiry) > 0)
99 lldp_mib_delete_objects(lldp);
101 r = lldp_mib_add_objects(lldp->by_expiry, lldp->neighbour_mib, tlv);
105 log_lldp("Packet added. MIB size: %d , PQ size: %d",
106 hashmap_size(lldp->neighbour_mib),
107 prioq_size(lldp->by_expiry));
109 lldp->statitics.stats_frames_in_total ++;
115 log_lldp("Receive frame failed: %s", strerror(-r));
120 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
121 int lldp_handle_packet(tlv_packet *tlv, uint16_t length) {
122 uint16_t type, len, i, l, t;
123 bool chassis_id = false;
124 bool malformed = false;
125 bool port_id = false;
136 port = (lldp_port *) tlv->userdata;
137 lldp = (sd_lldp *) port->userdata;
139 if (lldp->port->status == LLDP_PORT_STATUS_DISABLED) {
140 log_lldp("Port is disabled : %s . Dropping ...",
146 p += sizeof(struct ether_header);
148 for (i = 1, l = 0; l <= length; i++) {
150 memcpy(&t, p, sizeof(uint16_t));
152 type = ntohs(t) >> 9;
153 len = ntohs(t) & 0x01ff;
155 if (type == LLDP_TYPE_END) {
157 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
167 } else if (type >=_LLDP_TYPE_MAX) {
168 log_lldp("TLV type not recognized %d . Dropping ...",
175 /* skip type and lengh encoding */
184 log_lldp("TLV missing or out of order. Dropping ...");
192 case LLDP_TYPE_CHASSIS_ID:
195 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
203 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
209 /* Look what subtype it has */
210 if (*q == LLDP_CHASSIS_SUBTYPE_RESERVED ||
211 *q > LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED) {
212 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
223 case LLDP_TYPE_PORT_ID:
226 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
234 log_lldp("Duplicate Port ID TLV found. Dropping ...");
240 /* Look what subtype it has */
241 if (*q == LLDP_PORT_SUBTYPE_RESERVED ||
242 *q > LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED) {
243 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
258 "Received invalid lenth: %d TTL TLV. Dropping ...",
266 log_lldp("Duplicate TTL TLV found. Dropping ...");
278 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
288 if(!chassis_id || !port_id || !ttl || !end) {
289 log_lldp( "One or more mandotory TLV missing . Dropping ...");
296 r = tlv_packet_parse_pdu(tlv, length);
298 log_lldp( "Failed to parse the TLV. Dropping ...");
304 return lldp_receive_frame(lldp, tlv);
308 lldp->statitics.stats_frames_discarded_total ++;
309 lldp->statitics.stats_frames_in_errors_total ++;
312 tlv_packet_free(tlv);
317 static int ttl_expiry_item_prioq_compare_func(const void *a, const void *b) {
318 const lldp_neighbour_port *p = a, *q = b;
320 if (p->until < q->until)
323 if (p->until > q->until)
329 /* 10.5.5.2.1 mibDeleteObjects ()
330 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
331 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
332 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
334 static void lldp_mib_delete_objects(sd_lldp *lldp) {
335 lldp_neighbour_port *p;
338 /* Remove all entries that are past their TTL */
341 if (prioq_size(lldp->by_expiry) <= 0)
344 p = prioq_peek(lldp->by_expiry);
349 t = now(CLOCK_BOOTTIME);
354 lldp_neighbour_port_remove_and_free(p);
356 lldp->statitics.stats_ageouts_total ++;
360 static void lldp_mib_objects_flush(sd_lldp *lldp) {
361 lldp_neighbour_port *p, *q;
365 assert(lldp->neighbour_mib);
366 assert(lldp->by_expiry);
368 /* Drop all packets */
369 while ((c = hashmap_steal_first(lldp->neighbour_mib))) {
371 LIST_FOREACH_SAFE(port, p, q, c->ports) {
372 lldp_neighbour_port_remove_and_free(p);
376 assert(hashmap_size(lldp->neighbour_mib) == 0);
377 assert(prioq_size(lldp->by_expiry) == 0);
380 int sd_lldp_start(sd_lldp *lldp) {
383 assert_return(lldp, -EINVAL);
384 assert_return(lldp->port, -EINVAL);
386 lldp->port->status = LLDP_PORT_STATUS_ENABLED;
388 r = lldp_port_start(lldp->port);
390 log_lldp("Failed to start Port : %s , %s",
399 int sd_lldp_stop(sd_lldp *lldp) {
402 assert_return(lldp, -EINVAL);
403 assert_return(lldp->port, -EINVAL);
405 lldp->port->status = LLDP_PORT_STATUS_DISABLED;
407 r = lldp_port_stop(lldp->port);
411 lldp_mib_objects_flush(lldp);
416 int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int priority) {
419 assert_return(lldp, -EINVAL);
420 assert_return(!lldp->port->event, -EBUSY);
423 lldp->port->event = sd_event_ref(event);
425 r = sd_event_default(&lldp->port->event);
430 lldp->port->event_priority = priority;
435 int sd_lldp_detach_event(sd_lldp *lldp) {
437 assert_return(lldp, -EINVAL);
439 lldp->port->event = sd_event_unref(lldp->port->event);
444 void sd_lldp_free(sd_lldp *lldp) {
449 /* Drop all packets */
450 lldp_mib_objects_flush(lldp);
452 lldp_port_free(lldp->port);
454 hashmap_free(lldp->neighbour_mib);
455 prioq_free(lldp->by_expiry);
460 int sd_lldp_new(int ifindex,
462 struct ether_addr *mac,
464 _cleanup_sd_lldp_free_ sd_lldp *lldp = NULL;
467 assert_return(ret, -EINVAL);
468 assert_return(ifindex > 0, -EINVAL);
469 assert_return(ifname, -EINVAL);
470 assert_return(mac, -EINVAL);
472 lldp = new0(sd_lldp, 1);
476 r = lldp_port_new(ifindex, ifname, mac, lldp, &lldp->port);
480 lldp->neighbour_mib = hashmap_new(&chassis_id_hash_ops);
481 if (!lldp->neighbour_mib)
484 r = prioq_ensure_allocated(&lldp->by_expiry,
485 ttl_expiry_item_prioq_compare_func);