Individual per-passphrase Wifi VLANs using wpa_psk_file (no RADIUS required)

Hello *,

I'm playing around with the wpa_psk_file option for wireless/hostapd because I found that it supports vlan assignment using the vlanid tag. This would be really helpful to create a segmented and client-individualised wifi network without having to spin up a dedicated RADIUS server.

Extend wpa_psk_file to allow an optional VLAN ID to be specified with "vlanid=" prefix on the line. If VLAN ID is specified and the particular wpa_psk_file entry is used for a station, that station is bound to the specified VLAN. This can be used to operate a single WPA2-Personal BSS with multiple VLANs based on the used passphrase/PSK. This is similar to the WPA2-Enterprise case where the RADIUS server can assign stations to different VLANs.

Edit: I removed the problem for which I found the solution because I believe it has not a lot of value to anyone else. This a how-to now.

6 Likes

:information_source: IMPORTANT NOTE: I wrote this in early 2023, when 22.03 was current and the default SSL library was wolfssl. With 23.05, the default SSL library has changed to mbedtls. If you're following this topic on 23.05 or later, please substitute any mentions of wolfssl with mbedtls, especially in the packages you install.


After a lot of fiddling and trial-and-error I found the problem: The default hostapd provided by wpad-basic is not enough, one needs the fully featured hostapd or wpad. This is not immediately obvious because the stripped down default one fails silently for most of the relevant options, with the exception of vlan_tagged_interface which it rejects violently.

So for posterity, here's my recipe for a dumb AP, and per-passphrase VLAN segmentation of clients:

First of all, the default wpad-basic will not suffice. We need a fully featured hostapd, either installed on its own or as part of the non-basic wpad:

# remove the default stripped-down wpad containing a stripped-down hostapd:
opkg remove wpad-basic-wolfssl
# removing the package does not kill the process, we need to manually do that:
killall hostapd
# install a fully-featured wpad containing a fully-featured hostapd:
opkg install wpad-wolfssl

For a single tagged ethernet port connecting to the router upstream, in /etc/config/network no changes are necessary. Especially it's not necessary to create custom bridges or interfaces. All of that will be done by hostapd at startup.

/etc/config/wireless needs the following options for the relevant wifi-iface:

        option wpa_psk_file '/etc/hostapd.wpa_psk'
        option vlan_file '/etc/hostapd.vlan'
        option vlan_tagged_interface 'eth0'
        option vlan_bridge 'br-vlan'
        option dynamic_vlan '1'

Of note: vlan_tagged_interface and vlan_bridge are prefixes to which the VLAN ID will be appended.

/etc/hostapd.wpa_psk needs to specify which passphrase should be go into which VLAN ID (in this example, clients connecting with the passphrase "supersecret" will go into VLAN ID 101):

vlanid=101 00:00:00:00:00:00 supersecret

/etc/hostapd.vlan needs to specify the wifi interface created for each (possible) VLAN ID. This can be named anything, but for consistency I choose to name it like a VLAN-tagged interface:

101 wlan0.101

This will result in:

  • creation of the wifi interface wlan0.101 as specified in /etc/hostapd.vlan
  • creation of the eth0.101 interface as specified by the vlan_tagged_interface prefix plus a dot and the VLAN ID, conveniently automatically tagging VLAN ID 101 on eth0
  • creation of the br-vlan101 bridge as specified by the vlan_bridge prefix plus the VLAN ID, with the previous two interfaces in it
root@OpenWrt:~# brctl show
bridge name     bridge id               STP enabled     interfaces
br-vlan101              8000.4018b1eb3c80       no              wlan0.101
                                                        eth0.101
br-lan          7fff.4018b1eb3c80       no              eth0

It may get a little bit more complicated if the outgoing interface should not be a singular tagged ethernet port but a bridge. In this case one would probably have to, in network config, create a bridge named something like br-vlan.101 containing the tagged ethernet ports and then use br-vlan as the vlan_tagged_interface prefix option. I haven't tried that yet because I have no need for that, but I don't see why it shouldn't work.

16 Likes

Addendum: using hostapd_cli (provided by the hostapd-utils package) the wpa_psk file can be reloaded without restarting the interface:

hostapd_cli reload_wpa_psk

Only clients whose passphrase changed will be booted off the radio, clients connected with passphrases that didn't change remain untouched.

