diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index 86f0d80a5..f8b698335 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -100,6 +100,12 @@ typedef struct {
 
 	/* offset to L4 hdr (TCP, UDP, SCTP, also ICMP) */
 	uint16_t l4_offset;
+
+	/* Partial sum for L4 checksumming */
+	uint32_t l4_part_sum;
+
+	/* L4 checksum */
+	uint32_t l4_sum;
 } packet_parser_t;
 
 /* Packet extra data length */
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index d3c608ab0..c95665506 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -12,6 +12,7 @@
 #include <odp_debug_internal.h>
 #include <odp/api/hints.h>
 #include <odp/api/byteorder.h>
+#include <odp_chksum_internal.h>
 
 #include <protocols/eth.h>
 #include <protocols/ip.h>
@@ -1948,6 +1949,35 @@ int _odp_packet_copy_md_to_packet(odp_packet_t srcpkt, odp_packet_t dstpkt)
 	return dst_uarea_size < src_uarea_size;
 }
 
+static uint16_t packet_sum_ones_comp16(odp_packet_hdr_t *pkt_hdr,
+				       uint32_t offset,
+				       uint32_t len)
+{
+	uint32_t sum = pkt_hdr->p.l4_part_sum;
+	odp_bool_t odd_offset = false;
+
+	if (offset + len > pkt_hdr->frame_len)
+		return 0;
+
+	while (len > 0) {
+		uint32_t seglen = 0; /* GCC */
+		void *mapaddr = packet_map(pkt_hdr, offset, &seglen, NULL);
+
+		if (seglen < len)
+			seglen = len;
+
+		len -= seglen;
+		sum += _odp_chksum_ones_comp16_32(mapaddr, seglen, odd_offset);
+		odd_offset ^= (seglen % 2);
+	}
+
+	/* Not more than two additions */
+	sum = (sum & 0xffff) + (sum >> 16);
+	sum = (sum & 0xffff) + (sum >> 16);
+
+	return ~sum;
+}
+
 /** Parser helper function for Ethernet packets */
 static inline uint16_t parse_eth(packet_parser_t *prs, const uint8_t **parseptr,
 				 uint32_t *offset, uint32_t frame_len)
@@ -2033,6 +2063,7 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, const uint8_t **parseptr,
 	uint16_t frag_offset;
 	uint32_t dstaddr = odp_be_to_cpu_32(ipv4->dst_addr);
 	uint32_t l3_len = odp_be_to_cpu_16(ipv4->tot_len);
