Project

General

Profile

Download (41.5 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 gettext("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(gettext("Error: cannot open radius DB file in captiveportal_configure().%s"), "\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
			printf(gettext("done%s"), "\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
	$ipcount = 0;
425
	foreach ($cpips as $cpip) {
426
		if($ipcount == 0) {
427
			$ips = "{$cpip} ";
428
		} else {
429
			$ips .= "or {$cpip} ";
430
		}
431
		$ipcount++;
432
	}
433
	$ips = "{ {$ips} }";
434
	$cprules .= "add {$rulenum} set 1 pass ip from any to {$ips} in\n";
435
	$rulenum++;
436
	$cprules .= "add {$rulenum} set 1 pass ip from {$ips} to any out\n";
437
	$rulenum++;
438
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
439
	$rulenum++;
440
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
441
	$rulenum++;
442
	/* Allowed ips */
443
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
444
	$rulenum++;
445
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
446
	$rulenum++;
447
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
448
	$rulenum++;
449
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
450
	$rulenum++;
451
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
452
	$rulenum++;
453
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
454
	$rulenum++;
455
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
456
	$rulenum++;
457
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
458
	$rulenum++;
459

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

    
475
# redirect non-authenticated clients to captive portal
476
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
477
# let the responses from the captive portal web server back out
478
add 65532 set 1 pass tcp from any to any out
479
# block everything else
480
add 65533 set 1 deny all from any to any
481
# pass everything else on layer2
482
add 65534 set 1 pass all from any to any layer2
483

    
484
EOD;
485

    
486
	/* generate passthru mac database */
487
	$cprules .= captiveportal_passthrumac_configure(true);
488
	$cprules .= "\n";
489
	/* allowed ipfw rules to make allowed ip work */
490
	$cprules .= captiveportal_allowedip_configure();
491

    
492
	/* load rules */
493
	if ($reinit == true)
494
		$cprules = "table all flush\nflush\n{$cprules}";
495
	else {
496
		$tmprules = "table 3 flush\n";
497
		$tmprules .= "table 4 flush\n";
498
		$tmprules .= "table 5 flush\n";
499
		$tmprules .= "table 6 flush\n";
500
		$tmprules .= "table 7 flush\n";
501
		$tmprules .= "table 8 flush\n";
502
		$tmprules .= "table 9 flush\n";
503
		$tmprules .= "table 10 flush\n";
504
		$tmprules .= "flush\n";
505
		$cprules = "{$tmprules}\n{$cprules}";
506
	}
507

    
508
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
509
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
510
	@unlink("{$g['tmp_path']}/ipfw.cp.rules");
511

    
512
	if ($reinit == false)
513
		unlock($captiveportallck);
514

    
515

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

    
519
	return $cprules;
520
}
521

    
522
/* remove clients that have been around for longer than the specified amount of time */
523
/* db file structure:
524
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
525

    
526
/* (password is in Base64 and only saved when reauthentication is enabled) */
527
function captiveportal_prune_old() {
528

    
529
    global $g, $config;
530

    
531
    /* check for expired entries */
532
    if ($config['captiveportal']['timeout'])
533
        $timeout = $config['captiveportal']['timeout'] * 60;
534
    else
535
        $timeout = 0;
536

    
537
    if ($config['captiveportal']['idletimeout'])
538
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
539
    else
540
        $idletimeout = 0;
541

    
542
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
543
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
544
        return;
545

    
546
    $captiveportallck = lock('captiveportal');
547

    
548
    /* read database */
549
    $cpdb = captiveportal_read_db();
550

    
551
    $radiusservers = captiveportal_get_radius_servers();
552

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

    
562
        $timedout = false;
563
        $term_cause = 1;
564

    
565
        /* hard timeout? */
566
        if ($timeout) {
567
            if ((time() - $cpdb[$i][0]) >= $timeout) {
568
                $timedout = true;
569
                $term_cause = 5; // Session-Timeout
570
            }
571
        }
572

    
573
        /* Session-Terminate-Time */
574
        if (!$timedout && !empty($cpdb[$i][9])) {
575
            if (time() >= $cpdb[$i][9]) {
576
                $timedout = true;
577
                $term_cause = 5; // Session-Timeout
578
            }
579
        }
580

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

    
597
	/* if vouchers are configured, activate session timeouts */
598
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
599
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
600
			$timedout = true;
601
			$term_cause = 5; // Session-Timeout
602
		}
603
	}
604

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

    
613
        if ($timedout) {
614
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
615
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
616
	    $unsetindexes[$i] = $i;
617
        }
618

    
619
        /* do periodic RADIUS reauthentication? */
620
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
621
            !empty($radiusservers)) {
622

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

    
655
            /* check this user against RADIUS again */
656
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
657
                                          base64_decode($cpdb[$i][6]), // password
658
                                            $radiusservers,
659
                                          $cpdb[$i][2], // clientip
660
                                          $cpdb[$i][3], // clientmac
661
                                          $cpdb[$i][1]); // ruleno
