xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision 48950c12d106c85f315112191a0228d7b83b9510)
1 /*	$OpenBSD: parser.c,v 1.66 2013/03/07 21:28:34 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 	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
247 	{ ENDTOKEN,	"",		NONE,			NULL}
248 };
249 
250 static const struct token t_show_rib_as[] = {
251 	{ ASNUM,	"",		NONE,		t_show_rib},
252 	{ ENDTOKEN,	"",		NONE,		NULL}
253 };
254 
255 static const struct token t_show_mrt_as[] = {
256 	{ ASNUM,	"",		NONE,		t_show_mrt},
257 	{ ENDTOKEN,	"",		NONE,		NULL}
258 };
259 
260 static const struct token t_show_prefix[] = {
261 	{ NOTOKEN,	"",		NONE,		NULL},
262 	{ FLAG,		"all",		F_LONGER,	NULL},
263 	{ FLAG,		"longer-prefixes", F_LONGER,	NULL},
264 	{ ENDTOKEN,	"",		NONE,		NULL}
265 };
266 
267 static const struct token t_show_ip[] = {
268 	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
269 	{ ENDTOKEN,	"",		NONE,		NULL}
270 };
271 
272 static const struct token t_show_community[] = {
273 	{ COMMUNITY,	"",		NONE,		t_show_rib},
274 	{ ENDTOKEN,	"",		NONE,		NULL}
275 };
276 
277 static const struct token t_network[] = {
278 	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
279 	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
280 	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
281 	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
282 	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
283 	{ ENDTOKEN,	"",		NONE,		NULL}
284 };
285 
286 static const struct token t_prefix[] = {
287 	{ PREFIX,	"",		NONE,		t_set},
288 	{ ENDTOKEN,	"",		NONE,		NULL}
289 };
290 
291 static const struct token t_network_show[] = {
292 	{ NOTOKEN,	"",		NONE,			NULL},
293 	{ FAMILY,	"",		NONE,			NULL},
294 	{ ENDTOKEN,	"",		NONE,			NULL}
295 };
296 
297 static const struct token t_set[] = {
298 	{ NOTOKEN,	"",			NONE,	NULL},
299 	{ KEYWORD,	"community",		NONE,	t_community},
300 	{ KEYWORD,	"localpref",		NONE,	t_localpref},
301 	{ KEYWORD,	"med",			NONE,	t_med},
302 	{ KEYWORD,	"metric",		NONE,	t_med},
303 	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
304 	{ KEYWORD,	"pftable",		NONE,	t_pftable},
305 	{ KEYWORD,	"prepend-neighbor",	NONE,	t_prepnbr},
306 	{ KEYWORD,	"prepend-self",		NONE,	t_prepself},
307 	{ KEYWORD,	"weight",		NONE,	t_weight},
308 	{ ENDTOKEN,	"",			NONE,	NULL}
309 };
310 
311 static const struct token t_community[] = {
312 	{ COMMUNITY,	"",			NONE,	t_set},
313 	{ ENDTOKEN,	"",			NONE,	NULL}
314 };
315 
316 static const struct token t_localpref[] = {
317 	{ LOCALPREF,	"",			NONE,	t_set},
318 	{ ENDTOKEN,	"",			NONE,	NULL}
319 };
320 
321 static const struct token t_med[] = {
322 	{ MED,		"",			NONE,	t_set},
323 	{ ENDTOKEN,	"",			NONE,	NULL}
324 };
325 
326 static const struct token t_nexthop[] = {
327 	{ NEXTHOP,	"",			NONE,	t_set},
328 	{ ENDTOKEN,	"",			NONE,	NULL}
329 };
330 
331 static const struct token t_pftable[] = {
332 	{ PFTABLE,	"",			NONE,	t_set},
333 	{ ENDTOKEN,	"",			NONE,	NULL}
334 };
335 
336 static const struct token t_prepnbr[] = {
337 	{ PREPNBR,	"",			NONE,	t_set},
338 	{ ENDTOKEN,	"",			NONE,	NULL}
339 };
340 
341 static const struct token t_prepself[] = {
342 	{ PREPSELF,	"",			NONE,	t_set},
343 	{ ENDTOKEN,	"",			NONE,	NULL}
344 };
345 
346 static const struct token t_weight[] = {
347 	{ WEIGHT,	"",			NONE,	t_set},
348 	{ ENDTOKEN,	"",			NONE,	NULL}
349 };
350 
351 static const struct token t_irrfilter[] = {
352 	{ GETOPT,	"",	GETOPT_IRRFILTER,	t_irrfilter},
353 	{ ASNUM,	"",	NONE,			t_irrfilter_opts},
354 	{ ENDTOKEN,	"",	NONE,			NULL}
355 };
356 
357 static const struct token t_irrfilter_opts[] = {
358 	{ NOTOKEN,	"",		NONE,			NULL},
359 	{ FLAG,		"importonly",	F_IMPORTONLY,		t_irrfilter_opts},
360 	{ ENDTOKEN,	"",		NONE,			NULL}
361 };
362 
363 static const struct token t_log[] = {
364 	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
365 	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
366 	{ ENDTOKEN,	"",		NONE,		NULL}
367 };
368 
369 static const struct token t_fib_table[] = {
370 	{ RTABLE,	"",			NONE,	t_fib},
371 	{ ENDTOKEN,	"",			NONE,	NULL}
372 };
373 
374 static const struct token t_show_fib_table[] = {
375 	{ RTABLE,	"",			NONE,	t_show_fib},
376 	{ ENDTOKEN,	"",			NONE,	NULL}
377 };
378 
379 static struct parse_result	res;
380 
381 const struct token	*match_token(int *argc, char **argv[],
382 			    const struct token []);
383 void			 show_valid_args(const struct token []);
384 int			 parse_addr(const char *, struct bgpd_addr *);
385 int			 parse_prefix(const char *, struct bgpd_addr *,
386 			     u_int8_t *);
387 int			 parse_asnum(const char *, u_int32_t *);
388 int			 parse_number(const char *, struct parse_result *,
389 			     enum token_type);
390 int			 getcommunity(const char *);
391 int			 parse_community(const char *, struct parse_result *);
392 int			 parse_nexthop(const char *, struct parse_result *);
393 int			 bgpctl_getopt(int *, char **[], int);
394 
395 struct parse_result *
396 parse(int argc, char *argv[])
397 {
398 	const struct token	*table = t_main;
399 	const struct token	*match;
400 
401 	bzero(&res, sizeof(res));
402 	res.community.as = COMMUNITY_UNSET;
403 	res.community.type = COMMUNITY_UNSET;
404 	TAILQ_INIT(&res.set);
405 	if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
406 		fprintf(stderr, "getcwd failed: %s", strerror(errno));
407 		return (NULL);
408 	}
409 
410 	while (argc >= 0) {
411 		if ((match = match_token(&argc, &argv, table)) == NULL) {
412 			fprintf(stderr, "valid commands/args:\n");
413 			show_valid_args(table);
414 			return (NULL);
415 		}
416 
417 		argc--;
418 		argv++;
419 
420 		if (match->type == NOTOKEN || match->next == NULL)
421 			break;
422 
423 		table = match->next;
424 	}
425 
426 	if (argc > 0) {
427 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
428 		return (NULL);
429 	}
430 
431 	return (&res);
432 }
433 
434 const struct token *
435 match_token(int *argc, char **argv[], const struct token table[])
436 {
437 	u_int			 i, match;
438 	const struct token	*t = NULL;
439 	struct filter_set	*fs;
440 	const char		*word = *argv[0];
441 
442 	match = 0;
443 
444 	for (i = 0; table[i].type != ENDTOKEN; i++) {
445 		switch (table[i].type) {
446 		case NOTOKEN:
447 			if (word == NULL || strlen(word) == 0) {
448 				match++;
449 				t = &table[i];
450 			}
451 			break;
452 		case KEYWORD:
453 			if (word != NULL && strncmp(word, table[i].keyword,
454 			    strlen(word)) == 0) {
455 				match++;
456 				t = &table[i];
457 				if (t->value)
458 					res.action = t->value;
459 			}
460 			break;
461 		case FLAG:
462 			if (word != NULL && strncmp(word, table[i].keyword,
463 			    strlen(word)) == 0) {
464 				match++;
465 				t = &table[i];
466 				res.flags |= t->value;
467 			}
468 			break;
469 		case FAMILY:
470 			if (word == NULL)
471 				break;
472 			if (!strcmp(word, "inet") ||
473 			    !strcasecmp(word, "IPv4")) {
474 				match++;
475 				t = &table[i];
476 				res.aid = AID_INET;
477 			}
478 			if (!strcmp(word, "inet6") ||
479 			    !strcasecmp(word, "IPv6")) {
480 				match++;
481 				t = &table[i];
482 				res.aid = AID_INET6;
483 			}
484 			if (!strcasecmp(word, "VPNv4")) {
485 				match++;
486 				t = &table[i];
487 				res.aid = AID_VPN_IPv4;
488 			}
489 			break;
490 		case ADDRESS:
491 			if (parse_addr(word, &res.addr)) {
492 				match++;
493 				t = &table[i];
494 				if (t->value)
495 					res.action = t->value;
496 			}
497 			break;
498 		case PEERADDRESS:
499 			if (parse_addr(word, &res.peeraddr)) {
500 				match++;
501 				t = &table[i];
502 				if (t->value)
503 					res.action = t->value;
504 			}
505 			break;
506 		case PREFIX:
507 			if (parse_prefix(word, &res.addr, &res.prefixlen)) {
508 				match++;
509 				t = &table[i];
510 				if (t->value)
511 					res.action = t->value;
512 			}
513 			break;
514 		case ASTYPE:
515 			if (word != NULL && strncmp(word, table[i].keyword,
516 			    strlen(word)) == 0) {
517 				match++;
518 				t = &table[i];
519 				res.as.type = t->value;
520 			}
521 			break;
522 		case ASNUM:
523 			if (parse_asnum(word, &res.as.as)) {
524 				match++;
525 				t = &table[i];
526 			}
527 			break;
528 		case PEERDESC:
529 			if (!match && word != NULL && strlen(word) > 0) {
530 				if (strlcpy(res.peerdesc, word,
531 				    sizeof(res.peerdesc)) >=
532 				    sizeof(res.peerdesc))
533 					errx(1, "neighbor description too "
534 					    "long");
535 				match++;
536 				t = &table[i];
537 			}
538 			break;
539 		case RIBNAME:
540 			if (!match && word != NULL && strlen(word) > 0) {
541 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
542 				    sizeof(res.rib))
543 					errx(1, "rib name too long");
544 				match++;
545 				t = &table[i];
546 			}
547 			break;
548 		case COMMUNITY:
549 			if (word != NULL && strlen(word) > 0 &&
550 			    parse_community(word, &res)) {
551 				match++;
552 				t = &table[i];
553 			}
554 			break;
555 		case LOCALPREF:
556 		case MED:
557 		case PREPNBR:
558 		case PREPSELF:
559 		case WEIGHT:
560 		case RTABLE:
561 			if (word != NULL && strlen(word) > 0 &&
562 			    parse_number(word, &res, table[i].type)) {
563 				match++;
564 				t = &table[i];
565 			}
566 			break;
567 		case NEXTHOP:
568 			if (word != NULL && strlen(word) > 0 &&
569 			    parse_nexthop(word, &res)) {
570 				match++;
571 				t = &table[i];
572 			}
573 			break;
574 		case PFTABLE:
575 			if (word != NULL && strlen(word) > 0) {
576 				if ((fs = calloc(1,
577 				    sizeof(struct filter_set))) == NULL)
578 					err(1, NULL);
579 				if (strlcpy(fs->action.pftable, word,
580 				    sizeof(fs->action.pftable)) >=
581 				    sizeof(fs->action.pftable))
582 					errx(1, "pftable name too long");
583 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
584 				match++;
585 				t = &table[i];
586 			}
587 			break;
588 		case GETOPT:
589 			if (bgpctl_getopt(argc, argv, table[i].value)) {
590 				match++;
591 				t = &table[i];
592 			}
593 			break;
594 		case FILENAME:
595 			if (word != NULL && strlen(word) > 0) {
596 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
597 					/*
598 					 * ignore error if path has no / and
599 					 * does not exist. In hope to print
600 					 * usage.
601 					 */
602 					if (errno == ENOENT &&
603 					    !strchr(word, '/'))
604 						break;
605 					err(1, "mrt open(%s)", word);
606 				}
607 				match++;
608 				t = &table[i];
609 			}
610 			break;
611 		case ENDTOKEN:
612 			break;
613 		}
614 	}
615 
616 	if (match != 1) {
617 		if (word == NULL)
618 			fprintf(stderr, "missing argument:\n");
619 		else if (match > 1)
620 			fprintf(stderr, "ambiguous argument: %s\n", word);
621 		else if (match < 1)
622 			fprintf(stderr, "unknown argument: %s\n", word);
623 		return (NULL);
624 	}
625 
626 	return (t);
627 }
628 
629 void
630 show_valid_args(const struct token table[])
631 {
632 	int	i;
633 
634 	for (i = 0; table[i].type != ENDTOKEN; i++) {
635 		switch (table[i].type) {
636 		case NOTOKEN:
637 			fprintf(stderr, "  <cr>\n");
638 			break;
639 		case KEYWORD:
640 		case FLAG:
641 		case ASTYPE:
642 			fprintf(stderr, "  %s\n", table[i].keyword);
643 			break;
644 		case ADDRESS:
645 		case PEERADDRESS:
646 			fprintf(stderr, "  <address>\n");
647 			break;
648 		case PREFIX:
649 			fprintf(stderr, "  <address>[/<len>]\n");
650 			break;
651 		case ASNUM:
652 			fprintf(stderr, "  <asnum>\n");
653 			break;
654 		case PEERDESC:
655 			fprintf(stderr, "  <neighbor description>\n");
656 			break;
657 		case RIBNAME:
658 			fprintf(stderr, "  <rib name>\n");
659 			break;
660 		case COMMUNITY:
661 			fprintf(stderr, "  <community>\n");
662 			break;
663 		case LOCALPREF:
664 		case MED:
665 		case PREPNBR:
666 		case PREPSELF:
667 		case WEIGHT:
668 			fprintf(stderr, "  <number>\n");
669 			break;
670 		case RTABLE:
671 			fprintf(stderr, "  <rtableid>\n");
672 			break;
673 		case NEXTHOP:
674 			fprintf(stderr, "  <address>\n");
675 			break;
676 		case PFTABLE:
677 			fprintf(stderr, "  <pftable>\n");
678 			break;
679 		case FAMILY:
680 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
681 			break;
682 		case GETOPT:
683 			fprintf(stderr, "  <options>\n");
684 			break;
685 		case FILENAME:
686 			fprintf(stderr, "  <filename>\n");
687 			break;
688 		case ENDTOKEN:
689 			break;
690 		}
691 	}
692 }
693 
694 int
695 parse_addr(const char *word, struct bgpd_addr *addr)
696 {
697 	struct in_addr	ina;
698 	struct addrinfo	hints, *r;
699 
700 	if (word == NULL)
701 		return (0);
702 
703 	bzero(addr, sizeof(struct bgpd_addr));
704 	bzero(&ina, sizeof(ina));
705 
706 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
707 		addr->aid = AID_INET;
708 		addr->v4 = ina;
709 		return (1);
710 	}
711 
712 	bzero(&hints, sizeof(hints));
713 	hints.ai_family = AF_INET6;
714 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
715 	hints.ai_flags = AI_NUMERICHOST;
716 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
717 		sa2addr(r->ai_addr, addr);
718 		freeaddrinfo(r);
719 		return (1);
720 	}
721 
722 	return (0);
723 }
724 
725 int
726 parse_prefix(const char *word, struct bgpd_addr *addr, u_int8_t *prefixlen)
727 {
728 	char		*p, *ps;
729 	const char	*errstr;
730 	int		 mask = -1;
731 
732 	if (word == NULL)
733 		return (0);
734 
735 	bzero(addr, sizeof(struct bgpd_addr));
736 
737 	if ((p = strrchr(word, '/')) != NULL) {
738 		mask = strtonum(p + 1, 0, 128, &errstr);
739 		if (errstr)
740 			errx(1, "netmask %s", errstr);
741 
742 		if ((ps = malloc(strlen(word) - strlen(p) + 1)) == NULL)
743 			err(1, "parse_prefix: malloc");
744 		strlcpy(ps, word, strlen(word) - strlen(p) + 1);
745 
746 		if (parse_addr(ps, addr) == 0) {
747 			free(ps);
748 			return (0);
749 		}
750 
751 		free(ps);
752 	} else
753 		if (parse_addr(word, addr) == 0)
754 			return (0);
755 
756 	switch (addr->aid) {
757 	case AID_INET:
758 		if (mask == -1)
759 			mask = 32;
760 		if (mask > 32)
761 			errx(1, "invalid netmask: too large");
762 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
763 		break;
764 	case AID_INET6:
765 		if (mask == -1)
766 			mask = 128;
767 		inet6applymask(&addr->v6, &addr->v6, mask);
768 		break;
769 	default:
770 		return (0);
771 	}
772 
773 	*prefixlen = mask;
774 	return (1);
775 }
776 
777 int
778 parse_asnum(const char *word, u_int32_t *asnum)
779 {
780 	const char	*errstr;
781 	char		*dot;
782 	u_int32_t	 uval, uvalh = 0;
783 
784 	if (word == NULL)
785 		return (0);
786 
787 	if (strlen(word) < 1 || word[0] < '0' || word[0] > '9')
788 		return (0);
789 
790 	if ((dot = strchr(word,'.')) != NULL) {
791 		*dot++ = '\0';
792 		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
793 		if (errstr)
794 			errx(1, "AS number is %s: %s", errstr, word);
795 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
796 		if (errstr)
797 			errx(1, "AS number is %s: %s", errstr, word);
798 	} else {
799 		uval = strtonum(word, 0, UINT_MAX, &errstr);
800 		if (errstr)
801 			errx(1, "AS number is %s: %s", errstr, word);
802 	}
803 
804 	*asnum = uval | (uvalh << 16);
805 	return (1);
806 }
807 
808 int
809 parse_number(const char *word, struct parse_result *r, enum token_type type)
810 {
811 	struct filter_set	*fs;
812 	const char		*errstr;
813 	u_int			 uval;
814 
815 	if (word == NULL)
816 		return (0);
817 
818 	uval = strtonum(word, 0, UINT_MAX, &errstr);
819 	if (errstr)
820 		errx(1, "number is %s: %s", errstr, word);
821 
822 	/* number was parseable */
823 	if (type == RTABLE) {
824 		r->rtableid = uval;
825 		return (1);
826 	}
827 
828 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
829 		err(1, NULL);
830 	switch (type) {
831 	case LOCALPREF:
832 		fs->type = ACTION_SET_LOCALPREF;
833 		fs->action.metric = uval;
834 		break;
835 	case MED:
836 		fs->type = ACTION_SET_MED;
837 		fs->action.metric = uval;
838 		break;
839 	case PREPNBR:
840 		if (uval > 128) {
841 			free(fs);
842 			return (0);
843 		}
844 		fs->type = ACTION_SET_PREPEND_PEER;
845 		fs->action.prepend = uval;
846 		break;
847 	case PREPSELF:
848 		if (uval > 128) {
849 			free(fs);
850 			return (0);
851 		}
852 		fs->type = ACTION_SET_PREPEND_SELF;
853 		fs->action.prepend = uval;
854 		break;
855 	case WEIGHT:
856 		fs->type = ACTION_SET_WEIGHT;
857 		fs->action.metric = uval;
858 		break;
859 	default:
860 		errx(1, "king bula sez bad things happen");
861 	}
862 
863 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
864 	return (1);
865 }
866 
867 int
868 getcommunity(const char *s)
869 {
870 	const char	*errstr;
871 	u_int16_t	 uval;
872 
873 	if (strcmp(s, "*") == 0)
874 		return (COMMUNITY_ANY);
875 
876 	uval = strtonum(s, 0, USHRT_MAX, &errstr);
877 	if (errstr)
878 		errx(1, "Community is %s: %s", errstr, s);
879 
880 	return (uval);
881 }
882 
883 int
884 parse_community(const char *word, struct parse_result *r)
885 {
886 	struct filter_set	*fs;
887 	char			*p;
888 	int			 as, type;
889 
890 	/* Well-known communities */
891 	if (strcasecmp(word, "NO_EXPORT") == 0) {
892 		as = COMMUNITY_WELLKNOWN;
893 		type = COMMUNITY_NO_EXPORT;
894 		goto done;
895 	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
896 		as = COMMUNITY_WELLKNOWN;
897 		type = COMMUNITY_NO_ADVERTISE;
898 		goto done;
899 	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
900 		as = COMMUNITY_WELLKNOWN;
901 		type = COMMUNITY_NO_EXPSUBCONFED;
902 		goto done;
903 	} else if (strcasecmp(word, "NO_PEER") == 0) {
904 		as = COMMUNITY_WELLKNOWN;
905 		type = COMMUNITY_NO_PEER;
906 		goto done;
907 	}
908 
909 	if ((p = strchr(word, ':')) == NULL) {
910 		fprintf(stderr, "Bad community syntax\n");
911 		return (0);
912 	}
913 	*p++ = 0;
914 
915 	as = getcommunity(word);
916 	type = getcommunity(p);
917 
918 done:
919 	if (as == 0) {
920 		fprintf(stderr, "Invalid community\n");
921 		return (0);
922 	}
923 	if (as == COMMUNITY_WELLKNOWN)
924 		switch (type) {
925 		case COMMUNITY_NO_EXPORT:
926 		case COMMUNITY_NO_ADVERTISE:
927 		case COMMUNITY_NO_EXPSUBCONFED:
928 			/* valid */
929 			break;
930 		default:
931 			/* unknown */
932 			fprintf(stderr, "Unknown well-known community\n");
933 			return (0);
934 		}
935 
936 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
937 		err(1, NULL);
938 	fs->type = ACTION_SET_COMMUNITY;
939 	fs->action.community.as = as;
940 	fs->action.community.type = type;
941 
942 	r->community.as = as;
943 	r->community.type = type;
944 
945 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
946 	return (1);
947 }
948 
949 int
950 parse_nexthop(const char *word, struct parse_result *r)
951 {
952 	struct filter_set	*fs;
953 
954 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
955 		err(1, NULL);
956 
957 	if (strcmp(word, "blackhole") == 0)
958 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
959 	else if (strcmp(word, "reject") == 0)
960 		fs->type = ACTION_SET_NEXTHOP_REJECT;
961 	else if (strcmp(word, "no-modify") == 0)
962 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
963 	else if (parse_addr(word, &fs->action.nexthop)) {
964 		fs->type = ACTION_SET_NEXTHOP;
965 	} else {
966 		free(fs);
967 		return (0);
968 	}
969 
970 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
971 	return (1);
972 }
973 
974 int
975 bgpctl_getopt(int *argc, char **argv[], int type)
976 {
977 	int	  ch;
978 
979 	optind = optreset = 1;
980 	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
981 		switch (ch) {
982 		case '4':
983 			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
984 			break;
985 		case '6':
986 			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
987 			break;
988 		case 'o':
989 			res.irr_outdir = optarg;
990 			break;
991 		default:
992 			usage();
993 			/* NOTREACHED */
994 		}
995 	}
996 
997 	if (optind > 1) {
998 		(*argc) -= (optind - 1);
999 		(*argv) += (optind - 1);
1000 
1001 		/* need to move one backwards as calling code moves forward */
1002 		(*argc)++;
1003 		(*argv)--;
1004 		return (1);
1005 	} else
1006 		return (0);
1007 }
1008