diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index ab25e24..54e0774 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -316,7 +316,7 @@ static void cmd_help_long_parsed(void *parsed_result,
 			"    Disable hardware insertion of a VLAN header in"
 			" packets sent on a port.\n\n"
 
-			"tx_cksum set (ip|udp|tcp|sctp|vxlan) (hw|sw) (port_id)\n"
+			"tx_cksum set (ip|udp|tcp|sctp|vxlan|nvgre) (hw|sw) (port_id)\n"
 			"    Select hardware or software calculation of the"
 			" checksum with when transmitting a packet using the"
 			" csum forward engine.\n"
@@ -2899,8 +2899,9 @@ cmd_tx_cksum_parsed(void *parsed_result,
 			mask = TESTPMD_TX_OFFLOAD_TCP_CKSUM;
 		} else if (!strcmp(res->proto, "sctp")) {
 			mask = TESTPMD_TX_OFFLOAD_SCTP_CKSUM;
-		} else if (!strcmp(res->proto, "vxlan")) {
-			mask = TESTPMD_TX_OFFLOAD_VXLAN_CKSUM;
+		} else if (!strcmp(res->proto, "vxlan") ||
+				!strcmp(res->proto, "nvgre")) {
+			mask = TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM;
 		}
 
 		if (hw)
@@ -2918,8 +2919,14 @@ cmd_tx_cksum_parsed(void *parsed_result,
 		(ol_flags & TESTPMD_TX_OFFLOAD_TCP_CKSUM) ? "hw" : "sw");
 	printf("SCTP checksum offload is %s\n",
 		(ol_flags & TESTPMD_TX_OFFLOAD_SCTP_CKSUM) ? "hw" : "sw");
-	printf("VxLAN checksum offload is %s\n",
-		(ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) ? "hw" : "sw");
+
+	if (!strcmp(res->proto, "vxlan")) {
+		printf("VxLAN checksum offload is %s\n",
+		(ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM) ? "hw" : "sw");
+	} else if (!strcmp(res->proto, "nvgre")) {
+		printf("NVGRE checksum offload is %s\n",
+		(ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM) ? "hw" : "sw");
+	}
 
 	/* display warnings if configuration is not supported by the NIC */
 	rte_eth_dev_info_get(res->port_id, &dev_info);
@@ -2953,7 +2960,7 @@ cmdline_parse_token_string_t cmd_tx_cksum_mode =
 				mode, "set");
 cmdline_parse_token_string_t cmd_tx_cksum_proto =
 	TOKEN_STRING_INITIALIZER(struct cmd_tx_cksum_result,
-				proto, "ip#tcp#udp#sctp#vxlan");
+				proto, "ip#tcp#udp#sctp#vxlan#nvgre");
 cmdline_parse_token_string_t cmd_tx_cksum_hwsw =
 	TOKEN_STRING_INITIALIZER(struct cmd_tx_cksum_result,
 				hwsw, "hw#sw");
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index 41711fd..8a87fbb 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -86,6 +86,12 @@
 #define _htons(x) (x)
 #endif
 
+/* simplified GRE header */
+struct simple_gre_hdr {
+	uint16_t flags;
+	uint16_t proto;
+};
+
 static uint16_t
 get_psd_sum(void *l3_hdr, uint16_t ethertype, uint64_t ol_flags)
 {
@@ -244,42 +250,42 @@ process_inner_cksums(void *l3_hdr, uint16_t ethertype, uint16_t l3_len,
 	return ol_flags;
 }
 
-/* Calculate the checksum of outer header (only vxlan is supported,
- * meaning IP + UDP). The caller already checked that it's a vxlan
+/* Calculate the checksum of outer header (only vxlan/nvgre is supported,
+ * meaning IP + UDP/GRE). The caller already checked that it's a vxlan/NVGRE
  * packet */
 static uint64_t
 process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
-	uint16_t outer_l3_len, uint16_t testpmd_ol_flags)
+	uint16_t outer_l3_len, uint16_t testpmd_ol_flags, uint16_t l4_prot)
 {
 	struct ipv4_hdr *ipv4_hdr = outer_l3_hdr;
 	struct ipv6_hdr *ipv6_hdr = outer_l3_hdr;
 	struct udp_hdr *udp_hdr;
 	uint64_t ol_flags = 0;
 
-	if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
-		ol_flags |= PKT_TX_UDP_TUNNEL_PKT;
-
 	if (outer_ethertype == _htons(ETHER_TYPE_IPv4)) {
 		ipv4_hdr->hdr_checksum = 0;
 
-		if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
+		if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM)
 			ol_flags |= PKT_TX_OUTER_IP_CKSUM;
 		else
 			ipv4_hdr->hdr_checksum = rte_ipv4_cksum(ipv4_hdr);
-	} else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM)
+	} else if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM)
 		ol_flags |= PKT_TX_OUTER_IPV6;
 