662

    
663
            if ($auth_list['auth_val'] == 3) {
664
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
665
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
666
	        $unsetindexes[$i] = $i;
667
            }
668
        }
669
    }
670
    /* This is a kludge to overcome some php weirdness */
671
    foreach($unsetindexes as $unsetindex)
672
	unset($cpdb[$unsetindex]);
673

    
674
    /* write database */
675
    captiveportal_write_db($cpdb);
676

    
677
    unlock($captiveportallck);
678
}
679

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

    
683
	global $g, $config;
684

    
685
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
686

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

    
704
	/* Release the ruleno so it can be reallocated to new clients. */
705
	captiveportal_free_ipfw_ruleno($dbent[1]);
706

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

    
716
	/* XXX: Redundant?! Ensure all pf(4) states are killed. */
717
	mwexec("pfctl -k {$dbent[2]}");
718
	mwexec("pfctl -K {$dbent[2]}");
719

    
720
}
721

    
722
/* remove a single client by ipfw rule number */
723
function captiveportal_disconnect_client($id,$term_cause = 1) {
724

    
725
	global $g, $config;
726

    
727
	$captiveportallck = lock('captiveportal');
728

    
729
	/* read database */
730
	$cpdb = captiveportal_read_db();
731
	$radiusservers = captiveportal_get_radius_servers();
732

    
733
	/* find entry */
734
	$tmpindex = 0;
735
	$cpdbcount = count($cpdb);
736
	for ($i = 0; $i < $cpdbcount; $i++) {
737
		if ($cpdb[$i][1] == $id) {
738
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
739
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
740
			unset($cpdb[$i]);
741
			break;
742
		}
743
	}		
744

    
745
	/* write database */
746
	captiveportal_write_db($cpdb);
747

    
748
	unlock($captiveportallck);
749
}
750

    
751
/* send RADIUS acct stop for all current clients */
752
function captiveportal_radius_stop_all($lock = false) {
753
	global $g, $config;
754

    
755
	if (!isset($config['captiveportal']['radacct_enable']))
756
		return;
757

    
758
	if (!$lock)
759
		$captiveportallck = lock('captiveportal');
760

    
761
	$cpdb = captiveportal_read_db();
762

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

    
780
function captiveportal_passthrumac_configure_entry($macent) {
781
	$rules = "";
782
        $enBwup = isset($macent['bw_up']);
783
        $enBwdown = isset($macent['bw_down']);
784
	$actionup = "allow";
785
	$actiondown = "allow";
786

    
787
        if ($enBwup && $enBwdown)
788
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
789
        else
790
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
791

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

    
806
	return $rules;
807
}
808

    
809
function captiveportal_passthrumac_configure($lock = false) {
810
	global $config, $g;
811

    
812
	$rules = "";
813

    
814
	if (is_array($config['captiveportal']['passthrumac'])) {
815
		$macdb = array();
816
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
817
			$rules .= captiveportal_passthrumac_configure_entry($macent);
818
			$macdb[$macent['mac']]['active']  = true;
819

    
820
		}
821
	}
822

    
823
	return $rules;
824
}
825

    
826
function captiveportal_passthrumac_findbyname($username) {
827
	global $config;
828

    
829
	if (is_array($config['captiveportal']['passthrumac'])) {
830
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
831
			if ($macent['username'] == $username)
832
				return $macent;
833
		}
834
	}
835
	return NULL;
836
}
837

    
838
/* 
839
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
840
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
841
 */
