]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/lldp-neighbor.c
Merge pull request #2685 from poettering/lldp-fixes2
[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 "lldp.h"
28 #include "unaligned.h"
29
30 static 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
39 static 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
65 const struct hash_ops lldp_neighbor_id_hash_ops = {
66 .hash = lldp_neighbor_id_hash_func,
67 .compare = lldp_neighbor_id_compare_func
68 };
69
70 int 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
92 static 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
122 sd_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
143 sd_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
156 static 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
192 int 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)) {
201 log_lldp("Recieved truncated packet, ignoring.");
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
248 case LLDP_TYPE_END:
249 if (length != 0) {
250 log_lldp("End marker TLV not zero-sized, ignoring datagram.");
251 return -EBADMSG;
252 }
253 if (left != 0) {
254 log_lldp("Trailing garbage in datagram, ignoring datagram.");
255 return -EBADMSG;
256 }
257
258 goto end_marker;
259
260 case LLDP_TYPE_CHASSIS_ID:
261 if (length < 2 || length > 256) { /* includes the chassis subtype, hence one extra byte */
262 log_lldp("Chassis ID field size out of range, ignoring datagram.");
263 return -EBADMSG;
264 }
265 if (n->id.chassis_id) {
266 log_lldp("Duplicate chassis ID field, ignoring datagram.");
267 return -EBADMSG;
268 }
269
270 n->id.chassis_id = memdup(p, length);
271 if (!n->id.chassis_id)
272 return -ENOMEM;
273
274 n->id.chassis_id_size = length;
275 break;
276
277 case LLDP_TYPE_PORT_ID:
278 if (length < 2 || length > 256) { /* includes the port subtype, hence one extra byte */
279 log_lldp("Port ID field size out of range, ignoring datagram.");
280 return -EBADMSG;
281 }
282 if (n->id.port_id) {
283 log_lldp("Duplicate port ID field, ignoring datagram.");
284 return -EBADMSG;
285 }
286
287 n->id.port_id = memdup(p, length);
288 if (!n->id.port_id)
289 return -ENOMEM;
290
291 n->id.port_id_size = length;
292 break;
293
294 case LLDP_TYPE_TTL:
295 if (length != 2) {
296 log_lldp("TTL field has wrong size, ignoring datagram.");
297 return -EBADMSG;
298 }
299
300 if (n->has_ttl) {
301 log_lldp("Duplicate TTL field, ignoring datagram.");
302 return -EBADMSG;
303 }
304
305 n->ttl = unaligned_read_be16(p);
306 n->has_ttl = true;
307 break;
308
309 case LLDP_TYPE_PORT_DESCRIPTION:
310 r = parse_string(&n->port_description, p, length);
311 if (r < 0)
312 return r;
313 break;
314
315 case LLDP_TYPE_SYSTEM_NAME:
316 r = parse_string(&n->system_name, p, length);
317 if (r < 0)
318 return r;
319 break;
320
321 case LLDP_TYPE_SYSTEM_DESCRIPTION:
322 r = parse_string(&n->system_description, p, length);
323 if (r < 0)
324 return r;
325 break;
326
327 case LLDP_TYPE_SYSTEM_CAPABILITIES:
328 if (length != 4)
329 log_lldp("System capabilities field has wrong size, ignoring.");
330 else {
331 n->system_capabilities = unaligned_read_be16(p);
332 n->enabled_capabilities = unaligned_read_be16(p + 2);
333 n->has_capabilities = true;
334 }
335
336 break;
337
338 case LLDP_TYPE_PRIVATE:
339 if (length < 4)
340 log_lldp("Found private TLV that is too short, ignoring.");
341
342 break;
343 }
344
345
346 p += length, left -= length;
347 }
348
349 end_marker:
350 if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
351 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
352 return -EBADMSG;
353
354 }
355
356 n->rindex = sizeof(struct ether_header);
357
358 return 0;
359 }
360
361 void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
362 assert(n);
363
364 if (n->ttl > 0)
365 n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
366 else
367 n->until = 0;
368
369 if (n->lldp)
370 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
371 }
372
373 bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
374 if (a == b)
375 return true;
376
377 if (!a || !b)
378 return false;
379
380 if (a->raw_size != b->raw_size)
381 return false;
382
383 return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
384 }
385
386 _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
387 assert_return(n, -EINVAL);
388 assert_return(address, -EINVAL);
389
390 *address = n->source_address;
391 return 0;
392 }
393
394 _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
395 assert_return(n, -EINVAL);
396 assert_return(address, -EINVAL);
397
398 *address = n->destination_address;
399 return 0;
400 }
401
402 _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
403 assert_return(n, -EINVAL);
404 assert_return(ret, -EINVAL);
405 assert_return(size, -EINVAL);
406
407 *ret = LLDP_NEIGHBOR_RAW(n);
408 *size = n->raw_size;
409
410 return 0;
411 }
412
413 _public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
414 assert_return(n, -EINVAL);
415 assert_return(type, -EINVAL);
416 assert_return(ret, -EINVAL);
417 assert_return(size, -EINVAL);
418
419 assert(n->id.chassis_id_size > 0);
420
421 *type = *(uint8_t*) n->id.chassis_id;
422 *ret = (uint8_t*) n->id.chassis_id + 1;
423 *size = n->id.chassis_id_size - 1;
424
425 return 0;
426 }
427
428 static int format_mac_address(const void *data, size_t sz, char **ret) {
429 struct ether_addr a;
430 char *k;
431
432 assert(data || sz <= 0);
433
434 if (sz != 7)
435 return 0;
436
437 memcpy(&a, (uint8_t*) data + 1, sizeof(a));
438
439 k = new(char, ETHER_ADDR_TO_STRING_MAX);
440 if (!k)
441 return -ENOMEM;
442
443 *ret = ether_addr_to_string(&a, k);
444 return 1;
445 }
446
447 static int format_network_address(const void *data, size_t sz, char **ret) {
448 union in_addr_union a;
449 int family;
450
451 if (sz == 6 && ((uint8_t*) data)[1] == 1) {
452 memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
453 family = AF_INET;
454 } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
455 memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
456 family = AF_INET6;
457 } else
458 return 0;
459
460 return in_addr_to_string(family, &a, ret);
461 }
462
463 _public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
464 char *k;
465 int r;
466
467 assert_return(n, -EINVAL);
468 assert_return(ret, -EINVAL);
469
470 if (n->chassis_id_as_string) {
471 *ret = n->chassis_id_as_string;
472 return 0;
473 }
474
475 assert(n->id.chassis_id_size > 0);
476
477 switch (*(uint8_t*) n->id.chassis_id) {
478
479 case LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
480 case LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
481 case LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
482 case LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
483 case LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
484 k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
485 if (!k)
486 return -ENOMEM;
487
488 goto done;
489
490 case LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
491 r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
492 if (r < 0)
493 return r;
494 if (r > 0)
495 goto done;
496
497 break;
498
499 case LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
500 r = format_network_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 }
508
509 /* Generic fallback */
510 k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
511 if (!k)
512 return -ENOMEM;
513
514 done:
515 *ret = n->chassis_id_as_string = k;
516 return 0;
517 }
518
519 _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
520 assert_return(n, -EINVAL);
521 assert_return(type, -EINVAL);
522 assert_return(ret, -EINVAL);
523 assert_return(size, -EINVAL);
524
525 assert(n->id.port_id_size > 0);
526
527 *type = *(uint8_t*) n->id.port_id;
528 *ret = (uint8_t*) n->id.port_id + 1;
529 *size = n->id.port_id_size - 1;
530
531 return 0;
532 }
533
534 _public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
535 char *k;
536 int r;
537
538 assert_return(n, -EINVAL);
539 assert_return(ret, -EINVAL);
540
541 if (n->port_id_as_string) {
542 *ret = n->port_id_as_string;
543 return 0;
544 }
545
546 assert(n->id.port_id_size > 0);
547
548 switch (*(uint8_t*) n->id.port_id) {
549
550 case LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
551 case LLDP_PORT_SUBTYPE_PORT_COMPONENT:
552 case LLDP_PORT_SUBTYPE_INTERFACE_NAME:
553 case LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
554 k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
555 if (!k)
556 return -ENOMEM;
557
558 goto done;
559
560 case LLDP_PORT_SUBTYPE_MAC_ADDRESS:
561 r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
562 if (r < 0)
563 return r;
564 if (r > 0)
565 goto done;
566
567 break;
568
569 case LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
570 r = format_network_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 }
578
579 /* Generic fallback */
580 k = hexmem(n->id.port_id, n->id.port_id_size);
581 if (!k)
582 return -ENOMEM;
583
584 done:
585 *ret = n->port_id_as_string = k;
586 return 0;
587 }
588
589 _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
590 assert_return(n, -EINVAL);
591 assert_return(ret, -EINVAL);
592
593 *ret = n->ttl;
594 return 0;
595 }
596
597 _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
598 assert_return(n, -EINVAL);
599 assert_return(ret, -EINVAL);
600
601 if (!n->system_name)
602 return -ENODATA;
603
604 *ret = n->system_name;
605 return 0;
606 }
607
608 _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
609 assert_return(n, -EINVAL);
610 assert_return(ret, -EINVAL);
611
612 if (!n->system_description)
613 return -ENODATA;
614
615 *ret = n->system_description;
616 return 0;
617 }
618
619 _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
620 assert_return(n, -EINVAL);
621 assert_return(ret, -EINVAL);
622
623 if (!n->port_description)
624 return -ENODATA;
625
626 *ret = n->port_description;
627 return 0;
628 }
629
630 _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
631 assert_return(n, -EINVAL);
632 assert_return(ret, -EINVAL);
633
634 if (!n->has_capabilities)
635 return -ENODATA;
636
637 *ret = n->system_capabilities;
638 return 0;
639 }
640
641 _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
642 assert_return(n, -EINVAL);
643 assert_return(ret, -EINVAL);
644
645 if (!n->has_capabilities)
646 return -ENODATA;
647
648 *ret = n->enabled_capabilities;
649 return 0;
650 }
651
652 int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
653 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
654 int r;
655
656 assert_return(ret, -EINVAL);
657 assert_return(raw || raw_size <= 0, -EINVAL);
658
659 n = lldp_neighbor_new(raw_size);
660 if (!n)
661 return -ENOMEM;
662
663 memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
664 r = lldp_neighbor_parse(n);
665 if (r < 0)
666 return r;
667
668 *ret = n;
669 n = 0;
670
671 return r;
672 }
673
674 _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
675 assert_return(n, -EINVAL);
676
677 assert(n->raw_size >= sizeof(struct ether_header));
678 n->rindex = sizeof(struct ether_header);
679
680 return 0;
681 }
682
683 _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
684 size_t length;
685
686 assert_return(n, -EINVAL);
687
688 if (n->rindex == n->raw_size) /* EOF */
689 return -ESPIPE;
690
691 if (n->rindex + 2 > n->raw_size) /* Truncated message */
692 return -EBADMSG;
693
694 length = LLDP_NEIGHBOR_LENGTH(n);
695 if (n->rindex + 2 + length > n->raw_size)
696 return -EBADMSG;
697
698 n->rindex += 2 + length;
699 return n->rindex < n->raw_size;
700 }
701
702 _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
703 assert_return(n, -EINVAL);
704 assert_return(type, -EINVAL);
705
706 if (n->rindex == n->raw_size) /* EOF */
707 return -ESPIPE;
708
709 if (n->rindex + 2 > n->raw_size)
710 return -EBADMSG;
711
712 *type = LLDP_NEIGHBOR_TYPE(n);
713 return 0;
714 }
715
716 _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
717 uint8_t k;
718 int r;
719
720 assert_return(n, -EINVAL);
721
722 r = sd_lldp_neighbor_tlv_get_type(n, &k);
723 if (r < 0)
724 return r;
725
726 return type == k;
727 }
728
729 _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
730 const uint8_t *d;
731 size_t length;
732 int r;
733
734 assert_return(n, -EINVAL);
735 assert_return(oui, -EINVAL);
736 assert_return(subtype, -EINVAL);
737
738 r = sd_lldp_neighbor_tlv_is_type(n, LLDP_TYPE_PRIVATE);
739 if (r < 0)
740 return r;
741 if (r == 0)
742 return -ENXIO;
743
744 length = LLDP_NEIGHBOR_LENGTH(n);
745 if (length < 4)
746 return -EBADMSG;
747
748 if (n->rindex + 2 + length > n->raw_size)
749 return -EBADMSG;
750
751 d = LLDP_NEIGHBOR_DATA(n);
752 memcpy(oui, d, 3);
753 *subtype = d[3];
754
755 return 0;
756 }
757
758 _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
759 uint8_t k[3], st;
760 int r;
761
762 r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
763 if (r == -ENXIO)
764 return 0;
765 if (r < 0)
766 return r;
767
768 return memcmp(k, oui, 3) == 0 && st == subtype;
769 }
770
771 _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
772 size_t length;
773
774 assert_return(n, -EINVAL);
775 assert_return(ret, -EINVAL);
776 assert_return(size, -EINVAL);
777
778 /* Note that this returns the full TLV, including the TLV header */
779
780 if (n->rindex + 2 > n->raw_size)
781 return -EBADMSG;
782
783 length = LLDP_NEIGHBOR_LENGTH(n);
784
785 if (n->rindex + 2 + length > n->raw_size)
786 return -EBADMSG;
787
788 *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
789 *size = length + 2;
790
791 return 0;
792 }