CoovaChilli is the most capable open-source captive portal daemon available for OpenWrt. It handles guest isolation, UAM (Universal Access Method) splash page redirect, RADIUS accounting, and per-session bandwidth and data limits โ all without requiring proprietary cloud infrastructure. This guide walks through the full install path for Weird Network providers: package install, UAM configuration, RADIUS settings, and end-to-end session verification.
Prerequisites: An OpenWrt device running 21.02 or newer (ath79, ipq40xx, x86/64, or any target with sufficient flash). Minimum 16 MB flash / 128 MB RAM recommended โ CoovaChilli + dependencies consume ~2.5 MB installed. You need SSH access and a working WAN connection on the router before starting.
1. Install CoovaChilli
CoovaChilli is available in the standard OpenWrt package feed as coova-chilli.
SSH into your router and run:
opkg update
opkg install coova-chilli
This installs the daemon, its UCI config schema, and the init script at
/etc/init.d/chilli. The package also pulls in kmod-tun
(TUN/TAP kernel module) automatically. Verify the install succeeded:
opkg info coova-chilli | grep Version
Flash space: If opkg reports insufficient space,
CoovaChilli can be installed on a USB-attached ext4 volume using
extroot overlay. See the
OpenWrt extroot docs
before proceeding. Do not attempt the install on a device with less than 4 MB free.
2. Network Interface Setup
CoovaChilli manages a dedicated guest network interface. It bridges or aliases off one of your LAN ports โ guests connect to that interface and are placed in an unauthenticated state until they complete the captive portal flow.
Create the guest interface
In this example the guest SSID is served on wlan0 and CoovaChilli
will create a virtual interface tun0 for its internal network.
Edit /etc/config/network and add:
config interface 'guest'
option ifname 'wlan0'
option proto 'none'
Setting the proto to none tells OpenWrt not to assign an IP to this
interface โ CoovaChilli handles addressing for the guest network internally via
the TUN device it creates (tun0 by default).
Create a guest SSID in wireless config
config wifi-iface
option device 'radio0'
option mode 'ap'
option ssid 'Guest WiFi'
option network 'guest'
option encryption 'none'
option isolate '1'
isolate 1 prevents guests from communicating with
each other on the wireless segment โ essential for a shared guest network.
No WPA passphrase is set because CoovaChilli controls access at layer 3.
Multiple SSIDs: If your device has two radios (2.4 GHz + 5 GHz),
add a second wifi-iface block pointing to radio1 with
the same network 'guest' binding. CoovaChilli will control both
through the same TUN interface.
3. UAM Captive Portal Configuration
CoovaChilli's main config lives at /etc/chilli/config. The file is
sourced by the init script and supports both UCI (/etc/config/chilli)
and flat key-value formats. The flat format shown here works on all OpenWrt releases:
# Network interface CoovaChilli listens on (the guest SSID interface)
HS_WANIF=eth0 # your WAN/uplink interface
HS_LANIF=wlan0 # guest-facing interface
# IP pool for guest devices (CoovaChilli DHCP range)
HS_NETWORK=10.1.0.0
HS_NETMASK=255.255.255.0
HS_UAMLISTEN=10.1.0.1 # CoovaChilli's own IP on the guest segment
HS_UAMPORT=3990 # Port for the UAM HTTP listener
# UAM โ where unauthenticated guests are redirected
HS_UAMSERVER=https://weird-network.io/portal/<YOUR_LOCATION_ID>
# Walled garden: hosts reachable before authentication
HS_UAMALLOWED=weird-network.io,weird-network.polsia.app,fonts.googleapis.com,fonts.gstatic.com
# UAM shared secret (used to sign redirect URLs โ must match your Weird Network portal config)
HS_UAMSECRET=change_this_to_a_strong_random_secret
# DNS servers pushed to guest devices
HS_DNS1=8.8.8.8
HS_DNS2=8.8.4.4
# Logging
HS_SYSLOG=on
Replace <YOUR_LOCATION_ID> with the location ID from your
Weird Network provider dashboard (Settings โ Locations โ copy the ID).
The HS_UAMSECRET value must match exactly what you enter in the
portal's router configuration screen.
HTTPS redirect requirement: HS_UAMSERVER must use
HTTPS. Browsers block plaintext captive portal redirects on many modern operating
systems. Weird Network's portal endpoint is always HTTPS โ do not change it to HTTP.
Optional: session limits
Add these lines to /etc/chilli/config to enforce per-session caps:
# Session timeout (seconds) โ 3600 = 1 hour, 0 = no limit
HS_DEFsessiontimeout=3600
# Idle timeout (seconds) โ kick inactive guests after N seconds
HS_DEFIDLETIMEOUT=600
# Per-session data cap (bytes) โ 107374182400 = 100 GB, 0 = unlimited
HS_DEFBANDWIDTHMAXDOWN=10000000 # 10 Mbps download cap per session
HS_DEFBANDWIDTHMAXUP=2000000 # 2 Mbps upload cap per session
4. RADIUS Settings
CoovaChilli uses RADIUS for authentication and accounting. If you are using Weird Network's built-in session tracking (recommended), you can point to the Weird Network RADIUS bridge or run without RADIUS for simpler deployments.
Option A: Weird Network RADIUS bridge (recommended)
Weird Network provides a RADIUS-compatible endpoint that maps authentication
events to captive portal sessions in your dashboard. Add to
/etc/chilli/config:
# RADIUS authentication server
HS_RADIUS=radius.weird-network.io
HS_RADIUS2=radius.weird-network.io # failover (same host, retry)
HS_RADSECRET=<YOUR_RADIUS_SECRET> # from provider dashboard โ Locations โ RADIUS
# RADIUS accounting (sends session start/stop events)
HS_RADIUSACC=radius.weird-network.io
HS_RADIUSACC2=radius.weird-network.io
# NAS identifier โ use your location ID for clean dashboard attribution
HS_NASID=<YOUR_LOCATION_ID>
HS_NASSTATIONID=<YOUR_LOCATION_ID>
The RADIUS secret is generated per-location in your provider dashboard under Settings โ Locations โ RADIUS Config. It is separate from the UAM secret.
Option B: No RADIUS (UAM-only mode)
If you prefer to skip RADIUS and report sessions directly via the Weird Network
REST API instead, omit the HS_RADIUS* lines and set:
# Disable RADIUS (use UAM callback + Weird Network session API instead)
HS_NORADIUS=on
In this mode, session start/end events fire via the UAM callback URL. Your
captive portal page calls POST /api/sessions/start and
POST /api/sessions/:id/end directly. See
Section 8 for details.
5. Firewall and Walled Garden
CoovaChilli sets up its own iptables rules automatically, but you need to configure the OpenWrt firewall to allow the guest zone to reach the CoovaChilli UAM port and your WAN.
config zone
option name 'guest'
option network 'guest'
option input 'DROP'
option output 'ACCEPT'
option forward 'DROP'
config forwarding
option src 'guest'
option dest 'wan'
CoovaChilli intercepts packets from the guest zone before they reach the firewall
forwarding rules. Unauthenticated traffic is redirected to the UAM port
(10.1.0.1:3990); authenticated sessions are forwarded to WAN by
CoovaChilli's own iptables chain, not by the OpenWrt forwarding rule above.
The forwarding rule is a fallback for edge cases only.
Allow CoovaChilli management port
CoovaChilli exposes a JSON management interface on port 4990 (localhost only by default). If you want to query it from your server scripts, add a loopback rule:
# Allow CoovaChilli JSON interface from localhost only
iptables -A INPUT -i lo -p tcp --dport 4990 -j ACCEPT
6. Start and Enable the Service
Apply the wireless and network changes, then start CoovaChilli:
Reload network and wireless
/etc/init.d/network reload
wifi down && wifi up
Start CoovaChilli
/etc/init.d/chilli start
Enable at boot
/etc/init.d/chilli enable
Check the process is running
ps | grep chilli
You should see two chilli processes โ the parent daemon and the
child worker. If only one appears, check logread | grep chilli
for startup errors.
Confirm TUN device: Run ip link show tun0 โ if CoovaChilli
started correctly you will see the tun0 interface with state UP.
If it is absent, CoovaChilli failed to bind to the guest interface; double-check
HS_LANIF in /etc/chilli/config.
7. Verify Captive Portal Redirect
Connect a test device (phone or laptop) to the guest SSID. Open a browser and
navigate to any HTTP URL โ do not use HTTPS for the initial test,
as modern browsers skip HTTP for cached HTTPS sites. Try http://neverssl.com.
Confirm DHCP assignment
The test device should receive an IP in the 10.1.0.x range
(or whatever range you set in HS_NETWORK). If it stays on APIPA
(169.254.x.x), CoovaChilli's built-in DHCP is not running โ verify tun0
is up and HS_LANIF matches the actual interface name.
Confirm redirect fires
Navigating to http://neverssl.com should redirect the browser
to your HS_UAMSERVER URL with query parameters appended:
?mac=<device_mac>&ip=<device_ip>&called=<...>&nasid=<...>.
Confirm walled garden access
Before completing the portal, the device should be able to load
weird-network.io but NOT reach arbitrary internet hosts.
If internet is fully open pre-auth, HS_UAMALLOWED is too
permissive or CoovaChilli's iptables rules weren't applied โ restart
the daemon and check iptables -L -n | grep chilli.
Complete a test authentication
Submit the portal form (email capture or voucher). Weird Network's
portal calls back to CoovaChilli's UAM interface at
http://10.1.0.1:3990/json with an Accept
action to authorize the MAC. The device should then have unrestricted
internet access.
8. Connect to Weird Network Session API
Weird Network's captive portal page handles session creation automatically when a guest authenticates. Under the hood it calls the session API on your behalf. For reference, here is what happens on each auth event:
Session start (on portal form submit)
POST https://weird-network.io/api/sessions/start
Content-Type: application/json
{
"mac_address": "aa:bb:cc:dd:ee:ff",
"ip_address": "10.1.0.42",
"provider_id": "<YOUR_PROVIDER_ID>",
"location_id": "<YOUR_LOCATION_ID>",
"router_id": "<YOUR_ROUTER_ID>", // optional โ from pairing flow
"auth_method": "form_capture" // or "voucher" / "social_gate"
}
The API returns a session_id (UUID). The portal page stores this
and sends the UAM Accept call to CoovaChilli to grant internet
access for that MAC address.
Session end (on disconnect or timeout)
CoovaChilli sends a RADIUS Accounting-Stop packet when a session
ends. If you are using the Weird Network RADIUS bridge, this automatically
closes the session record. In UAM-only mode, you can fire the end event
from a script that polls http://localhost:4990/json (CoovaChilli's
local management interface):
curl -s http://localhost:4990/json | python3 -m json.tool
The response lists all active sessions with MAC, IP, bytes in/out, and
session duration. A session absent from this list has disconnected and
should be ended via POST /api/sessions/:id/end.
Router pairing: To surface this router in your Weird Network
provider dashboard (router status, alerts, firmware tracking), complete the
pairing flow: generate a pairing code in your dashboard under
Routers โ Generate Code, then call
POST /api/routers/pair with the code and the router's MAC address.
Paired routers appear in session records and trigger router-offline alerts
automatically.
9. Troubleshooting
Common issues and fixes:
Guests get DHCP but no redirect
CoovaChilli is running but iptables rules are not intercepting traffic.
Run iptables -t nat -L -n | grep -i chilli โ if empty,
CoovaChilli's rules were not inserted. Try /etc/init.d/chilli restart.
Also confirm HS_LANIF matches exactly the interface name shown
in ip link output.
Redirect loop โ portal keeps redirecting back
The UAM Accept callback from the portal is not reaching
CoovaChilli. Verify HS_UAMLISTEN is the correct IP of the
tun0 interface and that port 3990 is not firewalled. Run
netstat -tlnp | grep 3990 to confirm CoovaChilli is listening.
RADIUS authentication rejected
Double-check HS_RADSECRET against the secret shown in your
provider dashboard. Shared secrets are case-sensitive and whitespace-sensitive.
Run logread | grep -i radius to see the raw RADIUS error code โ
Access-Reject usually means a bad secret; timeout means the server is
unreachable (check DNS and port 1812/UDP firewall rules).
Portal page loads but form submit does nothing
The UAM secret mismatch. The portal signs its callback URL using
HS_UAMSECRET โ if the secret in your provider dashboard
differs from the one in /etc/chilli/config, CoovaChilli
will reject the callback silently. Update both values to match and
restart the daemon.
Guests can access internet before authenticating
CoovaChilli's iptables DROP rules are being overridden by an existing
OpenWrt firewall rule. Check iptables -L FORWARD -n for
broad ACCEPT rules above CoovaChilli's chain. Move the guest zone to
option forward DROP and remove any catch-all ACCEPT rules
in /etc/config/firewall that match the guest interface.
Logs: All CoovaChilli events go to syslog when
HS_SYSLOG=on is set. Run logread | grep chilli for
a real-time view, or logread -f | grep chilli to tail.
Session start/end, RADIUS events, and UAM callbacks all appear here.
10. Reporting sessions to Weird Network
CoovaChilli doesn't natively POST to a third-party API, so the
recommended pattern is to wire a tiny shim into CoovaChilli's
HS_UAM_SCRIPT hook (which fires on every successful
authentication). The shim translates each event into the
POST /api/sessions/start shape your dashboard already
understands, and a successful run shows up in the
/provider/sessions live page within
about 10 seconds.
Quick start (HS_UAM_SCRIPT wrapper)
Install the script and a one-line wrapper on the router:
# /etc/chilli/report-form.sh โ invoked by CoovaChilli on auth success
#!/bin/bash
exec /opt/wn/report-session.sh \
--mac "$1" \
--ip "${HS_REMOTEIP:-}" \
--auth form_capture \
--location "$WEIRD_NETWORK_LOCATION_ID"
Then point CoovaChilli at it from /etc/chilli/config:
HS_UAM_SCRIPT="/etc/chilli/report-form.sh"
The full script (with argument validation, env fallback, exit codes, and cron / hotplug alternatives) lives at /coovachilli/report-session.sh with full setup notes at /coovachilli/README.md.
Manual smoke test
After saving the script (don't forget to chmod +x), verify the network path:
./report-session.sh \
--api-url https://weird-network.io/api/sessions/start \
--mac AA:BB:CC:DD:EE:FF \
--auth form_capture \
--provider 42 --location 7
Open /provider/sessions in another tab โ
the row should appear within the next 10-second poll and flash for 2
seconds. If you see FAIL http=... on the router, walk
through the exit codes in the README.
Tip: If your router can't reach weird-network.io
directly (NAT, captive portals on the management network), set
WEIRD_NETWORK_API_URL in the script's environment to
a reachable mirror or Render host.
Related Guides
Other router setup guides and references for Weird Network providers: