Project

General

Profile

« Previous | Next » 

Revision d2eee7c8

Added by Steve Beaver over 4 years ago

Refactor firewall_nat_out for MVC

View differences:

src/usr/local/pfSense/include/www/firewall_nat_out.inc
1
<?php
2
/*
3
 * firewall_nat_out.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2014-2021 Rubicon Communications, LLC (Netgate)
7
 * All rights reserved.
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 * http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21

  
22
// Functions to support firewall_nat_out.php and firewall_nat_out_edit.php
23

  
24
require_once("config.gui.inc");
25
require_once("interfaces.inc");
26
require_once("util.inc");
27
require_once("pfsense-utils.inc");
28
require_once("ipsec.inc");
29
require_once("filter.inc");
30

  
31
// Save Outbound mode
32
function saveNAToutMode($post, $json = false) {
33
	global $config;
34

  
35
	if (!isset($config['nat']['outbound']['mode'])) {
36
		$config['nat']['outbound']['mode'] = "automatic";
37
	}
38
	
39
	$mode = $config['nat']['outbound']['mode'];
40

  
41
	/* mutually exclusive settings - if user wants advanced NAT, we don't generate automatic rules */
42
	if ($post['mode'] == "advanced" && ($mode == "automatic" || $mode == "hybrid")) {
43
		/*
44
		 *	user has enabled advanced outbound NAT and doesn't have rules
45
		 *	lets automatically create entries
46
		 *	for all of the interfaces to make life easier on the pip-o-chap
47
		 */
48
		if (empty($FilterIflist)) {
49
			filter_generate_optcfg_array();
50
		}
51

  
52
		if (empty($GatewaysList)) {
53
			filter_generate_gateways();
54
		}
55

  
56
		$tonathosts = filter_nat_rules_automatic_tonathosts(true);
57
		$automatic_rules = filter_nat_rules_outbound_automatic("");
58

  
59
		foreach ($tonathosts as $tonathost) {
60
			foreach ($automatic_rules as $natent) {
61
				$natent['source']['network'] = $tonathost['subnet'];
62
				$natent['descr'] .= sprintf(gettext(' - %1$s to %2$s'),
63
					$tonathost['descr'],
64
					convert_real_interface_to_friendly_descr($natent['interface']));
65
				$natent['created'] = make_config_revision_entry(null, gettext("Manual Outbound NAT Switch"));
66

  
67
				/* Try to detect already auto created rules and avoid duplicating them */
68
				$found = false;
69
				foreach ($a_out as $rule) {
70
					if ($rule['interface'] == $natent['interface'] &&
71
					    $rule['source']['network'] == $natent['source']['network'] &&
72
					    $rule['dstport'] == $natent['dstport'] &&
73
					    $rule['target'] == $natent['target'] &&
74
					    $rule['descr'] == $natent['descr']) {
75
						$found = true;
76
						break;
77
					}
78
				}
79

  
80
				if ($found === false) {
81
					$a_out[] = $natent;
82
				}
83
			}
84
		}
85
		$default_rules_msg = gettext("Default rules for each interface have been created.");
86
		unset($FilterIflist, $GatewaysList);
87
	}
88

  
89
	$config['nat']['outbound']['mode'] = $post['mode'];
90

  
91
	if (write_config(gettext("Firewall: NAT: Outbound - saved outbound NAT settings."))) {
92
		mark_subsystem_dirty('natconf');
93
	}
94

  
95
	if (!$json) {
96
		header("Location: firewall_nat_out.php");
97
		exit;
98
	}
