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 void chassis_id_hash_func(const void *p
, struct siphash
*state
) {
72 const lldp_chassis_id
*id
= p
;
77 siphash24_compress(&id
->length
, sizeof(id
->length
), state
);
78 siphash24_compress(id
->data
, id
->length
, state
);
81 static int chassis_id_compare_func(const void *_a
, const void *_b
) {
82 const lldp_chassis_id
*a
, *b
;
87 assert(!a
->length
|| a
->data
);
88 assert(!b
->length
|| b
->data
);
90 if (a
->type
!= b
->type
)
93 if (a
->length
!= b
->length
)
94 return a
->length
< b
->length
? -1 : 1;
96 return memcmp(a
->data
, b
->data
, a
->length
);
99 static const struct hash_ops chassis_id_hash_ops
= {
100 .hash
= chassis_id_hash_func
,
101 .compare
= chassis_id_compare_func
104 static void lldp_mib_delete_objects(sd_lldp
*lldp
);
105 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
);
106 static void lldp_run_state_machine(sd_lldp
*ll
);
108 static int lldp_receive_frame(sd_lldp
*lldp
, tlv_packet
*tlv
) {
114 /* Remove expired packets */
115 if (prioq_size(lldp
->by_expiry
) > 0) {
117 lldp_set_state(lldp
, LLDP_AGENT_RX_DELETE_INFO
);
119 lldp_mib_delete_objects(lldp
);
122 r
= lldp_mib_add_objects(lldp
->by_expiry
, lldp
->neighbour_mib
, tlv
);
126 lldp_set_state(lldp
, LLDP_AGENT_RX_UPDATE_INFO
);
128 log_lldp("Packet added. MIB size: %d , PQ size: %d",
129 hashmap_size(lldp
->neighbour_mib
),
130 prioq_size(lldp
->by_expiry
));
132 lldp
->statistics
.stats_frames_in_total
++;
136 log_lldp("Receive frame failed: %s", strerror(-r
));
138 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
143 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
144 int lldp_handle_packet(tlv_packet
*tlv
, uint16_t length
) {
145 uint16_t type
, len
, i
, l
, t
;
146 bool chassis_id
= false;
147 bool malformed
= false;
148 bool port_id
= false;
159 port
= (lldp_port
*) tlv
->userdata
;
160 lldp
= (sd_lldp
*) port
->userdata
;
162 if (lldp
->port
->status
== LLDP_PORT_STATUS_DISABLED
) {
163 log_lldp("Port is disabled : %s . Dropping ...",
168 lldp_set_state(lldp
, LLDP_AGENT_RX_RX_FRAME
);
171 p
+= sizeof(struct ether_header
);
173 for (i
= 1, l
= 0; l
<= length
; i
++) {
175 memcpy(&t
, p
, sizeof(uint16_t));
177 type
= ntohs(t
) >> 9;
178 len
= ntohs(t
) & 0x01ff;
180 if (type
== LLDP_TYPE_END
) {
182 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
192 } else if (type
>=_LLDP_TYPE_MAX
) {
193 log_lldp("TLV type not recognized %d . Dropping ...",
200 /* skip type and length encoding */
209 log_lldp("TLV missing or out of order. Dropping ...");
217 case LLDP_TYPE_CHASSIS_ID
:
220 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
228 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
234 /* Look what subtype it has */
235 if (*q
== LLDP_CHASSIS_SUBTYPE_RESERVED
||
236 *q
> LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
) {
237 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
248 case LLDP_TYPE_PORT_ID
:
251 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
259 log_lldp("Duplicate Port ID TLV found. Dropping ...");
265 /* Look what subtype it has */
266 if (*q
== LLDP_PORT_SUBTYPE_RESERVED
||
267 *q
> LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
) {
268 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
283 "Received invalid lenth: %d TTL TLV. Dropping ...",
291 log_lldp("Duplicate TTL TLV found. Dropping ...");
303 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
313 if(!chassis_id
|| !port_id
|| !ttl
|| !end
) {
314 log_lldp( "One or more mandotory TLV missing . Dropping ...");
321 r
= tlv_packet_parse_pdu(tlv
, length
);
323 log_lldp( "Failed to parse the TLV. Dropping ...");
329 return lldp_receive_frame(lldp
, tlv
);
332 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
335 lldp
->statistics
.stats_frames_discarded_total
++;
336 lldp
->statistics
.stats_frames_in_errors_total
++;
339 sd_lldp_packet_unref(tlv
);
344 static int ttl_expiry_item_prioq_compare_func(const void *a
, const void *b
) {
345 const lldp_neighbour_port
*p
= a
, *q
= b
;
347 if (p
->until
< q
->until
)
350 if (p
->until
> q
->until
)
356 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
) {
359 assert(state
< _LLDP_AGENT_RX_STATE_MAX
);
361 lldp
->rx_state
= state
;
363 lldp_run_state_machine(lldp
);
366 static void lldp_run_state_machine(sd_lldp
*lldp
) {
370 switch (lldp
->rx_state
) {
371 case LLDP_AGENT_RX_UPDATE_INFO
:
372 lldp
->cb(lldp
, SD_LLDP_EVENT_UPDATE_INFO
, lldp
->userdata
);
379 /* 10.5.5.2.1 mibDeleteObjects ()
380 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
381 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
382 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
384 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
385 lldp_neighbour_port
*p
;
388 /* Remove all entries that are past their TTL */
391 if (prioq_size(lldp
->by_expiry
) <= 0)
394 p
= prioq_peek(lldp
->by_expiry
);
399 t
= now(clock_boottime_or_monotonic());
404 lldp_neighbour_port_remove_and_free(p
);
406 lldp
->statistics
.stats_ageouts_total
++;
410 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
411 lldp_neighbour_port
*p
, *q
;
415 assert(lldp
->neighbour_mib
);
416 assert(lldp
->by_expiry
);
418 /* Drop all packets */
419 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
421 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
422 lldp_neighbour_port_remove_and_free(p
);
426 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
427 assert(prioq_size(lldp
->by_expiry
) == 0);
430 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
431 _cleanup_free_
char *temp_path
= NULL
;
432 _cleanup_fclose_
FILE *f
= NULL
;
433 uint8_t *mac
, *port_id
, type
;
434 lldp_neighbour_port
*p
;
435 uint16_t data
= 0, length
= 0;
445 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
449 fchmod(fileno(f
), 0644);
451 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
452 LIST_FOREACH(port
, p
, c
->ports
) {
453 _cleanup_free_
char *s
= NULL
;
456 r
= sd_lldp_packet_read_chassis_id(p
->packet
, &type
, &mac
, &length
);
460 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
461 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
469 r
= sd_lldp_packet_read_port_id(p
->packet
, &type
, &port_id
, &length
);
473 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
474 k
= strndup((char *) port_id
, length
-1);
480 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
484 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
485 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
488 k
= strappend(s
, buf
);
497 time
= now(clock_boottime_or_monotonic());
499 /* Don't write expired packets */
500 if (time
- p
->until
<= 0)
503 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
505 k
= strappend(s
, buf
);
514 r
= sd_lldp_packet_read_system_name(p
->packet
, &k
, &length
);
516 k
= strappend(s
, "'_NAME=N/A' ");
518 t
= strndup(k
, length
);
524 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
536 (void) sd_lldp_packet_read_system_capability(p
->packet
, &data
);
538 sprintf(buf
, "'_CAP=%x'", data
);
540 k
= strappend(s
, buf
);
549 fprintf(f
, "%s\n", s
);
553 r
= fflush_and_check(f
);
557 if (rename(temp_path
, lldp_file
) < 0) {
566 (void) unlink(temp_path
);
568 return log_error_errno(r
, "Failed to save lldp data %s: %m", lldp_file
);
571 int sd_lldp_start(sd_lldp
*lldp
) {
574 assert_return(lldp
, -EINVAL
);
575 assert_return(lldp
->port
, -EINVAL
);
577 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
579 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
581 r
= lldp_port_start(lldp
->port
);
583 log_lldp("Failed to start Port : %s , %s",
587 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
592 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
597 int sd_lldp_stop(sd_lldp
*lldp
) {
600 assert_return(lldp
, -EINVAL
);
601 assert_return(lldp
->port
, -EINVAL
);
603 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
605 r
= lldp_port_stop(lldp
->port
);
609 lldp_mib_objects_flush(lldp
);
614 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
617 assert_return(lldp
, -EINVAL
);
618 assert_return(!lldp
->port
->event
, -EBUSY
);
621 lldp
->port
->event
= sd_event_ref(event
);
623 r
= sd_event_default(&lldp
->port
->event
);
628 lldp
->port
->event_priority
= priority
;
633 int sd_lldp_detach_event(sd_lldp
*lldp
) {
635 assert_return(lldp
, -EINVAL
);
637 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
642 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
643 assert_return(lldp
, -EINVAL
);
646 lldp
->userdata
= userdata
;
651 void sd_lldp_free(sd_lldp
*lldp
) {
656 /* Drop all packets */
657 lldp_mib_objects_flush(lldp
);
659 lldp_port_free(lldp
->port
);
661 hashmap_free(lldp
->neighbour_mib
);
662 prioq_free(lldp
->by_expiry
);
667 int sd_lldp_new(int ifindex
,
669 const struct ether_addr
*mac
,
671 _cleanup_lldp_free_ sd_lldp
*lldp
= NULL
;
674 assert_return(ret
, -EINVAL
);
675 assert_return(ifindex
> 0, -EINVAL
);
676 assert_return(ifname
, -EINVAL
);
677 assert_return(mac
, -EINVAL
);
679 lldp
= new0(sd_lldp
, 1);
683 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
687 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
688 if (!lldp
->neighbour_mib
)
691 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
692 ttl_expiry_item_prioq_compare_func
);
696 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;
704 int sd_lldp_get_packets(sd_lldp
*lldp
, sd_lldp_packet
***tlvs
) {
705 lldp_neighbour_port
*p
;
708 unsigned count
= 0, i
;
710 assert_return(lldp
, -EINVAL
);
711 assert_return(tlvs
, -EINVAL
);
713 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, iter
) {
714 LIST_FOREACH(port
, p
, c
->ports
)
723 *tlvs
= new(sd_lldp_packet
*, count
);
728 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, iter
) {
729 LIST_FOREACH(port
, p
, c
->ports
)
730 (*tlvs
)[i
++] = sd_lldp_packet_ref(p
->packet
);