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