Skip to content

Instantly share code, notes, and snippets.

@williamtu
Created February 3, 2017 03:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save williamtu/abaeb11563872508d47cfabed23ac9ea to your computer and use it in GitHub Desktop.
Save williamtu/abaeb11563872508d47cfabed23ac9ea to your computer and use it in GitHub Desktop.
xdp3.c
#define KBUILD_MODNAME "xdptest"
#include <linux/bpf.h>
#include "bpf_helpers.h"
#define load_byte(data, b) (*(u8 *)(data + (b)))
#define load_half(data, b) __constant_ntohs(*(u16 *)(data + (b)))
#define load_word(data, b) __constant_ntohl(*(u32 *)(data + (b)))
#define htonl(d) __constant_htonl(d)
#define htons(d) __constant_htons(d)
enum ebpf_errorCodes {
NoError,
PacketTooShort,
NoMatch,
StackOutOfBounds,
OverwritingHeader,
HeaderTooShort,
ParserTimeout,
};
#define EBPF_MASK(t, w) ((((t)(1)) << (w)) - (t)1)
#define BYTES(w) ((w) / 8)
#define write_partial(a, s, v) do { u8 mask = EBPF_MASK(u8, s); *((u8*)a) = ((*((u8*)a)) & ~mask) | (((v) >> (8 - (s))) & mask); } while (0)
#define write_byte(base, offset, v) do { *(u8*)((base) + (offset)) = (v); } while (0)
struct xdp_input {
u32 input_port; /* bit<32> */
};
struct xdp_output {
u8 drop; /* bool */
u32 output_port; /* bit<32> */
};
struct Ethernet {
char source[6]; /* bit<48> */
char destination[6]; /* bit<48> */
u16 protocol; /* bit<16> */
u8 ebpf_valid;
};
struct IPv4 {
u8 version; /* bit<4> */
u8 ihl; /* bit<4> */
u8 diffserv; /* bit<8> */
u16 totalLen; /* bit<16> */
u16 identification; /* bit<16> */
u8 flags; /* bit<3> */
u16 fragOffset; /* bit<13> */
u8 ttl; /* bit<8> */
u8 protocol; /* bit<8> */
u16 hdrChecksum; /* bit<16> */
u32 srcAddr; /* bit<32> */
u32 dstAddr; /* bit<32> */
u8 ebpf_valid;
};
struct icmp_h {
u8 type_; /* bit<8> */
u8 code; /* bit<8> */
u16 hdrChecksum; /* bit<16> */
u8 ebpf_valid;
};
struct Headers {
struct Ethernet ethernet; /* Ethernet */
struct IPv4 ipv4; /* IPv4 */
struct icmp_h icmp; /* icmp_h */
};
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){
struct Headers hd = {
.ethernet = {
.ebpf_valid = 0
},
.ipv4 = {
.ebpf_valid = 0
},
.icmp = {
.ebpf_valid = 0
},
};
unsigned ebpf_packetOffsetInBits = 0;
enum ebpf_errorCodes ebpf_errorCode = NoError;
void* ebpf_packetStart = ((void*)(long)skb->data);
void* ebpf_packetEnd = ((void*)(long)skb->data_end);
u32 ebpf_zero = 0;
u8 ebpf_byte = 0;
u32 ebpf_outHeaderLength = 0;
struct xdp_output xout;
/* TODO: this should be initialized by the environment. HOW? */
struct xdp_input xin;
goto start;
start: {
/* extract(hd.ethernet)*/
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 112)) {
ebpf_errorCode = PacketTooShort;
goto reject;
}
hd.ethernet.source[0] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0) >> 0));
hd.ethernet.source[1] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1) >> 0));
hd.ethernet.source[2] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 2) >> 0));
hd.ethernet.source[3] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3) >> 0));
hd.ethernet.source[4] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 4) >> 0));
hd.ethernet.source[5] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 5) >> 0));
ebpf_packetOffsetInBits += 48;
hd.ethernet.destination[0] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0) >> 0));
hd.ethernet.destination[1] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1) >> 0));
hd.ethernet.destination[2] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 2) >> 0));
hd.ethernet.destination[3] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3) >> 0));
hd.ethernet.destination[4] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 4) >> 0));
hd.ethernet.destination[5] = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 5) >> 0));
ebpf_packetOffsetInBits += 48;
hd.ethernet.protocol = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 16;
hd.ethernet.ebpf_valid = 1;
switch (hd.ethernet.protocol) {
case 2048: goto parse_ipv4;
default: goto accept;
}
}
parse_ipv4: {
/* extract(hd.ipv4)*/
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 160)) {
ebpf_errorCode = PacketTooShort;
goto reject;
}
hd.ipv4.version = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits)) >> 4) & EBPF_MASK(u8, 4));
ebpf_packetOffsetInBits += 4;
hd.ipv4.ihl = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))) & EBPF_MASK(u8, 4));
ebpf_packetOffsetInBits += 4;
hd.ipv4.diffserv = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 8;
hd.ipv4.totalLen = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 16;
hd.ipv4.identification = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 16;
hd.ipv4.flags = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits)) >> 5) & EBPF_MASK(u8, 3));
ebpf_packetOffsetInBits += 3;
hd.ipv4.fragOffset = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))) & EBPF_MASK(u16, 13));
ebpf_packetOffsetInBits += 13;
hd.ipv4.ttl = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 8;
hd.ipv4.protocol = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 8;
hd.ipv4.hdrChecksum = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 16;
hd.ipv4.srcAddr = (u32)((load_word(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 32;
hd.ipv4.dstAddr = (u32)((load_word(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 32;
hd.ipv4.ebpf_valid = 1;
switch (hd.ipv4.protocol) {
case 1: goto parse_icmp;
default: goto accept;
}
}
parse_icmp: {
/* extract(hd.icmp)*/
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 32)) {
ebpf_errorCode = PacketTooShort;
goto reject;
}
hd.icmp.type_ = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 8;
hd.icmp.code = (u8)((load_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 8;
hd.icmp.hdrChecksum = (u16)((load_half(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits))));
ebpf_packetOffsetInBits += 16;
hd.icmp.ebpf_valid = 1;
goto accept;
}
reject: { return XDP_ABORTED; }
accept:
{
u8 hit;
u8 tmp_0;
{
xout.output_port = 0;
tmp_0 = (hd.ethernet.protocol != 2048);
xout.drop = tmp_0;
}
}
/* deparser */
{
{
if (hd.ipv4.ebpf_valid) ebpf_outHeaderLength += 160;
if (hd.icmp.ebpf_valid) ebpf_outHeaderLength += 32;
}
bpf_xdp_adjust_head(skb, BYTES(ebpf_packetOffsetInBits) - ebpf_outHeaderLength);
ebpf_packetStart = ((void*)(long)skb->data);
ebpf_packetEnd = ((void*)(long)skb->data_end);
ebpf_packetOffsetInBits = 0;
u8 hit_0;
{
/* packet.emit(hd.ipv4)*/
if (hd.ipv4.ebpf_valid) {
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 160)) {
ebpf_errorCode = PacketTooShort;
goto ebpf_end;
}
ebpf_byte = ((char*)(&hd.ipv4.version))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 4);
ebpf_packetOffsetInBits += 4;
ebpf_byte = ((char*)(&hd.ipv4.ihl))[0];
write_partial(ebpf_packetStart + BYTES(ebpf_packetOffsetInBits) + 0, 4, (ebpf_byte) << 4);
ebpf_packetOffsetInBits += 4;
ebpf_byte = ((char*)(&hd.ipv4.diffserv))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 8;
hd.ipv4.totalLen = htons(hd.ipv4.totalLen);
ebpf_byte = ((char*)(&hd.ipv4.totalLen))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.totalLen))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 16;
hd.ipv4.identification = htons(hd.ipv4.identification);
ebpf_byte = ((char*)(&hd.ipv4.identification))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.identification))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 16;
ebpf_byte = ((char*)(&hd.ipv4.flags))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 5);
ebpf_packetOffsetInBits += 3;
ebpf_byte = ((char*)(&hd.ipv4.fragOffset))[0];
write_partial(ebpf_packetStart + BYTES(ebpf_packetOffsetInBits) + 0, 3, (ebpf_byte) << 3);
ebpf_byte = ((char*)(&hd.ipv4.fragOffset))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 13;
ebpf_byte = ((char*)(&hd.ipv4.ttl))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 8;
ebpf_byte = ((char*)(&hd.ipv4.protocol))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 8;
hd.ipv4.hdrChecksum = htons(hd.ipv4.hdrChecksum);
ebpf_byte = ((char*)(&hd.ipv4.hdrChecksum))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.hdrChecksum))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 16;
hd.ipv4.srcAddr = htonl(hd.ipv4.srcAddr);
ebpf_byte = ((char*)(&hd.ipv4.srcAddr))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.srcAddr))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.srcAddr))[2];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.srcAddr))[3];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 32;
hd.ipv4.dstAddr = htonl(hd.ipv4.dstAddr);
ebpf_byte = ((char*)(&hd.ipv4.dstAddr))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.dstAddr))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.dstAddr))[2];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 2, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.ipv4.dstAddr))[3];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 3, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 32;
}
;
/* packet.emit(hd.icmp)*/
if (hd.icmp.ebpf_valid) {
if (ebpf_packetEnd < ebpf_packetStart + BYTES(ebpf_packetOffsetInBits + 32)) {
ebpf_errorCode = PacketTooShort;
goto ebpf_end;
}
ebpf_byte = ((char*)(&hd.icmp.type_))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 8;
ebpf_byte = ((char*)(&hd.icmp.code))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 8;
hd.icmp.hdrChecksum = htons(hd.icmp.hdrChecksum);
ebpf_byte = ((char*)(&hd.icmp.hdrChecksum))[0];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 0, (ebpf_byte) << 0);
ebpf_byte = ((char*)(&hd.icmp.hdrChecksum))[1];
write_byte(ebpf_packetStart, BYTES(ebpf_packetOffsetInBits) + 1, (ebpf_byte) << 0);
ebpf_packetOffsetInBits += 16;
}
;
}
}
ebpf_end:
bpf_map_update_elem(&ebpf_outTable, &ebpf_zero, &xout.output_port, BPF_ANY);
if (xout.drop) return XDP_DROP;
else return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment