Port Based Network Access Control (802.1x)
Note: The support for 802.1x was added with the BISDN Linux release 4.9 within the major version 4 stream and 5.3 within the major version 5 stream.
Introduction
IEEE 802.1X is a network access control protocol that provides authentication and security for wired and wireless networks. It ensures that only authorized devices and users can connect to a network by using a process called port-based network access control (PNAC).
802.1X operates with three key components:
- Supplicant (Client/Device) – The device that wants to connect to the network (e.g., a laptop or access point).
- Authenticator (Network Switch/Access Point) – The gateway that enforces authentication before allowing network access.
- Authentication Server (e.g. RADIUS Server) – The backend system that verifies credentials and grants or denies access.
On BISDN Linux, we provide an integrated freeRADIUS Server on the switch to allow using 802.1x without the need to set up an external authentication server.
Authentication Flow:
- The supplicant sends authentication credentials (username/password) to the authenticator. The following authentication and authorization sequence is done via EAPoL (Extensible Authentication Protocol over Local Area Network). For supplicants that do not support 802.1x EAPoL, we support MAC Authentication Bypass (MAB), allowing authorization based on the supplicant’s MAC address.
- The authenticator (hostapd running on the BISDN Linux switch) forwards these credentials (or the MAC as Client Identifier) to the authentication server (e.g. freeRADIUS on the BISDN Linux switch).
- The authentication server validates the credentials and informs the authenticator whether to allow or block access.
To use 802.1x on BISDN Linux switches, you need to configure hostapd on the switch as well as an authentication server. Configuration examples for EAPoL and MAB (MAC Authentication Bypass) with hostapd and freeRADIUS on the switch are documented below.
Both example scenarios described below assume that you are familiar with the configuration of bridges, VLANs and ports on BISDN Linux and that you have set up a fully functional L2 VLAN bridge between all ports on the switch on which supplicants are connected. Documentation and examples for L2 VLAN bridge configurations on BISDN Linux can be found here:
When building a new set up (for testing or production), we recommend to verify the functionality of the VLAN bridge BEFORE adding PNAC with 802.1x.
The only extension we need to enable port based network access control is to set the [Link]
ActivationPolicy
to manual
for each connected port. This change is needed, since we want hostapd to control the link state, based on the authentication and authorization status.
For the systemd example given in the documentation linked above, the configuration for port1 would need to be extended as shown below.
BEFORE:
20-port1.network:
[Match]
Name=port1
[Network]
Bridge=swbridge
[BridgeVLAN]
PVID=1
EgressUntagged=1
VLAN=1-10
AFTER:
20-port1.network:
[Match]
Name=port1
[Link]
ActivationPolicy=manual
[Network]
Bridge=swbridge
[BridgeVLAN]
PVID=1
EgressUntagged=1
VLAN=1-10
The .netdev and .network configuration of the swbridge can be done in exactly the same way as described in the linked documentation.
802.1x with EAPoL
For this case, we assume that the supplicant is connected to port2 of the BISDN Linux switch and wants to use EAPoL with the username bob
and the password hello
to authenticate. The BISDN Linux switch follows the EAPoL sequence to first authenticate and then authorize the supplicant and unlock port2.
Configuration of freeRADIUS
The upstream configuration reference for freeRADIUS can be found here:
The freeRADIUS installation on BISDN Linux comes with a set of configuration files, which can be used without major changes for our testing case. The only change needed to authenticate and authorize the user bob
with the password hello
is to uncomment those credentials in the file /etc/raddb/users
on the switch.
BEFORE:
# The canonical testing user which is in most of the
# examples.
#
#bob Cleartext-Password := "hello"
# Reply-Message := "Hello, %{User-Name}"
#
AFTER:
# The canonical testing user which is in most of the
# examples.
#
bob Cleartext-Password := "hello"
# Reply-Message := "Hello, %{User-Name}"
#
After those changes are done, the systemd service for radiusd can be enabled and started with the commands shown below.
sudo systemctl enable radiusd.service
sudo systemctl start radiusd.service
The freeRADIUS daemon radiusd should now be running and ready to accept connections and requests from hostapd - which is configured in the next step.
Configuration of hostapd
The upstream configuration reference for hostapd version 2.10 can be found here:
To ease the initial configuration of hostapd on BISDN Linux, we ship an example configuration file for the described EAPoL use case. The file can be found on the switch at /etc/hostapd/hostapd-wired.conf.example
. For this scenario, the file can be copied without any modifications to configure hostapd for port2. Please make sure to use the naming scheme hostapd-wired-$PORTNAME.conf
to allow the systemd service configured in the next step to find the right configuration. For our scenario, the value of $PORTNAME
is port2
sudo cp /etc/hostapd/hostapd-wired.conf.example /etc/hostapd/hostapd-wired-port2.conf
After the configuration was copied, the port specific hostapd service instance can be enabled and started in systemd by running the commands below. Please make sure to use the service naming scheme hostapd-wired@$PORTNAME
to allow the systemd service to find the right configuration for this hostapd service instance. For our example, the value of $PORTNAME
is port2
sudo systemctl enable hostapd-wired@port2.service
sudo systemctl start hostapd-wired@port2.service
After the service instance has been started, it should automatically connect to the radiusd service configured and started before and be ready to authenticate the supplicant connected to port2 following the EAPoL sequence.
Configuration of wpa_supplicant on Ubuntu for testing
To configure a Ubuntu server as supplicant, we first install the package wpa_supplicant
on it with the command shown below.
sudo apt install wpasupplicant
After the package is installed, we need to create a configuration for the supplicant, with the same credentials we used for our freeRADIUS configuration before. The configuration file needs to be created as /etc/wpa_supplicant/wpa_supplicant-wired-$PORTNAME.conf
, while $PORTNAME
is the name of the port connected to port2
of the BISDN Linux switch configured before. For our example, the value of $PORTNAME
is eno2
. The content of the file /etc/wpa_supplicant/wpa_supplicant-wired-eno2.conf
for our example scenario on the server is shown below.
ap_scan=0
network={
key_mgmt=IEEE8021X
eap=MD5
identity="bob"
password="hello"
}
After the configuration file was created, the service instance on our server for the network interface eno2
can be enabled and started by running the command shown below.
sudo systemctl enable wpa_supplicant-wired@eno2
sudo systemctl start wpa_supplicant-wired@eno2
As soon as the service is started, the EAPoL authentication sequence of the supplicant with the switch should be started and the corresponding port2 on the switch should be unlocked.
If you connect a second supplicant (e.g. a second Ubuntu server with wpa_supplicant) following the process for radiusd and hostapd described above, as well as assign IP addresses to the respective interfaces of both supplicants, you should be able to send traffic between them after they have been authenticated and authorized by the BISDN Linux switch.
802.1x with MAB
For this case, we assume that the supplicant is connected to port2 of the BISDN Linux switch and is unable to use EAPoL. Instead of trying to authenticate, the supplicant is just sending some network traffic towards the switch, to try to reach other devices on the network. The BISDN Linux switch receives this traffic and uses MAB to authorize the supplicant based on its MAC address and unlock port2.
Configuration of freeRADIUS
The upstream configuration reference for freeRADIUS, as well as the MAB specific section can be found here:
The freeRADIUS installation on BISDN Linux comes with a set of configuration files, which can be used with some minor extensions for our testing case. The first extension to the default configuration needed is described in the upstream documentation here:
Since the option compat = no
shown in this example has already been removed from the freeRADIUS version shipped in BISDN Linux, we only need to extend the file /etc/raddb/mods-available/files
as shown below. If you try to use the option compat
, radiusd will fail to start.
BEFORE:
# -*- text -*-
#
# $Id: e3f3bf568d92eba8eb17bbad590f846f2d9e1ac8 $
# Livingston-style 'users' file
#
# See "man users" for more information.
#
files {
# Search for files in a subdirectory of mods-config which
# matches this instance of the files module.
moddir = ${modconfdir}/${.:instance}
# The default key attribute to use for matches. The content
# of this attribute is used to match the "name" of the
# entry.
#key = "%{%{Stripped-User-Name}:-%{User-Name}}"
# The old "users" style file is now located here.
filename = ${moddir}/authorize
# This is accepted for backwards compatibility
# It will be removed in a future release.
# usersfile = ${moddir}/authorize
# These are accepted for backwards compatibility.
# They will be renamed in a future release.
acctusersfile = ${moddir}/accounting
preproxy_usersfile = ${moddir}/pre-proxy
}
AFTER:
# -*- text -*-
#
# $Id: e3f3bf568d92eba8eb17bbad590f846f2d9e1ac8 $
# Livingston-style 'users' file
#
# See "man users" for more information.
#
files {
# Search for files in a subdirectory of mods-config which
# matches this instance of the files module.
moddir = ${modconfdir}/${.:instance}
# The default key attribute to use for matches. The content
# of this attribute is used to match the "name" of the
# entry.
#key = "%{%{Stripped-User-Name}:-%{User-Name}}"
# The old "users" style file is now located here.
filename = ${moddir}/authorize
# This is accepted for backwards compatibility
# It will be removed in a future release.
# usersfile = ${moddir}/authorize
# These are accepted for backwards compatibility.
# They will be renamed in a future release.
acctusersfile = ${moddir}/accounting
preproxy_usersfile = ${moddir}/pre-proxy
}
files authorized_macs {
# The default key attribute to use for matches. The content
# of this attribute is used to match the "name" of the
# entry.
key = "%{Calling-Station-ID}"
usersfile = ${confdir}/authorized_macs
}
The next file needed for MAB with freeRADIUS needs to be created at /etc/raddb/authorized_macs
, like documented upstream here:
Assuming the MAC address on the port of our supplicant that is connected to our switch is 1a:2b:3c:4d:5f:66
, the configuration on our switch should look like the one shown below. Be aware that freeRADIUS will rewrite the MAC address of the supplicant in the function rewrite_calling_station_id
within the authorize
block to be all capital and use -
as separator before comparing it to the MAC in the authorized_macs file.
1A-2B-3C-4D-5F-66
Reply-Message = "Device with MAC Address %{Calling-Station-Id} authorized for network access"
Last, but not least, we need to replace the authorization block in the /etc/raddb/sites-available/default
file on our BISDN Linux switch with the block shown below.
authorize {
preprocess
# If cleaning up the Calling-Station-Id...
rewrite_calling_station_id
# Now check against the authorized_macs file
authorized_macs
if (!ok) {
# No match was found, so reject
reject
}
else {
# The MAC address was found, so update Auth-Type
# to accept this auth.
update control {
Auth-Type := Accept
}
}
}
The block shown above was copied without modifications from the upstream documentation here:
Please make sure to replace the authorize {}
block and do not try to amend the file. If you want to configure 802.1x EAPoL and MAB as fallback in freeRADIUS, please refer to the upstream documentation here:
For the case documented upstream, you will also need to modify the /etc/raddb/users
file, as documented for our EAPoL based authentication example above.
After those changes are done, the systemd service for radiusd can be enabled and started with the commands shown below.
sudo systemctl enable radiusd.service
sudo systemctl start radiusd.service
The freeRADIUS daemon radiusd should now be running and be ready to accept connections and requests from hostapd - which is configured in the next step.
Configuration of hostapd
The upstream configuration reference for hostapd version 2.10 can be found here:
To ease the initial configuration of hostapd on BISDN Linux, we ship an example configuration file for the EAPoL use case. The file can be found on the switch at /etc/hostapd/hostapd-wired.conf.example
. For the MAB scenario, the file can be copied, but has to be slightly modified, before it is used to configure hostapd for port2.
Please make sure to use the naming scheme hostapd-wired-$PORTNAME.conf
to allow the systemd service configured in the next step to find the right configuration. For our scenario, the value of $PORTNAME
is port2
sudo cp /etc/hostapd/hostapd-wired.conf.example /etc/hostapd/hostapd-wired-port2.conf
The only line that has to be modified in the example configuration copied above, is the one targeting mac_auth
. By default, this option is set to mac_auth=0
, to completely disable MAC authentication bypassing. To enable MAB for our scenario, we need to set it to mac_auth=1
like shown below.
...
# Enable MAC Authentication Bypass (MAB):
# 0 = disable MAB
# 1 = enable MAB
mac_auth=1
...
All other settings in this file can be kept as they are.
After the configuration was copied and modified, the port specific hostapd service instance can be enabled and started in systemd by running the commands below. Please make sure to use the service naming scheme hostapd-wired@$PORTNAME
to allow the systemd service to find the right configuration for this hostapd service instance. For our example, the value of $PORTNAME
is port2
sudo systemctl enable hostapd-wired@port2.service
sudo systemctl start hostapd-wired@port2.service
After the service instance has been started, it should automatically connect to the radiusd service configured and started before and be ready to authorize the supplicant connected to port2 with MAB.
Configuration of Ubuntu for testing
Nothing to do here!
Since MAB is based on just the MAC address of the supplicant, there is no configuration needed on our Ubuntu test server. By default, it will be authorized via MAB by its MAC address when sending the first couple of packets towards the switch.
If you connect a second supplicant (e.g. a second Ubuntu server) following the process for radiusd and hostapd described above, as well as assign IP addresses to the respective interfaces of both supplicants, you should be able to send traffic between them after they have been authorized by the BISDN Linux switch.
Troubleshooting
In the unlikely case that following the example configurations given here did not yield the expected outcome, you can try the steps documented below to narrow down what the cause of the issue might be.
-
Verify that the service instance of hostapd for the given port (e.g. port2) on the switch is up and running.
sudo systemctl status hostapd-wired@port2 ● hostapd-wired@port2.service - Hostapd IEEE 802.1X Authenticator Loaded: loaded (/lib/systemd/system/hostapd-wired@.service; enabled; vendor preset: disabled) Active: active (running) since Fri 2025-02-14 14:26:40 UTC; 1min 0s ago Main PID: 3415 (hostapd) Tasks: 1 (limit: 4721) Memory: 516.0K CGroup: /system.slice/system-hostapd\x2dwired.slice/hostapd-wired@port2.service └─ 3415 /usr/sbin/hostapd /etc/hostapd/hostapd-wired-port2.conf -P /run/hostapd-port2.pid -i port2
-
Verify that radiusd on the switch is up and running (only applicable if you do not run an external authentication server).
sudo systemctl status radiusd ● radiusd.service - FreeRADIUS high performance RADIUS server. Loaded: loaded (/lib/systemd/system/radiusd.service; enabled; vendor preset: disabled) Active: active (running) since Fri 2025-02-14 14:09:27 UTC; 26min ago Main PID: 3330 (radiusd) Tasks: 6 (limit: 4721) Memory: 39.8M CGroup: /system.slice/radiusd.service └─ 3330 /usr/sbin/radiusd -d /etc/raddb
-
Verify that all ports connected to the swbridge and the bridge itself are up.
sudo ip l show dev port2 9: port2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master swbridge state UP mode DEFAULT group default qlen 1000 link/ether 04:f8:f8:22:f9:c2 brd ff:ff:ff:ff:ff:ff
sudo ip l show dev swbridge 6: swbridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether c2:75:49:92:d5:ca brd ff:ff:ff:ff:ff:ff
-
Check the output from the hostapd service instance and verify that the authentication has completed successfully. Look for specific messages like
EAP Success
andIEEE 802.1X: authorizing port
sudo journalctl -eu hostapd-wired@port2 Feb 14 14:38:44 accton-as4610-54 systemd[1]: Started Hostapd IEEE 802.1X Authenticator. Feb 14 14:38:44 accton-as4610-54 hostapd[1434]: driver_linux_wired: attaching to port2 (9) ... Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: CTRL-EVENT-EAP-STARTED 0c:c4:7a:9c:27:d1 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=1 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: Sending EAP Packet (identifier 54) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: received EAP packet (code=2 id=54 len=8) from STA: EAP Response-Identity (1) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: STA identity 'bob' Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Sending RADIUS message to authentication server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Next RADIUS client retransmit in 3 seconds Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received 80 bytes from RADIUS server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received RADIUS message Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 RADIUS: Received RADIUS packet matched with a pending request, round trip time 0.00 sec Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: decapsulated EAP packet (code=1 id=55 len=22) from RADIUS server: EAP-Request-MD5 (4) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: Sending EAP Packet (identifier 55) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: received EAP packet (code=2 id=55 len=22) from STA: EAP Response-MD5 (4) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Sending RADIUS message to authentication server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Next RADIUS client retransmit in 3 seconds Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received 49 bytes from RADIUS server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received RADIUS message Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 RADIUS: Received RADIUS packet matched with a pending request, round trip time 0.00 sec Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: old identity 'bob' updated with User-Name from Access-Accept 'bob' Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: decapsulated EAP packet (code=3 id=55 len=4) from RADIUS server: EAP Success Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: CTRL-EVENT-EAP-SUCCESS2 0c:c4:7a:9c:27:d1 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: Sending EAP Packet (identifier 55) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: authorizing 0c:c4:7a:9c:27:d1 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: AP-STA-CONNECTED 0c:c4:7a:9c:27:d1 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: authorizing port Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 RADIUS: starting accounting session EF57F24C58987FA3 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Sending RADIUS message to accounting server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Next RADIUS client retransmit in 3 seconds Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: authenticated - EAP type: 4 (MD5) Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 RADIUS: starting accounting session EF57F24C58987FA3 Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received 20 bytes from RADIUS server Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: RADIUS Received RADIUS message Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 RADIUS: Received RADIUS packet matched with a pending request, round trip time 0.00 sec Feb 14 14:40:02 accton-as4610-54 hostapd[1434]: port2: STA 0c:c4:7a:9c:27:d1 IEEE 802.1X: authenticated - EAP type: 4 (MD5)