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