From c8f2dd8b5363b9026ae3de6ce84b5b64df657892 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 13 Sep 2013 11:22:55 +0100 Subject: [PATCH] Cope with DHCPv6 REQUESTs without address options. --- CHANGELOG | 3 +++ src/rfc3315.c | 57 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 83f1192..14ac3d6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -111,6 +111,9 @@ version 2.67 revealing size changes during development. Thanks to Vladislav Grishenko for the patch. + Cope with DHCPv6 clients which send REQUESTs without + address options - treat them as SOLICIT with rapid commit. + version 2.66 Add the ability to act as an authoritative DNS diff --git a/src/rfc3315.c b/src/rfc3315.c index f0e9982..12df5cf 100644 --- a/src/rfc3315.c +++ b/src/rfc3315.c @@ -21,7 +21,7 @@ struct state { unsigned char *clid; - int clid_len, iaid, ia_type, interface, hostname_auth; + int clid_len, iaid, ia_type, interface, hostname_auth, lease_allocate; char *client_hostname, *hostname, *domain, *send_domain; struct dhcp_context *context; struct in6_addr *link_address; @@ -55,7 +55,7 @@ static void mark_context_used(struct state *state, struct dhcp_context *context, static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr); static int check_address(struct state *state, struct in6_addr *addr); static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, - unsigned int *min_time, struct in6_addr *addr, int update_lease, time_t now); + unsigned int *min_time, struct in6_addr *addr, time_t now); static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now); static int add_local_addrs(struct dhcp_context *context); static struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context, int do_refresh); @@ -226,6 +226,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh state.end = inbuff + sz; state.clid = NULL; state.clid_len = 0; + state.lease_allocate = 0; state.context_tags = NULL; state.tags = tags; state.link_address = link_address; @@ -536,27 +537,29 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh { default: return 0; - - + + case DHCP6SOLICIT: { - void *rapid_commit = opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0); int address_assigned = 0; /* tags without all prefix-class tags */ struct dhcp_netid *solicit_tags = tagif; struct dhcp_context *c; - - if (rapid_commit) + + *outmsgtypep = DHCP6ADVERTISE; + + if (opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0)) { + *outmsgtypep = DHCP6REPLY; + state.lease_allocate = 1; o = new_opt6(OPTION6_RAPID_COMMIT); end_opt6(o); } - - /* set reply message type */ - *outmsgtypep = rapid_commit ? DHCP6REPLY : DHCP6ADVERTISE; - - log6_packet(&state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); + log6_packet(&state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); + + request_no_address: + if (ignore) return 0; @@ -671,7 +674,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, lease_time, ia_option, &min_time, req_addr, rapid_commit != NULL, now); + add_address(&state, c, lease_time, ia_option, &min_time, req_addr, now); mark_context_used(&state, context, req_addr); get_context_tag(&state, c); address_assigned = 1; @@ -695,7 +698,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now); + add_address(&state, c, lease_time, NULL, &min_time, &addr, now); mark_context_used(&state, context, &addr); get_context_tag(&state, c); address_assigned = 1; @@ -712,7 +715,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, c->lease_time, NULL, &min_time, req_addr, rapid_commit != NULL, now); + add_address(&state, c, c->lease_time, NULL, &min_time, req_addr, now); mark_context_used(&state, context, req_addr); get_context_tag(&state, c); address_assigned = 1; @@ -726,7 +729,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, c, c->lease_time, NULL, &min_time, &addr, rapid_commit != NULL, now); + add_address(&state, c, c->lease_time, NULL, &min_time, &addr, now); mark_context_used(&state, context, &addr); get_context_tag(&state, c); address_assigned = 1; @@ -766,9 +769,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh case DHCP6REQUEST: { int address_assigned = 0; - + int start = save_counter(-1); + /* set reply message type */ *outmsgtypep = DHCP6REPLY; + state.lease_allocate = 1; log6_packet(&state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL); @@ -783,7 +788,15 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (!check_ia(&state, opt, &ia_end, &ia_option)) continue; - + + if (!ia_option) + { + /* If we get a request with a IA_*A without addresses, treat it exactly like + a SOLICT with rapid commit set. */ + save_counter(start); + goto request_no_address; + } + o = build_ia(&state, &t1cntr); for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) @@ -829,7 +842,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA) state.send_prefix_class = prefix_class_from_context(c); #endif - add_address(&state, dynamic, lease_time, ia_option, &min_time, req_addr, 1, now); + add_address(&state, dynamic, lease_time, ia_option, &min_time, req_addr, now); get_context_tag(&state, dynamic); address_assigned = 1; } @@ -1505,7 +1518,7 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz) } static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option, - unsigned int *min_time, struct in6_addr *addr, int do_update, time_t now) + unsigned int *min_time, struct in6_addr *addr, time_t now) { unsigned int valid_time = 0, preferred_time = 0; int o = new_opt6(OPTION6_IAADDR); @@ -1535,7 +1548,7 @@ static void add_address(struct state *state, struct dhcp_context *context, unsig end_opt6(o); - if (do_update) + if (state->lease_allocate) update_leases(state, context, addr, valid_time, now); if ((lease = lease6_find_by_addr(addr, 128, 0))) @@ -1559,7 +1572,7 @@ static void add_address(struct state *state, struct dhcp_context *context, unsig } } - log6_packet(state, do_update ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); + log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname); }