Project

General

Profile

Download (41.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	captiveportal.inc
4
	part of m0n0wall (http://m0n0.ch/wall)
5

    
6
	Copyright (C) 2009 Ermal Lu?i
7
	Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
8
	All rights reserved.
9

    
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12

    
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15

    
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19

    
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30

    
31
	This version of captiveportal.inc has been modified by Rob Parker
32
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
33
	via returned RADIUS attributes. This page has been modified to delete any
34
	added rules which may have been created by other per-user code (index.php, etc).
35
	These changes are (c) 2004 Keycom PLC.
36
	
37
	pfSense_BUILDER_BINARIES:	/sbin/ipfw	/sbin/sysctl	/sbin/kldunload
38
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/lighttpd	/usr/local/bin/minicron	/sbin/pfctl
39
	pfSense_BUILDER_BINARIES:	/bin/hostname	/bin/cp	
40
	pfSense_MODULE:	captiveportal
41
*/
42

    
43
/* include all configuration functions */
44
require_once("config.inc");
45
require_once("functions.inc");
46
require_once("filter.inc");
47
require_once("radius.inc");
48
require_once("voucher.inc");
49

    
50
function captiveportal_configure() {
51
	global $config, $g;
52

    
53
	$captiveportallck = lock('captiveportal');
54
	
55
	if (isset($config['captiveportal']['enable'])) {
56

    
57
		if ($g['booting'])
58
			echo "Starting captive portal... ";
59

    
60
		/* kill any running mini_httpd */
61
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
62
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
63

    
64
		/* remove old information */
65
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
66
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
67
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
68
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
69

    
70
		/* setup new database in case someone tries to access the status -> captive portal page */
71
		touch("{$g['vardb_path']}/captiveportal.db");
72

    
73
		/* kill any running minicron */
74
		killbypid("{$g['varrun_path']}/minicron.pid");
75

    
76
		/* init ipfw rules */
77
		captiveportal_init_rules(true);
78

    
79
		/* stop accounting on all clients */
80
		captiveportal_radius_stop_all(true);
81

    
82
		/* initialize minicron interval value */
83
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
84

    
85
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
86
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; }
87

    
88
		/* write portal page */
89
		if ($config['captiveportal']['page']['htmltext'])
90
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
91
		else {
92
			/* example/template page */
93
			$htmltext = <<<EOD
94
<html>
95
<head>
96
<title>{$g['product_name']} captive portal</title>
97
</head>
98
<body>
99
<center>
100
<h2>{$g['product_name']} captive portal</h2>
101
Welcome to the {$g['product_name']} Captive Portal!
102
<p>
103
<form method="post" action="\$PORTAL_ACTION\$">
104
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
105
<table>
106
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
107
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
108
   <tr><td>&nbsp;</td></tr>
109
   <tr>
110
     <td colspan="2">
111
	<center><input name="accept" type="submit" value="Continue"></center>
112
     </td>
113
   </tr>
114
</table>
115
</center>
116
</form>
117
</body>
118
</html>
119

    
120

    
121

    
122
EOD;
123
		}
124

    
125
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
126
		if ($fd) {
127
			// Special case handling.  Convert so that we can pass this page
128
			// through the PHP interpreter later without clobbering the vars.
129
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
130
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
131
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
132
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
133
			$htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
134
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
135
			fwrite($fd, $htmltext);
136
			fclose($fd);
137
		}
138

    
139
		/* write error page */
140
		if ($config['captiveportal']['page']['errtext'])
141
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
142
		else {
143
			/* example page */
144
			$errtext = <<<EOD
145
<html>
146
<head>
147
<title>Authentication error</title>
148
</head>
149
<body>
150
<font color="#cc0000"><h2>Authentication error</h2></font>
151
<b>
152
Username and/or password invalid.
153
<br><br>
154
<a href="javascript:history.back(); ">Go back</a>
155
</b>
156
</body>
157
</html>
158

    
159
EOD;
160
		}
161

    
162
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
163
		if ($fd) {
164
			// Special case handling.  Convert so that we can pass this page
165
			// through the PHP interpreter later without clobbering the vars.
166
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
167
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
168
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
169
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
170
			$errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
171
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
172
			fwrite($fd, $errtext);
173
			fclose($fd);
174
		}
175

    
176
		/* write error page */
177
		if ($config['captiveportal']['page']['logouttext'])
178
			$logouttext = base64_decode($config['captiveportal']['page']['logouttext']);
179
		else {
180
			/* example page */
181
			$logouttext = <<<EOD
182
<HTML>
183
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
184
<BODY>
185
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
186
<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
187
</SPAN>
188
<SCRIPT LANGUAGE="JavaScript">
189
<!--
190
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
191
if (LogoutWin) {
192
    LogoutWin.document.write('<HTML>');
193
    LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
194
    LogoutWin.document.write('<BODY BGCOLOR="#435370">');
195
    LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
196
    LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
197
    LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
198
    LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
199
    LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
200
    LogoutWin.document.write('</FORM>');
201
    LogoutWin.document.write('</DIV></BODY>');
202
    LogoutWin.document.write('</HTML>');
203
    LogoutWin.document.close();
204
}
205

    
206
document.location.href="{$my_redirurl}";
207
-->
208
</SCRIPT>
209
</BODY>
210
</HTML>
211

    
212
EOD;
213
		}
214

    
215
		$fd = @fopen("{$g['varetc_path']}/captiveportal-logout.html", "w");
216
		if ($fd) {
217
			fwrite($fd, $logouttext);
218
			fclose($fd);
219
		}
220
		/* write elements */
221
		captiveportal_write_elements();
222

    
223
		/* start up the webserving daemon */
224
		captiveportal_init_webgui();
225

    
226
		/* start pruning process (interval defaults to 60 seconds) */
227
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " .
228
			"/etc/rc.prunecaptiveportal");
229

    
230
		/* generate radius server database */
231
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
232
				($config['captiveportal']['auth_method'] == "radius"))) {
233
			$radiusip = $config['captiveportal']['radiusip'];
234
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
235

    
236
			if ($config['captiveportal']['radiusport'])
237
				$radiusport = $config['captiveportal']['radiusport'];
238
			else
239
				$radiusport = 1812;
240

    
241
			if ($config['captiveportal']['radiusacctport'])
242
				$radiusacctport = $config['captiveportal']['radiusacctport'];
243
			else
244
				$radiusacctport = 1813;
245

    
246
			if ($config['captiveportal']['radiusport2'])
247
				$radiusport2 = $config['captiveportal']['radiusport2'];
248
			else
249
				$radiusport2 = 1812;
250

    
251
			$radiuskey = $config['captiveportal']['radiuskey'];
252
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
253

    
254
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
255
			if (!$fd) {
256
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
257
				return 1;
258
			} else if (isset($radiusip2, $radiuskey2)) {
259
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
260
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
261
			} else {
262
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
263
			}
264
			fclose($fd);
265
		}
