Solved Allowing selective traffic from/to wlan with -apbridge set (starting with ARP)

I have an AP+bridge+firewall ("router") running FreeBSD 12.2-RELEASE r366954 GENERIC amd64.

It's roughly set up as follows:

- igb0 connects to my ISP via DHCP. (Disabled while I debug this.)
- bridge0 has dnsmasq running on it as a DHCP server and DNS server. (Only dynamic addresses at the moment.)
- igb1 and igb2 are in bridge0, with nothing special going on.
- wlan0 (ath) is in bridge0, in hostap mode via hostapd, with -apbridge set to control traffic between wireless clients.
- pf(8) does NAT from bridge0 to igb0. (Disabled and modules not even loaded right now.)
- ipfw(8) is disabled and the module isn't loaded.

High-level summary of the situation:

- With -apbridge set, I can't figure out how to allow any traffic between wireless clients. (Not even ARP.)
- If I set apbridge (i.e., unset -apbridge) then I can connect between wireless clients.
- With igb0 and NAT set up, all clients can connect to the router and to the Internet. Connections from wlan0 -> wlan0 are all-or-nothing based on apbridge, regardless of attempted filtering.
- Filtering with pf(8) between wlan0 <-> igb1 with net.link.bridge.pfil_bridge=1 works as expected.

I spent an entire day trying various combinations of configurations and manual tweaks with route(8), arp(8), sysctl(1), ifconfig(8), pf(8), and ipfw(8), to at least get ARP to work. I tried so many random combinations that I can't remember what exactly I tried. (Here are the specific ones I remember; more details about my config and results are below.)

- The web answer I found that seemed the most promising was from Thread routing-between-bridged-interfaces.73803/post-450898 (also adding an extra rule for wlan0), but it had no effect.
- I attempted to use NAT to wlan0 in pf(8) (with ipfw(8) disabled) plus a pass all rule, which had no effect.
- I attempted several combinations of allow rules in ipfw(8) (with pf(8) disabled), including bridged, bridge0, and wlan0 in various combinations.
- The closest I got was to manually add an ARP entry for one client to another client using arp, but that quickly got stale, and didn't make connections possible even with a fully-open or fully-unloaded firewall. So, it seems like the router isn't responding to ARP requests from wlan0 about another wlan0 client?
- At one point I tried adding one of the wireless IPs as an alias to wlan0 and then removing it, mostly out of frustration. This temporarily created an ARP entry with (the wrong) MAC, which somehow got translated into the correct MAC when another wireless client sent a ping, as seen from tcpdump on the router. (But, the ping still failed.)
- (I didn't try getting rid of bridge0 and going with just wlan0 temporarily because I didn't want to solve a different problem and have it not work with the bridge.)

So, here are the dirty details.

Code:
# /boot/loader.conf

# Shorten boot delay.
autoboot_delay="5"

# Enable support for the serial termial.
boot_multicons="YES"
boot_serial="YES"
comconsole_speed="115200"
console="comconsole"

# Enable MLS and BIBA.
mac_mls_load="YES"
mac_biba_load="YES"

# Allow speaker output in scripts.
speaker_load="YES"

# Legacy pty support.
pty_load="YES"

# Bridge support needs to be enabled before sysctl.conf is processed.
if_bridge_load="YES"

# Enable rctl.
kern.racct.enable=1

Code:
# /etc/rc.conf (minimal)

clear_tmp_enable="YES"
syslogd_flags="-ss"
sendmail_enable="NONE"
hostname="router.localnet"
sshd_enable="YES"
ntpdate_enable="YES"
ntpd_enable="YES"
powerd_enable="YES"
dumpdev="AUTO"
rctl_enable="YES"

cloned_interfaces="bridge0 lo1"
wlans_ath0="wlan0"
create_args_wlan0="wlanmode hostap -apbridge"
ifconfig_bridge0="inet 192.168.0.1 netmask 255.255.255.0 addm igb1 addm igb2 addm wlan0 up"
#ifconfig_igb0="DHCP"
ifconfig_igb1="up"
ifconfig_igb2="up"
ifconfig_wlan0="ssid SSSSS mode 11a channel 157 regdomain FCC country US up"
hostapd_enable="YES"
dnsmasq_enable="YES"
gateway_enable="YES"

Code:
# /etc/sysctl.conf (minimal)

security.bsd.see_other_uids=0
security.bsd.see_other_gids=0
security.bsd.see_jail_proc=0
security.bsd.unprivileged_read_msgbuf=0
kern.randompid=1

Code:
# /etc/hostapd.conf (minimal)

interface=wlan0
debug=1
ssid=SSSSS
wpa=2
wpa_passphrase=PPPPP
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0

Code:
# /usr/local/etc/dnsmasq.conf (minimal)

bogus-priv
filterwin2k
dhcp-authoritative
log-dhcp
expand-hosts
interface=bridge0
addn-hosts=/usr/local/etc/hosts

domain=localnet

dhcp-option=option:dns-server,192.168.0.1
dhcp-range=192.168.0.128,192.168.0.254,24h

Once the router starts up and clients connect to it, I have something like this from arp -a (from the router):

