]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd-network/lldp-neighbor.c
Fixes for vscode/intellisense parsing (#38040)
[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"
5cdf13c7 6#include "hashmap.h"
34437b4f
LP
7#include "hexdecoct.h"
8#include "in-addr-util.h"
309a747f 9#include "json-util.h"
34437b4f 10#include "lldp-neighbor.h"
5cdf13c7 11#include "lldp-rx-internal.h"
0a970718 12#include "memory-util.h"
5cdf13c7
DDM
13#include "prioq.h"
14#include "siphash24.h"
34437b4f
LP
15#include "unaligned.h"
16
7a08d314 17static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
90496cc6
YW
18 assert(id);
19 assert(state);
20
c01a5c05
YW
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);
34437b4f
LP
25}
26
dc5f9c6f 27int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
90496cc6
YW
28 assert(x);
29 assert(y);
30
dc6bf94d
FB
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);
34437b4f
LP
33}
34
71c4f7e8
YW
35DEFINE_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);
34437b4f 42
f9550d11 43int lldp_neighbor_prioq_compare_func(const sd_lldp_neighbor *x, const sd_lldp_neighbor *y) {
90496cc6
YW
44 assert(x);
45 assert(y);
46
a0edd02e 47 return CMP(x->until, y->until);
34437b4f
LP
48}
49
17347053 50sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
34437b4f
LP
51 if (!n)
52 return NULL;
53
35778343 54 assert(n->n_ref > 0 || n->lldp_rx);
34437b4f
LP
55 n->n_ref++;
56
57 return n;
58}
59
35777f51
YW
60static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
61 if (!n)
62 return NULL;
34437b4f
LP
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);
f69b4ae8 69 free(n->mud_url);
34437b4f
LP
70 free(n->chassis_id_as_string);
71 free(n->port_id_as_string);
35777f51 72 return mfree(n);
34437b4f
LP
73}
74
17347053 75sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
34437b4f
LP
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
35778343 86 if (n->n_ref <= 0 && !n->lldp_rx)
34437b4f
LP
87 lldp_neighbor_free(n);
88
89 return NULL;
90}
91
92sd_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
35778343 99 if (!n->lldp_rx)
34437b4f
LP
100 return NULL;
101
8ae1a821
LP
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
35778343 106 (void) hashmap_remove_value(n->lldp_rx->neighbor_by_id, &n->id, n);
8ae1a821 107
35778343 108 assert_se(prioq_remove(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
34437b4f 109
35778343 110 n->lldp_rx = NULL;
34437b4f
LP
111
112 if (n->n_ref <= 0)
113 lldp_neighbor_free(n);
114
115 return NULL;
116}
117
118sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
119 sd_lldp_neighbor *n;
120
4e88a46b
YW
121 if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_lldp_neighbor)))
122 return NULL;
123
34437b4f
LP
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
35778343 134static int parse_string(sd_lldp_rx *lldp_rx, char **s, const void *q, size_t n) {
34437b4f
LP
135 const char *p = q;
136 char *k;
137
138 assert(s);
139 assert(p || n == 0);
140
141 if (*s) {
35778343 142 log_lldp_rx(lldp_rx, "Found duplicate string, ignoring field.");
34437b4f
LP
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)) {
35778343 155 log_lldp_rx(lldp_rx, "Found inner NUL in string, ignoring field.");
34437b4f
LP
156 return 0;
157 }
158
159 /* Let's escape weird chars, for security reasons */
160 k = cescape_length(p, n);
161 if (!k)
35388783 162 return log_oom_debug();
34437b4f 163
d6f2cd67 164 free_and_replace(*s, k);
34437b4f
LP
165
166 return 1;
167}
168
169int 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
35388783 177 if (n->raw_size < sizeof(struct ether_header))
35778343
YW
178 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
179 "Received truncated packet, ignoring.");
34437b4f
LP
180
181 memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
182
92b64168 183 if (h.ether_type != htobe16(ETH_P_LLDP))
35778343
YW
184 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
185 "Received packet with wrong type, ignoring.");
34437b4f
LP
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 ||
35388783 192 !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e))
35778343
YW
193 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
194 "Received packet with wrong destination address, ignoring.");
34437b4f
LP
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
35388783 206 if (left < 2)
35778343 207 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
35388783 208 "TLV lacks header, ignoring.");
34437b4f
LP
209
210 type = p[0] >> 1;
211 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
212 p += 2, left -= 2;
213
35388783 214 if (left < length)
35778343
YW
215 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
216 "TLV truncated, ignoring datagram.");
34437b4f
LP
217
218 switch (type) {
219
6afa6767 220 case SD_LLDP_TYPE_END:
35388783 221 if (length != 0)
35778343
YW
222 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
223 "End marker TLV not zero-sized, ignoring datagram.");
34437b4f 224
8c7c7100 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
34437b4f
LP
228 goto end_marker;
229
6afa6767 230 case SD_LLDP_TYPE_CHASSIS_ID:
35388783
YW
231 if (length < 2 || length > 256)
232 /* includes the chassis subtype, hence one extra byte */
35778343
YW
233 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
234 "Chassis ID field size out of range, ignoring datagram.");
35388783
YW
235
236 if (n->id.chassis_id)
35778343
YW
237 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
238 "Duplicate chassis ID field, ignoring datagram.");
34437b4f
LP
239
240 n->id.chassis_id = memdup(p, length);
241 if (!n->id.chassis_id)
35388783 242 return log_oom_debug();
34437b4f
LP
243
244 n->id.chassis_id_size = length;
245 break;
246
6afa6767 247 case SD_LLDP_TYPE_PORT_ID:
35388783
YW
248 if (length < 2 || length > 256)
249 /* includes the port subtype, hence one extra byte */
35778343
YW
250 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
251 "Port ID field size out of range, ignoring datagram.");
35388783
YW
252
253 if (n->id.port_id)
35778343
YW
254 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
255 "Duplicate port ID field, ignoring datagram.");
34437b4f
LP
256
257 n->id.port_id = memdup(p, length);
258 if (!n->id.port_id)
35388783 259 return log_oom_debug();
34437b4f
LP
260
261 n->id.port_id_size = length;
262 break;
263
6afa6767 264 case SD_LLDP_TYPE_TTL:
35388783 265 if (length != 2)
35778343
YW
266 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
267 "TTL field has wrong size, ignoring datagram.");
34437b4f 268
35388783 269 if (n->has_ttl)
35778343
YW
270 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
271 "Duplicate TTL field, ignoring datagram.");
34437b4f
LP
272
273 n->ttl = unaligned_read_be16(p);
274 n->has_ttl = true;
275 break;
276
6afa6767 277 case SD_LLDP_TYPE_PORT_DESCRIPTION:
35778343 278 r = parse_string(n->lldp_rx, &n->port_description, p, length);
34437b4f
LP
279 if (r < 0)
280 return r;
281 break;
282
6afa6767 283 case SD_LLDP_TYPE_SYSTEM_NAME:
35778343 284 r = parse_string(n->lldp_rx, &n->system_name, p, length);
34437b4f
LP
285 if (r < 0)
286 return r;
287 break;
288
6afa6767 289 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
35778343 290 r = parse_string(n->lldp_rx, &n->system_description, p, length);
34437b4f
LP
291 if (r < 0)
292 return r;
293 break;
294
6afa6767 295 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
34437b4f 296 if (length != 4)
35778343
YW
297 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
298 "System capabilities field has wrong size.");
34437b4f 299
35388783
YW
300 n->system_capabilities = unaligned_read_be16(p);
301 n->enabled_capabilities = unaligned_read_be16(p + 2);
302 n->has_capabilities = true;
34437b4f
LP
303 break;
304
35388783 305 case SD_LLDP_TYPE_PRIVATE:
34437b4f 306 if (length < 4)
35778343
YW
307 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
308 "Found private TLV that is too short, ignoring.");
35388783
YW
309
310 /* RFC 8520: MUD URL */
3be1e84f
YW
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));
35388783
YW
314 if (r < 0)
315 return r;
f69b4ae8 316 }
496b21ab
LA
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 }
34437b4f
LP
327 break;
328 }
329
34437b4f
LP
330 p += length, left -= length;
331 }
332
333end_marker:
35388783 334 if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl)
35778343
YW
335 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
336 "One or more mandatory TLV missing in datagram. Ignoring.");
34437b4f
LP
337
338 n->rindex = sizeof(struct ether_header);
339
340 return 0;
341}
342
343void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
344 assert(n);
345
16fed825
LP
346 if (n->ttl > 0) {
347 usec_t base;
348
349 /* Use the packet's timestamp if there is one known */
ba4e0427 350 base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME);
0da36375 351 if (!timestamp_is_set(base))
ba4e0427 352 base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */
16fed825
LP
353
354 n->until = usec_add(base, n->ttl * USEC_PER_SEC);
355 } else
34437b4f
LP
356 n->until = 0;
357
35778343
YW
358 if (n->lldp_rx)
359 prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
34437b4f
LP
360}
361
362bool 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
17347053 375int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
34437b4f
LP
376 assert_return(n, -EINVAL);
377 assert_return(address, -EINVAL);
378
379 *address = n->source_address;
380 return 0;
381}
382
17347053 383int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
34437b4f
LP
384 assert_return(n, -EINVAL);
385 assert_return(address, -EINVAL);
386
387 *address = n->destination_address;
388 return 0;
389}
390
17347053 391int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
34437b4f
LP
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
406static 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
425static int format_network_address(const void *data, size_t sz, char **ret) {
426 union in_addr_union a;
b3ad5fa9 427 int family, r;
34437b4f
LP
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
b3ad5fa9
ZJS
438 r = in_addr_to_string(family, &a, ret);
439 if (r < 0)
440 return r;
441 return 1;
34437b4f
LP
442}
443
17347053 444int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
34437b4f
LP
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
6afa6767
BG
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:
34437b4f
LP
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
6afa6767 471 case SD_LLDP_CHASSIS_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
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
6afa6767 480 case SD_LLDP_CHASSIS_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
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
495done:
496 *ret = n->chassis_id_as_string = k;
497 return 0;
498}
499
17347053 500int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
34437b4f
LP
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
17347053 515int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
34437b4f
LP
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
6afa6767
BG
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:
34437b4f
LP
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
6afa6767 541 case SD_LLDP_PORT_SUBTYPE_MAC_ADDRESS:
34437b4f
LP
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
6afa6767 550 case SD_LLDP_PORT_SUBTYPE_NETWORK_ADDRESS:
34437b4f
LP
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
565done:
566 *ret = n->port_id_as_string = k;
567 return 0;
568}
569
17347053 570int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
34437b4f 571 assert_return(n, -EINVAL);
8a19206d 572 assert_return(ret_sec, -EINVAL);
34437b4f 573
8a19206d 574 *ret_sec = n->ttl;
34437b4f
LP
575 return 0;
576}
577
17347053 578int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
34437b4f
LP
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
17347053 589int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
34437b4f
LP
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
17347053 600int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
34437b4f
LP
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
17347053 611int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
f69b4ae8
SS
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
17347053 622int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
34437b4f
LP
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
17347053 633int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
34437b4f
LP
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
496b21ab
LA
644int 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
17347053 655int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
34437b4f
LP
656 assert_return(n, -EINVAL);
657
658 assert(n->raw_size >= sizeof(struct ether_header));
659 n->rindex = sizeof(struct ether_header);
660
a85b46c3 661 return n->rindex < n->raw_size;
34437b4f
LP
662}
663
17347053 664int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
34437b4f
LP
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
f137029b 675 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
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
17347053 683int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
34437b4f
LP
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
f137029b 693 *type = LLDP_NEIGHBOR_TLV_TYPE(n);
34437b4f
LP
694 return 0;
695}
696
17347053 697int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
34437b4f
LP
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
a40f6dd4 710int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[static 3], uint8_t *subtype) {
34437b4f
LP
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
6afa6767 719 r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
34437b4f
LP
720 if (r < 0)
721 return r;
722 if (r == 0)
723 return -ENXIO;
724
f137029b 725 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
726 if (length < 4)
727 return -EBADMSG;
728
729 if (n->rindex + 2 + length > n->raw_size)
730 return -EBADMSG;
731
f137029b 732 d = LLDP_NEIGHBOR_TLV_DATA(n);
34437b4f
LP
733 memcpy(oui, d, 3);
734 *subtype = d[3];
735
736 return 0;
737}
738
a40f6dd4 739int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[static 3], uint8_t subtype) {
34437b4f
LP
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
17347053 752int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
34437b4f
LP
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
f137029b 764 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
34437b4f
LP
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}
16fed825 773
17347053 774int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
16fed825
LP
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}
329146a9 786
309a747f 787int lldp_neighbor_build_json(sd_lldp_neighbor *n, sd_json_variant **ret) {
329146a9
TP
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;
496b21ab
LA
792 uint16_t vlanid = 0;
793 bool valid_vlanid;
329146a9
TP
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
e2290a77 804 valid_cc = sd_lldp_neighbor_get_enabled_capabilities(n, &cc) >= 0;
496b21ab 805 valid_vlanid = sd_lldp_neighbor_get_port_vlan_id(n, &vlanid) >= 0;
329146a9 806
be5bee2a
LP
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),
496b21ab 816 SD_JSON_BUILD_PAIR_CONDITION(valid_cc, "EnabledCapabilities", SD_JSON_BUILD_UNSIGNED(cc)),
4c45437b 817 JSON_BUILD_PAIR_STRING_NON_EMPTY("MUDURL", n->mud_url),
496b21ab 818 SD_JSON_BUILD_PAIR_CONDITION(valid_vlanid, "VlanID", SD_JSON_BUILD_UNSIGNED(vlanid)));
329146a9 819}