Skip to content

Commit

Permalink
Adds the draft of the XDP scheduler testing tool
Browse files Browse the repository at this point in the history
This commit contains the XDP scheduling framework. It consists of a
testing program called xdq-tester used to test schedulers using the XDP
and DEQUEUE hooks. It uses trace files written in Lua that the
xdq-tester program uses to check the XDP schedulers for correctness.

The FIFO, SPRIO, and WFQ are fully functional in this commit. The SPRIO
and WFQ have an API to set the weights from the Lua scripts.

The HPFQ is funcitonal. However, still missing a few more test cases.

Signed-off-by: Frey Alfredsson <[email protected]>
  • Loading branch information
freysteinn committed Jun 23, 2022
1 parent daefd11 commit 3c1fe0f
Show file tree
Hide file tree
Showing 14 changed files with 2,304 additions and 0 deletions.
10 changes: 10 additions & 0 deletions xdq-tester/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)

USER_TARGETS := xdq-tester
BPF_TARGETS := $(patsubst %.c,%,$(wildcard *.bpf.c))

USER_LIBS = -llua -ldl -lm

LIB_DIR = ../lib

include $(LIB_DIR)/common.mk
115 changes: 115 additions & 0 deletions xdq-tester/bpf_local_helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)

#ifndef BPF_LOCAL_HELPERS_H_
#define BPF_LOCAL_HELPERS_H_

#include "bpf_shared_data.h"

#define EEXIST 17 /* File exists */

#define BPF_MAP_TYPE_PIFO_GENERIC 31
#define BPF_MAP_TYPE_PIFO_XDP 32

/*
* bpf_packet_dequeue
*
* Dequeue the packet at the head of the PIFO in *map* and return a pointer
* to the packet (or NULL if the PIFO is empty).
*
* Returns
* On success, a pointer to the packet, or NULL if the PIFO is empty. The
* packet pointer must be freed using *bpf_packet_drop()* or returning
* the packet pointer. The *rank* pointer will be set to the rank of
* the dequeued packet on success, or a negative error code on error.
*/
static long (*bpf_packet_dequeue)(void *ctx, void *map, __u64 flags, __u64 *rank) = (void *) 196;
static long (*bpf_packet_drop)(void *ctx, void *pkt) = (void *) 197;

struct parsing_context {
void *data; // Start of eth hdr
void *data_end; // End of safe acessible area
struct hdr_cursor nh; // Position to parse next
__u32 pkt_len; // Full packet length (headers+data)
};

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
static __always_inline void *
bpf_map_lookup_or_try_init(void *map, const void *key, const void *init)
{
void *val;
long err;

val = bpf_map_lookup_elem(map, key);
if (val)
return val;

err = bpf_map_update_elem(map, key, init, BPF_NOEXIST);
if (err && err != -EEXIST)
return NULL;

return bpf_map_lookup_elem(map, key);
}

static __always_inline int bpf_max(__u64 left, __u64 right)
{
return right > left ? right : left;
}


/*
* Maps an IPv4 address into an IPv6 address according to RFC 4291 sec 2.5.5.2
*/
static void map_ipv4_to_ipv6(struct in6_addr *ipv6, __be32 ipv4)
{
__builtin_memset(&ipv6->in6_u.u6_addr8[0], 0x00, 10);
__builtin_memset(&ipv6->in6_u.u6_addr8[10], 0xff, 2);
ipv6->in6_u.u6_addr32[3] = ipv4;
}

/*
* Five-tuple helpers
*/

