{"id":16161412,"url":"https://github.com/gamemann/xdp-tcp-header-options","last_synced_at":"2025-07-15T20:43:01.824Z","repository":{"id":92735956,"uuid":"406531284","full_name":"gamemann/XDP-TCP-Header-Options","owner":"gamemann","description":"Repository for attempting to parse TCP header options in XDP.","archived":false,"fork":false,"pushed_at":"2021-11-10T02:26:06.000Z","size":52,"stargazers_count":18,"open_issues_count":2,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T12:25:23.660Z","etag":null,"topics":["bpf","c","header","options","parsing","tcp","xdp"],"latest_commit_sha":null,"homepage":"https://moddingcommunity.com/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gamemann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-14T21:51:37.000Z","updated_at":"2025-01-07T18:16:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"3ccf9f4d-23c2-4c3e-a488-65df546cadd9","html_url":"https://github.com/gamemann/XDP-TCP-Header-Options","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gamemann%2FXDP-TCP-Header-Options","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gamemann%2FXDP-TCP-Header-Options/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gamemann%2FXDP-TCP-Header-Options/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gamemann%2FXDP-TCP-Header-Options/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gamemann","download_url":"https://codeload.github.com/gamemann/XDP-TCP-Header-Options/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243950813,"owners_count":20373664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bpf","c","header","options","parsing","tcp","xdp"],"created_at":"2024-10-10T02:25:11.025Z","updated_at":"2025-03-18T22:30:30.114Z","avatar_url":"https://github.com/gamemann.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# XDP/BPF TCP Header Options Parsing\n## Update 11-9-2021\nAround 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\u0026m=163178833212690\u0026w=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.\n\n```C\n#define MAX_TCP_OPTIONS 10\n\nstatic __always_inline int parse_tcp_ts(struct tcphdr *tcph, void *data_end, __u32 **tsval, __u32 **tsecr)\n{\n    int len = tcph-\u003edoff \u003c\u003c 2;\n    void *opt_end = (void *)tcph + len;\n    __u8 *pos = (__u8 *)(tcph + 1);\n    __u8 i, opt;\n    volatile __u8 opt_size;\n\n    if (tcph + 1 \u003e (struct tcphdr *)data_end || len \u003c= sizeof(struct tcphdr))\n    {\n        #ifdef DEBUG\n        bpf_printk(\"parse_tcp_ts() :: tcph + 1 \u003e (struct tcphdr *)data_end || len \u003c= sizeof(struct tcphdr)\\n\");\n        #endif\n\n        return -1;\n    }\n\n    #pragma unroll\n    for (i = 0; i \u003c MAX_TCP_OPTIONS; i++) \n    {\n        if (pos + 1 \u003e (__u8 *)opt_end || pos + 1 \u003e (__u8 *)data_end)\n        {\n            #ifdef DEBUG\n            bpf_printk(\"parse_tcp_ts() :: pos + 1 \u003e (__u8 *)opt_end || pos + 1 \u003e (__u8 *)data_end\\n\");\n            #endif\n\n            return -1;\n        }\n\n        opt = *pos;\n\n        if (opt == 0)\n        {\n            #ifdef DEBUG\n            bpf_printk(\"parse_tcp_ts() :: opt == 0\\n\");\n            #endif\n\n            return -1;\n        }\n\n        if (opt == 1)\n        {\n            pos++;\n\n            continue;\n        }\n\n        if (pos + 2 \u003e (__u8 *)opt_end || pos + 2 \u003e (__u8 *)data_end)\n        {\n            #ifdef DEBUG\n            bpf_printk(\"parse_tcp_ts() :: pos + 2 \u003e (__u8 *)opt_end || pos + 2 \u003e (__u8 *)data_end\\n\");\n            #endif\n\n            return -1;\n        }\n\n        opt_size = *(pos + 1);\n\n        if (opt_size \u003c 2)\n        {\n            #ifdef DEBUG\n            bpf_printk(\"parse_tcp_ts() :: opt_size \u003c 2\\n\");\n            #endif\n\n            return -1;\n        }\n\n        if (opt == 8 \u0026\u0026 opt_size == 10) \n        {\n            if (pos + 10 \u003e (__u8 *)opt_end || pos + 10 \u003e (__u8 *)data_end)\n            {\n                #ifdef DEBUG\n                bpf_printk(\"parse_tcp_ts() :: pos + 10 \u003e (__u8 *)opt_end || pos + 10 \u003e (__u8 *)data_end\\n\");\n                #endif\n\n                return -1;\n            }\n\n            *tsval = (__u32 *)(pos + 2);\n            *tsecr = (__u32 *)(pos + 6);\n\n            return 0;\n        }\n\n        pos += opt_size;\n    }\n\n    #ifdef DEBUG\n    bpf_printk(\"parse_tcp_ts() :: Reached end (return -1).\\n\");\n    #endif\n\n    return -1;\n}\n\n...\n\n__u32 *sendval = NULL;\n__u32 *echoval = NULL;\n\nparse_tcp_ts(tcph, data_end, \u0026sendval, \u0026echoval);\n\nif (sendval != NULL \u0026\u0026 echoval != NULL)\n{\n    // Timestamps found and pointers are valid.\n}\n```\n\n## Description\nA repository to show attempts at dynamically parsing the TCP header options (specifically timestamps in this repository) within XDP/BPF.\n\n## Command Line Options\nThere are two command line options for this program which may be found below.\n\n* `-i --interface` =\u003e The interface name to attempt to attach the XDP program to (**required**).\n* `-o --obj` =\u003e A path to the BPF object file (default is `/etc/tcpopts/xdp.o` which `make install` installs to).\n\n## Building\nYou may use the following to build the program.\n\n```\n# Clone the repository and libbpf (with the --recursive flag).\ngit clone --recursive https://github.com/gamemann/XDP-TCP-Header-Options.git\n\n# Change directory to the repository.\ncd XDP-TCP-Header-Options/\n\n# Build the program.\nmake\n\n# Install the program. The program is installed to /usr/bin/tcpopts\nsudo make install\n```\n\n## Fails\nThe current code fails with the following:\n\n```\n49: (2d) if r6 \u003e r2 goto pc+24\n 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\n50: (71) r7 = *(u8 *)(r7 +0)\ninvalid access to packet, off=34 size=1, R7(id=258,off=34,r=0)\nR7 offset is outside of the packet\nprocessed 8789 insns (limit 1000000) max_states_per_insn 4 total_states 142 peak_states 142 mark_read 4\n\nlibbpf: -- END LOG --\nlibbpf: failed to load program 'xdp_prog'\nlibbpf: failed to load object '/etc/tcpopts/xdp.o'\nError loading BPF program.\n```\n\nThe full log may be found in the `logs/` directory.\n\nThe error is caused by this piece of code:\n\n```C\n// This check shouldn't be needed, but just for safe measure, perform another check before incrementing optdata by the option's length.\nif (len \u003c= (__u8 *)data_end \u0026\u0026 len \u003e= (__u8 *)data)\n{\n    optdata += (*len \u003e 0) ? *len : 1;\n}\nelse\n{\n    // Avoid an infinite loop.\n    optdata++;\n}\n```\n\nIf you stop incrementing `optdata` by `*len`, the XDP program loads. For example:\n\n```C\nif (len \u003c= (__u8 *)data_end \u0026\u0026 len \u003e= (__u8 *)data)\n{\n    optdata++;\n}\nelse\n{\n    // Avoid an infinite loop.\n    optdata++;\n}\n```\n\nLoads without any issues.\n\n## Other Notes\nI 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:\n\n\u003e Parsing variable number of TCP options is challenging for the verifier\n\nBeing 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.\n\n## Credits\n* [Christian Deacon](https://github.com/gamemann)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgamemann%2Fxdp-tcp-header-options","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgamemann%2Fxdp-tcp-header-options","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgamemann%2Fxdp-tcp-header-options/lists"}