266

    
267
		if ($g['booting'])
268
			echo "done\n";
269

    
270
	} else {
271
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
272
		killbypid("{$g['varrun_path']}/minicron.pid");
273

    
274
		captiveportal_radius_stop_all(true);
275

    
276
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
277

    
278
		/* unload ipfw */
279
		if (is_module_loaded("ipfw.ko"))		
280
			mwexec("/sbin/kldunload ipfw.ko");
281
		$listifs = get_configured_interface_list_by_realif();
282
		foreach ($listifs as $listrealif => $listif) {
283
			if (!empty($listrealif)) {
284
				if (does_interface_exist($listrealif)) {
285
					pfSense_interface_flags($listrealif, -IFF_IPFW_FILTER);
286
					$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
287
                        		if (!empty($carpif)) {
288
						$carpsif = explode(" ", $carpif);
289
						foreach ($carpsif as $cpcarp)
290
							pfSense_interface_flags($cpcarp, -IFF_IPFW_FILTER);
291
					}
292
				}
293
			}
294
		}
295
	}
296

    
297
	unlock($captiveportallck);
298
	
299
	return 0;
300
}
301

    
302
function captiveportal_init_webgui() {
303
	global $g, $config;
304

    
305
	 if (!isset($config['captiveportal']['enable']))
306
                return;
307

    
308
	if ($config['captiveportal']['maxproc'])
309
		$maxproc = $config['captiveportal']['maxproc'];
310
	else
311
		$maxproc = 16;
312

    
313
	$use_fastcgi = true;
314

    
315
	if (isset($config['captiveportal']['httpslogin'])) {
316
		$cert = base64_decode($config['captiveportal']['certificate']);
317
		if (isset($config['captiveportal']['cacertificate']))
318
			$cacert = base64_decode($config['captiveportal']['cacertificate']);
319
		else
320
			$cacert = "";
321
		$key = base64_decode($config['captiveportal']['private-key']);
322
		/* generate lighttpd configuration */
323
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
324
			$cert, $key, $cacert, "lighty-CaptivePortal-ssl.pid", "8001", "/usr/local/captiveportal/",
325
			"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
326
	}
327

    
328
	/* generate lighttpd configuration */
329
	system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
330
		"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
331
		"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
332

    
333
	/* attempt to start lighttpd */
334
	$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
335

    
336
	/* fire up https instance */
337
	if (isset($config['captiveportal']['httpslogin']))
338
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
339
}
340

    
341
function captiveportal_init_rules($reinit = false) {
342
	global $config, $g;
343

    
344
	if (!isset($config['captiveportal']['enable']))
345
		return;
346

    
347
	$cpips = array();
348
	$ifaces = get_configured_interface_list();
349
	foreach ($ifaces as $kiface => $kiface2) {
350
		$tmpif = get_real_interface($kiface);
351
		pfSense_interface_flags($tmpif, -IFF_IPFW_FILTER);
352
	}
353
	$cpinterfaces = explode(",", $config['captiveportal']['interface']);
354
	$firsttime = 0;
355
	foreach ($cpinterfaces as $cpifgrp) {
356
		if (!isset($ifaces[$cpifgrp]))
357
			continue;
358
		$tmpif = get_real_interface($cpifgrp);
359
		if (!empty($tmpif)) {
360
			if ($firsttime > 0)
361
				$cpinterface .= " or ";
362
			$cpinterface .= "via {$tmpif}";
363
			$firsttime = 1;
364
			$cpipm = get_interface_ip($cpifgrp);
365
			if (is_ipaddr($cpipm)) {
366
				$carpif = link_ip_to_carp_interface($cpipm);
367
				if (!empty($carpif)) {
368
					$carpsif = explode(" ", $carpif);
369
					foreach ($carpsif as $cpcarp) {
370
						pfSense_interface_flags($cpcarp, IFF_IPFW_FILTER);
371
						$carpip = find_interface_ip($cpcarp);
372
						if (is_ipaddr($carpip))
373
							$cpips[] = $carpip;
374
					}
375
				}
376
				$cpips[] = $cpipm;
377
				pfSense_interface_flags($tmpif, IFF_IPFW_FILTER);
378
			}
379
		}
380
	}
381
	if (count($cpips) > 0) {
382
		$cpactive = true;
383
		$cpinterface = "{ {$cpinterface} } ";
384
        } else
385
		return false;
386

    
387
	if ($reinit == false)
388
		$captiveportallck = lock('captiveportal');
389

    
390
	/* init dummynet/ipfw rules number database */
391
	captiveportal_init_ipfw_ruleno();
392

    
393
	/* make sure ipfw is loaded */
394
	if (!is_module_loaded("ipfw.ko"))
395
		filter_load_ipfw();
396
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
397
	if (!is_module_loaded("dummynet.ko"))
398
		mwexec("/sbin/kldload dummynet");
399

    
400
	$cprules =  "add 65291 set 1 allow pfsync from any to any\n";
401
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
402

    
403
	$cprules .= <<<EOD
404
# add 65300 set 1 skipto 65534 all from any to any not layer2
405
# layer 2: pass ARP
406
add 65301 set 1 pass layer2 mac-type arp
407
# pfsense requires for WPA
408
add 65302 set 1 pass layer2 mac-type 0x888e
409
add 65303 set 1 pass layer2 mac-type 0x88c7
410

    
411
# PPP Over Ethernet Discovery Stage
412
add 65304 set 1 pass layer2 mac-type 0x8863
413
# PPP Over Ethernet Session Stage
414
add 65305 set 1 pass layer2 mac-type 0x8864
415
# Allow WPA
416
add 65306 set 1 pass layer2 mac-type 0x888e
417

    
418
# layer 2: block anything else non-IP
419
add 65307 set 1 deny layer2 not mac-type ip
420

    
421
EOD;
422

    
423
	$rulenum = 65310;
424
	$ips = "255.255.255.255 ";
425
	foreach ($cpips as $cpip)
426
		$ips .= "or {$cpip} ";
427
	$ips = "{ {$ips} }";
428
	$cprules .= "add {$rulenum} set 1 pass ip from any to {$ips} in\n";
429
	$rulenum++;
430
	$cprules .= "add {$rulenum} set 1 pass ip from {$ips} to any out\n";
431
	$rulenum++;
432
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
433
	$rulenum++;
434
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
435
	$rulenum++;
436
	/* Allowed ips */
437
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
438
	$rulenum++;
439
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
440
	$rulenum++;
441
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
442
	$rulenum++;
443
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
444
	$rulenum++;
445
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
446
	$rulenum++;
447
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
448
	$rulenum++;
449
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
450
	$rulenum++;
451
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
452
	$rulenum++;
453

    
454
	/* Authenticated users rules. */
455
	if (isset($config['captiveportal']['peruserbw'])) {
456
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
457
		$rulenum++;
458
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
459
		$rulenum++;
460
	} else {
461
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
462
                $rulenum++;
463
                $cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
464
                $rulenum++;
465
	}
466
	
467
       $cprules .= <<<EOD
468

    
469
# redirect non-authenticated clients to captive portal
470
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
471
# let the responses from the captive portal web server back out
472
add 65532 set 1 pass tcp from any to any out
473
# block everything else
474
add 65533 set 1 deny all from any to any
475
# pass everything else on layer2
476
add 65534 set 1 pass all from any to any layer2
477

    
478
EOD;
479

    
480
	/* generate passthru mac database */
481
	$cprules .= captiveportal_passthrumac_configure(true);
482
	$cprules .= "\n";
483
	/* allowed ipfw rules to make allowed ip work */
484
	$cprules .= captiveportal_allowedip_configure();
485

    
486
	/* load rules */
487
	if ($reinit == true)
488
		$cprules = "table all flush\nflush\n{$cprules}";
489
	else {
490
		$tmprules = "table 3 flush\n";
491
		$tmprules .= "table 4 flush\n";
492
		$tmprules .= "table 5 flush\n";
493
		$tmprules .= "table 6 flush\n";
494
		$tmprules .= "table 7 flush\n";
495
		$tmprules .= "table 8 flush\n";
496
		$tmprules .= "table 9 flush\n";
497
		$tmprules .= "table 10 flush\n";
498
		$tmprules .= "flush\n";
499
		$cprules = "{$tmprules}\n{$cprules}";
500
	}
501

    
502
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
503
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
504
	@unlink("{$g['tmp_path']}/ipfw.cp.rules");
505

    
506
	if ($reinit == false)
507
		unlock($captiveportallck);
508

    
509

    
510
	/* filter on layer2 as well so we can check MAC addresses */
511
	mwexec("/sbin/sysctl net.link.ether.ipfw=1");
512

    
513
	return $cprules;
514
}
515

    
516
/* remove clients that have been around for longer than the specified amount of time */
517
/* db file structure:
518
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
519

    
520
/* (password is in Base64 and only saved when reauthentication is enabled) */
521
function captiveportal_prune_old() {
522

    
523
    global $g, $config;
524

    
525
    /* check for expired entries */
526
    if ($config['captiveportal']['timeout'])
527
        $timeout = $config['captiveportal']['timeout'] * 60;
528
    else
529
        $timeout = 0;
530

    
531
    if ($config['captiveportal']['idletimeout'])
532
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
533
    else
534
        $idletimeout = 0;
535

    
536
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
537
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
538
        return;
539

    
540
    $captiveportallck = lock('captiveportal');
541

    
542
    /* read database */
543
    $cpdb = captiveportal_read_db();
544

    
545
    $radiusservers = captiveportal_get_radius_servers();
546

    
547
    /*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
548
     *  outside of the loop. Otherwise the loop would evaluate count() on every iteration
549
     *  and since $i would increase and count() would decrement they would meet before we
550
     *  had a chance to iterate over all accounts.
551
     */
552
    $unsetindexes = array();
553
    $no_users = count($cpdb);
554
    for ($i = 0; $i < $no_users; $i++) {
555

    
556
        $timedout = false;
557
        $term_cause = 1;
558

    
559
        /* hard timeout? */
560
        if ($timeout) {
561
            if ((time() - $cpdb[$i][0]) >= $timeout) {
562
                $timedout = true;
563
                $term_cause = 5; // Session-Timeout
564
            }
565
        }
566

    
567
        /* Session-Terminate-Time */
568
        if (!$timedout && !empty($cpdb[$i][9])) {
569
            if (time() >= $cpdb[$i][9]) {
570
                $timedout = true;
571
                $term_cause = 5; // Session-Timeout
572
            }
573
        }
574

    
575
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
576
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
577
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
578
        if (!$timedout && $idletimeout) {
579
            $lastact = captiveportal_get_last_activity($cpdb[$i][2]);
580
			/*  If the user has logged on but not sent any traffic they will never be logged out.
581
			 *  We "fix" this by setting lastact to the login timestamp. 
582
			 */
583
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
584
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
585
                $timedout = true;
586
                $term_cause = 4; // Idle-Timeout
587
                $stop_time = $lastact; // Entry added to comply with WISPr
588
            }
589
        }
590

    
591
	/* if vouchers are configured, activate session timeouts */
592
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
593
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
594
			$timedout = true;
595
			$term_cause = 5; // Session-Timeout
596
		}