+	uint32_t l4_part_sum = 0;
 
 	if (odp_unlikely(ihl < _ODP_IPV4HDR_IHL_MIN) ||
 	    odp_unlikely(ver != 4) ||
@@ -2052,6 +2083,18 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, const uint8_t **parseptr,
 	*offset   += ihl * 4;
 	*parseptr += ihl * 4;
 
+	if (chksums.chksum.udp || chksums.chksum.tcp) {
+		l4_part_sum = _odp_chksum_ones_comp16_32(
+				(const uint16_t *)&ipv4->src_addr,
+				2 * _ODP_IPV4ADDR_LEN, false);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+		l4_part_sum += ipv4->proto;
+#else
+		l4_part_sum += ((uint16_t)ipv4->proto) << 8;
+#endif
+		prs->l4_part_sum = l4_part_sum;
+	}
+
 	if (odp_unlikely(ihl > _ODP_IPV4HDR_IHL_MIN))
 		prs->input_flags.ipopt = 1;
 
@@ -2076,13 +2119,15 @@ static inline uint8_t parse_ipv4(packet_parser_t *prs, const uint8_t **parseptr,
  */
 static inline uint8_t parse_ipv6(packet_parser_t *prs, const uint8_t **parseptr,
 				 uint32_t *offset, uint32_t frame_len,
-				 uint32_t seg_len)
+				 uint32_t seg_len,
+				 odp_proto_chksums_t chksums)
 {
 	const _odp_ipv6hdr_t *ipv6 = (const _odp_ipv6hdr_t *)*parseptr;
 	const _odp_ipv6hdr_ext_t *ipv6ext;
 	uint32_t dstaddr0 = odp_be_to_cpu_32(ipv6->dst_addr.u8[0]);
 	uint32_t l3_len = odp_be_to_cpu_16(ipv6->payload_len) +
 			_ODP_IPV6HDR_LEN;
+	uint32_t l4_part_sum = 0;
 
 	/* Basic sanity checks on IPv6 header */
 	if ((odp_be_to_cpu_32(ipv6->ver_tc_flow) >> 28) != 6 ||
@@ -2099,6 +2144,18 @@ static inline uint8_t parse_ipv6(packet_parser_t *prs, const uint8_t **parseptr,
 	*offset   += sizeof(_odp_ipv6hdr_t);
 	*parseptr += sizeof(_odp_ipv6hdr_t);
 
+	if (chksums.chksum.udp || chksums.chksum.tcp) {
+		l4_part_sum = _odp_chksum_ones_comp16_32(
+				(const uint16_t *)&ipv4->src_addr,
+				2 * _ODP_IPV4ADDR_LEN, false);
+#if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+		l4_part_sum += ipv6->next_hdr;
+#else
+		l4_part_sum += ((uint16_t)ipv6->next_hdr) << 8;
+#endif
+		prs->l4_part_sum = l4_part_sum;
+	}
+
 	/* Skip past any IPv6 extension headers */
 	if (ipv6->next_hdr == _ODP_IPPROTO_HOPOPTS ||
 	    ipv6->next_hdr == _ODP_IPPROTO_ROUTE) {
@@ -2155,14 +2212,35 @@ static inline void parse_tcp(packet_parser_t *prs,
 /**
  * Parser helper function for UDP
  */
-static inline void parse_udp(packet_parser_t *prs,
-			     const uint8_t **parseptr, uint32_t *offset)
+static inline void parse_udp(packet_parser_t *prs, const uint8_t **parseptr,
+			     uint32_t *offset, odp_proto_chksums_t chksums)
 {
 	const _odp_udphdr_t *udp = (const _odp_udphdr_t *)*parseptr;
 	uint32_t udplen = odp_be_to_cpu_16(udp->length);
 
-	if (odp_unlikely(udplen < sizeof(_odp_udphdr_t)))
+	if (odp_unlikely(udplen < sizeof(_odp_udphdr_t))) {
 		prs->error_flags.udp_err = 1;
+		return;
+	}
+
+	if (chksums.chksum.udp &&
+	    !prs->input_flags.ipfrag) {
+		if (udp->chksum == 0) {
+			prs->input_flags.l4_chksum_done =
+				(prs->input_flags.ipv4 != 1);
+			prs->error_flags.l4_chksum =
+				(prs->input_flags.ipv4 != 1);
+		} else {
+			prs->input_flags.l4_chksum_done = 1;
+			prs->l4_part_sum += udp->length;
+			/* Do not include checksum into partial sum */
+			prs->l4_part_sum += _odp_chksum_ones_comp16_32(
+					(const void *)udp,
+					_ODP_UDPHDR_LEN - 2,
+					false);
+		}
+		prs->l4_sum = udp->chksum;
+	}
 
 	if (odp_cpu_to_be_16(_ODP_UDP_IPSEC_PORT) == udp->dst_port &&
 	    udplen > 4) {
@@ -2208,7 +2286,7 @@ int packet_parse_common_l3_l4(packet_parser_t *prs, const uint8_t *parseptr,
 	case _ODP_ETHTYPE_IPV6:
 		prs->input_flags.ipv6 = 1;
 		ip_proto = parse_ipv6(prs, &parseptr, &offset, frame_len,
-				      seg_len);
+				      seg_len, chksums);
 		prs->l4_offset = offset;
 		break;
 
@@ -2252,7 +2330,7 @@ int packet_parse_common_l3_l4(packet_parser_t *prs, const uint8_t *parseptr,
 		if (odp_unlikely(offset + _ODP_UDPHDR_LEN > seg_len))
 			return -1;
 		prs->input_flags.udp = 1;
-		parse_udp(prs, &parseptr, NULL);
+		parse_udp(prs, &parseptr, NULL, chksums);
 		break;
 
 	case _ODP_IPPROTO_AH:
@@ -2309,6 +2387,35 @@ int packet_parse_common(packet_parser_t *prs, const uint8_t *ptr,
 					 seg_len, layer, ethtype, chksums);
 }
 
+static int packet_l4_chksum(odp_packet_hdr_t *pkt_hdr,
+			    odp_proto_chksums_t chksums)
+{
+	/* UDP chksum == 0 case is covered in parse_udp() */
+	if (chksums.chksum.udp &&
+	    pkt_hdr->p.input_flags.udp &&
+	    !pkt_hdr->p.input_flags.ipfrag &&
+	    pkt_hdr->p.l4_sum != 0) {
+		uint16_t sum = packet_sum_ones_comp16(pkt_hdr,
+						      pkt_hdr->p.l4_offset +
+						      _ODP_UDPHDR_LEN,
+						      pkt_hdr->frame_len -
+						      pkt_hdr->p.l4_offset -
+						      _ODP_UDPHDR_LEN);
+
+		if (sum == 0)
+			sum = 0xffff;
+
+		if (sum != pkt_hdr->p.l4_sum) {
+			pkt_hdr->p.error_flags.l4_chksum = 1;
+			ODP_DBG("UDP chksum fail (%x)!\n", sum);
+		} else {
+			ODP_DBG("UDP chksum OK!\n");
+		}
+	}
+
+	return pkt_hdr->p.error_flags.all != 0;
+}
+
 /**
  * Simple packet parser
  */
@@ -2318,9 +2425,15 @@ int packet_parse_layer(odp_packet_hdr_t *pkt_hdr,
 {
 	uint32_t seg_len = packet_first_seg_len(pkt_hdr);
 	void *base = packet_data(pkt_hdr);
+	int rc;
+
+	rc = packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
+				 seg_len, layer, chksums);
 
-	return packet_parse_common(&pkt_hdr->p, base, pkt_hdr->frame_len,
-				   seg_len, layer, chksums);
+	if (rc != 0)
+		return rc;
+
+	return packet_l4_chksum(pkt_hdr, chksums);
 }
 
 int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
@@ -2363,7 +2476,12 @@ int odp_packet_parse(odp_packet_t pkt, uint32_t offset,
 						layer, ethtype,
 						param->chksums);
 
+		if (ret)
+			return -1;
+	}
 
+	if (layer == ODP_PROTO_LAYER_L4) {
+		ret = packet_l4_chksum(pkt_hdr, param->chksums);
 		if (ret)
 			return -1;
 	}
