[qca-nss-sfe] update PPPoE and VLAN to use kernel API

Change-Id: I6bd406f31b20cd9ebf73b98c42cb34bc819aa2c2
Signed-off-by: Nitin Shetty <quic_nitinsj@quicinc.com>
diff --git a/sfe.c b/sfe.c
index 1f451f7..182288f 100644
--- a/sfe.c
+++ b/sfe.c
@@ -562,12 +562,6 @@
 static bool sfe_recv_parse_l2(struct net_device *dev, struct sk_buff *skb, struct sfe_l2_info *l2_info)
 {
 	/*
-	 * l2_hdr_offset will not change as we parse more L2.5 headers
-	 * TODO: Move from storing offsets to storing pointers
-	 */
-	sfe_l2_hdr_offset_set(l2_info, ((skb->data - ETH_HLEN) - skb->head));
-
-	/*
 	 * VLAN parsing
 	 */
 	if (unlikely(!sfe_vlan_check_and_parse_tag(skb, l2_info))) {
@@ -575,13 +569,9 @@
 	}
 
 	/*
-	 * PPPoE parsing
+	 * Parse only PPPoE session packets
 	 */
 	if (htons(ETH_P_PPP_SES) == skb->protocol) {
-		/*
-		 * Parse only PPPoE session packets
-		 * skb->data is pointing to PPPoE hdr
-		 */
 		if (!sfe_pppoe_parse_hdr(skb, l2_info)) {
 
 			/*
@@ -590,11 +580,6 @@
 			 */
 			return false;
 		}
-
-		/*
-		 * Pull by L2 header size
-		 */
-		__skb_pull(skb, sfe_l2_hdr_size_get(l2_info));
 	}
 	return true;
 }
@@ -607,12 +592,17 @@
 	/*
 	 * PPPoE undo
 	 */
-	__skb_push(skb, sfe_l2_hdr_size_get(l2_info));
+	sfe_pppoe_undo_parse(skb, l2_info);
 
 	/*
 	 * VLAN undo
 	 */
 	sfe_vlan_undo_parse(skb, l2_info);
+
+	/*
+	 * packet is not handled by SFE, so reset the network header
+	 */
+	skb_reset_network_header(skb);
 }
 
 /*
@@ -1278,7 +1268,6 @@
 	 * Setting parse flags to 0 since l2_info is passed for non L2.5 header case as well
 	 */
 	l2_info.parse_flags = 0;
-	l2_info.l2_hdr_size = 0;
 	l2_info.vlan_hdr_cnt = 0;
 
 #ifdef CONFIG_NET_CLS_ACT
diff --git a/sfe.h b/sfe.h
index c117729..6cadbfe 100644
--- a/sfe.h
+++ b/sfe.h
@@ -65,9 +65,7 @@
  */
 struct sfe_l2_info {
 	u16 parse_flags;	/* Flags indicating L2.5 headers presence */
-	u16 l2_hdr_offset;	/* Offset of L2 header */
-	u16 l2_hdr_size;	/* L2 header size */
-	u16 pppoe_hdr_offset;	/* PPPOE header offset */
+	u16 pppoe_session_id;	/* PPPOE header offset */
 	u16 protocol;		/* L3 Protocol */
 	struct sfe_vlan_hdr vlan_hdr[SFE_MAX_VLAN_DEPTH];
 				/* VLAN tag(s) of ingress packet */
@@ -265,57 +263,21 @@
 }
 
 /*
- * sfe_l2_hdr_offset_get()
- *	Get L2 header offset
+ * sfe_l2_pppoe_session_id_get()
+ *	Get PPPPoE session ID from l2_info
  */
-static inline u16 sfe_l2_hdr_offset_get(struct sfe_l2_info *l2_info)
+static inline u16 sfe_l2_pppoe_session_id_get(struct sfe_l2_info *l2_info)
 {
-	return l2_info->l2_hdr_offset;
+	return l2_info->pppoe_session_id;
 }
 
 /*
- * sfe_l2_hdr_offset_set()
- *	Set L2 header offset
+ * sfe_l2_pppoe_session_id_set()
+ *	Set PPPoE session ID to l2_info
  */
