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