Project

General

Profile

Download (41.9 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
	$cpactive = false;
56
	if (isset($config['captiveportal']['enable'])) {
57
		$cpips = array();
58
		$ifaces = get_configured_interface_list();
59
		foreach ($ifaces as $kiface => $kiface2) {
60
			$tmpif = get_real_interface($kiface);
61
			pfSense_interface_flags($tmpif, -IFF_IPFW_FILTER);
62
		}
63
		$cpinterfaces = explode(",", $config['captiveportal']['interface']);
64
		$firsttime = 0;
65
		foreach ($cpinterfaces as $cpifgrp) {
66
			if (!isset($ifaces[$cpifgrp]))
67
				continue;
68
			$tmpif = get_real_interface($cpifgrp);
69
			if (!empty($tmpif)) {
70
				if ($firsttime > 0)
71
					$cpinterface .= " or ";
72
				$cpinterface .= "via {$tmpif}"; 
73
				$firsttime = 1;
74
				$cpipm = get_interface_ip($cpifgrp);
75
				if (is_ipaddr($cpipm)) {
76
					$carpif = link_ip_to_carp_interface($cpipm);
77
					if (!empty($carpif)) {
78
						$carpsif = explode(" ", $carpif);
79
						foreach ($carpsif as $cpcarp) {
80
							pfSense_interface_flags($cpcarp, IFF_IPFW_FILTER);
81
							$carpip = find_interface_ip($cpcarp);
82
							if (is_ipaddr($carpip))
83
								$cpips[] = $carpip;
84
						}
85
					}
86
					$cpips[] = $cpipm;
87
					pfSense_interface_flags($tmpif, IFF_IPFW_FILTER);
88
				}
89
			}
90
		}
91
		if (count($cpips) > 0) {
92
			$cpactive = true;
93
			$cpinterface = "{ {$cpinterface} } ";
94
		}
95
	}
96

    
97
	if ($cpactive == true) {
98

    
99
		if ($g['booting'])
100
			echo "Starting captive portal... ";
101

    
102
		/* kill any running mini_httpd */
103
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
104
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
105

    
106
		/* remove old information */
107
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
108
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
109
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
110
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
111
		mwexec("/sbin/ipfw -q table all flush", true);
112

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

    
116
		/* kill any running minicron */
117
		killbypid("{$g['varrun_path']}/minicron.pid");
118

    
119
		/* make sure ipfw is loaded */
120
		if (!is_module_loaded("ipfw.ko"))
121
			filter_load_ipfw();
122
		/* Always load dummynet now that even allowed ip and mac passthrough use it. */
123
		if (!is_module_loaded("dummynet.ko"))
124
                        mwexec("/sbin/kldload dummynet");
125

    
126
		/* generate ipfw rules */
127
		captiveportal_init_ipfw_ruleno();
128
		$cprules = captiveportal_rules_generate($cpinterface, $cpips);
129
		$cprules .= "\n";
130
		/* generate passthru mac database */
131
		$cprules .= captiveportal_passthrumac_configure(true);
132
		$cprules .= "\n";
133
		/* allowed ipfw rules to make allowed ip work */
134
		$cprules .= captiveportal_allowedip_configure();
135

    
136
		/* stop accounting on all clients */
137
		captiveportal_radius_stop_all(true);
138

    
139
		/* initialize minicron interval value */
140
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
141

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

    
145
		/* write portal page */
146
		if ($config['captiveportal']['page']['htmltext'])
147
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
148
		else {
149
			/* example/template page */
150
			$htmltext = <<<EOD
151
<html>
152
<head>
153
<title>{$g['product_name']} captive portal</title>
154
</head>
155
<body>
156
<center>
157
<h2>{$g['product_name']} captive portal</h2>
158
Welcome to the {$g['product_name']} Captive Portal!
159
<p>
160
<form method="post" action="\$PORTAL_ACTION\$">
161
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
162
<table>
163
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
164
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
165
   <tr><td>&nbsp;</td></tr>
166
   <tr>
167
     <td colspan="2">
168
	<center><input name="accept" type="submit" value="Continue"></center>
169
     </td>
170
   </tr>
171
</table>
172
</center>
173
</form>
174
</body>
175
</html>
176

    
177

    
178

    
179
EOD;
180
		}
181

    
182
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
183
		if ($fd) {
184
			// Special case handling.  Convert so that we can pass this page
185
			// through the PHP interpreter later without clobbering the vars.
186
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
187
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
188
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
189
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
190
			$htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
191
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
192
			fwrite($fd, $htmltext);
193
			fclose($fd);
194
		}
195

    
196
		/* write error page */
197
		if ($config['captiveportal']['page']['errtext'])
198
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
199
		else {
200
			/* example page */
201
			$errtext = <<<EOD
202
<html>
203
<head>
204
<title>Authentication error</title>
205
</head>
206
<body>
207
<font color="#cc0000"><h2>Authentication error</h2></font>
208
<b>
209
Username and/or password invalid.
210
<br><br>
211
<a href="javascript:history.back(); ">Go back</a>
212
</b>
213
</body>
214
</html>
215

    
216
EOD;
217
		}
218

    
219
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
220
		if ($fd) {
221
			// Special case handling.  Convert so that we can pass this page
222
			// through the PHP interpreter later without clobbering the vars.
223
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
224
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
225
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
226
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
227
			$errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
228
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
229
			fwrite($fd, $errtext);
230
			fclose($fd);
231
		}
232

    
233
		/* write error page */
234
		if ($config['captiveportal']['page']['logouttext'])
235
			$logouttext = base64_decode($config['captiveportal']['page']['logouttext']);
236
		else {
237
			/* example page */
238
			$logouttext = <<<EOD
239
<HTML>
240
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
241
<BODY>
242
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
243
<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
244
</SPAN>
245
<SCRIPT LANGUAGE="JavaScript">
246
<!--
247
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
248
if (LogoutWin) {
249
    LogoutWin.document.write('<HTML>');
250
    LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
251
    LogoutWin.document.write('<BODY BGCOLOR="#435370">');
252
    LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
253
    LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
254
    LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
255
    LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
256
    LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
257
    LogoutWin.document.write('</FORM>');
258
    LogoutWin.document.write('</DIV></BODY>');
259
    LogoutWin.document.write('</HTML>');
260
    LogoutWin.document.close();
261
}
262

    
263
document.location.href="{$my_redirurl}";
264
-->
265
</SCRIPT>
266
</BODY>
267
</HTML>
268

    
269
EOD;
270
		}
271

    
272
		$fd = @fopen("{$g['varetc_path']}/captiveportal-logout.html", "w");
273
		if ($fd) {
274
			fwrite($fd, $logouttext);
275
			fclose($fd);
276
		}
277
		/* write elements */
278
		captiveportal_write_elements();
279

    
280
		/* load rules */
281
		mwexec("/sbin/ipfw -q flush");
282

    
283
		/* ipfw cannot accept rules directly on stdin,
284
		   so we have to write them to a temporary file first */
285
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
286
		if (!$fd) {
287
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
288
			return 1;
289
		}
290

    
291
		fwrite($fd, $cprules);
292
		fclose($fd);
293

    
294
		mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules");
295

    
296
		@unlink("{$g['tmp_path']}/ipfw.cp.rules");
297

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

    
301
		chdir($g['captiveportal_path']);
302

    
303
		if ($config['captiveportal']['maxproc'])
304
			$maxproc = $config['captiveportal']['maxproc'];
305
		else
306
			$maxproc = 16;
307

    
308
		$use_fastcgi = true;
309

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

    
323
		/* generate lighttpd configuration */
324
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
325
			"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
326
				"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
327

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

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

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

    
339
		/* generate radius server database */
340
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
341
				($config['captiveportal']['auth_method'] == "radius"))) {
342
			$radiusip = $config['captiveportal']['radiusip'];
343
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
344

    
345
			if ($config['captiveportal']['radiusport'])
346
				$radiusport = $config['captiveportal']['radiusport'];
347
			else
348
				$radiusport = 1812;
349

    
350
			if ($config['captiveportal']['radiusacctport'])
351
				$radiusacctport = $config['captiveportal']['radiusacctport'];
352
			else
353
				$radiusacctport = 1813;
354

    
355
			if ($config['captiveportal']['radiusport2'])
356
				$radiusport2 = $config['captiveportal']['radiusport2'];
357
			else
358
				$radiusport2 = 1812;
359

    
360
			$radiuskey = $config['captiveportal']['radiuskey'];
361
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
362

    
363
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
364
			if (!$fd) {
365
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
366
				return 1;
367
			} else if (isset($radiusip2, $radiuskey2)) {
368
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
369
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
370
			} else {
371
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
372
			}
373
			fclose($fd);
374
		}
