vmware-archive / p4c-xdp Goto Github PK
View Code? Open in Web Editor NEWBackend for the P4 compiler targeting XDP
License: Apache License 2.0
Backend for the P4 compiler targeting XDP
License: Apache License 2.0
R9 is a pkt ptr, after <<32 and >>32, it becomes invalid ?
R0=pkt(id=0,off=25,r=58) R1=imm2,min_value=2,max_value=2 R2=inv56 R3=inv60,min_value=0,max_value=15 R4=imm6,min_value=6,max_value=6 R5=pkt_end R6=inv R7=inv60,min_value=0,max_value=15 R8=inv
R9=pkt(id=0,off=0,r=0) R10=fp fp-184=imm fp-176=imm
407: (b7) r1 = 1
408: (b7) r0 = 0
409: (63) *(u32 *)(r10 -12) = r0
410: (63) *(u32 *)(r10 -16) = r1
411: (67) r5 <<= 32
412: (77) r5 >>= 32
413: (67) r9 <<= 32
414: (77) r9 >>= 32
415: (bf) r1 = r9
416: (07) r1 += 14
417: (2d) if r1 > r5 goto pc+188
R0=imm0,min_value=0,max_value=0 R1=inv31 R2=inv56 R3=inv60,min_value=0,max_value=15 R4=imm6,min_value=6,max_value=6 R5=inv32 R6=inv R7=inv60,min_value=0,max_value=15 R8=inv
R9=inv32 R10=fp fp-184=imm fp-176=imm fp-16=imm fp-8=imm
418: (79) r1 = *(u64 *)(r10 -168)
419: (73) *(u8 *)(r9 +0) = r1
R9 invalid mem access 'inv'
change xdp6.p4 to use 2 fields as key
table dstmactable() {
key = {
hd.ethernet.protocol : exact;
hd.ipv4.dstAddr : exact;
}
actions = {
got compile error
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp6.c -o -| llc -march=bpf -filetype=obj -o xdp6.o
In file included from xdp6.c:3:
./xdp6.h:57:9: error: duplicate member 'field0'
u32 field0; /* hd.ipv4.dstAddrexact */
^
./xdp6.h:56:9: note: previous declaration is here
u16 field0; /* hd.ethernet.protocolexact */
^
patch with the following
--- a/backends/ebpf/ebpfTable.cpp
+++ b/backends/ebpf/ebpfTable.cpp
@@ -97,6 +97,7 @@ void EBPFTable::emitKeyType(CodeBuilder* builder) {
auto matchType = mtdecl->getNode()->to<IR::Declaration_ID>();
if (matchType->name.name != P4::P4CoreLibrary::instance.exactMatch.name)
::error("Match of type %1% not supported", c->matchType);
+ fieldNumber++;
}
builder->blockEnd(false);
@@ -270,6 +271,7 @@ void EBPFTable::emitKey(CodeBuilder* builder, cstring keyName) {
codeGen->visit(c->expression);
}
builder->endOfStatement(true);
+ fieldNumber++;
}
}
error message
../p4c-xdp -I ../p4include --target xdp -o xdp4.c xdp4.p4;
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp4.c -o -| llc -march=bpf -filetype=obj -o xdp4.o
xdp4.c:179:57: error: extraneous ')' before ';'
&key.field0, &hd.ethernet.destination, 6);
^
due to
/* construct key */
struct dstmactable_key key = {};
&key.field0, &hd.ethernet.destination, 6);
not sure if it is correct, but it works with the following patch
--- a/backends/ebpf/ebpfTable.cpp
+++ b/backends/ebpf/ebpfTable.cpp
@@ -275,7 +275,7 @@ void EBPFTable::emitKey(CodeBuilder* builder, cstring keyName) {
builder->emitIndent();
if (memcpy) {
- builder->appendFormat("&%s.%s, &", keyName.c_str(), fieldName.c_str());
+ builder->appendFormat("memcpy(&%s.%s, &", keyName.c_str(), fieldName.c_str());
codeGen->visit(c->expression);
builder->appendFormat(", %d)", scalar->bytesRequired());
} else {
I still have no idea why such a simple program will spill the 1 byte value to use 8 byte on stack.
#include "xdp_model.p4"
header Ethernet {
bit<48> source;
}
struct Headers {
Ethernet ethernet;
}
parser Parser(packet_in packet, out Headers hd) {
state start {
packet.extract(hd.ethernet);
transition accept;
}
}
control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) {
apply {
xout.output_port = 0;
xout.output_action = xdp_action.XDP_PASS;
}
}
control Deparser(in Headers hdrs, packet_out packet) {
apply {
packet.emit(hdrs.ethernet);
}
}
xdp(Parser(), Ingress(), Deparser()) main;
then I greatly reduce a lot of stuff, to this very simple C code.
#include "xdp1.h"
#define KBUILD_MODNAME "xdptest"
#include <linux/bpf.h>
#include "bpf_helpers.h"
#define load_byte(data, b) (*((u8*)(data) + (b)))
#define htonl
#define htons
#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1)
#define write_byte(base, offset, v) *(u8*)(((u8*)base) + (offset)) = v
struct bpf_map_def SEC("maps") ebpf_outTable = {
.type = BPF_MAP_TYPE_PERCPU_ARRAY,
.key_size = sizeof(u32),
.value_size = sizeof(u32),
.pinning = 2, /* PIN_GLOBAL_NS */
.max_entries = 1 /* No multicast support */
};
SEC("prog")
int ebpf_filter(struct xdp_md* skb){
u8 ebpf_byte;
u32 ebpf_zero = 0;
u32 ebpf_outHeaderLength = 0;
struct xdp_output xout;
/* TODO: this should be initialized by the environment. HOW? */
struct xdp_input xin;
struct Headers hd = {};
void* pktstart = ((u8*)(long)skb->data);
void* ebpf_packetEnd = ((u8*)(long)skb->data_end);
/* extract(hd.ethernet)*/
if (ebpf_packetEnd < pktstart + 14) {
return XDP_ABORTED;
}
xout.output_port = 0;
xout.output_action = XDP_PASS;
hd.ethernet.source[0] = ((load_byte(pktstart, 0) ));
hd.ethernet.source[1] = ((load_byte(pktstart, 1) ));
hd.ethernet.source[2] = ((load_byte(pktstart, 2) ));
hd.ethernet.source[3] = ((load_byte(pktstart, 3) ));
hd.ethernet.source[4] = ((load_byte(pktstart, 4) ));
hd.ethernet.source[5] = ((load_byte(pktstart, 5) ));
hd.ethernet.ebpf_valid = 1;
/* deparser */
if (hd.ethernet.ebpf_valid) ebpf_outHeaderLength += 48;
bpf_xdp_adjust_head(skb, 14 - ebpf_outHeaderLength);
pktstart = ((u8*)(long)skb->data);
ebpf_packetEnd = ((u8*)(long)skb->data_end);
/* packet.emit(hd.ethernet)*/
if (hd.ethernet.ebpf_valid) {
if (ebpf_packetEnd < pktstart + 14) {
return XDP_ABORTED;
}
ebpf_byte = hd.ethernet.source[0];
write_byte(pktstart, 0, (ebpf_byte));
ebpf_byte = hd.ethernet.source[1];
write_byte(pktstart, 1, (ebpf_byte));
ebpf_byte = hd.ethernet.source[2];
write_byte(pktstart, 2, (ebpf_byte));
ebpf_byte = hd.ethernet.source[3];
write_byte(pktstart, 3, (ebpf_byte));
ebpf_byte = hd.ethernet.source[4];
write_byte(pktstart, 4, (ebpf_byte));
ebpf_byte = hd.ethernet.source[5];
write_byte(pktstart, 5, (ebpf_byte));
}
bpf_map_update_elem(&ebpf_outTable, &ebpf_zero, &xout.output_port, BPF_ANY);
return xout.output_action;
}
char _license[] SEC("license") = "GPL";
Still the objdump shows usage of 8byte on stack
Disassembly of section prog:
ebpf_filter:
; int ebpf_filter(struct xdp_md* skb){
0: r7 = r1
1: r6 = 0
; u32 ebpf_zero = 0;
2: *(u32 *)(r10 - 4) = r6
; void* ebpf_packetEnd = ((u8*)(long)skb->data_end);
3: r2 = *(u32 *)(r7 + 4)
; void* pktstart = ((u8*)(long)skb->data);
4: r1 = *(u32 *)(r7 + 0)
; if (ebpf_packetEnd < pktstart + 14) {
5: r3 = r1
6: r3 += 14
7: if r3 > r2 goto 40
8: r2 = 2
; xout.output_action = XDP_PASS;
9: *(u64 *)(r10 - 16) = r2
; hd.ethernet.source[5] = ((load_byte(pktstart, 5) ));
10: r2 = *(u8 *)(r1 + 5)
; hd.ethernet.source[4] = ((load_byte(pktstart, 4) ));
11: *(u64 *)(r10 - 24) = r2
12: r2 = *(u8 *)(r1 + 4)
; hd.ethernet.source[3] = ((load_byte(pktstart, 3) ));
13: *(u64 *)(r10 - 32) = r2
14: r2 = *(u8 *)(r1 + 3)
; hd.ethernet.source[2] = ((load_byte(pktstart, 2) ));
15: *(u64 *)(r10 - 40) = r2
16: r2 = *(u8 *)(r1 + 2)
; hd.ethernet.source[1] = ((load_byte(pktstart, 1) ));
17: *(u64 *)(r10 - 48) = r2
18: r8 = *(u8 *)(r1 + 1)
what I'm hoping to see is s.t like this, with r10 - off, off is diff by 1
; *(u8 *)data = (u8) hds.eth.fields[i];
10: r7 = *(u8 *)(r6 + 0)
; hds.eth.fields[i] = (u8)(load_byte(data, BYTES(i*8)) >> 0);
11: *(u8 *)(r10 - 14) = r7
12: r2 = *(u8 *)(r6 + 1)
13: *(u8 *)(r10 - 13) = r2
14: r2 = *(u8 *)(r6 + 2)
15: *(u8 *)(r10 - 12) = r2
16: r2 = *(u8 *)(r6 + 3)
17: *(u8 *)(r10 - 11) = r2
18: r2 = *(u8 *)(r6 + 4)
19: *(u8 *)(r10 - 10) = r2
20: r2 = *(u8 *)(r6 + 5)
21: *(u8 *)(r10 - 9) = r2
A nice way to show the usefulness of the compiler is to reimplement most of the xdp examples in the /linux/samples folder in pure P4 code.
This is also a good way to determine discrepancies or detect missing features.
add xdp10.p4, compile and there are missing definitions for
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp10.c -o -| llc -march=bpf -filetype=obj -o xdp10.o
xdp10.c:173:21: error: use of undeclared identifier 'counters_value'
counters_value *value;
^
xdp10.c:173:37: error: use of undeclared identifier 'value'
counters_value *value;
adding the following in xdp10.h solves the problem
typedef u32 counters_value;
typedef u32 counters_key;
At Deparser stage, can I unconditionally emit a new header?
so at xdp15.p4, I add a new header
/* encap my own header */
header myhdr_t {
bit<32> timestamp;
bit<32> id;
}
control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) {
... <skip> ...
apply {
hdr.myhdr.id = 0xfefefefe; // get ID from map or else
hdr.myhdr.timestamp = 0xabababab; // get TS from system
dstmactable.apply();
xout.output_port = 0;
xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_PASS;
}
}
control Deparser(in Headers hdrs, packet_out packet) {
apply {
packet.emit(hdrs.myhdr); // I want to insert in front of ethernet
packet.emit(hdrs.ethernet);
packet.emit(hdrs.ipv4);
}
}
However, the generated C code will always check the valid bit (hd.myhdr.ebpf_valid) and since it's not part of the packet, it is always false and emit doesn't happen.
When setting default_action in p4, we create a map and look it up the index 0 entry "bpf_map_lookup_elem(&dstmactable_defaultAction, &ebpf_zero);"
but we don't actually populate this map entry with the default action.
Now it always assumes the first entry in actions as default.
Not critical issue. I could work around it by putting the default at index 0
control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) {
action SetTTL_action(action_md_t md)
{
hd.ipv4.ttl = md.ttl;
}
action Fallback_action()
{
xout.drop = false;
}
action Drop_action()
{
xout.drop = true;
}
table dstmactable() {
key = { hd.ethernet.protocol : exact; }
actions = {
SetTTL_action; // miss will trigger this one index 0
Fallback_action;
Drop_action;
}
default_action = Drop_action; // default entry at index 2
implementation = hash_table(64);
}
apply {
dstmactable.apply();
xout.output_port = 0;
}
}
Discussed this with @mbudiu-vmw over email, but this compiler should eventually support p4 header stacks. Adding this issue just to keep track of any work that is related.
in xdp4.p4, I expect a ebpf table should be created, but got this error
root@osboxes:~/p4c/extensions/p4c-xdp/tests# make
p4c-xdp -I ../ --target xdp -o xdp4.c xdp4.p4;
xdp4.p4(58): warning: Table dstmactable is not used; removing
table dstmactable() {
^^^^^^^^^^^
the xdp4.p4, I also pushed the xdp4.p4
control Ingress(inout Headers hdr, in xdp_input xin, out xdp_output xout) {
bool xoutdrop = false;
action Fallback_action()
{
xoutdrop = false;
}
action Drop_action()
{
xoutdrop = true;
}
table dstmactable() {
key = { hdr.ethernet.destination : exact; }
actions = {
Fallback_action;
Drop_action;
}
default_action = Drop_action;
implementation = hash_table(64);
}
apply {
xout.output_port = 0;
xout.drop = xoutdrop;
}
}
Hello,
When we compile the .p4 file using p4c-xdp, it generates a .h and .c file. The .h file is missing a macro for dword, which needs to be inserted manually. Can you please fix this?
Thank you,
Sincerely,
The easiest way to test was to use the docker image provided. I'm trying to do tests on XDP again but the docker image seems to be out of date and I couldn't get it to rebuild in old image after git pull in p4c and p4c-xdp directories.
I did:
apt-get update
apt-get install cmake
cmake .. (in build dir)
Output was the following:
CMake Warning at backends/bmv2/CMakeLists.txt:125 (MESSAGE):
BMv2 simple switch is not available, not adding BMv2 tests
-- Added 9 tests to 'ebpf' (0 xfails)
-- Added 486 tests to 'p4' (1 xfails)
-- Added 177 tests to 'p14_to_16' (0 xfails)
-- CTest parallel: -j 8
-- Configuring incomplete, errors occurred!
See also "/home/p4c/build/CMakeFiles/CMakeOutput.log".
See also "/home/p4c/build/CMakeFiles/CMakeError.log".
FWIW the last few lines of CMakeError.log are as follows:
Determining if the function pthread_create exists in the pthreads failed with the following output:
Change Dir: /home/p4c/build/CMakeFiles/CMakeTmp
Run Build Command:"/usr/bin/make" "cmTC_7e0ae/fast"
/usr/bin/make -f CMakeFiles/cmTC_7e0ae.dir/build.make CMakeFiles/cmTC_7e0ae.dir/build
make[1]: Entering directory '/home/p4c/build/CMakeFiles/CMakeTmp'
Building C object CMakeFiles/cmTC_7e0ae.dir/CheckFunctionExists.c.o
/usr/bin/cc -DCHECK_FUNCTION_EXISTS=pthread_create -o CMakeFiles/cmTC_7e0ae.dir/CheckFunctionExists.c.o -c /usr/share/cmake-3.7/Modules/CheckFunctionExists.c
Linking C executable cmTC_7e0ae
/usr/bin/cmake -E cmake_link_script CMakeFiles/cmTC_7e0ae.dir/link.txt --verbose=1
/usr/bin/cc -DCHECK_FUNCTION_EXISTS=pthread_create CMakeFiles/cmTC_7e0ae.dir/CheckFunctionExists.c.o -o cmTC_7e0ae -rdynamic -lpthreads
/usr/bin/ld: cannot find -lpthreads
collect2: error: ld returned 1 exit status
CMakeFiles/cmTC_7e0ae.dir/build.make:97: recipe for target 'cmTC_7e0ae' failed
make[1]: *** [cmTC_7e0ae] Error 1
make[1]: Leaving directory '/home/p4c/build/CMakeFiles/CMakeTmp'
Makefile:126: recipe for target 'cmTC_7e0ae/fast' failed
make: *** [cmTC_7e0ae/fast] Error 2
The following code causes a syntax error:
const entries = {
(0) : l2l3_lookup(0xAABBCCDDEEFF,0xFFEEDDCCBBAA,0xa0000001,0x0a000001);
}
From @mbudiu-vmw in #53 :
"There is no support for const entries yet. But please file issues for every one of these."
try to mimic the same behavior as sample/bpf/xdp2_kern.c
which swaps the ether src and dst, then output to the same port
It works ok, however, there is no way to return XDP_TX
currently depends on xout.drop then return XDP_DROP or XDP_PASS
XDP_TX tells the driver to send the packet to the same port as receiving.
should we allow P4 programmer to say "return XDP_TX" ?
Hi, I'm testing the xdp3.p4 which parse L2, L3 and action could be drop or pass.
The table is described like this:
table dstmactable {
key = { hdr.ethernet.destination : exact; }
actions = {
Fallback_action;
Drop_action;
}
default_action = Drop_action;
implementation = hash_table(64);
}
After loading xdp3, I found that all the packets were passed to the kernel.
My understanding is that if the default action of a table is Drop_action, Then if a table has no entries or the packet does not match the table entries, The default action will be executed on the packet. I didn't add any entries to the dstmactable table through a control plane program. So the packet should be the default action: they should be dropped . Is my understanding right?
But the fact seems not like what I thought.
The xdp compiler currently only features a kernel runtime. It would be nice to add a userspace emulation to verify forwarding behaviour and packet modification in userspace.
Hi,
After building p4-xdp on Ubuntu16.04, I'd like to run all test cases to valid p4-xdp correctly working! However, do make under the tests directory, some error messages come out! plz see below~
In my guess, it seems can't find the proper bpf header, not in "/usr/include/linux/bpf.h". In the file, for example, there is no type BPF_PROG_TYPE_XDP in enum bpf_prog_type!
I installed linux-headers-4.10.0-28-generic and included it in Makefile under lib directory, but it conflicts with the existing headers!
========================
p4@p4-VirtualBox:/workspace/p4c/extensions/p4c-xdp/tests$ uname -a16.04.2-Ubuntu SMP Thu Jul 20 10:19:48 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
Linux p4-VirtualBox 4.10.0-28-generic #32
p4@p4-VirtualBox:~/workspace/p4c/extensions/p4c-xdp/tests$ make
make -C ../lib/
make[1]: Entering directory '/home/p4/workspace/p4c/extensions/p4c-xdp/lib'
gcc -I -g -c bpf_load.c
In file included from bpf_load.c:25:0:
bpf_helpers.h:45:11: error: โBPF_FUNC_get_stackidโ undeclared here (not in a function)
(void *) BPF_FUNC_get_stackid;
^
bpf_helpers.h:47:11: error: โBPF_FUNC_probe_write_userโ undeclared here (not in a function)
(void *) BPF_FUNC_probe_write_user;
^
bpf_helpers.h:49:11: error: โBPF_FUNC_current_task_under_cgroupโ undeclared here (not in a function)
(void *) BPF_FUNC_current_task_under_cgroup;
^
bpf_helpers.h:55:11: error: โBPF_FUNC_skb_get_tunnel_optโ undeclared here (not in a function)
(void *) BPF_FUNC_skb_get_tunnel_opt;
^
bpf_helpers.h:57:11: error: โBPF_FUNC_skb_set_tunnel_optโ undeclared here (not in a function)
(void *) BPF_FUNC_skb_set_tunnel_opt;
^
bpf_helpers.h:61:11: error: โBPF_FUNC_xdp_adjust_headโ undeclared here (not in a function)
(void *) BPF_FUNC_xdp_adjust_head;
^
bpf_helpers.h:90:11: error: โBPF_FUNC_skb_load_bytesโ undeclared here (not in a function)
(void *) BPF_FUNC_skb_load_bytes;
^
bpf_helpers.h:98:11: error: โBPF_FUNC_skb_under_cgroupโ undeclared here (not in a function)
(void *) BPF_FUNC_skb_under_cgroup;
^
bpf_helpers.h:100:11: error: โBPF_FUNC_skb_change_headโ undeclared here (not in a function)
(void *) BPF_FUNC_skb_change_head;
^
bpf_load.c: In function โload_and_attachโ:
bpf_load.c:78:15: error: โBPF_PROG_TYPE_TRACEPOINTโ undeclared (first use in this function)
prog_type = BPF_PROG_TYPE_TRACEPOINT;
^
bpf_load.c:78:15: note: each undeclared identifier is reported only once for each function it appears in
bpf_load.c:80:15: error: โBPF_PROG_TYPE_XDPโ undeclared (first use in this function)
prog_type = BPF_PROG_TYPE_XDP;
^
bpf_load.c:82:15: error: โBPF_PROG_TYPE_PERF_EVENTโ undeclared (first use in this function)
prog_type = BPF_PROG_TYPE_PERF_EVENT;
^
bpf_load.c:84:15: error: โBPF_PROG_TYPE_CGROUP_SKBโ undeclared (first use in this function)
prog_type = BPF_PROG_TYPE_CGROUP_SKB;
^
bpf_load.c:86:15: error: โBPF_PROG_TYPE_CGROUP_SOCKโ undeclared (first use in this function)
prog_type = BPF_PROG_TYPE_CGROUP_SOCK;
^
Makefile:9: recipe for target 'bpf_load.o' failed
make[1]: *** [bpf_load.o] Error 1
make[1]: Leaving directory '/home/p4/workspace/p4c/extensions/p4c-xdp/lib'
Makefile:17: recipe for target 'bpfloader' failed
make: *** [bpfloader] Error 2
I observed 2 issues.
/**
* i40e_xdp_setup - Add/remove an XDP program to a VSI
* @vsi: the VSI to add the program
* @prog: the XDP program
**/
int i40e_xdp_setup(struct i40e_vsi *vsi,
struct bpf_prog *prog)
{
struct i40e_pf *pf = vsi->back;
struct net_device *netdev = vsi->netdev;
int i, frame_size = netdev->mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
bool need_reset;
struct bpf_prog *old_prog;
if (prog && prog->xdp_adjust_head)
return -EOPNOTSUPP;
static bool i40e_run_xdp(struct i40e_ring *rx_ring,
struct i40e_rx_buffer *rx_buffer,
union i40e_rx_desc *rx_desc,
unsigned int size,
struct bpf_prog *xdp_prog)
{
...
case XDP_DROP:
do_drop:
if (likely(i40e_page_is_reusable(rx_buffer->page))) {
i40e_reuse_rx_page(rx_ring, rx_buffer);
rx_ring->rx_stats.page_reuse_count++;
break;
}
dma_unmap_page(rx_ring->dev, rx_buffer->dma, PAGE_SIZE,
DMA_FROM_DEVICE);
__free_pages(rx_buffer->page, 0);
break;
default:
bpf_warn_invalid_xdp_action(xdp_action);
goto do_drop;
}
/* clear contents of buffer_info */
rx_buffer->page = NULL;
return true; /* Swallowed by XDP */
return true will add packets stats to txdp_consumed_bytes
static int i40e_clean_rx_irq(struct i40e_ring *rx_ring, int budget)
{
...
skb = i40e_fetch_rx_buffer(rx_ring, rx_desc, skb,
&xdp_consumed_bytes);
if (xdp_consumed_bytes) {
cleaned_count++;
i40e_update_rx_next_to_clean(rx_ring);
total_rx_bytes += xdp_consumed_bytes;
total_rx_packets++;
Trying to set 48 bit fields fields causes an error as shown below. Setting 32 bit fields does work.
It seems like the function memcpy
tries to use the value to which the field must be set as an address:
18838586676582 = 0x112233445566 (the value to which the MAC address should be set)
memcpy(&hd.outer_eth.destination, &18838586676582, 6);
For some reason I also can't use setValid();
inside an action. When using it directly inside the apply{}
block it works.
action l2l3_lookup(macAddr_t dmac, macAddr_t smac, ipAddr_t dip, ipAddr_t sip) {
hd.outer_eth.destination = dmac;
hd.outer_eth.source = smac;
hd.outer_ipv4.dstAddr = dip;
hd.outer_ipv4.srcAddr = sip;
}
p4c-xdp --Werror -I ../p4include --target xdp -o xdp_vlan_to_vxlan.c xdp_vlan_to_vxlan.p4;
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp_vlan_to_vxlan.c -o -| llc -march=bpf -filetype=obj -o xdp_vlan_to_vxlan.o
xdp_vlan_to_vxlan.c:213:43: error: no member named 'setValid' in 'struct ipv4_t'
hd.outer_ipv4.setValid();
~~~~~~~~~~~~~ ^
xdp_vlan_to_vxlan.c:233:47: error: cannot take the address of an rvalue of type
'long'
memcpy(&hd.outer_eth.destination, &18838586676582, 6);
^~~~~~~~~~~~~~~
xdp_vlan_to_vxlan.c:234:42: error: cannot take the address of an rvalue of type
'long'
memcpy(&hd.outer_eth.source, &37603585123959, 6);
^~~~~~~~~~~~~~~
3 errors generated.
sudo ./bpfloader xdp_vlan_to_vxlan.o || true
xdp7.p4 show the error message
394: (05) goto pc+56
451: (7b) *(u64 *)(r10 -528) = r0
invalid stack off=-528 size=8
looking at the source code
} else if (reg->type == FRAME_PTR || reg->type == PTR_TO_STACK) {
if (off >= 0 || off < -MAX_BPF_STACK) {
verbose("invalid stack off=%d size=%d\n", off, size);
return -EACCES;
}
we only allow stack size of 512 + 32 + 8
#define MAX_BPF_STACK (512 /* from filter.h */ + \
32 /* space for rbx,r13,r14,r15 */ + \
8 /* space for skb_copy_bits */)
need to discuss with kernel bpf maintainer...
Hello,
When doing a make in the test directory I got an error from the xdp7 example, which is already referenced here: #22
But then when I try to load any other program I get this error:
% sudo ip link set dev eno1 xdp obj xdp1.o
0 maps not supported in current map section!
Error fixing up map structure, incompatible struct bpf_elf_map used?
Error fetching ELF ancillary data!
I followed your README to install and setup everything and then I tried running this XDP code:
#include <linux/bpf.h>
#ifndef __section
# define __section(NAME) \
__attribute__((section(NAME), used))
#endif
__section("prog")
int xdp_drop(struct xdp_md *ctx) {
return XDP_DROP;
}
char __license[] __section("license") = "GPL";
This code works well so I guess it's not an issue about the installation of the tool but more like an issue on the configuration of my system ?
Thanks in advance
This is in the #CONTROL_PLANE section of the generated header.
This error happens when not emitting ethernet header
when testing the same case without icmp hader, it works OK.
control Deparser(in Headers hdrs, packet_out packet) {
apply {
//packet.emit(hdrs.ethernet);
packet.emit(hdrs.ipv4);
packet.emit(hdrs.icmp);
}
}
BPF verifier
R0=imm0,min_value=0,max_value=0 R1=pkt(id=0,off=0,r=4) R2=pkt_end R3=fp-12 R4=imm4,min_value=4,max_value=4 R5=pkt(id=0,off=4,r=4) R6=ctx R7=imm0,min_value=0,max_value=0 R8=inv,min_value=0,max_value=0 R9=inv R10=fp
269: (bf) r2 = r0
270: (77) r2 >>= 3
271: (bf) r4 = r1
272: (0f) r4 += r2
addition of negative constant to packet pointer is not allowed
LLVM objdump
; if (hd.icmp.ebpf_valid) {
261: r4 = *(u64 *)(r10 - 40)
262: if r4 == 0 goto 29
; if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 32)) {
263: r4 = r0
264: r4 += 32
265: r4 >>= 3
266: r5 = r1
267: r5 += r4
268: if r5 > r2 goto 23
; write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
269: r2 = r0
270: r2 >>= 3
271: r4 = r1
272: r4 += r2
273: r2 = *(u64 *)(r10 - 80)
274: *(u8 *)(r4 + 0) = r2
; ebpf_packetOffsetInBits += 8;
275: r0 |= 8
; write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
276: r2 = r0
Hello everyone,
We are working on P4C-XDP and XDP-BPF applications. But we have occured this error after XDP test scenario.
root@machine:~/p4c/p4c# ip link set dev ens1f0 xdp obj xdp16.o verb
Error: either "dev" is duplicate, or "xdp" is a garbage.
the latest LLVM will also check the 512 byte BPF stack size. So we fails earlier at compiling the object instead of loading to verifier. Patch llvm and kernel net-next to 4096.
diff --git a/lib/Target/BPF/BPFRegisterInfo.cpp b/lib/Target/BPF/BPFRegisterInfo.cpp
index 7925bee..2636acb 100644
--- a/lib/Target/BPF/BPFRegisterInfo.cpp
+++ b/lib/Target/BPF/BPFRegisterInfo.cpp
@@ -44,7 +44,7 @@ BitVector BPFRegisterInfo::getReservedRegs(const MachineFunction &MF) const {
static void WarnSize(int Offset, MachineFunction &MF, DebugLoc& DL)
{
- if (Offset <= -512) {
+ if (Offset <= -4096) {
auto F = MF.getFunction();
DiagnosticInfoUnsupported DiagStackSize(*F,
"Looks like the BPF stack limit of 512 bytes is exceeded. "
patch kernel net-next
--- a/arch/x86/net/bpf_jit.S
+++ b/arch/x86/net/bpf_jit.S
@@ -19,7 +19,7 @@
*/
#define SKBDATA %r10
#define SKF_MAX_NEG_OFF $(-0x200000) /* SKF_LL_OFF from filter.h */
-#define MAX_BPF_STACK (512 /* from filter.h */ + \
+#define MAX_BPF_STACK (4096 /* from filter.h */ + \
32 /* space for rbx,r13,r14,r15 */ + \
8 /* space for skb_copy_bits */)
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e4eb254..fb532fc 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -55,7 +55,8 @@ struct bpf_prog_aux;
#define MAX_BPF_JIT_REG (MAX_BPF_REG + 1)
/* BPF program can access up to 512 bytes of stack space. */
-#define MAX_BPF_STACK 512
+//#define MAX_BPF_STACK 512
+#define MAX_BPF_STACK 4096
#define BPF_TAG_SIZE 8
Hi,
When I run the below command in the /p4c/extensions/p4c-xdp/tests :
# gcc -I ../lib/ ../lib/libbpf.o user_xdp5.c -o xdp5
there's an error that
In file included from xdp5.h:6:0,
from user_xdp5.c:20:
ebpf_xdp.h:24:25: fatal error: ebpf_kernel.h: No such file or directory
#include "ebpf_kernel.h"
^
compilation terminated.
And then I found the header files to be included are in the /p4c/backends/ebpf/runtime/,
so I move the ebpf_kernel.h and ebpf_common.h into the /tests :
root@debian:~/p4c/extensions/p4c-xdp/tests# cp /root/p4c/backends/ebpf/runtime/ebpf_common.h ./
root@debian:~/p4c/extensions/p4c-xdp/tests# cp /root/p4c/backends/ebpf/runtime/ebpf_kernel.h ./
however, when I compile again , some other errors occured :
In file included from ebpf_xdp.h:24:0,
from xdp5.h:6,
from user_xdp5.c:20:
ebpf_kernel.h:111:19: error: static declaration of โbpf_lookup_elemโ follows non-static declaration
static inline int bpf_lookup_elem(int fd, void *key, void *value) {
^~~~~~~~~~~~~~~
In file included from user_xdp5.c:17:0:
../lib/libbpf.h:10:5: note: previous declaration of โbpf_lookup_elemโ was here
int bpf_lookup_elem(int fd, void *key, void *value);
^~~~~~~~~~~~~~~
In file included from ebpf_xdp.h:24:0,
from xdp5.h:6,
from user_xdp5.c:20:
ebpf_kernel.h:131:19: error: static declaration of โbpf_update_elemโ follows non-static declaration
static inline int bpf_update_elem(int fd, void *key, void *value, u64 flags) {
^~~~~~~~~~~~~~~
In file included from user_xdp5.c:17:0:
../lib/libbpf.h:9:5: note: previous declaration of โbpf_update_elemโ was here
int bpf_update_elem(int fd, void *key, void *value, unsigned long long flags);
^~~~~~~~~~~~~~~
In file included from ebpf_xdp.h:24:0,
from xdp5.h:6,
from user_xdp5.c:20:
ebpf_kernel.h:149:19: error: static declaration of โbpf_delete_elemโ follows non-static declaration
static inline int bpf_delete_elem(int fd, void *key) {
^~~~~~~~~~~~~~~
In file included from user_xdp5.c:17:0:
../lib/libbpf.h:11:5: note: previous declaration of โbpf_delete_elemโ was here
int bpf_delete_elem(int fd, void *key);
^~~~~~~~~~~~~~~
In file included from ebpf_xdp.h:24:0,
from xdp5.h:6,
from user_xdp5.c:20:
ebpf_kernel.h:165:19: error: static declaration of โbpf_obj_pinโ follows non-static declaration
static inline int bpf_obj_pin(int fd, const char *pathname) {
^~~~~~~~~~~
In file included from user_xdp5.c:17:0:
../lib/libbpf.h:18:5: note: previous declaration of โbpf_obj_pinโ was here
int bpf_obj_pin(int fd, const char *pathname);
^~~~~~~~~~~
In file included from ebpf_xdp.h:24:0,
from xdp5.h:6,
from user_xdp5.c:20:
ebpf_kernel.h:181:19: error: static declaration of โbpf_obj_getโ follows non-static declaration
static inline int bpf_obj_get(const char *pathname) {
^~~~~~~~~~~
In file included from user_xdp5.c:17:0:
../lib/libbpf.h:19:5: note: previous declaration of โbpf_obj_getโ was here
int bpf_obj_get(const char *pathname);
^~~~~~~~~~~
user_xdp5.c: In function โmainโ:
user_xdp5.c:29:25: error: storage size of โkeyโ isnโt known
struct dstmactable_key key;
^~~
user_xdp5.c:30:27: error: storage size of โvalueโ isnโt known
struct dstmactable_value value;
^~~~~
user_xdp5.c:33:2: warning: implicit declaration of function โinitialize_tablesโ [-Wimplicit-function-declaration]
initialize_tables();
^~~~~~~~~~~~~~~~~
user_xdp5.c:35:17: error: โFallback_actionโ undeclared (first use in this function)
value.action = Fallback_action;
^~~~~~~~~~~~~~~
user_xdp5.c:35:17: note: each undeclared identifier is reported only once for each function it appears in
could you give me some advice?
Thanks.
Should we make the current ipv4 csum implementation generic to support tcp/udp/ipv6 csum?
libbpf.c: In function 'bpf_create_map':
libbpf.c:29:3: error: unknown field 'map_flags' specified in initializer
.map_flags = map_flags,
^
libbpf.c:29:16: warning: excess elements in union initializer
.map_flags = map_flags,
^
A table with 6 keys:
table flow_table {
key = {
hdr.out_ip.srcAddr : exact;
hdr.out_ip.dstAddr : exact;
hdr.out_ethernet.source : exact;
hdr.out_ethernet.destination : exact;
flow_dst_port : exact;
flow_src_port : exact;
}
actions = {
encap;
NoAction;
}
default_action = NoAction;
implementation = hash_table(128);
}
produced C code in header file:
struct flow_table_key {
u8 field2[6]; /* hdr.out_ethernet.source */
u32 field0; /* hdr.out_ip.srcAddr */
u16 field4; /* flow_dst_port */
};
enum flow_table_actions {
encap,
NoAction,
};
struct flow_table_value {
enum flow_table_actions action;
union {
struct {
u8 srcMac[6];
u8 dstMac[6];
u32 srcIP;
u32 dstIP;
} encap;
struct {
} NoAction;
} u;
};
field1, field3 and field5 are missing, but they are referenced inside c file
/* construct key */
struct flow_table_key key = {};
key.field0 = hdr.out_ip.srcAddr;
key.field1 = hdr.out_ip.dstAddr;
memcpy(&key.field2, &hdr.out_ethernet.source, 6);
memcpy(&key.field3, &hdr.out_ethernet.destination, 6);
key.field4 = flow_dst_port;
key.field5 = flow_src_port;
hence the compiler error by clang
geneve.c:439:25: error: no member named 'field1' in 'struct flow_table_key'
key.field1 = hdr.out_ip.dstAddr;
~~~ ^
geneve.c:441:33: error: no member named 'field3' in 'struct flow_table_key'
memcpy(&key.field3, &hdr.out_ethernet.destination, 6);
~~~ ^
geneve.c:443:25: error: no member named 'field5' in 'struct flow_table_key'
key.field5 = flow_src_port;
~~~ ^
3 errors generated.
I use vagrant to download the Ubuntu and run the below command :
# docker images
the output is
REPOSITORY TAG IMAGE ID CREATED SIZE
u9012063/p4xdp latest 975d533176d4 2 years ago 2.66GB
And then I run the docker image and found there are lots of diffrences between the docker and the git repo.
So for now , I must compile the p4c and extend the compiler using the bash command? And it seems there are some header including problems when I run the make in the ~/p4c/extensions/p4c-xdp/tests
Hello,
As we know, the compilation process involves two parts P4 -> P4c -> .c and .h and .c -> .o. In the /p4c-xdp/tests/ folder, we ran the make command which completes both the phases of compilation.
When compiling with clang from the .c file to .o, the following error is encountered:
In file included from xdp2.c:3:
./xdp2.h:16:5: error: unknown type name 'u32'
u32 input_port; /* bit<32> /
^
./xdp2.h:21:5: error: unknown type name 'u32'
u32 output_port; / bit<32> */
^
.
.
.
./xdp2.h:44:5: error: unknown type name 'u8'
u8 ebpf_valid;
^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.
Makefile:55: recipe for target 'xdp2.o' failed
make: *** [xdp2.o] Error 1
The program terminates with these errors. Can someone please guide us regarding this issue?
Thank you for your time and help.
Sincerely,
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp4.c -o -| llc -march=bpf -filetype=obj -o xdp4.o
xdp4.c:212:30: error: use of undeclared identifier 'hdr'; did you mean 'hd'?
key.field0 = hdr.ethernet.destination;
^~~
hd
xdp4.c:102:20: note: 'hd' declared here
struct Headers hd = {
^
xdp4.c:212:28: error: array type 'char [6]' is not assignable
key.field0 = hdr.ethernet.destination;
~~~~~~~~~~ ^
2 errors generated.
when parsing the vlan header from XDP program, my xdp program does not see vlan tag, while tcpdump can see it. I suspect the i40e nic is doing hardware vlan offload so vlan header is kept in skb metadata. I tried disable the vlan offload but fails.
root@prmh-nsx-perf-server139:# ethtool -K enp66s0f0 rx-vlan-offload off
ethtool: bad command line argument(s)
For more information run ethtool -h
In xdp5.p4, initialize xoutdrop to false
control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) {
bool xoutdrop = false;
but the generated xdp5.c does not initialize it, causing the following verifier error:
R0=inv,min_value=0,max_value=0 R2=imm272,min_value=272,max_value=272 R3=imm0,min_value=0,max_value=0 R4=inv,min_value=0,max_value=0 R6=ctx R7=imm34,min_value=34,max_value=34 R8=inv56 R9=inv56 R10=fp
171: (b7) r2 = 112
172: (63) *(u32 *)(r10 -12) = r3
173: (73) *(u8 *)(r10 -16) = r1
R1 !read_ok
Makefile:54: recipe for target 'xdp5.o' failed
llvm-objdump
; xout.output_port = 0;
172: *(u32 *)(r10 - 12) = r3
; xout.drop = xoutdrop;
173: *(u8 *)(r10 - 16) = r1
; bpf_xdp_adjust_head(skb, BYTES(ebpf_packetOffsetInBits) - ebpf_outHeaderLength);
174: r7 -= r2
175: r1 = r6
when testing the latest with p4c-xdp, the generated C code has:
SEC("ebpf_filter")
SEC("prog")
int ebpf_filter(struct xdp_md* skb){
struct Headers_t headers = {
.ethernet = {
maybe we should allow users to specify the SEC name, for example
./p4c-xdp --target xdp --sec <mysection name> -o <in> src.p4
if not, I'd prefer using "prog" since it is default in ip tool.
I tried to port P4C tutorial program - "calc" to XDP. The compiling failed with following error:
p4c-xdp --Werror --target xdp -o calc.c calc.p4;
terminate called after throwing an instance of 'Util::CompilerBug'
what(): In file: /home/wyklq/p4c/extensions/p4c-xdp/p4c/backends/ebpf/ebpfParser.cpp:294
Compiler Bug: calc.p4(114): Unhandled packet method packet.lookahead;
transition select(packet.lookahead<p4calc_t>().p,
The P4 source code is (".txt" is added to support github upload):
calc.p4.txt
even when I run docker with
docker run -u root
../p4c-xdp -I ../p4include --target xdp -o xdp16.c xdp16.p4;
clang \
-D__KERNEL__ -D__ASM_SYSREG_H -Wno-unused-value -Wno-pointer-sign \
-Wno-compare-distinct-pointer-types \
-Wno-gnu-variable-sized-type-not-at-end \
-Wno-tautological-compare \
-O2 -emit-llvm -g -c xdp16.c -o -| llc -march=bpf -filetype=obj -o xdp16.o
sudo ./bpfloader xdp16.o || true
setrlimit(RLIMIT_MEMLOCK, RLIM_INFINITY): Operation not permitted
when compiling
gcc -I../lib/ ../lib//libbpf.o ../lib//bpf_load.o load_and_verify.c -lelf -o bpfloader
../p4c-xdp -I ../p4include --target xdp -o xdp14.c xdp14.p4;
xdp14.p4(90): warning: action_md.output.port may be uninitialized
outport = action_md.output.port;
^^^^^^^^^^^^^^^^^^^^^
...
verifier error
56: (5d) if r1 != r4 goto pc+1
R0=map_value(ks=4,vs=36,id=0),min_value=0,max_value=0 R1=imm1,min_value=0,max_value=0 R3=imm1,min_value=1,max_value=1 R4=imm0,min_value=0,max_value=0 R5=imm0,min_value=0,max_value=0 R6=ctx R7=imm38,min_value=38,max_value=38 R8=imm0,min_value=0,max_value=0 R10=fp
57: (bf) r5 = r2
R2 !read_ok
Fix it with
accept:
{
u8 hit;
u32 outport;
u32 bitmap;
- struct action_md_t action_md;
+ struct action_md_t action_md = {};
enum xdp_action xact;
u32 tmp_5;
u8 tmp_6;
u32 tmp_7;
u8 tmp_8;
u32 tmp_9;
u8 tmp_10;
{
outport = 0;
bitmap = 0;
xact = XDP_PASS;
/* action_bitmap.apply()*/
{
kernel image 4.10.1 should have our BPF patches, however installing it in my vagrant box
http://kernel.ubuntu.com/~kernel-ppa/mainline/v4.10.1/
still fails as below:
R0=inv56 R1=pkt(id=0,off=0,r=34) R2=pkt_end R3=imm272,min_value=272,max_value=272 R4=inv R5=inv56 R6=ctx R7=inv56,min_value=0,max_value=255 R8=imm0,min_value=0,max_value=0 R9=inv R10=fp
441: (bf) r5 = r3
442: (07) r5 += 32
443: (18) r4 = 0xfffffff8
445: (5f) r5 &= r4
446: (77) r5 >>= 3
447: (bf) r0 = r1
448: (0f) r0 += r5
cannot add integer value with 3 upper zero bits to ptr_to_packet
#On xdp3.h
In file included from xdp3.c:3:
./xdp3.h:53:15: error: expected identifier or '('
struct Ingress.dstmactable_key {
^
./xdp3.h:57:12: error: expected '= constant-expression' or end of enumerator definition
Ingress.Fallback_action,
^
./xdp3.h:58:5: error: redefinition of enumerator 'Ingress'
Ingress.Drop_action,
^
./xdp3.h:57:5: note: previous definition is here
Ingress.Fallback_action,
^
./xdp3.h:58:12: error: expected '= constant-expression' or end of enumerator definition
Ingress.Drop_action,
in the source code
struct Headers {
struct Ethernet ethernet; /* Ethernet */
struct IPv4 ipv4; /* IPv4 */
};
struct Ingress.dstmactable_key {
u64 field0; /* hdr.ethernet.destination */
};
enum dstmactable_actions {
Ingress.Fallback_action,
Ingress.Drop_action,
};
currently travis failed due to this issue:
https://api.travis-ci.org/v3/job/336961449/log.txt
I declare another "struct Headers hd__" at the deparser code block, so that the live range of "hd" is reset at deparser.
An example of using xdp7.p4, before the patch, we use 616 byte stack memory
# llvm-objdump -S -no-show-raw-insn xdp7.o | grep "r10 -" | awk '{print $7}' | sort -n
616
After the patch:
392
What do you think?
index db5fff7..433d546 100644
--- a/tests/xdp7.c
+++ b/root/xdp7.c
@@ -1,4 +1,4 @@
-/* Automatically generated by p4c-xdp from xdp7.p4 on Thu Mar 2 08:04:24 2017
+/* Automatically generated by p4c-xdp from xdp7.p4 on Wed Mar 1 09:52:18 2017
*/
#include "xdp7.h"
#define KBUILD_MODNAME "xdptest"
@@ -133,6 +133,7 @@ int ebpf_filter(struct xdp_md* skb){
.ebpf_valid = 0
},
};
+ struct Headers hd__;
unsigned ebpf_packetOffsetInBits = 0;
enum ebpf_errorCodes ebpf_errorCode = NoError;
void* ebpf_packetStart = ((void*)(long)skb->data);
@@ -379,12 +380,13 @@ int ebpf_filter(struct xdp_md* skb){
}
/* deparser */
{
+ hd__ = hd;
{
- if (hd.ethernet.ebpf_valid) ebpf_outHeaderLength += 112;
- if (hd.ipv4.ebpf_valid) ebpf_outHeaderLength += 160;
- if (hd.icmp.ebpf_valid) ebpf_outHeaderLength += 32;
- if (hd.udp.ebpf_valid) ebpf_outHeaderLength += 64;
- if (hd.tcp.ebpf_valid) ebpf_outHeaderLength += 160;
+ if (hd__.ethernet.ebpf_valid) ebpf_outHeaderLength += 112;
+ if (hd__.ipv4.ebpf_valid) ebpf_outHeaderLength += 160;
+ if (hd__.icmp.ebpf_valid) ebpf_outHeaderLength += 32;
+ if (hd__.udp.ebpf_valid) ebpf_outHeaderLength += 64;
+ if (hd__.tcp.ebpf_valid) ebpf_outHeaderLength += 160;
}
bpf_xdp_adjust_head(skb, BYTES(ebpf_packetOffsetInBits) - BYTES(ebpf_outHeaderLength));
ebpf_packetStart = ((void*)(long)skb->data);
@@ -392,230 +394,230 @@ int ebpf_filter(struct xdp_md* skb){
ebpf_packetOffsetInBits = 0;
u8 hit_0;
{
- /* packet.emit(hd.ethernet)*/
- if (hd.ethernet.ebpf_valid) {
+ /* packet.emit(hd__.ethernet)*/
+ if (hd__.ethernet.ebpf_valid) {
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 112)) {
ebpf_errorCode = PacketTooShort;
return XDP_ABORTED;
}
- ebpf_byte = ((char*)(&hd.ethernet.destination))[0];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
- ebpf_byte = ((char*)(&hd.ethernet.destination))[1];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
- ebpf_byte = ((char*)(&hd.ethernet.destination))[2];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[2];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte) << 0);
- ebpf_byte = ((char*)(&hd.ethernet.destination))[3];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[3];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte) << 0);
- ebpf_byte = ((char*)(&hd.ethernet.destination))[4];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[4];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 4, (ebpf_byte) << 0);
- ebpf_byte = ((char*)(&hd.ethernet.destination))[5];
+ ebpf_byte = ((char*)(&hd__.ethernet.destination))[5];
... <skip the rest>
Hi,
I am testing the xdp16.p4 and wanting to receive packets sent by BPF_PERF_EVENT_OUTPUT.
Is there any user level code? or if not could you provide me a example to get packets?
Thx~
Using setValid()/isValid()/setInvalid() inside action block create issues on output c code.
for example:
action forward_port(bit<32> egress_port){
xout.output_port = egress_port;
if (hdr.geneve.isValid()){
hdr.out_ethernet.setInvalid();
hdr.out_ip.setInvalid();
hdr.out_udp.setInvalid();
hdr.geneve.setInvalid();
}
}
would produce
case forward_port:
{
xout.output_port = value->u.forward_port.egress_port;if (hdr.geneve.isValid()) {
hdr.out_ethernet.setInvalid();hdr.out_ip.setInvalid();hdr.out_udp.setInvalid();hdr.geneve.setInvalid(); } }
break;
which does not work. It should test isValid() by testing ebpf_valid bit instead of calling isValid(), similar issue with setValid() and setInvalid().
These work fine inside "apply" block.
I forgot whether we want to do csum update at this moment or later. But let me keep track of it.
xdp8.p4 will by default write ttl=4, and my working implementation of csum update is below
--- a/tmp/xdp8.c
+++ b/tmp/xdp8.c.0
@@ -309,10 +309,16 @@ int ebpf_filter(struct xdp_md* skb){
;
/* packet.emit(hd.ipv4)*/
if (hd.ipv4.ebpf_valid) {
+ u32 csum = 0, i = 0;
+ u16 *iph;
+
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 160)) {
ebpf_errorCode = PacketTooShort;
goto ebpf_end;
}
+
+ iph = (u16 *)(ebpf_packetStart + BYTES(ebpf_packetOffsetInBits));
+
ebpf_byte = ((char*)(&hd.ipv4.version))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 4);
ebpf_packetOffsetInBits += 4;
@@ -374,6 +380,13 @@ int ebpf_filter(struct xdp_md* skb){
ebpf_byte = ((char*)(&hd.ipv4.dstAddr))[3];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 32;
+
+ /* update csum, the ip csum is at 10 byte offset */
+ *(iph + 5) = 0;
+ for (i = 0; i < (20>>1); i++)
+ csum += iph[i];
+
+ *(iph + 5) = ~((csum & 0xffff) + (csum >> 16));
}
;
}
Hi,
I want to implement a simple forwarding function based on xdp7.p4 by using XDP_TX.
What I thought is B send packets to A , then I load xdp program on A's driver, and then A modify the packet's mac address, ip address , tcp port .After all these , the modified packet is sent to C by using XDP_TX.
What the program does is set the packets' four tuple and mac address from B. And I write like this:
/*
This program is based on xdp7.p4. The goal is to implement a simple forward behavior
by set the static ip dstAddr, tcp srcPort and dstPort.
*/
#include "xdp_model.p4"
header Ethernet {
bit<48> destination;
bit<48> source;
bit<16> protocol;
}
header IPv4 {
bit<4> version;
bit<4> ihl;
bit<8> diffserv;
bit<16> totalLen;
bit<16> identification;
bit<3> flags;
bit<13> fragOffset;
bit<8> ttl;
bit<8> protocol;
bit<16> checksum;
bit<32> srcAddr;
bit<32> dstAddr;
}
header icmp_t {
bit<16> typeCode;
bit<16> checksum;
}
header tcp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<32> seqNo;
bit<32> ackNo;
bit<4> dataOffset;
bit<4> res;
bit<8> flags;
bit<16> window;
bit<16> checksum;
bit<16> urgentPtr;
}
header udp_t {
bit<16> srcPort;
bit<16> dstPort;
bit<16> length_;
bit<16> checksum;
}
struct Headers {
Ethernet ethernet;
IPv4 ipv4;
tcp_t tcp;
udp_t udp;
icmp_t icmp;
}
parser Parser(packet_in packet, out Headers hd) {
state start {
packet.extract(hd.ethernet);
transition select(hd.ethernet.protocol) {
16w0x800: parse_ipv4;
default: accept;
}
}
state parse_ipv4 {
packet.extract(hd.ipv4);
transition select(hd.ipv4.protocol) {
8w6: parse_tcp;
8w17: parse_udp;
8w1: parse_icmp;
default: accept;
}
}
state parse_icmp {
packet.extract(hd.icmp);
transition accept;
}
state parse_tcp {
packet.extract(hd.tcp);
transition accept;
}
state parse_udp {
packet.extract(hd.udp);
transition accept;
}
}
control Ingress(inout Headers hd, in xdp_input xin, out xdp_output xout) {
bool xoutdrop = false;
// from, to are host byte order
bit<16> from;
bit<16> to;
bit<32> from_addr;
bit<32> to_addr;
bit<48> tmp;
action Fallback_action()
{
// exchange ethernet's destination and source
tmp = hd.ethernet.source;
hd.ethernet.source = hd.ethernet.destination;
hd.ethernet.destination = tmp;
// TCP: set the packet's srcPort = 6666 , dstPort = 8888
from = hd.tcp.srcPort;
// srcPort: 6666
to = 16w0x1a0a;
hd.tcp.srcPort = to;
hd.tcp.checksum = csum_replace2(hd.tcp.checksum, from, to);
from = hd.tcp.dstPort;
// dstPort: 8888
to = 16w0x22b8;
hd.tcp.dstPort = to;
hd.tcp.checksum = csum_replace2(hd.tcp.checksum, from, to);
// IP: set the packet's srcAddr = dstAddr ,dstAddr = 172.16.100.1
from_addr = hd.ipv4.srcAddr;
// srcAddr = dstAddr
to_addr = hd.ipv4.dstAddr;
hd.ipv4.srcAddr = to_addr;
hd.ipv4.checksum = csum_replace4(hd.ipv4.checksum, from_addr, to_addr);
hd.tcp.checksum = csum_replace4(hd.tcp.checksum, from_addr, to_addr);
from_addr = hd.ipv4.dstAddr;
// dstAddr = 172.16.100.1
to_addr = 32w0xac106401;
hd.ipv4.checksum = csum_replace4(hd.ipv4.checksum, from_addr, to_addr);
hd.tcp.checksum = csum_replace4(hd.tcp.checksum, from_addr, to_addr);
xoutdrop = false;
}
action Drop_action()
{
xoutdrop = true;
}
table dstmactable {
key = { hd.ethernet.protocol : exact; }
actions = {
Fallback_action;
Drop_action;
}
default_action = Fallback_action;
implementation = hash_table(64);
}
apply {
dstmactable.apply();
xout.output_port = 0;
// action set to XDP_TX
xout.output_action = xoutdrop ? xdp_action.XDP_DROP : xdp_action.XDP_TX;
}
}
control Deparser(in Headers hdrs, packet_out packet) {
apply {
packet.emit(hdrs.ethernet);
packet.emit(hdrs.ipv4);
// hit Verifier MAX_BPF_STACK issue
// packet.emit(hdrs.tcp);
// packet.emit(hdrs.udp);
packet.emit(hdrs.icmp);
packet.emit(hdrs.udp);
packet.emit(hdrs.tcp);
}
}
xdp(Parser(), Ingress(), Deparser()) main;
And I update the checksum . However this program could be compiled and loaded but seems not work as what I want.
Do I need to add ebpf_ipv4_checksum() to recalc the ipv4 checksum or anything else I forget to update ?
Could you give me some advices?
Thanks a lot!
This is related to #4
encounter the following verifier error. I guess this is similar to the previous one, but when spill the register to stack and restore, the imm upper zero bits state is missing?
R0=imm0,min_value=0,max_value=0 R1=imm58,min_value=58,max_value=58 R3=pkt(id=0,off=58,r=58) R4=inv61 R5=pkt_end R6=imm144,min_value=144,max_value=144 R7=imm0,min_value=0,max_value=0 R8=ctx R9=pkt(id=0,off=0,r=58) R10=fp
260: (bf) r5 = r6
261: (47) r5 |= 12
262: (bf) r1 = r5
263: (07) r1 += 44
264: (77) r1 >>= 3
265: (7b) *(u64 *)(r10 -64) = r1
266: (bf) r7 = r5
267: (07) r7 += 36
268: (77) r7 >>= 3
269: (bf) r0 = r5
270: (07) r0 += 20
271: (77) r0 >>= 3
272: (bf) r1 = r5
273: (07) r1 += 52
274: (77) r1 >>= 3
275: (77) r6 >>= 3
276: (79) r2 = *(u64 *)(r10 -24)
277: (bf) r2 = r5
278: (77) r2 >>= 3
279: (7b) *(u64 *)(r10 -184) = r2
280: (07) r5 += 180
281: (77) r5 >>= 3
282: (bf) r4 = r9
283: (0f) r4 += r5
284: (47) r5 |= 1
285: (bf) r3 = r9
286: (0f) r3 += r6
287: (bf) r6 = r9
288: (0f) r6 += r1
289: (47) r1 |= 1
290: (bf) r2 = r9
291: (0f) r2 += r0
292: (7b) *(u64 *)(r10 -312) = r2
293: (bf) r2 = r9
294: (79) r0 = *(u64 *)(r10 -184)
295: (0f) r2 += r0
cannot add integer value with 0 upper zero bits to ptr_to_packet
The objdump
; hd.ipv6.version = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits)) >> 4) & EBPF_MASK(u8, 4));
285: r3 = r9
286: r3 += r6
287: r6 = r9
288: r6 += r1
; hd.ipv6.srcAddr[1] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1) >> 0));
289: r1 |= 1
; hd.ipv6.payloadLen = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
290: r2 = r9
291: r2 += r0
292: *(u64 *)(r10 - 312) = r2
; hd.ipv6.flowLabel = (u32)((load_word(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits)) >> 8) & EBPF_MASK(u32, 20));
293: r2 = r9
294: r0 = *(u64 *)(r10 - 184)
295: r2 += r0
296: *(u64 *)(r10 - 336) = r2
; hd.ipv6.nextHdr = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
I notice that there are both functions to get map descriptor and update in the control plane of user_xdp5.c and the header of xdp5.h .
In user_xdp5.c , it's
fd = bpf_obj_get(MAP_PATH TABLE);
[...]
ret = bpf_update_elem(fd, &key, &value, BPF_ANY);
In xdp5.h, it's
int tableFileDescriptor = BPF_OBJ_GET(MAP_PATH "/Ingress_dstmactable_defaultAction");
[...]
int ok = BPF_USER_MAP_UPDATE_ELEM(tableFileDescriptor, &ebpf_zero, &value, BPF_ANY);
I know the lowercase function in user_xdp5.c actually is from libbpf.h which finally calls the syscall:
return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
# or
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
But where is the captital function BPF_OBJ_GET() definition ? Why use this function in xdp.h ?
And I did some test that I use lowercase function bpf_obj_get() in xdp5.h to replace BPF_OBG_GET(). It seems work
After google it , I didn't find any source code in linux kernel matches this . And just know that BPF_OBJ_GET is a type of bpf_cmd in /linux/bpf.h
not urgent at all but is there a way to treat warning as error, so the shell returns non-zero?
for example:
# make
../p4c-xdp -I ../p4include --target xdp -o xdp14.c xdp14.p4;
xdp14.p4(90): warning: action_md.output.port may be uninitialized
outport = action_md.output.port;
I want the "make" stops when seeing this warning. So that the travis-ci could report build failed.
My setup is:
git/p4c
git/p4c-xdp
git/p4c/extensions/p4c-xdp -> symlink to ../../p4c-xdp
When I type cmake ..
in the build directory of p4c I get this:
CMake Error at cmake/P4CUtils.cmake:86 (file):
file failed to open for writing (Not a directory):
/home/mbudiu/git/p4c/build/xdp/../p4c-xdp/tests/xdp15.p4.test
Call Stack (most recent call first):
cmake/P4CUtils.cmake:136 (p4c_add_test_with_args)
cmake/P4CUtils.cmake:180 (p4c_add_test_list)
extensions/p4c-xdp/CMakeLists.txt:60 (p4c_add_tests)
The patch fixes the verifier error due to skb->data and data_end checking.
We have to follow the way verifier checking the packet boundary
if (data + offset > data_end) ...
With this patch, I test TCP parsing and works OK. However, I also mark out the parse_vlan and parse_ipv6. I still don't know why they cause verifier failed.
p4-xdp1.diff.txt
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.