Commit 6e355488d0 for strongswan.org
commit 6e355488d02030df3df2523bb1ab33fd87cd40d3
Author: Tobias Brunner <tobias@strongswan.org>
Date: Mon Mar 16 11:39:44 2026 +0100
kernel-netlink: Use state migration on newer kernels
This allows to properly update an SA even if lockdown is set to
confidentiality.
We now always pass the reqid in the new_reqid field as the kernel just
adopts the passed value (even if zero). The previous reqid is passed
in a new field so changes can be detected (e.g. in the kernel-pfkey
plugin).
We can't use the XFRM_MIGRATE_STATE_UPDATE_H2H_SEL flag, at least for
BEET mode, as that always sets the new addresses in the selector of the
migrated SA. So we just set the selector the same way we do in add_sa().
When only updating SAs, we now still update the TS and store the new
ones.
As we don't know which HW offload mode worked during the initial
installation (and it might not be the same on a different interface),
we use the same approach here but determine the address from the new
information.
Also changed the technically incorrect `ntohs()` calls to `htons()` in
the existing code.
Another change is that we now fallback to rekeying for any failures
that occur when updating SAs. If the update for the outbound SA fails
after we were able to update the inbound SA, we try to restore the latter
and fallback to deleting it if that fails. Otherwise, we won't be able
to delete the migrated inbound SA after the rekeying.
diff --git a/src/libcharon/kernel/kernel_ipsec.h b/src/libcharon/kernel/kernel_ipsec.h
index b6de950071..39ba6f2713 100644
--- a/src/libcharon/kernel/kernel_ipsec.h
+++ b/src/libcharon/kernel/kernel_ipsec.h
@@ -129,6 +129,8 @@ struct kernel_ipsec_add_sa_t {
* Data required to update the hosts of an SA in the kernel
*/
struct kernel_ipsec_update_sa_t {
+ /** Mode (tunnel, transport...) */
+ ipsec_mode_t mode;
/** CPI in case IPComp is used */
uint16_t cpi;
/** New source address */
@@ -139,8 +141,20 @@ struct kernel_ipsec_update_sa_t {
bool encap;
/** TRUE to enable UDP encapsulation */
bool new_encap;
- /** New reqid, or 0 if unchanged */
+ /** Old reqid */
+ uint32_t old_reqid;
+ /** New reqid, might be the same as old_reqid */
uint32_t new_reqid;
+ /** HW offload mode */
+ hw_offload_t hw_offload;
+ /** List of updated source traffic selectors */
+ linked_list_t *src_ts;
+ /** List of updated destination traffic selectors */
+ linked_list_t *dst_ts;
+ /** Network interface restricting policy */
+ char *interface;
+ /** TRUE if this is an inbound SA */
+ bool inbound;
};
/**
diff --git a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
index 4eb6195745..0236d2073d 100644
--- a/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
+++ b/src/libcharon/plugins/kernel_netlink/kernel_netlink_ipsec.c
@@ -381,6 +381,11 @@ struct private_kernel_netlink_ipsec_t {
*/
bool sa_dir;
+ /**
+ * Whether the kernel supports migrating SAs
+ */
+ bool sa_migrate;
+
/**
* Whether to install routes along policies
*/
@@ -1217,6 +1222,9 @@ CALLBACK(receive_events, void,
case XFRM_MSG_MAPPING:
process_mapping(this, hdr);
break;
+ case XFRM_MSG_MIGRATE_STATE:
+ /* ignore these events we receive for our own SA updates */
+ break;
default:
DBG1(DBG_KNL, "received unknown event from XFRM event "
"socket: %d", hdr->nlmsg_type);
@@ -1685,22 +1693,20 @@ out:
}
/**
- * Add a HW offload attribute to the given SA-related message.
+ * Add a HW offload attribute for the given local address to the given
+ * SA-related message.
*/
-static bool add_hw_offload_sa(struct nlmsghdr *hdr, int buflen,
- kernel_ipsec_sa_id_t *id,
- kernel_ipsec_add_sa_t *data,
+static bool add_hw_offload_sa(struct nlmsghdr *hdr, int buflen, host_t *local,
+ bool inbound, hw_offload_t hw_offload,
struct xfrm_user_offload **offload)
{
- host_t *local = data->inbound ? id->dst : id->src;
-
- if (!add_hw_offload(hdr, buflen, local, NULL, data->hw_offload, offload))
+ if (!add_hw_offload(hdr, buflen, local, NULL, hw_offload, offload))
{
return FALSE;
}
else if (*offload)
{
- (*offload)->flags |= data->inbound ? XFRM_OFFLOAD_INBOUND : 0;
+ (*offload)->flags |= inbound ? XFRM_OFFLOAD_INBOUND : 0;
}
return TRUE;
}
@@ -2257,7 +2263,9 @@ METHOD(kernel_ipsec_t, add_sa, status_t,
}
DBG2(DBG_KNL, " HW offload: %N", hw_offload_names, data->hw_offload);
- if (!add_hw_offload_sa(hdr, sizeof(request), id, data, &offload))
+ if (!add_hw_offload_sa(hdr, sizeof(request),
+ data->inbound ? id->dst : id->src, data->inbound,
+ data->hw_offload, &offload))
{
DBG1(DBG_KNL, "failed to configure HW offload");
goto failed;
@@ -2586,6 +2594,156 @@ METHOD(kernel_ipsec_t, del_sa, status_t,
}
}
+/**
+ * Migrate an SA if supported by the kernel.
+ */
+static status_t migrate_sa(private_kernel_netlink_ipsec_t *this,
+ kernel_ipsec_sa_id_t *id,
+ kernel_ipsec_update_sa_t *data)
+{
+ netlink_buf_t request;
+ struct nlmsghdr *hdr;
+ struct xfrm_user_migrate_state *migrate;
+ struct xfrm_encap_tmpl *encap;
+ struct xfrm_user_offload *offload = NULL;
+ ipsec_mode_t mode = data->mode, original_mode = data->mode;
+ traffic_selector_t *first_src_ts, *first_dst_ts;
+ status_t status = FAILED;
+ char markstr[32] = "";
+
+ /* if IPComp is used, we first migrate the IPComp SA */
+ if (data->cpi)
+ {
+ kernel_ipsec_sa_id_t ipcomp_id = {
+ .src = id->src,
+ .dst = id->dst,
+ .spi = htonl(ntohs(data->cpi)),
+ .proto = IPPROTO_COMP,
+ .mark = id->mark,
+ .if_id = id->if_id,
+ };
+ kernel_ipsec_update_sa_t ipcomp = {
+ .mode = data->mode,
+ .new_src = data->new_src,
+ .new_dst = data->new_dst,
+ .new_reqid = data->new_reqid,
+ .src_ts = data->src_ts,
+ .dst_ts = data->dst_ts,
+ };
+ migrate_sa(this, &ipcomp_id, &ipcomp);
+ mode = MODE_TRANSPORT;
+ }
+
+ memset(&request, 0, sizeof(request));
+ format_mark(markstr, sizeof(markstr), id->mark);
+
+ DBG2(DBG_KNL, "migrating SAD entry with SPI %.8x%s from %#H..%#H to "
+ "%#H..%#H", ntohl(id->spi), markstr, id->src, id->dst, data->new_src,
+ data->new_dst);
+
+ hdr = &request.hdr;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ hdr->nlmsg_type = XFRM_MSG_MIGRATE_STATE;
+ hdr->nlmsg_len = NLMSG_LENGTH(sizeof(struct xfrm_user_migrate_state));
+
+ migrate = NLMSG_DATA(hdr);
+ host2xfrm(id->dst, &migrate->id.daddr);
+ migrate->id.spi = id->spi;
+ migrate->id.proto = id->proto;
+ migrate->id.family = id->dst->get_family(id->dst);
+ migrate->old_mark.v = id->mark.value;
+ migrate->old_mark.m = id->mark.mask;
+
+ host2xfrm(data->new_src, &migrate->new_saddr);
+ host2xfrm(data->new_dst, &migrate->new_daddr);
+ migrate->new_reqid = data->new_reqid;
+ migrate->new_family = data->new_dst->get_family(data->new_dst);
+
+ /* set selector using the same condition as in add_sa() */
+ if ((mode == MODE_TRANSPORT || mode == MODE_BEET) &&
+ original_mode != MODE_TUNNEL)
+ {
+ if (data->src_ts->get_first(data->src_ts,
+ (void**)&first_src_ts) == SUCCESS &&
+ data->dst_ts->get_first(data->dst_ts,
+ (void**)&first_dst_ts) == SUCCESS)
+ {
+ migrate->new_sel = ts2selector(first_src_ts, first_dst_ts,
+ data->interface);
+ if (!this->proto_port_transport)
+ {
+ migrate->new_sel.proto = 0;
+ migrate->new_sel.dport = migrate->new_sel.dport_mask = 0;
+ migrate->new_sel.sport = migrate->new_sel.sport_mask = 0;
+ }
+ }
+ }
+
+ if (data->new_encap)
+ { /* enable/update encap */
+ encap = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP,
+ sizeof(*encap));
+ if (!encap)
+ {
+ goto failed;
+ }
+ encap->encap_type = UDP_ENCAP_ESPINUDP;
+ encap->encap_sport = htons(data->new_src->get_port(data->new_src));
+ encap->encap_dport = htons(data->new_dst->get_port(data->new_dst));
+ memset(&encap->encap_oa, 0, sizeof (xfrm_address_t));
+ }
+ else if (data->encap)
+ { /* disable encap with an empty attribute (encap_type = 0) */
+ encap = netlink_reserve(hdr, sizeof(request), XFRMA_ENCAP,
+ sizeof(*encap));
+ if (!encap)
+ {
+ goto failed;
+ }
+ }
+
+ if (id->proto != IPPROTO_COMP)
+ {
+ if (!add_hw_offload_sa(hdr, sizeof(request),
+ data->inbound ? data->new_dst : data->new_src,
+ data->inbound, data->hw_offload, &offload))
+ {
+ DBG1(DBG_KNL, "failed to configure HW offload during migration");
+ goto failed;
+ }
+ /* clear potentially stale offload state when e.g. switching to an
+ * interface that doesn't support offloading */
+ if (!offload)
+ {
+ migrate->flags |= XFRM_MIGRATE_STATE_CLEAR_OFFLOAD;
+ }
+ }
+
+ status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
+
+ if (status != SUCCESS && offload && data->hw_offload == HW_OFFLOAD_AUTO)
+ {
+ DBG1(DBG_KNL, "failed to migrate SA with %N HW offload, trying with "
+ "%N HW offload", hw_offload_names, HW_OFFLOAD_PACKET,
+ hw_offload_names, HW_OFFLOAD_CRYPTO);
+ offload->flags &= ~XFRM_OFFLOAD_PACKET;
+ status = this->socket_xfrm->send_ack(this->socket_xfrm, hdr);
+ }
+
+ if (status != SUCCESS)
+ {
+ DBG1(DBG_KNL, "unable to migrate SAD entry with SPI %.8x%s",
+ ntohl(id->spi), markstr);
+ status = FAILED;
+ goto failed;
+ }
+
+ status = SUCCESS;
+
+failed:
+ return status;
+}
+
/**
* Check if the kernel's lockdown feature is set to "confidentiality", which
* means it won't allow userland to access confidential information like IPsec
@@ -2631,6 +2789,14 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
traffic_selector_t *ts;
char markstr[32] = "";
+ /* fallback to the old approach if this fails (e.g. because the kernel
+ * is compiled without the feature) */
+ if (id->proto != IPPROTO_COMP && this->sa_migrate &&
+ migrate_sa(this, id, data) == SUCCESS)
+ {
+ return SUCCESS;
+ }
+
if (lockdown_confidentiality())
{
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x due to "
@@ -2738,10 +2904,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
sa = NLMSG_DATA(hdr);
memcpy(sa, NLMSG_DATA(out_hdr), sizeof(struct xfrm_usersa_info));
sa->family = data->new_dst->get_family(data->new_dst);
- if (data->new_reqid)
- {
- sa->reqid = data->new_reqid;
- }
+ sa->reqid = data->new_reqid;
if (!id->src->ip_equals(id->src, data->new_src))
{
@@ -2781,8 +2944,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
if (rta->rta_type == XFRMA_ENCAP)
{ /* update encap tmpl */
encap = RTA_DATA(rta);
- encap->encap_sport = ntohs(data->new_src->get_port(data->new_src));
- encap->encap_dport = ntohs(data->new_dst->get_port(data->new_dst));
+ encap->encap_sport = htons(data->new_src->get_port(data->new_src));
+ encap->encap_dport = htons(data->new_dst->get_port(data->new_dst));
}
if (rta->rta_type == XFRMA_OFFLOAD_DEV)
{ /* update offload device */
@@ -2822,8 +2985,8 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
goto failed;
}
encap->encap_type = UDP_ENCAP_ESPINUDP;
- encap->encap_sport = ntohs(data->new_src->get_port(data->new_src));
- encap->encap_dport = ntohs(data->new_dst->get_port(data->new_dst));
+ encap->encap_sport = htons(data->new_src->get_port(data->new_src));
+ encap->encap_dport = htons(data->new_dst->get_port(data->new_dst));
memset(&encap->encap_oa, 0, sizeof (xfrm_address_t));
}
@@ -4361,6 +4524,8 @@ static void check_kernel_features(private_kernel_netlink_ipsec_t *this)
/* 6.10 added support for SA direction and enforces certain
* flags e.g. 0 replay window for outbound SAs */
this->sa_dir = a > 6 || (a == 6 && b >= 10);
+ /* 7.2 added support for SA migration */
+ this->sa_migrate = a > 7 || (a == 7 && b >= 2);
break;
default:
break;
diff --git a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
index a5b04d8d74..b4eb2b5fb9 100644
--- a/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
+++ b/src/libcharon/plugins/kernel_pfkey/kernel_pfkey_ipsec.c
@@ -2007,7 +2007,7 @@ METHOD(kernel_ipsec_t, update_sa, status_t,
size_t len;
status_t status = FAILED;
- if (data->new_reqid)
+ if (data->old_reqid != data->new_reqid)
{
DBG1(DBG_KNL, "unable to update SAD entry with SPI %.8x: reqid "
"change is not supported", ntohl(id->spi));
diff --git a/src/libcharon/sa/child_sa.c b/src/libcharon/sa/child_sa.c
index 0805bfb9e5..e21c3eb198 100644
--- a/src/libcharon/sa/child_sa.c
+++ b/src/libcharon/sa/child_sa.c
@@ -1730,9 +1730,19 @@ CALLBACK(reinstall_vip, void,
/**
* Update addresses and encap state of IPsec SAs in the kernel
*/
-static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
- bool encap, uint32_t reqid)
+static bool update_sas(private_child_sa_t *this, host_t *me, host_t *other,
+ bool encap, uint32_t reqid, array_t *new_my_ts,
+ array_t *new_other_ts)
{
+ linked_list_t *my_ts, *other_ts;
+ bool success = FALSE, inbound_updated = FALSE;
+
+ /* BEET requires the bound address from the traffic selectors */
+ my_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(new_my_ts));
+ other_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(new_other_ts));
+
/* update our (initiator) SA */
if (this->inbound_installed)
{
@@ -1745,18 +1755,25 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
.if_id = this->if_id_in,
};
kernel_ipsec_update_sa_t sa = {
+ .mode = this->mode,
.cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
.new_src = other,
.new_dst = me,
.encap = this->encap,
.new_encap = encap,
- .new_reqid = reqid,
+ .old_reqid = this->reqid,
+ .new_reqid = reqid ?: this->reqid,
+ .hw_offload = this->config->get_hw_offload(this->config),
+ .src_ts = other_ts,
+ .dst_ts = my_ts,
+ .inbound = TRUE,
};
if (charon->kernel->update_sa(charon->kernel, &id,
- &sa) == NOT_SUPPORTED)
+ &sa) != SUCCESS)
{
- return NOT_SUPPORTED;
+ goto failed;
}
+ inbound_updated = TRUE;
}
/* update his (responder) SA */
@@ -1771,21 +1788,86 @@ static status_t update_sas(private_child_sa_t *this, host_t *me, host_t *other,
.if_id = this->if_id_out,
};
kernel_ipsec_update_sa_t sa = {
+ .mode = this->mode,
.cpi = this->ipcomp != IPCOMP_NONE ? this->other_cpi : 0,
.new_src = me,
.new_dst = other,
.encap = this->encap,
.new_encap = encap,
- .new_reqid = reqid,
+ .old_reqid = this->reqid,
+ .new_reqid = reqid ?: this->reqid,
+ .hw_offload = this->config->get_hw_offload(this->config),
+ .src_ts = my_ts,
+ .dst_ts = other_ts,
+ .interface = this->config->get_interface(this->config),
};
if (charon->kernel->update_sa(charon->kernel, &id,
- &sa) == NOT_SUPPORTED)
+ &sa) != SUCCESS)
{
- return NOT_SUPPORTED;
+ if (inbound_updated)
+ {
+ /* restore the already migrated inbound SA as the rekey fallback
+ * is otherwise not able to delete it using the old addresses */
+ kernel_ipsec_sa_id_t restore_id = {
+ .src = other,
+ .dst = me,
+ .spi = this->my_spi,
+ .proto = proto_ike2ip(this->protocol),
+ .mark = mark_in_sa(this),
+ .if_id = this->if_id_in,
+ };
+ kernel_ipsec_update_sa_t restore_sa = {
+ .mode = this->mode,
+ .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
+ .new_src = this->other_addr,
+ .new_dst = this->my_addr,
+ .encap = encap,
+ .new_encap = this->encap,
+ .old_reqid = reqid ?: this->reqid,
+ .new_reqid = this->reqid,
+ .hw_offload = this->config->get_hw_offload(this->config),
+ .inbound = TRUE,
+ };
+ linked_list_t *restore_my_ts, *restore_other_ts;
+
+ /* we need to use the original TS */
+ restore_my_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(this->my_ts));
+ restore_other_ts = linked_list_create_from_enumerator(
+ array_create_enumerator(this->other_ts));
+ restore_sa.src_ts = restore_other_ts;
+ restore_sa.dst_ts = restore_my_ts;
+
+ if (charon->kernel->update_sa(charon->kernel, &restore_id,
+ &restore_sa) != SUCCESS)
+ {
+ /* delete the migrated inbound SA if we can't restore it
+ * to avoid it's left in the kernel after the rekeying */
+ kernel_ipsec_del_sa_t del = {
+ .cpi = this->ipcomp != IPCOMP_NONE ? this->my_cpi : 0,
+ };
+ DBG1(DBG_CHD, "failed to restore inbound SA with SPI "
+ "0x%.8x, deleting it", ntohl(this->my_spi));
+ charon->kernel->del_sa(charon->kernel, &restore_id, &del);
+ }
+ else
+ {
+ DBG1(DBG_CHD, "restored inbound SA with SPI 0x%.8x to "
+ "previous addresses after failure to update outbound "
+ "SA", ntohl(this->my_spi));
+ }
+ restore_my_ts->destroy(restore_my_ts);
+ restore_other_ts->destroy(restore_other_ts);
+ }
+ goto failed;
}
}
- /* we currently ignore the actual return values above */
- return SUCCESS;
+ success = TRUE;
+
+failed:
+ my_ts->destroy(my_ts);
+ other_ts->destroy(other_ts);
+ return success;
}
/**
@@ -1816,6 +1898,7 @@ METHOD(child_sa_t, update, status_t,
private_child_sa_t *this, host_t *me, host_t *other, linked_list_t *vips,
bool encap)
{
+ array_t *new_my_ts = NULL, *new_other_ts = NULL;
child_sa_state_t old;
bool transport_proxy_mode;
@@ -1838,11 +1921,9 @@ METHOD(child_sa_t, update, status_t,
ipsec_sa_cfg_t my_sa, other_sa;
enumerator_t *enumerator;
traffic_selector_t *my_ts, *other_ts;
- array_t *new_my_ts = NULL, *new_other_ts = NULL;
policy_priority_t priority;
uint32_t manual_prio, new_reqid = 0;
- status_t state;
- bool outbound;
+ bool outbound, sas_updated;
prepare_sa_cfg(this, &my_sa, &other_sa);
manual_prio = this->config->get_manual_prio(this->config);
@@ -1897,11 +1978,13 @@ METHOD(child_sa_t, update, status_t,
}
/* update the IPsec SAs */
- state = update_sas(this, me, other, encap, new_reqid);
+ sas_updated = update_sas(this, me, other, encap, new_reqid,
+ new_my_ts ?: this->my_ts,
+ new_other_ts ?: this->other_ts);
/* install new/updated policies only if we were able to update the
* SAs, otherwise we reinstall the old policies further below */
- if (state != NOT_SUPPORTED)
+ if (sas_updated)
{
/* we reinstall the virtual IP to handle interface roaming
* correctly */
@@ -1933,7 +2016,7 @@ METHOD(child_sa_t, update, status_t,
while (enumerator->enumerate(enumerator, &my_ts, &other_ts))
{
/* reinstall the previous policies if we can't update the SAs */
- if (state == NOT_SUPPORTED)
+ if (!sas_updated)
{
install_policies_internal(this, this->my_addr, this->other_addr,
my_ts, other_ts, &my_sa, &other_sa, POLICY_IPSEC,
@@ -1949,7 +2032,7 @@ METHOD(child_sa_t, update, status_t,
}
enumerator->destroy(enumerator);
- if (state == NOT_SUPPORTED)
+ if (!sas_updated)
{
if (new_reqid &&
charon->kernel->release_reqid(charon->kernel,
@@ -1977,28 +2060,45 @@ METHOD(child_sa_t, update, status_t,
this->unique_id);
this->reqid = new_reqid;
}
- if (new_my_ts)
+ }
+ else if (!transport_proxy_mode)
+ {
+ if (!me->ip_equals(me, this->my_addr))
{
- array_destroy_offset(this->my_ts,
- offsetof(traffic_selector_t, destroy));
- this->my_ts = new_my_ts;
+ new_my_ts = array_create(0, 0);
+ update_ts(this->my_addr, me, this->my_ts, new_my_ts);
}
- if (new_other_ts)
+ if (!other->ip_equals(other, this->other_addr))
{
- array_destroy_offset(this->other_ts,
- offsetof(traffic_selector_t, destroy));
- this->other_ts = new_other_ts;
+ new_other_ts = array_create(0, 0);
+ update_ts(this->other_addr, other, this->other_ts, new_other_ts);
}
- }
- else if (!transport_proxy_mode)
- {
- if (update_sas(this, me, other, encap, 0) == NOT_SUPPORTED)
+ if (!update_sas(this, me, other, encap, 0,
+ new_my_ts ?: this->my_ts,
+ new_other_ts ?: this->other_ts))
{
+ array_destroy_offset(new_my_ts,
+ offsetof(traffic_selector_t, destroy));
+ array_destroy_offset(new_other_ts,
+ offsetof(traffic_selector_t, destroy));
set_state(this, old);
return NOT_SUPPORTED;
}
}
+ if (new_my_ts)
+ {
+ array_destroy_offset(this->my_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->my_ts = new_my_ts;
+ }
+ if (new_other_ts)
+ {
+ array_destroy_offset(this->other_ts,
+ offsetof(traffic_selector_t, destroy));
+ this->other_ts = new_other_ts;
+ }
+
if (!transport_proxy_mode)
{
/* apply hosts */