dhcpv6: sanitize option request list
authorHans Dedecker <dedeckeh@gmail.com>
Wed, 9 Jan 2019 10:38:14 +0000 (11:38 +0100)
committerHans Dedecker <dedeckeh@gmail.com>
Thu, 10 Jan 2019 15:09:57 +0000 (16:09 +0100)
By config statefull options can be specified for the option request
list which will be included in DHCPv6 INFORMATION request messages.
Obviously this is wrong as both RFC3315 and RFC8415 specify no
statefull options must be included in stateless mode.
In RFC8415 Table 4 specifies the options which can be included in
the option request list and which options are required to be requested
in some messages.
This commit extends the option table with flags indicating which options
may be included in the option request list.
On top the client adds by default the options 32/82/83 as they're
required to be sent depending on the message.

Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
src/dhcpv6.c
src/odhcp6c.c
src/odhcp6c.h

index 01dd497e33ea7159269e9dfc8def043f6225867a..81887c409203ab6ff7c51c5918a3f5128cfc8e67 100644 (file)
@@ -197,13 +197,14 @@ int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
                        htons(DHCPV6_OPT_S46_CONT_LW),
                };
                odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
-
-               if (!(client_options & DHCPV6_IGNORE_OPT_UNICAST)) {
-                       uint16_t otype = htons(DHCPV6_OPT_UNICAST);
-
-                       odhcp6c_add_state(STATE_ORO, &otype, sizeof(otype));
-               }
        }
+       // Required oro
+       uint16_t req_oro[] = {
+               htons(DHCPV6_OPT_INF_MAX_RT),
+               htons(DHCPV6_OPT_SOL_MAX_RT),
+               htons(DHCPV6_OPT_INFO_REFRESH),
+       };
+       odhcp6c_add_state(STATE_ORO, req_oro, sizeof(req_oro));
 
        // Configure IPv6-options
        int val = 1;
@@ -237,7 +238,6 @@ failure:
 enum {
        IOV_HDR=0,
        IOV_ORO,
-       IOV_ORO_REFRESH,
        IOV_CL_ID,
        IOV_SRV_ID,
        IOV_OPTS,
@@ -439,16 +439,39 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
                uint16_t length;
        } reconf_accept = {htons(DHCPV6_OPT_RECONF_ACCEPT), 0};
 
-       // Request Information Refresh
-       uint16_t oro_refresh = htons(DHCPV6_OPT_INFO_REFRESH);
-
        // Option list
        size_t opts_len;
        void *opts = odhcp6c_get_state(STATE_OPTS, &opts_len);
 
+       // Option Request List
+       size_t oro_entries, oro_len = 0;
+       uint16_t *oro, *s_oro = odhcp6c_get_state(STATE_ORO, &oro_entries);
+
+       oro_entries /= sizeof(*s_oro);
+       oro = alloca(oro_entries * sizeof(*oro));
+
+       for (size_t i = 0; i < oro_entries; i++) {
+               struct odhcp6c_opt *opt = odhcp6c_find_opt(htons(s_oro[i]));
+
+               if (opt) {
+                       if (!(opt->flags & OPT_ORO))
+                               continue;
+
+                       if ((opt->flags & OPT_ORO_SOLICIT) && type != DHCPV6_MSG_SOLICIT)
+                               continue;
+
+                       if ((opt->flags & OPT_ORO_STATELESS) && type != DHCPV6_MSG_INFO_REQ)
+                               continue;
+
+                       if ((opt->flags & OPT_ORO_STATEFUL) && type == DHCPV6_MSG_INFO_REQ)
+                               continue;
+               }
+
+               oro[oro_len++] = s_oro[i];
+       }
+       oro_len *= sizeof(*oro);
+
        // Prepare Header
