X-Git-Url: https://git.decadent.org.uk/gitweb/?p=odhcp6c.git;a=blobdiff_plain;f=src%2Fodhcp6c.c;h=061cb42a607f7ac8e94f477c2eb0e6d466431f7d;hp=4e50758c1aee91775f53f73959998eb0cfa9cc5c;hb=49db5eb7406cd57b6ed64f5ee7130226d0fef66b;hpb=d30e43bca363c003dfa41c963ab2e36d67365cc1 diff --git a/src/odhcp6c.c b/src/odhcp6c.c index 4e50758..061cb42 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ #include #include "odhcp6c.h" +#include "ra.h" static void sighandler(int signal); @@ -38,6 +40,8 @@ static uint8_t *state_data[_STATE_MAX] = {NULL}; static size_t state_len[_STATE_MAX] = {0}; static volatile int do_signal = 0; +static int urandom_fd = -1; +static bool bound = false, allow_slaac_only = false; int main(_unused int argc, char* const argv[]) @@ -55,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; @@ -122,12 +130,14 @@ int main(_unused int argc, char* const argv[]) if (help || !ifname) return usage(); - if (init_dhcpv6(ifname, request_pd) || init_rtnetlink() || + if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 || + init_dhcpv6(ifname, request_pd) || ra_init(ifname) || script_init(script, ifname)) { syslog(LOG_ERR, "failed to initialize: %s", strerror(errno)); return 3; } + signal(SIGIO, sighandler); signal(SIGHUP, sighandler); signal(SIGINT, sighandler); signal(SIGCHLD, sighandler); @@ -170,9 +180,11 @@ int main(_unused int argc, char* const argv[]) odhcp6c_clear_state(STATE_SIP_IP); odhcp6c_clear_state(STATE_SIP_FQDN); dhcpv6_set_ia_na_mode(ia_na_mode); + bound = false; do_signal = 0; int res = dhcpv6_request(DHCPV6_MSG_SOLICIT); + odhcp6c_signal_process(); if (res < 0) { continue; // Might happen if we got a signal @@ -181,6 +193,7 @@ int main(_unused int argc, char* const argv[]) do_signal = 0; res = dhcpv6_request(DHCPV6_MSG_INFO_REQ); + odhcp6c_signal_process(); if (do_signal == SIGUSR1) continue; else if (res < 0) @@ -188,6 +201,8 @@ int main(_unused int argc, char* const argv[]) else if (res > 0) script_call("informed"); + bound = true; + if (dhcpv6_poll_reconfigure() > 0) script_call("informed"); } @@ -199,12 +214,15 @@ int main(_unused int argc, char* const argv[]) if (dhcpv6_request(DHCPV6_MSG_REQUEST) < 0) continue; + odhcp6c_signal_process(); script_call("bound"); + bound = true; while (do_signal == 0 || do_signal == SIGUSR1) { // Renew Cycle // Wait for T1 to expire or until we get a reconfigure int res = dhcpv6_poll_reconfigure(); + odhcp6c_signal_process(); if (res >= 0) { if (res > 0) script_call("updated"); @@ -228,6 +246,7 @@ int main(_unused int argc, char* const argv[]) r = dhcpv6_request(DHCPV6_MSG_REQUEST); else r = dhcpv6_request(DHCPV6_MSG_RENEW); + odhcp6c_signal_process(); if (r > 0) // Publish updates script_call("updated"); if (r >= 0) @@ -237,6 +256,7 @@ int main(_unused int argc, char* const argv[]) // If we have IAs, try rebind otherwise restart res = dhcpv6_request(DHCPV6_MSG_REBIND); + odhcp6c_signal_process(); odhcp6c_get_state(STATE_IA_PD, &ia_pd_new); odhcp6c_get_state(STATE_IA_NA, &ia_na_new); @@ -254,15 +274,14 @@ int main(_unused int argc, char* const argv[]) odhcp6c_get_state(STATE_SERVER_ID, &server_id_len); // Add all prefixes to lost prefixes - odhcp6c_clear_state(STATE_IA_PD); + bound = false; script_call("unbound"); - // Remove assigned addresses - if (ia_na_len > 0) - dhcpv6_remove_addrs(); - if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0)) dhcpv6_request(DHCPV6_MSG_RELEASE); + + odhcp6c_clear_state(STATE_IA_NA); + odhcp6c_clear_state(STATE_IA_PD); } script_call("stopped"); @@ -275,6 +294,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" @@ -303,6 +323,8 @@ static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len) { if (len == 0) return state_data[state] + state_len[state]; + else if (state_len[state] + len > 1024) + return NULL; uint8_t *n = realloc(state_data[state], state_len[state] + len); if (n || state_len[state] + len == 0) { @@ -314,8 +336,18 @@ static uint8_t* odhcp6c_resize_state(enum odhcp6c_state state, ssize_t len) } -bool odhcp6c_signal_is_pending(void) +bool odhcp6c_signal_process(void) { + if (do_signal == SIGIO) { + do_signal = 0; + bool updated = ra_process(); + updated |= ra_rtnl_process(); + if (updated && (bound || allow_slaac_only)) { + odhcp6c_expire(); + script_call("ra-updated"); + } + } + return do_signal != 0; } @@ -346,23 +378,93 @@ size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len) } -bool odhcp6c_commit_state(enum odhcp6c_state state, size_t old_len) +void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len) +{ + *len = state_len[state]; + return state_data[state]; +} + + +struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new) { - size_t new_len = state_len[state] - old_len; - uint8_t *old_data = state_data[state], *new_data = old_data + old_len; - bool upd = new_len != old_len || memcmp(old_data, new_data, new_len); + size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + new->length / 8; + struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); + struct odhcp6c_entry *x = NULL; - memmove(old_data, new_data, new_len); - odhcp6c_resize_state(state, -old_len); + for (struct odhcp6c_entry *c = start; !x && c < &start[len/sizeof(*c)]; ++c) + if (!memcmp(c, new, cmplen)) + return c; - return upd; + return NULL; } -void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len) +void odhcp6c_update_entry_safe(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe) { - *len = state_len[state]; - return state_data[state]; + size_t len; + struct odhcp6c_entry *x = odhcp6c_find_entry(state, new); + struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); + + if (x && x->valid > new->valid && new->valid < safe) + new->valid = safe; + + if (new->valid > 0) { + if (x) + *x = *new; + else + odhcp6c_add_state(state, new, sizeof(*new)); + } else if (x) { + odhcp6c_remove_state(state, (x - start) * sizeof(*x), sizeof(*x)); + } +} + + +void odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new) +{ + odhcp6c_update_entry_safe(state, new, 0); +} + + +static void odhcp6c_expire_list(enum odhcp6c_state state, uint32_t elapsed) +{ + size_t len; + struct odhcp6c_entry *start = odhcp6c_get_state(state, &len); + for (struct odhcp6c_entry *c = start; c < &start[len / sizeof(*c)]; ++c) { + if (c->preferred < elapsed) + c->preferred = 0; + else if (c->preferred != UINT32_MAX) + c->preferred -= elapsed; + + if (c->valid < elapsed) + c->valid = 0; + else if (c->valid != UINT32_MAX) + c->valid -= elapsed; + + if (!c->valid) + odhcp6c_remove_state(state, (c - start) * sizeof(*c), sizeof(*c)); + } +} + + +void odhcp6c_expire(void) +{ + static time_t last_update = 0; + time_t now = odhcp6c_get_milli_time() / 1000; + + uint32_t elapsed = now - last_update; + last_update = now; + + 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); +} + + +void odhcp6c_random(void *buf, size_t len) +{ + read(urandom_fd, buf, len); } @@ -374,6 +476,8 @@ static void sighandler(int signal) do_signal = SIGUSR1; else if (signal == SIGUSR2) do_signal = SIGUSR2; + else if (signal == SIGIO) + do_signal = SIGIO; else do_signal = SIGTERM; }