597
	}
598

    
599
        /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
600
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
601
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
602
                $timedout = true;
603
                $term_cause = 5; // Session-Timeout
604
            }
605
        }
606

    
607
        if ($timedout) {
608
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
609
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
610
	    $unsetindexes[$i] = $i;
611
        }
612

    
613
        /* do periodic RADIUS reauthentication? */
614
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
615
            !empty($radiusservers)) {
616

    
617
            if (isset($config['captiveportal']['radacct_enable'])) {
618
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
619
                    /* stop and restart accounting */
620
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
621
                                           $cpdb[$i][4], // username
622
                                           $cpdb[$i][5], // sessionid
623
                                           $cpdb[$i][0], // start time
624
                                           $radiusservers,
625
                                           $cpdb[$i][2], // clientip
626
                                           $cpdb[$i][3], // clientmac
627
                                           10); // NAS Request
628
                    exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}");
629
                    exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}");
630
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
631
                                            $cpdb[$i][4], // username
632
                                            $cpdb[$i][5], // sessionid
633
                                            $radiusservers,
634
                                            $cpdb[$i][2], // clientip
635
                                            $cpdb[$i][3]); // clientmac
636
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
637
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
638
                                           $cpdb[$i][4], // username
639
                                           $cpdb[$i][5], // sessionid
