1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 Copyright 2016 Lennart Poettering
6 #include "alloc-util.h"
8 #include "ether-addr-util.h"
10 #include "in-addr-util.h"
11 #include "lldp-internal.h"
12 #include "lldp-neighbor.h"
13 #include "unaligned.h"
15 static void lldp_neighbor_id_hash_func(const void *p
, struct siphash
*state
) {
16 const LLDPNeighborID
*id
= p
;
18 siphash24_compress(id
->chassis_id
, id
->chassis_id_size
, state
);
19 siphash24_compress(&id
->chassis_id_size
, sizeof(id
->chassis_id_size
), state
);
20 siphash24_compress(id
->port_id
, id
->port_id_size
, state
);
21 siphash24_compress(&id
->port_id_size
, sizeof(id
->port_id_size
), state
);
24 static int lldp_neighbor_id_compare_func(const void *a
, const void *b
) {
25 const LLDPNeighborID
*x
= a
, *y
= b
;
28 r
= memcmp(x
->chassis_id
, y
->chassis_id
, MIN(x
->chassis_id_size
, y
->chassis_id_size
));
32 if (x
->chassis_id_size
< y
->chassis_id_size
)
35 if (x
->chassis_id_size
> y
->chassis_id_size
)
38 r
= memcmp(x
->port_id
, y
->port_id
, MIN(x
->port_id_size
, y
->port_id_size
));
42 if (x
->port_id_size
< y
->port_id_size
)
44 if (x
->port_id_size
> y
->port_id_size
)
50 const struct hash_ops lldp_neighbor_id_hash_ops
= {
51 .hash
= lldp_neighbor_id_hash_func
,
52 .compare
= lldp_neighbor_id_compare_func
55 int lldp_neighbor_prioq_compare_func(const void *a
, const void *b
) {
56 const sd_lldp_neighbor
*x
= a
, *y
= b
;
58 if (x
->until
< y
->until
)
61 if (x
->until
> y
->until
)
67 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_ref(sd_lldp_neighbor
*n
) {
71 assert(n
->n_ref
> 0 || n
->lldp
);
77 static void lldp_neighbor_free(sd_lldp_neighbor
*n
) {
81 free(n
->id
.chassis_id
);
82 free(n
->port_description
);
84 free(n
->system_description
);
85 free(n
->chassis_id_as_string
);
86 free(n
->port_id_as_string
);
90 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_unref(sd_lldp_neighbor
*n
) {
92 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
93 * the sd_lldp object. */
101 if (n
->n_ref
<= 0 && !n
->lldp
)
102 lldp_neighbor_free(n
);
107 sd_lldp_neighbor
*lldp_neighbor_unlink(sd_lldp_neighbor
*n
) {
109 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
117 assert_se(hashmap_remove(n
->lldp
->neighbor_by_id
, &n
->id
) == n
);
118 assert_se(prioq_remove(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
) >= 0);
123 lldp_neighbor_free(n
);
128 sd_lldp_neighbor
*lldp_neighbor_new(size_t raw_size
) {
131 n
= malloc0(ALIGN(sizeof(sd_lldp_neighbor
)) + raw_size
);
135 n
->raw_size
= raw_size
;
141 static int parse_string(char **s
, const void *q
, size_t n
) {
149 log_lldp("Found duplicate string, ignoring field.");
153 /* Strip trailing NULs, just to be nice */
154 while (n
> 0 && p
[n
-1] == 0)
157 if (n
<= 0) /* Ignore empty strings */
160 /* Look for inner NULs */
161 if (memchr(p
, 0, n
)) {
162 log_lldp("Found inner NUL in string, ignoring field.");
166 /* Let's escape weird chars, for security reasons */
167 k
= cescape_length(p
, n
);
177 int lldp_neighbor_parse(sd_lldp_neighbor
*n
) {
178 struct ether_header h
;
185 if (n
->raw_size
< sizeof(struct ether_header
)) {
186 log_lldp("Received truncated packet, ignoring.");
190 memcpy(&h
, LLDP_NEIGHBOR_RAW(n
), sizeof(h
));
192 if (h
.ether_type
!= htobe16(ETHERTYPE_LLDP
)) {
193 log_lldp("Received packet with wrong type, ignoring.");
197 if (h
.ether_dhost
[0] != 0x01 ||
198 h
.ether_dhost
[1] != 0x80 ||
199 h
.ether_dhost
[2] != 0xc2 ||
200 h
.ether_dhost
[3] != 0x00 ||
201 h
.ether_dhost
[4] != 0x00 ||
202 !IN_SET(h
.ether_dhost
[5], 0x00, 0x03, 0x0e)) {
203 log_lldp("Received packet with wrong destination address, ignoring.");
207 memcpy(&n
->source_address
, h
.ether_shost
, sizeof(struct ether_addr
));
208 memcpy(&n
->destination_address
, h
.ether_dhost
, sizeof(struct ether_addr
));
210 p
= (const uint8_t*) LLDP_NEIGHBOR_RAW(n
) + sizeof(struct ether_header
);
211 left
= n
->raw_size
- sizeof(struct ether_header
);
218 log_lldp("TLV lacks header, ignoring.");
223 length
= p
[1] + (((uint16_t) (p
[0] & 1)) << 8);
227 log_lldp("TLV truncated, ignoring datagram.");
233 case SD_LLDP_TYPE_END
:
235 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
239 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
240 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
244 case SD_LLDP_TYPE_CHASSIS_ID
:
245 if (length
< 2 || length
> 256) { /* includes the chassis subtype, hence one extra byte */
246 log_lldp("Chassis ID field size out of range, ignoring datagram.");
249 if (n
->id
.chassis_id
) {
250 log_lldp("Duplicate chassis ID field, ignoring datagram.");
254 n
->id
.chassis_id
= memdup(p
, length
);
255 if (!n
->id
.chassis_id
)
258 n
->id
.chassis_id_size
= length
;
261 case SD_LLDP_TYPE_PORT_ID
:
262 if (length
< 2 || length
> 256) { /* includes the port subtype, hence one extra byte */
263 log_lldp("Port ID field size out of range, ignoring datagram.");
267 log_lldp("Duplicate port ID field, ignoring datagram.");
271 n
->id
.port_id
= memdup(p
, length
);
275 n
->id
.port_id_size
= length
;
278 case SD_LLDP_TYPE_TTL
:
280 log_lldp("TTL field has wrong size, ignoring datagram.");
285 log_lldp("Duplicate TTL field, ignoring datagram.");
289 n
->ttl
= unaligned_read_be16(p
);
293 case SD_LLDP_TYPE_PORT_DESCRIPTION
:
294 r
= parse_string(&n
->port_description
, p
, length
);
299 case SD_LLDP_TYPE_SYSTEM_NAME
:
300 r
= parse_string(&n
->system_name
, p
, length
);
305 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION
:
306 r
= parse_string(&n
->system_description
, p
, length
);
311 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES
:
313 log_lldp("System capabilities field has wrong size, ignoring.");
315 n
->system_capabilities
= unaligned_read_be16(p
);
316 n
->enabled_capabilities
= unaligned_read_be16(p
+ 2);
317 n
->has_capabilities
= true;
322 case SD_LLDP_TYPE_PRIVATE
:
324 log_lldp("Found private TLV that is too short, ignoring.");
329 p
+= length
, left
-= length
;
333 if (!n
->id
.chassis_id
|| !n
->id
.port_id
|| !n
->has_ttl
) {
334 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
339 n
->rindex
= sizeof(struct ether_header
);
344 void lldp_neighbor_start_ttl(sd_lldp_neighbor
*n
) {
350 /* Use the packet's timestamp if there is one known */
351 base
= triple_timestamp_by_clock(&n
->timestamp
, clock_boottime_or_monotonic());
352 if (base
<= 0 || base
== USEC_INFINITY
)
353 base
= now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
355 n
->until
= usec_add(base
, n
->ttl
* USEC_PER_SEC
);
360 prioq_reshuffle(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
363 bool lldp_neighbor_equal(const sd_lldp_neighbor
*a
, const sd_lldp_neighbor
*b
) {
370 if (a
->raw_size
!= b
->raw_size
)
373 return memcmp(LLDP_NEIGHBOR_RAW(a
), LLDP_NEIGHBOR_RAW(b
), a
->raw_size
) == 0;
376 _public_
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
377 assert_return(n
, -EINVAL
);
378 assert_return(address
, -EINVAL
);
380 *address
= n
->source_address
;
384 _public_
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
385 assert_return(n
, -EINVAL
);
386 assert_return(address
, -EINVAL
);
388 *address
= n
->destination_address
;
392 _public_
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
393 assert_return(n
, -EINVAL
);
394 assert_return(ret
, -EINVAL
);
395 assert_return(size
, -EINVAL
);
397 *ret
= LLDP_NEIGHBOR_RAW(n
);
403 _public_
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
404 assert_return(n
, -EINVAL
);
405 assert_return(type
, -EINVAL
);
406 assert_return(ret
, -EINVAL
);
407 assert_return(size
, -EINVAL
);
409 assert(n
->id
.chassis_id_size
> 0);
411 *type
= *(uint8_t*) n
->id
.chassis_id
;
412 *ret
= (uint8_t*) n
->id
.chassis_id
+ 1;
413 *size
= n
->id
.chassis_id_size
- 1;
418 static int format_mac_address(const void *data
, size_t sz
, char **ret
) {
422 assert(data
|| sz
<= 0);
427 memcpy(&a
, (uint8_t*) data
+ 1, sizeof(a
));
429 k
= new(char, ETHER_ADDR_TO_STRING_MAX
);
433 *ret
= ether_addr_to_string(&a
, k
);
437 static int format_network_address(const void *data
, size_t sz
, char **ret
) {
438 union in_addr_union a
;
441 if (sz
== 6 && ((uint8_t*) data
)[1] == 1) {
442 memcpy(&a
.in
, (uint8_t*) data
+ 2, sizeof(a
.in
));
444 } else if (sz
== 18 && ((uint8_t*) data
)[1] == 2) {
445 memcpy(&a
.in6
, (uint8_t*) data
+ 2, sizeof(a
.in6
));
450 r
= in_addr_to_string(family
, &a
, ret
);
456 _public_
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
460 assert_return(n
, -EINVAL
);
461 assert_return(ret
, -EINVAL
);
463 if (n
->chassis_id_as_string
) {
464 *ret
= n
->chassis_id_as_string
;
468 assert(n
->id
.chassis_id_size
> 0);
470 switch (*(uint8_t*) n
->id
.chassis_id
) {
472 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT
:
473 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS
:
474 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT
:
475 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME
:
476 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
:
477 k
= cescape_length((char*) n
->id
.chassis_id
+ 1, n
->id
.chassis_id_size
- 1);
483 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
484 r
= format_mac_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
492 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS
:
493 r
= format_network_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
502 /* Generic fallback */
503 k
= hexmem(n
->id
.chassis_id
, n
->id
.chassis_id_size
);
508 *ret
= n
->chassis_id_as_string
= k
;
512 _public_
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
513 assert_return(n
, -EINVAL
);
514 assert_return(type
, -EINVAL
);
515 assert_return(ret
, -EINVAL
);
516 assert_return(size
, -EINVAL
);
518 assert(n
->id
.port_id_size
> 0);
520 *type
= *(uint8_t*) n
->id
.port_id
;
521 *ret
= (uint8_t*) n
->id
.port_id
+ 1;
522 *size
= n
->id
.port_id_size
- 1;
527 _public_
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
531 assert_return(n
, -EINVAL
);
532 assert_return(ret
, -EINVAL
);
534 if (n
->port_id_as_string
) {
535 *ret
= n
->port_id_as_string
;
539 assert(n
->id
.port_id_size
> 0);
541 switch (*(uint8_t*) n
->id
.port_id
) {
543 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
544 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
545 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
546 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
547 k
= cescape_length((char*) n
->id
.port_id
+ 1, n
->id
.port_id_size
- 1);
553 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
554 r
= format_mac_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
562 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS
:
563 r
= format_network_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
572 /* Generic fallback */
573 k
= hexmem(n
->id
.port_id
, n
->id
.port_id_size
);
578 *ret
= n
->port_id_as_string
= k
;
582 _public_
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor
*n
, uint16_t *ret_sec
) {
583 assert_return(n
, -EINVAL
);
584 assert_return(ret_sec
, -EINVAL
);
590 _public_
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor
*n
, const char **ret
) {
591 assert_return(n
, -EINVAL
);
592 assert_return(ret
, -EINVAL
);
597 *ret
= n
->system_name
;
601 _public_
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor
*n
, const char **ret
) {
602 assert_return(n
, -EINVAL
);
603 assert_return(ret
, -EINVAL
);
605 if (!n
->system_description
)
608 *ret
= n
->system_description
;
612 _public_
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor
*n
, const char **ret
) {
613 assert_return(n
, -EINVAL
);
614 assert_return(ret
, -EINVAL
);
616 if (!n
->port_description
)
619 *ret
= n
->port_description
;
623 _public_
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
624 assert_return(n
, -EINVAL
);
625 assert_return(ret
, -EINVAL
);
627 if (!n
->has_capabilities
)
630 *ret
= n
->system_capabilities
;
634 _public_
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
635 assert_return(n
, -EINVAL
);
636 assert_return(ret
, -EINVAL
);
638 if (!n
->has_capabilities
)
641 *ret
= n
->enabled_capabilities
;
645 _public_
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor
**ret
, const void *raw
, size_t raw_size
) {
646 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
649 assert_return(ret
, -EINVAL
);
650 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
652 n
= lldp_neighbor_new(raw_size
);
656 memcpy(LLDP_NEIGHBOR_RAW(n
), raw
, raw_size
);
657 r
= lldp_neighbor_parse(n
);
666 _public_
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor
*n
) {
667 assert_return(n
, -EINVAL
);
669 assert(n
->raw_size
>= sizeof(struct ether_header
));
670 n
->rindex
= sizeof(struct ether_header
);
672 return n
->rindex
< n
->raw_size
;
675 _public_
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor
*n
) {
678 assert_return(n
, -EINVAL
);
680 if (n
->rindex
== n
->raw_size
) /* EOF */
683 if (n
->rindex
+ 2 > n
->raw_size
) /* Truncated message */
686 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
687 if (n
->rindex
+ 2 + length
> n
->raw_size
)
690 n
->rindex
+= 2 + length
;
691 return n
->rindex
< n
->raw_size
;
694 _public_
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor
*n
, uint8_t *type
) {
695 assert_return(n
, -EINVAL
);
696 assert_return(type
, -EINVAL
);
698 if (n
->rindex
== n
->raw_size
) /* EOF */
701 if (n
->rindex
+ 2 > n
->raw_size
)
704 *type
= LLDP_NEIGHBOR_TLV_TYPE(n
);
708 _public_
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor
*n
, uint8_t type
) {
712 assert_return(n
, -EINVAL
);
714 r
= sd_lldp_neighbor_tlv_get_type(n
, &k
);
721 _public_
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor
*n
, uint8_t oui
[3], uint8_t *subtype
) {
726 assert_return(n
, -EINVAL
);
727 assert_return(oui
, -EINVAL
);
728 assert_return(subtype
, -EINVAL
);
730 r
= sd_lldp_neighbor_tlv_is_type(n
, SD_LLDP_TYPE_PRIVATE
);
736 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
740 if (n
->rindex
+ 2 + length
> n
->raw_size
)
743 d
= LLDP_NEIGHBOR_TLV_DATA(n
);
750 _public_
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor
*n
, const uint8_t oui
[3], uint8_t subtype
) {
754 r
= sd_lldp_neighbor_tlv_get_oui(n
, k
, &st
);
760 return memcmp(k
, oui
, 3) == 0 && st
== subtype
;
763 _public_
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
766 assert_return(n
, -EINVAL
);
767 assert_return(ret
, -EINVAL
);
768 assert_return(size
, -EINVAL
);
770 /* Note that this returns the full TLV, including the TLV header */
772 if (n
->rindex
+ 2 > n
->raw_size
)
775 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
776 if (n
->rindex
+ 2 + length
> n
->raw_size
)
779 *ret
= (uint8_t*) LLDP_NEIGHBOR_RAW(n
) + n
->rindex
;
785 _public_
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor
*n
, clockid_t clock
, uint64_t *ret
) {
786 assert_return(n
, -EINVAL
);
787 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
788 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
789 assert_return(ret
, -EINVAL
);
791 if (!triple_timestamp_is_set(&n
->timestamp
))
794 *ret
= triple_timestamp_by_clock(&n
->timestamp
, clock
);