router: support ra_mininterval and ra_lifetime uci parameters (FS#397)
authorHans Dedecker <dedeckeh@gmail.com>
Thu, 16 Feb 2017 11:20:08 +0000 (12:20 +0100)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 16 Feb 2017 17:25:41 +0000 (18:25 +0100)
Add support for uci parameters ra_mininterval and ra_lifetime as described
in RFC4861 paragraph 6.2.1.

Variable ra_mininterval allows to configure the minimum interval time
between unsolicited router advertisement messages; default value is
200 seconds. The minimum allowed value is 4 seconds while the maximum
value is limited to 0.75 of the maximum interval time.

The calculation of the maximum interval time between unsolicited
router advertisement messages has been reworked. The default value is 600
seconds as specified in RFC4861; if the maximum interval time exceeds
0.33 * the minimal valid lifetime of all IPv6 prefixes it will be limited
to 0.33 * the minimal valid lifetime of all IPv6 prefixes

Variable ra_lifetime allows to configure the Router Lifetime field
in the router advertisement messages; the value is either 0 or a value
between the maximum interval time and 9000 seconds. If the router lifetime
is smaller than the RA maximum interval it will be set equal to the RA
maximum interval time.

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
README
src/config.c
src/odhcpd.h
src/router.c
src/router.h

diff --git a/README b/README
index 8c3b12c..bc52ee9 100644 (file)
--- a/README
+++ b/README
@@ -105,6 +105,12 @@ ra_management      integer 1                       RA management mode
 ra_offlink     bool    0                       Announce prefixes off-link
 ra_preference  string  medium                  Route(r) preference
                [medium|high|low]
+ra_maxinterval integer 600                     Maximum time allowed between
+                                               sending unsolicited RA
+ra_mininterval integer 200                     Minimum time allowed between
+                                               sending unsolicited RA
+ra_lifetime    integer 1800                    Value to be placed in Router
+                                               Lifetime field of RA
 ndproxy_routing        bool    1                       Learn routes from NDP
 ndproxy_slave  bool    0                       NDProxy external slave
 
index 69a3ad1..b621c75 100644 (file)
@@ -45,7 +45,9 @@ enum {
        IFACE_ATTR_RA_OFFLINK,
        IFACE_ATTR_RA_PREFERENCE,
        IFACE_ATTR_RA_ADVROUTER,
+       IFACE_ATTR_RA_MININTERVAL,
        IFACE_ATTR_RA_MAXINTERVAL,
+       IFACE_ATTR_RA_LIFETIME,
        IFACE_ATTR_PD_MANAGER,
        IFACE_ATTR_PD_CER,
        IFACE_ATTR_NDPROXY_ROUTING,
@@ -80,7 +82,9 @@ static const struct blobmsg_policy iface_attrs[IFACE_ATTR_MAX] = {
        [IFACE_ATTR_RA_OFFLINK] = { .name = "ra_offlink", .type = BLOBMSG_TYPE_BOOL },
        [IFACE_ATTR_RA_PREFERENCE] = { .name = "ra_preference", .type = BLOBMSG_TYPE_STRING },
        [IFACE_ATTR_RA_ADVROUTER] = { .name = "ra_advrouter", .type = BLOBMSG_TYPE_BOOL },
+       [IFACE_ATTR_RA_MININTERVAL] = { .name = "ra_mininterval", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_RA_MAXINTERVAL] = { .name = "ra_maxinterval", .type = BLOBMSG_TYPE_INT32 },
+       [IFACE_ATTR_RA_LIFETIME] = { .name = "ra_lifetime", .type = BLOBMSG_TYPE_INT32 },
        [IFACE_ATTR_NDPROXY_ROUTING] = { .name = "ndproxy_routing", .type = BLOBMSG_TYPE_BOOL },
        [IFACE_ATTR_NDPROXY_SLAVE] = { .name = "ndproxy_slave", .type = BLOBMSG_TYPE_BOOL },
 };
@@ -188,6 +192,9 @@ static void set_interface_defaults(struct interface *iface)
 {
        iface->managed = 1;
        iface->learn_routes = 1;
+       iface->ra_maxinterval = 600;
+       iface->ra_mininterval = iface->ra_maxinterval/3;
+       iface->ra_lifetime = -1;
 }
 
 static void clean_interface(struct interface *iface)
@@ -584,9 +591,15 @@ int config_parse_interface(void *data, size_t len, const char *name, bool overwr
        if ((c = tb[IFACE_ATTR_RA_ADVROUTER]))
                iface->ra_advrouter = blobmsg_get_bool(c);
 
+       if ((c = tb[IFACE_ATTR_RA_MININTERVAL]))
+               iface->ra_mininterval =  blobmsg_get_u32(c);
+
        if ((c = tb[IFACE_ATTR_RA_MAXINTERVAL]))
                iface->ra_maxinterval = blobmsg_get_u32(c);
 
+       if ((c = tb[IFACE_ATTR_RA_LIFETIME]))
+               iface->ra_lifetime = blobmsg_get_u32(c);
+
        if ((c = tb[IFACE_ATTR_RA_PREFERENCE])) {
                const char *prio = blobmsg_get_string(c);
 
index 8db3e7d..a88eb08 100644 (file)
@@ -152,11 +152,14 @@ struct interface {
        bool ra_advrouter;
        bool no_dynamic_dhcp;
 
+       // RA
        int learn_routes;
        int default_router;
        int managed;
        int route_preference;
        int ra_maxinterval;
+       int ra_mininterval;
+       int ra_lifetime;
 
        // DHCPv4
        struct in_addr dhcpv4_start;
index 36267be..4ab06b6 100644 (file)
@@ -211,6 +211,48 @@ static bool parse_routes(struct odhcpd_ipaddr *n, ssize_t len)
        return found_default;
 }
 
+static int calc_adv_interval(struct interface *iface, uint32_t minvalid,
+               uint32_t *maxival)
+{
+       uint32_t minival = iface->ra_mininterval;
+       int msecs;
+
+       *maxival = iface->ra_maxinterval;
+
+       if (*maxival > minvalid/3)
+               *maxival = minvalid/3;
+
+       if (*maxival > MaxRtrAdvInterval)
+               *maxival = MaxRtrAdvInterval;
+       else if (*maxival < 4)
+               *maxival = 4;
+
+       if (minival < MinRtrAdvInterval)
+               minival = MinRtrAdvInterval;
+       else if (minival > (*maxival * 3)/4)
+               minival = (*maxival >= 9 ? *maxival/3 : *maxival);
+
+       odhcpd_urandom(&msecs, sizeof(msecs));
+       msecs = (labs(msecs) % ((*maxival - minival)*1000)) + minival*1000;
+
+       return msecs;
+}
+
+static uint16_t calc_ra_lifetime(struct interface *iface, uint32_t maxival)
+{
+       uint16_t lifetime = 3*maxival;
+
+       if (iface->ra_lifetime >= 0) {
+               lifetime = iface->ra_lifetime;
+               if (lifetime < maxival)
+                       lifetime = maxival;
+               else if (lifetime > 9000)
+                       lifetime = 9000;
+       }
+
+       return lifetime;
+}
+
 // Router Advert server mode
 static uint64_t send_router_advert(struct interface *iface, const struct in6_addr *from)
 {
@@ -250,7 +292,9 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
        // If not currently shutting down
        struct odhcpd_ipaddr addrs[RELAYD_MAX_ADDRS];
        ssize_t ipcnt = 0;
-       int64_t minvalid = INT64_MAX;
+       uint32_t minvalid = UINT32_MAX;
+       bool default_route = false;
+       bool valid_prefix = false;
 
        // If not shutdown
        if (iface->timer_rs.cb) {
@@ -258,13 +302,13 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
                memcpy(addrs, iface->ia_addr, ipcnt * sizeof(*addrs));
 
                // Check default route
-               if (iface->default_router > 1)
-                       adv.h.nd_ra_router_lifetime = htons(iface->default_router);
-               else if (parse_routes(addrs, ipcnt))
-                       adv.h.nd_ra_router_lifetime = htons(1);
+               if (iface->default_router) {
+                       default_route = true;
 
-               syslog(LOG_INFO, "Initial RA router lifetime %d, %d address(es) available on %s",
-                               ntohs(adv.h.nd_ra_router_lifetime), (int)ipcnt, iface->ifname);
+                       if (iface->default_router > 1)
+                               valid_prefix = true;
+               } else if (parse_routes(addrs, ipcnt))
+                       default_route = true;
        }
 
        // Construct Prefix Information options
@@ -303,19 +347,11 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
                }
 
                if (addr->preferred > (uint32_t)now &&
-                               minvalid > 1000LL * TIME_LEFT(addr->valid, now))
-                       minvalid = 1000LL * TIME_LEFT(addr->valid, now);
-
-               uint32_t this_lifetime = TIME_LEFT(addr->valid, now);
-               if (this_lifetime > UINT16_MAX)
-                       this_lifetime = UINT16_MAX;
-               if ((!IN6_IS_ADDR_ULA(&addr->addr) || iface->default_router)
-                               && adv.h.nd_ra_router_lifetime
-                               && ntohs(adv.h.nd_ra_router_lifetime) < this_lifetime) {
-                       adv.h.nd_ra_router_lifetime = htons(this_lifetime);
-
-                       syslog(LOG_INFO, "Updating RA router lifetime to %d on %s", this_lifetime, iface->ifname);
-               }
+                               minvalid > TIME_LEFT(addr->valid, now))
+                       minvalid = TIME_LEFT(addr->valid, now);
+
+               if (!IN6_IS_ADDR_ULA(&addr->addr) || iface->default_router)
+                       valid_prefix = true;
 
                odhcpd_bmemcpy(&p->nd_opt_pi_prefix, &addr->addr,
                                (iface->ra_advrouter) ? 128 : addr->prefix);
@@ -336,11 +372,22 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
                        p->nd_opt_pi_valid_time = 0;
        }
 
-       if (!iface->default_router && adv.h.nd_ra_router_lifetime == htons(1)) {
-               syslog(LOG_WARNING, "A default route is present but there is no public prefix "
-                               "on %s thus we don't announce a default route!", iface->ifname);
+       // Calculate periodic transmit
+       uint32_t maxival;
+       int msecs = calc_adv_interval(iface, minvalid, &maxival);
+
+       if (default_route) {
+               if (!valid_prefix) {
+                       syslog(LOG_WARNING, "A default route is present but there is no public prefix "
+                                       "on %s thus we don't announce a default route!", iface->ifname);
+                       adv.h.nd_ra_router_lifetime = 0;
+               } else
+                       adv.h.nd_ra_router_lifetime = htons(calc_ra_lifetime(iface, maxival));
+
+       } else
                adv.h.nd_ra_router_lifetime = 0;
-       }
+
+       syslog(LOG_INFO, "Using a RA lifetime of %d seconds on %s", ntohs(adv.h.nd_ra_router_lifetime), iface->ifname);
 
        // DNS Recursive DNS
        if (iface->dns_cnt > 0) {
@@ -433,33 +480,13 @@ static uint64_t send_router_advert(struct interface *iface, const struct in6_add
                ++routes_cnt;
        }
 
-       // Calculate periodic transmit
-       int msecs = 0;
-       uint32_t maxival = iface->ra_maxinterval * 1000;
-       uint32_t minival;
-
-       if (maxival < 4000 || maxival > MaxRtrAdvInterval * 1000)
-               maxival = MaxRtrAdvInterval * 1000;
-
-       if (maxival > minvalid / 3) {
-               maxival = minvalid / 3;
-
-               if (maxival < 4000)
-                       maxival = 4000;
-       }
-
-       minival = (maxival * 3) / 4;
-
-       search->lifetime = htonl(maxival / 100);
+       search->lifetime = htonl(maxival*10);
        dns.lifetime = search->lifetime;
 
-       odhcpd_urandom(&msecs, sizeof(msecs));
-       msecs = (labs(msecs) % (maxival - minival)) + minival;
-
        struct icmpv6_opt adv_interval = {
                .type = ND_OPT_RTR_ADV_INTERVAL,
                .len = 1,
-               .data = {0, 0, maxival >> 24, maxival >> 16, maxival >> 8, maxival}
+               .data = {0, 0, (maxival*1000) >> 24, (maxival*1000) >> 16, (maxival*1000) >> 8, maxival*1000}
        };
 
        struct iovec iov[RA_IOV_LEN] = {
index 546af3e..bdfe52c 100644 (file)
@@ -30,8 +30,8 @@ struct icmpv6_opt {
        (void*)(opt + opt->len) <= (void*)(end); opt += opt->len)
 
 
-#define MaxValidTime           65535
 #define MaxRtrAdvInterval      1800
+#define MinRtrAdvInterval      3
 
 #define ND_RA_FLAG_PROXY       0x4
 #define ND_RA_PREF_HIGH                (1 << 3)