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