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

Code References


Published

03 February 2011

blog comments powered by Disqus