-static inline void sfe_l2_hdr_offset_set(struct sfe_l2_info *l2_info, u16 offset)
+static inline void sfe_l2_pppoe_session_id_set(struct sfe_l2_info *l2_info, u16 session_id)
 {
-	l2_info->l2_hdr_offset = offset;
-}
-
-/*
- * sfe_l2_pppoe_hdr_offset_get()
- *	Get L2 PPPoE header offset
- */
-static inline u16 sfe_l2_pppoe_hdr_offset_get(struct sfe_l2_info *l2_info)
-{
-	return l2_info->pppoe_hdr_offset;
-}
-
-/*
- * sfe_l2_pppoe_hdr_offset_set()
- *	Set L2 PPPoE header offset
- */
-static inline void sfe_l2_pppoe_hdr_offset_set(struct sfe_l2_info *l2_info, u16 offset)
-{
-	l2_info->pppoe_hdr_offset = offset;
-}
-
-/*
- * sfe_l2_hdr_size_get()
- *	Get L2 header size
- */
-static inline u16 sfe_l2_hdr_size_get(struct sfe_l2_info *l2_info)
-{
-	return l2_info->l2_hdr_size;
-}
-
-/*
- * sfe_l2_hdr_size_set()
- *	Set L2 header size
- */
-static inline void sfe_l2_hdr_size_set(struct sfe_l2_info *l2_info, u16 size)
-{
-	l2_info->l2_hdr_size = size;
+	l2_info->pppoe_session_id = session_id;
 }
 
 /*
diff --git a/sfe_ipv4_tcp.c b/sfe_ipv4_tcp.c
index e957259..14283ea 100644
--- a/sfe_ipv4_tcp.c
+++ b/sfe_ipv4_tcp.c
@@ -499,8 +499,8 @@
 	 * For PPPoE packets, match server MAC and session id
 	 */
 	if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
-		struct pppoe_hdr *ph;
 		struct ethhdr *eth;
+		bool pppoe_match;
 
 		if (unlikely(!sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 			rcu_read_unlock();
@@ -509,22 +509,27 @@
 			return 0;
 		}
 
-		ph = (struct pppoe_hdr *)(skb->head + sfe_l2_pppoe_hdr_offset_get(l2_info));
-		eth = (struct ethhdr *)(skb->head + sfe_l2_hdr_offset_get(l2_info));
-		if (unlikely(cm->pppoe_session_id != ntohs(ph->sid)) || unlikely(!(ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source)))) {
-			DEBUG_TRACE("%px: PPPoE sessions with session IDs %d and %d or server MACs %pM and %pM did not match\n",
-							skb, cm->pppoe_session_id, htons(ph->sid), cm->pppoe_remote_mac, eth->h_source);
+		eth = eth_hdr(skb);
+
+		pppoe_match = (cm->pppoe_session_id == sfe_l2_pppoe_session_id_get(l2_info)) &&
+				ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source);
+
+		if (unlikely(!pppoe_match)) {
+			DEBUG_TRACE("%px: PPPoE session ID %d and %d or MAC %pM and %pM did not match\n",
+					skb, cm->pppoe_session_id, sfe_l2_pppoe_session_id_get(l2_info),
+					cm->pppoe_remote_mac, eth->h_source);
 			rcu_read_unlock();
 			sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_PPPOE_SESSION);
 			return 0;
 		}
+
 		skb->protocol = htons(l2_info->protocol);
 		this_cpu_inc(si->stats_pcpu->pppoe_decap_packets_forwarded64);
-
 	} else if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 
 		/*
-		 * If packet contains PPPoE header but CME doesn't contain PPPoE flag yet we are exceptioning the packet to linux
+		 * If packet contains PPPoE header but CME doesn't contain PPPoE flag yet we are exceptioning
+		 * the packet to linux
 		 */
 		if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_BRIDGE_FLOW))) {
 			rcu_read_unlock();
@@ -534,10 +539,10 @@
 		}
 
 		/*
-		 * For bridged flows when packet contains PPPoE header, restore the header back and forward to xmit interface
+		 * For bridged flows when packet contains PPPoE header, restore the header back and forward
+		 * to xmit interface
 		 */
 		__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
-		l2_info->l2_hdr_size -= (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr));
 		this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
 	}
 
diff --git a/sfe_ipv4_udp.c b/sfe_ipv4_udp.c
index 1bef924..61ed28e 100644
--- a/sfe_ipv4_udp.c
+++ b/sfe_ipv4_udp.c
@@ -293,8 +293,8 @@
 	 * For PPPoE packets, match server MAC and session id
 	 */
 	if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
-		struct pppoe_hdr *ph;
 		struct ethhdr *eth;
