Project

General

Profile

Download (20.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	captiveportal.inc
4
	part of m0n0wall (http://m0n0.ch/wall)
5
	
6
	Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>.
7
	All rights reserved.
8
	
9
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11
	
12
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14
	
15
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18
	
19
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29

    
30
	This version of captiveportal.inc has been modified by Rob Parker
31
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
32
	via returned RADIUS attributes. This page has been modified to delete any
33
	added rules which may have been created by other per-user code (index.php, etc).
34
	These changes are (c) 2004 Keycom PLC.
35
*/
36
	
37
/* include all configuration functions */
38
require_once("functions.inc");
39
require_once("radius_authentication.inc");
40
require_once("radius_accounting.inc") ;
41

    
42
function captiveportal_configure() {
43
	global $config, $g;
44
	
45
	if (isset($config['captiveportal']['enable']) &&
46
		(($config['captiveportal']['interface'] == "lan") ||
47
			isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) {
48
	
49
		if ($g['booting'])
50
			echo "Starting captive portal... ";
51
		
52
		/* kill any running mini_httpd */
53
		killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
54
		killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
55
		
56
		/* kill any running minicron */
57
		killbypid("{$g['varrun_path']}/minicron.pid");
58
		
59
		/* generate ipfw rules */
60
		$cprules = captiveportal_rules_generate();
61
		
62
		/* make sure ipfw is loaded */
63
		mwexec("/sbin/kldload ipfw");
64
		
65
		/* stop accounting on all clients */
66
		captiveportal_radius_stop_all();
67

    
68
		/* remove old information */
69
		unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule");
70
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
71
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
72
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
73
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
74
		
75
		/* write portal page */
76
		if ($config['captiveportal']['page']['htmltext'])
77
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
78
		else {
79
			/* example/template page */
80
			$htmltext = <<<EOD
81
<html>
82
<head>
83
<title>pfSense captive portal</title>
84
</head>
85
<body>
86
<h2>pfSense captive portal</h2>
87
<p>This is the default captive portal page. Please upload your own custom HTML file on the <em>Services: Captive portal</em> screen in the pfSense webConfigurator.</p>
88
<form method="post" action="\$PORTAL_ACTION\$">
89
  <input name="accept" type="submit" value="Continue">
90
  <input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
91
</form>
92
</body>
93
</html>
94

    
95
EOD;
96
		}
97

    
98
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
99
		if ($fd) {
100
			fwrite($fd, $htmltext);
101
			fclose($fd);	
102
		}
103
		
104
		/* write error page */
105
		if ($config['captiveportal']['page']['errtext'])
106
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
107
		else {
108
			/* example page */
109
			$errtext = <<<EOD
110
<html>
111
<head>
112
<title>Authentication error</title>
113
</head>
114
<body>
115
<font color="#cc0000"><h2>Authentication error</h2></font>
116
<b>
117
Username and/or password invalid.
118
<br><br>
119
<a href="javascript:history.back()">Go back</a>
120
</b>
121
</body>
122
</html>
123

    
124
EOD;
125
		}
126

    
127
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
128
		if ($fd) {
129
			fwrite($fd, $errtext);
130
			fclose($fd);	
131
		}
132

    
133
		/* load rules */
134
		mwexec("/sbin/ipfw -f delete set 1");
135
		mwexec("/sbin/ipfw -f delete set 2");
136
		mwexec("/sbin/ipfw -f delete set 3");
137
		
138
		/* XXX - seems like ipfw cannot accept rules directly on stdin,
139
		   so we have to write them to a temporary file first */
140
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
141
		if (!$fd) {
142
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
143
			return 1;
144
		}
145
			
146
		fwrite($fd, $cprules);
147
		fclose($fd);
148
		
149
		mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules");
150
		
151
		unlink("{$g['tmp_path']}/ipfw.cp.rules");
152
		
153
		/* filter on layer2 as well so we can check MAC addresses */
154
		mwexec("/sbin/sysctl net.link.ether.ipfw=1");
155
		
156
		chdir($g['captiveportal_path']);
157
		
158
		/* start web server */
159
		mwexec("/usr/local/sbin/mini_httpd -a -M 0 -u root -maxproc 16" .
160
			" -p 8000 -i {$g['varrun_path']}/mini_httpd.cp.pid");
161
		
162
		/* fire up another one for HTTPS if requested */
163
		if (isset($config['captiveportal']['httpslogin']) &&
164
			$config['captiveportal']['certificate'] && $config['captiveportal']['private-key']) {
165
			
166
			$cert = base64_decode($config['captiveportal']['certificate']);
167
			$key = base64_decode($config['captiveportal']['private-key']);
168
			
169
			$fd = fopen("{$g['varetc_path']}/cert-portal.pem", "w");
170
			if (!$fd) {
171
				printf("Error: cannot open cert-portal.pem in system_webgui_start().\n");
172
				return 1;
173
			}
174
			chmod("{$g['varetc_path']}/cert-portal.pem", 0600);
175
			fwrite($fd, $cert);
176
			fwrite($fd, "\n");
177
			fwrite($fd, $key);
178
			fclose($fd);
179
			
180
			mwexec("/usr/local/sbin/mini_httpd -S -a -M 0 -E {$g['varetc_path']}/cert-portal.pem" .
181
				" -u root -maxproc 16 -p 8001" .
182
				" -i {$g['varrun_path']}/mini_httpd.cps.pid");
183
		}
184
			
185
		/* start pruning process (interval = 60 seconds) */
186
		mwexec("/usr/local/bin/minicron 60 {$g['varrun_path']}/minicron.pid " .
187
			"/etc/rc.prunecaptiveportal");
188
		
189
		/* generate passthru mac database */
190
		captiveportal_passthrumac_configure();
191
		/* create allowed ip database and insert ipfw rules to make it so */
192
		captiveportal_allowedip_configure();
193

    
194
		/* generate radius server database */
195
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
196
				($config['captiveportal']['auth_method'] == "radius"))) {
197
			$radiusip = $config['captiveportal']['radiusip'];
198

    
199
			if ($config['captiveportal']['radiusport'])
200
				$radiusport = $config['captiveportal']['radiusport'];
201
			else
202
				$radiusport = 1812;
203

    
204
			if ($config['captiveportal']['radiusacctport'])
205
				$radiusacctport = $config['captiveportal']['radiusacctport'];
206
			else
207
				$radiusacctport = 1813;
208

    
209
			$radiuskey = $config['captiveportal']['radiuskey'];
210

    
211
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
212
			if (!$fd) {
213
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
214
				return 1;
215
			} else {
216
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
217
			}
218
			fclose($fd);
219
		}
220

    
221
		if ($g['booting'])
222
			echo "done\n";
223
		
224
	} else {
225
		killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
226
		killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
227
		killbypid("{$g['varrun_path']}/minicron.pid");
228

    
229
		captiveportal_radius_stop_all();
230

    
231
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
232

    
233
		if (!isset($config['shaper']['enable'])) {
234
			/* unload ipfw */
235
			mwexec("/sbin/kldunload ipfw");
236
		} else {
237
			/* shaper is on - just remove our rules */
238
			mwexec("/sbin/ipfw -f delete set 1");
239
			mwexec("/sbin/ipfw -f delete set 2");
240
			mwexec("/sbin/ipfw -f delete set 3");
241
		}
242
	}
243
	
244
	return 0;
245
}
246

    
247
function captiveportal_rules_generate() {
248
	global $config, $g;
249
	
250
	$cpifn = $config['captiveportal']['interface'];
251
	$cpif = $config['interfaces'][$cpifn]['if'];
252
	$cpip = $config['interfaces'][$cpifn]['ipaddr'];
253

    
254
	/* note: the captive portal daemon inserts all pass rules for authenticated
255
	   clients as skipto 50000 rules to make traffic shaping work */
256

    
257
	$cprules = "";
258
	
259
	/* captive portal on LAN interface? */
260
	if ($cpifn == "lan") {
261
		/* add anti-lockout rules */
262
		$cprules .= <<<EOD
263
add 500 set 1 pass all from $cpip to any out via $cpif
264
add 501 set 1 pass all from any to $cpip in via $cpif
265

    
266
EOD;
267
	}
268

    
269
	$cprules .= <<<EOD
270
# skip to traffic shaper if not on captive portal interface
271
add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif
272
# pass all layer2 traffic on other interfaces
273
add 1001 set 1 pass layer2 not via $cpif
274

    
275
# layer 2: pass ARP
276
add 1100 set 1 pass layer2 mac-type arp
277
# layer 2: block anything else non-IP
278
add 1101 set 1 deny layer2 not mac-type ip
279
# layer 2: check if MAC addresses of authenticated clients are correct
280
add 1102 set 1 skipto 20000 layer2
281

    
282
# allow access to our DHCP server (which needs to be able to ping clients as well)
283
add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in
284
add 1201 set 1 pass udp from any 68 to $cpip 67 in
285
add 1202 set 1 pass udp from $cpip 67 to any 68 out
286
add 1203 set 1 pass icmp from $cpip to any out icmptype 8
287
add 1204 set 1 pass icmp from any to $cpip in icmptype 0
288

    
289
# allow access to our DNS forwarder
290
add 1300 set 1 pass udp from any to $cpip 53 in
291
add 1301 set 1 pass udp from $cpip 53 to any out
292

    
293
# allow access to our web server
294
add 1302 set 1 pass tcp from any to $cpip 8000 in
295
add 1303 set 1 pass tcp from $cpip 8000 to any out
296

    
297
EOD;
298

    
299
	if (isset($config['captiveportal']['httpslogin'])) {
300
		$cprules .= <<<EOD
301
add 1304 set 1 pass tcp from any to $cpip 8001 in
302
add 1305 set 1 pass tcp from $cpip 8001 to any out
303

    
304
EOD;
305
	}
306
	
307
	$cprules .= <<<EOD
308

    
309
# ... 10000-19899: rules per authenticated client go here...
310

    
311
# redirect non-authenticated clients to captive portal
312
add 19900 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in
313
# let the responses from the captive portal web server back out
314
add 19901 set 1 pass tcp from any 80 to any out
315
# block everything else
316
add 19902 set 1 deny all from any to any
317

    
318
# ... 20000-29899: layer2 block rules per authenticated client go here...
319

    
320
# pass everything else on layer2
321
add 29900 set 1 pass all from any to any layer2
322

    
323
EOD;
324

    
325
	return $cprules;
326
}
327

    
328
/* remove clients that have been around for longer than the specified amount of time */
329
/* db file structure: timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password */
330
/* (password is in Base64 and only saved when reauthentication is enabled) */
331
function captiveportal_prune_old() {
332
	
333
	global $g, $config;
334
	
335
	/* check for expired entries */
336
	if ($config['captiveportal']['timeout'])
337
		$timeout = $config['captiveportal']['timeout'] * 60;
338
	else
339
		$timeout = 0;
340
		
341
	if ($config['captiveportal']['idletimeout'])
342
		$idletimeout = $config['captiveportal']['idletimeout'] * 60;
343
	else
344
		$idletimeout = 0;
345
	
346
	if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']))
347
		return;
348
	
349
	captiveportal_lock();
350
	
351
	/* read database */
352
	$cpdb = captiveportal_read_db();
353
	
354
	$radiusservers = captiveportal_get_radius_servers();
355
	
356
	for ($i = 0; $i < count($cpdb); $i++) {
357
		
358
		$timedout = false;
359
		
360
		/* hard timeout? */
361
		if ($timeout) {
362
			if ((time() - $cpdb[$i][0]) >= $timeout)
363
				$timedout = true;	
364
		}
365
		
366
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
367
		if (!$timedout && $idletimeout) {
368
			$lastact = captiveportal_get_last_activity($cpdb[$i][1]);
369
			if ($lastact && ((time() - $lastact) >= $idletimeout))
370
				$timedout = true;
371
		}
372
		
373
		if ($timedout) {
374
			captiveportal_disconnect($cpdb[$i], $radiusservers);
375
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
376
			unset($cpdb[$i]);
377
		}
378
		
379
		/* do periodic RADIUS reauthentication? */
380
		if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
381
			($radiusservers !== false)) {
382
		
383
			if (isset($config['captiveportal']['radacct_enable'])) {
384
				if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
385
					/* stop and restart accounting */
386
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
387
										   $cpdb[$i][4], // username
388
										   $cpdb[$i][5], // sessionid
389
										   $cpdb[$i][0], // start time
390
										   $radiusservers[0]['ipaddr'],
391
										   $radiusservers[0]['acctport'],
392
										   $radiusservers[0]['key'],
393
										   $cpdb[$i][2]); //clientip
394
					exec("/sbin/ipfw zero {$cpdb[$i][1]}");
395
					RADIUS_ACCOUNTING_START($cpdb[$i][4],
396
											$cpdb[$i][5],
397
											$radiusservers[0]['ipaddr'],
398
											$radiusservers[0]['acctport'],
399
											$radiusservers[0]['key'],
400
											$cpdb[$i][2]);
401
				} else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
402
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
403
										   $cpdb[$i][4], // username
404
										   $cpdb[$i][5], // sessionid
405
										   $cpdb[$i][0], // start time
406
										   $radiusservers[0]['ipaddr'],
407
										   $radiusservers[0]['acctport'],
408
										   $radiusservers[0]['key'],
409
										   $cpdb[$i][2], //clientip
410
										   true);
411
				}
