LCOV - code coverage report
Current view: top level - libsystemd-network - lldp-neighbor.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 228 454 50.2 %
Date: 2019-08-22 15:41:25 Functions: 24 37 64.9 %

          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          27 : static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) {
      15          27 :         siphash24_compress(id->chassis_id, id->chassis_id_size, state);
      16          27 :         siphash24_compress(&id->chassis_id_size, sizeof(id->chassis_id_size), state);
      17          27 :         siphash24_compress(id->port_id, id->port_id_size, state);
      18          27 :         siphash24_compress(&id->port_id_size, sizeof(id->port_id_size), state);
      19          27 : }
      20             : 
      21          16 : int lldp_neighbor_id_compare_func(const LLDPNeighborID *x, const LLDPNeighborID *y) {
      22          16 :         return memcmp_nn(x->chassis_id, x->chassis_id_size, y->chassis_id, y->chassis_id_size)
      23          16 :             ?: memcmp_nn(x->port_id, x->port_id_size, y->port_id, y->port_id_size);
      24             : }
      25             : 
      26           8 : 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          28 : int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
      30          28 :         const sd_lldp_neighbor *x = a, *y = b;
      31             : 
      32          28 :         return CMP(x->until, y->until);
      33             : }
      34             : 
      35          13 : _public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
      36          13 :         if (!n)
      37           0 :                 return NULL;
      38             : 
      39          13 :         assert(n->n_ref > 0 || n->lldp);
      40          13 :         n->n_ref++;
      41             : 
      42          13 :         return n;
      43             : }
      44             : 
      45           9 : static void lldp_neighbor_free(sd_lldp_neighbor *n) {
      46           9 :         assert(n);
      47             : 
      48           9 :         free(n->id.port_id);
      49           9 :         free(n->id.chassis_id);
      50           9 :         free(n->port_description);
      51           9 :         free(n->system_name);
      52           9 :         free(n->system_description);
      53           9 :         free(n->chassis_id_as_string);
      54           9 :         free(n->port_id_as_string);
      55           9 :         free(n);
      56           9 : }
      57             : 
      58          22 : _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          22 :         if (!n)
      64           0 :                 return NULL;
      65             : 
      66          22 :         assert(n->n_ref > 0);
      67          22 :         n->n_ref--;
      68             : 
      69          22 :         if (n->n_ref <= 0 && !n->lldp)
      70           1 :                 lldp_neighbor_free(n);
      71             : 
      72          22 :         return NULL;
      73             : }
      74             : 
      75           8 : 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           8 :         if (!n)
      80           0 :                 return NULL;
      81             : 
      82           8 :         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           8 :         (void) hashmap_remove_value(n->lldp->neighbor_by_id, &n->id, n);
      90             : 
      91           8 :         assert_se(prioq_remove(n->lldp->neighbor_by_expiry, n, &n->prioq_idx) >= 0);
      92             : 
      93           8 :         n->lldp = NULL;
      94             : 
      95           8 :         if (n->n_ref <= 0)
      96           8 :                 lldp_neighbor_free(n);
      97             : 
      98           8 :         return NULL;
      99             : }
     100             : 
     101           9 : sd_lldp_neighbor *lldp_neighbor_new(size_t raw_size) {
     102             :         sd_lldp_neighbor *n;
     103             : 
     104           9 :         n = malloc0(ALIGN(sizeof(sd_lldp_neighbor)) + raw_size);
     105           9 :         if (!n)
     106           0 :                 return NULL;
     107             : 
     108           9 :         n->raw_size = raw_size;
     109           9 :         n->n_ref = 1;
     110             : 
     111           9 :         return n;
     112             : }
     113             : 
     114           3 : static int parse_string(char **s, const void *q, size_t n) {
     115           3 :         const char *p = q;
     116             :         char *k;
     117             : 
     118           3 :         assert(s);
     119           3 :         assert(p || n == 0);
     120             : 
     121           3 :         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           4 :         while (n > 0 && p[n-1] == 0)
     128           1 :                 n--;
     129             : 
     130           3 :         if (n <= 0) /* Ignore empty strings */
     131           0 :                 return 0;
     132             : 
     133             :         /* Look for inner NULs */
     134           3 :         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           3 :         k = cescape_length(p, n);
     141           3 :         if (!k)
     142           0 :                 return -ENOMEM;
     143             : 
     144           3 :         free(*s);
     145           3 :         *s = k;
     146             : 
     147           3 :         return 1;
     148             : }
     149             : 
     150           9 : 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           9 :         assert(n);
     157             : 
     158           9 :         if (n->raw_size < sizeof(struct ether_header)) {
     159           0 :                 log_lldp("Received truncated packet, ignoring.");
     160           0 :                 return -EBADMSG;
     161             :         }
     162             : 
     163           9 :         memcpy(&h, LLDP_NEIGHBOR_RAW(n), sizeof(h));
     164             : 
     165           9 :         if (h.ether_type != htobe16(ETHERTYPE_LLDP)) {
     166           0 :                 log_lldp("Received packet with wrong type, ignoring.");
     167           0 :                 return -EBADMSG;
     168             :         }
     169             : 
     170           9 :         if (h.ether_dhost[0] != 0x01 ||
     171           9 :             h.ether_dhost[1] != 0x80 ||
     172           9 :             h.ether_dhost[2] != 0xc2 ||
     173           9 :             h.ether_dhost[3] != 0x00 ||
     174           9 :             h.ether_dhost[4] != 0x00 ||
     175           9 :             !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           9 :         memcpy(&n->source_address, h.ether_shost, sizeof(struct ether_addr));
     181           9 :         memcpy(&n->destination_address, h.ether_dhost, sizeof(struct ether_addr));
     182             : 
     183           9 :         p = (const uint8_t*) LLDP_NEIGHBOR_RAW(n) + sizeof(struct ether_header);
     184           9 :         left = n->raw_size - sizeof(struct ether_header);
     185             : 
     186          35 :         for (;;) {
     187             :                 uint8_t type;
     188             :                 uint16_t length;
     189             : 
     190          44 :                 if (left < 2) {
     191           0 :                         log_lldp("TLV lacks header, ignoring.");
     192           0 :                         return -EBADMSG;
     193             :                 }
     194             : 
     195          44 :                 type = p[0] >> 1;
     196          44 :                 length = p[1] + (((uint16_t) (p[0] & 1)) << 8);
     197          44 :                 p += 2, left -= 2;
     198             : 
     199          44 :                 if (left < length) {
     200           0 :                         log_lldp("TLV truncated, ignoring datagram.");
     201           0 :                         return -EBADMSG;
     202             :                 }
     203             : 
     204          44 :                 switch (type) {
     205             : 
     206           9 :                 case SD_LLDP_TYPE_END:
     207           9 :                         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           9 :                         goto end_marker;
     216             : 
     217           9 :                 case SD_LLDP_TYPE_CHASSIS_ID:
     218           9 :                         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           9 :                         if (n->id.chassis_id) {
     223           0 :                                 log_lldp("Duplicate chassis ID field, ignoring datagram.");
     224           0 :                                 return -EBADMSG;
     225             :                         }
     226             : 
     227           9 :                         n->id.chassis_id = memdup(p, length);
     228           9 :                         if (!n->id.chassis_id)
     229           0 :                                 return -ENOMEM;
     230             : 
     231           9 :                         n->id.chassis_id_size = length;
     232           9 :                         break;
     233             : 
     234           9 :                 case SD_LLDP_TYPE_PORT_ID:
     235           9 :                         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           9 :                         if (n->id.port_id) {
     240           0 :                                 log_lldp("Duplicate port ID field, ignoring datagram.");
     241           0 :                                 return -EBADMSG;
     242             :                         }
     243             : 
     244           9 :                         n->id.port_id = memdup(p, length);
     245           9 :                         if (!n->id.port_id)
     246           0 :                                 return -ENOMEM;
     247             : 
     248           9 :                         n->id.port_id_size = length;
     249           9 :                         break;
     250             : 
     251           8 :                 case SD_LLDP_TYPE_TTL:
     252           8 :                         if (length != 2) {
     253           0 :                                 log_lldp("TTL field has wrong size, ignoring datagram.");
     254           0 :                                 return -EBADMSG;
     255             :                         }
     256             : 
     257           8 :                         if (n->has_ttl) {
     258           0 :                                 log_lldp("Duplicate TTL field, ignoring datagram.");
     259           0 :                                 return -EBADMSG;
     260             :                         }
     261             : 
     262           8 :                         n->ttl = unaligned_read_be16(p);
     263           8 :                         n->has_ttl = true;
     264           8 :                         break;
     265             : 
     266           1 :                 case SD_LLDP_TYPE_PORT_DESCRIPTION:
     267           1 :                         r = parse_string(&n->port_description, p, length);
     268           1 :                         if (r < 0)
     269           0 :                                 return r;
     270           1 :                         break;
     271             : 
     272           1 :                 case SD_LLDP_TYPE_SYSTEM_NAME:
     273           1 :                         r = parse_string(&n->system_name, p, length);
     274           1 :                         if (r < 0)
     275           0 :                                 return r;
     276           1 :                         break;
     277             : 
     278           1 :                 case SD_LLDP_TYPE_SYSTEM_DESCRIPTION:
     279           1 :                         r = parse_string(&n->system_description, p, length);
     280           1 :                         if (r < 0)
     281           0 :                                 return r;
     282           1 :                         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           6 :                 case SD_LLDP_TYPE_PRIVATE:
     296           6 :                         if (length < 4)
     297           0 :                                 log_lldp("Found private TLV that is too short, ignoring.");
     298             : 
     299           6 :                         break;
     300             :                 }
     301             : 
     302          35 :                 p += length, left -= length;
     303             :         }
     304             : 
     305           9 : end_marker:
     306           9 :         if (!n->id.chassis_id || !n->id.port_id || !n->has_ttl) {
     307           1 :                 log_lldp("One or more mandatory TLV missing in datagram. Ignoring.");
     308           1 :                 return -EBADMSG;
     309             : 
     310             :         }
     311             : 
     312           8 :         n->rindex = sizeof(struct ether_header);
     313             : 
     314           8 :         return 0;
     315             : }
     316             : 
     317           8 : void lldp_neighbor_start_ttl(sd_lldp_neighbor *n) {
     318           8 :         assert(n);
     319             : 
     320           8 :         if (n->ttl > 0) {
     321             :                 usec_t base;
     322             : 
     323             :                 /* Use the packet's timestamp if there is one known */
     324           8 :                 base = triple_timestamp_by_clock(&n->timestamp, clock_boottime_or_monotonic());
     325           8 :                 if (base <= 0 || base == USEC_INFINITY)
     326           0 :                         base = now(clock_boottime_or_monotonic()); /* Otherwise, take the current time */
     327             : 
     328           8 :                 n->until = usec_add(base, n->ttl * USEC_PER_SEC);
     329             :         } else
     330           0 :                 n->until = 0;
     331             : 
     332           8 :         if (n->lldp)
     333           8 :                 prioq_reshuffle(n->lldp->neighbor_by_expiry, n, &n->prioq_idx);
     334           8 : }
     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           7 : _public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
     377           7 :         assert_return(n, -EINVAL);
     378           7 :         assert_return(type, -EINVAL);
     379           7 :         assert_return(ret, -EINVAL);
     380           7 :         assert_return(size, -EINVAL);
     381             : 
     382           7 :         assert(n->id.chassis_id_size > 0);
     383             : 
     384           7 :         *type = *(uint8_t*) n->id.chassis_id;
     385           7 :         *ret = (uint8_t*) n->id.chassis_id + 1;
     386           7 :         *size = n->id.chassis_id_size - 1;
     387             : 
     388           7 :         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           7 : _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
     486           7 :         assert_return(n, -EINVAL);
     487           7 :         assert_return(type, -EINVAL);
     488           7 :         assert_return(ret, -EINVAL);
     489           7 :         assert_return(size, -EINVAL);
     490             : 
     491           7 :         assert(n->id.port_id_size > 0);
     492             : 
     493           7 :         *type = *(uint8_t*) n->id.port_id;
     494           7 :         *ret = (uint8_t*) n->id.port_id + 1;
     495           7 :         *size = n->id.port_id_size - 1;
     496             : 
     497           7 :         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           7 : _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
     556           7 :         assert_return(n, -EINVAL);
     557           7 :         assert_return(ret_sec, -EINVAL);
     558             : 
     559           7 :         *ret_sec = n->ttl;
     560           7 :         return 0;
     561             : }
     562             : 
     563           1 : _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
     564           1 :         assert_return(n, -EINVAL);
     565           1 :         assert_return(ret, -EINVAL);
     566             : 
     567           1 :         if (!n->system_name)
     568           0 :                 return -ENODATA;
     569             : 
     570           1 :         *ret = n->system_name;
     571           1 :         return 0;
     572             : }
     573             : 
     574           1 : _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
     575           1 :         assert_return(n, -EINVAL);
     576           1 :         assert_return(ret, -EINVAL);
     577             : 
     578           1 :         if (!n->system_description)
     579           0 :                 return -ENODATA;
     580             : 
     581           1 :         *ret = n->system_description;
     582           1 :         return 0;
     583             : }
     584             : 
     585           1 : _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
     586           1 :         assert_return(n, -EINVAL);
     587           1 :         assert_return(ret, -EINVAL);
     588             : 
     589           1 :         if (!n->port_description)
     590           0 :                 return -ENODATA;
     591             : 
     592           1 :         *ret = n->port_description;
     593           1 :         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           1 : _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
     640           1 :         assert_return(n, -EINVAL);
     641             : 
     642           1 :         assert(n->raw_size >= sizeof(struct ether_header));
     643           1 :         n->rindex = sizeof(struct ether_header);
     644             : 
     645           1 :         return n->rindex < n->raw_size;
     646             : }
     647             : 
     648          10 : _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
     649             :         size_t length;
     650             : 
     651          10 :         assert_return(n, -EINVAL);
     652             : 
     653          10 :         if (n->rindex == n->raw_size) /* EOF */
     654           0 :                 return -ESPIPE;
     655             : 
     656          10 :         if (n->rindex + 2 > n->raw_size) /* Truncated message */
     657           0 :                 return -EBADMSG;
     658             : 
     659          10 :         length = LLDP_NEIGHBOR_TLV_LENGTH(n);
     660          10 :         if (n->rindex + 2 + length > n->raw_size)
     661           0 :                 return -EBADMSG;
     662             : 
     663          10 :         n->rindex += 2 + length;
     664          10 :         return n->rindex < n->raw_size;
     665             : }
     666             : 
     667          10 : _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
     668          10 :         assert_return(n, -EINVAL);
     669          10 :         assert_return(type, -EINVAL);
     670             : 
     671          10 :         if (n->rindex == n->raw_size) /* EOF */
     672           0 :                 return -ESPIPE;
     673             : 
     674          10 :         if (n->rindex + 2 > n->raw_size)
     675           0 :                 return -EBADMSG;
     676             : 
     677          10 :         *type = LLDP_NEIGHBOR_TLV_TYPE(n);
     678          10 :         return 0;
     679             : }
     680             : 
     681          10 : _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
     682             :         uint8_t k;
     683             :         int r;
     684             : 
     685          10 :         assert_return(n, -EINVAL);
     686             : 
     687          10 :         r = sd_lldp_neighbor_tlv_get_type(n, &k);
     688          10 :         if (r < 0)
     689           0 :                 return r;
     690             : 
     691          10 :         return type == k;
     692             : }
     693             : 
     694           6 : _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           6 :         assert_return(n, -EINVAL);
     700           6 :         assert_return(oui, -EINVAL);
     701           6 :         assert_return(subtype, -EINVAL);
     702             : 
     703           6 :         r = sd_lldp_neighbor_tlv_is_type(n, SD_LLDP_TYPE_PRIVATE);
     704           6 :         if (r < 0)
     705           0 :                 return r;
     706           6 :         if (r == 0)
     707           0 :                 return -ENXIO;
     708             : 
     709           6 :         length = LLDP_NEIGHBOR_TLV_LENGTH(n);
     710           6 :         if (length < 4)
     711           0 :                 return -EBADMSG;
     712             : 
     713           6 :         if (n->rindex + 2 + length > n->raw_size)
     714           0 :                 return -EBADMSG;
     715             : 
     716           6 :         d = LLDP_NEIGHBOR_TLV_DATA(n);
     717           6 :         memcpy(oui, d, 3);
     718           6 :         *subtype = d[3];
     719             : 
     720           6 :         return 0;
     721             : }
     722             : 
     723           6 : _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           6 :         r = sd_lldp_neighbor_tlv_get_oui(n, k, &st);
     728           6 :         if (r == -ENXIO)
     729           0 :                 return 0;
     730           6 :         if (r < 0)
     731           0 :                 return r;
     732             : 
     733           6 :         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             : }

Generated by: LCOV version 1.14