99
}
100
// Save Outbound rule
101
function saveoutNATrule($post, $id, $json = false) {
102
	global $config;
103

  
104
	init_config_arr(array('nat', 'outbound', 'rule'));
105
	$a_out = &$config['nat']['outbound']['rule'];
106

  
107
	if ($post['destination_type'] == "any") {
108
		$post['destination'] = "any";
109
		$post['destination_subnet'] = 24;
110
	}
111

  
112
	if ($post['source_type'] == "any") {
113
		$post['source'] = "any";
114
		$post['source_subnet'] = 24;
115
	} elseif ($post['source_type'] == "(self)") {
116
		$post['source'] = "(self)";
117
		$post['source_subnet'] = 24;
118
	}
119

  
120
	unset($input_errors);
121
	$pconfig = $post;
122

  
123
	/*  run through $post items encoding HTML entitles so that the user
124
	 *  cannot think he is slick and perform a XSS attack on the unwilling
125
	 */
126
	foreach ($post as $key => $value) {
127
		if ($key == 'descr') {
128
			continue;
129
		}
130

  
131
		$temp = str_replace(">", "", $value);
132
		$newpost = htmlentities($temp);
133
		if ($newpost <> $temp) {
134
			$input_errors[] = sprintf(gettext("Invalid characters detected (%s).  Please remove invalid characters and save again."), $temp);
135
		}
136
	}
137

  
138
	/* input validation */
139
	$reqdfields = explode(" ", "interface protocol source source_subnet destination destination_subnet");
140
	$reqdfieldsn = array(gettext("Interface"), gettext("Protocol"), gettext("Source"), gettext("Source bit count"), gettext("Destination"), gettext("Destination bit count"));
141

  
142
	do_input_validation($post, $reqdfields, $reqdfieldsn, $input_errors);
143

  
144
	$protocol_uses_ports = in_array($post['protocol'], explode(" ", "any tcp udp tcp/udp"));
145

  
146
	if ($post['source']) {
147
		$post['source'] = trim($post['source']);
148
	}
149
	if ($post['destination']) {
150
		$post['destination'] = trim($post['destination']);
151
	}
152
	if ($post['targetip']) {
153
		$post['targetip'] = trim($post['targetip']);
154
	}
155
	if ($post['sourceport']) {
156
		$post['sourceport'] = trim($post['sourceport']);
157
	}
158
	if ($post['dstport']) {
159
		$post['dstport'] = trim($post['dstport']);
160
	}
161
	if ($post['natport']) {
162
		$post['natport'] = trim($post['natport']);
163
	}
164

  
165
	if (strlen($post['target']) > 0) {
166
		// Strip the target code 1-char code from the front before validating and saving.
167
		$post['target'] = substr($post['target'], 1);
168
	}
169

  
170
	if ($protocol_uses_ports && $post['sourceport'] <> "" && !is_port_or_range_or_alias($post['sourceport'])) {
171
		$input_errors[] = gettext("A valid port or port alias must be supplied for the source port entry.");
172
	}
173

  
174
	if ($protocol_uses_ports && $post['dstport'] <> "" && !is_port_or_range_or_alias($post['dstport'])) {
175
		$input_errors[] = gettext("A valid port or port alias must be supplied for the destination port entry.");
176
	}
177

  
178
	if ($protocol_uses_ports && $post['natport'] <> "" && !is_port_or_range_or_alias($post['natport']) && !isset($post['nonat'])) {
179
		$input_errors[] = gettext("A valid port must be supplied for the NAT port entry.");
180
	}
181

  
182
	if (($post['source_type'] != "any") && ($post['source_type'] != "(self)")) {
183
		if ($post['source'] && !is_ipaddroralias($post['source']) && $post['source'] != "any") {
184
			$input_errors[] = gettext("A valid source must be specified.");
185
		}
186
	}
187

  
188
	if ($post['source_subnet'] && !is_numericint($post['source_subnet'])) {
189
		$input_errors[] = gettext("A valid source bit count must be specified.");
190
	}
191

  
192
	if ($post['destination_type'] != "any") {
193
		if ($post['destination'] && !is_ipaddroralias($post['destination'])) {
194
			$input_errors[] = gettext("A valid destination must be specified.");
195
		}
196
	}
197

  
198
	if ($post['destination_subnet'] && !is_numericint($post['destination_subnet'])) {
199
		$input_errors[] = gettext("A valid destination bit count must be specified.");
200
	}
201

  
202
	if ($post['destination_type'] == "any") {
203
		if ($post['destination_not']) {
204
			$input_errors[] = gettext("Negating destination address of \"any\" is invalid.");
205
		}
206
	}
207

  
208
	if ($post['target'] && !is_ipaddr($post['target']) && !is_subnet($post['target']) && !is_alias($post['target']) && !isset($post['nonat']) && !($post['target'] == "other-subnet")) {
209
		$input_errors[] = gettext("A valid target IP address must be specified.");
210
	}
211

  
212
	if ($post['target'] == "other-subnet") {
213
		if (!is_ipaddr($post['targetip'])) {
214
			$input_errors[] = gettext("A valid target IP must be specified when using the 'Other Subnet' type.");
215
		}
216

  
217
		if (!is_numericint($post['targetip_subnet'])) {
218
			$input_errors[] = gettext("A valid target bit count must be specified when using the 'Other Subnet' type.");
219
		}
220
	}
221

  
222
	/* Verify Pool Options */
223
	$poolopts = "";
224
	$source_hash_key = "";
225
	if ($post['poolopts']) {
226
		if (is_subnet($post['target']) || ($post['target'] == "other-subnet")) {
227
			$poolopts = $post['poolopts'];
228
		} elseif (is_alias($post['target'])) {
229
			if (substr($post['poolopts'], 0, 11) == "round-robin") {
230
				$poolopts = $post['poolopts'];
231
			} else {
232
				$input_errors[] = gettext("Only Round Robin pool options may be chosen when selecting an alias.");
233
			}
234
		}
235
		/* If specified, verify valid source-hash key or generate a valid key using md5 */
236
		if ($post['source_hash_key']) {
237
			if (substr($post['source_hash_key'],0,2) == "0x") {
238
				if (ctype_xdigit(substr($post['source_hash_key'],2)) && strlen($post['source_hash_key']) == 34) {
239
					$source_hash_key = $post['source_hash_key'];
240
				} else {
241
					$input_errors[] = gettext("Incorrect format for source-hash key, \"0x\" must be followed by exactly 32 hexadecimal characters.");
242
				}
243
			} else {
244
				$source_hash_key = "0x".md5($post['source_hash_key']);
245
			}
246
		}
247
	}
248

  
249
	/* if user has selected any as source, set it here */
250
	if ($post['source_type'] == "any") {
251
		$osn = "any";
252
	} else if ($post['source_type'] == "(self)") {
253
		$osn = "(self)";
254
	} else if (is_alias($post['source'])) {
255
		$osn = $post['source'];
256
	} else {
257
		$osn = gen_subnet($post['source'], $post['source_subnet']) . "/" . $post['source_subnet'];
258
	}
259

  
260
	/* check for existing entries */
261
	if ($post['destination_type'] == "any") {
262
		$ext = "any";
263
	} else if (is_alias($post['destination'])) {
264
		$ext = $post['destination'];
265
	} else {
266
		$ext = gen_subnet($post['destination'], $post['destination_subnet']) . "/" . $post['destination_subnet'];
267
	}
268

  
269
	foreach ($a_out as $natent) {
270
		if (isset($id) && ($a_out[$id]) && ($a_out[$id] === $natent)) {
271
			continue;
272
		}
273

  
274
		if (!$natent['interface']) {
275
			$natent['interface'] = "wan";
276
		}
277
	}
278

  
279
	if (!$input_errors) {
280
		$natent = array();
281
		$natent['source']['network'] = $osn;
282
		$natent['sourceport'] = ($protocol_uses_ports) ? $post['sourceport'] : "";
283
		$natent['descr'] = $post['descr'];
284
		$natent['target'] = (!isset($post['nonat'])) ? $post['target'] : "";
285
		$natent['targetip'] = (!isset($post['nonat'])) ? $post['targetip'] : "";
286
		$natent['targetip_subnet'] = (!isset($post['nonat'])) ? $post['targetip_subnet'] : "";
287
		$natent['interface'] = $post['interface'];
288
		$natent['poolopts'] = $poolopts;
289
		$natent['source_hash_key'] = $source_hash_key;
290

  
291
		/* static-port */
292
		if (isset($post['staticnatport']) && $protocol_uses_ports && !isset($post['nonat'])) {
293
			$natent['staticnatport'] = true;
294
		} else {
295
			unset($natent['staticnatport']);
296
		}
297

  
298
		if (isset($post['disabled'])) {
299
			$natent['disabled'] = true;
300
		} else {
301
			unset($natent['disabled']);
302
		}
303

  
304
		/* if user has selected not nat, set it here */
305
		if (isset($post['nonat'])) {
306
			$natent['nonat'] = true;
307
		} else {
308
			unset($natent['nonat']);
309
		}
310

  
311
		if ($post['ipprotocol'] && $post['ipprotocol'] != "inet46") {
312
			$natent['ipprotocol'] = $post['ipprotocol'];
313
		} else {
314
			unset($natent['ipprotocol']);
315
		}
316
		
317
		if ($post['protocol'] && $post['protocol'] != "any") {
318
			$natent['protocol'] = $post['protocol'];
319
		} else {
320
			unset($natent['protocol']);
321
		}
322

  
323
		if ($ext == "any") {
324
			$natent['destination']['any'] = true;
325
		} else {
326
			$natent['destination']['address'] = $ext;
327
		}
328
		if ($post['natport'] != "" && $protocol_uses_ports && !isset($post['nonat'])) {
329
				$natent['natport'] = $post['natport'];
330
		} else {
331
			unset($natent['natport']);
332
		}
333
		if ($post['dstport'] != "" && $protocol_uses_ports) {
334
			$natent['dstport'] = $post['dstport'];
335
		} else {
336
			unset($natent['dstport']);
337
		}
338

  
339
		if ($post['nosync'] == "yes") {
340
			$natent['nosync'] = true;
341
		} else {
342
			unset($natent['nosync']);
343
		}
344

  
345
		if (isset($post['destination_not']) && $ext != "any") {
346
			$natent['destination']['not'] = true;
347
		}
348

  
349
		if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) {
350
			$natent['created'] = $a_out[$id]['created'];
351
		}
352

  
353
		$natent['updated'] = make_config_revision_entry();
354

  
355
		// Allow extending of the firewall edit page and include custom input validation
356
		pfSense_handle_custom_code("/usr/local/pkg/firewall_aon/pre_write_config");
357

  
358
		if (isset($id) && $a_out[$id]) {
359
			$a_out[$id] = $natent;
360
		} else {
361
			$natent['created'] = make_config_revision_entry();
362
			if (is_numeric($after)) {
363
				array_splice($a_out, $after+1, 0, array($natent));
364
			} else {
365
				$a_out[] = $natent;
366
			}
367
		}
368

  
369
		if (write_config(gettext("Firewall: NAT: Outbound - saved/edited outbound NAT mapping.")) && !$json) {
370
			mark_subsystem_dirty('natconf');
371
		}
372

  
373
		$rv = array();
374
		$rv['input_errors'] = $input_errors;
375
		$rv['pconfig'] = $pconfig;
376
	}
