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