xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision 6c6408334dbede3a2c0dcd9ff9c489157df0c856)
1 /*	$OpenBSD: parser.c,v 1.81 2017/10/15 20:44:21 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5  * Copyright (c) 2016 Job Snijders <job@instituut.net>
6  * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 
23 #include <err.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <limits.h>
27 #include <netdb.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "parser.h"
34 #include "irrfilter.h"
35 
36 enum token_type {
37 	NOTOKEN,
38 	ENDTOKEN,
39 	KEYWORD,
40 	ADDRESS,
41 	PEERADDRESS,
42 	FLAG,
43 	ASNUM,
44 	ASTYPE,
45 	PREFIX,
46 	PEERDESC,
47 	RIBNAME,
48 	SHUTDOWN_COMMUNICATION,
49 	COMMUNITY,
50 	EXTCOMMUNITY,
51 	EXTCOM_SUBTYPE,
52 	LARGE_COMMUNITY,
53 	LOCALPREF,
54 	MED,
55 	NEXTHOP,
56 	PFTABLE,
57 	PREPNBR,
58 	PREPSELF,
59 	WEIGHT,
60 	FAMILY,
61 	GETOPT,
62 	RTABLE,
63 	FILENAME,
64 	BULK
65 };
66 
67 enum getopts {
68 	GETOPT_NONE,
69 	GETOPT_IRRFILTER
70 };
71 
72 struct token {
73 	enum token_type		 type;
74 	const char		*keyword;
75 	int			 value;
76 	const struct token	*next;
77 };
78 
79 static const struct token t_main[];
80 static const struct token t_show[];
81 static const struct token t_show_summary[];
82 static const struct token t_show_fib[];
83 static const struct token t_show_rib[];
84 static const struct token t_show_mrt[];
85 static const struct token t_show_mrt_file[];
86 static const struct token t_show_rib_neigh[];
87 static const struct token t_show_mrt_neigh[];
88 static const struct token t_show_rib_rib[];
89 static const struct token t_show_neighbor[];
90 static const struct token t_show_neighbor_modifiers[];
91 static const struct token t_fib[];
92 static const struct token t_neighbor[];
93 static const struct token t_neighbor_modifiers[];
94 static const struct token t_show_rib_as[];
95 static const struct token t_show_mrt_as[];
96 static const struct token t_show_prefix[];
97 static const struct token t_show_ip[];
98 static const struct token t_show_community[];
99 static const struct token t_show_extcommunity[];
100 static const struct token t_show_ext_subtype[];
101 static const struct token t_show_largecommunity[];
102 static const struct token t_network[];
103 static const struct token t_network_show[];
104 static const struct token t_prefix[];
105 static const struct token t_set[];
106 static const struct token t_community[];
107 static const struct token t_extcommunity[];
108 static const struct token t_ext_subtype[];
109 static const struct token t_largecommunity[];
110 static const struct token t_localpref[];
111 static const struct token t_med[];
112 static const struct token t_nexthop[];
113 static const struct token t_pftable[];
114 static const struct token t_prepnbr[];
115 static const struct token t_prepself[];
116 static const struct token t_weight[];
117 static const struct token t_irrfilter[];
118 static const struct token t_irrfilter_opts[];
119 static const struct token t_log[];
120 static const struct token t_fib_table[];
121 static const struct token t_show_fib_table[];
122 
123 static const struct token t_main[] = {
124 	{ KEYWORD,	"reload",	RELOAD,		NULL},
125 	{ KEYWORD,	"show",		SHOW,		t_show},
126 	{ KEYWORD,	"fib",		FIB,		t_fib},
127 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
128 	{ KEYWORD,	"network",	NONE,		t_network},
129 	{ KEYWORD,	"irrfilter",	IRRFILTER,	t_irrfilter},
130 	{ KEYWORD,	"log",		NONE,		t_log},
131 	{ ENDTOKEN,	"",		NONE,		NULL}
132 };
133 
134 static const struct token t_show[] = {
135 	{ NOTOKEN,	"",		NONE,		NULL},
136 	{ KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
137 	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
138 	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
139 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
140 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
141 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
142 	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
143 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
144 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
145 	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
146 	{ ENDTOKEN,	"",		NONE,		NULL}
147 };
148 
149 static const struct token t_show_summary[] = {
150 	{ NOTOKEN,	"",		NONE,			NULL},
151 	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
152 	{ ENDTOKEN,	"",		NONE,			NULL}
153 };
154 
155 static const struct token t_show_fib[] = {
156 	{ NOTOKEN,	"",		NONE,		 NULL},
157 	{ FLAG,		"connected",	F_CONNECTED,	 t_show_fib},
158 	{ FLAG,		"static",	F_STATIC,	 t_show_fib},
159 	{ FLAG,		"bgp",		F_BGPD_INSERTED, t_show_fib},
160 	{ FLAG,		"nexthop",	F_NEXTHOP,	 t_show_fib},
161 	{ KEYWORD,	"table",	NONE,		 t_show_fib_table},
162 	{ FAMILY,	"",		NONE,		 t_show_fib},
163 	{ ADDRESS,	"",		NONE,		 NULL},
164 	{ ENDTOKEN,	"",		NONE,		 NULL}
165 };
166 
167 static const struct token t_show_rib[] = {
168 	{ NOTOKEN,	"",		NONE,		NULL},
169 	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
170 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
171 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
172 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
173 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
174 	{ KEYWORD,	"community",	NONE,		t_show_community},
175 	{ KEYWORD,	"ext-community", NONE,		t_show_extcommunity},
176 	{ KEYWORD,	"large-community", NONE,	t_show_largecommunity},
177 	{ FLAG,		"best",		F_CTL_ACTIVE,	t_show_rib},
178 	{ FLAG,		"selected",	F_CTL_ACTIVE,	t_show_rib},
179 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_rib},
180 	{ FLAG,		"ssv"	,	F_CTL_SSV,	t_show_rib},
181 	{ FLAG,		"in",		F_CTL_ADJ_IN,	t_show_rib},
182 	{ FLAG,		"out",		F_CTL_ADJ_OUT,	t_show_rib},
183 	{ KEYWORD,	"neighbor",	NONE,		t_show_rib_neigh},
184 	{ KEYWORD,	"table",	NONE,		t_show_rib_rib},
185 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
186 	{ KEYWORD,	"memory",	SHOW_RIB_MEM,	NULL},
187 	{ FAMILY,	"",		NONE,		t_show_rib},
188 	{ PREFIX,	"",		NONE,		t_show_prefix},
189 	{ ENDTOKEN,	"",		NONE,		NULL}
190 };
191 
192 
193 static const struct token t_show_mrt[] = {
194 	{ NOTOKEN,	"",		NONE,		NULL},
195 	{ ASTYPE,	"as",		AS_ALL,		t_show_mrt_as},
196 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_mrt_as},
197 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_mrt_as},
198 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_mrt_as},
199 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_mrt},
200 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_mrt},
201 	{ FLAG,		"ssv"	,	F_CTL_SSV,	t_show_mrt},
202 	{ KEYWORD,	"neighbor",	NONE,		t_show_mrt_neigh},
203 	{ KEYWORD,	"file",		NONE,		t_show_mrt_file},
204 	{ FAMILY,	"",		NONE,		t_show_mrt},
205 	{ PREFIX,	"",		NONE,		t_show_prefix},
206 	{ ENDTOKEN,	"",		NONE,		NULL}
207 };
208 
209 static const struct token t_show_mrt_file[] = {
210 	{ FILENAME,	"",		NONE,		t_show_mrt},
211 	{ ENDTOKEN,	"",		NONE,	NULL}
212 };
213 
214 static const struct token t_show_rib_neigh[] = {
215 	{ PEERADDRESS,	"",		NONE,	t_show_rib},
216 	{ PEERDESC,	"",		NONE,	t_show_rib},
217 	{ ENDTOKEN,	"",		NONE,	NULL}
218 };
219 
220 static const struct token t_show_mrt_neigh[] = {
221 	{ PEERADDRESS,	"",		NONE,	t_show_mrt},
222 	{ ENDTOKEN,	"",		NONE,	NULL}
223 };
224 
225 static const struct token t_show_rib_rib[] = {
226 	{ RIBNAME,	"",		NONE,	t_show_rib},
227 	{ ENDTOKEN,	"",		NONE,	NULL}
228 };
229 
230 static const struct token t_show_neighbor[] = {
231 	{ NOTOKEN,	"",		NONE,	NULL},
232 	{ PEERADDRESS,	"",		NONE,	t_show_neighbor_modifiers},
233 	{ PEERDESC,	"",		NONE,	t_show_neighbor_modifiers},
234 	{ ENDTOKEN,	"",		NONE,	NULL}
235 };
236 
237 static const struct token t_show_neighbor_modifiers[] = {
238 	{ NOTOKEN,	"",		NONE,			NULL},
239 	{ KEYWORD,	"timers",	SHOW_NEIGHBOR_TIMERS,	NULL},
240 	{ KEYWORD,	"messages",	SHOW_NEIGHBOR,		NULL},
241 	{ KEYWORD,	"terse",	SHOW_NEIGHBOR_TERSE,	NULL},
242 	{ ENDTOKEN,	"",		NONE,			NULL}
243 };
244 
245 static const struct token t_fib[] = {
246 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
247 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
248 	{ KEYWORD,	"table",	NONE,		t_fib_table},
249 	{ ENDTOKEN,	"",		NONE,		NULL}
250 };
251 
252 static const struct token t_neighbor[] = {
253 	{ PEERADDRESS,	"",		NONE,		t_neighbor_modifiers},
254 	{ PEERDESC,	"",		NONE,		t_neighbor_modifiers},
255 	{ ENDTOKEN,	"",		NONE,		NULL}
256 };
257 
258 static const struct token t_nei_mod_shutc[] = {
259 	{ NOTOKEN,	"",		NONE,		NULL},
260 	{ SHUTDOWN_COMMUNICATION, "",	NONE,		NULL},
261 	{ ENDTOKEN,	"",		NONE,		NULL}
262 };
263 
264 static const struct token t_neighbor_modifiers[] = {
265 	{ KEYWORD,	"up",		NEIGHBOR_UP,		NULL},
266 	{ KEYWORD,	"down",		NEIGHBOR_DOWN,		t_nei_mod_shutc},
267 	{ KEYWORD,	"clear",	NEIGHBOR_CLEAR,		t_nei_mod_shutc},
268 	{ KEYWORD,	"refresh",	NEIGHBOR_RREFRESH,	NULL},
269 	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
270 	{ ENDTOKEN,	"",		NONE,			NULL}
271 };
272 
273 static const struct token t_show_rib_as[] = {
274 	{ ASNUM,	"",		NONE,		t_show_rib},
275 	{ ENDTOKEN,	"",		NONE,		NULL}
276 };
277 
278 static const struct token t_show_mrt_as[] = {
279 	{ ASNUM,	"",		NONE,		t_show_mrt},
280 	{ ENDTOKEN,	"",		NONE,		NULL}
281 };
282 
283 static const struct token t_show_prefix[] = {
284 	{ NOTOKEN,	"",		NONE,		NULL},
285 	{ FLAG,		"all",		F_LONGER,	NULL},
286 	{ FLAG,		"longer-prefixes", F_LONGER,	NULL},
287 	{ ENDTOKEN,	"",		NONE,		NULL}
288 };
289 
290 static const struct token t_show_ip[] = {
291 	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
292 	{ ENDTOKEN,	"",		NONE,		NULL}
293 };
294 
295 static const struct token t_show_community[] = {
296 	{ COMMUNITY,	"",		NONE,		t_show_rib},
297 	{ ENDTOKEN,	"",		NONE,		NULL}
298 };
299 
300 static const struct token t_show_extcommunity[] = {
301 	{ EXTCOM_SUBTYPE,	"bdc",		NONE,	t_show_ext_subtype},
302 	{ EXTCOM_SUBTYPE,	"defgw",	NONE,	t_show_ext_subtype},
303 	{ EXTCOM_SUBTYPE,	"esi-lab",	NONE,	t_show_ext_subtype},
304 	{ EXTCOM_SUBTYPE,	"esi-rt",	NONE,	t_show_ext_subtype},
305 	{ EXTCOM_SUBTYPE,	"l2vid",	NONE,	t_show_ext_subtype},
306 	{ EXTCOM_SUBTYPE,	"mac-mob",	NONE,	t_show_ext_subtype},
307 	{ EXTCOM_SUBTYPE,	"odi",		NONE,	t_show_ext_subtype},
308 	{ EXTCOM_SUBTYPE,	"ort",		NONE,	t_show_ext_subtype},
309 	{ EXTCOM_SUBTYPE,	"ori",		NONE,	t_show_ext_subtype},
310 	{ EXTCOM_SUBTYPE,	"ovs",		NONE,	t_show_ext_subtype},
311 	{ EXTCOM_SUBTYPE,	"rt",		NONE,	t_show_ext_subtype},
312 	{ EXTCOM_SUBTYPE,	"soo",		NONE,	t_show_ext_subtype},
313 	{ EXTCOM_SUBTYPE,	"srcas",	NONE,	t_show_ext_subtype},
314 	{ EXTCOM_SUBTYPE,	"vrfri",	NONE,	t_show_ext_subtype},
315 	{ ENDTOKEN,	"",	NONE,	NULL}
316 };
317 
318 static const struct token t_show_ext_subtype[] = {
319 	{ EXTCOMMUNITY,	"",	NONE,	t_show_rib},
320 	{ ENDTOKEN,	"",	NONE,	NULL}
321 };
322 
323 static const struct token t_show_largecommunity[] = {
324 	{ LARGE_COMMUNITY,	"",	NONE,		t_show_rib},
325 	{ ENDTOKEN,	"",		NONE,		NULL}
326 };
327 
328 static const struct token t_network[] = {
329 	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
330 	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
331 	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
332 	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
333 	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
334 	{ KEYWORD,	"bulk",		NETWORK_BULK_ADD,	t_set},
335 	{ ENDTOKEN,	"",		NONE,		NULL}
336 };
337 
338 static const struct token t_prefix[] = {
339 	{ PREFIX,	"",		NONE,		t_set},
340 	{ ENDTOKEN,	"",		NONE,		NULL}
341 };
342 
343 static const struct token t_network_show[] = {
344 	{ NOTOKEN,	"",		NONE,			NULL},
345 	{ FAMILY,	"",		NONE,			NULL},
346 	{ ENDTOKEN,	"",		NONE,			NULL}
347 };
348 
349 static const struct token t_set[] = {
350 	{ NOTOKEN,	"",			NONE,	NULL},
351 	{ KEYWORD,	"community",		NONE,	t_community},
352 	{ KEYWORD,	"ext-community",	NONE,	t_extcommunity},
353 	{ KEYWORD,	"large-community",	NONE,	t_largecommunity},
354 	{ KEYWORD,	"localpref",		NONE,	t_localpref},
355 	{ KEYWORD,	"med",			NONE,	t_med},
356 	{ KEYWORD,	"metric",		NONE,	t_med},
357 	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
358 	{ KEYWORD,	"pftable",		NONE,	t_pftable},
359 	{ KEYWORD,	"prepend-neighbor",	NONE,	t_prepnbr},
360 	{ KEYWORD,	"prepend-self",		NONE,	t_prepself},
361 	{ KEYWORD,	"weight",		NONE,	t_weight},
362 	{ KEYWORD,	"add",			NETWORK_BULK_ADD,	NULL},
363 	{ KEYWORD,	"delete",		NETWORK_BULK_REMOVE,	NULL},
364 	{ ENDTOKEN,	"",			NONE,	NULL}
365 };
366 
367 static const struct token t_community[] = {
368 	{ COMMUNITY,	"",			NONE,	t_set},
369 	{ ENDTOKEN,	"",			NONE,	NULL}
370 };
371 
372 static const struct token t_extcommunity[] = {
373 	{ EXTCOM_SUBTYPE,	"bdc",		NONE,	t_ext_subtype},
374 	{ EXTCOM_SUBTYPE,	"defgw",	NONE,	t_ext_subtype},
375 	{ EXTCOM_SUBTYPE,	"esi-lab",	NONE,	t_ext_subtype},
376 	{ EXTCOM_SUBTYPE,	"esi-rt",	NONE,	t_ext_subtype},
377 	{ EXTCOM_SUBTYPE,	"l2vid",	NONE,	t_ext_subtype},
378 	{ EXTCOM_SUBTYPE,	"mac-mob",	NONE,	t_ext_subtype},
379 	{ EXTCOM_SUBTYPE,	"odi",		NONE,	t_ext_subtype},
380 	{ EXTCOM_SUBTYPE,	"ort",		NONE,	t_ext_subtype},
381 	{ EXTCOM_SUBTYPE,	"ori",		NONE,	t_ext_subtype},
382 	{ EXTCOM_SUBTYPE,	"ovs",		NONE,	t_ext_subtype},
383 	{ EXTCOM_SUBTYPE,	"rt",		NONE,	t_ext_subtype},
384 	{ EXTCOM_SUBTYPE,	"soo",		NONE,	t_ext_subtype},
385 	{ EXTCOM_SUBTYPE,	"srcas",	NONE,	t_ext_subtype},
386 	{ EXTCOM_SUBTYPE,	"vrfri",	NONE,	t_ext_subtype},
387 	{ ENDTOKEN,	"",	NONE,	NULL}
388 };
389 
390 static const struct token t_ext_subtype[] = {
391 	{ EXTCOMMUNITY,	"",	NONE,	t_set},
392 	{ ENDTOKEN,	"",	NONE,	NULL}
393 };
394 
395 static const struct token t_largecommunity[] = {
396 	{ LARGE_COMMUNITY,	"",		NONE,	t_set},
397 	{ ENDTOKEN,	"",			NONE,	NULL}
398 };
399 
400 static const struct token t_localpref[] = {
401 	{ LOCALPREF,	"",			NONE,	t_set},
402 	{ ENDTOKEN,	"",			NONE,	NULL}
403 };
404 
405 static const struct token t_med[] = {
406 	{ MED,		"",			NONE,	t_set},
407 	{ ENDTOKEN,	"",			NONE,	NULL}
408 };
409 
410 static const struct token t_nexthop[] = {
411 	{ NEXTHOP,	"",			NONE,	t_set},
412 	{ ENDTOKEN,	"",			NONE,	NULL}
413 };
414 
415 static const struct token t_pftable[] = {
416 	{ PFTABLE,	"",			NONE,	t_set},
417 	{ ENDTOKEN,	"",			NONE,	NULL}
418 };
419 
420 static const struct token t_prepnbr[] = {
421 	{ PREPNBR,	"",			NONE,	t_set},
422 	{ ENDTOKEN,	"",			NONE,	NULL}
423 };
424 
425 static const struct token t_prepself[] = {
426 	{ PREPSELF,	"",			NONE,	t_set},
427 	{ ENDTOKEN,	"",			NONE,	NULL}
428 };
429 
430 static const struct token t_weight[] = {
431 	{ WEIGHT,	"",			NONE,	t_set},
432 	{ ENDTOKEN,	"",			NONE,	NULL}
433 };
434 
435 static const struct token t_irrfilter[] = {
436 	{ GETOPT,	"",	GETOPT_IRRFILTER,	t_irrfilter},
437 	{ ASNUM,	"",	NONE,			t_irrfilter_opts},
438 	{ ENDTOKEN,	"",	NONE,			NULL}
439 };
440 
441 static const struct token t_irrfilter_opts[] = {
442 	{ NOTOKEN,	"",		NONE,			NULL},
443 	{ FLAG,		"importonly",	F_IMPORTONLY,		t_irrfilter_opts},
444 	{ ENDTOKEN,	"",		NONE,			NULL}
445 };
446 
447 static const struct token t_log[] = {
448 	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
449 	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
450 	{ ENDTOKEN,	"",		NONE,		NULL}
451 };
452 
453 static const struct token t_fib_table[] = {
454 	{ RTABLE,	"",			NONE,	t_fib},
455 	{ ENDTOKEN,	"",			NONE,	NULL}
456 };
457 
458 static const struct token t_show_fib_table[] = {
459 	{ RTABLE,	"",			NONE,	t_show_fib},
460 	{ ENDTOKEN,	"",			NONE,	NULL}
461 };
462 
463 static struct parse_result	res;
464 
465 const struct token	*match_token(int *argc, char **argv[],
466 			    const struct token []);
467 void			 show_valid_args(const struct token []);
468 int			 parse_addr(const char *, struct bgpd_addr *);
469 int			 parse_asnum(const char *, size_t, u_int32_t *);
470 int			 parse_number(const char *, struct parse_result *,
471 			     enum token_type);
472 int			 getcommunity(const char *);
473 int			 parse_community(const char *, struct parse_result *);
474 int			 parsesubtype(const char *, u_int8_t *, u_int8_t *);
475 int			 parseextvalue(const char *, u_int32_t *);
476 u_int			 parseextcommunity(const char *, struct parse_result *);
477 u_int			 getlargecommunity(const char *);
478 int			 parse_largecommunity(const char *,
479 			     struct parse_result *);
480 int			 parse_nexthop(const char *, struct parse_result *);
481 int			 bgpctl_getopt(int *, char **[], int);
482 
483 struct parse_result *
484 parse(int argc, char *argv[])
485 {
486 	const struct token	*table = t_main;
487 	const struct token	*match;
488 
489 	bzero(&res, sizeof(res));
490 	res.community.as = COMMUNITY_UNSET;
491 	res.community.type = COMMUNITY_UNSET;
492 	res.large_community.as = COMMUNITY_UNSET;
493 	res.large_community.ld1 = COMMUNITY_UNSET;
494 	res.large_community.ld2 = COMMUNITY_UNSET;
495 	TAILQ_INIT(&res.set);
496 	if ((res.irr_outdir = getcwd(NULL, 0)) == NULL) {
497 		fprintf(stderr, "getcwd failed: %s\n", strerror(errno));
498 		return (NULL);
499 	}
500 
501 	while (argc >= 0) {
502 		if ((match = match_token(&argc, &argv, table)) == NULL) {
503 			fprintf(stderr, "valid commands/args:\n");
504 			show_valid_args(table);
505 			return (NULL);
506 		}
507 
508 		argc--;
509 		argv++;
510 
511 		if (match->type == NOTOKEN || match->next == NULL)
512 			break;
513 
514 		table = match->next;
515 	}
516 
517 	if (argc > 0) {
518 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
519 		return (NULL);
520 	}
521 
522 	return (&res);
523 }
524 
525 const struct token *
526 match_token(int *argc, char **argv[], const struct token table[])
527 {
528 	u_int			 i, match;
529 	const struct token	*t = NULL;
530 	struct filter_set	*fs;
531 	const char		*word = *argv[0];
532 	size_t			wordlen = 0;
533 
534 	match = 0;
535 	if (word != NULL)
536 		wordlen = strlen(word);
537 	for (i = 0; table[i].type != ENDTOKEN; i++) {
538 		switch (table[i].type) {
539 		case NOTOKEN:
540 			if (word == NULL || wordlen == 0) {
541 				match++;
542 				t = &table[i];
543 			}
544 			break;
545 		case KEYWORD:
546 			if (word != NULL && strncmp(word, table[i].keyword,
547 			    wordlen) == 0) {
548 				match++;
549 				t = &table[i];
550 				if (t->value)
551 					res.action = t->value;
552 			}
553 			break;
554 		case FLAG:
555 			if (word != NULL && strncmp(word, table[i].keyword,
556 			    wordlen) == 0) {
557 				match++;
558 				t = &table[i];
559 				res.flags |= t->value;
560 			}
561 			break;
562 		case FAMILY:
563 			if (word == NULL)
564 				break;
565 			if (!strcmp(word, "inet") ||
566 			    !strcasecmp(word, "IPv4")) {
567 				match++;
568 				t = &table[i];
569 				res.aid = AID_INET;
570 			}
571 			if (!strcmp(word, "inet6") ||
572 			    !strcasecmp(word, "IPv6")) {
573 				match++;
574 				t = &table[i];
575 				res.aid = AID_INET6;
576 			}
577 			if (!strcasecmp(word, "VPNv4")) {
578 				match++;
579 				t = &table[i];
580 				res.aid = AID_VPN_IPv4;
581 			}
582 			break;
583 		case ADDRESS:
584 			if (parse_addr(word, &res.addr)) {
585 				match++;
586 				t = &table[i];
587 				if (t->value)
588 					res.action = t->value;
589 			}
590 			break;
591 		case PEERADDRESS:
592 			if (parse_addr(word, &res.peeraddr)) {
593 				match++;
594 				t = &table[i];
595 				if (t->value)
596 					res.action = t->value;
597 			}
598 			break;
599 		case PREFIX:
600 			if (parse_prefix(word, wordlen, &res.addr, &res.prefixlen)) {
601 				match++;
602 				t = &table[i];
603 				if (t->value)
604 					res.action = t->value;
605 			}
606 			break;
607 		case ASTYPE:
608 			if (word != NULL && strncmp(word, table[i].keyword,
609 			    wordlen) == 0) {
610 				match++;
611 				t = &table[i];
612 				res.as.type = t->value;
613 			}
614 			break;
615 		case ASNUM:
616 			if (parse_asnum(word, wordlen, &res.as.as)) {
617 				match++;
618 				t = &table[i];
619 			}
620 			break;
621 		case PEERDESC:
622 			if (!match && word != NULL && wordlen > 0) {
623 				if (strlcpy(res.peerdesc, word,
624 				    sizeof(res.peerdesc)) >=
625 				    sizeof(res.peerdesc))
626 					errx(1, "neighbor description too "
627 					    "long");
628 				match++;
629 				t = &table[i];
630 			}
631 			break;
632 		case RIBNAME:
633 			if (!match && word != NULL && wordlen > 0) {
634 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
635 				    sizeof(res.rib))
636 					errx(1, "rib name too long");
637 				match++;
638 				t = &table[i];
639 			}
640 			break;
641 		case SHUTDOWN_COMMUNICATION:
642 			if (!match && word != NULL && wordlen > 0) {
643 				if (strlcpy(res.shutcomm, word,
644 				    sizeof(res.shutcomm)) >=
645 				    sizeof(res.shutcomm))
646 					errx(1, "shutdown reason too long");
647 				match++;
648 				t = &table[i];
649 			}
650 			break;
651 		case COMMUNITY:
652 			if (word != NULL && wordlen > 0 &&
653 			    parse_community(word, &res)) {
654 				match++;
655 				t = &table[i];
656 			}
657 			break;
658 		case EXTCOM_SUBTYPE:
659 			if (word != NULL && strncmp(word, table[i].keyword,
660 			    wordlen) == 0) {
661 				if (parsesubtype(word, &res.extcommunity.type,
662 				    &res.extcommunity.subtype) == 0)
663 					errx(1, "Bad ext-community unknown "
664 					    "type");
665 				match++;
666 				t = &table[i];
667 			}
668 			break;
669 		case EXTCOMMUNITY:
670 			if (word != NULL && wordlen > 0 &&
671 			    parseextcommunity(word, &res)) {
672 				match++;
673 				t = &table[i];
674 			}
675 			break;
676 		case LARGE_COMMUNITY:
677 			if (word != NULL && wordlen > 0 &&
678 			    parse_largecommunity(word, &res)) {
679 				match++;
680 				t = &table[i];
681 			}
682 			break;
683 		case LOCALPREF:
684 		case MED:
685 		case PREPNBR:
686 		case PREPSELF:
687 		case WEIGHT:
688 		case RTABLE:
689 			if (word != NULL && wordlen > 0 &&
690 			    parse_number(word, &res, table[i].type)) {
691 				match++;
692 				t = &table[i];
693 			}
694 			break;
695 		case NEXTHOP:
696 			if (word != NULL && wordlen > 0 &&
697 			    parse_nexthop(word, &res)) {
698 				match++;
699 				t = &table[i];
700 			}
701 			break;
702 		case PFTABLE:
703 			if (word != NULL && wordlen > 0) {
704 				if ((fs = calloc(1,
705 				    sizeof(struct filter_set))) == NULL)
706 					err(1, NULL);
707 				if (strlcpy(fs->action.pftable, word,
708 				    sizeof(fs->action.pftable)) >=
709 				    sizeof(fs->action.pftable))
710 					errx(1, "pftable name too long");
711 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
712 				match++;
713 				t = &table[i];
714 			}
715 			break;
716 		case GETOPT:
717 			if (bgpctl_getopt(argc, argv, table[i].value)) {
718 				match++;
719 				t = &table[i];
720 			}
721 			break;
722 		case FILENAME:
723 			if (word != NULL && wordlen > 0) {
724 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
725 					/*
726 					 * ignore error if path has no / and
727 					 * does not exist. In hope to print
728 					 * usage.
729 					 */
730 					if (errno == ENOENT &&
731 					    !strchr(word, '/'))
732 						break;
733 					err(1, "mrt open(%s)", word);
734 				}
735 				match++;
736 				t = &table[i];
737 			}
738 			break;
739 		case BULK:
740 			match++;
741 			t = &table[i];
742 			break;
743 		case ENDTOKEN:
744 			break;
745 		}
746 	}
747 
748 	if (match != 1) {
749 		if (word == NULL)
750 			fprintf(stderr, "missing argument:\n");
751 		else if (match > 1)
752 			fprintf(stderr, "ambiguous argument: %s\n", word);
753 		else if (match < 1)
754 			fprintf(stderr, "unknown argument: %s\n", word);
755 		return (NULL);
756 	}
757 
758 	return (t);
759 }
760 
761 void
762 show_valid_args(const struct token table[])
763 {
764 	int	i;
765 
766 	for (i = 0; table[i].type != ENDTOKEN; i++) {
767 		switch (table[i].type) {
768 		case NOTOKEN:
769 			fprintf(stderr, "  <cr>\n");
770 			break;
771 		case KEYWORD:
772 		case FLAG:
773 		case ASTYPE:
774 		case EXTCOM_SUBTYPE:
775 			fprintf(stderr, "  %s\n", table[i].keyword);
776 			break;
777 		case ADDRESS:
778 		case PEERADDRESS:
779 			fprintf(stderr, "  <address>\n");
780 			break;
781 		case PREFIX:
782 			fprintf(stderr, "  <address>[/<len>]\n");
783 			break;
784 		case ASNUM:
785 			fprintf(stderr, "  <asnum>\n");
786 			break;
787 		case PEERDESC:
788 			fprintf(stderr, "  <neighbor description>\n");
789 			break;
790 		case RIBNAME:
791 			fprintf(stderr, "  <rib name>\n");
792 			break;
793 		case SHUTDOWN_COMMUNICATION:
794 			fprintf(stderr, "  <shutdown reason>\n");
795 			break;
796 		case COMMUNITY:
797 			fprintf(stderr, "  <community>\n");
798 			break;
799 		case EXTCOMMUNITY:
800 			fprintf(stderr, "  <extended-community>\n");
801 			break;
802 		case LARGE_COMMUNITY:
803 			fprintf(stderr, "  <large-community>\n");
804 			break;
805 		case LOCALPREF:
806 		case MED:
807 		case PREPNBR:
808 		case PREPSELF:
809 		case WEIGHT:
810 			fprintf(stderr, "  <number>\n");
811 			break;
812 		case RTABLE:
813 			fprintf(stderr, "  <rtableid>\n");
814 			break;
815 		case NEXTHOP:
816 			fprintf(stderr, "  <address>\n");
817 			break;
818 		case PFTABLE:
819 			fprintf(stderr, "  <pftable>\n");
820 			break;
821 		case FAMILY:
822 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | VPNv4 ]\n");
823 			break;
824 		case GETOPT:
825 			fprintf(stderr, "  <options>\n");
826 			break;
827 		case FILENAME:
828 			fprintf(stderr, "  <filename>\n");
829 			break;
830 		case BULK:
831 		case ENDTOKEN:
832 			break;
833 		}
834 	}
835 }
836 
837 int
838 parse_addr(const char *word, struct bgpd_addr *addr)
839 {
840 	struct in_addr	ina;
841 	struct addrinfo	hints, *r;
842 
843 	if (word == NULL)
844 		return (0);
845 
846 	bzero(addr, sizeof(struct bgpd_addr));
847 	bzero(&ina, sizeof(ina));
848 
849 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
850 		addr->aid = AID_INET;
851 		addr->v4 = ina;
852 		return (1);
853 	}
854 
855 	bzero(&hints, sizeof(hints));
856 	hints.ai_family = AF_INET6;
857 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
858 	hints.ai_flags = AI_NUMERICHOST;
859 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
860 		sa2addr(r->ai_addr, addr);
861 		freeaddrinfo(r);
862 		return (1);
863 	}
864 
865 	return (0);
866 }
867 
868 int
869 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr, u_int8_t *prefixlen)
870 {
871 	char		*p, *ps;
872 	const char	*errstr;
873 	int		 mask = -1;
874 
875 	if (word == NULL)
876 		return (0);
877 
878 	bzero(addr, sizeof(struct bgpd_addr));
879 
880 	if ((p = strrchr(word, '/')) != NULL) {
881 		size_t plen = strlen(p);
882 		mask = strtonum(p + 1, 0, 128, &errstr);
883 		if (errstr)
884 			errx(1, "netmask %s", errstr);
885 
886 		if ((ps = malloc(wordlen - plen + 1)) == NULL)
887 			err(1, "parse_prefix: malloc");
888 		strlcpy(ps, word, wordlen - plen + 1);
889 
890 		if (parse_addr(ps, addr) == 0) {
891 			free(ps);
892 			return (0);
893 		}
894 
895 		free(ps);
896 	} else
897 		if (parse_addr(word, addr) == 0)
898 			return (0);
899 
900 	switch (addr->aid) {
901 	case AID_INET:
902 		if (mask == -1)
903 			mask = 32;
904 		if (mask > 32)
905 			errx(1, "invalid netmask: too large");
906 		addr->v4.s_addr = addr->v4.s_addr & htonl(prefixlen2mask(mask));
907 		break;
908 	case AID_INET6:
909 		if (mask == -1)
910 			mask = 128;
911 		inet6applymask(&addr->v6, &addr->v6, mask);
912 		break;
913 	default:
914 		return (0);
915 	}
916 
917 	*prefixlen = mask;
918 	return (1);
919 }
920 
921 int
922 parse_asnum(const char *word, size_t wordlen, u_int32_t *asnum)
923 {
924 	const char	*errstr;
925 	char		*dot;
926 	u_int32_t	 uval, uvalh = 0;
927 
928 	if (word == NULL)
929 		return (0);
930 
931 	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
932 		return (0);
933 
934 	if ((dot = strchr(word,'.')) != NULL) {
935 		*dot++ = '\0';
936 		uvalh = strtonum(word, 0, USHRT_MAX, &errstr);
937 		if (errstr)
938 			errx(1, "AS number is %s: %s", errstr, word);
939 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
940 		if (errstr)
941 			errx(1, "AS number is %s: %s", errstr, word);
942 	} else {
943 		uval = strtonum(word, 0, UINT_MAX, &errstr);
944 		if (errstr)
945 			errx(1, "AS number is %s: %s", errstr, word);
946 	}
947 
948 	*asnum = uval | (uvalh << 16);
949 	return (1);
950 }
951 
952 int
953 parse_number(const char *word, struct parse_result *r, enum token_type type)
954 {
955 	struct filter_set	*fs;
956 	const char		*errstr;
957 	u_int			 uval;
958 
959 	if (word == NULL)
960 		return (0);
961 
962 	uval = strtonum(word, 0, UINT_MAX, &errstr);
963 	if (errstr)
964 		errx(1, "number is %s: %s", errstr, word);
965 
966 	/* number was parseable */
967 	if (type == RTABLE) {
968 		r->rtableid = uval;
969 		return (1);
970 	}
971 
972 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
973 		err(1, NULL);
974 	switch (type) {
975 	case LOCALPREF:
976 		fs->type = ACTION_SET_LOCALPREF;
977 		fs->action.metric = uval;
978 		break;
979 	case MED:
980 		fs->type = ACTION_SET_MED;
981 		fs->action.metric = uval;
982 		break;
983 	case PREPNBR:
984 		if (uval > 128) {
985 			free(fs);
986 			return (0);
987 		}
988 		fs->type = ACTION_SET_PREPEND_PEER;
989 		fs->action.prepend = uval;
990 		break;
991 	case PREPSELF:
992 		if (uval > 128) {
993 			free(fs);
994 			return (0);
995 		}
996 		fs->type = ACTION_SET_PREPEND_SELF;
997 		fs->action.prepend = uval;
998 		break;
999 	case WEIGHT:
1000 		fs->type = ACTION_SET_WEIGHT;
1001 		fs->action.metric = uval;
1002 		break;
1003 	default:
1004 		errx(1, "king bula sez bad things happen");
1005 	}
1006 
1007 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1008 	return (1);
1009 }
1010 
1011 int
1012 getcommunity(const char *s)
1013 {
1014 	const char	*errstr;
1015 	u_int16_t	 uval;
1016 
1017 	if (strcmp(s, "*") == 0)
1018 		return (COMMUNITY_ANY);
1019 
1020 	uval = strtonum(s, 0, USHRT_MAX, &errstr);
1021 	if (errstr)
1022 		errx(1, "Community is %s: %s", errstr, s);
1023 
1024 	return (uval);
1025 }
1026 
1027 int
1028 parse_community(const char *word, struct parse_result *r)
1029 {
1030 	struct filter_set	*fs;
1031 	char			*p;
1032 	int			 as, type;
1033 
1034 	/* Well-known communities */
1035 	if (strcasecmp(word, "GRACEFUL_SHUTDOWN") == 0) {
1036 		as = COMMUNITY_WELLKNOWN;
1037 		type = COMMUNITY_GRACEFUL_SHUTDOWN;
1038 		goto done;
1039 	} else if (strcasecmp(word, "NO_EXPORT") == 0) {
1040 		as = COMMUNITY_WELLKNOWN;
1041 		type = COMMUNITY_NO_EXPORT;
1042 		goto done;
1043 	} else if (strcasecmp(word, "NO_ADVERTISE") == 0) {
1044 		as = COMMUNITY_WELLKNOWN;
1045 		type = COMMUNITY_NO_ADVERTISE;
1046 		goto done;
1047 	} else if (strcasecmp(word, "NO_EXPORT_SUBCONFED") == 0) {
1048 		as = COMMUNITY_WELLKNOWN;
1049 		type = COMMUNITY_NO_EXPSUBCONFED;
1050 		goto done;
1051 	} else if (strcasecmp(word, "NO_PEER") == 0) {
1052 		as = COMMUNITY_WELLKNOWN;
1053 		type = COMMUNITY_NO_PEER;
1054 		goto done;
1055 	} else if (strcasecmp(word, "BLACKHOLE") == 0) {
1056 		as = COMMUNITY_WELLKNOWN;
1057 		type = COMMUNITY_BLACKHOLE;
1058 		goto done;
1059 	}
1060 
1061 	if ((p = strchr(word, ':')) == NULL) {
1062 		fprintf(stderr, "Bad community syntax\n");
1063 		return (0);
1064 	}
1065 	*p++ = 0;
1066 
1067 	as = getcommunity(word);
1068 	type = getcommunity(p);
1069 
1070 done:
1071 	if (as == 0) {
1072 		fprintf(stderr, "Invalid community\n");
1073 		return (0);
1074 	}
1075 	if (as == COMMUNITY_WELLKNOWN)
1076 		switch (type) {
1077 		case COMMUNITY_GRACEFUL_SHUTDOWN:
1078 		case COMMUNITY_NO_EXPORT:
1079 		case COMMUNITY_NO_ADVERTISE:
1080 		case COMMUNITY_NO_EXPSUBCONFED:
1081 		case COMMUNITY_BLACKHOLE:
1082 			/* valid */
1083 			break;
1084 		}
1085 
1086 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1087 		err(1, NULL);
1088 	fs->type = ACTION_SET_COMMUNITY;
1089 	fs->action.community.as = as;
1090 	fs->action.community.type = type;
1091 
1092 	r->community.as = as;
1093 	r->community.type = type;
1094 
1095 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1096 	return (1);
1097 }
1098 
1099 int
1100 parsesubtype(const char *name, u_int8_t *type, u_int8_t *subtype)
1101 {
1102 	const struct ext_comm_pairs *cp;
1103 	int found = 0;
1104 
1105 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1106 		if (strcmp(name, cp->subname) == 0) {
1107 			if (found == 0) {
1108 				*type = cp->type;
1109 				*subtype = cp->subtype;
1110 			}
1111 			found++;
1112 		}
1113 	}
1114 	if (found > 1)
1115 		*type = -1;
1116 	return (found);
1117 }
1118 
1119 int
1120 parseextvalue(const char *s, u_int32_t *v)
1121 {
1122 	const char	*errstr;
1123 	char		*p;
1124 	struct in_addr	 ip;
1125 	u_int32_t	 uvalh = 0, uval;
1126 
1127 	if ((p = strchr(s, '.')) == NULL) {
1128 		/* AS_PLAIN number (4 or 2 byte) */
1129 		uval = strtonum(s, 0, UINT_MAX, &errstr);
1130 		if (errstr) {
1131 			fprintf(stderr, "Bad ext-community: %s is %s\n", s,
1132 			    errstr);
1133 			return (-1);
1134 		}
1135 		*v = uval;
1136 		if (uval <= USHRT_MAX)
1137 			return (EXT_COMMUNITY_TRANS_TWO_AS);
1138 		else
1139 			return (EXT_COMMUNITY_TRANS_FOUR_AS);
1140 	} else if (strchr(p + 1, '.') == NULL) {
1141 		/* AS_DOT number (4-byte) */
1142 		*p++ = '\0';
1143 		uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
1144 		if (errstr) {
1145 			fprintf(stderr, "Bad ext-community: %s is %s\n", s,
1146 			    errstr);
1147 			return (-1);
1148 		}
1149 		uval = strtonum(p, 0, USHRT_MAX, &errstr);
1150 		if (errstr) {
1151 			fprintf(stderr, "Bad ext-community: %s is %s\n", p,
1152 			    errstr);
1153 			return (-1);
1154 		}
1155 		*v = uval | (uvalh << 16);
1156 		return (EXT_COMMUNITY_TRANS_FOUR_AS);
1157 	} else {
1158 		/* more than one dot -> IP address */
1159 		if (inet_aton(s, &ip) == 0) {
1160 			fprintf(stderr, "Bad ext-community: %s not parseable\n",
1161 			    s);
1162 			return (-1);
1163 		}
1164 		*v = ip.s_addr;
1165 		return (EXT_COMMUNITY_TRANS_IPV4);
1166 	}
1167 	return (-1);
1168 }
1169 
1170 u_int
1171 parseextcommunity(const char *word, struct parse_result *r)
1172 {
1173 	struct filter_set		*fs;
1174 	const struct ext_comm_pairs	*cp;
1175 	const char			*errstr;
1176 	u_int64_t			 ullval;
1177 	u_int32_t			 uval;
1178 	char				*p, *ep;
1179 	int				 type;
1180 
1181 	type = r->extcommunity.type;
1182 
1183 	switch (type) {
1184 	case 0xff:
1185 		if ((p = strchr(word, ':')) == NULL) {
1186 			fprintf(stderr, "Bad ext-community: %s\n", word);
1187 			return (0);
1188 		}
1189 		*p++ = '\0';
1190 		if ((type = parseextvalue(word, &uval)) == -1)
1191 			return (0);
1192 		switch (type) {
1193 		case EXT_COMMUNITY_TRANS_TWO_AS:
1194 			ullval = strtonum(p, 0, UINT_MAX, &errstr);
1195 			break;
1196 		case EXT_COMMUNITY_TRANS_IPV4:
1197 		case EXT_COMMUNITY_TRANS_FOUR_AS:
1198 			ullval = strtonum(p, 0, USHRT_MAX, &errstr);
1199 			break;
1200 		default:
1201 			fprintf(stderr, "parseextcommunity: unexpected "
1202 			    "result\n");
1203 			return (0);
1204 		}
1205 		if (errstr) {
1206 			fprintf(stderr, "Bad ext-community: %s is %s\n", p,
1207 			    errstr);
1208 			return (0);
1209 		}
1210 		switch (type) {
1211 		case EXT_COMMUNITY_TRANS_TWO_AS:
1212 			r->extcommunity.data.ext_as.as = uval;
1213 			r->extcommunity.data.ext_as.val = ullval;
1214 			break;
1215 		case EXT_COMMUNITY_TRANS_IPV4:
1216 			r->extcommunity.data.ext_ip.addr.s_addr = uval;
1217 			r->extcommunity.data.ext_ip.val = ullval;
1218 			break;
1219 		case EXT_COMMUNITY_TRANS_FOUR_AS:
1220 			r->extcommunity.data.ext_as4.as4 = uval;
1221 			r->extcommunity.data.ext_as4.val = ullval;
1222 			break;
1223 		}
1224 		break;
1225 	case EXT_COMMUNITY_TRANS_OPAQUE:
1226 	case EXT_COMMUNITY_TRANS_EVPN:
1227 		errno = 0;
1228 		ullval = strtoull(word, &ep, 0);
1229 		if (word[0] == '\0' || *ep != '\0') {
1230 			fprintf(stderr, "Bad ext-community: bad value\n");
1231 			return (0);
1232 		}
1233 		if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
1234 			fprintf(stderr, "Bad ext-community: too big\n");
1235 			return (0);
1236 		}
1237 		r->extcommunity.data.ext_opaq = ullval;
1238 		break;
1239 	case EXT_COMMUNITY_NON_TRANS_OPAQUE:
1240 		if (strcmp(word, "valid") == 0)
1241 			r->extcommunity.data.ext_opaq = EXT_COMMUNITY_OVS_VALID;
1242 		else if (strcmp(word, "invalid") == 0)
1243 			r->extcommunity.data.ext_opaq =
1244 			    EXT_COMMUNITY_OVS_INVALID;
1245 		else if (strcmp(word, "not-found") == 0)
1246 			r->extcommunity.data.ext_opaq =
1247 			    EXT_COMMUNITY_OVS_NOTFOUND;
1248 		else {
1249 			fprintf(stderr, "Bad ext-community value: %s\n", word);
1250 			return (0);
1251 		}
1252 		break;
1253 	}
1254 	r->extcommunity.type = type;
1255 
1256 	/* verify type/subtype combo */
1257 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1258 		if (cp->type == r->extcommunity.type &&
1259 		    cp->subtype == r->extcommunity.subtype) {
1260 			r->extcommunity.flags |= EXT_COMMUNITY_FLAG_VALID;
1261 			if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1262 				err(1, NULL);
1263 
1264 			fs->type = ACTION_SET_EXT_COMMUNITY;
1265 			memcpy(&fs->action.ext_community, &r->extcommunity,
1266 			    sizeof(struct filter_extcommunity));
1267 
1268 			TAILQ_INSERT_TAIL(&r->set, fs, entry);
1269 			return (1);
1270 		}
1271 	}
1272 
1273 	fprintf(stderr, "Bad ext-community: bad format for type\n");
1274 	return (0);
1275 }
1276 
1277 u_int
1278 getlargecommunity(const char *s)
1279 {
1280 	const char	*errstr;
1281 	u_int32_t	 uval;
1282 
1283 	if (strcmp(s, "*") == 0)
1284 		return (COMMUNITY_ANY);
1285 
1286 	uval = strtonum(s, 0, UINT_MAX, &errstr);
1287 	if (errstr)
1288 		errx(1, "Large Community is %s: %s", errstr, s);
1289 
1290 	return (uval);
1291 }
1292 
1293 int
1294 parse_largecommunity(const char *word, struct parse_result *r)
1295 {
1296 	struct filter_set *fs;
1297 	char		*p, *po = strdup(word);
1298 	char		*array[3] = { NULL, NULL, NULL };
1299 	char		*val;
1300 	int64_t		 as, ld1, ld2;
1301 	int		 i = 0;
1302 
1303 	p = po;
1304 	while ((p != NULL) && (i < 3)) {
1305 		val = strsep(&p, ":");
1306 		array[i++] = val;
1307 	}
1308 
1309 	if ((p != NULL) || !(array[0] && array[1] && array[2]))
1310 		errx(1, "Invalid Large-Community syntax");
1311 
1312 	as   = getlargecommunity(array[0]);
1313 	ld1  = getlargecommunity(array[1]);
1314 	ld2  = getlargecommunity(array[2]);
1315 
1316 	free(po);
1317 
1318 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1319 		err(1, NULL);
1320 	fs->type = ACTION_SET_LARGE_COMMUNITY;
1321 	fs->action.large_community.as = as;
1322 	fs->action.large_community.ld1 = ld1;
1323 	fs->action.large_community.ld2 = ld2;
1324 
1325 	r->large_community.as = as;
1326 	r->large_community.ld1 = ld1;
1327 	r->large_community.ld2 = ld2;
1328 
1329 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1330 	return (1);
1331 }
1332 
1333 int
1334 parse_nexthop(const char *word, struct parse_result *r)
1335 {
1336 	struct filter_set	*fs;
1337 
1338 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1339 		err(1, NULL);
1340 
1341 	if (strcmp(word, "blackhole") == 0)
1342 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
1343 	else if (strcmp(word, "reject") == 0)
1344 		fs->type = ACTION_SET_NEXTHOP_REJECT;
1345 	else if (strcmp(word, "no-modify") == 0)
1346 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
1347 	else if (parse_addr(word, &fs->action.nexthop)) {
1348 		fs->type = ACTION_SET_NEXTHOP;
1349 	} else {
1350 		free(fs);
1351 		return (0);
1352 	}
1353 
1354 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1355 	return (1);
1356 }
1357 
1358 int
1359 bgpctl_getopt(int *argc, char **argv[], int type)
1360 {
1361 	int	  ch;
1362 
1363 	optind = optreset = 1;
1364 	while ((ch = getopt((*argc) + 1, (*argv) - 1, "46o:")) != -1) {
1365 		switch (ch) {
1366 		case '4':
1367 			res.flags = (res.flags | F_IPV4) & ~F_IPV6;
1368 			break;
1369 		case '6':
1370 			res.flags = (res.flags | F_IPV6) & ~F_IPV4;
1371 			break;
1372 		case 'o':
1373 			res.irr_outdir = optarg;
1374 			break;
1375 		default:
1376 			usage();
1377 			/* NOTREACHED */
1378 		}
1379 	}
1380 
1381 	if (optind > 1) {
1382 		(*argc) -= (optind - 1);
1383 		(*argv) += (optind - 1);
1384 
1385 		/* need to move one backwards as calling code moves forward */
1386 		(*argc)++;
1387 		(*argv)--;
1388 		return (1);
1389 	} else
1390 		return (0);
1391 }
1392