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
lldp_neighbor_id_hash_func(const LLDPNeighborID * id,struct siphash * state)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
lldp_neighbor_id_compare_func(const LLDPNeighborID * x,const LLDPNeighborID * y)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
lldp_neighbor_prioq_compare_func(const void * a,const void * b)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
sd_lldp_neighbor_ref(sd_lldp_neighbor * n)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
lldp_neighbor_free(sd_lldp_neighbor * n)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
sd_lldp_neighbor_unref(sd_lldp_neighbor * n)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
lldp_neighbor_unlink(sd_lldp_neighbor * n)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
lldp_neighbor_new(size_t raw_size)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
parse_string(sd_lldp_rx * lldp_rx,char ** s,const void * q,size_t n)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(*s);
163 *s = k;
164
165 return 1;
166 }
167
lldp_neighbor_parse(sd_lldp_neighbor * n)168 int lldp_neighbor_parse(sd_lldp_neighbor *n) {
169 struct ether_header h;
170 const uint8_t *p;
171 size_t left;
172 int r;
173
174 assert(n);
175
176 if (n->raw_size < sizeof(struct ether_header))
177 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
178 "Received truncated packet, ignoring.");
179
180 memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
181
182 if (h.ether_type != htobe16(ETHERTYPE_LLDP))
183 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
184 "Received packet with wrong type, ignoring.");
185
186 if (h.ether_dhost[0] != 0x01 ||
187 h.ether_dhost[1] != 0x80 ||
188 h.ether_dhost[2] != 0xc2 ||
189 h.ether_dhost[3] != 0x00 ||
190 h.ether_dhost[4] != 0x00 ||
191 !IN_SET(h.ether_dhost[5], 0x00, 0x03, 0x0e))
192 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
193 "Received packet with wrong destination address, ignoring.");
194
195 memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
196 memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
197
198 p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
199 left = n->raw_size - sizeof(struct ether_header);
200
201 for (;;) {
202 uint8_t type;
203 uint16_t length;
204
205 if (left < 2)
206 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
207 "TLV lacks header, ignoring.");
208
209 type = p[0] >> 1;
210 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
211 p += 2, left -= 2;
212
213 if (left < length)
214 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
215 "TLV truncated, ignoring datagram.");
216
217 switch (type) {
218
219 case SD_LLDP_TYPE_END:
220 if (length != 0)
221 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
222 "End marker TLV not zero-sized, ignoring datagram.");
223
224 /* Note that after processing the SD_LLDP_TYPE_END left could still be > 0
225 * as the message may contain padding (see IEEE 802.1AB-2016, sec. 8.5.12) */
226
227 goto end_marker;
228
229 case SD_LLDP_TYPE_CHASSIS_ID:
230 if (length < 2 || length > 256)
231 /* includes the chassis subtype, hence one extra byte */
232 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
233 "Chassis ID field size out of range, ignoring datagram.");
234
235 if (n->id.chassis_id)
236 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
237 "Duplicate chassis ID field, ignoring datagram.");
238
239 n->id.chassis_id = memdup(p, length);
240 if (!n->id.chassis_id)
241 return log_oom_debug();
242
243 n->id.chassis_id_size = length;
244 break;
245
246 case SD_LLDP_TYPE_PORT_ID:
247 if (length < 2 || length > 256)
248 /* includes the port subtype, hence one extra byte */
249 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
250 "Port ID field size out of range, ignoring datagram.");
251
252 if (n->id.port_id)
253 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
254 "Duplicate port ID field, ignoring datagram.");
255
256 n->id.port_id = memdup(p, length);
257 if (!n->id.port_id)
258 return log_oom_debug();
259
260 n->id.port_id_size = length;
261 break;
262
263 case SD_LLDP_TYPE_TTL:
264 if (length != 2)
265 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
266 "TTL field has wrong size, ignoring datagram.");
267
268 if (n->has_ttl)
269 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
270 "Duplicate TTL field, ignoring datagram.");
271
272 n->ttl = unaligned_read_be16(p);
273 n->has_ttl = true;
274 break;
275
276 case SD_LLDP_TYPE_PORT_DESCRIPTION:
277 r = parse_string(n->lldp_rx, &n->port_description, p, length);
278 if (r < 0)
279 return r;
280 break;
281
282 case SD_LLDP_TYPE_SYSTEM_NAME:
283 r = parse_string(n->lldp_rx, &n->system_name, p, length);
284 if (r < 0)
285 return r;
286 break;
287
288 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
289 r = parse_string(n->lldp_rx, &n->system_description, p, length);
290 if (r < 0)
291 return r;
292 break;
293
294 case SD_LLDP_TYPE_SYSTEM_CAPABILITIES:
295 if (length != 4)
296 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
297 "System capabilities field has wrong size.");
298
299 n->system_capabilities = unaligned_read_be16(p);
300 n->enabled_capabilities = unaligned_read_be16(p + 2);
301 n->has_capabilities = true;
302 break;
303
304 case SD_LLDP_TYPE_PRIVATE:
305 if (length < 4)
306 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
307 "Found private TLV that is too short, ignoring.");
308
309 /* RFC 8520: MUD URL */
310 if (memcmp(p, SD_LLDP_OUI_IANA_MUD, sizeof(SD_LLDP_OUI_IANA_MUD)) == 0) {
311 r = parse_string(n->lldp_rx, &n->mud_url, p + sizeof(SD_LLDP_OUI_IANA_MUD),
312 length - sizeof(SD_LLDP_OUI_IANA_MUD));
313 if (r < 0)
314 return r;
315 }
316 break;
317 }
318
319 p += length, left -= length;
320 }
321
322 end_marker:
323 if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl)
324 return log_lldp_rx_errno(n->lldp_rx, SYNTHETIC_ERRNO(EBADMSG),
325 "One or more mandatory TLV missing in datagram. Ignoring.");
326
327 n->rindex = sizeof(struct ether_header);
328
329 return 0;
330 }
331
lldp_neighbor_start_ttl(sd_lldp_neighbor * n)332 void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
333 assert(n);
334
335 if (n->ttl > 0) {
336 usec_t base;
337
338 /* Use the packet's timestamp if there is one known */
339 base = triple_timestamp_by_clock(&n->timestamp, CLOCK_BOOTTIME);
340 if (!timestamp_is_set(base))
341 base = now(CLOCK_BOOTTIME); /* Otherwise, take the current time */
342
343 n->until = usec_add(base, n->ttl * USEC_PER_SEC);
344 } else
345 n->until = 0;
346
347 if (n->lldp_rx)
348 prioq_reshuffle(n->lldp_rx->neighbor_by_expiry, n, &n->prioq_idx);
349 }
350
lldp_neighbor_equal(const sd_lldp_neighbor * a,const sd_lldp_neighbor * b)351 bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
352 if (a == b)
353 return true;
354
355 if (!a || !b)
356 return false;
357
358 if (a->raw_size != b->raw_size)
359 return false;
360
361 return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
362 }
363
sd_lldp_neighbor_get_source_address(sd_lldp_neighbor * n,struct ether_addr * address)364 int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
365 assert_return(n, -EINVAL);
366 assert_return(address, -EINVAL);
367
368 *address = n->source_address;
369 return 0;
370 }
371
sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor * n,struct ether_addr * address)372 int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
373 assert_return(n, -EINVAL);
374 assert_return(address, -EINVAL);
375
376 *address = n->destination_address;
377 return 0;
378 }
379
sd_lldp_neighbor_get_raw(sd_lldp_neighbor * n,const void ** ret,size_t * size)380 int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
381 assert_return(n, -EINVAL);
382 assert_return(ret, -EINVAL);
383 assert_return(size, -EINVAL);
384
385 *ret = LLDP_NEIGHBOR_RAW(n);
386 *size = n->raw_size;
387
388 return 0;
389 }
390
sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor * n,uint8_t * type,const void ** ret,size_t * size)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
format_mac_address(const void * data,size_t sz,char ** ret)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
format_network_address(const void * data,size_t sz,char ** ret)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
sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_port_id(sd_lldp_neighbor * n,uint8_t * type,const void ** ret,size_t * size)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
sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_ttl(sd_lldp_neighbor * n,uint16_t * ret_sec)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
sd_lldp_neighbor_get_system_name(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_system_description(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_port_description(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor * n,const char ** ret)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
sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor * n,uint16_t * ret)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
sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor * n,uint16_t * ret)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
sd_lldp_neighbor_from_raw(sd_lldp_neighbor ** ret,const void * raw,size_t raw_size)644 int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
645 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
646 int r;
647
648 assert_return(ret, -EINVAL);
649 assert_return(raw || raw_size <= 0, -EINVAL);
650
651 n = lldp_neighbor_new(raw_size);
652 if (!n)
653 return -ENOMEM;
654
655 memcpy_safe(LLDP_NEIGHBOR_RAW(n), raw, raw_size);
656
657 r = lldp_neighbor_parse(n);
658 if (r < 0)
659 return r;
660
661 *ret = TAKE_PTR(n);
662
663 return r;
664 }
665
sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor * n)666 int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
667 assert_return(n, -EINVAL);
668
669 assert(n->raw_size >= sizeof(struct ether_header));
670 n->rindex = sizeof(struct ether_header);
671
672 return n->rindex < n->raw_size;
673 }
674
sd_lldp_neighbor_tlv_next(sd_lldp_neighbor * n)675 int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
676 size_t length;
677
678 assert_return(n, -EINVAL);
679
680 if (n->rindex == n->raw_size) /* EOF */
681 return -ESPIPE;
682
683 if (n->rindex + 2 > n->raw_size) /* Truncated message */
684 return -EBADMSG;
685
686 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
687 if (n->rindex + 2 + length > n->raw_size)
688 return -EBADMSG;
689
690 n->rindex += 2 + length;
691 return n->rindex < n->raw_size;
692 }
693
sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor * n,uint8_t * type)694 int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
695 assert_return(n, -EINVAL);
696 assert_return(type, -EINVAL);
697
698 if (n->rindex == n->raw_size) /* EOF */
699 return -ESPIPE;
700
701 if (n->rindex + 2 > n->raw_size)
702 return -EBADMSG;
703
704 *type = LLDP_NEIGHBOR_TLV_TYPE(n);
705 return 0;
706 }
707
sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor * n,uint8_t type)708 int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
709 uint8_t k;
710 int r;
711
712 assert_return(n, -EINVAL);
713
714 r = sd_lldp_neighbor_tlv_get_type(n, &k);
715 if (r < 0)
716 return r;
717
718 return type == k;
719 }
720
sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor * n,uint8_t oui[_SD_ARRAY_STATIC3],uint8_t * subtype)721 int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
722 const uint8_t *d;
723 size_t length;
724 int r;
725
726 assert_return(n, -EINVAL);
727 assert_return(oui, -EINVAL);
728 assert_return(subtype, -EINVAL);
729
730 r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
731 if (r < 0)
732 return r;
733 if (r == 0)
734 return -ENXIO;
735
736 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
737 if (length < 4)
738 return -EBADMSG;
739
740 if (n->rindex + 2 + length > n->raw_size)
741 return -EBADMSG;
742
743 d = LLDP_NEIGHBOR_TLV_DATA(n);
744 memcpy(oui, d, 3);
745 *subtype = d[3];
746
747 return 0;
748 }
749
sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor * n,const uint8_t oui[_SD_ARRAY_STATIC3],uint8_t subtype)750 int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
751 uint8_t k[3], st;
752 int r;
753
754 r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
755 if (r == -ENXIO)
756 return 0;
757 if (r < 0)
758 return r;
759
760 return memcmp(k, oui, 3) == 0 && st == subtype;
761 }
762
sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor * n,const void ** ret,size_t * size)763 int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
764 size_t length;
765
766 assert_return(n, -EINVAL);
767 assert_return(ret, -EINVAL);
768 assert_return(size, -EINVAL);
769
770 /* Note that this returns the full TLV, including the TLV header */
771
772 if (n->rindex + 2 > n->raw_size)
773 return -EBADMSG;
774
775 length = LLDP_NEIGHBOR_TLV_LENGTH(n);
776 if (n->rindex + 2 + length > n->raw_size)
777 return -EBADMSG;
778
779 *ret = (uint8_t*) LLDP_NEIGHBOR_RAW(n) + n->rindex;
780 *size = length + 2;
781
782 return 0;
783 }
784
sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor * n,clockid_t clock,uint64_t * ret)785 int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
786 assert_return(n, -EINVAL);
787 assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
788 assert_return(clock_supported(clock), -EOPNOTSUPP);
789 assert_return(ret, -EINVAL);
790
791 if (!triple_timestamp_is_set(&n->timestamp))
792 return -ENODATA;
793
794 *ret = triple_timestamp_by_clock(&n->timestamp, clock);
795 return 0;
796 }
797