[qca-nss-ecm] NSS acceleration support for bridged PPPoE sessions.

- CRs-Fixed: 824100
- Accelerate bridged PPPoE session flows carrying IPv4/IPv6
  payloads. In such scenarios PPPoE client/server are
  running on machines connected to LAN ports.
- Fix ECM tracker issue where incorrect protocol id was being
  extracted from IPv6 header.

Change-Id: I428e930d29305bc4a228210758ec295ccf556ab2
Signed-off-by: Tushar Mathur <tushar@codeaurora.org>
diff --git a/ecm_tracker.c b/ecm_tracker.c
index 4bb8a6d..69d6e1f 100644
--- a/ecm_tracker.c
+++ b/ecm_tracker.c
@@ -411,6 +411,7 @@
 #ifdef ECM_IPV6_ENABLE
 	struct ipv6hdr *v6_hdr = NULL;
 	int16_t this_header;
+	int16_t prev_header;
 	uint32_t offset;
 #endif
 
@@ -553,6 +554,7 @@
 	 */
 	offset = 40;
 	this_header = (int16_t)v6_hdr->nexthdr;
+	prev_header = this_header;
 	while ((remain > 0) && (this_header >= 0) && (this_header != 59)) {
 		struct ecm_tracker_ip_protocols *etip;
 		struct ecm_tracker_ip_protocol_header *etiph;
@@ -589,13 +591,14 @@
 		DEBUG_ASSERT(remain >= etiph->size, "v6 remain: %u goes negative after header size: %u", remain, etiph->size);
 		remain -= etiph->size;
 
+		prev_header = this_header;
 		this_header = next_header;
 	}
 
 	/*
 	 * Generally the last protocol seen is the upper layer protocol
 	 */
-	ip_hdr->protocol = (int)this_header;
+	ip_hdr->protocol = (int)prev_header;
 
 	return true;
 #endif
diff --git a/frontends/include/ecm_front_end_common.h b/frontends/include/ecm_front_end_common.h
new file mode 100644
index 0000000..8ce3cef
--- /dev/null
+++ b/frontends/include/ecm_front_end_common.h
@@ -0,0 +1,54 @@
+/*
+ **************************************************************************
+ * Copyright (c) 2015 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.
+ **************************************************************************
+ */
+
+#ifndef _ECM_FRONT_END_COMMON_
+#define _ECM_FRONT_END_COMMON_
+
+/*
+ * 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)
+{
+	switch (skb->protocol) {
+	case ntohs(ETH_P_PPP_SES):
+		return PPPOE_SES_HLEN;
+	default:
+		return 0;
+	}
+}
+
+/*
+ * ecm_front_end_pull_l2_encap_header()
+ *      Pull encapsulating L2 header
+ */
+static void ecm_front_end_pull_l2_encap_header(struct sk_buff *skb, uint32_t len)
+{
+	skb->data += len;
+	skb->network_header += len;
+}
+
+/*
+ * ecm_front_end_push_l2_encap_header()
+ *      Push encapsulating L2 header
+ */
+static void ecm_front_end_push_l2_encap_header(struct sk_buff *skb, uint32_t len)
+{
+	skb->data -= len;
+	skb->network_header -= len;
+}
+
+#endif
diff --git a/frontends/nss/ecm_front_end_ipv6.c b/frontends/nss/ecm_front_end_ipv6.c
index ce70e49..0276896 100644
--- a/frontends/nss/ecm_front_end_ipv6.c
+++ b/frontends/nss/ecm_front_end_ipv6.c
@@ -39,6 +39,7 @@
 #include <linux/in6.h>
 #include <linux/udp.h>
 #include <linux/tcp.h>
+#include <linux/ppp_defs.h>
 
 #include <linux/inetdevice.h>
 #include <linux/if_arp.h>
@@ -92,6 +93,7 @@
 #include "ecm_classifier_dscp.h"
 #endif
 #include "ecm_interface.h"
