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"
35 #include "siphash24.h"
36 #include "string-util.h"
38 typedef enum LLDPAgentRXState
{
39 LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
= 4,
40 LLDP_AGENT_RX_DELETE_AGED_INFO
,
41 LLDP_AGENT_RX_LLDP_INITIALIZE
,
42 LLDP_AGENT_RX_WAIT_FOR_FRAME
,
43 LLDP_AGENT_RX_RX_FRAME
,
44 LLDP_AGENT_RX_DELETE_INFO
,
45 LLDP_AGENT_RX_UPDATE_INFO
,
46 _LLDP_AGENT_RX_STATE_MAX
,
47 _LLDP_AGENT_RX_INVALID
= -1,
50 /* Section 10.5.2.2 Reception counters */
51 struct lldp_agent_statistics
{
52 uint64_t stats_ageouts_total
;
53 uint64_t stats_frames_discarded_total
;
54 uint64_t stats_frames_in_errors_total
;
55 uint64_t stats_frames_in_total
;
56 uint64_t stats_tlvs_discarded_total
;
57 uint64_t stats_tlvs_unrecognized_total
;
64 Hashmap
*neighbour_mib
;
70 LLDPAgentRXState rx_state
;
71 lldp_agent_statistics statistics
;
74 static void chassis_id_hash_func(const void *p
, struct siphash
*state
) {
75 const lldp_chassis_id
*id
= p
;
80 siphash24_compress(&id
->length
, sizeof(id
->length
), state
);
81 siphash24_compress(id
->data
, id
->length
, state
);
84 static int chassis_id_compare_func(const void *_a
, const void *_b
) {
85 const lldp_chassis_id
*a
, *b
;
90 assert(!a
->length
|| a
->data
);
91 assert(!b
->length
|| b
->data
);
93 if (a
->type
!= b
->type
)
96 if (a
->length
!= b
->length
)
97 return a
->length
< b
->length
? -1 : 1;
99 return memcmp(a
->data
, b
->data
, a
->length
);
102 static const struct hash_ops chassis_id_hash_ops
= {
103 .hash
= chassis_id_hash_func
,
104 .compare
= chassis_id_compare_func
107 static void lldp_mib_delete_objects(sd_lldp
*lldp
);
108 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
);
109 static void lldp_run_state_machine(sd_lldp
*ll
);
111 static int lldp_receive_frame(sd_lldp
*lldp
, tlv_packet
*tlv
) {
117 /* Remove expired packets */
118 if (prioq_size(lldp
->by_expiry
) > 0) {
120 lldp_set_state(lldp
, LLDP_AGENT_RX_DELETE_INFO
);
122 lldp_mib_delete_objects(lldp
);
125 r
= lldp_mib_add_objects(lldp
->by_expiry
, lldp
->neighbour_mib
, tlv
);
129 lldp_set_state(lldp
, LLDP_AGENT_RX_UPDATE_INFO
);
131 log_lldp("Packet added. MIB size: %d , PQ size: %d",
132 hashmap_size(lldp
->neighbour_mib
),
133 prioq_size(lldp
->by_expiry
));
135 lldp
->statistics
.stats_frames_in_total
++;
139 log_lldp("Receive frame failed: %s", strerror(-r
));
141 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
146 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
147 int lldp_handle_packet(tlv_packet
*tlv
, uint16_t length
) {
148 uint16_t type
, len
, i
, l
, t
;
149 bool chassis_id
= false;
150 bool malformed
= false;
151 bool port_id
= false;
162 port
= (lldp_port
*) tlv
->userdata
;
163 lldp
= (sd_lldp
*) port
->userdata
;
165 if (lldp
->port
->status
== LLDP_PORT_STATUS_DISABLED
) {
166 log_lldp("Port is disabled : %s . Dropping ...",
171 lldp_set_state(lldp
, LLDP_AGENT_RX_RX_FRAME
);
174 p
+= sizeof(struct ether_header
);
176 for (i
= 1, l
= 0; l
<= length
; i
++) {
178 memcpy(&t
, p
, sizeof(uint16_t));
180 type
= ntohs(t
) >> 9;
181 len
= ntohs(t
) & 0x01ff;
183 if (type
== LLDP_TYPE_END
) {
185 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
195 } else if (type
>=_LLDP_TYPE_MAX
) {
196 log_lldp("TLV type not recognized %d . Dropping ...",
203 /* skip type and length encoding */
212 log_lldp("TLV missing or out of order. Dropping ...");
220 case LLDP_TYPE_CHASSIS_ID
:
223 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
231 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
237 /* Look what subtype it has */
238 if (*q
== LLDP_CHASSIS_SUBTYPE_RESERVED
||
239 *q
> LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
) {
240 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
251 case LLDP_TYPE_PORT_ID
:
254 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
262 log_lldp("Duplicate Port ID TLV found. Dropping ...");
268 /* Look what subtype it has */
269 if (*q
== LLDP_PORT_SUBTYPE_RESERVED
||
270 *q
> LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
) {
271 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
286 "Received invalid lenth: %d TTL TLV. Dropping ...",
294 log_lldp("Duplicate TTL TLV found. Dropping ...");
306 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
316 if(!chassis_id
|| !port_id
|| !ttl
|| !end
) {
317 log_lldp( "One or more mandotory TLV missing . Dropping ...");
324 r
= tlv_packet_parse_pdu(tlv
, length
);
326 log_lldp( "Failed to parse the TLV. Dropping ...");
332 return lldp_receive_frame(lldp
, tlv
);
335 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
338 lldp
->statistics
.stats_frames_discarded_total
++;
339 lldp
->statistics
.stats_frames_in_errors_total
++;
342 sd_lldp_packet_unref(tlv
);
347 static int ttl_expiry_item_prioq_compare_func(const void *a
, const void *b
) {
348 const lldp_neighbour_port
*p
= a
, *q
= b
;
350 if (p
->until
< q
->until
)
353 if (p
->until
> q
->until
)
359 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
) {
362 assert(state
< _LLDP_AGENT_RX_STATE_MAX
);
364 lldp
->rx_state
= state
;
366 lldp_run_state_machine(lldp
);
369 static void lldp_run_state_machine(sd_lldp
*lldp
) {
373 switch (lldp
->rx_state
) {
374 case LLDP_AGENT_RX_UPDATE_INFO
:
375 lldp
->cb(lldp
, SD_LLDP_EVENT_UPDATE_INFO
, lldp
->userdata
);
382 /* 10.5.5.2.1 mibDeleteObjects ()
383 * The mibDeleteObjects () procedure deletes all information in the LLDP remote
384 * systems MIB associated with the MSAP identifier if an LLDPDU is received with
385 * an rxTTL value of zero (see 10.3.2) or the timing counter rxInfoTTL expires. */
387 static void lldp_mib_delete_objects(sd_lldp
*lldp
) {
388 lldp_neighbour_port
*p
;
391 /* Remove all entries that are past their TTL */
394 if (prioq_size(lldp
->by_expiry
) <= 0)
397 p
= prioq_peek(lldp
->by_expiry
);
402 t
= now(clock_boottime_or_monotonic());
407 lldp_neighbour_port_remove_and_free(p
);
409 lldp
->statistics
.stats_ageouts_total
++;
413 static void lldp_mib_objects_flush(sd_lldp
*lldp
) {
414 lldp_neighbour_port
*p
, *q
;
418 assert(lldp
->neighbour_mib
);
419 assert(lldp
->by_expiry
);
421 /* Drop all packets */
422 while ((c
= hashmap_steal_first(lldp
->neighbour_mib
))) {
424 LIST_FOREACH_SAFE(port
, p
, q
, c
->ports
) {
425 lldp_neighbour_port_remove_and_free(p
);
429 assert(hashmap_size(lldp
->neighbour_mib
) == 0);
430 assert(prioq_size(lldp
->by_expiry
) == 0);
433 int sd_lldp_save(sd_lldp
*lldp
, const char *lldp_file
) {
434 _cleanup_free_
char *temp_path
= NULL
;
435 _cleanup_fclose_
FILE *f
= NULL
;
436 uint8_t *mac
, *port_id
, type
;
437 lldp_neighbour_port
*p
;
438 uint16_t data
= 0, length
= 0;
448 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
452 fchmod(fileno(f
), 0644);
454 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
455 LIST_FOREACH(port
, p
, c
->ports
) {
456 _cleanup_free_
char *s
= NULL
;
459 r
= sd_lldp_packet_read_chassis_id(p
->packet
, &type
, &mac
, &length
);
463 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
464 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
472 r
= sd_lldp_packet_read_port_id(p
->packet
, &type
, &port_id
, &length
);
476 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
477 k
= strndup((char *) port_id
, length
-1);
483 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
487 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
488 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
491 k
= strappend(s
, buf
);
500 time
= now(clock_boottime_or_monotonic());
502 /* Don't write expired packets */
503 if (time
- p
->until
<= 0)
506 sprintf(buf
, "'_TTL="USEC_FMT
"' ", p
->until
);
508 k
= strappend(s
, buf
);
517 r
= sd_lldp_packet_read_system_name(p
->packet
, &k
, &length
);
519 k
= strappend(s
, "'_NAME=N/A' ");
521 t
= strndup(k
, length
);
527 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
539 (void) sd_lldp_packet_read_system_capability(p
->packet
, &data
);
541 sprintf(buf
, "'_CAP=%x'", data
);
543 k
= strappend(s
, buf
);
552 fprintf(f
, "%s\n", s
);
556 r
= fflush_and_check(f
);
560 if (rename(temp_path
, lldp_file
) < 0) {
569 (void) unlink(temp_path
);
571 return log_error_errno(r
, "Failed to save lldp data %s: %m", lldp_file
);
574 int sd_lldp_start(sd_lldp
*lldp
) {
577 assert_return(lldp
, -EINVAL
);
578 assert_return(lldp
->port
, -EINVAL
);
580 lldp
->port
->status
= LLDP_PORT_STATUS_ENABLED
;
582 lldp_set_state(lldp
, LLDP_AGENT_RX_LLDP_INITIALIZE
);
584 r
= lldp_port_start(lldp
->port
);
586 log_lldp("Failed to start Port : %s , %s",
590 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
);
595 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
600 int sd_lldp_stop(sd_lldp
*lldp
) {
603 assert_return(lldp
, -EINVAL
);
604 assert_return(lldp
->port
, -EINVAL
);
606 lldp
->port
->status
= LLDP_PORT_STATUS_DISABLED
;
608 r
= lldp_port_stop(lldp
->port
);
612 lldp_mib_objects_flush(lldp
);
617 int sd_lldp_attach_event(sd_lldp
*lldp
, sd_event
*event
, int priority
) {
620 assert_return(lldp
, -EINVAL
);
621 assert_return(!lldp
->port
->event
, -EBUSY
);
624 lldp
->port
->event
= sd_event_ref(event
);
626 r
= sd_event_default(&lldp
->port
->event
);
631 lldp
->port
->event_priority
= priority
;
636 int sd_lldp_detach_event(sd_lldp
*lldp
) {
638 assert_return(lldp
, -EINVAL
);
640 lldp
->port
->event
= sd_event_unref(lldp
->port
->event
);
645 int sd_lldp_set_callback(sd_lldp
*lldp
, sd_lldp_cb_t cb
, void *userdata
) {
646 assert_return(lldp
, -EINVAL
);
649 lldp
->userdata
= userdata
;
654 sd_lldp
* sd_lldp_unref(sd_lldp
*lldp
) {
659 /* Drop all packets */
660 lldp_mib_objects_flush(lldp
);
662 lldp_port_free(lldp
->port
);
664 hashmap_free(lldp
->neighbour_mib
);
665 prioq_free(lldp
->by_expiry
);
671 int sd_lldp_new(int ifindex
,
673 const struct ether_addr
*mac
,
675 _cleanup_(sd_lldp_unrefp
) 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
);