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