412
			}
413
		
414
			/* check this user against RADIUS again */
415
			$auth_val = RADIUS_AUTHENTICATION($cpdb[$i][4],
416
										  base64_decode($cpdb[$i][6]),
417
							  			  $radiusservers[0]['ipaddr'],
418
							  			  $radiusservers[0]['port'],
419
							  			  $radiusservers[0]['key']);
420
			
421
			if ($auth_val == 3) {
422
				captiveportal_disconnect($cpdb[$i], $radiusservers);
423
				captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT");
424
				unset($cpdb[$i]);
425
			}
426
		}
427
	}
428
	
429
	/* write database */
430
	captiveportal_write_db($cpdb);
431
	
432
	captiveportal_unlock();
433
}
434

    
435
/* remove a single client according to the DB entry */
436
function captiveportal_disconnect($dbent, $radiusservers) {
437
	
438
	global $g, $config;
439
	
440
	/* this client needs to be deleted - remove ipfw rules */
441
	if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
442
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
443
							   $dbent[4], // username
444
							   $dbent[5], // sessionid
445
							   $dbent[0], // start time
446
							   $radiusservers[0]['ipaddr'],
447
							   $radiusservers[0]['acctport'],
448
							   $radiusservers[0]['key'],
449
							   $dbent[2]); //clientip