375

    
376
		if ($g['booting'])
377
			echo "done\n";
378

    
379
	} else {
380
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
381
		killbypid("{$g['varrun_path']}/minicron.pid");
382

    
383
		captiveportal_radius_stop_all(true);
384

    
385
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
386

    
387
		/* unload ipfw */
388
		if (is_module_loaded("ipfw.ko"))		
389
			mwexec("/sbin/kldunload ipfw.ko");
390
		$listifs = get_configured_interface_list_by_realif();
391
		foreach ($listifs as $listrealif => $listif) {
392
			if (!empty($listrealif)) {
393
				if (does_interface_exist($listrealif)) {
394
					pfSense_interface_flags($listrealif, -IFF_IPFW_FILTER);
395
					$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
396
                        		if (!empty($carpif)) {
397
						$carpsif = explode(" ", $carpif);
398
						foreach ($carpsif as $cpcarp)
399
							pfSense_interface_flags($cpcarp, -IFF_IPFW_FILTER);
400
					}
401
				}
402
			}
403
		}
404
	}
405

    
406
	unlock($captiveportallck);
407
	
408
	return 0;
409
}
410

    
411
function captiveportal_rules_generate($cpif, &$cpiparray) {
412
	global $config, $g;
413

    
414
	$cprules =  "add 65291 set 1 allow pfsync from any to any\n";
415
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
416

    
417
	$cprules .= <<<EOD
418
# add 65300 set 1 skipto 65534 all from any to any not layer2
419
# layer 2: pass ARP
420
add 65301 set 1 pass layer2 mac-type arp
421
# pfsense requires for WPA
422
add 65302 set 1 pass layer2 mac-type 0x888e
423
add 65303 set 1 pass layer2 mac-type 0x88c7
424

    
425
# PPP Over Ethernet Discovery Stage
426
add 65304 set 1 pass layer2 mac-type 0x8863
427
# PPP Over Ethernet Session Stage
428
add 65305 set 1 pass layer2 mac-type 0x8864
429
# Allow WPA
430
add 65306 set 1 pass layer2 mac-type 0x888e
431

    
432
# layer 2: block anything else non-IP
433
add 65307 set 1 deny layer2 not mac-type ip
434

    
435
EOD;
436

    
437
	$rulenum = 65310;
438
	$ips = "255.255.255.255 ";
439
	foreach ($cpiparray as $cpip)
440
		$ips .= "or {$cpip} ";
441
	$ips = "{ {$ips} }";
442
	//# allow access to our DHCP server (which needs to be able to ping clients as well)
443
	$cprules .= "add {$rulenum} set 1 pass udp from any 68 to {$ips} 67 in \n";
444
	$rulenum++;
445
	$cprules .= "add {$rulenum} set 1 pass udp from any 68 to {$ips} 67 in \n";
446
	$rulenum++;
447
	$cprules .= "add {$rulenum} set 1 pass udp from {$ips} 67 to any 68 out \n";
448
	$rulenum++;
449
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
450
	$rulenum++;
451
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
452
	$rulenum++;
453
	//# allow access to our DNS forwarder
454
	$cprules .= "add {$rulenum} set 1 pass udp from any to {$ips} 53 in \n";
455
	$rulenum++;
456
	$cprules .= "add {$rulenum} set 1 pass udp from {$ips} 53 to any out \n";
457
	$rulenum++;
458
	# allow access to our web server
459
	$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} 8000 in \n";
