]>
Commit | Line | Data |
---|---|---|
eceb1066 QY |
1 | #include "command_match.h" |
2 | #include "command_parse.h" | |
9d0662e0 QY |
3 | #include <zebra.h> |
4 | #include "memory.h" | |
9d0662e0 | 5 | |
eceb1066 | 6 | /* matcher helper prototypes */ |
880e24a1 QY |
7 | static int |
8 | add_nexthops(struct list *, struct graph_node *); | |
18be0e59 | 9 | |
eceb1066 QY |
10 | static struct list * |
11 | match_build_argv_r (struct graph_node *start, vector vline, unsigned int n); | |
12 | ||
13 | /* token matcher prototypes */ | |
14 | static enum match_type | |
15 | match_ipv4 (const char *); | |
16 | ||
880e24a1 | 17 | static enum match_type |
eceb1066 | 18 | match_ipv4_prefix (const char *); |
18be0e59 | 19 | |
880e24a1 | 20 | static enum match_type |
eceb1066 | 21 | match_ipv6 (const char *); |
18be0e59 | 22 | |
880e24a1 | 23 | static enum match_type |
eceb1066 | 24 | match_ipv6_prefix (const char *); |
18be0e59 | 25 | |
880e24a1 | 26 | static enum match_type |
eceb1066 | 27 | match_range (struct graph_node *, const char *str); |
18be0e59 | 28 | |
880e24a1 | 29 | static enum match_type |
eceb1066 | 30 | match_word (struct graph_node *, enum filter_type, const char *); |
18be0e59 | 31 | |
880e24a1 | 32 | static enum match_type |
eceb1066 | 33 | match_number (struct graph_node *, const char *); |
18be0e59 | 34 | |
eceb1066 QY |
35 | static enum match_type |
36 | match_variable (struct graph_node *, const char *); | |
18be0e59 | 37 | |
880e24a1 QY |
38 | static enum match_type |
39 | match_token (struct graph_node *, char *, enum filter_type); | |
18be0e59 | 40 | |
880e24a1 | 41 | /* matching functions */ |
18be0e59 | 42 | |
eceb1066 QY |
43 | struct cmd_element * |
44 | match_command (struct graph_node *start, const char *line, enum filter_type filter) | |
a53fbbf5 QY |
45 | { |
46 | // get all possible completions | |
eceb1066 | 47 | struct list *completions = match_command_complete (start, line, filter); |
a53fbbf5 QY |
48 | |
49 | // one of them should be END_GN if this command matches | |
50 | struct graph_node *gn; | |
51 | struct listnode *node; | |
eceb1066 | 52 | for (ALL_LIST_ELEMENTS_RO(completions,node,gn)) |
a53fbbf5 QY |
53 | { |
54 | if (gn->type == END_GN) | |
55 | break; | |
56 | gn = NULL; | |
57 | } | |
58 | return gn ? gn->element : NULL; | |
59 | } | |
60 | ||
880e24a1 | 61 | struct list * |
eceb1066 | 62 | match_command_complete (struct graph_node *start, const char *line, enum filter_type filter) |
9d0662e0 | 63 | { |
eceb1066 QY |
64 | // vectorize command line |
65 | vector vline = cmd_make_strvec (line); | |
18be0e59 QY |
66 | |
67 | // pointer to next input token to match | |
68 | char *token; | |
69 | ||
70 | struct list *current = list_new(), // current nodes to match input token against | |
71 | *matched = list_new(), // current nodes that match the input token | |
72 | *next = list_new(); // possible next hops to current input token | |
73 | ||
74 | // pointers used for iterating lists | |
a53fbbf5 | 75 | struct graph_node *gn; |
18be0e59 QY |
76 | struct listnode *node; |
77 | ||
78 | // add all children of start node to list | |
79 | add_nexthops(next, start); | |
80 | ||
81 | unsigned int idx; | |
eceb1066 | 82 | for (idx = 0; idx < vector_active(vline) && next->count > 0; idx++) |
18be0e59 QY |
83 | { |
84 | list_free (current); | |
85 | current = next; | |
86 | next = list_new(); | |
87 | ||
eceb1066 | 88 | token = vector_slot(vline, idx); |
18be0e59 QY |
89 | |
90 | list_delete_all_node(matched); | |
91 | ||
a53fbbf5 | 92 | for (ALL_LIST_ELEMENTS_RO(current,node,gn)) |
18be0e59 | 93 | { |
a53fbbf5 QY |
94 | if (match_token(gn, token, filter) == exact_match) { |
95 | listnode_add(matched, gn); | |
96 | add_nexthops(next, gn); | |
18be0e59 QY |
97 | } |
98 | } | |
99 | } | |
100 | ||
101 | /* Variable summary | |
102 | * ----------------------------------------------------------------- | |
103 | * token = last input token processed | |
104 | * idx = index in `command` of last token processed | |
105 | * current = set of all transitions from the previous input token | |
106 | * matched = set of all nodes reachable with current input | |
107 | * next = set of all nodes reachable from all nodes in `matched` | |
108 | */ | |
880e24a1 QY |
109 | list_free (current); |
110 | list_free (matched); | |
111 | ||
eceb1066 QY |
112 | cmd_free_strvec(vline); |
113 | ||
880e24a1 QY |
114 | return next; |
115 | } | |
116 | ||
117 | /** | |
118 | * Adds all children that are reachable by one parser hop | |
119 | * to the given list. NUL_GN, SELECTOR_GN, and OPTION_GN | |
eceb1066 | 120 | * nodes are treated as transparent. |
880e24a1 QY |
121 | * |
122 | * @param[out] l the list to add the children to | |
123 | * @param[in] node the node to get the children of | |
124 | * @return the number of children added to the list | |
125 | */ | |
126 | static int | |
127 | add_nexthops(struct list *l, struct graph_node *node) | |
128 | { | |
129 | int added = 0; | |
130 | struct graph_node *child; | |
131 | for (unsigned int i = 0; i < vector_active(node->children); i++) | |
132 | { | |
133 | child = vector_slot(node->children, i); | |
a53fbbf5 QY |
134 | switch (child->type) { |
135 | case OPTION_GN: | |
136 | case SELECTOR_GN: | |
137 | case NUL_GN: | |
138 | added += add_nexthops(l, child); | |
139 | break; | |
140 | default: | |
141 | listnode_add(l, child); | |
142 | added++; | |
880e24a1 QY |
143 | } |
144 | } | |
145 | return added; | |
146 | } | |
18be0e59 | 147 | |
eceb1066 QY |
148 | struct list * |
149 | match_build_argv (const char *line, struct cmd_element *element) | |
a53fbbf5 | 150 | { |
eceb1066 QY |
151 | struct list *argv = NULL; |
152 | ||
153 | // parse command | |
a53fbbf5 | 154 | struct graph_node *start = new_node(NUL_GN); |
eceb1066 QY |
155 | parse_command_format(start, element); |
156 | ||
157 | vector vline = cmd_make_strvec (line); | |
158 | ||
159 | for (unsigned int i = 0; i < vector_active(start->children); i++) | |
160 | { | |
161 | // call recursive builder on each starting child | |
162 | argv = match_build_argv_r (vector_slot(start->children, i), vline, 0); | |
163 | // if any of them succeed, return their argv (there should only be one) | |
164 | if (argv) break; | |
165 | } | |
166 | ||
167 | return argv; | |
a53fbbf5 | 168 | } |
a53fbbf5 | 169 | |
eceb1066 QY |
170 | static struct list * |
171 | match_build_argv_r (struct graph_node *start, vector vline, unsigned int n) | |
172 | { | |
173 | // if we don't match this node, die | |
174 | if (match_token(start, vector_slot(vline, n), FILTER_STRICT) == no_match) | |
175 | return NULL; | |
176 | ||
177 | // some stuffs we need | |
178 | struct list *argv = list_new(); | |
179 | struct graph_node *gn; | |
180 | struct listnode *ln; | |
181 | ||
182 | // append current arg | |
183 | start->arg = strdup(vector_slot(vline, n)); | |
184 | listnode_add(argv, start); | |
185 | ||
186 | // get all possible nexthops | |
187 | struct list *next = list_new(); | |
188 | add_nexthops(next, start); | |
189 | ||
190 | // check if one of them is END_GN | |
191 | for (ALL_LIST_ELEMENTS_RO(next,ln,gn)) | |
192 | { | |
193 | if (gn->type == END_GN){ | |
194 | fprintf(stderr, "Hit END_GN while searching next set of node with text %s\n", start->text); | |
195 | return argv; | |
196 | } | |
197 | } | |
198 | ||
199 | // if we have no more input, why even live? | |
200 | if (n+1 >= vector_active(vline)) return NULL; | |
201 | ||
202 | // otherwise recurse on all nexthops | |
203 | for (ALL_LIST_ELEMENTS_RO(next,ln,gn)) | |
204 | { | |
205 | for (unsigned int i = 0; i < n; i++) fprintf(stderr, "\t"); | |
206 | fprintf(stderr, "Recursing on node %s for token %s\n", gn->text, (char*) vector_slot(vline, n+1)); | |
207 | struct list *result = match_build_argv_r (gn, vline, n+1); | |
208 | if (result != NULL) { | |
209 | list_add_list (argv, result); | |
210 | return argv; | |
211 | } | |
212 | } | |
213 | return NULL; | |
214 | } | |
18be0e59 | 215 | |
880e24a1 QY |
216 | /* matching utility functions */ |
217 | ||
218 | static enum match_type | |
219 | match_token (struct graph_node *node, char *token, enum filter_type filter) | |
220 | { | |
221 | switch (node->type) { | |
222 | case WORD_GN: | |
eceb1066 | 223 | return match_word (node, filter, token); |
880e24a1 | 224 | case IPV4_GN: |
eceb1066 | 225 | return match_ipv4 (token); |
880e24a1 | 226 | case IPV4_PREFIX_GN: |
eceb1066 | 227 | return match_ipv4_prefix (token); |
880e24a1 | 228 | case IPV6_GN: |
eceb1066 | 229 | return match_ipv6 (token); |
880e24a1 | 230 | case IPV6_PREFIX_GN: |
eceb1066 | 231 | return match_ipv6_prefix (token); |
880e24a1 | 232 | case RANGE_GN: |
eceb1066 | 233 | return match_range (node, token); |
880e24a1 | 234 | case NUMBER_GN: |
eceb1066 | 235 | return match_number (node, token); |
880e24a1 | 236 | case VARIABLE_GN: |
eceb1066 QY |
237 | return match_variable (node, token); |
238 | case END_GN: | |
880e24a1 QY |
239 | default: |
240 | return no_match; | |
241 | } | |
9d0662e0 QY |
242 | } |
243 | ||
9d0662e0 QY |
244 | #define IPV4_ADDR_STR "0123456789." |
245 | #define IPV4_PREFIX_STR "0123456789./" | |
246 | ||
880e24a1 | 247 | static enum match_type |
eceb1066 | 248 | match_ipv4 (const char *str) |
9d0662e0 QY |
249 | { |
250 | struct sockaddr_in sin_dummy; | |
251 | ||
252 | if (str == NULL) | |
253 | return partly_match; | |
254 | ||
255 | if (strspn (str, IPV4_ADDR_STR) != strlen (str)) | |
256 | return no_match; | |
257 | ||
258 | if (inet_pton(AF_INET, str, &sin_dummy.sin_addr) != 1) | |
259 | return no_match; | |
260 | ||
261 | return exact_match; | |
262 | } | |
263 | ||
880e24a1 | 264 | static enum match_type |
eceb1066 | 265 | match_ipv4_prefix (const char *str) |
9d0662e0 QY |
266 | { |
267 | struct sockaddr_in sin_dummy; | |
268 | const char *delim = "/\0"; | |
269 | char *dupe, *prefix, *mask, *context, *endptr; | |
270 | int nmask = -1; | |
271 | ||
272 | if (str == NULL) | |
273 | return partly_match; | |
274 | ||
275 | if (strspn (str, IPV4_PREFIX_STR) != strlen (str)) | |
276 | return no_match; | |
277 | ||
278 | /* tokenize to address + mask */ | |
279 | dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); | |
280 | strncpy(dupe, str, strlen(str)+1); | |
281 | prefix = strtok_r(dupe, delim, &context); | |
282 | mask = strtok_r(NULL, delim, &context); | |
283 | ||
284 | if (!mask) | |
285 | return partly_match; | |
286 | ||
287 | /* validate prefix */ | |
288 | if (inet_pton(AF_INET, prefix, &sin_dummy.sin_addr) != 1) | |
289 | return no_match; | |
290 | ||
291 | /* validate mask */ | |
292 | nmask = strtol (mask, &endptr, 10); | |
293 | if (*endptr != '\0' || nmask < 0 || nmask > 32) | |
294 | return no_match; | |
295 | ||
296 | XFREE(MTYPE_TMP, dupe); | |
297 | ||
298 | return exact_match; | |
299 | } | |
300 | ||
a53fbbf5 | 301 | #ifdef HAVE_IPV6 |
9d0662e0 QY |
302 | #define IPV6_ADDR_STR "0123456789abcdefABCDEF:." |
303 | #define IPV6_PREFIX_STR "0123456789abcdefABCDEF:./" | |
304 | ||
880e24a1 | 305 | static enum match_type |
eceb1066 | 306 | match_ipv6 (const char *str) |
9d0662e0 QY |
307 | { |
308 | struct sockaddr_in6 sin6_dummy; | |
309 | int ret; | |
310 | ||
311 | if (str == NULL) | |
312 | return partly_match; | |
313 | ||
314 | if (strspn (str, IPV6_ADDR_STR) != strlen (str)) | |
315 | return no_match; | |
316 | ||
317 | ret = inet_pton(AF_INET6, str, &sin6_dummy.sin6_addr); | |
318 | ||
319 | if (ret == 1) | |
320 | return exact_match; | |
321 | ||
322 | return no_match; | |
323 | } | |
324 | ||
880e24a1 | 325 | static enum match_type |
eceb1066 | 326 | match_ipv6_prefix (const char *str) |
9d0662e0 QY |
327 | { |
328 | struct sockaddr_in6 sin6_dummy; | |
329 | const char *delim = "/\0"; | |
330 | char *dupe, *prefix, *mask, *context, *endptr; | |
331 | int nmask = -1; | |
332 | ||
333 | if (str == NULL) | |
334 | return partly_match; | |
335 | ||
336 | if (strspn (str, IPV6_PREFIX_STR) != strlen (str)) | |
337 | return no_match; | |
338 | ||
339 | /* tokenize to address + mask */ | |
340 | dupe = XMALLOC(MTYPE_TMP, strlen(str)+1); | |
341 | strncpy(dupe, str, strlen(str)+1); | |
342 | prefix = strtok_r(dupe, delim, &context); | |
343 | mask = strtok_r(NULL, delim, &context); | |
344 | ||
345 | if (!mask) | |
346 | return partly_match; | |
347 | ||
348 | /* validate prefix */ | |
349 | if (inet_pton(AF_INET6, prefix, &sin6_dummy.sin6_addr) != 1) | |
350 | return no_match; | |
351 | ||
352 | /* validate mask */ | |
353 | nmask = strtol (mask, &endptr, 10); | |
354 | if (*endptr != '\0' || nmask < 0 || nmask > 128) | |
355 | return no_match; | |
356 | ||
357 | XFREE(MTYPE_TMP, dupe); | |
358 | ||
359 | return exact_match; | |
360 | } | |
361 | #endif | |
362 | ||
880e24a1 | 363 | static enum match_type |
eceb1066 | 364 | match_range (struct graph_node *rangenode, const char *str) |
9d0662e0 QY |
365 | { |
366 | char *endptr = NULL; | |
eceb1066 | 367 | signed long val; |
9d0662e0 QY |
368 | |
369 | if (str == NULL) | |
370 | return 1; | |
371 | ||
372 | val = strtoll (str, &endptr, 10); | |
373 | if (*endptr != '\0') | |
374 | return 0; | |
375 | val = llabs(val); | |
376 | ||
377 | if (val < rangenode->min || val > rangenode->max) | |
378 | return no_match; | |
379 | else | |
380 | return exact_match; | |
381 | } | |
382 | ||
880e24a1 | 383 | static enum match_type |
eceb1066 QY |
384 | match_word(struct graph_node *wordnode, |
385 | enum filter_type filter, | |
386 | const char *word) | |
9d0662e0 QY |
387 | { |
388 | if (filter == FILTER_RELAXED) | |
a53fbbf5 | 389 | { |
9d0662e0 QY |
390 | if (!word || !strlen(word)) |
391 | return partly_match; | |
a53fbbf5 QY |
392 | else if (!strncmp(wordnode->text, word, strlen(word))) |
393 | return !strcmp(wordnode->text, word) ? exact_match : partly_match; | |
394 | else | |
395 | return no_match; | |
396 | } | |
397 | else | |
9d0662e0 | 398 | { |
a53fbbf5 QY |
399 | if (!word) |
400 | return no_match; | |
401 | else | |
402 | return !strcmp(wordnode->text, word) ? exact_match : no_match; | |
9d0662e0 | 403 | } |
a53fbbf5 | 404 | } |
9d0662e0 | 405 | |
eceb1066 QY |
406 | static enum match_type |
407 | match_number(struct graph_node *numnode, const char *word) | |
a53fbbf5 QY |
408 | { |
409 | if (!strcmp("\0", word)) return no_match; | |
410 | char *endptr; | |
411 | long num = strtol(word, &endptr, 10); | |
412 | if (endptr != '\0') return no_match; | |
413 | return num == numnode->value ? exact_match : no_match; | |
414 | } | |
415 | ||
eceb1066 | 416 | #define VARIABLE_ALPHABET "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890" |
a53fbbf5 | 417 | |
eceb1066 QY |
418 | static enum match_type |
419 | match_variable(struct graph_node *varnode, const char *word) | |
a53fbbf5 | 420 | { |
eceb1066 QY |
421 | return strlen(word) == strspn(word, VARIABLE_ALPHABET) && isalpha(word[0]) ? |
422 | exact_match : no_match; | |
9d0662e0 | 423 | } |