How to prevent IP spoofing using MAC and ebtables?

I am trying to create IP-MAC pairing rules in ebtables. There are few tutorials and related questions [1] available but I have kind of specific setting.

ENVIRONMENT: I have many physical hosts. Each host has few ethernet cards, joined in bond and used as slave for bridge. There are many virtual machines on each host (kvm, qemu, libvirt). Each virtual machine is connected to a bridge of its physical host via new port called vnet[0-9]+. There is no NAT. Networking works fine, all the physical hosts can be pinged, all the virtual machines too. Each virtual machine has its own IP address and MAC address.

PROBLEM: Inside of a virtual machine, IP address can be changed to another one.

FOUND SOLUTION: There is known solution on the ebtables site [2], but this solution is aplicable when only one host is used. It allows all traffic and if there is a packet from IP with another MAC than allowed, packet is droped. If there are more than one host, it requires all existing IP-MAC pairs to be registered on all hosts. There is need for reverse policy solution.

CRAFTED SOLUTION: I have tried to use ebtables in inverted manner. Here is an example what I tried.

EXAMPLE 1

Bridge table: filter
Bridge chain: INPUT, entries: 2, policy: DROP
-i bond0 -j ACCEPT 
-p IPv4 -s 54:52:0:98:d7:b6 --ip-src 192.168.11.122 -j ACCEPT 
Bridge chain: FORWARD, entries: 0, policy: ACCEPT
Bridge chain: OUTPUT, entries: 0, policy: ACCEPT

EXAMPLE 2

Bridge table: filter
Bridge chain: INPUT, entries: 0, policy: ACCEPT
Bridge chain: FORWARD, entries: 1, policy: DROP
-p IPv4 -s 54:52:0:98:d7:b6 --ip-src 192.168.11.122 -j ACCEPT 
Bridge chain: OUTPUT, entries: 0, policy: ACCEPT

The core of what I want is to have default policy DROP and only allow traffic from virtual machines with correct IP-MAC pair deployed on given host. However, those solutions do not work.

QUESTION: How to allow traffic on bridge only for specified IP-MAC pairs of running virtual machines and drop all unknown IP-MAC pairs comming from ports vnet[0-9]+?

Thank you very much for any answers.

  • [1] Linux-KVM / iptables: prevent guest spoofing by matching ip+mac address on bridge?
  • [2] http://ebtables.sourceforge.net/examples/basic.html#ex_anti-spoof

Solution 1:

I have finally managed to craft a working solution.

  1. Solution uses ebtables and IP-MAC pairs.
  2. Only needed table is the default 'filter' table.
  3. There is no need to add any rules or policy to INPUT chain, since INPUT chain is NOT related to running virtual machines. Explanation of meaning of INPUT, OUTPUT and FORWARD chains in filter table is in ebtables manpage.
  4. Since ebtables works on ethernet level and the IP-MAC pairing is aplicable only for IP packets, there is need to emphesize that in the rules in order not to block ARP frames and other vital traffic.

So, in the begining, there are no rules whatsoever and all policies are set up to ACCEPT. There are no user defined chains. Filter table looks like this:

Bridge table: filter
Bridge chain: INPUT, entries: 0, policy: ACCEPT
Bridge chain: FORWARD, entries: 0, policy: ACCEPT
Bridge chain: OUTPUT, entries: 0, policy: ACCEPT

A new chain is addded. This chain contains all the allowed IP-MAC pairs. It is called VMS.

# ebtables -N VMS

Now, the important part. For every frame containing IP packet (or its parts) which is going thru bridge from port vnet[0-9]+, apply chain policy and rules of chain VMS. In other words, for every IP packet comming from any virtual machine, apply VMS chain.

# ebtables -A FORWARD -p ip -i vnet+ -j VMS

The default policy of chain VMS has to be DROP. This way, every IP packet comming from any virtual machine is droped by default. Later, allowed IP-MAC pairs exceptions are added. The default policy DROP causes, that all traffic from any virtual machine with unknown IP-MAC pair is droped at once, making IP spoofing impossible.

# ebtables -P VMS DROP

The table filter looks now this way. Also, this way it looks when there are no virtual machines running (allowed).

Bridge table: filter
Bridge chain: INPUT, entries: 0, policy: ACCEPT
Bridge chain: FORWARD, entries: 1, policy: ACCEPT
-p IPv4 -i vnet+ -j VMS
Bridge chain: OUTPUT, entries: 0, policy: ACCEPT
Bridge chain: VMS, entries: 0, policy: DROP

Suppose, there are two running machines. If we try to ping to/from them, traffic is dropped and destination is unreachable. This is desired result, as of this traffic has not been allowed yet. Only one command is enough to allow every one virtual machine traffic.

# ebtables -A VMS -p ip --ip-src 192.168.11.125 -s 54:52:00:cc:35:fa -j ACCEPT
# ebtables -A VMS -p ip --ip-src 192.168.11.122 -s 54:52:00:98:d7:b6 -j ACCEPT

Now, traffic from allowed virtual machines is flowing alright and IP spoofing is prevented.

This solution may be unperfect and if you have any comments or improvements, I will gladly hear them.