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