LCOV - code coverage report
Current view: top level - libsystemd-network - sd-icmp6-nd.c (source / functions) Hit Total Coverage
Test: systemd test coverage Lines: 244 330 73.9 %
Date: 2015-07-29 18:47:03 Functions: 23 28 82.1 %

          Line data    Source code
       1             : /***
       2             :   This file is part of systemd.
       3             : 
       4             :   Copyright (C) 2014 Intel Corporation. All rights reserved.
       5             : 
       6             :   systemd is free software; you can redistribute it and/or modify it
       7             :   under the terms of the GNU Lesser General Public License as published by
       8             :   the Free Software Foundation; either version 2.1 of the License, or
       9             :   (at your option) any later version.
      10             : 
      11             :   systemd is distributed in the hope that it will be useful, but
      12             :   WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
      14             :   Lesser General Public License for more details.
      15             : 
      16             :   You should have received a copy of the GNU Lesser General Public License
      17             :   along with systemd; If not, see <http://www.gnu.org/licenses/>.
      18             : ***/
      19             : 
      20             : #include <netinet/icmp6.h>
      21             : #include <netinet/ip6.h>
      22             : #include <string.h>
      23             : #include <stdbool.h>
      24             : #include <netinet/in.h>
      25             : #include <sys/ioctl.h>
      26             : 
      27             : #include "socket-util.h"
      28             : #include "refcnt.h"
      29             : #include "async.h"
      30             : 
      31             : #include "dhcp6-internal.h"
      32             : #include "sd-icmp6-nd.h"
      33             : 
      34             : #define ICMP6_ROUTER_SOLICITATION_INTERVAL      4 * USEC_PER_SEC
      35             : #define ICMP6_MAX_ROUTER_SOLICITATIONS          3
      36             : 
      37             : enum icmp6_nd_state {
      38             :         ICMP6_NEIGHBOR_DISCOVERY_IDLE           = 0,
      39             :         ICMP6_ROUTER_SOLICITATION_SENT          = 10,
      40             :         ICMP6_ROUTER_ADVERTISMENT_LISTEN        = 11,
      41             : };
      42             : 
      43             : #define IP6_MIN_MTU (unsigned)1280
      44             : #define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
      45             : #define ICMP6_OPT_LEN_UNITS 8
      46             : 
      47             : typedef struct ICMP6Prefix ICMP6Prefix;
      48             : 
      49             : struct ICMP6Prefix {
      50             :         RefCount n_ref;
      51             : 
      52             :         LIST_FIELDS(ICMP6Prefix, prefixes);
      53             : 
      54             :         uint8_t len;
      55             :         sd_event_source *timeout_valid;
      56             :         struct in6_addr addr;
      57             : };
      58             : 
      59             : struct sd_icmp6_nd {
      60             :         RefCount n_ref;
      61             : 
      62             :         enum icmp6_nd_state state;
      63             :         sd_event *event;
      64             :         int event_priority;
      65             :         int index;
      66             :         struct ether_addr mac_addr;
      67             :         uint32_t mtu;
      68             :         ICMP6Prefix *expired_prefix;
      69             :         LIST_HEAD(ICMP6Prefix, prefixes);
      70             :         int fd;
      71             :         sd_event_source *recv;
      72             :         sd_event_source *timeout;
      73             :         int nd_sent;
      74             :         sd_icmp6_nd_callback_t callback;
      75             :         void *userdata;
      76             : };
      77             : 
      78             : #define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__)
      79             : 
      80           4 : static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) {
      81           4 :         if (prefix && REFCNT_DEC(prefix->n_ref) <= 0) {
      82           4 :                 prefix->timeout_valid =
      83           4 :                         sd_event_source_unref(prefix->timeout_valid);
      84             : 
      85           4 :                 free(prefix);
      86             :         }
      87             : 
      88           4 :         return NULL;
      89             : }
      90             : 
      91           4 : static int icmp6_prefix_new(ICMP6Prefix **ret) {
      92           8 :         _cleanup_free_ ICMP6Prefix *prefix = NULL;
      93             : 
      94           4 :         assert(ret);
      95             : 
      96           4 :         prefix = new0(ICMP6Prefix, 1);
      97           4 :         if (!prefix)
      98           0 :                 return -ENOMEM;
      99             : 
     100           4 :         prefix->n_ref = REFCNT_INIT;
     101           4 :         LIST_INIT(prefixes, prefix);
     102             : 
     103           4 :         *ret = prefix;
     104           4 :         prefix = NULL;
     105             : 
     106           4 :         return 0;
     107             : }
     108             : 
     109           5 : static void icmp6_nd_notify(sd_icmp6_nd *nd, int event)
     110             : {
     111           5 :         if (nd->callback)
     112           5 :                 nd->callback(nd, event, nd->userdata);
     113           5 : }
     114             : 
     115           3 : int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback,
     116             :                              void *userdata) {
     117           3 :         assert(nd);
     118             : 
     119           3 :         nd->callback = callback;
     120           3 :         nd->userdata = userdata;
     121             : 
     122           3 :         return 0;
     123             : }
     124             : 
     125           2 : int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) {
     126           2 :         assert(nd);
     127           2 :         assert(interface_index >= -1);
     128             : 
     129           2 :         nd->index = interface_index;
     130             : 
     131           2 :         return 0;
     132             : }
     133             : 
     134           2 : int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) {
     135           2 :         assert(nd);
     136             : 
     137           2 :         if (mac_addr)
     138           2 :                 memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
     139             :         else
     140           0 :                 zero(nd->mac_addr);
     141             : 
     142           2 :         return 0;
     143             : 
     144             : }
     145             : 
     146           2 : int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) {
     147             :         int r;
     148             : 
     149           2 :         assert_return(nd, -EINVAL);
     150           2 :         assert_return(!nd->event, -EBUSY);
     151             : 
     152           2 :         if (event)
     153           2 :                 nd->event = sd_event_ref(event);
     154             :         else {
     155           0 :                 r = sd_event_default(&nd->event);
     156           0 :                 if (r < 0)
     157           0 :                         return 0;
     158             :         }
     159             : 
     160           2 :         nd->event_priority = priority;
     161             : 
     162           2 :         return 0;
     163             : }
     164             : 
     165           2 : int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) {
     166           2 :         assert_return(nd, -EINVAL);
     167             : 
     168           2 :         nd->event = sd_event_unref(nd->event);
     169             : 
     170           2 :         return 0;
     171             : }
     172             : 
     173           0 : sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) {
     174           0 :         assert(nd);
     175             : 
     176           0 :         return nd->event;
     177             : }
     178             : 
     179           0 : sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) {
     180           0 :         assert (nd);
     181             : 
     182           0 :         assert_se(REFCNT_INC(nd->n_ref) >= 2);
     183             : 
     184           0 :         return nd;
     185             : }
     186             : 
     187           5 : static int icmp6_nd_init(sd_icmp6_nd *nd) {
     188           5 :         assert(nd);
     189             : 
     190           5 :         nd->recv = sd_event_source_unref(nd->recv);
     191           5 :         nd->fd = asynchronous_close(nd->fd);
     192           5 :         nd->timeout = sd_event_source_unref(nd->timeout);
     193             : 
     194           5 :         return 0;
     195             : }
     196             : 
     197           6 : sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) {
     198           6 :         if (nd && REFCNT_DEC(nd->n_ref) == 0) {
     199             :                 ICMP6Prefix *prefix, *p;
     200             : 
     201           2 :                 icmp6_nd_init(nd);
     202           2 :                 sd_icmp6_nd_detach_event(nd);
     203             : 
     204           6 :                 LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
     205           4 :                         LIST_REMOVE(prefixes, nd->prefixes, prefix);
     206             : 
     207           4 :                         prefix = icmp6_prefix_unref(prefix);
     208             :                 }
     209             : 
     210           2 :                 free(nd);
     211             :         }
     212             : 
     213           6 :         return NULL;
     214             : }
     215             : 
     216           2 : DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref);
     217             : #define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp)
     218             : 
     219           2 : int sd_icmp6_nd_new(sd_icmp6_nd **ret) {
     220           4 :         _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL;
     221             : 
     222           2 :         assert(ret);
     223             : 
     224           2 :         nd = new0(sd_icmp6_nd, 1);
     225           2 :         if (!nd)
     226           0 :                 return -ENOMEM;
     227             : 
     228           2 :         nd->n_ref = REFCNT_INIT;
     229             : 
     230           2 :         nd->index = -1;
     231           2 :         nd->fd = -1;
     232             : 
     233           2 :         LIST_HEAD_INIT(nd->prefixes);
     234             : 
     235           2 :         *ret = nd;
     236           2 :         nd = NULL;
     237             : 
     238           2 :         return 0;
     239             : }
     240             : 
     241           1 : int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) {
     242           1 :         assert_return(nd, -EINVAL);
     243           1 :         assert_return(mtu, -EINVAL);
     244             : 
     245           1 :         if (nd->mtu == 0)
     246           1 :                 return -ENOMSG;
     247             : 
     248           0 :         *mtu = nd->mtu;
     249             : 
     250           0 :         return 0;
     251             : }
     252             : 
     253           0 : static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec,
     254             :                                    void *userdata) {
     255           0 :         sd_icmp6_nd *nd = userdata;
     256             :         ICMP6Prefix *prefix, *p;
     257             : 
     258           0 :         assert(nd);
     259             : 
     260           0 :         LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
     261           0 :                 if (prefix->timeout_valid != s)
     262           0 :                         continue;
     263             : 
     264           0 :                 log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ADDRESS_FORMAT_STR"/%d",
     265             :                              SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
     266             :                              prefix->len);
     267             : 
     268           0 :                 LIST_REMOVE(prefixes, nd->prefixes, prefix);
     269             : 
     270           0 :                 nd->expired_prefix = prefix;
     271           0 :                 icmp6_nd_notify(nd,
     272             :                                 ICMP6_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED);
     273           0 :                 nd->expired_prefix = NULL;
     274             : 
     275           0 :                 prefix = icmp6_prefix_unref(prefix);
     276             : 
     277           0 :                 break;
     278             :         }
     279             : 
     280           0 :         return 0;
     281             : }
     282             : 
     283           6 : static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd,
     284             :                                        ICMP6Prefix *prefix,
     285             :                                        usec_t valid) {
     286             :         usec_t time_now;
     287             :         int r;
     288             : 
     289           6 :         assert_return(prefix, -EINVAL);
     290             : 
     291           6 :         r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
     292           6 :         if (r < 0)
     293           0 :                 return r;
     294             : 
     295           6 :         prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid);
     296             : 
     297           6 :         r = sd_event_add_time(nd->event, &prefix->timeout_valid,
     298             :                         clock_boottime_or_monotonic(), time_now + valid,
     299             :                         USEC_PER_SEC, icmp6_ra_prefix_timeout, nd);
     300           6 :         if (r < 0)
     301           0 :                 goto error;
     302             : 
     303           6 :         r = sd_event_source_set_priority(prefix->timeout_valid,
     304           6 :                                         nd->event_priority);
     305           6 :         if (r < 0)
     306           0 :                 goto error;
     307             : 
     308           6 :         r = sd_event_source_set_description(prefix->timeout_valid,
     309             :                                         "icmp6-prefix-timeout");
     310             : 
     311             : error:
     312           6 :         if (r < 0)
     313           0 :                 prefix->timeout_valid =
     314           0 :                         sd_event_source_unref(prefix->timeout_valid);
     315             : 
     316           6 :         return r;
     317             : }
     318             : 
     319          31 : static int icmp6_prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
     320             :                               const struct in6_addr *addr,
     321             :                               uint8_t addr_prefixlen) {
     322             :         uint8_t bytes, mask, len;
     323             : 
     324          31 :         assert_return(prefix, -EINVAL);
     325          31 :         assert_return(addr, -EINVAL);
     326             : 
     327          31 :         len = MIN(prefixlen, addr_prefixlen);
     328             : 
     329          31 :         bytes = len / 8;
     330          31 :         mask = 0xff << (8 - len % 8);
     331             : 
     332          42 :         if (memcmp(prefix, addr, bytes) != 0 ||
     333          11 :             (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
     334          22 :                 return -EADDRNOTAVAIL;
     335             : 
     336           9 :         return 0;
     337             : }
     338             : 
     339          16 : static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr,
     340             :                                  uint8_t addr_len, ICMP6Prefix **result) {
     341             :         ICMP6Prefix *prefix;
     342             : 
     343          38 :         LIST_FOREACH(prefixes, prefix, head) {
     344          31 :                 if (icmp6_prefix_match(&prefix->addr, prefix->len, addr,
     345             :                                        addr_len) >= 0) {
     346           9 :                         *result = prefix;
     347           9 :                         return 0;
     348             :                 }
     349             :         }
     350             : 
     351           7 :         return -EADDRNOTAVAIL;
     352             : }
     353             : 
     354           0 : int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen,
     355             :                           struct in6_addr *addr) {
     356           0 :         return icmp6_prefix_match(prefix, prefixlen, addr,
     357             :                                   sizeof(addr->s6_addr) * 8);
     358             : }
     359             : 
     360          10 : int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr,
     361             :                               uint8_t *prefixlen) {
     362             :         int r;
     363             :         ICMP6Prefix *prefix;
     364             : 
     365          10 :         assert_return(nd, -EINVAL);
     366          10 :         assert_return(addr, -EINVAL);
     367          10 :         assert_return(prefixlen, -EINVAL);
     368             : 
     369          10 :         r = icmp6_ra_prefix_match(nd->prefixes, addr,
     370             :                                   sizeof(addr->s6_addr) * 8, &prefix);
     371          10 :         if (r < 0)
     372           3 :                 return r;
     373             : 
     374           7 :         *prefixlen = prefix->len;
     375             : 
     376           7 :         return 0;
     377             : }
     378             : 
     379           0 : int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr,
     380             :                                 uint8_t *prefixlen)
     381             : {
     382           0 :         assert_return(nd, -EINVAL);
     383           0 :         assert_return(addr, -EINVAL);
     384           0 :         assert_return(prefixlen, -EINVAL);
     385             : 
     386           0 :         if (!nd->expired_prefix)
     387           0 :                 return -EADDRNOTAVAIL;
     388             : 
     389           0 :         *addr = &nd->expired_prefix->addr;
     390           0 :         *prefixlen = nd->expired_prefix->len;
     391             : 
     392           0 :         return 0;
     393             : }
     394             : 
     395           7 : static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len,
     396             :                                   const struct nd_opt_prefix_info *prefix_opt) {
     397             :         int r;
     398             :         ICMP6Prefix *prefix;
     399             :         uint32_t lifetime;
     400             :         char time_string[FORMAT_TIMESPAN_MAX];
     401             : 
     402           7 :         assert_return(nd, -EINVAL);
     403           7 :         assert_return(prefix_opt, -EINVAL);
     404             : 
     405           7 :         if (len < prefix_opt->nd_opt_pi_len)
     406           0 :                 return -ENOMSG;
     407             : 
     408           7 :         if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK))
     409           1 :                 return 0;
     410             : 
     411           6 :         lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time);
     412             : 
     413           6 :         r = icmp6_ra_prefix_match(nd->prefixes,
     414             :                                   &prefix_opt->nd_opt_pi_prefix,
     415           6 :                                   prefix_opt->nd_opt_pi_prefix_len, &prefix);
     416             : 
     417           6 :         if (r < 0 && r != -EADDRNOTAVAIL)
     418           0 :                 return r;
     419             : 
     420             :         /* if router advertisment prefix valid timeout is zero, the timeout
     421             :            callback will be called immediately to clean up the prefix */
     422             : 
     423           6 :         if (r == -EADDRNOTAVAIL) {
     424           4 :                 r = icmp6_prefix_new(&prefix);
     425           4 :                 if (r < 0)
     426           0 :                         return r;
     427             : 
     428           4 :                 prefix->len = prefix_opt->nd_opt_pi_prefix_len;
     429             : 
     430           4 :                 memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
     431             :                         sizeof(prefix->addr));
     432             : 
     433           4 :                 log_icmp6_nd(nd, "New prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
     434             :                              SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
     435             :                              prefix->len, lifetime,
     436             :                              format_timespan(time_string, FORMAT_TIMESPAN_MAX,
     437             :                                              lifetime * USEC_PER_SEC, 0));
     438             : 
     439           4 :                 LIST_PREPEND(prefixes, nd->prefixes, prefix);
     440             : 
     441             :         } else {
     442           2 :                 if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
     443             :                         uint8_t prefixlen;
     444             : 
     445           1 :                         prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
     446             : 
     447           1 :                         log_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d",
     448             :                                      prefix->len,
     449             :                                      prefix_opt->nd_opt_pi_prefix_len,
     450             :                                      prefixlen);
     451             : 
     452           1 :                         prefix->len = prefixlen;
     453             :                 }
     454             : 
     455           2 :                 log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
     456             :                              SD_ICMP6_ADDRESS_FORMAT_VAL(prefix->addr),
     457             :                              prefix->len, lifetime,
     458             :                              format_timespan(time_string, FORMAT_TIMESPAN_MAX,
     459             :                                              lifetime * USEC_PER_SEC, 0));
     460             :         }
     461             : 
     462           6 :         r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC);
     463             : 
     464           6 :         return r;
     465             : }
     466             : 
     467           4 : static int icmp6_ra_parse(sd_icmp6_nd *nd, struct nd_router_advert *ra,
     468             :                           ssize_t len) {
     469             :         void *opt;
     470             :         struct nd_opt_hdr *opt_hdr;
     471             : 
     472           4 :         assert_return(nd, -EINVAL);
     473           4 :         assert_return(ra, -EINVAL);
     474             : 
     475           4 :         len -= sizeof(*ra);
     476           4 :         if (len < ICMP6_OPT_LEN_UNITS) {
     477           0 :                 log_icmp6_nd(nd, "Router Advertisement below minimum length");
     478             : 
     479           0 :                 return -ENOMSG;
     480             :         }
     481             : 
     482           4 :         opt = ra + 1;
     483           4 :         opt_hdr = opt;
     484             : 
     485          24 :         while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS) {
     486             :                 struct nd_opt_mtu *opt_mtu;
     487             :                 uint32_t mtu;
     488             :                 struct nd_opt_prefix_info *opt_prefix;
     489             : 
     490          16 :                 if (opt_hdr->nd_opt_len == 0)
     491           0 :                         return -ENOMSG;
     492             : 
     493          16 :                 switch (opt_hdr->nd_opt_type) {
     494             :                 case ND_OPT_MTU:
     495           0 :                         opt_mtu = opt;
     496             : 
     497           0 :                         mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
     498             : 
     499           0 :                         if (mtu != nd->mtu) {
     500           0 :                                 nd->mtu = MAX(mtu, IP6_MIN_MTU);
     501             : 
     502           0 :                                 log_icmp6_nd(nd, "Router Advertisement link MTU %d using %d",
     503             :                                              mtu, nd->mtu);
     504             :                         }
     505             : 
     506           0 :                         break;
     507             : 
     508             :                 case ND_OPT_PREFIX_INFORMATION:
     509           7 :                         opt_prefix = opt;
     510             : 
     511           7 :                         icmp6_ra_prefix_update(nd, len, opt_prefix);
     512             : 
     513           7 :                         break;
     514             :                 }
     515             : 
     516          16 :                 len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS;
     517          16 :                 opt = (void *)((char *)opt +
     518          16 :                         opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS);
     519          16 :                 opt_hdr = opt;
     520             :         }
     521             : 
     522           4 :         if (len > 0)
     523           0 :                 log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
     524             : 
     525           4 :         return 0;
     526             : }
     527             : 
     528           5 : static int icmp6_router_advertisment_recv(sd_event_source *s, int fd,
     529             :                                           uint32_t revents, void *userdata)
     530             : {
     531           5 :         sd_icmp6_nd *nd = userdata;
     532           5 :         int r, buflen = 0;
     533             :         ssize_t len;
     534          10 :         _cleanup_free_ struct nd_router_advert *ra = NULL;
     535           5 :         int event = ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE;
     536             : 
     537           5 :         assert(s);
     538           5 :         assert(nd);
     539           5 :         assert(nd->event);
     540             : 
     541           5 :         r = ioctl(fd, FIONREAD, &buflen);
     542           5 :         if (r < 0 || buflen <= 0)
     543           0 :                 buflen = ICMP6_ND_RECV_SIZE;
     544             : 
     545           5 :         ra = malloc(buflen);
     546           5 :         if (!ra)
     547           0 :                 return -ENOMEM;
     548             : 
     549           5 :         len = read(fd, ra, buflen);
     550           5 :         if (len < 0) {
     551           0 :                 log_icmp6_nd(nd, "Could not receive message from UDP socket: %m");
     552           0 :                 return 0;
     553             :         }
     554             : 
     555           5 :         if (ra->nd_ra_type != ND_ROUTER_ADVERT)
     556           0 :                 return 0;
     557             : 
     558           5 :         if (ra->nd_ra_code != 0)
     559           0 :                 return 0;
     560             : 
     561           5 :         nd->timeout = sd_event_source_unref(nd->timeout);
     562             : 
     563           5 :         nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
     564             : 
     565           5 :         if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER )
     566           3 :                 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_OTHER;
     567             : 
     568           5 :         if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
     569           3 :                 event = ICMP6_EVENT_ROUTER_ADVERTISMENT_MANAGED;
     570             : 
     571           5 :         log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s",
     572             :                      ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none",
     573             :                      ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none");
     574             : 
     575           5 :         if (event != ICMP6_EVENT_ROUTER_ADVERTISMENT_NONE) {
     576           4 :                 r = icmp6_ra_parse(nd, ra, len);
     577           4 :                 if (r < 0) {
     578           0 :                         log_icmp6_nd(nd, "Could not parse Router Advertisement: %s",
     579             :                                      strerror(-r));
     580           0 :                         return 0;
     581             :                 }
     582             :         }
     583             : 
     584           5 :         icmp6_nd_notify(nd, event);
     585             : 
     586           5 :         return 0;
     587             : }
     588             : 
     589           3 : static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
     590             :                                              void *userdata)
     591             : {
     592           3 :         sd_icmp6_nd *nd = userdata;
     593             :         uint64_t time_now, next_timeout;
     594           3 :         struct ether_addr unset = { };
     595           3 :         struct ether_addr *addr = NULL;
     596             :         int r;
     597             : 
     598           3 :         assert(s);
     599           3 :         assert(nd);
     600           3 :         assert(nd->event);
     601             : 
     602           3 :         nd->timeout = sd_event_source_unref(nd->timeout);
     603             : 
     604           3 :         if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) {
     605           0 :                 icmp6_nd_notify(nd, ICMP6_EVENT_ROUTER_ADVERTISMENT_TIMEOUT);
     606           0 :                 nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN;
     607             :         } else {
     608           3 :                 if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr)))
     609           3 :                         addr = &nd->mac_addr;
     610             : 
     611           3 :                 r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr);
     612           3 :                 if (r < 0)
     613           0 :                         log_icmp6_nd(nd, "Error sending Router Solicitation");
     614             :                 else {
     615           3 :                         nd->state = ICMP6_ROUTER_SOLICITATION_SENT;
     616           3 :                         log_icmp6_nd(nd, "Sent Router Solicitation");
     617             :                 }
     618             : 
     619           3 :                 nd->nd_sent++;
     620             : 
     621           3 :                 r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
     622           3 :                 if (r < 0) {
     623           0 :                         icmp6_nd_notify(nd, r);
     624           0 :                         return 0;
     625             :                 }
     626             : 
     627           3 :                 next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL;
     628             : 
     629           3 :                 r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
     630             :                                       next_timeout, 0,
     631             :                                       icmp6_router_solicitation_timeout, nd);
     632           3 :                 if (r < 0) {
     633           0 :                         icmp6_nd_notify(nd, r);
     634           0 :                         return 0;
     635             :                 }
     636             : 
     637           3 :                 r = sd_event_source_set_priority(nd->timeout,
     638           3 :                                                  nd->event_priority);
     639           3 :                 if (r < 0) {
     640           0 :                         icmp6_nd_notify(nd, r);
     641           0 :                         return 0;
     642             :                 }
     643             : 
     644           3 :                 r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
     645           3 :                 if (r < 0) {
     646           0 :                         icmp6_nd_notify(nd, r);
     647           0 :                         return 0;
     648             :                 }
     649             :         }
     650             : 
     651           3 :         return 0;
     652             : }
     653             : 
     654           3 : int sd_icmp6_nd_stop(sd_icmp6_nd *nd) {
     655           3 :         assert_return(nd, -EINVAL);
     656           3 :         assert_return(nd->event, -EINVAL);
     657             : 
     658           3 :         log_icmp6_nd(client, "Stop ICMPv6");
     659             : 
     660           3 :         icmp6_nd_init(nd);
     661             : 
     662           3 :         nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE;
     663             : 
     664           3 :         return 0;
     665             : }
     666             : 
     667           4 : int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) {
     668             :         int r;
     669             : 
     670           4 :         assert(nd);
     671           4 :         assert(nd->event);
     672             : 
     673           4 :         if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE)
     674           0 :                 return -EINVAL;
     675             : 
     676           4 :         if (nd->index < 1)
     677           0 :                 return -EINVAL;
     678             : 
     679           4 :         r = dhcp_network_icmp6_bind_router_solicitation(nd->index);
     680           4 :         if (r < 0)
     681           0 :                 return r;
     682             : 
     683           4 :         nd->fd = r;
     684             : 
     685           4 :         r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN,
     686             :                             icmp6_router_advertisment_recv, nd);
     687           4 :         if (r < 0)
     688           0 :                 goto error;
     689             : 
     690           4 :         r = sd_event_source_set_priority(nd->recv, nd->event_priority);
     691           4 :         if (r < 0)
     692           0 :                 goto error;
     693             : 
     694           4 :         r = sd_event_source_set_description(nd->recv, "icmp6-receive-message");
     695           4 :         if (r < 0)
     696           0 :                 goto error;
     697             : 
     698           4 :         r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(),
     699             :                               0, 0, icmp6_router_solicitation_timeout, nd);
     700           4 :         if (r < 0)
     701           0 :                 goto error;
     702             : 
     703           4 :         r = sd_event_source_set_priority(nd->timeout, nd->event_priority);
     704           4 :         if (r < 0)
     705           0 :                 goto error;
     706             : 
     707           4 :         r = sd_event_source_set_description(nd->timeout, "icmp6-timeout");
     708             : error:
     709           4 :         if (r < 0)
     710           0 :                 icmp6_nd_init(nd);
     711             :         else
     712           4 :                 log_icmp6_nd(client, "Start Router Solicitation");
     713             : 
     714           4 :         return r;
     715             : }

Generated by: LCOV version 1.11