All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dave Seddon <dave.seddon.ca@gmail.com>
To: netdev@vger.kernel.org
Cc: "David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Stanislav Fomichev <sdf@fomichev.me>,
	Tom Herbert <tom@herbertland.com>,
	Willem de Bruijn <willemdebruijn.kernel@gmail.com>,
	linux-kernel@vger.kernel.org,
	Dave Seddon <dave.seddon.ca@gmail.com>
Subject: [PATCH net-next v1 04/11] net: flow_dissector: add fast-path for PPPoE session + IPv{4,6} + TCP/UDP
Date: Wed, 15 Jul 2026 17:43:50 -0700	[thread overview]
Message-ID: <20260716004357.3652679-5-dave.seddon.ca@gmail.com> (raw)
In-Reply-To: <20260716004357.3652679-1-dave.seddon.ca@gmail.com>

Adds flow_dissect_fast_pppoe() — a straight-line extractor for PPPoE
session frames (ETH_P_PPP_SES, EtherType 0x8864 per RFC 2516) carrying
IPv4 or IPv6 inside PPP. Strips the 6-byte pppoe_hdr + 2-byte PPP
protocol field, writes FLOW_DISSECTOR_KEY_PPPOE (session_id, ppp_proto,
type) when requested, and tail-calls into the eth_ip fast-path for
the inner IPv4 or IPv6 + TCP/UDP.

Byte-identical with the slow path's `case htons(ETH_P_PPP_SES)` block
for the eligible shape (RFC-2516-valid header + PPP_IP / PPP_IPV6 +
straight-line IP). On any miss — invalid header, PFC-compressed PPP
proto, PPP_MPLS_UC / PPP_MPLS_MC (defer until MPLS fast-path lands),
LCP / IPCP / other control frames — returns false so the dispatcher
falls through to the slow path. Output is exactly what the slow path
would produce.

Why this matters: PPPoE is the dominant L2 over millions of ISP last-
mile DSL/FTTH links, and the existing slow-path PPPoE block does a
__skb_header_pointer + per-field switch on every packet at the
graph-walk entry. The fast-path saves the walk overhead for the
common IPv4 / IPv6 inner case while keeping the slow path as the
escape valve for control / less-common inner protos.

Gated by:
  - new DEFINE_STATIC_KEY_FALSE(flow_dissector_pppoe_key)
  - new sysctl /proc/sys/net/flow_dissector/pppoe (default 0)
  - dispatcher case `htons(ETH_P_PPP_SES)` with
    `static_branch_unlikely(&flow_dissector_pppoe_key)` guard

When the sysctl is 0, the dispatcher hits a forward not-taken JMP —
same per-call cost as the vlan / qinq cases. The new dispatcher
case also doesn't widen the switch table (it adds one case to a
switch the compiler can already lower to a jump table on the
ETH_P_* constants).

Documentation/admin-guide/sysctl/net.rst grows a `pppoe` subsection
under the existing `/proc/sys/net/flow_dissector/` chapter, between
qinq and the next chapter, matching the existing per-shape layout.

Assisted-by: Claude:claude-fable-5 sparse smatch
Signed-off-by: Dave Seddon <dave.seddon.ca@gmail.com>
---
 Documentation/admin-guide/sysctl/net.rst | 18 ++++++
 include/net/flow_dissector.h             |  1 +
 net/core/flow_dissector.c                | 76 ++++++++++++++++++++++++
 3 files changed, 95 insertions(+)

diff --git a/Documentation/admin-guide/sysctl/net.rst b/Documentation/admin-guide/sysctl/net.rst
index 824f4b735caa..1096e6ddca39 100644
--- a/Documentation/admin-guide/sysctl/net.rst
+++ b/Documentation/admin-guide/sysctl/net.rst
@@ -516,6 +516,24 @@ the outer entry is off).
 
 Default: 0
 