450
	}
451
	
452
	mwexec("/sbin/ipfw delete " . $dbent[1] . " " . ($dbent[1]+10000));
453
	
454
	//KEYCOM: we need to delete +40500 and +45500 as well...
455
	//these are the rule numbers we use to control traffic shaping for each logged in user via captive portal
456
	//we only need to remove our rules if peruserbw is turned on.
457
	if (isset($config['captiveportal']['peruserbw'])) {
458
		mwexec("/sbin/ipfw delete " . ($dbent[1]+40500));
459
		mwexec("/sbin/ipfw delete " . ($dbent[1]+45500));
460
	}
461
}
462

    
463
/* remove a single client by ipfw rule number */
464
function captiveportal_disconnect_client($id) {
465
	
466
	global $g, $config;
467
	
468
	captiveportal_lock();
469
	
470
	/* read database */
471
	$cpdb = captiveportal_read_db();
472
	$radiusservers = captiveportal_get_radius_servers();
473
	
474
	/* find entry */	
475
	for ($i = 0; $i < count($cpdb); $i++) {
476
		if ($cpdb[$i][1] == $id) {
477
			captiveportal_disconnect($cpdb[$i], $radiusservers);
478
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
479
			unset($cpdb[$i]);
480
			break;
481
		}
482
	}
483
	
484
	/* write database */
485
	captiveportal_write_db($cpdb);
486
	
487
	captiveportal_unlock();
488
}
489

    
490
/* send RADIUS acct stop for all current clients */
491
function captiveportal_radius_stop_all() {
492
	global $g, $config;
493

    
494
	captiveportal_lock();
495
	$cpdb = captiveportal_read_db();
496
	
497
	$radiusservers = captiveportal_get_radius_servers();
498
	
499
	if (isset($radiusservers[0])) {
500
		for ($i = 0; $i < count($cpdb); $i++) {
501
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
502
								   $cpdb[$i][4], // username
503
								   $cpdb[$i][5], // sessionid
504
								   $cpdb[$i][0], // start time
505
								   $radiusservers[0]['ipaddr'],
506
								   $radiusservers[0]['acctport'],
507
								   $radiusservers[0]['key'],
508
								   $cpdb[$i][2]); //clientip
509
		}
510
	}
