|
@@ -12,60 +12,21 @@ use std::path::PathBuf;
|
|
|
extern crate rbpf;
|
|
|
use rbpf::helpers;
|
|
|
|
|
|
-// The following example uses an ELF file that has been compiled from this C source code:
|
|
|
-//
|
|
|
-// ```
|
|
|
-// #include <linux/ip.h>
|
|
|
-// #include <linux/in.h>
|
|
|
-// #include <linux/tcp.h>
|
|
|
-// #include <linux/bpf.h>
|
|
|
-//
|
|
|
-// #define ETH_ALEN 6
|
|
|
-// #define ETH_P_IP 0x0008 /* htons(0x0800) */
|
|
|
-// #define TCP_HDR_LEN 20
|
|
|
-//
|
|
|
-// #define BLOCKED_TCP_PORT 0x9999
|
|
|
-//
|
|
|
-// struct eth_hdr {
|
|
|
-// unsigned char h_dest[ETH_ALEN];
|
|
|
-// unsigned char h_source[ETH_ALEN];
|
|
|
-// unsigned short h_proto;
|
|
|
-// };
|
|
|
-//
|
|
|
-// #define SEC(NAME) __attribute__((section(NAME), used))
|
|
|
-// SEC(".classifier")
|
|
|
-// int handle_ingress(struct __sk_buff *skb)
|
|
|
-// {
|
|
|
-// void *data = (void *)(long)skb->data;
|
|
|
-// void *data_end = (void *)(long)skb->data_end;
|
|
|
-// struct eth_hdr *eth = data;
|
|
|
-// struct iphdr *iph = data + sizeof(*eth);
|
|
|
-// struct tcphdr *tcp = data + sizeof(*eth) + sizeof(*iph);
|
|
|
-//
|
|
|
-// /* single length check */
|
|
|
-// if (data + sizeof(*eth) + sizeof(*iph) + sizeof(*tcp) > data_end)
|
|
|
-// return 0;
|
|
|
-// if (eth->h_proto != ETH_P_IP)
|
|
|
-// return 0;
|
|
|
-// if (iph->protocol != IPPROTO_TCP)
|
|
|
-// return 0;
|
|
|
-// if (tcp->source == BLOCKED_TCP_PORT || tcp->dest == BLOCKED_TCP_PORT)
|
|
|
-// return -1;
|
|
|
-// return 0;
|
|
|
-// }
|
|
|
-// ```
|
|
|
+// The following example uses an ELF file that has been compiled from the C program available in
|
|
|
+// `load_elf__block_a_port.c` in the same directory.
|
|
|
//
|
|
|
// It was compiled with the following command:
|
|
|
//
|
|
|
// ```bash
|
|
|
-// clang -O2 -emit-llvm -c block_a_port.c -o - | \ llc -march=bpf -filetype=obj -o block_a_port.o
|
|
|
+// clang -O2 -emit-llvm -c load_elf__block_a_port.c -o - | \
|
|
|
+// llc -march=bpf -filetype=obj -o load_elf__block_a_port.o
|
|
|
// ```
|
|
|
//
|
|
|
-// Once compiled, can be injected into Linux kernel, with tc for instance. Sadly, we need to bring
|
|
|
-// some modifications to the generated bytecode in order to run it: the three instructions with
|
|
|
-// opcode 0x61 load data from a packet area as 4-byte words, where we need to load it as 8-bytes
|
|
|
-// double words (0x79). The kernel does the same kind of translation before running the program,
|
|
|
-// but rbpf does not implement this.
|
|
|
+// Once compiled, this program can be injected into Linux kernel, with tc for instance. Sadly, we
|
|
|
+// need to bring some modifications to the generated bytecode in order to run it: the three
|
|
|
+// instructions with opcode 0x61 load data from a packet area as 4-byte words, where we need to
|
|
|
+// load it as 8-bytes double words (0x79). The kernel does the same kind of translation before
|
|
|
+// running the program, but rbpf does not implement this.
|
|
|
//
|
|
|
// In addition, the offset at which the pointer to the packet data is stored must be changed: since
|
|
|
// we use 8 bytes instead of 4 for the start and end addresses of the data packet, we cannot use
|
|
@@ -75,22 +36,23 @@ use rbpf::helpers;
|
|
|
// These change were applied with the following script:
|
|
|
//
|
|
|
// ```bash
|
|
|
-// xxd block_a_port.o | sed '
|
|
|
+// xxd load_elf__block_a_port.o | sed '
|
|
|
// s/6112 5000 0000 0000/7912 5000 0000 0000/ ;
|
|
|
// s/6111 4c00 0000 0000/7911 4000 0000 0000/ ;
|
|
|
-// s/6111 2200 0000 0000/7911 2200 0000 0000/' | xxd -r > block_a_port.tmp
|
|
|
-// mv block_a_port.tmp block_a_port.o
|
|
|
+// s/6111 2200 0000 0000/7911 2200 0000 0000/' | xxd -r > load_elf__block_a_port.tmp
|
|
|
+
|
|
|
+// mv load_elf__block_a_port.tmp load_elf__block_a_port.o
|
|
|
// ```
|
|
|
//
|
|
|
-// The eBPF program was placed into the `.classifier` ELF section (see C code above), which means
|
|
|
-// that you can retrieve the raw bytecode with `readelf -x .classifier block_a_port.o` or with
|
|
|
-// `objdump -s -j .classifier block_a_port.o`.
|
|
|
+// The eBPF program was placed into the `.classifier` ELF section (see C code above), which means
|
|
|
+// that you can retrieve the raw bytecode with `readelf -x .classifier load_elf__block_a_port.o` or
|
|
|
+// with `objdump -s -j .classifier load_elf__block_a_port.o`.
|
|
|
//
|
|
|
// Once the bytecode has been edited, we can load the bytecode directly from the ELF object file.
|
|
|
|
|
|
fn main() {
|
|
|
|
|
|
- let filename = "examples/block_a_port.o";
|
|
|
+ let filename = "examples/load_elf__block_a_port.o";
|
|
|
|
|
|
let path = PathBuf::from(filename);
|
|
|
let file = match elf::File::open_path(&path) {
|