reverse path filter (rp_filter) by example
Recently, I ran into an rp_filter change for all Kernels after 2.6.31. So read along for an explanation of both the sysctl change and a practical example of rp_filter usage.
Lets say you had the following entry in your /etc/sysctl.conf
net.ipv4.conf.all.rp_filter = 1
with the intention of turning on reverse path filtering for all interfaces. Well you didn't get your wish- rp_filter remained disabled if you are running a Kernel older than 2.6.31.
This could come as a suprise if you upgrade your Kernel and have a system relying on rp_filter being disabled or enabled (e.g. multicast routing, multi-homed servers). If you have a single-homed unicast server setups this change will probably go unnoticed however.
The fix was implemented upstream in v2.6.31 and the basic issue was that each individual interface has an rp_filter setting which defaulted to 0 and the interface setting overrides the "all interface setting" since they were AND'd together.
Simple Example
This is a simple example to show how rp_filter will filter packets in the three modes: 0 disabled, 1 strict and 2 loose.
Client A - 192.168.2.10 - connected to router via eth0
Router
eth0 - 192.168.2.150
routes - 192.168.2.0/24
eth1 - 10.42.43.1
routes - 10.42.43.0/24
_Note: No default route_
Client C - 10.42.43.50 - connected to router via eth1
With this setup and rp_filter on the router set to "loose mode" (2) a packet on eth0 from 1.2.3.4 to 10.42.43.50 will be blocked. With rp_filter on the router set to "strict mode" (1) a packet on eth0 from source address 10.42.43.2 will be blocked. When set to "disabled" (0) both packets would go through.
Testing
Lets try this out with some real systems. You could do this with some virtual machines or three physical hosts. For example Client A could be a workstation, the Router could be a laptop with eth0 connected to Client A and an ad-hoc wifi networking connecting to Client C.
Client A
Setup the route to our gateway to 10.42.43.0 via 192.168.2.103
route add -net 10.42.43.0/24 gw 192.168.2.103 dev eth0
Start sending udp messages to 10.42.43.50 from a variety of addresses.
$ cat udp-hello.sh
while true; do
# Use mz to generate packets http://www.perihel.at/sec/mz
sudo mz eth0 -t udp "sp=$3,dp=$3" -A $1 -B $2 -P "hello
from $1"
sleep 1
done
./udp-hello.sh 10.42.43.2 10.42.43.50 1113
./udp-hello.sh 1.2.3.4 10.42.43.50 1112
./udp-hello.sh 192.168.2.10 10.42.43.50 1111
Router
Routing setup for the router.
$ route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
10.42.43.0 * 255.255.255.0 U 2 0 0 eth1
192.168.2.0 * 255.255.255.0 U 0 0 0 eth0
loopback * 255.0.0.0 U 0 0 0 lo
Script that will be used to reset rp_filter on our incoming route device.
$ cat set-rp_filter.sh
find /proc/sys -name rp_filter -exec sh -c "echo $1 > {}" \;
ifconfig eth0 down
ifconfig eth0 up
route del default
Client C
The client is listening on three ports to the messages sent from Client A using netcat:
Term 1: $ nc -u -l 10.42.43.50 1111
Term 2: $ nc -u -l 10.42.43.50 1112
Term 3: $ nc -u -l 10.42.43.50 1113
Illustration of rp_filter modes
With no reverse path filtering (rp_filter = 0) on the router the client C prints the following:
Term 1: hello from 192.168.2.10
Term 2: hello from 1.2.3.4
Term 3: hello from 10.42.43.2
No filtering means anything can pass through the router no matter what the reverse path is.
With strict reverse path filtering (rp_filter = 1) on the router the client C prints the following:
Term 1: hello from 192.168.2.10
Term 2:
Term 3:
In strict mode the router only accepts packets that match routes on eth0. In this case it is this route:
192.168.2.0 * 255.255.255.0 U 0 0 0 eth0
With loose reverse path filtering (rp_filter = 2) on the router the client C prints the following:
Term 1: hello from 192.168.2.10
Term 2:
Term 3: hello from 10.42.43.2
In loose mode the router will accept packets from any matching routes even on interfaces that don't match. In this case it is these two routes:
10.42.43.0 * 255.255.255.0 U 2 0 0 eth1
192.168.2.0 * 255.255.255.0 U 0 0 0 eth0
