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