# IPFW + MAC filtering + NAT



## Kesano (Jul 6, 2011)

Hi there.

Need some help from BSD seniors. I'm trying to build a simple filter for WiFi users that want to get Internet access, and allow them access by MAC address.

I've rebuilt the kernel with:

```
options 	IPFIREWALL
options 	IPFIREWALL_FORWARD
options 	IPFIREWALL_NAT
options 	IPDIVERT
options 	DUMMYNET
options 	LIBALIAS
```

Then enabled MAC filter: 
	
	



```
#sysctl net.link.ether.ipfw=1
```

ae0 - interface to the internet
wlan0 - WiFi AP for LAN

So I have some rules:


```
00010 1255 519814 divert 8668 ip from any to any
00020   68   4348 allow ip from any to any dst-port 53
00040   16    736 allow ip from any to me dst-port 80
01000  388 317422 allow ip from 192.168.0.11 to any MAC any 00:19:e0:8d:d0:43
60000  409 321902 fwd 192.168.0.1,80 ip from 192.168.0.0/24 to any in via wlan0
65000  542 182189 allow ip from any to any
65535  336  54118 deny ip from any to any
```

My logic:

```
#10 - NAT
#20 - DNS queries
#40 - www at localhost
#1000 - accept ip 192.168.0.11 with mac 00:19:e0:8d:d0:43 to any
#60000 - forwarding to localhost www page with authorization. If password is correct, script adding to firewall rule such as #1000
```

But it didn't work. As you can see, packets rut by rule #1000 too, but when I'm trying to open some webpage, it forwards me back to the authentication page.

Additional rules, ex:

```
01000  312 131227 allow ip from any to 192.168.0.11 MAC 00:19:e0:8d:d0:43 any #packets runs too
```
and other additional rules to approve reverse traffic didn't help..

But if I make a rule with keep-state

```
allow ip from 192.168.0.11 to any MAC any 00:19:e0:8d:d0:43 keep-state
```
 *it works normally.* 

If I change MAC or IP -> redirect to authentication page.

I Googled about my problem for five days and six nights before asking you. I've not found an answer. FreeBSD.Org comunity - my last hope.


----------



## phoenix (Jul 6, 2011)

You need to have rules for both directions (incoming and outgoing) for Layer2 (MAC).  You're first set of rules only match ethernet packets in one direction.  Adding the keep-state creates a temporary rule that matches in both directions.

Here's the rules we use to block Internet access based on MAC:

```
# Block Internet access by MAC address
# Allow ARP traffic
$IPFW add 4 allow ip from any to any layer2 mac-type arp

# Block ethernet traffic from specific MAC addresses (note: MACs are listed in "dest src" order)
$IPFW add 5 deny ip from any to any $BAD_MACS_F in recv $PRIVATE

# Block ethernet traffic to specific MAC addresses (note: MACs are listed in "dest src" order)
$IPFW add 6 deny ip from any to any $BAD_MACS_R out xmit $PRIVATE

# Allow ethernet traffic
$IPFW add 7 allow ip from any to any MAC any any
```

Rule 4 allows ARP requests, which is needed for TCP/IP-based ethernet networking to work.

Rule 5 blocks traffic from any IP address to any IP address, with a specific destination MAC address, coming in on the internal network interface on the firewall.

Rule 6 blocks traffic from any IP address to any IP address, with a specific source MAC address, going out of internet network interface on the firewall.

Rule 7 allows all other ethernet packets.  This is very important, as all packets go through the IPFW rules list twice, once as an ethernet packet, once as an IP packet, and blocking one blocks the other.

We use a variable for BAD_MAC_F and BAD_MAC_R that look like so, which allows us to list multiple MACs:

```
BAD_MACS_F=" { MAC any 00:1E:68:C7:B7:AF or MAC any 00:11:24:3E:FA:86 or MAC any 00:20:ED:48:8C:38 or MAC any 00:21:9B:DA:08:23 }"
BAD_MACS_R=" { MAC 00:1E:68:C7:B7:AF any or MAC 00:11:24:3E:FA:86 any or MAC 00:20:ED:48:8C:38 any or MAC 00:21:9B:DA:08:23 any }"
```

So, you need to add your Layer2 (ethernet) rules to the top of your rules list, and be sure to cover both directions.  Then do your Layer3 (IP) rules.

And try to avoid generic rules full of "any"s, except when blocking stuff.


----------



## Kesano (Jul 12, 2011)

I understood. I've seen somewere an example, where rules was exploded to sections with layer2 and layer3 type with skipto commands.

You're described method "allow all BUT NOT mac:addr"...
I'll try "allow mac:addr BUT NOT all"


----------

