Project

General

Profile

Download (18.1 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * status_ipsec.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2016 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * originally based on m0n0wall (http://m0n0.ch/wall)
10
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
11
 * All rights reserved.
12
 *
13
 * Redistribution and use in source and binary forms, with or without
14
 * modification, are permitted provided that the following conditions are met:
15
 *
16
 * 1. Redistributions of source code must retain the above copyright notice,
17
 *    this list of conditions and the following disclaimer.
18
 *
19
 * 2. Redistributions in binary form must reproduce the above copyright
20
 *    notice, this list of conditions and the following disclaimer in
21
 *    the documentation and/or other materials provided with the
22
 *    distribution.
23
 *
24
 * 3. All advertising materials mentioning features or use of this software
25
 *    must display the following acknowledgment:
26
 *    "This product includes software developed by the pfSense Project
27
 *    for use in the pfSense® software distribution. (http://www.pfsense.org/).
28
 *
29
 * 4. The names "pfSense" and "pfSense Project" must not be used to
30
 *    endorse or promote products derived from this software without
31
 *    prior written permission. For written permission, please contact
32
 *    coreteam@pfsense.org.
33
 *
34
 * 5. Products derived from this software may not be called "pfSense"
35
 *    nor may "pfSense" appear in their names without prior written
36
 *    permission of the Electric Sheep Fencing, LLC.
37
 *
38
 * 6. Redistributions of any form whatsoever must retain the following
39
 *    acknowledgment:
40
 *
41
 * "This product includes software developed by the pfSense Project
42
 * for use in the pfSense software distribution (http://www.pfsense.org/).
43
 *
44
 * THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
45
 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
47
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
48
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
49
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
50
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
51
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
53
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
54
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
55
 * OF THE POSSIBILITY OF SUCH DAMAGE.
56
 */
57

    
58
##|+PRIV
59
##|*IDENT=page-status-ipsec
60
##|*NAME=Status: IPsec
61
##|*DESCR=Allow access to the 'Status: IPsec' page.
62
##|*MATCH=status_ipsec.php*
63
##|-PRIV
64

    
65
require_once("guiconfig.inc");
66
require_once("ipsec.inc");
67

    
68
global $g;
69

    
70
if (!is_array($config['ipsec']['phase1'])) {
71
	$config['ipsec']['phase1'] = array();
72
}
73

    
74
// If this is just an AJAX call to update the table body, just generate the body and quit
75
if ($_REQUEST['ajax']) {
76
	print_ipsec_body();
77
	exit;
78
}
79

    
80
if ($_GET['act'] == 'connect') {
81
	if (ctype_digit($_GET['ikeid'])) {
82
		$ph1ent = ipsec_get_phase1($_GET['ikeid']);
83
		if (!empty($ph1ent)) {
84
			if (empty($ph1ent['iketype']) || $ph1ent['iketype'] == 'ikev1' || isset($ph1ent['splitconn'])) {
85
				$ph2entries = ipsec_get_number_of_phase2($_GET['ikeid']);
86
				for ($i = 0; $i < $ph2entries; $i++) {
87
					$connid = escapeshellarg("con{$_GET['ikeid']}00{$i}");
88
					mwexec_bg("/usr/local/sbin/ipsec down {$connid}");
89
					mwexec_bg("/usr/local/sbin/ipsec up {$connid}");
90
				}
91
			} else {
92
				mwexec_bg("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']));
93
				mwexec_bg("/usr/local/sbin/ipsec up con" . escapeshellarg($_GET['ikeid']));
94
			}
95
		}
96
	}
97
} else if ($_GET['act'] == 'ikedisconnect') {
98
	if (ctype_digit($_GET['ikeid'])) {
99
		if (!empty($_GET['ikesaid']) && ctype_digit($_GET['ikesaid'])) {
100
			mwexec_bg("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']) . "[" . escapeshellarg($_GET['ikesaid']) . "]");
101
		} else {
102
			mwexec_bg("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']));
103
		}
104
	}
105
} else if ($_GET['act'] == 'childdisconnect') {
106
	if (ctype_digit($_GET['ikeid'])) {
107
		if (!empty($_GET['ikesaid']) && ctype_digit($_GET['ikesaid'])) {
108
			mwexec_bg("/usr/local/sbin/ipsec down con" . escapeshellarg($_GET['ikeid']) . "{" . escapeshellarg($_GET['ikesaid']) . "}");
109
		}
110
	}
111
}
112

    
113
// Table body is composed here so that it can be more easily updated via AJAX
114
function print_ipsec_body() {
115
	global $config;
116

    
117
	$a_phase1 = &$config['ipsec']['phase1'];
118
	$status = ipsec_list_sa();
119
	$ipsecconnected = array();
120

    
121
	if (is_array($status)) {
122
		foreach ($status as $ikeid => $ikesa) {
123
			$con_id = substr($ikeid, 3);
124

    
125
			if ($ikesa['version'] == 1) {
126
				$ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00'));
127
				$ipsecconnected[$ph1idx] = $ph1idx;
128
			} else {
129
				if (!ipsec_ikeid_used($con_id)) {
130
					// probably a v2 with split connection then
131
					$ph1idx = substr($con_id, 0, strrpos(substr($con_id, 0, -1), '00'));
132
					$ipsecconnected[$ph1idx] = $ph1idx;
133
				} else {
134
					$ipsecconnected[$con_id] = $ph1idx = $con_id;
135
				}
136
			}
137

    
138
			print("<tr>\n");
139
			print("<td>\n");
140
			print(htmlspecialchars(ipsec_get_descr($ph1idx)));
141
			print("</td>\n");
142
			print("<td>\n");
143

    
144
			if (!empty($ikesa['local-id'])) {
145
				if ($ikesa['local-id'] == '%any') {
146
					print(gettext('Any identifier'));
147
				} else {
148
					print(htmlspecialchars($ikesa['local-id']));
149
				}
150
			} else {
151
				print(gettext("Unknown"));
152
			}
153

    
154
			print("</td>\n");
155
			print("<td>\n");
156

    
157
			if (!empty($ikesa['local-host'])) {
158
				print(htmlspecialchars($ikesa['local-host']));
159
			} else {
160
				print(gettext("Unknown"));
161
			}
162

    
163
			/*
164
			 * XXX: local-nat-t was defined by pfSense
165
			 * When strongswan team accepted the change, they changed it to
166
			 * nat-local. Keep both for a while and remove local-nat-t in
167
			 * the future
168
			 */
169
			if (isset($ikesa['local-nat-t']) || isset($ikesa['nat-local'])) {
170
				print(" NAT-T");
171
			}
172

    
173
			print("</td>\n");
174
			print("<td>\n");
175

    
176
			$identity = "";
177
			if (!empty($ikesa['remote-id'])) {
178
				if ($ikesa['remote-id'] == '%any') {
179
					$identity = htmlspecialchars(gettext('Any identifier'));
180
				} else {
181
					$identity = htmlspecialchars($ikesa['remote-id']);
182
				}
183
			}
184

    
185
			if (!empty($ikesa['remote-xauth-id'])) {
186
				echo htmlspecialchars($ikesa['remote-xauth-id']);
187
				echo "<br/>{$identity}";
188
			} elseif (!empty($ikesa['remote-eap-id'])) {
189
				echo htmlspecialchars($ikesa['remote-eap-id']);
190
				echo "<br/>{$identity}";
191
			} else {
192
				if (empty($identity)) {
193
					print(gettext("Unknown"));
194
				} else {
195
					print($identity);
196
				}
197
			}
198

    
199
			print("</td>\n");
200
			print("<td>\n");
201

    
202
			if (!empty($ikesa['remote-host'])) {
203
				print(htmlspecialchars($ikesa['remote-host']));
204
			} else {
205
				print(gettext("Unknown"));
206
			}
207
			/*
208
			 * XXX: remote-nat-t was defined by pfSense
209
			 * When strongswan team accepted the change, they changed it to
210
			 * nat-remote. Keep both for a while and remove remote-nat-t in
211
			 * the future
212
			 */
213
			if (isset($ikesa['remote-nat-t']) || isset($ikesa['nat-remote'])) {
214
				print(" NAT-T");
215
			}
216

    
217
			print("</td>\n");
218
			print("<td>\n");
219
			print("IKEv" . htmlspecialchars($ikesa['version']));
220
			print("<br/>\n");
221

    
222
			if ($ikesa['initiator'] == 'yes') {
223
				print("initiator");
224
			} else {
225
				print("responder");
226
			}
227

    
228
			print("</td>\n");
229
			print("<td>\n");
230
			print(htmlspecialchars($ikesa['reauth-time']) . gettext(" seconds (") . convert_seconds_to_dhms($ikesa['reauth-time']) . ")");
231
			print("</td>\n");
232
			print("<td>\n");
233
			print(htmlspecialchars($ikesa['encr-alg']));
234
			print("<br/>");
235
			print(htmlspecialchars($ikesa['integ-alg']));
236
			print("<br/>");
237
			print(htmlspecialchars($ikesa['prf-alg']));
238
			print("<br/>\n");
239
			print(htmlspecialchars($ikesa['dh-group']));
240
			print("</td>\n");
241
			print("<td>\n");
242

    
243
			if ($ikesa['state'] == 'ESTABLISHED') {
244
				print('<span class="text-success">');
245
			} else {
246
				print('<span>');
247
			}
248

    
249
			print(ucfirst(htmlspecialchars($ikesa['state'])));
250

    
251
			if ($ikesa['state'] == 'ESTABLISHED') {
252
				print("<br/>" . htmlspecialchars($ikesa['established']) . gettext(" seconds (") . convert_seconds_to_dhms($ikesa['established']) . gettext(") ago"));
253
			}
254

    
255
			print("</span>");
256
			print("</td>\n");
257
			print("<td>\n");
258

    
259
			if ($ikesa['state'] != 'ESTABLISHED') {
260

    
261
				print('<a href="status_ipsec.php?act=connect&amp;ikeid=' . $con_id . '" class="btn btn-xs btn-success" data-toggle="tooltip" title="' . gettext("Connect VPN"). '" >');
262
				print('<i class="fa fa-sign-in icon-embed-btn"></i>');
263
				print(gettext("Connect VPN"));
264
				print("</a>\n");
265

    
266
			} else {
267

    
268
				print('<a href="status_ipsec.php?act=ikedisconnect&amp;ikeid=' . $con_id . '" class="btn btn-xs btn-danger" data-toggle="tooltip" title="' . gettext("Disconnect VPN") . '">');
269
				print('<i class="fa fa-trash icon-embed-btn"></i>');
270
				print(gettext("Disconnect"));
271
				print("</a><br />\n");
272

    
273
			}
274

    
275
			print("</td>\n");
276
			print("</tr>\n");
277
			print("<tr>\n");
278
			print("<td colspan = 10>\n");
279

    
280
			if (is_array($ikesa['child-sas']) && (count($ikesa['child-sas']) > 0)) {
281

    
282
				print('<div>');
283
				print('<a type="button" id="btnchildsa-' . $ikeid .  '" class="btn btn-sm btn-info">');
284
				print('<i class="fa fa-plus-circle icon-embed-btn"></i>');
285
				print(gettext('Show child SA entries'));
286
				print("</a>\n");
287
				print("	</div>\n");
288

    
289
				print('<table class="table table-hover table-condensed" id="childsa-' . $ikeid . '" style="display:none">');
290
				print("<thead>\n");
291
				print('<tr class="bg-info">');
292
				print('<th><?=gettext("Local subnets")?></th>');
293
				print('<th><?=gettext("Local SPI(s)")?></th>');
294
				print('<th><?=gettext("Remote subnets")?></th>');
295
				print('<th><?=gettext("Times")?></th>');
296
				print('<th><?=gettext("Algo")?></th>');
297
				print('<th><?=gettext("Stats")?></th>');
298
				print('<th><!-- Buttons --></th>');
299
				print("</tr\n");
300
				print("</thead>\n");
301
				print("<tbody>\n");
302

    
303
				foreach ($ikesa['child-sas'] as $childid => $childsa) {
304
					print("<tr>");
305
					print("<td>\n");
306

    
307
					if (is_array($childsa['local-ts'])) {
308
						foreach ($childsa['local-ts'] as $lnets) {
309
							print(htmlspecialchars(ipsec_fixup_network($lnets)) . "<br />");
310
						}
311
					} else {
312
						print(gettext("Unknown"));
313
					}
314

    
315
					print("</td>\n");
316
					print("<td>\n");
317

    
318
					if (isset($childsa['spi-in'])) {
319
						print(gettext("Local: ") . htmlspecialchars($childsa['spi-in']));
320
					}
321

    
322
					if (isset($childsa['spi-out'])) {
323
						print('<br/>' . gettext('Remote: ') . htmlspecialchars($childsa['spi-out']));
324
					}
325

    
326
					print("</td>\n");
327
					print("<td>\n");
328

    
329
					if (is_array($childsa['remote-ts'])) {
330
						foreach ($childsa['remote-ts'] as $rnets) {
331
							print(htmlspecialchars(ipsec_fixup_network($rnets)) . '<br />');
332
						}
333
					} else {
334
						print(gettext("Unknown"));
335
					}
336

    
337
					print("</td>\n");
338
					print("<td>\n");
339

    
340
					print(gettext("Rekey: ") . htmlspecialchars($childsa['rekey-time']) . gettext(" seconds (") . convert_seconds_to_dhms($childsa['rekey-time']) . ")");
341
					print('<br/>' . gettext('Life: ') . htmlspecialchars($childsa['life-time']) . gettext(" seconds (") . convert_seconds_to_dhms($childsa['life-time']) . ")");
342
					print('<br/>' . gettext('Install: ') .htmlspecialchars($childsa['install-time']) . gettext(" seconds (") . convert_seconds_to_dhms($childsa['install-time']) . ")");
343

    
344

    
345
					print("</td>\n");
346
					print("<td>\n");
347

    
348
					print(htmlspecialchars($childsa['encr-alg']) . '<br/>');
349
					print(htmlspecialchars($childsa['integ-alg']) . '<br/>');
350

    
351
					if (!empty($childsa['prf-alg'])) {
352
						print(htmlspecialchars($childsa['prf-alg']) . '<br/>');
353
					}
354

    
355
					if (!empty($childsa['dh-group'])) {
356
						print(htmlspecialchars($childsa['dh-group']) . '<br/>');
357
					}
358

    
359
					if (!empty($childsa['esn'])) {
360
						print(htmlspecialchars($childsa['esn']) . '<br/>');
361
					}
362

    
363
					print(gettext("IPComp: "));
364
					if (!empty($childsa['cpi-in']) || !empty($childsa['cpi-out'])) {
365
						print(htmlspecialchars($childsa['cpi-in']) . " " . htmlspecialchars($childsa['cpi-out']));
366
					} else {
367
						print(gettext('none'));
368
					}
369

    
370
					print("</td>\n");
371
					print("<td>\n");
372

    
373
					print(gettext("Bytes-In: ") . htmlspecialchars(number_format($childsa['bytes-in'])) . ' (' . htmlspecialchars(format_bytes($childsa['bytes-in'])) . ')<br/>');
374
					print(gettext("Packets-In: ") . htmlspecialchars(number_format($childsa['packets-in'])) . '<br/>');
375
					print(gettext("Bytes-Out: ") . htmlspecialchars(number_format($childsa['bytes-out'])) . ' (' . htmlspecialchars(format_bytes($childsa['bytes-out'])) . ')<br/>');
376
					print(gettext("Packets-Out: ") . htmlspecialchars(number_format($childsa['packets-out'])) . '<br/>');
377

    
378
					print("</td>\n");
379
					print("<td>\n");
380
					print('<a href="status_ipsec.php?act=childdisconnect&amp;ikeid=' . $con_id . '&amp;ikesaid=' . $childsa['uniqueid'] . '" class="btn btn-xs btn-warning" data-toggle="tooltip" title="' . gettext('Disconnect Child SA') . '">');
381
					print('<i class="fa fa-trash icon-embed-btn"></i>');
382
					print(gettext("Disconnect"));
383
					print("</a>\n");
384
					print("</td>\n");
385
					print("</tr>\n");
386

    
387
				}
388

    
389
				print("</tbody>\n");
390
				print("	</table>\n");
391
				print("</td>\n");
392
				print("</tr>\n");
393

    
394
			}
395

    
396
			unset($con_id);
397
		}
398

    
399
	}
400

    
401
	$rgmap = array();
402
	if (is_array($a_phase1)) {
403
		foreach ($a_phase1 as $ph1ent) {
404
			if (isset($ph1ent['disabled'])) {
405
				continue;
406
			}
407

    
408
			$rgmap[$ph1ent['remote-gateway']] = $ph1ent['remote-gateway'];
409

    
410
			if ($ipsecconnected[$ph1ent['ikeid']]) {
411
				continue;
412
			}
413

    
414
			print("<tr>\n");
415
			print("<td>\n");
416

    
417
			print(htmlspecialchars($ph1ent['descr']));
418
			print("</td>\n");
419
			print("<td>\n");
420
			list ($myid_type, $myid_data) = ipsec_find_id($ph1ent, "local");
421

    
422
			if (empty($myid_data)) {
423
				print(gettext("Unknown"));
424
			} else {
425
				print(htmlspecialchars($myid_data));
426
			}
427

    
428
			print("</td>\n");
429
			print("<td>\n");
430
			$ph1src = ipsec_get_phase1_src($ph1ent);
431

    
432
			if (empty($ph1src)) {
433
				print(gettext("Unknown"));
434
			} else {
435
				print(htmlspecialchars($ph1src));
436
			}
437

    
438
			print("</td>\n");
439
			print("<td>\n");
440

    
441
			list ($peerid_type, $peerid_data) = ipsec_find_id($ph1ent, "peer", $rgmap);
442

    
443
			if (empty($peerid_data)) {
444
				print(gettext("Unknown"));
445
			} else {
446
				print(htmlspecialchars($peerid_data));
447
			}
448
			print("			</td>\n");
449
			print("			<td>\n");
450
			$ph1src = ipsec_get_phase1_dst($ph1ent);
451

    
452
			if (empty($ph1src)) {
453
				print(gettext("Unknown"));
454
			} else {
455
				print(htmlspecialchars($ph1src));
456
			}
457

    
458
			print("</td>\n");
459
			print("<td>\n");
460
			print("</td>\n");
461
			print("<td>\n");
462
			print("</td>\n");
463
			print("<td>\n");
464
			print("</td>\n");
465

    
466
			if (isset($ph1ent['mobile'])) {
467

    
468
				print("<td>\n");
469
				print(gettext("Awaiting connections"));
470
				print("</td>\n");
471
				print("<td>\n");
472
				print("</td>\n");
473
				print("</td>\n");
474
			} else {
475

    
476
				print("<td>\n");
477
				print(gettext("Disconnected"));
478
				print("</td>\n");
479
				print("<td>\n");
480
				print('<a href="status_ipsec.php?act=connect&amp;ikeid=' . $ph1ent['ikeid'] . '" class="btn btn-xs btn-success">');
481
				print('<i class="fa fa-sign-in icon-embed-btn"></i>');
482
				print(gettext("Connect VPN"));
483
				print("</a>\n");
484
				print("</td>\n");
485

    
486
			}
487
			print("</tr>\n");
488
		}
489
	}
490

    
491
	unset($ipsecconnected, $phase1, $rgmap);
492
}
493

    
494
$pgtitle = array(gettext("Status"), gettext("IPsec"), gettext("Overview"));
495
$pglinks = array("", "@self", "@self");
496
$shortcut_section = "ipsec";
497

    
498
include("head.inc");
499

    
500
$tab_array = array();
501
$tab_array[] = array(gettext("Overview"), true, "status_ipsec.php");
502
$tab_array[] = array(gettext("Leases"), false, "status_ipsec_leases.php");
503
$tab_array[] = array(gettext("SADs"), false, "status_ipsec_sad.php");
504
$tab_array[] = array(gettext("SPDs"), false, "status_ipsec_spd.php");
505
display_top_tabs($tab_array);
506
?>
507

    
508
<div class="panel panel-default">
509
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("IPsec Status");?></h2></div>
510
	<div class="panel-body table-responsive">
511
		<table class="table table-striped table-condensed table-hover sortable-theme-bootstrap" data-sortable>
512
			<thead>
513
				<tr>
514
					<th><?=gettext("Description")?></th>
515
					<th><?=gettext("Local ID")?></th>
516
					<th><?=gettext("Local IP")?></th>
517
					<th><?=gettext("Remote ID")?></th>
518
					<th><?=gettext("Remote IP")?></th>
519
					<th><?=gettext("Role")?></th>
520
					<th><?=gettext("Reauth")?></th>
521
					<th><?=gettext("Algo")?></th>
522
					<th><?=gettext("Status")?></th>
523
					<th></th>
524
				</tr>
525
			</thead>
526
			<tbody id="ipsec-body">
527
				<tr>
528
					<td colspan="10">
529
						<?=print_info_box(gettext("Collecting IPsec status information."), "warning", "")?>
530
					</td>
531
				</tr>
532
			</tbody>
533
		</table>
534
	</div>
535
</div>
536

    
537
<?php
538
unset($status);
539

    
540
if (ipsec_enabled()) {
541
	print('<div class="infoblock">');
542
} else {
543
	print('<div class="infoblock blockopen">');
544
}
545

    
546
print_info_box(sprintf(gettext('IPsec can be configured %1$shere%2$s.'), '<a href="vpn_ipsec.php">', '</a>'), 'info', false);
547
?>
548
</div>
549

    
550
<script type="text/javascript">
551
//<![CDATA[
552

    
553
events.push(function() {
554
	ajax_lock = false;		// Mutex so we don't make a call until the previous call is finished
555
	sa_open = new Array();	// Array in which to keep the child SA show/hide state
556

    
557
	// Fetch the tbody contents from the server
558
	function update_table() {
559
		if (ajax_lock) {
560
			return;
561
		}
562

    
563
		ajax_lock = true;
564

    
565
		ajaxRequest = $.ajax(
566
			{
567
				url: "/status_ipsec.php",
568
				type: "post",
569
				data: {
570
					ajax: 	"ajax"
571
				}
572
			}
573
		);
574

    
575
		// Deal with the results of the above ajax call
576
		ajaxRequest.done(function (response, textStatus, jqXHR) {
577

    
578
			if (!response) {
579
				response = '<tr><td colspan="10"><?=print_info_box(gettext("No IPsec status information available."), "warning", "")?></td></tr>';
580
			}
581

    
582
			$('#ipsec-body').html(response);
583
			ajax_lock = false;
584

    
585
			// Update "Show child SA" handlers
586
			$('[id^=btnchildsa-]').click(function () {
587
				show_childsa($(this).prop("id").replace( /^\D+/g, ''));
588
			});
589

    
590
			// Check the sa_open array for child SAs that have been opened
591
			$('[id^=childsa-con]').each(function(idx) {
592
				sa_idx = $(this).prop("id").replace( /^\D+/g, '');
593

    
594
				if (sa_open[sa_idx]) {
595
					show_childsa(sa_idx);
596
				}
597
			});
598

    
599
			// and do it again
600
			setTimeout(update_table, 5000);
601
		});
602
	}
603

    
604
	function show_childsa(said) {
605
		sa_open[said] = true;
606
		$('#childsa-con' + said).show();
607
		$('#btnchildsa-con' + said).hide();
608
	}
609

    
610
	// Populate the tbody on page load
611
	update_table();
612
});
613
//]]>
614
</script>
615

    
616
<?php
617
include("foot.inc"); ?>
(169-169/230)