[qca-nss-sfe] Add Tx support for PPPoE

Change-Id: Iae53d1eb116ff847457c2da2864ecd21ad8ae0be
Signed-off-by: Guduri Prathyusha <quic_gprathyu@quicinc.com>
diff --git a/Makefile b/Makefile
index a685a65..fb015f7 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@
 SFE_BASE_OBJS := sfe.o sfe_init.o
 SFE_IPV4_OBJS := sfe_ipv4.o sfe_ipv4_udp.o sfe_ipv4_tcp.o sfe_ipv4_icmp.o
 SFE_IPV6_OBJS := sfe_ipv6.o sfe_ipv6_udp.o sfe_ipv6_tcp.o sfe_ipv6_icmp.o
+SFE_PPPOE_OBJS := sfe_pppoe.o
 
 obj-m += qca-nss-sfe.o
 
@@ -17,6 +18,7 @@
 # IPv4 files
 #
 qca-nss-sfe-objs += $(SFE_IPV4_OBJS)
+qca-nss-sfe-objs += $(SFE_PPPOE_OBJS)
 
 ifdef SFE_SUPPORT_IPV6
 qca-nss-sfe-objs += $(SFE_IPV6_OBJS)
diff --git a/sfe_ipv4.c b/sfe_ipv4.c
index 868e996..f2fd60e 100644
--- a/sfe_ipv4.c
+++ b/sfe_ipv4.c
@@ -919,6 +919,26 @@
 }
 
 /*
+ * sfe_ipv4_xmit_eth_type_check()
+ *	Checking if MAC header has to be written.
+ */
+static inline bool sfe_ipv4_xmit_eth_type_check(struct net_device *dev, u32 cm_flags)
+{
+	if (!(dev->flags & IFF_NOARP)) {
+		return true;
+	}
+
+	/*
+	 * For PPPoE, since we are now supporting PPPoE encapsulation, we are writing L2 header.
+	 */
+	if (unlikely(cm_flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP)) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * sfe_ipv4_create_rule()
  *	Create a forwarding rule.
  */
@@ -1141,7 +1161,7 @@
 	/*
 	 * For the non-arp interface, we don't write L2 HDR.
 	 */
-	if (!(dest_dev->flags & IFF_NOARP)) {
+	if (sfe_ipv4_xmit_eth_type_check(dest_dev, original_cm->flags)) {
 
 		/*
 		 * Check whether the rule has configured a specific source MAC address to use.
@@ -1230,7 +1250,7 @@
 	/*
 	 * For the non-arp interface, we don't write L2 HDR.
 	 */
-	if (!(src_dev->flags & IFF_NOARP)) {
+	if (sfe_ipv4_xmit_eth_type_check(src_dev, reply_cm->flags)) {
 
 		/*
 		 * Check whether the rule has configured a specific source MAC address to use.
@@ -1713,7 +1733,7 @@
 				last_sync_jiffies, mark);
 
 	if (original_cm_flags &= (SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_DECAP | SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP)) {
-		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "pppoe session_id=\"%u\" pppoe server MAC=\"%pM\" ",
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "pppoe_session_id=\"%u\" pppoe_server MAC=\"%pM\" ",
 				pppoe_session_id, pppoe_remote_mac);
 	}
 
diff --git a/sfe_ipv4.h b/sfe_ipv4.h
index 2496327..75b89d5 100644
--- a/sfe_ipv4.h
+++ b/sfe_ipv4.h
@@ -233,6 +233,7 @@
 	SFE_IPV4_EXCEPTION_EVENT_DATAGRAM_INCOMPLETE,
 	SFE_IPV4_EXCEPTION_EVENT_IP_OPTIONS_INCOMPLETE,
 	SFE_IPV4_EXCEPTION_EVENT_UNHANDLED_PROTOCOL,
+	SFE_IPV4_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED,
 	SFE_IPV4_EXCEPTION_EVENT_LAST
 };
 
@@ -262,6 +263,7 @@
 	u64 packets_forwarded64;		/* Number of IPv4 packets forwarded */
 	u64 packets_not_forwarded64;	/* Number of IPv4 packets not forwarded */
 	u64 exception_events64[SFE_IPV4_EXCEPTION_EVENT_LAST];
+	u64 pppoe_encap_packets_forwarded64;	/* Number of IPv4 PPPOE encap packets forwarded */
 };
 
 /*
diff --git a/sfe_ipv4_tcp.c b/sfe_ipv4_tcp.c
index b9dcdce..4450184 100644
--- a/sfe_ipv4_tcp.c
+++ b/sfe_ipv4_tcp.c
@@ -28,6 +28,7 @@
 #include "sfe.h"
 #include "sfe_flow_cookie.h"
 #include "sfe_ipv4.h"
+#include "sfe_pppoe.h"
 
 /*
  * sfe_ipv4_process_tcp_option_sack()
@@ -485,6 +486,23 @@
 	}
 
 	/*
+	 * For PPPoE flows, add PPPoE header before L2 header is added.
+	 */
+	if (cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
+		if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP))) {
+			rcu_read_unlock();
+			DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
+			sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED);
+			return 0;
+		}
+		this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
+	}
+
+	/*
+	 * TODO : VLAN headers if any should be added here when supported.
+	 */
+
+	/*
 	 * Update DSCP
 	 */
 	if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
