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