[qca-nss-sfe] 3-tuple acceleration for PPPoE bridged flow
Change-Id: Ie42f735e22f9b3a46c81ede6c2d51a86fce37b52
Signed-off-by: Wayne Tan <quic_wtan@quicinc.com>
diff --git a/Makefile b/Makefile
index 0f0e9e6..63e287e 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,9 @@
KERNELVERSION := $(word 1, $(subst ., ,$(KERNELVERSION))).$(word 2, $(subst ., ,$(KERNELVERSION)))
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_ipv4_tun6rd.o
-SFE_IPV6_OBJS := sfe_ipv6.o sfe_ipv6_udp.o sfe_ipv6_tcp.o sfe_ipv6_icmp.o sfe_ipv6_tunipip6.o
-SFE_PPPOE_OBJS := sfe_pppoe.o
+SFE_IPV4_OBJS := sfe_ipv4.o sfe_ipv4_udp.o sfe_ipv4_tcp.o sfe_ipv4_icmp.o sfe_ipv4_tun6rd.o sfe_ipv4_pppoe_br.o
+SFE_IPV6_OBJS := sfe_ipv6.o sfe_ipv6_udp.o sfe_ipv6_tcp.o sfe_ipv6_icmp.o sfe_ipv6_tunipip6.o sfe_ipv6_pppoe_br.o
+SFE_PPPOE_OBJS := sfe_pppoe.o sfe_pppoe_mgr.o
ifeq ($(findstring 4.4, $(KERNELVERSION)),)
diff --git a/exports/sfe_api.h b/exports/sfe_api.h
index 96fc4ee..50a7733 100644
--- a/exports/sfe_api.h
+++ b/exports/sfe_api.h
@@ -243,6 +243,17 @@
};
/**
+ * sfe_pppoe_br_accel_mode_t
+ * PPPoE bridge acceleration modes.
+ */
+typedef enum {
+ SFE_PPPOE_BR_ACCEL_MODE_DISABLED, /**< No acceleration */
+ SFE_PPPOE_BR_ACCEL_MODE_EN_5T, /**< 5-tuple (src_ip, dest_ip, src_port, dest_port, protocol) acceleration */
+ SFE_PPPOE_BR_ACCEL_MODE_EN_3T, /**< 3-tuple (src_ip, dest_ip, pppoe session id) acceleration */
+ SFE_PPPOE_BR_ACCEL_MODE_MAX /**< Indicates the last item */
+} __attribute__ ((__packed__)) sfe_pppoe_br_accel_mode_t;
+
+/**
* PPPoE connection rules structure.
*/
struct sfe_pppoe_rule {
@@ -807,6 +818,14 @@
void sfe_ipv6_mark_rule_update(struct sfe_connection_mark *mark);
/**
+ * Gets the acceleration mode of PPPoE bridge.
+ *
+ * @return
+ * The acceleration mode.
+ */
+sfe_pppoe_br_accel_mode_t sfe_pppoe_get_br_accel_mode(void);
+
+/**
* @}
*/
diff --git a/sfe.c b/sfe.c
index 885ebd7..72b4f1d 100644
--- a/sfe.c
+++ b/sfe.c
@@ -32,6 +32,7 @@
#include "sfe_api.h"
#include "sfe.h"
#include "sfe_pppoe.h"
+#include "sfe_pppoe_mgr.h"
#include "sfe_vlan.h"
#include "sfe_ipv4.h"
#include "sfe_ipv6.h"
@@ -679,6 +680,7 @@
sfe_incr_exceptions(SFE_EXCEPTION_TCP_INVALID);
goto failed_ret;
}
+ break;
case IPPROTO_UDP:
break;
@@ -689,6 +691,12 @@
case IPPROTO_IPV6:
break;
+ case IPPROTO_RAW:
+ /*
+ * for accelerating PPPoE bridged flows using 3-tuple information
+ */
+ break;
+
default:
ret = SFE_CMN_RESPONSE_EMSG;
sfe_incr_exceptions(SFE_EXCEPTION_PROTOCOL_NOT_SUPPORT);
@@ -1128,6 +1136,12 @@
case IPPROTO_GRE:
break;
+ case IPPROTO_RAW:
+ /*
+ * for accelerating PPPoE bridged flows using 3-tuple information
+ */
+ break;
+
default:
ret = SFE_CMN_RESPONSE_EMSG;
sfe_incr_exceptions(SFE_EXCEPTION_PROTOCOL_NOT_SUPPORT);
@@ -1628,6 +1642,76 @@
__ATTR(l2_feature, 0644, sfe_get_l2_feature, sfe_set_l2_feature);
/*
+ * sfe_get_pppoe_br_accel_mode()
+ * Get PPPoE bridge acceleration mode
+ */
+static ssize_t sfe_get_pppoe_br_accel_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ sfe_pppoe_br_accel_mode_t mode;
+ char *str;
+
+ mode = sfe_pppoe_get_br_accel_mode();
+ switch ((int)mode) {
+ case SFE_PPPOE_BR_ACCEL_MODE_DISABLED:
+ str = "ACCEL_MODE_DISABLED";
+ break;
+
+ case SFE_PPPOE_BR_ACCEL_MODE_EN_5T:
+ str = "ACCEL_MODE_5_TUPLE";
+ break;
+
+ case SFE_PPPOE_BR_ACCEL_MODE_EN_3T:
+ str = "ACCEL_MODE_3_TUPLE";
+ break;
+
+ default:
+ str = "Unknown ACCEL_MODE";
+ break;
+ }
+ len = snprintf(buf, PAGE_SIZE, "%s\n", str);
+
+ return len;
+}
+
+/*
+ * sfe_set_pppoe_br_accel_mode()
+ * Set PPPoE bridge acceleration mode
+ */
+static ssize_t sfe_set_pppoe_br_accel_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ uint32_t val;
+ int ret;
+
+ ret = sscanf(buf, "%u", &val);
+ if (ret != 1) {
+ DEBUG_ERROR("Unable to write the mode\n");
+ return -EINVAL;
+ }
+
+ ret = sfe_pppoe_set_br_accel_mode(val);
+ if (ret) {
+ DEBUG_ERROR("Wrong input: %d\n"
+ "Input should be %u or %u or %u\n"
+ "(%u==ACCEL_MODE_DISABLED %u==ACCEL_MODE_EN_5T %u==ACCEL_MODE_EN_3T)\n",
+ val,
+ SFE_PPPOE_BR_ACCEL_MODE_DISABLED, SFE_PPPOE_BR_ACCEL_MODE_EN_5T, SFE_PPPOE_BR_ACCEL_MODE_EN_3T,
+ SFE_PPPOE_BR_ACCEL_MODE_DISABLED, SFE_PPPOE_BR_ACCEL_MODE_EN_5T, SFE_PPPOE_BR_ACCEL_MODE_EN_3T);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static const struct device_attribute sfe_pppoe_br_accel_mode_attr =
+ __ATTR(pppoe_br_accel_mode, 0644, sfe_get_pppoe_br_accel_mode, sfe_set_pppoe_br_accel_mode);
+
+/*
* sfe_init_if()
*/
int sfe_init_if(void)
@@ -1658,12 +1742,26 @@
goto exit2;
}
+ /*
+ * Create sys/sfe/l2_feature
+ */
result = sysfs_create_file(sfe_ctx->sys_sfe, &sfe_l2_feature_attr.attr);
if (result) {
DEBUG_ERROR("failed to register L2 feature flag sysfs file: %d\n", result);
goto exit2;
}
+ /*
+ * Create sys/sfe/pppoe_br_accel_mode
+ */
+ result = sysfs_create_file(sfe_ctx->sys_sfe, &sfe_pppoe_br_accel_mode_attr.attr);
+ if (result) {
+ DEBUG_ERROR("failed to create pppoe_br_accel_mode: %d\n", result);
+ goto exit2;
+ }
+
+ sfe_pppoe_mgr_init();
+
spin_lock_init(&sfe_ctx->lock);
INIT_LIST_HEAD(&sfe_ctx->msg_queue);
@@ -1694,6 +1792,8 @@
*/
RCU_INIT_POINTER(athrs_fast_nat_recv, NULL);
+ sfe_pppoe_mgr_exit();
+
/*
* Wait for all callbacks to complete.
*/
diff --git a/sfe_ipv4.c b/sfe_ipv4.c
index 637ad9a..21683bc 100644
--- a/sfe_ipv4.c
+++ b/sfe_ipv4.c
@@ -45,6 +45,8 @@
#include "sfe_ipv4_tcp.h"
#include "sfe_ipv4_icmp.h"
#include "sfe_pppoe.h"
+#include "sfe_pppoe_mgr.h"
+#include "sfe_ipv4_pppoe_br.h"
#include "sfe_ipv4_gre.h"
#include "sfe_ipv4_tun6rd.h"
@@ -90,6 +92,7 @@
"INVALID_PPPOE_SESSION",
"INCORRECT_PPPOE_PARSING",
"PPPOE_NOT_SET_IN_CME",
+ "PPPOE_BR_NOT_IN_CME",
"INGRESS_VLAN_TAG_MISMATCH",
"INVALID_SOURCE_INTERFACE",
"TUN6RD_NO_CONNECTION",
@@ -325,6 +328,7 @@
stats->pppoe_encap_packets_forwarded64 += s->pppoe_encap_packets_forwarded64;
stats->pppoe_decap_packets_forwarded64 += s->pppoe_decap_packets_forwarded64;
stats->pppoe_bridge_packets_forwarded64 += s->pppoe_bridge_packets_forwarded64;
+ stats->pppoe_bridge_packets_3tuple_forwarded64 += s->pppoe_bridge_packets_3tuple_forwarded64;
}
}
@@ -425,7 +429,7 @@
static inline unsigned int sfe_ipv4_get_connection_hash(u8 protocol, __be32 src_ip, __be16 src_port,
__be32 dest_ip, __be16 dest_port)
{
- u32 hash = ntohl(src_ip ^ dest_ip) ^ protocol ^ ntohs(src_port ^ dest_port);
+ u32 hash = ntohl(src_ip ^ dest_ip) ^ protocol ^ ntohs(src_port) ^ dest_port;
return ((hash >> SFE_IPV4_CONNECTION_HASH_SHIFT) ^ hash) & SFE_IPV4_CONNECTION_HASH_MASK;
}
@@ -889,6 +893,17 @@
sync_on_find = true;
}
+ /*
+ * Handle PPPoE bridge packets using 3-tuple acceleration if SFE_PPPOE_BR_ACCEL_MODE_EN_3T
+ */
+ if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS)) &&
+ unlikely(sfe_pppoe_get_br_accel_mode() == SFE_PPPOE_BR_ACCEL_MODE_EN_3T)) {
+ struct ethhdr *eth = eth_hdr(skb);
+ if (!sfe_pppoe_mgr_find_session(l2_info->pppoe_session_id, eth->h_source)) {
+ return sfe_ipv4_recv_pppoe_bridge(si, skb, dev, len, iph, ihl, l2_info);
+ }
+ }
+
protocol = iph->protocol;
if (IPPROTO_UDP == protocol) {
return sfe_ipv4_recv_udp(si, skb, dev, len, iph, ihl, sync_on_find, l2_info, tun_outer);
@@ -1416,14 +1431,14 @@
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
- reply_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
+ reply_cm->l2_hdr_size += PPPOE_SES_HLEN;
reply_cm->pppoe_session_id = msg->pppoe_rule.flow_pppoe_session_id;
ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
}
if (msg->valid_flags & SFE_RULE_CREATE_PPPOE_ENCAP_VALID) {
original_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
- original_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
+ original_cm->l2_hdr_size += PPPOE_SES_HLEN;
original_cm->pppoe_session_id = msg->pppoe_rule.return_pppoe_session_id;
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac);
@@ -1782,6 +1797,16 @@
reply_cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_NO_SEQ_CHECK;
}
break;
+
+ case IPPROTO_RAW:
+ /*
+ * Set src_port to 0 to avoid hash collision in connection match lookups.
+ */
+ original_cm->match_src_port = 0;
+ original_cm->xlate_src_port = 0;
+ reply_cm->match_src_port = 0;
+ reply_cm->xlate_src_port = 0;
+ break;
}
/*
@@ -1813,7 +1838,7 @@
/*
* We have everything we need!
*/
- DEBUG_INFO("NEW connection - p: %d\n"
+ DEBUG_INFO("%px: NEW connection - p: %d\n"
"original_cm: match_dev=src_dev: %s %d %pM\n"
" xmit_dev=dest_dev: %s %d %pM\n"
" xmit_src_mac: %pM\n"
@@ -1831,7 +1856,7 @@
"return_ip_xlate: %pI4:%u\n"
"return_mac: %pM\n"
"flags: valid=%x src_mac_valid=%x\n",
- tuple->protocol,
+ c, tuple->protocol,
original_cm->match_dev->name, original_cm->match_dev->ifindex, original_cm->match_dev->dev_addr,
original_cm->xmit_dev->name, original_cm->xmit_dev->ifindex, original_cm->xmit_dev->dev_addr,
original_cm->xmit_src_mac, original_cm->xmit_dest_mac, original_cm->flags, original_cm->l2_hdr_size,
@@ -2433,7 +2458,8 @@
"hash_hits=\"%llu\" hash_reorders=\"%llu\" "
"pppoe_encap_pkts_fwded=\"%llu\" "
"pppoe_decap_pkts_fwded=\"%llu\" "
- "pppoe_bridge_pkts_fwded=\"%llu\" />\n",
+ "pppoe_bridge_pkts_fwded=\"%llu\" "
+ "pppoe_bridge_pkts_3tuple_fwded=\"%llu\" />\n",
num_conn,
stats.packets_dropped64,
stats.packets_fast_xmited64,
@@ -2449,7 +2475,8 @@
stats.connection_match_hash_reorders64,
stats.pppoe_encap_packets_forwarded64,
stats.pppoe_decap_packets_forwarded64,
- stats.pppoe_bridge_packets_forwarded64);
+ stats.pppoe_bridge_packets_forwarded64,
+ stats.pppoe_bridge_packets_3tuple_forwarded64);
if (copy_to_user(buffer + *total_read, msg, CHAR_DEV_MSG_SIZE)) {
return false;
}
diff --git a/sfe_ipv4.h b/sfe_ipv4.h
index 6c058e0..53e8557 100644
--- a/sfe_ipv4.h
+++ b/sfe_ipv4.h
@@ -289,6 +289,7 @@
SFE_IPV4_EXCEPTION_EVENT_INVALID_PPPOE_SESSION,
SFE_IPV4_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING,
SFE_IPV4_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME,
+ SFE_IPV4_EXCEPTION_EVENT_PPPOE_BR_NOT_IN_CME,
SFE_IPV4_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH,
SFE_IPV4_EXCEPTION_EVENT_INVALID_SRC_IFACE,
SFE_IPV4_EXCEPTION_EVENT_TUN6RD_NO_CONNECTION,
@@ -333,6 +334,7 @@
u64 pppoe_encap_packets_forwarded64; /* Number of IPv4 PPPoE encap packets forwarded */
u64 pppoe_decap_packets_forwarded64; /* Number of IPv4 PPPoE decap packets forwarded */
u64 pppoe_bridge_packets_forwarded64; /* Number of IPv4 PPPoE bridge packets forwarded */
+ u64 pppoe_bridge_packets_3tuple_forwarded64; /* Number of IPv4 PPPoE bridge packets forwarded based on 3-tuple info */
};
/*
diff --git a/sfe_ipv4_pppoe_br.c b/sfe_ipv4_pppoe_br.c
new file mode 100644
index 0000000..6d12853
--- /dev/null
+++ b/sfe_ipv4_pppoe_br.c
@@ -0,0 +1,204 @@
+/*
+ * sfe_ipv4_pppoe_br.c
+ * Shortcut forwarding engine - IPv4 PPPoE bridge implementation
+ *
+ * Copyright (c) 2022 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 <net/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+
+#include "sfe_debug.h"
+#include "sfe_api.h"
+#include "sfe.h"
+#include "sfe_ipv4.h"
+#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
+
+/*
+ * sfe_ipv4_recv_pppoe_bridge()
+ * Process PPPoE bridge packets using 3-tuple acceleration
+ *
+ */
+int sfe_ipv4_recv_pppoe_bridge(struct sfe_ipv4 *si, struct sk_buff *skb, struct net_device *dev,
+ unsigned int len, struct iphdr *iph, unsigned int ihl, struct sfe_l2_info *l2_info)
+{
+ struct sfe_ipv4_connection_match *cm;
+ u32 service_class_id;
+ struct net_device *xmit_dev;
+ int ret;
+ bool fast_xmit;
+ netdev_features_t features;
+
+ rcu_read_lock();
+
+ cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_RAW, iph->saddr, 0, iph->daddr, htons(sfe_l2_pppoe_session_id_get(l2_info)));
+ if (unlikely(!cm)) {
+ rcu_read_unlock();
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_PPPOE_BR_NOT_IN_CME);
+ DEBUG_TRACE("%px: no connection found in 3-tuple lookup for PPPoE bridge flow\n", skb);
+ return 0;
+ }
+
+ /*
+ * Source interface validate.
+ */
+ if (unlikely((cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) {
+ if (!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) {
+ struct sfe_ipv4_connection *c = cm->connection;
+ DEBUG_TRACE("flush on source interface check failure\n");
+ spin_lock_bh(&si->lock);
+ ret = sfe_ipv4_remove_connection(si, c);
+ spin_unlock_bh(&si->lock);
+
+ if (ret) {
+ sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
+ }
+ }
+ rcu_read_unlock();
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INVALID_SRC_IFACE);
+ DEBUG_TRACE("exception the packet on source interface check failure\n");
+ return 0;
+ }
+
+ /*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
+ /*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
+ * Restore PPPoE header back
+ */
+ __skb_push(skb, PPPOE_SES_HLEN);
+
+ /*
+ * Update traffic stats.
+ */
+ atomic_inc(&cm->rx_packet_count);
+ atomic_add(len, &cm->rx_byte_count);
+
+ xmit_dev = cm->xmit_dev;
+ skb->dev = xmit_dev;
+
+ /*
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
+ */
+ if (likely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
+ if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
+ dev_hard_header(skb, xmit_dev, ntohs(skb->protocol),
+ cm->xmit_dest_mac, cm->xmit_src_mac, len);
+ } else {
+ /*
+ * For the simple case we write this really fast.
+ */
+ struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
+ eth->h_proto = skb->protocol;
+ ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
+ ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
+ }
+ }
+
+ /*
+ * Update priority of skb.
+ */
+ if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
+ skb->priority = cm->priority;
+ }
+
+ /*
+ * Mark outgoing packet.
+ */
+ if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_MARK)) {
+ skb->mark = cm->mark;
+ /*
+ * Update service class stats if SAWF is valid.
+ */
+ if (likely(cm->sawf_valid)) {
+ service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+ sfe_ipv4_service_class_stats_inc(si, service_class_id, len);
+ }
+ }
+
+ /*
+ * For the first packets, check if it could got fast xmit.
+ */
+ if (unlikely(!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)
+ && (cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_DEV_ADMISSION))){
+ cm->features = netif_skb_features(skb);
+ if (likely(sfe_fast_xmit_check(skb, cm->features))) {
+ cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT;
+ }
+ cm->flags |= SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED;
+ }
+ features = cm->features;
+
+ fast_xmit = !!(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_FAST_XMIT);
+
+ rcu_read_unlock();
+
+ this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_3tuple_forwarded64);
+ this_cpu_inc(si->stats_pcpu->packets_forwarded64);
+
+ /*
+ * We're going to check for GSO flags when we transmit the packet so
+ * start fetching the necessary cache line now.
+ */
+ prefetch(skb_shinfo(skb));
+
+ /*
+ * We do per packet condition check before we could fast xmit the
+ * packet.
+ */
+ if (likely(fast_xmit && dev_fast_xmit(skb, xmit_dev, features))) {
+ this_cpu_inc(si->stats_pcpu->packets_fast_xmited64);
+ return 1;
+ }
+
+ /*
+ * Mark that this packet has been fast forwarded.
+ */
+ skb->fast_forwarded = 1;
+
+ /*
+ * Send the packet on its way.
+ */
+ dev_queue_xmit(skb);
+
+ return 1;
+}
diff --git a/sfe_ipv4_pppoe_br.h b/sfe_ipv4_pppoe_br.h
new file mode 100644
index 0000000..1491495
--- /dev/null
+++ b/sfe_ipv4_pppoe_br.h
@@ -0,0 +1,20 @@
+/*
+ * sfe_ipv4_pppoe_br.h
+ * Shortcut forwarding engine - IPv4 PPPoE bridge header file
+ *
+ * Copyright (c) 2022 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.
+ */
+int sfe_ipv4_recv_pppoe_bridge(struct sfe_ipv4 *si, struct sk_buff *skb, struct net_device *dev,
+ unsigned int len, struct iphdr *iph, unsigned int ihl, struct sfe_l2_info *l2_info);
diff --git a/sfe_ipv4_tcp.c b/sfe_ipv4_tcp.c
index aea240e..8ed26fd 100644
--- a/sfe_ipv4_tcp.c
+++ b/sfe_ipv4_tcp.c
@@ -168,6 +168,9 @@
cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_TCP, src_ip, src_port, dest_ip, dest_port);
}
#else
+ /*
+ * 5-tuple lookup for TCP flow.
+ */
cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_TCP, src_ip, src_port, dest_ip, dest_port);
#endif
if (unlikely(!cm)) {
@@ -213,7 +216,7 @@
}
/*
- * If our packet has beern marked as "flush on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before throw it slow path.
*/
@@ -288,8 +291,8 @@
ret = sfe_ipv4_remove_connection(si, c);
spin_unlock_bh(&si->lock);
- DEBUG_TRACE("TCP flags: 0x%x are not fast\n",
- flags & (TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_FIN | TCP_FLAG_ACK));
+ DEBUG_TRACE("TCP flags: %#x are not fast. %u->%u\n",
+ htonl(flags), htons(src_port), htons(dest_port));
if (ret) {
sfe_ipv4_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
}
@@ -500,6 +503,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* For PPPoE packets, match server MAC and session id
*/
if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
@@ -535,7 +548,7 @@
* 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))) {
+ if (unlikely(!bridge_flow)) {
rcu_read_unlock();
DEBUG_TRACE("%px: CME doesn't contain PPPoE flag but packet has PPPoE header\n", skb);
sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME);
@@ -546,21 +559,11 @@
* 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)));
+ __skb_push(skb, PPPOE_SES_HLEN);
this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
}
/*
- * Check if skb has enough headroom to write L2 headers
- */
- if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
- rcu_read_unlock();
- DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
- sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
-
- /*
* From this point on we're good to modify the packet.
*/
diff --git a/sfe_ipv4_udp.c b/sfe_ipv4_udp.c
index 922e54e..9e71111 100644
--- a/sfe_ipv4_udp.c
+++ b/sfe_ipv4_udp.c
@@ -176,7 +176,7 @@
if (unlikely(!cm)) {
/*
- * try a 4-tuple lookup; required for tunnels like vxlan.
+ * Try a 4-tuple lookup; required for tunnels like vxlan.
*/
cm = sfe_ipv4_find_connection_match_rcu(si, dev, IPPROTO_UDP, src_ip, 0, dest_ip, dest_port);
if (unlikely(!cm)) {
@@ -209,7 +209,7 @@
}
/*
- * If our packet has beern marked as "flush on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before exception it to slow path.
*/
@@ -217,7 +217,7 @@
sfe_ipv4_sync_status(si, cm->connection, SFE_SYNC_REASON_STATS);
rcu_read_unlock();
sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_UDP_IP_OPTIONS_OR_INITIAL_FRAGMENT);
- DEBUG_TRACE("%px: sfe: sync on find\n", cm);
+ DEBUG_TRACE("%px: sync on find\n", cm);
return 0;
}
@@ -294,6 +294,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* For PPPoE packets, match server MAC and session id
*/
if (unlikely(cm->flags & SFE_IPV4_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
@@ -329,33 +339,22 @@
* 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))) {
+ if (unlikely(!bridge_flow)) {
rcu_read_unlock();
DEBUG_TRACE("%px: CME doesn't contain PPPoE flag but packet has PPPoE header\n", skb);
sfe_ipv4_exception_stats_inc(si, SFE_IPV4_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)));
+ __skb_push(skb, PPPOE_SES_HLEN);
this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
}
/*
- * Check if skb has enough headroom to write L2 headers
- */
- if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
- rcu_read_unlock();
- DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
- sfe_ipv4_exception_stats_inc(si, SFE_IPV4_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
-
- /*
* From this point on we're good to modify the packet.
*/
diff --git a/sfe_ipv6.c b/sfe_ipv6.c
index 5f2d791..1a08f1e 100644
--- a/sfe_ipv6.c
+++ b/sfe_ipv6.c
@@ -45,6 +45,8 @@
#include "sfe_ipv6_tcp.h"
#include "sfe_ipv6_icmp.h"
#include "sfe_pppoe.h"
+#include "sfe_pppoe_mgr.h"
+#include "sfe_ipv6_pppoe_br.h"
#include "sfe_ipv6_tunipip6.h"
#include "sfe_ipv6_gre.h"
@@ -92,6 +94,7 @@
"INVALID_PPPOE_SESSION",
"INCORRECT_PPPOE_PARSING",
"PPPOE_NOT_SET_IN_CME",
+ "PPPOE_BR_NOT_IN_CME",
"INGRESS_VLAN_TAG_MISMATCH",
"INVALID_SOURCE_INTERFACE",
"TUNIPIP6_HEADER_INCOMPLETE",
@@ -330,6 +333,7 @@
stats->pppoe_encap_packets_forwarded64 += s->pppoe_encap_packets_forwarded64;
stats->pppoe_decap_packets_forwarded64 += s->pppoe_decap_packets_forwarded64;
stats->pppoe_bridge_packets_forwarded64 += s->pppoe_bridge_packets_forwarded64;
+ stats->pppoe_bridge_packets_3tuple_forwarded64 += s->pppoe_bridge_packets_3tuple_forwarded64;
}
}
@@ -436,7 +440,7 @@
for (idx = 0; idx < 4; idx++) {
hash ^= src_ip->addr[idx] ^ dest_ip->addr[idx];
}
- hash = hash ^ protocol ^ ntohs(src_port ^ dest_port);
+ hash = hash ^ protocol ^ ntohs(src_port) ^ dest_port;
return ((hash >> SFE_IPV6_CONNECTION_HASH_SHIFT) ^ hash) & SFE_IPV6_CONNECTION_HASH_MASK;
}
@@ -913,6 +917,17 @@
next_hdr = ext_hdr->next_hdr;
}
+ /*
+ * Handle PPPoE bridge packets using 3-tuple acceleration if SFE_PPPOE_BR_ACCEL_MODE_EN_3T
+ */
+ if (unlikely(sfe_l2_parse_flag_check(l2_info, SFE_L2_PARSE_FLAGS_PPPOE_INGRESS)) &&
+ unlikely(sfe_pppoe_get_br_accel_mode() == SFE_PPPOE_BR_ACCEL_MODE_EN_3T)) {
+ struct ethhdr *eth = eth_hdr(skb);
+ if (!sfe_pppoe_mgr_find_session(l2_info->pppoe_session_id, eth->h_source)) {
+ return sfe_ipv6_recv_pppoe_bridge(si, skb, dev, len, iph, ihl, l2_info);
+ }
+ }
+
if (IPPROTO_UDP == next_hdr) {
return sfe_ipv6_recv_udp(si, skb, dev, len, iph, ihl, sync_on_find, l2_info, tun_outer);
}
@@ -1389,14 +1404,14 @@
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
- reply_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
+ reply_cm->l2_hdr_size += PPPOE_SES_HLEN;
reply_cm->pppoe_session_id = msg->pppoe_rule.flow_pppoe_session_id;
ether_addr_copy(reply_cm->pppoe_remote_mac, msg->pppoe_rule.flow_pppoe_remote_mac);
}
if (msg->valid_flags & SFE_RULE_CREATE_PPPOE_ENCAP_VALID) {
original_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_ENCAP;
- original_cm->l2_hdr_size += SFE_PPPOE_SESSION_HEADER_SIZE;
+ original_cm->l2_hdr_size += PPPOE_SES_HLEN;
original_cm->pppoe_session_id = msg->pppoe_rule.return_pppoe_session_id;
ether_addr_copy(original_cm->pppoe_remote_mac, msg->pppoe_rule.return_pppoe_remote_mac);
@@ -1617,7 +1632,7 @@
reply_cm->top_interface_dev = NULL;
#ifdef SFE_GRE_TUN_ENABLE
- if (!(reply_cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH)) {
+ if ((IPPROTO_GRE == tuple->protocol) && !(reply_cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PASSTHROUGH)) {
rcu_read_lock();
reply_cm->proto = rcu_dereference(inet6_protos[tuple->protocol]);
rcu_read_unlock();
@@ -1745,6 +1760,16 @@
reply_cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_NO_SEQ_CHECK;
}
break;
+
+ case IPPROTO_RAW:
+ /*
+ * Set src_port to 0 to avoid hash collision in connection match lookups.
+ */
+ original_cm->match_src_port = 0;
+ original_cm->xlate_src_port = 0;
+ reply_cm->match_src_port = 0;
+ reply_cm->xlate_src_port = 0;
+ break;
}
/*
@@ -1778,10 +1803,10 @@
/*
* We have everything we need!
*/
- DEBUG_INFO("new connection - p: %d\n"
+ DEBUG_INFO("%px: new connection - p: %d\n"
" s: %s:%pxM(%pxM):%pI6(%pI6):%u(%u)\n"
" d: %s:%pxM(%pxM):%pI6(%pI6):%u(%u)\n",
- tuple->protocol,
+ c, tuple->protocol,
src_dev->name, msg->conn_rule.flow_mac, NULL,
(void *)tuple->flow_ip, (void *)tuple->flow_ip, ntohs(tuple->flow_ident), ntohs(tuple->flow_ident),
dest_dev->name, NULL, msg->conn_rule.return_mac,
@@ -2369,8 +2394,8 @@
"hash_hits=\"%llu\" hash_reorders=\"%llu\" "
"pppoe_encap_pkts_fwded=\"%llu\" "
"pppoe_decap_pkts_fwded=\"%llu\" "
- "pppoe_bridge_pkts_fwded=\"%llu\" />\n",
-
+ "pppoe_bridge_pkts_fwded=\"%llu\" "
+ "pppoe_bridge_pkts_3tuple_fwded=\"%llu\" />\n",
num_conn,
stats.packets_dropped64,
stats.packets_fast_xmited64,
@@ -2386,7 +2411,8 @@
stats.connection_match_hash_reorders64,
stats.pppoe_encap_packets_forwarded64,
stats.pppoe_decap_packets_forwarded64,
- stats.pppoe_bridge_packets_forwarded64);
+ stats.pppoe_bridge_packets_forwarded64,
+ stats.pppoe_bridge_packets_3tuple_forwarded64);
if (copy_to_user(buffer + *total_read, msg, CHAR_DEV_MSG_SIZE)) {
return false;
}
diff --git a/sfe_ipv6.h b/sfe_ipv6.h
index 865a7e1..2df32e7 100644
--- a/sfe_ipv6.h
+++ b/sfe_ipv6.h
@@ -296,6 +296,7 @@
SFE_IPV6_EXCEPTION_EVENT_INVALID_PPPOE_SESSION,
SFE_IPV6_EXCEPTION_EVENT_INCORRECT_PPPOE_PARSING,
SFE_IPV6_EXCEPTION_EVENT_PPPOE_NOT_SET_IN_CME,
+ SFE_IPV6_EXCEPTION_EVENT_PPPOE_BR_NOT_IN_CME,
SFE_IPV6_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH,
SFE_IPV6_EXCEPTION_EVENT_INVALID_SRC_IFACE,
SFE_IPV6_EXCEPTION_EVENT_TUNIPIP6_HEADER_INCOMPLETE,
@@ -344,6 +345,7 @@
u64 pppoe_encap_packets_forwarded64; /* Number of IPv6 PPPoE encap packets forwarded */
u64 pppoe_decap_packets_forwarded64; /* Number of IPv6 PPPoE decap packets forwarded */
u64 pppoe_bridge_packets_forwarded64; /* Number of IPv6 PPPoE decap packets forwarded */
+ u64 pppoe_bridge_packets_3tuple_forwarded64; /* Number of IPv6 PPPoE bridge packets forwarded based on 3-tuple info */
};
/*
diff --git a/sfe_ipv6_pppoe_br.c b/sfe_ipv6_pppoe_br.c
new file mode 100644
index 0000000..f3c80b7
--- /dev/null
+++ b/sfe_ipv6_pppoe_br.c
@@ -0,0 +1,207 @@
+/*
+ * sfe_ipv6_pppoe_br.c
+ * Shortcut forwarding engine - IPv6 PPPoE bridge implementation
+ *
+ * Copyright (c) 2022 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 <net/udp.h>
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+
+#include "sfe_debug.h"
+#include "sfe_api.h"
+#include "sfe.h"
+#include "sfe_ipv6.h"
+#include "sfe_pppoe.h"
+#include "sfe_vlan.h"
+
+/*
+ * sfe_ipv6_recv_pppoe_bridge()
+ * Process PPPoE bridge packets using 3-tuple acceleration
+ *
+ */
+int sfe_ipv6_recv_pppoe_bridge(struct sfe_ipv6 *si, struct sk_buff *skb, struct net_device *dev,
+ unsigned int len, struct ipv6hdr *iph, unsigned int ihl, struct sfe_l2_info *l2_info)
+{
+ struct sfe_ipv6_connection_match *cm;
+ u32 service_class_id;
+ struct net_device *xmit_dev;
+ int ret;
+ bool fast_xmit;
+ netdev_features_t features;
+
+ rcu_read_lock();
+
+ cm = sfe_ipv6_find_connection_match_rcu(si, dev, IPPROTO_RAW,
+ (struct sfe_ipv6_addr *)iph->saddr.s6_addr32, 0,
+ (struct sfe_ipv6_addr *)iph->daddr.s6_addr32,
+ htons(sfe_l2_pppoe_session_id_get(l2_info)));
+ if (unlikely(!cm)) {
+ rcu_read_unlock();
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_PPPOE_BR_NOT_IN_CME);
+ DEBUG_TRACE("%px: no connection found in 3-tuple lookup for PPPoE bridge flow\n", skb);
+ return 0;
+ }
+
+ /*
+ * Source interface validate.
+ */
+ if (unlikely((cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK) && (cm->match_dev != dev))) {
+ if (!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_SRC_INTERFACE_CHECK_NO_FLUSH)) {
+ struct sfe_ipv6_connection *c = cm->connection;
+ DEBUG_TRACE("flush on source interface check failure\n");
+ spin_lock_bh(&si->lock);
+ ret = sfe_ipv6_remove_connection(si, c);
+ spin_unlock_bh(&si->lock);
+
+ if (ret) {
+ sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
+ }
+ }
+ rcu_read_unlock();
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INVALID_SRC_IFACE);
+ DEBUG_TRACE("exception the packet on source interface check failure\n");
+ return 0;
+ }
+
+ /*
+ * Do we expect an ingress VLAN tag for this flow?
+ */
+ if (unlikely(!sfe_vlan_validate_ingress_tag(skb, cm->ingress_vlan_hdr_cnt, cm->ingress_vlan_hdr, l2_info))) {
+ rcu_read_unlock();
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_INGRESS_VLAN_TAG_MISMATCH);
+ DEBUG_TRACE("VLAN tag mismatch. skb=%px\n", skb);
+ return 0;
+ }
+
+ /*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
+ * Restore PPPoE header back
+ */
+ __skb_push(skb, PPPOE_SES_HLEN);
+
+ /*
+ * Update traffic stats.
+ */
+ atomic_inc(&cm->rx_packet_count);
+ atomic_add(len, &cm->rx_byte_count);
+
+ xmit_dev = cm->xmit_dev;
+ skb->dev = xmit_dev;
+
+ /*
+ * Check to see if we need to add VLAN tags
+ */
+ if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_INSERT_EGRESS_VLAN_TAG)) {
+ sfe_vlan_add_tag(skb, cm->egress_vlan_hdr_cnt, cm->egress_vlan_hdr);
+ }
+
+ /*
+ * Check to see if we need to write an Ethernet header.
+ */
+ if (likely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_L2_HDR)) {
+ if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_WRITE_FAST_ETH_HDR))) {
+ dev_hard_header(skb, xmit_dev, ntohs(skb->protocol),
+ cm->xmit_dest_mac, cm->xmit_src_mac, len);
+ } else {
+ /*
+ * For the simple case we write this really fast.
+ */
+ struct ethhdr *eth = (struct ethhdr *)__skb_push(skb, ETH_HLEN);
+ eth->h_proto = skb->protocol;
+ ether_addr_copy((u8 *)eth->h_dest, (u8 *)cm->xmit_dest_mac);
+ ether_addr_copy((u8 *)eth->h_source, (u8 *)cm->xmit_src_mac);
+ }
+ }
+
+ /*
+ * Update priority of skb.
+ */
+ if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PRIORITY_REMARK)) {
+ skb->priority = cm->priority;
+ }
+
+ /*
+ * Mark outgoing packet.
+ */
+ if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_MARK)) {
+ skb->mark = cm->mark;
+ /*
+ * Update service class stats if SAWF is valid.
+ */
+ if (likely(cm->sawf_valid)) {
+ service_class_id = SFE_GET_SAWF_SERVICE_CLASS(cm->mark);
+ sfe_ipv6_service_class_stats_inc(si, service_class_id, len);
+ }
+ }
+
+ /*
+ * For the first packets, check if it could got fast xmit.
+ */
+ if (unlikely(!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED)
+ && (cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_DEV_ADMISSION))){
+ cm->features = netif_skb_features(skb);
+ if (likely(sfe_fast_xmit_check(skb, cm->features))) {
+ cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT;
+ }
+ cm->flags |= SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT_FLOW_CHECKED;
+ }
+ features = cm->features;
+
+ fast_xmit = !!(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_FAST_XMIT);
+
+ rcu_read_unlock();
+
+ this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_3tuple_forwarded64);
+ this_cpu_inc(si->stats_pcpu->packets_forwarded64);
+
+ /*
+ * We're going to check for GSO flags when we transmit the packet so
+ * start fetching the necessary cache line now.
+ */
+ prefetch(skb_shinfo(skb));
+
+ /*
+ * We do per packet condition check before we could fast xmit the
+ * packet.
+ */
+ if (likely(fast_xmit && dev_fast_xmit(skb, xmit_dev, features))) {
+ this_cpu_inc(si->stats_pcpu->packets_fast_xmited64);
+ return 1;
+ }
+
+ /*
+ * Mark that this packet has been fast forwarded.
+ */
+ skb->fast_forwarded = 1;
+
+ /*
+ * Send the packet on its way.
+ */
+ dev_queue_xmit(skb);
+
+ return 1;
+}
diff --git a/sfe_ipv6_pppoe_br.h b/sfe_ipv6_pppoe_br.h
new file mode 100644
index 0000000..84dc313
--- /dev/null
+++ b/sfe_ipv6_pppoe_br.h
@@ -0,0 +1,20 @@
+/*
+ * sfe_ipv6_pppoe_br.h
+ * Shortcut forwarding engine - IPv6 PPPoE bridge header file
+ *
+ * Copyright (c) 2022 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.
+ */
+int sfe_ipv6_recv_pppoe_bridge(struct sfe_ipv6 *si, struct sk_buff *skb, struct net_device *dev,
+ unsigned int len, struct ipv6hdr *iph, unsigned int ihl, struct sfe_l2_info *l2_info);
diff --git a/sfe_ipv6_tcp.c b/sfe_ipv6_tcp.c
index e2f2cd8..0cf867f 100644
--- a/sfe_ipv6_tcp.c
+++ b/sfe_ipv6_tcp.c
@@ -215,7 +215,7 @@
}
/*
- * If our packet has beern marked as "flush on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before throw it slow path.
*/
@@ -289,8 +289,8 @@
ret = sfe_ipv6_remove_connection(si, c);
spin_unlock_bh(&si->lock);
- DEBUG_TRACE("TCP flags: 0x%x are not fast\n",
- flags & (TCP_FLAG_SYN | TCP_FLAG_RST | TCP_FLAG_FIN | TCP_FLAG_ACK));
+ DEBUG_TRACE("TCP flags: %#x are not fast. %u->%u skb=%px\n",
+ htonl(flags), htons(src_port), htons(dest_port), skb);
if (ret) {
sfe_ipv6_flush_connection(si, c, SFE_SYNC_REASON_FLUSH);
}
@@ -509,6 +509,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* For PPPoE packets, match server MAC and session id
*/
if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
@@ -543,7 +553,7 @@
/*
* 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))) {
+ if (unlikely(!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_PPPOE_NOT_SET_IN_CME);
@@ -554,21 +564,11 @@
/*
* 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)));
+ __skb_push(skb, PPPOE_SES_HLEN);
this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
}
/*
- * Check if skb has enough headroom to write L2 headers
- */
- if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
- rcu_read_unlock();
- DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
- sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
-
- /*
* From this point on we're good to modify the packet.
*/
diff --git a/sfe_ipv6_udp.c b/sfe_ipv6_udp.c
index 0afa6f3..18af651 100644
--- a/sfe_ipv6_udp.c
+++ b/sfe_ipv6_udp.c
@@ -225,7 +225,7 @@
}
/*
- * If our packet has been marked as "flush on find" we can't actually
+ * If our packet has been marked as "sync on find" we can't actually
* forward it in the fast path, but now that we've found an associated
* connection we need sync its status before exception it to slow path.
*/
@@ -301,6 +301,16 @@
}
/*
+ * Check if skb has enough headroom to write L2 headers
+ */
+ if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
+ rcu_read_unlock();
+ DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
+ sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
+ return 0;
+ }
+
+ /*
* For PPPoE packets, match server MAC and session id
*/
if (unlikely(cm->flags & SFE_IPV6_CONNECTION_MATCH_FLAG_PPPOE_DECAP)) {
@@ -335,32 +345,21 @@
/*
* 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))) {
+ if (unlikely(!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_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)));
+ __skb_push(skb, PPPOE_SES_HLEN);
this_cpu_inc(si->stats_pcpu->pppoe_bridge_packets_forwarded64);
}
/*
- * Check if skb has enough headroom to write L2 headers
- */
- if (unlikely(skb_headroom(skb) < cm->l2_hdr_size)) {
- rcu_read_unlock();
- DEBUG_WARN("%px: Not enough headroom: %u\n", skb, skb_headroom(skb));
- sfe_ipv6_exception_stats_inc(si, SFE_IPV6_EXCEPTION_EVENT_NO_HEADROOM);
- return 0;
- }
-
- /*
* From this point on we're good to modify the packet.
*/
diff --git a/sfe_pppoe.c b/sfe_pppoe.c
index 770a77d..0944a87 100644
--- a/sfe_pppoe.c
+++ b/sfe_pppoe.c
@@ -28,6 +28,38 @@
#include "sfe_pppoe.h"
/*
+ * sfe_pppoe_br_accel_mode controls how to accelerate PPPoE bridge flow.
+ * - SFE_PPPOE_BR_ACCEL_MODE_EN_5T: 5-tuple (src_ip, dest_ip, src_port, dest_port, protocol) acceleration
+ * - SFE_PPPOE_BR_ACCEL_MODE_EN_3T: 3-tuple (src_ip, dest_ip, PPPoE session id) acceleration
+ * - SFE_PPPOE_BR_ACCEL_MODE_DISABLED: No acceleration
+ */
+static sfe_pppoe_br_accel_mode_t sfe_pppoe_br_accel_mode __read_mostly = SFE_PPPOE_BR_ACCEL_MODE_EN_5T;
+
+/*
+ * sfe_pppoe_get_br_accel_mode()
+ * Gets PPPoE bridge acceleration mode
+ */
+sfe_pppoe_br_accel_mode_t sfe_pppoe_get_br_accel_mode(void)
+{
+ return sfe_pppoe_br_accel_mode;
+}
+EXPORT_SYMBOL(sfe_pppoe_get_br_accel_mode);
+
+/*
+ * sfe_pppoe_set_br_accel_mode()
+ * Sets PPPoE bridge acceleration mode
+ */
+int sfe_pppoe_set_br_accel_mode(sfe_pppoe_br_accel_mode_t mode)
+{
+ if (mode >= SFE_PPPOE_BR_ACCEL_MODE_MAX) {
+ return -1;
+ }
+
+ sfe_pppoe_br_accel_mode = mode;
+ return 0;
+}
+
+/*
* sfe_pppoe_add_header()
* Add PPPoE header.
*
diff --git a/sfe_pppoe.h b/sfe_pppoe.h
index cb0af1e..8329e30 100644
--- a/sfe_pppoe.h
+++ b/sfe_pppoe.h
@@ -27,13 +27,9 @@
u16 protocol;
};
-/*
- * PPPoE (6-byte) + PPP (2-byte) header.
- */
-#define SFE_PPPOE_SESSION_HEADER_SIZE 8
-
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);
+int sfe_pppoe_set_br_accel_mode(sfe_pppoe_br_accel_mode_t mode);
#endif /* __SFE_PPPOE_H */
diff --git a/sfe_pppoe_mgr.c b/sfe_pppoe_mgr.c
new file mode 100644
index 0000000..6422966
--- /dev/null
+++ b/sfe_pppoe_mgr.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2022, 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/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/rwlock_types.h>
+#include <linux/hashtable.h>
+#include <linux/inetdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_pppox.h>
+#include "sfe_pppoe_mgr.h"
+#include "sfe_debug.h"
+
+#define HASH_BUCKET_SIZE 2 /* ( 2^ HASH_BUCKET_SIZE ) == 4 */
+
+static DEFINE_HASHTABLE(pppoe_session_table, HASH_BUCKET_SIZE);
+
+/*
+ * sfe_pppoe_mgr_get_session_info()
+ * Retrieve PPPoE session info associated with this netdevice
+ */
+static bool sfe_pppoe_mgr_get_session_info(struct net_device *dev, struct pppoe_opt *addressing)
+{
+ struct ppp_channel *channel[1] = {NULL};
+ int px_proto;
+ int ppp_ch_count;
+
+ if (ppp_is_multilink(dev)) {
+ DEBUG_WARN("%s: channel is multilink PPP\n", dev->name);
+ return false;
+ }
+
+ ppp_ch_count = ppp_hold_channels(dev, channel, 1);
+ DEBUG_INFO("%s: PPP hold channel ret %d\n", dev->name, ppp_ch_count);
+ if (ppp_ch_count != 1) {
+ DEBUG_WARN("%s: hold channel for netdevice %px failed\n", dev->name, dev);
+ return false;
+ }
+
+ px_proto = ppp_channel_get_protocol(channel[0]);
+ if (px_proto != PX_PROTO_OE) {
+ DEBUG_WARN("%s: session socket is not of type PX_PROTO_OE\n", dev->name);
+ ppp_release_channels(channel, 1);
+ return false;
+ }
+
+ if (pppoe_channel_addressing_get(channel[0], addressing)) {
+ DEBUG_WARN("%s: failed to get addressing information\n", dev->name);
+ ppp_release_channels(channel, 1);
+ return false;
+ }
+
+ DEBUG_TRACE("dev=%px %s %d: opt_dev=%px opt_dev_name=%s opt_dev_ifindex=%d opt_ifindex=%d\n",
+ dev, dev->name, dev->ifindex,
+ addressing->dev, addressing->dev->name, addressing->dev->ifindex, addressing->ifindex);
+
+ /*
+ * pppoe_channel_addressing_get returns held device.
+ * So, put it back here.
+ */
+ dev_put(addressing->dev);
+ ppp_release_channels(channel, 1);
+ return true;
+}
+
+/*
+ * sfe_pppoe_mgr_remove_session()
+ * Remove PPPoE session entry from hash table
+ */
+static void sfe_pppoe_mgr_remove_session(struct sfe_pppoe_mgr_session_entry *entry)
+{
+ struct sfe_pppoe_mgr_session_info *info;
+ info = &entry->info;
+
+ DEBUG_INFO("%px %s %d: Remove PPPoE session entry with session_id=%u server_mac=%pM\n",
+ entry, entry->dev->name, entry->dev->ifindex,
+ info->session_id, info->server_mac);
+
+ hash_del_rcu(&entry->hash_list);
+ synchronize_rcu();
+ kfree(entry);
+}
+
+/*
+ * sfe_pppoe_mgr_add_session()
+ * Create a PPPoE session entry and add it into hash table
+ */
+static struct sfe_pppoe_mgr_session_entry *sfe_pppoe_mgr_add_session(struct net_device *dev, struct pppoe_opt *opt)
+
+{
+ struct sfe_pppoe_mgr_session_entry *entry;
+ struct sfe_pppoe_mgr_session_info *info;
+
+ entry = kzalloc(sizeof(struct sfe_pppoe_mgr_session_entry), GFP_KERNEL);
+ if (!entry) {
+ DEBUG_WARN("%px: failed to allocate pppoe session entry\n", dev);
+ return NULL;
+ }
+
+ info = &entry->info;
+
+ /*
+ * Save session info
+ */
+ info->session_id = (uint16_t)ntohs((uint16_t)opt->pa.sid);
+ ether_addr_copy(info->server_mac, opt->pa.remote);
+
+ entry->dev = dev;
+
+ /*
+ * There is no need for protecting simultaneous addition &
+ * deletion of pppoe sesion entry as the PPP notifier chain
+ * call back is called with mutex lock.
+ */
+ hash_add_rcu(pppoe_session_table,
+ &entry->hash_list,
+ dev->ifindex);
+
+ DEBUG_INFO("%px %s %d: Add PPPoE session entry with session_id=%u server_mac=%pM\n",
+ entry, dev->name, dev->ifindex,
+ info->session_id, info->server_mac);
+
+ return entry;
+}
+
+/*
+ * sfe_pppoe_mgr_disconnect()
+ * PPPoE interface's disconnect event handler
+ */
+static int sfe_pppoe_mgr_disconnect(struct net_device *dev)
+{
+ struct sfe_pppoe_mgr_session_entry *entry;
+ struct sfe_pppoe_mgr_session_entry *found = NULL;
+ struct hlist_node *temp;
+ /*
+ * check whether the interface is of type PPP
+ */
+ if (dev->type != ARPHRD_PPP || !(dev->flags & IFF_POINTOPOINT)) {
+ return NOTIFY_DONE;
+ }
+
+ hash_for_each_possible_safe(pppoe_session_table, entry,
+ temp, hash_list, dev->ifindex) {
+ if (entry->dev != dev) {
+ continue;
+ }
+
+ /*
+ * In the hash list, there must be only one entry match with this net device.
+ */
+ found = entry;
+ break;
+ }
+
+ if (!found) {
+ DEBUG_WARN("%px: PPPoE session is not found for %s\n", dev, dev->name);
+ return NOTIFY_DONE;
+ }
+
+ /*
+ * Remove entry from hash table
+ */
+ sfe_pppoe_mgr_remove_session(found);
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * sfe_pppoe_mgr_connect()
+ * PPPoE interface's connect event handler
+ */
+static int sfe_pppoe_mgr_connect(struct net_device *dev)
+{
+ struct pppoe_opt opt;
+ struct sfe_pppoe_mgr_session_entry *entry;
+
+ /*
+ * check whether the interface is of type PPP
+ */
+ if (dev->type != ARPHRD_PPP || !(dev->flags & IFF_POINTOPOINT)) {
+ return NOTIFY_DONE;
+ }
+
+ if (sfe_pppoe_mgr_get_session_info(dev, &opt) == false) {
+ DEBUG_WARN("%px: Unable to get pppoe session info from %s\n", dev, dev->name);
+ return NOTIFY_DONE;
+ }
+
+ /*
+ * Create an session entry and add it to hash table
+ */
+ entry = sfe_pppoe_mgr_add_session(dev, &opt);
+ if (!entry) {
+ DEBUG_WARN("%s: PPPoE session add failed\n", dev->name);
+ }
+
+ return NOTIFY_DONE;
+}
+
+/*
+ * sfe_pppoe_mgr_channel_notifier_handler()
+ * PPPoE channel notifier handler.
+ */
+static int sfe_pppoe_mgr_channel_notifier_handler(struct notifier_block *nb,
+ unsigned long event,
+ void *arg)
+{
+ struct net_device *dev = (struct net_device *)arg;
+
+ switch (event) {
+ case PPP_CHANNEL_CONNECT:
+ DEBUG_INFO("%s: PPP_CHANNEL_CONNECT event\n", dev->name);
+ return sfe_pppoe_mgr_connect(dev);
+
+ case PPP_CHANNEL_DISCONNECT:
+ DEBUG_INFO("%s: PPP_CHANNEL_DISCONNECT event\n", dev->name);
+ return sfe_pppoe_mgr_disconnect(dev);
+
+ default:
+ DEBUG_INFO("%s: Unhandled channel event: %lu\n", dev->name, event);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
+struct notifier_block sfe_pppoe_mgr_channel_notifier_nb = {
+ .notifier_call = sfe_pppoe_mgr_channel_notifier_handler,
+};
+
+/*
+ * sfe_pppoe_mgr_find_session()
+ * Find pppoe session entry given session ID and server MAC
+ */
+bool sfe_pppoe_mgr_find_session(uint16_t session_id, uint8_t *server_mac)
+{
+ struct sfe_pppoe_mgr_session_entry *entry;
+ struct sfe_pppoe_mgr_session_info *info;
+ struct hlist_node *temp;
+ int bkt;
+
+ hash_for_each_safe(pppoe_session_table, bkt, temp, entry, hash_list) {
+ info = &entry->info;
+ if ((uint16_t)info->session_id == session_id &&
+ ether_addr_equal(info->server_mac, server_mac)) {
+
+ return true;
+ }
+ }
+
+ DEBUG_INFO("PPPoE session entry not found: session_id %d server_mac %pM\n", session_id, server_mac);
+
+ return false;
+}
+
+/*
+ * sfe_pppoe_mgr_exit
+ * PPPoE mgr exit function
+ */
+void sfe_pppoe_mgr_exit(void)
+{
+ struct sfe_pppoe_mgr_session_entry *entry;
+ struct hlist_node *temp;
+ int bkt;
+
+ /*
+ * Unregister the module from the PPP channel events.
+ */
+ ppp_channel_connection_unregister_notify(&sfe_pppoe_mgr_channel_notifier_nb);
+
+ hash_for_each_safe(pppoe_session_table, bkt, temp, entry, hash_list) {
+ sfe_pppoe_mgr_remove_session(entry);
+ }
+}
+
+/*
+ * sfe_pppoe_mgr_init()
+ * PPPoE mgr init function
+ */
+int sfe_pppoe_mgr_init(void)
+{
+ /*
+ * Register the module to the PPP channel events.
+ */
+ ppp_channel_connection_register_notify(&sfe_pppoe_mgr_channel_notifier_nb);
+ return 0;
+}
diff --git a/sfe_pppoe_mgr.h b/sfe_pppoe_mgr.h
new file mode 100644
index 0000000..f909660
--- /dev/null
+++ b/sfe_pppoe_mgr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2022, 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.
+ */
+
+/*
+ * sfe_pppoe_mgr.h
+ * SFE PPPoE mgr definitions
+ */
+
+#ifndef _SFE_PPPOE_MGR_H_
+#define _SFE_PPPOE_MGR_H_
+
+/*
+ * struct sfe_pppoe_mgr_session_info
+ * Structure for PPPoE client driver session info
+ */
+struct sfe_pppoe_mgr_session_info {
+ uint32_t session_id; /* PPPoE Session ID */
+ uint8_t server_mac[ETH_ALEN]; /* PPPoE server's MAC address */
+};
+
+/*
+ * struct sfe_pppoe_mgr_session_entry
+ * Structure for PPPoE session entry into HASH table
+ */
+struct sfe_pppoe_mgr_session_entry {
+ struct sfe_pppoe_mgr_session_info info;
+ /* Session information */
+ struct net_device *dev; /* Net device */
+ struct hlist_node hash_list; /* Hash list for sessions */
+};
+
+bool sfe_pppoe_mgr_find_session(uint16_t session_id, uint8_t *server_mac);
+int sfe_pppoe_mgr_init(void);
+void sfe_pppoe_mgr_exit(void);
+
+#endif