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