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