diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index eb49f4555..7c288791e 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -163,6 +163,7 @@ noinst_HEADERS = \
 		  ${srcdir}/include/odp_errno_define.h \
 		  ${srcdir}/include/odp_forward_typedefs_internal.h \
 		  ${srcdir}/include/odp_internal.h \
+		  ${srcdir}/include/odp_ipsec_internal.h \
 		  ${srcdir}/include/odp_llqueue.h \
 		  ${srcdir}/include/odp_name_table_internal.h \
 		  ${srcdir}/include/odp_packet_internal.h \
@@ -220,6 +221,7 @@ __LIB__libodp_linux_la_SOURCES = \
 			   odp_init.c \
 			   odp_impl.c \
 			   odp_ipsec.c \
+			   odp_ipsec_events.c \
 			   odp_name_table.c \
 			   odp_packet.c \
 			   odp_packet_flags.c \
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index 3b8dd3350..a959e666c 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -74,6 +74,7 @@ enum init_stage {
 	CLASSIFICATION_INIT,
 	TRAFFIC_MNGR_INIT,
 	NAME_TABLE_INIT,
+	IPSEC_EVENTS_INIT,
 	MODULES_INIT,
 	ALL_INIT      /* All init stages completed */
 };
@@ -133,6 +134,9 @@ int _odp_ishm_init_local(void);
 int _odp_ishm_term_global(void);
 int _odp_ishm_term_local(void);
 
+int _odp_ipsec_events_init_global(void);
+int _odp_ipsec_events_term_global(void);
+
 int _odp_modules_init_global(void);
 
 int cpuinfo_parser(FILE *file, system_info_t *sysinfo);
diff --git a/platform/linux-generic/include/odp_ipsec_internal.h b/platform/linux-generic/include/odp_ipsec_internal.h
new file mode 100644
index 000000000..b31f048ff
--- /dev/null
+++ b/platform/linux-generic/include/odp_ipsec_internal.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+/**
+ * @file
+ *
+ * ODP internal IPsec routines
+ */
+
+#ifndef ODP_IPSEC_INTERNAL_H_
+#define ODP_IPSEC_INTERNAL_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <odp/api/std_types.h>
+#include <odp/api/plat/strong_types.h>
+
+#include <odp/api/ipsec.h>
+
+/** @ingroup odp_ipsec
+ *  @{
+ */
+
+typedef ODP_HANDLE_T(ipsec_status_t);
+
+#define ODP_IPSEC_STATUS_INVALID \
+	_odp_cast_scalar(ipsec_status_t, 0xffffffff)
+
+/**
+ * @internal Get ipsec_status handle from event
+ *
+ * Converts an ODP_EVENT_IPSEC_STATUS type event to an IPsec status event.
+ *
+ * @param ev   Event handle
+ *
+ * @return IPsec status handle
+ *
+ * @see odp_event_type()
+ */
+ipsec_status_t _odp_ipsec_status_from_event(odp_event_t ev);
+
+/**
+ * @internal Free IPsec status event
+ *
+ * Frees the ipsec_status into the ipsec_status pool it was allocated from.
+ *
+ * @param res           IPsec status handle
+ */
+void _odp_ipsec_status_free(ipsec_status_t status);
+
+/**
+ * @internal Send ODP_IPSEC_STATUS event
+ *
+ * Sends the ipsec_status event using provided information
+ *
+ * @param queue         destination queue
+ * @param id            status id
+ * @param sa            SA respective to the operation
+ * @param result        status value
+ * @param warn          generated warning
+ *
+ * @retval 0 on success
+ * @retval <0 on failure
+ */
+int _odp_ipsec_status_send(odp_queue_t queue,
+			   odp_ipsec_status_id_t id,
+			   odp_ipsec_sa_t sa,
+			   int result,
+			   odp_ipsec_warn_t warn);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/odp_event.c b/platform/linux-generic/odp_event.c
index 991d0e17d..66bc11fe4 100644
--- a/platform/linux-generic/odp_event.c
+++ b/platform/linux-generic/odp_event.c
@@ -13,6 +13,7 @@
 #include <odp/api/timer.h>
 #include <odp/api/pool.h>
 #include <odp_buffer_internal.h>
