Netgate Discussion Forum
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Search
    • Register
    • Login

    Force manually expired voucher to renew dhcp lease

    Scheduled Pinned Locked Moved Captive Portal
    11 Posts 3 Posters 3.1k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • J
      Just_Michel
      last edited by

      Hello,

      I am looking for a way to kick a user who is using a voucher, but if i manually expire the voucher, the user can still continue browsing until dhcp releases the lease. Is there any way to make this possible without setting the dhcp lease to a low number of seconds?

      1 Reply Last reply Reply Quote 0
      • GertjanG
        Gertjan
        last edited by

        Hi,

        Well. at first I was saying : why are you trying to force to disconnect a user when its voucher is expired ?
        Better said: you are forcing the expiration of a voucher by using Status: Captive portal: Expire Vouchers:"my cpzone". The user should be disconnected right away …. (edit: or is it "within 5 minutes ? - see below)

        I tried it myself.
        I created a voucher roll, with one voucher, "Ndw8S328MHv" - usage time : 5 minutes.
        Apr 10 13:11:30 logportalauth[11708]: Voucher: All 1 vouchers from Roll 1 marked unused

        I used that voucher:
        Apr 10 13:12:58 logportalauth[65029]: Zone: cpzone1 - Voucher login good for 5 min.: Ndw8S328MHv, 0c:77:1a:2b:55:35, 192.168.2.226

        One minute later, I used forced "Ndw8S328MHv" to an expiration:
        Apr 10 13:13:47 logportalauth[1047]: Zone: cpzone1 - Ndw8S328MHv (1/1) forced to expire
        Apr 10 13:13:47 logportalauth[1047]: Zone: cpzone1 - FORCLY TERMINATING VOUCHER Ndw8S328MHv SESSION: , ,

        I tested the voucher by using Status: Captive portal: Test Vouchers:"my cpzone" - and obtain a logic result:
        Apr 10 13:14:00 logportalauth[58247]: Zone: cpzone1 - Ndw8S328MHv (1/1) already used and expired

        But, my device ( 0c:77:1a:2b:55:35 - an iPhone) was still able to surf - I really had the impression that my iPhone was still connected to the net.

        Finally, the cron function "captiveportal_prune_old" which runs every 5 minutes, decided that it was time to "TIMOUT" my voucher session "Ndw8S328MHv".
        Apr 10 13:18:01 logportalauth[89098]: Zone: cpzone1 - TIMEOUT: Ndw8S328MHv, 0c:77:1a:2b:13:35, 192.168.2.226

        After this moment, and only after this moment, my iPhone was disconnected - a Portal popup showd up right away when I continued to 'surf'.

        Wat is missing ?
        I should have ran:

        ipfw -x 2 table 1 list
        

        and

        ipfw -x 2 table 2 list
        

        to check if my IP (= 192.168.2.226) was really thrown out at the moment i forced the voucher to be expired. I guess it wasn't.

        (The context of my captive portal is "2")

        I should have looked at Status: Captive portal to see if my phone was still listed as "connected".

        I know that captiveportal_disconnect($cpentry,null,13); is being called (from /etc/inc/voucher.inc - line 282) when I force a voucher to expire, the "FORCLY ;) TERMINATING VOUCHER" message in the logs is a proof, but I had this impression that something went wrong: the voucher is expired, but the user isn't disconnected (yet).
        Real disconnection is taken place when the 5 minutes cron task "captiveportal_prune_old()" has been run.

        Or, I'm I missing something  :o

        Anyway, Monday morning I'll have a more close look at this.

        Btw: removing a DHCP lease won't do it.

        Can some oe confirm: create a voucher roll, with one voucher, let say 60 minutes - use the voucher - force the voucher to be expired. Test the connection on device: is it still listed in the firewall ? (see commands above) Is Status: Captive portal still mentioning the device as 'connected' ? Is the device still connected (visit some random sites with Google) ?

        No "help me" PM's please. Use the forum, the community will thank you.
        Edit : and where are the logs ??

        1 Reply Last reply Reply Quote 0
        • DerelictD
          Derelict LAYER 8 Netgate
          last edited by

          Expiring a voucher doesn't kick a user off.  The pruning process sees the expired voucher and walks the table terminating sessions.  You should have been able to find the user in Status > Captive Portal or in the Services > Captive Portal, MAC (depending on whether you're using automatic MAC pass-through editions, etc) and manually kill the actual session (remove the table entry) there.

          Chattanooga, Tennessee, USA
          A comprehensive network diagram is worth 10,000 words and 15 conference calls.
          DO NOT set a source address/port in a port forward or firewall rule unless you KNOW you need it!
          Do Not Chat For Help! NO_WAN_EGRESS(TM)

          1 Reply Last reply Reply Quote 0
          • GertjanG
            Gertjan
            last edited by

            Thanks for the quick precision.

            @Derelict:

            Expiring a voucher doesn't kick a user off.

            Have a look at this:

            I just found this https://doc.pfsense.org/index.php/Captive_Portal_Vouchers#Troubleshooting - at the bottom of the page:

            User is online after voucher expires (?)
                The session timeout must be enabled in order to allow the voucher session to expire and deactivate.
            

            My "idle timeout" is 60 minutes and "hard timeout" is 360 minutes.

            But looking at the code, captiveportal_prune_old(),  "hard timeout" ($timeout from $cpcfg['timeout']) and "idle timeout" ($idletimeout from $cpcfg['idletimeout']) aren't really used when handling vouchers - its the vouchers time length that is being taken in account.

            Voucher time is being handled here https://github.com/pfsense/pfsense/blob/master/etc/inc/captiveportal.inc#L718
            (Start session time + voucher duration time) should not be greater then (actual time)
            If so, timeout is being executed here: https://github.com/pfsense/pfsense/blob/master/etc/inc/captiveportal.inc#L735
            and the user is shut off using the all mighty captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time); function.

            But, here: https://github.com/pfsense/pfsense/blob/master/etc/inc/voucher.inc#L295 - where vouchers are forced to be expired, and then the same
            captiveportal_disconnect($cpentry,null,13); is being called from there (/etc/inc/voucher.inc).

            ….... the Portal User should have been thrown off the portal at that moment. What else is calling captiveportal_disconnect(...) good for ?

            I'm ready to believe that
            @Gertjan:

            Expiring a voucher does kick a user off.

            :)

            The code states : https://github.com/pfsense/pfsense/blob/master/etc/inc/voucher.inc#L292 == "/* Check if this voucher has any active sessions */"
            I guess this test is being taken at that moment to …. disconnect the user.
            Right ?!

            As said above, I know the function captiveportal_disconnect($cpentry,null,13); is being called, because the portal log outputs just afterwards the typo "FORCLY" log line.

            Is this a a question of states in the firewall that remain existent ?

            edit: why, … why do I not have pfSEnse @ home ....  >:(

            No "help me" PM's please. Use the forum, the community will thank you.
            Edit : and where are the logs ??

            1 Reply Last reply Reply Quote 0
            • DerelictD
              Derelict LAYER 8 Netgate
              last edited by

              Mine might behave differently because I use auto-added MAC pass-throughs pretty much exclusively when I use vouchers.  The CP code takes a lot of different paths based on options.

              Do some experimentation, do like you said and look at the ipfw context tables, and see how it behaves for your set of options.

              Chattanooga, Tennessee, USA
              A comprehensive network diagram is worth 10,000 words and 15 conference calls.
              DO NOT set a source address/port in a port forward or firewall rule unless you KNOW you need it!
              Do Not Chat For Help! NO_WAN_EGRESS(TM)

              1 Reply Last reply Reply Quote 0
              • GertjanG
                Gertjan
                last edited by

                Ok, found it.

                This:
                https://github.com/pfsense/pfsense/blob/master/etc/inc/voucher.inc#L293

                $cpentry = captiveportal_read_db("WHERE username = '{$voucher}'");
                

                returns an array within array.
                So, using it like this

                 					captiveportal_disconnect($cpentry,null,13);
                					captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"FORCLY TERMINATING VOUCHER {$voucher} SESSION");
                

                will not disconnect a client.
                A very visible hint was always present:
                Look at the end of the logged line (as a result of captiveportal_logportalauth(…) showed above:

                logportalauth[35291]: Zone: cpzone1 - FORCLY TERMINATING VOUCHER 3eSUmPGMfwx SESSION: , ,

                All function parameters ($cpentry[4],$cpentry[3],$cpentry[2]) are empty or non-defined.

                Solution:
                https://github.com/Gertjanpfsense/pfsense/commit/59517485b2122e96f4f5c248ded659b2c472f2d0#diff-340e89e70c75a36e26138649eb6432ae

                Patch:
                https://github.com/Gertjanpfsense/pfsense/commit/59517485b2122e96f4f5c248ded659b2c472f2d0.patch

                So, use the first array from the array of arrays retrieved like this:

                $cpentry = $cpentry[0];
                

                There might be a better solution, but it seems the most logic way to me.

                Now, when forcing a voucher to expire, and a portal client is actually using that voucher at that moment, he WILL get disconnected right away.

                I received a log line like this:

                logportalauth[26366]: Zone: cpzone1 - FORCLY TERMINATING VOUCHER cqM6WyFhW8G SESSION: cqM6WyFhW8G, 0c:77:1a:xx:yy:35, 192.168.2.226

                Firewall tables 1 & 2 confirme that user "0c:77:1a:xx:yy:35, 192.168.2.226" was removed.

                Feedback is welcome, I'm not an active 'voucher' user.

                No "help me" PM's please. Use the forum, the community will thank you.
                Edit : and where are the logs ??

                1 Reply Last reply Reply Quote 0
                • J
                  Just_Michel
                  last edited by

                  I found out that when you manually kick a user on the status_captiveportal.php?zone= page, that the kick is instant.

                  1 Reply Last reply Reply Quote 0
                  • GertjanG
                    Gertjan
                    last edited by

                    @Just_Michel:

                    I found out that when you manually kick a user on the status_captiveportal.php?zone= page, that the kick is instant.

                    That is one case were captiveportal_disconnect(….) is called the correct way, so the user will be disconnected.

                    The voucher expire function in /etc/inc/voucher.inc wasn't calling captiveportal_disconnect(….) correctly.

                    Apply the pach (just one line) and check yourself.

                    No "help me" PM's please. Use the forum, the community will thank you.
                    Edit : and where are the logs ??

                    1 Reply Last reply Reply Quote 0
                    • GertjanG
                      Gertjan
                      last edited by

                      I guess its time for a pull request.

                      …. and I closed it.

                      This:

                      $cpentry = captiveportal_read_db("WHERE username = '{$voucher}'");
                      **    if (!empty($cpentry) && !empty($cpentry[0]) ) {
                            $cpentry = $cpentry[0];**
                        captiveportal_disconnect($cpentry,null,13);

                      and … because it works now, I've got something new: (see image) which originates from /etc/inc/captiveportal.inc - lines 890 - 898.

                      	if (is_ipaddr($dbent[2])) {
                      		/* Delete client's ip entry from tables 1 and 2\. */
                      		$clientsn = (is_ipaddrv6($dbent[2])) ? 128 : 32;
                      		pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 1, $dbent[2], $clientsn, $dbent[3]);
                      		pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XDEL, 2, $dbent[2], $clientsn, $dbent[3]);
                      		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
                      		$_gb = @pfSense_kill_states($dbent[2]);
                      		$_gb = @pfSense_kill_srcstates($dbent[2]);
                      	}
                      

                      $dbent[2] (client IP like 192.168.2.126) and $dbent[3] (client MAC like  0c:77:1a:xx:13:35) are correctly defined.

                      pfSense_ipfw_Tableaction() isn't happy about what ??
                      (must be him, the output of @pfSense_kill_states() is already suppressed).

                      New bug : the global $cpzoneid in captiveportal_disconnect() when called from /etc/inc/voucher.inc ISN'T defined  !!
                      …. back to the drawing table  >:(

                      edit: got it.
                      $cpzoneid (a global variable) isn't set.
                      This makes bark the code quoted above (the 2 functions pfSense_ipfw_Tableaction()) - logic, because the used $cpzoneid was '0'.

                      new test:

                      
                      				if (!empty($cpentry) && !empty($cpentry[0]) ) {
                      					$cpentry = $cpentry[0];
                      					// surface global variable $cpzoneid needed by captiveportal_disconnect()
                      					$cpzoneid = $config['captiveportal'][$cpzone]['zoneid'];
                      					captiveportal_disconnect($cpentry,null,13);
                      

                      I hit another test cycle.

                      pfsense-Failed-setsockopt.PNG_thumb
                      pfsense-Failed-setsockopt.PNG

                      No "help me" PM's please. Use the forum, the community will thank you.
                      Edit : and where are the logs ??

                      1 Reply Last reply Reply Quote 0
                      • GertjanG
                        Gertjan
                        last edited by

                        Brought this issue to https://redmine.pfsense.org/issues/4625

                        No "help me" PM's please. Use the forum, the community will thank you.
                        Edit : and where are the logs ??

                        1 Reply Last reply Reply Quote 0
                        • GertjanG
                          Gertjan
                          last edited by

                          The issue has been solved https://github.com/pfsense/pfsense/commit/ea6cbc390bba86336bf5a173922b20f0b3416c89

                          No "help me" PM's please. Use the forum, the community will thank you.
                          Edit : and where are the logs ??

                          1 Reply Last reply Reply Quote 0
                          • First post
                            Last post
                          Copyright 2025 Rubicon Communications LLC (Netgate). All rights reserved.