Configuring a single physical Wi-Fi radio to run simultaneous infrastructure (AP) SSIDs alongside ad-hoc (IBSS) or mesh interfaces requires leveraging Linux’s mac80211 subsystem, virtual interface (vif) creation via iw, and multi-interface management via hostapd and networking routing.
Because running AP and Ad-Hoc modes concurrently on the same radio depends heavily on hardware and driver concurrency capabilities, this guide walks through the complete procedure on an Ubuntu Linux environment.
Step 1: Verify Hardware & Driver Concurrency Support
Before creating virtual interfaces, verify that your wireless card supports simultaneous AP and Ad-Hoc (or Mesh) operations on the same physical chip.
-
List your wireless devices and check supported interface combinations:
Bash
iw list | grep -A 20 "Supported interface modes" -
Look specifically at the Valid interface combinations section in the output. It will explicitly state whether combinations like
AP+IBSS(Ad-Hoc) orAP+MESHare supported concurrently on the same radio index.
Step 2: Create Virtual Network Interfaces (vifs)
Assuming your physical wireless interface is wlan0, you need to spin up separate virtual interfaces for your Infrastructure APs and your Ad-Hoc/Mesh backbone.
**1.Bring down the physical interface:**Required before modifying virtual interface topologies.
Bash
sudo ip link set wlan0 down
**2.Create the Infrastructure AP virtual interface:**Designated for client connections.
Bash
sudo iw dev wlan0 interface add wlan0_ap type __ap
**3.Create the Ad-Hoc or Mesh virtual interface:**Designated for peer-to-peer or backbone nodes.
Bash
# For traditional Ad-Hoc (IBSS):
sudo iw dev wlan0 interface add wlan0_adhoc type ibss
# Alternatively, for 802.11s Mesh (recommended over legacy Ad-Hoc for stability):
# sudo iw dev wlan0 interface add wlan0_mesh type mesh
**4.Bring the virtual interfaces up:**Initialize interfaces before configuration.
Bash
sudo ip link set wlan0_ap up
sudo ip link set wlan0_adhoc up
Step 3: Configure Multiple Infrastructure SSIDs (hostapd)
To run multiple SSIDs on the infrastructure interface (wlan0_ap), hostapd supports virtual BSS (Multi-BSS). Create a configuration file named /etc/hostapd/multi_ssid.conf:
Ini, TOML
# Primary Interface & SSID
interface=wlan0_ap
driver=nl80211
ssid=Tactical-Primary-AP
hw_mode=g
channel=6
auth_algs=1
ignore_broadcast_ssid=0
# Virtual BSS #1 (Second simultaneous SSID on the same physical chip)
bss=wlan0_ap_guest
ssid=Tactical-Guest-Mesh
# Optional: isolate guest clients from primary network
ap_isolate=1
Note: If using multi-BSS via bss=, ensure you add the secondary virtual AP interface first:
Bash
sudo iw dev wlan0 interface add wlan0_ap_guest type __ap
sudo ip link set wlan0_ap_guest up
Start hostapd with your configuration:
Bash
sudo hostapd /etc/hostapd/multi_ssid.conf
Step 4: Configure the Ad-Hoc / Mesh Backbone
While your infrastructure SSIDs are broadcasting and serving clients on wlan0_ap, configure your second virtual interface (wlan0_adhoc) for peer-to-peer communication.
-
Join the Ad-Hoc (IBSS) Network:
Bash
sudo iw dev wlan0_adhoc set type ibss sudo ip link set wlan0_adhoc up sudo iw dev wlan0_adhoc ibss join TacticalAdHocNet 2412(Where
TacticalAdHocNetis your shared ad-hoc SSID name and2412is channel 1 frequency in MHz). -
Assign IP Addresses & Routing:
Bash
sudo ip addr add 192.168.100.10/24 dev wlan0_adhoc
Step 5: Bridge Infrastructure and Ad-Hoc Traffic (SDN / BATMAN-adv Integration)
If you need traffic flowing between your infrastructure clients (connected to your AP SSIDs) and your ad-hoc mesh nodes, you must bridge or route packets across the interfaces.
-
Enable IPv4 forwarding in the Linux kernel:
Bash
sudo sysctl -w net.ipv4.ip_forward=1 -
If integrating with Layer 2 mesh routing protocols (such as BATMAN-adv matching your system architecture), bind the ad-hoc interface directly to the batman mesh interface:
Bash
sudo modprobe batman-adv sudo batctl -m bat0 if add wlan0_adhoc sudo ip link set dev bat0 up sudo ip addr add 10.0.0.10/24 dev bat0
To automate the creation of your virtual wireless interfaces (wlan0_ap and wlan0_adhoc) and ensure hostapd starts cleanly on system boot, you can use a custom initialization shell script combined with a systemd service unit.
Step 1: Create the Wireless Setup Script
Create an initialization script at /usr/local/bin/setup_tactical_wifi.sh. This script safely tears down any existing state, provisions the virtual interfaces, configures the ad-hoc network, and brings everything online.
Bash
#!/bin/bash
set -e
PHY="wlan0"
AP_IF="wlan0_ap"
ADHOC_IF="wlan0_adhoc"
echo "[*] Initializing tactical wireless virtual interfaces..."
# 1. Bring down physical radio to clear stale states
ip link set "$PHY" down 2>/dev/null || true
# 2. Clean up virtual interfaces if they already exist
iw dev "$AP_IF" del 2>/dev/null || true
iw dev "$ADHOC_IF" del 2>/dev/null || true
# 3. Create virtual interfaces
iw dev "$PHY" interface add "$AP_IF" type __ap
iw dev "$PHY" interface add "$ADHOC_IF" type ibss
# 4. Bring interfaces up
ip link set "$AP_IF" up
ip link set "$ADHOC_IF" up
# 5. Join Ad-Hoc (IBSS) network & assign IP
iw dev "$ADHOC_IF" set type ibss
iw dev "$ADHOC_IF" ibss join TacticalAdHocNet 2412
ip addr add 192.168.100.10/24 dev "$ADHOC_IF" 2>/dev/null || true
echo "[+] Virtual interfaces successfully provisioned."
Make the script executable:
Bash
sudo chmod +x /usr/local/bin/setup_tactical_wifi.sh
Step 2: Create the Systemd Service File
Create the systemd service file at /etc/systemd/system/tactical-wifi.service.
Using ExecStartPre, systemd will execute your setup script first. Once the virtual interfaces are fully instantiated and ready, ExecStart launches hostapd in the foreground to manage the infrastructure SSIDs.
Ini, TOML
[Unit]
Description=Tactical Multi-SSID Wireless Infrastructure & Mesh Service
After=network.target local-fs.target
Wants=network.target
[Service]
Type=simple
ExecStartPre=/usr/local/bin/setup_tactical_wifi.sh
ExecStart=/usr/sbin/hostapd /etc/hostapd/multi_ssid.conf
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Step 3: Enable and Start the Service
Reload systemd to register the new service, enable it to start automatically on boot, and start it immediately:
Bash
# Reload systemd daemon
sudo systemctl daemon-reload
# Enable service on boot
sudo systemctl enable tactical-wifi.service
# Start the service now
sudo systemctl start tactical-wifi.service
Step 4: Verify Status and Logs
You can verify that the service started correctly and check real-time driver/hostapd output using journalctl:
Bash
sudo systemctl status tactical-wifi.service
To monitor live logs during startup or client association:
Bash
sudo journalctl -u tactical-wifi.service -f
To route and translate traffic between your infrastructure clients (connected to wlan0_ap) and your ad-hoc mesh backbone (wlan0_adhoc), you need to enable kernel packet forwarding and configure iptables NAT (Network Address Translation) and forwarding rules.
Here is how to update your initialization script to fully automate the bridge routing.
Updated Initialization Script (/usr/local/bin/setup_tactical_wifi.sh)
Replace your setup script with this expanded version, which provisions the interfaces, configures IP addresses, enables kernel forwarding, and applies the required iptables firewall/NAT rules:
Bash
#!/bin/bash
set -e
PHY="wlan0"
AP_IF="wlan0_ap"
ADHOC_IF="wlan0_adhoc"
echo "[*] Initializing tactical wireless virtual interfaces & routing..."
# 1. Bring down physical radio to clear stale states
ip link set "$PHY" down 2>/dev/null || true
# 2. Clean up virtual interfaces if they already exist
iw dev "$AP_IF" del 2>/dev/null || true
iw dev "$ADHOC_IF" del 2>/dev/null || true
# 3. Create virtual interfaces
iw dev "$PHY" interface add "$AP_IF" type __ap
iw dev "$PHY" interface add "$ADHOC_IF" type ibss
# 4. Bring interfaces up
ip link set "$AP_IF" up
ip link set "$ADHOC_IF" up
# 5. Configure IP addresses
ip addr add 192.168.10.1/24 dev "$AP_IF" 2>/dev/null || true # AP Gateway IP for clients
ip addr add 192.168.100.10/24 dev "$ADHOC_IF" 2>/dev/null || true # Ad-hoc node IP
# 6. Join Ad-Hoc (IBSS) network
iw dev "$ADHOC_IF" set type ibss
iw dev "$ADHOC_IF" ibss join TacticalAdHocNet 2412
# 7. Enable Kernel IP Forwarding
sysctl -w net.ipv4.ip_forward=1
# 8. Configure iptables Rules for NAT and Forwarding
# Flush existing tactical rules to prevent duplication
iptables -D FORWARD -i "$AP_IF" -o "$ADHOC_IF" -j ACCEPT 2>/dev/null || true
iptables -D FORWARD -i "$ADHOC_IF" -o "$AP_IF" -j ACCEPT 2>/dev/null || true
iptables -t nat -D POSTROUTING -o "$ADHOC_IF" -j MASQUERADE 2>/dev/null || true
# Allow bidirectional forwarding between AP clients and Ad-hoc mesh
iptables -A FORWARD -i "$AP_IF" -o "$ADHOC_IF" -j ACCEPT
iptables -A FORWARD -i "$ADHOC_IF" -o "$AP_IF" -j ACCEPT
# Allow established/related traffic to flow back seamlessly
iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Enable NAT masquerading out through the ad-hoc mesh interface
iptables -t nat -A POSTROUTING -o "$ADHOC_IF" -j MASQUERADE
echo "[+] Virtual interfaces and NAT routing rules successfully applied."
Make sure the script has execution permissions:
Bash
sudo chmod +x /usr/local/bin/setup_tactical_wifi.sh
What Each Rule Accomplishes
-
net.ipv4.ip_forward=1: Tells the Linux kernel to stop dropping packets arriving on one interface that are destined for another, allowing your node to act as an active router. -
FORWARD -i wlan0_ap -o wlan0_adhoc -j ACCEPT: Explicitly permits clients connected to your infrastructure Wi-Fi SSID to pass traffic into the ad-hoc mesh network. -
conntrack RELATED,ESTABLISHED: Ensures that return packets coming back from the mesh nodes can find their way back to the correct wireless client without getting blocked. -
POSTROUTING -o wlan0_adhoc -j MASQUERADE: Rewrites the source IP of packets leaving the AP subnet so they appear to originate from the routing node’s ad-hoc IP address (192.168.100.10), enabling seamless communication across remote mesh peers.
To provide automatic IP address assignment (DHCP) and name resolution (DNS) for clients connecting to your infrastructure AP (wlan0_ap), dnsmasq is the ideal lightweight service.
1
Install dnsmasq
Package Management
**1.Install dnsmasq:**Package Management.
Install the dnsmasq package via your package manager:
Bash
sudo apt update && sudo apt install -y dnsmasq
2
Stop default global service
Prerequisite
**2.Stop default global service:**Prerequisite.
Disable the default global dnsmasq service so it does not conflict with systemd-resolved on port 53:
Bash
sudo systemctl stop dnsmasq
sudo systemctl disable dnsmasq
3
Create a dedicated AP configuration file
/etc/dnsmasq.d/tactical_ap.conf
3.Create a dedicated AP configuration file:/etc/dnsmasq.d/tactical_ap.conf.
Create a custom configuration file to bind exclusively to your infrastructure AP interface (wlan0_ap) and establish the DHCP pool:
Ini, TOML
# Bind strictly to the AP interface
interface=wlan0_ap
bind-interfaces
# Define the DHCP IP pool and lease time (8 hours)
dhcp-range=192.168.10.50,192.168.10.150,255.255.255.0,8h
# Set the default gateway handed to clients
dhcp-option=3,192.168.10.1
# Provide DNS servers to clients (routing through gateway and upstream DNS)
dhcp-option=6,192.168.10.1,8.8.8.8
# Assign local domain scope
domain=tactical.local
4
Enable and start dnsmasq
Service Activation
**4.Enable and start dnsmasq:**Service Activation.
Start the service and verify its status:
Bash
sudo systemctl enable dnsmasq
sudo systemctl restart dnsmasq
Verification
Once wireless clients associate with your infrastructure SSID, you can inspect active IP assignments and leases in real-time with:
Bash
cat /var/lib/misc/dnsmasq.leases
To implement domain blacklisting (blocking unwanted domains, telemetry, or threat vectors) and custom DNS redirection (routing specific domains to designated local nameservers) in dnsmasq, you can add specific directives directly to your configuration file.
Step 1: Update the Dnsmasq Configuration
Append your blacklisting and custom redirection rules to your existing AP configuration file at /etc/dnsmasq.d/tactical_ap.conf:
Ini, TOML
# ==========================================
# 1. CORE AP & DHCP SETTINGS (Existing)
# ==========================================
interface=wlan0_ap
bind-interfaces
dhcp-range=192.168.10.50,192.168.10.150,255.255.255.0,8h
dhcp-option=3,192.168.10.1
dhcp-option=6,192.168.10.1,8.8.8.8
domain=tactical.local
# ==========================================
# 2. DOMAIN BLACKLISTING (Ad/Malware Blocking)
# ==========================================
# Directs unwanted or malicious domains to a null IP (0.0.0.0)
address=/ads.exampletracker.com/0.0.0.0
address=/telemetry.badactor.net/0.0.0.0
# Alternatively, return NXDOMAIN (Domain does not exist) instantly:
# address=/unwanted-domain.com/
# ==========================================
# 3. CUSTOM DNS REDIRECTION & SPLIT-DNS
# ==========================================
# Force specific local or mesh domains to resolve via a dedicated internal nameserver
server=/mesh.internal/192.168.100.1
server=/tactical.node/192.168.100.15
# Force a specific public domain to use a custom upstream resolver
server=/trusted-service.org/1.1.1.1
Step 2: Apply and Restart Dnsmasq
After saving the configuration changes, validate the syntax and restart the service to apply the rules:
Bash
# Test configuration syntax for errors
sudo dnsmasq --test
# Restart dnsmasq to load new rules
sudo systemctl restart dnsmasq
Step 3: Verify Rules Work
You can test both blacklisting and custom resolution from a client connected to your network (or directly on the host) using dig or nslookup:
-
Test Blacklisting:
Bash
dig ads.exampletracker.com @192.168.10.1Expected Result: The returned IP address will resolve to
0.0.0.0. -
Test Custom Redirection:
Bash
dig node-alpha.mesh.internal @192.168.10.1Expected Result: The query will be silently forwarded to your internal nameserver (
192.168.100.1) and resolve to its correct mesh address.
Create the Automated Ingestion Script
Execution script placed at /usr/local/bin/update_dns_blocklist.sh
**1.Create the Automated Ingestion Script:**Execution script placed at /usr/local/bin/update_dns_blocklist.sh.
Create a script that securely downloads a standard threat intelligence hosts file, parses it into dnsmasq-compatible address=/domain/0.0.0.0 syntax, validates the configuration, and reloads the service automatically:
Bash
sudo tee /usr/local/bin/update_dns_blocklist.sh > /dev/null << 'EOF'
#!/bin/bash
set -e
# Example feed: StevenBlack unified hosts or specialized threat Intel feeds
BLOCKLIST_URL="https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts"
OUTPUT_FILE="/etc/dnsmasq.d/threat_blocklist.conf"
TEMP_FILE="/tmp/dnsmasq_blocklist_raw.tmp"
echo "[*] Downloading latest threat intelligence blocklist..."
curl -sSL "$BLOCKLIST_URL" -o "$TEMP_FILE"
echo "[*] Parsing and formatting blocklist for dnsmasq..."
# Convert 0.0.0.0/127.0.0.1 entries into dnsmasq address rules, excluding local loopback
awk '/^0\.0\.0\.0|^127\.0\.0\.1/ {
if ($2 != "localhost" && $2 != "local" && $2 != "" && $2 !~ /#/) {
print "address=/" $2 "/0.0.0.0"
}
}' "$TEMP_FILE" > "$OUTPUT_FILE"
rm -f "$TEMP_FILE"
echo "[*] Verifying dnsmasq configuration syntax..."
if dnsmasq --test; then
echo "[+] Syntax valid. Reloading dnsmasq service..."
systemctl reload dnsmasq
echo "[+] Threat intelligence blocklist imported successfully."
else
echo "[-] ERROR: dnsmasq syntax validation failed! Removing malformed blocklist."
rm -f "$OUTPUT_FILE"
systemctl reload dnsmasq
exit 1
fi
EOF
sudo chmod +x /usr/local/bin/update_dns_blocklist.sh
Verification: Run ls -l /usr/local/bin/update_dns_blocklist.sh to confirm the script exists and has execute permissions.
2
Run Initial Import Test
Manual execution and syntax check
**2.Run Initial Import Test:**Manual execution and syntax check.
Execute the script manually to generate the initial blocklist file and load it into dnsmasq:
Bash
sudo /usr/local/bin/update_dns_blocklist.sh
Verification: Check that /etc/dnsmasq.d/threat_blocklist.conf was populated with thousands of filtered blocklist entries and that sudo systemctl status dnsmasq reports an active, running state.
3
Automate Periodic Updates via Cron
Scheduling regular updates
**3.Automate Periodic Updates via Cron:**Scheduling regular updates.
To keep threat intelligence feeds fresh, automate daily or weekly updates using a cron job. Open the root crontab:
Bash
sudo crontab -e
Add the following entry to run the update script every Sunday at 3:00 AM:
cron
0 3 * * 0 /usr/local/bin/update_dns_blocklist.sh >> /var/log/dns_blocklist_update.log 2>&1
Verification: Check active cron jobs with sudo crontab -l to ensure the schedule is registered.
Verification of Blocked Resolution
Test that the imported blocklist successfully intercepts queries on your network:
Bash
dig malware-example-domain.com @192.168.10.1
Expected Result: The query returns an immediate response pointing to 0.0.0.0.
To track and audit blocked domain attempts, you can enable dnsmasq query logging and route them to a dedicated log file. This allows you to monitor exactly which clients are attempting to query blocked threat intelligence domains in real time.
**1.Enable Query Logging and Dedicated File:**Configuration Update at /etc/dnsmasq.d/tactical_ap.conf.
Add the log-queries directive and specify a dedicated log facility so DNS requests don’t clutter standard system logs:
Ini, TOML
# Enable detailed logging of all DNS queries handled by dnsmasq
log-queries
# Route dnsmasq logs to a dedicated file instead of general syslog
log-facility=/var/log/dnsmasq.log
Apply the configuration changes by restarting the service:
Bash
sudo systemctl restart dnsmasq
**2.Configure Log Rotation:**Prevent Disk Exhaustion via Logrotate.
Because DNS traffic can generate large logs quickly, configure logrotate to automatically compress and archive the log file daily. Create /etc/logrotate.d/dnsmasq_tactical:
Bash
sudo tee /etc/logrotate.d/dnsmasq_tactical > /dev/null << 'EOF'
/var/log/dnsmasq.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 640 root adm
sharedscripts
postrotate
systemctl reload dnsmasq > /dev/null 2>&1 || true
endscript
}
EOF
Verification: Run sudo logrotate -d /etc/logrotate.d/dnsmasq_tactical to test the configuration syntax.
**3.Monitor Blocked Domain Attempts:**Real-time filtering using tail and grep.
Since blocked domains resolve to 0.0.0.0 based on your threat intelligence lists, you can filter the live log to watch blocked requests in real time:
Bash
tail -f /var/log/dnsmasq.log | grep "is 0.0.0.0"
Verification: From a connected client, try querying a known blocked domain (e.g., dig ads.exampletracker.com @192.168.10.1) and confirm the attempt instantly appears in your filtered log output with its client IP source.