+#include <odp_ipsec_internal.h>
 #include <odp_buffer_inlines.h>
 #include <odp_debug_internal.h>
 
@@ -51,6 +52,9 @@ void odp_event_free(odp_event_t event)
 	case ODP_EVENT_CRYPTO_COMPL:
 		odp_crypto_compl_free(odp_crypto_compl_from_event(event));
 		break;
+	case ODP_EVENT_IPSEC_STATUS:
+		_odp_ipsec_status_free(_odp_ipsec_status_from_event(event));
+		break;
 	default:
 		ODP_ABORT("Invalid event type: %d\n", odp_event_type(event));
 	}
diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
index fdccac7cd..8234df974 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -223,6 +223,12 @@ int odp_init_global(odp_instance_t *instance,
 	}
 	stage = NAME_TABLE_INIT;
 
+	if (_odp_ipsec_events_init_global()) {
+		ODP_ERR("ODP IPsec events init failed.\n");
+		goto init_failed;
+	}
+	stage = IPSEC_EVENTS_INIT;
+
 	if (_odp_modules_init_global()) {
 		ODP_ERR("ODP modules init failed\n");
 		goto init_failed;
@@ -253,6 +259,13 @@ int _odp_term_global(enum init_stage stage)
 	switch (stage) {
 	case ALL_INIT:
 	case MODULES_INIT:
+	case IPSEC_EVENTS_INIT:
+		if (_odp_ipsec_events_term_global()) {
+			ODP_ERR("ODP IPsec events term failed.\n");
+			rc = -1;
+		}
+		/* Fall through */
+
 	case NAME_TABLE_INIT:
 		if (_odp_int_name_tbl_term_global()) {
 			ODP_ERR("Name table term failed.\n");
diff --git a/platform/linux-generic/odp_ipsec.c b/platform/linux-generic/odp_ipsec.c
index 04757e788..e42b488f5 100644
--- a/platform/linux-generic/odp_ipsec.c
+++ b/platform/linux-generic/odp_ipsec.c
@@ -6,6 +6,8 @@
 
 #include <odp/api/ipsec.h>
 
+#include <odp_ipsec_internal.h>
+
 #include <string.h>
 
 int odp_ipsec_capability(odp_ipsec_capability_t *capa)
@@ -139,14 +141,6 @@ int odp_ipsec_result(odp_ipsec_packet_result_t *result, odp_packet_t packet)
 	return -1;
 }
 
-int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event)
-{
-	(void)status;
-	(void)event;
-
-	return -1;
-}
-
 int odp_ipsec_sa_mtu_update(odp_ipsec_sa_t sa, uint32_t mtu)
 {
 	(void)sa;
diff --git a/platform/linux-generic/odp_ipsec_events.c b/platform/linux-generic/odp_ipsec_events.c
new file mode 100644
index 000000000..3a7ebd6e8
--- /dev/null
+++ b/platform/linux-generic/odp_ipsec_events.c
@@ -0,0 +1,156 @@
+/* Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include "config.h"
+
+#include <odp/api/ipsec.h>
+#include <odp/api/shared_memory.h>
+
+#include <odp_buffer_internal.h>
+#include <odp_buffer_inlines.h>
+#include <odp_debug_internal.h>
+#include <odp_ipsec_internal.h>
+#include <odp_pool_internal.h>
+
+typedef struct {
+	/* common buffer header */
+	odp_buffer_hdr_t buf_hdr;
+
+	odp_ipsec_status_t status;
+} ipsec_status_hdr_t;
+
+static odp_pool_t ipsec_status_pool = ODP_POOL_INVALID;
+
+#define IPSEC_EVENTS_POOL_BUF_COUNT 1024
+
+int _odp_ipsec_events_init_global(void)
+{
+	odp_pool_param_t param;
+
+	odp_pool_param_init(&param);
+
+	param.buf.size  = sizeof(ipsec_status_hdr_t);
+	param.buf.align = 0;
+	param.buf.num   = IPSEC_EVENTS_POOL_BUF_COUNT;
+	param.type      = ODP_POOL_BUFFER;
+
+	ipsec_status_pool = odp_pool_create("ipsec_status_pool", &param);
+	if (ODP_POOL_INVALID == ipsec_status_pool) {
+		ODP_ERR("Error: status pool create failed.\n");
+		goto err_status;
+	}
+
+	return 0;
+
+err_status:
+	return -1;
+}
+
+int _odp_ipsec_events_term_global(void)
+{
+	int ret = 0;
+	int rc = 0;
+
+	ret = odp_pool_destroy(ipsec_status_pool);
+	if (ret < 0) {
+		ODP_ERR("status pool destroy failed");
+		rc = -1;
+	}
+
+	return rc;
+}
+
+ipsec_status_t _odp_ipsec_status_from_event(odp_event_t ev)
+{
+	ODP_ASSERT(ODP_EVENT_INVALID != ev);
+	ODP_ASSERT(ODP_EVENT_IPSEC_STATUS == odp_event_type(ev));
+
+	return (ipsec_status_t)ev;
+}
+
+static odp_event_t ipsec_status_to_event(ipsec_status_t status)
+{
+	ODP_ASSERT(ODP_IPSEC_STATUS_INVALID != status);
+
+	return (odp_event_t)status;
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr_from_buf(odp_buffer_t buf)
+{
+	return (ipsec_status_hdr_t *)(void *)buf_hdl_to_hdr(buf);
+}
+
+static ipsec_status_hdr_t *ipsec_status_hdr(ipsec_status_t status)
+{
+	odp_buffer_t buf = odp_buffer_from_event(ipsec_status_to_event(status));
+
+	return ipsec_status_hdr_from_buf(buf);
+}
+
+static ipsec_status_t odp_ipsec_status_alloc(void)
+{
+	odp_buffer_t buf = odp_buffer_alloc(ipsec_status_pool);
+
+	if (odp_unlikely(buf == ODP_BUFFER_INVALID))
+		return ODP_IPSEC_STATUS_INVALID;
+
+	_odp_buffer_event_type_set(buf, ODP_EVENT_IPSEC_STATUS);
+
+	return _odp_ipsec_status_from_event(odp_buffer_to_event(buf));
+}
+
+void _odp_ipsec_status_free(ipsec_status_t status)
+{
+	odp_event_t ev = ipsec_status_to_event(status);
+
+	odp_buffer_free(odp_buffer_from_event(ev));
+}
+
+int _odp_ipsec_status_send(odp_queue_t queue,
+			   odp_ipsec_status_id_t id,
+			   odp_ipsec_sa_t sa,
+			   int result,
+			   odp_ipsec_warn_t warn)
+{
+	ipsec_status_t ipsec_ev = odp_ipsec_status_alloc();
+	ipsec_status_hdr_t *status_hdr;
+
+	if (ODP_IPSEC_STATUS_INVALID == ipsec_ev)
+		return -1;
+
+	status_hdr = ipsec_status_hdr(ipsec_ev);
+
+	status_hdr->status.id = id;
+	status_hdr->status.sa = sa;
+	status_hdr->status.result = result;
+	status_hdr->status.warn = warn;
+
+	if (odp_queue_enq(queue, ipsec_status_to_event(ipsec_ev))) {
+		_odp_ipsec_status_free(ipsec_ev);
+		return -1;
+	}
+
+	return 0;
+}
+
+int odp_ipsec_status(odp_ipsec_status_t *status, odp_event_t event)
+{
+	ipsec_status_t ipsec_ev;
+	ipsec_status_hdr_t *status_hdr;
+
+	if (odp_unlikely(ODP_EVENT_INVALID == event))
+		return -1;
+
+	ipsec_ev = _odp_ipsec_status_from_event(event);
+	if (odp_unlikely(ODP_IPSEC_STATUS_INVALID == ipsec_ev))
+		return -1;
+
+	status_hdr = ipsec_status_hdr(ipsec_ev);
+
+	*status = status_hdr->status;
+
+	return 0;
+}
