Captive portal only for some MAC addresses

Hello, I guess this is a fairly uncommon request, and complicated too. Hey, if it was easy I wouldn't ask here anyway, so please bear with me :wink:

I've got two networks set up: LAN and GUEST. LAN is for private use only, since it can access private resources. GUEST is, well, for guests, although for security I use it myself too when connecting from certain devices. The GUEST network has no password, for several reasons. Number one reason: it's inconvenient to have to tell the password to every guest that comes (and there are a few). Some neighbor freeloaders also use it, which is fine with me as long as they don't hog bandwidth.

I have setup firewall rules using firewall.user - it's basically an awk script reading two text files to fill two ipsets. There's a list of banned MAC addresses that gets blocked ("foes"), and a list of MAC addresses that get the green light ("friends"). Both lists are dynamic, meaning that I update them manually when need or occasion arise.

So here's the question. I would like to set up a captive portal, but only on the guest network. Ideally, MAC addresses in the "friends" list shouldn't even need to go through it. The idea is that I would really like to keep GUEST access for "friends" smooth, but I would like the freeloaders to go through the captive portal first.

  1. Which captive portal solution do you think is best for me?

  2. Can I set it up how described? How do I go about it?

I don't know about the captive portal question, but I can suggest a way to limit bandwidth. Place an sqm shaper on the wifi interface and set it up to limit egress to something reasonable like say 25% of your download speed. If you do this maybe it doesn't matter about your captive portal?

You can also use an sqm on your wan egress using layer cake, and tag all your guest traffic CS1 so it goes lowest priority on upload.

Thank you! I prefer this more selective setup rather than the catch-all cap on wifi egress (wifi also carries LAN, as opposed to GUEST, traffic).

How/where do I tag packets? If it can be done while adding MAC addresses to the ipsets, I could tweak my awk script to add tagging, so the limit would only be enforced for non-"friends".

Your suggestion can indeed help with the bandwidth, but I would still like to be able to learn how my captive portal idea can be implemented, if it can.

As I say, I can't really help with captive portal because I don't use them, maybe someone else can. But in terms of bandwidth shaping. First off, there are separate virtual interfaces for each wifi network, so you won't limit your LAN only your GUESTs. For example on my access point wlan1 is my LAN and wlan1-1 is my GUEST. If I put the shaper on wlan1-1 then I only shape guest users, LAN operates at full speed.

Here is an example of how I do tagging in iptables, I encourage you to modify this for your own purposes, don't just copy it. This only works when you shape after tagging. So it doesn't work on ingress from WAN (because queuing happens before iptables), but it will work on egress to your GUEST network because iptables executes before egress queueing.

The script thing is kinda cool, but is it really that much better than putting a password on an "authorized guest" wifi? Now you have 3 networks: LAN, GUEST, AUTHGUEST. If your friends come over and just want to jump on the network without typing a password, they get on GUEST, but it has some bandwidth limits. If the limits there are bothersome, then they ask you for the password to AUTHGUEST, it's maybe something easy like "juansbestfriends" since it's not primarily a security measure, just an access control measure. It will probably be less work and delay for everyone than you manually logging into the router and updating a script to add them to the unlimited MAC list.

Feel free to go whatever route you like obviously, but sometimes thinking about alternative solutions to the problem can be helpful too.

Your example helps a lot, thanks.

My traffic shaper is SQM with piece_of_cake.qos. I believe it is the default for OpenWRT. Should I switch to some other discipline in order to define CS0, ..., CS5 ?

And would something like this work for me in firewall.user? I've edited out the irrelevant parts (about letting friends through etc).

# tagging preamble (thanks dlakelan)

iptables -t mangle -N dscp_mark
ip6tables -t mangle -N dscp_mark
iptables -t mangle -F dscp_mark
ip6tables -t mangle -F dscp_mark

iptables -t mangle -A FORWARD -j dscp_mark
ip6tables -t mangle -A FORWARD -j dscp_mark