511
	captiveportal_unlock();
512
}
513

    
514
function captiveportal_passthrumac_configure() {
515
	global $config, $g;
516
	
517
	captiveportal_lock();
518
	
519
	/* clear out passthru macs, if necessary */
520
	unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
521
	
522
	if (is_array($config['captiveportal']['passthrumac'])) {
523
		
524
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
525
		if (!$fd) {
526
			printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
527
			captiveportal_unlock();
528
			return 1;		
529
		}
530
		
531
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
532
			/* record passthru mac so it can be recognized and let thru */
533
			fwrite($fd, $macent['mac'] . "\n");
534
		}
535
		
536
		fclose($fd); 
537
	}
538
	
539
	captiveportal_unlock();
540
	
541
	return 0;
542
}
543

    
544
function captiveportal_allowedip_configure() {
545
	global $config, $g;
546
	
547
	captiveportal_lock();
548

    
549
	/* clear out existing allowed ips, if necessary */
550
	if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) {
551
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r");
552
		if ($fd) {
553
			while (!feof($fd)) {
554
				$line = trim(fgets($fd));
555
				if ($line) {
556
					list($ip,$rule) = explode(",",$line);
557
					mwexec("/sbin/ipfw delete $rule");
558
				}	
559
			}
560
		}
561
		fclose($fd);
562
		unlink("{$g['vardb_path']}/captiveportal_ip.db");
563
	}
564

    
565
	/* get next ipfw rule number */
566
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
567
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
568
	if (!$ruleno)
569
		$ruleno = 10000;	/* first rule number */
570
	
571
	if (is_array($config['captiveportal']['allowedip'])) {
572
		
573
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w");
574
		if (!$fd) {
575
			printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n");
576
			captiveportal_unlock();
577
			return 1;		
578
		}
579
		
580
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
581
		
582
			/* record allowed ip so it can be recognized and removed later */
583
			fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n");
584
			
585
			/* insert ipfw rule to allow ip thru */
586
			if ($ipent['dir'] == "from") {
587
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any in");
588
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " out");
589
			} else {
590
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " in");
591
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any out");
592
			}
593
			
594
			$ruleno++;
595
			if ($ruleno > 19899)
596
				$ruleno = 10000;
597
		}
598
		
599
		fclose($fd); 
600

    
601
		/* write next rule number */
602
		$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
603
		if ($fd) {
604
			fwrite($fd, $ruleno);
605
			fclose($fd);
606
		}
607
	}
608
	
609
	captiveportal_unlock();
610
	return 0;
611
}
612

    
613
/* get last activity timestamp given ipfw rule number */
614
function captiveportal_get_last_activity($ruleno) {
615
	
616
	exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput);
617
	
618
	/* in */
619
	if ($ipfwoutput[0]) {
620
		$ri = explode(" ", $ipfwoutput[0]);
621
		if ($ri[1])
622
			return $ri[1];
623
	}
624
	
625
	return 0;
626
}
627

    
628
/* read captive portal DB into array */
629
function captiveportal_read_db() {
630
	
631
	global $g;
632
	
633
	$cpdb = array();
634
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
635
	if ($fd) {
636
		while (!feof($fd)) {
637
			$line = trim(fgets($fd));
638
			if ($line) {
639
				$cpdb[] = explode(",", $line);
640
			}	
641
		}
642
		fclose($fd);
643
	}
644
	return $cpdb;
645
}
646

    
647
/* write captive portal DB */
648
function captiveportal_write_db($cpdb) {
649
	
650
	global $g;
651
	
652
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
653
	if ($fd) {
654
		foreach ($cpdb as $cpent) {
655
			fwrite($fd, join(",", $cpent) . "\n");
656
		}
657
		fclose($fd);
658
	}
659
}
660

    
661
/* read RADIUS servers into array */
662
function captiveportal_get_radius_servers() {
663
	
664
	global $g;
665
	
666
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
667
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
668
		if ($fd) {
669
			$radiusservers = array();
670
			while (!feof($fd)) {
671
				$line = trim(fgets($fd));
672
				if ($line) {
673
					$radsrv = array();
674
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
675
					$radiusservers[] = $radsrv;
676
				}
677
			}
678
			fclose($fd);
679
			
680
			return $radiusservers;
681
		}
682
	}
683
	
684
	return false;
685
}
686

    
687
/* lock captive portal information, decide that the lock file is stale after
688
   10 seconds */
689
function captiveportal_lock() {
690
	
691
	global $g;
692
	
693
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
694
	
695
	$n = 0;
696
	while ($n < 10) {
697
		/* open the lock file in append mode to avoid race condition */
698
		if ($fd = @fopen($lockfile, "x")) {
699
			/* succeeded */
700
			fclose($fd);
701
			return;
702
		} else {
703
			/* file locked, wait and try again */
704
			sleep(1);
705
			$n++;
706
		}
707
	}
708
}
709

    
710
/* unlock configuration file */
711
function captiveportal_unlock() {
712
	
713
	global $g;
714
	
715
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
716
	
717
	if (file_exists($lockfile))
718
		unlink($lockfile);
719
}
720

    
721
/* log successful captive portal authentication to syslog */
722
/* part of this code from php.net */
723
function captiveportal_logportalauth($user,$mac,$ip,$status) {
724
	define_syslog_variables();
725
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
726
	// Log it
727
	syslog(LOG_INFO, "$status: $user, $mac, $ip");
728
	closelog();
729
}
730

    
731
?>
(2-2/23)