Routing in overlapping ethernet segments

I have come across a few cases where you have (parts of) the same Ethernet segment (IP subnet) on two different interfaces. Overlapping segments only works when it is known in which segment the IP addresses are located, because explicit routes are used. If clients dynamically move between subnets, you are better off using bridging. With these constraints, let's move on.

Note: Please substitute addresses and interfaces names to match those of your actual situation when entering commands.

Router on the "left", network on the "right" side:

This situation can come up if the router is configured to use a certain subnet, and you want all traffic to go through your own server without having to resort to bridging or NAT. Bridging (in Linux) makes firewall scripts more complex and is a bit harder to debug because kernel/tcpdump seems to get packet flow tracing wrong. NAT is undesired because all clients in your local network will appear with the IP address of our server.

It is further assumed that the router does not provide any segment-dependent services (like DHCP), and/or our server handles the DHCP.

In this scenario, is the router, which is set up to use Our own server is, from which we run `ip addr` and `ip route`:

2: iet0: mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:20:18:8c:ce:79 brd ff:ff:ff:ff:ff:ff
    inet peer scope global iet0
    inet6 fe80::220:18ff:fe8c:ce79/64 scope link
      valid_lft forever preferred_lft forever
3: bond0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:0a:e6:98:ed:d7 brd ff:ff:ff:ff:ff:ff
    inet brd scope global bond0
    inet6 fe80::fe80::20a:e6ff:fe98:edd7/64 scope link
       valid_lft forever preferred_lft forever dev iet0 proto kernel scope link src dev bond0 proto kernel scope link src
default via dev iet0


The router is the only device on the iet0 segment. If there is another (static!) device in the iet0 segment, add another route for it. The router operates with, as do the clients in the actual network. This creates an interesting situation:

1. The clients will not be able to reach, because it is not on the bond0 segment. They usually do not need to; define as a router, which is just the right thing. If they do need for whatever reason, you need to add an extra routing rule on each client, because is only reachable through

2. (Assuming the clients already have a route to The router will not be able to reach the clients, because there is only on the iet0 segment. It may be problematic to add rules to the router, either because it is locked up by your ISP, or it does not provide a way to add routes of the required complexity (it would need and via

So what is required here is that we fake the "existence" of the clients on the iet0 segment by making send ARP replies for any ARP queries from the router. Because all clients are only reachable through our server anyway.

Network on the "left", single node at the "right" side

Another view (of the same case actually) is when you have a Bluetooth (or other sort of device) attached to your desktop machine. The router is, the desktop machine and the BT device is at

2: eth0: <BROADCAST,MULTICAST,NOTRAILERS,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 1000
    link/ether 00:15:f2:16:aa:46 brd ff:ff:ff:ff:ff:ff
    inet brd scope global eth0
    inet6 fe80::215:f2ff:fe16:aa46/64 scope link
       valid_lft forever preferred_lft forever
3: ppp0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast qlen 3
    inet peer scope global ppp0 dev ppp0 proto kernel scope link src dev eth0 proto kernel scope link src
default via dev eth0

The same as above happens: does not know how to contact any hosts in, and hosts on eth0 do not know about


As previously mentioned, the solution is faking up ARP responses on the intermedia machine (example 1:; example 2: To do this, we need brctl (package: bridge-utils) and ebtables. That is because the ebt_arpreply kernel module is only usable with ebtables, and that needs a bridge interface.

Well, did not we want to avoid bridging? Yes. And we do. The fact that we create a bridge does not imply bridging. (Just "close" the bridge and force everything to go through the valley.) This is what we will be doing. In fact, we will create what you could call a "half-bridge". Just remember that you have one (or more) "single" hosts and the usual network. In the first step, we will attach each network interface which needs arpreply to its own logical bridge:

brctl addbr br0
brctl setfd br0 1
brctl addif br0 iet0
ip link set br0 up

(Create new bridge interfaces br1, br2, etc. if there are more interfaces to fake ARP on.)

To make arpreply work, ARP packets must hit the ebtables kernel code, which happens only on bridging. So ARP packets continue to be 'bridged' (but are dropped then on arpreply, see --arpreply-target) while the rest is routed:

ebtables -t broute -P BROUTING DROP
ebtables -t broute -A BROUTING -p arp --arp-opcode request -j ACCEPT

In case ebtables complains about a missing table, make sure that the ebtables kernel module is already loaded; ebtables does not do that automatically, unfortunately.

Then add the ARP reply target:

ebtables -t nat -A PREROUTING -p arp --arp-opcode request -j arpreply --arpreply-mac aa:bb:cc:dd:ee:ff --arpreply-target DROP

Done. You may want to add this to a start script for the bridge to be set up at boot automatically.

External links

Linux Ethernet Bridging