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