Include IAs in Request to be more compatible with some servers
authorSteven Barth <steven@midlink.org>
Wed, 31 Jul 2013 17:05:55 +0000 (19:05 +0200)
committerSteven Barth <steven@midlink.org>
Wed, 31 Jul 2013 17:05:55 +0000 (19:05 +0200)
src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h

index 64c0cb44e90107b184590d6d265e03286c09c8c6..9742c39dd4316cf1c750246d19ef43319d6ec2b1 100644 (file)
@@ -559,12 +559,12 @@ static int dhcpv6_handle_reconfigure(_unused enum dhcpv6_msg orig,
 
 
 // Collect all advertised servers
-static int dhcpv6_handle_advert(_unused enum dhcpv6_msg orig,
+static int dhcpv6_handle_advert(enum dhcpv6_msg orig,
                const void *opt, const void *end)
 {
        uint16_t olen, otype;
        uint8_t *odata;
-       struct dhcpv6_server_cand cand = {false, false, 0, 0, {0}};
+       struct dhcpv6_server_cand cand = {false, false, 0, 0, {0}, NULL, NULL, 0, 0};
 
        dhcpv6_for_each_option(opt, end, otype, olen, odata) {
                if (otype == DHCPV6_OPT_SERVERID && olen <= 130) {
@@ -598,10 +598,25 @@ static int dhcpv6_handle_advert(_unused enum dhcpv6_msg orig,
                                        cand.preference -= 2000;
                        }
                }
+
+               if (orig == DHCPV6_MSG_SOLICIT &&
+                               (otype == DHCPV6_OPT_IA_PD || otype == DHCPV6_OPT_IA_NA) &&
+                               olen > sizeof(struct dhcpv6_ia_hdr)) {
+                       struct dhcpv6_ia_hdr *ia_hdr = (void*)(&odata[-4]);
+                       dhcpv6_parse_ia(&ia_hdr[1], odata + olen);
+               }
        }
 
-       if (cand.duid_len > 0)
+       if (cand.duid_len > 0) {
+               cand.ia_na = odhcp6c_move_state(STATE_IA_NA, &cand.ia_na_len);
+               cand.ia_pd = odhcp6c_move_state(STATE_IA_PD, &cand.ia_pd_len);
                odhcp6c_add_state(STATE_SERVER_CAND, &cand, sizeof(cand));
+       }
+
+       if (orig == DHCPV6_MSG_SOLICIT) {
+               odhcp6c_clear_state(STATE_IA_NA);
+               odhcp6c_clear_state(STATE_IA_PD);
+       }
 
        return -1;
 }
@@ -634,8 +649,14 @@ static int dhcpv6_commit_advert(void)
                odhcp6c_add_state(STATE_SERVER_ID, hdr, sizeof(hdr));
                odhcp6c_add_state(STATE_SERVER_ID, c->duid, c->duid_len);
                accept_reconfig = c->wants_reconfigure;
+               odhcp6c_add_state(STATE_IA_NA, c->ia_na, c->ia_na_len);
+               odhcp6c_add_state(STATE_IA_PD, c->ia_pd, c->ia_pd_len);
        }
 
+       for (size_t i = 0; i < cand_len / sizeof(*c); ++i) {
+               free(cand[i].ia_na);
+               free(cand[i].ia_pd);
+       }
        odhcp6c_clear_state(STATE_SERVER_CAND);
 
        if (!c)
@@ -691,6 +712,12 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig,
                t1 = t2 = t3 = UINT32_MAX;
        }
 
+       if (orig == DHCPV6_MSG_REQUEST) {
+               // Delete NA and PD we have in the state from the Advert
+               odhcp6c_clear_state(STATE_IA_NA);
+               odhcp6c_clear_state(STATE_IA_PD);
+       }
+
        if (opt) {
                odhcp6c_clear_state(STATE_DNS);
                odhcp6c_clear_state(STATE_SEARCH);
index df89eb9baeb68c050b620e3541cfd27eccb6d719..f732d9e8fa52b36f1f2f5d314e94e98967df8851 100644 (file)
@@ -192,7 +192,7 @@ int main(_unused int argc, char* const argv[])
 
        while (do_signal != SIGTERM) { // Main logic
                odhcp6c_clear_state(STATE_SERVER_ID);
-               odhcp6c_clear_state(STATE_SERVER_CAND);
+               odhcp6c_clear_state(STATE_IA_NA);
                odhcp6c_clear_state(STATE_IA_PD);
                odhcp6c_clear_state(STATE_SNTP_IP);
                odhcp6c_clear_state(STATE_SNTP_FQDN);
@@ -201,6 +201,15 @@ int main(_unused int argc, char* const argv[])
                dhcpv6_set_ia_na_mode(ia_na_mode);
                bound = false;
 
+               // Server candidates need deep-delete
+               size_t cand_len;
+               struct dhcpv6_server_cand *cand = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+               for (size_t i = 0; i < cand_len / sizeof(*cand); ++i) {
+                       free(cand[i].ia_na);
+                       free(cand[i].ia_pd);
+               }
+               odhcp6c_clear_state(STATE_SERVER_CAND);
+
                syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);
 
                do_signal = 0;
@@ -405,6 +414,18 @@ size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len)
 }
 
 
+void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len)
+{
+       *len = state_len[state];
+       void *data = state_data[state];
+
+       state_len[state] = 0;
+       state_data[state] = NULL;
+
+       return data;
+}
+
+
 void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len)
 {
        *len = state_len[state];
index 2e7107c5d582eb63748bb3018cfb27987b9c4e25..ac0932fa1486eb8f3060ac49d632aaa1042187ea 100644 (file)
@@ -158,6 +158,10 @@ struct dhcpv6_server_cand {
        int16_t preference;
        uint8_t duid_len;
        uint8_t duid[130];
+       void *ia_na;
+       void *ia_pd;
+       size_t ia_na_len;
+       size_t ia_pd_len;
 };
 
 
@@ -236,6 +240,7 @@ void odhcp6c_random(void *buf, size_t len);
 void odhcp6c_clear_state(enum odhcp6c_state state);
 void odhcp6c_add_state(enum odhcp6c_state state, const void *data, size_t len);
 size_t odhcp6c_remove_state(enum odhcp6c_state state, size_t offset, size_t len);
+void* odhcp6c_move_state(enum odhcp6c_state state, size_t *len);
 void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len);
 
 // Entry manipulation