+pppoe
+~~~~~
+
+PPPoE session frames (RFC 2516, EtherType ``ETH_P_PPP_SES`` 0x8864)
+carrying IPv4 or IPv6 inside PPP. The fast-path strips the 6-byte
+PPPoE session header + 2-byte PPP protocol field, writes
+FLOW_DISSECTOR_KEY_PPPOE (session_id, ppp_proto, type) when the
+dissector requests it, and tail-calls into the eth_ip fast-path for
+the inner IPv4 / IPv6 + TCP/UDP. Byte-identical with the slow path
+for the eligible shape.
+
+Defers to slow path for: PPP_MPLS_UC / PPP_MPLS_MC (until an MPLS
+fast-path lands), PFC-compressed PPP protocol fields, LCP/IPCP/etc.
+control frames, and any malformed PPPoE header (ver != 1, type != 1,
+code != 0).
+
+Default: 0
+
 3. /proc/sys/net/unix - Parameters for Unix domain sockets
 ----------------------------------------------------------
 
diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h
index 4f36f3889a28..df85162959cc 100644
--- a/include/net/flow_dissector.h
+++ b/include/net/flow_dissector.h
@@ -431,6 +431,7 @@ extern struct flow_dissector flow_keys_basic_dissector;
 extern struct static_key_false flow_dissector_eth_ip_key;
 extern struct static_key_false flow_dissector_vlan_key;
 extern struct static_key_false flow_dissector_qinq_key;
+extern struct static_key_false flow_dissector_pppoe_key;
 
 /* struct flow_keys_digest:
  *
diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c
index 826314e28db0..21a3f5e0ea05 100644
--- a/net/core/flow_dissector.c
+++ b/net/core/flow_dissector.c
@@ -46,6 +46,8 @@ DEFINE_STATIC_KEY_FALSE(flow_dissector_vlan_key);
 EXPORT_SYMBOL(flow_dissector_vlan_key);
 DEFINE_STATIC_KEY_FALSE(flow_dissector_qinq_key);
 EXPORT_SYMBOL(flow_dissector_qinq_key);
+DEFINE_STATIC_KEY_FALSE(flow_dissector_pppoe_key);
+EXPORT_SYMBOL(flow_dissector_pppoe_key);
 
 /* IPv4 version/IHL byte of an option-less header: version 4, IHL 5. */
 #define FLOW_DIS_IPV4_VIHL_NOOPT	0x45
@@ -1333,6 +1335,67 @@ static bool flow_dissect_fast_vlan(const struct sk_buff *skb,
 	}
 }
 
