diff --git a/example/ipsec/odp_ipsec_stream.c b/example/ipsec/odp_ipsec_stream.c
index 91eba88..15cdb3d 100644
--- a/example/ipsec/odp_ipsec_stream.c
+++ b/example/ipsec/odp_ipsec_stream.c
@@ -175,7 +175,6 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
 				odp_buffer_pool_t pkt_pool)
 {
 	ipsec_cache_entry_t *entry = stream->input.entry;
-	odp_buffer_t bfr;
 	odp_packet_t pkt;
 	uint8_t *base;
 	uint8_t *data;
@@ -187,12 +186,10 @@ odp_packet_t create_ipv4_packet(stream_db_entry_t *stream,
 	stream_pkt_hdr_t *test;
 	uint i;
 
-	/* Get buffer */
-	bfr = odp_buffer_alloc(pkt_pool);
-	if (ODP_BUFFER_INVALID == bfr)
+	/* Get packet */
+	pkt = odp_packet_alloc(pkt_pool, 0);
+	if (ODP_PACKET_INVALID == pkt)
 		return ODP_PACKET_INVALID;
-	pkt = odp_packet_from_buffer(bfr);
-	odp_packet_init(pkt);
 	base = odp_packet_data(pkt);
 	data = odp_packet_data(pkt);
 
diff --git a/platform/linux-generic/include/api/odp_packet.h b/platform/linux-generic/include/api/odp_packet.h
index 9e2e542..9a15114 100644
--- a/platform/linux-generic/include/api/odp_packet.h
+++ b/platform/linux-generic/include/api/odp_packet.h
@@ -19,6 +19,7 @@ extern "C" {
 #endif
 
 #include <odp_buffer.h>
+#include <odp_platform_types.h>
 
 /** @defgroup odp_packet ODP PACKET
  *  Operations on a packet.
@@ -26,33 +27,73 @@ extern "C" {
  */
 
 
+/*
+ * Packet API v0.5 notes
+ * - Push/pull operations only on packet level
+ * - Push/pull within limits of segment headroom/tailroom/data lengths
+ * - Segment data length must be always at least one byte (i.e. there are no
+ *   empty segments)
+ * - Head/tailroom content belong to packet content (in addition to data
+ *   and meta-data) and thus is preserved over packet ownership changes.
+ * - _addr refer to a fixed address, which operations do not modify
+ * - _ptr refer to pointer to data, which may be modified by operations
+ */
+
+
+/*
+ *
+ * Alloc and free
+ * ********************************************************
+ *
+ */
+
 /**
- * Allocate and initialize a packet buffer from a packet pool
+ * Allocate a packet from a buffer pool
  *
- * @param pool_id  Pool handle
+ * Allocates a packet of the requested length from the specified buffer pool.
+ * Pool must have been created with buffer type ODP_BUFFER_TYPE_PACKET. The
+ * packet is initialized with data pointers and lengths set according to the
+ * specified len, and the default headroom and tailroom length settings. All
+ * other packet metadata are set to their default values.
  *
- * @note  The pool must have been created with 'buf_type=ODP_BUFFER_TYPE_PACKET'
+ * @param pool          Pool handle
+ * @param len           Packet data length
  *
- * @return Packet handle or ODP_PACKET_INVALID
+ * @return Handle of allocated packet
+ * @retval ODP_PACKET_INVALID  Packet could not be allocated
+ *
+ * @note The default headroom and tailroom used for packets is specified by
+ * the ODP_CONFIG_PACKET_HEADROOM and ODP_CONFIG_PACKET_TAILROOM defines in
+ * odp_config.h.
  */
-odp_packet_t odp_packet_alloc(odp_buffer_pool_t pool_id);
+odp_packet_t odp_packet_alloc(odp_buffer_pool_t pool, uint32_t len);
 
 /**
- * Free a packet buffer back into the packet pool
+ * Free packet
  *
- * @param pkt  Packet handle
+ * Frees the packet into the buffer pool it was allocated from.
+ *
+ * @param pkt           Packet handle
  */
 void odp_packet_free(odp_packet_t pkt);
 
 /**
- * Initialize the packet
+ * Reset packet
  *
- * Needs to be called if the user allocates a packet buffer, i.e. the packet
- * has not been received from I/O through ODP.
+ * Resets all packet meta-data to their default values. Packet length is used
+ * to initialize pointers and lengths. It must be less than the total buffer
+ * length of the packet minus the default headroom length. Packet is not
+ * modified on failure.
  *
- * @param pkt  Packet handle
+ * @param pkt           Packet handle
+ * @param len           Packet data length
+ *
+ * @retval 0 Success
+ * @retval Non-zero Failure
+ *
+ * @see odp_packet_buf_len()
  */
-void odp_packet_init(odp_packet_t pkt);
+int odp_packet_reset(odp_packet_t pkt, uint32_t len);
 
 /**
  * Convert a buffer handle to a packet handle
diff --git a/platform/linux-generic/include/odp_packet_internal.h b/platform/linux-generic/include/odp_packet_internal.h
index f34a83d..9553f13 100644
--- a/platform/linux-generic/include/odp_packet_internal.h
+++ b/platform/linux-generic/include/odp_packet_internal.h
@@ -169,6 +169,7 @@ static inline void packet_init(pool_entry_t *pool,
 		(pool->s.headroom + size);
 }
 
+odp_packet_t _odp_packet_alloc(odp_buffer_pool_t pool_hdl);
 
 #ifdef __cplusplus
 }
diff --git a/platform/linux-generic/odp_packet.c b/platform/linux-generic/odp_packet.c
index 8a941ce..a4ad3e8 100644
--- a/platform/linux-generic/odp_packet.c
+++ b/platform/linux-generic/odp_packet.c
@@ -21,34 +21,54 @@ static inline uint8_t parse_ipv4(odp_packet_hdr_t *pkt_hdr,
 static inline uint8_t parse_ipv6(odp_packet_hdr_t *pkt_hdr,
 				 odph_ipv6hdr_t *ipv6, size_t *offset_out);
 
-odp_packet_t odp_packet_alloc(odp_buffer_pool_t pool_id)
+/*
+ *
+ * Alloc and free
+ * ********************************************************
+ *
+ */
+
+odp_packet_t odp_packet_alloc(odp_buffer_pool_t pool_hdl, uint32_t len)
 {
-	odp_packet_t pkt;
-	odp_buffer_t buf;
+	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
 
-	buf = odp_buffer_alloc(pool_id);
-	if (odp_unlikely(!odp_buffer_is_valid(buf)))
+	if (pool->s.params.buf_type != ODP_BUFFER_TYPE_PACKET)
 		return ODP_PACKET_INVALID;
 
-	pkt = odp_packet_from_buffer(buf);
-	odp_packet_init(pkt);
+	/* Handle special case for zero-length packets */
+	if (len == 0) {
+		odp_packet_t pkt =
+			(odp_packet_t)buffer_alloc(pool_hdl,
+						   pool->s.params.buf_size);
+		if (pkt != ODP_PACKET_INVALID) {
+			odp_packet_hdr_t *pkt_hdr = odp_packet_hdr(pkt);
+			uint32_t buf_size = pool->s.params.buf_size;
+			pkt_hdr->tailroom  += buf_size;
+			pkt_hdr->frame_len -= buf_size;
+		}
 
-	return pkt;
+		return pkt;
+	}
+
+	return (odp_packet_t)buffer_alloc(pool_hdl, len);
 }
 
 void odp_packet_free(odp_packet_t pkt)
 {
-	odp_buffer_t buf = odp_packet_to_buffer(pkt);
-
-	odp_buffer_free(buf);
+	odp_buffer_free((odp_buffer_t)pkt);
 }
 
-void odp_packet_init(odp_packet_t pkt)
+int odp_packet_reset(odp_packet_t pkt, uint32_t len)
 {
 	odp_packet_hdr_t *const pkt_hdr = odp_packet_hdr(pkt);
 	pool_entry_t *pool = odp_buf_to_pool(&pkt_hdr->buf_hdr);
+	uint32_t totsize = pool->s.headroom + len + pool->s.tailroom;
+
+	if (totsize > pkt_hdr->buf_hdr.size)
+		return -1;
 
-	packet_init(pool, pkt_hdr, 0);
+	packet_init(pool, pkt_hdr, len);
+	return 0;
 }
 
 odp_packet_t odp_packet_from_buffer(odp_buffer_t buf)
@@ -404,3 +424,21 @@ size_t odp_packet_buf_size(odp_packet_t pkt)
 
 	return odp_buffer_size(buf);
 }
+
+/*
+ *
+ * Internal Use Routines
+ * ********************************************************
+ *
+ */
+
+odp_packet_t _odp_packet_alloc(odp_buffer_pool_t pool_hdl)
+{
+	pool_entry_t *pool = odp_pool_to_entry(pool_hdl);
+
+	if (pool->s.params.buf_type != ODP_BUFFER_TYPE_PACKET)
+		return ODP_PACKET_INVALID;
+
+	return (odp_packet_t)buffer_alloc(pool_hdl,
+					  pool->s.params.buf_size);
+}
diff --git a/platform/linux-generic/odp_packet_socket.c b/platform/linux-generic/odp_packet_socket.c
index b11aa1c..76e437a 100644
--- a/platform/linux-generic/odp_packet_socket.c
+++ b/platform/linux-generic/odp_packet_socket.c
@@ -215,7 +215,7 @@ int setup_pkt_sock(pkt_sock_t *const pkt_sock, const char *netdev,
 		return -1;
 	pkt_sock->pool = pool;
 
-	pkt = odp_packet_alloc(pool);
+	pkt = _odp_packet_alloc(pool);
 	if (!odp_packet_is_valid(pkt))
 		return -1;
 
@@ -332,7 +332,7 @@ int recv_pkt_sock_basic(pkt_sock_t *const pkt_sock,
 
 	for (i = 0; i < len; i++) {
 		if (odp_likely(pkt == ODP_PACKET_INVALID)) {
-			pkt = odp_packet_alloc(pkt_sock->pool);
+			pkt = _odp_packet_alloc(pkt_sock->pool);
 			if (odp_unlikely(pkt == ODP_PACKET_INVALID))
 				break;
 		}
@@ -430,7 +430,7 @@ int recv_pkt_sock_mmsg(pkt_sock_t *const pkt_sock,
 	memset(msgvec, 0, sizeof(msgvec));
 
 	for (i = 0; i < (int)len; i++) {
-		pkt_table[i] = odp_packet_alloc(pkt_sock->pool);
+		pkt_table[i] = _odp_packet_alloc(pkt_sock->pool);
 		if (odp_unlikely(pkt_table[i] == ODP_PACKET_INVALID))
 			break;
 
@@ -605,7 +605,7 @@ static inline unsigned pkt_mmap_v2_rx(int sock, struct ring *ring,
 				continue;
 			}
 
-			pkt_table[i] = odp_packet_alloc(pool);
+			pkt_table[i] = _odp_packet_alloc(pool);
 			if (odp_unlikely(pkt_table[i] == ODP_PACKET_INVALID))
 				break;
 
@@ -851,7 +851,7 @@ int setup_pkt_sock_mmap(pkt_sock_mmap_t *const pkt_sock, const char *netdev,
 	if (pool == ODP_BUFFER_POOL_INVALID)
 		return -1;
 
-	pkt = odp_packet_alloc(pool);
+	pkt = _odp_packet_alloc(pool);
 	if (!odp_packet_is_valid(pkt))
 		return -1;
 
