]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
34437b4f
LP
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"
0a970718 10#include "memory-util.h"
f5947a5e 11#include "missing_network.h"
34437b4f
LP
12#include "unaligned.h"
13
7a08d314 14static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
34437b4f
LP
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
dc5f9c6f 21int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
dc6bf94d
FB
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);
34437b4f
LP
24}
25
8276855e
YW
26DEFINE_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);
34437b4f
LP
28
29int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
30 const sd_lldp_neighbor *x = a, *y = b;
31
a0edd02e 32 return CMP(x->until, y->until);
34437b4f
LP
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
45static 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);
f69b4ae8 53 free(n->mud_url);
34437b4f
LP
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
76sd_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
8ae1a821
LP
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
34437b4f
LP
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
102sd_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
35388783 115static int parse_string(sd_lldp *lldp, char **s, const void *q, size_t n) {
34437b4f
LP
116 const char *p = q;
117 char *k;
118
119 assert(s);
120 assert(p || n == 0);
121
122 if (*s) {
35388783 123 log_lldp(lldp, "Found duplicate string, ignoring field.");
34437b4f
LP
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)) {
35388783 136 log_lldp(lldp, "Found inner NUL in string, ignoring field.");
34437b4f
LP
137 return 0;
138 }
139
140 /* Let's escape weird chars, for security reasons */
141 k = cescape_length(p, n);
142 if (!k)
35388783 143 return log_oom_debug();
34437b4f
LP
144
145 free(*s);
146 *s = k;
147
148 return 1;
149}
150
151int 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
35388783
YW
159 if (n->raw_size < sizeof(struct ether_header))
160 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
161 "Received truncated packet, ignoring.");
34437b4f
LP
162
163 memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
164
35388783
YW
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.");
34437b4f
LP
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 ||
35388783
YW
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.");
34437b4f
LP
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
35388783
YW
188 if (left < 2)
189 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
190 "TLV lacks header, ignoring.");
34437b4f
LP
191
192 type = p[0] >> 1;
193 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
194 p += 2, left -= 2;
195
35388783
YW
196 if (left < length)
197 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
198 "TLV truncated, ignoring datagram.");
34437b4f
LP
199
200 switch (type) {
201
6afa6767 202 case SD_LLDP_TYPE_END:
35388783
YW
203 if (length != 0)
204 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
205 "End marker TLV not zero-sized, ignoring datagram.");
34437b4f 206
8c7c7100 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
34437b4f
LP
210 goto end_marker;
211
6afa6767 212 case SD_LLDP_TYPE_CHASSIS_ID:
35388783
YW
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.");
34437b4f
LP
221
222 n->id.chassis_id = memdup(p, length);
223 if (!n->id.chassis_id)
35388783 224 return log_oom_debug();
34437b4f
LP
225
226 n->id.chassis_id_size = length;
227 break;
228
6afa6767 229 case SD_LLDP_TYPE_PORT_ID:
35388783
YW
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.");
34437b4f
LP
238
239 n->id.port_id = memdup(p, length);
240 if (!n->id.port_id)
35388783 241 return log_oom_debug();
34437b4f
LP
242
243 n->id.port_id_size = length;
244 break;
245
6afa6767 246 case SD_LLDP_TYPE_TTL:
35388783
YW
247 if (length != 2)
248 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
249 "TTL field has wrong size, ignoring datagram.");
34437b4f 250
35388783
YW
251 if (n->has_ttl)
252 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
253 "Duplicate TTL field, ignoring datagram.");
34437b4f
LP
254
255 n->ttl = unaligned_read_be16(p);
256 n->has_ttl = true;
257 break;
258
6afa6767 259 case SD_LLDP_TYPE_PORT_DESCRIPTION:
35388783 260 r = parse_string(n->lldp, &n->port_description, p, length);
34437b4f
LP
261 if (r < 0)
262 return r;
263 break;
264
6afa6767 265 case SD_LLDP_TYPE_SYSTEM_NAME:
35388783 266 r = parse_string(n->lldp, &n->system_name, p, length);
34437b4f
LP
267 if (r < 0)
268 return r;
269 break;
270
6afa6767 271 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
35388783 272 r = parse_string(n->lldp, &n->system_description, p, length);
34437b4f
LP
273 if (r < 0)
274 return r;
275 break;
276
6afa6767 277 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
34437b4f 278 if (length != 4)
35388783
YW
279 return log_lldp_errno(n->lldp, SYNTHETIC_ERRNO(EBADMSG),
280 "System capabilities field has wrong size.");
34437b4f 281
35388783
YW
282 n->system_capabilities = unaligned_read_be16(p);
283 n->enabled_capabilities = unaligned_read_be16(p + 2);
284 n->has_capabilities = true;
34437b4f
LP
285 break;
286
35388783 287 case SD_LLDP_TYPE_PRIVATE:
34437b4f 288 if (length < 4)
35388783
YW
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;
f69b4ae8 299 }
34437b4f
LP
300 break;
301 }
302
34437b4f
LP
303 p += length, left -= length;
304 }
305
306end_marker:
35388783
YW
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.");
34437b4f
LP
310
311 n->rindex = sizeof(struct ether_header);
312
313 return 0;
314}
315
316void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
317 assert(n);
318
16fed825
LP
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
34437b4f
LP
329 n->until = 0;
330
331 if (n->lldp)
332 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
333}
334
335bool 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
390static 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
409static int format_network_address(const void *data, size_t sz, char **ret) {
410 union in_addr_union a;
b3ad5fa9 411 int family, r;
34437b4f
LP
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
b3ad5fa9
ZJS
422 r = in_addr_to_string(family, &a, ret);
423 if (r < 0)
424 return r;
425 return 1;
34437b4f
LP
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
6afa6767
BG
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:
34437b4f
LP
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
6afa6767 455 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
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
6afa6767 464 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
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
479done:
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
6afa6767
BG
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:
34437b4f
LP
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
6afa6767 525 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
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
6afa6767 534 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
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
549done:
550 *ret = n->port_id_as_string = k;
551 return 0;
552}
553
8a19206d 554_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
34437b4f 555 assert_return(n, -EINVAL);
8a19206d 556 assert_return(ret_sec, -EINVAL);
34437b4f 557
8a19206d 558 *ret_sec = n->ttl;
34437b4f
LP
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
f69b4ae8
SS
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
34437b4f
LP
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
09155f68 628_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
34437b4f
LP
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
1cc6c93a 644 *ret = TAKE_PTR(n);
34437b4f
LP
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
a85b46c3 655 return n->rindex < n->raw_size;
34437b4f
LP
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
f137029b 669 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
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
f137029b 687 *type = LLDP_NEIGHBOR_TLV_TYPE(n);
34437b4f
LP
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
4199f689 704_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
34437b4f
LP
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
6afa6767 713 r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
34437b4f
LP
714 if (r < 0)
715 return r;
716 if (r == 0)
717 return -ENXIO;
718
f137029b 719 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
720 if (length < 4)
721 return -EBADMSG;
722
723 if (n->rindex + 2 + length > n->raw_size)
724 return -EBADMSG;
725
f137029b 726 d = LLDP_NEIGHBOR_TLV_DATA(n);
34437b4f
LP
727 memcpy(oui, d, 3);
728 *subtype = d[3];
729
730 return 0;
731}
732
4199f689 733_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
34437b4f
LP
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
f137029b 758 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
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}
16fed825 767
09155f68 768_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
16fed825
LP
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}