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