LCOV - code coverage report
Current view: top level - libsystemd-network - sd-lldp.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 179 270 66.3 %
Date: 2019-08-22 15:41:25 Functions: 22 28 78.6 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <arpa/inet.h>
       4             : #include <linux/sockios.h>
       5             : #include <sys/ioctl.h>
       6             : 
       7             : #include "sd-lldp.h"
       8             : 
       9             : #include "alloc-util.h"
      10             : #include "ether-addr-util.h"
      11             : #include "event-util.h"
      12             : #include "fd-util.h"
      13             : #include "lldp-internal.h"
      14             : #include "lldp-neighbor.h"
      15             : #include "lldp-network.h"
      16             : #include "memory-util.h"
      17             : #include "socket-util.h"
      18             : #include "sort-util.h"
      19             : #include "string-table.h"
      20             : 
      21             : #define LLDP_DEFAULT_NEIGHBORS_MAX 128U
      22             : 
      23             : static const char * const lldp_event_table[_SD_LLDP_EVENT_MAX] = {
      24             :         [SD_LLDP_EVENT_ADDED]   = "added",
      25             :         [SD_LLDP_EVENT_REMOVED] = "removed",
      26             :         [SD_LLDP_EVENT_UPDATED]   = "updated",
      27             :         [SD_LLDP_EVENT_REFRESHED] = "refreshed",
      28             : };
      29             : 
      30          20 : DEFINE_STRING_TABLE_LOOKUP(lldp_event, sd_lldp_event);
      31             : 
      32           8 : static void lldp_flush_neighbors(sd_lldp *lldp) {
      33           8 :         assert(lldp);
      34             : 
      35           8 :         hashmap_clear(lldp->neighbor_by_id);
      36           8 : }
      37             : 
      38           8 : static void lldp_callback(sd_lldp *lldp, sd_lldp_event event, sd_lldp_neighbor *n) {
      39           8 :         assert(lldp);
      40           8 :         assert(event >= 0 && event < _SD_LLDP_EVENT_MAX);
      41             : 
      42           8 :         if (!lldp->callback) {
      43           0 :                 log_lldp("Received '%s' event.", lldp_event_to_string(event));
      44           0 :                 return;
      45             :         }
      46             : 
      47           8 :         log_lldp("Invoking callback for '%s' event.", lldp_event_to_string(event));
      48           8 :         lldp->callback(lldp, event, n, lldp->userdata);
      49             : }
      50             : 
      51           8 : static int lldp_make_space(sd_lldp *lldp, size_t extra) {
      52           8 :         usec_t t = USEC_INFINITY;
      53           8 :         bool changed = false;
      54             : 
      55           8 :         assert(lldp);
      56             : 
      57             :         /* Remove all entries that are past their TTL, and more until at least the specified number of extra entries
      58             :          * are free. */
      59             : 
      60           0 :         for (;;) {
      61           8 :                 _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
      62             : 
      63           8 :                 n = prioq_peek(lldp->neighbor_by_expiry);
      64           8 :                 if (!n)
      65           3 :                         break;
      66             : 
      67           5 :                 sd_lldp_neighbor_ref(n);
      68             : 
      69           5 :                 if (hashmap_size(lldp->neighbor_by_id) > LESS_BY(lldp->neighbors_max, extra))
      70           0 :                         goto remove_one;
      71             : 
      72           5 :                 if (t == USEC_INFINITY)
      73           5 :                         t = now(clock_boottime_or_monotonic());
      74             : 
      75           5 :                 if (n->until > t)
      76           5 :                         break;
      77             : 
      78           0 :         remove_one:
      79           0 :                 lldp_neighbor_unlink(n);
      80           0 :                 lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, n);
      81           0 :                 changed = true;
      82             :         }
      83             : 
      84           8 :         return changed;
      85             : }
      86             : 
      87           8 : static bool lldp_keep_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
      88           8 :         assert(lldp);
      89           8 :         assert(n);
      90             : 
      91             :         /* Don't keep data with a zero TTL */
      92           8 :         if (n->ttl <= 0)
      93           0 :                 return false;
      94             : 
      95             :         /* Filter out data from the filter address */
      96           8 :         if (!ether_addr_is_null(&lldp->filter_address) &&
      97           0 :             ether_addr_equal(&lldp->filter_address, &n->source_address))
      98           0 :                 return false;
      99             : 
     100             :         /* Only add if the neighbor has a capability we are interested in. Note that we also store all neighbors with
     101             :          * no caps field set. */
     102           8 :         if (n->has_capabilities &&
     103           0 :             (n->enabled_capabilities & lldp->capability_mask) == 0)
     104           0 :                 return false;
     105             : 
     106             :         /* Keep everything else */
     107           8 :         return true;
     108             : }
     109             : 
     110             : static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor);
     111             : 
     112           8 : static int lldp_add_neighbor(sd_lldp *lldp, sd_lldp_neighbor *n) {
     113           8 :         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *old = NULL;
     114             :         bool keep;
     115             :         int r;
     116             : 
     117           8 :         assert(lldp);
     118           8 :         assert(n);
     119           8 :         assert(!n->lldp);
     120             : 
     121           8 :         keep = lldp_keep_neighbor(lldp, n);
     122             : 
     123             :         /* First retrieve the old entry for this MSAP */
     124           8 :         old = hashmap_get(lldp->neighbor_by_id, &n->id);
     125           8 :         if (old) {
     126           0 :                 sd_lldp_neighbor_ref(old);
     127             : 
     128           0 :                 if (!keep) {
     129           0 :                         lldp_neighbor_unlink(old);
     130           0 :                         lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
     131           0 :                         return 0;
     132             :                 }
     133             : 
     134           0 :                 if (lldp_neighbor_equal(n, old)) {
     135             :                         /* Is this equal, then restart the TTL counter, but don't do anything else. */
     136           0 :                         old->timestamp = n->timestamp;
     137           0 :                         lldp_start_timer(lldp, old);
     138           0 :                         lldp_callback(lldp, SD_LLDP_EVENT_REFRESHED, old);
     139           0 :                         return 0;
     140             :                 }
     141             : 
     142             :                 /* Data changed, remove the old entry, and add a new one */
     143           0 :                 lldp_neighbor_unlink(old);
     144             : 
     145           8 :         } else if (!keep)
     146           0 :                 return 0;
     147             : 
     148             :         /* Then, make room for at least one new neighbor */
     149           8 :         lldp_make_space(lldp, 1);
     150             : 
     151           8 :         r = hashmap_put(lldp->neighbor_by_id, &n->id, n);
     152           8 :         if (r < 0)
     153           0 :                 goto finish;
     154             : 
     155           8 :         r = prioq_put(lldp->neighbor_by_expiry, n, &n->prioq_idx);
     156           8 :         if (r < 0) {
     157           0 :                 assert_se(hashmap_remove(lldp->neighbor_by_id, &n->id) == n);
     158           0 :                 goto finish;
     159             :         }
     160             : 
     161           8 :         n->lldp = lldp;
     162             : 
     163           8 :         lldp_start_timer(lldp, n);
     164           8 :         lldp_callback(lldp, old ? SD_LLDP_EVENT_UPDATED : SD_LLDP_EVENT_ADDED, n);
     165             : 
     166           8 :         return 1;
     167             : 
     168           0 : finish:
     169           0 :         if (old)
     170           0 :                 lldp_callback(lldp, SD_LLDP_EVENT_REMOVED, old);
     171             : 
     172           0 :         return r;
     173             : }
     174             : 
     175           9 : static int lldp_handle_datagram(sd_lldp *lldp, sd_lldp_neighbor *n) {
     176             :         int r;
     177             : 
     178           9 :         assert(lldp);
     179           9 :         assert(n);
     180             : 
     181           9 :         r = lldp_neighbor_parse(n);
     182           9 :         if (r == -EBADMSG) /* Ignore bad messages */
     183           1 :                 return 0;
     184           8 :         if (r < 0)
     185           0 :                 return r;
     186             : 
     187           8 :         r = lldp_add_neighbor(lldp, n);
     188           8 :         if (r < 0) {
     189           0 :                 log_lldp_errno(r, "Failed to add datagram. Ignoring.");
     190           0 :                 return 0;
     191             :         }
     192             : 
     193           8 :         log_lldp("Successfully processed LLDP datagram.");
     194           8 :         return 0;
     195             : }
     196             : 
     197           9 : static int lldp_receive_datagram(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     198           9 :         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
     199             :         ssize_t space, length;
     200           9 :         sd_lldp *lldp = userdata;
     201             :         struct timespec ts;
     202             : 
     203           9 :         assert(fd >= 0);
     204           9 :         assert(lldp);
     205             : 
     206           9 :         space = next_datagram_size_fd(fd);
     207           9 :         if (space < 0)
     208           0 :                 return log_lldp_errno(space, "Failed to determine datagram size to read: %m");
     209             : 
     210           9 :         n = lldp_neighbor_new(space);
     211           9 :         if (!n)
     212           0 :                 return -ENOMEM;
     213             : 
     214           9 :         length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
     215           9 :         if (length < 0) {
     216           0 :                 if (IN_SET(errno, EAGAIN, EINTR))
     217           0 :                         return 0;
     218             : 
     219           0 :                 return log_lldp_errno(errno, "Failed to read LLDP datagram: %m");
     220             :         }
     221             : 
     222           9 :         if ((size_t) length != n->raw_size) {
     223           0 :                 log_lldp("Packet size mismatch.");
     224           0 :                 return -EINVAL;
     225             :         }
     226             : 
     227             :         /* Try to get the timestamp of this packet if it is known */
     228           9 :         if (ioctl(fd, SIOCGSTAMPNS, &ts) >= 0)
     229           0 :                 triple_timestamp_from_realtime(&n->timestamp, timespec_load(&ts));
     230             :         else
     231           9 :                 triple_timestamp_get(&n->timestamp);
     232             : 
     233           9 :         return lldp_handle_datagram(lldp, n);
     234             : }
     235             : 
     236           8 : static void lldp_reset(sd_lldp *lldp) {
     237           8 :         assert(lldp);
     238             : 
     239           8 :         (void) event_source_disable(lldp->timer_event_source);
     240           8 :         lldp->io_event_source = sd_event_source_unref(lldp->io_event_source);
     241           8 :         lldp->fd = safe_close(lldp->fd);
     242           8 : }
     243             : 
     244           4 : _public_ int sd_lldp_start(sd_lldp *lldp) {
     245             :         int r;
     246             : 
     247           4 :         assert_return(lldp, -EINVAL);
     248           4 :         assert_return(lldp->event, -EINVAL);
     249           4 :         assert_return(lldp->ifindex > 0, -EINVAL);
     250             : 
     251           4 :         if (lldp->fd >= 0)
     252           0 :                 return 0;
     253             : 
     254           4 :         assert(!lldp->io_event_source);
     255             : 
     256           4 :         lldp->fd = lldp_network_bind_raw_socket(lldp->ifindex);
     257           4 :         if (lldp->fd < 0)
     258           0 :                 return lldp->fd;
     259             : 
     260           4 :         r = sd_event_add_io(lldp->event, &lldp->io_event_source, lldp->fd, EPOLLIN, lldp_receive_datagram, lldp);
     261           4 :         if (r < 0)
     262           0 :                 goto fail;
     263             : 
     264           4 :         r = sd_event_source_set_priority(lldp->io_event_source, lldp->event_priority);
     265           4 :         if (r < 0)
     266           0 :                 goto fail;
     267             : 
     268           4 :         (void) sd_event_source_set_description(lldp->io_event_source, "lldp-io");
     269             : 
     270           4 :         log_lldp("Started LLDP client");
     271           4 :         return 1;
     272             : 
     273           0 : fail:
     274           0 :         lldp_reset(lldp);
     275           0 :         return r;
     276             : }
     277             : 
     278           4 : _public_ int sd_lldp_stop(sd_lldp *lldp) {
     279           4 :         assert_return(lldp, -EINVAL);
     280             : 
     281           4 :         if (lldp->fd < 0)
     282           0 :                 return 0;
     283             : 
     284           4 :         log_lldp("Stopping LLDP client");
     285             : 
     286           4 :         lldp_reset(lldp);
     287           4 :         lldp_flush_neighbors(lldp);
     288             : 
     289           4 :         return 1;
     290             : }
     291             : 
     292           4 : _public_ int sd_lldp_attach_event(sd_lldp *lldp, sd_event *event, int64_t priority) {
     293             :         int r;
     294             : 
     295           4 :         assert_return(lldp, -EINVAL);
     296           4 :         assert_return(lldp->fd < 0, -EBUSY);
     297           4 :         assert_return(!lldp->event, -EBUSY);
     298             : 
     299           4 :         if (event)
     300           4 :                 lldp->event = sd_event_ref(event);
     301             :         else {
     302           0 :                 r = sd_event_default(&lldp->event);
     303           0 :                 if (r < 0)
     304           0 :                         return r;
     305             :         }
     306             : 
     307           4 :         lldp->event_priority = priority;
     308             : 
     309           4 :         return 0;
     310             : }
     311             : 
     312           8 : _public_ int sd_lldp_detach_event(sd_lldp *lldp) {
     313             : 
     314           8 :         assert_return(lldp, -EINVAL);
     315           8 :         assert_return(lldp->fd < 0, -EBUSY);
     316             : 
     317           8 :         lldp->event = sd_event_unref(lldp->event);
     318           8 :         return 0;
     319             : }
     320             : 
     321           0 : _public_ sd_event* sd_lldp_get_event(sd_lldp *lldp) {
     322           0 :         assert_return(lldp, NULL);
     323             : 
     324           0 :         return lldp->event;
     325             : }
     326             : 
     327           4 : _public_ int sd_lldp_set_callback(sd_lldp *lldp, sd_lldp_callback_t cb, void *userdata) {
     328           4 :         assert_return(lldp, -EINVAL);
     329             : 
     330           4 :         lldp->callback = cb;
     331           4 :         lldp->userdata = userdata;
     332             : 
     333           4 :         return 0;
     334             : }
     335             : 
     336           4 : _public_ int sd_lldp_set_ifindex(sd_lldp *lldp, int ifindex) {
     337           4 :         assert_return(lldp, -EINVAL);
     338           4 :         assert_return(ifindex > 0, -EINVAL);
     339           4 :         assert_return(lldp->fd < 0, -EBUSY);
     340             : 
     341           4 :         lldp->ifindex = ifindex;
     342           4 :         return 0;
     343             : }
     344             : 
     345           4 : static sd_lldp* lldp_free(sd_lldp *lldp) {
     346           4 :         assert(lldp);
     347             : 
     348           4 :         lldp->timer_event_source = sd_event_source_unref(lldp->timer_event_source);
     349             : 
     350           4 :         lldp_reset(lldp);
     351           4 :         sd_lldp_detach_event(lldp);
     352           4 :         lldp_flush_neighbors(lldp);
     353             : 
     354           4 :         hashmap_free(lldp->neighbor_by_id);
     355           4 :         prioq_free(lldp->neighbor_by_expiry);
     356           4 :         return mfree(lldp);
     357             : }
     358             : 
     359          10 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp, sd_lldp, lldp_free);
     360             : 
     361           4 : _public_ int sd_lldp_new(sd_lldp **ret) {
     362           4 :         _cleanup_(sd_lldp_unrefp) sd_lldp *lldp = NULL;
     363             :         int r;
     364             : 
     365           4 :         assert_return(ret, -EINVAL);
     366             : 
     367           4 :         lldp = new(sd_lldp, 1);
     368           4 :         if (!lldp)
     369           0 :                 return -ENOMEM;
     370             : 
     371           4 :         *lldp = (sd_lldp) {
     372             :                 .n_ref = 1,
     373             :                 .fd = -1,
     374             :                 .neighbors_max = LLDP_DEFAULT_NEIGHBORS_MAX,
     375             :                 .capability_mask = (uint16_t) -1,
     376             :         };
     377             : 
     378           4 :         lldp->neighbor_by_id = hashmap_new(&lldp_neighbor_hash_ops);
     379           4 :         if (!lldp->neighbor_by_id)
     380           0 :                 return -ENOMEM;
     381             : 
     382           4 :         r = prioq_ensure_allocated(&lldp->neighbor_by_expiry, lldp_neighbor_prioq_compare_func);
     383           4 :         if (r < 0)
     384           0 :                 return r;
     385             : 
     386           4 :         *ret = TAKE_PTR(lldp);
     387             : 
     388           4 :         return 0;
     389             : }
     390             : 
     391          11 : static int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_neighbor * const *b) {
     392          11 :         return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
     393             : }
     394             : 
     395           0 : static int on_timer_event(sd_event_source *s, uint64_t usec, void *userdata) {
     396           0 :         sd_lldp *lldp = userdata;
     397             :         int r;
     398             : 
     399           0 :         r = lldp_make_space(lldp, 0);
     400           0 :         if (r < 0)
     401           0 :                 return log_lldp_errno(r, "Failed to make space: %m");
     402             : 
     403           0 :         r = lldp_start_timer(lldp, NULL);
     404           0 :         if (r < 0)
     405           0 :                 return log_lldp_errno(r, "Failed to restart timer: %m");
     406             : 
     407           0 :         return 0;
     408             : }
     409             : 
     410          11 : static int lldp_start_timer(sd_lldp *lldp, sd_lldp_neighbor *neighbor) {
     411             :         sd_lldp_neighbor *n;
     412             : 
     413          11 :         assert(lldp);
     414             : 
     415          11 :         if (neighbor)
     416           8 :                 lldp_neighbor_start_ttl(neighbor);
     417             : 
     418          11 :         n = prioq_peek(lldp->neighbor_by_expiry);
     419          11 :         if (!n)
     420           0 :                 return event_source_disable(lldp->timer_event_source);
     421             : 
     422          11 :         if (!lldp->event)
     423           0 :                 return 0;
     424             : 
     425          11 :         return event_reset_time(lldp->event, &lldp->timer_event_source,
     426             :                                 clock_boottime_or_monotonic(),
     427             :                                 n->until, 0,
     428             :                                 on_timer_event, lldp,
     429             :                                 lldp->event_priority, "lldp-timer", true);
     430             : }
     431             : 
     432           4 : _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
     433           4 :         sd_lldp_neighbor **l = NULL, *n;
     434             :         Iterator i;
     435           4 :         int k = 0, r;
     436             : 
     437           4 :         assert_return(lldp, -EINVAL);
     438           4 :         assert_return(ret, -EINVAL);
     439             : 
     440           4 :         if (hashmap_isempty(lldp->neighbor_by_id)) { /* Special shortcut */
     441           1 :                 *ret = NULL;
     442           1 :                 return 0;
     443             :         }
     444             : 
     445           3 :         l = new0(sd_lldp_neighbor*, hashmap_size(lldp->neighbor_by_id));
     446           3 :         if (!l)
     447           0 :                 return -ENOMEM;
     448             : 
     449           3 :         r = lldp_start_timer(lldp, NULL);
     450           3 :         if (r < 0) {
     451           0 :                 free(l);
     452           0 :                 return r;
     453             :         }
     454             : 
     455          11 :         HASHMAP_FOREACH(n, lldp->neighbor_by_id, i)
     456           8 :                 l[k++] = sd_lldp_neighbor_ref(n);
     457             : 
     458           3 :         assert((size_t) k == hashmap_size(lldp->neighbor_by_id));
     459             : 
     460             :         /* Return things in a stable order */
     461           3 :         typesafe_qsort(l, k, neighbor_compare_func);
     462           3 :         *ret = l;
     463             : 
     464           3 :         return k;
     465             : }
     466             : 
     467           0 : _public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
     468           0 :         assert_return(lldp, -EINVAL);
     469           0 :         assert_return(m <= 0, -EINVAL);
     470             : 
     471           0 :         lldp->neighbors_max = m;
     472           0 :         lldp_make_space(lldp, 0);
     473             : 
     474           0 :         return 0;
     475             : }
     476             : 
     477           0 : _public_ int sd_lldp_match_capabilities(sd_lldp *lldp, uint16_t mask) {
     478           0 :         assert_return(lldp, -EINVAL);
     479           0 :         assert_return(mask != 0, -EINVAL);
     480             : 
     481           0 :         lldp->capability_mask = mask;
     482             : 
     483           0 :         return 0;
     484             : }
     485             : 
     486           0 : _public_ int sd_lldp_set_filter_address(sd_lldp *lldp, const struct ether_addr *addr) {
     487           0 :         assert_return(lldp, -EINVAL);
     488             : 
     489             :         /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
     490             :          * that our own can be filtered out here. */
     491             : 
     492           0 :         if (addr)
     493           0 :                 lldp->filter_address = *addr;
     494             :         else
     495           0 :                 zero(lldp->filter_address);
     496             : 
     497           0 :         return 0;
     498             : }

Generated by: LCOV version 1.14