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