Project

General

Profile

Download (18.4 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * services_radvd.php
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8
 * Copyright (c) 2014-2023 Rubicon Communications, LLC (Netgate)
9
 * Copyright (c) 2010 Seth Mos <seth.mos@dds.nl>
10
 * All rights reserved.
11
 *
12
 * originally based on m0n0wall (http://m0n0.ch/wall)
13
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
14
 * All rights reserved.
15
 *
16
 * Licensed under the Apache License, Version 2.0 (the "License");
17
 * you may not use this file except in compliance with the License.
18
 * You may obtain a copy of the License at
19
 *
20
 * http://www.apache.org/licenses/LICENSE-2.0
21
 *
22
 * Unless required by applicable law or agreed to in writing, software
23
 * distributed under the License is distributed on an "AS IS" BASIS,
24
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
 * See the License for the specific language governing permissions and
26
 * limitations under the License.
27
 */
28

    
29
##|+PRIV
30
##|*IDENT=page-services-router-advertisement
31
##|*NAME=Services: Router Advertisement
32
##|*DESCR=Allow access to the 'Services: Router Advertisement' page.
33
##|*MATCH=services_radvd.php*
34
##|-PRIV
35

    
36
require_once('guiconfig.inc');
37

    
38
if (!g_get('services_dhcp_server_enable')) {
39
	header('Location: /');
40
	exit;
41
}
42

    
43
$if = $_REQUEST['if'];
44
$iflist = get_configured_interface_with_descr();
45

    
46
/* set the starting interface */
47
if (!$if || !isset($iflist[$if])) {
48
	foreach ($iflist as $ifent => $ifname) {
49
		$ifaddr = config_get_path("interfaces/{$ifent}/ipaddrv6");
50
		if (!config_path_enabled("dhcpdv6/{$ifent}") &&
51
		    !(($ifaddr == 'track6') ||
52
		    (is_ipaddrv6($ifaddr) &&
53
		    !is_linklocal($ifaddr)))) {
54
			continue;
55
		}
56
		$if = $ifent;
57
		break;
58
	}
59
}
60

    
61
if (!empty(config_get_path("dhcpdv6/{$if}"))) {
62
	/* RA specific */
63
	$pconfig['ramode'] = config_get_path("dhcpdv6/{$if}/ramode");
64
	$pconfig['rapriority'] = config_get_path("dhcpdv6/{$if}/rapriority");
65
	$pconfig['rainterface'] = config_get_path("dhcpdv6/{$if}/rainterface");
66
	if ($pconfig['rapriority'] == "") {
67
		$pconfig['rapriority'] = "medium";
68
	}
69

    
70
	$pconfig['ravalidlifetime'] = config_get_path("dhcpdv6/{$if}/ravalidlifetime");
71
	$pconfig['rapreferredlifetime'] = config_get_path("dhcpdv6/{$if}/rapreferredlifetime");
72
	$pconfig['raminrtradvinterval'] = config_get_path("dhcpdv6/{$if}/raminrtradvinterval");
73
	$pconfig['ramaxrtradvinterval'] = config_get_path("dhcpdv6/{$if}/ramaxrtradvinterval");
74
	$pconfig['raadvdefaultlifetime'] = config_get_path("dhcpdv6/{$if}/raadvdefaultlifetime");
75

    
76
	$pconfig['radomainsearchlist'] = config_get_path("dhcpdv6/{$if}/radomainsearchlist");
77
	list($pconfig['radns1'], $pconfig['radns2'], $pconfig['radns3']) = config_get_path("dhcpdv6/{$if}/radnsserver");
78
	$pconfig['radvd-dns'] = ($config['dhcpdv6'][$if]['radvd-dns'] != 'disabled') ? true : false;
79
	$pconfig['rasamednsasdhcp6'] = isset($config['dhcpdv6'][$if]['rasamednsasdhcp6']);
80

    
81
	$pconfig['subnets'] = config_get_path("dhcpdv6/{$if}/subnets/item");
82
}
83
if (!is_array($pconfig['subnets'])) {
84
	$pconfig['subnets'] = array();
85
}
86

    
87
$advertise_modes = array(
88
	"disabled" => 	gettext("Disabled"),
89
	"router" => 	gettext("Router Only - RA Flags [none], Prefix Flags [router]"),
90
	"unmanaged" => 	gettext("Unmanaged - RA Flags [none], Prefix Flags [onlink, auto, router]"),
91
	"managed" => 	gettext("Managed - RA Flags [managed, other stateful], Prefix Flags [onlink, router]"),
92
	"assist" => 	gettext("Assisted - RA Flags [managed, other stateful], Prefix Flags [onlink, auto, router]"),
93
	"stateless_dhcp" => gettext("Stateless DHCP - RA Flags [other stateful], Prefix Flags [onlink, auto, router]"));
94
$priority_modes = array(
95
	"low" => 	gettext("Low"),
96
	"medium" => gettext("Normal"),
97
	"high" => 	gettext("High"));
98

    
99

    
100

    
101
// THe use of <div class="infoblock"> here causes the text to be hidden until the user clicks the "info" icon
102
$ramode_help = gettext('Select the Operating Mode for the Router Advertisement (RA) Daemon.') .
103
	'<div class="infoblock">' .
104
	'<dl class="dl-horizontal responsive">' .
105
	'<dt>' . gettext('Disabled') . 		 '</dt><dd>' . gettext('RADVD will not be enabled on this interface.') . '</dd>' .
106
	'<dt>' . gettext('Router Only') . 	 '</dt><dd>' . gettext('Will advertise this router.') . '</dd>' .
107
	'<dt>' . gettext('Unmanaged') . 	 '</dt><dd>' . gettext('Will advertise this router with Stateless Address Auto-Configuration (SLAAC).') . '</dd>' .
108
	'<dt>' . gettext('Managed') . 		 '</dt><dd>' . gettext('Will advertise this router with all configuration through a DHCPv6 server.') . '</dd>' .
109
	'<dt>' . gettext('Assisted') . 		 '</dt><dd>' . gettext('Will advertise this router with configuration through a DHCPv6 server and/or SLAAC.') . '</dd>' .
110
	'<dt>' . gettext('Stateless DHCP') . '</dt><dd>' . gettext('Will advertise this router with SLAAC and other configuration information available via DHCPv6.') . '</dd>' .
111
	'</dl>' .
112
	sprintf(gettext('It is not required to activate DHCPv6 server on %s ' .
113
	    'when set to "Managed", "Assisted" or "Stateless DHCP", it can ' .
114
	    'be another host on the network.'), g_get('product_label')) .
115
	'</div>';
116

    
117
if ($_POST['save']) {
118
	unset($input_errors);
119

    
120
	$pconfig = $_POST;
121

    
122
	/* input validation */
123

    
124
	$pconfig['subnets'] = array();
125
	for ($x = 0; $x < 5000; $x += 1) {
126
		$address = trim($_POST['subnet_address' . $x]);
127
		if ($address === "") {
128
			continue;
129
		}
130

    
131
		$bits = trim($_POST['subnet_bits' . $x]);
132
		if ($bits === "") {
133
			$bits = "128";
134
		}
135

    
136
		if (is_alias($address)) {
137
			$pconfig['subnets'][] = $address;
138
		} else {
139
			$pconfig['subnets'][] = $address . "/" . $bits;
140
			if (!is_ipaddrv6($address)) {
141
				$input_errors[] = sprintf(gettext('An invalid subnet or alias was specified. [%1$s/%2$s]'), $address, $bits);
142
			}
143
		}
144
	}
145

    
146
	if (($_POST['radns1'] && !is_ipaddrv6($_POST['radns1'])) || ($_POST['radns2'] && !is_ipaddrv6($_POST['radns2'])) || ($_POST['radns3'] && !is_ipaddrv6($_POST['radns3']))) {
147
		$input_errors[] = gettext("A valid IPv6 address must be specified for each of the DNS servers.");
148
	}
149
	if ($_POST['radomainsearchlist']) {
150
		$domain_array=preg_split("/[ ;]+/", $_POST['radomainsearchlist']);
151
		foreach ($domain_array as $curdomain) {
152
			if (!is_domain($curdomain)) {
153
				$input_errors[] = gettext("A valid domain search list must be specified.");
154
				break;
155
			}
156
		}
157
	}
158

    
159
	if ($_POST['ravalidlifetime'] && ($_POST['ravalidlifetime'] < 7200)) {
160
		$input_errors[] = gettext("A valid lifetime below 2 hours will be ignored by clients (RFC 4862 Section 5.5.3 point e)");
161
	}
162
	if ($_POST['ravalidlifetime'] && !is_numericint($_POST['ravalidlifetime'])) {
163
		$input_errors[] = gettext("Valid lifetime must be an integer.");
164
	}
165
	if ($_POST['raminrtradvinterval']) {
166
		if (!is_numericint($_POST['raminrtradvinterval'])) {
167
			$input_errors[] = gettext("Minimum advertisement interval must be an integer.");
168
		}
169
		if ($_POST['raminrtradvinterval'] < 3) {
170
			$input_errors[] = gettext("Minimum advertisement interval must be no less than 3.");
171
		}
172
		if ($_POST['ramaxrtradvinterval'] && $_POST['raminrtradvinterval'] > (0.75 * $_POST['ramaxrtradvinterval'])) {
173
			$input_errors[] = gettext("Minimum advertisement interval must be no greater than 0.75 * Maximum advertisement interval");
174
		}
175
	}
176
	if ($_POST['ramaxrtradvinterval']) {
177
		if (!is_numericint($_POST['ramaxrtradvinterval'])) {
178
			$input_errors[] = gettext("Maximum advertisement interval must be an integer.");
179
		}
180
		if ($_POST['ramaxrtradvinterval'] < 4 || $_POST['ramaxrtradvinterval'] > 1800) {
181
			$input_errors[] = gettext("Maximum advertisement interval must be no less than 4 and no greater than 1800.");
182
		}
183
	}
184
	if ($_POST['rapreferredlifetime']) {
185
		if (!is_numericint($_POST['rapreferredlifetime'])) {
186
			$input_errors[] = gettext("Default preferred lifetime must be an integer.");
187
		}
188
	}
189
	if ($_POST['raadvdefaultlifetime'] && (($_POST['raadvdefaultlifetime'] < 1) || ($_POST['raadvdefaultlifetime'] > 9000))) {
190
		$input_errors[] = gettext("Router lifetime must be an integer between 1 and 9000.");
191
	}
192
	if (($_POST['ravalidlifetime'] && $_POST['rapreferredlifetime'] &&
193
	    ($_POST['ravalidlifetime'] < $_POST['rapreferredlifetime'])) ||
194
	    ($_POST['ravalidlifetime'] && empty($_POST['rapreferredlifetime']) &&
195
	    ($_POST['ravalidlifetime'] < 14400)) || (empty($_POST['ravalidlifetime']) &&
196
	    $_POST['rapreferredlifetime'] && ($_POST['rapreferredlifetime'] > 86400))) {
197
		$input_errors[] = gettext("Default valid lifetime must be greater than Default preferred lifetime.");
198
	}
199

    
200
	if (!$input_errors) {
201
		if (!is_array($config['dhcpdv6'])) {
202
			config_set_path('dhcpdv6', array());
203
		}
204

    
205
		if (!is_array($config['dhcpdv6'][$if])) {
206
			$config['dhcpdv6'][$if] = array();
207
		}
208

    
209
		$config['dhcpdv6'][$if]['ramode'] = $_POST['ramode'];
210
		$config['dhcpdv6'][$if]['rapriority'] = $_POST['rapriority'];
211
		$config['dhcpdv6'][$if]['rainterface'] = $_POST['rainterface'];
212

    
213
		$config['dhcpdv6'][$if]['ravalidlifetime'] = $_POST['ravalidlifetime'];
214
		$config['dhcpdv6'][$if]['rapreferredlifetime'] = $_POST['rapreferredlifetime'];
215
		$config['dhcpdv6'][$if]['raminrtradvinterval'] = $_POST['raminrtradvinterval'];
216
		$config['dhcpdv6'][$if]['ramaxrtradvinterval'] = $_POST['ramaxrtradvinterval'];
217
		$config['dhcpdv6'][$if]['raadvdefaultlifetime'] = $_POST['raadvdefaultlifetime'];
218

    
219
		$config['dhcpdv6'][$if]['radomainsearchlist'] = $_POST['radomainsearchlist'];
220
		config_del_path("dhcpdv6/{$if}/radnsserver");
221
		if ($_POST['radns1']) {
222
			$config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns1'];
223
		}
224
		if ($_POST['radns2']) {
225
			$config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns2'];
226
		}
227
		if ($_POST['radns3']) {
228
			$config['dhcpdv6'][$if]['radnsserver'][] = $_POST['radns3'];
229
		}
230

    
231
		$config['dhcpdv6'][$if]['radvd-dns'] = ($_POST['radvd-dns']) ? "enabled" : "disabled";
232
		$config['dhcpdv6'][$if]['rasamednsasdhcp6'] = ($_POST['rasamednsasdhcp6']) ? true : false;
233

    
234
		if (count($pconfig['subnets'])) {
235
			$config['dhcpdv6'][$if]['subnets']['item'] = $pconfig['subnets'];
236
		} else {
237
			config_del_path("dhcpdv6/{$if}/subnets");
238
		}
239

    
240
		write_config("Router Advertisements settings saved");
241
		$changes_applied = true;
242
		$retval = 0;
243
		$retval |= services_radvd_configure();
244
	}
245
}
246

    
247
$pgtitle = [gettext('Services'), gettext('Router Advertisement')];
248
$pglinks = [null, 'services_radvd.php'];
249

    
250
if (!empty($if) && isset($iflist[$if])) {
251
	$pgtitle[] = $iflist[$if];
252
	$pglinks[] = 'services_radvd.php?if=' . $if;
253
}
254
$shortcut_section = 'radvd';
255

    
256
include('head.inc');
257

    
258
if ($input_errors) {
259
	print_input_errors($input_errors);
260
}
261

    
262
if ($changes_applied) {
263
	print_apply_result_box($retval);
264
}
265

    
266
if ($info_msg) {
267
	print_info_box($info_msg, 'success');
268
}
269

    
270
/* active tabs */
271
$tab_array = array();
272
$tabscounter = 0;
273
$i = 0;
274
foreach ($iflist as $ifent => $ifname) {
275
	$oc = config_get_path("interfaces/{$ifent}");
276
	// We need interfaces configured with a static IPv6 address or track6 for PD.
277
	if (!is_ipaddrv6($oc['ipaddrv6']) && $oc['ipaddrv6'] != "track6") {
278
		continue;
279
	}
280

    
281
	if ($ifent == $if) {
282
		$active = true;
283
	} else {
284
		$active = false;
285
	}
286

    
287
	$tab_array[] = array($ifname, $active, 'services_radvd.php?if='.$ifent);
288
	$tabscounter++;
289
}
290

    
291
if ($tabscounter == 0) {
292
	print_info_box(gettext('Router Advertisements can only be enabled on interfaces configured with a static IPv6 address. This system has none.'), 'danger', false);
293
	include('foot.inc');
294
	exit;
295
}
296

    
297
$valid_ra = in_array(config_get_path('dhcpdv6/'.$if.'/ramode', 'disabled'), ['managed', 'assist', 'stateless_dhcp']);
298
if (config_path_enabled('dhcpdv6/'.$if) && !$valid_ra) {
299
	print_info_box(sprintf(gettext('DHCPv6 is enabled but not being advertised to clients on %1$s. Router Advertisement must be enabled and Router Mode set to "Managed", "Assisted" or "Stateless DHCP."'), $iflist[$if]), 'danger', false);
300
}
301

    
302
display_top_tabs($tab_array);
303

    
304
$form = new Form();
305

    
306
$section = new Form_Section(gettext('Router Advertisement'));
307

    
308
$section->addInput(new Form_Select(
309
	'ramode',
310
	'*'.gettext('Router Mode'),
311
	$pconfig['ramode'],
312
	$advertise_modes
313
))->setHelp($ramode_help);
314

    
315
$section->addInput(new Form_Select(
316
	'rapriority',
317
	'*'.gettext('Router Priority'),
318
	$pconfig['rapriority'],
319
	$priority_modes
320
))->setHelp(gettext('Select the Priority for the RA Daemon.'));
321

    
322
$carplist = get_configured_vip_list("inet6", VIP_CARP);
323

    
324
$carplistif = array();
325

    
326
if (count($carplist) > 0) {
327
	foreach ($carplist as $ifname => $vip) {
328
		if (get_configured_vip_interface($ifname) == $if) {
329
			$carplistif[$ifname] = $vip;
330
		}
331
	}
332
}
333

    
334
if (count($carplistif) > 0) {
335
	$iflist = array();
336

    
337
	$iflist['interface'] = convert_friendly_interface_to_friendly_descr($if);
338
	foreach ($carplistif as $ifname => $vip) {
339
		$iflist[$ifname] = get_vip_descr($vip) . " - " . $vip;
340
	}
341

    
342
	$section->addInput(new Form_Select(
343
		'rainterface',
344
		gettext('RA Interface'),
345
		$pconfig['rainterface'],
346
		$iflist
347
	))->setHelp(gettext('Select the Interface for the Router Advertisement (RA) Daemon.'));
348
}
349

    
350
$section->addInput(new Form_Input(
351
	'ravalidlifetime',
352
	gettext('Valid Lifetime'),
353
	'number',
354
	$pconfig['ravalidlifetime'],
355
	['min' => 1, 'max' => 655350, 'placeholder' => 86400]
356
))->setHelp(gettext('The length of time in seconds (relative to the time the packet is sent) that the prefix is valid for the purpose of on-link determination.%1$s' .
357
'The default is 86400 seconds.'), '<br />');
358

    
359
$section->addInput(new Form_Input(
360
	'rapreferredlifetime',
361
	gettext('Preferred Lifetime'),
362
	'number',
363
	$pconfig['rapreferredlifetime'],
364
	['placeholder' => 14400]
365
))->setHelp(gettext('The length of time in seconds (relative to the time the packet is sent) that addresses generated from the prefix via SLAAC remain preferred.%1$s' .
366
			'The default is 14400 seconds.'), '<br />');
367

    
368
$section->addInput(new Form_Input(
369
	'raminrtradvinterval',
370
	gettext('Minimum RA Interval'),
371
	'number',
372
	$pconfig['raminrtradvinterval'],
373
	['min' => 3, 'max' => 1350, 'placeholder' => 200]
374
))->setHelp(gettext('The minimum time allowed between sending unsolicited multicast router advertisements in seconds.%1$s' .
375
'The default is 200 seconds.'), '<br />');
376

    
377
$section->addInput(new Form_Input(
378
	'ramaxrtradvinterval',
379
	gettext('Maximum RA Interval'),
380
	'number',
381
	$pconfig['ramaxrtradvinterval'],
382
	['min' => 4, 'max' => 1800, 'placeholder' => 600]
383
))->setHelp(gettext('The maximum time allowed between sending unsolicited multicast router advertisements in seconds.%1$s' .
384
'The default is 600 seconds.'), '<br />');
385

    
386
if (isset($pconfig['raadvdefaultlifetime']) &&
387
    is_numeric($pconfig['raadvdefaultlifetime'])) {
388
	$raadvdefaultlifetime = $pconfig['raadvdefaultlifetime'];
389
} elseif (isset($pconfig['ramaxrtradvinterval']) &&
390
    is_numeric($pconfig['ramaxrtradvinterval'])) {
391
	$raadvdefaultlifetime = $pconfig['ramaxrtradvinterval'] * 3;
392
} else {
393
	$raadvdefaultlifetime = 1800;
394
}
395

    
396
$section->addInput(new Form_Input(
397
	'raadvdefaultlifetime',
398
	gettext('Router Lifetime'),
399
	'number',
400
	$pconfig['raadvdefaultlifetime'],
401
	['min' => 1, 'max' => 9000, 'placeholder' => $raadvdefaultlifetime]
402
))->setHelp(gettext('The lifetime associated with the default router in seconds.%1$s' .
403
'The default is 3 * Maximum RA interval seconds.'), '<br />');
404

    
405

    
406
if (empty($pconfig['subnets'])) {
407
	$pconfig['subnets'] = array('0' => '/128');
408
}
409

    
410
$counter = 0;
411
$last = (count($pconfig['subnets']) - 1);
412
foreach ($pconfig['subnets'] as $subnet) {
413
	$group = new Form_Group(($counter == 0) ? gettext('RA Subnet(s)') : '');
414
	$group->addClass('repeatable');
415

    
416
	[$address, $subnet] = explode('/', $subnet);
417
	$group->add(new Form_IpAddress(
418
		'subnet_address' . $counter,
419
		gettext('RA Subnet'),
420
		$address,
421
		'V6'
422
	))->addClass('autotrim')
423
          ->addMask('subnet_bits' . $counter, $subnet)
424
	  ->setHelp(($counter === $last) ? gettext('Subnets are specified in CIDR format. Select the CIDR mask that pertains to each entry. If no subnets are specified here, the RA Daemon will advertise to the subnet to which the router\'s interface is assigned.') : '');
425

    
426
	$group->add(new Form_Button(
427
		'deleterow' . $counter,
428
		gettext('Delete'),
429
		null,
430
		'fa-trash'
431
	))->addClass('btn-sm btn-warning');
432

    
433
	$section->add($group);
434
	$counter++;
435
}
436

    
437
$group = new Form_Group(null);
438
$input = new Form_Button(
439
	'addrow',
440
	gettext('Add RA Subnet'),
441
	null,
442
	'fa-plus'
443
);
444
$input->addClass('btn-success');
445
$group->add($input);
446

    
447
$section->add($group);
448

    
449
$form->add($section);
450

    
451
$section = new Form_Section(gettext('DNS Configuration'));
452

    
453
$section->addInput(new Form_Checkbox(
454
	'radvd-dns',
455
	gettext('Enable DNS'),
456
	gettext('Provide DNS Configuration via the RA Daemon'),
457
	$pconfig['radvd-dns']
458
))->setHelp(gettext('Unchecking this box disables the RA Daemon RDNSS/DNSSL options. ' .
459
			'Use with caution, as the resulting behavior may violate some RFCs.'));
460

    
461
$section->addInput(new Form_Checkbox(
462
	'rasamednsasdhcp6',
463
	gettext('Mirror DHCPv6'),
464
	gettext('Mirror DHCPv6 DNS Configuration'),
465
	$pconfig['rasamednsasdhcp6']
466
))->setHelp(gettext('When checked, the DNS configuration will be copied from the primary DHCPv6 options automatically.'));
467

    
468
$ifipv6 = get_interface_ipv6($if);
469

    
470
$dns_arrv6 = [];
471
foreach (config_get_path('system/dnsserver', []) as $dnsserver) {
472
	if (is_ipaddrv6($dnsserver)) {
473
		$dns_arrv6[] = $dnsserver;
474
	}
475
}
476

    
477
if (config_path_enabled('dnsmasq') ||
478
    config_path_enabled('unbound')) {
479
	$dns_arrv6 = [$ifipv6];
480
}
481

    
482
if (is_numeric($pool) || ($act === 'newpool')) {
483
	$subnet_dnsservers = config_get_path('dhcpdv6/'.$if.'/dnsserver', []);
484
	if (!empty($subnet_dnsservers)) {
485
		$dns_arrv6 = $subnet_dnsservers;
486
	}
487
}
488

    
489
for ($idx = 1; $idx <= 4; $idx++) {
490
	$last = $section->addInput(new Form_IpAddress(
491
		'radns' . $idx,
492
		gettext('DNS Server') . ' '. $idx,
493
		$pconfig['radns' . $idx],
494
		'ALIASV6'
495
	))->addClass('autotrim')
496
	  ->setAttribute('placeholder', $dns_arrv6[$idx - 1] ?? sprintf(gettext('DNS Server %s'), $idx));
497
}
498
$last->setHelp(gettext('Leave blank to use the IP address of this firewall interface if DNS Resolver or Forwarder is enabled, the servers configured in General settings or those obtained dynamically.'));
499

    
500
$section->addInput(new Form_Input(
501
	'radomainsearchlist',
502
	gettext('Domain Search List'),
503
	'text',
504
	$pconfig['radomainsearchlist']
505
))->addClass('autotrim')
506
  ->setAttribute('placeholder', 'example.com;sub.example.com')
507
  ->setHelp(gettext('The RA Daemon can optionally provide a domain search list. Use the semicolon character as separator.'));
508

    
509
$form->addGlobal(new Form_Input(
510
	'if',
511
	null,
512
	'hidden',
513
	$if
514
));
515

    
516

    
517
$form->add($section);
518
print($form);
519
?>
520

    
521
<script type="text/javascript">
522
//<![CDATA[
523
events.push(function() {
524
	// Suppress "Delete row" button if there are fewer than two rows
525
	checkLastRow();
526

    
527
	// --------- Autocomplete -----------------------------------------------------------------------------------------
528
	var addressarray = <?= json_encode(get_alias_list(array("host", "network", "urltable"))) ?>;
529

    
530
	$('#radns1, #radns2, #radns3, #radns4').autocomplete({
531
		source: addressarray
532
	});
533

    
534
});
535
//]]>
536
</script>
537

    
538
<?php
539
include("foot.inc");
(137-137/228)