640
                                           $cpdb[$i][0], // start time
641
                                           $radiusservers,
642
                                           $cpdb[$i][2], // clientip
643
                                           $cpdb[$i][3], // clientmac
644
                                           10, // NAS Request
645
                                           true); // Interim Updates
646
                }
647
            }
648

    
649
            /* check this user against RADIUS again */
650
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
651
                                          base64_decode($cpdb[$i][6]), // password
652
                                            $radiusservers,
653
                                          $cpdb[$i][2], // clientip
654
                                          $cpdb[$i][3], // clientmac
655
                                          $cpdb[$i][1]); // ruleno
656

    
657
            if ($auth_list['auth_val'] == 3) {
658
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
659
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
660
	        $unsetindexes[$i] = $i;
661
            }
662
        }
663
    }
664
    /* This is a kludge to overcome some php weirdness */
665
    foreach($unsetindexes as $unsetindex)
666
	unset($cpdb[$unsetindex]);
667

    
668
    /* write database */
669
    captiveportal_write_db($cpdb);
670

    
671
    unlock($captiveportallck);
672
}
673

    
674
/* remove a single client according to the DB entry */
675
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
676

    
677
	global $g, $config;
678

    
679
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
680

    
681
	/* this client needs to be deleted - remove ipfw rules */