460
	$rulenum++;
461
	$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} 8000 to any out \n";
462

    
463
	if (isset($config['captiveportal']['httpslogin'])) {
464
		$rulenum++;
465
		$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} 8001 in \n";
466
		$rulenum++;
467
		$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} 8001 to any out \n";
468
	}
469
	if (!empty($config['system']['webgui']['port']))
470
		$port = $config['system']['webgui']['port'];
471
	else if ($config['system']['webgui']['proto'] == "http")
472
		$port = 80;
473
	else
474
		$port = 443;
475
	$rulenum++;
476
	$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} {$port} in \n";
477
	$rulenum++;
478
	$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} {$port} to any out \n";
479
	$rulenum++;
480

    
481
	/* Allowed ips */
482
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
483
	$rulenum++;
484
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
485
	$rulenum++;
486
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
487
	$rulenum++;
488
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
489
	$rulenum++;
490
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
491
	$rulenum++;
492
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
493
	$rulenum++;
494
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
495
	$rulenum++;
496
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
497
	$rulenum++;
498

    
499
	/* Authenticated users rules. */
500
	if (isset($config['captiveportal']['peruserbw'])) {
501
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
502
		$rulenum++;
503
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
504
		$rulenum++;
505
	} else {
506
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
507
                $rulenum++;
508
                $cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
509
                $rulenum++;
510
	}
