xref: /openbsd/usr.sbin/bgpctl/parser.c (revision 5af055cd)
1 /*	$OpenBSD: parser.c,v 1.73 2015/10/11 19:53:57 sthen 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 
21 #include <err.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <netdb.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "parser.h"
32 #include "irrfilter.h"
33 
34 enum token_type {
35 	NOTOKEN,
36 	ENDTOKEN,
37 	KEYWORD,
38 	ADDRESS,
39 	PEERADDRESS,
40 	FLAG,
41 	ASNUM,
42 	ASTYPE,
43 	PREFIX,
44 	PEERDESC,
45 	RIBNAME,
46 	COMMUNITY,
47 	LOCALPREF,
48 	MED,
49 	NEXTHOP,
50 	PFTABLE,
51 	PREPNBR,
52 	PREPSELF,
53 	WEIGHT,
54 	FAMILY,
55 	GETOPT,
56 	RTABLE,
57 	FILENAME,
58 	BULK
59 };
60 
61 enum getopts {
62 	GETOPT_NONE,
63 	GETOPT_IRRFILTER
64 };
65 
66 struct token {
67 	enum token_type		 type;
68 	const char		*keyword;
69 	int			 value;
70 	const struct token	*next;
71 };
72 
73 static const struct token t_main[];
74 static const struct token t_show[];
75 static const struct token t_show_summary[];
76 static const struct token t_show_fib[];
77 static const struct token t_show_rib[];
78 static const struct token t_show_mrt[];
79 static const struct token t_show_mrt_file[];
80 static const struct token t_show_rib_neigh[];
81 static const struct token t_show_mrt_neigh[];
82 static const struct token t_show_rib_rib[];
83 static const struct token t_show_neighbor[];
84 static const struct token t_show_neighbor_modifiers[];
85 static const struct token t_fib[];
86 static const struct token t_neighbor[];
87 static const struct token t_neighbor_modifiers[];
88 static const struct token t_show_rib_as[];
89 static const struct token t_show_mrt_as[];
90 static const struct token t_show_prefix[];
91 static const struct token t_show_ip[];
92 static const struct token t_show_community[];
93 static const struct token t_network[];
94 static const struct token t_network_show[];
95 static const struct token t_prefix[];
96 static const struct token t_set[];
97 static const struct token t_community[];
98 static const struct token t_localpref[];
99 static const struct token t_med[];
100 static const struct token t_nexthop[];
101 static const struct token t_pftable[];
102 static const struct token t_prepnbr[];
103 static const struct token t_prepself[];
104 static const struct token t_weight[];
105 static const struct token t_irrfilter[];
106 static const struct token t_irrfilter_opts[];
107 static const struct token t_log[];
108 static const struct token t_fib_table[];
109 static const struct token t_show_fib_table[];
110 
111 static const struct token t_main[] = {
112 	{ KEYWORD,	"reload",	RELOAD,		NULL},
113 	{ KEYWORD,	"show",		SHOW,		t_show},
114 	{ KEYWORD,	"fib",		FIB,		t_fib},
115 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
116 	{ KEYWORD,	"network",	NONE,		t_network},
117 	{ KEYWORD,	"irrfilter",	IRRFILTER,	t_irrfilter},
118 	{ KEYWORD,	"log",		NONE,		t_log},
119 	{ ENDTOKEN,	"",		NONE,		NULL}
120 };
121 
122 static const struct token t_show[] = {
123 	{ NOTOKEN,	"",		NONE,		NULL},
124 	{ KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
125 	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
126 	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
127 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
128 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
129 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
130 	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
131 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
132 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
133 	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
134 	{ ENDTOKEN,	"",		NONE,		NULL}
135 };
136 
137 static const struct token t_show_summary[] = {
138 	{ NOTOKEN,	"",		NONE,			NULL},
139 	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
140 	{ ENDTOKEN,	"",		NONE,			NULL}
141 };
142 
143 static const struct token t_show_fib[] = {
144 	{ NOTOKEN,	"",		NONE,		 NULL},
145 	{ FLAG,		"connected",	F_CONNECTED,	 t_show_fib},
146 	{ FLAG,		"static",	F_STATIC,	 t_show_fib},
147 	{ FLAG,		"bgp",		F_BGPD_INSERTED, t_show_fib},
148 	{ FLAG,		"nexthop",	F_NEXTHOP,	 t_show_fib},
149 	{ KEYWORD,	"table",	NONE,		 t_show_fib_table},
150 	{ FAMILY,	"",		NONE,		 t_show_fib},
151 	{ ADDRESS,	"",		NONE,		 NULL},
152 	{ ENDTOKEN,	"",		NONE,		 NULL}
153 };
154 
155 static const struct token t_show_rib[] = {
156 	{ NOTOKEN,	"",		NONE,		NULL},
157 	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
158 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
159 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
160 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
161 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
162 	{ KEYWORD,	"community",	NONE,		t_show_community},
163 	{ FLAG,		"best",		F_CTL_ACTIVE,	t_show_rib},
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_asnum(const char *, size_t, u_int32_t *);
390 int			 parse_number(const char *, struct parse_result *,
391 			     enum token_type);
392 int			 getcommunity(const char *);
393 int			 parse_community(const char *, struct parse_result *);
394 int			 parse_nexthop(const char *, struct parse_result *);
395 int			 bgpctl_getopt(int *, char **[], int);
396 
397 struct parse_result *
398 parse(int argc, char *argv[])
399 {
400 	const struct token	*table = t_main;
401 	const struct token	*match;
402 
403 	bzero(&res, sizeof(res));
404 	res.community.as = COMMUNITY_UNSET;
405 	res.community.type = COMMUNITY_UNSET;
406 	TAILQ_INIT(&res.set);
407 	if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
408 		fprintf(stderr, "getcwd failed: %s\n", strerror(errno));
409 		return (NULL);
410 	}
411 
412 	while (argc >= 0) {
413 		if ((match = match_token(&argc, &argv, table)) == NULL) {
414 			fprintf(stderr, "valid commands/args:\n");
415 			show_valid_args(table);
416 			return (NULL);
417 		}
418 
419 		argc--;
420 		argv++;
421 
422 		if (match->type == NOTOKEN || match->next == NULL)
423 			break;
424 
425 		table = match->next;
426 	}
427 
428 	if (argc > 0) {
429 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
430 		return (NULL);
431 	}
432 
433 	return (&res);
434 }
435 
436 const struct token *
437 match_token(int *argc, char **argv[], const struct token table[])
438 {
439 	u_int			 i, match;
440 	const struct token	*t = NULL;
441 	struct filter_set	*fs;
442 	const char		*word = *argv[0];
443 	size_t			wordlen = 0;
444 
445 	match = 0;
446 	if (word != NULL)
447 		wordlen = strlen(word);
448 	for (i = 0; table[i].type != ENDTOKEN; i++) {
449 		switch (table[i].type) {
450 		case NOTOKEN:
451 			if (word == NULL || wordlen == 0) {
452 				match++;
453 				t = &table[i];
454 			}
455 			break;
456 		case KEYWORD:
457 			if (word != NULL && strncmp(word, table[i].keyword,
458 			    wordlen) == 0) {
459 				match++;
460 				t = &table[i];
461 				if (t->value)
462 					res.action = t->value;
463 			}
464 			break;
465 		case FLAG:
466 			if (word != NULL && strncmp(word, table[i].keyword,
467 			    wordlen) == 0) {
468 				match++;
469 				t = &table[i];
470 				res.flags |= t->value;
471 			}
472 			break;
473 		case FAMILY:
474 			if (word == NULL)
475 				break;
476 			if (!strcmp(word, "inet") ||
477 			    !strcasecmp(word, "IPv4")) {
478 				match++;
479 				t = &table[i];
480 				res.aid = AID_INET;
481 			}
482 			if (!strcmp(word, "inet6") ||
483 			    !strcasecmp(word, "IPv6")) {
484 				match++;
485 				t = &table[i];
486 				res.aid = AID_INET6;
487 			}
488 			if (!strcasecmp(word, "VPNv4")) {
489 				match++;
490 				t = &table[i];
491 				res.aid = AID_VPN_IPv4;
492 			}
493 			break;
494 		case ADDRESS:
495 			if (parse_addr(word, &res.addr)) {
496 				match++;
497 				t = &table[i];
498 				if (t->value)
499 					res.action = t->value;
500 			}
501 			break;
502 		case PEERADDRESS:
503 			if (parse_addr(word, &res.peeraddr)) {
504 				match++;
505 				t = &table[i];
506 				if (t->value)
507 					res.action = t->value;
508 			}
509 			break;
510 		case PREFIX:
511 			if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) {
512 				match++;
513 				t = &table[i];
514 				if (t->value)
515 					res.action = t->value;
516 			}
517 			break;
518 		case ASTYPE:
519 			if (word != NULL && strncmp(word, table[i].keyword,
520 			    wordlen) == 0) {
521 				match++;
522 				t = &table[i];
523 				res.as.type = t->value;
524 			}
525 			break;
526 		case ASNUM:
527 			if (parse_asnum(word, wordlen, &res.as.as)) {
528 				match++;
529 				t = &table[i];
530 			}
531 			break;
532 		case PEERDESC:
533 			if (!match && word != NULL && wordlen > 0) {
534 				if (strlcpy(res.peerdesc, word,
535 				    sizeof(res.peerdesc)) >=
536 				    sizeof(res.peerdesc))
537 					errx(1, "neighbor description too "
538 					    "long");
539 				match++;
540 				t = &table[i];
541 			}
542 			break;
543 		case RIBNAME:
544 			if (!match && word != NULL && wordlen > 0) {
545 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
546 				    sizeof(res.rib))
547 					errx(1, "rib name too long");
548 				match++;
549 				t = &table[i];
550 			}
551 			break;
552 		case COMMUNITY:
553 			if (word != NULL && wordlen > 0 &&
554 			    parse_community(word, &res)) {
555 				match++;
556 				t = &table[i];
557 			}
558 			break;
559 		case LOCALPREF:
560 		case MED:
561 		case PREPNBR:
562 		case PREPSELF:
563 		case WEIGHT:
564 		case RTABLE:
565 			if (word != NULL && wordlen > 0 &&
566 			    parse_number(word, &res, table[i].type)) {
567 				match++;
568 				t = &table[i];
569 			}
570 			break;
571 		case NEXTHOP:
572 			if (word != NULL && wordlen > 0 &&
573 			    parse_nexthop(word, &res)) {
574 				match++;
575 				t = &table[i];
576 			}
577 			break;
578 		case PFTABLE:
579 			if (word != NULL && wordlen > 0) {
580 				if ((fs = calloc(1,
581 				    sizeof(struct filter_set))) == NULL)
582 					err(1, NULL);
583 				if (strlcpy(fs->action.pftable, word,
584 				    sizeof(fs->action.pftable)) >=
585 				    sizeof(fs->action.pftable))
586 					errx(1, "pftable name too long");
587 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
588 				match++;
589 				t = &table[i];
590 			}
591 			break;
592 		case GETOPT:
593 			if (bgpctl_getopt(argc, argv, table[i].value)) {
594 				match++;
595 				t = &table[i];
596 			}
597 			break;
598 		case FILENAME:
599 			if (word != NULL && wordlen > 0) {
600 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
601 					/*
602 					 * ignore error if path has no / and
603 					 * does not exist. In hope to print
604 					 * usage.
605 					 */
606 					if (errno == ENOENT &&
607 					    !strchr(word, '/'))
608 						break;
609 					err(1, "mrt open(%s)", word);
610 				}
611 				match++;
612 				t = &table[i];
613 			}
614 			break;
615 		case BULK:
616 			match++;
617 			t = &table[i];
618 			break;
619 		case ENDTOKEN:
620 			break;
621 		}
622 	}
623 
624 	if (match != 1) {
625 		if (word == NULL)
626 			fprintf(stderr, "missing argument:\n");
627 		else if (match > 1)
628 			fprintf(stderr, "ambiguous argument: %s\n", word);
629 		else if (match < 1)
630 			fprintf(stderr, "unknown argument: %s\n", word);
631 		return (NULL);
632 	}
633 
634 	return (t);
635 }
636 
637 void
638 show_valid_args(const struct token table[])
639 {
640 	int	i;
641 
642 	for (i = 0; table[i].type != ENDTOKEN; i++) {
643 		switch (table[i].type) {
644 		case NOTOKEN:
645 			fprintf(stderr, "  <cr>\n");
646 			break;
647 		case KEYWORD:
648 		case FLAG:
649 		case ASTYPE:
650 			fprintf(stderr, "  %s\n", table[i].keyword);
651 			break;
652 		case ADDRESS:
653 		case PEERADDRESS:
654 			fprintf(stderr, "  <address>\n");
655 			break;
656 		case PREFIX:
657 			fprintf(stderr, "  <address>[/<len>]\n");
658 			break;
659 		case ASNUM:
660 			fprintf(stderr, "  <asnum>\n");
661 			break;
662 		case PEERDESC:
663 			fprintf(stderr, "  <neighbor description>\n");
664 			break;
665 		case RIBNAME:
666 			fprintf(stderr, "  <rib name>\n");
667 			break;
668 		case COMMUNITY:
669 			fprintf(stderr, "  <community>\n");
670 			break;
671 		case LOCALPREF:
672 		case MED:
673 		case PREPNBR:
674 		case PREPSELF:
675 		case WEIGHT:
676 			fprintf(stderr, "  <number>\n");
677 			break;
678 		case RTABLE:
679 			fprintf(stderr, "  <rtableid>\n");
680 			break;
681 		case NEXTHOP:
682 			fprintf(stderr, "  <address>\n");
683 			break;
684 		case PFTABLE:
685 			fprintf(stderr, "  <pftable>\n");
686 			break;
687 		case FAMILY:
688 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
689 			break;
690 		case GETOPT:
691 			fprintf(stderr, "  <options>\n");
692 			break;
693 		case FILENAME:
694 			fprintf(stderr, "  <filename>\n");
695 			break;
696 		case BULK:
697 		case ENDTOKEN:
698 			break;
699 		}
700 	}
701 }
702 
703 int
704 parse_addr(const char *word, struct bgpd_addr *addr)
705 {
706 	struct in_addr	ina;
707 	struct addrinfo	hints, *r;
708 
709 	if (word == NULL)
710 		return (0);
711 
712 	bzero(addr, sizeof(struct bgpd_addr));
713 	bzero(&ina, sizeof(ina));
714 
715 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
716 		addr->aid = AID_INET;
717 		addr->v4 = ina;
718 		return (1);
719 	}
720 
721 	bzero(&hints, sizeof(hints));
722 	hints.ai_family = AF_INET6;
723 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
724 	hints.ai_flags = AI_NUMERICHOST;
725 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
726 		sa2addr(r->ai_addr, addr);
727 		freeaddrinfo(r);
728 		return (1);
729 	}
730 
731 	return (0);
732 }
733 
734 int
735 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen)
736 {
737 	char		*p, *ps;
738 	const char	*errstr;
739 	int		 mask = -1;
740 
741 	if (word == NULL)
742 		return (0);
743 
744 	bzero(addr, sizeof(struct bgpd_addr));
745 
746 	if ((p = strrchr(word, '/')) != NULL) {
747 		size_t plen = strlen(p);
748 		mask = strtonum(p + 1, 0, 128, &errstr);
749 		if (errstr)
750 			errx(1, "netmask %s", errstr);
751 
752 		if ((ps = malloc(wordlen - plen + 1)) == NULL)
753 			err(1, "parse_prefix: malloc");
754 		strlcpy(ps, word, wordlen - plen + 1);
755 
756 		if (parse_addr(ps, addr) == 0) {
757 			free(ps);
758 			return (0);
759 		}
760 
761 		free(ps);
762 	} else
763 		if (parse_addr(word, addr) == 0)
764 			return (0);
765 
766 	switch (addr->aid) {
767 	case AID_INET:
768 		if (mask == -1)
769 			mask = 32;
770 		if (mask > 32)
771 			errx(1, "invalid netmask: too large");
772 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
773 		break;
774 	case AID_INET6:
775 		if (mask == -1)
776 			mask = 128;
777 		inet6applymask(&addr->v6, &addr->v6, mask);
778 		break;
779 	default:
780 		return (0);
781 	}
782 
783 	*prefixlen = mask;
784 	return (1);
785 }
786 
787 int
788 parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum)
789 {
790 	const char	*errstr;
791 	char		*dot;
792 	u_int32_t	 uval, uvalh = 0;
793 
794 	if (word == NULL)
795 		return (0);
796 
797 	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
798 		return (0);
799 
800 	if ((dot = strchr(word,'.')) != NULL) {
801 		*dot++ = '\0';
802 		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
803 		if (errstr)
804 			errx(1, "AS number is %s: %s", errstr, word);
805 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
806 		if (errstr)
807 			errx(1, "AS number is %s: %s", errstr, word);
808 	} else {
809 		uval = strtonum(word, 0, UINT_MAX, &errstr);
810 		if (errstr)
811 			errx(1, "AS number is %s: %s", errstr, word);
812 	}
813 
814 	*asnum = uval | (uvalh << 16);
815 	return (1);
816 }
817 
818 int
819 parse_number(const char *word, struct parse_result *r, enum token_type type)
820 {
821 	struct filter_set	*fs;
822 	const char		*errstr;
823 	u_int			 uval;
824 
825 	if (word == NULL)
826 		return (0);
827 
828 	uval = strtonum(word, 0, UINT_MAX, &errstr);
829 	if (errstr)
830 		errx(1, "number is %s: %s", errstr, word);
831 
832 	/* number was parseable */
833 	if (type == RTABLE) {
834 		r->rtableid = uval;
835 		return (1);
836 	}
837 
838 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
839 		err(1, NULL);
840 	switch (type) {
841 	case LOCALPREF:
842 		fs->type = ACTION_SET_LOCALPREF;
843 		fs->action.metric = uval;
844 		break;
845 	case MED:
846 		fs->type = ACTION_SET_MED;
847 		fs->action.metric = uval;
848 		break;
849 	case PREPNBR:
850 		if (uval > 128) {
851 			free(fs);
852 			return (0);
853 		}
854 		fs->type = ACTION_SET_PREPEND_PEER;
855 		fs->action.prepend = uval;
856 		break;
857 	case PREPSELF:
858 		if (uval > 128) {
859 			free(fs);
860 			return (0);
861 		}
862 		fs->type = ACTION_SET_PREPEND_SELF;
863 		fs->action.prepend = uval;
864 		break;
865 	case WEIGHT:
866 		fs->type = ACTION_SET_WEIGHT;
867 		fs->action.metric = uval;
868 		break;
869 	default:
870 		errx(1, "king bula sez bad things happen");
871 	}
872 
873 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
874 	return (1);
875 }
876 
877 int
878 getcommunity(const char *s)
879 {
880 	const char	*errstr;
881 	u_int16_t	 uval;
882 
883 	if (strcmp(s, "*") == 0)
884 		return (COMMUNITY_ANY);
885 
886 	uval = strtonum(s, 0, USHRT_MAX, &errstr);
887 	if (errstr)
888 		errx(1, "Community is %s: %s", errstr, s);
889 
890 	return (uval);
891 }
892 
893 int
894 parse_community(const char *word, struct parse_result *r)
895 {
896 	struct filter_set	*fs;
897 	char			*p;
898 	int			 as, type;
899 
900 	/* Well-known communities */
901 	if (strcasecmp(word, "NO_EXPORT") == 0) {
902 		as = COMMUNITY_WELLKNOWN;
903 		type = COMMUNITY_NO_EXPORT;
904 		goto done;
905 	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
906 		as = COMMUNITY_WELLKNOWN;
907 		type = COMMUNITY_NO_ADVERTISE;
908 		goto done;
909 	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
910 		as = COMMUNITY_WELLKNOWN;
911 		type = COMMUNITY_NO_EXPSUBCONFED;
912 		goto done;
913 	} else if (strcasecmp(word, "NO_PEER") == 0) {
914 		as = COMMUNITY_WELLKNOWN;
915 		type = COMMUNITY_NO_PEER;
916 		goto done;
917 	} else if (strcasecmp(word, "BLACKHOLE") == 0) {
918 		as = COMMUNITY_WELLKNOWN;
919 		type = COMMUNITY_BLACKHOLE;
920 		goto done;
921 	}
922 
923 	if ((p = strchr(word, ':')) == NULL) {
924 		fprintf(stderr, "Bad community syntax\n");
925 		return (0);
926 	}
927 	*p++ = 0;
928 
929 	as = getcommunity(word);
930 	type = getcommunity(p);
931 
932 done:
933 	if (as == 0) {
934 		fprintf(stderr, "Invalid community\n");
935 		return (0);
936 	}
937 	if (as == COMMUNITY_WELLKNOWN)
938 		switch (type) {
939 		case COMMUNITY_NO_EXPORT:
940 		case COMMUNITY_NO_ADVERTISE:
941 		case COMMUNITY_NO_EXPSUBCONFED:
942 		case COMMUNITY_BLACKHOLE:
943 			/* valid */
944 			break;
945 		default:
946 			/* unknown */
947 			fprintf(stderr, "Unknown well-known community\n");
948 			return (0);
949 		}
950 
951 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
952 		err(1, NULL);
953 	fs->type = ACTION_SET_COMMUNITY;
954 	fs->action.community.as = as;
955 	fs->action.community.type = type;
956 
957 	r->community.as = as;
958 	r->community.type = type;
959 
960 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
961 	return (1);
962 }
963 
964 int
965 parse_nexthop(const char *word, struct parse_result *r)
966 {
967 	struct filter_set	*fs;
968 
969 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
970 		err(1, NULL);
971 
972 	if (strcmp(word, "blackhole") == 0)
973 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
974 	else if (strcmp(word, "reject") == 0)
975 		fs->type = ACTION_SET_NEXTHOP_REJECT;
976 	else if (strcmp(word, "no-modify") == 0)
977 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
978 	else if (parse_addr(word, &fs->action.nexthop)) {
979 		fs->type = ACTION_SET_NEXTHOP;
980 	} else {
981 		free(fs);
982 		return (0);
983 	}
984 
985 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
986 	return (1);
987 }
988 
989 int
990 bgpctl_getopt(int *argc, char **argv[], int type)
991 {
992 	int	  ch;
993 
994 	optind = optreset = 1;
995 	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
996 		switch (ch) {
997 		case '4':
998 			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
999 			break;
1000 		case '6':
1001 			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
1002 			break;
1003 		case 'o':
1004 			res.irr_outdir = optarg;
1005 			break;
1006 		default:
1007 			usage();
1008 			/* NOTREACHED */
1009 		}
1010 	}
1011 
1012 	if (optind > 1) {
1013 		(*argc) -= (optind - 1);
1014 		(*argv) += (optind - 1);
1015 
1016 		/* need to move one backwards as calling code moves forward */
1017 		(*argc)++;
1018 		(*argv)--;
1019 		return (1);
1020 	} else
1021 		return (0);
1022 }
1023