diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h
index b294e7c4a..822c9016b 100644
--- a/platform/linux-generic/include/odp_ipsec_internal.h
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -131,6 +131,7 @@ struct ipsec_sa_s {
 			unsigned	copy_df : 1;
 			unsigned	copy_flabel : 1;
 			unsigned	aes_ctr_iv : 1;
+			unsigned	udp_encap : 1;
 
 			/* Only for outbound */
 			unsigned	use_counter_iv : 1;
diff --git a/platform/linux-generic/include/protocols/udp.h b/platform/linux-generic/include/protocols/udp.h
index 535aba855..85984c992 100644
--- a/platform/linux-generic/include/protocols/udp.h
+++ b/platform/linux-generic/include/protocols/udp.h
@@ -38,6 +38,8 @@ typedef struct ODP_PACKED {
 ODP_STATIC_ASSERT(sizeof(_odp_udphdr_t) == _ODP_UDPHDR_LEN,
 		  "_ODP_UDPHDR_T__SIZE_ERROR");
 
+#define _ODP_UDP_IPSEC_PORT 4500
+
 /**
  * @}
  */
diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c
index 2749268ae..20031bddc 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -18,6 +18,7 @@
 #include <protocols/eth.h>
 #include <protocols/ip.h>
 #include <protocols/ipsec.h>
+#include <protocols/udp.h>
 
 #include <string.h>
 
@@ -378,9 +379,29 @@ static int ipsec_in_esp(odp_packet_t *pkt,
 	_odp_esphdr_t esp;
 	uint16_t ipsec_offset;
 	ipsec_sa_t *ipsec_sa;
+	odp_bool_t udp_encap = false;
 
 	ipsec_offset = state->ip_offset + state->ip_hdr_len;
 
+	if (_ODP_IPPROTO_UDP == state->ip_next_hdr) {
+		_odp_udphdr_t udp;
+		uint16_t ip_data_len = state->ip_tot_len -
+				       state->ip_hdr_len;
+
+		odp_packet_copy_to_mem(*pkt, ipsec_offset,
+				       _ODP_UDPHDR_LEN, &udp);
+
+		if (udp.dst_port != odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT) ||
+		    udp.length != odp_cpu_to_be_16(ip_data_len)) {
+			status->error.proto = 1;
+			return -1;
+		}
+
+		ipsec_offset += _ODP_UDPHDR_LEN;
+		state->ip_hdr_len += _ODP_UDPHDR_LEN;
+		udp_encap = true;
+	}
+
 	if (odp_packet_copy_to_mem(*pkt, ipsec_offset,
 				   sizeof(esp), &esp) < 0) {
 		status->error.alg = 1;
@@ -396,6 +417,11 @@ static int ipsec_in_esp(odp_packet_t *pkt,
 	if (status->error.all)
 		return -1;
 
+	if (!!ipsec_sa->udp_encap != udp_encap) {
+		status->error.proto = 1;
+		return -1;
+	}
+
 	if (ipsec_in_iv(*pkt, state, ipsec_sa,
 			ipsec_offset + _ODP_ESPHDR_LEN) < 0) {
 		status->error.alg = 1;
@@ -446,6 +472,11 @@ static int ipsec_in_esp_post(odp_packet_t pkt,
 				 ipsec_padding, esptrl.pad_len) != 0)
 		return -1;
 
+	if (udp_encap) {
+		state->ip_hdr_len -= _ODP_UDPHDR_LEN;
+		state->in.hdr_len += _ODP_UDPHDR_LEN;
+	}
+
 	odp_packet_copy_from_mem(pkt, state->ip_next_hdr_offset,
 				 1, &esptrl.next_header);
 	state->in.trl_len += esptrl.pad_len;
@@ -603,7 +634,8 @@ static ipsec_sa_t *ipsec_in_single(odp_packet_t pkt,
 	}
 
 	/* Check IP header for IPSec protocols and look it up */
-	if (_ODP_IPPROTO_ESP == state.ip_next_hdr) {
+	if (_ODP_IPPROTO_ESP == state.ip_next_hdr ||
+	    _ODP_IPPROTO_UDP == state.ip_next_hdr) {
 		rc = ipsec_in_esp(&pkt, &state, &ipsec_sa, sa, &param, status);
 	} else if (_ODP_IPPROTO_AH == state.ip_next_hdr) {
 		rc = ipsec_in_ah(&pkt, &state, &ipsec_sa, sa, &param, status);
@@ -962,6 +994,7 @@ static int ipsec_out_esp(odp_packet_t *pkt,
 {
 	_odp_esphdr_t esp;
 	_odp_esptrl_t esptrl;
+	_odp_udphdr_t udphdr;
 	uint32_t encrypt_len;
 	uint16_t ip_data_len = state->ip_tot_len -
 			       state->ip_hdr_len;
@@ -983,6 +1016,16 @@ static int ipsec_out_esp(odp_packet_t *pkt,
 		       ip_data_len +
 		       ipsec_sa->icv_len;
 
+	if (ipsec_sa->udp_encap) {
+		hdr_len += _ODP_UDPHDR_LEN;
+		proto = _ODP_IPPROTO_UDP;
+		udphdr.src_port = odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT);
+		udphdr.dst_port = odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT);
+		udphdr.length = odp_cpu_to_be_16(ip_data_len +
+						 hdr_len + trl_len);
+		udphdr.chksum = 0; /* should be 0 by RFC */
+	}
+
 	if (ipsec_out_iv(state, ipsec_sa) < 0) {
 		status->error.alg = 1;
 		return -1;
@@ -1030,6 +1073,14 @@ static int ipsec_out_esp(odp_packet_t *pkt,
 				 encrypt_len -
 				 _ODP_ESPTRL_LEN;
 
+	if (ipsec_sa->udp_encap) {
+		odp_packet_copy_from_mem(*pkt, ipsec_offset, _ODP_UDPHDR_LEN,
+					 &udphdr);
+		ipsec_offset += _ODP_UDPHDR_LEN;
+		hdr_len -= _ODP_UDPHDR_LEN;
+		state->ip_hdr_len += _ODP_UDPHDR_LEN;
+	}
+
 	odp_packet_copy_from_mem(*pkt,
 				 ipsec_offset, _ODP_ESPHDR_LEN,
 				 &esp);
diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c
index 812ad0c46..82b3c4522 100644
--- a/platform/linux-generic/odp_ipsec_sad.c
+++ b/platform/linux-generic/odp_ipsec_sad.c
@@ -235,6 +235,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 	ipsec_sa->copy_dscp = param->opt.copy_dscp;
 	ipsec_sa->copy_df = param->opt.copy_df;
 	ipsec_sa->copy_flabel = param->opt.copy_flabel;
+	ipsec_sa->udp_encap = param->opt.udp_encap;
 
 	odp_atomic_store_u64(&ipsec_sa->bytes, 0);
 	odp_atomic_store_u64(&ipsec_sa->packets, 0);
