buger / goreplay

GoReplay is an open-source tool for capturing and replaying live HTTP traffic into a test environment in order to continuously test your system with real data. It can be used to increase confidence in code deployments, configuration changes and infrastructure changes.
https://goreplay.org
Other
18.53k stars 13 forks source link

VXLAN engine doesn't capture body #1095

Closed monrax closed 2 years ago

monrax commented 2 years ago

Neither request body nor response body are being displayed when using --input-raw-engine-vxlan and --output-stdout

Environment: AWS How to repeat issue: Launch 2 t3-type EC2 instances, and set up a VPC traffic mirror filter and session between them. The ENI for one of them acts as a target and the other as the source. Create an inbound rule on the target's security group to allow UDP traffic on port 4789. SSH into both machines:

sudo ./gor --input-raw :8323 --input-raw-engine vxlan --input-raw-vxlan-vni 123 --input-raw-bpf-filter "(src port 8323) or (dst port 8323)" --output-stdout

In this case, 123 was chosen for VXLAN ID when creating the mirror session.

echo world > hello.txt && python3 -m http.server 8323

In this case, a simple webserver is exposed at port 8323. Remember to create an inbound rule in the security group of the source machine to be able to reach port 8323 from your browser.

Go to http://<source machine public ip>:8323/hello.txt in your browser

Expected result: Entire HTTP message printed to stdout in target machine. Actual result: Only headers are printed, without the body. See attached image.

image

Note: I experienced another issue while trying this engine: sometimes requests do not show up, only responses. ~I'll open another issue for this~ done! (#1096), but in case you don't see the request while trying to reproduce this one, try disabling cache and refreshing the page several times until the request shows up.

BridgeWind commented 2 years ago

I also get the same problem for request,here is my solution: increase the length for vxlan.go this might be work for you:

image

if it doesn't work, modify tcp_packet.go too:

image

(rest_len = totalLen - skip)

The issue for me is that when the http request is large,and the cap is bigger than totalLen, the code won't extend itself resulting incomplete packet assembling, so some message is missing

Hope it works for you

monrax commented 2 years ago

Thank you @BridgeWind ! It really helped. Turns out the issue wasn't related to the packet size, instead it is hidden in plain sight in the copySlice function in the tcp_packet.go file.

As you pointed out in your kind suggestion, the bug lies in the condition cap(to) < totalLen itself. More specifically, how it relates to the copy(to[skip:], s) part of this copySlice function. For the built-in copy function to copy elements into a slice, the destination slots must be initialized for this slice, not just allocated in the underlying array. In other words, this is a len vs cap issue. This is backed by the official docs :

(...) Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst)."

For this test in particular, the effect is that the second packet with the response body is never copied since the condition is false because the to slice has a much larger cap than totalLen. However, for different payloads where cap is smaller than totalLen the issue might persist because even though the len is increased, the diff number of elements that are appended might be larger than just totalLen-skip (or totalLen-len(to), if we are to avoid any tight coupling).

The logic of wanting to extend the slice only when it is needed is sound, so I don't think the if should be thrown away, only modified to use len instead of cap:

if len(to) < totalLen {
    diff := totalLen - len(to)
    to = append(to, make([]byte, diff)...)
}

I have tested this and it works fine for both small and large payloads. I'll open the corresponding PR and that should close this issue. Thanks again! 👍