511
	
512
       $cprules .= <<<EOD
513

    
514
# redirect non-authenticated clients to captive portal
515
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
516
# let the responses from the captive portal web server back out
517
add 65532 set 1 pass tcp from any to any out
518
# block everything else
519
add 65533 set 1 deny all from any to any
520
# pass everything else on layer2
521
add 65534 set 1 pass all from any to any layer2
522

    
523
EOD;
524

    
525
    return $cprules;
526
}
527

    
528
/* remove clients that have been around for longer than the specified amount of time */
529
/* db file structure:
530
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
531

    
532
/* (password is in Base64 and only saved when reauthentication is enabled) */
533
function captiveportal_prune_old() {
534

    
535
    global $g, $config;
536

    
537
    /* check for expired entries */
538
    if ($config['captiveportal']['timeout'])
539
        $timeout = $config['captiveportal']['timeout'] * 60;
540
    else
541
        $timeout = 0;
542

    
543
    if ($config['captiveportal']['idletimeout'])
544
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
545
    else
546
        $idletimeout = 0;
547

    
548
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
549
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
550
        return;
551

    
552
    $captiveportallck = lock('captiveportal');
553

    
554
    /* read database */
555
    $cpdb = captiveportal_read_db();
556

    
557
    $radiusservers = captiveportal_get_radius_servers();
558

    
559
    /*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
560
     *  outside of the loop. Otherwise the loop would evaluate count() on every iteration
561
     *  and since $i would increase and count() would decrement they would meet before we
562
     *  had a chance to iterate over all accounts.
563
     */
564
    $unsetindexes = array();
565
    $no_users = count($cpdb);
566
    for ($i = 0; $i < $no_users; $i++) {
567

    
568
        $timedout = false;
569
        $term_cause = 1;
570

    
571
        /* hard timeout? */
572
        if ($timeout) {
573
            if ((time() - $cpdb[$i][0]) >= $timeout) {
574
                $timedout = true;
575
                $term_cause = 5; // Session-Timeout
576
            }
577
        }
578

    
579
        /* Session-Terminate-Time */
580
        if (!$timedout && !empty($cpdb[$i][9])) {
581
            if (time() >= $cpdb[$i][9]) {
582
                $timedout = true;
583
                $term_cause = 5; // Session-Timeout
584
            }
585
        }
586

    
587
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
588
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
589
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
590
        if (!$timedout && $idletimeout) {
591
            $lastact = captiveportal_get_last_activity($cpdb[$i][2]);
592
			/*  If the user has logged on but not sent any traffic they will never be logged out.
593
			 *  We "fix" this by setting lastact to the login timestamp. 
594
			 */
595
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
596
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
597
                $timedout = true;
598
                $term_cause = 4; // Idle-Timeout
599
                $stop_time = $lastact; // Entry added to comply with WISPr
600
            }
601
        }
602

    
603
	/* if vouchers are configured, activate session timeouts */
604
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
605
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
606
			$timedout = true;
607
			$term_cause = 5; // Session-Timeout
608
		}
609
	}
610

    
611
        /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
612
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
613
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
614
                $timedout = true;
615
                $term_cause = 5; // Session-Timeout
616
            }
617
        }
618

    
619
        if ($timedout) {
620
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
621
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
622
	    $unsetindexes[$i] = $i;
623
        }
624

    
625
        /* do periodic RADIUS reauthentication? */
626
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
627
            !empty($radiusservers)) {
628

    
629
            if (isset($config['captiveportal']['radacct_enable'])) {
630
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
631
                    /* stop and restart accounting */
632
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
633
                                           $cpdb[$i][4], // username
634
                                           $cpdb[$i][5], // sessionid
635
                                           $cpdb[$i][0], // start time
636
                                           $radiusservers,
637
                                           $cpdb[$i][2], // clientip
638
                                           $cpdb[$i][3], // clientmac
639
                                           10); // NAS Request
640
                    exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}");
641
                    exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}");
642
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
643
                                            $cpdb[$i][4], // username
644
                                            $cpdb[$i][5], // sessionid
645
                                            $radiusservers,
646
                                            $cpdb[$i][2], // clientip
647
                                            $cpdb[$i][3]); // clientmac
648
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
649
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
650
                                           $cpdb[$i][4], // username
651
                                           $cpdb[$i][5], // sessionid
652
                                           $cpdb[$i][0], // start time
653
                                           $radiusservers,
654
                                           $cpdb[$i][2], // clientip
655
                                           $cpdb[$i][3], // clientmac
656
                                           10, // NAS Request
657
                                           true); // Interim Updates
658
                }
659
            }
660

    
661
            /* check this user against RADIUS again */
662
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
663
                                          base64_decode($cpdb[$i][6]), // password
664
                                            $radiusservers,
665
                                          $cpdb[$i][2], // clientip
666
                                          $cpdb[$i][3], // clientmac
667
                                          $cpdb[$i][1]); // ruleno
668

    
669
            if ($auth_list['auth_val'] == 3) {
670
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
671
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
672
	        $unsetindexes[$i] = $i;
673
            }
674
        }
675
    }
676
    /* This is a kludge to overcome some php weirdness */
677
    foreach($unsetindexes as $unsetindex)
678
	unset($cpdb[$unsetindex]);
679

    
680
    /* write database */
681
    captiveportal_write_db($cpdb);
682

    
683
    unlock($captiveportallck);
684
}
685

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

    
689
	global $g, $config;
690

    
691
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
692

    
693
	/* this client needs to be deleted - remove ipfw rules */
694
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
695
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
696
							   $dbent[4], // username
697
							   $dbent[5], // sessionid
698
							   $dbent[0], // start time
699
							   $radiusservers,
700
							   $dbent[2], // clientip
701
							   $dbent[3], // clientmac
702
							   $term_cause, // Acct-Terminate-Cause
703
							   false,
704
							   $stop_time);
705
	}
706
	/* Delete client's ip entry from tables 3 and 4. */
707
	mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
708
	mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
709

    
710
	/* Release the ruleno so it can be reallocated to new clients. */
711
	captiveportal_free_ipfw_ruleno($dbent[1]);
712

    
713
	/* 
714
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
715
	* We could get an error if the pipe doesn't exist but everything should still be fine
716
	*/
717
	if (isset($config['captiveportal']['peruserbw'])) {
718
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
719
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
720
	}
721

    
722
	/* XXX: Redundant?! Ensure all pf(4) states are killed. */
723
	mwexec("pfctl -k {$dbent[2]}");
724
	mwexec("pfctl -K {$dbent[2]}");
725

    
726
}
727

    
728
/* remove a single client by ipfw rule number */
729
function captiveportal_disconnect_client($id,$term_cause = 1) {
730

    
731
	global $g, $config;
732

    
733
	$captiveportallck = lock('captiveportal');
734

    
735
	/* read database */
736
	$cpdb = captiveportal_read_db();
737
	$radiusservers = captiveportal_get_radius_servers();
738

    
739
	/* find entry */
740
	$tmpindex = 0;
741
	$cpdbcount = count($cpdb);
742
	for ($i = 0; $i < $cpdbcount; $i++) {
743
		if ($cpdb[$i][1] == $id) {
744
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
745
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
746
			unset($cpdb[$i]);
747
			break;
748
		}
749
	}		
750

    
751
	/* write database */
752
	captiveportal_write_db($cpdb);
753

    
754
	unlock($captiveportallck);
755
}
756

    
757
/* send RADIUS acct stop for all current clients */
758
function captiveportal_radius_stop_all($lock = false) {
759
	global $g, $config;
760

    
761
	if (!isset($config['captiveportal']['radacct_enable']))
762
		return;
763

    
764
	if (!$lock)
765
		$captiveportallck = lock('captiveportal');
766

    
767
	$cpdb = captiveportal_read_db();
768

    
769
	$radiusservers = captiveportal_get_radius_servers();
770
	if (!empty($radiusservers)) {
771
		for ($i = 0; $i < count($cpdb); $i++) {
772
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
773
								   $cpdb[$i][4], // username
774
								   $cpdb[$i][5], // sessionid
775
								   $cpdb[$i][0], // start time
776
								   $radiusservers,
777
								   $cpdb[$i][2], // clientip
778
								   $cpdb[$i][3], // clientmac
779
								   7); // Admin Reboot
780
		}
781
	}