All in all, the wpa_psk_file opens up a host of new possibilities. Having all networks -- unrestricted, guest, IOT, etc. -- on one single radio, distinguished by their passphrases. Rotating passphrases, even in a staggered manner. Giving individual users their individual passphrases ... I am really impressed, and a little surprised that I seem to be the first one to actually try and use it.

7 Likes

The reason could be that this feature is not documented in the OpenWrt wiki.

3 Likes

Some of the parameters are documented under "WPA Enterprise", which makes sense if you consider them exclusively related to a RADIUS client setup, which they obviously don't necessarily have to be.

I'm thinking how those parameters should be described. Maybe a dedicated "wpa_psk_file" section, even if it duplicates some of the option descriptions?

Another problem is that there is no support for any of this in LuCI, and probably for good reason, with the versatility of settings I can't even imagine how to add LuCI support for any of this would look like. This may very well remain a CLI only setting.

Is there nowadays a wrapper which translates every UCI option into a hostapd config or is there explicit support for this option in the openwrt hostapd config generator script?
I have walked this path too but I never stumped over WPA psk file and "just" used WPA and radius. WPA not wpa2 enterprise.
I like this approach and will check this soon. This way I could avoid fiddling with radius. :+1:

Hi, what if I have two wifi networks:

  • guest psk2 with isolation,
  • home psk2 without isolation

"wpa_psk_file" method will not work in this case?
Full Radius won't help here either or am I wrong?

I need to set the network with wifi parameters the same for all VAPs (encryption method, isolation, FT, ...)?

No, UCI option to hostapd config translation still has to happen deliberately and individually. But all of the necessary options are present and accounted for. I assume that's because the wpa_psk_file option exists since something like 2015, and the vlan options existed for RADIUS support, and just by pure chance those two option sets started inter-operating in 2019.

Yes, it will work. You can still mix vlan-less and vlan-tagged networks, e.g. in hostapd.wpa_psk:

00:00:00:00:00:00 homepassword
vlanid=123 00:00:00:00:00:00 guestpassword

The lines that do not contain a vlanid parameter will not use a vlan and those clients will be connected to the interface you specify via the regular network parameter, just like in a regular radio setup.

Importantly, for mixed vlan-less and vlan-tagged operations you need to set the dynamic_vlan option to 1, like in my example above (0 means no vlan-tagged radios, 2 means only vlan-tagged ones).

("without isolation" is not completely correct, though. vlan-less networks are still separated from vlan-tagged networks.)

1 Like

Thank You for the tip, one more question if I may..
I'v read that full hostapd is intended for "hardware with less resources", so if I have full wpad-openssl/mbedtls installed do I still have to remove hostapd-common and install full hostapd in its place, doesn`t full wpad have such options like hostapd?

The whole hostapd/wpad affair is a bit confusing. I'll try to explain:

  • hostapd-common does not contain hostapd/wpad but scripts and configurations that are supporting it ("common" scripts and configurations). It is always needed and will be installed by any wpad/hostapd package you install.
  • wpad contains wpa_supplicant and hostapd in one file. As the names suggest, hostapd is a wifi "host" or "server", wpa_supplicant is a wifi "supplicant" or "client", putting both into one file saves some space.
  • If you never intend to use your device as a wifi client, you don't need wpa_supplicant, and just a hostapd package would be sufficient.
  • wpad-basic is a stripped down version of wpad that comes by default with OpenWrt. It contains most functionality that APs use, but not all of it. As far as wpa_psk_file is concerned, it does contain support for wpa_psk_file in general, but not VLANs (those are lumped into the "extended" RADIUS/EAP support). So if you want to use VLANs with wpa_psk_file, wpad-basic is not sufficient.
  • All wpad and hostapd packages come in various flavours of SSL library. If I'm not completely wrong, the default SSL library that comes with OpenWrt is currently wolfssl, so it would make sense to "upgrade" wpad-basic-wolfssl to the full wpad-wolfssl. As its name suggests, wpad-openssl would require/install libopenssl. I'm not entirely sure what the SSL-variant-suffix-less wpad uses, I'm guessing it uses "whatever SSL is installed in the system", but someone else is certainly more qualified than me to answer that.

No. You never need to uninstall hostapd-common, it does not contain hostapd but needed support files and it would be re-installed by any hostapd/wpad package anyway. At most you need to uninstall wpad-basic-wolfssl (the current default wpad-basic) and install a full hostapd or wpad, e.g. wpad-wolfssl. If you already have full wpad installed, you don't need to do anything. A "full wpad" contains a "full hostapd".