+/* PPPoE session (RFC 2516) + 2-byte PPP protocol: write
+ * FLOW_DISSECTOR_KEY_PPPOE if requested, then tail-call the eth_ip
+ * fast-path for PPP_IP / PPP_IPV6. Anything else (PFC-compressed
+ * protocol, LCP, MPLS) defers.
+ */
+static bool flow_dissect_fast_pppoe(const struct sk_buff *skb,
+				    struct flow_dissector *flow_dissector,
+				    void *target_container,
+				    const void *data,
+				    int nhoff, int hlen)
+{
+	struct flow_dissector_key_pppoe *key_pppoe;
+	const struct {
+		struct pppoe_hdr hdr;
+		__be16 proto;
+	} *hdr;
+	__be16 inner_eth_proto;
+	u16 ppp_proto;
+
+	if (unlikely(hlen - nhoff < (int)sizeof(*hdr)))
+		return false;
+	hdr = (const void *)((const u8 *)data + nhoff);
+	if (!is_pppoe_ses_hdr_valid(&hdr->hdr))
+		return false;
+
+	ppp_proto = ntohs(hdr->proto);
+	switch (ppp_proto) {
+	case PPP_IP:
+		inner_eth_proto = htons(ETH_P_IP);
+		break;
+	case PPP_IPV6:
+		inner_eth_proto = htons(ETH_P_IPV6);
+		break;
+	default:
+		/* MPLS unicast/multicast + everything else (LCP, IPCP,
+		 * compressed PFC, ...) defers to the slow path.
+		 */
+		return false;
+	}
+
+	if (dissector_uses_key(flow_dissector,
+			       FLOW_DISSECTOR_KEY_PPPOE)) {
+		key_pppoe = skb_flow_dissector_target(flow_dissector,
+						      FLOW_DISSECTOR_KEY_PPPOE,
+						      target_container);
+		key_pppoe->session_id = hdr->hdr.sid;
+		key_pppoe->ppp_proto = htons(ppp_proto);
+		key_pppoe->type = htons(ETH_P_PPP_SES);
+	}
+
+	nhoff += PPPOE_SES_HLEN;
+
+	if (inner_eth_proto == htons(ETH_P_IP))
+		return flow_dissect_fast_ipv4(skb, flow_dissector,
+					      target_container, data,
+					      nhoff, hlen);
+	return flow_dissect_fast_ipv6(skb, flow_dissector,
+				      target_container, data,
+				      nhoff, hlen);
+}
+
 /* Top-level dispatcher: eligibility check (only the two standard
  * dissectors and flag subset) + per-proto switch with per-shape
  * static_branch gating. Each case's branch is a forward not-taken JMP
@@ -1380,6 +1443,12 @@ static bool flow_dissect_fast(const struct sk_buff *skb,
 		return flow_dissect_fast_ipv6(skb, flow_dissector,
 					      target_container, data,
 					      nhoff, hlen);
+	case htons(ETH_P_PPP_SES):
+		if (!static_branch_unlikely(&flow_dissector_pppoe_key))
+			return false;
+		return flow_dissect_fast_pppoe(skb, flow_dissector,
+					       target_container, data,
+					       nhoff, hlen);
 	default:
 		return false;
 	}
@@ -2522,6 +2591,13 @@ static struct ctl_table flow_dissector_sysctl_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_set_qinq_key,
 	},
+	{
+		.procname	= "pppoe",
+		.data		= &flow_dissector_pppoe_key.key,
+		.maxlen		= sizeof(flow_dissector_pppoe_key),
+		.mode		= 0644,
+		.proc_handler	= proc_do_static_key,
+	},
 };
 
 static int __init flow_dissector_sysctl_init(void)
-- 
2.54.0


  parent reply	other threads:[~2026-07-16  0:44 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-16  0:43 [PATCH net-next v1 00/11] net: flow_dissector: opt-in byte-identical fast paths for common shapes Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 01/11] net: flow_dissector: gate BPF program lookup behind a static key Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 02/11] net: flow_dissector: opt-in fast-path for eth + IPv{4,6} + {TCP,UDP} Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 03/11] net: flow_dissector: add fast-path for VLAN and QinQ + IP + TCP/UDP Dave Seddon
2026-07-16  0:43 ` Dave Seddon [this message]
2026-07-16  0:43 ` [PATCH net-next v1 05/11] net: flow_dissector: add fast-path for single MPLS label + IP Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 06/11] net: flow_dissector: add fast-path for IP-in-IP family (IPIP / 4in6 / 6in4) Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 07/11] net: flow_dissector: add byte-identical fast-path for plain GRE inner Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 08/11] net: flow_dissector: per-shape counters + /proc/net/flow_dissector_stats Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 09/11] net: flow_dissector: bound fast-path tunnel recursion Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 10/11] net: flow_dissector: add KUnit fast/slow path equivalence tests Dave Seddon
2026-07-16  0:43 ` [PATCH net-next v1 11/11] Documentation: networking: add flow_dissector overview and fast-path guide Dave Seddon
2026-07-16  9:50 ` [PATCH net-next v1 00/11] net: flow_dissector: opt-in byte-identical fast paths for common shapes Willem de Bruijn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260716004357.3652679-5-dave.seddon.ca@gmail.com \
    --to=dave.seddon.ca@gmail.com \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    --cc=tom@herbertland.com \
    --cc=willemdebruijn.kernel@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.