[qca-nss-ecm] Add Mark Classifier

With this classifier an external kernel module which
is registered with this classifiers callbacks can inspect
the skb and make smart decisions on that buffer, returns
a mark value extracted from the buffer for future use.

Change-Id: I6b1cce6585d105603bef84f697bc0d47fb6b0105
Signed-off-by: Murat Sezgin <msezgin@codeaurora.org>
diff --git a/Makefile b/Makefile
index cd25069..34f48d4 100755
--- a/Makefile
+++ b/Makefile
@@ -196,6 +196,13 @@
 ccflags-$(ECM_IPV6_ENABLE) += -DECM_IPV6_ENABLE
 
 # #############################################################################
+# Define ECM_CLASSIFIER_MARK_ENABLE=y in order to enable mark classifier.
+# #############################################################################
+ECM_CLASSIFIER_MARK_ENABLE=y
+ecm-$(ECM_CLASSIFIER_MARK_ENABLE) += ecm_classifier_mark.o
+ccflags-$(ECM_CLASSIFIER_MARK_ENABLE) += -DECM_CLASSIFIER_MARK_ENABLE
+
+# #############################################################################
 # Define ECM_CLASSIFIER_NL_ENABLE=y in order to enable NL classifier.
 # #############################################################################
 ifeq ($(findstring 4.4., $(KERNELVERSION)),)
@@ -299,6 +306,7 @@
 # By turning off debugs you gain maximum ECM performance.
 # #############################################################################
 ccflags-y += -DECM_CLASSIFIER_DEBUG_LEVEL=1
+ccflags-y += -DECM_CLASSIFIER_MARK_DEBUG_LEVEL=1
 ccflags-y += -DECM_CLASSIFIER_DSCP_DEBUG_LEVEL=1
 ccflags-y += -DECM_CLASSIFIER_HYFI_DEBUG_LEVEL=1
 ccflags-y += -DECM_CLASSIFIER_PCC_DEBUG_LEVEL=1