-       size_t oro_len;
-       void *oro = odhcp6c_get_state(STATE_ORO, &oro_len);
        struct {
                uint8_t type;
                uint8_t trid[3];
@@ -467,7 +490,6 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
        struct iovec iov[IOV_TOTAL] = {
                [IOV_HDR] = {&hdr, sizeof(hdr)},
                [IOV_ORO] = {oro, oro_len},
-               [IOV_ORO_REFRESH] = {&oro_refresh, 0},
                [IOV_CL_ID] = {cl_id, cl_id_len},
                [IOV_SRV_ID] = {srv_id, srv_id_len},
                [IOV_OPTS] = { opts, opts_len },
@@ -479,12 +501,10 @@ static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
        };
 
        size_t cnt = IOV_TOTAL;
-       if (type == DHCPV6_MSG_INFO_REQ) {
-               cnt = 9;
-               iov[IOV_ORO_REFRESH].iov_len = sizeof(oro_refresh);
-               hdr.oro_len = htons(oro_len + sizeof(oro_refresh));
-       } else if (!request_prefix)
-               cnt = 13;
+       if (type == DHCPV6_MSG_INFO_REQ)
+               cnt = 8;
+       else if (!request_prefix)
+               cnt = 12;
 
        // Disable IAs if not used
        if (type != DHCPV6_MSG_SOLICIT && ia_na_len == 0)
index e0c9b76e31aefef96b362d79b84077ed1eb13ada..7e8377884790496e9eeed20f76c7efa0991c98d1 100644 (file)
@@ -87,20 +87,20 @@ static struct odhcp6c_opt opts[] = {
        { .code = DHCPV6_OPT_INTERFACE_ID, .flags = OPT_INTERNAL, .str = NULL },
        { .code = DHCPV6_OPT_RECONF_MESSAGE, .flags = OPT_INTERNAL, .str = NULL },
        { .code = DHCPV6_OPT_RECONF_ACCEPT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_DNS_SERVERS, .flags = OPT_IP6 | OPT_ARRAY, .str = "dns" },
-       { .code = DHCPV6_OPT_DNS_DOMAIN, .flags = OPT_DNS_STR, .str = "search" },
+       { .code = DHCPV6_OPT_SIP_SERVER_D, .flags = OPT_DNS_STR | OPT_ORO, .str = "sipserver_d" },
+       { .code = DHCPV6_OPT_SIP_SERVER_A, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "sipserver_a" },
+       { .code = DHCPV6_OPT_DNS_SERVERS, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "dns" },
+       { .code = DHCPV6_OPT_DNS_DOMAIN, .flags = OPT_DNS_STR | OPT_ORO, .str = "search" },
        { .code = DHCPV6_OPT_IA_PD, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
        { .code = DHCPV6_OPT_IA_PREFIX, .flags = OPT_INTERNAL, .str = NULL },
-       { .code = DHCPV6_OPT_SNTP_SERVERS, .flags = OPT_IP6 | OPT_ARRAY, .str = "sntpservers" },
-       { .code = DHCPV6_OPT_INFO_REFRESH, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_FQDN, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_NTP_SERVER, .flags = OPT_U8, .str = "ntpserver" },
-       { .code = DHCPV6_OPT_SIP_SERVER_D, .flags = OPT_DNS_STR, .str = "sipserver_d" },
-       { .code = DHCPV6_OPT_SIP_SERVER_A, .flags = OPT_IP6 | OPT_ARRAY, .str = "sipserver_a" },
-       { .code = DHCPV6_OPT_AFTR_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_PD_EXCLUDE, .flags = OPT_INTERNAL, .str = NULL },
-       { .code = DHCPV6_OPT_SOL_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_INF_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
+       { .code = DHCPV6_OPT_SNTP_SERVERS, .flags = OPT_IP6 | OPT_ARRAY | OPT_ORO, .str = "sntpservers" },
+       { .code = DHCPV6_OPT_INFO_REFRESH, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_STATELESS, .str = NULL },
+       { .code = DHCPV6_OPT_FQDN, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
+       { .code = DHCPV6_OPT_NTP_SERVER, .flags = OPT_U8 | OPT_ORO, .str = "ntpserver" },
+       { .code = DHCPV6_OPT_AFTR_NAME, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
+       { .code = DHCPV6_OPT_PD_EXCLUDE, .flags = OPT_INTERNAL | OPT_ORO | OPT_ORO_STATEFUL, .str = NULL },
+       { .code = DHCPV6_OPT_SOL_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_SOLICIT, .str = NULL },
+       { .code = DHCPV6_OPT_INF_MAX_RT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO | OPT_ORO_STATELESS, .str = NULL },
 #ifdef EXT_CER_ID
        { .code = DHCPV6_OPT_CER_ID, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
 #endif
@@ -109,9 +109,9 @@ static struct odhcp6c_opt opts[] = {
        { .code = DHCPV6_OPT_S46_DMR, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
        { .code = DHCPV6_OPT_S46_V4V6BIND, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
        { .code = DHCPV6_OPT_S46_PORTPARAMS, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_S46_CONT_MAPE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_S46_CONT_MAPT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
-       { .code = DHCPV6_OPT_S46_CONT_LW, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU, .str = NULL },
+       { .code = DHCPV6_OPT_S46_CONT_MAPE, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
+       { .code = DHCPV6_OPT_S46_CONT_MAPT, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
+       { .code = DHCPV6_OPT_S46_CONT_LW, .flags = OPT_INTERNAL | OPT_NO_PASSTHRU | OPT_ORO, .str = NULL },
        { .code = 0, .flags = 0, .str = NULL },
 };
 
index ad4f7937b3fd2c462d4556052f74a3b2de0e628d..38a2ca73e9f77227037356d949fec4a153fb88c7 100644 (file)
@@ -54,6 +54,8 @@ enum dhcvp6_opt {
        DHCPV6_OPT_INTERFACE_ID = 18,
        DHCPV6_OPT_RECONF_MESSAGE = 19,
        DHCPV6_OPT_RECONF_ACCEPT = 20,
+       DHCPV6_OPT_SIP_SERVER_D = 21,
+       DHCPV6_OPT_SIP_SERVER_A = 22,
        DHCPV6_OPT_DNS_SERVERS = 23,
        DHCPV6_OPT_DNS_DOMAIN = 24,
        DHCPV6_OPT_IA_PD = 25,
@@ -62,8 +64,6 @@ enum dhcvp6_opt {
        DHCPV6_OPT_INFO_REFRESH = 32,
        DHCPV6_OPT_FQDN = 39,
        DHCPV6_OPT_NTP_SERVER = 56,
-       DHCPV6_OPT_SIP_SERVER_D = 21,
-       DHCPV6_OPT_SIP_SERVER_A = 22,
        DHCPV6_OPT_AFTR_NAME = 64,
        DHCPV6_OPT_PD_EXCLUDE = 67,
        DHCPV6_OPT_SOL_MAX_RT = 82,
@@ -333,11 +333,15 @@ enum odhcp6c_opt_flags {
        OPT_ARRAY = 0x10,
        OPT_INTERNAL = 0x20,
        OPT_NO_PASSTHRU = 0x40,
+       OPT_ORO = 0x80,
+       OPT_ORO_STATEFUL = 0x100,
+       OPT_ORO_STATELESS = 0x200,
+       OPT_ORO_SOLICIT = 0x400
 };
 
 struct odhcp6c_opt {
        uint16_t code;
-       uint8_t flags;
+       uint16_t flags;
        const char *str;
 };