Merge branch 'nat-transport'
Enable transport mode in NAT situations when using IKEv2. Additionally brings an extended leftsubnet format, where each subnet can take a separate protocol and port.
This commit is contained in:
+34
-24
@@ -731,29 +731,10 @@ different from the default additionally requires a socket implementation that
|
||||
listens on this port.
|
||||
.TP
|
||||
.BR leftprotoport " = <protocol>/<port>"
|
||||
restrict the traffic selector to a single protocol and/or port.
|
||||
Examples:
|
||||
.B leftprotoport=tcp/http
|
||||
or
|
||||
.B leftprotoport=6/80
|
||||
or
|
||||
.B leftprotoport=udp
|
||||
or
|
||||
.BR leftprotoport=/53 .
|
||||
Instead of omitting either value
|
||||
.B %any
|
||||
can be used to the same effect, e.g.
|
||||
.B leftprotoport=udp/%any
|
||||
or
|
||||
.BR leftprotoport=%any/53 .
|
||||
|
||||
The port value can alternatively take the value
|
||||
.B %opaque
|
||||
for RFC 4301 OPAQUE selectors, or a numerical range in the form
|
||||
.BR 1024-65535 .
|
||||
None of the kernel backends currently supports opaque or port ranges and uses
|
||||
.B %any
|
||||
for policy installation instead.
|
||||
restrict the traffic selector to a single protocol and/or port. This option
|
||||
is now deprecated, protocol/port information can be defined for each subnet
|
||||
directly in
|
||||
.BR leftsubnet .
|
||||
.TP
|
||||
.BR leftsigkey " = <raw public key> | <path to public key>"
|
||||
the left participant's public key for public key signature authentication,
|
||||
@@ -807,7 +788,7 @@ echoed back. Also supported are address pools expressed as
|
||||
or the use of an external IP address pool using %\fIpoolname\fR,
|
||||
where \fIpoolname\fR is the name of the IP address pool used for the lookup.
|
||||
.TP
|
||||
.BR leftsubnet " = <ip subnet>"
|
||||
.BR leftsubnet " = <ip subnet>[:<proto/port>][,...]"
|
||||
private subnet behind the left participant, expressed as
|
||||
\fInetwork\fB/\fInetmask\fR;
|
||||
if omitted, essentially assumed to be \fIleft\fB/32\fR,
|
||||
@@ -818,6 +799,35 @@ implementations, make sure to configure identical subnets in such
|
||||
configurations. IKEv2 supports multiple subnets separated by commas. IKEv1 only
|
||||
interprets the first subnet of such a definition, unless the Cisco Unity
|
||||
extension plugin is enabled.
|
||||
|
||||
The part in each subnet following an optional colon specifies a protocol/port
|
||||
to restrict the selector for that subnet.
|
||||
|
||||
Example:
|
||||
.BR leftsubnet=10.0.0.1:tcp/http,10.0.0.2:6/80,10.0.0.3:udp,10.0.0.0/16:/53 .
|
||||
Instead of omitting either value
|
||||
.B %any
|
||||
can be used to the same effect, e.g.
|
||||
.BR leftsubnet=10.0.0.3:udp/%any,10.0.0.0/16=%any/53 .
|
||||
|
||||
The port value can alternatively take the value
|
||||
.B %opaque
|
||||
for RFC 4301 OPAQUE selectors, or a numerical range in the form
|
||||
.BR 1024-65535 .
|
||||
None of the kernel backends currently supports opaque or port ranges and uses
|
||||
.B %any
|
||||
for policy installation instead.
|
||||
|
||||
Instead of specifying a subnet,
|
||||
.B %dynamic
|
||||
can be used to replace it with the IKE address, having the same effect
|
||||
as omitting
|
||||
.B leftsubnet
|
||||
completely. Using
|
||||
.B %dynamic
|
||||
can be used to define multiple dynamic selectors, each having a potentially
|
||||
different protocol/port definiton.
|
||||
|
||||
.TP
|
||||
.BR leftupdown " = <path>"
|
||||
what ``updown'' script to run to adjust routing and/or firewalling
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <threading/mutex.h>
|
||||
#include <utils/lexparser.h>
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
typedef struct private_stroke_config_t private_stroke_config_t;
|
||||
|
||||
/**
|
||||
@@ -882,6 +884,89 @@ static peer_cfg_t *build_peer_cfg(private_stroke_config_t *this,
|
||||
return peer_cfg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a protoport specifier
|
||||
*/
|
||||
static bool parse_protoport(char *token, u_int16_t *from_port,
|
||||
u_int16_t *to_port, u_int8_t *protocol)
|
||||
{
|
||||
char *sep, *port = "", *endptr;
|
||||
struct protoent *proto;
|
||||
struct servent *svc;
|
||||
long int p;
|
||||
|
||||
sep = strchr(token, '/');
|
||||
if (sep)
|
||||
{ /* protocol/port */
|
||||
*sep = '\0';
|
||||
port = sep + 1;
|
||||
}
|
||||
|
||||
if (streq(token, "%any"))
|
||||
{
|
||||
*protocol = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
proto = getprotobyname(token);
|
||||
if (proto)
|
||||
{
|
||||
*protocol = proto->p_proto;
|
||||
}
|
||||
else
|
||||
{
|
||||
p = strtol(token, &endptr, 0);
|
||||
if ((*token && *endptr) || p < 0 || p > 0xff)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*protocol = (u_int8_t)p;
|
||||
}
|
||||
}
|
||||
if (streq(port, "%any"))
|
||||
{
|
||||
*from_port = 0;
|
||||
*to_port = 0xffff;
|
||||
}
|
||||
else if (streq(port, "%opaque"))
|
||||
{
|
||||
*from_port = 0xffff;
|
||||
*to_port = 0;
|
||||
}
|
||||
else if (*port)
|
||||
{
|
||||
svc = getservbyname(port, NULL);
|
||||
if (svc)
|
||||
{
|
||||
*from_port = *to_port = ntohs(svc->s_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = strtol(port, &endptr, 0);
|
||||
if (p < 0 || p > 0xffff)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
*from_port = p;
|
||||
if (*endptr == '-')
|
||||
{
|
||||
port = endptr + 1;
|
||||
p = strtol(port, &endptr, 0);
|
||||
if (p < 0 || p > 0xffff)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
*to_port = p;
|
||||
if (*endptr)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* build a traffic selector from a stroke_end
|
||||
*/
|
||||
@@ -913,13 +998,38 @@ static void add_ts(private_stroke_config_t *this,
|
||||
else
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
char *subnet;
|
||||
char *subnet, *pos;
|
||||
u_int16_t from_port, to_port;
|
||||
u_int8_t proto;
|
||||
|
||||
enumerator = enumerator_create_token(end->subnets, ",", " ");
|
||||
while (enumerator->enumerate(enumerator, &subnet))
|
||||
{
|
||||
ts = traffic_selector_create_from_cidr(subnet, end->protocol,
|
||||
end->from_port, end->to_port);
|
||||
from_port = end->from_port;
|
||||
to_port = end->to_port;
|
||||
proto = end->protocol;
|
||||
|
||||
pos = strchr(subnet, ':');
|
||||
if (pos)
|
||||
{
|
||||
*(pos++) = '\0';
|
||||
if (!parse_protoport(pos, &from_port, &to_port, &proto))
|
||||
{
|
||||
DBG1(DBG_CFG, "invalid proto/port: %s, skipped subnet",
|
||||
pos);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (streq(subnet, "%dynamic"))
|
||||
{
|
||||
ts = traffic_selector_create_dynamic(proto,
|
||||
from_port, to_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
ts = traffic_selector_create_from_cidr(subnet, proto,
|
||||
from_port, to_port);
|
||||
}
|
||||
if (ts)
|
||||
{
|
||||
child_cfg->add_traffic_selector(child_cfg, local, ts);
|
||||
|
||||
@@ -342,6 +342,79 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitude any host address with NATed address in traffic selector
|
||||
*/
|
||||
static linked_list_t* get_transport_nat_ts(private_child_create_t *this,
|
||||
bool local, linked_list_t *in)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
linked_list_t *out;
|
||||
traffic_selector_t *ts;
|
||||
host_t *ike, *first = NULL;
|
||||
u_int8_t mask;
|
||||
|
||||
if (local)
|
||||
{
|
||||
ike = this->ike_sa->get_my_host(this->ike_sa);
|
||||
}
|
||||
else
|
||||
{
|
||||
ike = this->ike_sa->get_other_host(this->ike_sa);
|
||||
}
|
||||
|
||||
out = linked_list_create();
|
||||
|
||||
enumerator = in->create_enumerator(in);
|
||||
while (enumerator->enumerate(enumerator, &ts))
|
||||
{
|
||||
/* require that all selectors match the first "host" selector */
|
||||
if (ts->is_host(ts, first))
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
ts->to_subnet(ts, &first, &mask);
|
||||
}
|
||||
ts = ts->clone(ts);
|
||||
ts->set_address(ts, ike);
|
||||
out->insert_last(out, ts);
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
DESTROY_IF(first);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrow received traffic selectors with configuration
|
||||
*/
|
||||
static linked_list_t* narrow_ts(private_child_create_t *this, bool local,
|
||||
linked_list_t *in)
|
||||
{
|
||||
linked_list_t *hosts, *nat, *ts;
|
||||
ike_condition_t cond;
|
||||
|
||||
cond = local ? COND_NAT_HERE : COND_NAT_THERE;
|
||||
hosts = get_dynamic_hosts(this->ike_sa, local);
|
||||
|
||||
if (this->mode == MODE_TRANSPORT &&
|
||||
this->ike_sa->has_condition(this->ike_sa, cond))
|
||||
{
|
||||
nat = get_transport_nat_ts(this, local, in);
|
||||
ts = this->config->get_traffic_selectors(this->config, local, nat, hosts);
|
||||
nat->destroy_offset(nat, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
else
|
||||
{
|
||||
ts = this->config->get_traffic_selectors(this->config, local, in, hosts);
|
||||
}
|
||||
|
||||
hosts->destroy(hosts);
|
||||
|
||||
return ts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Install a CHILD_SA for usage, return value:
|
||||
* - FAILED: no acceptable proposal
|
||||
@@ -355,7 +428,7 @@ static status_t select_and_install(private_child_create_t *this,
|
||||
chunk_t nonce_i, nonce_r;
|
||||
chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
|
||||
chunk_t integ_i = chunk_empty, integ_r = chunk_empty;
|
||||
linked_list_t *my_ts, *other_ts, *list;
|
||||
linked_list_t *my_ts, *other_ts;
|
||||
host_t *me, *other;
|
||||
bool private;
|
||||
|
||||
@@ -416,24 +489,16 @@ static status_t select_and_install(private_child_create_t *this,
|
||||
{
|
||||
nonce_i = this->my_nonce;
|
||||
nonce_r = this->other_nonce;
|
||||
my_ts = this->tsi;
|
||||
other_ts = this->tsr;
|
||||
my_ts = narrow_ts(this, TRUE, this->tsi);
|
||||
other_ts = narrow_ts(this, FALSE, this->tsr);
|
||||
}
|
||||
else
|
||||
{
|
||||
nonce_r = this->my_nonce;
|
||||
nonce_i = this->other_nonce;
|
||||
my_ts = this->tsr;
|
||||
other_ts = this->tsi;
|
||||
my_ts = narrow_ts(this, TRUE, this->tsr);
|
||||
other_ts = narrow_ts(this, FALSE, this->tsi);
|
||||
}
|
||||
list = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
my_ts = this->config->get_traffic_selectors(this->config,
|
||||
TRUE, my_ts, list);
|
||||
list->destroy(list);
|
||||
list = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
other_ts = this->config->get_traffic_selectors(this->config,
|
||||
FALSE, other_ts, list);
|
||||
list->destroy(list);
|
||||
|
||||
if (this->initiator)
|
||||
{
|
||||
@@ -490,10 +555,9 @@ static status_t select_and_install(private_child_create_t *this,
|
||||
this->mode = MODE_TUNNEL;
|
||||
DBG1(DBG_IKE, "not using transport mode, not host-to-host");
|
||||
}
|
||||
else if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
|
||||
if (this->config->get_mode(this->config) != MODE_TRANSPORT)
|
||||
{
|
||||
this->mode = MODE_TUNNEL;
|
||||
DBG1(DBG_IKE, "not using transport mode, connection NATed");
|
||||
}
|
||||
break;
|
||||
case MODE_BEET:
|
||||
@@ -503,6 +567,10 @@ static status_t select_and_install(private_child_create_t *this,
|
||||
this->mode = MODE_TUNNEL;
|
||||
DBG1(DBG_IKE, "not using BEET mode, not host-to-host");
|
||||
}
|
||||
if (this->config->get_mode(this->config) != MODE_BEET)
|
||||
{
|
||||
this->mode = MODE_TUNNEL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -895,12 +963,6 @@ METHOD(task_t, build_i, status_t,
|
||||
this->proposals = this->config->get_proposals(this->config,
|
||||
this->dh_group == MODP_NONE);
|
||||
this->mode = this->config->get_mode(this->config);
|
||||
if (this->mode == MODE_TRANSPORT &&
|
||||
this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
|
||||
{
|
||||
this->mode = MODE_TUNNEL;
|
||||
DBG1(DBG_IKE, "not using transport mode, connection NATed");
|
||||
}
|
||||
|
||||
this->child_sa = child_sa_create(this->ike_sa->get_my_host(this->ike_sa),
|
||||
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
|
||||
@@ -996,10 +1058,77 @@ static void handle_child_sa_failure(private_child_create_t *this,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Substitute transport mode NAT selectors, if applicable
|
||||
*/
|
||||
static linked_list_t* get_ts_if_nat_transport(private_child_create_t *this,
|
||||
bool local, linked_list_t *in)
|
||||
{
|
||||
linked_list_t *out = NULL;
|
||||
ike_condition_t cond;
|
||||
|
||||
if (this->mode == MODE_TRANSPORT)
|
||||
{
|
||||
cond = local ? COND_NAT_HERE : COND_NAT_THERE;
|
||||
if (this->ike_sa->has_condition(this->ike_sa, cond))
|
||||
{
|
||||
out = get_transport_nat_ts(this, local, in);
|
||||
if (out->get_count(out) == 0)
|
||||
{
|
||||
out->destroy(out);
|
||||
out = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a matching CHILD config as responder
|
||||
*/
|
||||
static child_cfg_t* select_child_cfg(private_child_create_t *this)
|
||||
{
|
||||
peer_cfg_t *peer_cfg;
|
||||
child_cfg_t *child_cfg = NULL;;
|
||||
|
||||
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
|
||||
if (peer_cfg && this->tsi && this->tsr)
|
||||
{
|
||||
linked_list_t *listr, *listi, *tsr, *tsi;
|
||||
|
||||
tsr = get_ts_if_nat_transport(this, TRUE, this->tsr);
|
||||
tsi = get_ts_if_nat_transport(this, FALSE, this->tsi);
|
||||
|
||||
listr = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
listi = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
child_cfg = peer_cfg->select_child_cfg(peer_cfg,
|
||||
tsr ?: this->tsr, tsi ?: this->tsi,
|
||||
listr, listi);
|
||||
if ((tsi || tsr) && child_cfg &&
|
||||
child_cfg->get_mode(child_cfg) != MODE_TRANSPORT)
|
||||
{
|
||||
/* found a CHILD config, but it doesn't use transport mode */
|
||||
child_cfg->destroy(child_cfg);
|
||||
child_cfg = NULL;
|
||||
}
|
||||
if (!child_cfg && (tsi || tsr))
|
||||
{
|
||||
/* no match for the substituted NAT selectors, try it without */
|
||||
child_cfg = peer_cfg->select_child_cfg(peer_cfg,
|
||||
this->tsr, this->tsi, listr, listi);
|
||||
}
|
||||
listr->destroy(listr);
|
||||
listi->destroy(listi);
|
||||
DESTROY_OFFSET_IF(tsi, offsetof(traffic_selector_t, destroy));
|
||||
DESTROY_OFFSET_IF(tsr, offsetof(traffic_selector_t, destroy));
|
||||
}
|
||||
|
||||
return child_cfg;
|
||||
}
|
||||
|
||||
METHOD(task_t, build_r, status_t,
|
||||
private_child_create_t *this, message_t *message)
|
||||
{
|
||||
peer_cfg_t *peer_cfg;
|
||||
payload_t *payload;
|
||||
enumerator_t *enumerator;
|
||||
bool no_dh = TRUE, ike_auth = FALSE;
|
||||
@@ -1034,19 +1163,10 @@ METHOD(task_t, build_r, status_t,
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
|
||||
if (!this->config && peer_cfg && this->tsi && this->tsr)
|
||||
if (this->config == NULL)
|
||||
{
|
||||
linked_list_t *listr, *listi;
|
||||
|
||||
listr = get_dynamic_hosts(this->ike_sa, TRUE);
|
||||
listi = get_dynamic_hosts(this->ike_sa, FALSE);
|
||||
this->config = peer_cfg->select_child_cfg(peer_cfg,
|
||||
this->tsr, this->tsi, listr, listi);
|
||||
listr->destroy(listr);
|
||||
listi->destroy(listi);
|
||||
this->config = select_child_cfg(this);
|
||||
}
|
||||
|
||||
if (this->config == NULL)
|
||||
{
|
||||
DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable",
|
||||
|
||||
@@ -1224,6 +1224,12 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
|
||||
if(src_ts && dst_ts)
|
||||
{
|
||||
sa->sel = ts2selector(src_ts, dst_ts);
|
||||
/* don't install proto/port on SA. This would break
|
||||
* potential secondary SAs for the same address using a
|
||||
* different prot/port. */
|
||||
sa->sel.proto = 0;
|
||||
sa->sel.dport = sa->sel.dport_mask = 0;
|
||||
sa->sel.sport = sa->sel.sport_mask = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user