377

  
378
	return $json ? json_encode($rv) : $rv;
379
}
380

  
381
// Retrieve the specified Outbound rule
382
function getoutNATrule($id, $json = false) {
383
	global $config;
384

  
385
	init_config_arr(array('nat', 'outbound', 'rule'));
386
	$a_out = &$config['nat']['outbound']['rule'];
387

  
388
	$pconfig = array();
389

  
390
	if (isset($id) && $a_out[$id]) {
391
		if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) {
392
			$pconfig['created'] = $a_out[$id]['created'];
393
		}
394
	
395
		if (isset($a_out[$id]['updated']) && is_array($a_out[$id]['updated'])) {
396
			$pconfig['updated'] = $a_out[$id]['updated'];
397
		}
398
	
399
		$pconfig['ipprotocol'] = $a_out[$id]['ipprotocol'];
400
		$pconfig['protocol'] = $a_out[$id]['protocol'];
401
		list($pconfig['source'], $pconfig['source_subnet']) = explode('/', $a_out[$id]['source']['network']);
402
		if (!is_numeric($pconfig['source_subnet'])) {
403
			$pconfig['source_subnet'] = 32;
404
		}
405
		$pconfig['sourceport'] = $a_out[$id]['sourceport'];
406
		address_to_pconfig($a_out[$id]['destination'], $pconfig['destination'],
407
			$pconfig['destination_subnet'], $pconfig['destination_not'],
408
			$none, $none);
409
	
410
		$pconfig['dstport'] = $a_out[$id]['dstport'];
411
		$pconfig['natport'] = $a_out[$id]['natport'];
412
		$pconfig['target'] = $a_out[$id]['target'];
413
		if (strlen($pconfig['target']) > 0) {
414
			// Deduce the target type and add to the front of the target string.
415
			if (is_subnet($pconfig['target'])) {
416
				$target_type = "S";
417
			} elseif (is_ipaddr($pconfig['target'])) {
418
				$target_type = "I";
419
			} elseif (is_alias($pconfig['target'])) {
420
				$target_type = "H";
421
			} else {
422
				$target_type = "O";
423
			}
424
			$pconfig['target'] = $target_type . $pconfig['target'];
425
		}
426
	
427
		$pconfig['targetip'] = $a_out[$id]['targetip'];
428
		$pconfig['targetip_subnet'] = $a_out[$id]['targetip_subnet'];
429
		$pconfig['poolopts'] = $a_out[$id]['poolopts'];
430
		$pconfig['source_hash_key'] = $a_out[$id]['source_hash_key'];
431
		$pconfig['interface'] = $a_out[$id]['interface'];
432
	
433
		if (!$pconfig['interface']) {
434
			$pconfig['interface'] = "wan";
435
		}
436
	
437
		$pconfig['descr'] = $a_out[$id]['descr'];
438
		$pconfig['nonat'] = $a_out[$id]['nonat'];
439
		$pconfig['disabled'] = isset($a_out[$id]['disabled']);
440
		$pconfig['staticnatport'] = isset($a_out[$id]['staticnatport']);
441
		$pconfig['nosync'] = isset($a_out[$id]['nosync']);
442
	} else {
443
		$pconfig['source_subnet'] = 24;
444
		$pconfig['destination'] = "any";
445
		$pconfig['destination_subnet'] = 24;
446
		$pconfig['interface'] = "wan";
447
	}
448

  
449
	return $json ? json_encode($pconfig):$pconfig;
