Make wildcard-configured addresses work on multiple networks.

This commit is contained in:
Simon Kelley
2013-03-15 18:25:10 +00:00
parent 0f128eb58c
commit de92b479d9
3 changed files with 163 additions and 118 deletions

View File

@@ -371,29 +371,36 @@ struct dhcp_context *address6_valid(struct dhcp_context *context,
return NULL;
}
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
{
/* expand wildcard on contructed contexts */
if ((config->flags & CONFIG_WILDCARD) &&
(context->flags & CONTEXT_CONSTRUCTED))
if (!config || !(config->flags & CONFIG_ADDR6))
return 0;
if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)
{
u64 addrpart = addr6part(&config->addr6);
config->addr6 = context->start6;
setaddr6part(&config->addr6, addrpart);
*addr = context->start6;
setaddr6part(addr, addr6part(&config->addr6));
return 1;
}
if (!(config->flags & CONFIG_ADDR6) || is_addr_in_context6(context, &config->addr6))
if (is_same_net6(&context->start6, &config->addr6, context->prefix))
{
*addr = config->addr6;
return 1;
}
return 0;
}
int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr)
static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)
{
if (!(config->flags & CONFIG_ADDR6) ||
(config->flags & CONFIG_WILDCARD))
return 1;
for (; context; context = context->current)
if (is_same_net6(addr, &context->start6, context->prefix))
if (is_same_net6(&config->addr6, &context->start6, context->prefix))
return 1;
return 0;

View File

@@ -723,6 +723,7 @@ struct dhcp_context {
#define CONTEXT_RA 8192
#define CONTEXT_WILDCARD 16384
#define CONTEXT_USED 32768
#define CONTEXT_CONF_USED 65536
struct ping_result {
struct in_addr addr;
@@ -1175,7 +1176,7 @@ void dhcp6_init(void);
void dhcp6_packet(time_t now);
struct dhcp_context *address6_allocate(struct dhcp_context *context, unsigned char *clid, int clid_len,
int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans);
int is_addr_in_context6(struct dhcp_context *context, struct in6_addr *addr);
int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
struct dhcp_context *address6_available(struct dhcp_context *context,
struct in6_addr *taddr,
struct dhcp_netid *netids,

View File

@@ -52,11 +52,15 @@ static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
static struct prefix_class *prefix_class_from_context(struct dhcp_context *context);
#endif
static void mark_context_used(struct state *state, struct dhcp_context *context, struct in6_addr *addr);
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, unsigned int requested_time,
unsigned int *min_time, struct in6_addr *addr, int update_lease, 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);
struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context);
static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time);
#define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
#define opt6_type(opt) (opt6_uint(opt, -4, 2))
@@ -522,9 +526,10 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
case DHCP6SOLICIT:
{
void *rapid_commit = opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0);
int used_config = 0, address_assigned = 0;
int address_assigned = 0;
/* tags without all prefix-class tags */
struct dhcp_netid *solicit_tags = run_tag_if(state.tags);
struct dhcp_context *c;
if (rapid_commit)
{
@@ -535,7 +540,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* 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);
if (ignore)
return 0;
@@ -543,17 +548,19 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* reset USED bits in leases */
lease6_reset();
/* Can use configured address max once per prefix */
for (c = context; c; c = c->current)
c->flags &= ~CONTEXT_CONF_USED;
for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))
{
void *ia_option, *ia_end;
unsigned int min_time = 0xffffffff;
int t1cntr;
int config_ok = 0;
int ia_counter;
/* set unless we're sending a particular prefix-class, when we
want only dhcp-ranges with the correct tags set and not those without any tags. */
int plain_range = 1;
struct dhcp_context *c;
u32 lease_time, requested_time;
struct dhcp_lease *ltmp;
struct in6_addr *req_addr;
@@ -565,19 +572,6 @@ 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 (have_config(config, CONFIG_ADDR6))
{
struct dhcp_lease *config_lease = lease6_find_by_addr(&config->addr6, 128, 0);
config_ok = 1;
if (config_lease &&
(config_lease->clid_len != state.clid_len ||
memcmp(config_lease->clid, state.clid, state.clid_len) != 0 ||
config_lease->hwaddr_type != state.iaid))
config_ok = 0; /* configured address leased elsewhere */
}
/* reset USED bits in contexts - one address per prefix per IAID */
for (c = context; c; c = c->current)
c->flags &= ~CONTEXT_USED;
@@ -649,7 +643,6 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
{
req_addr = opt6_ptr(ia_option, 0);
ltmp = lease6_find_by_addr(req_addr, 128, 0);
requested_time = opt6_uint(ia_option, 16, 4);
if ((c = address6_valid(context, req_addr, solicit_tags, plain_range)))
@@ -658,19 +651,16 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* If the client asks for an address on the same network as a configured address,
offer the configured address instead, to make moving to newly-configured
addresses automatic. */
if (!used_config && config_ok && is_same_net6(req_addr, &config->addr6, c->prefix))
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(&state, &addr))
{
req_addr = &config->addr6;
used_config = 1;
req_addr = &addr;
mark_config_used(c, &addr);
if (have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
}
else if (!(c = address6_available(context, req_addr, solicit_tags, plain_range)))
continue; /* not an address we're allowed */
else if (ltmp &&
(ltmp->clid_len != state.clid_len ||
memcmp(ltmp->clid, state.clid, state.clid_len) != 0 ||
ltmp->hwaddr_type != state.iaid))
else if (!check_address(&state, req_addr))
continue; /* address leased elsewhere */
/* add address to output packet */
@@ -685,10 +675,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
}
}
/* Suggest configured address */
if (!used_config && config_ok && (c = address6_valid(context, &config->addr6, solicit_tags, plain_range)))
/* Suggest configured address(es) */
for (c = context; c; c = c->current)
if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(&state, &addr))
{
used_config = 1;
mark_config_used(context, &addr);
if (have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
/* add address to output packet */
@@ -696,8 +687,8 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if (dump_all_prefix_classes)
state.send_prefix_class = prefix_class_from_context(c);
#endif
add_address(&state, c, lease_time, requested_time, &min_time, &config->addr6, rapid_commit != NULL, now);
mark_context_used(&state, context, &config->addr6);
add_address(&state, c, lease_time, requested_time, &min_time, &addr, rapid_commit != NULL, now);
mark_context_used(&state, context, &addr);
get_context_tag(&state, c);
address_assigned = 1;
}
@@ -741,7 +732,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS);
put_opt6_string("Oh hai from dnsmasq");
put_opt6_string(_("success"));
end_opt6(o1);
/* If --dhcp-authoritative is set, we can tell client not to wait for
@@ -756,9 +747,9 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* no address, return error */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string("No addresses available");
put_opt6_string(_("no addresses available"));
end_opt6(o1);
log6_packet(&state, "DHCPADVERTISE", NULL, _("No addresses available"));
log6_packet(&state, "DHCPADVERTISE", NULL, _("no addresses available"));
}
break;
@@ -771,7 +762,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* set reply message type */
*outmsgtypep = DHCP6REPLY;
log6_packet(&state, "DHCPREQUEST", NULL, ignore ? "ignored" : NULL);
log6_packet(&state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
if (ignore)
return 0;
@@ -793,24 +784,28 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
u32 requested_time = opt6_uint(ia_option, 16, 4);
struct dhcp_context *dynamic, *c;
unsigned int lease_time;
struct in6_addr addr;
int config_ok = 0;
if ((dynamic = address6_available(context, req_addr, tagif, 1)) || (c = address6_valid(context, req_addr, tagif, 1)))
if ((c = address6_valid(context, req_addr, tagif, 1)))
config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);
if ((dynamic = address6_available(context, req_addr, tagif, 1)) || c)
{
if (!dynamic && !(have_config(config, CONFIG_ADDR6) || !IN6_ARE_ADDR_EQUAL(&config->addr6, req_addr)))
if (!dynamic && !config_ok)
{
/* Static range, not configured. */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address unavailable");
put_opt6_string(_("address unavailable"));
end_opt6(o1);
}
else if (lease6_find_by_addr(req_addr, 128, 0) &&
!lease6_find(state.clid, state.clid_len, state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state.iaid, req_addr))
else if (!check_address(&state, req_addr))
{
/* Address leased to another DUID/IAID */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6UNSPEC);
put_opt6_string("Address in use");
put_opt6_string(_("address in use"));
end_opt6(o1);
}
else
@@ -820,9 +815,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
lease_time = dynamic->lease_time;
if (have_config(config, CONFIG_ADDR6) &&
IN6_ARE_ADDR_EQUAL(&config->addr6, req_addr) &&
have_config(config, CONFIG_TIME))
if (config_ok && have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
add_address(&state, dynamic, lease_time, requested_time, &min_time, req_addr, 1, now);
@@ -830,12 +823,12 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
address_assigned = 1;
}
}
else if (msg_type == DHCP6REQUEST)
else
{
/* requested address not on the correct link */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Not on link");
put_opt6_string(_("not on link"));
end_opt6(o1);
}
}
@@ -848,7 +841,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS);
put_opt6_string("Oh hai from dnsmasq");
put_opt6_string(_("success"));
end_opt6(o1);
}
else
@@ -856,9 +849,9 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
/* no address, return error */
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOADDRS);
put_opt6_string("No addresses available");
put_opt6_string(_("no addresses available"));
end_opt6(o1);
log6_packet(&state, "DHCPREPLY", NULL, _("No addresses available"));
log6_packet(&state, "DHCPREPLY", NULL, _("no addresses available"));
}
tagif = add_options(&state, fallback, context);
@@ -890,13 +883,10 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
struct dhcp_lease *lease = NULL;
struct in6_addr *req_addr = opt6_ptr(ia_option, 0);
u32 requested_time = opt6_uint(ia_option, 16, 4);
unsigned int lease_time;
unsigned int preferred_time = 0; /* in case renewal inappropriate */
unsigned int valid_time = 0;
char *message = NULL;
struct dhcp_context *this_context;
struct dhcp_config *valid_config = config;
/* don't use a config to set lease time if it specifies an address which isn't this. */
if (have_config(config, CONFIG_ADDR6) && !IN6_ARE_ADDR_EQUAL(&config->addr6, req_addr))
valid_config = NULL;
if (!(lease = lease6_find(state.clid, state.clid_len,
state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
@@ -908,11 +898,11 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
save_counter(iacntr);
t1cntr = 0;
log6_packet(&state, "DHCPREPLY", req_addr, "lease not found");
log6_packet(&state, "DHCPREPLY", req_addr, _("lease not found"));
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOBINDING);
put_opt6_string("No binding found");
put_opt6_string(_("no binding found"));
end_opt6(o1);
break;
}
@@ -921,35 +911,41 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
if ((this_context = address6_available(context, req_addr, tagif, 1)) ||
(this_context = address6_valid(context, req_addr, tagif, 1)))
{
struct in6_addr addr;
unsigned int lease_time;
get_context_tag(&state, this_context);
lease_time = have_config(valid_config, CONFIG_TIME) ? valid_config->lease_time : this_context->lease_time;
if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))
lease_time = config->lease_time;
else
lease_time = this_context->lease_time;
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time, requested_time);
lease_set_expires(lease, lease_time, now);
lease_set_expires(lease, valid_time, now);
if (state.ia_type == OPTION6_IA_NA && state.hostname)
{
char *addr_domain = get_domain6(req_addr);
if (!state.send_domain)
state.send_domain = addr_domain;
lease_set_hostname(lease, state.hostname, state.hostname_auth, addr_domain, state.domain);
message = state.hostname;
}
if (lease_time < min_time)
min_time = lease_time;
}
log6_packet(&state, "DHCPREPLY", req_addr, state.hostname);
if (preferred_time == 0)
message = _("deprecated");
}
else
message = _("address invalid");
log6_packet(&state, "DHCPREPLY", req_addr, message);
o1 = new_opt6(OPTION6_IAADDR);
put_opt6(req_addr, sizeof(*req_addr));
/* preferred lifetime */
put_opt6_long(!this_context || (this_context->flags & CONTEXT_DEPRECATE) ? 0 : lease_time);
put_opt6_long(!this_context ? 0 : lease_time); /* valid lifetime */
put_opt6_long(preferred_time);
put_opt6_long(valid_time);
end_opt6(o1);
}
@@ -983,7 +979,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOTONLINK);
put_opt6_string("Confirm failed");
put_opt6_string(_("confirm failed"));
end_opt6(o1);
return 1;
}
@@ -994,7 +990,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS );
put_opt6_string("All addresses still on link");
put_opt6_string(_("all addresses still on link"));
end_opt6(o1);
break;
}
@@ -1008,7 +1004,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
context->netid.next = NULL;
state.context_tags = &context->netid;
}
log6_packet(&state, "DHCPINFORMATION-REQUEST", NULL, ignore ? "ignored" : state.hostname);
log6_packet(&state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state.hostname);
if (ignore)
return 0;
*outmsgtypep = DHCP6REPLY;
@@ -1064,7 +1060,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOBINDING);
put_opt6_string("No binding found");
put_opt6_string(_("no binding found"));
end_opt6(o1);
end_opt6(o);
@@ -1073,7 +1069,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6SUCCESS);
put_opt6_string("Release received");
put_opt6_string(_("release received"));
end_opt6(o1);
break;
@@ -1141,7 +1137,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
{
o1 = new_opt6(OPTION6_STATUS_CODE);
put_opt6_short(DHCP6NOBINDING);
put_opt6_string("No binding found");
put_opt6_string(_("no binding found"));
end_opt6(o1);
end_opt6(o);
@@ -1459,23 +1455,15 @@ 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, unsigned int requested_time,
unsigned int *min_time, struct in6_addr *addr, int do_update, time_t now)
{
unsigned int valid_time = (context->valid < lease_time) ? context->valid : lease_time;
unsigned int valid_time, preferred_time;
int o = new_opt6(OPTION6_IAADDR);
struct dhcp_lease *lease;
calculate_times(context, min_time, &valid_time, &preferred_time, lease_time, requested_time);
put_opt6(addr, sizeof(*addr));
/* preferred lifetime */
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
if (lease_time < *min_time)
*min_time = lease_time;
put_opt6_long((context->preferred < lease_time) ? context->preferred : lease_time);
put_opt6_long(valid_time); /* valid lifetime */
put_opt6_long(preferred_time);
put_opt6_long(valid_time);
#ifdef OPTION6_PREFIX_CLASS
if (state->send_prefix_class)
@@ -1532,6 +1520,55 @@ static void mark_context_used(struct state *state, struct dhcp_context *context,
#endif
}
static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr)
{
for (; context; context = context->current)
if (is_same_net6(addr, &context->start6, context->prefix))
context->flags |= CONTEXT_CONF_USED;
}
/* make sure address not leased to another CLID/IAID */
static int check_address(struct state *state, struct in6_addr *addr)
{
struct dhcp_lease *lease;
if (!(lease = lease6_find_by_addr(addr, 128, 0)))
return 1;
if (lease->clid_len != state->clid_len ||
memcmp(lease->clid, state->clid, state->clid_len) != 0 ||
lease->hwaddr_type != state->iaid)
return 0;
return 1;
}
static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,
unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time)
{
unsigned int preferred_time, valid_time;
if (requested_time < 120u )
requested_time = 120u; /* sanity */
if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))
lease_time = requested_time;
valid_time = (context->valid < lease_time) ? context->valid : lease_time;
preferred_time = (context->preferred < lease_time) ? context->preferred : lease_time;
if (context->flags & CONTEXT_DEPRECATE)
preferred_time = 0;
if (preferred_time != 0 && preferred_time < *min_time)
*min_time = preferred_time;
if (valid_time != 0 && valid_time < *min_time)
*min_time = valid_time;
*valid_timep = valid_time;
*preferred_timep = preferred_time;
}
static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now)
{
struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0);