jcs's openbsd hax
openbsd
1/* $OpenBSD: parser.c,v 1.4 2016/01/15 12:57:49 renato Exp $ */
2
3/*
4 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
5 * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
6 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 */
20
21#include <sys/types.h>
22#include <sys/socket.h>
23#include <netinet/in.h>
24#include <arpa/inet.h>
25#include <err.h>
26#include <errno.h>
27#include <limits.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <netdb.h>
32
33#include "eigrpd.h"
34
35#include "parser.h"
36
37enum token_type {
38 NOTOKEN,
39 ENDTOKEN,
40 KEYWORD,
41 FAMILY,
42 ASNUM,
43 ADDRESS,
44 FLAG,
45 PREFIX,
46 IFNAME
47};
48
49struct token {
50 enum token_type type;
51 const char *keyword;
52 int value;
53 const struct token *next;
54};
55
56static const struct token t_main[];
57static const struct token t_fib[];
58static const struct token t_show[];
59static const struct token t_show_iface[];
60static const struct token t_show_iface_af[];
61static const struct token t_show_iface_as[];
62static const struct token t_show_nbr[];
63static const struct token t_show_nbr_af[];
64static const struct token t_show_nbr_as[];
65static const struct token t_show_topology[];
66static const struct token t_show_topology_af[];
67static const struct token t_show_topology_as[];
68static const struct token t_show_fib[];
69static const struct token t_show_fib_af[];
70static const struct token t_show_stats[];
71static const struct token t_show_stats_af[];
72static const struct token t_show_stats_as[];
73static const struct token t_log[];
74static const struct token t_clear[];
75static const struct token t_clear_nbr[];
76static const struct token t_clear_nbr_af[];
77static const struct token t_clear_nbr_as[];
78
79static const struct token t_main[] = {
80 {KEYWORD, "reload", RELOAD, NULL},
81 {KEYWORD, "fib", FIB, t_fib},
82 {KEYWORD, "show", SHOW, t_show},
83 {KEYWORD, "clear", NONE, t_clear},
84 {KEYWORD, "log", NONE, t_log},
85 {ENDTOKEN, "", NONE, NULL}
86};
87
88static const struct token t_fib[] = {
89 { KEYWORD, "couple", FIB_COUPLE, NULL},
90 { KEYWORD, "decouple", FIB_DECOUPLE, NULL},
91 { ENDTOKEN, "", NONE, NULL}
92};
93
94static const struct token t_show[] = {
95 {NOTOKEN, "", NONE, NULL},
96 {KEYWORD, "interfaces", SHOW_IFACE, t_show_iface},
97 {KEYWORD, "neighbor", SHOW_NBR, t_show_nbr},
98 {KEYWORD, "topology", SHOW_TOPOLOGY, t_show_topology},
99 {KEYWORD, "fib", SHOW_FIB, t_show_fib},
100 {KEYWORD, "traffic", SHOW_STATS, t_show_stats},
101 {ENDTOKEN, "", NONE, NULL}
102};
103
104static const struct token t_show_iface[] = {
105 {NOTOKEN, "", NONE, NULL},
106 {KEYWORD, "family", NONE, t_show_iface_af},
107 {KEYWORD, "as", NONE, t_show_iface_as},
108 {KEYWORD, "detail", SHOW_IFACE_DTAIL, NULL},
109 {IFNAME, "", SHOW_IFACE_DTAIL, NULL},
110 {ENDTOKEN, "", NONE, NULL}
111};
112
113static const struct token t_show_iface_af[] = {
114 {FAMILY, "", NONE, t_show_iface},
115 {ENDTOKEN, "", NONE, NULL}
116};
117
118static const struct token t_show_iface_as[] = {
119 {ASNUM, "", NONE, t_show_iface},
120 {ENDTOKEN, "", NONE, NULL}
121};
122
123static const struct token t_show_nbr[] = {
124 {NOTOKEN, "", NONE, NULL},
125 {KEYWORD, "family", NONE, t_show_nbr_af},
126 {KEYWORD, "as", NONE, t_show_nbr_as},
127 {ENDTOKEN, "", NONE, NULL}
128};
129
130static const struct token t_show_nbr_af[] = {
131 {FAMILY, "", NONE, t_show_nbr},
132 {ENDTOKEN, "", NONE, NULL}
133};
134
135static const struct token t_show_nbr_as[] = {
136 {ASNUM, "", NONE, t_show_nbr},
137 {ENDTOKEN, "", NONE, NULL}
138};
139
140static const struct token t_show_topology[] = {
141 {NOTOKEN, "", NONE, NULL},
142 {KEYWORD, "family", NONE, t_show_topology_af},
143 {KEYWORD, "as", NONE, t_show_topology_as},
144 {PREFIX, "", NONE, NULL},
145 {FLAG, "active", F_CTL_ACTIVE, NULL},
146 {FLAG, "all-links", F_CTL_ALLLINKS, NULL},
147 {ENDTOKEN, "", NONE, NULL}
148};
149
150static const struct token t_show_topology_af[] = {
151 {FAMILY, "", NONE, t_show_topology},
152 {ENDTOKEN, "", NONE, NULL}
153};
154
155static const struct token t_show_topology_as[] = {
156 {ASNUM, "", NONE, t_show_topology},
157 {ENDTOKEN, "", NONE, NULL}
158};
159
160static const struct token t_show_fib[] = {
161 {NOTOKEN, "", NONE, NULL},
162 {KEYWORD, "family", NONE, t_show_fib_af},
163 {KEYWORD, "interface", SHOW_FIB_IFACE, t_show_iface},
164 {FLAG, "connected", F_CONNECTED, t_show_fib},
165 {FLAG, "static", F_STATIC, t_show_fib},
166 {FLAG, "eigrp", F_EIGRPD_INSERTED, t_show_fib},
167 {ENDTOKEN, "", NONE, NULL}
168};
169
170static const struct token t_show_fib_af[] = {
171 {FAMILY, "", NONE, t_show_fib},
172 {ENDTOKEN, "", NONE, NULL}
173};
174
175
176static const struct token t_show_stats[] = {
177 {NOTOKEN, "", NONE, NULL},
178 {KEYWORD, "family", NONE, t_show_stats_af},
179 {KEYWORD, "as", NONE, t_show_stats_as},
180 {ENDTOKEN, "", NONE, NULL}
181};
182
183static const struct token t_show_stats_af[] = {
184 {FAMILY, "", NONE, t_show_stats},
185 {ENDTOKEN, "", NONE, NULL}
186};
187
188static const struct token t_show_stats_as[] = {
189 {ASNUM, "", NONE, t_show_stats},
190 {ENDTOKEN, "", NONE, NULL}
191};
192
193static const struct token t_clear[] = {
194 {KEYWORD, "neighbors", CLEAR_NBR, t_clear_nbr},
195 {ENDTOKEN, "", NONE, NULL}
196};
197
198static const struct token t_clear_nbr[] = {
199 {NOTOKEN, "", NONE, NULL},
200 {KEYWORD, "as", NONE, t_clear_nbr_as},
201 {KEYWORD, "family", NONE, t_clear_nbr_af},
202 {ADDRESS, "", NONE, NULL},
203 {ENDTOKEN, "", NONE, NULL}
204};
205
206static const struct token t_clear_nbr_af[] = {
207 {FAMILY, "", NONE, t_clear_nbr},
208 {ENDTOKEN, "", NONE, NULL}
209};
210
211static const struct token t_clear_nbr_as[] = {
212 {ASNUM, "", NONE, t_clear_nbr},
213 {ENDTOKEN, "", NONE, NULL}
214};
215
216static const struct token t_log[] = {
217 {KEYWORD, "verbose", LOG_VERBOSE, NULL},
218 {KEYWORD, "brief", LOG_BRIEF, NULL},
219 {ENDTOKEN, "", NONE, NULL}
220};
221
222static const struct token *match_token(const char *, const struct token *,
223 struct parse_result *);
224static void show_valid_args(const struct token *);
225
226struct parse_result *
227parse(int argc, char *argv[])
228{
229 static struct parse_result res;
230 const struct token *table = t_main;
231 const struct token *match;
232
233 memset(&res, 0, sizeof(res));
234
235 while (argc >= 0) {
236 if ((match = match_token(argv[0], table, &res)) == NULL) {
237 fprintf(stderr, "valid commands/args:\n");
238 show_valid_args(table);
239 return (NULL);
240 }
241
242 argc--;
243 argv++;
244
245 if (match->type == NOTOKEN || match->next == NULL)
246 break;
247
248 table = match->next;
249 }
250
251 if (argc > 0) {
252 fprintf(stderr, "superfluous argument: %s\n", argv[0]);
253 return (NULL);
254 }
255
256 return (&res);
257}
258
259static const struct token *
260match_token(const char *word, const struct token *table,
261 struct parse_result *res)
262{
263 unsigned int i, match;
264 const struct token *t = NULL;
265
266 match = 0;
267
268 for (i = 0; table[i].type != ENDTOKEN; i++) {
269 switch (table[i].type) {
270 case NOTOKEN:
271 if (word == NULL || strlen(word) == 0) {
272 match++;
273 t = &table[i];
274 }
275 break;
276 case KEYWORD:
277 if (word != NULL && strncmp(word, table[i].keyword,
278 strlen(word)) == 0) {
279 match++;
280 t = &table[i];
281 if (t->value)
282 res->action = t->value;
283 }
284 break;
285 case FLAG:
286 if (word != NULL && strncmp(word, table[i].keyword,
287 strlen(word)) == 0) {
288 match++;
289 t = &table[i];
290 res->flags |= t->value;
291 }
292 break;
293 case FAMILY:
294 if (word == NULL)
295 break;
296 if (!strcmp(word, "inet") ||
297 !strcasecmp(word, "IPv4")) {
298 match++;
299 t = &table[i];
300 res->family = AF_INET;
301 }
302 if (!strcmp(word, "inet6") ||
303 !strcasecmp(word, "IPv6")) {
304 match++;
305 t = &table[i];
306 res->family = AF_INET6;
307 }
308 break;
309 case ASNUM:
310 if (parse_asnum(word, &res->as)) {
311 match++;
312 t = &table[i];
313 }
314 break;
315 case ADDRESS:
316 if (parse_addr(word, &res->family, &res->addr)) {
317 match++;
318 t = &table[i];
319 if (t->value)
320 res->action = t->value;
321 }
322 break;
323 case PREFIX:
324 if (parse_prefix(word, &res->family, &res->addr,
325 &res->prefixlen)) {
326 match++;
327 t = &table[i];
328 if (t->value)
329 res->action = t->value;
330 }
331 break;
332 case IFNAME:
333 if (!match && word != NULL && strlen(word) > 0) {
334 if (strlcpy(res->ifname, word,
335 sizeof(res->ifname)) >=
336 sizeof(res->ifname))
337 err(1, "interface name too long");
338 match++;
339 t = &table[i];
340 if (t->value)
341 res->action = t->value;
342 }
343 break;
344
345 case ENDTOKEN:
346 break;
347 }
348 }
349
350 if (match != 1) {
351 if (word == NULL)
352 fprintf(stderr, "missing argument:\n");
353 else if (match > 1)
354 fprintf(stderr, "ambiguous argument: %s\n", word);
355 else if (match < 1)
356 fprintf(stderr, "unknown argument: %s\n", word);
357 return (NULL);
358 }
359
360 return (t);
361}
362
363static void
364show_valid_args(const struct token *table)
365{
366 int i;
367
368 for (i = 0; table[i].type != ENDTOKEN; i++) {
369 switch (table[i].type) {
370 case NOTOKEN:
371 fprintf(stderr, " <cr>\n");
372 break;
373 case KEYWORD:
374 case FLAG:
375 fprintf(stderr, " %s\n", table[i].keyword);
376 break;
377 case FAMILY:
378 fprintf(stderr, " [ inet | inet6 | IPv4 | IPv6 ]\n");
379 break;
380 case ASNUM:
381 fprintf(stderr, " <asnum>\n");
382 break;
383 case ADDRESS:
384 fprintf(stderr, " <address>\n");
385 break;
386 case PREFIX:
387 fprintf(stderr, " <address>[/<len>]\n");
388 break;
389 case IFNAME:
390 fprintf(stderr, " <interface>\n");
391 break;
392 case ENDTOKEN:
393 break;
394 }
395 }
396}
397
398int
399parse_asnum(const char *word, uint16_t *asnum)
400{
401 const char *errstr;
402 uint32_t uval;
403
404 if (word == NULL)
405 return (0);
406
407 uval = strtonum(word, EIGRP_MIN_AS, EIGRP_MAX_AS, &errstr);
408 if (errstr)
409 errx(1, "AS number is %s: %s", errstr, word);
410
411 *asnum = uval;
412 return (1);
413}
414
415int
416parse_addr(const char *word, int *family, union eigrpd_addr *addr)
417{
418 struct in_addr ina;
419 struct addrinfo hints, *r;
420 struct sockaddr_in6 *sa_in6;
421
422 if (word == NULL)
423 return (0);
424
425 memset(addr, 0, sizeof(union eigrpd_addr));
426 memset(&ina, 0, sizeof(ina));
427
428 if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
429 *family = AF_INET;
430 addr->v4.s_addr = ina.s_addr;
431 return (1);
432 }
433
434 memset(&hints, 0, sizeof(hints));
435 hints.ai_family = AF_INET6;
436 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
437 hints.ai_flags = AI_NUMERICHOST;
438 if (getaddrinfo(word, "0", &hints, &r) == 0) {
439 sa_in6 = (struct sockaddr_in6 *)r->ai_addr;
440 *family = AF_INET6;
441 addr->v6 = sa_in6->sin6_addr;
442 freeaddrinfo(r);
443 return (1);
444 }
445
446 return (0);
447}
448
449int
450parse_prefix(const char *word, int *family, union eigrpd_addr *addr,
451 uint8_t *prefixlen)
452{
453 char *p, *ps;
454 const char *errstr;
455 size_t wordlen;
456 int mask = -1;
457
458 if (word == NULL)
459 return (0);
460 wordlen = strlen(word);
461
462 memset(addr, 0, sizeof(union eigrpd_addr));
463
464 if ((p = strrchr(word, '/')) != NULL) {
465 size_t plen = strlen(p);
466 mask = strtonum(p + 1, 0, 128, &errstr);
467 if (errstr)
468 errx(1, "netmask %s", errstr);
469
470 if ((ps = malloc(wordlen - plen + 1)) == NULL)
471 err(1, "parse_prefix: malloc");
472 strlcpy(ps, word, wordlen - plen + 1);
473
474 if (parse_addr(ps, family, addr) == 0) {
475 free(ps);
476 return (0);
477 }
478
479 free(ps);
480 } else
481 if (parse_addr(word, family, addr) == 0)
482 return (0);
483
484 switch (*family) {
485 case AF_INET:
486 if (mask == UINT8_MAX)
487 mask = 32;
488 if (mask > 32)
489 errx(1, "invalid netmask: too large");
490 break;
491 case AF_INET6:
492 if (mask == UINT8_MAX)
493 mask = 128;
494 if (mask > 128)
495 errx(1, "invalid netmask: too large");
496 break;
497 default:
498 return (0);
499 }
500 eigrp_applymask(*family, addr, addr, mask);
501 *prefixlen = mask;
502
503 return (1);
504}