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