]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-network/lldp-neighbor.c
Merge pull request #2959 from keszybz/stop-resolving-localdomain
[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 "unaligned.h"
28
29 static 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
38 static 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
64 const struct hash_ops lldp_neighbor_id_hash_ops = {
65 .hash = lldp_neighbor_id_hash_func,
66 .compare = lldp_neighbor_id_compare_func
67 };
68
69 int 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
91 static 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
121 sd_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
142 sd_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
155 static 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
191 int 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
247 case SD_LLDP_TYPE_END:
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
259 case SD_LLDP_TYPE_CHASSIS_ID:
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
276 case SD_LLDP_TYPE_PORT_ID:
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
293 case SD_LLDP_TYPE_TTL:
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
308 case SD_LLDP_TYPE_PORT_DESCRIPTION:
309 r = parse_string(&n->port_description, p, length);
310 if (r < 0)
311 return r;
312 break;
313
314 case SD_LLDP_TYPE_SYSTEM_NAME:
315 r = parse_string(&n->system_name, p, length);
316 if (r < 0)
317 return r;
318 break;
319
320 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
321 r = parse_string(&n->system_description, p, length);
322 if (r < 0)
323 return r;
324 break;
325
326 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
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
337 case SD_LLDP_TYPE_PRIVATE:
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
348 end_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
360 void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
361 assert(n);
362
363 if (n->ttl > 0)
364 n->until = usec_add(now(clock_boottime_or_monotonic()), n->ttl * USEC_PER_SEC);
365 else
366 n->until = 0;
367
368 if (n->lldp)
369 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
370 }
371
372 bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
373 if (a == b)
374 return true;
375
376 if (!a || !b)
377 return false;
378
379 if (a->raw_size != b->raw_size)
380 return false;
381
382 return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
383 }
384
385 _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
386 assert_return(n, -EINVAL);
387 assert_return(address, -EINVAL);
388
389 *address = n->source_address;
390 return 0;
391 }
392
393 _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
394 assert_return(n, -EINVAL);
395 assert_return(address, -EINVAL);
396
397 *address = n->destination_address;
398 return 0;
399 }
400
401 _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
402 assert_return(n, -EINVAL);
403 assert_return(ret, -EINVAL);
404 assert_return(size, -EINVAL);
405
406 *ret = LLDP_NEIGHBOR_RAW(n);
407 *size = n->raw_size;
408
409 return 0;
410 }
411
412 _public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
413 assert_return(n, -EINVAL);
414 assert_return(type, -EINVAL);
415 assert_return(ret, -EINVAL);
416 assert_return(size, -EINVAL);
417
418 assert(n->id.chassis_id_size > 0);
419
420 *type = *(uint8_t*) n->id.chassis_id;
421 *ret = (uint8_t*) n->id.chassis_id + 1;
422 *size = n->id.chassis_id_size - 1;
423
424 return 0;
425 }
426
427 static int format_mac_address(const void *data, size_t sz, char **ret) {
428 struct ether_addr a;
429 char *k;
430
431 assert(data || sz <= 0);
432
433 if (sz != 7)
434 return 0;
435
436 memcpy(&a, (uint8_t*) data + 1, sizeof(a));
437
438 k = new(char, ETHER_ADDR_TO_STRING_MAX);
439 if (!k)
440 return -ENOMEM;
441
442 *ret = ether_addr_to_string(&a, k);
443 return 1;
444 }
445
446 static int format_network_address(const void *data, size_t sz, char **ret) {
447 union in_addr_union a;
448 int family, r;
449
450 if (sz == 6 && ((uint8_t*) data)[1] == 1) {
451 memcpy(&a.in, (uint8_t*) data + 2, sizeof(a.in));
452 family = AF_INET;
453 } else if (sz == 18 && ((uint8_t*) data)[1] == 2) {
454 memcpy(&a.in6, (uint8_t*) data + 2, sizeof(a.in6));
455 family = AF_INET6;
456 } else
457 return 0;
458
459 r = in_addr_to_string(family, &a, ret);
460 if (r < 0)
461 return r;
462 return 1;
463 }
464
465 _public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
466 char *k;
467 int r;
468
469 assert_return(n, -EINVAL);
470 assert_return(ret, -EINVAL);
471
472 if (n->chassis_id_as_string) {
473 *ret = n->chassis_id_as_string;
474 return 0;
475 }
476
477 assert(n->id.chassis_id_size > 0);
478
479 switch (*(uint8_t*) n->id.chassis_id) {
480
481 case SD_LLDP_CHASSIS_SUBTYPE_CHASSIS_COMPONENT:
482 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_ALIAS:
483 case SD_LLDP_CHASSIS_SUBTYPE_PORT_COMPONENT:
484 case SD_LLDP_CHASSIS_SUBTYPE_INTERFACE_NAME:
485 case SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED:
486 k = cescape_length((char*) n->id.chassis_id + 1, n->id.chassis_id_size - 1);
487 if (!k)
488 return -ENOMEM;
489
490 goto done;
491
492 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
493 r = format_mac_address(n->id.chassis_id, n->id.chassis_id_size, &k);
494 if (r < 0)
495 return r;
496 if (r > 0)
497 goto done;
498
499 break;
500
501 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
502 r = format_network_address(n->id.chassis_id, n->id.chassis_id_size, &k);
503 if (r < 0)
504 return r;
505 if (r > 0)
506 goto done;
507
508 break;
509 }
510
511 /* Generic fallback */
512 k = hexmem(n->id.chassis_id, n->id.chassis_id_size);
513 if (!k)
514 return -ENOMEM;
515
516 done:
517 *ret = n->chassis_id_as_string = k;
518 return 0;
519 }
520
521 _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
522 assert_return(n, -EINVAL);
523 assert_return(type, -EINVAL);
524 assert_return(ret, -EINVAL);
525 assert_return(size, -EINVAL);
526
527 assert(n->id.port_id_size > 0);
528
529 *type = *(uint8_t*) n->id.port_id;
530 *ret = (uint8_t*) n->id.port_id + 1;
531 *size = n->id.port_id_size - 1;
532
533 return 0;
534 }
535
536 _public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
537 char *k;
538 int r;
539
540 assert_return(n, -EINVAL);
541 assert_return(ret, -EINVAL);
542
543 if (n->port_id_as_string) {
544 *ret = n->port_id_as_string;
545 return 0;
546 }
547
548 assert(n->id.port_id_size > 0);
549
550 switch (*(uint8_t*) n->id.port_id) {
551
552 case SD_LLDP_PORT_SUBTYPE_INTERFACE_ALIAS:
553 case SD_LLDP_PORT_SUBTYPE_PORT_COMPONENT:
554 case SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME:
555 case SD_LLDP_PORT_SUBTYPE_LOCALLY_ASSIGNED:
556 k = cescape_length((char*) n->id.port_id + 1, n->id.port_id_size - 1);
557 if (!k)
558 return -ENOMEM;
559
560 goto done;
561
562 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
563 r = format_mac_address(n->id.port_id, n->id.port_id_size, &k);
564 if (r < 0)
565 return r;
566 if (r > 0)
567 goto done;
568
569 break;
570
571 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
572 r = format_network_address(n->id.port_id, n->id.port_id_size, &k);
573 if (r < 0)
574 return r;
575 if (r > 0)
576 goto done;
577
578 break;
579 }
580
581 /* Generic fallback */
582 k = hexmem(n->id.port_id, n->id.port_id_size);
583 if (!k)
584 return -ENOMEM;
585
586 done:
587 *ret = n->port_id_as_string = k;
588 return 0;
589 }
590
591 _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret) {
592 assert_return(n, -EINVAL);
593 assert_return(ret, -EINVAL);
594
595 *ret = n->ttl;
596 return 0;
597 }
598
599 _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
600 assert_return(n, -EINVAL);
601 assert_return(ret, -EINVAL);
602
603 if (!n->system_name)
604 return -ENODATA;
605
606 *ret = n->system_name;
607 return 0;
608 }
609
610 _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
611 assert_return(n, -EINVAL);
612 assert_return(ret, -EINVAL);
613
614 if (!n->system_description)
615 return -ENODATA;
616
617 *ret = n->system_description;
618 return 0;
619 }
620
621 _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
622 assert_return(n, -EINVAL);
623 assert_return(ret, -EINVAL);
624
625 if (!n->port_description)
626 return -ENODATA;
627
628 *ret = n->port_description;
629 return 0;
630 }
631
632 _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
633 assert_return(n, -EINVAL);
634 assert_return(ret, -EINVAL);
635
636 if (!n->has_capabilities)
637 return -ENODATA;
638
639 *ret = n->system_capabilities;
640 return 0;
641 }
642
643 _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
644 assert_return(n, -EINVAL);
645 assert_return(ret, -EINVAL);
646
647 if (!n->has_capabilities)
648 return -ENODATA;
649
650 *ret = n->enabled_capabilities;
651 return 0;
652 }
653
654 int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
655 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
656 int r;
657
658 assert_return(ret, -EINVAL);
659 assert_return(raw || raw_size <= 0, -EINVAL);
660
661 n = lldp_neighbor_new(raw_size);
662 if (!n)
663 return -ENOMEM;
664
665 memcpy(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
666 r = lldp_neighbor_parse(n);
667 if (r < 0)
668 return r;
669
670 *ret = n;
671 n = 0;
672
673 return r;
674 }
675
676 _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
677 assert_return(n, -EINVAL);
678
679 assert(n->raw_size >= sizeof(struct ether_header));
680 n->rindex = sizeof(struct ether_header);
681
682 return 0;
683 }
684
685 _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
686 size_t length;
687
688 assert_return(n, -EINVAL);
689
690 if (n->rindex == n->raw_size) /* EOF */
691 return -ESPIPE;
692
693 if (n->rindex + 2 > n->raw_size) /* Truncated message */
694 return -EBADMSG;
695
696 length = LLDP_NEIGHBOR_LENGTH(n);
697 if (n->rindex + 2 + length > n->raw_size)
698 return -EBADMSG;
699
700 n->rindex += 2 + length;
701 return n->rindex < n->raw_size;
702 }
703
704 _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
705 assert_return(n, -EINVAL);
706 assert_return(type, -EINVAL);
707
708 if (n->rindex == n->raw_size) /* EOF */
709 return -ESPIPE;
710
711 if (n->rindex + 2 > n->raw_size)
712 return -EBADMSG;
713
714 *type = LLDP_NEIGHBOR_TYPE(n);
715 return 0;
716 }
717
718 _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
719 uint8_t k;
720 int r;
721
722 assert_return(n, -EINVAL);
723
724 r = sd_lldp_neighbor_tlv_get_type(n, &k);
725 if (r < 0)
726 return r;
727
728 return type == k;
729 }
730
731 _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[3], uint8_t *subtype) {
732 const uint8_t *d;
733 size_t length;
734 int r;
735
736 assert_return(n, -EINVAL);
737 assert_return(oui, -EINVAL);
738 assert_return(subtype, -EINVAL);
739
740 r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
741 if (r < 0)
742 return r;
743 if (r == 0)
744 return -ENXIO;
745
746 length = LLDP_NEIGHBOR_LENGTH(n);
747 if (length < 4)
748 return -EBADMSG;
749
750 if (n->rindex + 2 + length > n->raw_size)
751 return -EBADMSG;
752
753 d = LLDP_NEIGHBOR_DATA(n);
754 memcpy(oui, d, 3);
755 *subtype = d[3];
756
757 return 0;
758 }
759
760 _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[3], uint8_t subtype) {
761 uint8_t k[3], st;
762 int r;
763
764 r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
765 if (r == -ENXIO)
766 return 0;
767 if (r < 0)
768 return r;
769
770 return memcmp(k, oui, 3) == 0 && st == subtype;
771 }
772
773 _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
774 size_t length;
775
776 assert_return(n, -EINVAL);
777 assert_return(ret, -EINVAL);
778 assert_return(size, -EINVAL);
779
780 /* Note that this returns the full TLV, including the TLV header */
781
782 if (n->rindex + 2 > n->raw_size)
783 return -EBADMSG;
784
785 length = LLDP_NEIGHBOR_LENGTH(n);
786
787 if (n->rindex + 2 + length > n->raw_size)
788 return -EBADMSG;
789
790 *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
791 *size = length + 2;
792
793 return 0;
794 }