From d4793a445aa0aa10041db7625fd6444f11a16e02 Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Tue, 22 Oct 2019 19:05:53 +0200
Subject: [PATCH 3/4] allow forcing udp encap only for ipv4

---
 src/libcharon/config/ike_cfg.c          | 10 +++++++
 src/libcharon/config/ike_cfg.h          |  3 ++
 src/libcharon/sa/ikev2/tasks/ike_natd.c | 38 +++++++++++++++++++------
 src/libstrongswan/networking/packet.c   | 17 +++++++++++
 src/libstrongswan/networking/packet.h   |  6 ++++
 5 files changed, 66 insertions(+), 8 deletions(-)

diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c
index 79a344e45..3b2ce644b 100644
--- a/src/libcharon/config/ike_cfg.c
+++ b/src/libcharon/config/ike_cfg.c
@@ -99,6 +99,7 @@ struct private_ike_cfg_t {
 	 * enforce UDP encapsulation
 	 */
 	bool force_encap;
+	bool force_encap_v6;
 
 	/**
 	 * use IKE fragmentation
@@ -139,6 +140,12 @@ METHOD(ike_cfg_t, force_encap_, bool,
 	return this->force_encap;
 }
 
+METHOD(ike_cfg_t, force_encap_v6_, bool,
+	private_ike_cfg_t *this)
+{
+	return this->force_encap_v6;
+}
+
 METHOD(ike_cfg_t, fragmentation, fragmentation_t,
 	private_ike_cfg_t *this)
 {
@@ -388,6 +395,7 @@ METHOD(ike_cfg_t, equals, bool,
 		this->version == other->version &&
 		this->certreq == other->certreq &&
 		this->force_encap == other->force_encap &&
+		this->force_encap_v6 == other->force_encap_v6 &&
 		this->fragmentation == other->fragmentation &&
 		this->childless == other->childless &&
 		streq(this->me, other->me) &&
@@ -587,6 +595,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data)
 			.get_version = _get_version,
 			.send_certreq = _send_certreq,
 			.force_encap = _force_encap_,
+			.force_encap_v6 = _force_encap_v6_,
 			.fragmentation = _fragmentation,
 			.childless = _childless,
 			.resolve_me = _resolve_me,
@@ -611,6 +620,7 @@ ike_cfg_t *ike_cfg_create(ike_cfg_create_t *data)
 		.version = data->version,
 		.certreq = !data->no_certreq,
 		.force_encap = data->force_encap,
+		.force_encap_v6 = data->force_encap_v6,
 		.fragmentation = data->fragmentation,
 		.childless = data->childless,
 		.me = strdup(data->local),
diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h
index 1e11a1e56..530cf0dd9 100644
--- a/src/libcharon/config/ike_cfg.h
+++ b/src/libcharon/config/ike_cfg.h
@@ -214,6 +214,7 @@ struct ike_cfg_t {
 	 * @return				TRUE to enforce UDP encapsulation
 	 */
 	bool (*force_encap) (ike_cfg_t *this);
+	bool (*force_encap_v6) (ike_cfg_t *this);
 
 	/**
 	 * Use IKE fragmentation
@@ -283,6 +284,8 @@ struct ike_cfg_create_t {
 	bool no_certreq;
 	/** Enforce UDP encapsulation by faking NATD notify */
 	bool force_encap;
+	/** Enforce UDP encapsulation by faking NATD notify only for ipv6 */
+	bool force_encap_v6;
 	/** Use IKE fragmentation */
 	fragmentation_t fragmentation;
 	/** Childless IKE_SA configuration */
diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index 008abbb1e..e1b8caa15 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -75,20 +75,34 @@ struct private_ike_natd_t {
 	 * whether NAT mappings for our NATed address has changed
 	 */
 	bool mapping_changed;
+
+	/**
+	 * used address family
+	 */
+	int af;
 };
 
 /**
  * Check if UDP encapsulation has to be forced either by config or required
  * by the kernel interface
  */
-static bool force_encap(ike_cfg_t *ike_cfg)
+static bool force_encap(ike_cfg_t *ike_cfg, int af)
 {
-	if (!ike_cfg->force_encap(ike_cfg))
-	{
+	switch (af) {
+	case AF_INET:
+		if (ike_cfg->force_encap(ike_cfg))
+			return true;
 		return charon->kernel->get_features(charon->kernel) &
-					KERNEL_REQUIRE_UDP_ENCAPSULATION;
+			KERNEL_REQUIRE_UDP_ENCAPSULATION;
+
+	case AF_INET6:
+		if (ike_cfg->force_encap_v6(ike_cfg))
+			return true;
+		return false;
+
+	default:
+		return false;
 	}
-	return TRUE;
 }
 
 /**
@@ -140,7 +154,7 @@ static notify_payload_t *build_natd_payload(private_ike_natd_t *this,
 
 	ike_sa_id = this->ike_sa->get_id(this->ike_sa);
 	config = this->ike_sa->get_ike_cfg(this->ike_sa);
-	if (force_encap(config) && type == NAT_DETECTION_SOURCE_IP)
+	if (force_encap(config, this->af) && type == NAT_DETECTION_SOURCE_IP)
 	{
 		uint32_t addr;
 
@@ -256,7 +270,7 @@ static void process_payloads(private_ike_natd_t *this, message_t *message)
 									!this->src_matched);
 		config = this->ike_sa->get_ike_cfg(this->ike_sa);
 		if (this->dst_matched && this->src_matched &&
-			force_encap(config))
+		    force_encap(config, this->af))
 		{
 			this->ike_sa->set_condition(this->ike_sa, COND_NAT_FAKE, TRUE);
 		}
@@ -304,6 +318,8 @@ METHOD(task_t, build_i, status_t,
 
 	/* destination is always set */
 	host = message->get_destination(message);
+	this->af = host->get_family(host);
+
 	notify = build_natd_payload(this, NAT_DETECTION_DESTINATION_IP, host);
 	if (notify)
 	{
@@ -316,7 +332,7 @@ METHOD(task_t, build_i, status_t,
 	 * 3. Include all possible addresses
 	 */
 	host = message->get_source(message);
-	if (!host->is_anyaddr(host) || force_encap(ike_cfg))
+	if (!host->is_anyaddr(host) || force_encap(ike_cfg, this->af))
 	{	/* 1. or if we force UDP encap, as it doesn't matter if it's %any */
 		notify = build_natd_payload(this, NAT_DETECTION_SOURCE_IP, host);
 		if (notify)
@@ -401,6 +417,11 @@ METHOD(task_t, build_r, status_t,
 METHOD(task_t, process_r, status_t,
 	private_ike_natd_t *this, message_t *message)
 {
+	host_t *host;
+
+	host = message->get_source(message);
+	this->af = host->get_family(host);
+
 	process_payloads(this, message);
 
 	return NEED_MORE;
@@ -455,6 +476,7 @@ ike_natd_t *ike_natd_create(ike_sa_t *ike_sa, bool initiator)
 		.ike_sa = ike_sa,
 		.initiator = initiator,
 		.hasher = lib->crypto->create_hasher(lib->crypto, HASH_SHA1),
+	        .af = AF_UNSPEC,
 	);
 
 	if (initiator)
diff --git a/src/libstrongswan/networking/packet.c b/src/libstrongswan/networking/packet.c
index 00993f92b..b9a3cabdf 100644
--- a/src/libstrongswan/networking/packet.c
+++ b/src/libstrongswan/networking/packet.c
@@ -53,6 +53,8 @@ struct private_packet_t {
 	 * actual chunk returned from get_data, adjusted when skip_bytes is called
 	 */
 	chunk_t adjusted_data;
+
+	bool encap;
 };
 
 METHOD(packet_t, set_source, void,
@@ -62,6 +64,18 @@ METHOD(packet_t, set_source, void,
 	this->source = source;
 }
 
+METHOD(packet_t, set_encap, void,
+       private_packet_t *this, bool val)
+{
+	this->encap = val;
+}
+
+METHOD(packet_t, get_encap, bool,
+       private_packet_t *this)
+{
+	return this->encap;
+}
+
 METHOD(packet_t, set_destination, void,
 	private_packet_t *this, host_t *destination)
 {
@@ -159,6 +173,8 @@ packet_t *packet_create_from_data(host_t *src, host_t *dst, chunk_t data)
 			.set_destination = _set_destination,
 			.get_destination = _get_destination,
 			.get_dscp = _get_dscp,
+			.get_encap = _get_encap,
+			.set_encap = _set_encap,
 			.set_dscp = _set_dscp,
 			.skip_bytes = _skip_bytes,
 			.clone = _clone_,
@@ -168,6 +184,7 @@ packet_t *packet_create_from_data(host_t *src, host_t *dst, chunk_t data)
 		.destination = dst,
 		.adjusted_data = data,
 		.data = data,
+	     .encap = true,
 	);
 
 	return &this->public;
diff --git a/src/libstrongswan/networking/packet.h b/src/libstrongswan/networking/packet.h
index 806337ba0..b08d2fa87 100644
--- a/src/libstrongswan/networking/packet.h
+++ b/src/libstrongswan/networking/packet.h
@@ -105,6 +105,12 @@ struct packet_t {
 	 */
 	void (*skip_bytes)(packet_t *packet, size_t bytes);
 
+	/**
+	 * udp encap
+	 */
+	bool (*get_encap)(packet_t *packet);
+	void (*set_encap)(packet_t *packet, bool val);
+
 	/**
 	 * Clones a packet_t object.
 	 *
-- 
2.17.1

