Skip to content

Commit 287c22b

Browse files
ThisSeanZhangdanielocfb
authored andcommitted
example: Add TCP header modification example
- Replaced tabs with spaces for consistency - Removed unstable features - Modified handling of attachment result - Replaced verbose options and included trigger command in README.md
1 parent b47f48e commit 287c22b

File tree

8 files changed

+474
-0
lines changed

8 files changed

+474
-0
lines changed

examples/tcp_option/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "tcp_option"
3+
version = "0.1.0"
4+
edition = "2021"
5+
license = "LGPL-2.1-only OR BSD-2-Clause"
6+
7+
[build-dependencies]
8+
libbpf-cargo = { path = "../../libbpf-cargo" }
9+
vmlinux = { path = "../../vmlinux" }
10+
11+
[dependencies]
12+
anyhow = "1.0"
13+
libbpf-rs = { path = "../../libbpf-rs" }
14+
clap = { version = "4.0.32", features = ["derive"] }
15+
libc = "0.2"
16+
ctrlc = "3.2"

examples/tcp_option/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE.BSD-2-Clause
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../LICENSE.LGPL-2.1

examples/tcp_option/README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# TCP Header Modification Example
2+
This example focuses on modifying TCP headers, demonstrating how users can extend and manipulate network packet headers as required.
3+
4+
## Building
5+
6+
```shell
7+
$ cargo build
8+
```
9+
10+
## Usage
11+
12+
```shell
13+
$ sudo ./target/debug/tcp_option --ip <target> --trace-id <id or something>
14+
```
15+
16+
## Trigger
17+
18+
```shell
19+
# Start
20+
$ sudo ./target/debug/tcp_option --ip 127.0.0.1 --trace-id 42
21+
22+
# Start a listener
23+
$ nc -l 127.0.0.1 65000 &
24+
25+
# Start a connector
26+
$ echo test | nc 127.0.0.1 65000
27+
```
28+
29+
> Note that using 127.0.0.1 may result in a mixed output of modified TCP headers in both directions. In fact, only the response message of the machine where the program is deployed will be modified.
30+
31+
## Output
32+
33+
```shell
34+
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
35+
```
36+
37+
```text
38+
<...>-190048 [000] d..21 2094364.690447: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 524, required space: 8
39+
<...>-190048 [000] d..21 2094364.690477: bpf_trace_printk: Stored a TCP option in TCP Flag: 2
40+
<...>-190048 [000] D..21 2094364.690500: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
41+
<...>-190048 [000] D..21 2094364.690501: bpf_trace_printk: ####=> Socket TCP option data: 42
42+
<...>-190048 [000] d.s31 2094364.690507: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
43+
<...>-190048 [000] d.s31 2094364.690508: bpf_trace_printk: ####=> Socket TCP option data: 42
44+
<...>-190048 [000] d..21 2094364.690540: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32741, required space: 8
45+
<...>-190048 [000] d..21 2094364.690551: bpf_trace_printk: Stored a TCP option in TCP Flag: 16
46+
<...>-190048 [000] D..21 2094364.690556: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
47+
<...>-190048 [000] D..21 2094364.690556: bpf_trace_printk: ####=> Socket TCP option data: 42
48+
<...>-190048 [000] d.s31 2094364.690560: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
49+
<...>-190048 [000] d.s31 2094364.690562: bpf_trace_printk: ####=> Socket TCP option data: 42
50+
<...>-190048 [000] d..21 2094364.690632: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32741, required space: 8
51+
<...>-190048 [000] d..21 2094364.690643: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32741, required space: 13
52+
<...>-190048 [000] d..21 2094364.690646: bpf_trace_printk: Stored a TCP option in TCP Flag: 24
53+
<...>-190048 [000] D..21 2094364.690651: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
54+
<...>-190048 [000] D..21 2094364.690654: bpf_trace_printk: ####=> Socket TCP option data: 42
55+
<...>-190048 [000] d.s31 2094364.690657: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
56+
<...>-190048 [000] d.s31 2094364.690658: bpf_trace_printk: ####=> Socket TCP option data: 42
57+
<...>-190048 [000] d.s41 2094364.690671: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
58+
<...>-190048 [000] d.s41 2094364.690673: bpf_trace_printk: Stored a TCP option in TCP Flag: 16
59+
<...>-190048 [000] D.s41 2094364.690677: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
60+
<...>-190048 [000] D.s41 2094364.690678: bpf_trace_printk: ####=> Socket TCP option data: 42
61+
<...>-190048 [000] d.s31 2094364.690681: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
62+
<...>-190048 [000] d.s31 2094364.690682: bpf_trace_printk: ####=> Socket TCP option data: 42
63+
nc-190048 [000] d..21 2094379.828439: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
64+
nc-190048 [000] d..21 2094379.828469: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
65+
nc-190048 [000] d..21 2094379.828472: bpf_trace_printk: Stored a TCP option in TCP Flag: 17
66+
nc-190048 [000] D..21 2094379.828496: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
67+
nc-190048 [000] D..21 2094379.828498: bpf_trace_printk: ####=> Socket TCP option data: 42
68+
nc-190048 [000] d.s31 2094379.828507: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
69+
nc-190048 [000] d.s31 2094379.828517: bpf_trace_printk: ####=> Socket TCP option data: 42
70+
<...>-190022 [002] d..21 2094379.828606: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
71+
<...>-190022 [002] d..21 2094379.828636: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
72+
<...>-190022 [002] d..21 2094379.828639: bpf_trace_printk: Stored a TCP option in TCP Flag: 17
73+
<...>-190022 [002] D..21 2094379.828652: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
74+
<...>-190022 [002] D..21 2094379.828653: bpf_trace_printk: ####=> Socket TCP option data: 42
75+
<...>-190022 [002] d.s31 2094379.828659: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
76+
<...>-190022 [002] d.s31 2094379.828661: bpf_trace_printk: ####=> Socket TCP option data: 42
77+
<...>-190022 [002] d.s41 2094379.828677: bpf_trace_printk: Sufficient space available to store a TCP option, total space: 32768, required space: 8
78+
<...>-190022 [002] d.s41 2094379.828690: bpf_trace_printk: Stored a TCP option in TCP Flag: 16
79+
<...>-190022 [002] D.s41 2094379.828696: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
80+
<...>-190022 [002] D.s41 2094379.828697: bpf_trace_printk: ####=> Socket TCP option data: 42
81+
<...>-190022 [002] d.s31 2094379.828717: bpf_trace_printk: ####=> Socket TCP option magic: 0xeb9f
82+
<...>-190022 [002] d.s31 2094379.828718: bpf_trace_printk: ####=> Socket TCP option data: 42
83+
```

