1
|
#!/usr/local/bin/php -f
|
2
|
<?php
|
3
|
/*
|
4
|
filter.inc
|
5
|
Copyright (C) 2004-2006 Scott Ullrich
|
6
|
Copyright (C) 2005 Bill Marquette
|
7
|
Copyright (C) 2006 Peter Allgeyer
|
8
|
Copyright (C) 2008 Ermal Luci
|
9
|
All rights reserved.
|
10
|
|
11
|
originally part of m0n0wall (http://m0n0.ch/wall)
|
12
|
Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
|
13
|
All rights reserved.
|
14
|
|
15
|
Redistribution and use in source and binary forms, with or without
|
16
|
modification, are permitted provided that the following conditions are met:
|
17
|
|
18
|
1. Redistributions of source code must retain the above copyright notice,
|
19
|
this list of conditions and the following disclaimer.
|
20
|
|
21
|
2. Redistributions in binary form must reproduce the above copyright
|
22
|
notice, this list of conditions and the following disclaimer in the
|
23
|
documentation and/or other materials provided with the distribution.
|
24
|
|
25
|
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
26
|
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
27
|
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
28
|
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
29
|
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
30
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
31
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
32
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
33
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
34
|
POSSIBILITY OF SUCH DAMAGE.
|
35
|
|
36
|
*/
|
37
|
|
38
|
require_once("globals.inc");
|
39
|
require_once("config.inc");
|
40
|
require_once("functions.inc");
|
41
|
require_once("filter.inc");
|
42
|
require_once("shaper.inc");
|
43
|
require_once("xmlrpc.inc");
|
44
|
require_once("interfaces.inc");
|
45
|
|
46
|
/*
|
47
|
* backup_vip_config_section($section): returns as an xml file string of
|
48
|
* the configuration section
|
49
|
*/
|
50
|
function backup_vip_config_section() {
|
51
|
global $config;
|
52
|
|
53
|
if (!is_array($config['virtualip']['vip']))
|
54
|
return;
|
55
|
$temp = array();
|
56
|
$temp['vip'] = array();
|
57
|
foreach($config['virtualip']['vip'] as $section) {
|
58
|
if ($section['mode'] != "carp")
|
59
|
continue;
|
60
|
if ($section['advskew'] <> "") {
|
61
|
$section_val = intval($section['advskew']);
|
62
|
$section_val=$section_val+100;
|
63
|
if ($section_val > 254)
|
64
|
$section_val = 254;
|
65
|
$section['advskew'] = $section_val;
|
66
|
}
|
67
|
if ($section['advbase'] <> "") {
|
68
|
$section_val = intval($section['advbase']);
|
69
|
if ($section_val > 254)
|
70
|
$section_val = 254;
|
71
|
$section['advbase'] = $section_val;
|
72
|
}
|
73
|
$temp['vip'][] = $section;
|
74
|
}
|
75
|
return $temp;
|
76
|
}
|
77
|
|
78
|
function remove_special_characters($string) {
|
79
|
$match_array = "";
|
80
|
preg_match_all("/[a-zA-Z0-9\_\-]+/",$string,$match_array);
|
81
|
$string = "";
|
82
|
foreach ($match_array[0] as $ma) {
|
83
|
if ($string <> "")
|
84
|
$string .= " ";
|
85
|
$string .= $ma;
|
86
|
}
|
87
|
return $string;
|
88
|
}
|
89
|
|
90
|
function carp_check_version($url, $username, $password, $port = 80, $method = 'pfsense.host_firmware_version') {
|
91
|
global $config, $g;
|
92
|
|
93
|
if(file_exists("{$g['varrun_path']}/booting") || $g['booting'])
|
94
|
return;
|
95
|
|
96
|
$params = array(
|
97
|
XML_RPC_encode($password)
|
98
|
);
|
99
|
|
100
|
$numberofruns = 0;
|
101
|
while ($numberofruns < 2) {
|
102
|
$msg = new XML_RPC_Message($method, $params);
|
103
|
$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
|
104
|
$cli->setCredentials($username, $password);
|
105
|
if($numberofruns > 0)
|
106
|
$cli->setDebug(1);
|
107
|
/* send our XMLRPC message and timeout after 240 seconds */
|
108
|
$resp = $cli->send($msg, "240");
|
109
|
if(!is_object($resp)) {
|
110
|
$error = "A communications error occurred while attempting XMLRPC sync with username {$username} {$url}:{$port}.";
|
111
|
} elseif($resp->faultCode()) {
|
112
|
$error = "An error code was received while attempting XMLRPC sync with username {$username} {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
|
113
|
} else {
|
114
|
$parsed_response = XML_RPC_decode($resp->value());
|
115
|
if(!is_array($parsed_response)) {
|
116
|
if (trim($parsed_response) == "Authentication failed") {
|
117
|
$error = "An authentication failure occurred while trying to access {$url}:{$port} ({$method}).";
|
118
|
log_error($error);
|
119
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
120
|
exit;
|
121
|
}
|
122
|
} else {
|
123
|
if (!isset($parsed_response['config_version']) ||
|
124
|
$parsed_response['config_version'] < $config['version']) {
|
125
|
update_filter_reload_status("The other member is on older configuration version of {$g['product_name']}. Sync will not be done to prevent problems!");
|
126
|
log_error("The other member is on older configuration version of {$g['product_name']}. Sync will not be done to prevent problems!");
|
127
|
return false;
|
128
|
} else
|
129
|
return true;
|
130
|
}
|
131
|
}
|
132
|
log_error($error);
|
133
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
134
|
$numberofruns++;
|
135
|
}
|
136
|
|
137
|
return false;
|
138
|
}
|
139
|
|
140
|
function carp_sync_xml($url, $username, $password, $sections, $port = 80, $method = 'pfsense.restore_config_section') {
|
141
|
global $config, $g;
|
142
|
|
143
|
if(file_exists("{$g['varrun_path']}/booting") || $g['booting'])
|
144
|
return;
|
145
|
|
146
|
update_filter_reload_status("Syncing CARP data to {$url}");
|
147
|
|
148
|
/* make a copy of config */
|
149
|
$config_copy = $config;
|
150
|
|
151
|
/* strip out nosync items */
|
152
|
if (is_array($config_copy['nat']['outbound']['rule'])) {
|
153
|
$rulescnt = count($config_copy['nat']['outbound']['rule']);
|
154
|
for ($x = 0; $x < $rulescnt; $x++) {
|
155
|
$config_copy['nat']['outbound']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['outbound']['rule'][$x]['descr']);
|
156
|
if (isset ($config_copy['nat']['outbound']['rule'][$x]['nosync']))
|
157
|
unset ($config_copy['nat']['outbound']['rule'][$x]);
|
158
|
}
|
159
|
}
|
160
|
if (is_array($config_copy['nat']['rule'])) {
|
161
|
$natcnt = count($config_copy['nat']['rule']);
|
162
|
for ($x = 0; $x < $natcnt; $x++) {
|
163
|
$config_copy['nat']['rule'][$x]['descr'] = remove_special_characters($config_copy['nat']['rule'][$x]['descr']);
|
164
|
if (isset ($config_copy['nat']['rule'][$x]['nosync']))
|
165
|
unset ($config_copy['nat']['rule'][$x]);
|
166
|
}
|
167
|
}
|
168
|
if (is_array($config_copy['filter']['rule'])) {
|
169
|
$filtercnt = count($config_copy['filter']['rule']);
|
170
|
for ($x = 0; $x < $filtercnt; $x++) {
|
171
|
$config_copy['filter']['rule'][$x]['descr'] = remove_special_characters($config_copy['filter']['rule'][$x]['descr']);
|
172
|
if (isset ($config_copy['filter']['rule'][$x]['nosync']))
|
173
|
unset ($config_copy['filter']['rule'][$x]);
|
174
|
}
|
175
|
}
|
176
|
if (is_array($config_copy['aliases']['alias'])) {
|
177
|
$aliascnt = count($config_copy['aliases']['alias']);
|
178
|
for ($x = 0; $x < $aliascnt; $x++) {
|
179
|
$config_copy['aliases']['alias'][$x]['descr'] = remove_special_characters($config_copy['aliases']['alias'][$x]['descr']);
|
180
|
if (isset ($config_copy['aliases']['alias'][$x]['nosync']))
|
181
|
unset ($config_copy['aliases']['alias'][$x]);
|
182
|
}
|
183
|
}
|
184
|
if (is_array($config_copy['dnsmasq']['hosts'])) {
|
185
|
$dnscnt = count($config_copy['dnsmasq']['hosts']);
|
186
|
for ($x = 0; $x < $dnscnt; $x++) {
|
187
|
$config_copy['dnsmasq']['hosts'][$x]['descr'] = remove_special_characters($config_copy['dnsmasq']['hosts'][$x]['descr']);
|
188
|
if (isset ($config_copy['dnsmasq']['hosts'][$x]['nosync']))
|
189
|
unset ($config_copy['dnsmasq']['hosts'][$x]);
|
190
|
}
|
191
|
}
|
192
|
if (is_array($config_copy['ipsec']['tunnel'])) {
|
193
|
$ipseccnt = count($config_copy['ipsec']['tunnel']);
|
194
|
for ($x = 0; $x < $ipseccnt; $x++) {
|
195
|
$config_copy['ipsec']['tunnel'][$x]['descr'] = remove_special_characters($config_copy['ipsec']['tunnel'][$x]['descr']);
|
196
|
if (isset ($config_copy['ipsec']['tunnel'][$x]['nosync']))
|
197
|
unset ($config_copy['ipsec']['tunnel'][$x]);
|
198
|
}
|
199
|
}
|
200
|
|
201
|
if (is_array($config_copy['dhcpd'])) {
|
202
|
foreach($config_copy['dhcpd'] as $dhcpif => $dhcpifconf) {
|
203
|
if($dhcpifconf['failover_peerip'] <> "") {
|
204
|
$int = guess_interface_from_ip($dhcpifconf['failover_peerip']);
|
205
|
$intip = find_interface_ip($int);
|
206
|
$config_copy['dhcpd'][$dhcpif]['failover_peerip'] = $intip;
|
207
|
}
|
208
|
}
|
209
|
}
|
210
|
|
211
|
foreach ($sections as $section) {
|
212
|
/* we can't use array_intersect_key()
|
213
|
* due to the vip 'special case'
|
214
|
*/
|
215
|
switch ($section) {
|
216
|
case 'virtualip':
|
217
|
$xml[$section] = backup_vip_config_section();
|
218
|
break;
|
219
|
case 'user':
|
220
|
$xml['system'][$section] = $config_copy['system'][$section];
|
221
|
$xml['system']['nextuid'] = $config_copy['system']['nextuid'];
|
222
|
break;
|
223
|
case 'group':
|
224
|
$xml['system'][$section] = $config_copy['system'][$section];
|
225
|
$xml['system']['nextgid'] = $config_copy['system']['nextgid'];
|
226
|
break;
|
227
|
case 'authserver':
|
228
|
$xml['system'][$section] = $config_copy['system'][$section];
|
229
|
default:
|
230
|
$xml[$section] = $config_copy[$section];
|
231
|
}
|
232
|
}
|
233
|
|
234
|
$params = array(
|
235
|
XML_RPC_encode($password),
|
236
|
XML_RPC_encode($xml)
|
237
|
);
|
238
|
|
239
|
$numberofruns = 0;
|
240
|
while ($numberofruns < 2) {
|
241
|
log_error("Beginning XMLRPC sync to {$url}:{$port}.");
|
242
|
$msg = new XML_RPC_Message($method, $params);
|
243
|
$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
|
244
|
$cli->setCredentials($username, $password);
|
245
|
if($numberofruns > 0)
|
246
|
$cli->setDebug(1);
|
247
|
/* send our XMLRPC message and timeout after 240 seconds */
|
248
|
$resp = $cli->send($msg, "240");
|
249
|
if(!is_object($resp)) {
|
250
|
$error = "A communications error occurred while attempting XMLRPC sync with username {$username} {$url}:{$port}.";
|
251
|
log_error($error);
|
252
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
253
|
} elseif($resp->faultCode()) {
|
254
|
$error = "An error code was received while attempting XMLRPC sync with username {$username} {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
|
255
|
log_error($error);
|
256
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
257
|
} else {
|
258
|
$parsed_response = XML_RPC_decode($resp->value());
|
259
|
if(!is_array($parsed_response) && trim($parsed_response) == "Authentication failed") {
|
260
|
$error = "An authentication failure occurred while trying to access {$url}:{$port} ($method).";
|
261
|
log_error($error);
|
262
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
263
|
exit;
|
264
|
} else {
|
265
|
log_error("XMLRPC sync successfully completed with {$url}:{$port}.");
|
266
|
update_filter_reload_status("XMLRPC sync successfully completed with {$url}:{$port}.");
|
267
|
}
|
268
|
$numberofruns = 3;
|
269
|
}
|
270
|
$numberofruns++;
|
271
|
}
|
272
|
}
|
273
|
|
274
|
global $g;
|
275
|
if (file_exists("{$g['varrun_path']}/booting") || $g['booting'])
|
276
|
return;
|
277
|
|
278
|
if (is_array($config['hasync'])) {
|
279
|
update_filter_reload_status("Building high availability sync information");
|
280
|
$hasync = $config['hasync'];
|
281
|
|
282
|
if (empty($hasync['synchronizetoip'])) {
|
283
|
log_error("Config sync not being done because of missing sync IP (this is normal on secondary systems).");
|
284
|
exit;
|
285
|
}
|
286
|
|
287
|
/*
|
288
|
* XXX: The way we're finding the port right now is really suboptimal -
|
289
|
* we can't assume that the other machine is setup identically.
|
290
|
*/
|
291
|
if (!empty($config['system']['webgui']['protocol'])) {
|
292
|
$synchronizetoip = $config['system']['webgui']['protocol'];
|
293
|
$synchronizetoip .= "://";
|
294
|
}
|
295
|
|
296
|
/* if port is empty lets rely on the protocol selection */
|
297
|
$port = $config['system']['webgui']['port'];
|
298
|
if (empty($port)) {
|
299
|
if ($config['system']['webgui']['protocol'] == "http")
|
300
|
$port = "80";
|
301
|
else
|
302
|
$port = "443";
|
303
|
}
|
304
|
|
305
|
if(is_ipaddrv6($hasync['synchronizetoip']))
|
306
|
$hasync['synchronizetoip'] = "[{$hasync['synchronizetoip']}]";
|
307
|
$synchronizetoip .= $hasync['synchronizetoip'];
|
308
|
if ($hasync['synchronizerules'] != "") {
|
309
|
if (!is_array($config['filter']))
|
310
|
$config['filter'] = array();
|
311
|
$sections[] = 'filter';
|
312
|
}
|
313
|
if ($hasync['synchronizenat'] != "") {
|
314
|
if (!is_array($config['nat']))
|
315
|
$config['nat'] = array();
|
316
|
$sections[] = 'nat';
|
317
|
}
|
318
|
if ($hasync['synchronizealiases'] != "") {
|
319
|
if (!is_array($config['aliases']))
|
320
|
$config['aliases'] = array();
|
321
|
$sections[] = 'aliases';
|
322
|
}
|
323
|
if ($hasync['synchronizedhcpd'] != "" and is_array($config['dhcpd']))
|
324
|
$sections[] = 'dhcpd';
|
325
|
if ($hasync['synchronizewol'] != "") {
|
326
|
if (!is_array($config['wol']))
|
327
|
$config['wol'] = array();
|
328
|
$sections[] = 'wol';
|
329
|
}
|
330
|
if ($hasync['synchronizetrafficshaper'] != "" and is_array($config['shaper']))
|
331
|
$sections[] = 'shaper';
|
332
|
if ($hasync['synchronizetrafficshaperlimiter'] != "" and is_array($config['dnshaper']))
|
333
|
$sections[] = 'dnshaper';
|
334
|
if ($hasync['synchronizetrafficshaperlayer7'] != "" and is_array($config['l7shaper']))
|
335
|
$sections[] = 'l7shaper';
|
336
|
if ($hasync['synchronizestaticroutes'] != "") {
|
337
|
if (!is_array($config['staticroutes']))
|
338
|
$config['staticroutes'] = array();
|
339
|
if (!is_array($config['staticroutes']['route']))
|
340
|
$config['staticroutes']['route'] = array();
|
341
|
$sections[] = 'staticroutes';
|
342
|
if (!is_array($config['gateways']))
|
343
|
$config['gateways'] = array();
|
344
|
$sections[] = 'gateways';
|
345
|
}
|
346
|
if ($hasync['synchronizevirtualip'] != "") {
|
347
|
if (!is_array($config['virtualip']))
|
348
|
$config['virtualip'] = array();
|
349
|
$sections[] = 'virtualip';
|
350
|
}
|
351
|
if ($hasync['synchronizelb'] != "") {
|
352
|
if (!is_array($config['load_balancer']))
|
353
|
$config['load_balancer'] = array();
|
354
|
$sections[] = 'load_balancer';
|
355
|
}
|
356
|
if ($hasync['synchronizeipsec'] != "") {
|
357
|
if (!is_array($config['ipsec']))
|
358
|
$config['ipsec'] = array();
|
359
|
$sections[] = 'ipsec';
|
360
|
}
|
361
|
if ($hasync['synchronizeopenvpn'] != "") {
|
362
|
if (!is_array($config['openvpn']))
|
363
|
$config['openvpn'] = array();
|
364
|
$sections[] = 'openvpn';
|
365
|
}
|
366
|
if ($hasync['synchronizecerts'] != "" || $hasync['synchronizeopenvpn'] != "") {
|
367
|
if (!is_array($config['cert']))
|
368
|
$config['cert'] = array();
|
369
|
$sections[] = 'cert';
|
370
|
|
371
|
if (!is_array($config['ca']))
|
372
|
$config['ca'] = array();
|
373
|
$sections[] = 'ca';
|
374
|
|
375
|
if (!is_array($config['crl']))
|
376
|
$config['crl'] = array();
|
377
|
$sections[] = 'crl';
|
378
|
}
|
379
|
if ($hasync['synchronizeusers'] != "") {
|
380
|
$sections[] = 'user';
|
381
|
$sections[] = 'group';
|
382
|
}
|
383
|
if ($hasync['synchronizeauthservers'] != "") {
|
384
|
$sections[] = 'authserver';
|
385
|
}
|
386
|
if ($hasync['synchronizednsforwarder'] != "" and is_array($config['dnsmasq']))
|
387
|
$sections[] = 'dnsmasq';
|
388
|
if ($hasync['synchronizeschedules'] != "" || $hasync['synchronizerules'] != "") {
|
389
|
if (!is_array($config['schedules']))
|
390
|
$config['schedules'] = array();
|
391
|
$sections[] = 'schedules';
|
392
|
}
|
393
|
if ($hasync['synchronizecaptiveportal'] != "" and is_array($config['captiveportal']))
|
394
|
$sections[] = 'captiveportal';
|
395
|
if ($hasync['synchronizecaptiveportal'] != "" and is_array($config['vouchers']))
|
396
|
$sections[] = 'vouchers';
|
397
|
|
398
|
if (count($sections) <= 0) {
|
399
|
log_error("Nothing has been configured to be synched. Skipping....");
|
400
|
exit;
|
401
|
}
|
402
|
|
403
|
if (empty($hasync['username']))
|
404
|
$username = "admin";
|
405
|
else
|
406
|
$username = $hasync['username'];
|
407
|
|
408
|
if (!carp_check_version($synchronizetoip, $username, $hasync['password'], $port))
|
409
|
exit;
|
410
|
|
411
|
update_filter_reload_status("Signaling CARP reload signal...");
|
412
|
carp_sync_xml($synchronizetoip, $username, $hasync['password'], $sections, $port);
|
413
|
$cli = new XML_RPC_Client('/xmlrpc.php', $synchronizetoip, $port);
|
414
|
$params = array(
|
415
|
XML_RPC_encode($hasync['password'])
|
416
|
);
|
417
|
|
418
|
$msg = new XML_RPC_Message('pfsense.filter_configure', $params);
|
419
|
$cli->setCredentials($username, $hasync['password']);
|
420
|
$resp = $cli->send($msg, "900");
|
421
|
|
422
|
if (!is_object($resp)) {
|
423
|
$error = "A communications error occurred while attempting Filter sync with username {$username} {$synchronizetoip}:{$port}.";
|
424
|
log_error($error);
|
425
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
426
|
} elseif($resp->faultCode()) {
|
427
|
$error = "An error code was received while attempting Filter sync with username {$username} {$synchronizetoip}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
|
428
|
log_error($error);
|
429
|
file_notice("sync_settings", $error, "Settings Sync", "");
|
430
|
} else {
|
431
|
log_error("Filter sync successfully completed with {$synchronizetoip}:{$port}.");
|
432
|
$numberofruns = 3;
|
433
|
}
|
434
|
}
|
435
|
|
436
|
?>
|