Fix and improve RA-handling code
authorSteven Barth <steven@midlink.org>
Wed, 30 Jan 2013 21:15:07 +0000 (22:15 +0100)
committerSteven Barth <steven@midlink.org>
Wed, 30 Jan 2013 21:15:07 +0000 (22:15 +0100)
README
src/odhcp6c.c
src/odhcp6c.h
src/ra.c
src/script.c

diff --git a/README b/README
index 421475fe7c7f53519f57a0501ccab2879b356616..f43c60ffed25c2350dd8e6b1187709bb445aeb84 100644 (file)
--- a/README
+++ b/README
@@ -39,8 +39,8 @@ States:
 * started              The DHCPv6 client has been started
 * bound                        A suitable server was found and addresses or prefixes acquired          
 * informed             A stateless information request returned updated information
-* timeout              The DHCPv6 operation did not succeed within the defined time
 * updated              Updated information was received from the DHCPv6 server
+* ra-updated   Updated information was received from via Router Advertisement
 * rebound              The DHCPv6 client switched to another server
 * unbound              The DHCPv6 client lost all DHCPv6 servers and will restart
 * stopped              The DHCPv6 client has been stopped
@@ -56,6 +56,13 @@ Environment:
 * OPTION_<num> Custom option received as base-16
 * PREFIXES             A space-separated list of prefixes currently assigned
                                Format: <prefix>/<length>,preferred,valid
+* ADDRESSES            A space-separated list of addresses currently assigned
+                               Format: <address>/<length>,preferred,valid
+* RA_ADDRESSES A space-separated list of addresses from RA-prefixes
+                               Format: <address>/<length>,preferred,valid
+* RA_ROUTES            A space-separated list of routes from the RA
+                               Format: <address>/<length>@gateway,preferred,valid,metric
+* RA_DNS               A space-separated list of recursive DNS servers from the RA
 
 
 
index e4da1ab2767b36ef067e4cb314f413ff40f779c8..6a1d1b4bdcc2caae05a79bfeda822f9339c1304d 100644 (file)
@@ -41,7 +41,7 @@ static size_t state_len[_STATE_MAX] = {0};
 
 static volatile int do_signal = 0;
 static int urandom_fd = -1;
-static bool bound = false;
+static bool bound = false, allow_slaac_only = false;
 
 
 int main(_unused int argc, char* const argv[])
@@ -59,8 +59,12 @@ int main(_unused int argc, char* const argv[])
 
        bool help = false, daemonize = false;
        int c, request_pd = 0;
-       while ((c = getopt(argc, argv, "N:P:c:r:s:hdp:")) != -1) {
+       while ((c = getopt(argc, argv, "SN:P:c:r:s:hdp:")) != -1) {
                switch (c) {
+               case 'S':
+                       allow_slaac_only = true;
+                       break;
+
                case 'N':
                        if (!strcmp(optarg, "force"))
                                ia_na_mode = IA_MODE_FORCE;
@@ -284,6 +288,7 @@ static int usage(void)
        const char buf[] =
        "Usage: odhcp6c [options] <interface>\n"
        "\nFeature options:\n"
+       "       -S              Allow SLAAC-only assignment\n"
        "       -N <mode>       Mode for requesting addresses [try|force|none]\n"
        "       -P <length>     Request IPv6-Prefix (0 = auto)\n"
        "       -c <clientid>   Override client-ID (base-16 encoded)\n"
@@ -329,7 +334,7 @@ bool odhcp6c_signal_process(void)
                do_signal = 0;
                bool updated = ra_process();
                updated |= ra_rtnl_process();
-               if (updated && bound) {
+               if (updated && (bound || allow_slaac_only)) {
                        odhcp6c_expire();
                        script_call("ra-updated");
                }
@@ -386,7 +391,7 @@ struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct
 }
 
 
-void odhcp6c_update_entry_safe(enum odhcp6c_state state, const struct odhcp6c_entry *new, uint32_t safe)
+void odhcp6c_update_entry_safe(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe)
 {
        size_t len;
        struct odhcp6c_entry *x = odhcp6c_find_entry(state, new);
@@ -406,7 +411,7 @@ void odhcp6c_update_entry_safe(enum odhcp6c_state state, const struct odhcp6c_en
 }
 
 
-void odhcp6c_update_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new)
+void odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new)
 {
        odhcp6c_update_entry_safe(state, new, 0);
 }
@@ -443,6 +448,7 @@ void odhcp6c_expire(void)
 
        odhcp6c_expire_list(STATE_RA_PREFIX, elapsed);
        odhcp6c_expire_list(STATE_RA_ROUTE, elapsed);
+       odhcp6c_expire_list(STATE_RA_DNS, elapsed);
        odhcp6c_expire_list(STATE_IA_NA, elapsed);
        odhcp6c_expire_list(STATE_IA_PD, elapsed);
 }
index 440381a56f1f37e04cedd4b9b48433c65bfe6f2b..eae9b7a8b9ccbedeb3fe3ea9f24b3ebd206f4d47 100644 (file)
@@ -164,6 +164,7 @@ enum odhcp6c_state {
        STATE_SIP_FQDN,
        STATE_RA_ROUTE,
        STATE_RA_PREFIX,
+       STATE_RA_DNS,
        _STATE_MAX
 };
 
@@ -224,7 +225,7 @@ void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len);
 
 // Entry manipulation
 struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new);
