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>
27 #include "alloc-util.h"
31 #include "lldp-internal.h"
32 #include "lldp-port.h"
34 #include "lldp-util.h"
36 #include "siphash24.h"
37 #include "string-util.h"
39 typedef enum LLDPAgentRXState
{
40 LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
= 4,
41 LLDP_AGENT_RX_DELETE_AGED_INFO
,
42 LLDP_AGENT_RX_LLDP_INITIALIZE
,
43 LLDP_AGENT_RX_WAIT_FOR_FRAME
,
44 LLDP_AGENT_RX_RX_FRAME
,
45 LLDP_AGENT_RX_DELETE_INFO
,
46 LLDP_AGENT_RX_UPDATE_INFO
,
47 _LLDP_AGENT_RX_STATE_MAX
,
48 _LLDP_AGENT_RX_INVALID
= -1,
51 /* Section 10.5.2.2 Reception counters */
52 struct lldp_agent_statistics
{
53 uint64_t stats_ageouts_total
;
54 uint64_t stats_frames_discarded_total
;
55 uint64_t stats_frames_in_errors_total
;
56 uint64_t stats_frames_in_total
;
57 uint64_t stats_tlvs_discarded_total
;
58 uint64_t stats_tlvs_unrecognized_total
;
65 Hashmap
*neighbour_mib
;
71 LLDPAgentRXState rx_state
;
72 lldp_agent_statistics statistics
;
75 static void chassis_id_hash_func(const void *p
, struct siphash
*state
) {
76 const lldp_chassis_id
*id
= p
;
81 siphash24_compress(&id
->length
, sizeof(id
->length
), state
);
82 siphash24_compress(id
->data
, id
->length
, state
);
85 static int chassis_id_compare_func(const void *_a
, const void *_b
) {
86 const lldp_chassis_id
*a
, *b
;
91 assert(!a
->length
|| a
->data
);
92 assert(!b
->length
|| b
->data
);
94 if (a
->type
!= b
->type
)
97 if (a
->length
!= b
->length
)
98 return a
->length
< b
->length
? -1 : 1;
100 return memcmp(a
->data
, b
->data
, a
->length
);
103 static const struct hash_ops chassis_id_hash_ops
= {
104 .hash
= chassis_id_hash_func
,
105 .compare
= chassis_id_compare_func
108 static void lldp_mib_delete_objects(sd_lldp
*lldp
);
109 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
);
110 static void lldp_run_state_machine(sd_lldp
*ll
);
112 static int lldp_receive_frame(sd_lldp
*lldp
, tlv_packet
*tlv
) {
118 /* Remove expired packets */
119 if (prioq_size(lldp
->by_expiry
) > 0) {
121 lldp_set_state(lldp
, LLDP_AGENT_RX_DELETE_INFO
);
123 lldp_mib_delete_objects(lldp
);
126 r
= lldp_mib_add_objects(lldp
->by_expiry
, lldp
->neighbour_mib
, tlv
);
130 lldp_set_state(lldp
, LLDP_AGENT_RX_UPDATE_INFO
);
132 log_lldp("Packet added. MIB size: %d , PQ size: %d",
133 hashmap_size(lldp
->neighbour_mib
),
134 prioq_size(lldp
->by_expiry
));
136 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 length 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 sd_lldp_packet_unref(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
) {
374 switch (lldp
->rx_state
) {
375 case LLDP_AGENT_RX_UPDATE_INFO
:
376 lldp
->cb(lldp
, SD_LLDP_EVENT_UPDATE_INFO
, lldp
->userdata
);
383 /* 10.5.5.2.1 mibDeleteObjects ()
384 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
385 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
386 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
388 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
389 lldp_neighbour_port
*p
;
392 /* Remove all entries that are past their TTL */
395 if (prioq_size(lldp
->by_expiry
) <= 0)
398 p
= prioq_peek(lldp
->by_expiry
);
403 t
= now(clock_boottime_or_monotonic());
408 lldp_neighbour_port_remove_and_free(p
);
410 lldp
->statistics
.stats_ageouts_total
++;
414 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
415 lldp_neighbour_port
*p
, *q
;
419 assert(lldp
->neighbour_mib
);
420 assert(lldp
->by_expiry
);
422 /* Drop all packets */
423 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
425 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
426 lldp_neighbour_port_remove_and_free(p
);
430 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
431 assert(prioq_size(lldp
->by_expiry
) == 0);
434 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
435 _cleanup_free_
char *temp_path
= NULL
;
436 _cleanup_fclose_
FILE *f
= NULL
;
437 uint8_t *mac
, *port_id
, type
;
438 lldp_neighbour_port
*p
;
439 uint16_t data
= 0, length
= 0;
449 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
453 fchmod(fileno(f
), 0644);
455 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
456 LIST_FOREACH(port
, p
, c
->ports
) {
457 _cleanup_free_
char *s
= NULL
;
460 r
= sd_lldp_packet_read_chassis_id(p
->packet
, &type
, &mac
, &length
);
464 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
465 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
473 r
= sd_lldp_packet_read_port_id(p
->packet
, &type
, &port_id
, &length
);
477 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
478 k
= strndup((char *) port_id
, length
-1);
484 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
488 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
489 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
492 k
= strappend(s
, buf
);
501 time
= now(clock_boottime_or_monotonic());
503 /* Don't write expired packets */
504 if (time
- p
->until
<= 0)
507 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
509 k
= strappend(s
, buf
);
518 r
= sd_lldp_packet_read_system_name(p
->packet
, &k
, &length
);
520 k
= strappend(s
, "'_NAME=N/A' ");
522 t
= strndup(k
, length
);
528 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
540 (void) sd_lldp_packet_read_system_capability(p
->packet
, &data
);
542 sprintf(buf
, "'_CAP=%x'", data
);
544 k
= strappend(s
, buf
);
553 fprintf(f
, "%s\n", s
);
557 r
= fflush_and_check(f
);
561 if (rename(temp_path
, lldp_file
) < 0) {
570 (void) unlink(temp_path
);
572 return log_error_errno(r
, "Failed to save lldp data %s: %m", lldp_file
);
575 int sd_lldp_start(sd_lldp
*lldp
) {
578 assert_return(lldp
, -EINVAL
);
579 assert_return(lldp
->port
, -EINVAL
);
581 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
583 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
585 r
= lldp_port_start(lldp
->port
);
587 log_lldp("Failed to start Port : %s , %s",
591 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
596 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
601 int sd_lldp_stop(sd_lldp
*lldp
) {
604 assert_return(lldp
, -EINVAL
);
605 assert_return(lldp
->port
, -EINVAL
);
607 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
609 r
= lldp_port_stop(lldp
->port
);
613 lldp_mib_objects_flush(lldp
);
618 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
621 assert_return(lldp
, -EINVAL
);
622 assert_return(!lldp
->port
->event
, -EBUSY
);
625 lldp
->port
->event
= sd_event_ref(event
);
627 r
= sd_event_default(&lldp
->port
->event
);
632 lldp
->port
->event_priority
= priority
;
637 int sd_lldp_detach_event(sd_lldp
*lldp
) {
639 assert_return(lldp
, -EINVAL
);
641 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
646 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
647 assert_return(lldp
, -EINVAL
);
650 lldp
->userdata
= userdata
;
655 void sd_lldp_free(sd_lldp
*lldp
) {
660 /* Drop all packets */
661 lldp_mib_objects_flush(lldp
);
663 lldp_port_free(lldp
->port
);
665 hashmap_free(lldp
->neighbour_mib
);
666 prioq_free(lldp
->by_expiry
);
671 int sd_lldp_new(int ifindex
,
673 const struct ether_addr
*mac
,
675 _cleanup_lldp_free_ sd_lldp
*lldp
= NULL
;
678 assert_return(ret
, -EINVAL
);
679 assert_return(ifindex
> 0, -EINVAL
);
680 assert_return(ifname
, -EINVAL
);
681 assert_return(mac
, -EINVAL
);
683 lldp
= new0(sd_lldp
, 1);
687 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
691 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
692 if (!lldp
->neighbour_mib
)
695 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
696 ttl_expiry_item_prioq_compare_func
);
700 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;
708 int sd_lldp_get_packets(sd_lldp
*lldp
, sd_lldp_packet
***tlvs
) {
709 lldp_neighbour_port
*p
;
712 unsigned count
= 0, i
;
714 assert_return(lldp
, -EINVAL
);
715 assert_return(tlvs
, -EINVAL
);
717 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, iter
) {
718 LIST_FOREACH(port
, p
, c
->ports
)
727 *tlvs
= new(sd_lldp_packet
*, count
);
732 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, iter
) {
733 LIST_FOREACH(port
, p
, c
->ports
)
734 (*tlvs
)[i
++] = sd_lldp_packet_ref(p
->packet
);