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"
29 #include "lldp-port.h"
32 #include "lldp-internal.h"
33 #include "lldp-util.h"
35 typedef enum LLDPAgentRXState
{
36 LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
= 4,
37 LLDP_AGENT_RX_DELETE_AGED_INFO
,
38 LLDP_AGENT_RX_LLDP_INITIALIZE
,
39 LLDP_AGENT_RX_WAIT_FOR_FRAME
,
40 LLDP_AGENT_RX_RX_FRAME
,
41 LLDP_AGENT_RX_DELETE_INFO
,
42 LLDP_AGENT_RX_UPDATE_INFO
,
43 _LLDP_AGENT_RX_STATE_MAX
,
44 _LLDP_AGENT_RX_INVALID
= -1,
47 /* Section 10.5.2.2 Reception counters */
48 struct lldp_agent_statistics
{
49 uint64_t stats_ageouts_total
;
50 uint64_t stats_frames_discarded_total
;
51 uint64_t stats_frames_in_errors_total
;
52 uint64_t stats_frames_in_total
;
53 uint64_t stats_tlvs_discarded_total
;
54 uint64_t stats_tlvs_unrecognized_total
;
61 Hashmap
*neighbour_mib
;
67 LLDPAgentRXState rx_state
;
68 lldp_agent_statistics statistics
;
71 static unsigned long chassis_id_hash_func(const void *p
,
72 const uint8_t hash_key
[HASH_KEY_SIZE
]) {
74 const lldp_chassis_id
*id
= p
;
78 siphash24((uint8_t *) &u
, id
->data
, id
->length
, hash_key
);
80 return (unsigned long) u
;
83 static int chassis_id_compare_func(const void *_a
, const void *_b
) {
84 const lldp_chassis_id
*a
, *b
;
89 assert(!a
->length
|| a
->data
);
90 assert(!b
->length
|| b
->data
);
92 if (a
->type
!= b
->type
)
95 if (a
->length
!= b
->length
)
96 return a
->length
< b
->length
? -1 : 1;
98 return memcmp(a
->data
, b
->data
, a
->length
);
101 static const struct hash_ops chassis_id_hash_ops
= {
102 .hash
= chassis_id_hash_func
,
103 .compare
= chassis_id_compare_func
106 static void lldp_mib_delete_objects(sd_lldp
*lldp
);
107 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
);
108 static void lldp_run_state_machine(sd_lldp
*ll
);
110 static int lldp_receive_frame(sd_lldp
*lldp
, tlv_packet
*tlv
) {
116 /* Remove expired packets */
117 if (prioq_size(lldp
->by_expiry
) > 0) {
119 lldp_set_state(lldp
, LLDP_AGENT_RX_DELETE_INFO
);
121 lldp_mib_delete_objects(lldp
);
124 r
= lldp_mib_add_objects(lldp
->by_expiry
, lldp
->neighbour_mib
, tlv
);
128 lldp_set_state(lldp
, LLDP_AGENT_RX_UPDATE_INFO
);
130 log_lldp("Packet added. MIB size: %d , PQ size: %d",
131 hashmap_size(lldp
->neighbour_mib
),
132 prioq_size(lldp
->by_expiry
));
134 lldp
->statistics
.stats_frames_in_total
++;
138 log_lldp("Receive frame failed: %s", strerror(-r
));
140 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
145 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
146 int lldp_handle_packet(tlv_packet
*tlv
, uint16_t length
) {
147 uint16_t type
, len
, i
, l
, t
;
148 bool chassis_id
= false;
149 bool malformed
= false;
150 bool port_id
= false;
161 port
= (lldp_port
*) tlv
->userdata
;
162 lldp
= (sd_lldp
*) port
->userdata
;
164 if (lldp
->port
->status
== LLDP_PORT_STATUS_DISABLED
) {
165 log_lldp("Port is disabled : %s . Dropping ...",
170 lldp_set_state(lldp
, LLDP_AGENT_RX_RX_FRAME
);
173 p
+= sizeof(struct ether_header
);
175 for (i
= 1, l
= 0; l
<= length
; i
++) {
177 memcpy(&t
, p
, sizeof(uint16_t));
179 type
= ntohs(t
) >> 9;
180 len
= ntohs(t
) & 0x01ff;
182 if (type
== LLDP_TYPE_END
) {
184 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
194 } else if (type
>=_LLDP_TYPE_MAX
) {
195 log_lldp("TLV type not recognized %d . Dropping ...",
202 /* skip type and lengh encoding */
211 log_lldp("TLV missing or out of order. Dropping ...");
219 case LLDP_TYPE_CHASSIS_ID
:
222 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
230 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
236 /* Look what subtype it has */
237 if (*q
== LLDP_CHASSIS_SUBTYPE_RESERVED
||
238 *q
> LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
) {
239 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
250 case LLDP_TYPE_PORT_ID
:
253 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
261 log_lldp("Duplicate Port ID TLV found. Dropping ...");
267 /* Look what subtype it has */
268 if (*q
== LLDP_PORT_SUBTYPE_RESERVED
||
269 *q
> LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
) {
270 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
285 "Received invalid lenth: %d TTL TLV. Dropping ...",
293 log_lldp("Duplicate TTL TLV found. Dropping ...");
305 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
315 if(!chassis_id
|| !port_id
|| !ttl
|| !end
) {
316 log_lldp( "One or more mandotory TLV missing . Dropping ...");
323 r
= tlv_packet_parse_pdu(tlv
, length
);
325 log_lldp( "Failed to parse the TLV. Dropping ...");
331 return lldp_receive_frame(lldp
, tlv
);
334 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
337 lldp
->statistics
.stats_frames_discarded_total
++;
338 lldp
->statistics
.stats_frames_in_errors_total
++;
341 tlv_packet_free(tlv
);
346 static int ttl_expiry_item_prioq_compare_func(const void *a
, const void *b
) {
347 const lldp_neighbour_port
*p
= a
, *q
= b
;
349 if (p
->until
< q
->until
)
352 if (p
->until
> q
->until
)
358 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
) {
361 assert(state
< _LLDP_AGENT_RX_STATE_MAX
);
363 lldp
->rx_state
= state
;
365 lldp_run_state_machine(lldp
);
368 static void lldp_run_state_machine(sd_lldp
*lldp
) {
370 if (lldp
->rx_state
== LLDP_AGENT_RX_UPDATE_INFO
)
372 lldp
->cb(lldp
, LLDP_AGENT_RX_UPDATE_INFO
, lldp
->userdata
);
375 /* 10.5.5.2.1 mibDeleteObjects ()
376 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
377 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
378 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
380 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
381 lldp_neighbour_port
*p
;
384 /* Remove all entries that are past their TTL */
387 if (prioq_size(lldp
->by_expiry
) <= 0)
390 p
= prioq_peek(lldp
->by_expiry
);
395 t
= now(CLOCK_BOOTTIME
);
400 lldp_neighbour_port_remove_and_free(p
);
402 lldp
->statistics
.stats_ageouts_total
++;
406 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
407 lldp_neighbour_port
*p
, *q
;
411 assert(lldp
->neighbour_mib
);
412 assert(lldp
->by_expiry
);
414 /* Drop all packets */
415 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
417 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
418 lldp_neighbour_port_remove_and_free(p
);
422 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
423 assert(prioq_size(lldp
->by_expiry
) == 0);
426 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
427 _cleanup_free_
char *temp_path
= NULL
;
428 _cleanup_fclose_
FILE *f
= NULL
;
429 uint8_t *mac
, *port_id
, type
;
430 lldp_neighbour_port
*p
;
431 uint16_t data
= 0, length
= 0;
441 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
445 fchmod(fileno(f
), 0644);
447 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
448 LIST_FOREACH(port
, p
, c
->ports
) {
449 _cleanup_free_
char *s
= NULL
;
452 r
= lldp_read_chassis_id(p
->packet
, &type
, &length
, &mac
);
456 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
457 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
463 r
= lldp_read_port_id(p
->packet
, &type
, &length
, &port_id
);
467 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
468 k
= strndup((char *) port_id
, length
-1);
472 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
476 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
477 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
480 k
= strappend(s
, buf
);
487 time
= now(CLOCK_BOOTTIME
);
489 /* Don't write expired packets */
490 if (time
- p
->until
<= 0)
493 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
495 k
= strappend(s
, buf
);
502 r
= lldp_read_system_name(p
->packet
, &length
, &k
);
504 k
= strappend(s
, "'_NAME=N/A' ");
506 t
= strndup(k
, length
);
510 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
520 (void) lldp_read_system_capability(p
->packet
, &data
);
522 sprintf(buf
, "'_CAP=%x'", data
);
524 k
= strappend(s
, buf
);
531 fprintf(f
, "%s\n", s
);
538 if (ferror(f
) || rename(temp_path
, lldp_file
) < 0) {
546 log_error("Failed to save lldp data %s: %s", lldp_file
, strerror(-r
));
551 int sd_lldp_start(sd_lldp
*lldp
) {
554 assert_return(lldp
, -EINVAL
);
555 assert_return(lldp
->port
, -EINVAL
);
557 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
559 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
561 r
= lldp_port_start(lldp
->port
);
563 log_lldp("Failed to start Port : %s , %s",
567 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
572 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
577 int sd_lldp_stop(sd_lldp
*lldp
) {
580 assert_return(lldp
, -EINVAL
);
581 assert_return(lldp
->port
, -EINVAL
);
583 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
585 r
= lldp_port_stop(lldp
->port
);
589 lldp_mib_objects_flush(lldp
);
594 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
597 assert_return(lldp
, -EINVAL
);
598 assert_return(!lldp
->port
->event
, -EBUSY
);
601 lldp
->port
->event
= sd_event_ref(event
);
603 r
= sd_event_default(&lldp
->port
->event
);
608 lldp
->port
->event_priority
= priority
;
613 int sd_lldp_detach_event(sd_lldp
*lldp
) {
615 assert_return(lldp
, -EINVAL
);
617 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
622 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
623 assert_return(lldp
, -EINVAL
);
626 lldp
->userdata
= userdata
;
631 void sd_lldp_free(sd_lldp
*lldp
) {
636 /* Drop all packets */
637 lldp_mib_objects_flush(lldp
);
639 lldp_port_free(lldp
->port
);
641 hashmap_free(lldp
->neighbour_mib
);
642 prioq_free(lldp
->by_expiry
);
647 int sd_lldp_new(int ifindex
,
649 const struct ether_addr
*mac
,
651 _cleanup_lldp_free_ sd_lldp
*lldp
= NULL
;
654 assert_return(ret
, -EINVAL
);
655 assert_return(ifindex
> 0, -EINVAL
);
656 assert_return(ifname
, -EINVAL
);
657 assert_return(mac
, -EINVAL
);
659 lldp
= new0(sd_lldp
, 1);
663 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
667 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
668 if (!lldp
->neighbour_mib
)
671 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
672 ttl_expiry_item_prioq_compare_func
);
676 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;