+		bool pppoe_match;
 
 		if (unlikely(!sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 			rcu_read_unlock();
@@ -303,22 +303,27 @@
 			return 0;
 		}
 
-		ph = (struct pppoe_hdr *)(skb->head + sfe_l2_pppoe_hdr_offset_get(l2_info));
-		eth = (struct ethhdr *)(skb->head + sfe_l2_hdr_offset_get(l2_info));
-		if (unlikely(cm->pppoe_session_id != ntohs(ph->sid)) || unlikely(!(ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source)))) {
-			DEBUG_TRACE("%px: PPPoE sessions with session IDs %d and %d or server MACs %pM and %pM did not match\n",
-							skb, cm->pppoe_session_id, htons(ph->sid), cm->pppoe_remote_mac, eth->h_source);
+		eth = eth_hdr(skb);
+
+		pppoe_match = (cm->pppoe_session_id == sfe_l2_pppoe_session_id_get(l2_info)) &&
+				ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source);
+
+		if (unlikely(!pppoe_match)) {
+			DEBUG_TRACE("%px: PPPoE session ID %d and %d or MAC %pM and %pM did not match\n",
+					skb, cm->pppoe_session_id, sfe_l2_pppoe_session_id_get(l2_info),
+					cm->pppoe_remote_mac, eth->h_source);
 			rcu_read_unlock();
 			sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_PPPOE_SESSION);
 			return 0;
 		}
+
 		skb->protocol = htons(l2_info->protocol);
 		this_cpu_inc(si->stats_pcpu->pppoe_decap_packets_forwarded64);
-
 	} else if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 
 		/*
-		 * If packet contains PPPoE header but CME doesn't contain PPPoE flag yet we are exceptioning the packet to linux
+		 * If packet contains PPPoE header but CME doesn't contain PPPoE flag yet we are exceptioning
+		 * the packet to linux
 		 */
 		if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_BRIDGE_FLOW))) {
 			rcu_read_unlock();
@@ -329,10 +334,10 @@
 		}
 
 		/*
-		 * For bridged flows when packet contains PPPoE header, restore the header back and forward to xmit interface
+		 * For bridged flows when packet contains PPPoE header, restore the header back and forward
+		 * to xmit interface
 		 */
 		__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
-		l2_info->l2_hdr_size -= (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr));
 		this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
 	}
 
diff --git a/sfe_ipv6_tcp.c b/sfe_ipv6_tcp.c
index 28cc541..1f377a2 100644
--- a/sfe_ipv6_tcp.c
+++ b/sfe_ipv6_tcp.c
@@ -508,29 +508,32 @@
 	 * For PPPoE packets, match server MAC and session id
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
-		struct pppoe_hdr *ph;
 		struct ethhdr *eth;
+		bool pppoe_match;
 
 		if (unlikely(!sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 			rcu_read_unlock();
 			DEBUG_TRACE("%px: PPPoE header not present in packet for PPPoE rule\n", skb);
-			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_PPPOE_SESSION);
+			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING);
 			return 0;
 		}
 
-		ph = (struct pppoe_hdr *)(skb->head + sfe_l2_pppoe_hdr_offset_get(l2_info));
-		eth = (struct ethhdr *)(skb->head + sfe_l2_hdr_offset_get(l2_info));
+		eth = eth_hdr(skb);
 
-		if (unlikely(cm->pppoe_session_id != ntohs(ph->sid)) || unlikely(!(ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source)))) {
-			DEBUG_TRACE("%px: PPPoE sessions with session IDs %d and %d or server MACs %pM and %pM did not match \n",
-							skb, cm->pppoe_session_id, htons(ph->sid), cm->pppoe_remote_mac, eth->h_source);
+		pppoe_match = (cm->pppoe_session_id == sfe_l2_pppoe_session_id_get(l2_info)) &&
+				ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source);
+
+		if (unlikely(!pppoe_match)) {
+			DEBUG_TRACE("%px: PPPoE sessions ID %d and %d or MAC %pM and %pM did not match\n",
+					skb, cm->pppoe_session_id, sfe_l2_pppoe_session_id_get(l2_info),
+					cm->pppoe_remote_mac, eth->h_source);
 			rcu_read_unlock();
 			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_PPPOE_SESSION);
 			return 0;
 		}
+
 		skb->protocol = htons(l2_info->protocol);
 		this_cpu_inc(si->stats_pcpu->pppoe_decap_packets_forwarded64);
