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