From 6bc31b22abc26b9ee31b789d56a8e2a07aa4c569 Mon Sep 17 00:00:00 2001 From: Steven Barth Date: Wed, 30 Jan 2013 22:15:07 +0100 Subject: [PATCH] Fix and improve RA-handling code --- README | 9 ++++++++- src/odhcp6c.c | 16 +++++++++++----- src/odhcp6c.h | 5 +++-- src/ra.c | 26 ++++++++++++++++++++++++-- src/script.c | 32 ++++++++++++++++++-------------- 5 files changed, 64 insertions(+), 24 deletions(-) diff --git a/README b/README index 421475f..f43c60f 100644 --- 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_ Custom option received as base-16 * PREFIXES A space-separated list of prefixes currently assigned Format: /,preferred,valid +* ADDRESSES A space-separated list of addresses currently assigned + Format:
/,preferred,valid +* RA_ADDRESSES A space-separated list of addresses from RA-prefixes + Format:
/,preferred,valid +* RA_ROUTES A space-separated list of routes from the RA + Format:
/@gateway,preferred,valid,metric +* RA_DNS A space-separated list of recursive DNS servers from the RA diff --git a/src/odhcp6c.c b/src/odhcp6c.c index e4da1ab..6a1d1b4 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -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] \n" "\nFeature options:\n" + " -S Allow SLAAC-only assignment\n" " -N Mode for requesting addresses [try|force|none]\n" " -P Request IPv6-Prefix (0 = auto)\n" " -c 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); } diff --git a/src/odhcp6c.h b/src/odhcp6c.h index 440381a..eae9b7a 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -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); diff --git a/src/ra.c b/src/ra.c index 0d51832..f9fd573 100644 --- 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; } diff --git a/src/script.c b/src/script.c index 0e01f3a..4eec04d 100644 --- a/src/script.c +++ b/src/script.c @@ -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); -- 2.25.1