ike: support multiple addresses, ranges and subnets in IKE address config

Replace the allowany semantic by a more powerful subnet and IP range matching.
Multiple addresses, DNS names, subnets and ranges can be specified in a comma
separated list. Initiators ignore the ranges/subnets, responders match
configurations against all addresses, ranges and subnets.
This commit is contained in:
Martin Willi
2013-09-04 10:38:37 +02:00
parent beffdc6ab8
commit 3070697f9f
16 changed files with 311 additions and 115 deletions
+208 -46
View File
@@ -50,24 +50,34 @@ struct private_ike_cfg_t {
ike_version_t version;
/**
* Address of local host
* Address list string for local host
*/
char *me;
/**
* Address of remote host
* Address list string for remote host
*/
char *other;
/**
* Allow override of local address
* Local single host or DNS names, as allocated char*
*/
bool my_allow_any;
linked_list_t *my_hosts;
/**
* Allow override of remote address
* Remote single host or DNS names, as allocated char*
*/
bool other_allow_any;
linked_list_t *other_hosts;
/**
* Local ranges/subnets this config matches to, as traffic_selector_t*
*/
linked_list_t *my_ranges;
/**
* Remote ranges/subnets this config matches to, as traffic_selector_t*
*/
linked_list_t *other_ranges;
/**
* our source port
@@ -129,60 +139,113 @@ METHOD(ike_cfg_t, fragmentation, fragmentation_t,
return this->fragmentation;
}
/**
* Common function for resolve_me/other
*/
static host_t* resolve(linked_list_t *hosts, int family, u_int16_t port)
{
enumerator_t *enumerator;
host_t *host = NULL;
bool tried = FALSE;
char *str;
enumerator = hosts->create_enumerator(hosts);
while (enumerator->enumerate(enumerator, &str))
{
host = host_create_from_dns(str, family, port);
if (host)
{
break;
}
tried = TRUE;
}
enumerator->destroy(enumerator);
if (!host && !tried)
{
/* we have no single host configured, return %any */
host = host_create_any(family ?: AF_INET);
host->set_port(host, port);
}
return host;
}
METHOD(ike_cfg_t, resolve_me, host_t*,
private_ike_cfg_t *this, int family)
{
return host_create_from_dns(this->me, family, this->my_port);
return resolve(this->my_hosts, family, this->my_port);
}
METHOD(ike_cfg_t, resolve_other, host_t*,
private_ike_cfg_t *this, int family)
{
return host_create_from_dns(this->other, family, this->other_port);
return resolve(this->other_hosts, family, this->other_port);
}
/**
* Common function for match_me/other
*/
static u_int match(linked_list_t *hosts, linked_list_t *ranges, host_t *cand)
{
enumerator_t *enumerator;
traffic_selector_t *ts;
char *str;
host_t *host;
u_int8_t mask;
u_int quality = 0;
/* try single hosts first */
enumerator = hosts->create_enumerator(hosts);
while (enumerator->enumerate(enumerator, &str))
{
host = host_create_from_dns(str, cand->get_family(cand), 0);
if (host)
{
if (host->ip_equals(host, cand))
{
quality = max(quality, 128 + 1);
}
if (host->is_anyaddr(host))
{
quality = max(quality, 1);
}
host->destroy(host);
}
}
enumerator->destroy(enumerator);
/* then ranges/subnets */
enumerator = ranges->create_enumerator(ranges);
while (enumerator->enumerate(enumerator, &ts))
{
if (ts->includes(ts, cand))
{
if (ts->to_subnet(ts, &host, &mask))
{
quality = max(quality, mask + 1);
host->destroy(host);
}
else
{
quality = max(quality, 1);
}
}
}
enumerator->destroy(enumerator);
return quality;
}
METHOD(ike_cfg_t, match_me, u_int,
private_ike_cfg_t *this, host_t *host)
{
host_t *me;
int quality = 0;
me = resolve_me(this, host->get_family(host));
if (me)
{
if (me->ip_equals(me, host))
{
quality = 2;
}
else if (this->my_allow_any)
{
quality = 1;
}
me->destroy(me);
}
return quality;
return match(this->my_hosts, this->my_ranges, host);
}
METHOD(ike_cfg_t, match_other, u_int,
private_ike_cfg_t *this, host_t *host)
{
host_t *other;
int quality = 0;
other = resolve_other(this, host->get_family(host));
if (other)
{
if (other->ip_equals(other, host))
{
quality = 2;
}
else if (this->other_allow_any)
{
quality = 1;
}
other->destroy(other);
}
return quality;
return match(this->other_hosts, this->other_ranges, host);
}
METHOD(ike_cfg_t, get_my_addr, char*,
@@ -361,16 +424,110 @@ METHOD(ike_cfg_t, destroy, void,
offsetof(proposal_t, destroy));
free(this->me);
free(this->other);
this->my_hosts->destroy_function(this->my_hosts, free);
this->other_hosts->destroy_function(this->other_hosts, free);
this->my_ranges->destroy_offset(this->my_ranges,
offsetof(traffic_selector_t, destroy));
this->other_ranges->destroy_offset(this->other_ranges,
offsetof(traffic_selector_t, destroy));
free(this);
}
}
/**
* Try to parse a string as subnet
*/
static traffic_selector_t* make_subnet(char *str)
{
char *pos;
pos = strchr(str, '/');
if (!pos)
{
return NULL;
}
return traffic_selector_create_from_cidr(str, 0, 0, 0);
}
/**
* Try to parse a string as an IP range
*/
static traffic_selector_t* make_range(char *str)
{
traffic_selector_t *ts;
ts_type_t type;
char *pos;
host_t *from, *to;
pos = strchr(str, '-');
if (!pos)
{
return NULL;
}
to = host_create_from_string(pos + 1, 0);
if (!to)
{
return NULL;
}
str = strndup(str, pos - str);
from = host_create_from_string_and_family(str, to->get_family(to), 0);
free(str);
if (!from)
{
to->destroy(to);
return NULL;
}
if (to->get_family(to) == AF_INET)
{
type = TS_IPV4_ADDR_RANGE;
}
else
{
type = TS_IPV6_ADDR_RANGE;
}
ts = traffic_selector_create_from_bytes(0, type,
from->get_address(from), 0,
to->get_address(to), 0);
from->destroy(from);
to->destroy(to);
return ts;
}
/**
* Parse address string into lists of single hosts and ranges/subnets
*/
static void parse_addresses(char *str, linked_list_t *hosts,
linked_list_t *ranges)
{
enumerator_t *enumerator;
traffic_selector_t *ts;
enumerator = enumerator_create_token(str, ",", " ");
while (enumerator->enumerate(enumerator, &str))
{
ts = make_subnet(str);
if (ts)
{
ranges->insert_last(ranges, ts);
continue;
}
ts = make_range(str);
if (ts)
{
ranges->insert_last(ranges, ts);
continue;
}
hosts->insert_last(hosts, strdup(str));
}
enumerator->destroy(enumerator);
}
/**
* Described in header.
*/
ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
char *me, bool my_allow_any, u_int16_t my_port,
char *other, bool other_allow_any, u_int16_t other_port,
char *me, u_int16_t my_port,
char *other, u_int16_t other_port,
fragmentation_t fragmentation, u_int8_t dscp)
{
private_ike_cfg_t *this;
@@ -404,14 +561,19 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
.force_encap = force_encap,
.fragmentation = fragmentation,
.me = strdup(me),
.my_ranges = linked_list_create(),
.my_hosts = linked_list_create(),
.other = strdup(other),
.my_allow_any = my_allow_any,
.other_allow_any = other_allow_any,
.other_ranges = linked_list_create(),
.other_hosts = linked_list_create(),
.my_port = my_port,
.other_port = other_port,
.dscp = dscp,
.proposals = linked_list_create(),
);
parse_addresses(me, this->my_hosts, this->my_ranges);
parse_addresses(other, this->other_hosts, this->other_ranges);
return &this->public;
}
+8 -5
View File
@@ -230,24 +230,27 @@ struct ike_cfg_t {
/**
* Creates a ike_cfg_t object.
*
* Supplied hosts become owned by ike_cfg, the name gets cloned.
* Supplied hosts become owned by ike_cfg, strings get cloned.
*
* me and other are comma separated lists of IP addresses, DNS names, IP ranges
* or subnets. When initiating, the first non-range/subnet address is used
* as address. When responding, a match is performed against all items in the
* list.
*
* @param version IKE major version to use for this config
* @param certreq TRUE to send a certificate request
* @param force_encap enforce UDP encapsulation by faking NATD notify
* @param me address/DNS name of local peer
* @param my_allow_any allow override of local address by any address
* @param my_port IKE port to use as source, 500 uses IKEv2 port floating
* @param other address/DNS name of remote peer
* @param other_allow_any allow override of remote address by any address
* @param other_port IKE port to use as dest, 500 uses IKEv2 port floating
* @param fragmentation use IKEv1 fragmentation
* @param dscp DSCP value to send IKE packets with
* @return ike_cfg_t object.
*/
ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
char *me, bool my_allow_any, u_int16_t my_port,
char *other, bool other_allow_any, u_int16_t other_port,
char *me, u_int16_t my_port,
char *other, u_int16_t other_port,
fragmentation_t fragmentation, u_int8_t dscp);
#endif /** IKE_CFG_H_ @}*/