diff --git a/ecm_classifier.c b/ecm_classifier.c
index a6da26c..0dac519 100644
--- a/ecm_classifier.c
+++ b/ecm_classifier.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2016 The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2016, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -50,6 +50,9 @@
 #ifdef ECM_CLASSIFIER_PCC_ENABLE
 #include "ecm_classifier_pcc.h"
 #endif
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+#include "ecm_classifier_mark.h"
+#endif
 
 /*
  * ecm_classifier_assign_classifier()
@@ -115,6 +118,19 @@
 		return (struct ecm_classifier_instance *)chfi;
 	}
 #endif
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+	if (type == ECM_CLASSIFIER_TYPE_MARK) {
+		struct ecm_classifier_mark_instance *ecmi;
+		ecmi = ecm_classifier_mark_instance_alloc(ci);
+		if (!ecmi) {
+			DEBUG_TRACE("%p: Failed to create mark classifier\n", ci);
+			return NULL;
+		}
+		DEBUG_TRACE("%p: Created mark classifier: %p\n", ci, ecmi);
+		ecm_db_connection_classifier_assign(ci, (struct ecm_classifier_instance *)ecmi);
+		return (struct ecm_classifier_instance *)ecmi;
+	}
+#endif
 
 	DEBUG_ASSERT(NULL, "%p: Unsupported type: %d\n", ci, type);
 	return NULL;
diff --git a/ecm_classifier.h b/ecm_classifier.h
index b3dffe8..fdda166 100644
--- a/ecm_classifier.h
+++ b/ecm_classifier.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2014-2015 The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2014-2015, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -22,6 +22,9 @@
  */
 enum ecm_classifier_types {
 	ECM_CLASSIFIER_TYPE_DEFAULT = 0,	/* MUST BE FIRST, Default classifier */
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+	ECM_CLASSIFIER_TYPE_MARK,		/* Mark classifier */
+#endif
 #ifdef ECM_CLASSIFIER_HYFI_ENABLE
 	ECM_CLASSIFIER_TYPE_HYFI,		/* HyFi classifier */
 #endif
diff --git a/ecm_classifier_mark.c b/ecm_classifier_mark.c
new file mode 100644
index 0000000..92a9972
--- /dev/null
+++ b/ecm_classifier_mark.c
@@ -0,0 +1,786 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2018, The Linux Foundation.  All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * Mark Classifier.
+ * This module provides an interface to customer's external module to interract with the ECM.
+ * It allows the customer to make smart decisions on the packets before offloading the connection to the
+ * acceleration engine.
+ */
+
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/ip.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/debugfs.h>
+#include <linux/ctype.h>
+#include <net/tcp.h>
+#include <net/ipv6.h>
+#include <linux/inet.h>
+#include <linux/in.h>
+#include <linux/etherdevice.h>
+
+/*
+ * Debug output levels
+ * 0 = OFF
+ * 1 = ASSERTS / ERRORS
+ * 2 = 1 + WARN
+ * 3 = 2 + INFO
+ * 4 = 3 + TRACE
+ */
+#define DEBUG_LEVEL ECM_CLASSIFIER_MARK_DEBUG_LEVEL
+
+#include "ecm_types.h"
+#include "ecm_db_types.h"
+#include "ecm_state.h"
+#include "ecm_tracker.h"
+#include "ecm_classifier.h"
+#include "ecm_front_end_types.h"
+#include "ecm_tracker_datagram.h"
+#include "ecm_tracker_udp.h"
+#include "ecm_tracker_tcp.h"
+#include "ecm_db.h"
+#include "ecm_classifier_mark.h"
+#include "ecm_classifier_mark_public.h"
+#include "ecm_front_end_common.h"
+#include "ecm_front_end_ipv4.h"
+#ifdef ECM_IPV6_ENABLE
+#include "ecm_front_end_ipv6.h"
+#endif
+
+/*
+ * Magic numbers
+ */
+#define ECM_CLASSIFIER_MARK_INSTANCE_MAGIC 0x2567
+
+/*
+ * struct ecm_classifier_mark_instance
+ * 	State per connection for MARK classifier
+ */
+struct ecm_classifier_mark_instance {
+	struct ecm_classifier_instance base;			/* Base type */
+
+	uint32_t ci_serial;					/* RO: Serial of the connection */
+
+	struct ecm_classifier_mark_instance *next;		/* Next classifier state instance (for accouting and reporting purposes) */
+	struct ecm_classifier_mark_instance *prev;		/* Next classifier state instance (for accouting and reporting purposes) */
+
+	uint32_t mark;						/* Mark value which this classifier extracted from the buffer */
+	struct ecm_classifier_process_response process_response;
+								/* Last process response computed */
+	int refs;						/* Integer to trap we never go negative */
+#if (DEBUG_LEVEL > 0)
+	uint16_t magic;
+#endif
+};
+
+/*
+ * Operational control. By default it is disabled.
+ */
+static int ecm_classifier_mark_enabled;			/* Operational behaviour */
+
+/*
+ * Management thread control
+ */
+static bool ecm_classifier_mark_terminate_pending;	/* True when the user wants us to terminate */
+
+/*
+ * Debugfs dentry object.
+ */
+static struct dentry *ecm_classifier_mark_dentry;
+
+/*
+ * Locking of the classifier structures
+ */
+static DEFINE_SPINLOCK(ecm_classifier_mark_lock);			/* Protect SMP access. */
+
+/*
+ * List of our classifier instances
+ */
+static struct ecm_classifier_mark_instance *ecm_classifier_mark_instances = NULL;
+								/* list of all active instances */
+static int ecm_classifier_mark_count = 0;			/* Tracks number of instances allocated */
+
+/*
+ * Callbacks to the external modules.
+ */
+ecm_classifier_mark_get_callback_t mark_get_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
+								/* Callbacks which gets the mark from the packets */
+ecm_classifier_mark_sync_to_ipv4_callback_t sync_to_ipv4_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
+								/* Callbacks which syncs the IPv4 stats to the registered external subsystems */
+ecm_classifier_mark_sync_to_ipv6_callback_t sync_to_ipv6_cb[ECM_CLASSIFIER_MARK_TYPE_MAX] = { NULL };
+								/* Callbacks which syncs the IPv6 stats to the registered external subsystems */
+
+/*
+ * _ecm_classifier_mark_ref()
+ *	Ref
+ */
+static void _ecm_classifier_mark_ref(struct ecm_classifier_mark_instance *ecmi)
+{
+	ecmi->refs++;
+	DEBUG_TRACE("%p: ecmi ref %d\n", ecmi, ecmi->refs);
+	DEBUG_ASSERT(ecmi->refs > 0, "%p: ref wrap\n", ecmi);
+}
+
+/*
+ * ecm_classifier_mark_ref()
+ *	Ref
+ */
+static void ecm_classifier_mark_ref(struct ecm_classifier_instance *ci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)ci;
+
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	_ecm_classifier_mark_ref(ecmi);
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+}
+
+/*
+ * ecm_classifier_mark_deref()
+ *	Deref
+ */
+static int ecm_classifier_mark_deref(struct ecm_classifier_instance *ci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)ci;
+
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecmi->refs--;
+	DEBUG_ASSERT(ecmi->refs >= 0, "%p: refs wrapped\n", ecmi);
+	DEBUG_TRACE("%p: Mark classifier deref %d\n", ecmi, ecmi->refs);
+	if (ecmi->refs) {
+		int refs = ecmi->refs;
+		spin_unlock_bh(&ecm_classifier_mark_lock);
+		return refs;
+	}
+
+	/*
+	 * Object to be destroyed
+	 */
+	ecm_classifier_mark_count--;
+	DEBUG_ASSERT(ecm_classifier_mark_count >= 0, "%p: ecm_classifier_mark_count wrap\n", ecmi);
+
+	/*
+	 * UnLink the instance from our list
+	 */
+	if (ecmi->next) {
+		ecmi->next->prev = ecmi->prev;
+	}
+	if (ecmi->prev) {
+		ecmi->prev->next = ecmi->next;
+	} else {
+		DEBUG_ASSERT(ecm_classifier_mark_instances == ecmi, "%p: list bad %p\n", ecmi, ecm_classifier_mark_instances);
+		ecm_classifier_mark_instances = ecmi->next;
+	}
+	ecmi->next = NULL;
+	ecmi->prev = NULL;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+
+	/*
+	 * Final
+	 */
+	DEBUG_INFO("%p: Final mark classifier instance\n", ecmi);
+	kfree(ecmi);
+
+	return 0;
+}
+
+/*
+ * ecm_classifier_mark_process()
+ *	Process new packet
+ *
+ * NOTE: This function would only ever be called if all other classifiers have failed.
+ */
+static void ecm_classifier_mark_process(struct ecm_classifier_instance *aci, ecm_tracker_sender_type_t sender,
+									struct ecm_tracker_ip_header *ip_hdr, struct sk_buff *skb,
+									struct ecm_classifier_process_response *process_response)
+{
+	struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
+	ecm_classifier_mark_result_t result;
+	struct ecm_db_connection_instance *ci;
+	bool skb_header_adjusted = false;
+	uint32_t l2_encap_len;
+	uint16_t l2_encap_proto;
+	uint16_t skb_proto;
+	uint32_t mark = 0;
+	ecm_classifier_mark_get_callback_t cb = NULL;
+	ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
+			/* TODO: By default we use this type. In the future the type will be gotten from the connection instance*/
+
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: invalid state magic\n", ecmi);
+
+	/*
+	 * Not relevant to the connection if not enabled.
+	 */
+	if (unlikely(!ecm_classifier_mark_enabled)) {
+		/*
+		 * Not relevant.
+		 */
+		DEBUG_WARN("%p: Mark classifier is not enabled.\n", aci);
+		goto not_relevant;
+	}
+
+	/*
+	 * Get connection
+	 */
+	ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
+	if (!ci) {
+		/*
+		 * Connection has gone from under us
+		 */
+		DEBUG_WARN("%p: connection instance gone while processing classifier.\n", aci);
+		goto not_relevant;
+	}
+
+	/*
+	 * Get the L2 encap protocol of the connection.
+	 */
+	l2_encap_proto = ecm_db_connection_l2_encap_proto_get(ci);
+	if (!l2_encap_proto) {
+		/*
+		 * The caller is not interested in the inspection. Not relevant.
+		 */
+		DEBUG_WARN("%p: L2 encap protocol is zero.\n", aci);
+		ecm_db_connection_deref(ci);
+		goto not_relevant;
+	}
+
+	/*
+	 * Is there an external callback to get the mark value from the packet?
+	 */
+	rcu_read_lock();
+	cb = rcu_dereference(mark_get_cb[type]);
+	if (!cb) {
+		/*
+		 * Not relevant.
+		 */
+		rcu_read_unlock();
+		DEBUG_WARN("%p: No inspection callback set with type: %d.\n", aci, type);
+		ecm_db_connection_deref(ci);
+		goto not_relevant;
+	}
+
+	/*
+	 * Before giving the skb to the external callback for getting the mark value,
+	 * let's adjust the L2 encap header.
+	 * If the skb's protocol is not equal to the connection's L2 encap protocol,
+	 * this means the skb's L2 header is pulled off. So, let's push it back.
+	 */
+	if (l2_encap_proto != ntohs(skb->protocol)) {
+		DEBUG_TRACE("%p: Connections L2 encap protocol: %x is different than skb's protocol: %x, need header adjustment.\n",
+			    aci, l2_encap_proto, ntohs(skb->protocol));
+		l2_encap_len = ecm_front_end_l2_encap_header_len(l2_encap_proto);
+		skb_header_adjusted = true;
+		ecm_front_end_push_l2_encap_header(skb, l2_encap_len);
+		skb_proto = skb->protocol;
+		skb->protocol = htons(l2_encap_proto);
+	}
+
+	/*
+	 * Call the external callback and get the result.
+	 */
+	result = cb(skb, &mark);
+	rcu_read_unlock();
+
+	/*
+	 * Pull the skb's L2 header, if it is adjusted in this classifier.
+	 */
+	if (skb_header_adjusted) {
+		DEBUG_TRACE("%p: Adjust the L2 header back.\n", aci);
+		ecm_front_end_pull_l2_encap_header(skb, l2_encap_len);
+		skb->protocol = skb_proto;
+	}
+
+	/*
+	 * Handle the result
+	 */
+	switch (result) {
+	case ECM_CLASSIFIER_MARK_RESULT_NOT_YET:
+		/*
+		 * Deny accel but don't change the permit state - we try again later
+		 */
+		DEBUG_WARN("%p: External callback has not decided yet.\n", aci);
+		goto deny_accel;
+	case ECM_CLASSIFIER_MARK_RESULT_NOT_RELEVANT:
+		/*
+		 * The callback told us that the classifier is not relevant to this connection.
+		 */
+		DEBUG_WARN("%p: External callback decided not relevant.\n", aci);
+		ecm_db_connection_deref(ci);
+		goto not_relevant;
+	case ECM_CLASSIFIER_MARK_RESULT_SUCCESS:
+
+		/*
+		 * Copy the returned mark value to the connection and the classifier instance.
+		 * We copy the mark value to the classifier instance as well here for quick access
+		 * in the sync functions.
+		 */
+		DEBUG_WARN("%p: External callback returned the mark value: %x\n", aci, mark);
+		ecm_db_connection_mark_set(ci, mark);
+		ecmi->mark = mark;
+		break;
+	default:
+		DEBUG_ASSERT(false, "Unhandled result: %d\n", result);
+	}
+
+	/*
+	 * Acceleration is permitted
+	 */
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
+	ecmi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
+	ecmi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_ACCEL;
+	*process_response = ecmi->process_response;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+	ecm_db_connection_deref(ci);
+
+	return;
+
+not_relevant:
+	/*
+	 * ecm_classifier_mark_lock MUST be held
+	 */
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_NO;
+	*process_response = ecmi->process_response;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+	return;
+
+deny_accel:
+	/*
+	 * ecm_classifier_mark_lock MUST be held
+	 */
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_YES;
+	ecmi->process_response.process_actions = ECM_CLASSIFIER_PROCESS_ACTION_ACCEL_MODE;
+	ecmi->process_response.accel_mode = ECM_CLASSIFIER_ACCELERATION_MODE_NO;
+	*process_response = ecmi->process_response;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+	ecm_db_connection_deref(ci);
+}
+
+/*
+ * ecm_classifier_mark_type_get()
+ *	Get type of classifier this is
+ */
+static ecm_classifier_type_t ecm_classifier_mark_type_get(struct ecm_classifier_instance *aci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+	return ECM_CLASSIFIER_TYPE_MARK;
+}
+
+/*
+ * ecm_classifier_mark_reclassify_allowed()
+ *	Get whether reclassification is allowed
+ */
+static bool ecm_classifier_mark_reclassify_allowed(struct ecm_classifier_instance *aci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+	return true;
+}
+
+/*
+ * ecm_classifier_mark_reclassify()
+ *	Reclassify
+ */
+static void ecm_classifier_mark_reclassify(struct ecm_classifier_instance *aci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+
+	/*
+	 * Revert back to MAYBE relevant - we will evaluate when we get the next process() call.
+	 */
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+}
+
+/*
+ * ecm_classifier_mark_last_process_response_get()
+ *	Get result code returned by the last process call
+ */
+static void ecm_classifier_mark_last_process_response_get(struct ecm_classifier_instance *aci,
+							struct ecm_classifier_process_response *process_response)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	*process_response = ecmi->process_response;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+}
+
+/*
+ * ecm_classifier_mark_sync_to_v4()
+ *	Front end is pushing accel engine state to us
+ */
+static void ecm_classifier_mark_sync_to_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
+{
+	int protocol;
+	int src_port;
+	int dst_port;
+	ip_addr_t src_ip;
+	ip_addr_t dst_ip;
+	__be32 src_ip4;
+	__be32 dest_ip4;
+	struct ecm_db_connection_instance *ci;
+	ecm_classifier_mark_sync_to_ipv4_callback_t cb = NULL;
+	ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
+			/* TODO: By default we use this type. In the future the type will be gotten from the connection instance */
+	struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+
+	/*
+	 * Nothing to update.
+	 * We only care about flows that are actively being accelerated.
+	 */
+	if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) {
+		return;
+	}
+
+	/*
+	 * Handle only the stats sync. We don't care about the evict or flush syncs.
+	 */
+	if (sync->reason != ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS) {
+		return;
+	}
+
+	ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
+	if (!ci) {
+		DEBUG_TRACE("%p: No ci found for %u\n", ecmi, ecmi->ci_serial);
+		return;
+	}
+
+	protocol = ecm_db_connection_protocol_get(ci);
+	src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
+	dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO));
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, src_ip);
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+
+	ecm_db_connection_deref(ci);
+
+	ECM_IP_ADDR_TO_NIN4_ADDR(src_ip4, src_ip);
+	ECM_IP_ADDR_TO_NIN4_ADDR(dest_ip4, dst_ip);
+
+	rcu_read_lock();
+	cb = rcu_dereference(sync_to_ipv4_cb[type]);
+	if (!cb) {
+		rcu_read_unlock();
+		DEBUG_WARN("%p: IPv4 mark sync callback is not set\n", ecmi);
+		return;
+	}
+
+	cb(ecmi->mark, src_ip4, src_port, dest_ip4, dst_port, protocol);
+	rcu_read_unlock();
+}
+
+/*
+ * ecm_classifier_mark_sync_from_v4()
+ *	Front end is retrieving accel engine state from us
+ */
+static void ecm_classifier_mark_sync_from_v4(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
+{
+	struct ecm_classifier_mark_instance *ecmi __attribute__((unused));
+
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+}
+
+/*
+ * ecm_classifier_mark_sync_to_v6()
+ *	Front end is pushing accel engine state to us
+ */
+static void ecm_classifier_mark_sync_to_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_sync *sync)
+{
+	int protocol;
+	int src_port;
+	int dst_port;
+	ip_addr_t src_ip;
+	ip_addr_t dst_ip;
+	struct in6_addr src_ip6;
+	struct in6_addr dest_ip6;
+	struct ecm_db_connection_instance *ci;
+	ecm_classifier_mark_sync_to_ipv6_callback_t cb = NULL;
+	ecm_classifier_mark_type_t type = ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP;
+			/* TODO: By default we use this type. In the future the type will be gotten from the connection instance */
+	struct ecm_classifier_mark_instance *ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+
+	/*
+	 * Nothing to update.
+	 * We only care about flows that are actively being accelerated.
+	 */
+	if (!(sync->flow_tx_packet_count || sync->return_tx_packet_count)) {
+		return;
+	}
+
+	/*
+	 * Handle only the stats sync. We don't care about the evict or flush syncs.
+	 */
+	if (sync->reason != ECM_FRONT_END_IPV4_RULE_SYNC_REASON_STATS) {
+		return;
+	}
+
+	ci = ecm_db_connection_serial_find_and_ref(ecmi->ci_serial);
+	if (!ci) {
+		DEBUG_TRACE("%p: No ci found for %u\n", ecmi, ecmi->ci_serial);
+		return;
+	}
+
+	protocol = ecm_db_connection_protocol_get(ci);
+	src_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_FROM));
+	dst_port = htons(ecm_db_connection_port_get(ci, ECM_DB_OBJ_DIR_TO));
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_FROM, src_ip);
+	ecm_db_connection_address_get(ci, ECM_DB_OBJ_DIR_TO, dst_ip);
+
+	ecm_db_connection_deref(ci);
+
+	ECM_IP_ADDR_TO_NIN6_ADDR(src_ip6, src_ip);
+	ECM_IP_ADDR_TO_NIN6_ADDR(dest_ip6, dst_ip);
+
+	rcu_read_lock();
+	cb = rcu_dereference(sync_to_ipv6_cb[type]);
+	if (!cb) {
+		rcu_read_unlock();
+		DEBUG_WARN("%p: IPv6 mark sync callback is not set\n", ecmi);
+		return;
+	}
+	cb(ecmi->mark, &src_ip6, src_port, &dest_ip6, dst_port, protocol);
+	rcu_read_unlock();
+}
+
+/*
+ * ecm_classifier_mark_sync_from_v6()
+ *	Front end is retrieving accel engine state from us
+ */
+static void ecm_classifier_mark_sync_from_v6(struct ecm_classifier_instance *aci, struct ecm_classifier_rule_create *ecrc)
+{
+	struct ecm_classifier_mark_instance *ecmi __attribute__((unused));
+
+	ecmi = (struct ecm_classifier_mark_instance *)aci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+}
+
+#ifdef ECM_STATE_OUTPUT_ENABLE
+/*
+ * ecm_classifier_mark_state_get()
+ *	Gets the state of this classfier and outputs it to debugfs.
+ */
+static int ecm_classifier_mark_state_get(struct ecm_classifier_instance *ci, struct ecm_state_file_instance *sfi)
+{
+	int result;
+	struct ecm_classifier_mark_instance *ecmi;
+	struct ecm_classifier_process_response process_response;
+	uint32_t mark;
+
+	ecmi = (struct ecm_classifier_mark_instance *)ci;
+	DEBUG_CHECK_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC, "%p: magic failed", ecmi);
+
+	if ((result = ecm_state_prefix_add(sfi, "mark"))) {
+		return result;
+	}
+
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	mark = ecmi->mark;
+	process_response = ecmi->process_response;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+
+	if ((result = ecm_state_write(sfi, "value", "%d", mark))) {
+		return result;
+	}
+
+	/*
+	 * Output our last process response
+	 */
+	if ((result = ecm_classifier_process_response_state_get(sfi, &process_response))) {
+		return result;
+	}
+
+	return ecm_state_prefix_remove(sfi);
+}
+#endif
+
+/*
+ * ecm_classifier_mark_instance_alloc()
+ *	Allocate an instance of the mark classifier
+ */
+struct ecm_classifier_mark_instance *ecm_classifier_mark_instance_alloc(struct ecm_db_connection_instance *ci)
+{
+	struct ecm_classifier_mark_instance *ecmi;
+
+	/*
+	 * Allocate the instance
+	 */
+	ecmi = (struct ecm_classifier_mark_instance *)kzalloc(sizeof(struct ecm_classifier_mark_instance), GFP_ATOMIC | __GFP_NOWARN);
+	if (!ecmi) {
+		DEBUG_WARN("i%p: Failed to allocate Mark Classifier instance\n", ci);
+		return NULL;
+	}
+
+	DEBUG_SET_MAGIC(ecmi, ECM_CLASSIFIER_MARK_INSTANCE_MAGIC);
+	ecmi->refs = 1;
+
+	/*
+	 * Methods generic to all classifiers.
+	 */
+	ecmi->base.process = ecm_classifier_mark_process;
+	ecmi->base.sync_from_v4 = ecm_classifier_mark_sync_from_v4;
+	ecmi->base.sync_to_v4 = ecm_classifier_mark_sync_to_v4;
+	ecmi->base.sync_from_v6 = ecm_classifier_mark_sync_from_v6;
+	ecmi->base.sync_to_v6 = ecm_classifier_mark_sync_to_v6;
+	ecmi->base.type_get = ecm_classifier_mark_type_get;
+	ecmi->base.reclassify_allowed = ecm_classifier_mark_reclassify_allowed;
+	ecmi->base.reclassify = ecm_classifier_mark_reclassify;
+	ecmi->base.last_process_response_get = ecm_classifier_mark_last_process_response_get;
+#ifdef ECM_STATE_OUTPUT_ENABLE
+	ecmi->base.state_get = ecm_classifier_mark_state_get;
+#endif
+	ecmi->base.ref = ecm_classifier_mark_ref;
+	ecmi->base.deref = ecm_classifier_mark_deref;
+	ecmi->ci_serial = ecm_db_connection_serial_get(ci);
+
+	ecmi->process_response.process_actions = 0;
+	ecmi->process_response.relevance = ECM_CLASSIFIER_RELEVANCE_MAYBE;
+
+	/*
+	 * Final check if we are pending termination
+	 */
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	if (ecm_classifier_mark_terminate_pending) {
+		spin_unlock_bh(&ecm_classifier_mark_lock);
+		DEBUG_WARN("%p: Terminating\n", ci);
+		kfree(ecmi);
+		return NULL;
+	}
+
+	/*
+	 * Link the new instance into our list at the head
+	 */
+	ecmi->next = ecm_classifier_mark_instances;
+	if (ecm_classifier_mark_instances) {
+		ecm_classifier_mark_instances->prev = ecmi;
+	}
+	ecm_classifier_mark_instances = ecmi;
+
+	/*
+	 * Increment stats
+	 */
+	ecm_classifier_mark_count++;
+	DEBUG_ASSERT(ecm_classifier_mark_count > 0, "%p: ecm_classifier_mark_count wrap for instance: %p\n", ci, ecmi);
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+
+	DEBUG_INFO("%p: Mark classifier instance alloc: %p\n", ci, ecmi);
+	return ecmi;
+}
+EXPORT_SYMBOL(ecm_classifier_mark_instance_alloc);
+
+/*
+ * ecm_classifier_mark_register_callbacks()
+ *	Register external callbacks.
+ */
+int ecm_classifier_mark_register_callbacks(ecm_classifier_mark_type_t type,
+					   ecm_classifier_mark_get_callback_t mark_get,
+					   ecm_classifier_mark_sync_to_ipv4_callback_t sync_to_ipv4,
+					   ecm_classifier_mark_sync_to_ipv6_callback_t sync_to_ipv6)
+{
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	if (ecm_classifier_mark_terminate_pending) {
+		spin_unlock_bh(&ecm_classifier_mark_lock);
+		DEBUG_WARN("Terminating\n");
+		return -1;
+	}
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+
+	DEBUG_ASSERT(!mark_get_cb[type] && !sync_to_ipv4_cb[type] && !sync_to_ipv6_cb[type],
+			"Mark callbacks are already registered\n");
+	rcu_assign_pointer(mark_get_cb[type], mark_get);
+	rcu_assign_pointer(sync_to_ipv4_cb[type], sync_to_ipv4);
+	rcu_assign_pointer(sync_to_ipv6_cb[type], sync_to_ipv6);
+
+	return 0;
+}
+EXPORT_SYMBOL(ecm_classifier_mark_register_callbacks);
+
+/*
+ * ecm_classifier_mark_unregister_callbacks()
+ *	Unregister external callbacks.
+ */
+void ecm_classifier_mark_unregister_callbacks(ecm_classifier_mark_type_t type)
+{
+	rcu_assign_pointer(mark_get_cb[type], NULL);
+	rcu_assign_pointer(sync_to_ipv4_cb[type], NULL);
+	rcu_assign_pointer(sync_to_ipv6_cb[type], NULL);
+
+}
+EXPORT_SYMBOL(ecm_classifier_mark_unregister_callbacks);
+
+/*
+ * ecm_classifier_mark_init()
+ */
+int ecm_classifier_mark_init(struct dentry *dentry)
+{
+	DEBUG_INFO("Mark classifier Module init\n");
+
+	ecm_classifier_mark_dentry = debugfs_create_dir("ecm_classifier_mark", dentry);
+	if (!ecm_classifier_mark_dentry) {
+		DEBUG_ERROR("Failed to create ecm mark directory in debugfs\n");
+		return -1;
+	}
+
+	if (!debugfs_create_u32("enabled", S_IRUGO | S_IWUSR, ecm_classifier_mark_dentry,
+					(u32 *)&ecm_classifier_mark_enabled)) {
+		DEBUG_ERROR("Failed to create mark enabled file in debugfs\n");
+		debugfs_remove_recursive(ecm_classifier_mark_dentry);
+		return -1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ecm_classifier_mark_init);
+
+/*
+ * ecm_classifier_mark_exit()
+ */
+void ecm_classifier_mark_exit(void)
+{
+	DEBUG_INFO("Mark classifier Module exit\n");
+
+	spin_lock_bh(&ecm_classifier_mark_lock);
+	ecm_classifier_mark_terminate_pending = true;
+	spin_unlock_bh(&ecm_classifier_mark_lock);
+
+	/*
+	 * Remove the debugfs files recursively.
+	 */
+	if (ecm_classifier_mark_dentry) {
+		debugfs_remove_recursive(ecm_classifier_mark_dentry);
+	}
+
+}
+EXPORT_SYMBOL(ecm_classifier_mark_exit);
diff --git a/ecm_classifier_mark.h b/ecm_classifier_mark.h
new file mode 100644
index 0000000..39693b8
--- /dev/null
+++ b/ecm_classifier_mark.h
@@ -0,0 +1,18 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2018, The Linux Foundation.  All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+struct ecm_classifier_mark_instance;
+struct ecm_classifier_mark_instance *ecm_classifier_mark_instance_alloc(struct ecm_db_connection_instance *ci);
diff --git a/ecm_classifier_mark_public.h b/ecm_classifier_mark_public.h
new file mode 100644
index 0000000..d5351b4
--- /dev/null
+++ b/ecm_classifier_mark_public.h
@@ -0,0 +1,63 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2018, The Linux Foundation.  All rights reserved.
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ **************************************************************************
+ */
+
+/*
+ * Mark types are used to register specific external mdoule callbacks to ECM.
+ * Each type of callback can process the packets based on its need.
+ */
+enum ecm_classifier_mark_types {
+	ECM_CLASSIFIER_MARK_TYPE_L2_ENCAP,
+	ECM_CLASSIFIER_MARK_TYPE_MAX
+};
+typedef enum ecm_classifier_mark_types ecm_classifier_mark_type_t;
+
+/*
+ * Result values of the external inspection module to the ECM's mark classifier.
+ * Based on the result the mark classifier takes the action for the inspected connection.
+ */
+enum ecm_classifier_mark_results {
+	ECM_CLASSIFIER_MARK_RESULT_NOT_YET,		/* Classifier has not decided yet - try again later */
+	ECM_CLASSIFIER_MARK_RESULT_NOT_RELEVANT,	/* Classifier is not relevant to this connection */
+	ECM_CLASSIFIER_MARK_RESULT_SUCCESS,		/* Inspection is completed successfully */
+};
+typedef enum ecm_classifier_mark_results ecm_classifier_mark_result_t;
+
+/*
+ * Callback function which extracts a mark value from the skb and write it to the mark parameter.
+ */
+typedef ecm_classifier_mark_result_t (*ecm_classifier_mark_get_callback_t)(struct sk_buff *skb, uint32_t *mark);
+
+/*
+ * Callback functions which are called from the mark classifier's IPv4 and IPv6 sync_to
+ * functions to update the external modules.
+ */
+typedef void (*ecm_classifier_mark_sync_to_ipv4_callback_t)(uint32_t mark,
+							    __be32 src_ip, int src_port,
+							    __be32 dest_ip, int dest_port,
+							    int protocol);
+typedef void (*ecm_classifier_mark_sync_to_ipv6_callback_t)(uint32_t mark,
+							    struct in6_addr *src_ip, int src_port,
+							    struct in6_addr *dest_ip, int dest_port,
+							    int protocol);
+
+/*
+ * Register/Unregister callback functions which are called from the external modules.
+ */
+extern int ecm_classifier_mark_register_callbacks(ecm_classifier_mark_type_t type, ecm_classifier_mark_get_callback_t mark_get,
+						  ecm_classifier_mark_sync_to_ipv4_callback_t sync_to_ipv4,
+						  ecm_classifier_mark_sync_to_ipv6_callback_t sync_to_ipv6);
+extern void ecm_classifier_mark_unregister_callbacks(ecm_classifier_mark_type_t type);
+
diff --git a/ecm_db/ecm_db_connection.c b/ecm_db/ecm_db_connection.c
index a316e4d..697fb8f 100644
--- a/ecm_db/ecm_db_connection.c
+++ b/ecm_db/ecm_db_connection.c
@@ -180,6 +180,65 @@
 EXPORT_SYMBOL(ecm_db_connection_count_by_protocol_get);
 
 /*
+ * ecm_db_connection_l2_encap_proto_set()
+ *	Sets the L2 encap protocol.
+ */
+void ecm_db_connection_l2_encap_proto_set(struct ecm_db_connection_instance *ci, uint16_t l2_encap_proto)
+{
+	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
+
+	spin_lock_bh(&ecm_db_lock);
+	ci->l2_encap_proto = l2_encap_proto;
+	spin_unlock_bh(&ecm_db_lock);
+}
+
+/*
+ * ecm_db_connection_l2_encap_proto_get()
+ *	Gets the L2 encap protocol.
+ */
+uint16_t ecm_db_connection_l2_encap_proto_get(struct ecm_db_connection_instance *ci)
+{
+	uint16_t proto;
+	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
+
+	spin_lock_bh(&ecm_db_lock);
+	proto = ci->l2_encap_proto;
+	spin_unlock_bh(&ecm_db_lock);
+
+	return proto;
+}
+
+/*
+ * ecm_db_connection_mark_set()
+ *	Sets the mark value of the connection.
+ */
+void ecm_db_connection_mark_set(struct ecm_db_connection_instance *ci, uint32_t mark)
+{
+	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
+
+	spin_lock_bh(&ecm_db_lock);
+	ci->mark = mark;
+	spin_unlock_bh(&ecm_db_lock);
+
+}
+
+/*
+ * ecm_db_connection_mark_get()
+ *	Gets the mark value of the connection.
+ */
+uint32_t ecm_db_connection_mark_get(struct ecm_db_connection_instance *ci)
+{
+	uint16_t mark;
+	DEBUG_CHECK_MAGIC(ci, ECM_DB_CONNECTION_INSTANCE_MAGIC, "%p: magic failed", ci);
+
+	spin_lock_bh(&ecm_db_lock);
+	mark = ci->mark;
+	spin_unlock_bh(&ecm_db_lock);
+
+	return mark;
+}
+
+/*
  * ecm_db_connection_front_end_get_and_ref()
  *	Return ref to the front end instance of the connection
  */
diff --git a/ecm_db/ecm_db_connection.h b/ecm_db/ecm_db_connection.h
index a1cbd78..b8c014a 100644
--- a/ecm_db/ecm_db_connection.h
+++ b/ecm_db/ecm_db_connection.h
@@ -61,6 +61,8 @@
 	int protocol;						/* RO: Protocol of the connection */
 	ecm_db_direction_t direction;				/* RO: 'Direction' of connection establishment. */
 	bool is_routed;						/* RO: True when connection is routed, false when not */
+	uint16_t l2_encap_proto;				/* L2 encap protocol of the flow of this connection */
+	uint32_t mark;						/* The result value of mark classifier on this connection */
 
 	/*
 	 * Connection endpoint mapping
@@ -356,5 +358,10 @@
 void ecm_db_front_end_instance_ref_and_set(struct ecm_db_connection_instance *ci,
 					   struct ecm_front_end_connection_instance *feci);
 
+void ecm_db_connection_l2_encap_proto_set(struct ecm_db_connection_instance *ci, uint16_t l2_encap_proto);
+uint16_t ecm_db_connection_l2_encap_proto_get(struct ecm_db_connection_instance *ci);
+void ecm_db_connection_mark_set(struct ecm_db_connection_instance *ci, uint32_t mark);
+uint32_t ecm_db_connection_mark_get(struct ecm_db_connection_instance *ci);
+
 bool ecm_db_connection_init(struct dentry *dentry);
 void ecm_db_connection_exit(void);
diff --git a/ecm_init.c b/ecm_init.c
index 68ee556..c8ef5bd 100644
--- a/ecm_init.c
+++ b/ecm_init.c
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2014-2016, The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2014-2016, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -59,6 +59,11 @@
 extern int ecm_classifier_default_init(struct dentry *dentry);
 extern void ecm_classifier_default_exit(void);
 
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+extern int ecm_classifier_mark_init(struct dentry *dentry);
+extern void ecm_classifier_mark_exit(void);
+#endif
+
 #ifdef ECM_CLASSIFIER_NL_ENABLE
 extern int ecm_classifier_nl_rules_init(struct dentry *dentry);
 extern void ecm_classifier_nl_rules_exit(void);
@@ -139,6 +144,12 @@
 		goto err_cls_pcc;
 	}
 #endif
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+	ret = ecm_classifier_mark_init(ecm_dentry);
+	if (0 != ret) {
+		goto err_cls_mark;
+	}
+#endif
 
 	ret = ecm_interface_init();
 	if (0 != ret) {
@@ -196,6 +207,10 @@
 #endif
 	ecm_interface_exit();
 err_iface:
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+	ecm_classifier_mark_exit();
+err_cls_mark:
+#endif
 #ifdef ECM_CLASSIFIER_PCC_ENABLE
 	ecm_classifier_pcc_exit();
 err_cls_pcc:
@@ -281,6 +296,10 @@
 	DEBUG_INFO("exit nl classifier\n");
 	ecm_classifier_nl_rules_exit();
 #endif
+#ifdef ECM_CLASSIFIER_MARK_ENABLE
+	DEBUG_INFO("exit mark classifier\n");
+	ecm_classifier_mark_exit();
+#endif
 	DEBUG_INFO("exit default classifier\n");
 	ecm_classifier_default_exit();
 	DEBUG_INFO("exit db\n");
diff --git a/frontends/include/ecm_front_end_common.h b/frontends/include/ecm_front_end_common.h
index ca3a35a..cf2aa3e 100644
--- a/frontends/include/ecm_front_end_common.h
+++ b/frontends/include/ecm_front_end_common.h
@@ -50,10 +50,10 @@
  * ecm_front_end_l2_encap_header_len()
  *      Return length of encapsulating L2 header
  */
-static inline uint32_t ecm_front_end_l2_encap_header_len(struct sk_buff *skb)
+static inline uint32_t ecm_front_end_l2_encap_header_len(uint16_t protocol)
 {
-	switch (skb->protocol) {
-	case ntohs(ETH_P_PPP_SES):
+	switch (protocol) {
+	case ETH_P_PPP_SES:
 		return PPPOE_SES_HLEN;
 	default:
 		return 0;
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index 7077f58..a521b3a 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -941,7 +941,7 @@
  */
 static unsigned int ecm_nss_ipv4_ip_process(struct net_device *out_dev, struct net_device *in_dev,
 							uint8_t *src_node_addr, uint8_t *dest_node_addr,
-							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb)
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb, uint16_t l2_encap_proto)
 {
 	struct ecm_tracker_ip_header ip_hdr;
         struct nf_conn *ct;
@@ -1428,7 +1428,7 @@
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
-				ip_src_addr, ip_dest_addr, ip_src_addr_nat, ip_dest_addr_nat);
+				ip_src_addr, ip_dest_addr, ip_src_addr_nat, ip_dest_addr_nat, l2_encap_proto);
 	}
 #ifdef ECM_NON_PORTED_SUPPORT_ENABLE
 	return ecm_nss_non_ported_ipv4_process(out_dev, out_dev_nat,
@@ -1439,7 +1439,7 @@
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
-				ip_src_addr, ip_dest_addr, ip_src_addr_nat, ip_dest_addr_nat);
+				ip_src_addr, ip_dest_addr, ip_src_addr_nat, ip_dest_addr_nat, l2_encap_proto);
 #else
 	return NF_ACCEPT;
 #endif