682
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
683
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
684
							   $dbent[4], // username
685
							   $dbent[5], // sessionid
686
							   $dbent[0], // start time
687
							   $radiusservers,
688
							   $dbent[2], // clientip
689
							   $dbent[3], // clientmac
690
							   $term_cause, // Acct-Terminate-Cause
691
							   false,
692
							   $stop_time);
693
	}
694
	/* Delete client's ip entry from tables 3 and 4. */
695
	mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
696
	mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
697

    
698
	/* Release the ruleno so it can be reallocated to new clients. */
699
	captiveportal_free_ipfw_ruleno($dbent[1]);
700

    
701
	/* 
702
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
703
	* We could get an error if the pipe doesn't exist but everything should still be fine
704
	*/
705
	if (isset($config['captiveportal']['peruserbw'])) {
706
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
707
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
708
	}
709

    
710
	/* XXX: Redundant?! Ensure all pf(4) states are killed. */
711
	mwexec("pfctl -k {$dbent[2]}");
712
	mwexec("pfctl -K {$dbent[2]}");
713

    
714
}
715

    
716
/* remove a single client by ipfw rule number */
717
function captiveportal_disconnect_client($id,$term_cause = 1) {
718

    
719
	global $g, $config;
720

    
721
	$captiveportallck = lock('captiveportal');
722

    
723
	/* read database */
724
	$cpdb = captiveportal_read_db();
725
	$radiusservers = captiveportal_get_radius_servers();
726

    
727
	/* find entry */
728
	$tmpindex = 0;
729
	$cpdbcount = count($cpdb);
730
	for ($i = 0; $i < $cpdbcount; $i++) {
731
		if ($cpdb[$i][1] == $id) {
732
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
733
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
734
			unset($cpdb[$i]);
735
			break;
736
		}
737
	}		
738

    
739
	/* write database */
740
	captiveportal_write_db($cpdb);
741

    
742
	unlock($captiveportallck);
743
}
744

    
745
/* send RADIUS acct stop for all current clients */
746
function captiveportal_radius_stop_all($lock = false) {
747
	global $g, $config;
748

    
749
	if (!isset($config['captiveportal']['radacct_enable']))
750
		return;
751

    
752
	if (!$lock)
753
		$captiveportallck = lock('captiveportal');
754

    
755
	$cpdb = captiveportal_read_db();
756

    
757
	$radiusservers = captiveportal_get_radius_servers();
758
	if (!empty($radiusservers)) {
759
		for ($i = 0; $i < count($cpdb); $i++) {
760
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
761
								   $cpdb[$i][4], // username
762
								   $cpdb[$i][5], // sessionid
763
								   $cpdb[$i][0], // start time
764
								   $radiusservers,
765
								   $cpdb[$i][2], // clientip
766
								   $cpdb[$i][3], // clientmac
767
								   7); // Admin Reboot
768
		}
769
	}
770
	if (!$lock)
771
		unlock($captiveportallck);