+#include "ecm_front_end_common.h"
 
 /*
  * Magic numbers
@@ -1433,7 +1435,7 @@
  */
 static void ecm_front_end_ipv6_connection_tcp_front_end_accelerate(struct ecm_front_end_connection_instance *feci,
 									struct ecm_classifier_process_response *pr,
-									struct nf_conn *ct)
+									struct nf_conn *ct, bool is_l2_encap)
 {
 	struct ecm_front_end_ipv6_connection_tcp_instance *fecti = (struct ecm_front_end_ipv6_connection_tcp_instance *)feci;
 	int32_t from_ifaces_first;
@@ -1885,6 +1887,9 @@
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_ROUTED;
 	} else {
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_BRIDGE_FLOW;
+		if (is_l2_encap) {
+			nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_L2_ENCAP;
+		}
 	}
 
 	/*
@@ -2886,7 +2891,7 @@
  * can all use and reduce the amount of code!
  */
 static void ecm_front_end_ipv6_connection_udp_front_end_accelerate(struct ecm_front_end_connection_instance *feci,
-									struct ecm_classifier_process_response *pr)
+									struct ecm_classifier_process_response *pr, bool is_l2_encap)
 {
 	struct ecm_front_end_ipv6_connection_udp_instance *fecui = (struct ecm_front_end_ipv6_connection_udp_instance *)feci;
 	int32_t from_ifaces_first;
@@ -3339,6 +3344,9 @@
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_ROUTED;
 	} else {
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_BRIDGE_FLOW;
+		if (is_l2_encap) {
+			nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_L2_ENCAP;
+		}
 	}
 
 	/*
@@ -4300,7 +4308,7 @@
  * can all use and reduce the amount of code!
  */
 static void ecm_front_end_ipv6_connection_non_ported_front_end_accelerate(struct ecm_front_end_connection_instance *feci,
-									struct ecm_classifier_process_response *pr)
+									struct ecm_classifier_process_response *pr, bool is_l2_encap)
 {
 	struct ecm_front_end_ipv6_connection_non_ported_instance *fecnpi = (struct ecm_front_end_ipv6_connection_non_ported_instance *)feci;
 	int protocol;
@@ -4765,6 +4773,9 @@
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_ROUTED;
 	} else {
 		nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_BRIDGE_FLOW;
+		if (is_l2_encap) {
+			nircm->rule_flags |= NSS_IPV6_RULE_CREATE_FLAG_L2_ENCAP;
+		}
 	}
 
 	/*
@@ -5807,7 +5818,7 @@
 							struct net_device *in_dev,
 							uint8_t *src_node_addr,
 							uint8_t *dest_node_addr,
-							bool can_accel,  bool is_routed, struct sk_buff *skb,
+							bool can_accel,  bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
@@ -6315,7 +6326,7 @@
 		struct ecm_front_end_connection_instance *feci;
 		DEBUG_TRACE("%p: accel\n", ci);
 		feci = ecm_db_connection_front_end_get_and_ref(ci);
-		ecm_front_end_ipv6_connection_tcp_front_end_accelerate(feci, &prevalent_pr, ct);
+		ecm_front_end_ipv6_connection_tcp_front_end_accelerate(feci, &prevalent_pr, ct, is_l2_encap);
 		feci->deref(feci);
 	}
 	ecm_db_connection_deref(ci);
@@ -6331,7 +6342,7 @@
 							struct net_device *in_dev,
 							uint8_t *src_node_addr,
 							uint8_t *dest_node_addr,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
@@ -6842,7 +6853,7 @@
 		struct ecm_front_end_connection_instance *feci;
 		DEBUG_TRACE("%p: accel\n", ci);
 		feci = ecm_db_connection_front_end_get_and_ref(ci);
-		ecm_front_end_ipv6_connection_udp_front_end_accelerate(feci, &prevalent_pr);
+		ecm_front_end_ipv6_connection_udp_front_end_accelerate(feci, &prevalent_pr, is_l2_encap);
 		feci->deref(feci);
 	}
 	ecm_db_connection_deref(ci);
@@ -6858,7 +6869,7 @@
 							struct net_device *in_dev,
 							uint8_t *src_node_addr,
 							uint8_t *dest_node_addr,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
@@ -7325,7 +7336,7 @@
 		struct ecm_front_end_connection_instance *feci;
 		DEBUG_TRACE("%p: accel\n", ci);
 		feci = ecm_db_connection_front_end_get_and_ref(ci);
-		ecm_front_end_ipv6_connection_non_ported_front_end_accelerate(feci, &prevalent_pr);
+		ecm_front_end_ipv6_connection_non_ported_front_end_accelerate(feci, &prevalent_pr, is_l2_encap);
 		feci->deref(feci);
 	}
 	ecm_db_connection_deref(ci);
@@ -7339,7 +7350,8 @@
  */
 static unsigned int ecm_front_end_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, struct sk_buff *skb)