-	udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr + outer_l3_len);
-	/* do not recalculate udp cksum if it was 0 */
-	if (udp_hdr->dgram_cksum != 0) {
-		udp_hdr->dgram_cksum = 0;
-		if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
-			udp_hdr->dgram_cksum =
-				rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
-		else
-			udp_hdr->dgram_cksum =
-				rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
-	}
+	if (l4_prot == IPPROTO_UDP) {
+		udp_hdr = (struct udp_hdr *)((char *)outer_l3_hdr
+				+ outer_l3_len);
+		/* do not recalculate udp cksum if it was 0 */
+		if (udp_hdr->dgram_cksum != 0) {
+			udp_hdr->dgram_cksum = 0;
+			if (outer_ethertype == _htons(ETHER_TYPE_IPv4))
+				udp_hdr->dgram_cksum =
+					rte_ipv4_udptcp_cksum(ipv4_hdr, udp_hdr);
+			else
+				udp_hdr->dgram_cksum =
+					rte_ipv6_udptcp_cksum(ipv6_hdr, udp_hdr);
+		}
+	} /* else if (l4_proto == IPPROTO_GRE), nothing to do here*/
 
 	return ol_flags;
 }
@@ -299,12 +305,15 @@ process_outer_cksums(void *outer_l3_hdr, uint16_t outer_ethertype,
  *   Ether / (vlan) / IP|IP6 / UDP|TCP|SCTP .
  *   Ether / (vlan) / outer IP|IP6 / outer UDP / VxLAN / Ether / IP|IP6 /
  *           UDP|TCP|SCTP
+ *   Ether / (vlan) / outer IP|IP6 / GRE / Ether / IP|IP6 /
+ *	     UDP|TCP|SCTP
  *
  * The testpmd command line for this forward engine sets the flags
  * TESTPMD_TX_OFFLOAD_* in ports[tx_port].tx_ol_flags. They control
  * wether a checksum must be calculated in software or in hardware. The
  * IP, UDP, TCP and SCTP flags always concern the inner layer.  The
- * VxLAN flag concerns the outer IP (if packet is recognized as a vxlan packet).
+ * TUNNEL flag concerns the outer IP (if packet is recognized as a vxlan/nvgre
+ * packet).
  */
 static void
 pkt_burst_checksum_forward(struct fwd_stream *fs)
@@ -315,6 +324,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 	struct ether_hdr *eth_hdr;
 	void *l3_hdr = NULL, *outer_l3_hdr = NULL; /* can be IPv4 or IPv6 */
 	struct udp_hdr *udp_hdr;
+	struct simple_gre_hdr *gre_hdr;
 	uint16_t nb_rx;
 	uint16_t nb_tx;
 	uint16_t i;
@@ -386,17 +396,20 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 				tunnel = 1;
 
 			/* currently, this flag is set by i40e only if the
-			 * packet is vxlan */
+			 * packet is tunneled packet */
 			} else if (m->ol_flags & (PKT_RX_TUNNEL_IPV4_HDR |
 					PKT_RX_TUNNEL_IPV6_HDR))
 				tunnel = 1;
 
 			if (tunnel == 1) {
+				if (testpmd_ol_flags &
+					TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM)
+					ol_flags |= PKT_TX_UDP_TUNNEL_PKT;
+
 				outer_ethertype = ethertype;
 				outer_l2_len = l2_len;
 				outer_l3_len = l3_len;
 				outer_l3_hdr = l3_hdr;
-
 				eth_hdr = (struct ether_hdr *)((char *)udp_hdr +
 					sizeof(struct udp_hdr) +
 					sizeof(struct vxlan_hdr));
@@ -407,6 +420,35 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 			}
 		}
 
