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