+							bool can_accel, bool is_routed, bool is_l2_encap,
+							struct sk_buff *skb)
 {
 	struct ecm_tracker_ip_header ip_hdr;
         struct nf_conn *ct;
@@ -7484,7 +7496,7 @@
 		return ecm_front_end_ipv6_tcp_process(out_dev, in_dev,
 				src_node_addr,
 				dest_node_addr,
-				can_accel, is_routed, skb,
+				can_accel, is_routed, is_l2_encap, skb,
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
@@ -7493,7 +7505,7 @@
 		return ecm_front_end_ipv6_udp_process(out_dev, in_dev,
 				src_node_addr,
 				dest_node_addr,
-				can_accel, is_routed, skb,
+				can_accel, is_routed, is_l2_encap, skb,
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
@@ -7502,7 +7514,7 @@
 	return ecm_front_end_ipv6_non_ported_process(out_dev, in_dev,
 				src_node_addr,
 				dest_node_addr,
-				can_accel, is_routed, skb,
+				can_accel, is_routed, is_l2_encap, skb,
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
@@ -7578,12 +7590,48 @@
 	}
 
 	DEBUG_TRACE("Post routing process skb %p, out: %p, in: %p\n", skb, out, in);
-	result = ecm_front_end_ipv6_ip_process((struct net_device *)out, in, NULL, NULL, can_accel, true, skb);
+	result = ecm_front_end_ipv6_ip_process((struct net_device *)out, in, NULL, NULL, can_accel, true, false, skb);
 	dev_put(in);
 	return result;
 }
 
 /*
+ * ecm_front_end_ipv6_pppoe_bridge_process()
+ *	Called for PPPoE session packets that are going
+ *	out to one of the bridge physical interfaces.
+ */
+static unsigned int ecm_front_end_ipv6_pppoe_bridge_process(struct net_device *out,
+						     struct net_device *in,
+						     struct ethhdr *skb_eth_hdr,
+						     bool can_accel,
+						     struct sk_buff *skb)
+{
+	unsigned int result = NF_ACCEPT;
+	struct pppoe_hdr *ph = pppoe_hdr(skb);
+	uint16_t ppp_proto = *(uint16_t *)ph->tag;
+	uint32_t encap_header_len = 0;
+
+	ppp_proto = ntohs(ppp_proto);
+	if (ppp_proto != PPP_IPV6) {
+		return NF_ACCEPT;
+	}
+
+	encap_header_len = ecm_front_end_l2_encap_header_len(skb);
+	ecm_front_end_pull_l2_encap_header(skb, encap_header_len);
+	skb->protocol = htons(ETH_P_IPV6);
+
+	result = ecm_front_end_ipv6_ip_process(out, in, skb_eth_hdr->h_source,
+					       skb_eth_hdr->h_dest, can_accel,
+					       false, true, skb);
+
+	 ecm_front_end_push_l2_encap_header(skb, encap_header_len);
+	skb->protocol = htons(ETH_P_PPP_SES);
+
+	return result;
+}
+
+
+/*
  * ecm_front_end_ipv6_bridge_post_routing_hook()
  *	Called for packets that are going out to one of the bridge physical interfaces.
  *
@@ -7655,8 +7703,8 @@
 		return NF_ACCEPT;
 	}
 	eth_type = ntohs(skb_eth_hdr->h_proto);
-	if (unlikely(eth_type != 0x86DD)) {
-		DEBUG_TRACE("%p: Not IP\n", skb);
+	if (unlikely((eth_type != 0x86DD) && (eth_type != ETH_P_PPP_SES))) {
+		DEBUG_TRACE("%p: Not IP/PPPoE session\n", skb);
 		return NF_ACCEPT;
 	}
 
@@ -7733,8 +7781,17 @@
 
 	DEBUG_TRACE("Bridge process skb: %p, bridge: %p (%s), In: %p (%s), Out: %p (%s)\n",
 			skb, bridge, bridge->name, in, in->name, out, out->name);
+
+	if (unlikely(eth_type != 0x86DD)) {
+		result = ecm_front_end_ipv6_pppoe_bridge_process((struct net_device *)out, in, skb_eth_hdr, can_accel, skb);
+		dev_put(in);
+		dev_put(bridge);
+		return result;
+	}
+
 	result = ecm_front_end_ipv6_ip_process((struct net_device *)out, in,
-							skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, skb);
+							skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb);
+
 	dev_put(in);
 	dev_put(bridge);
 	return result;
diff --git a/frontends/nss/ecm_nss_ipv4.c b/frontends/nss/ecm_nss_ipv4.c
index c115c8b..9f7ba76 100644
--- a/frontends/nss/ecm_nss_ipv4.c
+++ b/frontends/nss/ecm_nss_ipv4.c
@@ -37,6 +37,7 @@
 #include <linux/in.h>
 #include <linux/udp.h>
 #include <linux/tcp.h>
+#include <linux/ppp_defs.h>
 
 #include <linux/inetdevice.h>
 #include <linux/if_arp.h>
@@ -96,6 +97,8 @@
 #include "ecm_nss_non_ported_ipv4.h"
 #endif
 
+#include "ecm_front_end_common.h"
+
 int ecm_nss_ipv4_no_action_limit_default = 250;		/* Default no-action limit. */
 int ecm_nss_ipv4_driver_fail_limit_default = 250;		/* Default driver fail limit. */
 int ecm_nss_ipv4_nack_limit_default = 250;			/* Default nack limit. */
@@ -807,7 +810,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, struct sk_buff *skb)
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb)
 {
 	struct ecm_tracker_ip_header ip_hdr;
         struct nf_conn *ct;
@@ -1187,7 +1190,7 @@
 				in_dev, in_dev_nat,
 				src_node_addr, src_node_addr_nat,
 				dest_node_addr, dest_node_addr_nat,
-				can_accel, is_routed, skb,
+				can_accel, is_routed, is_l2_encap, skb,
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
@@ -1198,7 +1201,7 @@
 				in_dev, in_dev_nat,
 				src_node_addr, src_node_addr_nat,
 				dest_node_addr, dest_node_addr_nat,
-				can_accel, is_routed, skb,
+				can_accel, is_routed, is_l2_encap, skb,
 				&ip_hdr,
 				ct, sender, ecm_dir,
 				&orig_tuple, &reply_tuple,
@@ -1278,12 +1281,47 @@
 
 	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, skb);
