]> git.decadent.org.uk Git - odhcp6c.git/blobdiff - src/dhcpv6.c
Revert "Fix handling of DHCPv6 messages containing option lengths exceeding the message"
[odhcp6c.git] / src / dhcpv6.c
index b727818139a1aba2f5fffd9032a9a26856a6be26..cd8e43800dca33315047cd000262a5697c985be5 100644 (file)
@@ -59,6 +59,8 @@ static void dhcpv6_handle_ia_status_code(const enum dhcpv6_msg orig,
                const void *status_msg, const int len,
                bool handled_status_codes[_DHCPV6_Status_Max],
                int *ret);
+static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand);
+static void dhcpv6_clear_all_server_cand(void);
 
 static reply_handler dhcpv6_handle_reply;
 static reply_handler dhcpv6_handle_advert;
@@ -145,6 +147,7 @@ int init_dhcpv6(const char *ifname, int request_pd, int sol_timeout)
                                                sizeof(ifr.ifr_name));
                                if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
                                        continue;
+
                                memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
                                                ETHER_ADDR_LEN);
                        }
@@ -169,31 +172,21 @@ int init_dhcpv6(const char *ifname, int request_pd, int sol_timeout)
        };
        odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
 
-       do {
-               // Configure IPv6-options
-               int val = 1;
-               if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)) < 0)
-                       break;
-               if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
-                       break;
-               if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)) < 0)
-                       break;
-
-               val = 0;
-               if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val)) < 0)
-                       break;
-               if (setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)) < 0)
-                       break;
-
-               struct sockaddr_in6 client_addr = { .sin6_family = AF_INET6,
-                       .sin6_port = htons(DHCPV6_CLIENT_PORT), .sin6_flowinfo = 0 };
-               if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0)
-                       break;
-
-               return 0;
-       } while (0);
+       // Configure IPv6-options
+       int val = 1;
+       setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+       setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
+       val = 0;
+       setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val));
+       setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));
+
+       struct sockaddr_in6 client_addr = { .sin6_family = AF_INET6,
+               .sin6_port = htons(DHCPV6_CLIENT_PORT), .sin6_flowinfo = 0 };
+       if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0)
+               return -1;
 
-       return -1;
+       return 0;
 }
 
 
@@ -478,7 +471,7 @@ int dhcpv6_request(enum dhcpv6_msg type)
                }
 
                // Receive rounds
-               for (; len < 0 && round_start < round_end;
+               for (; len < 0 && (round_start < round_end);
                                round_start = odhcp6c_get_milli_time()) {
                        uint8_t buf[1536], cmsg_buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
                        struct iovec iov = {buf, sizeof(buf)};
@@ -760,7 +753,7 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
        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));
+               dhcpv6_add_server_cand(&cand);
        }
 
        return (rc > 1 || (pref == 255 && cand.preference > 0)) ? 1 : -1;
@@ -769,50 +762,7 @@ static int dhcpv6_handle_advert(enum dhcpv6_msg orig, const int rc,
 
 static int dhcpv6_commit_advert(void)
 {
-       size_t cand_len;
-       struct dhcpv6_server_cand *c = NULL, *cand =
-                       odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
-
-       bool retry = false;
-       for (size_t i = 0; i < cand_len / sizeof(*c); ++i) {
-               if (cand[i].has_noaddravail)
-                       retry = true; // We want to try again
-
-               if (!c || c->preference < cand[i].preference)
-                       c = &cand[i];
-       }
-
-       if (retry && na_mode == IA_MODE_TRY) {
-               // We give it a second try without the IA_NA
-               na_mode = IA_MODE_NONE;
-               return dhcpv6_request(DHCPV6_MSG_SOLICIT);
-       }
-
-       if (c) {
-               uint16_t hdr[2] = {htons(DHCPV6_OPT_SERVERID),
-                               htons(c->duid_len)};
-               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;
-               server_addr = c->server_addr;
-               if (c->ia_na_len)
-                       odhcp6c_add_state(STATE_IA_NA, c->ia_na, c->ia_na_len);
-               if (c->ia_pd_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)
-               return -1;
-       else if ((request_prefix && c->ia_pd_len) || (na_mode != IA_MODE_NONE && c->ia_na_len))
-               return DHCPV6_STATEFUL;
-       else
-               return DHCPV6_STATELESS;
+       return dhcpv6_promote_server_cand();
 }
 
 
@@ -982,13 +932,23 @@ static int dhcpv6_handle_reply(enum dhcpv6_msg orig, _unused const int rc,
                                        ret = -1;
                                break;
 
+                       case DHCPV6_MSG_REQUEST:
+                               // All server candidates can be cleared if not yet bound
+                               if (!odhcp6c_is_bound())
+                                       dhcpv6_clear_all_server_cand();
+
                        default :
                                break;
                        }
                }
        }
