From f7437e4d4b97ed829d567b3ca917b25cf5e62dfb Mon Sep 17 00:00:00 2001 From: Hans Dedecker Date: Wed, 9 Jan 2019 11:38:14 +0100 Subject: [PATCH] dhcpv6: sanitize option request list 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 --- src/dhcpv6.c | 58 ++++++++++++++++++++++++++++++++++----------------- src/odhcp6c.c | 30 +++++++++++++------------- src/odhcp6c.h | 10 ++++++--- 3 files changed, 61 insertions(+), 37 deletions(-) diff --git a/src/dhcpv6.c b/src/dhcpv6.c index 01dd497..81887c4 100644 --- a/src/dhcpv6.c +++ b/src/dhcpv6.c @@ -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) diff --git a/src/odhcp6c.c b/src/odhcp6c.c index e0c9b76..7e83778 100644 --- a/src/odhcp6c.c +++ b/src/odhcp6c.c @@ -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 }, }; diff --git a/src/odhcp6c.h b/src/odhcp6c.h index ad4f793..38a2ca7 100644 --- a/src/odhcp6c.h +++ b/src/odhcp6c.h @@ -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; }; -- 2.30.2