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