How does it work in the Background?
-
Hello Everyone,
i would like to better understand how the captive portal works in the background. From what i understood by looking at the code and trying a few things in the pfsense shell, it looks like it's creating ipfw rules for authorized clients matching their mac and ip address. All other requests seem to be forwarded to the local webserver at port 8002. What i don't understand is, that i thought pfsense uses pf as a firewall. It makes sense to use ipfw for the Captive Portal as it can allow / block packets based on the mac address(as far as i understood), but i don't quite understand the order in which packets are filtered. Does it first go through pf and then through ipfw, or vice versa? Or something completely different?
I hope someone can point me in the right direction, as i didn't find any info online.
-
@eterelle said in How does it work in the Background?:
Hello Everyone,
i would like to better understand how the captive portal works in the background. From what i understood by looking at the code and trying a few things in the pfsense shell, it looks like it's creating ipfw rules for authorized clients matching their mac and ip address. All other requests seem to be forwarded to the local webserver at port 8002. What i don't understand is, that i thought pfsense uses pf as a firewall. It makes sense to use ipfw for the Captive Portal as it can allow / block packets based on the mac address(as far as i understood), but i don't quite understand the order in which packets are filtered. Does it first go through pf and then through ipfw, or vice versa? Or something completely different?
I hope someone can point me in the right direction, as i didn't find any info online.
Hi,
That's correct, pfSense is using ipfw as backend for the captive portalAnd yes, pfsense is normally using pf for most of its functionalities (traffic shaper/ firewall/NAT, etc....) but it has been decided to use ipfw for this specific feature
It may seems odd at first....but in fact it make some sense : it help to split the firewall and cp modules
ipfw is triggered before pf (when a packet arrives, it goes first to ipfw if loaded...then to pf).
Ipfw is not enabled by default on pfsense. The kernel module is loaded automatically when a captive portal is enabled
Also, the PHP code is using specific functions to interact with ipfw, These functions are written in c (in pfSense.c, on the ports repository) and interact directly with the kernel, instead of using the ipfw command
Also, two web servers (1 for http, another one for https if enabled) are set up per zone, starting from port 8002 and 8003 (if you create another zone, port 8004 and 8005 will be used, etc...)
Users are identified based on their mac address+IP address couple (or based on their IP only if you disable MAC authentication)
Does that answers most of your questions?
-
@free4 Yes, that was really helpful, thanks! As a background info on why i want to know this, I am trying to make a pfsense package to control internet access of individual users using another custom software and i thought the captive portal would be a good example on how something like that could be implemented.
Maybe you can also help me understand this ipfw rule:01000 19700 2547034 skipto tablearg ip from any to any via table(cp_ifaces)
From what i understand, this skips to another rule if traffic enters via an interface enabled in the captive portal config. But where does it skip to(seems like it skips over the allow any rule)?
Thank you for helping, that was already really useful for me!
-
@eterelle I am not 100% sure (I don't have a pfsense available for test right now) but if I recall correctly this rule redirects ("skip to") packets to the rules of the interface it is coming from
The captive portal can be enabled on multiple interfaces (/multiple zones can be enabled).
ipfw rules are structured this way:
-
first are the general rules (which
1000 skipto tablearg all from any to any via table(cp_ifaces)
is part of) -
then the rules relative to each interface (allow DHCP and DNS, forward HTTP to the web server, allow traffic which is part of table ${cpzone}_auth_up and ${cpzone}_auth_down etc)
-
then the "final" rules (allow all / block all. These rules are used because there is some "skipto" pointing to them)
-
-
I also remember that this very specific line is the root cause of a bug
https://redmine.pfsense.org/issues/7553
The algorithm used internally by
tablearg
does not support having network cards which begin by the same namewhen you have em0 and em0.243 (because you created a vlan 243 on em0)...setting up a captive portal on em0 cause em0.243 to also be blocked.
It's a freeBSD kernel bug at this point, not a pfsense one. But still "funny" to look at
-
@free4 Here are all the rules:
01000 19700 2547034 skipto tablearg ip from any to any via table(cp_ifaces) 01100 1497103 1515944168 allow ip from any to any 02100 0 0 pipe tablearg ip from any to any MAC table(test_pipe_mac) 02101 0 0 allow pfsync from any to any 02102 0 0 allow carp from any to any 02103 855 0 allow ip from any to any layer2 mac-type 0x0806,0x8035 02104 0 0 allow ip from any to any layer2 mac-type 0x888e,0x88c7 02105 0 0 allow ip from any to any layer2 mac-type 0x8863,0x8864 02106 6 0 deny ip from any to any layer2 not mac-type 0x0800,0x86dd 02107 1123 85710 allow ip from any to table(test_host_ips) in 02108 1097 320180 allow ip from table(test_host_ips) to any out 02109 7 2370 allow ip from any to 255.255.255.255 in 02110 0 0 allow ip from 255.255.255.255 to any out 02111 0 0 pipe tablearg ip from table(test_allowed_up) to any in 02112 0 0 pipe tablearg ip from any to table(test_allowed_down) in 02113 0 0 pipe tablearg ip from table(test_allowed_up) to any out 02114 0 0 pipe tablearg ip from any to table(test_allowed_down) out 02115 918 193474 pipe tablearg ip from table(test_auth_up) to any layer2 in 02116 1390 510141 pipe tablearg ip from any to table(test_auth_down) layer2 out 02117 1293 108852 fwd 127.0.0.1,8002 tcp from any to any 80 in 02118 1095 129016 allow tcp from any to any out 02119 11916 1197291 skipto 65534 ip from any to any 65534 11916 1197291 deny ip from any to any 65535 4 186 allow ip from any to any
Also, if i run ipfw table cp_ifaces list, it shows this:
ipfw table cp_ifaces list --- table(cp_ifaces), set(0) --- re1.40 2100 273 21692 1599332255
I didn't really find anything about what the second value means, but as there is a rule with the number 02100, i guess maybe it skips to there?
That would be this rule:02100 0 0 pipe tablearg ip from any to any MAC table(test_pipe_mac)
which seems to be for the traffic shaper / rate limiter, but the traffic is only piped into there if the mac is listed in test_pipe_mac, which seems to be a list of mac addresses allowed under the MACs Tab in the CP config, so this rule would not envoke normally.
So i think for clients who are authenticated, the rules 2111 - 2116 would allow traffic, and for non authenticated users, all requests will be forwarded to 127.0.0.1:8002 by rule 2117.
Does that look correct to you?
-
@free4 Yeay, that's a really unintuitive bug.. Thanks in advance for saving me from some possible problems there :)
-
@eterelle the traffic shaper is using pf
The second rule you've shown correspond to the "Allowed MAC" feature... (the MAC tab)
You should have the same ipfw tables for "allowed IPs" of each zone -
@free4 Yes, i didn't mean the Traffic Shaping feature of pfsense but the Traffic Shaper of ipfw(or at least the manpage of ipfw says that the pipe command is used for the traffic shaper, so i figured that it is used for the Bandwidth limit that can be set in the Allowed MACs config).
-
@eterelle yup, I understand
Aside from this..your guesses are correct
Since you plan to create an external package, I advice you to have a look to the ACME module of pfsense (how it is structured, etc...)
ACME is a package that generates let's encrypt certificates. It is based on acme.sh
I do suggest you to have a look to this package because...it is up to date, using some advanced functionality of packages, and it is maintened/developed by the core pfsense team
Many modules (eg, freeRadius) are legacy/out of date and should not really be taken as reference...
-
@free4 I will definetely have a look at that. Thanks for all the help, i really appreciate it!