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