772
}
773

    
774
function captiveportal_passthrumac_configure_entry($macent) {
775
	$rules = "";
776
        $enBwup = isset($macent['bw_up']);
777
        $enBwdown = isset($macent['bw_down']);
778
	$actionup = "allow";
779
	$actiondown = "allow";
780

    
781
        if ($enBwup && $enBwdown)
782
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
783
        else
784
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
785

    
786
	if ($enBwup) {
787
                $bw_up = $ruleno + 20000;
788
                $rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
789
		$actionup = "pipe {$bw_up}";
790
        }
791
        if ($enBwdown) {
792
		$bw_down = $ruleno + 20001;
793
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
794
		$actiondown = "pipe {$bw_down}";
795
        }
796
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC {$macent['mac']} any\n";
797
	$ruleno++;
798
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC any {$macent['mac']}\n";
799

    
800
	return $rules;
801
}
802

    
803
function captiveportal_passthrumac_configure($lock = false) {
804
	global $config, $g;
805

    
806
	$rules = "";
807

    
808
	if (is_array($config['captiveportal']['passthrumac'])) {
809
		$macdb = array();
810
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
811
			$rules .= captiveportal_passthrumac_configure_entry($macent);
812
			$macdb[$macent['mac']]['active']  = true;
813

    
814
		}
815
	}
816

    
817
	return $rules;
818
}
819

    
820
function captiveportal_passthrumac_findbyname($username) {
821
	global $config;
822

    
823
	if (is_array($config['captiveportal']['passthrumac'])) {
824
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
825
			if ($macent['username'] == $username)
826
				return $macent;
827
		}
828
	}
829
	return NULL;
830
}
831

    
832
/* 
833
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
834
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
835
 */
836
function captiveportal_allowedip_configure_entry($ipent) {
837

    
838
	$rules = "";
839
	$enBwup = isset($ipent['bw_up']);
840
	$enBwdown = isset($ipent['bw_down']);
841
	$bw_up = "";
842
        $bw_down = "";
843
        $tablein = array();
844
        $tableout = array();
845

    
846
	if ($enBwup && $enBwdown)
847
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
848
	else
849
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
850

    
851
        if ($ipent['dir'] == "from") {
852
        	if ($enBwup)
853
                	$tablein[] = 5;
854
                else
855
                	$tablein[] = 3;
856
                if ($enBwdown)
857
                        $tableout[] = 6;
858
                else
859
                        $tableout[] = 4;
860
        } else if ($ipent['dir'] == "to") {
861
                if ($enBwup)
862
                	$tablein[] = 9;
863
                else
864
                        $tablein[] = 7;
865
                if ($enBwdown)
866
                        $tableout[] = 10;
867
                else
868
                        $tableout[] = 8;
869
        } else if ($ipent['dir'] == "both") {
870
                if ($enBwup) {
871
                        $tablein[] = 5;
872
                        $tablein[] = 9;
873
                } else {
874
                        $tablein[] = 3;
875
                        $tablein[] = 7;
876
                }
877
        	if ($enBwdown) {
878
                        $tableout[] = 6;
879
                        $tableout[] = 10;
880
                } else {
881
                        $tableout[] = 4;
882
                	$tableout[] = 8;
883
                }
884
        }
885
        if ($enBwup) {
886
                $bw_up = $ruleno + 20000;
887
        	$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
888
        }
889
	foreach ($tablein as $table)
890
               $rules .= "table {$table} add {$ipent['ip']} {$bw_up}\n";
891
        if ($enBwdown) {
892
               $bw_down = $ruleno + 20001;
893
               $rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
894
        }
895
        foreach ($tableout as $table)
896
        	$rules .= "table {$table} add {$ipent['ip']} {$bw_down}\n";
897

    
898
	return $rules;
899
}
900

    
901
function captiveportal_allowedip_configure() {
902
	global $config, $g;
903

    
904
	$rules = "";
905
	if (is_array($config['captiveportal']['allowedip'])) {
906
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
907
			$rules .= captiveportal_allowedip_configure_entry($ipent);
908
		}
909
	}
910

    
911
	return $rules;
912
}
913

    
914
/* get last activity timestamp given client IP address */
915
function captiveportal_get_last_activity($ip) {
916

    
917
	$ipfwoutput = "";
918

    
919
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
920
	/* Reading only from one of the tables is enough of approximation. */
921
	if ($ipfwoutput[0]) {
922
		$ri = explode(" ", $ipfwoutput[0]);
923
		if ($ri[4])
924
			return $ri[4];
925
	}
926

    
927
	return 0;
928
}
929

    
930
/* read RADIUS servers into array */
931
function captiveportal_get_radius_servers() {
932

    
933
        global $g;
934

    
935
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
936
                $radiusservers = array();
937
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
938
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
939
		if ($cpradiusdb)
940
		foreach($cpradiusdb as $cpradiusentry) {
941
                	$line = trim($cpradiusentry);
942
                        if ($line) {
943
                        	$radsrv = array();
944
                                list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
945
                        	$radiusservers[] = $radsrv;
946
                        }
947
		}
948

    
949
		return $radiusservers;
950
        }
