]> git.proxmox.com Git - mirror_frr.git/blame - lib/command_match.c
lib: Add matching and argv support
[mirror_frr.git] / lib / command_match.c
CommitLineData
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
7static int
8add_nexthops(struct list *, struct graph_node *);
18be0e59 9
eceb1066
QY
10static struct list *
11match_build_argv_r (struct graph_node *start, vector vline, unsigned int n);
12
13/* token matcher prototypes */
14static enum match_type
15match_ipv4 (const char *);
16
880e24a1 17static enum match_type
eceb1066 18match_ipv4_prefix (const char *);
18be0e59 19
880e24a1 20static enum match_type
eceb1066 21match_ipv6 (const char *);
18be0e59 22
880e24a1 23static enum match_type
eceb1066 24match_ipv6_prefix (const char *);
18be0e59 25
880e24a1 26static enum match_type
eceb1066 27match_range (struct graph_node *, const char *str);
18be0e59 28
880e24a1 29static enum match_type
eceb1066 30match_word (struct graph_node *, enum filter_type, const char *);
18be0e59 31
880e24a1 32static enum match_type
eceb1066 33match_number (struct graph_node *, const char *);
18be0e59 34
eceb1066
QY
35static enum match_type
36match_variable (struct graph_node *, const char *);
18be0e59 37
880e24a1
QY
38static enum match_type
39match_token (struct graph_node *, char *, enum filter_type);
18be0e59 40
880e24a1 41/* matching functions */
18be0e59 42
eceb1066
QY
43struct cmd_element *
44match_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 61struct list *
eceb1066 62match_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 */
126static int
127add_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
148struct list *
149match_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
170static struct list *
171match_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
218static enum match_type
219match_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 247static enum match_type
eceb1066 248match_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 264static enum match_type
eceb1066 265match_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 305static enum match_type
eceb1066 306match_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 325static enum match_type
eceb1066 326match_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 363static enum match_type
eceb1066 364match_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 383static enum match_type
eceb1066
QY
384match_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
406static enum match_type
407match_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
418static enum match_type
419match_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}