10 Likes

Thank you for this in-depth explanation :smiley:

One more thing that bothers me...

I have a dumb AP to which all vlans are trunked through the WAN port, in addition, each LAN port is associated with a different VLAN so that I can conveniently access each network. Part of my /etc/config/network on dumb AP looks like this:

config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'
    
...

config device
		option name 'br0'
		option type 'bridge'
		option igmp_snooping '1'

config bridge-vlan
		option device 'br0'
		option vlan '50'
		option alias 'mgmt'
		list ports 'wan:t'
		list ports 'lan2:u*'

config bridge-vlan
		option device 'br0'
		option vlan '60'
		option alias 'home'
		list ports 'wan:t'
		list ports 'lan3:u*'

config bridge-vlan
		option device 'br0'
		option vlan '70'
		option alias 'iot'
		list ports 'wan:t'
		list ports 'lan4:u*'

config bridge-vlan
		option device 'br0'
		option vlan '80'
		option alias 'guest'
		list ports 'wan:t'

config interface 'mgmt'
		option device 'br0.mgmt'
		option proto 'static'
		option ipaddr '192.168.50.11'
		option netmask '255.255.255.0'
		option gateway '192.168.50.1'
		option dns '192.168.50.1'
		option ip6assign '60'

config interface 'home'
		option device 'br0.home'
		option proto 'none'

config interface 'iot'
		option device 'br0.iot'
		option proto 'none'

config interface 'guest'
		option device 'br0.guest'
		option proto 'none'

In the /etc/config/wireless I have the VAPs connected to the above interfaces as usual.

What will these options be in my case? :

		...
		option vlan_tagged_interface 'eth0'
		option vlan_bridge 'br-vlan'
		...
1 Like

Edit: After some experimentation I see a bit clearer.

First of all, since your vlan-tagged bridges already exist you want to add the new "vlan-dedicated" radio interfaces to those bridges, not have hostapd create new ones. There is a mechanism for that, "override the bridge name" by stating the bridge name as the third entry in a line in the vlan_file file.

This should work in hostapd.vlan:

60 wlan0.60 br0.iot

Hostapd will still have to create a vlan-specific radio-specific interface (I named it "wlan0.60" but it can really be named anything), and it should insert it into the br0.iot bridge.

As for the options, you need to have a vlan_bridge option set, but it can literally be anything because it is overridden by the above mechanism. And you don't need vlan_tagged_interface at all, the bridge already has vlan-tagged interfaces.

(This is also helpful if two radios, e.g. 2.4GHz and 5GHz, should put radio interfaces into the same vlan-tagged bridges.)

1 Like

I inserted an (at least fundamental) documentation. It will probably need a few revisions because it is a challenge to describe the interconnected features and how interfaces and bridges are handled. And I'm not entirely sure I fully grasp how hostapd handles interfaces and bridges, especially when they are individually named. But it's a start.

8 Likes

Have you looked at the wifi-station and wifi-vlan sections introduced as part of https://git.openwrt.org/?p=openwrt/openwrt.git;a=commit;h=5aa2ddd0d6b9759c62bbb7bb11b72a7f4269c16b ?

I'm not fully sure if it's similar to what you are testing or want to achieve.

4 Likes

I have not, thanks for the pointer. That is an interesting patch, it does two things:

The first patch to mac80211.sh I "indirectly" knew about, it enables the wpa_psk_file and vlan_file options that are necessary to specify, well, wpa_psk and vlan files. I have been using these options, I have already documented them, and without these options it would be very hard to get these files known to hostapd.

The second patch to hostapd.sh was yet unknown to me. It is basically an abstraction that compiles respective UCI options into wpa_psk and vlan files.

The UCI way seems quite a bit more verbose at first, especially if one were to use it for multiple PSK/passphrase variants. But the abstraction may help if the options should apply to multiple wifi radios. And I guess it would be a prerequisite if someone (not me!) would ever extend LuCI to support the mechanism. Directly editing the files on the other hand keeps the wireless config a bit more decluttered and allows it to be manipulated by external scripts (to rotate passwords, for example). It's a tradeoff, both ways have their use.

But this patch also answers the question since when these options are available in release OpenWrt: 21.02.