/* This function currently only supports UDP packets */
static __always_inline int parse_packet(struct parsing_context *pctx, struct packet_info *p_info)
{
/* Parse Ethernet and IP/IPv6 headers */
p_info->eth_type = parse_ethhdr(&pctx->nh, pctx->data_end, &p_info->eth);
if (p_info->eth_type == bpf_htons(ETH_P_IP)) {
p_info->ip_type = parse_iphdr(&pctx->nh, pctx->data_end, &p_info->iph);
if (p_info->ip_type < 0)
goto err;
p_info->nt.ipv = 4;
map_ipv4_to_ipv6(&p_info->nt.saddr.ip, p_info->iph->saddr);
map_ipv4_to_ipv6(&p_info->nt.daddr.ip, p_info->iph->daddr);
} else if (p_info->eth_type == bpf_htons(ETH_P_IPV6)) {
p_info->ip_type = parse_ip6hdr(&pctx->nh, pctx->data_end, &p_info->ip6h);
if (p_info->ip_type < 0)
goto err;
p_info->nt.ipv = 6;
p_info->nt.saddr.ip = p_info->ip6h->saddr;
p_info->nt.daddr.ip = p_info->ip6h->daddr;
} else {
goto err;
}

/* Parse UDP header */
if (p_info->ip_type != IPPROTO_UDP)
goto err;
if (parse_udphdr(&pctx->nh, pctx->data_end, &p_info->udph) < 0)
goto err;

p_info->nt.proto = IPPROTO_UDP;
p_info->nt.saddr.port = p_info->udph->source;
p_info->nt.daddr.port = p_info->udph->dest;

return 0;
err:
bpf_printk("Failed to parse UDP packet");
return -1;
}

#pragma GCC diagnostic pop

#endif // BPF_LOCAL_HELPERS_H_
42 changes: 42 additions & 0 deletions xdq-tester/bpf_shared_data.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#ifndef BPF_SHARED_DATA_H_
#define BPF_SHARED_DATA_H_

struct flow_address {
struct in6_addr ip;
__u16 port;
__u16 reserved;
};

struct network_tuple {
struct flow_address saddr;
struct flow_address daddr;
__u16 proto;
__u8 ipv;
__u8 reserved;
};

struct flow_state {
__u32 pkts;
__u32 root_finish_bytes;
__u32 finish_bytes;
__u16 root_weight;
__u16 weight;
__u32 persistent;
__u64 root_priority;
};

struct packet_info {
struct ethhdr *eth;
union {
struct iphdr *iph;
struct ipv6hdr *ip6h;
};
union {
struct udphdr *udph;
};
struct network_tuple nt;
int eth_type;
int ip_type;
};

#endif // BPF_SHARED_DATA_H_
25 changes: 25 additions & 0 deletions xdq-tester/fifo.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- SPDX-License-Identifier: GPL-2.0
-- Copyright (c) 2022 Freysteinn Alfredsson <[email protected]>

-- FIFO scheduler
config.bpf.file = "./sched_fifo.bpf.o"

-- Setup flows
packet_flow1 = Udp:new()
packet_flow1.udp.dest = 8080

packet_flow2 = Udp:new()
packet_flow2.udp.dest = 8081

packet_flow3 = Udp:new()
packet_flow3.udp.dest = 8082


-- Test scheduler
enqueue(packet_flow1)
enqueue(packet_flow2)
enqueue(packet_flow3)

dequeue_cmp(packet_flow1)
dequeue_cmp(packet_flow2)
dequeue_cmp(packet_flow3)
42 changes: 42 additions & 0 deletions xdq-tester/hpfq.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
-- SPDX-License-Identifier: GPL-2.0
-- Copyright (c) 2022 Freysteinn Alfredsson <[email protected]>

-- Hierarchical Packet Fair Queueing (HPFQ)
config.bpf.file = "./sched_hpfq.bpf.o"

-- Create flows
packet_flow1 = Udp:new()
packet_flow1.udp.dest = 4000

packet_flow2 = Udp:new()
packet_flow2.udp.dest = 8001

packet_flow3 = Udp:new()
packet_flow3.udp.dest = 8002


function hpfq_test1()
enqueue(packet_flow1)
enqueue(packet_flow2)
enqueue(packet_flow3)

dequeue_cmp(packet_flow3)
dequeue_cmp(packet_flow2)
dequeue_cmp(packet_flow1)
end

function hpfq_debug()
enqueue(packet_flow1)
enqueue(packet_flow1)
dequeue_cmp(packet_flow1)
dequeue_cmp(packet_flow1)

enqueue(packet_flow1)
enqueue(packet_flow1)
dequeue_cmp(packet_flow1)
dequeue_cmp(packet_flow1)
end

-- hpfq_test1()

hpfq_debug()
Loading

0 comments on commit 3c1fe0f

Please sign in to comment.