diff --git a/sfe_ipv4_udp.c b/sfe_ipv4_udp.c
index 1d1a4df..28c7a43 100644
--- a/sfe_ipv4_udp.c
+++ b/sfe_ipv4_udp.c
@@ -28,6 +28,7 @@
 #include "sfe.h"
 #include "sfe_flow_cookie.h"
 #include "sfe_ipv4.h"
+#include "sfe_pppoe.h"
 
 /*
  * sfe_ipv4_recv_udp()
@@ -198,6 +199,23 @@
 	iph->ttl = ttl - 1;
 
 	/*
+	 * For PPPoE flows, add PPPoE header before L2 header is added.
+	 */
+	if (cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
+		if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IP))) {
+			rcu_read_unlock();
+			DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
+			sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED);
+			return 0;
+		}
+		this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
+	}
+
+	/*
+	 * TODO: VLAN header should be added here when they are supported.
+	 */
+
+	/*
 	 * Enable HW csum if rx checksum is verified and xmit interface is CSUM offload capable.
 	 * Note: If L4 csum at Rx was found to be incorrect, we (router) should use incremental L4 checksum here
 	 * so that HW does not re-calculate/replace the L4 csum
diff --git a/sfe_ipv6.c b/sfe_ipv6.c
index ad39f1e..5d2c32c 100644
--- a/sfe_ipv6.c
+++ b/sfe_ipv6.c
@@ -909,6 +909,26 @@
 }
 
 /*
+ * sfe_ipv6_xmit_eth_type_check
+ *	Checking if MAC header has to be written.
+ */
+static inline bool sfe_ipv6_xmit_eth_type_check(struct net_device *dev, u32 cm_flags)
+{
+	if (!(dev->flags & IFF_NOARP)) {
+		return true;
+	}
+
+	/*
+	 * For PPPoE, since we are now supporting PPPoE encapsulation, we are writing L2 header.
+	 */
+	if (cm_flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
+		return true;
+	}
+
+	return false;
+}
+
+/*
  * sfe_ipv6_create_rule()
  *	Create a forwarding rule.
  */
@@ -1090,8 +1110,9 @@
 
 	/*
 	 * For the non-arp interface, we don't write L2 HDR.
+	 * Excluding PPPoE from this, since we are now supporting PPPoE encap/decap.
 	 */