+							can_accel, true, false, skb);
 	dev_put(in);
 	return result;
 }
 
 /*
+ * ecm_front_end_ipv4_pppoe_bridge_process()
+ *	Called for PPPoE session packets that are going
+ *	out to one of the bridge physical interfaces.
+ */
+static unsigned int ecm_front_end_ipv4_pppoe_bridge_process(struct net_device *out,
+						     struct net_device *in,
+						     struct ethhdr *skb_eth_hdr,
+						     bool can_accel,
+						     struct sk_buff *skb)
+{
+	unsigned int result = NF_ACCEPT;
+	struct pppoe_hdr *ph = pppoe_hdr(skb);
+	uint16_t ppp_proto = *(uint16_t *)ph->tag;
+	uint32_t encap_header_len = 0;
+
+	ppp_proto = ntohs(ppp_proto);
+	if (ppp_proto != PPP_IP) {
+		return NF_ACCEPT;
+	}
+
+	encap_header_len = ecm_front_end_l2_encap_header_len(skb);
+	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);
+
+	ecm_front_end_push_l2_encap_header(skb, encap_header_len);
+	skb->protocol = htons(ETH_P_PPP_SES);
+
+	return result;
+}
+
+/*
  * ecm_nss_ipv4_bridge_post_routing_hook()
  *	Called for packets that are going out to one of the bridge physical interfaces.
  *
@@ -1355,8 +1393,8 @@
 		return NF_ACCEPT;
 	}
 	eth_type = ntohs(skb_eth_hdr->h_proto);
-	if (unlikely(eth_type != 0x0800)) {
-		DEBUG_TRACE("%p: Not IP\n", skb);
+	if (unlikely((eth_type != 0x0800) && (eth_type != ETH_P_PPP_SES))) {
+		DEBUG_TRACE("%p: Not IP/PPPoE session\n", skb);
 		return NF_ACCEPT;
 	}
 
@@ -1433,8 +1471,18 @@
 
 	DEBUG_TRACE("Bridge process skb: %p, bridge: %p (%s), In: %p (%s), Out: %p (%s)\n",
 			skb, bridge, bridge->name, in, in->name, out, out->name);
+
+	if (unlikely(eth_type != 0x0800)) {
+		result = ecm_front_end_ipv4_pppoe_bridge_process((struct net_device *)out, in, skb_eth_hdr, can_accel, skb);
+		dev_put(in);
+		dev_put(bridge);
+		return result;
+	}
+
 	result = ecm_nss_ipv4_ip_process((struct net_device *)out, in,
-				skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, skb);
+				skb_eth_hdr->h_source, skb_eth_hdr->h_dest, can_accel, false, false, skb);
+
+
 	dev_put(in);
 	dev_put(bridge);
 	return result;
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.c b/frontends/nss/ecm_nss_non_ported_ipv4.c
index 98cb855..406a185 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.c
@@ -379,7 +379,7 @@
  * GGG TODO Refactor this function into a single function that np, udp and tcp
  * can all use and reduce the amount of code!
  */
