├── .gitignore ├── .gitmodules ├── README.md ├── config └── src ├── dep └── config ├── ebpf ├── bpf_utils.c ├── bpf_utils.h ├── config ├── ebpf_kern.c └── ebpf_kern.obj.c ├── ngx_ebpf.c ├── ngx_ebpf.h ├── ngx_stream_ebpf_module.c └── ngx_stream_ebpf_module.h /.gitignore: -------------------------------------------------------------------------------- 1 | src/dep/.libbpf/ 2 | src/src/ebpf/*.o 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/dep/libbpf"] 2 | path = src/dep/libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nginx Stream eBPF module 2 | 3 | A module that use ebpf to accelerate nginx stream proxy forward. The usage is at the end of article. 4 | The main principle is that zero hack for Nginx source code and provide a separated module to accelerate stream proxy. 5 | Test success from nginx-1.15.0 to latest(nginx-1.27.0) with kernel version >= 4.18. 6 | 7 | Note that the latest kernel version (near 6.9) has defect with sockmap, I have send a patch to fix it, do not use 8 | latest kernel before it's merged and released. 9 | 10 | patch: https://github.com/torvalds/linux/commit/8ca2a1eeadf09862190b2810697702d803ceef2d 11 | 12 | # Performance Test 13 | 14 | Nginx configuration is supplied at the end of README. 15 | 16 | ## Direct test 17 | 18 | First we run iperf client and server on same host without any proxy. 19 | 20 | ## start iperf 21 | Run iperf server. We bind server to CPU 6 client 22 | ``` 23 | iperf3 -p 20001 -s -A 6 24 | ``` 25 | 26 | Run iperf client as download mode(-R) and bind it to CPU 5 27 | ``` 28 | [root]# iperf3 -p 20001 -c 127.0.0.1 -R -A 5 -t 100 29 | Connecting to host 127.0.0.1, port 20001 30 | Reverse mode, remote host 127.0.0.1 is sending 31 | [ 5] local 127.0.0.1 port 42726 connected to 127.0.0.1 port 20001 32 | [ ID] Interval Transfer Bitrate 33 | [ 5] 0.00-1.00 sec 8.49 GBytes 72.9 Gbits/sec 34 | [ 5] 1.00-2.00 sec 8.51 GBytes 73.1 Gbits/sec 35 | [ 5] 2.00-3.00 sec 8.51 GBytes 73.1 Gbits/sec 36 | [ 5] 3.00-4.00 sec 8.52 GBytes 73.2 Gbits/sec 37 | [ 5] 4.00-5.00 sec 8.49 GBytes 72.9 Gbits/sec 38 | [ 5] 5.00-6.00 sec 8.50 GBytes 73.0 Gbits/sec 39 | ``` 40 | Speed is up to 73 Gbits/sec. 41 | 42 | 43 | ## Native Nginx stream proxy test 44 | 45 | Now we still run iperf client and server on same host, but proxied by native Nginx stream proxy mode. 46 | 47 | 48 | ### Nginx config 49 | 50 | Forward all stream between local 0.0.0.0:10001 and remote 127.0.0.1:20001. 51 | ``` 52 | # bind worker process to CPU1 53 | worker_processes 1; 54 | worker_cpu_affinity 0001; 55 | ... 56 | # it's iperf server we runned later 57 | upstream perf_servsers { 58 | server 127.0.0.1:20001; 59 | } 60 | 61 | server { 62 | listen 10001; 63 | ebpf_enable off; 64 | proxy_pass perf_servsers; 65 | } 66 | .. 67 | ``` 68 | 69 | ### start iperf 70 | Run iperf server. We bind server to CPU6 which is different from Nginx worker process and client. 71 | ``` 72 | iperf3 -p 20001 -s -A 6 73 | ``` 74 | 75 | Run iperf client as download mode(-R) and bind it to CPU5 76 | ``` 77 | [root]# iperf3 -p 10001 -c 127.0.0.1 -R -A 5 -t 100 78 | Connecting to host 127.0.0.1, port 10001 79 | Reverse mode, remote host 127.0.0.1 is sending 80 | [ 5] local 127.0.0.1 port 39452 connected to 127.0.0.1 port 10001 81 | [ ID] Interval Transfer Bitrate 82 | [ 5] 0.00-1.00 sec 3.22 GBytes 27.6 Gbits/sec 83 | [ 5] 1.00-2.00 sec 3.24 GBytes 27.8 Gbits/sec 84 | [ 5] 2.00-3.00 sec 3.22 GBytes 27.7 Gbits/sec 85 | [ 5] 3.00-4.00 sec 3.22 GBytes 27.7 Gbits/sec 86 | [ 5] 4.00-5.00 sec 3.25 GBytes 27.9 Gbits/sec 87 | [ 5] 5.00-6.00 sec 3.24 GBytes 27.9 Gbits/sec 88 | [ 5] 6.00-7.00 sec 3.23 GBytes 27.8 Gbits/sec 89 | [ 5] 7.00-8.00 sec 3.22 GBytes 27.6 Gbits/sec 90 | [ 5] 8.00-9.00 sec 3.24 GBytes 27.8 Gbits/sec 91 | [ 5] 9.00-10.00 sec 3.23 GBytes 27.7 Gbits/sec 92 | [ 5] 10.00-11.00 sec 3.23 GBytes 27.7 Gbits/sec 93 | [ 5] 11.00-12.00 sec 3.25 GBytes 27.9 Gbits/sec 94 | [ 5] 12.00-13.00 sec 3.25 GBytes 27.9 Gbits/sec 95 | ``` 96 | 97 | The Speed now is up to 27 Gbits/sec only. 98 | 99 | 100 | CPU usage detail by `mpstat -P ALL 1`: 101 | ``` 102 | Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 103 | Average: all 0.25 0.00 3.30 0.00 0.03 0.97 0.00 0.00 0.00 95.44 104 | Average: 0 4.83 0.00 66.33 0.00 0.33 28.50 0.00 0.00 0.00 0.00 105 | Average: 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 106 | Average: 2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 107 | Average: 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 108 | Average: 4 0.00 0.00 0.00 0.00 0.17 0.00 0.00 0.00 0.00 99.83 109 | Average: 5 4.71 0.00 39.22 0.00 0.39 3.53 0.00 0.00 0.00 52.16 110 | Average: 6 0.83 0.00 20.40 0.00 0.17 0.33 0.00 0.00 0.00 78.28 111 | ``` 112 | 113 | ## eBPF accelerate for Stream proxy 114 | 115 | 116 | Forward all stream between local 0.0.0.0:10000 and remote 127.0.0.1:20001 using ebpf. 117 | ``` 118 | # bind worker process to CPU1 119 | worker_processes 1; 120 | worker_cpu_affinity 0001; 121 | ... 122 | # it's iperf server we runned later 123 | upstream perf_servsers { 124 | server 127.0.0.1:20001; 125 | } 126 | 127 | server { 128 | listen 10000; 129 | ebpf_enable on; 130 | proxy_pass perf_servsers; 131 | } 132 | .. 133 | ``` 134 | 135 | ### start iperf 136 | 137 | Run iperf server. We bind server to CPU6 which is different from Nginx worker process and client. 138 | ``` 139 | iperf3 -p 20001 -s -A 6 140 | ``` 141 | 142 | Run iperf client as download mode(-R) and bind it to CPU 5 143 | ``` 144 | [root]# iperf3 -p 10000 -c 127.0.0.1 -R -A 5 -t 10 145 | Connecting to host 127.0.0.1, port 10000 146 | Reverse mode, remote host 127.0.0.1 is sending 147 | [ 5] local 127.0.0.1 port 57144 connected to 127.0.0.1 port 10000 148 | [ ID] Interval Transfer Bitrate 149 | [ 5] 0.00-1.00 sec 3.95 GBytes 33.9 Gbits/sec 150 | [ 5] 1.00-2.00 sec 3.95 GBytes 34.0 Gbits/sec 151 | [ 5] 2.00-3.00 sec 3.99 GBytes 34.3 Gbits/sec 152 | [ 5] 3.00-4.00 sec 3.99 GBytes 34.3 Gbits/sec 153 | [ 5] 4.00-5.00 sec 3.99 GBytes 34.3 Gbits/sec 154 | [ 5] 5.00-6.00 sec 3.99 GBytes 34.3 Gbits/sec 155 | [ 5] 6.00-7.00 sec 4.00 GBytes 34.4 Gbits/sec 156 | [ 5] 7.00-8.00 sec 4.00 GBytes 34.4 Gbits/sec 157 | [ 5] 8.00-9.00 sec 3.99 GBytes 34.3 Gbits/sec 158 | [ 5] 9.00-10.00 sec 4.00 GBytes 34.3 Gbits/sec 159 | ``` 160 | 161 | The Speed is much higher than native Nginx stream proxy mode but still not meeting our expectations. 162 | 163 | CPU usage detail by `mpstat -P ALL 1`: 164 | ``` 165 | Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 166 | Average: all 0.11 0.00 3.95 0.00 0.03 1.25 0.00 0.00 0.00 94.65 167 | Average: 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 168 | Average: 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 169 | Average: 2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 170 | Average: 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 171 | Average: 4 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 172 | Average: 5 3.33 0.00 58.89 0.00 0.56 15.00 0.00 0.00 0.00 22.22 173 | Average: 6 0.50 0.00 73.00 0.00 0.50 26.00 0.00 0.00 0.00 0.00 174 | ``` 175 | 176 | It's weird that only 2 cpu works with high load which both are bounded to iperf client and server. 177 | 178 | `top` command indicate that ebpf code also run on CPU 6 179 | ``` 180 | 170941 root 20 0 0 0 0 R 30.2 0.0 0:44.01 kworker/6:2+events 181 | ``` 182 | 183 | It seems if kernel processes a packet on a specific CPU softirq, the eBPF code runs on the corresponding kthread on that CPU. 184 | 185 | 186 | I try not to bind iperf to specify cpu so that iperf server can be scheduled to different CPU. 187 | 188 | ``` 189 | iperf3 -p 20001 -s 190 | ``` 191 | 192 | 193 | ``` 194 | [root]# iperf3 -p 10000 -c 127.0.0.1 -R -t 10 195 | Connecting to host 127.0.0.1, port 10000 196 | Reverse mode, remote host 127.0.0.1 is sending 197 | [ 5] local 127.0.0.1 port 20001 connected to 127.0.0.1 port 59914 198 | [ ID] Interval Transfer Bitrate Retr Cwnd 199 | [ 5] 0.00-1.00 sec 5.49 GBytes 47.1 Gbits/sec 0 639 KBytes 200 | [ 5] 1.00-2.00 sec 6.14 GBytes 52.8 Gbits/sec 0 639 KBytes 201 | [ 5] 3.00-4.00 sec 6.11 GBytes 52.5 Gbits/sec 0 639 KBytes 202 | [ 5] 4.00-5.00 sec 6.12 GBytes 52.6 Gbits/sec 0 639 KBytes 203 | [ 5] 5.00-6.00 sec 6.12 GBytes 52.6 Gbits/sec 0 639 KBytes 204 | ``` 205 | Now the speed is what we excepted. 206 | 207 | ## support ebpf cpu affinity 208 | Currently, kernel does not support cpu affinity for sockmap. I modify the kernel and rebuild it. 209 | Still, we bind iperf server to CPU5 and iperf client to CPU6, and then set sockmap to CPU10(should be bound to the cpu as same as the `worker_cpu_affinity` specify, but I just doing the test) 210 | ``` 211 | # both bpf_map_update_elem_opts and bpf_map_update_opts are new feature. 212 | __u64 target_cpu = 10; 213 | LIBBPF_OPTS(bpf_map_update_opts, opts); 214 | opts.target_cpu = &target_cpu; 215 | //if (bpf_map_update_elem(global_ctx->sockmap_fd, &idx, &fd, BPF_ANY) < 0) { 216 | if (bpf_map_update_elem_opts(global_ctx->sockmap_fd, &idx, &fd, BPF_ANY, &opts) < 0) { 217 | ...... 218 | ``` 219 | 220 | ``` 221 | [root@]# iperf3 -p 10000 -c 127.0.0.1 -R -A 6 -t 10 222 | Connecting to host 127.0.0.1, port 10000 223 | Reverse mode, remote host 127.0.0.1 is sending 224 | [ 5] local 127.0.0.1 port 56518 connected to 127.0.0.1 port 10000 225 | [ ID] Interval Transfer Bitrate 226 | [ 5] 0.00-1.00 sec 7.76 GBytes 66.6 Gbits/sec 227 | [ 5] 1.00-2.00 sec 7.76 GBytes 66.7 Gbits/sec 228 | [ 5] 2.00-3.00 sec 7.76 GBytes 66.7 Gbits/sec 229 | [ 5] 3.00-4.00 sec 7.76 GBytes 66.7 Gbits/sec 230 | [ 5] 4.00-5.00 sec 7.76 GBytes 66.7 Gbits/sec 231 | [ 5] 5.00-6.00 sec 7.77 GBytes 66.7 Gbits/sec 232 | [ 5] 6.00-7.00 sec 7.76 GBytes 66.7 Gbits/sec 233 | [ 5] 7.00-8.00 sec 7.77 GBytes 66.7 Gbits/sec 234 | [ 5] 8.00-9.00 sec 7.76 GBytes 66.7 Gbits/sec 235 | ``` 236 | The speed is very near to the direct mode(63 Gbits/sec) 237 | 238 | The patch I send still on developing with community discussion. 239 | I'm not sure whether it can be accepted but I will try my best because it's really useful. 240 | 241 | ``` 242 | 04:14:45 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 243 | 04:14:46 PM all 0.06 0.00 7.27 0.00 0.00 1.94 0.00 0.00 0.00 90.73 244 | 04:14:46 PM 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 245 | 04:14:46 PM 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 246 | 04:14:46 PM 2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 247 | 04:14:46 PM 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 248 | 04:14:46 PM 4 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 249 | 04:14:46 PM 5 0.00 0.00 73.74 0.00 0.00 26.26 0.00 0.00 0.00 0.00 250 | 04:14:46 PM 6 2.06 0.00 93.81 0.00 0.00 4.12 0.00 0.00 0.00 0.00 251 | 04:14:46 PM 7 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 252 | 04:14:46 PM 8 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 253 | 04:14:46 PM 9 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 254 | 04:14:46 PM 10 0.00 0.00 67.68 0.00 0.00 32.32 0.00 0.00 0.00 0.00 255 | ``` 256 | and the the CPU affinity works correctly. 257 | 258 | 259 | ### why eBPF code running on the cpu which process the receive packet 260 | The stream_verdict process will save received packet into `psock->ingress_skb` and call `schedule_delayed_work` with delay 0 261 | ``` 262 | static int sk_psock_skb_redirect(struct sk_psock *from, struct sk_buff *skb) 263 | { 264 | ... 265 | skb_queue_tail(&psock_other->ingress_skb, skb); 266 | schedule_delayed_work(&psock_other->work, 0); 267 | spin_unlock_bh(&psock_other->ingress_lock); 268 | return 0; 269 | } 270 | ``` 271 | 272 | Although `queue_delayed_work` set target CPU as `WORK_CPU_UNBOUND` by default, but kernel still prefer to select current CPU to execute work. 273 | ``` 274 | * 275 | * When queueing an unbound work item to a wq, prefer local CPU if allowed 276 | * by wq_unbound_cpumask. Otherwise, round robin among the allowed ones to 277 | * avoid perturbing sensitive tasks. 278 | */ 279 | static int wq_select_unbound_cpu(int cpu) 280 | { 281 | int new_cpu; 282 | 283 | if (likely(!wq_debug_force_rr_cpu)) { 284 | if (cpumask_test_cpu(cpu, wq_unbound_cpumask)) 285 | return cpu; 286 | } else { 287 | pr_warn_once("workqueue: round-robin CPU selection forced, expect performance impact\n"); 288 | } 289 | ...... 290 | ``` 291 | Then I enable `debug_force_rr_cpu` by `echo "Y" > /sys/module/workqueue/parameters/debug_force_rr_cpu` to force kernel randomize target CPU, but find ebpf still run on the same CPU due to iperf just send large response by one stream and kernel thread is stuck in the one loop 292 | 293 | ``` 294 | static void sk_psock_backlog(struct work_struct *work) 295 | { 296 | ... 297 | while ((skb = skb_peek(&psock->ingress_skb))) { 298 | ... 299 | } 300 | ... 301 | ``` 302 | 303 | # Usage 304 | 305 | ## build dependency 306 | `clang elfutils-libelf-devel` 307 | 308 | ``` 309 | # download current module 310 | git clone --recurse-submodules https://ithub.com/mrpre/nginx-stream-ebpf-module.git 311 | 312 | # enter Nginx source tree then use --add-module to add the module you download before. 313 | # eBPF and it's dependency will be compiled at configure time 314 | ./configure --add-module=/PATH/nginx-stream-ebpf-module --with-stream --prefix=/root/nginxbuild/ 315 | 316 | # build & install 317 | make;make install 318 | 319 | ``` 320 | 321 | # Note 322 | 1. `user` directive must be used with privileged users like `root`,`admin`, etc. Alternatively you can set `/proc/sys/kernel/unprivileged_bpf_disabled` to 0 if your kernel support it to use unprivileged user. 323 | 2. Currently we have no way to install ebpf kern object to nginx install path at build time. We just use `xxd` command to convert object into C array as part of elf segment of `nginx` binary. 324 | 3. `worker_connections` must not greater than macro `EBPF_MAP_SIZE` defined in `ebpf_kern.c` 325 | 4. Because we use a separated module, most native stream proxy command become invalid. Create issue if you want. 326 | 327 | # Nginx test configuration 328 | ``` 329 | user root; 330 | worker_processes 1; 331 | worker_cpu_affinity 0001; 332 | 333 | error_log /root/nginxbuild/logs/error.log debug; 334 | 335 | events { 336 | worker_connections 1024; 337 | } 338 | 339 | stream { 340 | upstream download_servers { 341 | server 127.0.0.1:8000; 342 | } 343 | upstream upload_servers { 344 | server 127.0.0.1:8001; 345 | } 346 | upstream perf_servsers { 347 | server 127.0.0.1:20001; 348 | } 349 | 350 | # idle timeout if no data forwarded between downstream and upstream 351 | ebpf_proxy_timeout 600; 352 | # enable ebpf in main layer 353 | ebpf_enable on; 354 | 355 | server { 356 | listen 8081; 357 | proxy_pass upload_servers; 358 | } 359 | 360 | server { 361 | listen 8080; 362 | set $my_servers download_servers; 363 | proxy_pass $my_servers; 364 | } 365 | 366 | server { 367 | listen 10000; 368 | ebpf_enable on; 369 | proxy_pass perf_servsers; 370 | } 371 | 372 | server { 373 | listen 10001; 374 | ebpf_enable off; 375 | proxy_pass perf_servsers; 376 | } 377 | 378 | server { 379 | listen 30001; 380 | ebpf_enable off; 381 | ebpf_status_return; 382 | } 383 | } 384 | ``` 385 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_stream_ebpf_module 2 | ngx_module_libs= 3 | 4 | # compile libbpf and utils 5 | dep_absolute_path="$PWD/$ngx_addon_dir/src/dep" 6 | libpf_install_absolute_path= 7 | . $ngx_addon_dir/src/dep/config 8 | 9 | # ebpf obj array generate by xxd command 10 | ngx_ebpf_kern_obj_c= 11 | . $ngx_addon_dir/src/ebpf/config 12 | 13 | NGX_ADDON_STREAM_EBPF_SRCS=" \ 14 | $ngx_addon_dir/src/ngx_stream_ebpf_module.c \ 15 | $ngx_addon_dir/src/ebpf/bpf_utils.c \ 16 | $ngx_addon_dir/src/ngx_ebpf.c \ 17 | $ngx_ebpf_kern_obj_c \ 18 | " 19 | NGX_ADDON_STREAM_EBPF_INCLUDE=" \ 20 | $ngx_addon_dir/src/ \ 21 | $ngx_addon_dir/src/ebpf \ 22 | $libpf_install_absolute_path/usr/include/ \ 23 | " 24 | 25 | # compile stream module 26 | if [ $ngx_module_link = ADDON ] ; then 27 | ngx_module_type=STREAM 28 | ngx_module_name=$ngx_addon_name 29 | ngx_module_srcs=$NGX_ADDON_STREAM_EBPF_SRCS 30 | ngx_module_incs=$NGX_ADDON_STREAM_EBPF_INCLUDE 31 | ngx_module_deps=$ngx_addon_dir/src/ebpf/bpf_utils.h 32 | CORE_LIBS="$CORE_LIBS $libpf_install_absolute_path/usr/lib64/libbpf.a -lelf" 33 | #LINK_DEPS="$LINK_DEPS $libpf_install_absolute_path/usr/lib64/libbpf.a" 34 | . auto/module 35 | else 36 | echo "only support static link" 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /src/dep/config: -------------------------------------------------------------------------------- 1 | echo "compiling libbpf for stream ebpf module" 2 | 3 | libpf_install_absolute_path="$dep_absolute_path/.libbpf" 4 | cd $ngx_addon_dir/src/dep/libbpf/src && make clean && DESTDIR=$libpf_install_absolute_path BUILD_STATIC_ONLY=y make -j install && cd - -------------------------------------------------------------------------------- /src/ebpf/bpf_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | -------------------------------------------------------------------------------- /src/ebpf/bpf_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef _BPF_UTILS_H_ 2 | #define _BPF_UTILS_H_ 3 | 4 | struct ngx_sock_tuple { 5 | // network byte order 6 | __u32 laddr; 7 | __u32 raddr; 8 | // port must be host byte order otherwise it exceed the size of 2 bytes 9 | __u16 lport; 10 | __u16 rport; 11 | }; 12 | 13 | struct ngx_sock_meta { 14 | __u64 forward; 15 | }; 16 | 17 | static __always_inline __u64 ebpf_proxy_map_key(__u32 addr, __u32 port) { 18 | __u64 val = addr; 19 | return (val << 32) | port; 20 | } 21 | 22 | #if !defined(__bpf__) 23 | static __always_inline void ngx_ebpf_proxy_map_key(const struct sockaddr *remote_addr, const struct sockaddr *local_addr, struct ngx_sock_tuple *tuple) { 24 | const struct sockaddr_in *addr = (const struct sockaddr_in *)remote_addr; 25 | tuple->raddr = addr->sin_addr.s_addr; 26 | tuple->rport = ntohs(addr->sin_port); 27 | addr = (const struct sockaddr_in *)local_addr; 28 | tuple->laddr = addr->sin_addr.s_addr; 29 | tuple->lport = ntohs(addr->sin_port); 30 | } 31 | #endif 32 | #endif -------------------------------------------------------------------------------- /src/ebpf/config: -------------------------------------------------------------------------------- 1 | ngx_ebpf_kern_src="$ngx_addon_dir/src/ebpf/ebpf_kern.c" 2 | ngx_ebpf_kern_obj="$ngx_addon_dir/src/ebpf/ebpf_kern.o" 3 | ngx_ebpf_kern_obj_c="$ngx_addon_dir/src/ebpf/ebpf_kern.obj.c" 4 | ngx_ebpf_kern_obj_var_name="ebpf_kern_o" 5 | ngx_ebpf_kern_obj_var_len_name="ebpf_kern_o_len" 6 | 7 | # for btf, -g must be used 8 | if [ $NGX_DEBUG = YES ]; then 9 | DEBUG_FLAG=-DEBPF_DEBUG 10 | fi 11 | 12 | # enable it to use sockhash 13 | # USE_SOCKHASH=-DEBPF_USE_SOCKHASH 14 | 15 | # compile ebpf code 16 | set -x 17 | clang --version 18 | clang $USE_SOCKHASH $DEBUG_FLAG -I$libpf_install_absolute_path/usr/include/bpf -Wno-deprecated-declarations -target bpf -O2 -c -o $ngx_ebpf_kern_obj $ngx_ebpf_kern_src || exit 19 | 20 | # transfer ebpf object to hex array to avoid deploying seperate obj file 21 | # xxd will generate C code like this: 22 | # ' 23 | # unsigned char ebpf_kern_o[] = { 24 | # 0x7f, ...}; 25 | # unsigned int ebpf_kern_o_len = ...; 26 | # ' 27 | # we can't specify variable name unless https://github.com/vim/vim/pull/10599 released 28 | # xxd -i $ngx_ebpf_kern_obj > $ngx_ebpf_kern_obj_c 29 | echo "unsigned char $ngx_ebpf_kern_obj_var_name[] = {" > $ngx_ebpf_kern_obj_c 30 | cat $ngx_ebpf_kern_obj | xxd -i >> $ngx_ebpf_kern_obj_c 31 | echo "};" >> $ngx_ebpf_kern_obj_c 32 | obj_len=`cat $ngx_ebpf_kern_obj | xxd -i | grep -o "0x" | wc -l` 33 | echo "unsigned int $ngx_ebpf_kern_obj_var_len_name = $obj_len;" >> $ngx_ebpf_kern_obj_c 34 | set +x 35 | 36 | have=NGX_EBPF_KERN_OBJ_DEFINE value="extern unsigned char $ngx_ebpf_kern_obj_var_name[];" . auto/define 37 | have=NGX_EBPF_KERN_OBJ_LEN_DEFINE value="extern unsigned int $ngx_ebpf_kern_obj_var_len_name;" . auto/define 38 | have=NGX_EBPF_KERN_OBJ value="$ngx_ebpf_kern_obj_var_name" . auto/define 39 | have=NGX_EBPF_KERN_OBJ_LEN value="$ngx_ebpf_kern_obj_var_len_name" . auto/define 40 | -------------------------------------------------------------------------------- /src/ebpf/ebpf_kern.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "bpf_utils.h" 5 | 6 | #define EBPF_MAP_SIZE 100000 7 | #define USE_BPF_MAP 1 8 | //#define EBPF_DEBUG 1 enable by compiler 9 | //#define EBPF_USE_SOCKHASH 1 enable by compiler 10 | #ifdef USE_BPF_MAP 11 | 12 | #if LIBBPF_MAJOR_VERSION > 1 13 | struct bpf_map_def { 14 | unsigned int type; 15 | unsigned int key_size; 16 | unsigned int value_size; 17 | unsigned int max_entries; 18 | unsigned int map_flags; 19 | } __attribute__((deprecated("use BTF-defined maps in .maps section"))); 20 | #endif 21 | 22 | struct bpf_map_def SEC ("maps") proxy_map = { 23 | . type = BPF_MAP_TYPE_HASH , 24 | . key_size = sizeof(struct ngx_sock_tuple), 25 | . value_size = sizeof(__u32), 26 | . max_entries = EBPF_MAP_SIZE, 27 | }; 28 | 29 | struct bpf_map_def SEC ("maps") meta_map = { 30 | . type = BPF_MAP_TYPE_HASH , 31 | . key_size = sizeof(__u32), 32 | . value_size = sizeof(struct ngx_sock_meta), 33 | . max_entries = EBPF_MAP_SIZE, 34 | }; 35 | 36 | 37 | #ifdef EBPF_USE_SOCKHASH 38 | //see sock_hash_alloc, key size can be customized 39 | struct bpf_map_def SEC ("maps") sock_hash = { 40 | . type = BPF_MAP_TYPE_SOCKHASH , 41 | . key_size = sizeof(__u32), 42 | . value_size = sizeof(__u32), 43 | . max_entries = EBPF_MAP_SIZE, 44 | }; 45 | #else 46 | //see sock_map_alloc, key size must be u32 and value_size must be u32 or u64 47 | struct bpf_map_def SEC ("maps") sock_map = { 48 | . type = BPF_MAP_TYPE_SOCKMAP , 49 | . key_size = sizeof(__u32), 50 | . value_size = sizeof(__u32), 51 | . max_entries = EBPF_MAP_SIZE, 52 | }; 53 | #endif 54 | #elif USE_BTF_MAP 55 | 56 | struct { 57 | __uint(type, BPF_MAP_TYPE_HASH); 58 | __type(key, struct sock_tuple); 59 | __type(value, __u32); 60 | __uint(max_entries, EBPF_MAP_SIZE); 61 | } proxy_map SEC(".maps"); 62 | 63 | struct { 64 | __uint(type, BPF_MAP_TYPE_SOCKMAP); 65 | __type(key, __u32); 66 | __type(value, __u32); 67 | __uint(max_entries, EBPF_MAP_SIZE); 68 | } sock_map SEC(".maps"); 69 | #else 70 | // loader call bpf() to crete map 71 | #endif 72 | 73 | // section name: 74 | // libbpf can recognize 'sk_skb' prefix but attach type may not correct 75 | // so we specify the full section name with sk_skb/stream_xxx which hardcoded in libbpf 76 | // more details can be found under function 'sec_def_matches' and definition 'section_defs' in [libbpf] 77 | // function name: 78 | // function name will be referenced in function 'ngx_ebpf_init' and do not change it 79 | 80 | // both functions will be called when userspace add 'fd' into map like 'bpf_map_update_elem(sock_map, &idx, &fd, BPF_ANY)' 81 | // log: `cat /sys/kernel/debug/tracing/trace_pipe` 82 | 83 | // strp_data_ready 84 | // - strp_read_sock 85 | // -- strp_recv 86 | // ---- __strp_recv 87 | // ----- sk_psock_strp_read 88 | // ------ cb.parse_msg = sk_psock_strp_parse 89 | // ------ cb.rcv_msg = sk_psock_strp_read 90 | SEC ("sk_skb/stream_parser") 91 | int stream_parser (struct __sk_buff * skb) 92 | { 93 | #ifdef EBPF_DEBUG 94 | char info_fmt[] = "parse len %d\n" ; 95 | bpf_trace_printk(info_fmt , sizeof (info_fmt), skb->len); 96 | #endif 97 | return skb->len; 98 | } 99 | 100 | SEC ("sk_skb/stream_verdict") 101 | int stream_verdict(struct __sk_buff * skb) 102 | { 103 | // note that fields in skb/ctx are translated to sk field like: 104 | // remote_port <-> skc_dport, network byte order 105 | // local_port <-> skc_num, host byte order 106 | // see `bpf_convert_ctx_access` for more detail 107 | __u32 *index = 0; 108 | __u16 rport = (__u16)bpf_ntohl(skb->remote_port); 109 | __u32 rip = skb->remote_ip4; 110 | __u16 lport = (__u16)skb->local_port; 111 | __u32 lip = skb->local_ip4; 112 | struct ngx_sock_tuple tuple = { 113 | .laddr = lip, 114 | .raddr = rip, 115 | .lport = lport, 116 | .rport = rport 117 | }; 118 | 119 | index = bpf_map_lookup_elem(&proxy_map, &tuple); 120 | if (index == NULL) { 121 | #ifdef EBPF_DEBUG 122 | char info_fmt[] = "missing val %x %x\n" ; 123 | bpf_trace_printk(info_fmt , sizeof(info_fmt), lip, lport); 124 | bpf_trace_printk(info_fmt , sizeof(info_fmt), rip, rport); 125 | #endif 126 | 127 | return SK_PASS; 128 | } 129 | // for the received packet, redirect it from a receive queue of some socket 130 | // to a transmit queue of the socket living in sock_map under *index 131 | #ifdef EBPF_USE_SOCKHASH 132 | int ret = bpf_sk_redirect_hash(skb, &sock_hash, index , 0); 133 | #else 134 | int ret = bpf_sk_redirect_map(skb, &sock_map, *index , 0); 135 | #endif 136 | if (ret == SK_PASS) { 137 | struct ngx_sock_meta *meta = bpf_map_lookup_elem(&meta_map, index); 138 | if (meta == NULL) { 139 | struct ngx_sock_meta new_mata = { 140 | .forward = skb->len, 141 | }; 142 | if (bpf_map_update_elem(&meta_map, index, &new_mata, BPF_ANY)) { 143 | #ifdef EBPF_DEBUG 144 | char info_fmt[] = "create new meta fail, index %d\n" ; 145 | bpf_trace_printk(info_fmt , sizeof (info_fmt), *index); 146 | #endif 147 | } 148 | } else { 149 | __sync_fetch_and_add(&meta->forward, skb->len); 150 | //meta->forward += skb->len; 151 | } 152 | } 153 | #ifdef EBPF_DEBUG 154 | char info_fmt[] = "forward data to index [%d], ret %d\n"; 155 | bpf_trace_printk(info_fmt , sizeof (info_fmt), *index, ret); 156 | #endif 157 | return ret; 158 | } 159 | 160 | char _license [] SEC ( "license" ) = "GPL" ; 161 | -------------------------------------------------------------------------------- /src/ebpf/ebpf_kern.obj.c: -------------------------------------------------------------------------------- 1 | unsigned char ebpf_kern_o[] = { 2 | 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 3 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xf7, 0x00, 0x01, 0x00, 0x00, 0x00, 4 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 5 | 0x00, 0x00, 0x00, 0x00, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 6 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 7 | 0x0b, 0x00, 0x01, 0x00, 0xbf, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0xb7, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x6b, 0x1a, 0xfc, 0xff, 9 | 0x00, 0x00, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00, 0x6e, 0x20, 0x25, 0x64, 10 | 0x63, 0x1a, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 11 | 0x70, 0x61, 0x72, 0x73, 0x00, 0x00, 0x00, 0x00, 0x65, 0x20, 0x6c, 0x65, 12 | 0x7b, 0x1a, 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x61, 0x63, 0x00, 0x00, 13 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 14 | 0x07, 0x01, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xb7, 0x02, 0x00, 0x00, 15 | 0x0e, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 16 | 0x61, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 18 | 0x61, 0x82, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x81, 0x5c, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x61, 0x86, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x61, 0x89, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x09, 0x00, 0x00, 21 | 0x20, 0x00, 0x00, 0x00, 0x6b, 0x9a, 0xfe, 0xff, 0x00, 0x00, 0x00, 0x00, 22 | 0x6b, 0x6a, 0xfc, 0xff, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x1a, 0xb8, 0xff, 23 | 0x00, 0x00, 0x00, 0x00, 0x63, 0x1a, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00, 24 | 0x7b, 0x2a, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x63, 0x2a, 0xf4, 0xff, 25 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 26 | 0x07, 0x02, 0x00, 0x00, 0xf4, 0xff, 0xff, 0xff, 0x18, 0x01, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 | 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x07, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x55, 0x07, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x57, 0x06, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xb7, 0x01, 0x00, 0x00, 31 | 0x00, 0x00, 0x00, 0x00, 0x73, 0x1a, 0xd2, 0xff, 0x00, 0x00, 0x00, 0x00, 32 | 0xb7, 0x01, 0x00, 0x00, 0x78, 0x0a, 0x00, 0x00, 0x6b, 0x1a, 0xd0, 0xff, 33 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x76, 0x61, 0x6c, 0x20, 34 | 0x00, 0x00, 0x00, 0x00, 0x25, 0x78, 0x20, 0x25, 0x7b, 0x1a, 0xc8, 0xff, 35 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x6d, 0x69, 0x73, 0x73, 36 | 0x00, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x67, 0x20, 0x7b, 0x1a, 0xc0, 0xff, 37 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 38 | 0x07, 0x08, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xbf, 0x81, 0x00, 0x00, 39 | 0x00, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 40 | 0x79, 0xa3, 0xb0, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x64, 0x00, 0x00, 41 | 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 42 | 0x57, 0x09, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xbf, 0x81, 0x00, 0x00, 43 | 0x00, 0x00, 0x00, 0x00, 0xb7, 0x02, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 44 | 0x79, 0xa3, 0xb8, 0xff, 0x00, 0x00, 0x00, 0x00, 0xbf, 0x94, 0x00, 0x00, 45 | 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 46 | 0xb7, 0x06, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x18, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0xb7, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 51 | 0x48, 0x00, 0x00, 0x00, 0xbf, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0xbf, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00, 53 | 0x20, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 54 | 0x55, 0x01, 0x23, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0xbf, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 57 | 0x01, 0x00, 0x00, 0x00, 0x55, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 58 | 0x61, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x1a, 0xe8, 0xff, 59 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 60 | 0x07, 0x03, 0x00, 0x00, 0xe8, 0xff, 0xff, 0xff, 0x18, 0x01, 0x00, 0x00, 61 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 62 | 0xbf, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x04, 0x00, 0x00, 63 | 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 64 | 0x15, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 65 | 0x64, 0x65, 0x78, 0x20, 0x00, 0x00, 0x00, 0x00, 0x25, 0x64, 0x0a, 0x00, 66 | 0x7b, 0x1a, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 67 | 0x66, 0x61, 0x69, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x20, 0x69, 0x6e, 68 | 0x7b, 0x1a, 0xd0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 69 | 0x65, 0x77, 0x20, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x65, 0x74, 0x61, 0x20, 70 | 0x7b, 0x1a, 0xc8, 0xff, 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 71 | 0x63, 0x72, 0x65, 0x61, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x20, 0x6e, 72 | 0x7b, 0x1a, 0xc0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x61, 0x73, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x07, 0x01, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xb7, 0x02, 0x00, 0x00, 75 | 0x20, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 76 | 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x81, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0xdb, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 78 | 0xb7, 0x01, 0x00, 0x00, 0x25, 0x64, 0x0a, 0x00, 0x63, 0x1a, 0xe0, 0xff, 79 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x64, 0x5d, 0x2c, 0x20, 80 | 0x00, 0x00, 0x00, 0x00, 0x72, 0x65, 0x74, 0x20, 0x7b, 0x1a, 0xd8, 0xff, 81 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x69, 0x6e, 0x64, 0x65, 82 | 0x00, 0x00, 0x00, 0x00, 0x78, 0x20, 0x5b, 0x25, 0x7b, 0x1a, 0xd0, 0xff, 83 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x64, 0x61, 0x74, 0x61, 84 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x74, 0x6f, 0x20, 0x7b, 0x1a, 0xc8, 0xff, 85 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x66, 0x6f, 0x72, 0x77, 86 | 0x00, 0x00, 0x00, 0x00, 0x61, 0x72, 0x64, 0x20, 0x7b, 0x1a, 0xc0, 0xff, 87 | 0x00, 0x00, 0x00, 0x00, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 88 | 0xbf, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 89 | 0xc0, 0xff, 0xff, 0xff, 0xb7, 0x02, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 90 | 0xbf, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 91 | 0x06, 0x00, 0x00, 0x00, 0xbf, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 | 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 93 | 0x0c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa0, 0x86, 0x01, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 95 | 0x08, 0x00, 0x00, 0x00, 0xa0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x12, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 97 | 0xa0, 0x86, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x73, 98 | 0x65, 0x20, 0x6c, 0x65, 0x6e, 0x20, 0x25, 0x64, 0x0a, 0x00, 0x6d, 0x69, 99 | 0x73, 0x73, 0x69, 0x6e, 0x67, 0x20, 0x76, 0x61, 0x6c, 0x20, 0x25, 0x78, 100 | 0x20, 0x25, 0x78, 0x0a, 0x00, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x20, 101 | 0x6e, 0x65, 0x77, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x66, 0x61, 0x69, 102 | 0x6c, 0x2c, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x25, 0x64, 0x0a, 103 | 0x00, 0x66, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x20, 0x64, 0x61, 0x74, 104 | 0x61, 0x20, 0x74, 0x6f, 0x20, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x20, 0x5b, 105 | 0x25, 0x64, 0x5d, 0x2c, 0x20, 0x72, 0x65, 0x74, 0x20, 0x25, 0x64, 0x0a, 106 | 0x00, 0x47, 0x50, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 109 | 0x04, 0x00, 0xf1, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x04, 0x00, 0x68, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8b, 0x00, 0x00, 0x00, 113 | 0x00, 0x00, 0x04, 0x00, 0x78, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 115 | 0x00, 0x00, 0x04, 0x00, 0xd8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 116 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 117 | 0x00, 0x00, 0x04, 0x00, 0xc8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 118 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 119 | 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 120 | 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 121 | 0x12, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 122 | 0x88, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 123 | 0x11, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 125 | 0x11, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 126 | 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 127 | 0x11, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 128 | 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 129 | 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 132 | 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 133 | 0x09, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 134 | 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 135 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 136 | 0x06, 0x07, 0x08, 0x0a, 0x09, 0x0b, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 137 | 0x00, 0x2e, 0x72, 0x65, 0x6c, 0x73, 0x6b, 0x5f, 0x73, 0x6b, 0x62, 0x2f, 138 | 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x64, 0x69, 139 | 0x63, 0x74, 0x00, 0x6d, 0x61, 0x70, 0x73, 0x00, 0x73, 0x6b, 0x5f, 0x73, 140 | 0x6b, 0x62, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x70, 0x61, 141 | 0x72, 0x73, 0x65, 0x72, 0x00, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x6d, 142 | 0x61, 0x70, 0x00, 0x6d, 0x65, 0x74, 0x61, 0x5f, 0x6d, 0x61, 0x70, 0x00, 143 | 0x73, 0x6f, 0x63, 0x6b, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x00, 0x2e, 0x6c, 144 | 0x6c, 0x76, 0x6d, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x73, 0x69, 0x67, 0x00, 145 | 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x00, 0x65, 0x62, 0x70, 146 | 0x66, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x2e, 0x63, 0x00, 0x2e, 0x73, 0x74, 147 | 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x73, 0x79, 0x6d, 0x74, 0x61, 0x62, 148 | 0x00, 0x4c, 0x42, 0x42, 0x31, 0x5f, 0x38, 0x00, 0x4c, 0x42, 0x42, 0x31, 149 | 0x5f, 0x37, 0x00, 0x4c, 0x42, 0x42, 0x31, 0x5f, 0x36, 0x00, 0x4c, 0x42, 150 | 0x42, 0x31, 0x5f, 0x32, 0x00, 0x2e, 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 151 | 0x2e, 0x73, 0x74, 0x72, 0x31, 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 152 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0x4e, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 160 | 0xb6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 163 | 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 165 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 167 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x26, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 | 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 174 | 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 175 | 0x00, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 | 0x88, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 177 | 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 179 | 0x09, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 | 0x0a, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 184 | 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 186 | 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 187 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 188 | 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 189 | 0x00, 0x00, 0x00, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 190 | 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 191 | 0x00, 0x00, 0x00, 0x00, 0x7c, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 192 | 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 193 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 194 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 195 | 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 196 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe1, 0x04, 0x00, 0x00, 197 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 198 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 199 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 200 | 0x58, 0x00, 0x00, 0x00, 0x03, 0x4c, 0xff, 0x6f, 0x00, 0x00, 0x00, 0x80, 201 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 202 | 0x48, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 203 | 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 204 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 205 | 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 206 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0xe8, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 208 | 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 209 | 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 210 | 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 211 | }; 212 | unsigned int ebpf_kern_o_len = 2504; 213 | -------------------------------------------------------------------------------- /src/ngx_ebpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | NGX_EBPF_KERN_OBJ_DEFINE; 10 | NGX_EBPF_KERN_OBJ_LEN_DEFINE; 11 | 12 | // as ngx_cycle->connections are linear memory, we can use address offset as connection index 13 | static int ngx_get_connection_id(ngx_connection_t *c) { 14 | return c - ngx_cycle->connections; 15 | } 16 | 17 | static int ngx_libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) { 18 | ngx_log_t tmpLog = *ngx_cycle->log; 19 | char errstr[NGX_MAX_ERROR_STR]; 20 | char *p = errstr; 21 | // print libbpf log level 22 | #if (!NGX_DEBUG) 23 | if (level == LIBBPF_DEBUG) { 24 | return 0; 25 | } 26 | #endif 27 | int preLen = snprintf(p, NGX_MAX_ERROR_STR, "[%s] ", (level == LIBBPF_WARN)?"WARN":((level == LIBBPF_INFO)?"INFO":"DEBUG")); 28 | vsnprintf(p + preLen, NGX_MAX_ERROR_STR - preLen, format, args); 29 | 30 | // just print to error.log 31 | tmpLog.log_level = NGX_LOG_NOTICE; 32 | ngx_log_error(NGX_LOG_NOTICE, &tmpLog, 0, (char*)errstr); 33 | return 0; 34 | } 35 | 36 | void ngx_ebpf_obj_free(struct ngx_stream_ebpf_obj_ctx *global_ctx) { 37 | struct bpf_object *obj = (struct bpf_object *)global_ctx->bpf_object; 38 | bpf_object__close(obj); 39 | ngx_free(global_ctx); 40 | } 41 | 42 | int ngx_ebpf_get_meta_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, struct ngx_sock_meta *out) { 43 | int err; 44 | int connection_idx = ngx_get_connection_id(c); 45 | err = bpf_map_lookup_elem(global_ctx->meta_fd, &connection_idx, out); 46 | if (err < 0) { 47 | if (errno != ENOENT) { 48 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"bpf_map_lookup_elem meta failed"); 49 | return 1; 50 | } 51 | return 2; 52 | } 53 | return 0; 54 | } 55 | 56 | int ngx_ebpf_unregister_proxymap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_stream_ebpf_ctx_t *ctx) { 57 | int err; 58 | err = bpf_map_delete_elem(global_ctx->proxy_map_fd, ctx->client_key); 59 | if (err < 0) { 60 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete client proxy map failed, %z", err); 61 | } 62 | 63 | err = bpf_map_delete_elem(global_ctx->proxy_map_fd, ctx->upstream_key); 64 | if (err < 0) { 65 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete upstream proxy map failed, %z", err); 66 | } 67 | return 0; 68 | } 69 | 70 | int ngx_ebpf_register_proxymap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, 71 | ngx_stream_ebpf_ctx_t *ctx, ngx_connection_t *c, ngx_connection_t *pc, struct sockaddr *upstream_addr) { 72 | struct ngx_sock_tuple tuple_key; 73 | struct sockaddr addr; 74 | struct sockaddr addr2; 75 | __u32 connection_id; 76 | socklen_t socklen = sizeof(struct sockaddr); 77 | 78 | // use c->sockaddr instead 79 | if (getpeername(c->fd, &addr, &socklen)) { 80 | return NGX_ERROR; 81 | } 82 | 83 | if (getsockname(c->fd, &addr2, &socklen)) { 84 | return NGX_ERROR; 85 | } 86 | ngx_ebpf_proxy_map_key(&addr, &addr2, &tuple_key); 87 | 88 | memcpy(ctx->client_key, &tuple_key, sizeof(struct ngx_sock_tuple)); 89 | 90 | // it's also sockmap index 91 | connection_id = ngx_get_connection_id(pc); 92 | if (bpf_map_update_elem(global_ctx->proxy_map_fd, &tuple_key, &connection_id, BPF_ANY) < 0) { 93 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX" call 'bpf_map_update_elem' fail"); 94 | return NGX_ERROR; 95 | } 96 | ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, NGX_STREAM_LOG_PREFIX"client tuple_key %z %z %z %z", 97 | tuple_key.laddr,tuple_key.lport, tuple_key.raddr, tuple_key.rport); 98 | 99 | if (getsockname(pc->fd, (struct sockaddr*)&addr, &socklen)) { 100 | return NGX_ERROR; 101 | } 102 | 103 | ngx_ebpf_proxy_map_key(upstream_addr, &addr, &tuple_key); 104 | memcpy(ctx->upstream_key, &tuple_key, sizeof(struct ngx_sock_tuple)); 105 | 106 | // it's also sockmap index 107 | connection_id = ngx_get_connection_id(c); 108 | if (bpf_map_update_elem(global_ctx->proxy_map_fd, &tuple_key, &connection_id, BPF_ANY) < 0) { 109 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX" call 'bpf_map_update_elem' fail"); 110 | return NGX_ERROR; 111 | } 112 | 113 | ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0, NGX_STREAM_LOG_PREFIX"upstream tuple_key %z %z %z %z", 114 | tuple_key.laddr,tuple_key.lport, tuple_key.raddr, tuple_key.rport); 115 | 116 | return NGX_OK; 117 | } 118 | 119 | /* 120 | Actually kernel will automatically clean socket in sockmap when socket state changed to TCP_CLOSE: 121 | in kernel func tcp_set_state() 122 | case TCP_CLOSE: 123 | sk->sk_prot->unhash() 124 | 125 | 'unhash' handler is set to sock_map_unhash(): 126 | sock_map_unhash 127 | -- sock_map_remove_links 128 | ---- sock_map_unlink 129 | *link_raw = NULL 130 | where link_raw is pointer to sockmap idx addr 131 | 132 | kernel return EINVAL if sock already empty 133 | */ 134 | int ngx_ebpf_unregister_sockmap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc) { 135 | 136 | int err; 137 | __u32 idx = ngx_get_connection_id(c); 138 | err = bpf_map_delete_elem(global_ctx->map_fd, &idx); 139 | if (err < 0 && errno != EINVAL) { 140 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete client sock map failed, %d", err); 141 | } 142 | 143 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, 144 | NGX_STREAM_LOG_PREFIX"delete client sock map: %z", idx); 145 | 146 | idx = ngx_get_connection_id(pc); 147 | 148 | err = bpf_map_delete_elem(global_ctx->map_fd, &idx); 149 | if (err < 0 && errno != EINVAL) { 150 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete upstream sock map failed, %d", err); 151 | } 152 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, 153 | NGX_STREAM_LOG_PREFIX"delete upstream sock map: %z", idx); 154 | 155 | return 0; 156 | } 157 | 158 | int ngx_ebpf_unregister_metamap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc) { 159 | 160 | int err; 161 | __u32 idx = ngx_get_connection_id(c); 162 | 163 | err = bpf_map_delete_elem(global_ctx->meta_fd, &idx); 164 | if (err < 0 && errno != ENOENT) { 165 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete client meta map failed, %d", err); 166 | } 167 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, 168 | NGX_STREAM_LOG_PREFIX"delete client meta map: %z", idx); 169 | 170 | idx = ngx_get_connection_id(pc); 171 | 172 | err = bpf_map_delete_elem(global_ctx->meta_fd, &idx); 173 | if (err < 0 && errno != ENOENT) { 174 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX"delete upstream meta map failed, %d", err); 175 | } 176 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, log, 0, 177 | NGX_STREAM_LOG_PREFIX"delete upstream ent meta map: %z", idx); 178 | 179 | return 0; 180 | } 181 | 182 | int ngx_ebpf_register_sockmap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc) { 183 | 184 | // kernel function `sock_map_update_common` will replace sk's `sk_data_ready` handler with `sk_psock_strp_data_ready` 185 | __u32 idx = ngx_get_connection_id(c); 186 | __u32 fd = c->fd; 187 | //__u64 target_cpu = 10; 188 | if (bpf_map_update_elem(global_ctx->map_fd, &idx, &fd, BPF_ANY) < 0) { 189 | //if (bpf_map_update_elem_cpu(global_ctx->map_fd, &idx, &fd, BPF_ANY, &target_cpu) < 0) { 190 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX" call 'bpf_map_update_elem' fail"); 191 | return NGX_ERROR; 192 | } 193 | ngx_log_error(NGX_LOG_ERR, log, 0, NGX_STREAM_LOG_PREFIX" update sock map index %z to client fd %z", idx, fd); 194 | 195 | 196 | idx = ngx_get_connection_id(pc); 197 | fd = pc->fd; 198 | //target_cpu = 11; 199 | if (bpf_map_update_elem(global_ctx->map_fd, &idx, &fd, BPF_ANY) < 0) { 200 | //if (bpf_map_update_elem_cpu(global_ctx->map_fd, &idx, &fd, BPF_ANY, &target_cpu) < 0) { 201 | ngx_log_error(NGX_LOG_ERR, log, errno, NGX_STREAM_LOG_PREFIX" call 'bpf_map_update_elem' fail"); 202 | return NGX_ERROR; 203 | } 204 | 205 | ngx_log_error(NGX_LOG_ERR, log, 0, NGX_STREAM_LOG_PREFIX" update sock map index %z to upstream fd %z", idx, fd); 206 | 207 | return NGX_OK; 208 | } 209 | 210 | struct ngx_stream_ebpf_obj_ctx * ngx_ebpf_init(ngx_log_t *log) { 211 | struct bpf_object *obj; 212 | struct bpf_program *prog_paser; 213 | struct bpf_program *prog_redirect; 214 | struct ngx_stream_ebpf_obj_ctx *global_ctx; 215 | 216 | struct rlimit rlim = { 217 | .rlim_cur = 1024 * 1024 * 1024, 218 | .rlim_max = 1024 * 1024 * 1024, 219 | }; 220 | /* ignore error */ 221 | setrlimit(RLIMIT_MEMLOCK, &rlim); 222 | 223 | libbpf_set_print(ngx_libbpf_print_fn); 224 | 225 | if (NGX_EBPF_KERN_OBJ_LEN == 0) { 226 | ngx_log_error(NGX_LOG_EMERG, log, errno, 227 | NGX_STREAM_LOG_PREFIX" ebpf object not exist"); 228 | return NULL; 229 | } 230 | errno = 0; 231 | // it's just parse bpf binary code 232 | obj = bpf_object__open_mem(NGX_EBPF_KERN_OBJ , NGX_EBPF_KERN_OBJ_LEN , NULL); 233 | if (libbpf_get_error(obj)) { 234 | ngx_log_error(NGX_LOG_EMERG, log, errno, 235 | NGX_STREAM_LOG_PREFIX" call 'bpf_object__open_mem' fail"); 236 | return NULL; 237 | } 238 | ngx_log_error(NGX_LOG_NOTICE, log, 0, 239 | NGX_STREAM_LOG_PREFIX"open nginx stream ebpf code success"); 240 | 241 | // load prog 242 | int err = bpf_object__load(obj); 243 | if (err) { 244 | bpf_object__close(obj); 245 | ngx_log_error(NGX_LOG_EMERG, log, errno, 246 | NGX_STREAM_LOG_PREFIX"call 'bpf_object__load' fail, error code %d", err); 247 | return NULL; 248 | } 249 | 250 | ngx_log_error(NGX_LOG_INFO, log, 0, 251 | NGX_STREAM_LOG_PREFIX"load nginx stream ebpf code success"); 252 | 253 | // attach prog 254 | int sockmap_fd = bpf_object__find_map_fd_by_name(obj, "sock_map"); 255 | int sockhash_fd = bpf_object__find_map_fd_by_name(obj, "sock_hash"); 256 | int proxymap_fd = bpf_object__find_map_fd_by_name(obj, "proxy_map"); 257 | int metamap_fd = bpf_object__find_map_fd_by_name(obj, "meta_map"); 258 | int map_fd; 259 | prog_paser = bpf_object__find_program_by_name(obj, "stream_parser"); 260 | if (prog_paser == NULL) { 261 | ngx_log_error(NGX_LOG_EMERG, log, errno, 262 | NGX_STREAM_LOG_PREFIX"call 'bpf_object__find_program_by_name' fail, error code %d", err); 263 | return NULL; 264 | } 265 | 266 | if (sockmap_fd > 0) { 267 | map_fd = sockmap_fd; 268 | } else if (sockhash_fd > 0) { 269 | map_fd = sockhash_fd; 270 | } else { 271 | ngx_log_error(NGX_LOG_EMERG, log, errno, 272 | NGX_STREAM_LOG_PREFIX"can not find sockmap or sockhash in ebpf kern code"); 273 | return NULL; 274 | } 275 | 276 | err = bpf_prog_attach(bpf_program__fd(prog_paser), map_fd , BPF_SK_SKB_STREAM_PARSER, 0); 277 | if (err) { 278 | ngx_log_error(NGX_LOG_EMERG, log, errno, 279 | NGX_STREAM_LOG_PREFIX"call 'bpf_object__find_program_by_name' fail, error code %d", err); 280 | return NULL; 281 | } 282 | prog_redirect = bpf_object__find_program_by_name(obj, "stream_verdict"); 283 | if (prog_redirect == NULL) { 284 | ngx_log_error(NGX_LOG_EMERG, log, errno, 285 | NGX_STREAM_LOG_PREFIX"call 'bpf_object__find_program_by_name' fail, error code %d", err); 286 | return NULL; 287 | } 288 | 289 | err = bpf_prog_attach(bpf_program__fd(prog_redirect), map_fd , BPF_SK_SKB_STREAM_VERDICT, 0); 290 | if (err) { 291 | ngx_log_error(NGX_LOG_EMERG, log, errno, 292 | NGX_STREAM_LOG_PREFIX"call 'bpf_prog_attach' fail, error code %d", err); 293 | return NULL; 294 | } 295 | ngx_log_error(NGX_LOG_INFO, log, 0, 296 | NGX_STREAM_LOG_PREFIX"attach nginx stream ebpf code success, fd %d/%d %d %d", sockmap_fd, sockhash_fd, proxymap_fd, metamap_fd); 297 | 298 | global_ctx = ngx_alloc(sizeof(*global_ctx), log); 299 | if (global_ctx == NULL) { 300 | ngx_log_error(NGX_LOG_EMERG, log, errno, 301 | NGX_STREAM_LOG_PREFIX"alloc global_ctx fail"); 302 | return NULL; 303 | } 304 | 305 | global_ctx->bpf_object = obj; 306 | global_ctx->proxy_map_fd = proxymap_fd; 307 | global_ctx->sockmap_fd = sockmap_fd; 308 | global_ctx->sockhash_fd = sockhash_fd; 309 | global_ctx->map_fd = map_fd; 310 | global_ctx->meta_fd = metamap_fd; 311 | global_ctx->prog_parser_fd = bpf_program__fd(prog_paser); 312 | global_ctx->prog_redirect_fd = bpf_program__fd(prog_redirect); 313 | return global_ctx; 314 | } 315 | -------------------------------------------------------------------------------- /src/ngx_ebpf.h: -------------------------------------------------------------------------------- 1 | struct ngx_stream_ebpf_obj_ctx* ngx_ebpf_init(); 2 | void ngx_ebpf_obj_free(struct ngx_stream_ebpf_obj_ctx *global_ctx); 3 | int ngx_ebpf_register_proxymap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_stream_ebpf_ctx_t *ctx, ngx_connection_t *c, ngx_connection_t *pc ,struct sockaddr *upstream_addr); 4 | int ngx_ebpf_register_sockmap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc); 5 | int ngx_ebpf_unregister_proxymap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_stream_ebpf_ctx_t *ctx); 6 | int ngx_ebpf_unregister_sockmap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc); 7 | int ngx_ebpf_unregister_metamap_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, ngx_connection_t *pc); 8 | 9 | 10 | int ngx_ebpf_get_meta_fd(ngx_log_t *log, struct ngx_stream_ebpf_obj_ctx *global_ctx, ngx_connection_t *c, struct ngx_sock_meta *out); -------------------------------------------------------------------------------- /src/ngx_stream_ebpf_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | typedef struct { 7 | struct ngx_stream_ebpf_obj_ctx *global_ctx; 8 | ngx_flag_t ebpf_enable; 9 | ngx_stream_content_handler_pt handler; 10 | ngx_resolver_handler_pt resolver_handler; 11 | ngx_uint_t timeout; 12 | ngx_uint_t buffer_size; 13 | ngx_uint_t timer_period; 14 | ngx_str_t addr; 15 | } ngx_stream_ebpf_srv_conf_t; 16 | 17 | 18 | static ngx_int_t 19 | ngx_stream_ebpf_variable(ngx_stream_session_t *s, 20 | ngx_stream_variable_value_t *v, uintptr_t data); 21 | static ngx_int_t ngx_stream_ebpf_add_variables(ngx_conf_t *cf); 22 | static void *ngx_stream_ebpf_create_srv_conf(ngx_conf_t *cf); 23 | static char *ngx_stream_ebpf_merge_srv_conf(ngx_conf_t *cf, 24 | void *parent, void *child); 25 | static ngx_int_t ngx_stream_ebpf_init(ngx_conf_t *cf); 26 | static char *ngx_stream_ebpf_status_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 27 | static void ngx_stream_ebpf_proxy_downstream_handler(ngx_event_t *ev); 28 | static void ngx_stream_ebpf_proxy_upstream_handler(ngx_event_t *ev); 29 | static void ngx_stream_ebpf_proxy_upstream_connect_handler(ngx_event_t *ev); 30 | static void ngx_stream_ebpf_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc); 31 | static void ngx_stream_ebpf_proxy_process(ngx_event_t *ev, ngx_int_t from_upstream); 32 | static void ngx_stream_ebpf_timer_handler(ngx_event_t *ev); 33 | 34 | 35 | ngx_int_t ngx_stream_ebpf_init_process(ngx_cycle_t *cycle); 36 | static ngx_command_t ngx_stream_ebpf_commands[] = { 37 | 38 | // show debug info 39 | { ngx_string("ebpf_status_return"), 40 | NGX_STREAM_SRV_CONF|NGX_CONF_NOARGS, 41 | ngx_stream_ebpf_status_return, 42 | NGX_STREAM_SRV_CONF_OFFSET, 43 | 0, 44 | NULL }, 45 | 46 | { ngx_string("ebpf_enable"), 47 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 48 | ngx_conf_set_flag_slot, 49 | NGX_STREAM_SRV_CONF_OFFSET, 50 | offsetof(ngx_stream_ebpf_srv_conf_t, ebpf_enable), 51 | NULL }, 52 | 53 | { ngx_string("ebpf_proxy_timeout"), 54 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 55 | ngx_conf_set_msec_slot, 56 | NGX_STREAM_SRV_CONF_OFFSET, 57 | offsetof(ngx_stream_ebpf_srv_conf_t, timeout), 58 | NULL }, 59 | 60 | { ngx_string("ebpf_proxy_buffer_size"), 61 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 62 | ngx_conf_set_msec_slot, 63 | NGX_STREAM_SRV_CONF_OFFSET, 64 | offsetof(ngx_stream_ebpf_srv_conf_t, buffer_size), 65 | NULL }, 66 | 67 | { ngx_string("ebpf_timer_period"), 68 | NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1, 69 | ngx_conf_set_msec_slot, 70 | NGX_STREAM_SRV_CONF_OFFSET, 71 | offsetof(ngx_stream_ebpf_srv_conf_t, timer_period), 72 | NULL }, 73 | ngx_null_command 74 | }; 75 | 76 | static ngx_stream_module_t ngx_stream_ebpf_module_ctx = { 77 | ngx_stream_ebpf_add_variables, /* preconfiguration */ 78 | ngx_stream_ebpf_init, /* postconfiguration */ 79 | NULL, /* create main configuration */ 80 | NULL, /* init main configuration */ 81 | ngx_stream_ebpf_create_srv_conf, /* create server configuration */ 82 | ngx_stream_ebpf_merge_srv_conf /* merge server configuration */ 83 | }; 84 | 85 | ngx_module_t ngx_stream_ebpf_module = { 86 | NGX_MODULE_V1, 87 | &ngx_stream_ebpf_module_ctx, /* module context */ 88 | ngx_stream_ebpf_commands, /* module directives */ 89 | NGX_STREAM_MODULE, /* module type */ 90 | NULL, /* init master */ 91 | NULL, /* init module */ 92 | ngx_stream_ebpf_init_process, /* init process */ 93 | NULL, /* init thread */ 94 | NULL, /* exit thread */ 95 | NULL, /* exit process */ 96 | NULL, /* exit master */ 97 | NGX_MODULE_V1_PADDING 98 | }; 99 | 100 | static ngx_stream_variable_t ngx_stream_ebpf_vars[] = { 101 | 102 | { ngx_string("ebpf_var"), NULL, 103 | ngx_stream_ebpf_variable, 0, 0, 0 }, 104 | 105 | ngx_stream_null_variable 106 | }; 107 | 108 | static ngx_int_t 109 | ngx_stream_ebpf_variable(ngx_stream_session_t *s, 110 | ngx_stream_variable_value_t *v, uintptr_t data) 111 | { 112 | //ngx_stream_ebpf_ctx_t *ctx; 113 | return NGX_OK; 114 | } 115 | 116 | 117 | static ngx_int_t 118 | ngx_stream_ebpf_add_variables(ngx_conf_t *cf) 119 | { 120 | ngx_stream_variable_t *var, *v; 121 | 122 | for (v = ngx_stream_ebpf_vars; v->name.len; v++) { 123 | var = ngx_stream_add_variable(cf, &v->name, v->flags); 124 | if (var == NULL) { 125 | return NGX_ERROR; 126 | } 127 | 128 | var->get_handler = v->get_handler; 129 | var->data = v->data; 130 | } 131 | 132 | return NGX_OK; 133 | } 134 | 135 | 136 | static void * 137 | ngx_stream_ebpf_create_srv_conf(ngx_conf_t *cf) 138 | { 139 | ngx_stream_ebpf_srv_conf_t *conf; 140 | 141 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ebpf_srv_conf_t)); 142 | if (conf == NULL) { 143 | return NULL; 144 | } 145 | 146 | conf->ebpf_enable = NGX_CONF_UNSET; 147 | conf->global_ctx = NGX_CONF_UNSET_PTR; 148 | conf->timeout = NGX_CONF_UNSET; 149 | conf->timer_period = NGX_CONF_UNSET; 150 | conf->buffer_size = NGX_CONF_UNSET; 151 | return conf; 152 | } 153 | 154 | 155 | static char * 156 | ngx_stream_ebpf_merge_srv_conf(ngx_conf_t *cf, void *parent, 157 | void *child) 158 | { 159 | ngx_stream_ebpf_srv_conf_t *prev = parent; 160 | ngx_stream_ebpf_srv_conf_t *conf = child; 161 | 162 | ngx_conf_merge_value(conf->ebpf_enable, prev->ebpf_enable, 0); 163 | ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 10 * 60000); 164 | ngx_conf_merge_msec_value(conf->timer_period, prev->timer_period, 2000); 165 | ngx_conf_merge_msec_value(conf->buffer_size, prev->buffer_size, 16384); 166 | 167 | return NGX_CONF_OK; 168 | } 169 | 170 | 171 | static void 172 | ngx_stream_ebpf_status_return_handler(ngx_stream_session_t *s) 173 | { 174 | ngx_str_t text; 175 | ngx_buf_t *b; 176 | ngx_connection_t *c; 177 | ngx_chain_t *chain; 178 | c = s->connection; 179 | b = ngx_calloc_buf(c->pool); 180 | if (b == NULL) { 181 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 182 | return; 183 | } 184 | 185 | text.data = (u_char*)"aaa"; 186 | text.len = 3; 187 | b->memory = 1; 188 | b->pos = text.data; 189 | b->last = text.data + text.len; 190 | b->last_buf = 1; 191 | 192 | chain = ngx_alloc_chain_link(c->pool); 193 | if (chain == NULL) { 194 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 195 | return; 196 | } 197 | 198 | chain->buf = b; 199 | chain->next = NULL; 200 | 201 | if (ngx_stream_top_filter(s, chain, 1) == NGX_ERROR) { 202 | ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 203 | return; 204 | } 205 | } 206 | 207 | 208 | static char * 209 | ngx_stream_ebpf_status_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 210 | { 211 | ngx_stream_core_srv_conf_t *cscf; 212 | cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module); 213 | cscf->handler = ngx_stream_ebpf_status_return_handler; 214 | return NGX_CONF_OK; 215 | } 216 | 217 | static void 218 | ngx_stream_ebpf_proxy_resolve_handler(ngx_resolver_ctx_t *ctx) 219 | { 220 | ngx_connection_t *c, *pc; 221 | ngx_stream_session_t *s; 222 | ngx_stream_upstream_t *u; 223 | 224 | s = ctx->data; 225 | u = s->upstream; 226 | c = s->connection; 227 | ngx_log_error(NGX_LOG_INFO, c->log, 0, NGX_STREAM_LOG_PREFIX"resolve handler"); 228 | 229 | ngx_stream_ebpf_srv_conf_t *escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 230 | escf->resolver_handler(ctx); 231 | 232 | c->read->handler = ngx_stream_ebpf_proxy_downstream_handler; 233 | c->write->handler = ngx_stream_ebpf_proxy_downstream_handler; 234 | 235 | u = s->upstream; 236 | pc = u->peer.connection; 237 | pc->read->handler = ngx_stream_ebpf_proxy_upstream_connect_handler; 238 | pc->write->handler = ngx_stream_ebpf_proxy_upstream_connect_handler; 239 | 240 | return; 241 | } 242 | 243 | static void 244 | ngx_stream_ebpf_timer_handler(ngx_event_t *ev) { 245 | ngx_connection_t *c, *pc; 246 | ngx_stream_session_t *s; 247 | ngx_stream_upstream_t *u; 248 | ngx_stream_ebpf_srv_conf_t *escf; 249 | ngx_uint_t traffic; 250 | struct ngx_sock_meta meta; 251 | 252 | c = ev->data; 253 | s = c->data; 254 | u = s->upstream; 255 | pc = u->peer.connection; 256 | 257 | escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 258 | ngx_log_debug(NGX_LOG_ERR, c->log, 0, "ngx_stream_ebpf_timer_handler"); 259 | 260 | traffic = 0; 261 | // fixme: 262 | // the traffic info forwarded by userspace is erased by meta 263 | if (!ngx_ebpf_get_meta_fd(c->log, escf->global_ctx, c, &meta)) { 264 | if (c->sent != (off_t)meta.forward 265 | || u->received != (off_t)meta.forward) { 266 | traffic = 1; 267 | } 268 | c->sent = meta.forward; 269 | u->received = meta.forward; 270 | } 271 | 272 | if (!ngx_ebpf_get_meta_fd(c->log, escf->global_ctx, pc, &meta)) { 273 | if (pc->sent != (off_t)meta.forward 274 | || s->received != (off_t)meta.forward) { 275 | traffic = 1; 276 | } 277 | pc->sent = meta.forward; 278 | s->received = meta.forward; 279 | } 280 | 281 | if (traffic) { 282 | ngx_event_add_timer(pc->write, escf->timeout); 283 | } 284 | ngx_add_timer(c->write, escf->timer_period); 285 | } 286 | 287 | static void 288 | ngx_stream_ebpf_proxy_downstream_handler(ngx_event_t *ev) 289 | { 290 | ngx_connection_t *c, *pc; 291 | ngx_stream_session_t *s; 292 | ngx_buf_t *b; 293 | ngx_stream_upstream_t *u; 294 | ngx_stream_ebpf_ctx_t *ctx; 295 | ssize_t n; 296 | size_t size; 297 | ngx_int_t readed; 298 | c = ev->data; 299 | s = c->data; 300 | u = s->upstream; 301 | b = &u->downstream_buf; 302 | pc = u->peer.connection; 303 | 304 | ngx_log_error(NGX_LOG_INFO, c->log, 0, 305 | NGX_STREAM_LOG_PREFIX 306 | "downstream handler"); 307 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_ebpf_module); 308 | readed = ctx->client_read; 309 | for(;;) { 310 | b = &u->downstream_buf; 311 | size = b->last - b->pos; 312 | if (size == 0) { 313 | b->pos = b->start; 314 | b->last = b->start; 315 | } 316 | size = b->end - b->last; 317 | n = c->recv(c, b->last, size); 318 | if (n == NGX_AGAIN) { 319 | break; 320 | } 321 | 322 | if (n <= 0) { 323 | ngx_log_error(NGX_LOG_INFO, c->log, n, "downstream closed"); 324 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 325 | return; 326 | } 327 | b->last += n; 328 | ctx->client_read += n; 329 | } 330 | 331 | // readed == 0 means client 332 | if (readed == 0 && u->connected && pc) { 333 | ngx_stream_ebpf_proxy_process(pc->read, 0); 334 | } 335 | 336 | return; 337 | } 338 | 339 | 340 | static ngx_int_t 341 | ngx_stream_proxy_test_connect(ngx_connection_t *c) 342 | { 343 | int err; 344 | socklen_t len; 345 | 346 | #if (NGX_HAVE_KQUEUE) 347 | 348 | if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) { 349 | err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno; 350 | 351 | if (err) { 352 | (void) ngx_connection_error(c, err, 353 | "kevent() reported that connect() failed"); 354 | return NGX_ERROR; 355 | } 356 | 357 | } else 358 | #endif 359 | { 360 | err = 0; 361 | len = sizeof(int); 362 | 363 | /* 364 | * BSDs and Linux return 0 and set a pending error in err 365 | * Solaris returns -1 and sets errno 366 | */ 367 | 368 | if (getsockopt(c->fd, SOL_SOCKET, SO_ERROR, (void *) &err, &len) 369 | == -1) 370 | { 371 | err = ngx_socket_errno; 372 | } 373 | 374 | if (err) { 375 | (void) ngx_connection_error(c, err, "connect() failed"); 376 | return NGX_ERROR; 377 | } 378 | } 379 | 380 | return NGX_OK; 381 | } 382 | 383 | static void 384 | ngx_stream_ebpf_proxy_upstream_handler(ngx_event_t *ev) 385 | { 386 | ngx_stream_ebpf_proxy_process(ev, 1); 387 | } 388 | 389 | // from_upstream means it's from downstream handler 390 | // ev always the pc's ev 391 | static void 392 | ngx_stream_ebpf_proxy_process(ngx_event_t *ev, ngx_int_t from_upstream) 393 | { 394 | //ngx_stream_ebpf_srv_conf_t *escf; 395 | ngx_connection_t *pc, *c; 396 | ngx_stream_session_t *s; 397 | ngx_stream_upstream_t *u; 398 | ngx_stream_ebpf_ctx_t *ctx; 399 | ngx_stream_ebpf_srv_conf_t *escf; 400 | ngx_buf_t *b; 401 | ssize_t n,size; 402 | 403 | 404 | pc = ev->data; 405 | s = pc->data; 406 | c = s->connection; 407 | u = s->upstream; 408 | 409 | ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0, "ngx_stream_ebpf_proxy_process s %p from_upstream %z", s, from_upstream); 410 | 411 | if (pc->close) { 412 | ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "shutdown timeout"); 413 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 414 | return; 415 | } 416 | 417 | if (ev->timedout) { 418 | ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "timed out"); 419 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 420 | return; 421 | } 422 | 423 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_ebpf_module); 424 | escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 425 | 426 | // whenerer it read from, just send it to peer, and remain on fly data will be forwarded by ebpf 427 | 428 | // I know it's ugly but it works... 429 | for(;;) { 430 | // read from client 431 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, c->log, 0, "ngx_stream_ebpf_proxy_process forwarding data from client"); 432 | 433 | b = &u->downstream_buf; 434 | size = b->last - b->pos; 435 | if (size == 0) { 436 | b->pos = b->start; 437 | b->last = b->start; 438 | size = b->end - b->last; 439 | n = c->recv(c, b->last, size); 440 | if (n == NGX_AGAIN) { 441 | // no data, or typically s->connection has not been added to ep yet 442 | if (ctx->client_read == 0) { 443 | if (ngx_handle_read_event(c->read, 0) != NGX_OK) { 444 | ngx_log_error(NGX_LOG_NOTICE, c->log, n, "client ngx_handle_read_event fail from_upstream"); 445 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 446 | } 447 | // wait trigger downstream handler 448 | } 449 | break; 450 | } 451 | 452 | if (n <= 0) { 453 | ngx_log_error(NGX_LOG_NOTICE, c->log, n, "fail to read first packet from client"); 454 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 455 | return; 456 | } 457 | b->last += n; 458 | ctx->client_read += n; 459 | } 460 | 461 | size = b->last - b->pos; 462 | n = pc->send(pc, b->pos, (ssize_t)size); 463 | if (n <= 0 || n != size) { 464 | ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "send first packet fail from client to upstream"); 465 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 466 | return; 467 | } 468 | b->pos += n; 469 | } 470 | 471 | for(;;) { 472 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, c->log, 0, "ngx_stream_ebpf_proxy_process forwarding data from upstream"); 473 | 474 | 475 | if (u->upstream_buf.start == NULL) { 476 | u_char *p= ngx_pnalloc(c->pool, escf->buffer_size); 477 | if (p == NULL) { 478 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR); 479 | return; 480 | } 481 | 482 | u->upstream_buf.start = p; 483 | u->upstream_buf.end = p + escf->buffer_size; 484 | u->upstream_buf.pos = p; 485 | u->upstream_buf.last = p; 486 | } 487 | b = &u->upstream_buf; 488 | 489 | size = b->last - b->pos; 490 | if (size == 0) { 491 | b->pos = b->start; 492 | b->last = b->start; 493 | size = b->end - b->last; 494 | n = pc->recv(pc, b->last, size); 495 | if (n == NGX_AGAIN) { 496 | break; 497 | } 498 | 499 | if (n <= 0) { 500 | ngx_log_error(NGX_LOG_NOTICE, c->log, n, "fail to read first packet from upstream, %d", n); 501 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 502 | return; 503 | } 504 | b->last += n; 505 | } 506 | 507 | size = b->last - b->pos; 508 | n = c->send(c, b->pos, (ssize_t)size); 509 | if (n <= 0 || n != size) { 510 | ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, "send first packet fail from upstream to client"); 511 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 512 | return; 513 | } 514 | b->pos += n; 515 | } 516 | 517 | // idle time out, set one side event is enough 518 | // will be flesh by ngx_stream_ebpf_timer_handler 519 | ngx_event_add_timer(pc->write, escf->timeout); 520 | 521 | // pc handler used to update meta info like traffic 522 | if (!c->write->timer_set) { 523 | (void)ngx_stream_ebpf_timer_handler; 524 | c->write->handler = ngx_stream_ebpf_timer_handler; 525 | ngx_add_timer(c->write, escf->timer_period); 526 | } 527 | 528 | if (ev->pending_eof) { 529 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, c->log, 0, "EPOLLRDHUP detected, close it"); 530 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_OK); 531 | return; 532 | } 533 | 534 | return; 535 | } 536 | 537 | static void 538 | ngx_stream_ebpf_proxy_upstream_connect_handler(ngx_event_t *ev) 539 | { 540 | ngx_connection_t *c, *pc; 541 | ngx_stream_session_t *s; 542 | ngx_stream_ebpf_srv_conf_t *escf; 543 | ngx_stream_ebpf_ctx_t *ctx; 544 | ngx_stream_upstream_t *u; 545 | int err; 546 | pc = ev->data; 547 | s = pc->data; 548 | c = s->connection; 549 | u = s->upstream; 550 | ngx_log_error(NGX_LOG_NOTICE, pc->log, 0, NGX_STREAM_LOG_PREFIX"upstream connect handler"); 551 | 552 | if (ev->timedout) { 553 | ngx_log_error(NGX_LOG_ERR, pc->log, NGX_ETIMEDOUT, "upstream timed out"); 554 | //ngx_stream_proxy_next_upstream(s); 555 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); 556 | return; 557 | } 558 | 559 | if (ngx_stream_proxy_test_connect(pc) != NGX_OK) { 560 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); 561 | //ngx_stream_proxy_next_upstream(s); 562 | return; 563 | } 564 | 565 | // connect timeout 566 | if (pc->write->timer_set) { 567 | ngx_del_timer(pc->write); 568 | } 569 | 570 | s->upstream->connected = 1; 571 | pc->read->handler = ngx_stream_ebpf_proxy_upstream_handler; 572 | pc->write->handler = ngx_stream_ebpf_proxy_upstream_handler; 573 | 574 | escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 575 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_ebpf_module); 576 | 577 | // sleep can be used to generate race condition 578 | // sleep(5); 579 | 580 | // combine 2 connection. it's like route table, used in 'stream_verdict' 581 | if ((err = ngx_ebpf_register_proxymap_fd(c->log, escf->global_ctx, ctx, c, pc, u->peer.sockaddr)) < 0) { 582 | ngx_log_error(NGX_LOG_ERR, c->log, 0, NGX_STREAM_LOG_PREFIX"ngx_ebpf_register_proxymap_fd fail %d", err); 583 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); 584 | return; 585 | } 586 | 587 | // make ebpf kern function waked on packeted received 588 | if ((err = ngx_ebpf_register_sockmap_fd(c->log, escf->global_ctx, c, pc)) < 0) { 589 | ngx_log_error(NGX_LOG_ERR, c->log, 0, NGX_STREAM_LOG_PREFIX"ngx_ebpf_register_sockmap_fd fail %d", err); 590 | ngx_stream_ebpf_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY); 591 | return; 592 | } 593 | ctx->ebpf_inited = 1; 594 | 595 | 596 | // race condition here, before we add sockmap before 597 | // client may already send data and kernel save it to socket receive queue so it must be processed by origin path 598 | c->log->action = "forward data by userspace"; 599 | ngx_stream_ebpf_proxy_process(ev, 1); 600 | c->log->action = "forward data by ebpf"; 601 | return; 602 | } 603 | 604 | static void 605 | ngx_stream_ebpf_finalize(ngx_stream_session_t *s) { 606 | ngx_connection_t *c, *pc; 607 | ngx_stream_upstream_t *u; 608 | ngx_stream_ebpf_ctx_t *ctx; 609 | ngx_stream_ebpf_srv_conf_t *escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 610 | 611 | c = s->connection; 612 | 613 | u = s->upstream; 614 | if (u == NULL) { 615 | return; 616 | } 617 | pc = u->peer.connection; 618 | if (pc == NULL) { 619 | return; 620 | } 621 | 622 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_ebpf_module); 623 | if (ctx->ebpf_inited) { 624 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, c->log, 0, 625 | "finalize stream ebpf fd"); 626 | ngx_ebpf_unregister_proxymap_fd(c->log, escf->global_ctx, ctx); 627 | ngx_ebpf_unregister_sockmap_fd(c->log, escf->global_ctx, c, pc); 628 | ngx_ebpf_unregister_metamap_fd(c->log, escf->global_ctx, c, pc); 629 | } 630 | } 631 | 632 | static void 633 | ngx_stream_ebpf_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc) 634 | { 635 | ngx_uint_t state; 636 | ngx_connection_t *pc; 637 | ngx_stream_upstream_t *u; 638 | 639 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, 640 | "finalize stream ebpf proxy: %i", rc); 641 | 642 | ngx_stream_ebpf_finalize(s); 643 | u = s->upstream; 644 | 645 | if (u == NULL) { 646 | goto noupstream; 647 | } 648 | 649 | if (u->resolved && u->resolved->ctx) { 650 | ngx_resolve_name_done(u->resolved->ctx); 651 | u->resolved->ctx = NULL; 652 | } 653 | 654 | pc = u->peer.connection; 655 | 656 | if (u->state) { 657 | if (u->state->response_time == (ngx_msec_t) -1) { 658 | //u->state->response_time = ngx_current_msec - u->start_time; 659 | } 660 | 661 | if (pc) { 662 | u->state->bytes_received = u->received; 663 | u->state->bytes_sent = pc->sent; 664 | } 665 | } 666 | 667 | if (u->peer.free && u->peer.sockaddr) { 668 | state = 0; 669 | 670 | if (pc && pc->type == SOCK_DGRAM 671 | && (pc->read->error || pc->write->error)) 672 | { 673 | state = NGX_PEER_FAILED; 674 | } 675 | 676 | u->peer.free(&u->peer, u->peer.data, state); 677 | u->peer.sockaddr = NULL; 678 | } 679 | 680 | if (pc) { 681 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0, 682 | "close stream proxy upstream connection: %d", pc->fd); 683 | 684 | #if (NGX_STREAM_SSL) 685 | if (pc->ssl) { 686 | pc->ssl->no_wait_shutdown = 1; 687 | (void) ngx_ssl_shutdown(pc); 688 | } 689 | #endif 690 | 691 | ngx_close_connection(pc); 692 | u->peer.connection = NULL; 693 | } 694 | 695 | noupstream: 696 | 697 | ngx_stream_finalize_session(s, rc); 698 | } 699 | 700 | static void 701 | ngx_stream_ebpf_proxy_handler(ngx_stream_session_t *s) 702 | { 703 | ngx_connection_t *c; 704 | ngx_connection_t *pc; 705 | ngx_stream_upstream_t *u; 706 | ngx_stream_ebpf_ctx_t *ctx; 707 | ngx_stream_ebpf_srv_conf_t *escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 708 | 709 | if (escf->global_ctx == NGX_CONF_UNSET_PTR) { 710 | // not inited before 711 | ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, NGX_STREAM_LOG_PREFIX"ngx_stream_ebpf_proxy_handler without ebpf object"); 712 | escf->handler(s); 713 | return; 714 | } 715 | 716 | 717 | // handler = ngx_stream_proxy_handler 718 | // We have this assumption that since the socket is set to non-blocking mode, 719 | // the 'connect' call will always return EAGAIN, which means there won't be any data read or write operations. 720 | escf->handler(s); 721 | if (s->status != 0) { 722 | // handler call ngx_stream_proxy_finalize 723 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, NGX_STREAM_LOG_PREFIX"stream proxy process failed and ignore ebpf process, status %d", s->status); 724 | return; 725 | } 726 | 727 | if (s->upstream == NULL 728 | || s->upstream->peer.connection == NULL) { 729 | ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, NGX_STREAM_LOG_PREFIX"stream proxy process not call upstream process"); 730 | return; 731 | } 732 | 733 | c = s->connection; 734 | c->read->handler = ngx_stream_ebpf_proxy_downstream_handler; 735 | c->write->handler = ngx_stream_ebpf_proxy_downstream_handler; 736 | 737 | u = s->upstream; 738 | pc = u->peer.connection; 739 | pc->read->handler = ngx_stream_ebpf_proxy_upstream_connect_handler; 740 | pc->write->handler = ngx_stream_ebpf_proxy_upstream_connect_handler; 741 | 742 | if (u->resolved && u->resolved->ctx) { 743 | escf->resolver_handler = u->resolved->ctx->handler; 744 | u->resolved->ctx->handler = ngx_stream_ebpf_proxy_resolve_handler; 745 | } 746 | 747 | ctx = ngx_stream_get_module_ctx(s, ngx_stream_ebpf_module); 748 | if (ctx == NULL) { 749 | ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ebpf_ctx_t)); 750 | if (ctx == NULL) { 751 | return; 752 | } 753 | 754 | ngx_stream_set_ctx(s, ctx, ngx_stream_ebpf_module); 755 | ctx->client_read = 0; 756 | } 757 | return; 758 | } 759 | 760 | 761 | ngx_int_t ngx_stream_ebpf_init_process(ngx_cycle_t *cycle) { 762 | 763 | ngx_uint_t s; 764 | ngx_stream_core_srv_conf_t *cscf, **cscfp; 765 | ngx_stream_ebpf_srv_conf_t *escf; 766 | ngx_stream_core_main_conf_t *cmcf; 767 | 768 | cmcf = ngx_stream_cycle_get_module_main_conf(cycle, ngx_stream_core_module); 769 | cscfp = cmcf->servers.elts; 770 | 771 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, cycle->log, 0, NGX_STREAM_LOG_PREFIX"ngx_stream_ebpf_init_process from_upstream"); 772 | 773 | for (s = 0; s < cmcf->servers.nelts; s++) { 774 | cscf = cscfp[s]->ctx->srv_conf[ngx_stream_core_module.ctx_index]; 775 | escf = cscfp[s]->ctx->srv_conf[ngx_stream_ebpf_module.ctx_index]; 776 | 777 | if (!escf->ebpf_enable) { 778 | ngx_log_debug1(NGX_LOG_DEBUG_STREAM, cycle->log, 0, NGX_STREAM_LOG_PREFIX"ebpf_enable not enabled, escf %p", escf); 779 | return NGX_OK; 780 | } 781 | #if nginx_version >= 1025004 782 | ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, NGX_STREAM_LOG_PREFIX"%V server ebpf enabled, now init it", &escf->addr); 783 | #else 784 | ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, NGX_STREAM_LOG_PREFIX"finding server ebpf enabled, now init it"); 785 | #endif 786 | escf->global_ctx = ngx_ebpf_init(cycle->log);; 787 | if (escf->global_ctx == NULL) { 788 | ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "call 'ngx_ebpf_init' fail"); 789 | // recover 790 | cscf->handler = escf->handler; 791 | return NGX_OK; 792 | } 793 | ngx_log_debug(NGX_LOG_DEBUG_STREAM, cycle->log, 0, "escf %p global_ctx %p", escf, escf->global_ctx); 794 | } 795 | return NGX_OK; 796 | } 797 | 798 | /* 799 | // called when session closed 800 | static ngx_int_t 801 | ngx_stream_ebpf_log_handler(ngx_stream_session_t *s) 802 | { 803 | // clear map on session close 804 | ngx_connection_t *c; 805 | ngx_connection_t *pc; 806 | ngx_stream_upstream_t *u; 807 | ngx_stream_ebpf_srv_conf_t *escf = ngx_stream_get_module_srv_conf(s, ngx_stream_ebpf_module); 808 | 809 | c = s->connection; 810 | u = s->upstream; 811 | pc = u->peer.connection; 812 | 813 | return NGX_OK; 814 | } 815 | */ 816 | 817 | #if nginx_version >= 1025004 818 | static ngx_stream_conf_addr_t * 819 | get_srv_conf_addr(ngx_stream_core_main_conf_t *cmcf, ngx_stream_core_srv_conf_t *cscf) { 820 | ngx_stream_conf_port_t *port; 821 | ngx_stream_conf_addr_t *addr; 822 | ngx_uint_t a, p, s; 823 | ngx_uint_t p_len, s_len, a_len; 824 | ngx_stream_core_srv_conf_t **servers; 825 | 826 | port = cmcf->ports->elts; 827 | p_len = cmcf->ports->nelts; 828 | for (p = 0; p < p_len; p++) { 829 | addr = port[p].addrs.elts; 830 | a_len = port[p].addrs.nelts; 831 | for (a = 0; a < a_len; a++) { 832 | servers = addr[a].servers.elts; 833 | s_len = addr[a].servers.nelts; 834 | for (s = 0; s < s_len; s++) { 835 | if (servers[s] == cscf) { 836 | return addr; 837 | } 838 | } 839 | } 840 | } 841 | 842 | return NULL; 843 | } 844 | #endif 845 | 846 | ngx_int_t ngx_stream_ebpf_init(ngx_conf_t *cf) 847 | { 848 | ngx_uint_t s; 849 | ngx_stream_core_srv_conf_t *cscf, **cscfp; 850 | ngx_stream_ebpf_srv_conf_t *escf; 851 | ngx_stream_core_main_conf_t *cmcf; 852 | #if (NGX_STREAM_SSL) 853 | #if nginx_version >= 1025005 854 | ngx_stream_ssl_srv_conf_t *sscf; 855 | #else 856 | ngx_stream_ssl_conf_t *sscf; 857 | #endif 858 | #endif 859 | #if nginx_version >= 1025004 860 | ngx_stream_conf_addr_t *addr; 861 | #endif 862 | ngx_uint_t ebpf_enabled; 863 | 864 | cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module); 865 | cscfp = cmcf->servers.elts; 866 | 867 | ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, NGX_STREAM_LOG_PREFIX"ngx_stream_ebpf_init"); 868 | 869 | ebpf_enabled = 0; 870 | for (s = 0; s < cmcf->servers.nelts; s++) { 871 | #if (NGX_STREAM_SSL) 872 | sscf = cscfp[s]->ctx->srv_conf[ngx_stream_ssl_module.ctx_index]; 873 | #endif 874 | cscf = cscfp[s]->ctx->srv_conf[ngx_stream_core_module.ctx_index]; 875 | escf = cscfp[s]->ctx->srv_conf[ngx_stream_ebpf_module.ctx_index]; 876 | if (!escf->ebpf_enable) { 877 | ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, NGX_STREAM_LOG_PREFIX"ebpf_enable not enabled"); 878 | continue; 879 | } 880 | #if (NGX_STREAM_SSL) 881 | if (sscf->ssl.ctx != NULL) { 882 | ngx_log_error(NGX_LOG_ERR, cf->log, 0, NGX_STREAM_LOG_PREFIX"ssl is enabled which conflict with ebpf"); 883 | return NGX_ERROR; 884 | } 885 | #endif 886 | /* no content handler found*/ 887 | if (!cscf->handler) { 888 | ngx_log_error(NGX_LOG_ERR, cf->log, 0, NGX_STREAM_LOG_PREFIX"proxy_pass should be configured with ebpf_enable"); 889 | return NGX_ERROR; 890 | } 891 | escf->handler = cscf->handler; 892 | 893 | ngx_log_error(NGX_LOG_NOTICE, cf->log, 0, NGX_STREAM_LOG_PREFIX"ebpf proxy enable, replace stream proxy process, cscf %p", cscf); 894 | cscf->handler = ngx_stream_ebpf_proxy_handler; 895 | #if nginx_version >= 1025004 896 | addr = get_srv_conf_addr(cmcf, cscf); 897 | escf->addr.data = ngx_pstrdup(cf->pool, &addr->opt.addr_text); 898 | escf->addr.len = addr->opt.addr_text.len; 899 | #endif 900 | ebpf_enabled = 1; 901 | 902 | /* 903 | h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers); 904 | if (h == NULL) { 905 | return NGX_ERROR; 906 | } 907 | 908 | // now use ngx_stream_ebpf_proxy_finalize instead 909 | *h = ngx_stream_ebpf_log_handler; 910 | */ 911 | } 912 | 913 | // used to test when load config 914 | if (ebpf_enabled == 1) { 915 | struct ngx_stream_ebpf_obj_ctx *global_ctx = ngx_ebpf_init(cf->log); 916 | if (global_ctx == NULL) { 917 | ngx_log_error(NGX_LOG_ERR, cf->log, 0, NGX_STREAM_LOG_PREFIX"init ebpf error"); 918 | return NGX_ERROR; 919 | } 920 | ngx_ebpf_obj_free(global_ctx); 921 | } 922 | // it's main process, and we do not need such info 923 | //escf->global_ctx = global_ctx; 924 | return NGX_OK; 925 | } 926 | -------------------------------------------------------------------------------- /src/ngx_stream_ebpf_module.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #define NGX_STREAM_LOG_PREFIX "[stream_ebpf] " 6 | 7 | typedef struct { 8 | unsigned char client_key[sizeof(struct ngx_sock_tuple)]; 9 | unsigned char upstream_key[sizeof(struct ngx_sock_tuple)]; 10 | ngx_int_t client_read; 11 | ngx_flag_t ebpf_inited; 12 | } ngx_stream_ebpf_ctx_t; 13 | 14 | struct ngx_stream_ebpf_obj_ctx { 15 | void *bpf_object; 16 | int proxy_map_fd; 17 | int sockmap_fd; 18 | int sockhash_fd; 19 | int map_fd; 20 | int meta_fd; 21 | int prog_redirect_fd; 22 | int prog_parser_fd; 23 | }; --------------------------------------------------------------------------------