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"
27 #include "event-util.h"
30 #include "lldp-port.h"
34 #include "lldp-internal.h"
35 #include "ether-addr-util.h"
37 typedef enum LLDPAgentRXState
{
38 LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
= 4,
39 LLDP_AGENT_RX_DELETE_AGED_INFO
,
40 LLDP_AGENT_RX_LLDP_INITIALIZE
,
41 LLDP_AGENT_RX_WAIT_FOR_FRAME
,
42 LLDP_AGENT_RX_RX_FRAME
,
43 LLDP_AGENT_RX_DELETE_INFO
,
44 LLDP_AGENT_RX_UPDATE_INFO
,
45 _LLDP_AGENT_RX_STATE_MAX
,
46 _LLDP_AGENT_RX_INVALID
= -1,
49 /* Section 10.5.2.2 Reception counters */
50 struct lldp_agent_statitics
{
51 uint64_t stats_ageouts_total
;
52 uint64_t stats_frames_discarded_total
;
53 uint64_t stats_frames_in_errors_total
;
54 uint64_t stats_frames_in_total
;
55 uint64_t stats_tlvs_discarded_total
;
56 uint64_t stats_tlvs_unrecognized_total
;
63 Hashmap
*neighbour_mib
;
69 LLDPAgentRXState rx_state
;
70 lldp_agent_statitics statitics
;
73 static unsigned long chassis_id_hash_func(const void *p
,
74 const uint8_t hash_key
[HASH_KEY_SIZE
]) {
76 const lldp_chassis_id
*id
= p
;
80 siphash24((uint8_t *) &u
, id
->data
, id
->length
, hash_key
);
82 return (unsigned long) u
;
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
->statitics
.stats_frames_in_total
++;
142 log_lldp("Receive frame failed: %s", strerror(-r
));
144 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
149 /* 10.3.2 LLDPDU validation: rxProcessFrame() */
150 int lldp_handle_packet(tlv_packet
*tlv
, uint16_t length
) {
151 uint16_t type
, len
, i
, l
, t
;
152 bool chassis_id
= false;
153 bool malformed
= false;
154 bool port_id
= false;
165 port
= (lldp_port
*) tlv
->userdata
;
166 lldp
= (sd_lldp
*) port
->userdata
;
168 if (lldp
->port
->status
== LLDP_PORT_STATUS_DISABLED
) {
169 log_lldp("Port is disabled : %s . Dropping ...",
174 lldp_set_state(lldp
, LLDP_AGENT_RX_RX_FRAME
);
177 p
+= sizeof(struct ether_header
);
179 for (i
= 1, l
= 0; l
<= length
; i
++) {
181 memcpy(&t
, p
, sizeof(uint16_t));
183 type
= ntohs(t
) >> 9;
184 len
= ntohs(t
) & 0x01ff;
186 if (type
== LLDP_TYPE_END
) {
188 log_lldp("TLV type end is not length 0. Length:%d received . Dropping ...",
198 } else if (type
>=_LLDP_TYPE_MAX
) {
199 log_lldp("TLV type not recognized %d . Dropping ...",
206 /* skip type and lengh encoding */
215 log_lldp("TLV missing or out of order. Dropping ...");
223 case LLDP_TYPE_CHASSIS_ID
:
226 log_lldp("Received malformed Chassis ID TLV len = %d. Dropping",
234 log_lldp("Duplicate Chassis ID TLV found. Dropping ...");
240 /* Look what subtype it has */
241 if (*q
== LLDP_CHASSIS_SUBTYPE_RESERVED
||
242 *q
> LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
) {
243 log_lldp("Unknown subtype: %d found in Chassis ID TLV . Dropping ...",
254 case LLDP_TYPE_PORT_ID
:
257 log_lldp("Received malformed Port ID TLV len = %d. Dropping",
265 log_lldp("Duplicate Port ID TLV found. Dropping ...");
271 /* Look what subtype it has */
272 if (*q
== LLDP_PORT_SUBTYPE_RESERVED
||
273 *q
> LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
) {
274 log_lldp("Unknown subtype: %d found in Port ID TLV . Dropping ...",
289 "Received invalid lenth: %d TTL TLV. Dropping ...",
297 log_lldp("Duplicate TTL TLV found. Dropping ...");
309 log_lldp("TLV type = %d's, length 0 received . Dropping ...",
319 if(!chassis_id
|| !port_id
|| !ttl
|| !end
) {
320 log_lldp( "One or more mandotory TLV missing . Dropping ...");
327 r
= tlv_packet_parse_pdu(tlv
, length
);
329 log_lldp( "Failed to parse the TLV. Dropping ...");
335 return lldp_receive_frame(lldp
, tlv
);
338 lldp_set_state(lldp
, LLDP_AGENT_RX_WAIT_FOR_FRAME
);
341 lldp
->statitics
.stats_frames_discarded_total
++;
342 lldp
->statitics
.stats_frames_in_errors_total
++;
345 tlv_packet_free(tlv
);
350 static int ttl_expiry_item_prioq_compare_func(const void *a
, const void *b
) {
351 const lldp_neighbour_port
*p
= a
, *q
= b
;
353 if (p
->until
< q
->until
)
356 if (p
->until
> q
->until
)
362 static void lldp_set_state(sd_lldp
*lldp
, LLDPAgentRXState state
) {
365 assert(state
< _LLDP_AGENT_RX_STATE_MAX
);
367 lldp
->rx_state
= state
;
369 lldp_run_state_machine(lldp
);
372 static void lldp_run_state_machine(sd_lldp
*lldp
) {
374 if (lldp
->rx_state
== LLDP_AGENT_RX_UPDATE_INFO
)
376 lldp
->cb(lldp
, LLDP_AGENT_RX_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
);
404 lldp_neighbour_port_remove_and_free(p
);
406 lldp
->statitics
.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 *s
= NULL
, *t
= NULL
, *k
= NULL
;
432 _cleanup_free_
char *temp_path
= NULL
;
433 _cleanup_fclose_
FILE *f
= NULL
;
434 uint8_t *mac
, *port_id
, type
;
435 lldp_neighbour_port
*p
;
436 uint16_t data
= 0, length
= 0;
446 r
= fopen_temporary(lldp_file
, &f
, &temp_path
);
450 fchmod(fileno(f
), 0644);
452 HASHMAP_FOREACH(c
, lldp
->neighbour_mib
, i
) {
453 LIST_FOREACH(port
, p
, c
->ports
) {
455 r
= lldp_read_chassis_id(p
->packet
, &type
, &length
, &mac
);
459 memzero(buf
, LINE_MAX
);
461 sprintf(buf
, "'_Chassis=%02x:%02x:%02x:%02x:%02x:%02x' '_CType=%d' ",
462 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
468 r
= lldp_read_port_id(p
->packet
, &type
, &length
, &port_id
);
474 memzero(buf
, LINE_MAX
);
475 if (type
!= LLDP_PORT_SUBTYPE_MAC_ADDRESS
) {
477 k
= strndup((char *) port_id
, length
-1);
481 sprintf(buf
, "'_Port=%s' '_PType=%d' ", k
, type
);
483 t
= strappend(s
, buf
);
490 sprintf(buf
, "'_Port=%02x:%02x:%02x:%02x:%02x:%02x' '_PType=%d' ",
491 mac
[0], mac
[1], mac
[2], mac
[3], mac
[4], mac
[5], type
);
493 t
= strappend(s
, buf
);
502 time
= now(CLOCK_BOOTTIME
);
504 /* Don't write expired packets */
505 if(time
- p
->until
<= 0) {
510 memzero(buf
, LINE_MAX
);
511 sprintf(buf
, "'_TTL=%lu' ", p
->until
);
513 t
= strappend(s
, buf
);
520 r
= lldp_read_system_name(p
->packet
, &length
, &k
);
522 k
= strappend(s
, "'_NAME=N/A' ");
524 t
= strndup(k
, length
);
528 k
= strjoin(s
, "'_NAME=", t
, "' ", NULL
);
537 memzero(buf
, LINE_MAX
);
539 (void)lldp_read_system_capability(p
->packet
, &data
);
541 sprintf(buf
, "'_CAP=%x'", data
);
543 t
= strappend(s
, buf
);
547 fprintf(f
, "%s\n", t
);
561 if (ferror(f
) || rename(temp_path
, lldp_file
) < 0) {
569 log_error("Failed to save lldp data %s: %s", lldp_file
, strerror(-r
));
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 void sd_lldp_free(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
);
670 int sd_lldp_new(int ifindex
,
672 struct ether_addr
*mac
,
674 _cleanup_sd_lldp_free_ sd_lldp
*lldp
= NULL
;
677 assert_return(ret
, -EINVAL
);
678 assert_return(ifindex
> 0, -EINVAL
);
679 assert_return(ifname
, -EINVAL
);
680 assert_return(mac
, -EINVAL
);
682 lldp
= new0(sd_lldp
, 1);
686 r
= lldp_port_new(ifindex
, ifname
, mac
, lldp
, &lldp
->port
);
690 lldp
->neighbour_mib
= hashmap_new(&chassis_id_hash_ops
);
691 if (!lldp
->neighbour_mib
)
694 r
= prioq_ensure_allocated(&lldp
->by_expiry
,
695 ttl_expiry_item_prioq_compare_func
);
699 lldp
->rx_state
= LLDP_AGENT_RX_WAIT_PORT_OPERATIONAL
;