* [PATCH net-next v8 2/2] selftests: net: test PPPoE packets in gro.sh
[not found] <20260501035102.293031-1-qingfang.deng@linux.dev>
@ 2026-05-01 3:50 ` Qingfang Deng
2026-05-06 12:14 ` Simon Horman
2026-05-06 12:14 ` [PATCH net-next v8 1/2] net: pppoe: implement GRO/GSO support Simon Horman
1 sibling, 1 reply; 4+ messages in thread
From: Qingfang Deng @ 2026-05-01 3:50 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Shuah Khan, Simon Horman, Willem de Bruijn,
Petr Machata, Anubhav Singh, Richard Gobert, netdev,
linux-kselftest, linux-kernel
Cc: linux-ppp, Qingfang Deng
Add PPPoE test-cases to the GRO selftest. Only run a subset of
common_tests to avoid changing the hardcoded L3 offsets everywhere.
Add a new "pppoe_sid" test case to verify that packets with different
PPPoE session IDs are correctly identified as separate flows and not
coalesced.
Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev>
---
v8:
- add a new macro L2_HLEN_MAX for PPPoE
v7: https://lore.kernel.org/netdev/20260428064717.74794-2-qingfang.deng@linux.dev
- Do not run all the tests for PPPoE
- Add a new test for PPPoE
v6: https://lore.kernel.org/netdev/20260326081127.61229-2-dqfext@gmail.com
tools/testing/selftests/drivers/net/config | 2 +
tools/testing/selftests/drivers/net/gro.py | 11 +++
tools/testing/selftests/net/lib/gro.c | 100 +++++++++++++++++----
3 files changed, 97 insertions(+), 16 deletions(-)
diff --git a/tools/testing/selftests/drivers/net/config b/tools/testing/selftests/drivers/net/config
index fd16994366f4..07e386895b94 100644
--- a/tools/testing/selftests/drivers/net/config
+++ b/tools/testing/selftests/drivers/net/config
@@ -8,5 +8,7 @@ CONFIG_NETCONSOLE=m
CONFIG_NETCONSOLE_DYNAMIC=y
CONFIG_NETCONSOLE_EXTENDED_LOG=y
CONFIG_NETDEVSIM=m
+CONFIG_PPP=y
+CONFIG_PPPOE=y
CONFIG_VLAN_8021Q=m
CONFIG_XDP_SOCKETS=y
diff --git a/tools/testing/selftests/drivers/net/gro.py b/tools/testing/selftests/drivers/net/gro.py
index 221f27e57147..ad7c80f7ba96 100755
--- a/tools/testing/selftests/drivers/net/gro.py
+++ b/tools/testing/selftests/drivers/net/gro.py
@@ -313,6 +313,12 @@ def _gro_variants():
"ip_frag6", "ip_v6ext_same", "ip_v6ext_diff",
]
+ # Tests specific to PPPoE
+ pppoe_tests = [
+ "data_same", "data_lrg_sml", "data_sml_lrg", "data_lrg_1byte",
+ "data_burst", "pppoe_sid",
+ ]
+
for mode in ["sw", "hw", "lro"]:
for protocol in ["ipv4", "ipv6", "ipip", "ip6ip6"]:
for test_name in common_tests:
@@ -325,6 +331,11 @@ def _gro_variants():
for test_name in ipv6_tests:
yield mode, protocol, test_name
+ for mode in ["sw"]:
+ for protocol in ["pppoev4", "pppoev6"]:
+ for test_name in pppoe_tests:
+ yield mode, protocol, test_name
+
@ksft_variants(_gro_variants())
def test(cfg, mode, protocol, test_name):
diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
index 11b16ae5f0e8..4ffb0491c0da 100644
--- a/tools/testing/selftests/net/lib/gro.c
+++ b/tools/testing/selftests/net/lib/gro.c
@@ -67,12 +67,14 @@
#include <errno.h>
#include <error.h>
#include <getopt.h>
+#include <net/ethernet.h>
+#include <net/if.h>
#include <linux/filter.h>
#include <linux/if_packet.h>
+#include <linux/if_pppox.h>
#include <linux/ipv6.h>
#include <linux/net_tstamp.h>
-#include <net/ethernet.h>
-#include <net/if.h>
+#include <linux/ppp_defs.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
@@ -102,6 +104,7 @@
#define MAX_LARGE_PKT_CNT ((IP_MAXPACKET - (MAX_HDR_LEN - ETH_HLEN)) / \
(ASSUMED_MTU - (MAX_HDR_LEN - ETH_HLEN)))
#define MIN_EXTHDR_SIZE 8
+#define L2_HLEN_MAX (ETH_HLEN + PPPOE_SES_HLEN)
#define EXT_PAYLOAD_1 "\x00\x00\x00\x00\x00\x00"
#define EXT_PAYLOAD_2 "\x11\x11\x11\x11\x11\x11"
@@ -134,6 +137,7 @@ static int total_hdr_len = -1;
static int ethhdr_proto = -1;
static bool ipip;
static bool ip6ip6;
+static bool pppoe;
static uint64_t txtime_ns;
static int num_flows = 4;
static bool order_check;
@@ -171,6 +175,22 @@ static void vlog(const char *fmt, ...)
}
}
+static void fill_pppoelayer(void *buf, int payload_len, uint16_t sid)
+{
+ struct pppoe_ppp_hdr {
+ struct pppoe_hdr eh;
+ __be16 proto;
+ } *ph = buf;
+
+ payload_len += sizeof(struct tcphdr);
+ ph->eh.type = 1;
+ ph->eh.ver = 1;
+ ph->eh.code = 0;
+ ph->eh.sid = htons(sid);
+ ph->eh.length = htons(payload_len + sizeof(ph->proto));
+ ph->proto = htons(proto == PF_INET ? PPP_IP : PPP_IPV6);
+}
+
static void setup_sock_filter(int fd)
{
const int dport_off = tcp_offset + offsetof(struct tcphdr, dest);
@@ -412,11 +432,15 @@ static void create_packet(void *buf, int seq_offset, int ack_offset,
fill_networklayer(buf + inner_ip_off, payload_len, IPPROTO_TCP);
if (inner_ip_off > ETH_HLEN) {
- int encap_proto = (proto == PF_INET) ?
- IPPROTO_IPIP : IPPROTO_IPV6;
+ if (pppoe) {
+ fill_pppoelayer(buf + ETH_HLEN, payload_len + ip_hdr_len, 0x1234);
+ } else {
+ int encap_proto = (proto == PF_INET) ?
+ IPPROTO_IPIP : IPPROTO_IPV6;
- fill_networklayer(buf + ETH_HLEN,
- payload_len + ip_hdr_len, encap_proto);
+ fill_networklayer(buf + ETH_HLEN,
+ payload_len + ip_hdr_len, encap_proto);
+ }
}
fill_datalinklayer(buf);
@@ -526,7 +550,7 @@ static void send_flags(int fd, struct sockaddr_ll *daddr, int psh, int syn,
static void send_data_pkts(int fd, struct sockaddr_ll *daddr,
int payload_len1, int payload_len2)
{
- static char buf[ETH_HLEN + IP_MAXPACKET];
+ static char buf[L2_HLEN_MAX + IP_MAXPACKET];
create_packet(buf, 0, 0, payload_len1, 0);
write_packet(fd, buf, total_hdr_len + payload_len1, daddr);
@@ -1071,6 +1095,20 @@ static void send_fragment6(int fd, struct sockaddr_ll *daddr)
write_packet(fd, buf, bufpkt_len, daddr);
}
+static void send_changed_pppoe_sid(int fd, struct sockaddr_ll *daddr)
+{
+ static char buf[L2_HLEN_MAX + PAYLOAD_LEN];
+ int pkt_size = total_hdr_len + PAYLOAD_LEN;
+ struct pppoe_hdr *hdr = (struct pppoe_hdr *)(buf + ETH_HLEN);
+
+ create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
+ write_packet(fd, buf, pkt_size, daddr);
+
+ create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
+ hdr->sid = htons(0x4321);
+ write_packet(fd, buf, pkt_size, daddr);
+}
+
static void bind_packetsocket(int fd)
{
struct sockaddr_ll daddr = {};
@@ -1121,9 +1159,10 @@ static void recv_error(int fd, int rcv_errno)
static void check_recv_pkts(int fd, int *correct_payload,
int correct_num_pkts)
{
- static char buffer[IP_MAXPACKET + ETH_HLEN + 1];
- struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN);
- struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN);
+ static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1];
+ int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0);
+ struct iphdr *iph = (struct iphdr *)(buffer + nhoff);
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff);
struct tcphdr *tcph;
bool bad_packet = false;
int tcp_ext_len = 0;
@@ -1140,7 +1179,7 @@ static void check_recv_pkts(int fd, int *correct_payload,
while (1) {
ip_ext_len = 0;
- pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);
+ pkt_size = recv(fd, buffer, sizeof(buffer), 0);
if (pkt_size < 0)
recv_error(fd, errno);
@@ -1183,9 +1222,10 @@ static void check_recv_pkts(int fd, int *correct_payload,
static void check_capacity_pkts(int fd)
{
- static char buffer[IP_MAXPACKET + ETH_HLEN + 1];
- struct iphdr *iph = (struct iphdr *)(buffer + ETH_HLEN);
- struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + ETH_HLEN);
+ static char buffer[IP_MAXPACKET + L2_HLEN_MAX + 1];
+ int nhoff = ETH_HLEN + (pppoe ? PPPOE_SES_HLEN : 0);
+ struct iphdr *iph = (struct iphdr *)(buffer + nhoff);
+ struct ipv6hdr *ip6h = (struct ipv6hdr *)(buffer + nhoff);
int num_pkt = 0, num_coal = 0, pkt_idx;
const char *fail_reason = NULL;
int flow_order[num_flows * 2];
@@ -1203,7 +1243,7 @@ static void check_capacity_pkts(int fd)
while (1) {
ip_ext_len = 0;
- pkt_size = recv(fd, buffer, IP_MAXPACKET + ETH_HLEN + 1, 0);
+ pkt_size = recv(fd, buffer, sizeof(buffer), 0);
if (pkt_size < 0)
recv_error(fd, errno);
@@ -1499,6 +1539,12 @@ static void gro_sender(void)
usleep(fin_delay_us);
write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+ /* PPPoE sub-tests */
+ } else if (strcmp(testname, "pppoe_sid") == 0) {
+ send_changed_pppoe_sid(txfd, &daddr);
+ usleep(fin_delay_us);
+ write_packet(txfd, fin_pkt, total_hdr_len, &daddr);
+
} else {
error(1, 0, "Unknown testcase: %s", testname);
}
@@ -1716,6 +1762,12 @@ static void gro_receiver(void)
} else if (strcmp(testname, "capacity") == 0) {
check_capacity_pkts(rxfd);
+ } else if (strcmp(testname, "pppoe_sid") == 0) {
+ correct_payload[0] = PAYLOAD_LEN;
+ correct_payload[1] = PAYLOAD_LEN;
+ printf("different PPPoE session ID doesn't coalesce: ");
+ check_recv_pkts(rxfd, correct_payload, 2);
+
} else {
error(1, 0, "Test case error: unknown testname %s", testname);
}
@@ -1734,6 +1786,8 @@ static void parse_args(int argc, char **argv)
{ "ipv6", no_argument, NULL, '6' },
{ "ipip", no_argument, NULL, 'e' },
{ "ip6ip6", no_argument, NULL, 'E' },
+ { "pppoev4", no_argument, NULL, 'p' },
+ { "pppoev6", no_argument, NULL, 'P' },
{ "num-flows", required_argument, NULL, 'n' },
{ "rx", no_argument, NULL, 'r' },
{ "saddr", required_argument, NULL, 's' },
@@ -1745,7 +1799,7 @@ static void parse_args(int argc, char **argv)
};
int c;
- while ((c = getopt_long(argc, argv, "46d:D:eEi:n:rs:S:t:ov", opts, NULL)) != -1) {
+ while ((c = getopt_long(argc, argv, "46d:D:eEi:n:pPrs:S:t:ov", opts, NULL)) != -1) {
switch (c) {
case '4':
proto = PF_INET;
@@ -1765,6 +1819,16 @@ static void parse_args(int argc, char **argv)
proto = PF_INET6;
ethhdr_proto = htons(ETH_P_IPV6);
break;
+ case 'p':
+ pppoe = true;
+ proto = PF_INET;
+ ethhdr_proto = htons(ETH_P_PPP_SES);
+ break;
+ case 'P':
+ pppoe = true;
+ proto = PF_INET6;
+ ethhdr_proto = htons(ETH_P_PPP_SES);
+ break;
case 'd':
addr4_dst = addr6_dst = optarg;
break;
@@ -1812,6 +1876,10 @@ int main(int argc, char **argv)
} else if (ip6ip6) {
tcp_offset = ETH_HLEN + sizeof(struct ipv6hdr) * 2;
total_hdr_len = tcp_offset + sizeof(struct tcphdr);
+ } else if (pppoe) {
+ tcp_offset = ETH_HLEN + PPPOE_SES_HLEN +
+ (proto == PF_INET ? sizeof(struct iphdr) : sizeof(struct ipv6hdr));
+ total_hdr_len = tcp_offset + sizeof(struct tcphdr);
} else if (proto == PF_INET) {
tcp_offset = ETH_HLEN + sizeof(struct iphdr);
total_hdr_len = tcp_offset + sizeof(struct tcphdr);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v8 1/2] net: pppoe: implement GRO/GSO support
[not found] <20260501035102.293031-1-qingfang.deng@linux.dev>
2026-05-01 3:50 ` [PATCH net-next v8 2/2] selftests: net: test PPPoE packets in gro.sh Qingfang Deng
@ 2026-05-06 12:14 ` Simon Horman
2026-05-07 3:36 ` Qingfang Deng
1 sibling, 1 reply; 4+ messages in thread
From: Simon Horman @ 2026-05-06 12:14 UTC (permalink / raw)
To: qingfang.deng
Cc: 'Simon Horman', andrew+netdev, davem, edumazet, kuba,
pabeni, shuah, willemb, petrm, anubhavsinggh, richardbgobert,
netdev, linux-kselftest, linux-kernel, linux-ppp
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
net: pppoe: implement GRO/GSO support
This commit adds GRO and GSO support for PPPoE, which improves rx throughput
significantly by handling packets where the pppoe header length field matches
the exact packet length.
> diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
> index bdd61c504a1c4..363204e0c49a1 100644
> --- a/drivers/net/ppp/pppoe.c
> +++ b/drivers/net/ppp/pppoe.c
[ ... ]
> @@ -1103,6 +1104,164 @@ static struct pernet_operations pppoe_net_ops = {
> .size = sizeof(struct pppoe_net),
> };
>
> +static u16
> +compare_pppoe_header(const struct pppoe_hdr *phdr,
> + const struct pppoe_hdr *phdr2)
> +{
> + __be16 proto = *(const __be16 *)(phdr + 1);
Because struct pppoe_hdr is packed, the compiler assigns it 1-byte alignment
semantics, which phdr + 1 inherits.
Does explicitly casting this to __be16 * bypass the compiler's unaligned access
protections?
If skb->data has an odd alignment from the NIC, this could trigger a fatal
unaligned memory access exception on strict-alignment architectures. Would it
be safer to use get_unaligned() here?
> + __be16 proto2 = *(const __be16 *)(phdr2 + 1);
> +
> + return (__force u16)((phdr->sid ^ phdr2->sid) | (proto ^ proto2));
> +}
> +
> +static __be16 pppoe_hdr_proto(const struct pppoe_hdr *phdr)
> +{
> + __be16 proto = *(const __be16 *)(phdr + 1);
Could this also result in an unaligned access regression for the same reason?
[ ... ]
> +static struct sk_buff *pppoe_gro_receive(struct list_head *head,
> + struct sk_buff *skb)
> +{
[ ... ]
> + off_pppoe = skb_gro_offset(skb);
> + hlen = off_pppoe + PPPOE_SES_HLEN;
> + phdr = skb_gro_header(skb, hlen, off_pppoe);
> + if (unlikely(!phdr))
> + goto out;
> +
> + /* filter for session packets (type:1, ver:1, code:0) */
> + if (*(const __be16 *)phdr != cpu_to_be16(0x1100))
Is it possible this cast could similarly trigger an unaligned access exception
if the header offset is oddly aligned?
[ ... ]
> +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb,
> + netdev_features_t features)
> +{
[ ... ]
> + segs = ptype->callbacks.gso_segment(skb, features);
> + if (IS_ERR_OR_NULL(segs)) {
> + skb_gso_error_unwind(skb, orig_type, PPPOE_SES_HLEN, mac_offset,
> + mac_len);
If the inner protocol's gso_segment callback returns NULL (indicating the
hardware can natively offload the segmentation), skb_gso_error_unwind() will
be called.
skb_gso_error_unwind() unconditionally sets skb->encapsulation = 1. However,
PPPoE does not configure the inner header offsets (such as
skb->inner_network_header).
When the driver sees skb->encapsulation == 1, could it attempt to read the
uninitialized inner header offsets, parsing the outer MAC header as an inner
IP header and misprogramming the hardware TSO context?
Does PPPoE need to explicitly clear TSO features before calling the inner
segmentation to force software segmentation, avoiding the need for
skb_gso_error_unwind() here entirely?
> + goto out;
> + }
[ ... ]
--
pw-bot: changes-requested
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v8 2/2] selftests: net: test PPPoE packets in gro.sh
2026-05-01 3:50 ` [PATCH net-next v8 2/2] selftests: net: test PPPoE packets in gro.sh Qingfang Deng
@ 2026-05-06 12:14 ` Simon Horman
0 siblings, 0 replies; 4+ messages in thread
From: Simon Horman @ 2026-05-06 12:14 UTC (permalink / raw)
To: qingfang.deng
Cc: 'Simon Horman', andrew+netdev, davem, edumazet, kuba,
pabeni, shuah, willemb, petrm, anubhavsinggh, richardbgobert,
netdev, linux-kselftest, linux-kernel, linux-ppp
From: 'Simon Horman' <horms@kernel.org>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
selftests: net: test PPPoE packets in gro.sh
This commit adds PPPoE test-cases to the GRO selftest and a new "pppoe_sid"
test case to verify that packets with different PPPoE session IDs are correctly
identified as separate flows.
> diff --git a/tools/testing/selftests/net/lib/gro.c b/tools/testing/selftests/net/lib/gro.c
> index 11b16ae5f0e83..4ffb0491c0da7 100644
> --- a/tools/testing/selftests/net/lib/gro.c
> +++ b/tools/testing/selftests/net/lib/gro.c
[ ... ]
> @@ -1071,6 +1095,20 @@ static void send_fragment6(int fd, struct sockaddr_ll *daddr)
> write_packet(fd, buf, bufpkt_len, daddr);
> }
>
> +static void send_changed_pppoe_sid(int fd, struct sockaddr_ll *daddr)
> +{
> + static char buf[L2_HLEN_MAX + PAYLOAD_LEN];
> + int pkt_size = total_hdr_len + PAYLOAD_LEN;
> + struct pppoe_hdr *hdr = (struct pppoe_hdr *)(buf + ETH_HLEN);
> +
> + create_packet(buf, 0, 0, PAYLOAD_LEN, 0);
Will this write past the end of the buffer?
When create_packet() is called, it writes total_hdr_len + PAYLOAD_LEN bytes:
tools/testing/selftests/net/lib/gro.c:create_packet() {
...
memset(buf, 0, total_hdr_len);
memset(buf + total_hdr_len, 'a', payload_len);
...
}
However, buf is only sized for L2_HLEN_MAX + PAYLOAD_LEN. L2_HLEN_MAX
evaluates to 22 bytes (ETH_HLEN + PPPOE_SES_HLEN). For IPv6 PPPoE packets,
total_hdr_len can be up to 82 bytes (14 ETH + 8 PPPoE + 40 IPv6 + 20 TCP).
Does this result in an out-of-bounds write of 60 bytes when setting up the
IPv6 test case?
> + write_packet(fd, buf, pkt_size, daddr);
> +
> + create_packet(buf, PAYLOAD_LEN, 0, PAYLOAD_LEN, 0);
> + hdr->sid = htons(0x4321);
> + write_packet(fd, buf, pkt_size, daddr);
> +}
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH net-next v8 1/2] net: pppoe: implement GRO/GSO support
2026-05-06 12:14 ` [PATCH net-next v8 1/2] net: pppoe: implement GRO/GSO support Simon Horman
@ 2026-05-07 3:36 ` Qingfang Deng
0 siblings, 0 replies; 4+ messages in thread
From: Qingfang Deng @ 2026-05-07 3:36 UTC (permalink / raw)
To: Simon Horman
Cc: andrew+netdev, davem, edumazet, kuba, pabeni, shuah, willemb,
petrm, anubhavsinggh, richardbgobert, netdev, linux-kselftest,
linux-kernel, linux-ppp, Pablo Neira Ayuso
On 2026/5/6 20:14, Simon Horman wrote:
> From: 'Simon Horman' <horms@kernel.org>
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> ---
> net: pppoe: implement GRO/GSO support
>
> This commit adds GRO and GSO support for PPPoE, which improves rx throughput
> significantly by handling packets where the pppoe header length field matches
> the exact packet length.
>
>> diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
>> index bdd61c504a1c4..363204e0c49a1 100644
>> --- a/drivers/net/ppp/pppoe.c
>> +++ b/drivers/net/ppp/pppoe.c
> [ ... ]
>> @@ -1103,6 +1104,164 @@ static struct pernet_operations pppoe_net_ops = {
>> .size = sizeof(struct pppoe_net),
>> };
>>
>> +static u16
>> +compare_pppoe_header(const struct pppoe_hdr *phdr,
>> + const struct pppoe_hdr *phdr2)
>> +{
>> + __be16 proto = *(const __be16 *)(phdr + 1);
> Because struct pppoe_hdr is packed, the compiler assigns it 1-byte alignment
> semantics, which phdr + 1 inherits.
>
> Does explicitly casting this to __be16 * bypass the compiler's unaligned access
> protections?
>
> If skb->data has an odd alignment from the NIC, this could trigger a fatal
> unaligned memory access exception on strict-alignment architectures. Would it
> be safer to use get_unaligned() here?
You can disregard all the alignment warnings. On those architectures,
drivers are required to 4-byte align the network header, so the PPPoE
header will also be aligned.
>> + __be16 proto2 = *(const __be16 *)(phdr2 + 1);
>> +
>> + return (__force u16)((phdr->sid ^ phdr2->sid) | (proto ^ proto2));
>> +}
>> +
>> +static __be16 pppoe_hdr_proto(const struct pppoe_hdr *phdr)
>> +{
>> + __be16 proto = *(const __be16 *)(phdr + 1);
> Could this also result in an unaligned access regression for the same reason?
>
> [ ... ]
>> +static struct sk_buff *pppoe_gro_receive(struct list_head *head,
>> + struct sk_buff *skb)
>> +{
> [ ... ]
>> + off_pppoe = skb_gro_offset(skb);
>> + hlen = off_pppoe + PPPOE_SES_HLEN;
>> + phdr = skb_gro_header(skb, hlen, off_pppoe);
>> + if (unlikely(!phdr))
>> + goto out;
>> +
>> + /* filter for session packets (type:1, ver:1, code:0) */
>> + if (*(const __be16 *)phdr != cpu_to_be16(0x1100))
> Is it possible this cast could similarly trigger an unaligned access exception
> if the header offset is oddly aligned?
>
> [ ... ]
>> +static struct sk_buff *pppoe_gso_segment(struct sk_buff *skb,
>> + netdev_features_t features)
>> +{
> [ ... ]
>> + segs = ptype->callbacks.gso_segment(skb, features);
>> + if (IS_ERR_OR_NULL(segs)) {
>> + skb_gso_error_unwind(skb, orig_type, PPPOE_SES_HLEN, mac_offset,
>> + mac_len);
> If the inner protocol's gso_segment callback returns NULL (indicating the
> hardware can natively offload the segmentation), skb_gso_error_unwind() will
> be called.
>
> skb_gso_error_unwind() unconditionally sets skb->encapsulation = 1. However,
> PPPoE does not configure the inner header offsets (such as
> skb->inner_network_header).
>
> When the driver sees skb->encapsulation == 1, could it attempt to read the
> uninitialized inner header offsets, parsing the outer MAC header as an inner
> IP header and misprogramming the hardware TSO context?
>
> Does PPPoE need to explicitly clear TSO features before calling the inner
> segmentation to force software segmentation, avoiding the need for
> skb_gso_error_unwind() here entirely?
I'm not sure how to test this. Is this what happens when the underlying
interface supports TSO?
+Cc: Pablo
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-07 3:36 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260501035102.293031-1-qingfang.deng@linux.dev>
2026-05-01 3:50 ` [PATCH net-next v8 2/2] selftests: net: test PPPoE packets in gro.sh Qingfang Deng
2026-05-06 12:14 ` Simon Horman
2026-05-06 12:14 ` [PATCH net-next v8 1/2] net: pppoe: implement GRO/GSO support Simon Horman
2026-05-07 3:36 ` Qingfang Deng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox