xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: parser.c,v 1.68 2014/01/05 20:53:56 deraadt 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 *, struct bgpd_addr *,
390 			     u_int8_t *);
391 int			 parse_asnum(const char *, 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 
446 	match = 0;
447 
448 	for (i = 0; table[i].type != ENDTOKEN; i++) {
449 		switch (table[i].type) {
450 		case NOTOKEN:
451 			if (word == NULL || strlen(word) == 0) {
452 				match++;
453 				t = &table[i];
454 			}
455 			break;
456 		case KEYWORD:
457 			if (word != NULL && strncmp(word, table[i].keyword,
458 			    strlen(word)) == 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 			    strlen(word)) == 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, &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 			    strlen(word)) == 0) {
521 				match++;
522 				t = &table[i];
523 				res.as.type = t->value;
524 			}
525 			break;
526 		case ASNUM:
527 			if (parse_asnum(word, &res.as.as)) {
528 				match++;
529 				t = &table[i];
530 			}
531 			break;
532 		case PEERDESC:
533 			if (!match && word != NULL && strlen(word) > 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 && strlen(word) > 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 && strlen(word) > 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 && strlen(word) > 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 && strlen(word) > 0 &&
573 			    parse_nexthop(word, &res)) {
574 				match++;
575 				t = &table[i];
576 			}
577 			break;
578 		case PFTABLE:
579 			if (word != NULL && strlen(word) > 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 && strlen(word) > 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, 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 		mask = strtonum(p + 1, 0, 128, &errstr);
748 		if (errstr)
749 			errx(1, "netmask %s", errstr);
750 
751 		if ((ps = malloc(strlen(word) - strlen(p) + 1)) == NULL)
752 			err(1, "parse_prefix: malloc");
753 		strlcpy(ps, word, strlen(word) - strlen(p) + 1);
754 
755 		if (parse_addr(ps, addr) == 0) {
756 			free(ps);
757 			return (0);
758 		}
759 
760 		free(ps);
761 	} else
762 		if (parse_addr(word, addr) == 0)
763 			return (0);
764 
765 	switch (addr->aid) {
766 	case AID_INET:
767 		if (mask == -1)
768 			mask = 32;
769 		if (mask > 32)
770 			errx(1, "invalid netmask: too large");
771 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
772 		break;
773 	case AID_INET6:
774 		if (mask == -1)
775 			mask = 128;
776 		inet6applymask(&addr->v6, &addr->v6, mask);
777 		break;
778 	default:
779 		return (0);
780 	}
781 
782 	*prefixlen = mask;
783 	return (1);
784 }
785 
786 int
787 parse_asnum(const char *word, u_int32_t *asnum)
788 {
789 	const char	*errstr;
790 	char		*dot;
791 	u_int32_t	 uval, uvalh = 0;
792 
793 	if (word == NULL)
794 		return (0);
795 
796 	if (strlen(word) < 1 || word[0] < '0' || word[0] > '9')
797 		return (0);
798 
799 	if ((dot = strchr(word,'.')) != NULL) {
800 		*dot++ = '\0';
801 		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
802 		if (errstr)
803 			errx(1, "AS number is %s: %s", errstr, word);
804 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
805 		if (errstr)
806 			errx(1, "AS number is %s: %s", errstr, word);
807 	} else {
808 		uval = strtonum(word, 0, UINT_MAX, &errstr);
809 		if (errstr)
810 			errx(1, "AS number is %s: %s", errstr, word);
811 	}
812 
813 	*asnum = uval | (uvalh << 16);
814 	return (1);
815 }
816 
817 int
818 parse_number(const char *word, struct parse_result *r, enum token_type type)
819 {
820 	struct filter_set	*fs;
821 	const char		*errstr;
822 	u_int			 uval;
823 
824 	if (word == NULL)
825 		return (0);
826 
827 	uval = strtonum(word, 0, UINT_MAX, &errstr);
828 	if (errstr)
829 		errx(1, "number is %s: %s", errstr, word);
830 
831 	/* number was parseable */
832 	if (type == RTABLE) {
833 		r->rtableid = uval;
834 		return (1);
835 	}
836 
837 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
838 		err(1, NULL);
839 	switch (type) {
840 	case LOCALPREF:
841 		fs->type = ACTION_SET_LOCALPREF;
842 		fs->action.metric = uval;
843 		break;
844 	case MED:
845 		fs->type = ACTION_SET_MED;
846 		fs->action.metric = uval;
847 		break;
848 	case PREPNBR:
849 		if (uval > 128) {
850 			free(fs);
851 			return (0);
852 		}
853 		fs->type = ACTION_SET_PREPEND_PEER;
854 		fs->action.prepend = uval;
855 		break;
856 	case PREPSELF:
857 		if (uval > 128) {
858 			free(fs);
859 			return (0);
860 		}
861 		fs->type = ACTION_SET_PREPEND_SELF;
862 		fs->action.prepend = uval;
863 		break;
864 	case WEIGHT:
865 		fs->type = ACTION_SET_WEIGHT;
866 		fs->action.metric = uval;
867 		break;
868 	default:
869 		errx(1, "king bula sez bad things happen");
870 	}
871 
872 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
873 	return (1);
874 }
875 
876 int
877 getcommunity(const char *s)
878 {
879 	const char	*errstr;
880 	u_int16_t	 uval;
881 
882 	if (strcmp(s, "*") == 0)
883 		return (COMMUNITY_ANY);
884 
885 	uval = strtonum(s, 0, USHRT_MAX, &errstr);
886 	if (errstr)
887 		errx(1, "Community is %s: %s", errstr, s);
888 
889 	return (uval);
890 }
891 
892 int
893 parse_community(const char *word, struct parse_result *r)
894 {
895 	struct filter_set	*fs;
896 	char			*p;
897 	int			 as, type;
898 
899 	/* Well-known communities */
900 	if (strcasecmp(word, "NO_EXPORT") == 0) {
901 		as = COMMUNITY_WELLKNOWN;
902 		type = COMMUNITY_NO_EXPORT;
903 		goto done;
904 	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
905 		as = COMMUNITY_WELLKNOWN;
906 		type = COMMUNITY_NO_ADVERTISE;
907 		goto done;
908 	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
909 		as = COMMUNITY_WELLKNOWN;
910 		type = COMMUNITY_NO_EXPSUBCONFED;
911 		goto done;
912 	} else if (strcasecmp(word, "NO_PEER") == 0) {
913 		as = COMMUNITY_WELLKNOWN;
914 		type = COMMUNITY_NO_PEER;
915 		goto done;
916 	}
917 
918 	if ((p = strchr(word, ':')) == NULL) {
919 		fprintf(stderr, "Bad community syntax\n");
920 		return (0);
921 	}
922 	*p++ = 0;
923 
924 	as = getcommunity(word);
925 	type = getcommunity(p);
926 
927 done:
928 	if (as == 0) {
929 		fprintf(stderr, "Invalid community\n");
930 		return (0);
931 	}
932 	if (as == COMMUNITY_WELLKNOWN)
933 		switch (type) {
934 		case COMMUNITY_NO_EXPORT:
935 		case COMMUNITY_NO_ADVERTISE:
936 		case COMMUNITY_NO_EXPSUBCONFED:
937 			/* valid */
938 			break;
939 		default:
940 			/* unknown */
941 			fprintf(stderr, "Unknown well-known community\n");
942 			return (0);
943 		}
944 
945 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
946 		err(1, NULL);
947 	fs->type = ACTION_SET_COMMUNITY;
948 	fs->action.community.as = as;
949 	fs->action.community.type = type;
950 
951 	r->community.as = as;
952 	r->community.type = type;
953 
954 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
955 	return (1);
956 }
957 
958 int
959 parse_nexthop(const char *word, struct parse_result *r)
960 {
961 	struct filter_set	*fs;
962 
963 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
964 		err(1, NULL);
965 
966 	if (strcmp(word, "blackhole") == 0)
967 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
968 	else if (strcmp(word, "reject") == 0)
969 		fs->type = ACTION_SET_NEXTHOP_REJECT;
970 	else if (strcmp(word, "no-modify") == 0)
971 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
972 	else if (parse_addr(word, &fs->action.nexthop)) {
973 		fs->type = ACTION_SET_NEXTHOP;
974 	} else {
975 		free(fs);
976 		return (0);
977 	}
978 
979 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
980 	return (1);
981 }
982 
983 int
984 bgpctl_getopt(int *argc, char **argv[], int type)
985 {
986 	int	  ch;
987 
988 	optind = optreset = 1;
989 	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
990 		switch (ch) {
991 		case '4':
992 			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
993 			break;
994 		case '6':
995 			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
996 			break;
997 		case 'o':
998 			res.irr_outdir = optarg;
999 			break;
1000 		default:
1001 			usage();
1002 			/* NOTREACHED */
1003 		}
1004 	}
1005 
1006 	if (optind > 1) {
1007 		(*argc) -= (optind - 1);
1008 		(*argv) += (optind - 1);
1009 
1010 		/* need to move one backwards as calling code moves forward */
1011 		(*argc)++;
1012 		(*argv)--;
1013 		return (1);
1014 	} else
1015 		return (0);
1016 }
1017