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