Created
February 3, 2017 03:11
-
-
Save williamtu/abaeb11563872508d47cfabed23ac9ea to your computer and use it in GitHub Desktop.
xdp3.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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