782
	if (!$lock)
783
		unlock($captiveportallck);
784
}
785

    
786
function captiveportal_passthrumac_configure_entry($macent) {
787
	$rules = "";
788
        $enBwup = isset($macent['bw_up']);
789
        $enBwdown = isset($macent['bw_down']);
790
	$actionup = "allow";
791
	$actiondown = "allow";
792

    
793
        if ($enBwup && $enBwdown)
794
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
795
        else
796
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
797

    
798
	if ($enBwup) {
799
                $bw_up = $ruleno + 20000;
800
                $rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
801
		$actionup = "pipe {$bw_up}";
802
        }
803
        if ($enBwdown) {
804
		$bw_down = $ruleno + 20001;
805
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
806
		$actiondown = "pipe {$bw_down}";
807
        }
808
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC {$macent['mac']} any\n";
809
	$ruleno++;
810
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC any {$macent['mac']}\n";
811

    
812
	return $rules;
813
}
814

    
815
function captiveportal_passthrumac_configure($lock = false) {
816
	global $config, $g;
817

    
818
	$rules = "";
819

    
820
	if (is_array($config['captiveportal']['passthrumac'])) {
821
		$macdb = array();
822
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
823
			$rules .= captiveportal_passthrumac_configure_entry($macent);
824
			$macdb[$macent['mac']]['active']  = true;
825

    
826
		}
827
	}
828

    
829
	return $rules;
830
}
831

    
832
function captiveportal_passthrumac_findbyname($username) {
833
	global $config;
834

    
835
	if (is_array($config['captiveportal']['passthrumac'])) {
836
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
837
			if ($macent['username'] == $username)
838
				return $macent;
839
		}
840
	}
841
	return NULL;
842
}
843

    
844
/* 
845
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
846
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
847
 */
848
function captiveportal_allowedip_configure_entry($ipent) {
849

    
850
	$rules = "";
851
	$enBwup = isset($ipent['bw_up']);
852
	$enBwdown = isset($ipent['bw_down']);
853
	$bw_up = "";
854
        $bw_down = "";
855
        $tablein = array();
856
        $tableout = array();
857

    
858
	if ($enBwup && $enBwdown)
859
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
860
	else
861
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
862

    
863
        if ($ipent['dir'] == "from") {
864
        	if ($enBwup)
865
                	$tablein[] = 5;
866
                else
867
                	$tablein[] = 3;
868
                if ($enBwdown)
869
                        $tableout[] = 6;
870
                else
871
                        $tableout[] = 4;
872
        } else if ($ipent['dir'] == "to") {
873
                if ($enBwup)
874
                	$tablein[] = 9;
875
                else
876
                        $tablein[] = 7;
877
                if ($enBwdown)
878
                        $tableout[] = 10;
879
                else
880
                        $tableout[] = 8;
881
        } else if ($ipent['dir'] == "both") {
882
                if ($enBwup) {
883
                        $tablein[] = 5;
884
                        $tablein[] = 9;
885
                } else {
886
                        $tablein[] = 3;
887
                        $tablein[] = 7;
888
                }
889
        	if ($enBwdown) {
890
                        $tableout[] = 6;
891
                        $tableout[] = 10;
892
                } else {
893
                        $tableout[] = 4;
894
                	$tableout[] = 8;
895
                }
896
        }
897
        if ($enBwup) {
898
                $bw_up = $ruleno + 20000;
899
        	$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
900
        }
901
	foreach ($tablein as $table)
902
               $rules .= "table {$table} add {$ipent['ip']} {$bw_up}\n";
903
        if ($enBwdown) {
904
               $bw_down = $ruleno + 20001;
905
               $rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
906
        }
907
        foreach ($tableout as $table)
908
        	$rules .= "table {$table} add {$ipent['ip']} {$bw_down}\n";
909

    
910
	return $rules;
911
}
912

    
913
function captiveportal_allowedip_configure() {
914
	global $config, $g;
915

    
916
	$rules = "";
917
	if (is_array($config['captiveportal']['allowedip'])) {
918
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
919
			$rules .= captiveportal_allowedip_configure_entry($ipent);
920
		}
921
	}