@@ -1535,7 +1535,7 @@
 
 	DEBUG_TRACE("Post routing process skb %p, out: %p (%s), in: %p (%s)\n", skb, out, out->name, in, in->name);
 	result = ecm_nss_ipv4_ip_process((struct net_device *)out, in, NULL, NULL,
-							can_accel, true, false, skb);
+							can_accel, true, false, skb, 0);
 	dev_put(in);
 	return result;
 }
@@ -1561,13 +1561,13 @@
 		return NF_ACCEPT;
 	}
 
-	encap_header_len = ecm_front_end_l2_encap_header_len(skb);
+	encap_header_len = ecm_front_end_l2_encap_header_len(ntohs(skb->protocol));
 	ecm_front_end_pull_l2_encap_header(skb, encap_header_len);
 	skb->protocol = htons(ETH_P_IP);
 
 	result = ecm_nss_ipv4_ip_process(out, in, skb_eth_hdr->h_source,
 					 skb_eth_hdr->h_dest, can_accel,
-					 false, true, skb);
+					 false, true, skb, ETH_P_PPP_SES);
 
 	ecm_front_end_push_l2_encap_header(skb, encap_header_len);
 	skb->protocol = htons(ETH_P_PPP_SES);
@@ -1741,7 +1741,7 @@
 	}
 
 	result = ecm_nss_ipv4_ip_process((struct net_device *)out, in,
-				skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb);
+				skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb, 0);
 
 	dev_put(in);
 	dev_put(bridge);
