xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision 6f05df2d9be0954bec42d51d943d77bd250fb664)
1 /*	$OpenBSD: parser.c,v 1.69 2014/11/19 21:11:41 tedu Exp $ */
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 
22 #include <err.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <netdb.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "parser.h"
33 #include "irrfilter.h"
34 
35 enum token_type {
36 	NOTOKEN,
37 	ENDTOKEN,
38 	KEYWORD,
39 	ADDRESS,
40 	PEERADDRESS,
41 	FLAG,
42 	ASNUM,
43 	ASTYPE,
44 	PREFIX,
45 	PEERDESC,
46 	RIBNAME,
47 	COMMUNITY,
48 	LOCALPREF,
49 	MED,
50 	NEXTHOP,
51 	PFTABLE,
52 	PREPNBR,
53 	PREPSELF,
54 	WEIGHT,
55 	FAMILY,
56 	GETOPT,
57 	RTABLE,
58 	FILENAME,
59 	BULK
60 };
61 
62 enum getopts {
63 	GETOPT_NONE,
64 	GETOPT_IRRFILTER
65 };
66 
67 struct token {
68 	enum token_type		 type;
69 	const char		*keyword;
70 	int			 value;
71 	const struct token	*next;
72 };
73 
74 static const struct token t_main[];
75 static const struct token t_show[];
76 static const struct token t_show_summary[];
77 static const struct token t_show_fib[];
78 static const struct token t_show_rib[];
79 static const struct token t_show_mrt[];
80 static const struct token t_show_mrt_file[];
81 static const struct token t_show_rib_neigh[];
82 static const struct token t_show_mrt_neigh[];
83 static const struct token t_show_rib_rib[];
84 static const struct token t_show_neighbor[];
85 static const struct token t_show_neighbor_modifiers[];
86 static const struct token t_fib[];
87 static const struct token t_neighbor[];
88 static const struct token t_neighbor_modifiers[];
89 static const struct token t_show_rib_as[];
90 static const struct token t_show_mrt_as[];
91 static const struct token t_show_prefix[];
92 static const struct token t_show_ip[];
93 static const struct token t_show_community[];
94 static const struct token t_network[];
95 static const struct token t_network_show[];
96 static const struct token t_prefix[];
97 static const struct token t_set[];
98 static const struct token t_community[];
99 static const struct token t_localpref[];
100 static const struct token t_med[];
101 static const struct token t_nexthop[];
102 static const struct token t_pftable[];
103 static const struct token t_prepnbr[];
104 static const struct token t_prepself[];
105 static const struct token t_weight[];
106 static const struct token t_irrfilter[];
107 static const struct token t_irrfilter_opts[];
108 static const struct token t_log[];
109 static const struct token t_fib_table[];
110 static const struct token t_show_fib_table[];
111 
112 static const struct token t_main[] = {
113 	{ KEYWORD,	"reload",	RELOAD,		NULL},
114 	{ KEYWORD,	"show",		SHOW,		t_show},
115 	{ KEYWORD,	"fib",		FIB,		t_fib},
116 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
117 	{ KEYWORD,	"network",	NONE,		t_network},
118 	{ KEYWORD,	"irrfilter",	IRRFILTER,	t_irrfilter},
119 	{ KEYWORD,	"log",		NONE,		t_log},
120 	{ ENDTOKEN,	"",		NONE,		NULL}
121 };
122 
123 static const struct token t_show[] = {
124 	{ NOTOKEN,	"",		NONE,		NULL},
125 	{ KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
126 	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
127 	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
128 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
129 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
130 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
131 	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
132 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
133 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
134 	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
135 	{ ENDTOKEN,	"",		NONE,		NULL}
136 };
137 
138 static const struct token t_show_summary[] = {
139 	{ NOTOKEN,	"",		NONE,			NULL},
140 	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
141 	{ ENDTOKEN,	"",		NONE,			NULL}
142 };
143 
144 static const struct token t_show_fib[] = {
145 	{ NOTOKEN,	"",		NONE,		 NULL},
146 	{ FLAG,		"connected",	F_CONNECTED,	 t_show_fib},
147 	{ FLAG,		"static",	F_STATIC,	 t_show_fib},
148 	{ FLAG,		"bgp",		F_BGPD_INSERTED, t_show_fib},
149 	{ FLAG,		"nexthop",	F_NEXTHOP,	 t_show_fib},
150 	{ KEYWORD,	"table",	NONE,		 t_show_fib_table},
151 	{ FAMILY,	"",		NONE,		 t_show_fib},
152 	{ ADDRESS,	"",		NONE,		 NULL},
153 	{ ENDTOKEN,	"",		NONE,		 NULL}
154 };
155 
156 static const struct token t_show_rib[] = {
157 	{ NOTOKEN,	"",		NONE,		NULL},
158 	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
159 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
160 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
161 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
162 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
163 	{ KEYWORD,	"community",	NONE,		t_show_community},
164 	{ FLAG,		"selected",	F_CTL_ACTIVE,	t_show_rib},
165 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_rib},
166 	{ FLAG,		"in",		F_CTL_ADJ_IN,	t_show_rib},
167 	{ FLAG,		"out",		F_CTL_ADJ_OUT,	t_show_rib},
168 	{ KEYWORD,	"neighbor",	NONE,		t_show_rib_neigh},
169 	{ KEYWORD,	"table",	NONE,		t_show_rib_rib},
170 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
171 	{ KEYWORD,	"memory",	SHOW_RIB_MEM,	NULL},
172 	{ FAMILY,	"",		NONE,		t_show_rib},
173 	{ PREFIX,	"",		NONE,		t_show_prefix},
174 	{ ENDTOKEN,	"",		NONE,		NULL}
175 };
176 
177 
178 static const struct token t_show_mrt[] = {
179 	{ NOTOKEN,	"",		NONE,		NULL},
180 	{ ASTYPE,	"as",		AS_ALL,		t_show_mrt_as},
181 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_mrt_as},
182 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_mrt_as},
183 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_mrt_as},
184 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_mrt},
185 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_mrt},
186 	{ KEYWORD,	"neighbor",	NONE,		t_show_mrt_neigh},
187 	{ KEYWORD,	"file",		NONE,		t_show_mrt_file},
188 	{ FAMILY,	"",		NONE,		t_show_mrt},
189 	{ PREFIX,	"",		NONE,		t_show_prefix},
190 	{ ENDTOKEN,	"",		NONE,		NULL}
191 };
192 
193 static const struct token t_show_mrt_file[] = {
194 	{ FILENAME,	"",		NONE,		t_show_mrt},
195 	{ ENDTOKEN,	"",		NONE,	NULL}
196 };
197 
198 static const struct token t_show_rib_neigh[] = {
199 	{ PEERADDRESS,	"",		NONE,	t_show_rib},
200 	{ PEERDESC,	"",		NONE,	t_show_rib},
201 	{ ENDTOKEN,	"",		NONE,	NULL}
202 };
203 
204 static const struct token t_show_mrt_neigh[] = {
205 	{ PEERADDRESS,	"",		NONE,	t_show_mrt},
206 	{ ENDTOKEN,	"",		NONE,	NULL}
207 };
208 
209 static const struct token t_show_rib_rib[] = {
210 	{ RIBNAME,	"",		NONE,	t_show_rib},
211 	{ ENDTOKEN,	"",		NONE,	NULL}
212 };
213 
214 static const struct token t_show_neighbor[] = {
215 	{ NOTOKEN,	"",		NONE,	NULL},
216 	{ PEERADDRESS,	"",		NONE,	t_show_neighbor_modifiers},
217 	{ PEERDESC,	"",		NONE,	t_show_neighbor_modifiers},
218 	{ ENDTOKEN,	"",		NONE,	NULL}
219 };
220 
221 static const struct token t_show_neighbor_modifiers[] = {
222 	{ NOTOKEN,	"",		NONE,			NULL},
223 	{ KEYWORD,	"timers",	SHOW_NEIGHBOR_TIMERS,	NULL},
224 	{ KEYWORD,	"messages",	SHOW_NEIGHBOR,		NULL},
225 	{ KEYWORD,	"terse",	SHOW_NEIGHBOR_TERSE,	NULL},
226 	{ ENDTOKEN,	"",		NONE,			NULL}
227 };
228 
229 static const struct token t_fib[] = {
230 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
231 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
232 	{ KEYWORD,	"table",	NONE,		t_fib_table},
233 	{ ENDTOKEN,	"",		NONE,		NULL}
234 };
235 
236 static const struct token t_neighbor[] = {
237 	{ PEERADDRESS,	"",		NONE,		t_neighbor_modifiers},
238 	{ PEERDESC,	"",		NONE,		t_neighbor_modifiers},
239 	{ ENDTOKEN,	"",		NONE,		NULL}
240 };
241 
242 static const struct token t_neighbor_modifiers[] = {
243 	{ KEYWORD,	"up",		NEIGHBOR_UP,		NULL},
244 	{ KEYWORD,	"down",		NEIGHBOR_DOWN,		NULL},
245 	{ KEYWORD,	"clear",	NEIGHBOR_CLEAR,		NULL},
246 	{ KEYWORD,	"refresh",	NEIGHBOR_RREFRESH,	NULL},
247 	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
248 	{ ENDTOKEN,	"",		NONE,			NULL}
249 };
250 
251 static const struct token t_show_rib_as[] = {
252 	{ ASNUM,	"",		NONE,		t_show_rib},
253 	{ ENDTOKEN,	"",		NONE,		NULL}
254 };
255 
256 static const struct token t_show_mrt_as[] = {
257 	{ ASNUM,	"",		NONE,		t_show_mrt},
258 	{ ENDTOKEN,	"",		NONE,		NULL}
259 };
260 
261 static const struct token t_show_prefix[] = {
262 	{ NOTOKEN,	"",		NONE,		NULL},
263 	{ FLAG,		"all",		F_LONGER,	NULL},
264 	{ FLAG,		"longer-prefixes", F_LONGER,	NULL},
265 	{ ENDTOKEN,	"",		NONE,		NULL}
266 };
267 
268 static const struct token t_show_ip[] = {
269 	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
270 	{ ENDTOKEN,	"",		NONE,		NULL}
271 };
272 
273 static const struct token t_show_community[] = {
274 	{ COMMUNITY,	"",		NONE,		t_show_rib},
275 	{ ENDTOKEN,	"",		NONE,		NULL}
276 };
277 
278 static const struct token t_network[] = {
279 	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
280 	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
281 	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
282 	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
283 	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
284 	{ KEYWORD,	"bulk",		NETWORK_BULK_ADD,	t_set},
285 	{ ENDTOKEN,	"",		NONE,		NULL}
286 };
287 
288 static const struct token t_prefix[] = {
289 	{ PREFIX,	"",		NONE,		t_set},
290 	{ ENDTOKEN,	"",		NONE,		NULL}
291 };
292 
293 static const struct token t_network_show[] = {
294 	{ NOTOKEN,	"",		NONE,			NULL},
295 	{ FAMILY,	"",		NONE,			NULL},
296 	{ ENDTOKEN,	"",		NONE,			NULL}
297 };
298 
299 static const struct token t_set[] = {
300 	{ NOTOKEN,	"",			NONE,	NULL},
301 	{ KEYWORD,	"community",		NONE,	t_community},
302 	{ KEYWORD,	"localpref",		NONE,	t_localpref},
303 	{ KEYWORD,	"med",			NONE,	t_med},
304 	{ KEYWORD,	"metric",		NONE,	t_med},
305 	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
306 	{ KEYWORD,	"pftable",		NONE,	t_pftable},
307 	{ KEYWORD,	"prepend-neighbor",	NONE,	t_prepnbr},
308 	{ KEYWORD,	"prepend-self",		NONE,	t_prepself},
309 	{ KEYWORD,	"weight",		NONE,	t_weight},
310 	{ KEYWORD,	"add",			NETWORK_BULK_ADD,	NULL},
311 	{ KEYWORD,	"delete",		NETWORK_BULK_REMOVE,	NULL},
312 	{ ENDTOKEN,	"",			NONE,	NULL}
313 };
314 
315 static const struct token t_community[] = {
316 	{ COMMUNITY,	"",			NONE,	t_set},
317 	{ ENDTOKEN,	"",			NONE,	NULL}
318 };
319 
320 static const struct token t_localpref[] = {
321 	{ LOCALPREF,	"",			NONE,	t_set},
322 	{ ENDTOKEN,	"",			NONE,	NULL}
323 };
324 
325 static const struct token t_med[] = {
326 	{ MED,		"",			NONE,	t_set},
327 	{ ENDTOKEN,	"",			NONE,	NULL}
328 };
329 
330 static const struct token t_nexthop[] = {
331 	{ NEXTHOP,	"",			NONE,	t_set},
332 	{ ENDTOKEN,	"",			NONE,	NULL}
333 };
334 
335 static const struct token t_pftable[] = {
336 	{ PFTABLE,	"",			NONE,	t_set},
337 	{ ENDTOKEN,	"",			NONE,	NULL}
338 };
339 
340 static const struct token t_prepnbr[] = {
341 	{ PREPNBR,	"",			NONE,	t_set},
342 	{ ENDTOKEN,	"",			NONE,	NULL}
343 };
344 
345 static const struct token t_prepself[] = {
346 	{ PREPSELF,	"",			NONE,	t_set},
347 	{ ENDTOKEN,	"",			NONE,	NULL}
348 };
349 
350 static const struct token t_weight[] = {
351 	{ WEIGHT,	"",			NONE,	t_set},
352 	{ ENDTOKEN,	"",			NONE,	NULL}
353 };
354 
355 static const struct token t_irrfilter[] = {
356 	{ GETOPT,	"",	GETOPT_IRRFILTER,	t_irrfilter},
357 	{ ASNUM,	"",	NONE,			t_irrfilter_opts},
358 	{ ENDTOKEN,	"",	NONE,			NULL}
359 };
360 
361 static const struct token t_irrfilter_opts[] = {
362 	{ NOTOKEN,	"",		NONE,			NULL},
363 	{ FLAG,		"importonly",	F_IMPORTONLY,		t_irrfilter_opts},
364 	{ ENDTOKEN,	"",		NONE,			NULL}
365 };
366 
367 static const struct token t_log[] = {
368 	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
369 	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
370 	{ ENDTOKEN,	"",		NONE,		NULL}
371 };
372 
373 static const struct token t_fib_table[] = {
374 	{ RTABLE,	"",			NONE,	t_fib},
375 	{ ENDTOKEN,	"",			NONE,	NULL}
376 };
377 
378 static const struct token t_show_fib_table[] = {
379 	{ RTABLE,	"",			NONE,	t_show_fib},
380 	{ ENDTOKEN,	"",			NONE,	NULL}
381 };
382 
383 static struct parse_result	res;
384 
385 const struct token	*match_token(int *argc, char **argv[],
386 			    const struct token []);
387 void			 show_valid_args(const struct token []);
388 int			 parse_addr(const char *, struct bgpd_addr *);
389 int			 parse_prefix(const char *, size_t, struct bgpd_addr *,
390 			     u_int8_t *);
391 int			 parse_asnum(const char *, size_t, u_int32_t *);
392 int			 parse_number(const char *, struct parse_result *,
393 			     enum token_type);
394 int			 getcommunity(const char *);
395 int			 parse_community(const char *, struct parse_result *);
396 int			 parse_nexthop(const char *, struct parse_result *);
397 int			 bgpctl_getopt(int *, char **[], int);
398 
399 struct parse_result *
400 parse(int argc, char *argv[])
401 {
402 	const struct token	*table = t_main;
403 	const struct token	*match;
404 
405 	bzero(&res, sizeof(res));
406 	res.community.as = COMMUNITY_UNSET;
407 	res.community.type = COMMUNITY_UNSET;
408 	TAILQ_INIT(&res.set);
409 	if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
410 		fprintf(stderr, "getcwd failed: %s\n", strerror(errno));
411 		return (NULL);
412 	}
413 
414 	while (argc >= 0) {
415 		if ((match = match_token(&argc, &argv, table)) == NULL) {
416 			fprintf(stderr, "valid commands/args:\n");
417 			show_valid_args(table);
418 			return (NULL);
419 		}
420 
421 		argc--;
422 		argv++;
423 
424 		if (match->type == NOTOKEN || match->next == NULL)
425 			break;
426 
427 		table = match->next;
428 	}
429 
430 	if (argc > 0) {
431 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
432 		return (NULL);
433 	}
434 
435 	return (&res);
436 }
437 
438 const struct token *
439 match_token(int *argc, char **argv[], const struct token table[])
440 {
441 	u_int			 i, match;
442 	const struct token	*t = NULL;
443 	struct filter_set	*fs;
444 	const char		*word = *argv[0];
445 	size_t			wordlen = 0;
446 
447 	match = 0;
448 	if (word != NULL)
449 		wordlen = strlen(word);
450 	for (i = 0; table[i].type != ENDTOKEN; i++) {
451 		switch (table[i].type) {
452 		case NOTOKEN:
453 			if (word == NULL || wordlen == 0) {
454 				match++;
455 				t = &table[i];
456 			}
457 			break;
458 		case KEYWORD:
459 			if (word != NULL && strncmp(word, table[i].keyword,
460 			    wordlen) == 0) {
461 				match++;
462 				t = &table[i];
463 				if (t->value)
464 					res.action = t->value;
465 			}
466 			break;
467 		case FLAG:
468 			if (word != NULL && strncmp(word, table[i].keyword,
469 			    wordlen) == 0) {
470 				match++;
471 				t = &table[i];
472 				res.flags |= t->value;
473 			}
474 			break;
475 		case FAMILY:
476 			if (word == NULL)
477 				break;
478 			if (!strcmp(word, "inet") ||
479 			    !strcasecmp(word, "IPv4")) {
480 				match++;
481 				t = &table[i];
482 				res.aid = AID_INET;
483 			}
484 			if (!strcmp(word, "inet6") ||
485 			    !strcasecmp(word, "IPv6")) {
486 				match++;
487 				t = &table[i];
488 				res.aid = AID_INET6;
489 			}
490 			if (!strcasecmp(word, "VPNv4")) {
491 				match++;
492 				t = &table[i];
493 				res.aid = AID_VPN_IPv4;
494 			}
495 			break;
496 		case ADDRESS:
497 			if (parse_addr(word, &res.addr)) {
498 				match++;
499 				t = &table[i];
500 				if (t->value)
501 					res.action = t->value;
502 			}
503 			break;
504 		case PEERADDRESS:
505 			if (parse_addr(word, &res.peeraddr)) {
506 				match++;
507 				t = &table[i];
508 				if (t->value)
509 					res.action = t->value;
510 			}
511 			break;
512 		case PREFIX:
513 			if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) {
514 				match++;
515 				t = &table[i];
516 				if (t->value)
517 					res.action = t->value;
518 			}
519 			break;
520 		case ASTYPE:
521 			if (word != NULL && strncmp(word, table[i].keyword,
522 			    wordlen) == 0) {
523 				match++;
524 				t = &table[i];
525 				res.as.type = t->value;
526 			}
527 			break;
528 		case ASNUM:
529 			if (parse_asnum(word, wordlen, &res.as.as)) {
530 				match++;
531 				t = &table[i];
532 			}
533 			break;
534 		case PEERDESC:
535 			if (!match && word != NULL && wordlen > 0) {
536 				if (strlcpy(res.peerdesc, word,
537 				    sizeof(res.peerdesc)) >=
538 				    sizeof(res.peerdesc))
539 					errx(1, "neighbor description too "
540 					    "long");
541 				match++;
542 				t = &table[i];
543 			}
544 			break;
545 		case RIBNAME:
546 			if (!match && word != NULL && wordlen > 0) {
547 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
548 				    sizeof(res.rib))
549 					errx(1, "rib name too long");
550 				match++;
551 				t = &table[i];
552 			}
553 			break;
554 		case COMMUNITY:
555 			if (word != NULL && wordlen > 0 &&
556 			    parse_community(word, &res)) {
557 				match++;
558 				t = &table[i];
559 			}
560 			break;
561 		case LOCALPREF:
562 		case MED:
563 		case PREPNBR:
564 		case PREPSELF:
565 		case WEIGHT:
566 		case RTABLE:
567 			if (word != NULL && wordlen > 0 &&
568 			    parse_number(word, &res, table[i].type)) {
569 				match++;
570 				t = &table[i];
571 			}
572 			break;
573 		case NEXTHOP:
574 			if (word != NULL && wordlen > 0 &&
575 			    parse_nexthop(word, &res)) {
576 				match++;
577 				t = &table[i];
578 			}
579 			break;
580 		case PFTABLE:
581 			if (word != NULL && wordlen > 0) {
582 				if ((fs = calloc(1,
583 				    sizeof(struct filter_set))) == NULL)
584 					err(1, NULL);
585 				if (strlcpy(fs->action.pftable, word,
586 				    sizeof(fs->action.pftable)) >=
587 				    sizeof(fs->action.pftable))
588 					errx(1, "pftable name too long");
589 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
590 				match++;
591 				t = &table[i];
592 			}
593 			break;
594 		case GETOPT:
595 			if (bgpctl_getopt(argc, argv, table[i].value)) {
596 				match++;
597 				t = &table[i];
598 			}
599 			break;
600 		case FILENAME:
601 			if (word != NULL && wordlen > 0) {
602 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
603 					/*
604 					 * ignore error if path has no / and
605 					 * does not exist. In hope to print
606 					 * usage.
607 					 */
608 					if (errno == ENOENT &&
609 					    !strchr(word, '/'))
610 						break;
611 					err(1, "mrt open(%s)", word);
612 				}
613 				match++;
614 				t = &table[i];
615 			}
616 			break;
617 		case BULK:
618 			match++;
619 			t = &table[i];
620 			break;
621 		case ENDTOKEN:
622 			break;
623 		}
624 	}
625 
626 	if (match != 1) {
627 		if (word == NULL)
628 			fprintf(stderr, "missing argument:\n");
629 		else if (match > 1)
630 			fprintf(stderr, "ambiguous argument: %s\n", word);
631 		else if (match < 1)
632 			fprintf(stderr, "unknown argument: %s\n", word);
633 		return (NULL);
634 	}
635 
636 	return (t);
637 }
638 
639 void
640 show_valid_args(const struct token table[])
641 {
642 	int	i;
643 
644 	for (i = 0; table[i].type != ENDTOKEN; i++) {
645 		switch (table[i].type) {
646 		case NOTOKEN:
647 			fprintf(stderr, "  <cr>\n");
648 			break;
649 		case KEYWORD:
650 		case FLAG:
651 		case ASTYPE:
652 			fprintf(stderr, "  %s\n", table[i].keyword);
653 			break;
654 		case ADDRESS:
655 		case PEERADDRESS:
656 			fprintf(stderr, "  <address>\n");
657 			break;
658 		case PREFIX:
659 			fprintf(stderr, "  <address>[/<len>]\n");
660 			break;
661 		case ASNUM:
662 			fprintf(stderr, "  <asnum>\n");
663 			break;
664 		case PEERDESC:
665 			fprintf(stderr, "  <neighbor description>\n");
666 			break;
667 		case RIBNAME:
668 			fprintf(stderr, "  <rib name>\n");
669 			break;
670 		case COMMUNITY:
671 			fprintf(stderr, "  <community>\n");
672 			break;
673 		case LOCALPREF:
674 		case MED:
675 		case PREPNBR:
676 		case PREPSELF:
677 		case WEIGHT:
678 			fprintf(stderr, "  <number>\n");
679 			break;
680 		case RTABLE:
681 			fprintf(stderr, "  <rtableid>\n");
682 			break;
683 		case NEXTHOP:
684 			fprintf(stderr, "  <address>\n");
685 			break;
686 		case PFTABLE:
687 			fprintf(stderr, "  <pftable>\n");
688 			break;
689 		case FAMILY:
690 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
691 			break;
692 		case GETOPT:
693 			fprintf(stderr, "  <options>\n");
694 			break;
695 		case FILENAME:
696 			fprintf(stderr, "  <filename>\n");
697 			break;
698 		case BULK:
699 		case ENDTOKEN:
700 			break;
701 		}
702 	}
703 }
704 
705 int
706 parse_addr(const char *word, struct bgpd_addr *addr)
707 {
708 	struct in_addr	ina;
709 	struct addrinfo	hints, *r;
710 
711 	if (word == NULL)
712 		return (0);
713 
714 	bzero(addr, sizeof(struct bgpd_addr));
715 	bzero(&ina, sizeof(ina));
716 
717 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
718 		addr->aid = AID_INET;
719 		addr->v4 = ina;
720 		return (1);
721 	}
722 
723 	bzero(&hints, sizeof(hints));
724 	hints.ai_family = AF_INET6;
725 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
726 	hints.ai_flags = AI_NUMERICHOST;
727 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
728 		sa2addr(r->ai_addr, addr);
729 		freeaddrinfo(r);
730 		return (1);
731 	}
732 
733 	return (0);
734 }
735 
736 int
737 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen)
738 {
739 	char		*p, *ps;
740 	const char	*errstr;
741 	int		 mask = -1;
742 
743 	if (word == NULL)
744 		return (0);
745 
746 	bzero(addr, sizeof(struct bgpd_addr));
747 
748 	if ((p = strrchr(word, '/')) != NULL) {
749 		size_t plen = strlen(p);
750 		mask = strtonum(p + 1, 0, 128, &errstr);
751 		if (errstr)
752 			errx(1, "netmask %s", errstr);
753 
754 		if ((ps = malloc(wordlen - plen + 1)) == NULL)
755 			err(1, "parse_prefix: malloc");
756 		strlcpy(ps, word, wordlen - plen + 1);
757 
758 		if (parse_addr(ps, addr) == 0) {
759 			free(ps);
760 			return (0);
761 		}
762 
763 		free(ps);
764 	} else
765 		if (parse_addr(word, addr) == 0)
766 			return (0);
767 
768 	switch (addr->aid) {
769 	case AID_INET:
770 		if (mask == -1)
771 			mask = 32;
772 		if (mask > 32)
773 			errx(1, "invalid netmask: too large");
774 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
775 		break;
776 	case AID_INET6:
777 		if (mask == -1)
778 			mask = 128;
779 		inet6applymask(&addr->v6, &addr->v6, mask);
780 		break;
781 	default:
782 		return (0);
783 	}
784 
785 	*prefixlen = mask;
786 	return (1);
787 }
788 
789 int
790 parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum)
791 {
792 	const char	*errstr;
793 	char		*dot;
794 	u_int32_t	 uval, uvalh = 0;
795 
796 	if (word == NULL)
797 		return (0);
798 
799 	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
800 		return (0);
801 
802 	if ((dot = strchr(word,'.')) != NULL) {
803 		*dot++ = '\0';
804 		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
805 		if (errstr)
806 			errx(1, "AS number is %s: %s", errstr, word);
807 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
808 		if (errstr)
809 			errx(1, "AS number is %s: %s", errstr, word);
810 	} else {
811 		uval = strtonum(word, 0, UINT_MAX, &errstr);
812 		if (errstr)
813 			errx(1, "AS number is %s: %s", errstr, word);
814 	}
815 
816 	*asnum = uval | (uvalh << 16);
817 	return (1);
818 }
819 
820 int
821 parse_number(const char *word, struct parse_result *r, enum token_type type)
822 {
823 	struct filter_set	*fs;
824 	const char		*errstr;
825 	u_int			 uval;
826 
827 	if (word == NULL)
828 		return (0);
829 
830 	uval = strtonum(word, 0, UINT_MAX, &errstr);
831 	if (errstr)
832 		errx(1, "number is %s: %s", errstr, word);
833 
834 	/* number was parseable */
835 	if (type == RTABLE) {
836 		r->rtableid = uval;
837 		return (1);
838 	}
839 
840 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
841 		err(1, NULL);
842 	switch (type) {
843 	case LOCALPREF:
844 		fs->type = ACTION_SET_LOCALPREF;
845 		fs->action.metric = uval;
846 		break;
847 	case MED:
848 		fs->type = ACTION_SET_MED;
849 		fs->action.metric = uval;
850 		break;
851 	case PREPNBR:
852 		if (uval > 128) {
853 			free(fs);
854 			return (0);
855 		}
856 		fs->type = ACTION_SET_PREPEND_PEER;
857 		fs->action.prepend = uval;
858 		break;
859 	case PREPSELF:
860 		if (uval > 128) {
861 			free(fs);
862 			return (0);
863 		}
864 		fs->type = ACTION_SET_PREPEND_SELF;
865 		fs->action.prepend = uval;
866 		break;
867 	case WEIGHT:
868 		fs->type = ACTION_SET_WEIGHT;
869 		fs->action.metric = uval;
870 		break;
871 	default:
872 		errx(1, "king bula sez bad things happen");
873 	}
874 
875 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
876 	return (1);
877 }
878 
879 int
880 getcommunity(const char *s)
881 {
882 	const char	*errstr;
883 	u_int16_t	 uval;
884 
885 	if (strcmp(s, "*") == 0)
886 		return (COMMUNITY_ANY);
887 
888 	uval = strtonum(s, 0, USHRT_MAX, &errstr);
889 	if (errstr)
890 		errx(1, "Community is %s: %s", errstr, s);
891 
892 	return (uval);
893 }
894 
895 int
896 parse_community(const char *word, struct parse_result *r)
897 {
898 	struct filter_set	*fs;
899 	char			*p;
900 	int			 as, type;
901 
902 	/* Well-known communities */
903 	if (strcasecmp(word, "NO_EXPORT") == 0) {
904 		as = COMMUNITY_WELLKNOWN;
905 		type = COMMUNITY_NO_EXPORT;
906 		goto done;
907 	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
908 		as = COMMUNITY_WELLKNOWN;
909 		type = COMMUNITY_NO_ADVERTISE;
910 		goto done;
911 	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
912 		as = COMMUNITY_WELLKNOWN;
913 		type = COMMUNITY_NO_EXPSUBCONFED;
914 		goto done;
915 	} else if (strcasecmp(word, "NO_PEER") == 0) {
916 		as = COMMUNITY_WELLKNOWN;
917 		type = COMMUNITY_NO_PEER;
918 		goto done;
919 	}
920 
921 	if ((p = strchr(word, ':')) == NULL) {
922 		fprintf(stderr, "Bad community syntax\n");
923 		return (0);
924 	}
925 	*p++ = 0;
926 
927 	as = getcommunity(word);
928 	type = getcommunity(p);
929 
930 done:
931 	if (as == 0) {
932 		fprintf(stderr, "Invalid community\n");
933 		return (0);
934 	}
935 	if (as == COMMUNITY_WELLKNOWN)
936 		switch (type) {
937 		case COMMUNITY_NO_EXPORT:
938 		case COMMUNITY_NO_ADVERTISE:
939 		case COMMUNITY_NO_EXPSUBCONFED:
940 			/* valid */
941 			break;
942 		default:
943 			/* unknown */
944 			fprintf(stderr, "Unknown well-known community\n");
945 			return (0);
946 		}
947 
948 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
949 		err(1, NULL);
950 	fs->type = ACTION_SET_COMMUNITY;
951 	fs->action.community.as = as;
952 	fs->action.community.type = type;
953 
954 	r->community.as = as;
955 	r->community.type = type;
956 
957 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
958 	return (1);
959 }
960 
961 int
962 parse_nexthop(const char *word, struct parse_result *r)
963 {
964 	struct filter_set	*fs;
965 
966 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
967 		err(1, NULL);
968 
969 	if (strcmp(word, "blackhole") == 0)
970 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
971 	else if (strcmp(word, "reject") == 0)
972 		fs->type = ACTION_SET_NEXTHOP_REJECT;
973 	else if (strcmp(word, "no-modify") == 0)
974 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
975 	else if (parse_addr(word, &fs->action.nexthop)) {
976 		fs->type = ACTION_SET_NEXTHOP;
977 	} else {
978 		free(fs);
979 		return (0);
980 	}
981 
982 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
983 	return (1);
984 }
985 
986 int
987 bgpctl_getopt(int *argc, char **argv[], int type)
988 {
989 	int	  ch;
990 
991 	optind = optreset = 1;
992 	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
993 		switch (ch) {
994 		case '4':
995 			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
996 			break;
997 		case '6':
998 			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
999 			break;
1000 		case 'o':
1001 			res.irr_outdir = optarg;
1002 			break;
1003 		default:
1004 			usage();
1005 			/* NOTREACHED */
1006 		}
1007 	}
1008 
1009 	if (optind > 1) {
1010 		(*argc) -= (optind - 1);
1011 		(*argv) += (optind - 1);
1012 
1013 		/* need to move one backwards as calling code moves forward */
1014 		(*argc)++;
1015 		(*argv)--;
1016 		return (1);
1017 	} else
1018 		return (0);
1019 }
1020