922

    
923
	return $rules;
924
}
925

    
926
/* get last activity timestamp given client IP address */
927
function captiveportal_get_last_activity($ip) {
928

    
929
	$ipfwoutput = "";
930

    
931
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
932
	/* Reading only from one of the tables is enough of approximation. */
933
	if ($ipfwoutput[0]) {
934
		$ri = explode(" ", $ipfwoutput[0]);
935
		if ($ri[4])
936
			return $ri[4];
937
	}
938

    
939
	return 0;
940
}
941

    
942
/* read RADIUS servers into array */
943
function captiveportal_get_radius_servers() {
944

    
945
        global $g;
946

    
947
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
948
                $radiusservers = array();
949
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
950
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
951
		if ($cpradiusdb)
952
		foreach($cpradiusdb as $cpradiusentry) {
953
                	$line = trim($cpradiusentry);
954
                        if ($line) {
955
                        	$radsrv = array();
956
                                list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
957
                        	$radiusservers[] = $radsrv;
958
                        }
959
		}
960

    
961
		return $radiusservers;
962
        }
963

    
964
        return false;
965
}
966

    
967
/* log successful captive portal authentication to syslog */
968
/* part of this code from php.net */
969
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
970
	$message = trim($message);
971
	// Log it
972
	if (!$message)
973
		$message = "$status: $user, $mac, $ip";
974
	else
975
		$message = "$status: $user, $mac, $ip, $message";
976
	captiveportal_syslog($message);
977
	closelog();
978
}
979

    
980
/* log simple messages to syslog */
981
function captiveportal_syslog($message) {
982
	define_syslog_variables();
983
	$message = trim($message);
984
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
985
	// Log it
986
	syslog(LOG_INFO, $message);
987
	closelog();
988
}
989

    
990
function radius($username,$password,$clientip,$clientmac,$type) {
991
    global $g, $config;
992

    
993
    /* Start locking from the beginning of an authentication session */
994
    $captiveportallck = lock('captiveportal');
995

    
996
    $ruleno = captiveportal_get_next_ipfw_ruleno();
997

    
998
    /* If the pool is empty, return appropriate message and fail authentication */
999
    if (is_null($ruleno)) {
1000
        $auth_list = array();
1001
        $auth_list['auth_val'] = 1;
1002
        $auth_list['error'] = "System reached maximum login capacity";
1003
        unlock($captiveportallck);
1004
        return $auth_list;
1005
    }
1006

    
1007
    /*
1008
     * Drop the lock since radius takes some time to finish.
1009
     * The implementation is reentrant so we gain speed with this.
1010
     */
1011
    unlock($captiveportallck);
1012

    
1013
    $radiusservers = captiveportal_get_radius_servers();
1014

    
1015
    $auth_list = RADIUS_AUTHENTICATION($username,
1016
                    $password,
1017
                    $radiusservers,
1018
                    $clientip,
1019
                    $clientmac,
1020
                    $ruleno);
1021

    
1022
    $captiveportallck = lock('captiveportal');
1023

    
1024
    if ($auth_list['auth_val'] == 2) {
1025
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1026
        $sessionid = portal_allow($clientip,
1027
                    $clientmac,
1028
                    $username,
1029
                    $password,
1030
                    $auth_list,
1031
                    $ruleno);
1032
    }
1033

    
1034
    unlock($captiveportallck);
1035

    
1036
    return $auth_list;
1037

    
1038
}
1039

    
1040
/* read captive portal DB into array */
1041
function captiveportal_read_db() {
1042

    
1043
        global $g;
1044

    
1045
        $cpdb = array();
1046
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1047
        if ($fd) {
1048
                while (!feof($fd)) {
1049
                        $line = trim(fgets($fd));
1050
                        if ($line) {
1051
                                $cpdb[] = explode(",", $line);
1052
                        }
1053
                }
1054
                fclose($fd);
1055
        }
1056
        return $cpdb;
1057
}
1058

    
1059
/* write captive portal DB */
1060
function captiveportal_write_db($cpdb) {
1061
                 
1062
        global $g;
1063
                
1064
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1065
        if ($fd) { 
1066
                foreach ($cpdb as $cpent) {
1067
                        fwrite($fd, join(",", $cpent) . "\n");
1068
                }       
1069
                fclose($fd);
1070
        }       
1071
}
1072

    
1073
function captiveportal_write_elements() {
1074
    global $g, $config;
1075
    
1076
    /* delete any existing elements */
1077
    if (is_dir($g['captiveportal_element_path'])) {
1078
        $dh = opendir($g['captiveportal_element_path']);
1079
        while (($file = readdir($dh)) !== false) {
1080
            if ($file != "." && $file != "..")
1081
                unlink($g['captiveportal_element_path'] . "/" . $file);
1082
        }
1083
        closedir($dh);
1084
    } else {
1085
        @mkdir($g['captiveportal_element_path']);
1086
    }
1087
    
1088
	if (is_array($config['captiveportal']['element'])) {
1089
		conf_mount_rw();
1090
		foreach ($config['captiveportal']['element'] as $data) {
1091
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1092
			if (!$fd) {
1093
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1094
				return 1;
1095
			}
1096
			$decoded = base64_decode($data['content']);
1097
			fwrite($fd,$decoded);
1098
			fclose($fd);
1099
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1100
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1101
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1102
		}
1103
		conf_mount_ro();
1104
	}
1105
    
1106
    return 0;
1107
}
1108

    
1109
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1110
	global $g;
