Inspired by code that is in victek's tomato-RAF, Toastman and I have added a new branch to git (Static-ARP). The code in this branch adds 2 new flags to the static DHCP page which allows you to do static-ARP for the devices listed in the table (and if wanted restrict other devices from entering the network).

The code in Static-ARP branch merges easily with tomato-RT. Comments are welcome.

Static ARP

On Ethernet (either wired or wireless) all communication between devices goes via physical layer data packets that contain the physical address of the source of the message and the physical address of the destination of the message. We call this physical address MAC address.

Besides MAC addresses devices also have IP addresses. These are higher level addresses. The source and destination IP address of data sent on the network is put the header of IP packets. The IP packets of the devices on a LAN are encapsulated in the physical layer packets.

When the router wants to send data to a client with a certain IP-address 'X' (either obtained via DHCP or set manually as a static IP address) the router needs to know what the MAC address of that client is. Therefore the router sends an ARP request on the LAN, which is a broadcast. The ARP request basically asks all devices that are on the network "if you're the one with IP address 'X', could you tell you tell me what your MAC address is?". One client should reply: "Yes I have IP address 'X' and my MAC address is 'Y', please send your data to this MAC address".

Now the router knows how to fill in header of the physical layer packet and it can start sending data to the client.

** ARP spoofing **

Suppose there's somebody with bad intentions on the network that want to intercept data of client with MAC address X and IP address Y. This malicious client could give himself the same static IP address Y, his MAC address is Z. When the router would ask "Are you the one with IP address Y", this malicious client would reply : "yes, I am the one, please send your data to MAC address Z". So, data that was meant to go to MAC address X will go to the wrong client with MAC address Z.

** Static ARP binding **

Static ARP binding is a way to ignore ARP spoofing attempts. On the router static DHCP page you can enable Static ARP binding. When enabled the router will ignore all ARP replies (of devices listed in the table). Instead, the router will look in the static DHCP tables for finding out the MAC address that belongs to a certain IP address. Because this table is filled in by the administrator it is assumed to be correct and data will always be sent to the listed MAC address.

** Restricting unlisted devices **

Clients that that have assigned themselves a static IP address which is not in the static DHCP table normally can get Internet access if they fill in the router IP address for the gateway and router IP address for the DNS-server (when their MAC address is not restricted from entering the network).

When they try to get Internet access they will send an ARP request to the gateway and the router will reply "yes, I have this gateway IP address, my MAC address is Q". The data can now be sent to the router (with MAC address Q) and when receiving the data the router manages to fill in the ARP table by inspecting the data. So, the router will know the clients MAC address and IP address and it can sent messages back to the client. In other words: the client will have full Internet access.

But in some networks we want to avoid that unlisted clients can get Internet access. We can do this with static ARP binding. All clients within the same subnet that are not listed will get assigned to MAC address 00:00:00:00:00:00, which is an invalid MAC address. So, all other IP addresses besides those listed will not be able to receive any data. Moreover, the IPs that are listed will not be vulnerable to ARP spoofing.

** Is this useful? **

This might be useful for distributing Internet services in a network where you can not trust every client, for instance if you offer Internet access in a condominium.
When using checking the "Restrict unlisted machines" option all unknown IP addresses will be banned from the network and it will not be possible for a malicious client to hijack the IP address of another (paying) user.

Also, there's no need anymore to fill in MAC addresses in the access restriction page. All administration for the network/condominium can be done in a single page.

** How should I use this? **

This new feature which will be added to some versions of Tomato needs a little explanation.

1) When using "Restrict unlisted devices" the DHCP service with a dynamic IP range tends to overwrite the static ARP entries in the table. Therefore you should set the DHCP range to issue only one of the static IP addresses in the static DHCP table (preferably the administrators IP address). e.g. 192.168.1.100-100 (see Basic - Network - LAN - IP address range).

2) When using "Restrict unlisted devices" you MUST enter your (admin) IP address and MAC address in the table, or you may be locked out of the router.

3) Static ARP only supports one MAC address per IP address.

4) If you have access points connected to the LAN ports of your router and you use "Restrict unlisted machines", you should add their IP and MAC address to the static DHCP table.

5) Restricting unlisted devices only works for all devices in the subnet 255.255.255.0. Wider subnet masks will not be completely restricted.

restrict unlisted clients by MAC addresses so only devices explicitly allowed to connect will be able to do so;

prevent ARP spoofing, i.e. disallow a client with a MAC address different from the listed one to connect using the same IP.

But why to use ARP binding to restrict unlisted MACs when we can use the iptables "mac" match instead? Using iptables will eliminate 3 out of 5 problems you listed yourself:

it won't be affected by a conflict between static and dynamic IP ranges;

both MAC addresses listed for the same IP can be allowed to connect;

it will work with any subnet mask you can specify for your LAN.

In addition, "mac" match is supported for both - IPv4 and IPv6 - iptables, so this will restrict clients from connecting via IPv6 as well.

Basically, limiting clients access by MAC is already implemented via "Access Restriction" but I agree that adding a single check-box to the Static DHCP page will be more convenient, especially if you have a large list of clients to list. This would be a useful feature - just don't use ARP binding to implement it…

