Project

General

Profile

Download (41.4 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
	$subnet = "";
890
	if (!empty($ipent['sn']))
891
		$subnet = "/{$ipent['sn']}";
892
	foreach ($tablein as $table)
893
               $rules .= "table {$table} add {$ipent['ip']}{$subnet} {$bw_up}\n";
894
        if ($enBwdown) {
895
               $bw_down = $ruleno + 20001;
896
               $rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
897
        }
898
        foreach ($tableout as $table)
899
        	$rules .= "table {$table} add {$ipent['ip']}{$subnet} {$bw_down}\n";
900

    
901
	return $rules;
902
}
903

    
904
function captiveportal_allowedip_configure() {
905
	global $config, $g;
906

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

    
914
	return $rules;
915
}
916

    
917
/* get last activity timestamp given client IP address */
918
function captiveportal_get_last_activity($ip) {
919

    
920
	$ipfwoutput = "";
921

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

    
930
	return 0;
931
}
932

    
933
/* read RADIUS servers into array */
934
function captiveportal_get_radius_servers() {
935

    
936
        global $g;
937

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

    
952
		return $radiusservers;
953
        }
954

    
955
        return false;
956
}
957

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

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

    
981
function radius($username,$password,$clientip,$clientmac,$type) {
982
    global $g, $config;
983

    
984
    /* Start locking from the beginning of an authentication session */
985
    $captiveportallck = lock('captiveportal');
986

    
987
    $ruleno = captiveportal_get_next_ipfw_ruleno();
988

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

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

    
1004
    $radiusservers = captiveportal_get_radius_servers();
1005

    
1006
    $auth_list = RADIUS_AUTHENTICATION($username,
1007
                    $password,
1008
                    $radiusservers,
1009
                    $clientip,
1010
                    $clientmac,
1011
                    $ruleno);
1012

    
1013
    $captiveportallck = lock('captiveportal');
1014

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

    
1025
    unlock($captiveportallck);
1026

    
1027
    return $auth_list;
1028

    
1029
}
1030

    
1031
/* read captive portal DB into array */
1032
function captiveportal_read_db() {
1033

    
1034
        global $g;
1035

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

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

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

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

    
1099
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1100
	global $g;
1101

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

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

    
1115
	if(!isset($config['captiveportal']['enable']))
1116
		return NULL;
1117

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

    
1147
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1148
	global $config, $g;
1149

    
1150
	if(!isset($config['captiveportal']['enable']))
1151
		return NULL;
1152

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

    
1162
function captiveportal_get_ipfw_passthru_ruleno($value) {
1163
	global $config, $g;
1164

    
1165
	if(!isset($config['captiveportal']['enable']))
1166
                return NULL;
1167

    
1168
        if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1169
                $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1170
		$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`);
1171
		if ($rules[$ruleno])
1172
			return $ruleno;
1173
        }
1174

    
1175
	return NULL;
1176
}
1177

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

    
1189
function getVolume($ip) {
1190

    
1191
    $volume = array();
1192

    
1193
    // Initialize vars properly, since we don't want NULL vars
1194
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1195

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

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

    
1215
    return $volume;
1216
}
1217

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

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

    
1239
function getNasIP()
1240
{
1241
	global $config;
1242

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

    
1255
	return $nasIp;
1256
}
1257

    
1258
function portal_ip_from_client_ip($cliip) {
1259
	global $config;
1260

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

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

    
1279
	return false;
1280
}
1281

    
1282
?>
(6-6/51)