1
|
<?php
|
2
|
/*
|
3
|
* parser_dhcpv6_leases.inc
|
4
|
*
|
5
|
* Copyright (c) 2017-2019 Anders Lind (anders.lind@gmail.com)
|
6
|
* All rights reserved.
|
7
|
*
|
8
|
* part of pfSense (https://www.pfsense.org)
|
9
|
* Copyright (c) 2004-2013 BSD Perimeter
|
10
|
* Copyright (c) 2013-2016 Electric Sheep Fencing
|
11
|
* Copyright (c) 2014-2022 Rubicon Communications, LLC (Netgate)
|
12
|
* Copyright (c) 2011 Seth Mos
|
13
|
* All rights reserved.
|
14
|
*
|
15
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
16
|
* you may not use this file except in compliance with the License.
|
17
|
* You may obtain a copy of the License at
|
18
|
*
|
19
|
* http://www.apache.org/licenses/LICENSE-2.0
|
20
|
*
|
21
|
* Unless required by applicable law or agreed to in writing, software
|
22
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
23
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
24
|
* See the License for the specific language governing permissions and
|
25
|
* limitations under the License.
|
26
|
*/
|
27
|
|
28
|
require_once 'tools_for_debugging_n_testing.inc';
|
29
|
require_once 'parser_ipv6.inc';
|
30
|
|
31
|
/*
|
32
|
* Moved over from status_dhcpv6_leases.php
|
33
|
*/
|
34
|
function parse_duid($duid_string) {
|
35
|
$parsed_duid = array();
|
36
|
for ($i = 0; $i < strlen($duid_string); $i++) {
|
37
|
$s = substr($duid_string, $i, 1);
|
38
|
if ($s == '\\') {
|
39
|
$n = substr($duid_string, $i+1, 1);
|
40
|
if (($n == '\\') || ($n == '"')) {
|
41
|
$parsed_duid[] = sprintf("%02x", ord($n));
|
42
|
$i += 1;
|
43
|
} else {
|
44
|
$n = substr($duid_string, $i+1, 3);
|
45
|
if (preg_match('/[0-3][0-7]{2}/', $n)) {
|
46
|
$parsed_duid[] = sprintf("%02x", octdec($n));
|
47
|
$i += 3;
|
48
|
}
|
49
|
}
|
50
|
} else {
|
51
|
$parsed_duid[] = sprintf("%02x", ord($s));
|
52
|
}
|
53
|
}
|
54
|
$iaid = array_slice($parsed_duid, 0, 4);
|
55
|
$duid = array_slice($parsed_duid, 4);
|
56
|
return array($iaid, $duid);
|
57
|
}
|
58
|
|
59
|
/*
|
60
|
* Moved over from status_dhcpv6_leases.php
|
61
|
*/
|
62
|
function get_ndpdata() {
|
63
|
exec("/usr/sbin/ndp -an", $rawdata);
|
64
|
$ndpdata = array();
|
65
|
foreach ($rawdata as $line) {
|
66
|
$elements = preg_split('/\s+/ ', $line);
|
67
|
if ($elements[1] != "(incomplete)") {
|
68
|
$ndpent = array();
|
69
|
$ip = trim(str_replace(array('(', ')'), '', $elements[0]));
|
70
|
$ndpent['mac'] = trim($elements[1]);
|
71
|
$ndpent['interface'] = trim($elements[2]);
|
72
|
$ndpdata[$ip] = $ndpent;
|
73
|
}
|
74
|
}
|
75
|
|
76
|
return $ndpdata;
|
77
|
}
|
78
|
|
79
|
const IA_NA = 1;
|
80
|
const IA_PD = 2;
|
81
|
const IA_TA = 3;
|
82
|
|
83
|
/*
|
84
|
* returns false if it was not possible to parse the leases.
|
85
|
*/
|
86
|
function gui_parse_leases(&$pools, &$leases, &$prefixes, &$mappings, $raw_leases, $ndpdata, $lang_pack) {
|
87
|
$amount_of_matches = preg_match_all('/'.base_regex.'/i', (string) $raw_leases, $lease_array, PREG_SET_ORDER);
|
88
|
|
89
|
if ($amount_of_matches == false) {
|
90
|
return false;
|
91
|
}
|
92
|
|
93
|
$amount_of_leases = 0;
|
94
|
foreach ($lease_array as $val) {
|
95
|
general_debug('---LEASE '.++$amount_of_leases.' START---');
|
96
|
|
97
|
$iaid_duid = parse_duid($val['IAID_DUID']);
|
98
|
|
99
|
general_debug($val, 'IAID_DUID');
|
100
|
|
101
|
$entry = [ 'iaid' => hexdec(implode("", array_reverse($iaid_duid[0]))),
|
102
|
'duid' => implode(":", $iaid_duid[1]) ];
|
103
|
|
104
|
general_debug($entry, 'iaid');
|
105
|
general_debug($entry, 'duid');
|
106
|
|
107
|
if (preg_match('/^ia-na$/i', $val['IA_ASSOC'])) {
|
108
|
gui_parse_single_lease(IA_NA, $val['NEXT_LEVEL'], $lang_pack, $ndpdata, $mappings, $entry);
|
109
|
$leases[] = $entry;
|
110
|
} elseif (preg_match('/^ia-pd$/i', $val['IA_ASSOC'])) {
|
111
|
gui_parse_single_lease(IA_PD, $val['NEXT_LEVEL'], $lang_pack, $ndpdata, $mappings, $entry);
|
112
|
$prefixes[] = $entry;
|
113
|
} elseif (preg_match('/^ia-ta$/i', $val['IA_ASSOC'])) {
|
114
|
// Not used in the pfSense UI at the moment.
|
115
|
general_debug('Notice: ia-ta is unhandled for now.');
|
116
|
}
|
117
|
|
118
|
general_debug("---LEASE $amount_of_leases END---\n");
|
119
|
}
|
120
|
|
121
|
// Handle failover - NOTE Experimental;
|
122
|
// has not been tested on a live configured system.
|
123
|
$amount_of_matches = preg_match_all('/'.failover_base_regex.'/i', $raw_leases, $lease_array, PREG_SET_ORDER);
|
124
|
$amount_of_leases = 0;
|
125
|
foreach ($lease_array as $val) {
|
126
|
general_debug('---FAILOVER '.++$amount_of_leases.' START---');
|
127
|
$entry = ['name' => $val['FPN']];
|
128
|
general_debug($val, 'FPN');
|
129
|
gui_parse_single_failover($val['NEXT_LEVEL'], $entry);
|
130
|
$pools[] = $entry;
|
131
|
general_debug("---FAILOVER $amount_of_leases END---\n");
|
132
|
}
|
133
|
|
134
|
general_debug('Leases', $leases);
|
135
|
general_debug('Prefixes', $prefixes);
|
136
|
general_debug('Mappings', $mappings);
|
137
|
general_debug('Pools', $pools);
|
138
|
}
|
139
|
|
140
|
function gui_parse_single_lease($type, $content, $lang_pack, $ndpdata, &$mappings, &$entry) {
|
141
|
switch ($type) {
|
142
|
case IA_NA:
|
143
|
preg_match_all('/'.na_regex.'/i', $content, $matches, PREG_SET_ORDER);
|
144
|
break;
|
145
|
case IA_PD:
|
146
|
preg_match_all('/'.pd_regex.'/i', $content, $matches, PREG_SET_ORDER);
|
147
|
break;
|
148
|
// case IA_TA: // Not implemented in pfSense.
|
149
|
default:
|
150
|
general_debug('Error: Unhandled lease type.');
|
151
|
return -1;
|
152
|
}
|
153
|
|
154
|
$amount_of_matches = 0;
|
155
|
|
156
|
foreach ($matches as $val) {
|
157
|
general_debug("\n --Match number " . ++$amount_of_matches . ' start.');
|
158
|
general_debug("Matched: " . $val[0]);
|
159
|
|
160
|
check_value_set_entry($val, 'CLTT', $entry, 'start', true, function () use (&$val) {
|
161
|
general_debug($val, 'WEEKDAY', 'DATE', 'YEAR', 'MONTH', 'DAY', 'TIME');
|
162
|
return $val['DATE'] . ' ' . $val['TIME'];});
|
163
|
|
164
|
if (setnlen($val, 'NEXT_LEVEL')) {
|
165
|
general_debug($val, 'NEXT_LEVEL');
|
166
|
|
167
|
gui_handle_ia($val['NEXT_LEVEL'], $entry, $lang_pack);
|
168
|
}
|
169
|
|
170
|
// For now it does not matter that this goes last.
|
171
|
if ($type === IA_NA) {
|
172
|
gui_parse_single_na_lease($val, $lang_pack, $ndpdata, $mappings, $entry);
|
173
|
} elseif ($type === IA_PD) {
|
174
|
gui_parse_single_pd_lease($val, $lang_pack, $entry);
|
175
|
}
|
176
|
|
177
|
general_debug(' --Match number ' . $amount_of_matches . ' end.');
|
178
|
}
|
179
|
}
|
180
|
|
181
|
function gui_parse_single_na_lease($val, $lang_pack, $ndpdata, &$mappings, &$entry) {
|
182
|
if (setnlen($val, 'ADDRESS')) {
|
183
|
general_debug($val, 'ADDRESS');
|
184
|
|
185
|
$space_result = parse_all_ipv6_to_array($val['ADDRESS']);
|
186
|
|
187
|
if (count($space_result) == 1) {
|
188
|
if (setnlen($space_result[0], 'MATCH')) {
|
189
|
general_debug("We have a valid ipv6 address");
|
190
|
if (check_n_set($entry, 'ip', $space_result[0]['MATCH'], true)) {
|
191
|
// In general we trust that $lang_pack exists with values so no checking (either
|
192
|
// it works for a certain language/translation or it does not (for all entries.)
|
193
|
check_n_set($entry, 'type', $lang_pack['dynamic'], true);
|
194
|
|
195
|
in_array($entry['ip'], array_keys($ndpdata))
|
196
|
? check_n_set($entry, 'online', $lang_pack['online'], true)
|
197
|
: check_n_set($entry, 'online', $lang_pack['offline'], true);
|
198
|
|
199
|
// mappings - using DUID and IAID
|
200
|
if (setnlen($entry, 'duid') && setnlen($entry, 'iaid')) {
|
201
|
// We assume $mappings array exist - else nothing works.
|
202
|
general_debug('Setting the mappings array for DUID ' . $entry['duid'] .
|
203
|
' with IAID ' . $entry['iaid'] . ' as index with value ' .
|
204
|
$entry['ip']);
|
205
|
check_n_set($mappings[$entry['duid']], $entry['iaid'], $entry['ip'], true);
|
206
|
} else {
|
207
|
general_debug('WARNING: DUID or IAID is missing in entry. Likely either has zero length content.');
|
208
|
}
|
209
|
} // else { } - we use overwrite set to true and entry must have been set.
|
210
|
} else {
|
211
|
general_debug('Unknown problem with IPv6 address. See output just above.');
|
212
|
}
|
213
|
} else {
|
214
|
general_debug('WARNING: Count of IPv6 addresses is: '.count($space_result));
|
215
|
}
|
216
|
}
|
217
|
}
|
218
|
|
219
|
function gui_parse_single_pd_lease($val, $lang_pack, &$entry) {
|
220
|
if (setnlen($val, 'SPACE')) {
|
221
|
general_debug($val, 'SPACE');
|
222
|
|
223
|
$space_result = parse_all_ipv6_to_array($val['SPACE']);
|
224
|
|
225
|
if (count($space_result) == 1) {
|
226
|
if (setnlen($space_result[0], 'MATCH')) {
|
227
|
general_debug("We have a valid ipv6 address");
|
228
|
|
229
|
if (setnlen($space_result[0], 'CMR6') &&
|
230
|
preg_match('/::$/', $space_result[0]['CMR6'])) {
|
231
|
general_debug("that is properly terminated with ::");
|
232
|
} else {
|
233
|
general_debug(", but not a proper subnet that ends with ::");
|
234
|
}
|
235
|
|
236
|
check_value_set_entry($val, 'PREFIX', $entry, 'prefix', true,
|
237
|
function () use (&$val, &$lang_pack, &$entry, &$space_result) {
|
238
|
check_n_set($entry, 'type', $lang_pack['dynamic'], true);
|
239
|
return $space_result[0]['MATCH'].'/'.$val['PREFIX'];});
|
240
|
} else {
|
241
|
general_debug('Unknown problem with IPv6 address. See output just above.');
|
242
|
}
|
243
|
} else {
|
244
|
general_debug('WARNING: Count of IPv6 addresses is: '.count($space_result));
|
245
|
}
|
246
|
}
|
247
|
}
|
248
|
|
249
|
function gui_handle_ia($content, &$entry, $lang_pack) {
|
250
|
preg_match_all('/'.ia_regex.'/i', $content, $ia_matches, PREG_SET_ORDER);
|
251
|
|
252
|
/* The reason why we use foreach and not simply just try lookup content is due to the
|
253
|
* fact that we match with alternation different sets of values from different
|
254
|
* regex groups.
|
255
|
* In theory one set/group could be repeated inside $ia_matches which means we want to
|
256
|
* know if that is the case. check_n_set makes sure to check if the value exists already
|
257
|
* and returns debug info about that.
|
258
|
*/
|
259
|
foreach ($ia_matches as $ia_val) {
|
260
|
if (setnlen($ia_val, 'BINDINGSTATE')) {
|
261
|
switch ($ia_val['BINDINGSTATE']) {
|
262
|
case "active":
|
263
|
check_n_set($entry, 'act', $lang_pack['active'], true);
|
264
|
break;
|
265
|
case "expired":
|
266
|
check_n_set($entry, 'act', $lang_pack['expired'], true);
|
267
|
break;
|
268
|
case "free":
|
269
|
check_n_set($entry, 'act', $lang_pack['expired'], true);
|
270
|
check_n_set($entry, 'online', $lang_pack['offline'], true);
|
271
|
break;
|
272
|
case "backup":
|
273
|
check_n_set($entry, 'act', $lang_pack['reserved'], true);
|
274
|
check_n_set($entry, 'online', $lang_pack['offline'], true);
|
275
|
break;
|
276
|
case "released":
|
277
|
check_n_set($entry, 'act', $lang_pack['released'], true);
|
278
|
check_n_set($entry, 'online', $lang_pack['offline'], true);
|
279
|
break;
|
280
|
default:
|
281
|
general_debug('Notice: Binding state "' . $ia_val['BINDINGSTATE'] .
|
282
|
'" is not handled.');
|
283
|
break;
|
284
|
}
|
285
|
|
286
|
general_debug($ia_val, 'BINDINGSTATE');
|
287
|
}
|
288
|
|
289
|
// does not seem to be used by lease gui so we do not set anything; we just debug.
|
290
|
check_value_set_entry($ia_val, 'PREFLIFE');
|
291
|
|
292
|
// does not seem to be used by lease gui so we do not set anything; we just debug.
|
293
|
check_value_set_entry($ia_val, 'MAXLIFE');
|
294
|
|
295
|
check_value_set_entry($ia_val, 'ENDS', $entry, 'end', true, function () use (&$ia_val) {
|
296
|
general_debug($ia_val, 'WEEKDAY', 'DATE', 'YEAR', 'MONTH', 'DAY', 'TIME');
|
297
|
return $ia_val['DATE'] . ' ' . $ia_val['TIME'];});
|
298
|
}
|
299
|
}
|
300
|
|
301
|
function gui_parse_single_failover($content, &$entry) {
|
302
|
preg_match_all('/'.failover_level1_regex.'/i', $content, $matches, PREG_SET_ORDER);
|
303
|
|
304
|
/* The reason why we use foreach and not simply just try lookup content is due to the
|
305
|
* fact that we match with alternation different sets of values from different
|
306
|
* regex groups.
|
307
|
* In theory one set/group could be repeated inside $matches which means we want to
|
308
|
* know if that is the case. check_value_set_entry makes sure to check if the value
|
309
|
* exists already and returns debug info about that.
|
310
|
*/
|
311
|
foreach ($matches as $val) {
|
312
|
check_value_set_entry($val, 'MYSTATE', $entry, 'mystate', true);
|
313
|
check_value_set_entry($val, 'MYDATE', $entry, 'mydate', true,
|
314
|
function () use (&$val) {return $val['DATE'] . ' ' . $val['TIME'];});
|
315
|
check_value_set_entry($val, 'PEERSTATE', $entry, 'peerstate', true);
|
316
|
check_value_set_entry($val, 'PEERDATE', $entry, 'peerdate', true,
|
317
|
function () use (&$val) {return $val['PDATE'] . ' ' . $val['PTIME'];});
|
318
|
/* Does not seem to be used by lease gui so we do not set anything; we just debug.
|
319
|
* mclt (Maximum Client Lead Time):
|
320
|
* https://kb.isc.org/article/AA-00502/0/A-Basic-Guide-to-Configuring-DHCP-Failover.html
|
321
|
* https://kb.isc.org/article/AA-00268/0/DHCP-Failover-and-MCLT-configuration-implications.html
|
322
|
*/
|
323
|
check_value_set_entry($val, 'MCLT');
|
324
|
}
|
325
|
}
|
326
|
|
327
|
/* Main leases - Level 0
|
328
|
* regex matches the outer/top level of a lease
|
329
|
* We allow free spacing and comments with (?x)
|
330
|
* We capture:
|
331
|
* . the identity association of a temporary address and non-temporary address
|
332
|
* and delegated prefix (IA-TA, IA-NA and IA-PD)
|
333
|
* . the partly octal encoded iaid + duid (Identity association identifier + DHCP Unique Identifier)
|
334
|
* . the content at the next level,
|
335
|
* but our matching also reuses our
|
336
|
* current match (RECURSE) to make sure
|
337
|
* there is a balance to the curly braces {}
|
338
|
* semicolons where there are no curly braces
|
339
|
* and sub-content to curly braces - but neither the
|
340
|
* next level and sub levels are directly
|
341
|
* individually matched/captured (that is for later
|
342
|
* regex matches to achieve!)
|
343
|
*
|
344
|
* \x5C = \
|
345
|
* Octal numbers are the characters that are found in the IAID and DUID with a
|
346
|
* backslash \ in front of them.
|
347
|
* Hex values in ranges are those characters that are printed as characters (not
|
348
|
* octal values):
|
349
|
* [\x20\x21\x23-\x5B\x5D-\x7E]
|
350
|
* 1. See ascii table that includes hex values:
|
351
|
* https://en.wikipedia.org/wiki/ASCII#Printable_characters
|
352
|
* https://en.wikipedia.org/wiki/ASCII#Character_set
|
353
|
* 2. Check: https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=common/print.c
|
354
|
* 3. isprint is more limited than isascii: http://www.cplusplus.com/reference/cctype/isprint/
|
355
|
*/
|
356
|
|
357
|
/* Notice (?x) must be a the beginning of the line - cannot be tabbed in,
|
358
|
* because free spacing mode must start at the very beginning (^) of the
|
359
|
* content.
|
360
|
*/
|
361
|
const base_regex = <<<'BASE'
|
362
|
(?x)
|
363
|
(?'IA_ASSOC'ia-(?>ta|na|pd))
|
364
|
\s+"
|
365
|
(?'IAID_DUID'
|
366
|
(?>
|
367
|
[\x20\x21\x23-\x5B\x5D-\x7E]
|
368
|
|
|
369
|
\x5C
|
370
|
(?>
|
371
|
"
|
372
|
|
|
373
|
\x5C
|
374
|
|
|
375
|
(?>00[0-7]|0[1-3][0-7]|177|[2-3][0-7]{2})
|
376
|
)
|
377
|
)*
|
378
|
)
|
379
|
"\s+
|
380
|
\{
|
381
|
(?'NEXT_LEVEL'
|
382
|
(?>
|
383
|
\s*
|
384
|
(?>
|
385
|
[^\{\}\n]+;
|
386
|
|
|
387
|
[^\{\}\n]+
|
388
|
\s*
|
389
|
\{
|
390
|
(?&NEXT_LEVEL)
|
391
|
\}
|
392
|
\s*
|
393
|
)*
|
394
|
)*
|
395
|
)
|
396
|
\}
|
397
|
BASE;
|
398
|
/* Inside the: (?'NEXT_LEVEL' ... )
|
399
|
* various white space characters where substituted with \s* to match all white
|
400
|
* space, but at different places before the substitutions some of the white
|
401
|
* space characters were as mentioned below and their purpose:
|
402
|
* * (star) needed for when no space or newline e.g. nested {} is used in e.g.:
|
403
|
* something {dww w{} w {d;}}
|
404
|
* \n (new line) needed for quicker matching (here)
|
405
|
* \ (space) needed so we eat all space before a faulty double {}:
|
406
|
* something {} {}
|
407
|
* , is repeated - which we consider illegal.
|
408
|
* \ \n has been substituted with \s to cover more white space characters!
|
409
|
*/
|
410
|
|
411
|
/* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=includes/dhcpd.h
|
412
|
* struct lease etc. and various write_lease methods
|
413
|
*
|
414
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/db.c
|
415
|
* method for write lease file content is found here e.g. write_lease()
|
416
|
*
|
417
|
* Notice lease time entries are written with C gmtime() as UTC:
|
418
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=common/print.c
|
419
|
* const char * print_time(TIME t) last else clause:
|
420
|
* if (strftime(buf, sizeof(buf), "%w %Y/%m/%d %H:%M:%S;",
|
421
|
* gmtime(&t)) == 0)
|
422
|
*/
|
423
|
const date_time = <<<'DATETIME'
|
424
|
(?'WEEKDAY'[0-6])\s+
|
425
|
(?'DATE'
|
426
|
(?'YEAR'\d+)\/(?'MONTH'(?>0[1-9]|1[0-2]))\/
|
427
|
(?'DAY'(?>0[1-9]|[1-2][0-9]|30|31))
|
428
|
)\s+
|
429
|
(?'TIME'(?>[0-1][0-9]|2[0-3]):(?>[0-5][0-9]):(?>[0-5][0-9]|60))
|
430
|
DATETIME;
|
431
|
|
432
|
// Level 1
|
433
|
const common_napd_top =
|
434
|
'(?x)
|
435
|
cltt\s+(?\'CLTT\''.date_time.');|';
|
436
|
|
437
|
# Perform real check of the IPv6 address/space somewhere else.
|
438
|
const pd_specific = <<<'PD'
|
439
|
iaprefix\s+(?'SPACE'[\.\da-fA-F:]*)\/(?'PREFIX'\d{1,3})\s+\{
|
440
|
PD;
|
441
|
const na_specific = <<<'NA'
|
442
|
iaaddr\s+(?'ADDRESS'[\.\da-fA-F:]*)\s+\{
|
443
|
NA;
|
444
|
|
445
|
/* Level 1
|
446
|
* This part handles matching properly placed curly braces and semicolons.
|
447
|
* If in doubt just do experiments! To achieve matching the curly braces we need
|
448
|
* to check at all levels however we do just capture it all since matching the
|
449
|
* individual components needs to be handled separately at each level by other
|
450
|
* regexes.
|
451
|
* Same idea as with base_regex above, but not expanded to save space.
|
452
|
*/
|
453
|
const common_napd_bottom = <<<'COMMON'
|
454
|
(?'NEXT_LEVEL'(?>\s*
|
455
|
(?>[^\{\}\n]+;|
|
456
|
[^\{\}\n]+\s*\{
|
457
|
(?&NEXT_LEVEL)
|
458
|
\}\s*
|
459
|
)*)*)
|
460
|
\}
|
461
|
COMMON;
|
462
|
|
463
|
/* Level 2 stuff
|
464
|
* binding state names defined in array binding_state_names
|
465
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/stables.c
|
466
|
* "free", "active", "expired", "released", "abandoned", "reset", "backup"
|
467
|
* write_lease and write_ia use binding_state_names in:
|
468
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/db.c
|
469
|
* defined in https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/stables.c
|
470
|
*/
|
471
|
const ia_regex =
|
472
|
'(?x)
|
473
|
(binding\s+state\s+(?\'BINDINGSTATE\'free|active|expired|released|abandoned|reset|backup)|
|
474
|
preferred-life\s+(?\'PREFLIFE\'\d+)|
|
475
|
max-life\s+(?\'MAXLIFE\'\d+)|
|
476
|
ends\s+(?\'ENDS\''.date_time.'));\s*';
|
477
|
|
478
|
const pd_regex = common_napd_top."\n".pd_specific."\n".common_napd_bottom;
|
479
|
const na_regex = common_napd_top."\n".na_specific."\n".common_napd_bottom;
|
480
|
|
481
|
/* Print these out if you would like to test and debug with (online) tools such
|
482
|
as https://regex101.com - you could also test and debug some parts mentioned
|
483
|
above. Just use the raw lease file as input and see how they match. */
|
484
|
|
485
|
// NOTE THIS IS NOT TESTED ON A REAL FAILOVER CONFIGURED SYSTEM
|
486
|
/*
|
487
|
* Handling of failover base - level 0
|
488
|
* Documentation is misleading so the source of ISC DHCP server is the best
|
489
|
* resource combined with this article:
|
490
|
* https://deepthought.isc.org/article/AA-00252/31/Putting-the-working-server-in-a-failover-pair-into-partner-down-state.html
|
491
|
* , that seems to be right! Whereas the documentation tells something different
|
492
|
* in: https://www.isc.org/wp-content/uploads/2017/08/dhcp43leases.html
|
493
|
* , it writes: peer state state at date;
|
494
|
* For more about how we implement see under level 1 below.
|
495
|
*
|
496
|
* FPN = failover pair name
|
497
|
* Relevant source places:
|
498
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/db.c
|
499
|
* method: write_failover_state
|
500
|
*
|
501
|
* Same idea as with base_regex above, but not expanded to save space.
|
502
|
*/
|
503
|
const failover_base_regex = <<<'FAILOVERBASE'
|
504
|
(?x)
|
505
|
failover\s+peer\s+\"(?'FPN'.*)\"\s+state\s+\{
|
506
|
(?'NEXT_LEVEL'(?>\s*
|
507
|
(?>[^\{\}\n]+;|
|
508
|
[^\{\}\n]+\s*\{
|
509
|
(?&NEXT_LEVEL)
|
510
|
\}\s*
|
511
|
)*)*)
|
512
|
\}
|
513
|
FAILOVERBASE;
|
514
|
|
515
|
/*
|
516
|
* Handling of failover statements - level 1
|
517
|
*
|
518
|
* Definition of state names:
|
519
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/failover.c;h=25e1b72b5ed1705981fa7867d7c6172daa27f5a0;hb=HEAD
|
520
|
* const char *dhcp_failover_state_name_print (enum failover_state state)
|
521
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=includes/failover.h
|
522
|
* enum failover_state, that holds the enumerated state names and the typedef
|
523
|
* of dhcp_failover_state_t with references to dhcp_failover_config_t me,
|
524
|
* partner and the enumerated failover_state for saved_state that is used by
|
525
|
* int write_failover_state (dhcp_failover_state_t *state) in:
|
526
|
* https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/db.c
|
527
|
* to write the actual failover info in our lease file!
|
528
|
*
|
529
|
*/
|
530
|
const p_date_time = <<< 'PDATETIME'
|
531
|
(?'PWEEKDAY'[0-6])\s+
|
532
|
(?'PDATE'
|
533
|
(?'PYEAR'\d+)\/(?'PMONTH'(?>0[1-9]|1[0-2]))\/
|
534
|
(?'PDAY'(?>0[1-9]|[1-2][0-9]|30|31))
|
535
|
)\s+
|
536
|
(?'PTIME'(?>[0-1][0-9]|2[0-3]):(?>[0-5][0-9]):(?>[0-5][0-9]|60))
|
537
|
PDATETIME;
|
538
|
|
539
|
/* Layout of these matters. 'recover-...' must come before 'recover', because
|
540
|
* we make use of atomic grouping in failover_level1 to save amount of steps to
|
541
|
* match!
|
542
|
*/
|
543
|
const failover_states = 'unknown-state|partner-down|normal|conflict-done'.
|
544
|
'|communications-interrupted|resolution-interrupted|potential-conflict'.
|
545
|
'|recover-done|recover-wait|recover|shutdown|paused|startup';
|
546
|
|
547
|
const failover_level1_regex =
|
548
|
'(?x)
|
549
|
(
|
550
|
my\s+state\s+(?\'MYSTATE\'(?>'.failover_states.'))\s+
|
551
|
at\s+(?\'MYDATE\''.date_time.')
|
552
|
|
|
553
|
partner\s+state\s+(?\'PEERSTATE\'(?&MYSTATE))\s+
|
554
|
at\s+(?\'PEERDATE\''.p_date_time.')
|
555
|
|
|
556
|
mclt\s+(?\'MCLT\'\d+)
|
557
|
);\s*';
|