1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <stddef.h> |
3 | #include <string.h> |
4 | #include <netinet/in.h> |
5 | #include <linux/bpf.h> |
6 | #include <linux/if_ether.h> |
7 | #include <linux/if_packet.h> |
8 | #include <linux/ip.h> |
9 | #include <linux/ipv6.h> |
10 | #include <linux/types.h> |
11 | #include <linux/socket.h> |
12 | #include <linux/tcp.h> |
13 | #include <bpf/bpf_helpers.h> |
14 | #include <bpf/bpf_endian.h> |
15 | #include "bpf_tcp_helpers.h" |
16 | #include "test_tcpbpf.h" |
17 | |
18 | struct tcpbpf_globals global = {}; |
19 | |
20 | /** |
21 | * SOL_TCP is defined in <netinet/tcp.h> while |
22 | * TCP_SAVED_SYN is defined in already included <linux/tcp.h> |
23 | */ |
24 | #ifndef SOL_TCP |
25 | #define SOL_TCP 6 |
26 | #endif |
27 | |
28 | static __always_inline int get_tp_window_clamp(struct bpf_sock_ops *skops) |
29 | { |
30 | struct bpf_sock *sk; |
31 | struct tcp_sock *tp; |
32 | |
33 | sk = skops->sk; |
34 | if (!sk) |
35 | return -1; |
36 | tp = bpf_skc_to_tcp_sock(sk); |
37 | if (!tp) |
38 | return -1; |
39 | return tp->window_clamp; |
40 | } |
41 | |
42 | SEC("sockops" ) |
43 | int bpf_testcb(struct bpf_sock_ops *skops) |
44 | { |
45 | char [sizeof(struct ipv6hdr) + sizeof(struct tcphdr)]; |
46 | struct bpf_sock_ops *reuse = skops; |
47 | struct tcphdr *thdr; |
48 | int window_clamp = 9216; |
49 | int save_syn = 1; |
50 | int rv = -1; |
51 | int v = 0; |
52 | int op; |
53 | |
54 | /* Test reading fields in bpf_sock_ops using single register */ |
55 | asm volatile ( |
56 | "%[reuse] = *(u32 *)(%[reuse] +96)" |
57 | : [reuse] "+r" (reuse) |
58 | :); |
59 | |
60 | asm volatile ( |
61 | "%[op] = *(u32 *)(%[skops] +96)" |
62 | : [op] "=r" (op) |
63 | : [skops] "r" (skops) |
64 | :); |
65 | |
66 | asm volatile ( |
67 | "r9 = %[skops];\n" |
68 | "r8 = *(u32 *)(r9 +164);\n" |
69 | "*(u32 *)(r9 +164) = r8;\n" |
70 | :: [skops] "r" (skops) |
71 | : "r9" , "r8" ); |
72 | |
73 | asm volatile ( |
74 | "r1 = %[skops];\n" |
75 | "r1 = *(u64 *)(r1 +184);\n" |
76 | "if r1 == 0 goto +1;\n" |
77 | "r1 = *(u32 *)(r1 +4);\n" |
78 | :: [skops] "r" (skops):"r1" ); |
79 | |
80 | asm volatile ( |
81 | "r9 = %[skops];\n" |
82 | "r9 = *(u64 *)(r9 +184);\n" |
83 | "if r9 == 0 goto +1;\n" |
84 | "r9 = *(u32 *)(r9 +4);\n" |
85 | :: [skops] "r" (skops):"r9" ); |
86 | |
87 | asm volatile ( |
88 | "r1 = %[skops];\n" |
89 | "r2 = *(u64 *)(r1 +184);\n" |
90 | "if r2 == 0 goto +1;\n" |
91 | "r2 = *(u32 *)(r2 +4);\n" |
92 | :: [skops] "r" (skops):"r1" , "r2" ); |
93 | |
94 | op = (int) skops->op; |
95 | |
96 | global.event_map |= (1 << op); |
97 | |
98 | switch (op) { |
99 | case BPF_SOCK_OPS_TCP_CONNECT_CB: |
100 | rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP, |
101 | &window_clamp, sizeof(window_clamp)); |
102 | global.window_clamp_client = get_tp_window_clamp(skops); |
103 | break; |
104 | case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: |
105 | /* Test failure to set largest cb flag (assumes not defined) */ |
106 | global.bad_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, 0x80); |
107 | /* Set callback */ |
108 | global.good_cb_test_rv = bpf_sock_ops_cb_flags_set(skops, |
109 | BPF_SOCK_OPS_STATE_CB_FLAG); |
110 | break; |
111 | case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: |
112 | skops->sk_txhash = 0x12345f; |
113 | v = 0xff; |
114 | rv = bpf_setsockopt(skops, SOL_IPV6, IPV6_TCLASS, &v, |
115 | sizeof(v)); |
116 | if (skops->family == AF_INET6) { |
117 | v = bpf_getsockopt(skops, IPPROTO_TCP, TCP_SAVED_SYN, |
118 | header, (sizeof(struct ipv6hdr) + |
119 | sizeof(struct tcphdr))); |
120 | if (!v) { |
121 | int offset = sizeof(struct ipv6hdr); |
122 | |
123 | thdr = (struct tcphdr *)(header + offset); |
124 | v = thdr->syn; |
125 | |
126 | global.tcp_saved_syn = v; |
127 | } |
128 | } |
129 | rv = bpf_setsockopt(skops, SOL_TCP, TCP_WINDOW_CLAMP, |
130 | &window_clamp, sizeof(window_clamp)); |
131 | |
132 | global.window_clamp_server = get_tp_window_clamp(skops); |
133 | break; |
134 | case BPF_SOCK_OPS_RTO_CB: |
135 | break; |
136 | case BPF_SOCK_OPS_RETRANS_CB: |
137 | break; |
138 | case BPF_SOCK_OPS_STATE_CB: |
139 | if (skops->args[1] == BPF_TCP_CLOSE) { |
140 | if (skops->args[0] == BPF_TCP_LISTEN) { |
141 | global.num_listen++; |
142 | } else { |
143 | global.total_retrans = skops->total_retrans; |
144 | global.data_segs_in = skops->data_segs_in; |
145 | global.data_segs_out = skops->data_segs_out; |
146 | global.bytes_received = skops->bytes_received; |
147 | global.bytes_acked = skops->bytes_acked; |
148 | } |
149 | global.num_close_events++; |
150 | } |
151 | break; |
152 | case BPF_SOCK_OPS_TCP_LISTEN_CB: |
153 | bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG); |
154 | v = bpf_setsockopt(skops, IPPROTO_TCP, TCP_SAVE_SYN, |
155 | &save_syn, sizeof(save_syn)); |
156 | /* Update global map w/ result of setsock opt */ |
157 | global.tcp_save_syn = v; |
158 | break; |
159 | default: |
160 | rv = -1; |
161 | } |
162 | skops->reply = rv; |
163 | return 1; |
164 | } |
165 | char _license[] SEC("license" ) = "GPL" ; |
166 | |