Following on from my previous writings on eBPF, I wanted to build something a bit more useful. Enter the eBPF Network Vershitifier! The vershitifier is a simple ebpf-go application that randomly drops outgoing packets for targeted processes using the eBPF TC filters. This looks like ordinary packet loss to the targeted process, which lets us see how it will react under carefully controlled conditions.
The CLI takes the name of the process to vershitify, and the % of traffic to drop. For instance, to drop 25% of all the traffic ping sends:
> ./vershitifier -interface eth0 -command ping -drop 25
The rest of the post will look at the different eBPF hooks used, with a link at the start of each section to the corresponding code.
Finding Process IDs ππ
Firstly, we need to find all processes that have been launched for a given
binary. To do this, we can attach a kprobe
and a kretprobe
to execve, which is
part of the process launching mechanism in the kernel 1. In the kprobe
we see
what the parent process looks like, in the kretprobe
the launched child.
In the kretprobe
, we then use an eBPF helper to get the processes comm
, which
will contain the name of the process - e.g. curl
- with the leading path
removed. Once we have this, we can write the bits weβve discovered to a map -
the process and parent back to our CLI application so it knows weβve found a new
instance, and pass the PID to the next bit of our eBPF stack.
Identifying Sockets ππ
Now that we know which process IDs to watch, we want to catch their creation of sockets, so we know which traffic we are targeting. Here weβre using a cgroup probe, on sock_create. I spent a bunch of time spelunking 2 through potential probes here to try and find one that saw both the source PID and the socket information we might care about - ports and so on. Because weβre using a cgroup probe, we need to figure out which cgroup to attach it to 3 which adds some complexity.
When we discover a socket associated with a PID weβre tracking, we mark it with a magic number 4. We can pick up socket marks in the TC filter later on, which makes this a straightforward option, but in situations where other things are chucking marks onto sockets, strange things will happen.
Anyhow - as it stands the code for this bit is quite straightforward:
Vershitifying Traffic ππ
Finally, we need to actually drop some traffic, this time using an eBPF TC
program. The Traffic Control Subsystem
in Linux lets us muck about with the
transmission of packets, and is typically used to do quality-of-service based
priority mangling. For vershitification purposes itβs a great place to mess
about because it βseesβ the socket options of the transmitting process -
including our socket mark - and it includes the TC_ACT_STOLEN
action, which
pretends the packet was queued for transmission while actually silently dropping
it.
to decide whether or not to drop a packet, we generate a random number between
0 and 100 with bpf_get_prandom_u32() % 100
, then compare that to our drop
percentage, which we pass into the program dynamically at load time using
rewriteconstants.
Potential Improvements
- Iβm using TC because itβs easy to track which traffic is associated with a particular process, but so far weβre dropping outgoing traffic. I believe it should be possible on TC ingress too.
- I donβt think I need to use the cgroup-specific probe to discover sockets.
Footnotes
-
When a process launches another process in Linux, what typically happens is that the program forks itself, copying itself under a new PID, and then uses execve to replace the contents of that process with the process it is launching. A common thing to see would be a shell - βbashβ and friends - in the kprobe - then the process the shell has run in the kretprobe. β©
-
Read: attaching probes and dumping out the contents of all the things passed in to see what they do. β©
-
Code for this here. I suspect that the way iβve done this may not work well nested cgroups. β©
-
A socket mark is a uint32 that is carried along on the internal data structures used to track sockets in the Kernel, and can be used for things like making iptables decisions. β©