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