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