diff --git a/frontends/nss/ecm_nss_ipv6.c b/frontends/nss/ecm_nss_ipv6.c
index 7a0b8b8..c6c631f 100644
--- a/frontends/nss/ecm_nss_ipv6.c
+++ b/frontends/nss/ecm_nss_ipv6.c
@@ -872,7 +872,7 @@
 static unsigned int ecm_nss_ipv6_ip_process(struct net_device *out_dev, struct net_device *in_dev,
 							uint8_t *src_node_addr, uint8_t *dest_node_addr,
 							bool can_accel, bool is_routed, bool is_l2_encap,
-							struct sk_buff *skb)
+							struct sk_buff *skb, uint16_t l2_encap_proto)
 {
 	struct ecm_tracker_ip_header ip_hdr;
 	struct nf_conn *ct;
@@ -1109,7 +1109,7 @@
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
-				ip_src_addr, ip_dest_addr);
+				ip_src_addr, ip_dest_addr, l2_encap_proto);
 	}
 #ifdef ECM_NON_PORTED_SUPPORT_ENABLE
 	return ecm_nss_non_ported_ipv6_process(out_dev, in_dev,
@@ -1119,7 +1119,7 @@
 			&ip_hdr,
 			ct, sender, ecm_dir,
 			&orig_tuple, &reply_tuple,
-			ip_src_addr, ip_dest_addr);
+			ip_src_addr, ip_dest_addr, l2_encap_proto);
 #else
 	return NF_ACCEPT;
 #endif