examples/tcp_option/build.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use std::env;
2+
use std::ffi::OsStr;
3+
use std::path::PathBuf;
4+
5+
use libbpf_cargo::SkeletonBuilder;
6+
7+
const SRC: &str = "src/bpf/tcp_option.bpf.c";
8+
9+
fn main() {
10+
let out = PathBuf::from(
11+
env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set in build script"),
12+
)
13+
.join("src")
14+
.join("bpf")
15+
.join("tcp_option.skel.rs");
16+
17+
let arch = env::var("CARGO_CFG_TARGET_ARCH")
18+
.expect("CARGO_CFG_TARGET_ARCH must be set in build script");
19+
20+
SkeletonBuilder::new()
21+
.source(SRC)
22+
.clang_args([
23+
OsStr::new("-I"),
24+
vmlinux::include_path_root().join(arch).as_os_str(),
25+
])
26+
.build_and_generate(&out)
27+
.unwrap();
28+
println!("cargo:rerun-if-changed={SRC}");
29+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include "vmlinux.h"
4+
5+
#include <bpf/bpf_helpers.h>
6+
#include <bpf/bpf_tracing.h>
7+
#include <bpf/bpf_endian.h>
8+
9+
#define IP_MF 0x2000
10+
#define IP_OFFSET 0x1FFF
11+
#define ETH_HLEN 14
12+
#define ETH_P_IP 0x0800
13+
14+
#define TCP_OPTION_CODE 253
15+
#define TCP_OPTION_MAGIC 0xEB9F
16+
17+
char _license[] SEC("license") = "GPL";
18+
19+
const volatile __u32 targ_ip = 0;
20+
const volatile __u32 data_such_as_trace_id = 0;
21+
22+
struct __attribute__((packed)) tcp_option
23+
{
24+
u8 kind;
25+
u8 length;
26+
u16 magic;
27+
u32 data;
28+
};
29+
30+
static void reserve_space_for_tcp_option(struct bpf_sock_ops *skops)
31+
{
32+
u32 need_space = skops->skb_len + sizeof(struct tcp_option);
33+
if (need_space > skops->mss_cache)
34+
return;
35+
36+
bpf_printk("Sufficient space available to store a TCP option, total space: %u, required space: %u", skops->mss_cache, need_space);
37+
bpf_reserve_hdr_opt(skops, sizeof(struct tcp_option), 0);
38+
}
39+
40+
static inline void store_tcp_option_header(struct bpf_sock_ops *skops)
41+
{
42+
struct tcp_option tcp_option;
43+
struct tcphdr *th = skops->skb_data;
44+
45+
if (skops->skb_len + sizeof(struct tcp_option) > skops->mss_cache)
46+
return;
47+
48+
tcp_option.kind = TCP_OPTION_CODE;
49+
tcp_option.length = sizeof(struct tcp_option);
50+
tcp_option.magic = bpf_htons(TCP_OPTION_MAGIC);
51+
tcp_option.data = data_such_as_trace_id;
52+
bpf_store_hdr_opt(skops, &tcp_option, sizeof(tcp_option), 0);
53+
bpf_printk("Stored a TCP option in TCP Flag: %u", skops->skb_tcp_flags);
54+
}
55+
56+
SEC("sockops")
57+
int sockops_write_tcp_options(struct bpf_sock_ops *skops)
58+
{
59+
u32 l_ip, r_ip;
60+
l_ip = skops->local_ip4;
61+
r_ip = skops->remote_ip4;
62+
63+
// Check if the IP addresses match the target IP
64+
if (r_ip != targ_ip && l_ip != targ_ip) {
65+
return 1;
66+
}
67+
switch (skops->op)
68+
{
69+
// When creating a connection to another host
70+
case BPF_SOCK_OPS_TCP_CONNECT_CB:
71+
bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags | BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG);
72+
break;
73+
// When accepting a connection from another host
74+
case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB:
75+
bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags | BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG);
76+
break;
77+
// When the socket is established
78+
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB:
79+
bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags | BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG);
80+
break;
81+
// When reserving space for TCP options header
82+
case BPF_SOCK_OPS_HDR_OPT_LEN_CB:
83+
reserve_space_for_tcp_option(skops);
84+
break;
85+
// When writing TCP options header
86+
case BPF_SOCK_OPS_WRITE_HDR_OPT_CB:
87+
store_tcp_option_header(skops);
88+
break;
89+
}
90+
return 1;
91+
}
92+
93+
struct __tcphdr
94+
{
95+
__be16 source;
96+
__be16 dest;
97+
__be32 seq;
98+
__be32 ack_seq;
99+
__u16 res1 : 4, doff : 4, fin : 1, syn : 1, rst : 1, psh : 1, ack : 1, urg : 1, ece : 1, cwr : 1;
100+
__be16 window;
101+
__sum16 check;
102+
__be16 urg_ptr;
103+
};
104+
105+
static inline int ip_is_fragment(struct __sk_buff *skb, __u32 nhoff)
106+
{
107+
__u16 frag_off;
108+
109+
bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 2);
110+
frag_off = __bpf_ntohs(frag_off);
111+
return frag_off & (IP_MF | IP_OFFSET);
112+
}
113+
114+
SEC("socket")
115+
int socket_handler(struct __sk_buff *skb) {
116+
u16 proto;
117+
u32 nhoff = ETH_HLEN;
118+
u8 hdr_len;
119+
u32 tcp_hdr_start = 0;
120+
u32 ip_proto = 0;
121+
u32 l_ip, r_ip;
122+
bpf_skb_load_bytes(skb, 12, &proto, 2);
123+
proto = __bpf_ntohs(proto);
124+
if (proto != ETH_P_IP)
125+
return 0;
126+
127+
if (ip_is_fragment(skb, nhoff))
128+
return 0;
129+
130+
bpf_skb_load_bytes(skb, ETH_HLEN, &hdr_len, sizeof(hdr_len));
131+
hdr_len &= 0x0f;
132+
hdr_len *= 4;
133+
134+
135+
bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), &ip_proto, 1);
136+
137+
if (ip_proto != IPPROTO_TCP)
138+
{
139+
return 0;
140+
}
141+
142+
bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), &l_ip, 4);
143+
bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), &r_ip, 4);
144+
145+
if (r_ip == targ_ip || l_ip == targ_ip) {
146+
147+
tcp_hdr_start = nhoff + hdr_len;
148+
u8 tcp_flag;
149+
bpf_skb_load_bytes(skb, tcp_hdr_start + offsetof(struct __tcphdr, ack_seq) + 5, &tcp_flag, sizeof(tcp_flag));
150+
151+
u16 tcp_data_offset;
152+
bpf_skb_load_bytes(skb, tcp_hdr_start + offsetof(struct __tcphdr, ack_seq) + 4, &tcp_data_offset, sizeof(tcp_data_offset));
153+
154+
tcp_data_offset = __bpf_ntohs(tcp_data_offset) >> 12;
155+
tcp_data_offset *= 4;
156+
157+
u32 option_start = tcp_hdr_start + 20;
158+
u32 option_end = tcp_hdr_start + tcp_data_offset;
159+
int i = 0;
160+
for (i = 0; i < 10; i++) {
161+
u16 option_hdr;
162+
bpf_skb_load_bytes(skb, option_start, &option_hdr, sizeof(option_hdr));
163+
u8 length = option_hdr>>8;
164+
u8 kind = option_hdr & 0xff;
165+
166+
if (kind == 1) {
167+
option_start += 1;
168+
goto END;
169+
}
170+
171+
if (kind == TCP_OPTION_CODE) {
172+
u16 magic;
173+
u32 data;
174+
175+
// Load magic number from TCP option header
176+
bpf_skb_load_bytes(skb, option_start + 2, &magic, sizeof(magic));
177+
magic = __bpf_ntohs(magic);
178+
bpf_printk("####=> Socket TCP option magic: 0x%x", magic);
179+
180+
if (magic == TCP_OPTION_MAGIC) {
181+
// Load data from TCP option header
182+
bpf_skb_load_bytes(skb, option_start + 4, &data, sizeof(data));
183+
bpf_printk("####=> Socket TCP option data: %u", data);
184+
}
185+
}
186+
187+
option_start += length;
188+
END:
189+
if (option_start >= option_end) {
190+
break;
191+
}
192+
}
193+
194+
}
195+
return skb->len;
196+
}

0 commit comments

Comments
 (0)