Now, how useful would be the ARP spoofing prevention? That is very questionable to me… If you restrict unlisted clients, you already allow only trusted clients to connect to your network, so there's no need to enforce yet another protection. OTOH, if you allow any unlisted device to connect, why bother with ARP binding if anyone still will be able to get access to your LAN - with one IP or the other? Yes, if you're really paranoiac, you may find this useful, but I think it's only a fraction of one percent of all Tomato users who fall into this category.

To sum it up, if you submit a good patch to restrict clients not present in the Static DHCP list using iptables "mac" match, that'd be great and I'll be happy to include it. As for ARP spoofing prevention, for now I don't think there's a real need for it in Tomato at all.

Firstly, ref the ARP spoofing. Just because we enter clients MAC into a static list doesn't mean they are "trusted". In large apartment blocks we have no knowledge or control over what people do to their PC's configuration. It's not uncommon for people to bring home a PC or laptop configured with a static IP allocated to them at their place of work, which just happens to belong to someone else on our network. We also often have people changing their IP address to see what it does, so they can avoid paying for access next time round. Each time these things happen it causes total mayhem trying to gain access to that user's PC to stop it, but it happens again as soon as his IT dept. resets the IP. We can't force him to switch off his wireless when he's at home, and many of them don't know how.

Static ARP prevents that happening and avoids a lot of trouble. Of course, it wouldn't stop people such as real "hackers" who are really determined and could change both MAC address and IP. But so far that has not happened here.

Next: - using Static ARP to do the access restrictions actually works very well. Several people have already tested and sent in their approval of it for the reduction in work. However, it does have the implications that you mention, although many of these are actually just cosmetic changes in the way the Tomato GUI looks. Probably the "limit unlisted machines" should be done differently, and then may as well be in the correct place in "Access Restrictions". We will look at that.

Question - if iptables is used to restrict, then we have to run 250 or so clients through the rules. Doing it via ARP binding to an invalid MAC will be much less processor intensive. Am I right?

As for normal static ARP binding (without restricting unlisted machines), many commercial routers have this feature. So, I think it brings some added value to the system. It could for instance avoid that one user can use bandwidth of the other user when having an IP-based bandwidth limiter (IP/MAC bandwidth limiting is also part of victeks tomato-RAF and it improved lately thanks the help of PrinceAMD).

For instance, suppose I have 2 users:
1 gets 800 kbit,
the other one pays less and get's only 400kbit

Both are allowed to access the network.
If the 400 kbit user would give himself the IP address of 800 kbit user he could get faster internet access.
But when using static ARP he can't do this.

So what about this proposal?
- we keep the static ARP flag (it's after all a feature available in many routers, and it's useful for me and others that do ip based bandwidth limiting or care about network security)
- we make the "Restrict unlisted machines" flag restrict unlisted machines via IP tables.

Some countries have a law that forces everyone that distributes internet services to log web usage (most countries were I lived in have this law).
The web-log feature in tomato-RT is a useful tool for that.
But this feature because pretty useless if I can borrow the IP address of my neighbor.

In large apartment blocks… it's not uncommon for people to bring home a PC or laptop configured with a static IP allocated to them at their place of work, which just happens to belong to someone else on our network.

That makes sense.

So what about this proposal?
- we keep the static ARP flag
- we make the "Restrict unlisted machines" flag restrict unlisted machines via IP tables.

Sounds good. A few more suggestions/wishes though, if you're going to work on this :)…

Since these 2 features are independent, split them into 2 separate git commits - it's a good practice in general, and often simplifies integration/merges/troubleshooting.

Clean up the rather ugly ARP binding implementation - there's no need to generate a temporary script file and then execute it. The ARP entries can be set via ioctl() calls, or by calling eval("arp", "-s", ip, mac) without any intermediate scripts. The "ip neigh flush all" command can be used to clear the cache instead of (ouch!) "for HOST in ‘cat /proc/net/arp |sed -n ’s/\\([0-9]*\\.[0-9]*\\.[0-9]*\\.[0-9]*\\).*/\\1/p'‘; do arp -d $HOST; done".

Why on earth arp binding is started/activated in start_wan_done() when it’s clearly a LAN feature? I think the start/stop_arp_bind calls should be paired with start/stop_dnsmasq. Or - even simpler - eliminate start/stop_arp_bind completely and actually do it all inside the start/stop_dnsmasq() functions - it's only logical when using static DHCP list, and setting up ARP entries can be done in the same existing loop that creates dnsmasq hostname entries.

We need to think about where to place the "Block unlisted clients" option. It could stay on the same Static DHCP page. But I also like Toastman's sugestion to move it to "Access Restrictions". We can add 2 new options to the "Applies To" drop-down there - "All Clients on the Static DHCP list" and "All clients NOT on Static DHCP list". It requires more work, but more flexible, allows to use schedules, and is still simple to setup.

Comments make sense. Some of the legacy weirdos from the original conanxu code should be kicked out.

My thoughts were to mod the restrict.c code to use the NVRAM static DHCP list when those options are selected, no need to have yet another list for this when we want to apply to everyone. The Restrict list will usually be used for individuals and therefore be quite small. I was assuming that since the Restriction function already works with ipv6 etc then the amount of work needed should be quite small.

I've been struggling myself to understand why in the old implementation static arp binding was started with the wan. Before it was even stopped together with the wan (but I took that out).

My first thought was also to put the static ARP binding in the same loop were static DHCP was done. The reason I choose to not do it over there was that my branch would be much easier to merge with different tomato variants if most of the arp related functionality would be in a separate file. However, I do agree with your remark.