diff --git a/platform/linux-generic/include/odp_buffer_internal.h b/platform/linux-generic/include/odp_buffer_internal.h
index 4cacca1..7ab53c6 100644
--- a/platform/linux-generic/include/odp_buffer_internal.h
+++ b/platform/linux-generic/include/odp_buffer_internal.h
@@ -65,7 +65,11 @@ _ODP_STATIC_ASSERT((ODP_CONFIG_PACKET_BUF_LEN_MAX %
 
 #define ODP_BUFFER_POOL_BITS   ODP_BITSIZE(ODP_CONFIG_POOLS)
 #define ODP_BUFFER_SEG_BITS    ODP_BITSIZE(ODP_BUFFER_MAX_SEG)
-#define ODP_BUFFER_INDEX_BITS  (32 - ODP_BUFFER_POOL_BITS - ODP_BUFFER_SEG_BITS)
+#define ODP_PACKET_REFS_BITS  ODP_BITSIZE(ODP_CONFIG_PACKET_REFS)
+
+#define ODP_BUFFER_INDEX_BITS  (32 - ODP_BUFFER_POOL_BITS - \
+				     ODP_BUFFER_SEG_BITS - \
+				     ODP_PACKET_REFS_BITS)
 #define ODP_BUFFER_PREFIX_BITS (ODP_BUFFER_POOL_BITS + ODP_BUFFER_INDEX_BITS)
 #define ODP_BUFFER_MAX_POOLS   (1 << ODP_BUFFER_POOL_BITS)
 #define ODP_BUFFER_MAX_BUFFERS (1 << ODP_BUFFER_INDEX_BITS)
@@ -79,6 +83,7 @@ typedef union odp_buffer_bits_t {
 		uint32_t     u32;
 		struct {
 #if ODP_BYTE_ORDER == ODP_BIG_ENDIAN
+			uint32_t ref:ODP_PACKET_REFS_BITS;
 			uint32_t pool_id:ODP_BUFFER_POOL_BITS;
 			uint32_t index:ODP_BUFFER_INDEX_BITS;
 			uint32_t seg:ODP_BUFFER_SEG_BITS;
@@ -86,6 +91,7 @@ typedef union odp_buffer_bits_t {
 			uint32_t seg:ODP_BUFFER_SEG_BITS;
 			uint32_t index:ODP_BUFFER_INDEX_BITS;
 			uint32_t pool_id:ODP_BUFFER_POOL_BITS;
+			uint32_t ref:ODP_PACKET_REFS_BITS;
 #endif
 		};
 
diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index ba2cd7e..2d6b58f 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -272,12 +272,30 @@ odp_packet_t _odp_packet_alloc(odp_pool_t pool_hdl);
 
 int _odp_packet_parse(odp_packet_hdr_t *pkt_hdr);
 
+void _odp_packet_init(odp_packet_t pkt);
+void _odp_packet_get(odp_packet_t pkt);
+uint32_t _odp_packet_put(odp_packet_t pkt);
+
 /* Convert a packet handle to a buffer handle */
 odp_buffer_t _odp_packet_to_buffer(odp_packet_t pkt);
 
 /* Convert a buffer handle to a packet handle */
 odp_packet_t _odp_packet_from_buffer(odp_buffer_t buf);
 
+/*  Internal debug function to check if packet has references
+ *
+ * @param pkt	Packet handle
+ * @retval 0	Packet does not have references
+ * @retval 1	Packet has references
+*/
+static inline int _odp_packet_refcheck(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	return odp_atomic_load_u32(&hdr->ref_count) - 1;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index 209a6e6..f6c5509 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -28,28 +28,34 @@
 odp_packet_t odp_packet_alloc(odp_pool_t pool_hdl, uint32_t len)
 {
 	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
+	odp_packet_t pkt = ODP_PACKET_INVALID;
 
 	if (pool->s.params.type != ODP_POOL_PACKET)
 		return ODP_PACKET_INVALID;
 
 	/* Handle special case for zero-length packets */
 	if (len == 0) {
-		odp_packet_t pkt =
+		pkt =
 			(odp_packet_t)buffer_alloc(pool_hdl,
 						   pool->s.params.buf.size);
-		if (pkt != ODP_PACKET_INVALID)
+		if (pkt != ODP_PACKET_INVALID) {
 			pull_tail(odp_packet_hdr(pkt),
 				  pool->s.params.buf.size);
-
+			_odp_packet_init(pkt);
+		}
 		return pkt;
 	}
 
-	return (odp_packet_t)buffer_alloc(pool_hdl, len);
+	pkt = (odp_packet_t)buffer_alloc(pool_hdl, len);
+	if (pkt != ODP_PACKET_INVALID)
+		_odp_packet_init(pkt);
+	return pkt;
 }
 
 void odp_packet_free(odp_packet_t pkt)
 {
-	odp_buffer_free((odp_buffer_t)pkt);
+	if (!_odp_packet_put(pkt))
+		odp_buffer_free((odp_buffer_t)pkt);
 }
 
 int odp_packet_reset(odp_packet_t pkt, uint32_t len)
@@ -85,6 +91,46 @@ odp_event_t odp_packet_to_event(odp_packet_t pkt)
 	return (odp_event_t)pkt;
 }
 
+void _odp_packet_init(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	odp_atomic_store_u32(&hdr->ref_count, 1);
+}
+
+void _odp_packet_get(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	odp_atomic_inc_u32(&hdr->ref_count);
+}
+
+uint32_t _odp_packet_put(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_hdr_t *hdr = odp_buf_to_hdr(buf);
+
+	return odp_atomic_fetch_dec_u32(&hdr->ref_count) - 1;
+}
+
+odp_packet_t odp_packet_create_ref(odp_packet_t pkt)
+{
+	odp_buffer_t buf = _odp_packet_to_buffer(pkt);
+	odp_buffer_bits_t handle;
+
+	_odp_packet_get(pkt);
+
+	handle.handle = buf;
+	if (handle.ref > ODP_CONFIG_PACKET_REFS)
+		ODP_ABORT("Maximum %d refs exceeded.\n",
+			  ODP_CONFIG_PACKET_REFS);
+	handle.ref += 1;
+
+	return _odp_packet_from_buffer(handle.handle);
+}
+
 /*
  *
  * Pointers and lengths
@@ -472,6 +518,9 @@ odp_packet_t odp_packet_rem_data(odp_packet_t pkt, uint32_t offset,
 	uint32_t pktlen = pkt_hdr->frame_len;
 	odp_packet_t newpkt;
 
+	if (_odp_packet_refcheck(pkt))
+		ODP_ABORT("modifying with refcounters is prohibited\n");
+
 	if (offset > pktlen || offset + len > pktlen)
 		return ODP_PACKET_INVALID;
 
@@ -577,6 +626,9 @@ int odp_packet_copydata_in(odp_packet_t pkt, uint32_t offset,
 	const uint8_t *srcaddr = (const uint8_t *)src;
 	odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
 
+	if (_odp_packet_refcheck(pkt))
+		ODP_ABORT("modifying with refcounters is prohibited\n");
+
 	if (offset + len > pkt_hdr->frame_len)
 		return -1;
 
@@ -679,6 +731,9 @@ int _odp_packet_copy_to_packet(odp_packet_t srcpkt, uint32_t srcoffset,
 	uint32_t srcseglen = 0; /* GCC */
 	uint32_t dstseglen = 0; /* GCC */
 
+	if (_odp_packet_refcheck(dstpkt))
+		ODP_ABORT("copiyng to packet with refcounters is prohibited\n");
+
 	if (srcoffset + len > srchdr->frame_len ||
 	    dstoffset + len > dsthdr->frame_len)
 		return -1;
