linux/tools/testing/selftests/net/lib/xdp_native.bpf.c

622 lines
13 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
#include <stddef.h>
#include <linux/bpf.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
#define MAX_ADJST_OFFSET 256
#define MAX_PAYLOAD_LEN 5000
#define MAX_HDR_LEN 64
enum {
XDP_MODE = 0,
XDP_PORT = 1,
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
XDP_ADJST_OFFSET = 2,
XDP_ADJST_TAG = 3,
} xdp_map_setup_keys;
enum {
XDP_MODE_PASS = 0,
XDP_MODE_DROP = 1,
XDP_MODE_TX = 2,
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
XDP_MODE_TAIL_ADJST = 3,
XDP_MODE_HEAD_ADJST = 4,
} xdp_map_modes;
enum {
STATS_RX = 0,
STATS_PASS = 1,
STATS_DROP = 2,
STATS_TX = 3,
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
STATS_ABORT = 4,
} xdp_stats;
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
__uint(max_entries, 5);
__type(key, __u32);
__type(value, __s32);
} map_xdp_setup SEC(".maps");
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
__uint(max_entries, 5);
__type(key, __u32);
__type(value, __u64);
} map_xdp_stats SEC(".maps");
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
static __u32 min(__u32 a, __u32 b)
{
return a < b ? a : b;
}
static void record_stats(struct xdp_md *ctx, __u32 stat_type)
{
__u64 *count;
count = bpf_map_lookup_elem(&map_xdp_stats, &stat_type);
if (count)
__sync_fetch_and_add(count, 1);
}
static struct udphdr *filter_udphdr(struct xdp_md *ctx, __u16 port)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL;
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return NULL;
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth);
if (iph + 1 > (struct iphdr *)data_end ||
iph->protocol != IPPROTO_UDP)
return NULL;
udph = (void *)eth + sizeof(*iph) + sizeof(*eth);
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ipv6h = data + sizeof(*eth);
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
ipv6h->nexthdr != IPPROTO_UDP)
return NULL;
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth);
} else {
return NULL;
}
if (udph + 1 > (struct udphdr *)data_end)
return NULL;
if (udph->dest != bpf_htons(port))
return NULL;
record_stats(ctx, STATS_RX);
return udph;
}
static int xdp_mode_pass(struct xdp_md *ctx, __u16 port)
{
struct udphdr *udph = NULL;
udph = filter_udphdr(ctx, port);
if (!udph)
return XDP_PASS;
record_stats(ctx, STATS_PASS);
return XDP_PASS;
}
static int xdp_mode_drop_handler(struct xdp_md *ctx, __u16 port)
{
struct udphdr *udph = NULL;
udph = filter_udphdr(ctx, port);
if (!udph)
return XDP_PASS;
record_stats(ctx, STATS_DROP);
return XDP_DROP;
}
static void swap_machdr(void *data)
{
struct ethhdr *eth = data;
__u8 tmp_mac[ETH_ALEN];
__builtin_memcpy(tmp_mac, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, tmp_mac, ETH_ALEN);
}
static int xdp_mode_tx_handler(struct xdp_md *ctx, __u16 port)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL;
struct ethhdr *eth = data;
if (data + sizeof(*eth) > data_end)
return XDP_PASS;
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth);
__be32 tmp_ip = iph->saddr;
if (iph + 1 > (struct iphdr *)data_end ||
iph->protocol != IPPROTO_UDP)
return XDP_PASS;
udph = data + sizeof(*iph) + sizeof(*eth);
if (udph + 1 > (struct udphdr *)data_end)
return XDP_PASS;
if (udph->dest != bpf_htons(port))
return XDP_PASS;
record_stats(ctx, STATS_RX);
swap_machdr((void *)eth);
iph->saddr = iph->daddr;
iph->daddr = tmp_ip;
record_stats(ctx, STATS_TX);
return XDP_TX;
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ipv6h = data + sizeof(*eth);
struct in6_addr tmp_ipv6;
if (ipv6h + 1 > (struct ipv6hdr *)data_end ||
ipv6h->nexthdr != IPPROTO_UDP)
return XDP_PASS;
udph = data + sizeof(*ipv6h) + sizeof(*eth);
if (udph + 1 > (struct udphdr *)data_end)
return XDP_PASS;
if (udph->dest != bpf_htons(port))
return XDP_PASS;
record_stats(ctx, STATS_RX);
swap_machdr((void *)eth);
__builtin_memcpy(&tmp_ipv6, &ipv6h->saddr, sizeof(tmp_ipv6));
__builtin_memcpy(&ipv6h->saddr, &ipv6h->daddr,
sizeof(tmp_ipv6));
__builtin_memcpy(&ipv6h->daddr, &tmp_ipv6, sizeof(tmp_ipv6));
record_stats(ctx, STATS_TX);
return XDP_TX;
}
return XDP_PASS;
}
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
static void *update_pkt(struct xdp_md *ctx, __s16 offset, __u32 *udp_csum)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL;
struct ethhdr *eth = data;
__u32 len, len_new;
if (data + sizeof(*eth) > data_end)
return NULL;
if (eth->h_proto == bpf_htons(ETH_P_IP)) {
struct iphdr *iph = data + sizeof(*eth);
__u16 total_len;
if (iph + 1 > (struct iphdr *)data_end)
return NULL;
iph->tot_len = bpf_htons(bpf_ntohs(iph->tot_len) + offset);
udph = (void *)eth + sizeof(*iph) + sizeof(*eth);
if (!udph || udph + 1 > (struct udphdr *)data_end)
return NULL;
len_new = bpf_htons(bpf_ntohs(udph->len) + offset);
} else if (eth->h_proto == bpf_htons(ETH_P_IPV6)) {
struct ipv6hdr *ipv6h = data + sizeof(*eth);
__u16 payload_len;
if (ipv6h + 1 > (struct ipv6hdr *)data_end)
return NULL;
udph = (void *)eth + sizeof(*ipv6h) + sizeof(*eth);
if (!udph || udph + 1 > (struct udphdr *)data_end)
return NULL;
*udp_csum = ~((__u32)udph->check);
len = ipv6h->payload_len;
len_new = bpf_htons(bpf_ntohs(len) + offset);
ipv6h->payload_len = len_new;
*udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new,
sizeof(len_new), *udp_csum);
len = udph->len;
len_new = bpf_htons(bpf_ntohs(udph->len) + offset);
*udp_csum = bpf_csum_diff(&len, sizeof(len), &len_new,
sizeof(len_new), *udp_csum);
} else {
return NULL;
}
udph->len = len_new;
return udph;
}
static __u16 csum_fold_helper(__u32 csum)
{
return ~((csum & 0xffff) + (csum >> 16)) ? : 0xffff;
}
static int xdp_adjst_tail_shrnk_data(struct xdp_md *ctx, __u16 offset,
__u32 hdr_len)
{
char tmp_buff[MAX_ADJST_OFFSET];
__u32 buff_pos, udp_csum = 0;
struct udphdr *udph = NULL;
__u32 buff_len;
udph = update_pkt(ctx, 0 - offset, &udp_csum);
if (!udph)
return -1;
buff_len = bpf_xdp_get_buff_len(ctx);
offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET :
offset & 0xff;
if (offset == 0)
return -1;
/* Make sure we have enough data to avoid eating the header */
if (buff_len - offset < hdr_len)
return -1;
buff_pos = buff_len - offset;
if (bpf_xdp_load_bytes(ctx, buff_pos, tmp_buff, offset) < 0)
return -1;
udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum);
udph->check = (__u16)csum_fold_helper(udp_csum);
if (bpf_xdp_adjust_tail(ctx, 0 - offset) < 0)
return -1;
return 0;
}
static int xdp_adjst_tail_grow_data(struct xdp_md *ctx, __u16 offset)
{
char tmp_buff[MAX_ADJST_OFFSET];
__u32 buff_pos, udp_csum = 0;
__u32 buff_len, hdr_len, key;
struct udphdr *udph;
__s32 *val;
__u8 tag;
/* Proceed to update the packet headers before attempting to adjuste
* the tail. Once the tail is adjusted we lose access to the offset
* amount of data at the end of the packet which is crucial to update
* the checksum.
* Since any failure beyond this would abort the packet, we should
* not worry about passing a packet up the stack with wrong headers
*/
udph = update_pkt(ctx, offset, &udp_csum);
if (!udph)
return -1;
key = XDP_ADJST_TAG;
val = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!val)
return -1;
tag = (__u8)(*val);
for (int i = 0; i < MAX_ADJST_OFFSET; i++)
__builtin_memcpy(&tmp_buff[i], &tag, 1);
offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET :
offset & 0xff;
if (offset == 0)
return -1;
udp_csum = bpf_csum_diff(0, 0, (__be32 *)tmp_buff, offset, udp_csum);
udph->check = (__u16)csum_fold_helper(udp_csum);
buff_len = bpf_xdp_get_buff_len(ctx);
if (bpf_xdp_adjust_tail(ctx, offset) < 0) {
bpf_printk("Failed to adjust tail\n");
return -1;
}
if (bpf_xdp_store_bytes(ctx, buff_len, tmp_buff, offset) < 0)
return -1;
return 0;
}
static int xdp_adjst_tail(struct xdp_md *ctx, __u16 port)
{
void *data = (void *)(long)ctx->data;
struct udphdr *udph = NULL;
__s32 *adjust_offset, *val;
__u32 key, hdr_len;
void *offset_ptr;
__u8 tag;
int ret;
udph = filter_udphdr(ctx, port);
if (!udph)
return XDP_PASS;
hdr_len = (void *)udph - data + sizeof(struct udphdr);
key = XDP_ADJST_OFFSET;
adjust_offset = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!adjust_offset)
return XDP_PASS;
if (*adjust_offset < 0)
ret = xdp_adjst_tail_shrnk_data(ctx,
(__u16)(0 - *adjust_offset),
hdr_len);
else
ret = xdp_adjst_tail_grow_data(ctx, (__u16)(*adjust_offset));
if (ret)
goto abort_pkt;
record_stats(ctx, STATS_PASS);
return XDP_PASS;
abort_pkt:
record_stats(ctx, STATS_ABORT);
return XDP_ABORTED;
}
static int xdp_adjst_head_shrnk_data(struct xdp_md *ctx, __u64 hdr_len,
__u32 offset)
{
char tmp_buff[MAX_ADJST_OFFSET];
struct udphdr *udph;
void *offset_ptr;
__u32 udp_csum = 0;
/* Update the length information in the IP and UDP headers before
* adjusting the headroom. This simplifies accessing the relevant
* fields in the IP and UDP headers for fragmented packets. Any
* failure beyond this point will result in the packet being aborted,
* so we don't need to worry about incorrect length information for
* passed packets.
*/
udph = update_pkt(ctx, (__s16)(0 - offset), &udp_csum);
if (!udph)
return -1;
offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET :
offset & 0xff;
if (offset == 0)
return -1;
if (bpf_xdp_load_bytes(ctx, hdr_len, tmp_buff, offset) < 0)
return -1;
udp_csum = bpf_csum_diff((__be32 *)tmp_buff, offset, 0, 0, udp_csum);
udph->check = (__u16)csum_fold_helper(udp_csum);
if (bpf_xdp_load_bytes(ctx, 0, tmp_buff, MAX_ADJST_OFFSET) < 0)
return -1;
if (bpf_xdp_adjust_head(ctx, offset) < 0)
return -1;
if (offset > MAX_ADJST_OFFSET)
return -1;
if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0)
return -1;
/* Added here to handle clang complain about negative value */
hdr_len = hdr_len & 0xff;
if (hdr_len == 0)
return -1;
if (bpf_xdp_store_bytes(ctx, 0, tmp_buff, hdr_len) < 0)
return -1;
return 0;
}
static int xdp_adjst_head_grow_data(struct xdp_md *ctx, __u64 hdr_len,
__u32 offset)
{
char hdr_buff[MAX_HDR_LEN];
char data_buff[MAX_ADJST_OFFSET];
void *offset_ptr;
__s32 *val;
__u32 key;
__u8 tag;
__u32 udp_csum = 0;
struct udphdr *udph;
udph = update_pkt(ctx, (__s16)(offset), &udp_csum);
if (!udph)
return -1;
key = XDP_ADJST_TAG;
val = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!val)
return -1;
tag = (__u8)(*val);
for (int i = 0; i < MAX_ADJST_OFFSET; i++)
__builtin_memcpy(&data_buff[i], &tag, 1);
offset = (offset & 0x1ff) >= MAX_ADJST_OFFSET ? MAX_ADJST_OFFSET :
offset & 0xff;
if (offset == 0)
return -1;
udp_csum = bpf_csum_diff(0, 0, (__be32 *)data_buff, offset, udp_csum);
udph->check = (__u16)csum_fold_helper(udp_csum);
if (hdr_len > MAX_ADJST_OFFSET || hdr_len == 0)
return -1;
/* Added here to handle clang complain about negative value */
hdr_len = hdr_len & 0xff;
if (hdr_len == 0)
return -1;
if (bpf_xdp_load_bytes(ctx, 0, hdr_buff, hdr_len) < 0)
return -1;
if (offset > MAX_ADJST_OFFSET)
return -1;
if (bpf_xdp_adjust_head(ctx, 0 - offset) < 0)
return -1;
if (bpf_xdp_store_bytes(ctx, 0, hdr_buff, hdr_len) < 0)
return -1;
if (bpf_xdp_store_bytes(ctx, hdr_len, data_buff, offset) < 0)
return -1;
return 0;
}
static int xdp_head_adjst(struct xdp_md *ctx, __u16 port)
{
void *data_end = (void *)(long)ctx->data_end;
void *data = (void *)(long)ctx->data;
struct udphdr *udph_ptr = NULL;
__u32 key, size, hdr_len;
__s32 *val;
int res;
/* Filter packets based on UDP port */
udph_ptr = filter_udphdr(ctx, port);
if (!udph_ptr)
return XDP_PASS;
hdr_len = (void *)udph_ptr - data + sizeof(struct udphdr);
key = XDP_ADJST_OFFSET;
val = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!val)
return XDP_PASS;
switch (*val) {
case -16:
case 16:
size = 16;
break;
case -32:
case 32:
size = 32;
break;
case -64:
case 64:
size = 64;
break;
case -128:
case 128:
size = 128;
break;
case -256:
case 256:
size = 256;
break;
default:
bpf_printk("Invalid adjustment offset: %d\n", *val);
goto abort;
}
if (*val < 0)
res = xdp_adjst_head_grow_data(ctx, hdr_len, size);
else
res = xdp_adjst_head_shrnk_data(ctx, hdr_len, size);
if (res)
goto abort;
record_stats(ctx, STATS_PASS);
return XDP_PASS;
abort:
record_stats(ctx, STATS_ABORT);
return XDP_ABORTED;
}
static int xdp_prog_common(struct xdp_md *ctx)
{
__u32 key, *port;
__s32 *mode;
key = XDP_MODE;
mode = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!mode)
return XDP_PASS;
key = XDP_PORT;
port = bpf_map_lookup_elem(&map_xdp_setup, &key);
if (!port)
return XDP_PASS;
switch (*mode) {
case XDP_MODE_PASS:
return xdp_mode_pass(ctx, (__u16)(*port));
case XDP_MODE_DROP:
return xdp_mode_drop_handler(ctx, (__u16)(*port));
case XDP_MODE_TX:
return xdp_mode_tx_handler(ctx, (__u16)(*port));
selftests: drv-net: Test tail-adjustment support Add test to validate support for the two cases of tail adjustment: 1) tail extension, and 2) tail shrinking across different frame sizes and offset values. For each of the two cases, test both the single and multi-buffer cases by choosing appropriate packet size. The negative offset value result in growing of tailroom (shrinking of payload) while the positive offset result in shrinking of tailroom (growing of payload). Since the support for tail adjustment varies across drivers, classify the test as pass if at least one combination of packet size and offset from a pre-selected list results in a successful run. In case of an unsuccessful run, report the failure and highlight the packet size and offset values that caused the test to fail, as well as the values that resulted in the last successful run. Note: The growing part of this test for netdevsim may appear flaky when the offset value is larger than 1. This behavior occurs because tailroom is not explicitly reserved for netdevsim, with 1 being the typical tailroom value. However, in certain cases, such as payload being the last in the page with additional available space, the truesize is expanded. This also result increases the tailroom causing the test to pass intermittently. In contrast, when tailrrom is explicitly reserved, such as in the of fbnic, the test results are deterministic. ./drivers/net/xdp.py TAP version 13 1..7 ok 1 xdp.test_xdp_native_pass_sb ok 2 xdp.test_xdp_native_pass_mb ok 3 xdp.test_xdp_native_drop_sb ok 4 xdp.test_xdp_native_drop_mb ok 5 xdp.test_xdp_native_tx_mb \# Failed run: ... successful run: ... offset 1. Reason: Adjustment failed ok 6 xdp.test_xdp_native_adjst_tail_grow_data ok 7 xdp.test_xdp_native_adjst_tail_shrnk_data \# Totals: pass:7 fail:0 xfail:0 xpass:0 skip:0 error:0 Signed-off-by: Mohsin Bashir <mohsin.bashr@gmail.com> Link: https://patch.msgid.link/20250719083059.3209169-5-mohsin.bashr@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2025-07-19 01:30:58 -07:00
case XDP_MODE_TAIL_ADJST:
return xdp_adjst_tail(ctx, (__u16)(*port));
case XDP_MODE_HEAD_ADJST:
return xdp_head_adjst(ctx, (__u16)(*port));
}
/* Default action is to simple pass */
return XDP_PASS;
}
SEC("xdp")
int xdp_prog(struct xdp_md *ctx)
{
return xdp_prog_common(ctx);
}
SEC("xdp.frags")
int xdp_prog_frags(struct xdp_md *ctx)
{
return xdp_prog_common(ctx);
}
char _license[] SEC("license") = "GPL";