1111

    
1112
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1113
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1114
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1115
}
1116

    
1117
/*
1118
 * This function will calculate the lowest free firewall ruleno
1119
 * within the range specified based on the actual logged on users
1120
 *
1121
 */
1122
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1123
	global $config, $g;
1124

    
1125
	if(!isset($config['captiveportal']['enable']))
1126
		return NULL;
1127

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

    
1157
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1158
	global $config, $g;
1159

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

    
1163
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1164
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1165
		$rules[$ruleno] = false;
1166
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1167
			$rules[++$ruleno] = false;
1168
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1169
	}
1170
}
1171

    
1172
function captiveportal_get_ipfw_passthru_ruleno($value) {
1173
	global $config, $g;
1174

    
1175
	if(!isset($config['captiveportal']['enable']))
1176
                return NULL;
1177

    
1178
        if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1179
                $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1180
		$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`);
1181
		if ($rules[$ruleno])
1182
			return $ruleno;
1183
        }
1184

    
1185
	return NULL;
1186
}
1187

    
1188
/**
1189
 * This function will calculate the traffic produced by a client
1190
 * based on its firewall rule
1191
 *
1192
 * Point of view: NAS
1193
 *
1194
 * Input means: from the client
1195
 * Output means: to the client
1196
 *
1197
 */
1198

    
1199
function getVolume($ip) {
1200

    
1201
    $volume = array();
1202

    
1203
    // Initialize vars properly, since we don't want NULL vars
1204
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1205

    
1206
    // Ingress
1207
    $ipfwin = "";
1208
    $ipfwout = "";
1209
    $matchesin = "";
1210
    $matchesout = "";
1211
    exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1212
    if ($ipfwin[0]) {
1213
		$ipfwin = split(" ", $ipfwin[0]);
1214
		$volume['input_pkts'] = $ipfwin[2];
1215
		$volume['input_bytes'] = $ipfwin[3];
1216
    }
1217

    
1218
    exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1219
    if ($ipfwout[0]) {
1220
        $ipfwout = split(" ", $ipfwout[0]);
1221
        $volume['output_pkts'] = $ipfwout[2];
1222
        $volume['output_bytes'] = $ipfwout[3];
1223
    }
1224

    
1225
    return $volume;
1226
}
1227

    
1228
/**
1229
 * Get the NAS-Identifier
1230
 *
1231
 * We will use our local hostname to make up the nas_id
1232
 */
1233
function getNasID()
1234
{
1235
    $nasId = "";
1236
    exec("/bin/hostname", $nasId);
1237
    if(!$nasId[0])
1238
        $nasId[0] = "{$g['product_name']}";
1239
    return $nasId[0];
1240
}
1241

    
1242
/**
1243
 * Get the NAS-IP-Address based on the current wan address
1244
 *
1245
 * Use functions in interfaces.inc to find this out
1246
 *
1247
 */
1248

    
1249
function getNasIP()
1250
{
1251
    $nasIp = get_interface_ip();
1252
    if(!$nasIp)
1253
        $nasIp = "0.0.0.0";
1254
    return $nasIp;
1255
}
1256

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

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

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

    
1278
	return false;
1279
}
1280

    
1281
?>
(6-6/50)