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