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-internal.h"
9 #include "lldp-neighbor.h"
10 #include "memory-util.h"
11 #include "missing_network.h"
12 #include "unaligned.h"
14 static void lldp_neighbor_id_hash_func(const LLDPNeighborID
*id
, struct siphash
*state
) {
15 siphash24_compress(id
->chassis_id
, id
->chassis_id_size
, state
);
16 siphash24_compress(&id
->chassis_id_size
, sizeof(id
->chassis_id_size
), state
);
17 siphash24_compress(id
->port_id
, id
->port_id_size
, state
);
18 siphash24_compress(&id
->port_id_size
, sizeof(id
->port_id_size
), state
);
21 int lldp_neighbor_id_compare_func(const LLDPNeighborID
*x
, const LLDPNeighborID
*y
) {
22 return memcmp_nn(x
->chassis_id
, x
->chassis_id_size
, y
->chassis_id
, y
->chassis_id_size
)
23 ?: memcmp_nn(x
->port_id
, x
->port_id_size
, y
->port_id
, y
->port_id_size
);
26 DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(lldp_neighbor_hash_ops
, LLDPNeighborID
, lldp_neighbor_id_hash_func
, lldp_neighbor_id_compare_func
,
27 sd_lldp_neighbor
, lldp_neighbor_unlink
);
29 int lldp_neighbor_prioq_compare_func(const void *a
, const void *b
) {
30 const sd_lldp_neighbor
*x
= a
, *y
= b
;
32 return CMP(x
->until
, y
->until
);
35 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_ref(sd_lldp_neighbor
*n
) {
39 assert(n
->n_ref
> 0 || n
->lldp
);
45 static void lldp_neighbor_free(sd_lldp_neighbor
*n
) {
49 free(n
->id
.chassis_id
);
50 free(n
->port_description
);
52 free(n
->system_description
);
54 free(n
->chassis_id_as_string
);
55 free(n
->port_id_as_string
);
59 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_unref(sd_lldp_neighbor
*n
) {
61 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
62 * the sd_lldp object. */
70 if (n
->n_ref
<= 0 && !n
->lldp
)
71 lldp_neighbor_free(n
);
76 sd_lldp_neighbor
*lldp_neighbor_unlink(sd_lldp_neighbor
*n
) {
78 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
86 /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
87 * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
88 * ourselves from the hashtable and sometimes are called after we already are de-registered. */
90 (void) hashmap_remove_value(n
->lldp
->neighbor_by_id
, &n
->id
, n
);
92 assert_se(prioq_remove(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
) >= 0);
97 lldp_neighbor_free(n
);
102 sd_lldp_neighbor
*lldp_neighbor_new(size_t raw_size
) {
105 n
= malloc0(ALIGN(sizeof(sd_lldp_neighbor
)) + raw_size
);
109 n
->raw_size
= raw_size
;
115 static int parse_string(sd_lldp
*lldp
, char **s
, const void *q
, size_t n
) {
123 log_lldp(lldp
, "Found duplicate string, ignoring field.");
127 /* Strip trailing NULs, just to be nice */
128 while (n
> 0 && p
[n
-1] == 0)
131 if (n
<= 0) /* Ignore empty strings */
134 /* Look for inner NULs */
135 if (memchr(p
, 0, n
)) {
136 log_lldp(lldp
, "Found inner NUL in string, ignoring field.");
140 /* Let's escape weird chars, for security reasons */
141 k
= cescape_length(p
, n
);
143 return log_oom_debug();
151 int lldp_neighbor_parse(sd_lldp_neighbor
*n
) {
152 struct ether_header h
;
159 if (n
->raw_size
< sizeof(struct ether_header
))
160 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
161 "Received truncated packet, ignoring.");
163 memcpy(&h
, LLDP_NEIGHBOR_RAW(n
), sizeof(h
));
165 if (h
.ether_type
!= htobe16(ETHERTYPE_LLDP
))
166 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
167 "Received packet with wrong type, ignoring.");
169 if (h
.ether_dhost
[0] != 0x01 ||
170 h
.ether_dhost
[1] != 0x80 ||
171 h
.ether_dhost
[2] != 0xc2 ||
172 h
.ether_dhost
[3] != 0x00 ||
173 h
.ether_dhost
[4] != 0x00 ||
174 !IN_SET(h
.ether_dhost
[5], 0x00, 0x03, 0x0e))
175 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
176 "Received packet with wrong destination address, ignoring.");
178 memcpy(&n
->source_address
, h
.ether_shost
, sizeof(struct ether_addr
));
179 memcpy(&n
->destination_address
, h
.ether_dhost
, sizeof(struct ether_addr
));
181 p
= (const uint8_t*) LLDP_NEIGHBOR_RAW(n
) + sizeof(struct ether_header
);
182 left
= n
->raw_size
- sizeof(struct ether_header
);
189 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
190 "TLV lacks header, ignoring.");
193 length
= p
[1] + (((uint16_t) (p
[0] & 1)) << 8);
197 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
198 "TLV truncated, ignoring datagram.");
202 case SD_LLDP_TYPE_END
:
204 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
205 "End marker TLV not zero-sized, ignoring datagram.");
207 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
208 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
212 case SD_LLDP_TYPE_CHASSIS_ID
:
213 if (length
< 2 || length
> 256)
214 /* includes the chassis subtype, hence one extra byte */
215 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
216 "Chassis ID field size out of range, ignoring datagram.");
218 if (n
->id
.chassis_id
)
219 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
220 "Duplicate chassis ID field, ignoring datagram.");
222 n
->id
.chassis_id
= memdup(p
, length
);
223 if (!n
->id
.chassis_id
)
224 return log_oom_debug();
226 n
->id
.chassis_id_size
= length
;
229 case SD_LLDP_TYPE_PORT_ID
:
230 if (length
< 2 || length
> 256)
231 /* includes the port subtype, hence one extra byte */
232 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
233 "Port ID field size out of range, ignoring datagram.");
236 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
237 "Duplicate port ID field, ignoring datagram.");
239 n
->id
.port_id
= memdup(p
, length
);
241 return log_oom_debug();
243 n
->id
.port_id_size
= length
;
246 case SD_LLDP_TYPE_TTL
:
248 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
249 "TTL field has wrong size, ignoring datagram.");
252 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
253 "Duplicate TTL field, ignoring datagram.");
255 n
->ttl
= unaligned_read_be16(p
);
259 case SD_LLDP_TYPE_PORT_DESCRIPTION
:
260 r
= parse_string(n
->lldp
, &n
->port_description
, p
, length
);
265 case SD_LLDP_TYPE_SYSTEM_NAME
:
266 r
= parse_string(n
->lldp
, &n
->system_name
, p
, length
);
271 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION
:
272 r
= parse_string(n
->lldp
, &n
->system_description
, p
, length
);
277 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES
:
279 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
280 "System capabilities field has wrong size.");
282 n
->system_capabilities
= unaligned_read_be16(p
);
283 n
->enabled_capabilities
= unaligned_read_be16(p
+ 2);
284 n
->has_capabilities
= true;
287 case SD_LLDP_TYPE_PRIVATE
:
289 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
290 "Found private TLV that is too short, ignoring.");
292 /* RFC 8520: MUD URL */
293 if (memcmp(p
, SD_LLDP_OUI_MUD
, sizeof(SD_LLDP_OUI_MUD
)) == 0 &&
294 p
[sizeof(SD_LLDP_OUI_MUD
)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION
) {
295 r
= parse_string(n
->lldp
, &n
->mud_url
, p
+ sizeof(SD_LLDP_OUI_MUD
) + 1,
296 length
- 1 - sizeof(SD_LLDP_OUI_MUD
));
303 p
+= length
, left
-= length
;
307 if (!n
->id
.chassis_id
|| !n
->id
.port_id
|| !n
->has_ttl
)
308 return log_lldp_errno(n
->lldp
, SYNTHETIC_ERRNO(EBADMSG
),
309 "One or more mandatory TLV missing in datagram. Ignoring.");
311 n
->rindex
= sizeof(struct ether_header
);
316 void lldp_neighbor_start_ttl(sd_lldp_neighbor
*n
) {
322 /* Use the packet's timestamp if there is one known */
323 base
= triple_timestamp_by_clock(&n
->timestamp
, clock_boottime_or_monotonic());
324 if (base
<= 0 || base
== USEC_INFINITY
)
325 base
= now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
327 n
->until
= usec_add(base
, n
->ttl
* USEC_PER_SEC
);
332 prioq_reshuffle(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
335 bool lldp_neighbor_equal(const sd_lldp_neighbor
*a
, const sd_lldp_neighbor
*b
) {
342 if (a
->raw_size
!= b
->raw_size
)
345 return memcmp(LLDP_NEIGHBOR_RAW(a
), LLDP_NEIGHBOR_RAW(b
), a
->raw_size
) == 0;
348 _public_
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
349 assert_return(n
, -EINVAL
);
350 assert_return(address
, -EINVAL
);
352 *address
= n
->source_address
;
356 _public_
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
357 assert_return(n
, -EINVAL
);
358 assert_return(address
, -EINVAL
);
360 *address
= n
->destination_address
;
364 _public_
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
365 assert_return(n
, -EINVAL
);
366 assert_return(ret
, -EINVAL
);
367 assert_return(size
, -EINVAL
);
369 *ret
= LLDP_NEIGHBOR_RAW(n
);
375 _public_
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
376 assert_return(n
, -EINVAL
);
377 assert_return(type
, -EINVAL
);
378 assert_return(ret
, -EINVAL
);
379 assert_return(size
, -EINVAL
);
381 assert(n
->id
.chassis_id_size
> 0);
383 *type
= *(uint8_t*) n
->id
.chassis_id
;
384 *ret
= (uint8_t*) n
->id
.chassis_id
+ 1;
385 *size
= n
->id
.chassis_id_size
- 1;
390 static int format_mac_address(const void *data
, size_t sz
, char **ret
) {
394 assert(data
|| sz
<= 0);
399 memcpy(&a
, (uint8_t*) data
+ 1, sizeof(a
));
401 k
= new(char, ETHER_ADDR_TO_STRING_MAX
);
405 *ret
= ether_addr_to_string(&a
, k
);
409 static int format_network_address(const void *data
, size_t sz
, char **ret
) {
410 union in_addr_union a
;
413 if (sz
== 6 && ((uint8_t*) data
)[1] == 1) {
414 memcpy(&a
.in
, (uint8_t*) data
+ 2, sizeof(a
.in
));
416 } else if (sz
== 18 && ((uint8_t*) data
)[1] == 2) {
417 memcpy(&a
.in6
, (uint8_t*) data
+ 2, sizeof(a
.in6
));
422 r
= in_addr_to_string(family
, &a
, ret
);
428 _public_
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
432 assert_return(n
, -EINVAL
);
433 assert_return(ret
, -EINVAL
);
435 if (n
->chassis_id_as_string
) {
436 *ret
= n
->chassis_id_as_string
;
440 assert(n
->id
.chassis_id_size
> 0);
442 switch (*(uint8_t*) n
->id
.chassis_id
) {
444 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT
:
445 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS
:
446 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT
:
447 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME
:
448 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
:
449 k
= cescape_length((char*) n
->id
.chassis_id
+ 1, n
->id
.chassis_id_size
- 1);
455 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
456 r
= format_mac_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
464 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS
:
465 r
= format_network_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
474 /* Generic fallback */
475 k
= hexmem(n
->id
.chassis_id
, n
->id
.chassis_id_size
);
480 *ret
= n
->chassis_id_as_string
= k
;
484 _public_
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
485 assert_return(n
, -EINVAL
);
486 assert_return(type
, -EINVAL
);
487 assert_return(ret
, -EINVAL
);
488 assert_return(size
, -EINVAL
);
490 assert(n
->id
.port_id_size
> 0);
492 *type
= *(uint8_t*) n
->id
.port_id
;
493 *ret
= (uint8_t*) n
->id
.port_id
+ 1;
494 *size
= n
->id
.port_id_size
- 1;
499 _public_
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
503 assert_return(n
, -EINVAL
);
504 assert_return(ret
, -EINVAL
);
506 if (n
->port_id_as_string
) {
507 *ret
= n
->port_id_as_string
;
511 assert(n
->id
.port_id_size
> 0);
513 switch (*(uint8_t*) n
->id
.port_id
) {
515 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
516 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
517 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
518 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
519 k
= cescape_length((char*) n
->id
.port_id
+ 1, n
->id
.port_id_size
- 1);
525 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
526 r
= format_mac_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
534 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS
:
535 r
= format_network_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
544 /* Generic fallback */
545 k
= hexmem(n
->id
.port_id
, n
->id
.port_id_size
);
550 *ret
= n
->port_id_as_string
= k
;
554 _public_
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor
*n
, uint16_t *ret_sec
) {
555 assert_return(n
, -EINVAL
);
556 assert_return(ret_sec
, -EINVAL
);
562 _public_
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor
*n
, const char **ret
) {
563 assert_return(n
, -EINVAL
);
564 assert_return(ret
, -EINVAL
);
569 *ret
= n
->system_name
;
573 _public_
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor
*n
, const char **ret
) {
574 assert_return(n
, -EINVAL
);
575 assert_return(ret
, -EINVAL
);
577 if (!n
->system_description
)
580 *ret
= n
->system_description
;
584 _public_
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor
*n
, const char **ret
) {
585 assert_return(n
, -EINVAL
);
586 assert_return(ret
, -EINVAL
);
588 if (!n
->port_description
)
591 *ret
= n
->port_description
;
595 _public_
int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor
*n
, const char **ret
) {
596 assert_return(n
, -EINVAL
);
597 assert_return(ret
, -EINVAL
);
606 _public_
int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
607 assert_return(n
, -EINVAL
);
608 assert_return(ret
, -EINVAL
);
610 if (!n
->has_capabilities
)
613 *ret
= n
->system_capabilities
;
617 _public_
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
618 assert_return(n
, -EINVAL
);
619 assert_return(ret
, -EINVAL
);
621 if (!n
->has_capabilities
)
624 *ret
= n
->enabled_capabilities
;
628 _public_
int sd_lldp_neighbor_from_raw(sd_lldp_neighbor
**ret
, const void *raw
, size_t raw_size
) {
629 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
632 assert_return(ret
, -EINVAL
);
633 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
635 n
= lldp_neighbor_new(raw_size
);
639 memcpy(LLDP_NEIGHBOR_RAW(n
), raw
, raw_size
);
640 r
= lldp_neighbor_parse(n
);
649 _public_
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor
*n
) {
650 assert_return(n
, -EINVAL
);
652 assert(n
->raw_size
>= sizeof(struct ether_header
));
653 n
->rindex
= sizeof(struct ether_header
);
655 return n
->rindex
< n
->raw_size
;
658 _public_
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor
*n
) {
661 assert_return(n
, -EINVAL
);
663 if (n
->rindex
== n
->raw_size
) /* EOF */
666 if (n
->rindex
+ 2 > n
->raw_size
) /* Truncated message */
669 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
670 if (n
->rindex
+ 2 + length
> n
->raw_size
)
673 n
->rindex
+= 2 + length
;
674 return n
->rindex
< n
->raw_size
;
677 _public_
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor
*n
, uint8_t *type
) {
678 assert_return(n
, -EINVAL
);
679 assert_return(type
, -EINVAL
);
681 if (n
->rindex
== n
->raw_size
) /* EOF */
684 if (n
->rindex
+ 2 > n
->raw_size
)
687 *type
= LLDP_NEIGHBOR_TLV_TYPE(n
);
691 _public_
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor
*n
, uint8_t type
) {
695 assert_return(n
, -EINVAL
);
697 r
= sd_lldp_neighbor_tlv_get_type(n
, &k
);
704 _public_
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor
*n
, uint8_t oui
[_SD_ARRAY_STATIC
3], uint8_t *subtype
) {
709 assert_return(n
, -EINVAL
);
710 assert_return(oui
, -EINVAL
);
711 assert_return(subtype
, -EINVAL
);
713 r
= sd_lldp_neighbor_tlv_is_type(n
, SD_LLDP_TYPE_PRIVATE
);
719 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
723 if (n
->rindex
+ 2 + length
> n
->raw_size
)
726 d
= LLDP_NEIGHBOR_TLV_DATA(n
);
733 _public_
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor
*n
, const uint8_t oui
[_SD_ARRAY_STATIC
3], uint8_t subtype
) {
737 r
= sd_lldp_neighbor_tlv_get_oui(n
, k
, &st
);
743 return memcmp(k
, oui
, 3) == 0 && st
== subtype
;
746 _public_
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
749 assert_return(n
, -EINVAL
);
750 assert_return(ret
, -EINVAL
);
751 assert_return(size
, -EINVAL
);
753 /* Note that this returns the full TLV, including the TLV header */
755 if (n
->rindex
+ 2 > n
->raw_size
)
758 length
= LLDP_NEIGHBOR_TLV_LENGTH(n
);
759 if (n
->rindex
+ 2 + length
> n
->raw_size
)
762 *ret
= (uint8_t*) LLDP_NEIGHBOR_RAW(n
) + n
->rindex
;
768 _public_
int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor
*n
, clockid_t clock
, uint64_t *ret
) {
769 assert_return(n
, -EINVAL
);
770 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock
), -EOPNOTSUPP
);
771 assert_return(clock_supported(clock
), -EOPNOTSUPP
);
772 assert_return(ret
, -EINVAL
);
774 if (!triple_timestamp_is_set(&n
->timestamp
))
777 *ret
= triple_timestamp_by_clock(&n
->timestamp
, clock
);