ikev2: support transport mode over NAT
This commit is contained in:
@@ -342,6 +342,79 @@ static linked_list_t *get_dynamic_hosts(ike_sa_t *ike_sa, bool local)
|
|||||||
return list;
|
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:
|
* Install a CHILD_SA for usage, return value:
|
||||||
* - FAILED: no acceptable proposal
|
* - 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 nonce_i, nonce_r;
|
||||||
chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
|
chunk_t encr_i = chunk_empty, encr_r = chunk_empty;
|
||||||
chunk_t integ_i = chunk_empty, integ_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;
|
host_t *me, *other;
|
||||||
bool private;
|
bool private;
|
||||||
|
|
||||||
@@ -416,24 +489,16 @@ static status_t select_and_install(private_child_create_t *this,
|
|||||||
{
|
{
|
||||||
nonce_i = this->my_nonce;
|
nonce_i = this->my_nonce;
|
||||||
nonce_r = this->other_nonce;
|
nonce_r = this->other_nonce;
|
||||||
my_ts = this->tsi;
|
my_ts = narrow_ts(this, TRUE, this->tsi);
|
||||||
other_ts = this->tsr;
|
other_ts = narrow_ts(this, FALSE, this->tsr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
nonce_r = this->my_nonce;
|
nonce_r = this->my_nonce;
|
||||||
nonce_i = this->other_nonce;
|
nonce_i = this->other_nonce;
|
||||||
my_ts = this->tsr;
|
my_ts = narrow_ts(this, TRUE, this->tsr);
|
||||||
other_ts = this->tsi;
|
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)
|
if (this->initiator)
|
||||||
{
|
{
|
||||||
@@ -490,11 +555,6 @@ static status_t select_and_install(private_child_create_t *this,
|
|||||||
this->mode = MODE_TUNNEL;
|
this->mode = MODE_TUNNEL;
|
||||||
DBG1(DBG_IKE, "not using transport mode, not host-to-host");
|
DBG1(DBG_IKE, "not using transport mode, not host-to-host");
|
||||||
}
|
}
|
||||||
else if (this->ike_sa->has_condition(this->ike_sa, COND_NAT_ANY))
|
|
||||||
{
|
|
||||||
this->mode = MODE_TUNNEL;
|
|
||||||
DBG1(DBG_IKE, "not using transport mode, connection NATed");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case MODE_BEET:
|
case MODE_BEET:
|
||||||
if (!ts_list_is_host(this->tsi, NULL) ||
|
if (!ts_list_is_host(this->tsi, NULL) ||
|
||||||
@@ -895,12 +955,6 @@ METHOD(task_t, build_i, status_t,
|
|||||||
this->proposals = this->config->get_proposals(this->config,
|
this->proposals = this->config->get_proposals(this->config,
|
||||||
this->dh_group == MODP_NONE);
|
this->dh_group == MODP_NONE);
|
||||||
this->mode = this->config->get_mode(this->config);
|
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->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,
|
this->ike_sa->get_other_host(this->ike_sa), this->config, this->reqid,
|
||||||
@@ -996,10 +1050,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,
|
METHOD(task_t, build_r, status_t,
|
||||||
private_child_create_t *this, message_t *message)
|
private_child_create_t *this, message_t *message)
|
||||||
{
|
{
|
||||||
peer_cfg_t *peer_cfg;
|
|
||||||
payload_t *payload;
|
payload_t *payload;
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
bool no_dh = TRUE, ike_auth = FALSE;
|
bool no_dh = TRUE, ike_auth = FALSE;
|
||||||
@@ -1034,19 +1155,10 @@ METHOD(task_t, build_r, status_t,
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
peer_cfg = this->ike_sa->get_peer_cfg(this->ike_sa);
|
if (this->config == NULL)
|
||||||
if (!this->config && peer_cfg && this->tsi && this->tsr)
|
|
||||||
{
|
{
|
||||||
linked_list_t *listr, *listi;
|
this->config = select_child_cfg(this);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->config == NULL)
|
if (this->config == NULL)
|
||||||
{
|
{
|
||||||
DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable",
|
DBG1(DBG_IKE, "traffic selectors %#R=== %#R inacceptable",
|
||||||
@@ -1057,6 +1169,8 @@ METHOD(task_t, build_r, status_t,
|
|||||||
return SUCCESS;
|
return SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->mode = this->config->get_mode(this->config);
|
||||||
|
|
||||||
/* check if ike_config_t included non-critical error notifies */
|
/* check if ike_config_t included non-critical error notifies */
|
||||||
enumerator = message->create_payload_enumerator(message);
|
enumerator = message->create_payload_enumerator(message);
|
||||||
while (enumerator->enumerate(enumerator, &payload))
|
while (enumerator->enumerate(enumerator, &payload))
|
||||||
|
|||||||
Reference in New Issue
Block a user