LCOV - code coverage report
Current view: top level - libsystemd-network - sd-ndisc.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 145 210 69.0 %
Date: 2019-08-23 13:36:53 Functions: 19 23 82.6 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 90 191 47.1 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : /***
       3                 :            :   Copyright © 2014 Intel Corporation. All rights reserved.
       4                 :            : ***/
       5                 :            : 
       6                 :            : #include <netinet/icmp6.h>
       7                 :            : #include <netinet/in.h>
       8                 :            : 
       9                 :            : #include "sd-ndisc.h"
      10                 :            : 
      11                 :            : #include "alloc-util.h"
      12                 :            : #include "event-util.h"
      13                 :            : #include "fd-util.h"
      14                 :            : #include "icmp6-util.h"
      15                 :            : #include "in-addr-util.h"
      16                 :            : #include "memory-util.h"
      17                 :            : #include "ndisc-internal.h"
      18                 :            : #include "ndisc-router.h"
      19                 :            : #include "random-util.h"
      20                 :            : #include "socket-util.h"
      21                 :            : #include "string-table.h"
      22                 :            : #include "string-util.h"
      23                 :            : 
      24                 :            : #define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
      25                 :            : 
      26                 :            : static const char * const ndisc_event_table[_SD_NDISC_EVENT_MAX] = {
      27                 :            :         [SD_NDISC_EVENT_TIMEOUT] = "timeout",
      28                 :            :         [SD_NDISC_EVENT_ROUTER] = "router",
      29                 :            : };
      30                 :            : 
      31   [ +  +  +  + ]:         52 : DEFINE_STRING_TABLE_LOOKUP(ndisc_event, sd_ndisc_event);
      32                 :            : 
      33                 :         20 : static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
      34         [ -  + ]:         20 :         assert(ndisc);
      35   [ +  -  -  + ]:         20 :         assert(event >= 0 && event < _SD_NDISC_EVENT_MAX);
      36                 :            : 
      37         [ -  + ]:         20 :         if (!ndisc->callback) {
      38                 :          0 :                 log_ndisc("Received '%s' event.", ndisc_event_to_string(event));
      39                 :          0 :                 return;
      40                 :            :         }
      41                 :            : 
      42                 :         20 :         log_ndisc("Invoking callback for '%s' event.", ndisc_event_to_string(event));
      43                 :         20 :         ndisc->callback(ndisc, event, rt, ndisc->userdata);
      44                 :            : }
      45                 :            : 
      46                 :          4 : _public_ int sd_ndisc_set_callback(
      47                 :            :                 sd_ndisc *nd,
      48                 :            :                 sd_ndisc_callback_t callback,
      49                 :            :                 void *userdata) {
      50                 :            : 
      51   [ -  +  -  + ]:          4 :         assert_return(nd, -EINVAL);
      52                 :            : 
      53                 :          4 :         nd->callback = callback;
      54                 :          4 :         nd->userdata = userdata;
      55                 :            : 
      56                 :          4 :         return 0;
      57                 :            : }
      58                 :            : 
      59                 :          8 : _public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
      60   [ -  +  -  + ]:          8 :         assert_return(nd, -EINVAL);
      61   [ -  +  -  + ]:          8 :         assert_return(ifindex > 0, -EINVAL);
      62   [ -  +  -  + ]:          8 :         assert_return(nd->fd < 0, -EBUSY);
      63                 :            : 
      64                 :          8 :         nd->ifindex = ifindex;
      65                 :          8 :         return 0;
      66                 :            : }
      67                 :            : 
      68                 :          8 : _public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
      69   [ -  +  -  + ]:          8 :         assert_return(nd, -EINVAL);
      70                 :            : 
      71         [ +  - ]:          8 :         if (mac_addr)
      72                 :          8 :                 nd->mac_addr = *mac_addr;
      73                 :            :         else
      74         [ #  # ]:          0 :                 zero(nd->mac_addr);
      75                 :            : 
      76                 :          8 :         return 0;
      77                 :            : }
      78                 :            : 
      79                 :          8 : _public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
      80                 :            :         int r;
      81                 :            : 
      82   [ -  +  -  + ]:          8 :         assert_return(nd, -EINVAL);
      83   [ -  +  -  + ]:          8 :         assert_return(nd->fd < 0, -EBUSY);
      84   [ -  +  -  + ]:          8 :         assert_return(!nd->event, -EBUSY);
      85                 :            : 
      86         [ +  - ]:          8 :         if (event)
      87                 :          8 :                 nd->event = sd_event_ref(event);
      88                 :            :         else {
      89                 :          0 :                 r = sd_event_default(&nd->event);
      90         [ #  # ]:          0 :                 if (r < 0)
      91                 :          0 :                         return 0;
      92                 :            :         }
      93                 :            : 
      94                 :          8 :         nd->event_priority = priority;
      95                 :            : 
      96                 :          8 :         return 0;
      97                 :            : }
      98                 :            : 
      99                 :          8 : _public_ int sd_ndisc_detach_event(sd_ndisc *nd) {
     100                 :            : 
     101   [ -  +  -  + ]:          8 :         assert_return(nd, -EINVAL);
     102   [ -  +  -  + ]:          8 :         assert_return(nd->fd < 0, -EBUSY);
     103                 :            : 
     104                 :          8 :         nd->event = sd_event_unref(nd->event);
     105                 :          8 :         return 0;
     106                 :            : }
     107                 :            : 
     108                 :          0 : _public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
     109   [ #  #  #  # ]:          0 :         assert_return(nd, NULL);
     110                 :            : 
     111                 :          0 :         return nd->event;
     112                 :            : }
     113                 :            : 
     114                 :         12 : static void ndisc_reset(sd_ndisc *nd) {
     115         [ -  + ]:         12 :         assert(nd);
     116                 :            : 
     117                 :         12 :         (void) event_source_disable(nd->timeout_event_source);
     118                 :         12 :         (void) event_source_disable(nd->timeout_no_ra);
     119                 :         12 :         nd->retransmit_time = 0;
     120                 :         12 :         nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
     121                 :         12 :         nd->fd = safe_close(nd->fd);
     122                 :         12 : }
     123                 :            : 
     124                 :          8 : static sd_ndisc *ndisc_free(sd_ndisc *nd) {
     125         [ -  + ]:          8 :         assert(nd);
     126                 :            : 
     127                 :          8 :         nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
     128                 :          8 :         nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
     129                 :            : 
     130                 :          8 :         ndisc_reset(nd);
     131                 :          8 :         sd_ndisc_detach_event(nd);
     132                 :          8 :         return mfree(nd);
     133                 :            : }
     134                 :            : 
     135   [ +  +  -  +  :         36 : DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc, sd_ndisc, ndisc_free);
                   -  + ]
     136                 :            : 
     137                 :          8 : _public_ int sd_ndisc_new(sd_ndisc **ret) {
     138                 :          8 :         _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
     139                 :            : 
     140   [ -  +  -  + ]:          8 :         assert_return(ret, -EINVAL);
     141                 :            : 
     142                 :          8 :         nd = new(sd_ndisc, 1);
     143         [ -  + ]:          8 :         if (!nd)
     144                 :          0 :                 return -ENOMEM;
     145                 :            : 
     146                 :          8 :         *nd = (sd_ndisc) {
     147                 :            :                 .n_ref = 1,
     148                 :            :                 .fd = -1,
     149                 :            :         };
     150                 :            : 
     151                 :          8 :         *ret = TAKE_PTR(nd);
     152                 :            : 
     153                 :          8 :         return 0;
     154                 :            : }
     155                 :            : 
     156                 :          4 : _public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
     157   [ -  +  -  + ]:          4 :         assert_return(nd, -EINVAL);
     158   [ -  +  -  + ]:          4 :         assert_return(mtu, -EINVAL);
     159                 :            : 
     160         [ +  - ]:          4 :         if (nd->mtu == 0)
     161                 :          4 :                 return -ENODATA;
     162                 :            : 
     163                 :          0 :         *mtu = nd->mtu;
     164                 :          0 :         return 0;
     165                 :            : }
     166                 :            : 
     167                 :          0 : _public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) {
     168   [ #  #  #  # ]:          0 :         assert_return(nd, -EINVAL);
     169   [ #  #  #  # ]:          0 :         assert_return(ret, -EINVAL);
     170                 :            : 
     171         [ #  # ]:          0 :         if (nd->hop_limit == 0)
     172                 :          0 :                 return -ENODATA;
     173                 :            : 
     174                 :          0 :         *ret = nd->hop_limit;
     175                 :          0 :         return 0;
     176                 :            : }
     177                 :            : 
     178                 :         20 : static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
     179                 :            :         int r;
     180                 :            : 
     181         [ -  + ]:         20 :         assert(nd);
     182         [ -  + ]:         20 :         assert(rt);
     183                 :            : 
     184                 :         20 :         r = ndisc_router_parse(rt);
     185         [ -  + ]:         20 :         if (r == -EBADMSG) /* Bad packet */
     186                 :          0 :                 return 0;
     187         [ -  + ]:         20 :         if (r < 0)
     188                 :          0 :                 return 0;
     189                 :            : 
     190                 :            :         /* Update global variables we keep */
     191         [ -  + ]:         20 :         if (rt->mtu > 0)
     192                 :          0 :                 nd->mtu = rt->mtu;
     193         [ +  - ]:         20 :         if (rt->hop_limit > 0)
     194                 :         20 :                 nd->hop_limit = rt->hop_limit;
     195                 :            : 
     196   [ +  -  -  +  :         20 :         log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec",
             +  +  +  + ]
     197                 :            :                   rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none",
     198                 :            :                   rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium",
     199                 :            :                   rt->lifetime);
     200                 :            : 
     201                 :         20 :         ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt);
     202                 :         20 :         return 0;
     203                 :            : }
     204                 :            : 
     205                 :         20 : static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     206                 :         20 :         _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
     207                 :         20 :         sd_ndisc *nd = userdata;
     208                 :            :         ssize_t buflen;
     209                 :            :         int r;
     210                 :         20 :         _cleanup_free_ char *addr = NULL;
     211                 :            : 
     212         [ -  + ]:         20 :         assert(s);
     213         [ -  + ]:         20 :         assert(nd);
     214         [ -  + ]:         20 :         assert(nd->event);
     215                 :            : 
     216                 :         20 :         buflen = next_datagram_size_fd(fd);
     217         [ -  + ]:         20 :         if (buflen < 0)
     218                 :          0 :                 return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m");
     219                 :            : 
     220                 :         20 :         rt = ndisc_router_new(buflen);
     221         [ -  + ]:         20 :         if (!rt)
     222                 :          0 :                 return -ENOMEM;
     223                 :            : 
     224                 :         20 :         r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address,
     225                 :         20 :                      &rt->timestamp);
     226         [ -  + ]:         20 :         if (r < 0) {
     227   [ #  #  #  #  :          0 :                 switch (r) {
                      # ]
     228                 :          0 :                 case -EADDRNOTAVAIL:
     229                 :          0 :                         (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &rt->address, &addr);
     230                 :          0 :                         log_ndisc("Received RA from non-link-local address %s. Ignoring", addr);
     231                 :          0 :                         break;
     232                 :            : 
     233                 :          0 :                 case -EMULTIHOP:
     234                 :          0 :                         log_ndisc("Received RA with invalid hop limit. Ignoring.");
     235                 :          0 :                         break;
     236                 :            : 
     237                 :          0 :                 case -EPFNOSUPPORT:
     238                 :          0 :                         log_ndisc("Received invalid source address from ICMPv6 socket. Ignoring.");
     239                 :          0 :                         break;
     240                 :            : 
     241                 :          0 :                 case -EAGAIN: /* ignore spurious wakeups */
     242                 :          0 :                         break;
     243                 :            : 
     244                 :          0 :                 default:
     245                 :          0 :                         log_ndisc_errno(r, "Unexpected error while reading from ICMPv6, ignoring: %m");
     246                 :          0 :                         break;
     247                 :            :                 }
     248                 :            : 
     249                 :          0 :                 return 0;
     250                 :            :         }
     251                 :            : 
     252                 :         20 :         (void) event_source_disable(nd->timeout_event_source);
     253                 :            : 
     254                 :         20 :         return ndisc_handle_datagram(nd, rt);
     255                 :            : }
     256                 :            : 
     257                 :         84 : static usec_t ndisc_timeout_compute_random(usec_t val) {
     258                 :            :         /* compute a time that is random within ±10% of the given value */
     259                 :        252 :         return val - val / 10 +
     260                 :         84 :                 (random_u64() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
     261                 :            : }
     262                 :            : 
     263                 :         84 : static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
     264                 :            :         char time_string[FORMAT_TIMESPAN_MAX];
     265                 :         84 :         sd_ndisc *nd = userdata;
     266                 :            :         usec_t time_now;
     267                 :            :         int r;
     268                 :            : 
     269         [ -  + ]:         84 :         assert(s);
     270         [ -  + ]:         84 :         assert(nd);
     271         [ -  + ]:         84 :         assert(nd->event);
     272                 :            : 
     273         [ -  + ]:         84 :         assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
     274                 :            : 
     275         [ +  + ]:         84 :         if (!nd->retransmit_time)
     276                 :          8 :                 nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL);
     277                 :            :         else {
     278         [ +  + ]:         76 :                 if (nd->retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2)
     279                 :         40 :                         nd->retransmit_time = ndisc_timeout_compute_random(NDISC_MAX_ROUTER_SOLICITATION_INTERVAL);
     280                 :            :                 else
     281                 :         36 :                         nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time);
     282                 :            :         }
     283                 :            : 
     284                 :         84 :         r = event_reset_time(nd->event, &nd->timeout_event_source,
     285                 :            :                              clock_boottime_or_monotonic(),
     286                 :         84 :                              time_now + nd->retransmit_time, 10 * USEC_PER_MSEC,
     287                 :            :                              ndisc_timeout, nd,
     288                 :         84 :                              nd->event_priority, "ndisc-timeout-no-ra", true);
     289         [ -  + ]:         84 :         if (r < 0)
     290                 :          0 :                 goto fail;
     291                 :            : 
     292                 :         84 :         r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
     293         [ -  + ]:         84 :         if (r < 0) {
     294                 :          0 :                 log_ndisc_errno(r, "Error sending Router Solicitation: %m");
     295                 :          0 :                 goto fail;
     296                 :            :         }
     297                 :            : 
     298                 :         84 :         log_ndisc("Sent Router Solicitation, next solicitation in %s",
     299                 :            :                   format_timespan(time_string, FORMAT_TIMESPAN_MAX,
     300                 :            :                                   nd->retransmit_time, USEC_PER_SEC));
     301                 :            : 
     302                 :         84 :         return 0;
     303                 :            : 
     304                 :          0 : fail:
     305                 :          0 :         (void) sd_ndisc_stop(nd);
     306                 :          0 :         return 0;
     307                 :            : }
     308                 :            : 
     309                 :          0 : static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata) {
     310                 :          0 :         sd_ndisc *nd = userdata;
     311                 :            : 
     312         [ #  # ]:          0 :         assert(s);
     313         [ #  # ]:          0 :         assert(nd);
     314                 :            : 
     315                 :          0 :         log_ndisc("No RA received before link confirmation timeout");
     316                 :            : 
     317                 :          0 :         (void) event_source_disable(nd->timeout_no_ra);
     318                 :          0 :         ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
     319                 :            : 
     320                 :          0 :         return 0;
     321                 :            : }
     322                 :            : 
     323                 :          8 : _public_ int sd_ndisc_stop(sd_ndisc *nd) {
     324   [ -  +  -  + ]:          8 :         assert_return(nd, -EINVAL);
     325                 :            : 
     326         [ +  + ]:          8 :         if (nd->fd < 0)
     327                 :          4 :                 return 0;
     328                 :            : 
     329                 :          4 :         log_ndisc("Stopping IPv6 Router Solicitation client");
     330                 :            : 
     331                 :          4 :         ndisc_reset(nd);
     332                 :          4 :         return 1;
     333                 :            : }
     334                 :            : 
     335                 :         12 : _public_ int sd_ndisc_start(sd_ndisc *nd) {
     336                 :            :         int r;
     337                 :            :         usec_t time_now;
     338                 :            : 
     339   [ -  +  -  + ]:         12 :         assert_return(nd, -EINVAL);
     340   [ -  +  -  + ]:         12 :         assert_return(nd->event, -EINVAL);
     341   [ -  +  -  + ]:         12 :         assert_return(nd->ifindex > 0, -EINVAL);
     342                 :            : 
     343         [ -  + ]:         12 :         if (nd->fd >= 0)
     344                 :          0 :                 return 0;
     345                 :            : 
     346         [ -  + ]:         12 :         assert(!nd->recv_event_source);
     347                 :            : 
     348                 :         12 :         r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
     349         [ -  + ]:         12 :         if (r < 0)
     350                 :          0 :                 goto fail;
     351                 :            : 
     352                 :         12 :         nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
     353         [ -  + ]:         12 :         if (nd->fd < 0)
     354                 :          0 :                 return nd->fd;
     355                 :            : 
     356                 :         12 :         r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd);
     357         [ -  + ]:         12 :         if (r < 0)
     358                 :          0 :                 goto fail;
     359                 :            : 
     360                 :         12 :         r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority);
     361         [ -  + ]:         12 :         if (r < 0)
     362                 :          0 :                 goto fail;
     363                 :            : 
     364                 :         12 :         (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
     365                 :            : 
     366                 :         12 :         r = event_reset_time(nd->event, &nd->timeout_event_source,
     367                 :            :                              clock_boottime_or_monotonic(),
     368                 :            :                              0, 0,
     369                 :            :                              ndisc_timeout, nd,
     370                 :         12 :                              nd->event_priority, "ndisc-timeout", true);
     371         [ -  + ]:         12 :         if (r < 0)
     372                 :          0 :                 goto fail;
     373                 :            : 
     374                 :         12 :         r = event_reset_time(nd->event, &nd->timeout_no_ra,
     375                 :            :                              clock_boottime_or_monotonic(),
     376                 :            :                              time_now + NDISC_TIMEOUT_NO_RA_USEC, 10 * USEC_PER_MSEC,
     377                 :            :                              ndisc_timeout_no_ra, nd,
     378                 :         12 :                              nd->event_priority, "ndisc-timeout-no-ra", true);
     379         [ -  + ]:         12 :         if (r < 0)
     380                 :          0 :                 goto fail;
     381                 :            : 
     382                 :         12 :         log_ndisc("Started IPv6 Router Solicitation client");
     383                 :         12 :         return 1;
     384                 :            : 
     385                 :          0 : fail:
     386                 :          0 :         ndisc_reset(nd);
     387                 :          0 :         return r;
     388                 :            : }

Generated by: LCOV version 1.14