Code:
router.localnet (192.168.0.1) at WW:WW:WW:WW:WW:00 on bridge0 permanent [bridge]
CLIENT1 (192.168.0.171) at XX:XX:XX:XX:XX:c1 on bridge0 expires in 1189 seconds [bridge]  # via igb1
CLIENT3 (192.168.0.218) at YY:YY:YY:YY:YY:25 on bridge0 expires in 1192 seconds [bridge]  # via wlan0
CLIENT4 (192.168.0.251) at ZZ:ZZ:ZZ:ZZ:ZZ:39 on bridge0 expires in 1113 seconds [bridge]  # via wlan0

If I ping from 192.168.0.251 to 192.168.0.218 (wlan0 -> wlan0), I get numerous messages like this from tcpdump on the router:

Code:
21:11:07.735375 ZZ:ZZ:ZZ:ZZ:ZZ:39 > ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.0.218 tell 192.168.0.251, length 28

I would not expect the ping to actually succeed, but I'm surprised that it's seemingly not straightforward to get ARP to work. If I arp -a on 192.168.0.251 then I get <incomplete> as the MAC for 192.168.0.218.

Doing a ping from 192.168.0.251 to 192.168.0.171 (wlan0 -> igb1) succeeds (as expected), and arp -a on 192.168.0.251 shows the correct MAC for 192.168.0.171. And, if I set [icodeMD]apbridge[/icode] on wlan0 then wlan0 -> wlan0 also works fine, aside from a complete inability to filter traffic. So, the problem seems to be either the "robustness" of -apbridge against unintended connections, or something I've missed with routing or redirection.

In summary, while I'm aware that both -apbridge and apbridge give me exactly what I ask for, I'm not getting the intuition behind having something in between the two. Ideally, I'd like to treat wlan0 <-> wlan0 the same way I treat wlan0 <-> igb0, as far as packet filtering. But, I'd settle for one completely-open subnet with the rest completely closed.
 
Last edited:
I noticed something interesting when I was messing around with arp:

- If I ping from wlan0 -> wlan0 when -apbridge is set, the ARP request is broadcast on bridge0.
- If I then arp -s to add an entry for the second wlan0 client to the first's ARP table, then the ping never makes it to bridge0.

This implies that wlan0 still works as a member of bridge0 when the route isn't clear, but skips bridge0 when the destination is on wlan0.

So, the compromise I came up with was this:

1. Add a second clone of ath0 (wlan1) with apbridge and its own ssid, with all other parameters matching those of wlan0.
2. Add wlan1 to bridge0.
3. Copy /etc/hostapd.conf with config for the second ssid on wlan1.
4. Update /etc/rc.d/hostapd to include the additional config file in conf_file. (Do not run a second hostapd daemon or you might have trouble connecting to one of the ssids. That took me hours to figure out.)

Now I have two ssids:

- Clients on wlan0 cannot access each other.
- Clients on wlan1 can access each other without restriction.
- Traffic between wlan0 <-> wlan1 can be controlled, with bridge filtering enabled.
 
Last edited:
But then you'd know exactly where the problem is if it worked.
I understand the sentiment, but the word "it" obscures quite a bit of detail, and "if it worked" is closer to 0 than it is to 1. "It" could include the entirety of my original post minus "bridge0".

Specifically, pruning the setup down to wlan0 with -apbridge would obviously still isolate clients from each other. From there, it's the same process all over again to find the right combination of tweaks to selectively allow ARP broadcasts, etc. In the absence of someone more experienced saying that such a thing is possible, the most likely outcome is that I spend another 10 hours and find nothing.

In many situations (such as this one) finding the real cause of the de facto problem is less valuable than finding a similar problem that's much easier to solve. In this case, splitting the ssid is actually preferable to what I was trying to do, and it works with the bridge, which makes it closer to the goal than first solving the problem without the bridge. But, I'm still curious if there's an apbridge expert out there who knows if what I was trying to do is actually possible.

(On a tangent, I'm going to mention "ap_isolate=1" for search indexing, so that people who aren't aware of -apbridge become aware. I could have used that info myself several months ago.)
 
Last edited:
Please stop abusing the [cmd] bbcode for everything.

 
"It" simply means the computer functions as a router out from igb0 for things connected over igb1. You could then switch out the wlan0 for igb1 and make sure that works. Then throw the bridge in and by that time you'll know which part is the problem. Honestly, this setup is too complicated to debug all at once over a medium like a web forum.
 
A bridge(4) interface only forwards packets between interfaces. It never forwards a packet back to the same interface it was received from (in this case, wlan0). Therefore, when you disable the bridging feature of hostap mode (using ifconfig -apbridge), there is no way for clients on the same WLAN to communicate with each other.

In theory it should be possible to create a small program that performs forwarding between WLAN clients, and attach that program to a divert(4) socket connected to wlan0. I’m not aware of an existing, ready-to-use solution, unfortunately.
 
A bridge(4) interface only forwards packets between interfaces. It never forwards a packet back to the same interface it was received from (in this case, wlan0). Therefore, when you disable the bridging feature of hostap mode (using ifconfig apbridge), there is no way for clients on the same WLAN to communicate with each other.
Thanks for the info.

From my understanding, enabling similar isolation in Linux causes the interface to bump the packets up to a layer-2 filter, rather than simply dropping the packets, and nothing is blocked unless you add layer-2 filtering rules. (When ap_isolate=1 in hostapd.conf(5) failed to isolate clients in my setup here, I thought that I was missing some layer-2 filtering, but then I found -apbridge by accident.)

Is it possible that ap_isolate=1 allows partial filtering here? Even if so, I'm starting to appreciate how definite the blocking is with -apbridge.
 
Back
Top