2 This file is part of systemd.
4 Copyright 2016 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 #include "alloc-util.h"
22 #include "ether-addr-util.h"
23 #include "hexdecoct.h"
24 #include "in-addr-util.h"
25 #include "lldp-internal.h"
26 #include "lldp-neighbor.h"
27 #include "unaligned.h"
29 static void lldp_neighbor_id_hash_func(const void *p
, struct siphash
*state
) {
30 const LLDPNeighborID
*id
= p
;
32 siphash24_compress(id
->chassis_id
, id
->chassis_id_size
, state
);
33 siphash24_compress(&id
->chassis_id_size
, sizeof(id
->chassis_id_size
), state
);
34 siphash24_compress(id
->port_id
, id
->port_id_size
, state
);
35 siphash24_compress(&id
->port_id_size
, sizeof(id
->port_id_size
), state
);
38 static int lldp_neighbor_id_compare_func(const void *a
, const void *b
) {
39 const LLDPNeighborID
*x
= a
, *y
= b
;
42 r
= memcmp(x
->chassis_id
, y
->chassis_id
, MIN(x
->chassis_id_size
, y
->chassis_id_size
));
46 if (x
->chassis_id_size
< y
->chassis_id_size
)
49 if (x
->chassis_id_size
> y
->chassis_id_size
)
52 r
= memcmp(x
->port_id
, y
->port_id
, MIN(x
->port_id_size
, y
->port_id_size
));
56 if (x
->port_id_size
< y
->port_id_size
)
58 if (x
->port_id_size
> y
->port_id_size
)
64 const struct hash_ops lldp_neighbor_id_hash_ops
= {
65 .hash
= lldp_neighbor_id_hash_func
,
66 .compare
= lldp_neighbor_id_compare_func
69 int lldp_neighbor_prioq_compare_func(const void *a
, const void *b
) {
70 const sd_lldp_neighbor
*x
= a
, *y
= b
;
72 if (x
->until
< y
->until
)
75 if (x
->until
> y
->until
)
81 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_ref(sd_lldp_neighbor
*n
) {
85 assert(n
->n_ref
> 0 || n
->lldp
);
91 static void lldp_neighbor_free(sd_lldp_neighbor
*n
) {
95 free(n
->id
.chassis_id
);
96 free(n
->port_description
);
98 free(n
->system_description
);
99 free(n
->chassis_id_as_string
);
100 free(n
->port_id_as_string
);
104 _public_ sd_lldp_neighbor
*sd_lldp_neighbor_unref(sd_lldp_neighbor
*n
) {
106 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
107 * the sd_lldp object. */
112 assert(n
->n_ref
> 0);
115 if (n
->n_ref
<= 0 && !n
->lldp
)
116 lldp_neighbor_free(n
);
121 sd_lldp_neighbor
*lldp_neighbor_unlink(sd_lldp_neighbor
*n
) {
123 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
131 assert_se(hashmap_remove(n
->lldp
->neighbor_by_id
, &n
->id
) == n
);
132 assert_se(prioq_remove(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
) >= 0);
137 lldp_neighbor_free(n
);
142 sd_lldp_neighbor
*lldp_neighbor_new(size_t raw_size
) {
145 n
= malloc0(ALIGN(sizeof(sd_lldp_neighbor
)) + raw_size
);
149 n
->raw_size
= raw_size
;
155 static int parse_string(char **s
, const void *q
, size_t n
) {
163 log_lldp("Found duplicate string, ignoring field.");
167 /* Strip trailing NULs, just to be nice */
168 while (n
> 0 && p
[n
-1] == 0)
171 if (n
<= 0) /* Ignore empty strings */
174 /* Look for inner NULs */
175 if (memchr(p
, 0, n
)) {
176 log_lldp("Found inner NUL in string, ignoring field.");
180 /* Let's escape weird chars, for security reasons */
181 k
= cescape_length(p
, n
);
191 int lldp_neighbor_parse(sd_lldp_neighbor
*n
) {
192 struct ether_header h
;
199 if (n
->raw_size
< sizeof(struct ether_header
)) {
200 log_lldp("Recieved truncated packet, ignoring.");
204 memcpy(&h
, LLDP_NEIGHBOR_RAW(n
), sizeof(h
));
206 if (h
.ether_type
!= htobe16(ETHERTYPE_LLDP
)) {
207 log_lldp("Received packet with wrong type, ignoring.");
211 if (h
.ether_dhost
[0] != 0x01 ||
212 h
.ether_dhost
[1] != 0x80 ||
213 h
.ether_dhost
[2] != 0xc2 ||
214 h
.ether_dhost
[3] != 0x00 ||
215 h
.ether_dhost
[4] != 0x00 ||
216 !IN_SET(h
.ether_dhost
[5], 0x00, 0x03, 0x0e)) {
217 log_lldp("Received packet with wrong destination address, ignoring.");
221 memcpy(&n
->source_address
, h
.ether_shost
, sizeof(struct ether_addr
));
222 memcpy(&n
->destination_address
, h
.ether_dhost
, sizeof(struct ether_addr
));
224 p
= (const uint8_t*) LLDP_NEIGHBOR_RAW(n
) + sizeof(struct ether_header
);
225 left
= n
->raw_size
- sizeof(struct ether_header
);
232 log_lldp("TLV lacks header, ignoring.");
237 length
= p
[1] + (((uint16_t) (p
[0] & 1)) << 8);
241 log_lldp("TLV truncated, ignoring datagram.");
247 case SD_LLDP_TYPE_END
:
249 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
253 log_lldp("Trailing garbage in datagram, ignoring datagram.");
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
) {
364 n
->until
= usec_add(now(clock_boottime_or_monotonic()), n
->ttl
* USEC_PER_SEC
);
369 prioq_reshuffle(n
->lldp
->neighbor_by_expiry
, n
, &n
->prioq_idx
);
372 bool lldp_neighbor_equal(const sd_lldp_neighbor
*a
, const sd_lldp_neighbor
*b
) {
379 if (a
->raw_size
!= b
->raw_size
)
382 return memcmp(LLDP_NEIGHBOR_RAW(a
), LLDP_NEIGHBOR_RAW(b
), a
->raw_size
) == 0;
385 _public_
int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
386 assert_return(n
, -EINVAL
);
387 assert_return(address
, -EINVAL
);
389 *address
= n
->source_address
;
393 _public_
int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor
*n
, struct ether_addr
* address
) {
394 assert_return(n
, -EINVAL
);
395 assert_return(address
, -EINVAL
);
397 *address
= n
->destination_address
;
401 _public_
int sd_lldp_neighbor_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
402 assert_return(n
, -EINVAL
);
403 assert_return(ret
, -EINVAL
);
404 assert_return(size
, -EINVAL
);
406 *ret
= LLDP_NEIGHBOR_RAW(n
);
412 _public_
int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
413 assert_return(n
, -EINVAL
);
414 assert_return(type
, -EINVAL
);
415 assert_return(ret
, -EINVAL
);
416 assert_return(size
, -EINVAL
);
418 assert(n
->id
.chassis_id_size
> 0);
420 *type
= *(uint8_t*) n
->id
.chassis_id
;
421 *ret
= (uint8_t*) n
->id
.chassis_id
+ 1;
422 *size
= n
->id
.chassis_id_size
- 1;
427 static int format_mac_address(const void *data
, size_t sz
, char **ret
) {
431 assert(data
|| sz
<= 0);
436 memcpy(&a
, (uint8_t*) data
+ 1, sizeof(a
));
438 k
= new(char, ETHER_ADDR_TO_STRING_MAX
);
442 *ret
= ether_addr_to_string(&a
, k
);
446 static int format_network_address(const void *data
, size_t sz
, char **ret
) {
447 union in_addr_union a
;
450 if (sz
== 6 && ((uint8_t*) data
)[1] == 1) {
451 memcpy(&a
.in
, (uint8_t*) data
+ 2, sizeof(a
.in
));
453 } else if (sz
== 18 && ((uint8_t*) data
)[1] == 2) {
454 memcpy(&a
.in6
, (uint8_t*) data
+ 2, sizeof(a
.in6
));
459 r
= in_addr_to_string(family
, &a
, ret
);
465 _public_
int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
469 assert_return(n
, -EINVAL
);
470 assert_return(ret
, -EINVAL
);
472 if (n
->chassis_id_as_string
) {
473 *ret
= n
->chassis_id_as_string
;
477 assert(n
->id
.chassis_id_size
> 0);
479 switch (*(uint8_t*) n
->id
.chassis_id
) {
481 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT
:
482 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS
:
483 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT
:
484 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME
:
485 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED
:
486 k
= cescape_length((char*) n
->id
.chassis_id
+ 1, n
->id
.chassis_id_size
- 1);
492 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS
:
493 r
= format_mac_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
501 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS
:
502 r
= format_network_address(n
->id
.chassis_id
, n
->id
.chassis_id_size
, &k
);
511 /* Generic fallback */
512 k
= hexmem(n
->id
.chassis_id
, n
->id
.chassis_id_size
);
517 *ret
= n
->chassis_id_as_string
= k
;
521 _public_
int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor
*n
, uint8_t *type
, const void **ret
, size_t *size
) {
522 assert_return(n
, -EINVAL
);
523 assert_return(type
, -EINVAL
);
524 assert_return(ret
, -EINVAL
);
525 assert_return(size
, -EINVAL
);
527 assert(n
->id
.port_id_size
> 0);
529 *type
= *(uint8_t*) n
->id
.port_id
;
530 *ret
= (uint8_t*) n
->id
.port_id
+ 1;
531 *size
= n
->id
.port_id_size
- 1;
536 _public_
int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor
*n
, const char **ret
) {
540 assert_return(n
, -EINVAL
);
541 assert_return(ret
, -EINVAL
);
543 if (n
->port_id_as_string
) {
544 *ret
= n
->port_id_as_string
;
548 assert(n
->id
.port_id_size
> 0);
550 switch (*(uint8_t*) n
->id
.port_id
) {
552 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS
:
553 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT
:
554 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME
:
555 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED
:
556 k
= cescape_length((char*) n
->id
.port_id
+ 1, n
->id
.port_id_size
- 1);
562 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS
:
563 r
= format_mac_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
571 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS
:
572 r
= format_network_address(n
->id
.port_id
, n
->id
.port_id_size
, &k
);
581 /* Generic fallback */
582 k
= hexmem(n
->id
.port_id
, n
->id
.port_id_size
);
587 *ret
= n
->port_id_as_string
= k
;
591 _public_
int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor
*n
, uint16_t *ret
) {
592 assert_return(n
, -EINVAL
);
593 assert_return(ret
, -EINVAL
);
599 _public_
int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor
*n
, const char **ret
) {
600 assert_return(n
, -EINVAL
);
601 assert_return(ret
, -EINVAL
);
606 *ret
= n
->system_name
;
610 _public_
int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor
*n
, const char **ret
) {
611 assert_return(n
, -EINVAL
);
612 assert_return(ret
, -EINVAL
);
614 if (!n
->system_description
)
617 *ret
= n
->system_description
;
621 _public_
int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor
*n
, const char **ret
) {
622 assert_return(n
, -EINVAL
);
623 assert_return(ret
, -EINVAL
);
625 if (!n
->port_description
)
628 *ret
= n
->port_description
;
632 _public_
int sd_lldp_neighbor_get_system_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
->system_capabilities
;
643 _public_
int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor
*n
, uint16_t *ret
) {
644 assert_return(n
, -EINVAL
);
645 assert_return(ret
, -EINVAL
);
647 if (!n
->has_capabilities
)
650 *ret
= n
->enabled_capabilities
;
654 int sd_lldp_neighbor_from_raw(sd_lldp_neighbor
**ret
, const void *raw
, size_t raw_size
) {
655 _cleanup_(sd_lldp_neighbor_unrefp
) sd_lldp_neighbor
*n
= NULL
;
658 assert_return(ret
, -EINVAL
);
659 assert_return(raw
|| raw_size
<= 0, -EINVAL
);
661 n
= lldp_neighbor_new(raw_size
);
665 memcpy(LLDP_NEIGHBOR_RAW(n
), raw
, raw_size
);
666 r
= lldp_neighbor_parse(n
);
676 _public_
int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor
*n
) {
677 assert_return(n
, -EINVAL
);
679 assert(n
->raw_size
>= sizeof(struct ether_header
));
680 n
->rindex
= sizeof(struct ether_header
);
685 _public_
int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor
*n
) {
688 assert_return(n
, -EINVAL
);
690 if (n
->rindex
== n
->raw_size
) /* EOF */
693 if (n
->rindex
+ 2 > n
->raw_size
) /* Truncated message */
696 length
= LLDP_NEIGHBOR_LENGTH(n
);
697 if (n
->rindex
+ 2 + length
> n
->raw_size
)
700 n
->rindex
+= 2 + length
;
701 return n
->rindex
< n
->raw_size
;
704 _public_
int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor
*n
, uint8_t *type
) {
705 assert_return(n
, -EINVAL
);
706 assert_return(type
, -EINVAL
);
708 if (n
->rindex
== n
->raw_size
) /* EOF */
711 if (n
->rindex
+ 2 > n
->raw_size
)
714 *type
= LLDP_NEIGHBOR_TYPE(n
);
718 _public_
int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor
*n
, uint8_t type
) {
722 assert_return(n
, -EINVAL
);
724 r
= sd_lldp_neighbor_tlv_get_type(n
, &k
);
731 _public_
int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor
*n
, uint8_t oui
[3], uint8_t *subtype
) {
736 assert_return(n
, -EINVAL
);
737 assert_return(oui
, -EINVAL
);
738 assert_return(subtype
, -EINVAL
);
740 r
= sd_lldp_neighbor_tlv_is_type(n
, SD_LLDP_TYPE_PRIVATE
);
746 length
= LLDP_NEIGHBOR_LENGTH(n
);
750 if (n
->rindex
+ 2 + length
> n
->raw_size
)
753 d
= LLDP_NEIGHBOR_DATA(n
);
760 _public_
int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor
*n
, const uint8_t oui
[3], uint8_t subtype
) {
764 r
= sd_lldp_neighbor_tlv_get_oui(n
, k
, &st
);
770 return memcmp(k
, oui
, 3) == 0 && st
== subtype
;
773 _public_
int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor
*n
, const void **ret
, size_t *size
) {
776 assert_return(n
, -EINVAL
);
777 assert_return(ret
, -EINVAL
);
778 assert_return(size
, -EINVAL
);
780 /* Note that this returns the full TLV, including the TLV header */
782 if (n
->rindex
+ 2 > n
->raw_size
)
785 length
= LLDP_NEIGHBOR_LENGTH(n
);
787 if (n
->rindex
+ 2 + length
> n
->raw_size
)
790 *ret
= (uint8_t*) LLDP_NEIGHBOR_RAW(n
) + n
->rindex
;