-static void ecm_nss_non_ported_ipv4_connection_accelerate(struct ecm_front_end_connection_instance *feci,
+static void ecm_nss_non_ported_ipv4_connection_accelerate(struct ecm_front_end_connection_instance *feci, bool is_l2_encap,
 									struct ecm_classifier_process_response *pr)
 {
 	struct ecm_nss_non_ported_ipv4_connection_instance *nnpci = (struct ecm_nss_non_ported_ipv4_connection_instance *)feci;
@@ -844,6 +844,9 @@
 		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ROUTED;
 	} else {
 		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_BRIDGE_FLOW;
+		if (is_l2_encap) {
+			nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_L2_ENCAP;
+		}
 	}
 
 	/*
@@ -1659,7 +1662,7 @@
 							struct net_device *in_dev, struct net_device *in_dev_nat,
 							uint8_t *src_node_addr, uint8_t *src_node_addr_nat,
 							uint8_t *dest_node_addr, uint8_t *dest_node_addr_nat,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
@@ -2272,7 +2275,7 @@
 		struct ecm_front_end_connection_instance *feci;
 		DEBUG_TRACE("%p: accel\n", ci);
 		feci = ecm_db_connection_front_end_get_and_ref(ci);
-		ecm_nss_non_ported_ipv4_connection_accelerate(feci, &prevalent_pr);
+		ecm_nss_non_ported_ipv4_connection_accelerate(feci, is_l2_encap, &prevalent_pr);
 		feci->deref(feci);
 	}
 	ecm_db_connection_deref(ci);
diff --git a/frontends/nss/ecm_nss_non_ported_ipv4.h b/frontends/nss/ecm_nss_non_ported_ipv4.h
index 96fbba6..5c538be 100644
--- a/frontends/nss/ecm_nss_non_ported_ipv4.h
+++ b/frontends/nss/ecm_nss_non_ported_ipv4.h
@@ -18,7 +18,7 @@
 							struct net_device *in_dev, struct net_device *in_dev_nat,
 							uint8_t *src_node_addr, uint8_t *src_node_addr_nat,
 							uint8_t *dest_node_addr, uint8_t *dest_node_addr_nat,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
diff --git a/frontends/nss/ecm_nss_ported_ipv4.c b/frontends/nss/ecm_nss_ported_ipv4.c
index dd605e0..170105f 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.c
+++ b/frontends/nss/ecm_nss_ported_ipv4.c
@@ -323,7 +323,7 @@
  * can all use and reduce the amount of code!
  */
 static void ecm_nss_ported_ipv4_connection_accelerate(struct ecm_front_end_connection_instance *feci,
-									struct ecm_classifier_process_response *pr,
+									struct ecm_classifier_process_response *pr, bool is_l2_encap,
 									struct nf_conn *ct)
 {
 	struct ecm_nss_ported_ipv4_connection_instance *npci = (struct ecm_nss_ported_ipv4_connection_instance *)feci;
@@ -776,6 +776,9 @@
 		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_ROUTED;
 	} else {
 		nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_BRIDGE_FLOW;
+		if (is_l2_encap) {
+			nircm->rule_flags |= NSS_IPV4_RULE_CREATE_FLAG_L2_ENCAP;
+		}
 	}
 
 	/*
@@ -1031,7 +1034,7 @@
 			nircm->tcp_rule.return_end,
 			nircm->tcp_rule.return_max_end);
 	}
-		
+
 	/*
 	 * Ref the connection before issuing an NSS rule
 	 * This ensures that when the NSS responds to the command - which may even be immediately -
@@ -1651,7 +1654,7 @@
 		DEBUG_CLEAR_MAGIC(npci);
 		kfree(npci);
 		return NULL;
-	}		
+	}
 
 	return npci;
 }
@@ -1664,11 +1667,11 @@
 							struct net_device *in_dev, struct net_device *in_dev_nat,
 							uint8_t *src_node_addr, uint8_t *src_node_addr_nat,
 							uint8_t *dest_node_addr, uint8_t *dest_node_addr_nat,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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,
 							ip_addr_t ip_src_addr_nat, ip_addr_t ip_dest_addr_nat)
 {
 	struct tcphdr *tcp_hdr;
@@ -2381,7 +2384,7 @@
 		struct ecm_front_end_connection_instance *feci;
 		DEBUG_TRACE("%p: accel\n", ci);
 		feci = ecm_db_connection_front_end_get_and_ref(ci);
-		ecm_nss_ported_ipv4_connection_accelerate(feci, &prevalent_pr, ct);
+		ecm_nss_ported_ipv4_connection_accelerate(feci, &prevalent_pr, is_l2_encap, ct);
 		feci->deref(feci);
 	}
 	ecm_db_connection_deref(ci);
diff --git a/frontends/nss/ecm_nss_ported_ipv4.h b/frontends/nss/ecm_nss_ported_ipv4.h
index c14bca3..c0bfc39 100644
--- a/frontends/nss/ecm_nss_ported_ipv4.h
+++ b/frontends/nss/ecm_nss_ported_ipv4.h
@@ -18,11 +18,11 @@
 							struct net_device *in_dev, struct net_device *in_dev_nat,
 							uint8_t *src_node_addr, uint8_t *src_node_addr_nat,
 							uint8_t *dest_node_addr, uint8_t *dest_node_addr_nat,
-							bool can_accel, bool is_routed, struct sk_buff *skb,
+							bool can_accel, bool is_routed, bool is_l2_encap, struct sk_buff *skb,
 							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_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);
 
 extern ssize_t ecm_nss_ported_ipv4_get_tcp_accelerated_count(struct device *dev,