-	if (!(dest_dev->flags & IFF_NOARP)) {
+	if (sfe_ipv6_xmit_eth_type_check(dest_dev, original_cm->flags)) {
 
 		/*
 		 * Check whether the rule has configured a specific source MAC address to use.
@@ -1208,8 +1229,9 @@
 
 	/*
 	 * For the non-arp interface, we don't write L2 HDR.
+	 * Excluding PPPoE from this, since we are now supporting PPPoE encap/decap.
 	 */
-	if (!(src_dev->flags & IFF_NOARP)) {
+	if (sfe_ipv6_xmit_eth_type_check(src_dev, reply_cm->flags)) {
 
 		/*
 		 * Check whether the rule has configured a specific source MAC address to use.
@@ -1678,7 +1700,7 @@
 				last_sync_jiffies, mark);
 
 	if (original_cm_flags &= (SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_DECAP | SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP)) {
-		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "pppoe session_id=\"%u\" pppoe server MAC=\"%pM\" ",
+		bytes_read += snprintf(msg + bytes_read, CHAR_DEV_MSG_SIZE, "pppoe_session_id=\"%u\" pppoe_server_MAC=\"%pM\" ",
 			pppoe_session_id, pppoe_remote_mac);
 	}
 
diff --git a/sfe_ipv6.h b/sfe_ipv6.h
index 1bb773a..5e11cc7 100644
--- a/sfe_ipv6.h
+++ b/sfe_ipv6.h
@@ -240,6 +240,7 @@
 	SFE_IPV6_EXCEPTION_EVENT_IP_OPTIONS_INCOMPLETE,
 	SFE_IPV6_EXCEPTION_EVENT_UNHANDLED_PROTOCOL,
 	SFE_IPV6_EXCEPTION_EVENT_FLOW_COOKIE_ADD_FAIL,
+	SFE_IPV6_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED,
 	SFE_IPV6_EXCEPTION_EVENT_LAST
 };
 
@@ -270,6 +271,7 @@
 	u64 packets_forwarded64;		/* Number of IPv6 packets forwarded */
 	u64 packets_not_forwarded64;	/* Number of IPv6 packets not forwarded */
 	u64 exception_events64[SFE_IPV6_EXCEPTION_EVENT_LAST];
+	u64 pppoe_encap_packets_forwarded64;	/* Number of IPv6 PPPOE encap packets forwarded */
 };
 
 /*
diff --git a/sfe_ipv6_tcp.c b/sfe_ipv6_tcp.c
index 04870e9..e0d362c 100644
--- a/sfe_ipv6_tcp.c
+++ b/sfe_ipv6_tcp.c
@@ -28,6 +28,7 @@
 #include "sfe.h"
 #include "sfe_flow_cookie.h"
 #include "sfe_ipv6.h"
+#include "sfe_pppoe.h"
 
 /*
  * sfe_ipv6_process_tcp_option_sack()
@@ -493,6 +494,23 @@
 	}
 
 	/*
+	 * For PPPoE flows, add PPPoE header before L2 header is added.
+	 */
+	if (cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
+		if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6))) {
+			rcu_read_unlock();
+			DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
+			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED);
+			return 0;
+		}
+		this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
+	}
+
+	/*
+	 * TODO: VLAN header should be added here when they are supported.
+	 */
+
+	/*
 	 * Update DSCP
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
diff --git a/sfe_ipv6_udp.c b/sfe_ipv6_udp.c
index 2523255..2fc79dc 100644
--- a/sfe_ipv6_udp.c
+++ b/sfe_ipv6_udp.c
@@ -28,6 +28,7 @@
 #include "sfe.h"
 #include "sfe_flow_cookie.h"
 #include "sfe_ipv6.h"
+#include "sfe_pppoe.h"
 
 /*
  * sfe_ipv6_recv_udp()
@@ -187,6 +188,23 @@
 	}
 
 	/*
+	 * For PPPoE flows, add PPPoE header before L2 header is added.
+	 */
+	if (cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP) {
+		if (unlikely(!sfe_pppoe_add_header(skb, cm->pppoe_session_id, PPP_IPV6))) {
+			rcu_read_unlock();
+			DEBUG_WARN("%px: PPPoE header addition failed\n", skb);
+			sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_PPPOE_HEADER_ENCAP_FAILED);
+			return 0;
+		}
+		this_cpu_inc(si->stats_pcpu->pppoe_encap_packets_forwarded64);
+	}
+
+	/*
+	 * TODO: VLAN header should be added here when they are supported.
+	 */
+
+	/*
 	 * Update DSCP
 	 */
 	if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_DSCP_REMARK)) {
diff --git a/sfe_pppoe.c b/sfe_pppoe.c
new file mode 100644
index 0000000..4b74185
--- /dev/null
+++ b/sfe_pppoe.c
@@ -0,0 +1,82 @@
+/*
+ * sfe_pppoe.c
+ *     API for shortcut forwarding engine PPPoE flows
+ *
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/if_pppox.h>
+#include <linux/ppp_defs.h>
+
+#include "sfe_debug.h"
+#include "sfe_api.h"
+#include "sfe.h"
+#include "sfe_pppoe.h"
+
+#define SFE_PPPOE_HEADER_SIZE (sizeof(struct pppoe_hdr) + 2)
+
+/*
+ * sfe_pppoe_add_header()
+ *	Add PPPoE header.
+ *
+ * skb->data will point to PPPoE header after the function
+ */
+int sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol)
+{
+	u16 *l2_header;
+	struct pppoe_hdr *ph;
+	u16 *l3_header = (u16 *)skb->data;
+
+	/*
+	 * Check that we have space for PPPoE header and PPP header (2 bytes)
+	 */
+	if (unlikely(!pskb_may_pull(skb, SFE_PPPOE_HEADER_SIZE))) {
+		DEBUG_TRACE("%px: Not enough headroom for PPPoE header \n", skb);
+		return 0;
+	}
+
+	/*
+	 * PPPoE header (8 bytes) + PPP header (2 bytes)
+	 *
+	 * Hence move by 10 bytes to accomodate PPPoE header
+	 */
+	l2_header = l3_header - (SFE_PPPOE_HEADER_SIZE / 2);
+
+	/*
+	 * Headers in skb will look like in below sequence
+	 *	| PPPoE hdr(10 bytes) | L3 hdr |
+	 */
+	ph = (struct pppoe_hdr *)l2_header;
+	ph->ver = 1;
+	ph->type = 1;
+	ph->code = 0;
+	ph->sid = pppoe_session_id;
+	ph->length = skb->len;
+	skb->protocol = cpu_to_be16(ETH_P_PPP_SES);
+
+	/*
+	 * Insert the PPP header protocol
+	 */
+	*(l2_header + 4) = ppp_protocol;
+
+	/*
+	 * L2 header offset will point to PPPoE header,
+	 * since sfe_ipv4_recv_tcp/udp() does skb_push by ETH_HLEN before adding L2 header.
+	 */
+	__skb_push(skb, SFE_PPPOE_HEADER_SIZE);
+
+	return 1;
+}
diff --git a/sfe_pppoe.h b/sfe_pppoe.h
new file mode 100644
index 0000000..6a64bdf
--- /dev/null
+++ b/sfe_pppoe.h
@@ -0,0 +1,21 @@
+/*
+ * sfe_pppoe.h
+ *	Shortcut flow acceleration for PPPoE flow
+ *
+ * Copyright (c) 2021 Qualcomm Innovation Center, Inc. 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.
+ */
+#include <linux/ppp_defs.h>
+
+int sfe_pppoe_add_header(struct sk_buff *skb, u16 pppoe_session_id, u16 ppp_protocol);