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