450
}
451

  
452
// Toggle enabled/disabled status of an Outbound rule
453
function toggleoutNATrule($post, $json = false) {
454
	global $config;
455

  
456
	init_config_arr(array('nat', 'outbound', 'rule'));
457
	$a_out = &$config['nat']['outbound']['rule'];
458

  
459
	if (isset($a_out[$post['id']]['disabled'])) {
460
		unset($a_out[$post['id']]['disabled']);
461
		$wc_msg = gettext('Firewall: NAT: Outbound - enabled a NAT Outbound rule.');
462
	} else {
463
		$a_out[$post['id']]['disabled'] = true;
464
		$wc_msg = gettext('Firewall: NAT: Outbound - disabled a NAT Outbound rule.');
465
	}
466

  
467
	if (write_config($wc_msg) && !$json) {
468
		mark_subsystem_dirty('natconf');
469
	}
470

  
471
	if (!$json) {
472
		header("Location: firewall_nat_out.php");
473
		exit;
474
	}
475
}
476

  
477
// Delete multiple Outbound rules
478
function deleteMultipleoutNATrules($post, $json = false) {
479
	global $config;
480

  
481
	init_config_arr(array('nat', 'outbound', 'rule'));
482
	$a_out = &$config['nat']['outbound']['rule'];
483

  
484
	foreach ($post['rule'] as $rulei) {
485
		unset($a_out[$rulei]);
486
	}
487

  
488
	if (write_config(gettext("Firewall: NAT: Outbound - deleted selected outbound mappings.")) && !$json) {
489
		mark_subsystem_dirty('natconf');
490
	}
491

  
492
	if (!$json) {
493
		header("Location: firewall_nat_out.php");
494
		exit;
495
	}
496
}
497

  
498
// Delete outbound rule
499
function deleteoutNATrule($post, $json = false) {
500
	global $config;
501

  
502
	init_config_arr(array('nat', 'outbound', 'rule'));
503
	$a_out = &$config['nat']['outbound']['rule'];
504

  
505
	unset($a_out[$post['id']]);
506
	if (write_config(gettext("Firewall: NAT: Outbound - deleted NPt mapping.")) && !$json) {
507
		mark_subsystem_dirty('natconf');
508
	}
509

  
510
	if(!$json) {
511
		header("Location: firewall_nat_out.php");
512
		exit;
513
	}
514
}
515

  
516
// Re-order the NPtNAT rules per the array of iindicies passed in $post
517
function reorderoutNATrules($post, $json = false) {
518
	global $config;
519

  
520
	if (is_array($post['rule']) && !empty($post['rule'])) {
521
		init_config_arr(array('nat', 'outbound', 'rule'));
522
		$a_out = &$config['nat']['outbound']['rule'];
523
		$a_out_new = array();
524

  
525
		// if a rule is not in POST[rule], it has been deleted by the user
526
		foreach ($post['rule'] as $id) {
527
			$a_out_new[] = $a_out[$id];
528
		}
529

  
530
		$a_out = $a_out_new;
531

  
532
		if (write_config(gettext("Firewall: NAT: Outbound - reordered outbound mappings.")) && !$json) {
533
			mark_subsystem_dirty('natconf');
534
		}
535

  
536
		if (!$json) {
537
			header("Location: firewall_nat_out.php");
538
			exit;
539
		}
540
	}
541
}
542

  
543
function applyoutNATrules() {
544
	$retval = 0;
545
	$retval |= filter_configure();
546

  
547
	if ($retval == 0) {
548
		clear_subsystem_dirty('natconf');
549
		clear_subsystem_dirty('filter');
550
	}
551

  
552
	return $retval;
553
}
554

  
555
function build_target_list() {
556
	global $config;
557

  
558
	init_config_arr(array('aliases', 'alias'));
559
	$a_aliases = &$config['aliases']['alias'];
560

  
561
	$list = array();
562
	// Target list entries are made to start with the following characters:
563
	// "" (blank) - the interface address of the selected interface
564
	// S - a subnet
565
	// I - an ordinary IP address
566
	// H - a host alias
567
	// O - other subnet
568
	// The prefix letter makes it easy for the JavaScript to distinguish
569
	// the type of entry based on the first letter of the value.
570
	// The prefix letter is removed before saving in the config,
571
	// and added back when reading from the config.
572

  
573
	$list[""] = gettext('Interface Address');
574

  
575
	//Temporary array so we can sort IPs
576
	$templist = array();
577
	if (is_array($config['virtualip']['vip'])) {
578
		foreach ($config['virtualip']['vip'] as $sn) {
579
			if (($sn['mode'] == "proxyarp" || $sn['mode'] == "other") && $sn['type'] == "network") {
580
				$templist['S' . $sn['subnet'] . '/' . $sn['subnet_bits']] = gettext('Subnet: ') . $sn['subnet'] . '/' . $sn['subnet_bits'] . ' (' . $sn['descr'] . ')';
581
				if (isset($sn['noexpand'])) {
582
					continue;
583
				}
584
				$start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits']));
585
				$end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits']));
586
				$len = $end - $start;
587
				for ($i = 0; $i <= $len; $i++) {
588
					$snip = long2ip32($start+$i);
589

  
590
					$templist['I' . $snip] = $snip . ' (' . $sn['descr'] . ')';
591
				}
592
			} else {
593
				$templist['I' . $sn['subnet']] = $sn['subnet'] . ' (' . $sn['descr'] . ')';
594
			}
595
		}
596
	}
597
	asort($templist);
598
	//Append sorted IP array onto main array
599
	$list = array_merge($list, $templist);
600
	unset($templist);
601

  
602
	foreach ($a_aliases as $alias) {
603
		if ($alias['type'] != "host") {
604
			continue;
605
		}
606

  
607
		$list['H' . $alias['name']] = gettext('Host Alias: ') . $alias['name'] . ' (' . $alias['descr'] . ')';
608
	}
609

  
610
	$list['Oother-subnet'] = gettext('Other Subnet (Enter Below)');
611

  
612
	return($list);
613
}
614
?>
src/usr/local/www/firewall_nat_out.php
36 36
require_once("functions.inc");
37 37
require_once("filter.inc");
38 38
require_once("shaper.inc");
39
require_once("firewall_nat_out.inc");
39 40

  
40 41
global $FilterIflist;
41 42
global $GatewaysList;
......
46 47
// update rule order, POST[rule] is an array of ordered IDs
47 48
// All rule are 'checked' before posting
48 49
if (isset($_REQUEST['order-store'])) {
49
	if (is_array($_REQUEST['rule']) && !empty($_REQUEST['rule'])) {
50

  
51
		$a_out_new = array();
52

  
53
		// if a rule is not in POST[rule], it has been deleted by the user
54
		foreach ($_REQUEST['rule'] as $id) {
55
			$a_out_new[] = $a_out[$id];
56
		}
57

  
58
		$a_out = $a_out_new;
59

  
60
		if (write_config(gettext("Firewall: NAT: Outbound - reordered outbound NAT mappings."))) {
61
			mark_subsystem_dirty('natconf');
62
		}
63

  
64
		header("Location: firewall_nat_out.php");
65
		exit;
66

  
67
	}
50
	reorderoutNATrules($_POST);
68 51
}
69 52

  
70 53
if (!isset($config['nat']['outbound']['mode'])) {
......
74 57
$mode = $config['nat']['outbound']['mode'];
75 58

  
76 59
if ($_POST['apply']) {
77
	$retval = 0;
78
	$retval |= filter_configure();
79

  
80
	if ($retval == 0) {
81
		clear_subsystem_dirty('natconf');
82
		clear_subsystem_dirty('filter');
83
	}
84
}
85

  
86
if ($_POST['save']) {
87
	/* mutually exclusive settings - if user wants advanced NAT, we don't generate automatic rules */
88
	if ($_POST['mode'] == "advanced" && ($mode == "automatic" || $mode == "hybrid")) {
89
		/*
90
		 *	user has enabled advanced outbound NAT and doesn't have rules
91
		 *	lets automatically create entries
92
		 *	for all of the interfaces to make life easier on the pip-o-chap
93
		 */
94
		if (empty($FilterIflist)) {
95
			filter_generate_optcfg_array();
96
		}
97

  
98
		if (empty($GatewaysList)) {
99
			filter_generate_gateways();
100
		}
101

  
102
		$tonathosts = filter_nat_rules_automatic_tonathosts(true);
103
		$automatic_rules = filter_nat_rules_outbound_automatic("");
104

  
105
		foreach ($tonathosts as $tonathost) {
106
			foreach ($automatic_rules as $natent) {
107
				$natent['source']['network'] = $tonathost['subnet'];
108
				$natent['descr'] .= sprintf(gettext(' - %1$s to %2$s'),
109
					$tonathost['descr'],
110
					convert_real_interface_to_friendly_descr($natent['interface']));
111
				$natent['created'] = make_config_revision_entry(null, gettext("Manual Outbound NAT Switch"));
112

  
113
				/* Try to detect already auto created rules and avoid duplicating them */
114
				$found = false;
115
				foreach ($a_out as $rule) {
116
					if ($rule['interface'] == $natent['interface'] &&
117
					    $rule['source']['network'] == $natent['source']['network'] &&
118
					    $rule['dstport'] == $natent['dstport'] &&
119
					    $rule['target'] == $natent['target'] &&
120
					    $rule['descr'] == $natent['descr']) {
121
						$found = true;
122
						break;
123
					}
124
				}
125

  
126
				if ($found === false) {
127
					$a_out[] = $natent;
128
				}
129
			}
130
		}
131
		$default_rules_msg = gettext("Default rules for each interface have been created.");
132
		unset($FilterIflist, $GatewaysList);
133
	}
134

  
135
	$config['nat']['outbound']['mode'] = $_POST['mode'];
136

  
137
	if (write_config(gettext("Firewall: NAT: Outbound - saved outbound NAT settings."))) {
138
		mark_subsystem_dirty('natconf');
139
	}
140

  
141
	header("Location: firewall_nat_out.php");
142
	exit;
143
}
144

  
145
//	Delete a single rule/map
146
if ($_POST['act'] == "del") {
147

  
148
	if ($a_out[$_POST['id']]) {
149
		unset($a_out[$_POST['id']]);
150
		if (write_config(gettext("Firewall: NAT: Outbound - deleted outbound NAT mapping."))) {
151
			mark_subsystem_dirty('natconf');
152
		}
153

  
154
	header("Location: firewall_nat_out.php");
155
	exit;
156
	}
157
}
158

  
159
// Delete multiple maps Only checked rules will be in the
160
// POST
161
if (isset($_POST['del_x'])) {
60
	$retval = applyoutNATrules();
61
} else if ($_POST['save']) {
62
	saveNAToutMode($_POST);
63
} else if ($_POST['act'] == "del") {
64
	deleteoutNATrule($_POST);
65
} else if (isset($_POST['del_x'])) {
162 66
	/* delete selected rules */
163
	print('Deleting rows<br />');
164

  
165
	if (is_array($_POST['rule']) && count($_POST['rule'])) {
166
		foreach ($_POST['rule'] as $rulei) {
167
			print('Deleting ' . $rulei . '<br />');
168
			unset($a_out[$rulei]);
169
		}
170

  
171
		if (write_config(gettext("Firewall: NAT: Outbound - deleted selected outbound NAT mappings."))) {
172
			mark_subsystem_dirty('natconf');
173
		}
174

  
175
		header("Location: firewall_nat_out.php");
176
		exit;
177
	}
178

  
67
	deleteMultipleoutNATrules($_POST);
179 68
} else if ($_POST['act'] == "toggle") {
180
	if ($a_out[$_POST['id']]) {
181
		if (isset($a_out[$_POST['id']]['disabled'])) {
182
			unset($a_out[$_POST['id']]['disabled']);
183
			$wc_msg = gettext('Firewall: NAT: Outbound - enabled outbound NAT rule.');
184
		} else {
185
			$a_out[$_POST['id']]['disabled'] = true;
186
			$wc_msg = gettext('Firewall: NAT: Outbound - disabled outbound NAT rule.');
187
		}
188
		if (write_config($wc_msg)) {
189
			mark_subsystem_dirty('natconf');
190
		}
191

  
192
		header("Location: firewall_nat_out.php");
193
		exit;
194
	}
69
	toggleoutNATrule($_POST);
195 70
}
196 71

  
197 72
$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("Outbound"));
src/usr/local/www/firewall_nat_out_edit.php
36 36
require_once("ipsec.inc");
37 37
require_once("filter.inc");
38 38
require_once("shaper.inc");
39
require_once("firewall_nat_out.inc");
39 40

  
40 41
init_config_arr(array('nat', 'outbound', 'rule'));
41 42
$a_out = &$config['nat']['outbound']['rule'];
42 43

  
43
init_config_arr(array('aliases', 'alias'));
44
$a_aliases = &$config['aliases']['alias'];
45

  
46 44
if (isset($_REQUEST['id']) && is_numericint($_REQUEST['id'])) {
47 45
	$id = $_REQUEST['id'];
48 46
}
......
56 54
	$after = $_REQUEST['dup'];
