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
) {
372 switch (lldp
->rx_state
) {
373 case LLDP_AGENT_RX_UPDATE_INFO
:
374 lldp
->cb(lldp
, SD_LLDP_EVENT_UPDATE_INFO
, lldp
->userdata
);
381 /* 10.5.5.2.1 mibDeleteObjects ()
382 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
383 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
384 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
386 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
387 lldp_neighbour_port
*p
;
390 /* Remove all entries that are past their TTL */
393 if (prioq_size(lldp
->by_expiry
) <= 0)
396 p
= prioq_peek(lldp
->by_expiry
);
401 t
= now(clock_boottime_or_monotonic());
406 lldp_neighbour_port_remove_and_free(p
);
408 lldp
->statistics
.stats_ageouts_total
++;
412 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
413 lldp_neighbour_port
*p
, *q
;
417 assert(lldp
->neighbour_mib
);
418 assert(lldp
->by_expiry
);
420 /* Drop all packets */
421 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
423 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
424 lldp_neighbour_port_remove_and_free(p
);
428 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
429 assert(prioq_size(lldp
->by_expiry
) == 0);
432 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
433 _cleanup_free_
char *temp_path
= NULL
;
434 _cleanup_fclose_
FILE *f
= NULL
;
435 uint8_t *mac
, *port_id
, type
;
436 lldp_neighbour_port
*p
;
437 uint16_t data
= 0, length
= 0;
447 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
451 fchmod(fileno(f
), 0644);
453 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
454 LIST_FOREACH(port
, p
, c
->ports
) {
455 _cleanup_free_
char *s
= NULL
;
458 r
= lldp_read_chassis_id(p
->packet
, &type
, &length
, &mac
);
462 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
463 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
471 r
= lldp_read_port_id(p
->packet
, &type
, &length
, &port_id
);
475 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
476 k
= strndup((char *) port_id
, length
-1);
482 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
486 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
487 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
490 k
= strappend(s
, buf
);
499 time
= now(clock_boottime_or_monotonic());
501 /* Don't write expired packets */
502 if (time
- p
->until
<= 0)
505 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
507 k
= strappend(s
, buf
);
516 r
= lldp_read_system_name(p
->packet
, &length
, &k
);
518 k
= strappend(s
, "'_NAME=N/A' ");
520 t
= strndup(k
, length
);
526 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
538 (void) lldp_read_system_capability(p
->packet
, &data
);
540 sprintf(buf
, "'_CAP=%x'", data
);
542 k
= strappend(s
, buf
);
551 fprintf(f
, "%s\n", s
);
555 r
= fflush_and_check(f
);
559 if (rename(temp_path
, lldp_file
) < 0) {
568 (void) unlink(temp_path
);
570 return log_error_errno(r
, "Failed to save lldp data %s: %m", lldp_file
);
573 int sd_lldp_start(sd_lldp
*lldp
) {
576 assert_return(lldp
, -EINVAL
);
577 assert_return(lldp
->port
, -EINVAL
);
579 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
581 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
583 r
= lldp_port_start(lldp
->port
);
585 log_lldp("Failed to start Port : %s , %s",
589 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
594 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
599 int sd_lldp_stop(sd_lldp
*lldp
) {
602 assert_return(lldp
, -EINVAL
);
603 assert_return(lldp
->port
, -EINVAL
);
605 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
607 r
= lldp_port_stop(lldp
->port
);
611 lldp_mib_objects_flush(lldp
);
616 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
619 assert_return(lldp
, -EINVAL
);
620 assert_return(!lldp
->port
->event
, -EBUSY
);
623 lldp
->port
->event
= sd_event_ref(event
);
625 r
= sd_event_default(&lldp
->port
->event
);
630 lldp
->port
->event_priority
= priority
;
635 int sd_lldp_detach_event(sd_lldp
*lldp
) {
637 assert_return(lldp
, -EINVAL
);
639 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
644 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
645 assert_return(lldp
, -EINVAL
);
648 lldp
->userdata
= userdata
;
653 void sd_lldp_free(sd_lldp
*lldp
) {
658 /* Drop all packets */
659 lldp_mib_objects_flush(lldp
);
661 lldp_port_free(lldp
->port
);
663 hashmap_free(lldp
->neighbour_mib
);
664 prioq_free(lldp
->by_expiry
);
669 int sd_lldp_new(int ifindex
,
671 const struct ether_addr
*mac
,
673 _cleanup_lldp_free_ sd_lldp
*lldp
= NULL
;
676 assert_return(ret
, -EINVAL
);
677 assert_return(ifindex
> 0, -EINVAL
);
678 assert_return(ifname
, -EINVAL
);
679 assert_return(mac
, -EINVAL
);
681 lldp
= new0(sd_lldp
, 1);
685 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
689 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
690 if (!lldp
->neighbour_mib
)
693 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
694 ttl_expiry_item_prioq_compare_func
);
698 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;