1 |
c56471a7
|
Anders Lind
|
<?php
|
2 |
|
|
/*
|
3 |
|
|
* parser_dhcpv6_leases.inc
|
4 |
|
|
*
|
5 |
0b4c14a4
|
Steve Beaver
|
* Copyright (c) 2017-2019 Anders Lind (anders.lind@gmail.com)
|
6 |
c56471a7
|
Anders Lind
|
* All rights reserved.
|
7 |
|
|
*
|
8 |
|
|
* part of pfSense (https://www.pfsense.org)
|
9 |
38809d47
|
Renato Botelho do Couto
|
* Copyright (c) 2004-2013 BSD Perimeter
|
10 |
|
|
* Copyright (c) 2013-2016 Electric Sheep Fencing
|
11 |
|
|
* Copyright (c) 2014-2019 Rubicon Communications, LLC (Netgate)
|
12 |
c56471a7
|
Anders Lind
|
* 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 |
f95579a6
|
Anders Lind
|
function get_ndpdata() {
|
63 |
c56471a7
|
Anders Lind
|
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 |
f95579a6
|
Anders Lind
|
function gui_parse_leases(&$pools, &$leases, &$prefixes, &$mappings, $raw_leases, $ndpdata, $lang_pack) {
|
87 |
2a0be57e
|
Stephen Jones
|
$amount_of_matches = preg_match_all('/'.base_regex.'/i', (string) $raw_leases, $lease_array, PREG_SET_ORDER);
|
88 |
c56471a7
|
Anders Lind
|
|
89 |
|
|
if ($amount_of_matches == false) {
|
90 |
|
|
return false;
|
91 |
|
|
}
|
92 |
|
|
|
93 |
f95579a6
|
Anders Lind
|
$amount_of_leases = 0;
|
94 |
c56471a7
|
Anders Lind
|
foreach ($lease_array as $val) {
|
95 |
f95579a6
|
Anders Lind
|
general_debug('---LEASE '.++$amount_of_leases.' START---');
|
96 |
c56471a7
|
Anders Lind
|
|
97 |
f95579a6
|
Anders Lind
|
$iaid_duid = parse_duid($val['IAID_DUID']);
|
98 |
c56471a7
|
Anders Lind
|
|
99 |
f95579a6
|
Anders Lind
|
general_debug($val, 'IAID_DUID');
|
100 |
c56471a7
|
Anders Lind
|
|
101 |
f95579a6
|
Anders Lind
|
$entry = [ 'iaid' => hexdec(implode("", array_reverse($iaid_duid[0]))),
|
102 |
|
|
'duid' => implode(":", $iaid_duid[1]) ];
|
103 |
c56471a7
|
Anders Lind
|
|
104 |
f95579a6
|
Anders Lind
|
general_debug($entry, 'iaid');
|
105 |
|
|
general_debug($entry, 'duid');
|
106 |
c56471a7
|
Anders Lind
|
|
107 |
|
|
if (preg_match('/^ia-na$/i', $val['IA_ASSOC'])) {
|
108 |
f95579a6
|
Anders Lind
|
gui_parse_single_lease(IA_NA, $val['NEXT_LEVEL'], $lang_pack, $ndpdata, $mappings, $entry);
|
109 |
c56471a7
|
Anders Lind
|
$leases[] = $entry;
|
110 |
f95579a6
|
Anders Lind
|
} 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 |
c56471a7
|
Anders Lind
|
// Not used in the pfSense UI at the moment.
|
115 |
f95579a6
|
Anders Lind
|
general_debug('Notice: ia-ta is unhandled for now.');
|
116 |
c56471a7
|
Anders Lind
|
}
|
117 |
|
|
|
118 |
f95579a6
|
Anders Lind
|
general_debug("---LEASE $amount_of_leases END---\n");
|
119 |
c56471a7
|
Anders Lind
|
}
|
120 |
|
|
|
121 |
|
|
// Handle failover - NOTE Experimental;
|
122 |
|
|
// has not been tested on a live configured system.
|
123 |
f95579a6
|
Anders Lind
|
$amount_of_matches = preg_match_all('/'.failover_base_regex.'/i', $raw_leases, $lease_array, PREG_SET_ORDER);
|
124 |
|
|
$amount_of_leases = 0;
|
125 |
c56471a7
|
Anders Lind
|
foreach ($lease_array as $val) {
|
126 |
f95579a6
|
Anders Lind
|
general_debug('---FAILOVER '.++$amount_of_leases.' START---');
|
127 |
c56471a7
|
Anders Lind
|
$entry = ['name' => $val['FPN']];
|
128 |
f95579a6
|
Anders Lind
|
general_debug($val, 'FPN');
|
129 |
|
|
gui_parse_single_failover($val['NEXT_LEVEL'], $entry);
|
130 |
c56471a7
|
Anders Lind
|
$pools[] = $entry;
|
131 |
f95579a6
|
Anders Lind
|
general_debug("---FAILOVER $amount_of_leases END---\n");
|
132 |
c56471a7
|
Anders Lind
|
}
|
133 |
|
|
|
134 |
f95579a6
|
Anders Lind
|
general_debug('Leases', $leases);
|
135 |
|
|
general_debug('Prefixes', $prefixes);
|
136 |
|
|
general_debug('Mappings', $mappings);
|
137 |
|
|
general_debug('Pools', $pools);
|
138 |
c56471a7
|
Anders Lind
|
}
|
139 |
|
|
|
140 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
153 |
|
|
|
154 |
f95579a6
|
Anders Lind
|
$amount_of_matches = 0;
|
155 |
c56471a7
|
Anders Lind
|
|
156 |
|
|
foreach ($matches as $val) {
|
157 |
f95579a6
|
Anders Lind
|
general_debug("\n --Match number " . ++$amount_of_matches . ' start.');
|
158 |
|
|
general_debug("Matched: " . $val[0]);
|
159 |
c56471a7
|
Anders Lind
|
|
160 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
|
164 |
f95579a6
|
Anders Lind
|
if (setnlen($val, 'NEXT_LEVEL')) {
|
165 |
|
|
general_debug($val, 'NEXT_LEVEL');
|
166 |
c56471a7
|
Anders Lind
|
|
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 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
176 |
|
|
|
177 |
f95579a6
|
Anders Lind
|
general_debug(' --Match number ' . $amount_of_matches . ' end.');
|
178 |
c56471a7
|
Anders Lind
|
}
|
179 |
|
|
}
|
180 |
|
|
|
181 |
f95579a6
|
Anders Lind
|
function gui_parse_single_na_lease($val, $lang_pack, $ndpdata, &$mappings, &$entry) {
|
182 |
|
|
if (setnlen($val, 'ADDRESS')) {
|
183 |
|
|
general_debug($val, 'ADDRESS');
|
184 |
c56471a7
|
Anders Lind
|
|
185 |
f95579a6
|
Anders Lind
|
$space_result = parse_all_ipv6_to_array($val['ADDRESS']);
|
186 |
c56471a7
|
Anders Lind
|
|
187 |
|
|
if (count($space_result) == 1) {
|
188 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
213 |
f95579a6
|
Anders Lind
|
} else {
|
214 |
|
|
general_debug('WARNING: Count of IPv6 addresses is: '.count($space_result));
|
215 |
c56471a7
|
Anders Lind
|
}
|
216 |
|
|
}
|
217 |
|
|
}
|
218 |
|
|
|
219 |
f95579a6
|
Anders Lind
|
function gui_parse_single_pd_lease($val, $lang_pack, &$entry) {
|
220 |
|
|
if (setnlen($val, 'SPACE')) {
|
221 |
|
|
general_debug($val, 'SPACE');
|
222 |
c56471a7
|
Anders Lind
|
|
223 |
f95579a6
|
Anders Lind
|
$space_result = parse_all_ipv6_to_array($val['SPACE']);
|
224 |
c56471a7
|
Anders Lind
|
|
225 |
|
|
if (count($space_result) == 1) {
|
226 |
f95579a6
|
Anders Lind
|
if (setnlen($space_result[0], 'MATCH')) {
|
227 |
|
|
general_debug("We have a valid ipv6 address");
|
228 |
c56471a7
|
Anders Lind
|
|
229 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
235 |
|
|
|
236 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
243 |
f95579a6
|
Anders Lind
|
} else {
|
244 |
|
|
general_debug('WARNING: Count of IPv6 addresses is: '.count($space_result));
|
245 |
c56471a7
|
Anders Lind
|
}
|
246 |
|
|
}
|
247 |
|
|
}
|
248 |
|
|
|
249 |
|
|
function gui_handle_ia($content, &$entry, $lang_pack) {
|
250 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
foreach ($ia_matches as $ia_val) {
|
260 |
f95579a6
|
Anders Lind
|
if (setnlen($ia_val, 'BINDINGSTATE')) {
|
261 |
c56471a7
|
Anders Lind
|
switch ($ia_val['BINDINGSTATE']) {
|
262 |
|
|
case "active":
|
263 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
break;
|
268 |
|
|
case "free":
|
269 |
f95579a6
|
Anders Lind
|
check_n_set($entry, 'act', $lang_pack['expired'], true);
|
270 |
|
|
check_n_set($entry, 'online', $lang_pack['offline'], true);
|
271 |
c56471a7
|
Anders Lind
|
break;
|
272 |
|
|
case "backup":
|
273 |
f95579a6
|
Anders Lind
|
check_n_set($entry, 'act', $lang_pack['reserved'], true);
|
274 |
|
|
check_n_set($entry, 'online', $lang_pack['offline'], true);
|
275 |
c56471a7
|
Anders Lind
|
break;
|
276 |
|
|
case "released":
|
277 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
break;
|
284 |
|
|
}
|
285 |
|
|
|
286 |
f95579a6
|
Anders Lind
|
general_debug($ia_val, 'BINDINGSTATE');
|
287 |
c56471a7
|
Anders Lind
|
}
|
288 |
|
|
|
289 |
f95579a6
|
Anders Lind
|
// 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 |
c56471a7
|
Anders Lind
|
}
|
299 |
|
|
}
|
300 |
|
|
|
301 |
f95579a6
|
Anders Lind
|
function gui_parse_single_failover($content, &$entry) {
|
302 |
|
|
preg_match_all('/'.failover_level1_regex.'/i', $content, $matches, PREG_SET_ORDER);
|
303 |
c56471a7
|
Anders Lind
|
|
304 |
f95579a6
|
Anders Lind
|
/* 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 |
c56471a7
|
Anders Lind
|
foreach ($matches as $val) {
|
312 |
f95579a6
|
Anders Lind
|
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 |
c56471a7
|
Anders Lind
|
}
|
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 |
f3f98e97
|
Phil Davis
|
* individual components needs to be handled separately at each level by other
|
450 |
c56471a7
|
Anders Lind
|
* 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 |
f95579a6
|
Anders Lind
|
* defined in https://source.isc.org/cgi-bin/gitweb.cgi?p=dhcp.git;a=blob;f=server/stables.c
|
470 |
c56471a7
|
Anders Lind
|
*/
|
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*';
|