-       else if (ret > 0)
+       else if (ret > 0) {
+               // All server candidates can be cleared if not yet bound
+               if (!odhcp6c_is_bound())
+                       dhcpv6_clear_all_server_cand();
+
                t1 = refresh;
+       }
 
        return ret;
 }
@@ -1263,3 +1223,86 @@ static void dhcpv6_handle_ia_status_code(const enum dhcpv6_msg orig,
                break;
        }
 }
+
+static void dhcpv6_add_server_cand(const struct dhcpv6_server_cand *cand)
+{
+       size_t cand_len, i;
+       struct dhcpv6_server_cand *c = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+
+       // Remove identical duid server candidate
+       for (i = 0; i < cand_len / sizeof(*c); ++i) {
+               if (cand->duid_len == c[i].duid_len &&
+                       !memcmp(cand->duid, c[i].duid, cand->duid_len)) {
+                       free(c[i].ia_na);
+                       free(c[i].ia_pd);
+                       odhcp6c_remove_state(STATE_SERVER_CAND, i * sizeof(*c), sizeof(*c));
+                       break;
+               }
+       }
+
+       for (i = 0, c = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+               i < cand_len / sizeof(*c); ++i) {
+               if (c[i].preference < cand->preference)
+                       break;
+       }
+
+       odhcp6c_insert_state(STATE_SERVER_CAND, i * sizeof(*c), cand, sizeof(*cand));
+}
+
+static void dhcpv6_clear_all_server_cand(void)
+{
+       size_t cand_len, i;
+       struct dhcpv6_server_cand *c = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+
+       // Server candidates need deep delete for IA_NA/IA_PD
+       for (i = 0; i < cand_len / sizeof(*c); ++i) {
+               if (c[i].ia_na)
+                       free(c[i].ia_na);
+               if (c[i].ia_pd)
+                       free(c[i].ia_pd);
+       }
+       odhcp6c_clear_state(STATE_SERVER_CAND);
+}
+
+int dhcpv6_promote_server_cand(void)
+{
+       size_t cand_len;
+       struct dhcpv6_server_cand *cand = odhcp6c_get_state(STATE_SERVER_CAND, &cand_len);
+       uint16_t hdr[2];
+       int ret = DHCPV6_STATELESS;
+
+       // Clear lingering candidate state info
+       odhcp6c_clear_state(STATE_SERVER_ID);
+       odhcp6c_clear_state(STATE_IA_NA);
+       odhcp6c_clear_state(STATE_IA_PD);
+
+       if (!cand_len)
+               return -1;
+
+       if (cand->has_noaddravail && na_mode == IA_MODE_TRY) {
+               na_mode = IA_MODE_NONE;
+               return dhcpv6_request(DHCPV6_MSG_SOLICIT);
+       }
+
+       hdr[0] = htons(DHCPV6_OPT_SERVERID);
+       hdr[1] = htons(cand->duid_len);
+       odhcp6c_add_state(STATE_SERVER_ID, hdr, sizeof(hdr));
+       odhcp6c_add_state(STATE_SERVER_ID, cand->duid, cand->duid_len);
+       accept_reconfig = cand->wants_reconfigure;
+       if (cand->ia_na_len) {
+               odhcp6c_add_state(STATE_IA_NA, cand->ia_na, cand->ia_na_len);
+               free(cand->ia_na);
+               if (na_mode != IA_MODE_NONE)
+                       ret = DHCPV6_STATEFUL;
+       }
+       if (cand->ia_pd_len) {
+               odhcp6c_add_state(STATE_IA_PD, cand->ia_pd, cand->ia_pd_len);
+               free(cand->ia_pd);
+               if (request_prefix)
+                       ret = DHCPV6_STATEFUL;
+       }
+
+       odhcp6c_remove_state(STATE_SERVER_CAND, 0, sizeof(*cand));
+
+       return ret;
+}