@@ -1213,7 +1213,7 @@
 	}
 
 	DEBUG_TRACE("Post routing process skb %p, out: %p, in: %p\n", skb, out, in);
-	result = ecm_nss_ipv6_ip_process((struct net_device *)out, in, NULL, NULL, can_accel, true, false, skb);
+	result = ecm_nss_ipv6_ip_process((struct net_device *)out, in, NULL, NULL, can_accel, true, false, skb, 0);
 	dev_put(in);
 	return result;
 }
@@ -1239,13 +1239,13 @@
 		return NF_ACCEPT;
 	}
 
-	encap_header_len = ecm_front_end_l2_encap_header_len(skb);
+	encap_header_len = ecm_front_end_l2_encap_header_len(ntohs(skb->protocol));
 	ecm_front_end_pull_l2_encap_header(skb, encap_header_len);
 	skb->protocol = htons(ETH_P_IPV6);
 
 	result = ecm_nss_ipv6_ip_process(out, in, skb_eth_hdr->h_source,
 					 skb_eth_hdr->h_dest, can_accel,
-					 false, true, skb);
+					 false, true, skb, ETH_P_PPP_SES);
 
 	 ecm_front_end_push_l2_encap_header(skb, encap_header_len);
 	skb->protocol = htons(ETH_P_PPP_SES);