-void odhcp6c_update_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new);
-void odhcp6c_update_entry_safe(enum odhcp6c_state state, const struct odhcp6c_entry *new, uint32_t safe);
+void odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new);
+void odhcp6c_update_entry_safe(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe);
 
 void odhcp6c_expire(void);
index 0d51832176fd2f59fec2ec1518cec08f857d65e1..f9fd573f8009ee8b82a05c8a2f609c694937faa4 100644 (file)
--- a/src/ra.c
+++ b/src/ra.c
@@ -193,13 +193,14 @@ bool ra_process(void)
                }
 
                found = true;
+               uint32_t router_valid = ntohs(adv->nd_ra_router_lifetime);
 
                // Parse default route
                entry.router = from.sin6_addr;
                entry.priority = pref_to_priority(adv->nd_ra_flags_reserved);
                if (entry.priority < 0)
                        entry.priority = pref_to_priority(0);
-               entry.valid = ntohs(adv->nd_ra_router_lifetime);
+               entry.valid = router_valid;
                entry.preferred = entry.valid;
                odhcp6c_update_entry(STATE_RA_ROUTE, &entry);
 
@@ -210,6 +211,7 @@ bool ra_process(void)
                if (adv->nd_ra_retransmit)
                        update_proc("neigh", "retrans_time_ms", ntohl(adv->nd_ra_retransmit));
 
+
                // Evaluate options
                struct icmpv6_opt *opt;
                icmpv6_for_each_option(opt, &adv[1], &buf[len]) {
@@ -256,9 +258,29 @@ bool ra_process(void)
                                entry.target.s6_addr32[3] = lladdr.s6_addr32[3];
 
                                odhcp6c_update_entry_safe(STATE_RA_PREFIX, &entry, 7200);
-                       }
+                       } else if (opt->type == ND_OPT_RECURSIVE_DNS && opt->len > 2) {
+                               entry.router = from.sin6_addr;
+                               entry.priority = 0;
+                               entry.length = 128;
+                               entry.valid = ntohl(*((uint32_t*)&opt->data[2]));
+                               entry.preferred = 0;
 
+                               for (ssize_t i = 0; i < (opt->len - 1) / 2; ++i) {
+                                       memcpy(&entry.target, &opt->data[6 + i * sizeof(entry.target)],
+                                                       sizeof(entry.target));
+                                       odhcp6c_update_entry(STATE_RA_DNS, &entry);
+                               }
+                       }
                }
+
+               size_t ra_dns_len;
+               struct odhcp6c_entry *entry = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len);
+               for (size_t i = 0; i < len / sizeof(*entry); ++i)
+                       if (IN6_ARE_ADDR_EQUAL(&entry[i].router, &from.sin6_addr) &&
+                                       entry[i].valid > router_valid)
+                               entry[i].valid = router_valid;
        }
+
+       odhcp6c_expire();
        return found;
 }
index 0e01f3aa6d094c2fb78d4b440b73660e91835942..4eec04dceba3b4fc193c0f7a1492ccb7fc2821cd 100644 (file)
@@ -129,7 +129,7 @@ static void bin_to_env(uint8_t *opts, size_t len)
 }
 
 
