From 5081c7096ee58ff5be4602f1ed60c1d0712f057a Mon Sep 17 00:00:00 2001
From: Maxime Bizon <mbizon@freebox.fr>
Date: Tue, 16 Feb 2016 18:23:00 +0100
Subject: [PATCH 1/2] support raw ipv6 mode

---
 src/libcharon/config/ike_cfg.c                     |  15 +-
 src/libcharon/config/ike_cfg.h                     |   2 +
 src/libcharon/network/receiver.c                   |  17 +++
 .../plugins/socket_default/socket_default_socket.c | 156 +++++++++++++++------
 src/libcharon/sa/ikev2/tasks/ike_natd.c            |  37 +++--
 src/libhydra/kernel/kernel_interface.h             |   2 +
 src/libipsec/esp_packet.c                          |  28 ++++
 src/libipsec/esp_packet.h                          |   3 +
 src/libipsec/ipsec_processor.c                     |   2 +
 src/libipsec/ipsec_sa.c                            |  33 ++++-
 src/libipsec/ipsec_sa.h                            |   5 +
 src/libipsec/ipsec_sa_mgr.c                        |   8 +-
 src/libstrongswan/networking/packet.c              |  17 +++
 src/libstrongswan/networking/packet.h              |   6 +
 14 files changed, 261 insertions(+), 70 deletions(-)

diff --git a/src/libcharon/config/ike_cfg.c b/src/libcharon/config/ike_cfg.c
index dee9e4c..beb9143 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 IKEv1 fragmentation
@@ -128,12 +129,18 @@ METHOD(ike_cfg_t, send_certreq, bool,
 	return this->certreq;
 }
 