-
 	} else if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 
 		/*
@@ -548,7 +551,6 @@
 		 * For bridged flows when packet contains PPPoE header, restore the header back and forward to xmit interface
 		 */
 		__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
-		l2_info->l2_hdr_size -= (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr));
 		this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
 	}
 
diff --git a/sfe_ipv6_udp.c b/sfe_ipv6_udp.c
index 5ffefdd..3df6d51 100644
--- a/sfe_ipv6_udp.c
+++ b/sfe_ipv6_udp.c
@@ -300,8 +300,8 @@
 	 * For PPPoE packets, match server MAC and session id
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
-		struct pppoe_hdr *ph;
 		struct ethhdr *eth;
+		bool pppoe_match;
 
 		if (unlikely(!sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 			rcu_read_unlock();
@@ -310,38 +310,40 @@
 			return 0;
 		}
 
-		ph = (struct pppoe_hdr *)(skb->head + sfe_l2_pppoe_hdr_offset_get(l2_info));
-		eth = (struct ethhdr *)(skb->head + sfe_l2_hdr_offset_get(l2_info));
-		if (unlikely(cm->pppoe_session_id != ntohs(ph->sid)) || unlikely(!(ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source)))) {
-			DEBUG_TRACE("%px: PPPoE sessions with session IDs %d and %d or server MACs %pM and %pM did not match\n",
-							skb, cm->pppoe_session_id, htons(ph->sid), cm->pppoe_remote_mac, eth->h_source);
+		eth = eth_hdr(skb);
+
+		pppoe_match = (cm->pppoe_session_id == sfe_l2_pppoe_session_id_get(l2_info)) &&
+				ether_addr_equal((u8*)cm->pppoe_remote_mac, (u8 *)eth->h_source);
+
+		if (unlikely(!pppoe_match)) {
+			DEBUG_TRACE("%px: PPPoE sessions ID %d and %d or MAC %pM and %pM did not match\n",
+					skb, cm->pppoe_session_id, sfe_l2_pppoe_session_id_get(l2_info),
+					cm->pppoe_remote_mac, eth->h_source);
 			rcu_read_unlock();
 			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_PPPOE_SESSION);
 			return 0;
 		}
+
 		skb->protocol = htons(l2_info->protocol);
 		this_cpu_inc(si->stats_pcpu->pppoe_decap_packets_forwarded64);
-
 	} else if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS))) {
 
 		/*
 		 * If packet contains PPPoE header but CME doesn't contain PPPoE flag yet we are exceptioning the packet to linux
 		 */
-
 		if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_BRIDGE_FLOW))) {
 			rcu_read_unlock();
 			DEBUG_TRACE("%px: CME doesn't contain PPPoE flag but packet has PPPoE header\n", skb);
-			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING);
+			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME);
 			return 0;
+
 		}
 
 		/*
 		 * For bridged flows when packet contains PPPoE header, restore the header back and forward to xmit interface
 		 */
 		__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
-		l2_info->l2_hdr_size -= (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr));
 		this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
-
 	}
 
 	/*
diff --git a/sfe_pppoe.c b/sfe_pppoe.c
index 4b20311..770a77d 100644
--- a/sfe_pppoe.c
+++ b/sfe_pppoe.c
@@ -20,6 +20,7 @@
 #include <linux/skbuff.h>
 #include <linux/if_pppox.h>
 #include <linux/ppp_defs.h>
+#include <asm/unaligned.h>
 
 #include "sfe_debug.h"
 #include "sfe_api.h"
@@ -34,17 +35,20 @@
  */
 void sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol)
 {
-	u16 *l2_header;
 	struct pppoe_hdr *ph;
-	struct sfe_ppp_hdr *ppp;
-	u16 *l3_header = (u16 *)skb->data;
+	unsigned char *pp;
+	unsigned int data_len;
 
 	/*
-	 * PPPoE header (6 bytes) + PPP header (2 bytes)
-	 *
-	 * Hence move by 8 bytes to accomodate PPPoE header
+	 * Insert the PPP header protocol
 	 */
-	l2_header = l3_header - ((sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)) / 2);
+	pp = __skb_push(skb, 2);
+	put_unaligned_be16(ppp_protocol, pp);
+
+	data_len = skb->len;
+
+	ph = (struct pppoe_hdr *)__skb_push(skb, sizeof(*ph));
+	skb_reset_network_header(skb);
 
 	/*
 	 * Headers in skb will look like in below sequence
@@ -53,24 +57,12 @@
 	 *	The length field in the PPPoE header indicates the length of the PPPoE payload which
 	 *	consists of a 2-byte PPP header plus a skb->len.
 	 */
