]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/lldp-neighbor.c
lldp: add proper ref counting to sd_lldp object and a separate call for setting the...
[thirdparty/systemd.git] / src / libsystemd-network / lldp-neighbor.c
CommitLineData
34437b4f
LP
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"
34437b4f
LP
27#include "unaligned.h"
28
29static 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
38static 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
64const struct hash_ops lldp_neighbor_id_hash_ops = {
65 .hash = lldp_neighbor_id_hash_func,
66 .compare = lldp_neighbor_id_compare_func
67};
68
69int 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
91static 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
121sd_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
142sd_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
155static 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
191int 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("Recieved 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
6afa6767 247 case SD_LLDP_TYPE_END:
34437b4f
LP
248 if (length != 0) {
249 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
250 return -EBADMSG;
251 }
252 if (left != 0) {
253 log_lldp("Trailing garbage in datagram, ignoring datagram.");
254 return -EBADMSG;
255 }
256
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
598_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
599 assert_return(n, -EINVAL);
600 assert_return(ret, -EINVAL);
601
602 *ret = n->ttl;
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
661int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
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;
678 n = 0;
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
689 return 0;
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
703 length = LLDP_NEIGHBOR_LENGTH(n);
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
721 *type = LLDP_NEIGHBOR_TYPE(n);
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
753 length = LLDP_NEIGHBOR_LENGTH(n);
754 if (length < 4)
755 return -EBADMSG;
756
757 if (n->rindex + 2 + length > n->raw_size)
758 return -EBADMSG;
759
760 d = LLDP_NEIGHBOR_DATA(n);
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
792 length = LLDP_NEIGHBOR_LENGTH(n);
793
794 if (n->rindex + 2 + length > n->raw_size)
795 return -EBADMSG;
796
797 *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
798 *size = length + 2;
799
800 return 0;
801}
16fed825
LP
802
803int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
804 assert_return(n, -EINVAL);
805 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
806 assert_return(clock_supported(clock), -EOPNOTSUPP);
807 assert_return(ret, -EINVAL);
808
809 if (!triple_timestamp_is_set(&n->timestamp))
810 return -ENODATA;
811
812 *ret = triple_timestamp_by_clock(&n->timestamp, clock);
813 return 0;
814}