@@ -1420,7 +1420,7 @@
 	}
 
 	result = ecm_nss_ipv6_ip_process((struct net_device *)out, in,
-							skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb);
+							skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb, 0);
 
 	dev_put(in);
 	dev_put(bridge);
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index 7eae2d0..8f6212a 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -1876,7 +1876,8 @@
 							struct ecm_tracker_ip_header *ip_hdr,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat)
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat,
+							uint16_t l2_encap_proto)
 {
 	struct ecm_db_connection_instance *ci;
 	int protocol;
@@ -2118,6 +2119,8 @@
 
 		ecm_db_front_end_instance_ref_and_set(nci, feci);
 
+		ecm_db_connection_l2_encap_proto_set(nci, l2_encap_proto);
+
 		/*
 		 * Now add the connection into the database.
 		 * NOTE: In an SMP situation such as ours there is a possibility that more than one packet for the same
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.h b/frontends/nss/ecm_nss_non_ported_ipv4.h
index 9ddb747..2b97d78 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.h
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015, The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2015, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -22,6 +22,7 @@
 							struct ecm_tracker_ip_header *ip_hdr,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat);
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat,
+							uint16_t l2_encap_proto);
 extern bool ecm_nss_non_ported_ipv4_debugfs_init(struct dentry *dentry);
 
diff --git a/frontends/nss/ecm_nss_non_ported_ipv6.c b/frontends/nss/ecm_nss_non_ported_ipv6.c
index 048f62a..7ab0114 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv6.c
@@ -1674,7 +1674,7 @@
 							struct ecm_tracker_ip_header *ip_hdr,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr)
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, uint16_t l2_encap_proto)
 {
 	struct ecm_db_connection_instance *ci;
 	int protocol;
@@ -1858,6 +1858,8 @@
 
 		ecm_db_front_end_instance_ref_and_set(nci, feci);
 
+		ecm_db_connection_l2_encap_proto_set(nci, l2_encap_proto);
+
 		/*
 		 * Now add the connection into the database.
 		 * NOTE: In an SMP situation such as ours there is a possibility that more than one packet for the same
diff --git a/frontends/nss/ecm_nss_non_ported_ipv6.h b/frontends/nss/ecm_nss_non_ported_ipv6.h
index 5f02c58..82667f7 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv6.h
+++ b/frontends/nss/ecm_nss_non_ported_ipv6.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015, The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2015, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -22,6 +22,6 @@
 							struct ecm_tracker_ip_header *ip_hdr,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr);
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, uint16_t l2_encap_proto);
 extern bool ecm_nss_non_ported_ipv6_debugfs_init(struct dentry *dentry);
 
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index 23dbefd..cb9b122 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -1864,7 +1864,7 @@
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
 							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr,
-							ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat)
+							ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat, uint16_t l2_encap_proto)
 {
 	struct tcphdr *tcp_hdr;
 	struct tcphdr tcp_hdr_buff;
@@ -2250,6 +2250,8 @@
 
 		ecm_db_front_end_instance_ref_and_set(nci, feci);
 
+		ecm_db_connection_l2_encap_proto_set(nci, l2_encap_proto);
+
 		/*
 		 * Now add the connection into the database.
 		 * NOTE: In an SMP situation such as ours there is a possibility that more than one packet for the same
diff --git a/frontends/nss/ecm_nss_ported_ipv4.h b/frontends/nss/ecm_nss_ported_ipv4.h
index 7b0577b..dfcf02a 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.h
+++ b/frontends/nss/ecm_nss_ported_ipv4.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015, The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2015, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -23,6 +23,6 @@
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
 							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, ip_addr_t ip_src_addr_nat,
-							ip_addr_t ip_dest_addr_nat);
+							ip_addr_t ip_dest_addr_nat, uint16_t l2_encap_proto);
 extern bool ecm_nss_ported_ipv4_debugfs_init(struct dentry *dentry);
 
diff --git a/frontends/nss/ecm_nss_ported_ipv6.c b/frontends/nss/ecm_nss_ported_ipv6.c
index 5486550..df8820e 100644
--- a/frontends/nss/ecm_nss_ported_ipv6.c
+++ b/frontends/nss/ecm_nss_ported_ipv6.c
@@ -1754,7 +1754,7 @@
 							struct ecm_tracker_ip_header *iph,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr)
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, uint16_t l2_encap_proto)
 {
 	struct tcphdr *tcp_hdr;
 	struct tcphdr tcp_hdr_buff;
@@ -2054,6 +2054,8 @@
 
 		ecm_db_front_end_instance_ref_and_set(nci, feci);
 
+		ecm_db_connection_l2_encap_proto_set(nci, l2_encap_proto);
+
 		/*
 		 * Now add the connection into the database.
 		 * NOTE: In an SMP situation such as ours there is a possibility that more than one packet for the same
diff --git a/frontends/nss/ecm_nss_ported_ipv6.h b/frontends/nss/ecm_nss_ported_ipv6.h
index 5e2a9cf..ad8a933 100644
--- a/frontends/nss/ecm_nss_ported_ipv6.h
+++ b/frontends/nss/ecm_nss_ported_ipv6.h
@@ -1,6 +1,6 @@
 /*
  **************************************************************************
- * Copyright (c) 2015, The Linux Foundation.  All rights reserved.
+ * Copyright (c) 2015, 2018, The Linux Foundation.  All rights reserved.
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
  * above copyright notice and this permission notice appear in all copies.
@@ -22,6 +22,6 @@
 							struct ecm_tracker_ip_header *iph,
 							struct nf_conn *ct, ecm_tracker_sender_type_t sender, ecm_db_direction_t ecm_dir,
 							struct nf_conntrack_tuple *orig_tuple, struct nf_conntrack_tuple *reply_tuple,
-							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr);
+							ip_addr_t ip_src_addr, ip_addr_t ip_dest_addr, uint16_t l2_encap_proto);
 extern bool ecm_nss_ported_ipv6_debugfs_init(struct dentry *dentry);