# add all mac addresses in /etc/foes.txt to the rogues hash:mac ipset
ipset flush
ipset destroy
ipset create foes hash:mac
(awk '{if ($1 && $1 !~ /^#/) {printf "ipset add foes " $1 "\n"}}' /etc/foes.txt) | sh

## no filtering, just traffic shaping
# filter foes in input_rule
# iptables -w -A input_rule -i br-guest -m set --match-set foes src,dst -j REJECT
# filter foes in forwarding_rule
# iptables -w -A forwarding_rule -i br-guest -m set --match-set foes src,dst -j REJECT

# tag foes for traffic shaping (thanks dlakelan)

iptables -t mangle -A dscp_mark  -m set --match-set foes src,dst -j DSCP --set-dscp-class CS1
ip6tables -t mangle -A dscp_mark -m set --match-set foes src,dst -j DSCP --set-dscp-class CS1
iptables -t mangle -A dscp_mark  -m set --match-set foes src,dst -j DSCP --set-dscp-class CS1
ip6tables -t mangle -A dscp_mark -m set --match-set foes src,dst -j DSCP --set-dscp-class CS1

It's actually no big deal to edit the config manually, because I only need to update a text file. The script doesn't have to be touched--just rerun, which only requires restarting the firewall.

1 Like

So I think you want layer_cake if you want to handle DSCP tiers. And you ideally will put a layer_cake shaper on the egress of wlanx-y device that provides your guest network in addition to the one you have on your WAN (this egress on wifi shaper will respond to the CS1 tags, but the tags won't be there yet on ingress from WAN.

If you go this route, I'd recommend start by marking all packets CS0 then for the ipset match mark them CS1. You might even mark your "friends" CS2, so you can have CS0 for default, CS1 for known bandwidth suckers, and CS2 for friends who you want to prioritize.

The "professional" solution is to use "coova-chilli" as captive portal. As it works in coop with a backend-server, running freeradius, this setup allows definition of various userpolicies. In your case it would be "macauth" (authorization based on list of allowed MACs) and thruput limits, enforced by coova-chilli using a tun-interface. You have full flexibility regarding user restrictions, like volume limits (daily, monthly etc.), online-schedule etc.
But be warned: This has a steep learning curve. In case, you give it a try: Do the first setup on real LINUX. Then port to openwrt.

I think you can do this with nodogsplash. Edit the lease file to give your preferred guest MAC addresses infinite expiration.

that sounds like typical not-invented-here syndrome. We have very good traffic shaper technology in the linux kernel.

Honestly every time I connect to a hotel or whatever and I get their splash screen I think "this is terribly unprofessional stuff masquerading as slick professional stuff" like if your car mechanic walked out to greet you in an Armani suit. It's clear he must not know much about what he's doing, but man that suit is really cool. This is however just my personal opinion, which I should probably just keep to myself (but evidently am not doing a good job at :wink: )

coova-chilli is a rather old, and prooven tool. Using the tun-interface also throttles upload traffic and incorporates volume limits. Sometimes old tools are better for the basic task, compared to the new ones with bells and whistles, you do not need. I often compare this to the reliability and ease-of-use of my old Merc compared to the new one :slight_smile:

I guess the thing I disagree with is the whole concept of a captive portal. A good network, you should connect to it and it just works really well, it doesn't make you sign in and click useless agreements etc, it shares the bandwidth among all the users, and no one has any reason to even notice it, except if you ask them how well it works they say "gee it was great". This is accomplished well with a simple QoS scheme and either no password or a good scheme for distributing the password. If necessary an IDS that selectively boots infected devices.

A nice simple solution is Nodogsplash 3.2.1.
opkg install nodogsplash
or use Luci.
Set up the uci config to bind to the interface you want it to manage.
eg if your guest network is using wlan0-1, configure NDS to use that.
A couple of minutes to install and config and you will be up and running.
Simple to add a block list for the bad guys.
You can customize as much as you like, even build your own authentication page or even use all the fancy stuff mentioned elsewhere in this thread, but most likely you will be happy with it in default mode.
Full details at

More nice quick solution use cloudwifizone cloud base captive portal time/ data usage managing
The installation page can explain step by step

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.