-static void entry_to_env(const char *name, const void *data, size_t len)
+static void entry_to_env(const char *name, const void *data, size_t len, bool host)
 {
        size_t buf_len = strlen(name);
        const struct odhcp6c_entry *e = data;
@@ -141,15 +141,17 @@ static void entry_to_env(const char *name, const void *data, size_t len)
        for (size_t i = 0; i < len / sizeof(*e); ++i) {
                inet_ntop(AF_INET6, &e[i].target, &buf[buf_len], INET6_ADDRSTRLEN);
                buf_len += strlen(&buf[buf_len]);
-               buf_len += snprintf(&buf[buf_len], 6, "/%hhu", e[i].length);
-               if (!IN6_ARE_ADDR_EQUAL(&any, &e[i].router)) {
-                       buf[buf_len++] = '@';
-                       inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN);
-                       buf_len += strlen(&buf[buf_len]);
+               if (!host) {
+                       buf_len += snprintf(&buf[buf_len], 6, "/%hhu", e[i].length);
+                       if (!IN6_ARE_ADDR_EQUAL(&any, &e[i].router)) {
+                               buf[buf_len++] = '@';
+                               inet_ntop(AF_INET6, &e[i].router, &buf[buf_len], INET6_ADDRSTRLEN);
+                               buf_len += strlen(&buf[buf_len]);
+                       }
+                       buf_len += snprintf(&buf[buf_len], 24, ",%u,%u", e[i].preferred, e[i].valid);
+                       if (e[i].priority)
+                               buf_len += snprintf(&buf[buf_len], 12, ",%u", e[i].priority);
                }
-               buf_len += snprintf(&buf[buf_len], 24, ",%u,%u", e[i].preferred, e[i].valid);
-               if (e[i].priority)
-                       buf_len += snprintf(&buf[buf_len], 12, ",%u", e[i].priority);
                buf[buf_len++] = ' ';
        }
 
@@ -172,11 +174,12 @@ void script_call(const char *status)
        struct in6_addr *sip = odhcp6c_get_state(STATE_SIP_IP, &sip_ip_len);
        uint8_t *sip_fqdn = odhcp6c_get_state(STATE_SIP_FQDN, &sip_fqdn_len);
 
-       size_t prefix_len, address_len, ra_pref_len, ra_route_len;
+       size_t prefix_len, address_len, ra_pref_len, ra_route_len, ra_dns_len;
        uint8_t *prefix = odhcp6c_get_state(STATE_IA_PD, &prefix_len);
        uint8_t *address = odhcp6c_get_state(STATE_IA_NA, &address_len);
        uint8_t *ra_pref = odhcp6c_get_state(STATE_RA_PREFIX, &ra_pref_len);
        uint8_t *ra_route = odhcp6c_get_state(STATE_RA_ROUTE, &ra_route_len);
+       uint8_t *ra_dns = odhcp6c_get_state(STATE_RA_DNS, &ra_dns_len);
 
        // Don't set environment before forking, because env is leaky.
        if (fork() == 0) {
@@ -187,10 +190,11 @@ void script_call(const char *status)
                fqdn_to_env("SNTP_FQDN", sntp_dns, sntp_dns_len);
                fqdn_to_env("SIP_DOMAIN", sip_fqdn, sip_fqdn_len);
                bin_to_env(custom, custom_len);
-               entry_to_env("PREFIXES", prefix, prefix_len);
-               entry_to_env("ADDRESSES", address, address_len);
-               entry_to_env("RA_ADDRESSES", ra_pref, ra_pref_len);
-               entry_to_env("RA_ROUTES", ra_route, ra_route_len);
+               entry_to_env("PREFIXES", prefix, prefix_len, false);
+               entry_to_env("ADDRESSES", address, address_len, false);
+               entry_to_env("RA_ADDRESSES", ra_pref, ra_pref_len, false);
+               entry_to_env("RA_ROUTES", ra_route, ra_route_len, false);
+               entry_to_env("RA_DNS", ra_dns, ra_dns_len, true);
 
                argv[2] = (char*)status;
                execv(argv[0], argv);