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