Hostapd – Mapping MAC Address to VLAN ID via accept_mac_file

access-pointhostapdlinuxmac addressvlan

I want hostapd to assign each station a given VLAN based on MAC address. BTW, my network is quite small. Using RADIUS would be an overkill.

According to the hostapd manual:

Optionally, the local MAC ACL list (accept_mac_file) can be used to
set static client MAC address to VLAN ID mapping.

Let's assume I only have 1 station (with MAC DE:AD:BE:EF:CA:FE). I've created a VLAN using:

sudo ip link add link wlan0 name vlan.100 type vlan id 100
sudo ip addr add 192.168.100.1/24 brd 192.168.100.255 dev vlan.100
sudo ip link set dev vlan.100 up

On hostapd.conf I'm using:

#  Interface to use
interface=wlan0
#  Driver
driver=nl80211
#  Name of the network
ssid=YaddaYadda
#  Use the 2.4GHz band: g = IEEE 802.11g (2.4 GHz)
hw_mode=g
#  Use channel 6
channel=6
#  Enable 802.11n
ieee80211n=1
#  Enable 40MHz channels with 20ns guard interval
ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
#  Accept only known MAC addresses
macaddr_acl=1
accept_mac_file=/etc/hostapd/accept
#  Use WPA authentication
auth_algs=1
#  Send empty SSID in beacons and ignore probe request frames that do not specify full SSID
ignore_broadcast_ssid=1
#  Use WPA2
wpa=2
#  Use a pre-shared key
wpa_key_mgmt=WPA-PSK
#  Enable the wireless multimedia extensions
wmm_enabled=1
#  The network hashed passphrase
wpa_psk=786451648446NotReallyTheHashedPassphrase849989654651651651654564
#  Use AES, instead of TKIP
rsn_pairwise=CCMP
# Isolate Clients
ap_isolate=1

#  HOSTAPD event logger configuration
logger_syslog=-1
logger_syslog_level=2
logger_stdout=-1
logger_stdout_level=2

#  Country code
country_code=NO

On file /etc/hostapd/accept I have included the following:

DE:AD:BE:EF:CA:FE vlan.100

hostapd starts without any issues. However, after connecting my station with MAC DE:AD:BE:EF:CA:FE, I'm still getting all the traffic on wlan0 and not on vlan.100…

hostapd does not say much about how the file accept_mac_file should look like to map MAC <-> VLAN ID. I've tried comma, with and without spaces, but no luck…

Has anyone been able to get this MAC <-> VLAN mapping to work?

Best Answer

Looking at the hostapd source code, I find that there is a hostapd_config_read_maclist() function in config_file.c that parses the files referenced by the accept_mac_file and deny_mac_file configuration values:

static int hostapd_config_read_maclist(const char *fname,
                       struct mac_acl_entry **acl, int *num)
{
    FILE *f;
    char buf[128], *pos;
    int line = 0;
    u8 addr[ETH_ALEN];
    int vlan_id;

    f = fopen(fname, "r");
    if (!f) {
        wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
        return -1;
    }

    while (fgets(buf, sizeof(buf), f)) {
        int rem = 0;

        line++;

        if (buf[0] == '#')
            continue;
        pos = buf;
        while (*pos != '\0') {
            if (*pos == '\n') {
                *pos = '\0';
                break;
            }
            pos++;
        }
        if (buf[0] == '\0')
            continue;
        pos = buf;
        if (buf[0] == '-') {
            rem = 1;
            pos++;
        }

        if (hwaddr_aton(pos, addr)) {
            wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
                   "line %d in '%s'", pos, line, fname);
            fclose(f);
            return -1;
        }

        if (rem) {
            hostapd_remove_acl_mac(acl, num, addr);
            continue;
        }
        vlan_id = 0;
        pos = buf;
        while (*pos != '\0' && *pos != ' ' && *pos != '\t')
            pos++;
        while (*pos == ' ' || *pos == '\t')
            pos++;
        if (*pos != '\0')
            vlan_id = atoi(pos);

        if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) {
            fclose(f);
            return -1;
        }
    }

    fclose(f);

    if (*acl)
        qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);

    return 0;
}

I haven't (yet) tested this to confirm, but based on the code above, it looks like the rules for *_mac_file files are as follows:

  1. Lines beginning with # are ignored.
  2. Empty lines are ignored.
  3. A MAC address is specified by six octets in hexadecimal, each separated by :.
  • A MAC address can optionally be prefixed with - to remove all occurrences of it from the list thus far.
  • Otherwise, a MAC address can optionally be followed by some combination of space and tab characters followed by an integer specifying the VLAN. If the VLAN is 0, not specified, or not an integer this is treated as no VLAN.

Based on these rules, I believe the way to map the MAC address DE:AD:BE:EF:CA:FE to the VLAN with name vlan.100 and ID 100 is with this line:

DE:AD:BE:EF:CA:FE 100

That is, only the VLAN ID matters to hostapd, not the VLAN name.