57 55
}
58 56

  
59
if (isset($id) && $a_out[$id]) {
60
	if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) {
61
		$pconfig['created'] = $a_out[$id]['created'];
62
	}
63

  
64
	if (isset($a_out[$id]['updated']) && is_array($a_out[$id]['updated'])) {
65
		$pconfig['updated'] = $a_out[$id]['updated'];
66
	}
67

  
68
	$pconfig['ipprotocol'] = $a_out[$id]['ipprotocol'];
69
	$pconfig['protocol'] = $a_out[$id]['protocol'];
70
	list($pconfig['source'], $pconfig['source_subnet']) = explode('/', $a_out[$id]['source']['network']);
71
	if (!is_numeric($pconfig['source_subnet'])) {
72
		$pconfig['source_subnet'] = 32;
73
	}
74
	$pconfig['sourceport'] = $a_out[$id]['sourceport'];
75
	address_to_pconfig($a_out[$id]['destination'], $pconfig['destination'],
76
		$pconfig['destination_subnet'], $pconfig['destination_not'],
77
		$none, $none);
78

  
79
	$pconfig['dstport'] = $a_out[$id]['dstport'];
80
	$pconfig['natport'] = $a_out[$id]['natport'];
81
	$pconfig['target'] = $a_out[$id]['target'];