842
function captiveportal_allowedip_configure_entry($ipent) {
843

    
844
	$rules = "";
845
	$enBwup = isset($ipent['bw_up']);
846
	$enBwdown = isset($ipent['bw_down']);
847
	$bw_up = "";
848
        $bw_down = "";
849
        $tablein = array();
850
        $tableout = array();
851

    
852
	if ($enBwup && $enBwdown)
853
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
854
	else
855
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
856

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

    
907
	return $rules;
908
}
909

    
910
function captiveportal_allowedip_configure() {
911
	global $config, $g;
912

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

    
920
	return $rules;
921
}
922

    
923
/* get last activity timestamp given client IP address */
924
function captiveportal_get_last_activity($ip) {
925

    
926
	$ipfwoutput = "";
927

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

    
936
	return 0;
937
}
938

    
939
/* read RADIUS servers into array */
940
function captiveportal_get_radius_servers() {
941

    
942
        global $g;
943

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

    
958
		return $radiusservers;
959
        }
960

    
961
        return false;
962
}
963

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

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

    
987
function radius($username,$password,$clientip,$clientmac,$type) {
988
    global $g, $config;
989

    
990
    /* Start locking from the beginning of an authentication session */
991
    $captiveportallck = lock('captiveportal');
992

    
993
    $ruleno = captiveportal_get_next_ipfw_ruleno();
994

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

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

    
1010
    $radiusservers = captiveportal_get_radius_servers();
1011

    
1012
    $auth_list = RADIUS_AUTHENTICATION($username,
1013
                    $password,
1014
                    $radiusservers,
1015
                    $clientip,
1016
                    $clientmac,
1017
                    $ruleno);
1018

    
1019
    $captiveportallck = lock('captiveportal');
1020

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

    
1031
    unlock($captiveportallck);
1032

    
1033
    return $auth_list;
1034

    
1035
}
1036

    
1037
/* read captive portal DB into array */
1038
function captiveportal_read_db() {
1039

    
1040
        global $g;
1041

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

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

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

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

    
1105
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1106
	global $g;
1107

    
1108
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1109
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1110
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1111
}
1112

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

    
1121
	if(!isset($config['captiveportal']['enable']))
1122
		return NULL;
1123

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

    
1153
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1154
	global $config, $g;
1155

    
1156
	if(!isset($config['captiveportal']['enable']))
1157
		return NULL;
1158

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

    
1168
function captiveportal_get_ipfw_passthru_ruleno($value) {
1169
	global $config, $g;
1170

    
1171
	if(!isset($config['captiveportal']['enable']))
1172
                return NULL;
1173

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

    
1181
	return NULL;
1182
}
1183

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

    
1195
function getVolume($ip) {
1196

    
1197
    $volume = array();
1198

    
1199
    // Initialize vars properly, since we don't want NULL vars
1200
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1201

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

    
1214
    exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1215
    if ($ipfwout[0]) {
1216
        $ipfwout = split(" ", $ipfwout[0]);
1217
        $volume['output_pkts'] = $ipfwout[2];
1218
        $volume['output_bytes'] = $ipfwout[3];
1219
    }
1220

    
1221
    return $volume;
1222
}
1223

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

    
1238
/**
1239
 * Get the NAS-IP-Address based on the current wan address
1240
 *
1241
 * Use functions in interfaces.inc to find this out
1242
 *
1243
 */
1244

    
1245
function getNasIP()
1246
{
1247
	global $config;
1248

    
1249
	if (empty($config['captiveportal']['radiussrcip_attribute']))
1250
    		$nasIp = get_interface_ip();
1251
	else {
1252
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1253
                        $nasIp = $config['captiveportal']['radiussrcip_attribute'];
1254
                else
1255
                        $nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1256
	}
1257
		
1258
    	if(!is_ipaddr($nasIp))
1259
        	$nasIp = "0.0.0.0";
1260

    
1261
	return $nasIp;
1262
}
1263

    
1264
function portal_ip_from_client_ip($cliip) {
1265
	global $config;
1266

    
1267
	$interfaces = explode(",", $config['captiveportal']['interface']);
1268
	foreach ($interfaces as $cpif) {
1269
		$ip = get_interface_ip($cpif);
1270
		$sn = get_interface_subnet($cpif);
1271
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1272
			return $ip;
1273
	}
1274

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

    
1285
	return false;
1286
}
1287

    
1288
?>
(6-6/53)