+		if ((l4_proto == IPPROTO_GRE)
+			&& ((m->ol_flags & (PKT_RX_TUNNEL_IPV4_HDR |
+				PKT_RX_TUNNEL_IPV6_HDR)))) {
+			gre_hdr = (struct simple_gre_hdr *)((char *)l3_hdr + l3_len);
+			if (gre_hdr->proto == _htons(ETHER_TYPE_TEB)) {
+				l4_tun_len = sizeof(struct nvgre_hdr);
+				tunnel = 1;
+			}
+
+			if (tunnel == 1) {
+				if (testpmd_ol_flags &
+					TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM)
+					ol_flags |= PKT_TX_GRE_TUNNEL_PKT;
+
+				outer_ethertype = ethertype;
+				outer_l2_len = l2_len;
+				outer_l3_len = l3_len;
+				outer_l3_hdr = l3_hdr;
+
+				/* currently, only NVGRE packet is supported */
+				eth_hdr = (struct ether_hdr *)((char *)gre_hdr +
+					sizeof(struct nvgre_hdr));
+				parse_ethernet(eth_hdr, &ethertype, &l2_len,
+						&l3_len, &l4_proto, &l4_len);
+				l3_hdr = (char *)eth_hdr + l2_len;
+			}
+
+		}
+
 		/* step 2: change all source IPs (v4 or v6) so we need
 		 * to recompute the chksums even if they were correct */
 
@@ -428,13 +470,14 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 		 * processed in hardware. */
 		if (tunnel == 1) {
 			ol_flags |= process_outer_cksums(outer_l3_hdr,
-				outer_ethertype, outer_l3_len, testpmd_ol_flags);
+				outer_ethertype, outer_l3_len, testpmd_ol_flags,
+				l4_proto);
 		}
 
 		/* step 4: fill the mbuf meta data (flags and header lengths) */
 
 		if (tunnel == 1) {
-			if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM) {
+			if (testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM) {
 				m->outer_l2_len = outer_l2_len;
 				m->outer_l3_len = outer_l3_len;
 				m->l2_len = l4_tun_len + l2_len;
@@ -446,9 +489,15 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 				   we changed the ip, but it shows that
 				   we can process the inner header cksum
 				   in the nic */
-				m->l2_len = outer_l2_len + outer_l3_len +
-					sizeof(struct udp_hdr) +
-					sizeof(struct vxlan_hdr) + l2_len;
+				if (l4_proto == IPPROTO_UDP)
+					m->l2_len = outer_l2_len + outer_l3_len +
+						sizeof(struct udp_hdr) +
+						sizeof(struct vxlan_hdr) + l2_len;
+
+				/* currently, only NVGRE is supported */
+				else if (l4_proto == IPPROTO_GRE)
+					m->l2_len = outer_l2_len + outer_l3_len +
+						sizeof(struct nvgre_hdr) + l2_len;
 				m->l3_len = l3_len;
 				m->l4_len = l4_len;
 			}
@@ -505,7 +554,7 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
 					"m->l4_len=%d\n",
 					m->l2_len, m->l3_len, m->l4_len);
 			if ((tunnel == 1) &&
-				(testpmd_ol_flags & TESTPMD_TX_OFFLOAD_VXLAN_CKSUM))
+				(testpmd_ol_flags & TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM))
 				printf("tx: m->outer_l2_len=%d m->outer_l3_len=%d\n",
 					m->outer_l2_len, m->outer_l3_len);
 			if (tso_segsz != 0)
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index f8b0740..528457b 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -125,8 +125,8 @@ struct fwd_stream {
 #define TESTPMD_TX_OFFLOAD_TCP_CKSUM         0x0004
 /** Offload SCTP checksum in csum forward engine */
 #define TESTPMD_TX_OFFLOAD_SCTP_CKSUM        0x0008
-/** Offload VxLAN checksum in csum forward engine */
-#define TESTPMD_TX_OFFLOAD_VXLAN_CKSUM       0x0010
+/** Offload tunneled packet checksum in csum forward engine */
+#define TESTPMD_TX_OFFLOAD_TUNNEL_CKSUM       0x0010
 /** Insert VLAN header in forward engine */
 #define TESTPMD_TX_OFFLOAD_INSERT_VLAN       0x0020
 
diff --git a/lib/librte_pmd_i40e/i40e_rxtx.c b/lib/librte_pmd_i40e/i40e_rxtx.c
index 6c1e324..bf6c17c 100644
--- a/lib/librte_pmd_i40e/i40e_rxtx.c
+++ b/lib/librte_pmd_i40e/i40e_rxtx.c
@@ -1169,6 +1169,8 @@ i40e_calc_context_desc(uint64_t flags)
 
 	if (flags | PKT_TX_UDP_TUNNEL_PKT)
 		mask |= PKT_TX_UDP_TUNNEL_PKT;
+	if (flags | PKT_TX_GRE_TUNNEL_PKT)
+		mask |= PKT_TX_GRE_TUNNEL_PKT;
 
 #ifdef RTE_LIBRTE_IEEE1588
 	mask |= PKT_TX_IEEE1588_TMST;