(And now I should probably add those new UCI sections to the wireless config wiki page, shouldn't I?)

Has anyone tried to do the following:

  • set up multiple APs with different passphrases (using wpa_psk_file) - exactly the same as here
  • set up 11r (FT-PSK) - both with "generate PMK locally" and without (using wildcard addresses in r0 and r1)

It fails for me, but I'm not entirely sure it is related to multiple PSKs or dynamic_vlan that I was using.

1 Like

Replying to myself:

It works out of the box on 23.05.0-rc1 and the main branch. The issue was fixed in commit: FT: Store PMK-R0/PMK-R1 after EAPOL-Key msg 2/4 MIC validation

I have tried porting this back to 19.07.10 (as I have 4/32 device I would like to test this on), but so far haven't managed. I'll share the patch if I manage it.

Another update:

Here is a patch if someone wants it that can be applied on top of hostapd-2019-08-08-ca8c2bd2 to fix FT with wpa_psk_file on 19.07.10

openwrt-19.07.10-multi-psk-ft.patch

Hopefully someone finds it useful.

2 Likes

Hi @takimata, I was interested in using wpa_psk_file and I am struggling to get it up and running. Can you please share a demo network and wireless config file which I could use to setup an BSS with vlan functionality?
Following are my wireless and network config files:

config wifi-device 'radio0'
        option type 'mac80211'
        option path '1e140000.pcie/pci0000:00/0000:00:01.0/0000:02:00.0'
        option channel '1'
        option band '2g'
        option htmode 'HT20'
        option disabled '1'

config wifi-device 'radio1'
        option type 'mac80211'
        option path '1e140000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0'
        option channel '36'
        option band '5g'
        option htmode 'VHT80'
        option country 'IN'
        option cell_density '0'

config wifi-iface 'wifinet0'
        option device 'radio1'
        option mode 'ap'
        option ssid 'HomeWifi'
        option encryption 'psk2'
        option network 'lan'
        option wpa_psk_file '/etc/hostapd.wpa_psk'
        option vlan_file '/etc/hostapd.vlan'
        option vlan_bridge 'br-vlan'
        option dynamic_vlan '1'

and


config interface 'loopback'
        option device 'lo'
        option proto 'static'
        option ipaddr '127.0.0.1'
        option netmask '255.0.0.0'

config globals 'globals'
        option packet_steering '1'
        option ula_prefix 'fd42:2825:343a::/48'

config device
        option type 'bridge'
        option name 'br-switch'
        list ports 'lan1'
        list ports 'lan2'
        list ports 'wan'

config bridge-vlan
        option device 'br-switch'
        option vlan '4'
        list ports 'lan1:t'
        list ports 'lan2:t'
        list ports 'wan:t'

config bridge-vlan
        option device 'br-switch'
        option vlan '8'
        list ports 'lan1:t'
        list ports 'lan2:t'
        list ports 'wan:t'

config bridge-vlan
        option device 'br-switch'
        option vlan '16'
        list ports 'lan1:u*'
        list ports 'lan2:u*'
        list ports 'wan:u*'

config bridge-vlan
        option device 'br-switch'
        option vlan '32'
        list ports 'lan1:t'
        list ports 'lan2:t'
        list ports 'wan:t'

config interface 'iot'
        option proto 'none'
        option device 'br-switch.4'
        option defaultroute '0'
        option delegate '0'

config interface 'guest'
        option proto 'none'
        option device 'br-switch.8'
        option defaultroute '0'
        option delegate '0'

config interface 'mgmt'
        option proto 'dhcp'
        option device 'br-switch.32'
        option delegate '0'

config interface 'lan'
        option proto 'none'
        option device 'br-switch.16'
        option defaultroute '0'
        option delegate '0'

And there are the wpa_psk and vlan files:

vlanid=8 00:00:00:00:00:00 supersecret
vlanid=16 00:00:00:00:00:00 othersecret

and

8 wlan0.8 br-switch.8
16 wlan0.16 br-switch.16

EDIT: After some debugging, I could figure out that wpa_psk_file is being read but the vlan option isn't working. I have installed the full version of wpad-wolfssl. In the logs it shows this error for passphrases with vlanid:

daemon.notice hostapd: wlan1: AP-STA-POSSIBLE-PSK-MISMATCH

Please help me figure this out... :slight_smile:, Thanks!