diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h
index 1340ca7bd..afc2f686e 100644
--- a/platform/linux-generic/include/odp_ipsec_internal.h
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -118,9 +118,17 @@ struct ipsec_sa_s {
 	uint8_t		salt[IPSEC_MAX_SALT_LEN];
 	uint32_t	salt_length;
 
-	unsigned	dec_ttl : 1;
-	unsigned	copy_dscp : 1;
-	unsigned	copy_df : 1;
+	union {
+		unsigned flags;
+		struct {
+			unsigned	dec_ttl : 1;
+			unsigned	copy_dscp : 1;
+			unsigned	copy_df : 1;
+
+			/* Only for outbound */
+			unsigned	use_counter_iv : 1;
+		};
+	};
 
 	union {
 		struct {
@@ -136,6 +144,8 @@ struct ipsec_sa_s {
 			odp_atomic_u32_t tun_hdr_id;
 			odp_atomic_u32_t seq;
 
+			odp_atomic_u64_t counter; /* for CTR/GCM */
+
 			uint8_t		tun_ttl;
 			uint8_t		tun_dscp;
 			uint8_t		tun_df;
diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c
index e57736c2a..6a731e999 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -676,23 +676,36 @@ static ipsec_sa_t *ipsec_out_single(odp_packet_t pkt,
 			       ip_data_len +
 			       ipsec_sa->icv_len;
 
-		if (ipsec_sa->esp_iv_len) {
+		if (ipsec_sa->use_counter_iv) {
+			uint64_t ctr;
+
+			/* Both GCM and CTR use 8-bit counters */
+			ODP_ASSERT(sizeof(ctr) == ipsec_sa->esp_iv_len);
+
+			ctr = odp_atomic_fetch_add_u64(&ipsec_sa->out.counter,
+						       1);
+			/* Check for overrun */
+			if (ctr == 0)
+				goto out;
+
+			memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
+			memcpy(iv + ipsec_sa->salt_length, &ctr,
+			       ipsec_sa->esp_iv_len);
+
+		} else if (ipsec_sa->esp_iv_len) {
 			uint32_t len;
 
-			len = odp_random_data(iv + ipsec_sa->salt_length,
-					      ipsec_sa->esp_iv_len,
+			len = odp_random_data(iv, ipsec_sa->esp_iv_len,
 					      ODP_RANDOM_CRYPTO);
 
 			if (len != ipsec_sa->esp_iv_len) {
 				status->error.alg = 1;
 				goto out;
 			}
-
-			memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
-
-			param.override_iv_ptr = iv;
 		}
 
+		param.override_iv_ptr = iv;
+
 		if (odp_packet_extend_tail(&pkt, trl_len, NULL, NULL) < 0) {
 			status->error.alg = 1;
 			goto out;
@@ -734,7 +747,6 @@ static ipsec_sa_t *ipsec_out_single(odp_packet_t pkt,
 		odp_packet_copy_from_mem(pkt,
 					 ipsec_offset, _ODP_ESPHDR_LEN,
 					 &esp);
-		memcpy(iv, ipsec_sa->salt, ipsec_sa->salt_length);
 		odp_packet_copy_from_mem(pkt,
 					 ipsec_offset + _ODP_ESPHDR_LEN,
 					 ipsec_sa->esp_iv_len,
diff --git a/platform/linux-generic/odp_ipsec_sad.c b/platform/linux-generic/odp_ipsec_sad.c
index f0b5b9e4a..dc338bfcd 100644
--- a/platform/linux-generic/odp_ipsec_sad.c
+++ b/platform/linux-generic/odp_ipsec_sad.c
@@ -207,6 +207,7 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 	ipsec_sa->context = param->context;
 	ipsec_sa->queue = param->dest_queue;
 	ipsec_sa->mode = param->mode;
+	ipsec_sa->flags = 0;
 	if (ODP_IPSEC_DIR_INBOUND == param->dir) {
 		ipsec_sa->in.lookup_mode = param->inbound.lookup_mode;
 		if (ODP_IPSEC_LOOKUP_DSTADDR_SPI == ipsec_sa->in.lookup_mode)
@@ -298,11 +299,13 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 	case ODP_CIPHER_ALG_NULL:
 		ipsec_sa->esp_iv_len = 0;
 		ipsec_sa->esp_block_len = 1;
+		crypto_param.iv.length = 0;
 		break;
 	case ODP_CIPHER_ALG_DES:
 	case ODP_CIPHER_ALG_3DES_CBC:
 		ipsec_sa->esp_iv_len = 8;
 		ipsec_sa->esp_block_len = 8;
+		crypto_param.iv.length = 8;
 		break;
 #if ODP_DEPRECATED_API
 	case ODP_CIPHER_ALG_AES128_CBC:
@@ -310,11 +313,13 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 	case ODP_CIPHER_ALG_AES_CBC:
 		ipsec_sa->esp_iv_len = 16;
 		ipsec_sa->esp_block_len = 16;
+		crypto_param.iv.length = 16;
 		break;
 #if ODP_DEPRECATED_API
 	case ODP_CIPHER_ALG_AES128_GCM:
 #endif
 	case ODP_CIPHER_ALG_AES_GCM:
+		ipsec_sa->use_counter_iv = 1;
 		ipsec_sa->esp_iv_len = 8;
 		ipsec_sa->esp_block_len = 16;
 		crypto_param.iv.length = 12;
@@ -323,6 +328,10 @@ odp_ipsec_sa_t odp_ipsec_sa_create(const odp_ipsec_sa_param_t *param)
 		return ODP_IPSEC_SA_INVALID;
 	}
 
+	if (1 == ipsec_sa->use_counter_iv &&
+	    ODP_IPSEC_DIR_OUTBOUND == param->dir)
+		odp_atomic_init_u64(&ipsec_sa->out.counter, 1);
+
 	crypto_param.auth_digest_len = ipsec_sa->icv_len;
 
 	if (param->crypto.cipher_key_extra.length) {
