https://github.com/gamemann/xdp-tcp-header-options
Repository for attempting to parse TCP header options in XDP.
https://github.com/gamemann/xdp-tcp-header-options
bpf c header options parsing tcp xdp
Last synced: 3 months ago
JSON representation
Repository for attempting to parse TCP header options in XDP.
- Host: GitHub
- URL: https://github.com/gamemann/xdp-tcp-header-options
- Owner: gamemann
- Created: 2021-09-14T21:51:37.000Z (almost 4 years ago)
- Default Branch: master
- Last Pushed: 2021-11-10T02:26:06.000Z (over 3 years ago)
- Last Synced: 2025-02-28T12:25:23.660Z (4 months ago)
- Topics: bpf, c, header, options, parsing, tcp, xdp
- Language: C
- Homepage: https://moddingcommunity.com/
- Size: 50.8 KB
- Stars: 18
- Watchers: 2
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# XDP/BPF TCP Header Options Parsing
## Update 11-9-2021
Around a month or so ago, I asked how to locate the TCP header timestamp options inside of XDP and thanks to Toke Høiland-Jørgensen [here](https://marc.info/?l=xdp-newbies&m=163178833212690&w=2), I was able to parse the TCP header timestamp options. I ended up using a modified version of [this](https://github.com/xdp-project/bpf-examples/blob/master/pping/pping_kern.c#L83) function which can be found below.```C
#define MAX_TCP_OPTIONS 10static __always_inline int parse_tcp_ts(struct tcphdr *tcph, void *data_end, __u32 **tsval, __u32 **tsecr)
{
int len = tcph->doff << 2;
void *opt_end = (void *)tcph + len;
__u8 *pos = (__u8 *)(tcph + 1);
__u8 i, opt;
volatile __u8 opt_size;if (tcph + 1 > (struct tcphdr *)data_end || len <= sizeof(struct tcphdr))
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: tcph + 1 > (struct tcphdr *)data_end || len <= sizeof(struct tcphdr)\n");
#endifreturn -1;
}#pragma unroll
for (i = 0; i < MAX_TCP_OPTIONS; i++)
{
if (pos + 1 > (__u8 *)opt_end || pos + 1 > (__u8 *)data_end)
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: pos + 1 > (__u8 *)opt_end || pos + 1 > (__u8 *)data_end\n");
#endifreturn -1;
}opt = *pos;
if (opt == 0)
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: opt == 0\n");
#endifreturn -1;
}if (opt == 1)
{
pos++;continue;
}if (pos + 2 > (__u8 *)opt_end || pos + 2 > (__u8 *)data_end)
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: pos + 2 > (__u8 *)opt_end || pos + 2 > (__u8 *)data_end\n");
#endifreturn -1;
}opt_size = *(pos + 1);
if (opt_size < 2)
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: opt_size < 2\n");
#endifreturn -1;
}if (opt == 8 && opt_size == 10)
{
if (pos + 10 > (__u8 *)opt_end || pos + 10 > (__u8 *)data_end)
{
#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: pos + 10 > (__u8 *)opt_end || pos + 10 > (__u8 *)data_end\n");
#endifreturn -1;
}*tsval = (__u32 *)(pos + 2);
*tsecr = (__u32 *)(pos + 6);return 0;
}pos += opt_size;
}#ifdef DEBUG
bpf_printk("parse_tcp_ts() :: Reached end (return -1).\n");
#endifreturn -1;
}...
__u32 *sendval = NULL;
__u32 *echoval = NULL;parse_tcp_ts(tcph, data_end, &sendval, &echoval);
if (sendval != NULL && echoval != NULL)
{
// Timestamps found and pointers are valid.
}
```## Description
A repository to show attempts at dynamically parsing the TCP header options (specifically timestamps in this repository) within XDP/BPF.## Command Line Options
There are two command line options for this program which may be found below.* `-i --interface` => The interface name to attempt to attach the XDP program to (**required**).
* `-o --obj` => A path to the BPF object file (default is `/etc/tcpopts/xdp.o` which `make install` installs to).## Building
You may use the following to build the program.```
# Clone the repository and libbpf (with the --recursive flag).
git clone --recursive https://github.com/gamemann/XDP-TCP-Header-Options.git# Change directory to the repository.
cd XDP-TCP-Header-Options/# Build the program.
make# Install the program. The program is installed to /usr/bin/tcpopts
sudo make install
```## Fails
The current code fails with the following:```
49: (2d) if r6 > r2 goto pc+24
R0_w=inv2 R1=pkt(id=0,off=0,r=34,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=1,off=34,r=36,umax_value=60,var_off=(0x0; 0x3c)) R4=inv41 R5_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff)) R6_w=pkt(id=258,off=35,r=0,umax_value=65595,var_off=(0x0; 0x1ffff)) R7_w=pkt(id=258,off=34,r=0,umax_value=65595,var_off=(0x0; 0x1ffff)) R10=fp0
50: (71) r7 = *(u8 *)(r7 +0)
invalid access to packet, off=34 size=1, R7(id=258,off=34,r=0)
R7 offset is outside of the packet
processed 8789 insns (limit 1000000) max_states_per_insn 4 total_states 142 peak_states 142 mark_read 4libbpf: -- END LOG --
libbpf: failed to load program 'xdp_prog'
libbpf: failed to load object '/etc/tcpopts/xdp.o'
Error loading BPF program.
```The full log may be found in the `logs/` directory.
The error is caused by this piece of code:
```C
// This check shouldn't be needed, but just for safe measure, perform another check before incrementing optdata by the option's length.
if (len <= (__u8 *)data_end && len >= (__u8 *)data)
{
optdata += (*len > 0) ? *len : 1;
}
else
{
// Avoid an infinite loop.
optdata++;
}
```If you stop incrementing `optdata` by `*len`, the XDP program loads. For example:
```C
if (len <= (__u8 *)data_end && len >= (__u8 *)data)
{
optdata++;
}
else
{
// Avoid an infinite loop.
optdata++;
}
```Loads without any issues.
## Other Notes
I found an article [here](https://legacy.netdevconf.info/0x14/pub/slides/50/Issuing%20SYN%20Cookies%20in%20XDP.pdf) where it appears the creator was having similar issues. At the end, under challenges/next steps you can see:> Parsing variable number of TCP options is challenging for the verifier
Being able to parse TCP header options in XDP/BPF would be very useful in my opinion. The code I have right now has many checks that I don't think are needed, but I'm not able to tell the BPF verifier the code is safe/within the packet range for some reason when incrementing by a dynamic value.
## Credits
* [Christian Deacon](https://github.com/gamemann)