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