82
	if (strlen($pconfig['target']) > 0) {
83
		// Deduce the target type and add to the front of the target string.
84
		if (is_subnet($pconfig['target'])) {
85
			$target_type = "S";
86
		} elseif (is_ipaddr($pconfig['target'])) {
87
			$target_type = "I";
88
		} elseif (is_alias($pconfig['target'])) {
89
			$target_type = "H";
90
		} else {
91
			$target_type = "O";
92
		}
93
		$pconfig['target'] = $target_type . $pconfig['target'];
94
	}
95

  
96
	$pconfig['targetip'] = $a_out[$id]['targetip'];
97
	$pconfig['targetip_subnet'] = $a_out[$id]['targetip_subnet'];
98
	$pconfig['poolopts'] = $a_out[$id]['poolopts'];
99
	$pconfig['source_hash_key'] = $a_out[$id]['source_hash_key'];
100
	$pconfig['interface'] = $a_out[$id]['interface'];
101

  
102
	if (!$pconfig['interface']) {
103
		$pconfig['interface'] = "wan";
104
	}
105

  
106
	$pconfig['descr'] = $a_out[$id]['descr'];
107
	$pconfig['nonat'] = $a_out[$id]['nonat'];
108
	$pconfig['disabled'] = isset($a_out[$id]['disabled']);
109
	$pconfig['staticnatport'] = isset($a_out[$id]['staticnatport']);
110
	$pconfig['nosync'] = isset($a_out[$id]['nosync']);
