Thought I'd post this after trying a few different approaches to getting the outcome I'm after, if it helps anyone else, or if someone sees this and knows a cleaner approach.
I have a network with a few VLANs for different devices, I want IPv6 support but also want to attribute traffic for eg. stats in AdGuard Home.
I start with a set of files per network in the format <mac> <internet allowed> <hostname>
eg. my IOT vlan config can look like:
00:11:22:33:44:55 y smarttoaster.iot.lan
AA:BB:CC:DD:EE:FF n camera.iot.lan
I then use a script to fetch IPs associated with those mac addresses:
#!/bin/sh
# Check if the correct number of arguments is provided
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input_file_with_mac_and_host> <output_host_file>"
exit 1
fi
# Input file containing MAC and Host info
input_file="$1"
# Output host file path
output_file="$2"
# Clear the output file if it exists
> "$output_file"
# Get the IP-to-MAC mapping from `ip neigh` and store it
ip_neigh_output=$(ip neigh)
# Loop through the file line by line
while read -r line; do
# Skip empty lines
if [ -z "$line" ]; then
continue
fi
# Split out each column address
mac=$(echo "$line" | awk '{print $1}')
allowed=$(echo "$line" | awk '{print $2}')
host=$(echo "$line" | awk '{print $3}')
# Get the IP addresses associated with the MAC address from the stored `ip neigh` output
ips=$(echo "$ip_neigh_output" | grep -i "$mac" | awk '{print $1}')
# Check if any IP addresses were found
if [ -n "$ips" ]; then
# Loop through each found IP and append to the output file
for ip in $ips; do
echo "$ip $host" >> "$output_file"
done
fi
done < "$input_file"
and generate per-vlan hosts file + trigger DNSMasq to update with a script like below:
#!/bin/sh
# Generate DNS hosts files
./gen_dns_list.sh /path/to/config/<vlan1> /path/to/hosts/<vlan1>
./gen_dns_list.sh /path/to/config/<vlan2> /path/to/hosts/<vlan2>
# Trigger dnsmasq update
odhcpd-update > /dev/null 2>&1
echo "odchpd-update triggered"
which I have triggered by a few hotplug events/on a timer so the hosts files stay up to date. The host files are loaded into DNSmasq with list addnhosts '/path/to/hosts/<vlan>'
entries in /etc/config/dhcp
.
I also have a script that extracts the MAC addresses that have internet allowed
column set to y
:
#!/bin/sh
# Check if the correct number of arguments is provided
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input_file_with_mac_and_host> <output_mac_file>"
exit 1
fi
# Input file containing MAC and Host info
input_file="$1"
# Output mac file path
output_file="$2"
# Clear the output file if it exists
> "$output_file"
# Loop through the file line by line
while read -r line; do
# Skip empty lines
if [ -z "$line" ]; then
continue
fi
# Split out each column address
mac=$(echo "$line" | awk '{print $1}')
allowed=$(echo "$line" | awk '{print $2}')
host=$(echo "$line" | awk '{print $3}')
# Write only if internet allowed flag set
if [ "$allowed" = "y" ]; then
echo "$mac" >> "$output_file"
fi
done < "$input_file"
echo "Mac file generated: $output_file"
and I generate the MAC list per-vlan and trigger firewall4 to update the ipset with a script like:
#!/bin/sh
# Generate MAC hosts files
./gen_mac_list.sh /path/toconfig/<vlan 1> /path/to/maclists/<vlan 1>
./gen_mac_list.sh /path/toconfig/<vlan 2> /path/to/maclists/<vlan 2>
# Flush and rebuild firewall ipset
echo "Flushing NFT mac sets"
nft flush set inet fw4 mac_allowlist_<vlan 1>
nft flush set inet fw4 mac_allowlist_<vlan 2>
fw4 reload-sets
which I run everytime I edit my config files.
In my firewall rules I have every vlan covered by the above set to not allow WAN access in my zone rules, and setup ipset and explicit allowlist rules in /etc/config/firewall
as follows:
config ipset
option name 'mac_allowlist_<vlan>'
list match 'src_mac'
option loadfile /path/to/maclists/<vlan>'
config rule
option name '<vlan>-Allowlist'
list proto 'all'
option src '<vlan>'
option ipset 'mac_allowlist_<vlan>'
option dest 'wan'
option target 'ACCEPT'
this allows me to have some basic allowlist control over internet connectivity of IOT devices, and causes devices to lose internet connectivity if they rotate mac addresses (eg. I forget to turn off rotating mac or forget and reconnect to the wifi network causing the mac address to be re-randomized) and makes it quickly noticeable so I can update the MAC address associated with that host. Note the emphasis on basic, this isn't a security solution and doesn't protect against MAC cloning.
I also enter the hosts in the format <hostname>.<vlan>.lan
and have dnsmasq local domain set to unassigned.lan
so hosts on the guest network or otherwise don't appear in the config get <hostname>.unassigned.lan