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