111
} else {
112
	$pconfig['source_subnet'] = 24;
113
	$pconfig['destination'] = "any";
114
	$pconfig['destination_subnet'] = 24;
115
	$pconfig['interface'] = "wan";
116
}
117

  
118 57
if (isset($_REQUEST['dup']) && is_numericint($_REQUEST['dup'])) {
119 58
	unset($id);
120 59
}
121 60

  
122 61
if ($_POST['save']) {
123
	if ($_POST['destination_type'] == "any") {
124
		$_POST['destination'] = "any";
125
		$_POST['destination_subnet'] = 24;
126
	}
127

  
128
	if ($_POST['source_type'] == "any") {
129
		$_POST['source'] = "any";
130
		$_POST['source_subnet'] = 24;
131
	} elseif ($_POST['source_type'] == "(self)") {
132
		$_POST['source'] = "(self)";
133
		$_POST['source_subnet'] = 24;
134
	}
135

  
136
	unset($input_errors);
137
	$pconfig = $_POST;
138
	/*  run through $_POST items encoding HTML entitles so that the user
139
	 *  cannot think he is slick and perform a XSS attack on the unwilling
140
	 */
141
	foreach ($_POST as $key => $value) {
142
		if ($key == 'descr') {
143
			continue;
144
		}
145

  
146
		$temp = str_replace(">", "", $value);
147
		$newpost = htmlentities($temp);
148
		if ($newpost <> $temp) {
149
			$input_errors[] = sprintf(gettext("Invalid characters detected (%s).  Please remove invalid characters and save again."), $temp);
150
		}
151
	}
152

  
153
	/* input validation */
154
	$reqdfields = explode(" ", "interface protocol source source_subnet destination destination_subnet");
155
	$reqdfieldsn = array(gettext("Interface"), gettext("Protocol"), gettext("Source"), gettext("Source bit count"), gettext("Destination"), gettext("Destination bit count"));
156

  
157
	do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
158

  
159
	$protocol_uses_ports = in_array($_POST['protocol'], explode(" ", "any tcp udp tcp/udp"));
160

  
161
	if ($_POST['source']) {
162
		$_POST['source'] = trim($_POST['source']);
163
	}
164
	if ($_POST['destination']) {
165
		$_POST['destination'] = trim($_POST['destination']);
166
	}
167
	if ($_POST['targetip']) {
168
		$_POST['targetip'] = trim($_POST['targetip']);
169
	}
170
	if ($_POST['sourceport']) {
171
		$_POST['sourceport'] = trim($_POST['sourceport']);
172
	}
173
	if ($_POST['dstport']) {
174
		$_POST['dstport'] = trim($_POST['dstport']);
175
	}
176
	if ($_POST['natport']) {
177
		$_POST['natport'] = trim($_POST['natport']);
178
	}
179

  
180
	if (strlen($_POST['target']) > 0) {
181
		// Strip the target code 1-char code from the front before validating and saving.
182
		$_POST['target'] = substr($_POST['target'], 1);
183
	}
184

  
185
	if ($protocol_uses_ports && $_POST['sourceport'] <> "" && !is_port_or_range_or_alias($_POST['sourceport'])) {
186
		$input_errors[] = gettext("A valid port or port alias must be supplied for the source port entry.");
187
	}
188

  
189
	if ($protocol_uses_ports && $_POST['dstport'] <> "" && !is_port_or_range_or_alias($_POST['dstport'])) {
190
		$input_errors[] = gettext("A valid port or port alias must be supplied for the destination port entry.");
191
	}
192

  
193
	if ($protocol_uses_ports && $_POST['natport'] <> "" && !is_port_or_range_or_alias($_POST['natport']) && !isset($_POST['nonat'])) {
194
		$input_errors[] = gettext("A valid port must be supplied for the NAT port entry.");
195
	}
196

  
197
	if (($_POST['source_type'] != "any") && ($_POST['source_type'] != "(self)")) {
198
		if ($_POST['source'] && !is_ipaddroralias($_POST['source']) && $_POST['source'] != "any") {
199
			$input_errors[] = gettext("A valid source must be specified.");
200
		}
201
	}
202

  
203
	if ($_POST['source_subnet'] && !is_numericint($_POST['source_subnet'])) {
204
		$input_errors[] = gettext("A valid source bit count must be specified.");
205
	}
62
	$rv = saveoutNATrule($_POST, $id);
206 63

  
207
	if ($_POST['destination_type'] != "any") {
208
		if ($_POST['destination'] && !is_ipaddroralias($_POST['destination'])) {
209
			$input_errors[] = gettext("A valid destination must be specified.");
210
		}
211
	}
212

  
213
	if ($_POST['destination_subnet'] && !is_numericint($_POST['destination_subnet'])) {
214
		$input_errors[] = gettext("A valid destination bit count must be specified.");
215
	}
216

  
217
	if ($_POST['destination_type'] == "any") {
218
		if ($_POST['destination_not']) {
219
			$input_errors[] = gettext("Negating destination address of \"any\" is invalid.");
220
		}
221
	}
222

  
223
	if ($_POST['target'] && !is_ipaddr($_POST['target']) && !is_subnet($_POST['target']) && !is_alias($_POST['target']) && !isset($_POST['nonat']) && !($_POST['target'] == "other-subnet")) {
224
		$input_errors[] = gettext("A valid target IP address must be specified.");
225
	}
226

  
227
	if ($_POST['target'] == "other-subnet") {
228
		if (!is_ipaddr($_POST['targetip'])) {
229
			$input_errors[] = gettext("A valid target IP must be specified when using the 'Other Subnet' type.");
230
		}
231

  
232
		if (!is_numericint($_POST['targetip_subnet'])) {
233
			$input_errors[] = gettext("A valid target bit count must be specified when using the 'Other Subnet' type.");
234
		}
235
	}
236

  
237
	/* Verify Pool Options */
238
	$poolopts = "";
239
	$source_hash_key = "";
240
	if ($_POST['poolopts']) {
241
		if (is_subnet($_POST['target']) || ($_POST['target'] == "other-subnet")) {
242
			$poolopts = $_POST['poolopts'];
243
		} elseif (is_alias($_POST['target'])) {
244
			if (substr($_POST['poolopts'], 0, 11) == "round-robin") {
245
				$poolopts = $_POST['poolopts'];
246
			} else {
247
				$input_errors[] = gettext("Only Round Robin pool options may be chosen when selecting an alias.");
248
			}
249
		}
250
		/* If specified, verify valid source-hash key or generate a valid key using md5 */
251
		if ($_POST['source_hash_key']) {
252
			if (substr($_POST['source_hash_key'],0,2) == "0x") {
253
				if (ctype_xdigit(substr($_POST['source_hash_key'],2)) && strlen($_POST['source_hash_key']) == 34) {
254
					$source_hash_key = $_POST['source_hash_key'];
255
				} else {
256
					$input_errors[] = gettext("Incorrect format for source-hash key, \"0x\" must be followed by exactly 32 hexadecimal characters.");
257
				}
258
			} else {
259
				$source_hash_key = "0x".md5($_POST['source_hash_key']);
260
			}
261
		}
262
	}
263

  
264
	/* if user has selected any as source, set it here */
265
	if ($_POST['source_type'] == "any") {
266
		$osn = "any";
267
	} else if ($_POST['source_type'] == "(self)") {
268
		$osn = "(self)";
269
	} else if (is_alias($_POST['source'])) {
270
		$osn = $_POST['source'];
271
	} else {
272
		$osn = gen_subnet($_POST['source'], $_POST['source_subnet']) . "/" . $_POST['source_subnet'];
273
	}
274

  
275
	/* check for existing entries */
276
	if ($_POST['destination_type'] == "any") {
277
		$ext = "any";
278
	} else if (is_alias($_POST['destination'])) {
279
		$ext = $_POST['destination'];
280
	} else {
281
		$ext = gen_subnet($_POST['destination'], $_POST['destination_subnet']) . "/" . $_POST['destination_subnet'];
282
	}
283

  
284
	foreach ($a_out as $natent) {
285
		if (isset($id) && ($a_out[$id]) && ($a_out[$id] === $natent)) {
286
			continue;
287
		}
288

  
289
		if (!$natent['interface']) {
290
			$natent['interface'] = "wan";
291
		}
292
	}
64
	$input_errors = $rv['input_errors'];
65
	$pconfig = $rv['pconfig'];
293 66

  
294 67
	if (!$input_errors) {
295
		$natent = array();
296
		$natent['source']['network'] = $osn;
297
		$natent['sourceport'] = ($protocol_uses_ports) ? $_POST['sourceport'] : "";
298
		$natent['descr'] = $_POST['descr'];
299
		$natent['target'] = (!isset($_POST['nonat'])) ? $_POST['target'] : "";
300
		$natent['targetip'] = (!isset($_POST['nonat'])) ? $_POST['targetip'] : "";
301
		$natent['targetip_subnet'] = (!isset($_POST['nonat'])) ? $_POST['targetip_subnet'] : "";
302
		$natent['interface'] = $_POST['interface'];
303
		$natent['poolopts'] = $poolopts;
304
		$natent['source_hash_key'] = $source_hash_key;
305

  
306
		/* static-port */
307
		if (isset($_POST['staticnatport']) && $protocol_uses_ports && !isset($_POST['nonat'])) {
308
			$natent['staticnatport'] = true;
309
		} else {
310
			unset($natent['staticnatport']);
311
		}
312

  
313
		if (isset($_POST['disabled'])) {
314
			$natent['disabled'] = true;
315
		} else {
316
			unset($natent['disabled']);
317
		}
318

  
319
		/* if user has selected not nat, set it here */
320
		if (isset($_POST['nonat'])) {
321
			$natent['nonat'] = true;
322
		} else {
323
			unset($natent['nonat']);
324
		}
325

  
326
		if ($_POST['ipprotocol'] && $_POST['ipprotocol'] != "inet46") {
327
			$natent['ipprotocol'] = $_POST['ipprotocol'];
328
		} else {
329
			unset($natent['ipprotocol']);
330
		}
331
		
332
		if ($_POST['protocol'] && $_POST['protocol'] != "any") {
333
			$natent['protocol'] = $_POST['protocol'];
334
		} else {
335
			unset($natent['protocol']);
336
		}
337

  
338
		if ($ext == "any") {
339
			$natent['destination']['any'] = true;
340
		} else {
341
			$natent['destination']['address'] = $ext;
342
		}
343
		if ($_POST['natport'] != "" && $protocol_uses_ports && !isset($_POST['nonat'])) {
344
				$natent['natport'] = $_POST['natport'];
345
		} else {
346
			unset($natent['natport']);
347
		}
348
		if ($_POST['dstport'] != "" && $protocol_uses_ports) {
349
			$natent['dstport'] = $_POST['dstport'];
350
		} else {
351
			unset($natent['dstport']);
352
		}
353

  
354
		if ($_POST['nosync'] == "yes") {
355
			$natent['nosync'] = true;
356
		} else {
357
			unset($natent['nosync']);
358
		}
359

  
360
		if (isset($_POST['destination_not']) && $ext != "any") {
361
			$natent['destination']['not'] = true;
362
		}
363

  
364
		if (isset($a_out[$id]['created']) && is_array($a_out[$id]['created'])) {
365
			$natent['created'] = $a_out[$id]['created'];
366
		}
367

  
368
		$natent['updated'] = make_config_revision_entry();
369

  
370
		// Allow extending of the firewall edit page and include custom input validation
371
		pfSense_handle_custom_code("/usr/local/pkg/firewall_aon/pre_write_config");
372

  
373
		if (isset($id) && $a_out[$id]) {
374
			$a_out[$id] = $natent;
375
		} else {
376
			$natent['created'] = make_config_revision_entry();
377
			if (is_numeric($after)) {
378
				array_splice($a_out, $after+1, 0, array($natent));
379
			} else {
380
				$a_out[] = $natent;
381
			}
382
		}
383

  
384
		if (write_config(gettext("Firewall: NAT: Outbound - saved/edited outbound NAT mapping."))) {
385
			mark_subsystem_dirty('natconf');
386
		}
387 68
		header("Location: firewall_nat_out.php");
388 69
		exit;
389 70
	}
71
} else {
72
	$pconfig = getoutNATrule($id);
390 73
}
391 74

  
392 75
$pgtitle = array(gettext("Firewall"), gettext("NAT"), gettext("Outbound"), gettext("Edit"));
393 76
$pglinks = array("", "firewall_nat.php", "firewall_nat_out.php", "@self");
394 77
include("head.inc");
395 78

  
396
function build_target_list() {
397
	global $config, $sn, $a_aliases;
398
	$list = array();
399
	// Target list entries are made to start with the following characters:
400
	// "" (blank) - the interface address of the selected interface
401
	// S - a subnet
402
	// I - an ordinary IP address
403
	// H - a host alias
404
	// O - other subnet
405
	// The prefix letter makes it easy for the JavaScript to distinguish
406
	// the type of entry based on the first letter of the value.
407
	// The prefix letter is removed before saving in the config,
408
	// and added back when reading from the config.
409

  
410
	$list[""] = gettext('Interface Address');
411

  
412
	//Temporary array so we can sort IPs
413
	$templist = array();
414
	if (is_array($config['virtualip']['vip'])) {
415
		foreach ($config['virtualip']['vip'] as $sn) {
416
			if (($sn['mode'] == "proxyarp" || $sn['mode'] == "other") && $sn['type'] == "network") {
417
				$templist['S' . $sn['subnet'] . '/' . $sn['subnet_bits']] = gettext('Subnet: ') . $sn['subnet'] . '/' . $sn['subnet_bits'] . ' (' . $sn['descr'] . ')';
418
				if (isset($sn['noexpand'])) {
419
					continue;
420
				}
421
				$start = ip2long32(gen_subnet($sn['subnet'], $sn['subnet_bits']));
422
				$end = ip2long32(gen_subnet_max($sn['subnet'], $sn['subnet_bits']));
423
				$len = $end - $start;
424
				for ($i = 0; $i <= $len; $i++) {
425
					$snip = long2ip32($start+$i);
426

  
427
					$templist['I' . $snip] = $snip . ' (' . $sn['descr'] . ')';
428
				}
429
			} else {
430
				$templist['I' . $sn['subnet']] = $sn['subnet'] . ' (' . $sn['descr'] . ')';
431
			}
432
		}
433
	}
434
	asort($templist);
435
	//Append sorted IP array onto main array
436
	$list = array_merge($list, $templist);
437
	unset($templist);
438

  
439
	foreach ($a_aliases as $alias) {
440
		if ($alias['type'] != "host") {
441
			continue;
442
		}
443

  
444
		$list['H' . $alias['name']] = gettext('Host Alias: ') . $alias['name'] . ' (' . $alias['descr'] . ')';
445
	}
446

  
447
	$list['Oother-subnet'] = gettext('Other Subnet (Enter Below)');
448

  
449
	return($list);
450
}
451

  
452 79
if ($input_errors) {
453 80
	print_input_errors($input_errors);
454 81
}

Also available in: Unified diff