1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include "alloc-util.h"
23 #include "ether-addr-util.h"
24 #include "hexdecoct.h"
25 #include "in-addr-util.h"
26 #include "lldp-internal.h"
27 #include "lldp-neighbor.h"
28 #include "unaligned.h"
30 static void lldp_neighbor_id_hash_func(const void *p
, struct siphash
*state
) {
31 const LLDPNeighborID
*id
= p
;
33 siphash24_compress(id
->chassis_id
, id
->chassis_id_size
, state
);
34 siphash24_compress(&id
->chassis_id_size
, sizeof(id
->chassis_id_size
), state
);
35 siphash24_compress(id
->port_id
, id
->port_id_size
, state
);
36 siphash24_compress(&id
->port_id_size
, sizeof(id
->port_id_size
), state
);
39 static int lldp_neighbor_id_compare_func(const void *a
, const void *b
) {
40 const LLDPNeighborID
*x
= a
, *y
= b
;
43 r
= memcmp(x
->chassis_id
, y
->chassis_id
, MIN(x
->chassis_id_size
, y
->chassis_id_size
));
47 if (x
->chassis_id_size
< y
->chassis_id_size
)
50 if (x
->chassis_id_size
> y
->chassis_id_size
)
53 r
= memcmp(x
->port_id
, y
->port_id
, MIN(x
->port_id_size
, y
->port_id_size
));
57 if (x
->port_id_size
< y
->port_id_size
)
59 if (x
->port_id_size
> y
->port_id_size
)
65 const struct hash_ops lldp_neighbor_id_hash_ops
= {
66 .hash
= lldp_neighbor_id_hash_func
,
67 .compare
= lldp_neighbor_id_compare_func
70 int lldp_neighbor_prioq_compare_func(const void *a
, const void *b
) {
71 const sd_lldp_neighbor
*x
= a
, *y
= b
;
73 if (x
->until
< y
->until
)
76 if (x
->until
> y
->until
)
82 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_ref(sd_lldp_neighbor
*n
) {
86 assert(n
->n_ref
> 0 || n
->lldp
);
92 static void lldp_neighbor_free(sd_lldp_neighbor
*n
) {
96 free(n
->id
.chassis_id
);
97 free(n
->port_description
);
99 free(n
->system_description
);
100 free(n
->chassis_id_as_string
);
101 free(n
->port_id_as_string
);
105 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_unref(sd_lldp_neighbor
*n
) {
107 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
108 * the sd_lldp object. */
113 assert(n
->n_ref
> 0);
116 if (n
->n_ref
<= 0 && !n
->lldp
)
117 lldp_neighbor_free(n
);
122 sd_lldp_neighbor
*lldp_neighbor_unlink(sd_lldp_neighbor
*n
) {
124 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
132 assert_se(hashmap_remove(n
->lldp
->neighbor_by_id
, &n
->id
) == n
);
133 assert_se(prioq_remove(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
) >= 0);
138 lldp_neighbor_free(n
);
143 sd_lldp_neighbor
*lldp_neighbor_new(size_t raw_size
) {
146 n
= malloc0(ALIGN(sizeof(sd_lldp_neighbor
)) + raw_size
);
150 n
->raw_size
= raw_size
;
156 static int parse_string(char **s
, const void *q
, size_t n
) {
164 log_lldp("Found duplicate string, ignoring field.");
168 /* Strip trailing NULs, just to be nice */
169 while (n
> 0 && p
[n
-1] == 0)
172 if (n
<= 0) /* Ignore empty strings */
175 /* Look for inner NULs */
176 if (memchr(p
, 0, n
)) {
177 log_lldp("Found inner NUL in string, ignoring field.");
181 /* Let's escape weird chars, for security reasons */
182 k
= cescape_length(p
, n
);
192 int lldp_neighbor_parse(sd_lldp_neighbor
*n
) {
193 struct ether_header h
;
200 if (n
->raw_size
< sizeof(struct ether_header
)) {
201 log_lldp("Received truncated packet, ignoring.");
205 memcpy(&h
, LLDP_NEIGHBOR_RAW(n
), sizeof(h
));
207 if (h
.ether_type
!= htobe16(ETHERTYPE_LLDP
)) {
208 log_lldp("Received packet with wrong type, ignoring.");
212 if (h
.ether_dhost
[0] != 0x01 ||
213 h
.ether_dhost
[1] != 0x80 ||
214 h
.ether_dhost
[2] != 0xc2 ||
215 h
.ether_dhost
[3] != 0x00 ||
216 h
.ether_dhost
[4] != 0x00 ||
217 !IN_SET(h
.ether_dhost
[5], 0x00, 0x03, 0x0e)) {
218 log_lldp("Received packet with wrong destination address, ignoring.");
222 memcpy(&n
->source_address
, h
.ether_shost
, sizeof(struct ether_addr
));
223 memcpy(&n
->destination_address
, h
.ether_dhost
, sizeof(struct ether_addr
));
225 p
= (const uint8_t*) LLDP_NEIGHBOR_RAW(n
) + sizeof(struct ether_header
);
226 left
= n
->raw_size
- sizeof(struct ether_header
);
233 log_lldp("TLV lacks header, ignoring.");
238 length
= p
[1] + (((uint16_t) (p
[0] & 1)) << 8);
242 log_lldp("TLV truncated, ignoring datagram.");
248 case SD_LLDP_TYPE_END
:
250 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
254 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
255 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
259 case SD_LLDP_TYPE_CHASSIS_ID
:
260 if (length
< 2 || length
> 256) { /* includes the chassis subtype, hence one extra byte */
261 log_lldp("Chassis ID field size out of range, ignoring datagram.");
264 if (n
->id
.chassis_id
) {
265 log_lldp("Duplicate chassis ID field, ignoring datagram.");
269 n
->id
.chassis_id
= memdup(p
, length
);
270 if (!n
->id
.chassis_id
)
273 n
->id
.chassis_id_size
= length
;
276 case SD_LLDP_TYPE_PORT_ID
:
277 if (length
< 2 || length
> 256) { /* includes the port subtype, hence one extra byte */
278 log_lldp("Port ID field size out of range, ignoring datagram.");
282 log_lldp("Duplicate port ID field, ignoring datagram.");
286 n
->id
.port_id
= memdup(p
, length
);
290 n
->id
.port_id_size
= length
;
293 case SD_LLDP_TYPE_TTL
:
295 log_lldp("TTL field has wrong size, ignoring datagram.");
300 log_lldp("Duplicate TTL field, ignoring datagram.");
304 n
->ttl
= unaligned_read_be16(p
);
308 case SD_LLDP_TYPE_PORT_DESCRIPTION
:
309 r
= parse_string(&n
->port_description
, p
, length
);
314 case SD_LLDP_TYPE_SYSTEM_NAME
:
315 r
= parse_string(&n
->system_name
, p
, length
);
320 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION
:
321 r
= parse_string(&n
->system_description
, p
, length
);
326 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES
:
328 log_lldp("System capabilities field has wrong size, ignoring.");
330 n
->system_capabilities
= unaligned_read_be16(p
);
331 n
->enabled_capabilities
= unaligned_read_be16(p
+ 2);
332 n
->has_capabilities
= true;
337 case SD_LLDP_TYPE_PRIVATE
:
339 log_lldp("Found private TLV that is too short, ignoring.");
345 p
+= length
, left
-= length
;
349 if (!n
->id
.chassis_id
|| !n
->id
.port_id
|| !n
->has_ttl
) {
350 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
355 n
->rindex
= sizeof(struct ether_header
);
360 void lldp_neighbor_start_ttl(sd_lldp_neighbor
*n
) {
366 /* Use the packet's timestamp if there is one known */
367 base
= triple_timestamp_by_clock(&n
->timestamp
, clock_boottime_or_monotonic());
368 if (base
<= 0 || base
== USEC_INFINITY
)
369 base
= now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
371 n
->until
= usec_add(base
, n
->ttl
* USEC_PER_SEC
);
376 prioq_reshuffle(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
379 bool lldp_neighbor_equal(const sd_lldp_neighbor
*a
, const sd_lldp_neighbor
*b
) {
386 if (a
->raw_size
!= b
->raw_size
)
389 return memcmp(LLDP_NEIGHBOR_RAW(a
), LLDP_NEIGHBOR_RAW(b
), a
->raw_size
) == 0;
392 _public_
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
393 assert_return(n
, -EINVAL
);
394 assert_return(address
, -EINVAL
);
396 *address
= n
->source_address
;
400 _public_
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
401 assert_return(n
, -EINVAL
);
402 assert_return(address
, -EINVAL
);
404 *address
= n
->destination_address
;
408 _public_
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
409 assert_return(n
, -EINVAL
);
410 assert_return(ret
, -EINVAL
);
411 assert_return(size
, -EINVAL
);
413 *ret
= LLDP_NEIGHBOR_RAW(n
);
419 _public_
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
420 assert_return(n
, -EINVAL
);
421 assert_return(type
, -EINVAL
);
422 assert_return(ret
, -EINVAL
);
423 assert_return(size
, -EINVAL
);
425 assert(n
->id
.chassis_id_size
> 0);
427 *type
= *(uint8_t*) n
->id
.chassis_id
;
428 *ret
= (uint8_t*) n
->id
.chassis_id
+ 1;
429 *size
= n
->id
.chassis_id_size
- 1;
434 static int format_mac_address(const void *data
, size_t sz
, char **ret
) {
438 assert(data
|| sz
<= 0);
443 memcpy(&a
, (uint8_t*) data
+ 1, sizeof(a
));
445 k
= new(char, ETHER_ADDR_TO_STRING_MAX
);
449 *ret
= ether_addr_to_string(&a
, k
);
453 static int format_network_address(const void *data
, size_t sz
, char **ret
) {
454 union in_addr_union a
;
457 if (sz
== 6 && ((uint8_t*) data
)[1] == 1) {
458 memcpy(&a
.in
, (uint8_t*) data
+ 2, sizeof(a
.in
));
460 } else if (sz
== 18 && ((uint8_t*) data
)[1] == 2) {
461 memcpy(&a
.in6
, (uint8_t*) data
+ 2, sizeof(a
.in6
));
466 r
= in_addr_to_string(family
, &a
, ret
);
472 _public_
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
476 assert_return(n
, -EINVAL
);
477 assert_return(ret
, -EINVAL
);
479 if (n
->chassis_id_as_string
) {
480 *ret
= n
->chassis_id_as_string
;
484 assert(n
->id
.chassis_id_size
> 0);
486 switch (*(uint8_t*) n
->id
.chassis_id
) {
488 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT
:
489 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS
:
490 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT
:
491 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME
:
492 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
:
493 k
= cescape_length((char*) n
->id
.chassis_id
+ 1, n
->id
.chassis_id_size
- 1);
499 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
500 r
= format_mac_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
508 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS
:
509 r
= format_network_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
518 /* Generic fallback */
519 k
= hexmem(n
->id
.chassis_id
, n
->id
.chassis_id_size
);
524 *ret
= n
->chassis_id_as_string
= k
;
528 _public_
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
529 assert_return(n
, -EINVAL
);
530 assert_return(type
, -EINVAL
);
531 assert_return(ret
, -EINVAL
);
532 assert_return(size
, -EINVAL
);
534 assert(n
->id
.port_id_size
> 0);
536 *type
= *(uint8_t*) n
->id
.port_id
;
537 *ret
= (uint8_t*) n
->id
.port_id
+ 1;
538 *size
= n
->id
.port_id_size
- 1;
543 _public_
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
547 assert_return(n
, -EINVAL
);
548 assert_return(ret
, -EINVAL
);
550 if (n
->port_id_as_string
) {
551 *ret
= n
->port_id_as_string
;
555 assert(n
->id
.port_id_size
> 0);
557 switch (*(uint8_t*) n
->id
.port_id
) {
559 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
560 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
561 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
562 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
563 k
= cescape_length((char*) n
->id
.port_id
+ 1, n
->id
.port_id_size
- 1);
569 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
570 r
= format_mac_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
578 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS
:
579 r
= format_network_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
588 /* Generic fallback */
589 k
= hexmem(n
->id
.port_id
, n
->id
.port_id_size
);
594 *ret
= n
->port_id_as_string
= k
;
598 _public_
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor
*n
, uint16_t *ret_sec
) {
599 assert_return(n
, -EINVAL
);
600 assert_return(ret_sec
, -EINVAL
);
606 _public_
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor
*n
, const char **ret
) {
607 assert_return(n
, -EINVAL
);
608 assert_return(ret
, -EINVAL
);
613 *ret
= n
->system_name
;
617 _public_
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor
*n
, const char **ret
) {
618 assert_return(n
, -EINVAL
);
619 assert_return(ret
, -EINVAL
);
621 if (!n
->system_description
)
624 *ret
= n
->system_description
;
628 _public_
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor
*n
, const char **ret
) {
629 assert_return(n
, -EINVAL
);
630 assert_return(ret
, -EINVAL
);
632 if (!n
->port_description
)
635 *ret
= n
->port_description
;
639 _public_
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
640 assert_return(n
, -EINVAL
);
641 assert_return(ret
, -EINVAL
);
643 if (!n
->has_capabilities
)
646 *ret
= n
->system_capabilities
;
650 _public_
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
651 assert_return(n
, -EINVAL
);
652 assert_return(ret
, -EINVAL
);
654 if (!n
->has_capabilities
)
657 *ret
= n
->enabled_capabilities
;
661 _public_
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor
**ret
, const void *raw
, size_t raw_size
) {
662 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
665 assert_return(ret
, -EINVAL
);
666 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
668 n
= lldp_neighbor_new(raw_size
);
672 memcpy(LLDP_NEIGHBOR_RAW(n
), raw
, raw_size
);
673 r
= lldp_neighbor_parse(n
);
683 _public_
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor
*n
) {
684 assert_return(n
, -EINVAL
);
686 assert(n
->raw_size
>= sizeof(struct ether_header
));
687 n
->rindex
= sizeof(struct ether_header
);
689 return n
->rindex
< n
->raw_size
;
692 _public_
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor
*n
) {
695 assert_return(n
, -EINVAL
);
697 if (n
->rindex
== n
->raw_size
) /* EOF */
700 if (n
->rindex
+ 2 > n
->raw_size
) /* Truncated message */
703 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
704 if (n
->rindex
+ 2 + length
> n
->raw_size
)
707 n
->rindex
+= 2 + length
;
708 return n
->rindex
< n
->raw_size
;
711 _public_
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor
*n
, uint8_t *type
) {
712 assert_return(n
, -EINVAL
);
713 assert_return(type
, -EINVAL
);
715 if (n
->rindex
== n
->raw_size
) /* EOF */
718 if (n
->rindex
+ 2 > n
->raw_size
)
721 *type
= LLDP_NEIGHBOR_TLV_TYPE(n
);
725 _public_
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor
*n
, uint8_t type
) {
729 assert_return(n
, -EINVAL
);
731 r
= sd_lldp_neighbor_tlv_get_type(n
, &k
);
738 _public_
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor
*n
, uint8_t oui
[3], uint8_t *subtype
) {
743 assert_return(n
, -EINVAL
);
744 assert_return(oui
, -EINVAL
);
745 assert_return(subtype
, -EINVAL
);
747 r
= sd_lldp_neighbor_tlv_is_type(n
, SD_LLDP_TYPE_PRIVATE
);
753 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
757 if (n
->rindex
+ 2 + length
> n
->raw_size
)
760 d
= LLDP_NEIGHBOR_TLV_DATA(n
);
767 _public_
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor
*n
, const uint8_t oui
[3], uint8_t subtype
) {
771 r
= sd_lldp_neighbor_tlv_get_oui(n
, k
, &st
);
777 return memcmp(k
, oui
, 3) == 0 && st
== subtype
;
780 _public_
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
783 assert_return(n
, -EINVAL
);
784 assert_return(ret
, -EINVAL
);
785 assert_return(size
, -EINVAL
);
787 /* Note that this returns the full TLV, including the TLV header */
789 if (n
->rindex
+ 2 > n
->raw_size
)
792 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
793 if (n
->rindex
+ 2 + length
> n
->raw_size
)
796 *ret
= (uint8_t*) LLDP_NEIGHBOR_RAW(n
) + n
->rindex
;
802 _public_
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor
*n
, clockid_t clock
, uint64_t *ret
) {
803 assert_return(n
, -EINVAL
);
804 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
805 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
806 assert_return(ret
, -EINVAL
);
808 if (!triple_timestamp_is_set(&n
->timestamp
))
811 *ret
= triple_timestamp_by_clock(&n
->timestamp
, clock
);