-	ph = (struct pppoe_hdr *)l2_header;
 	ph->ver = 1;
 	ph->type = 1;
 	ph->code = 0;
 	ph->sid = htons(pppoe_session_id);
-	ph->length = htons(skb->len + sizeof(struct sfe_ppp_hdr));
+	ph->length = htons(data_len);
 	skb->protocol = htons(ETH_P_PPP_SES);
-
-	/*
-	 * Insert the PPP header protocol
-	 */
-	ppp = (struct sfe_ppp_hdr *)(l2_header + (sizeof(struct pppoe_hdr) / 2));
-	ppp->protocol = htons(ppp_protocol);
-
-	/*
-	 * L2 header offset will point to PPPoE header,
-	 */
-	__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
 }
 
 /*
@@ -84,7 +76,7 @@
 	unsigned int len;
 	int pppoe_len;
 	struct sfe_ppp_hdr *ppp;
-	struct pppoe_hdr *ph = (struct pppoe_hdr *)skb->data;
+	struct pppoe_hdr *ph = pppoe_hdr(skb);
 
 	/*
 	 * Check that we have space for PPPoE header here.
@@ -125,8 +117,24 @@
 	}
 
 	sfe_l2_parse_flag_set(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS);
-	sfe_l2_pppoe_hdr_offset_set(l2_info, (skb->data - skb->head));
-	sfe_l2_hdr_size_set(l2_info, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
+	sfe_l2_pppoe_session_id_set(l2_info, ntohs(ph->sid));
+
+	/*
+	 * strip PPPoE header
+	 */
+	__skb_pull(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
+	skb_reset_network_header(skb);
 
 	return true;
 }
+
+/*
+ * sfe_pppoe_undo_parse()
+ *	undo changes done to skb during PPPoE parsing
+ */
+void sfe_pppoe_undo_parse(struct sk_buff *skb, struct sfe_l2_info *l2_info)
+{
+	if (sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS)) {
+		__skb_push(skb, (sizeof(struct pppoe_hdr) + sizeof(struct sfe_ppp_hdr)));
+	}
+}
diff --git a/sfe_pppoe.h b/sfe_pppoe.h
index c797dc7..cb0af1e 100644
--- a/sfe_pppoe.h
+++ b/sfe_pppoe.h
@@ -34,5 +34,6 @@
 
 void sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol);
 bool sfe_pppoe_parse_hdr(struct sk_buff *skb, struct sfe_l2_info *l2_info);
+void sfe_pppoe_undo_parse(struct sk_buff *skb, struct sfe_l2_info *l2_info);
 
 #endif /* __SFE_PPPOE_H */
diff --git a/sfe_vlan.h b/sfe_vlan.h
index 505b24b..263b69a 100644
--- a/sfe_vlan.h
+++ b/sfe_vlan.h
@@ -101,8 +101,6 @@
 {
 	struct vlan_hdr *vhdr;
 
-	l2_info->vlan_hdr_cnt = 0;
-
 	while ((skb->protocol == htons(ETH_P_8021AD) || skb->protocol == htons(ETH_P_8021Q)) &&
 			l2_info->vlan_hdr_cnt < SFE_MAX_VLAN_DEPTH) {
 		if (unlikely(!pskb_may_pull(skb, VLAN_HLEN))) {
@@ -113,7 +111,11 @@
 		l2_info->vlan_hdr[l2_info->vlan_hdr_cnt].tci = ntohs(vhdr->h_vlan_TCI);
 		skb->protocol = vhdr->h_vlan_encapsulated_proto;
 		l2_info->vlan_hdr_cnt++;
+		/*
+		 * strip VLAN header
+		 */
 		__skb_pull(skb, VLAN_HLEN);
+		skb_reset_network_header(skb);
 	}
 
 	l2_info->protocol = htons(skb->protocol);
@@ -205,8 +207,7 @@
 	vlan += (count - 1);
 
 	for (i = 0; i < count; i++) {
-		skb_push(skb, VLAN_HLEN);
-		vhdr = (struct vlan_hdr *)skb->data;
+		vhdr = (struct vlan_hdr *)skb_push(skb, VLAN_HLEN);
 		vhdr->h_vlan_TCI = htons(vlan->tci);
 		vhdr->h_vlan_encapsulated_proto = skb->protocol;
 		skb->protocol = vlan->tpid;