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