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
++;
140 log_lldp("Receive frame failed: %s", strerror(-r
));
142 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
147 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
148 int lldp_handle_packet(tlv_packet
*tlv
, uint16_t length
) {
149 uint16_t type
, len
, i
, l
, t
;
150 bool chassis_id
= false;
151 bool malformed
= false;
152 bool port_id
= false;
163 port
= (lldp_port
*) tlv
->userdata
;
164 lldp
= (sd_lldp
*) port
->userdata
;
166 if (lldp
->port
->status
== LLDP_PORT_STATUS_DISABLED
) {
167 log_lldp("Port is disabled : %s . Dropping ...",
172 lldp_set_state(lldp
, LLDP_AGENT_RX_RX_FRAME
);
175 p
+= sizeof(struct ether_header
);
177 for (i
= 1, l
= 0; l
<= length
; i
++) {
179 memcpy(&t
, p
, sizeof(uint16_t));
181 type
= ntohs(t
) >> 9;
182 len
= ntohs(t
) & 0x01ff;
184 if (type
== LLDP_TYPE_END
) {
186 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
196 } else if (type
>=_LLDP_TYPE_MAX
) {
197 log_lldp("TLV type not recognized %d . Dropping ...",
204 /* skip type and lengh encoding */
213 log_lldp("TLV missing or out of order. Dropping ...");
221 case LLDP_TYPE_CHASSIS_ID
:
224 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
232 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
238 /* Look what subtype it has */
239 if (*q
== LLDP_CHASSIS_SUBTYPE_RESERVED
||
240 *q
> LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
) {
241 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
252 case LLDP_TYPE_PORT_ID
:
255 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
263 log_lldp("Duplicate Port ID TLV found. Dropping ...");
269 /* Look what subtype it has */
270 if (*q
== LLDP_PORT_SUBTYPE_RESERVED
||
271 *q
> LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
) {
272 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
287 "Received invalid lenth: %d TTL TLV. Dropping ...",
295 log_lldp("Duplicate TTL TLV found. Dropping ...");
307 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
317 if(!chassis_id
|| !port_id
|| !ttl
|| !end
) {
318 log_lldp( "One or more mandotory TLV missing . Dropping ...");
325 r
= tlv_packet_parse_pdu(tlv
, length
);
327 log_lldp( "Failed to parse the TLV. Dropping ...");
333 return lldp_receive_frame(lldp
, tlv
);
336 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
339 lldp
->statistics
.stats_frames_discarded_total
++;
340 lldp
->statistics
.stats_frames_in_errors_total
++;
343 tlv_packet_free(tlv
);
348 static int ttl_expiry_item_prioq_compare_func(const void *a
, const void *b
) {
349 const lldp_neighbour_port
*p
= a
, *q
= b
;
351 if (p
->until
< q
->until
)
354 if (p
->until
> q
->until
)
360 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
) {
363 assert(state
< _LLDP_AGENT_RX_STATE_MAX
);
365 lldp
->rx_state
= state
;
367 lldp_run_state_machine(lldp
);
370 static void lldp_run_state_machine(sd_lldp
*lldp
) {
372 if (lldp
->rx_state
== LLDP_AGENT_RX_UPDATE_INFO
)
374 lldp
->cb(lldp
, LLDP_AGENT_RX_UPDATE_INFO
, lldp
->userdata
);
377 /* 10.5.5.2.1 mibDeleteObjects ()
378 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
379 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
380 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
382 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
383 lldp_neighbour_port
*p
;
386 /* Remove all entries that are past their TTL */
389 if (prioq_size(lldp
->by_expiry
) <= 0)
392 p
= prioq_peek(lldp
->by_expiry
);
397 t
= now(CLOCK_BOOTTIME
);
402 lldp_neighbour_port_remove_and_free(p
);
404 lldp
->statistics
.stats_ageouts_total
++;
408 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
409 lldp_neighbour_port
*p
, *q
;
413 assert(lldp
->neighbour_mib
);
414 assert(lldp
->by_expiry
);
416 /* Drop all packets */
417 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
419 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
420 lldp_neighbour_port_remove_and_free(p
);
424 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
425 assert(prioq_size(lldp
->by_expiry
) == 0);
428 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
429 _cleanup_free_
char *temp_path
= NULL
;
430 _cleanup_fclose_
FILE *f
= NULL
;
431 uint8_t *mac
, *port_id
, type
;
432 lldp_neighbour_port
*p
;
433 uint16_t data
= 0, length
= 0;
443 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
447 fchmod(fileno(f
), 0644);
449 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
450 LIST_FOREACH(port
, p
, c
->ports
) {
451 _cleanup_free_
char *s
= NULL
;
454 r
= lldp_read_chassis_id(p
->packet
, &type
, &length
, &mac
);
458 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
459 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
465 r
= lldp_read_port_id(p
->packet
, &type
, &length
, &port_id
);
469 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
470 k
= strndup((char *) port_id
, length
-1);
474 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
478 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
479 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
482 k
= strappend(s
, buf
);
489 time
= now(CLOCK_BOOTTIME
);
491 /* Don't write expired packets */
492 if (time
- p
->until
<= 0)
495 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
497 k
= strappend(s
, buf
);
504 r
= lldp_read_system_name(p
->packet
, &length
, &k
);
506 k
= strappend(s
, "'_NAME=N/A' ");
508 t
= strndup(k
, length
);
512 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
522 (void) lldp_read_system_capability(p
->packet
, &data
);
524 sprintf(buf
, "'_CAP=%x'", data
);
526 k
= strappend(s
, buf
);
533 fprintf(f
, "%s\n", s
);
540 if (ferror(f
) || rename(temp_path
, lldp_file
) < 0) {
548 log_error("Failed to save lldp data %s: %s", lldp_file
, strerror(-r
));
553 int sd_lldp_start(sd_lldp
*lldp
) {
556 assert_return(lldp
, -EINVAL
);
557 assert_return(lldp
->port
, -EINVAL
);
559 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
561 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
563 r
= lldp_port_start(lldp
->port
);
565 log_lldp("Failed to start Port : %s , %s",
569 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
574 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
579 int sd_lldp_stop(sd_lldp
*lldp
) {
582 assert_return(lldp
, -EINVAL
);
583 assert_return(lldp
->port
, -EINVAL
);
585 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
587 r
= lldp_port_stop(lldp
->port
);
591 lldp_mib_objects_flush(lldp
);
596 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
599 assert_return(lldp
, -EINVAL
);
600 assert_return(!lldp
->port
->event
, -EBUSY
);
603 lldp
->port
->event
= sd_event_ref(event
);
605 r
= sd_event_default(&lldp
->port
->event
);
610 lldp
->port
->event_priority
= priority
;
615 int sd_lldp_detach_event(sd_lldp
*lldp
) {
617 assert_return(lldp
, -EINVAL
);
619 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
624 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
625 assert_return(lldp
, -EINVAL
);
628 lldp
->userdata
= userdata
;
633 void sd_lldp_free(sd_lldp
*lldp
) {
638 /* Drop all packets */
639 lldp_mib_objects_flush(lldp
);
641 lldp_port_free(lldp
->port
);
643 hashmap_free(lldp
->neighbour_mib
);
644 prioq_free(lldp
->by_expiry
);
649 int sd_lldp_new(int ifindex
,
651 const struct ether_addr
*mac
,
653 _cleanup_lldp_free_ sd_lldp
*lldp
= NULL
;
656 assert_return(ret
, -EINVAL
);
657 assert_return(ifindex
> 0, -EINVAL
);
658 assert_return(ifname
, -EINVAL
);
659 assert_return(mac
, -EINVAL
);
661 lldp
= new0(sd_lldp
, 1);
665 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
669 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
670 if (!lldp
->neighbour_mib
)
673 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
674 ttl_expiry_item_prioq_compare_func
);
678 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;