951

    
952
        return false;
953
}
954

    
955
/* log successful captive portal authentication to syslog */
956
/* part of this code from php.net */
957
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
958
	$message = trim($message);
959
	// Log it
960
	if (!$message)
961
		$message = "$status: $user, $mac, $ip";
962
	else
963
		$message = "$status: $user, $mac, $ip, $message";
964
	captiveportal_syslog($message);
965
	closelog();
966
}
967

    
968
/* log simple messages to syslog */
969
function captiveportal_syslog($message) {
970
	define_syslog_variables();
971
	$message = trim($message);
972
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
973
	// Log it
974
	syslog(LOG_INFO, $message);
975
	closelog();
976
}
977

    
978
function radius($username,$password,$clientip,$clientmac,$type) {
979
    global $g, $config;
980

    
981
    /* Start locking from the beginning of an authentication session */
982
    $captiveportallck = lock('captiveportal');
983

    
984
    $ruleno = captiveportal_get_next_ipfw_ruleno();
985

    
986
    /* If the pool is empty, return appropriate message and fail authentication */
987
    if (is_null($ruleno)) {
988
        $auth_list = array();
989
        $auth_list['auth_val'] = 1;
990
        $auth_list['error'] = "System reached maximum login capacity";
991
        unlock($captiveportallck);
992
        return $auth_list;
993
    }
994

    
995
    /*
996
     * Drop the lock since radius takes some time to finish.
997
     * The implementation is reentrant so we gain speed with this.
998
     */
999
    unlock($captiveportallck);
1000

    
1001
    $radiusservers = captiveportal_get_radius_servers();
1002

    
1003
    $auth_list = RADIUS_AUTHENTICATION($username,
1004
                    $password,
1005
                    $radiusservers,
1006
                    $clientip,
1007
                    $clientmac,
1008
                    $ruleno);
1009

    
1010
    $captiveportallck = lock('captiveportal');
1011

    
1012
    if ($auth_list['auth_val'] == 2) {
1013
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1014
        $sessionid = portal_allow($clientip,
1015
                    $clientmac,
1016
                    $username,
1017
                    $password,
1018
                    $auth_list,
1019
                    $ruleno);
1020
    }
1021

    
1022
    unlock($captiveportallck);
1023

    
1024
    return $auth_list;
1025

    
1026
}
1027

    
1028
/* read captive portal DB into array */
1029
function captiveportal_read_db() {
1030

    
1031
        global $g;
1032

    
1033
        $cpdb = array();
1034
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1035
        if ($fd) {
1036
                while (!feof($fd)) {
1037
                        $line = trim(fgets($fd));
1038
                        if ($line) {
1039
                                $cpdb[] = explode(",", $line);
1040
                        }
1041
                }
1042
                fclose($fd);
1043
        }
1044
        return $cpdb;
1045
}
1046

    
1047
/* write captive portal DB */
1048
function captiveportal_write_db($cpdb) {
1049
                 
1050
        global $g;
1051
                
1052
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1053
        if ($fd) { 
1054
                foreach ($cpdb as $cpent) {
1055
                        fwrite($fd, join(",", $cpent) . "\n");
1056
                }       
1057
                fclose($fd);
1058
        }       
1059
}
1060

    
1061
function captiveportal_write_elements() {
1062
	global $g, $config;
1063
    
1064
	/* delete any existing elements */
1065
	if (is_dir($g['captiveportal_element_path'])) {
1066
		$dh = opendir($g['captiveportal_element_path']);
1067
		while (($file = readdir($dh)) !== false) {
1068
			if ($file != "." && $file != "..")
1069
				unlink($g['captiveportal_element_path'] . "/" . $file);
1070
		}
1071
		closedir($dh);
1072
	} else
1073
		@mkdir($g['captiveportal_element_path']);
1074

    
1075
	if (is_array($config['captiveportal']['element'])) {
1076
		conf_mount_rw();
1077
		foreach ($config['captiveportal']['element'] as $data) {
1078
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1079
			if (!$fd) {
1080
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1081
				return 1;
1082
			}
1083
			$decoded = base64_decode($data['content']);
1084
			fwrite($fd,$decoded);
1085
			fclose($fd);
1086
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1087
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1088
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1089
		}
1090
		conf_mount_ro();
1091
	}
1092
    
1093
	return 0;
1094
}
1095

    
1096
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1097
	global $g;
1098

    
1099
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1100
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1101
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1102
}
1103

    
1104
/*
1105
 * This function will calculate the lowest free firewall ruleno
1106
 * within the range specified based on the actual logged on users
1107
 *
1108
 */
1109
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1110
	global $config, $g;
1111

    
1112
	if(!isset($config['captiveportal']['enable']))
1113
		return NULL;
1114

    
1115
	$ruleno = 0;
