I apologize for resurrecting an old thread.
Warning: This is a total kludge, It is a "this needs to work" solution meant to persist between updates and not require that I build and package my own dnsmasq-full package. This is not what what good looks like.
I have several mwan3 rules that are just ineffective without ipset (or some kind of set) support. In order to make things more-or-less work with 23.05.* I cobbled together this solution:
Packages: mwan3
dnsmasq-full
In System -> Startup -> Local Startup:
# Create ipset rules
# IPSet for HBOMax
ipset create hbomax hash:ip
[...]
In Network -> DHCP and DNS -> IP Sets (this seems misnamed since it creates nft sets):
Name, then FQDN matches, Netfilter and IP Family are left at default. I.e.:
hbomax hbomaxcdn.com fw4 IPv4+6
api.hbo.com
h264.io
I then use the following script to sync the nft ruleset with ipset every 10 seconds. In cron I have it set to run once per minute, though each launch will check to see if the script is already running and, if it is, will self-terminate.
This script dumps the entries in the nft rulesets to a simple text file in /tmp/ It compares the dump from 10 seconds ago against the current dump, identifies whatever is new, and imports those into the kernel's current ipsets (these were created at startup earlier). If it find an ipset specified (somehow dnsmasq is populating one that we forgot to create) then it will create the missing set.
This script is intentionally using /tmp in order to avoid frequent writes to persistent storage. There will be no persistence of the ipsets (or nft sets) between reboots.
#!/bin/bash
# Check if the script is already running
pid=$$
script=$(basename $0)
guard="/tmp/$script-$(id -nu).pid"
if test -f $guard ; then
echo >&2 "ERROR: Script already runs... own PID=$pid"
ps | grep $script | grep -v grep >&2
exit 1
fi
trap "rm -f $guard" EXIT
echo $pid >$guard
# Get fresh data every 10 seconds
# Populate our old, new, and import sets
# We want this while loop to persist forever
while true; do
# Remove old files
rm -rf /tmp/set2 /tmp/set_import
# If set1 exists, rename it to set2
if [ -e "/tmp/set1" ]; then
mv /tmp/set1 /tmp/set2
else
# If set1 doesn't exist, create a 0 length file called set2
touch /tmp/set2
fi
# Find the contents of the nft set tables and export them to a text file
nft list ruleset |\
awk '/set .* \{/ {ipset=$2; getline; while ($1 != "}") {gsub(/[{};]/, ""); print $1, ipset; getline}}' |\
sed 's/,//' |\
sed -n '/^[0-9]/p' > /tmp/set1
# Compare new data in set1 against older data in set2 and save the delta to set_import
# We are looking for new entries by looking for the lines that start with "+"
# We are stripping anything that doesn't start with "+" and are then stripping the "+" itself
diff -u /tmp/set2 /tmp/set1 | sed -n '/^+[^+]/s/^+//p' > /tmp/set_import
# Process the IP sets from set_import
# Take the entries we've identified as new and import them to ipset
while IFS= read -r line; do
if [ -z "$line" ]; then
continue
fi
ip_address=$(echo "$line" | awk '{print $1}')
ipset_name=$(echo "$line" | awk '{print $2}')
if [ -z "$ip_address" ] || [ -z "$ipset_name" ]; then
echo "Malformed line: $line. Skipping."
continue
fi
# Check if the IPset exists
ipset_exists=$(ipset list -n "$ipset_name")
# If IPset does not exist, create it
if [ -z "$ipset_exists" ]; then
ipset create "$ipset_name" hash:ip
echo "Created IPset: $ipset_name"
fi
# Add IP address to the IPset if not already added
ipset test "$ipset_name" "$ip_address" >/dev/null 2>&1
if [ $? -ne 0 ]; then
ipset add -q "$ipset_name" "$ip_address"
logger "Added IP address to $ipset_name: $ip_address"
fi
done < /tmp/set_import
# Sleep for 10 seconds before the next iteration
sleep 10
done
Improvements for the future could be a persistent list of the nft rulesets that I care about and have the script auto-create the ipsets as needed (the script is already capable of doing this, so strictly speaking creating the ipsets in Startup -> Local Startup is not needed). Persisting the ipset (and nft ruleset) could be contemplated by writing the files to persistent storage on some regular interval (60 min?) and importing that set at startup.