1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "alloc-util.h"
5 #include "ether-addr-util.h"
7 #include "in-addr-util.h"
8 #include "lldp-neighbor.h"
9 #include "memory-util.h"
10 #include "missing_network.h"
11 #include "unaligned.h"
13 static void lldp_neighbor_id_hash_func(const LLDPNeighborID
*id
, struct siphash
*state
) {
17 siphash24_compress(id
->chassis_id
, id
->chassis_id_size
, state
);
18 siphash24_compress(&id
->chassis_id_size
, sizeof(id
->chassis_id_size
), state
);
19 siphash24_compress(id
->port_id
, id
->port_id_size
, state
);
20 siphash24_compress(&id
->port_id_size
, sizeof(id
->port_id_size
), state
);
23 int lldp_neighbor_id_compare_func(const LLDPNeighborID
*x
, const LLDPNeighborID
*y
) {
27 return memcmp_nn(x
->chassis_id
, x
->chassis_id_size
, y
->chassis_id
, y
->chassis_id_size
)
28 ?: memcmp_nn(x
->port_id
, x
->port_id_size
, y
->port_id
, y
->port_id_size
);
31 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
32 lldp_neighbor_hash_ops
,
34 lldp_neighbor_id_hash_func
,
35 lldp_neighbor_id_compare_func
,
37 lldp_neighbor_unlink
);
39 int lldp_neighbor_prioq_compare_func(const void *a
, const void *b
) {
40 const sd_lldp_neighbor
*x
= a
, *y
= b
;
45 return CMP(x
->until
, y
->until
);
48 sd_lldp_neighbor
*sd_lldp_neighbor_ref(sd_lldp_neighbor
*n
) {
52 assert(n
->n_ref
> 0 || n
->lldp_rx
);
58 static sd_lldp_neighbor
*lldp_neighbor_free(sd_lldp_neighbor
*n
) {
63 free(n
->id
.chassis_id
);
64 free(n
->port_description
);
66 free(n
->system_description
);
68 free(n
->chassis_id_as_string
);
69 free(n
->port_id_as_string
);
73 sd_lldp_neighbor
*sd_lldp_neighbor_unref(sd_lldp_neighbor
*n
) {
75 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
76 * the sd_lldp object. */
84 if (n
->n_ref
<= 0 && !n
->lldp_rx
)
85 lldp_neighbor_free(n
);
90 sd_lldp_neighbor
*lldp_neighbor_unlink(sd_lldp_neighbor
*n
) {
92 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
100 /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
101 * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
102 * ourselves from the hashtable and sometimes are called after we already are de-registered. */
104 (void) hashmap_remove_value(n
->lldp_rx
->neighbor_by_id
, &n
->id
, n
);
106 assert_se(prioq_remove(n
->lldp_rx
->neighbor_by_expiry
, n
, &n
->prioq_idx
) >= 0);
111 lldp_neighbor_free(n
);
116 sd_lldp_neighbor
*lldp_neighbor_new(size_t raw_size
) {
119 if (raw_size
> SIZE_MAX
- ALIGN(sizeof(sd_lldp_neighbor
)))
122 n
= malloc0(ALIGN(sizeof(sd_lldp_neighbor
)) + raw_size
);
126 n
->raw_size
= raw_size
;
132 static int parse_string(sd_lldp_rx
*lldp_rx
, char **s
, const void *q
, size_t n
) {
140 log_lldp_rx(lldp_rx
, "Found duplicate string, ignoring field.");
144 /* Strip trailing NULs, just to be nice */
145 while (n
> 0 && p
[n
-1] == 0)
148 if (n
<= 0) /* Ignore empty strings */
151 /* Look for inner NULs */
152 if (memchr(p
, 0, n
)) {
153 log_lldp_rx(lldp_rx
, "Found inner NUL in string, ignoring field.");
157 /* Let's escape weird chars, for security reasons */
158 k
= cescape_length(p
, n
);
160 return log_oom_debug();
162 free_and_replace(*s
, k
);
167 int lldp_neighbor_parse(sd_lldp_neighbor
*n
) {
168 struct ether_header h
;
175 if (n
->raw_size
< sizeof(struct ether_header
))
176 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
177 "Received truncated packet, ignoring.");
179 memcpy(&h
, LLDP_NEIGHBOR_RAW(n
), sizeof(h
));
181 if (h
.ether_type
!= htobe16(ETHERTYPE_LLDP
))
182 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
183 "Received packet with wrong type, ignoring.");
185 if (h
.ether_dhost
[0] != 0x01 ||
186 h
.ether_dhost
[1] != 0x80 ||
187 h
.ether_dhost
[2] != 0xc2 ||
188 h
.ether_dhost
[3] != 0x00 ||
189 h
.ether_dhost
[4] != 0x00 ||
190 !IN_SET(h
.ether_dhost
[5], 0x00, 0x03, 0x0e))
191 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
192 "Received packet with wrong destination address, ignoring.");
194 memcpy(&n
->source_address
, h
.ether_shost
, sizeof(struct ether_addr
));
195 memcpy(&n
->destination_address
, h
.ether_dhost
, sizeof(struct ether_addr
));
197 p
= (const uint8_t*) LLDP_NEIGHBOR_RAW(n
) + sizeof(struct ether_header
);
198 left
= n
->raw_size
- sizeof(struct ether_header
);
205 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
206 "TLV lacks header, ignoring.");
209 length
= p
[1] + (((uint16_t) (p
[0] & 1)) << 8);
213 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
214 "TLV truncated, ignoring datagram.");
218 case SD_LLDP_TYPE_END
:
220 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
221 "End marker TLV not zero-sized, ignoring datagram.");
223 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
224 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
228 case SD_LLDP_TYPE_CHASSIS_ID
:
229 if (length
< 2 || length
> 256)
230 /* includes the chassis subtype, hence one extra byte */
231 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
232 "Chassis ID field size out of range, ignoring datagram.");
234 if (n
->id
.chassis_id
)
235 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
236 "Duplicate chassis ID field, ignoring datagram.");
238 n
->id
.chassis_id
= memdup(p
, length
);
239 if (!n
->id
.chassis_id
)
240 return log_oom_debug();
242 n
->id
.chassis_id_size
= length
;
245 case SD_LLDP_TYPE_PORT_ID
:
246 if (length
< 2 || length
> 256)
247 /* includes the port subtype, hence one extra byte */
248 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
249 "Port ID field size out of range, ignoring datagram.");
252 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
253 "Duplicate port ID field, ignoring datagram.");
255 n
->id
.port_id
= memdup(p
, length
);
257 return log_oom_debug();
259 n
->id
.port_id_size
= length
;
262 case SD_LLDP_TYPE_TTL
:
264 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
265 "TTL field has wrong size, ignoring datagram.");
268 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
269 "Duplicate TTL field, ignoring datagram.");
271 n
->ttl
= unaligned_read_be16(p
);
275 case SD_LLDP_TYPE_PORT_DESCRIPTION
:
276 r
= parse_string(n
->lldp_rx
, &n
->port_description
, p
, length
);
281 case SD_LLDP_TYPE_SYSTEM_NAME
:
282 r
= parse_string(n
->lldp_rx
, &n
->system_name
, p
, length
);
287 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION
:
288 r
= parse_string(n
->lldp_rx
, &n
->system_description
, p
, length
);
293 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES
:
295 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
296 "System capabilities field has wrong size.");
298 n
->system_capabilities
= unaligned_read_be16(p
);
299 n
->enabled_capabilities
= unaligned_read_be16(p
+ 2);
300 n
->has_capabilities
= true;
303 case SD_LLDP_TYPE_PRIVATE
:
305 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
306 "Found private TLV that is too short, ignoring.");
308 /* RFC 8520: MUD URL */
309 if (memcmp(p
, SD_LLDP_OUI_IANA_MUD
, sizeof(SD_LLDP_OUI_IANA_MUD
)) == 0) {
310 r
= parse_string(n
->lldp_rx
, &n
->mud_url
, p
+ sizeof(SD_LLDP_OUI_IANA_MUD
),
311 length
- sizeof(SD_LLDP_OUI_IANA_MUD
));
318 p
+= length
, left
-= length
;
322 if (!n
->id
.chassis_id
|| !n
->id
.port_id
|| !n
->has_ttl
)
323 return log_lldp_rx_errno(n
->lldp_rx
, SYNTHETIC_ERRNO(EBADMSG
),
324 "One or more mandatory TLV missing in datagram. Ignoring.");
326 n
->rindex
= sizeof(struct ether_header
);
331 void lldp_neighbor_start_ttl(sd_lldp_neighbor
*n
) {
337 /* Use the packet's timestamp if there is one known */
338 base
= triple_timestamp_by_clock(&n
->timestamp
, CLOCK_BOOTTIME
);
339 if (!timestamp_is_set(base
))
340 base
= now(CLOCK_BOOTTIME
); /* Otherwise, take the current time */
342 n
->until
= usec_add(base
, n
->ttl
* USEC_PER_SEC
);
347 prioq_reshuffle(n
->lldp_rx
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
350 bool lldp_neighbor_equal(const sd_lldp_neighbor
*a
, const sd_lldp_neighbor
*b
) {
357 if (a
->raw_size
!= b
->raw_size
)
360 return memcmp(LLDP_NEIGHBOR_RAW(a
), LLDP_NEIGHBOR_RAW(b
), a
->raw_size
) == 0;
363 int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
364 assert_return(n
, -EINVAL
);
365 assert_return(address
, -EINVAL
);
367 *address
= n
->source_address
;
371 int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
372 assert_return(n
, -EINVAL
);
373 assert_return(address
, -EINVAL
);
375 *address
= n
->destination_address
;
379 int sd_lldp_neighbor_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
380 assert_return(n
, -EINVAL
);
381 assert_return(ret
, -EINVAL
);
382 assert_return(size
, -EINVAL
);
384 *ret
= LLDP_NEIGHBOR_RAW(n
);
390 int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
391 assert_return(n
, -EINVAL
);
392 assert_return(type
, -EINVAL
);
393 assert_return(ret
, -EINVAL
);
394 assert_return(size
, -EINVAL
);
396 assert(n
->id
.chassis_id_size
> 0);
398 *type
= *(uint8_t*) n
->id
.chassis_id
;
399 *ret
= (uint8_t*) n
->id
.chassis_id
+ 1;
400 *size
= n
->id
.chassis_id_size
- 1;
405 static int format_mac_address(const void *data
, size_t sz
, char **ret
) {
409 assert(data
|| sz
<= 0);
414 memcpy(&a
, (uint8_t*) data
+ 1, sizeof(a
));
416 k
= new(char, ETHER_ADDR_TO_STRING_MAX
);
420 *ret
= ether_addr_to_string(&a
, k
);
424 static int format_network_address(const void *data
, size_t sz
, char **ret
) {
425 union in_addr_union a
;
428 if (sz
== 6 && ((uint8_t*) data
)[1] == 1) {
429 memcpy(&a
.in
, (uint8_t*) data
+ 2, sizeof(a
.in
));
431 } else if (sz
== 18 && ((uint8_t*) data
)[1] == 2) {
432 memcpy(&a
.in6
, (uint8_t*) data
+ 2, sizeof(a
.in6
));
437 r
= in_addr_to_string(family
, &a
, ret
);
443 int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
447 assert_return(n
, -EINVAL
);
448 assert_return(ret
, -EINVAL
);
450 if (n
->chassis_id_as_string
) {
451 *ret
= n
->chassis_id_as_string
;
455 assert(n
->id
.chassis_id_size
> 0);
457 switch (*(uint8_t*) n
->id
.chassis_id
) {
459 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT
:
460 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS
:
461 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT
:
462 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME
:
463 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
:
464 k
= cescape_length((char*) n
->id
.chassis_id
+ 1, n
->id
.chassis_id_size
- 1);
470 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
471 r
= format_mac_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
479 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS
:
480 r
= format_network_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
489 /* Generic fallback */
490 k
= hexmem(n
->id
.chassis_id
, n
->id
.chassis_id_size
);
495 *ret
= n
->chassis_id_as_string
= k
;
499 int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
500 assert_return(n
, -EINVAL
);
501 assert_return(type
, -EINVAL
);
502 assert_return(ret
, -EINVAL
);
503 assert_return(size
, -EINVAL
);
505 assert(n
->id
.port_id_size
> 0);
507 *type
= *(uint8_t*) n
->id
.port_id
;
508 *ret
= (uint8_t*) n
->id
.port_id
+ 1;
509 *size
= n
->id
.port_id_size
- 1;
514 int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
518 assert_return(n
, -EINVAL
);
519 assert_return(ret
, -EINVAL
);
521 if (n
->port_id_as_string
) {
522 *ret
= n
->port_id_as_string
;
526 assert(n
->id
.port_id_size
> 0);
528 switch (*(uint8_t*) n
->id
.port_id
) {
530 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
531 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
532 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
533 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
534 k
= cescape_length((char*) n
->id
.port_id
+ 1, n
->id
.port_id_size
- 1);
540 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
541 r
= format_mac_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
549 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS
:
550 r
= format_network_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
559 /* Generic fallback */
560 k
= hexmem(n
->id
.port_id
, n
->id
.port_id_size
);
565 *ret
= n
->port_id_as_string
= k
;
569 int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor
*n
, uint16_t *ret_sec
) {
570 assert_return(n
, -EINVAL
);
571 assert_return(ret_sec
, -EINVAL
);
577 int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor
*n
, const char **ret
) {
578 assert_return(n
, -EINVAL
);
579 assert_return(ret
, -EINVAL
);
584 *ret
= n
->system_name
;
588 int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor
*n
, const char **ret
) {
589 assert_return(n
, -EINVAL
);
590 assert_return(ret
, -EINVAL
);
592 if (!n
->system_description
)
595 *ret
= n
->system_description
;
599 int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor
*n
, const char **ret
) {
600 assert_return(n
, -EINVAL
);
601 assert_return(ret
, -EINVAL
);
603 if (!n
->port_description
)
606 *ret
= n
->port_description
;
610 int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor
*n
, const char **ret
) {
611 assert_return(n
, -EINVAL
);
612 assert_return(ret
, -EINVAL
);
621 int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
622 assert_return(n
, -EINVAL
);
623 assert_return(ret
, -EINVAL
);
625 if (!n
->has_capabilities
)
628 *ret
= n
->system_capabilities
;
632 int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
633 assert_return(n
, -EINVAL
);
634 assert_return(ret
, -EINVAL
);
636 if (!n
->has_capabilities
)
639 *ret
= n
->enabled_capabilities
;
643 int sd_lldp_neighbor_from_raw(sd_lldp_neighbor
**ret
, const void *raw
, size_t raw_size
) {
644 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
647 assert_return(ret
, -EINVAL
);
648 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
650 n
= lldp_neighbor_new(raw_size
);
654 memcpy_safe(LLDP_NEIGHBOR_RAW(n
), raw
, raw_size
);
656 r
= lldp_neighbor_parse(n
);
665 int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor
*n
) {
666 assert_return(n
, -EINVAL
);
668 assert(n
->raw_size
>= sizeof(struct ether_header
));
669 n
->rindex
= sizeof(struct ether_header
);
671 return n
->rindex
< n
->raw_size
;
674 int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor
*n
) {
677 assert_return(n
, -EINVAL
);
679 if (n
->rindex
== n
->raw_size
) /* EOF */
682 if (n
->rindex
+ 2 > n
->raw_size
) /* Truncated message */
685 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
686 if (n
->rindex
+ 2 + length
> n
->raw_size
)
689 n
->rindex
+= 2 + length
;
690 return n
->rindex
< n
->raw_size
;
693 int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor
*n
, uint8_t *type
) {
694 assert_return(n
, -EINVAL
);
695 assert_return(type
, -EINVAL
);
697 if (n
->rindex
== n
->raw_size
) /* EOF */
700 if (n
->rindex
+ 2 > n
->raw_size
)
703 *type
= LLDP_NEIGHBOR_TLV_TYPE(n
);
707 int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor
*n
, uint8_t type
) {
711 assert_return(n
, -EINVAL
);
713 r
= sd_lldp_neighbor_tlv_get_type(n
, &k
);
720 int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor
*n
, uint8_t oui
[_SD_ARRAY_STATIC
3], uint8_t *subtype
) {
725 assert_return(n
, -EINVAL
);
726 assert_return(oui
, -EINVAL
);
727 assert_return(subtype
, -EINVAL
);
729 r
= sd_lldp_neighbor_tlv_is_type(n
, SD_LLDP_TYPE_PRIVATE
);
735 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
739 if (n
->rindex
+ 2 + length
> n
->raw_size
)
742 d
= LLDP_NEIGHBOR_TLV_DATA(n
);
749 int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor
*n
, const uint8_t oui
[_SD_ARRAY_STATIC
3], uint8_t subtype
) {
753 r
= sd_lldp_neighbor_tlv_get_oui(n
, k
, &st
);
759 return memcmp(k
, oui
, 3) == 0 && st
== subtype
;
762 int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
765 assert_return(n
, -EINVAL
);
766 assert_return(ret
, -EINVAL
);
767 assert_return(size
, -EINVAL
);
769 /* Note that this returns the full TLV, including the TLV header */
771 if (n
->rindex
+ 2 > n
->raw_size
)
774 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
775 if (n
->rindex
+ 2 + length
> n
->raw_size
)
778 *ret
= (uint8_t*) LLDP_NEIGHBOR_RAW(n
) + n
->rindex
;
784 int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor
*n
, clockid_t clock
, uint64_t *ret
) {
785 assert_return(n
, -EINVAL
);
786 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
787 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
788 assert_return(ret
, -EINVAL
);
790 if (!triple_timestamp_is_set(&n
->timestamp
))
793 *ret
= triple_timestamp_by_clock(&n
->timestamp
, clock
);