-METHOD(ike_cfg_t, force_encap_, bool,
+METHOD(ike_cfg_t, force_encap, bool,
 	private_ike_cfg_t *this)
 {
 	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)
 {
@@ -405,6 +412,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 &&
 		streq(this->me, other->me) &&
 		streq(this->other, other->other) &&
@@ -561,6 +569,7 @@ int ike_cfg_get_family(ike_cfg_t *cfg, bool local)
  * Described in header.
  */
 ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
+			  bool force_encap_v6,
 						  char *me, u_int16_t my_port,
 						  char *other, u_int16_t other_port,
 						  fragmentation_t fragmentation, u_int8_t dscp)
@@ -571,7 +580,8 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
 		.public = {
 			.get_version = _get_version,
 			.send_certreq = _send_certreq,
-			.force_encap = _force_encap_,
+			.force_encap = _force_encap,
+			.force_encap_v6 = _force_encap_v6,
 			.fragmentation = _fragmentation,
 			.resolve_me = _resolve_me,
 			.resolve_other = _resolve_other,
@@ -594,6 +604,7 @@ ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
 		.version = version,
 		.certreq = certreq,
 		.force_encap = force_encap,
+		.force_encap_v6 = force_encap_v6,
 		.fragmentation = fragmentation,
 		.me = strdup(me),
 		.my_ranges = linked_list_create(),
diff --git a/src/libcharon/config/ike_cfg.h b/src/libcharon/config/ike_cfg.h
index a72960f..197ce3b 100644
--- a/src/libcharon/config/ike_cfg.h
+++ b/src/libcharon/config/ike_cfg.h
@@ -189,6 +189,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 proprietary IKEv1 fragmentation
@@ -250,6 +251,7 @@ struct ike_cfg_t {
  * @return					ike_cfg_t object.
  */
 ike_cfg_t *ike_cfg_create(ike_version_t version, bool certreq, bool force_encap,
+			  bool force_encap_v6,
 						  char *me, u_int16_t my_port,
 						  char *other, u_int16_t other_port,
 						  fragmentation_t fragmentation, u_int8_t dscp);
diff --git a/src/libcharon/network/receiver.c b/src/libcharon/network/receiver.c
index a2f2016..8031894 100644
--- a/src/libcharon/network/receiver.c
+++ b/src/libcharon/network/receiver.c
@@ -422,6 +422,7 @@ static job_requeue_t receive_packets(private_receiver_t *this)
 	host_t *src, *dst;
 	status_t status;
 	bool supported = TRUE;
+	bool encap;
 	chunk_t data, marker = chunk_from_chars(0x00, 0x00, 0x00, 0x00);
 
 	/* read in a packet */
@@ -436,6 +437,22 @@ static job_requeue_t receive_packets(private_receiver_t *this)
 		return JOB_REQUEUE_FAIR;
 	}
 
+	encap = packet->get_encap(packet);
+
+	if (!encap) {
+		this->esp_cb_mutex->lock(this->esp_cb_mutex);
+		if (this->esp_cb.cb)
+		{
+			this->esp_cb.cb(this->esp_cb.data, packet);
+		}
+		else
+		{
+			packet->destroy(packet);
+		}
+		this->esp_cb_mutex->unlock(this->esp_cb_mutex);
+		return JOB_REQUEUE_DIRECT;
+	}
+
 	data = packet->get_data(packet);
 	if (data.len == 1 && data.ptr[0] == 0xFF)
 	{	/* silently drop NAT-T keepalives */
diff --git a/src/libcharon/plugins/socket_default/socket_default_socket.c b/src/libcharon/plugins/socket_default/socket_default_socket.c
index 13bf3e7..6ac0542 100644
--- a/src/libcharon/plugins/socket_default/socket_default_socket.c
+++ b/src/libcharon/plugins/socket_default/socket_default_socket.c
@@ -107,6 +107,9 @@ struct private_socket_default_socket_t {
 	 */
 	int ipv6;
 
+	/* raw ESP ipv6 socket */
+	int ipv6_raw;
+
 	/**
 	 * IPv6 socket for NAT-T (4500 or natt)
 	 */
@@ -248,10 +251,11 @@ METHOD(socket_t, receiver, status_t,
 		{ .fd = this->ipv4_natt,	.events = POLLIN },
 		{ .fd = this->ipv6,			.events = POLLIN },
 		{ .fd = this->ipv6_natt,	.events = POLLIN },
+		{ .fd = this->ipv6_raw,	.events = POLLIN },
 	};
 	int ports[] = {
 		/* port numbers associated to pollfds */
-		this->port, this->natt, this->port, this->natt,
+		this->port, this->natt, this->port, this->natt, 0,
 	};
 
 	DBG2(DBG_NET, "waiting for data on sockets");
@@ -341,6 +345,8 @@ METHOD(socket_t, receiver, status_t,
 		pkt = packet_create();
 		pkt->set_source(pkt, source);
 		pkt->set_destination(pkt, dest);
+		if (selected == this->ipv6_raw)
+			pkt->set_encap(pkt, false);
 		DBG2(DBG_NET, "received packet: from %#H to %#H", source, dest);
 		data = chunk_create(buffer, bytes_read);
 		pkt->set_data(pkt, chunk_clone(data));
@@ -465,21 +471,27 @@ METHOD(socket_t, sender, status_t,
 	host_t *src, *dst;
 	struct msghdr msg;
 	struct iovec iov;
-	u_int8_t *dscp;
+	u_int8_t *dscp = NULL;
+	bool encap;
+	struct sockaddr_in6 sa6;
+	struct sockaddr_in sa;
 
 	src = packet->get_source(packet);
 	dst = packet->get_destination(packet);
 	data = packet->get_data(packet);
+	encap = packet->get_encap(packet);
 
-	DBG2(DBG_NET, "sending packet: from %#H to %#H", src, dst);
+	DBG2(DBG_NET, "sending packet: from %#H to %#H (encap %u)", src, dst,
+	     encap);
 
 	/* send data */
-	sport = src->get_port(src);
 	family = dst->get_family(dst);
-	if (sport == 0 || sport == this->port)
-	{
-		switch (family)
+	if (encap) {
+		sport = src->get_port(src);
+		if (sport == 0 || sport == this->port)
 		{
+			switch (family)
+			{
 			case AF_INET:
 				skt = this->ipv4;
 				dscp = &this->dscp4;
@@ -490,12 +502,12 @@ METHOD(socket_t, sender, status_t,
 				break;
 			default:
 				return FAILED;
+			}
 		}
-	}
-	else if (sport == this->natt)
-	{
-		switch (family)
+		else if (sport == this->natt)
 		{
+			switch (family)
+			{
 			case AF_INET:
 				skt = this->ipv4_natt;
 				dscp = &this->dscp4_natt;
@@ -506,8 +518,13 @@ METHOD(socket_t, sender, status_t,
 				break;
 			default:
 				return FAILED;
+			}
 		}
+	} else {
+		skt = this->ipv6_raw;
+		sport = 0;
 	}
+
 	if (skt == -1)
 	{
 		DBG1(DBG_NET, "no socket found to send IPv%d packet from port %d",
@@ -518,7 +535,7 @@ METHOD(socket_t, sender, status_t,
 	/* setting DSCP values per-packet in a cmsg seems not to be supported
 	 * on Linux. We instead setsockopt() before sending it, this should be
 	 * safe as only a single thread calls send(). */
-	if (*dscp != packet->get_dscp(packet))
+	if (dscp && *dscp != packet->get_dscp(packet))
 	{
 		if (family == AF_INET)
 		{
@@ -553,8 +570,23 @@ METHOD(socket_t, sender, status_t,
 	}
 
 	memset(&msg, 0, sizeof(struct msghdr));
-	msg.msg_name = dst->get_sockaddr(dst);;
+
+	if (encap)
+		msg.msg_name = dst->get_sockaddr(dst);
+	else {
+		/* zero port */
+		if (family == AF_INET) {
+			memcpy(&sa, dst->get_sockaddr(dst), sizeof (sa));
+			sa.sin_port = 0;
+			msg.msg_name = (struct sockaddr *)&sa;
+		} else {
+			memcpy(&sa6, dst->get_sockaddr(dst), sizeof (sa6));
+			sa6.sin6_port = 0;
+			msg.msg_name = (struct sockaddr *)&sa6;
+		}
+	}
 	msg.msg_namelen = *dst->get_sockaddr_len(dst);
+
 	iov.iov_base = data.ptr;
 	iov.iov_len = data.len;
 	msg.msg_iov = &iov;
@@ -611,7 +643,7 @@ METHOD(socket_t, supported_families, socket_family_t,
  * open a socket to send and receive packets
  */
 static int open_socket(private_socket_default_socket_t *this,
-					   int family, u_int16_t *port)
+		       int family, u_int16_t *port, bool raw)
 {
 	int on = TRUE;
 	union {
@@ -641,7 +673,8 @@ static int open_socket(private_socket_default_socket_t *this,
 			break;
 		case AF_INET6:
 			memcpy(&addr.sin6.sin6_addr, &in6addr_any, sizeof(in6addr_any));
-			addr.sin6.sin6_port = htons(*port);
+			if (!raw)
+				addr.sin6.sin6_port = htons(*port);
 			addrlen = sizeof(addr.sin6);
 			sol = SOL_IPV6;
 			pktinfo = IPV6_RECVPKTINFO;
@@ -650,44 +683,62 @@ static int open_socket(private_socket_default_socket_t *this,
 			return -1;
 	}
 
-	skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
-	if (skt < 0)
-	{
-		DBG1(DBG_NET, "could not open socket: %s", strerror(errno));
-		return -1;
-	}
-	if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
-	{
-		DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
-		close(skt);
-		return -1;
-	}
-
-	/* bind the socket */
-	if (bind(skt, &addr.sockaddr, addrlen) < 0)
-	{
-		DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
-		close(skt);
-		return -1;
-	}
+	if (!raw) {
+		skt = socket(family, SOCK_DGRAM, IPPROTO_UDP);
+		if (skt < 0)
+		{
+			DBG1(DBG_NET, "could not open socket: %s", strerror(errno));
+			return -1;
+		}
+		if (setsockopt(skt, SOL_SOCKET, SO_REUSEADDR, (void*)&on, sizeof(on)) < 0)
+		{
+			DBG1(DBG_NET, "unable to set SO_REUSEADDR on socket: %s", strerror(errno));
+			close(skt);
+			return -1;
+		}
 
-	/* retrieve randomly allocated port if needed */
-	if (*port == 0)
-	{
-		if (getsockname(skt, &addr.sockaddr, &addrlen) < 0)
+		/* bind the socket */
+		if (bind(skt, &addr.sockaddr, addrlen) < 0)
 		{
-			DBG1(DBG_NET, "unable to determine port: %s", strerror(errno));
+			DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
 			close(skt);
 			return -1;
 		}
-		switch (family)
+
+		/* retrieve randomly allocated port if needed */
+		if (*port == 0)
 		{
+			if (getsockname(skt, &addr.sockaddr, &addrlen) < 0)
+			{
+				DBG1(DBG_NET, "unable to determine port: %s", strerror(errno));
+				close(skt);
+				return -1;
+			}
+			switch (family)
+			{
 			case AF_INET:
 				*port = ntohs(addr.sin.sin_port);
 				break;
 			case AF_INET6:
 				*port = ntohs(addr.sin6.sin6_port);
 				break;
+			}
+		}
+	} else {
+		/* raw socket ESP support */
+		skt = socket(family, SOCK_RAW, IPPROTO_ESP);
+		if (skt < 0)
+		{
+			DBG1(DBG_NET, "could not open socket: %s", strerror(errno));
+			return -1;
+		}
+
+		/* bind the socket */
+		if (bind(skt, &addr.sockaddr, addrlen) < 0)
+		{
+			DBG1(DBG_NET, "unable to bind socket: %s", strerror(errno));
+			close(skt);
+			return -1;
 		}
 	}
 
@@ -760,16 +811,25 @@ static bool use_family(int family)
  * Open a socket pair (normal and NAT traversal) for a given address family
  */
 static void open_socketpair(private_socket_default_socket_t *this, int family,
-							int *skt, int *skt_natt, char *label)
+			    int *skt, int *skt_natt, int *skt_raw, char *label)
 {
 	if (!use_family(family))
 	{
 		*skt = -1;
 		*skt_natt = -1;
+		if (skt_raw)
+			*skt_raw = -1;
 		return;
 	}
 
-	*skt = open_socket(this, family, &this->port);
+	if (skt_raw) {
+		*skt_raw = open_socket(this, family, &this->port, true);
+		if (*skt_raw == -1) {
+			DBG1(DBG_NET, "could not open %s raw socket, %s", label, label);
+		}
+	}
+
+	*skt = open_socket(this, family, &this->port, false);
 	if (*skt == -1)
 	{
 		*skt_natt = -1;
@@ -777,7 +837,7 @@ static void open_socketpair(private_socket_default_socket_t *this, int family,
 	}
 	else
 	{
-		*skt_natt = open_socket(this, family, &this->natt);
+		*skt_natt = open_socket(this, family, &this->natt, false);
 		if (*skt_natt == -1)
 		{
 			DBG1(DBG_NET, "could not open %s NAT-T socket", label);
@@ -804,6 +864,10 @@ METHOD(socket_t, destroy, void,
 	{
 		close(this->ipv6_natt);
 	}
+	if (this->ipv6_raw != -1)
+	{
+		close(this->ipv6_raw);
+	}
 	free(this);
 }
 
@@ -861,8 +925,8 @@ socket_default_socket_t *socket_default_socket_create()
 	open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4");
 	open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6");
 #else /* !__APPLE__ */
-	open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, "IPv6");
-	open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, "IPv4");
+	open_socketpair(this, AF_INET6, &this->ipv6, &this->ipv6_natt, &this->ipv6_raw,"IPv6");
+	open_socketpair(this, AF_INET, &this->ipv4, &this->ipv4_natt, NULL, "IPv4");
 #endif /* __APPLE__ */
 
 	if (this->ipv4 == -1 && this->ipv6 == -1)
diff --git a/src/libcharon/sa/ikev2/tasks/ike_natd.c b/src/libcharon/sa/ikev2/tasks/ike_natd.c
index dd34c12..63271f5 100644
--- a/src/libcharon/sa/ikev2/tasks/ike_natd.c
+++ b/src/libcharon/sa/ikev2/tasks/ike_natd.c
@@ -76,20 +76,29 @@ 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))
-	{
-		return hydra->kernel_interface->get_features(hydra->kernel_interface) &
-					KERNEL_REQUIRE_UDP_ENCAPSULATION;
-	}
-	return TRUE;
+	if (af == AF_INET)
+		return ike_cfg->force_encap(ike_cfg) ||
+			hydra->kernel_interface->get_features(hydra->kernel_interface) &
+			KERNEL_REQUIRE_UDP_ENCAPSULATION;
+	if (af == AF_INET6)
+		return ike_cfg->force_encap_v6(ike_cfg) ||
+			hydra->kernel_interface->get_features(hydra->kernel_interface) &
+			KERNEL_REQUIRE_UDP_V6_ENCAPSULATION;
+
+	return false;
 }
 
 /**
@@ -141,7 +150,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)
 	{
 		u_int32_t addr;
 
@@ -257,7 +266,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);
 		}
@@ -305,6 +314,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)
 	{
@@ -317,7 +328,7 @@ METHOD(task_t, build_i, status_t,
 	 * 3. Include all possbile 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)
@@ -402,6 +413,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;
@@ -456,6 +472,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/libhydra/kernel/kernel_interface.h b/src/libhydra/kernel/kernel_interface.h
index 45efe89..ba2f7cd 100644
--- a/src/libhydra/kernel/kernel_interface.h
+++ b/src/libhydra/kernel/kernel_interface.h
@@ -71,6 +71,8 @@ enum kernel_feature_t {
 	KERNEL_REQUIRE_UDP_ENCAPSULATION = (1<<2),
 	/** IPsec backend does not require a policy reinstall on SA updates */
 	KERNEL_NO_POLICY_UPDATES = (1<<3),
+	/** IPsec implementation requires UDP encapsulation of ESP packets over ipv6 */
+	KERNEL_REQUIRE_UDP_V6_ENCAPSULATION = (1<<4),
 };
 
 /**
diff --git a/src/libipsec/esp_packet.c b/src/libipsec/esp_packet.c
index 8223022..8d6ee09 100644
--- a/src/libipsec/esp_packet.c
+++ b/src/libipsec/esp_packet.c
@@ -61,6 +61,18 @@ struct private_esp_packet_t {
  */
 static private_esp_packet_t *esp_packet_create_internal(packet_t *packet);
 
+METHOD(esp_packet_t, get_encap, bool,
+	private_esp_packet_t *this)
+{
+	return this->packet->get_encap(this->packet);
+}
+
+METHOD(esp_packet_t, set_encap, void,
+       private_esp_packet_t *this, bool value)
+{
+	this->packet->set_encap(this->packet, value);
+}
+
 METHOD(packet_t, set_source, void,
 	private_esp_packet_t *this, host_t *src)
 {
@@ -103,6 +115,18 @@ METHOD(packet_t, get_dscp, u_int8_t,
 	return this->packet->get_dscp(this->packet);
 }
 
+METHOD(packet_t, pget_encap, bool,
+	private_esp_packet_t *this)
+{
+	return this->packet->get_encap(this->packet);
+}
+
+METHOD(packet_t, pset_encap, void,
+       private_esp_packet_t *this, bool value)
+{
+	this->packet->set_encap(this->packet, value);
+}
+
 METHOD(packet_t, set_dscp, void,
 	private_esp_packet_t *this, u_int8_t value)
 {
@@ -410,6 +434,8 @@ static private_esp_packet_t *esp_packet_create_internal(packet_t *packet)
 				.set_destination = _set_destination,
 				.get_destination = _get_destination,
 				.get_data = _get_data,
+				.get_encap = _pget_encap,
+				.set_encap = _pset_encap,
 				.set_data = _set_data,
 				.get_dscp = _get_dscp,
 				.set_dscp = _set_dscp,
@@ -417,6 +443,8 @@ static private_esp_packet_t *esp_packet_create_internal(packet_t *packet)
 				.clone = _clone_,
 				.destroy = _destroy,
 			},
+			.get_encap = _get_encap,
+			.set_encap = _set_encap,
 			.get_source = _get_source,
 			.get_destination = _get_destination,
 			.get_next_header = _get_next_header,
diff --git a/src/libipsec/esp_packet.h b/src/libipsec/esp_packet.h
index f1941a3..2c5deef 100644
--- a/src/libipsec/esp_packet.h
+++ b/src/libipsec/esp_packet.h
@@ -126,6 +126,9 @@ struct esp_packet_t {
 	 */
 	void (*destroy)(esp_packet_t *this);
 
+	bool (*get_encap)(esp_packet_t *this);
+	bool (*set_encap)(esp_packet_t *this, bool encap);
+
 };
 
 /**
diff --git a/src/libipsec/ipsec_processor.c b/src/libipsec/ipsec_processor.c
index ee297a3..74a3a23 100644
--- a/src/libipsec/ipsec_processor.c
+++ b/src/libipsec/ipsec_processor.c
@@ -127,6 +127,7 @@ static job_requeue_t process_inbound(private_ipsec_processor_t *this)
 		packet->destroy(packet);
 		return JOB_REQUEUE_DIRECT;
 	}
+
 	ip_packet = packet->get_payload(packet);
 	sa->update_usestats(sa, ip_packet->get_encoding(ip_packet).len);
 	reqid = sa->get_reqid(sa);
@@ -229,6 +230,7 @@ static job_requeue_t process_outbound(private_ipsec_processor_t *this)
 	sa->update_usestats(sa, packet->get_encoding(packet).len);
 	ipsec->sas->checkin(ipsec->sas, sa);
 	policy->destroy(policy);
+	esp_packet->set_encap(esp_packet, sa->get_encap(sa));
 	send_outbound(this, esp_packet);
 	return JOB_REQUEUE_DIRECT;
 }
diff --git a/src/libipsec/ipsec_sa.c b/src/libipsec/ipsec_sa.c
index ccbbb1b..475c7a6 100644
--- a/src/libipsec/ipsec_sa.c
+++ b/src/libipsec/ipsec_sa.c
@@ -104,8 +104,25 @@ struct private_ipsec_sa_t {
 	 * Has the SA hard-expired?
 	 */
 	bool hard_expired;
+
+	/**
+	 * using udp encap
+	 */
+	bool encap;
 };
 
+METHOD(ipsec_sa_t, get_encap, bool,
+	private_ipsec_sa_t *this)
+{
+	return this->encap;
+}
+
+METHOD(ipsec_sa_t, set_encap, void,
+       private_ipsec_sa_t *this, bool encap)
+{
+	this->encap = encap;
+}
+
 METHOD(ipsec_sa_t, get_source, host_t*,
 	private_ipsec_sa_t *this)
 {
@@ -238,6 +255,13 @@ METHOD(ipsec_sa_t, update_usestats, void,
 	}
 }
 
+METHOD(ipsec_sa_t, match_by_src_dst, bool,
+       private_ipsec_sa_t *this, host_t *src, host_t *dst)
+{
+	return this->src->ip_equals(this->src, src) &&
+		this->dst->ip_equals(this->dst, dst);
+}
+
 METHOD(ipsec_sa_t, match_by_spi_dst, bool,
 	private_ipsec_sa_t *this, u_int32_t spi, host_t *dst)
 {
@@ -284,11 +308,6 @@ ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
 		DBG1(DBG_ESP, "  IPsec SA: protocol not supported");
 		return NULL;
 	}
-	if (!encap)
-	{
-		DBG1(DBG_ESP, "  IPsec SA: only UDP encapsulation is supported");
-		return NULL;
-	}
 	if (esn)
 	{
 		DBG1(DBG_ESP, "  IPsec SA: ESN not supported");
@@ -312,12 +331,15 @@ ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
 			.get_destination = _get_destination,
 			.set_source = _set_source,
 			.set_destination = _set_destination,
+			     .get_encap = _get_encap,
+			     .set_encap = _set_encap,
 			.get_spi = _get_spi,
 			.get_reqid = _get_reqid,
 			.get_protocol = _get_protocol,
 			.get_lifetime = _get_lifetime,
 			.is_inbound = _is_inbound,
 			.match_by_spi_dst = _match_by_spi_dst,
+			.match_by_src_dst = _match_by_src_dst,
 			.match_by_spi_src_dst = _match_by_spi_src_dst,
 			.match_by_reqid = _match_by_reqid,
 			.get_esp_context = _get_esp_context,
@@ -334,6 +356,7 @@ ipsec_sa_t *ipsec_sa_create(u_int32_t spi, host_t *src, host_t *dst,
 		.mode = mode,
 		.esn = esn,
 		.inbound = inbound,
+	        .encap = encap,
 	);
 
 	this->esp_context = esp_context_create(enc_alg, enc_key, int_alg, int_key,
diff --git a/src/libipsec/ipsec_sa.h b/src/libipsec/ipsec_sa.h
index 8dad29a..26f48fd 100644
--- a/src/libipsec/ipsec_sa.h
+++ b/src/libipsec/ipsec_sa.h
@@ -147,6 +147,8 @@ struct ipsec_sa_t {
 	 */
 	bool (*match_by_spi_dst)(ipsec_sa_t *this, u_int32_t spi, host_t *dst);
 
+	bool (*match_by_src_dst)(ipsec_sa_t *this, host_t *src, host_t *dst);
+
 	/**
 	 * Check if this SA matches all given parameters
 	 *
@@ -169,6 +171,9 @@ struct ipsec_sa_t {
 	 */
 	bool (*match_by_reqid)(ipsec_sa_t *this, u_int32_t reqid, bool inbound);
 
+	bool (*get_encap)(ipsec_sa_t *this);
+	void (*set_encap)(ipsec_sa_t *this, bool encap);
+
 	/**
 	 * Destroy an ipsec_sa_t
 	 */
diff --git a/src/libipsec/ipsec_sa_mgr.c b/src/libipsec/ipsec_sa_mgr.c
index 9d461f2..3f342df 100644
--- a/src/libipsec/ipsec_sa_mgr.c
+++ b/src/libipsec/ipsec_sa_mgr.c
@@ -498,13 +498,6 @@ METHOD(ipsec_sa_mgr_t, update_sa, status_t,
 	DBG2(DBG_ESP, "updating SAD entry with SPI %.8x from %#H..%#H to %#H..%#H",
 		 ntohl(spi), src, dst, new_src, new_dst);
 
-	if (!new_encap)
-	{
-		DBG1(DBG_ESP, "failed to update SAD entry: can't deactivate UDP "
-			 "encapsulation");
-		return NOT_SUPPORTED;
-	}
-
 	this->mutex->lock(this->mutex);
 	if (this->sas->find_first(this->sas, (void*)match_entry_by_spi_src_dst,
 							 (void**)&entry, &spi, src, dst) == SUCCESS &&
@@ -512,6 +505,7 @@ METHOD(ipsec_sa_mgr_t, update_sa, status_t,
 	{
 		entry->sa->set_source(entry->sa, new_src);
 		entry->sa->set_destination(entry->sa, new_dst);
+		entry->sa->set_encap(entry->sa, new_encap);
 		/* checkin the entry */
 		entry->locked = FALSE;
 		entry->condvar->signal(entry->condvar);
diff --git a/src/libstrongswan/networking/packet.c b/src/libstrongswan/networking/packet.c
index 4ff7fc4..93a270d 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 1492dd0..8307922 100644
--- a/src/libstrongswan/networking/packet.h
+++ b/src/libstrongswan/networking/packet.h
@@ -106,6 +106,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.
 	 *
 	 * @note Data is cloned without skipped bytes.
-- 
1.9.1

