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.


  • Rebel Alliance

    @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 portal

    And 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!


  • Rebel Alliance

    @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)


  • Rebel Alliance

    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 name

    when 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 :)


  • Rebel Alliance

    @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).


  • Rebel Alliance

    @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!


Log in to reply