1116
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1117
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1118
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1119
			if ($rules[$ridx]) {
1120
				/* 
1121
	 			 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1122
	 			 * and the out pipe ruleno + 1. This removes limitation that where present in 
1123
	 			 * previous version of the peruserbw.
1124
	 			 */
1125
				if (isset($config['captiveportal']['peruserbw']))
1126
					$ridx++;
1127
				continue;
1128
			}
1129
			$ruleno = $ridx;
1130
			$rules[$ridx] = "used";
1131
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1132
				$rules[++$ridx] = "used";
1133
			break;
1134
		}
1135
	} else {
1136
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1137
		$rules[2] = "used";
1138
		$ruleno = 2;
1139
	}
1140
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1141
	return $ruleno;
1142
}
1143

    
1144
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1145
	global $config, $g;
1146

    
1147
	if(!isset($config['captiveportal']['enable']))
1148
		return NULL;
1149

    
1150
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1151
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1152
		$rules[$ruleno] = false;
1153
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1154
			$rules[++$ruleno] = false;
1155
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1156
	}
1157
}
1158

    
1159
function captiveportal_get_ipfw_passthru_ruleno($value) {
1160
	global $config, $g;
1161

    
1162
	if(!isset($config['captiveportal']['enable']))
1163
                return NULL;
1164

    
1165
        if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1166
                $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1167
		$ruleno = intval(`/sbin/ipfw show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`);
1168
		if ($rules[$ruleno])
1169
			return $ruleno;
1170
        }
1171

    
1172
	return NULL;
1173
}
1174

    
1175
/**
1176
 * This function will calculate the traffic produced by a client
1177
 * based on its firewall rule
1178
 *
1179
 * Point of view: NAS
1180
 *
1181
 * Input means: from the client
1182
 * Output means: to the client
1183
 *
1184
 */
1185

    
1186
function getVolume($ip) {
1187

    
1188
    $volume = array();
1189

    
1190
    // Initialize vars properly, since we don't want NULL vars
1191
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1192

    
1193
    // Ingress
1194
    $ipfwin = "";
1195
    $ipfwout = "";
1196
    $matchesin = "";
1197
    $matchesout = "";
1198
    exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1199
    if ($ipfwin[0]) {
1200
		$ipfwin = split(" ", $ipfwin[0]);
1201
		$volume['input_pkts'] = $ipfwin[2];
1202
		$volume['input_bytes'] = $ipfwin[3];
1203
    }
1204

    
1205
    exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1206
    if ($ipfwout[0]) {
1207
        $ipfwout = split(" ", $ipfwout[0]);
1208
        $volume['output_pkts'] = $ipfwout[2];
1209
        $volume['output_bytes'] = $ipfwout[3];
1210
    }
1211

    
1212
    return $volume;
1213
}
1214

    
1215
/**
1216
 * Get the NAS-Identifier
1217
 *
1218
 * We will use our local hostname to make up the nas_id
1219
 */
1220
function getNasID()
1221
{
1222
    $nasId = "";
1223
    exec("/bin/hostname", $nasId);
1224
    if(!$nasId[0])
1225
        $nasId[0] = "{$g['product_name']}";
1226
    return $nasId[0];
1227
}
1228

    
1229
/**
1230
 * Get the NAS-IP-Address based on the current wan address
1231
 *
1232
 * Use functions in interfaces.inc to find this out
1233
 *
1234
 */
1235

    
1236
function getNasIP()
1237
{
1238
	global $config;
1239

    
1240
	if (empty($config['captiveportal']['radiussrcip_attribute']))
1241
    		$nasIp = get_interface_ip();
1242
	else {
1243
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1244
                        $nasIp = $config['captiveportal']['radiussrcip_attribute'];
1245
                else
1246
                        $nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1247
	}
1248
		
1249
    	if(!is_ipaddr($nasIp))
1250
        	$nasIp = "0.0.0.0";
1251

    
1252
	return $nasIp;
1253
}
1254

    
1255
function portal_ip_from_client_ip($cliip) {
1256
	global $config;
1257

    
1258
	$interfaces = explode(",", $config['captiveportal']['interface']);
1259
	foreach ($interfaces as $cpif) {
1260
		$ip = get_interface_ip($cpif);
1261
		$sn = get_interface_subnet($cpif);
1262
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1263
			return $ip;
1264
	}
1265

    
1266
	// doesn't match up to any particular interface
1267
	// so let's set the portal IP to what PHP says 
1268
	// the server IP issuing the request is. 
1269
	// allows same behavior as 1.2.x where IP isn't 
1270
	// in the subnet of any CP interface (static routes, etc.)
1271
	// rather than forcing to DNS hostname resolution
1272
	$ip = $_SERVER['SERVER_ADDR'];
1273
	if (is_ipaddr($ip))
1274
		return $ip;
1275

    
1276
	return false;
1277
}
1278

    
1279
?>
(6-6/51)