]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/lldp-neighbor.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd-network / lldp-neighbor.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
34437b4f
LP
2
3#include "alloc-util.h"
4#include "escape.h"
5#include "ether-addr-util.h"
6#include "hexdecoct.h"
7#include "in-addr-util.h"
8#include "lldp-internal.h"
9#include "lldp-neighbor.h"
36dd5ffd 10#include "missing.h"
34437b4f 11#include "unaligned.h"
dc6bf94d 12#include "util.h"
34437b4f 13
7a08d314 14static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
34437b4f
LP
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);
19}
20
dc5f9c6f 21int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
dc6bf94d
FB
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);
34437b4f
LP
24}
25
8276855e
YW
26DEFINE_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);
34437b4f
LP
28
29int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
30 const sd_lldp_neighbor *x = a, *y = b;
31
a0edd02e 32 return CMP(x->until, y->until);
34437b4f
LP
33}
34
35_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
36 if (!n)
37 return NULL;
38
39 assert(n->n_ref > 0 || n->lldp);
40 n->n_ref++;
41
42 return n;
43}
44
45static void lldp_neighbor_free(sd_lldp_neighbor *n) {
46 assert(n);
47
48 free(n->id.port_id);
49 free(n->id.chassis_id);
50 free(n->port_description);
51 free(n->system_name);
52 free(n->system_description);
53 free(n->chassis_id_as_string);
54 free(n->port_id_as_string);
55 free(n);
56}
57
58_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
59
60 /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
61 * the sd_lldp object. */
62
63 if (!n)
64 return NULL;
65
66 assert(n->n_ref > 0);
67 n->n_ref--;
68
69 if (n->n_ref <= 0 && !n->lldp)
70 lldp_neighbor_free(n);
71
72 return NULL;
73}
74
75sd_lldp_neighbor *lldp_neighbor_unlink(sd_lldp_neighbor *n) {
76
77 /* Removes the neighbor object from the LLDP object, and frees it if it also has no other reference. */
78
79 if (!n)
80 return NULL;
81
82 if (!n->lldp)
83 return NULL;
84
8ae1a821
LP
85 /* Only remove the neighbor object from the hash table if it's in there, don't complain if it isn't. This is
86 * because we are used as destructor call for hashmap_clear() and thus sometimes are called to de-register
87 * ourselves from the hashtable and sometimes are called after we already are de-registered. */
88
89 (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
90
34437b4f
LP
91 assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
92
93 n->lldp = NULL;
94
95 if (n->n_ref <= 0)
96 lldp_neighbor_free(n);
97
98 return NULL;
99}
100
101sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
102 sd_lldp_neighbor *n;
103
104 n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
105 if (!n)
106 return NULL;
107
108 n->raw_size = raw_size;
109 n->n_ref = 1;
110
111 return n;
112}
113
114static int parse_string(char **s, const void *q, size_t n) {
115 const char *p = q;
116 char *k;
117
118 assert(s);
119 assert(p || n == 0);
120
121 if (*s) {
122 log_lldp("Found duplicate string, ignoring field.");
123 return 0;
124 }
125
126 /* Strip trailing NULs, just to be nice */
127 while (n > 0 && p[n-1] == 0)
128 n--;
129
130 if (n <= 0) /* Ignore empty strings */
131 return 0;
132
133 /* Look for inner NULs */
134 if (memchr(p, 0, n)) {
135 log_lldp("Found inner NUL in string, ignoring field.");
136 return 0;
137 }
138
139 /* Let's escape weird chars, for security reasons */
140 k = cescape_length(p, n);
141 if (!k)
142 return -ENOMEM;
143
144 free(*s);
145 *s = k;
146
147 return 1;
148}
149
150int lldp_neighbor_parse(sd_lldp_neighbor *n) {
151 struct ether_header h;
152 const uint8_t *p;
153 size_t left;
154 int r;
155
156 assert(n);
157
158 if (n->raw_size < sizeof(struct ether_header)) {
595bfe7d 159 log_lldp("Received truncated packet, ignoring.");
34437b4f
LP
160 return -EBADMSG;
161 }
162
163 memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
164
165 if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
166 log_lldp("Received packet with wrong type, ignoring.");
167 return -EBADMSG;
168 }
169
170 if (h.ether_dhost[0] != 0x01 ||
171 h.ether_dhost[1] != 0x80 ||
172 h.ether_dhost[2] != 0xc2 ||
173 h.ether_dhost[3] != 0x00 ||
174 h.ether_dhost[4] != 0x00 ||
175 !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e)) {
176 log_lldp("Received packet with wrong destination address, ignoring.");
177 return -EBADMSG;
178 }
179
180 memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
181 memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
182
183 p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
184 left = n->raw_size - sizeof(struct ether_header);
185
186 for (;;) {
187 uint8_t type;
188 uint16_t length;
189
190 if (left < 2) {
191 log_lldp("TLV lacks header, ignoring.");
192 return -EBADMSG;
193 }
194
195 type = p[0] >> 1;
196 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
197 p += 2, left -= 2;
198
199 if (left < length) {
200 log_lldp("TLV truncated, ignoring datagram.");
201 return -EBADMSG;
202 }
203
204 switch (type) {
205
6afa6767 206 case SD_LLDP_TYPE_END:
34437b4f
LP
207 if (length != 0) {
208 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
209 return -EBADMSG;
210 }
34437b4f 211
8c7c7100 212 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
213 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
214
34437b4f
LP
215 goto end_marker;
216
6afa6767 217 case SD_LLDP_TYPE_CHASSIS_ID:
34437b4f
LP
218 if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
219 log_lldp("Chassis ID field size out of range, ignoring datagram.");
220 return -EBADMSG;
221 }
222 if (n->id.chassis_id) {
223 log_lldp("Duplicate chassis ID field, ignoring datagram.");
224 return -EBADMSG;
225 }
226
227 n->id.chassis_id = memdup(p, length);
228 if (!n->id.chassis_id)
229 return -ENOMEM;
230
231 n->id.chassis_id_size = length;
232 break;
233
6afa6767 234 case SD_LLDP_TYPE_PORT_ID:
34437b4f
LP
235 if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
236 log_lldp("Port ID field size out of range, ignoring datagram.");
237 return -EBADMSG;
238 }
239 if (n->id.port_id) {
240 log_lldp("Duplicate port ID field, ignoring datagram.");
241 return -EBADMSG;
242 }
243
244 n->id.port_id = memdup(p, length);
245 if (!n->id.port_id)
246 return -ENOMEM;
247
248 n->id.port_id_size = length;
249 break;
250
6afa6767 251 case SD_LLDP_TYPE_TTL:
34437b4f
LP
252 if (length != 2) {
253 log_lldp("TTL field has wrong size, ignoring datagram.");
254 return -EBADMSG;
255 }
256
257 if (n->has_ttl) {
258 log_lldp("Duplicate TTL field, ignoring datagram.");
259 return -EBADMSG;
260 }
261
262 n->ttl = unaligned_read_be16(p);
263 n->has_ttl = true;
264 break;
265
6afa6767 266 case SD_LLDP_TYPE_PORT_DESCRIPTION:
34437b4f
LP
267 r = parse_string(&n->port_description, p, length);
268 if (r < 0)
269 return r;
270 break;
271
6afa6767 272 case SD_LLDP_TYPE_SYSTEM_NAME:
34437b4f
LP
273 r = parse_string(&n->system_name, p, length);
274 if (r < 0)
275 return r;
276 break;
277
6afa6767 278 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
34437b4f
LP
279 r = parse_string(&n->system_description, p, length);
280 if (r < 0)
281 return r;
282 break;
283
6afa6767 284 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
34437b4f
LP
285 if (length != 4)
286 log_lldp("System capabilities field has wrong size, ignoring.");
287 else {
288 n->system_capabilities = unaligned_read_be16(p);
289 n->enabled_capabilities = unaligned_read_be16(p + 2);
290 n->has_capabilities = true;
291 }
292
293 break;
294
6afa6767 295 case SD_LLDP_TYPE_PRIVATE:
34437b4f
LP
296 if (length < 4)
297 log_lldp("Found private TLV that is too short, ignoring.");
298
299 break;
300 }
301
34437b4f
LP
302 p += length, left -= length;
303 }
304
305end_marker:
306 if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
307 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
308 return -EBADMSG;
309
310 }
311
312 n->rindex = sizeof(struct ether_header);
313
314 return 0;
315}
316
317void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
318 assert(n);
319
16fed825
LP
320 if (n->ttl > 0) {
321 usec_t base;
322
323 /* Use the packet's timestamp if there is one known */
324 base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
325 if (base <= 0 || base == USEC_INFINITY)
326 base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
327
328 n->until = usec_add(base, n->ttl * USEC_PER_SEC);
329 } else
34437b4f
LP
330 n->until = 0;
331
332 if (n->lldp)
333 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
334}
335
336bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
337 if (a == b)
338 return true;
339
340 if (!a || !b)
341 return false;
342
343 if (a->raw_size != b->raw_size)
344 return false;
345
346 return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
347}
348
349_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
350 assert_return(n, -EINVAL);
351 assert_return(address, -EINVAL);
352
353 *address = n->source_address;
354 return 0;
355}
356
357_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
358 assert_return(n, -EINVAL);
359 assert_return(address, -EINVAL);
360
361 *address = n->destination_address;
362 return 0;
363}
364
365_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
366 assert_return(n, -EINVAL);
367 assert_return(ret, -EINVAL);
368 assert_return(size, -EINVAL);
369
370 *ret = LLDP_NEIGHBOR_RAW(n);
371 *size = n->raw_size;
372
373 return 0;
374}
375
376_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
377 assert_return(n, -EINVAL);
378 assert_return(type, -EINVAL);
379 assert_return(ret, -EINVAL);
380 assert_return(size, -EINVAL);
381
382 assert(n->id.chassis_id_size > 0);
383
384 *type = *(uint8_t*) n->id.chassis_id;
385 *ret = (uint8_t*) n->id.chassis_id + 1;
386 *size = n->id.chassis_id_size - 1;
387
388 return 0;
389}
390
391static int format_mac_address(const void *data, size_t sz, char **ret) {
392 struct ether_addr a;
393 char *k;
394
395 assert(data || sz <= 0);
396
397 if (sz != 7)
398 return 0;
399
400 memcpy(&a, (uint8_t*) data + 1, sizeof(a));
401
402 k = new(char, ETHER_ADDR_TO_STRING_MAX);
403 if (!k)
404 return -ENOMEM;
405
406 *ret = ether_addr_to_string(&a, k);
407 return 1;
408}
409
410static int format_network_address(const void *data, size_t sz, char **ret) {
411 union in_addr_union a;
b3ad5fa9 412 int family, r;
34437b4f
LP
413
414 if (sz == 6 && ((uint8_t*) data)[1] == 1) {
415 memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
416 family = AF_INET;
417 } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
418 memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
419 family = AF_INET6;
420 } else
421 return 0;
422
b3ad5fa9
ZJS
423 r = in_addr_to_string(family, &a, ret);
424 if (r < 0)
425 return r;
426 return 1;
34437b4f
LP
427}
428
429_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
430 char *k;
431 int r;
432
433 assert_return(n, -EINVAL);
434 assert_return(ret, -EINVAL);
435
436 if (n->chassis_id_as_string) {
437 *ret = n->chassis_id_as_string;
438 return 0;
439 }
440
441 assert(n->id.chassis_id_size > 0);
442
443 switch (*(uint8_t*) n->id.chassis_id) {
444
6afa6767
BG
445 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
446 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
447 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
448 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
449 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
34437b4f
LP
450 k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
451 if (!k)
452 return -ENOMEM;
453
454 goto done;
455
6afa6767 456 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
457 r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
458 if (r < 0)
459 return r;
460 if (r > 0)
461 goto done;
462
463 break;
464
6afa6767 465 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
466 r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
467 if (r < 0)
468 return r;
469 if (r > 0)
470 goto done;
471
472 break;
473 }
474
475 /* Generic fallback */
476 k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
477 if (!k)
478 return -ENOMEM;
479
480done:
481 *ret = n->chassis_id_as_string = k;
482 return 0;
483}
484
485_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
486 assert_return(n, -EINVAL);
487 assert_return(type, -EINVAL);
488 assert_return(ret, -EINVAL);
489 assert_return(size, -EINVAL);
490
491 assert(n->id.port_id_size > 0);
492
493 *type = *(uint8_t*) n->id.port_id;
494 *ret = (uint8_t*) n->id.port_id + 1;
495 *size = n->id.port_id_size - 1;
496
497 return 0;
498}
499
500_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
501 char *k;
502 int r;
503
504 assert_return(n, -EINVAL);
505 assert_return(ret, -EINVAL);
506
507 if (n->port_id_as_string) {
508 *ret = n->port_id_as_string;
509 return 0;
510 }
511
512 assert(n->id.port_id_size > 0);
513
514 switch (*(uint8_t*) n->id.port_id) {
515
6afa6767
BG
516 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
517 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
518 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
519 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
34437b4f
LP
520 k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
521 if (!k)
522 return -ENOMEM;
523
524 goto done;
525
6afa6767 526 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
527 r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
528 if (r < 0)
529 return r;
530 if (r > 0)
531 goto done;
532
533 break;
534
6afa6767 535 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
536 r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
537 if (r < 0)
538 return r;
539 if (r > 0)
540 goto done;
541
542 break;
543 }
544
545 /* Generic fallback */
546 k = hexmem(n->id.port_id, n->id.port_id_size);
547 if (!k)
548 return -ENOMEM;
549
550done:
551 *ret = n->port_id_as_string = k;
552 return 0;
553}
554
8a19206d 555_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
34437b4f 556 assert_return(n, -EINVAL);
8a19206d 557 assert_return(ret_sec, -EINVAL);
34437b4f 558
8a19206d 559 *ret_sec = n->ttl;
34437b4f
LP
560 return 0;
561}
562
563_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
564 assert_return(n, -EINVAL);
565 assert_return(ret, -EINVAL);
566
567 if (!n->system_name)
568 return -ENODATA;
569
570 *ret = n->system_name;
571 return 0;
572}
573
574_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
575 assert_return(n, -EINVAL);
576 assert_return(ret, -EINVAL);
577
578 if (!n->system_description)
579 return -ENODATA;
580
581 *ret = n->system_description;
582 return 0;
583}
584
585_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
586 assert_return(n, -EINVAL);
587 assert_return(ret, -EINVAL);
588
589 if (!n->port_description)
590 return -ENODATA;
591
592 *ret = n->port_description;
593 return 0;
594}
595
596_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
597 assert_return(n, -EINVAL);
598 assert_return(ret, -EINVAL);
599
600 if (!n->has_capabilities)
601 return -ENODATA;
602
603 *ret = n->system_capabilities;
604 return 0;
605}
606
607_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
608 assert_return(n, -EINVAL);
609 assert_return(ret, -EINVAL);
610
611 if (!n->has_capabilities)
612 return -ENODATA;
613
614 *ret = n->enabled_capabilities;
615 return 0;
616}
617
09155f68 618_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
34437b4f
LP
619 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
620 int r;
621
622 assert_return(ret, -EINVAL);
623 assert_return(raw || raw_size <= 0, -EINVAL);
624
625 n = lldp_neighbor_new(raw_size);
626 if (!n)
627 return -ENOMEM;
628
629 memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
630 r = lldp_neighbor_parse(n);
631 if (r < 0)
632 return r;
633
1cc6c93a 634 *ret = TAKE_PTR(n);
34437b4f
LP
635
636 return r;
637}
638
639_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
640 assert_return(n, -EINVAL);
641
642 assert(n->raw_size >= sizeof(struct ether_header));
643 n->rindex = sizeof(struct ether_header);
644
a85b46c3 645 return n->rindex < n->raw_size;
34437b4f
LP
646}
647
648_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
649 size_t length;
650
651 assert_return(n, -EINVAL);
652
653 if (n->rindex == n->raw_size) /* EOF */
654 return -ESPIPE;
655
656 if (n->rindex + 2 > n->raw_size) /* Truncated message */
657 return -EBADMSG;
658
f137029b 659 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
660 if (n->rindex + 2 + length > n->raw_size)
661 return -EBADMSG;
662
663 n->rindex += 2 + length;
664 return n->rindex < n->raw_size;
665}
666
667_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
668 assert_return(n, -EINVAL);
669 assert_return(type, -EINVAL);
670
671 if (n->rindex == n->raw_size) /* EOF */
672 return -ESPIPE;
673
674 if (n->rindex + 2 > n->raw_size)
675 return -EBADMSG;
676
f137029b 677 *type = LLDP_NEIGHBOR_TLV_TYPE(n);
34437b4f
LP
678 return 0;
679}
680
681_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
682 uint8_t k;
683 int r;
684
685 assert_return(n, -EINVAL);
686
687 r = sd_lldp_neighbor_tlv_get_type(n, &k);
688 if (r < 0)
689 return r;
690
691 return type == k;
692}
693
4199f689 694_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
34437b4f
LP
695 const uint8_t *d;
696 size_t length;
697 int r;
698
699 assert_return(n, -EINVAL);
700 assert_return(oui, -EINVAL);
701 assert_return(subtype, -EINVAL);
702
6afa6767 703 r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
34437b4f
LP
704 if (r < 0)
705 return r;
706 if (r == 0)
707 return -ENXIO;
708
f137029b 709 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
710 if (length < 4)
711 return -EBADMSG;
712
713 if (n->rindex + 2 + length > n->raw_size)
714 return -EBADMSG;
715
f137029b 716 d = LLDP_NEIGHBOR_TLV_DATA(n);
34437b4f
LP
717 memcpy(oui, d, 3);
718 *subtype = d[3];
719
720 return 0;
721}
722
4199f689 723_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
34437b4f
LP
724 uint8_t k[3], st;
725 int r;
726
727 r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
728 if (r == -ENXIO)
729 return 0;
730 if (r < 0)
731 return r;
732
733 return memcmp(k, oui, 3) == 0 && st == subtype;
734}
735
736_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
737 size_t length;
738
739 assert_return(n, -EINVAL);
740 assert_return(ret, -EINVAL);
741 assert_return(size, -EINVAL);
742
743 /* Note that this returns the full TLV, including the TLV header */
744
745 if (n->rindex + 2 > n->raw_size)
746 return -EBADMSG;
747
f137029b 748 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
749 if (n->rindex + 2 + length > n->raw_size)
750 return -EBADMSG;
751
752 *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
753 *size = length + 2;
754
755 return 0;
756}
16fed825 757
09155f68 758_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
16fed825
LP
759 assert_return(n, -EINVAL);
760 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
761 assert_return(clock_supported(clock), -EOPNOTSUPP);
762 assert_return(ret, -EINVAL);
763
764 if (!triple_timestamp_is_set(&n->timestamp))
765 return -ENODATA;
766
767 *ret = triple_timestamp_by_clock(&n->timestamp, clock);
768 return 0;
769}