xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: parser.c,v 1.133 2023/05/09 13:26:27 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 	ENDTOKEN,
38 	NOTOKEN,
39 	ANYTOKEN,
40 	KEYWORD,
41 	ADDRESS,
42 	PEERADDRESS,
43 	FLAG,
44 	ASNUM,
45 	ASTYPE,
46 	PREFIX,
47 	PEERDESC,
48 	GROUPDESC,
49 	RIBNAME,
50 	COMMUNICATION,
51 	COMMUNITY,
52 	EXTCOMMUNITY,
53 	LRGCOMMUNITY,
54 	LOCALPREF,
55 	MED,
56 	NEXTHOP,
57 	PFTABLE,
58 	PREPNBR,
59 	PREPSELF,
60 	WEIGHT,
61 	RD,
62 	FAMILY,
63 	RTABLE,
64 	FILENAME,
65 	PATHID,
66 	FLOW_PROTO,
67 	FLOW_SRC,
68 	FLOW_DST,
69 	FLOW_SRCPORT,
70 	FLOW_DSTPORT,
71 	FLOW_ICMPTYPE,
72 	FLOW_ICMPCODE,
73 	FLOW_LENGTH,
74 	FLOW_DSCP,
75 	FLOW_FLAGS,
76 	FLOW_FRAGS,
77 };
78 
79 struct token {
80 	enum token_type		 type;
81 	const char		*keyword;
82 	int			 value;
83 	const struct token	*next;
84 };
85 
86 static const struct token *prevtable;
87 
88 static const struct token t_main[];
89 static const struct token t_show[];
90 static const struct token t_show_summary[];
91 static const struct token t_show_fib[];
92 static const struct token t_show_rib[];
93 static const struct token t_show_avs[];
94 static const struct token t_show_ovs[];
95 static const struct token t_show_mrt[];
96 static const struct token t_show_mrt_file[];
97 static const struct token t_show_rib_neigh[];
98 static const struct token t_show_mrt_neigh[];
99 static const struct token t_show_rib_rib[];
100 static const struct token t_show_neighbor[];
101 static const struct token t_show_neighbor_modifiers[];
102 static const struct token t_fib[];
103 static const struct token t_neighbor[];
104 static const struct token t_neighbor_modifiers[];
105 static const struct token t_show_rib_as[];
106 static const struct token t_show_mrt_as[];
107 static const struct token t_show_prefix[];
108 static const struct token t_show_ip[];
109 static const struct token t_network[];
110 static const struct token t_flowspec[];
111 static const struct token t_flowfamily[];
112 static const struct token t_flowrule[];
113 static const struct token t_flowsrc[];
114 static const struct token t_flowdst[];
115 static const struct token t_flowsrcport[];
116 static const struct token t_flowdstport[];
117 static const struct token t_flowicmp[];
118 static const struct token t_bulk[];
119 static const struct token t_network_show[];
120 static const struct token t_prefix[];
121 static const struct token t_set[];
122 static const struct token t_nexthop[];
123 static const struct token t_pftable[];
124 static const struct token t_log[];
125 static const struct token t_communication[];
126 
127 static const struct token t_main[] = {
128 	{ KEYWORD,	"fib",		FIB,		t_fib},
129 	{ KEYWORD,	"flowspec",	NONE,		t_flowspec},
130 	{ KEYWORD,	"log",		NONE,		t_log},
131 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
132 	{ KEYWORD,	"network",	NONE,		t_network},
133 	{ KEYWORD,	"reload",	RELOAD,		t_communication},
134 	{ KEYWORD,	"show",		SHOW,		t_show},
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,	"flowspec",	FLOWSPEC_SHOW,	t_network_show},
142 	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
143 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
144 	{ KEYWORD,	"metrics",	SHOW_METRICS,	NULL},
145 	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
146 	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
147 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
148 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
149 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
150 	{ KEYWORD,	"rtr",		SHOW_RTR,	NULL},
151 	{ KEYWORD,	"sets",		SHOW_SET,	NULL},
152 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
153 	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
154 	{ ENDTOKEN,	"",		NONE,		NULL}
155 };
156 
157 static const struct token t_show_summary[] = {
158 	{ NOTOKEN,	"",		NONE,			NULL},
159 	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
160 	{ ENDTOKEN,	"",		NONE,			NULL}
161 };
162 
163 static const struct token t_show_fib[] = {
164 	{ NOTOKEN,	"",		NONE,		NULL},
165 	{ FLAG,		"bgp",		F_BGPD,		t_show_fib},
166 	{ FLAG,		"connected",	F_CONNECTED,	t_show_fib},
167 	{ FLAG,		"nexthop",	F_NEXTHOP,	t_show_fib},
168 	{ FLAG,		"static",	F_STATIC,	t_show_fib},
169 	{ RTABLE,	"table",	NONE,		t_show_fib},
170 	{ FAMILY,	"",		NONE,		t_show_fib},
171 	{ ADDRESS,	"",		NONE,		NULL},
172 	{ ENDTOKEN,	"",		NONE,		NULL}
173 };
174 
175 static const struct token t_show_rib[] = {
176 	{ NOTOKEN,	"",		NONE,		NULL},
177 	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
178 	{ KEYWORD,	"avs",		NONE,		t_show_avs},
179 	{ FLAG,		"best",		F_CTL_BEST,	t_show_rib},
180 	{ COMMUNITY,	"community",	NONE,		t_show_rib},
181 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_rib},
182 	{ FLAG,		"disqualified",	F_CTL_INELIGIBLE, t_show_rib},
183 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
184 	{ FLAG,		"error",	F_CTL_INVALID,	t_show_rib},
185 	{ EXTCOMMUNITY,	"ext-community", NONE,		t_show_rib},
186 	{ FLAG,		"in",		F_CTL_ADJ_IN,	t_show_rib},
187 	{ LRGCOMMUNITY,	"large-community", NONE,	t_show_rib},
188 	{ FLAG,		"leaked",	F_CTL_LEAKED,	t_show_rib},
189 	{ KEYWORD,	"memory",	SHOW_RIB_MEM,	NULL},
190 	{ KEYWORD,	"neighbor",	NONE,		t_show_rib_neigh},
191 	{ FLAG,		"out",		F_CTL_ADJ_OUT,	t_show_rib},
192 	{ KEYWORD,	"ovs",		NONE,		t_show_ovs},
193 	{ PATHID,	"path-id",	NONE,		t_show_rib},
194 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
195 	{ FLAG,		"selected",	F_CTL_BEST,	t_show_rib},
196 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
197 	{ FLAG,		"ssv",		F_CTL_SSV,	t_show_rib},
198 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
199 	{ KEYWORD,	"table",	NONE,		t_show_rib_rib},
200 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
201 	{ FAMILY,	"",		NONE,		t_show_rib},
202 	{ PREFIX,	"",		NONE,		t_show_prefix},
203 	{ ENDTOKEN,	"",		NONE,		NULL}
204 };
205 
206 static const struct token t_show_avs[] = {
207 	{ FLAG,		"invalid",	F_CTL_AVS_INVALID,	t_show_rib},
208 	{ FLAG,		"unknown",	F_CTL_AVS_UNKNOWN,	t_show_rib},
209 	{ FLAG,		"valid"	,	F_CTL_AVS_VALID,	t_show_rib},
210 	{ ENDTOKEN,	"",		NONE,		NULL}
211 };
212 
213 static const struct token t_show_ovs[] = {
214 	{ FLAG,		"invalid",	F_CTL_OVS_INVALID,	t_show_rib},
215 	{ FLAG,		"not-found",	F_CTL_OVS_NOTFOUND,	t_show_rib},
216 	{ FLAG,		"valid"	,	F_CTL_OVS_VALID,	t_show_rib},
217 	{ ENDTOKEN,	"",		NONE,		NULL}
218 };
219 
220 static const struct token t_show_mrt[] = {
221 	{ NOTOKEN,	"",		NONE,		NULL},
222 	{ ASTYPE,	"as",		AS_ALL,		t_show_mrt_as},
223 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_mrt},
224 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_mrt},
225 	{ KEYWORD,	"file",		NONE,		t_show_mrt_file},
226 	{ KEYWORD,	"neighbor",	NONE,		t_show_mrt_neigh},
227 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_mrt_as},
228 	{ FLAG,		"peers",	F_CTL_NEIGHBORS,t_show_mrt},
229 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_mrt_as},
230 	{ FLAG,		"ssv",		F_CTL_SSV,	t_show_mrt},
231 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_mrt_as},
232 	{ FAMILY,	"",		NONE,		t_show_mrt},
233 	{ PREFIX,	"",		NONE,		t_show_prefix},
234 	{ ENDTOKEN,	"",		NONE,		NULL}
235 };
236 
237 static const struct token t_show_mrt_file[] = {
238 	{ FILENAME,	"",		NONE,		t_show_mrt},
239 	{ ENDTOKEN,	"",		NONE,	NULL}
240 };
241 
242 static const struct token t_show_rib_neigh_group[] = {
243 	{ GROUPDESC,	"",		NONE,	t_show_rib},
244 	{ ENDTOKEN,	"",		NONE,	NULL}
245 };
246 
247 static const struct token t_show_rib_neigh[] = {
248 	{ KEYWORD,	"group",	NONE,	t_show_rib_neigh_group},
249 	{ PEERADDRESS,	"",		NONE,	t_show_rib},
250 	{ PEERDESC,	"",		NONE,	t_show_rib},
251 	{ ENDTOKEN,	"",		NONE,	NULL}
252 };
253 
254 static const struct token t_show_mrt_neigh[] = {
255 	{ PEERADDRESS,	"",		NONE,	t_show_mrt},
256 	{ ENDTOKEN,	"",		NONE,	NULL}
257 };
258 
259 static const struct token t_show_rib_rib[] = {
260 	{ RIBNAME,	"",		NONE,	t_show_rib},
261 	{ ENDTOKEN,	"",		NONE,	NULL}
262 };
263 
264 static const struct token t_show_neighbor_modifiers[] = {
265 	{ NOTOKEN,	"",		NONE,			NULL},
266 	{ KEYWORD,	"messages",	SHOW_NEIGHBOR,		NULL},
267 	{ KEYWORD,	"terse",	SHOW_NEIGHBOR_TERSE,	NULL},
268 	{ KEYWORD,	"timers",	SHOW_NEIGHBOR_TIMERS,	NULL},
269 	{ ENDTOKEN,	"",		NONE,			NULL}
270 };
271 
272 static const struct token t_show_neighbor_group[] = {
273 	{ GROUPDESC,	"",		NONE,	t_show_neighbor_modifiers},
274 	{ ENDTOKEN,	"",		NONE,	NULL}
275 };
276 
277 static const struct token t_show_neighbor[] = {
278 	{ NOTOKEN,	"",		NONE,	NULL},
279 	{ KEYWORD,	"group",	NONE,	t_show_neighbor_group},
280 	{ PEERADDRESS,	"",		NONE,	t_show_neighbor_modifiers},
281 	{ PEERDESC,	"",		NONE,	t_show_neighbor_modifiers},
282 	{ ENDTOKEN,	"",		NONE,	NULL}
283 };
284 
285 static const struct token t_fib[] = {
286 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
287 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
288 	{ RTABLE,	"table",	NONE,		t_fib},
289 	{ ENDTOKEN,	"",		NONE,		NULL}
290 };
291 
292 static const struct token t_neighbor_group[] = {
293 	{ GROUPDESC,	"",		NONE,		t_neighbor_modifiers},
294 	{ ENDTOKEN,	"",		NONE,		NULL}
295 };
296 
297 static const struct token t_neighbor[] = {
298 	{ KEYWORD,	"group",	NONE,		t_neighbor_group},
299 	{ PEERADDRESS,	"",		NONE,		t_neighbor_modifiers},
300 	{ PEERDESC,	"",		NONE,		t_neighbor_modifiers},
301 	{ ENDTOKEN,	"",		NONE,		NULL}
302 };
303 
304 static const struct token t_communication[] = {
305 	{ NOTOKEN,	"",		NONE,		NULL},
306 	{ COMMUNICATION, "",		NONE,		NULL},
307 	{ ENDTOKEN,	"",		NONE,		NULL}
308 };
309 
310 static const struct token t_neighbor_modifiers[] = {
311 	{ KEYWORD,	"clear",	NEIGHBOR_CLEAR,	t_communication},
312 	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
313 	{ KEYWORD,	"down",		NEIGHBOR_DOWN,	t_communication},
314 	{ KEYWORD,	"refresh",	NEIGHBOR_RREFRESH,	NULL},
315 	{ KEYWORD,	"up",		NEIGHBOR_UP,		NULL},
316 	{ ENDTOKEN,	"",		NONE,			NULL}
317 };
318 
319 static const struct token t_show_rib_as[] = {
320 	{ ASNUM,	"",		NONE,		t_show_rib},
321 	{ ENDTOKEN,	"",		NONE,		NULL}
322 };
323 
324 static const struct token t_show_mrt_as[] = {
325 	{ ASNUM,	"",		NONE,		t_show_mrt},
326 	{ ENDTOKEN,	"",		NONE,		NULL}
327 };
328 
329 static const struct token t_show_prefix[] = {
330 	{ FLAG,		"all",		F_LONGER,	t_show_rib},
331 	{ FLAG,		"longer-prefixes", F_LONGER,	t_show_rib},
332 	{ FLAG,		"or-longer", 	F_LONGER,	t_show_rib},
333 	{ FLAG,		"or-shorter", 	F_SHORTER,	t_show_rib},
334 	{ ANYTOKEN,	"",		NONE,		t_show_rib},
335 	{ ENDTOKEN,	"",		NONE,		NULL}
336 };
337 
338 static const struct token t_show_ip[] = {
339 	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
340 	{ ENDTOKEN,	"",		NONE,		NULL}
341 };
342 
343 static const struct token t_network[] = {
344 	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
345 	{ KEYWORD,	"bulk",		NONE,		t_bulk},
346 	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
347 	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
348 	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
349 	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
350 	{ ENDTOKEN,	"",		NONE,		NULL}
351 };
352 
353 static const struct token t_flowspec[] = {
354 	{ KEYWORD,	"add",		FLOWSPEC_ADD,	t_flowfamily},
355 	{ KEYWORD,	"delete",	FLOWSPEC_REMOVE,t_flowfamily},
356 	{ KEYWORD,	"flush",	FLOWSPEC_FLUSH,	NULL},
357 	{ KEYWORD,	"show",		FLOWSPEC_SHOW,	t_network_show},
358 	{ ENDTOKEN,	"",		NONE,		NULL}
359 };
360 
361 static const struct token t_flowfamily[] = {
362 	{ FAMILY,	"",		NONE,		t_flowrule},
363 	{ ENDTOKEN,	"",		NONE,		NULL}
364 };
365 
366 static const struct token t_flowrule[] = {
367 	{ NOTOKEN,	"",		NONE,		NULL},
368 	{ FLOW_FLAGS,	"flags",	NONE,		t_flowrule},
369 	{ FLOW_FRAGS,	"fragment",	NONE,		t_flowrule},
370 	{ KEYWORD,	"from",		NONE,		t_flowsrc},
371 	{ FLOW_ICMPTYPE,"icmp-type",	NONE,		t_flowicmp},
372 	{ FLOW_LENGTH,	"length",	NONE,		t_flowrule},
373 	{ FLOW_PROTO,	"proto",	NONE,		t_flowrule},
374 	{ KEYWORD,	"set",		NONE,		t_set},
375 	{ KEYWORD,	"to",		NONE,		t_flowdst},
376 	{ FLOW_DSCP,	"dscp",		NONE,		t_flowrule},
377 	{ ENDTOKEN,	"",		NONE,		NULL}
378 };
379 
380 static const struct token t_flowsrc[] = {
381 	{ KEYWORD,	"any",		NONE,		t_flowsrcport},
382 	{ FLOW_SRC,	"",		NONE,		t_flowsrcport},
383 	{ ENDTOKEN,	"",		NONE,		NULL}
384 };
385 
386 static const struct token t_flowdst[] = {
387 	{ KEYWORD,	"any",		NONE,		t_flowdstport},
388 	{ FLOW_DST,	"",		NONE,		t_flowdstport},
389 	{ ENDTOKEN,	"",		NONE,		NULL}
390 };
391 
392 static const struct token t_flowsrcport[] = {
393 	{ FLOW_SRCPORT,	"port",		NONE,		t_flowrule},
394 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
395 	{ ENDTOKEN,	"",		NONE,		NULL}
396 };
397 
398 static const struct token t_flowdstport[] = {
399 	{ FLOW_DSTPORT,	"port",		NONE,		t_flowrule},
400 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
401 	{ ENDTOKEN,	"",		NONE,		NULL}
402 };
403 
404 static const struct token t_flowicmp[] = {
405 	{ FLOW_ICMPCODE,"code",		NONE,		t_flowrule},
406 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
407 	{ ENDTOKEN,	"",		NONE,		NULL}
408 };
409 
410 static const struct token t_bulk[] = {
411 	{ KEYWORD,	"add",		NETWORK_BULK_ADD,	t_set},
412 	{ KEYWORD,	"delete",	NETWORK_BULK_REMOVE,	NULL},
413 	{ ENDTOKEN,	"",		NONE,			NULL}
414 };
415 
416 static const struct token t_prefix[] = {
417 	{ PREFIX,	"",		NONE,		t_set},
418 	{ ENDTOKEN,	"",		NONE,		NULL}
419 };
420 
421 static const struct token t_network_show[] = {
422 	{ NOTOKEN,	"",		NONE,		NULL},
423 	{ FAMILY,	"",		NONE,		NULL},
424 	{ ENDTOKEN,	"",		NONE,		NULL}
425 };
426 
427 static const struct token t_rd[] = {
428 	{ RD,		"",			NONE,	t_set},
429 	{ ENDTOKEN,	"",			NONE,	NULL}
430 };
431 
432 static const struct token t_set[] = {
433 	{ NOTOKEN,	"",			NONE,	NULL},
434 	{ COMMUNITY,	"community",		NONE,	t_set},
435 	{ EXTCOMMUNITY,	"ext-community",	NONE,	t_set},
436 	{ LRGCOMMUNITY,	"large-community",	NONE,	t_set},
437 	{ LOCALPREF,	"localpref",		NONE,	t_set},
438 	{ MED,		"med",			NONE,	t_set},
439 	{ MED,		"metric",		NONE,	t_set},
440 	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
441 	{ KEYWORD,	"pftable",		NONE,	t_pftable},
442 	{ PREPNBR,	"prepend-neighbor",	NONE,	t_set},
443 	{ PREPSELF,	"prepend-self",		NONE,	t_set},
444 	{ KEYWORD,	"rd",			NONE,	t_rd},
445 	{ WEIGHT,	"weight",		NONE,	t_set},
446 	{ ENDTOKEN,	"",			NONE,	NULL}
447 };
448 
449 static const struct token t_nexthop[] = {
450 	{ NEXTHOP,	"",			NONE,	t_set},
451 	{ ENDTOKEN,	"",			NONE,	NULL}
452 };
453 
454 static const struct token t_pftable[] = {
455 	{ PFTABLE,	"",			NONE,	t_set},
456 	{ ENDTOKEN,	"",			NONE,	NULL}
457 };
458 
459 static const struct token t_log[] = {
460 	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
461 	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
462 	{ ENDTOKEN,	"",		NONE,		NULL}
463 };
464 
465 static struct parse_result	res;
466 
467 const struct token	*match_token(int, char *[], const struct token [],
468 			    int *);
469 void			 show_valid_args(const struct token []);
470 
471 int	parse_addr(const char *, struct bgpd_addr *);
472 int	parse_asnum(const char *, size_t, uint32_t *);
473 int	parse_number(const char *, struct parse_result *, enum token_type);
474 void	parsecommunity(struct community *c, char *s);
475 void	parselargecommunity(struct community *c, char *s);
476 void	parseextcommunity(struct community *c, const char *t, char *s);
477 int	parse_nexthop(const char *, struct parse_result *);
478 int	parse_flow_numop(int, char *[], struct parse_result *, enum token_type);
479 
480 struct parse_result *
481 parse(int argc, char *argv[])
482 {
483 	const struct token	*table = t_main;
484 	const struct token	*match;
485 	int			 used;
486 
487 	memset(&res, 0, sizeof(res));
488 	res.rtableid = getrtable();
489 	TAILQ_INIT(&res.set);
490 
491 	while (argc >= 0) {
492 		if ((match = match_token(argc, argv, table, &used)) == NULL) {
493 			fprintf(stderr, "valid commands/args:\n");
494 			show_valid_args(table);
495 			return (NULL);
496 		}
497 		if (match->type == ANYTOKEN) {
498 			if (prevtable == NULL)
499 				prevtable = table;
500 			table = match->next;
501 			continue;
502 		}
503 
504 		argc -= used;
505 		argv += used;
506 
507 		if (match->type == NOTOKEN || match->next == NULL)
508 			break;
509 		table = match->next;
510 	}
511 
512 	if (argc > 0) {
513 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
514 		return (NULL);
515 	}
516 
517 	return (&res);
518 }
519 
520 const struct token *
521 match_token(int argc, char *argv[], const struct token table[], int *argsused)
522 {
523 	u_int			 i, match;
524 	const struct token	*t = NULL;
525 	struct filter_set	*fs;
526 	const char		*word = argv[0];
527 	size_t			 wordlen = 0;
528 
529 	*argsused = 1;
530 	match = 0;
531 	if (word != NULL)
532 		wordlen = strlen(word);
533 	for (i = 0; table[i].type != ENDTOKEN; i++) {
534 		switch (table[i].type) {
535 		case NOTOKEN:
536 			if (word == NULL || wordlen == 0) {
537 				match++;
538 				t = &table[i];
539 			}
540 			break;
541 		case ANYTOKEN:
542 			/* match anything if nothing else matched before */
543 			if (match == 0) {
544 				match++;
545 				t = &table[i];
546 			}
547 			break;
548 		case KEYWORD:
549 			if (word != NULL && strncmp(word, table[i].keyword,
550 			    wordlen) == 0) {
551 				match++;
552 				t = &table[i];
553 				if (t->value)
554 					res.action = t->value;
555 			}
556 			break;
557 		case FLAG:
558 			if (word != NULL && strncmp(word, table[i].keyword,
559 			    wordlen) == 0) {
560 				match++;
561 				t = &table[i];
562 				res.flags |= t->value;
563 			}
564 			break;
565 		case FAMILY:
566 			if (word == NULL)
567 				break;
568 			if (!strcmp(word, "inet") ||
569 			    !strcasecmp(word, "IPv4")) {
570 				match++;
571 				t = &table[i];
572 				res.aid = AID_INET;
573 			}
574 			if (!strcmp(word, "inet6") ||
575 			    !strcasecmp(word, "IPv6")) {
576 				match++;
577 				t = &table[i];
578 				res.aid = AID_INET6;
579 			}
580 			if (!strcasecmp(word, "VPNv4")) {
581 				match++;
582 				t = &table[i];
583 				res.aid = AID_VPN_IPv4;
584 			}
585 			if (!strcasecmp(word, "VPNv6")) {
586 				match++;
587 				t = &table[i];
588 				res.aid = AID_VPN_IPv6;
589 			}
590 			break;
591 		case ADDRESS:
592 			if (parse_addr(word, &res.addr)) {
593 				match++;
594 				t = &table[i];
595 			}
596 			break;
597 		case PEERADDRESS:
598 			if (parse_addr(word, &res.peeraddr)) {
599 				match++;
600 				t = &table[i];
601 			}
602 			break;
603 		case FLOW_SRC:
604 			if (parse_prefix(word, wordlen, &res.flow.src,
605 			    &res.flow.srclen)) {
606 				match++;
607 				t = &table[i];
608 				if (res.aid != res.flow.src.aid)
609 					errx(1, "wrong address family in "
610 					    "flowspec rule");
611 			}
612 			break;
613 		case FLOW_DST:
614 			if (parse_prefix(word, wordlen, &res.flow.dst,
615 			    &res.flow.dstlen)) {
616 				match++;
617 				t = &table[i];
618 				if (res.aid != res.flow.dst.aid)
619 					errx(1, "wrong address family in "
620 					    "flowspec rule");
621 			}
622 			break;
623 		case PREFIX:
624 			if (parse_prefix(word, wordlen, &res.addr,
625 			    &res.prefixlen)) {
626 				match++;
627 				t = &table[i];
628 			}
629 			break;
630 		case ASTYPE:
631 			if (word != NULL && strncmp(word, table[i].keyword,
632 			    wordlen) == 0) {
633 				match++;
634 				t = &table[i];
635 				res.as.type = t->value;
636 			}
637 			break;
638 		case ASNUM:
639 			if (parse_asnum(word, wordlen, &res.as.as_min)) {
640 				res.as.as_max = res.as.as_min;
641 				match++;
642 				t = &table[i];
643 			}
644 			break;
645 		case GROUPDESC:
646 			res.is_group = 1;
647 			/* FALLTHROUGH */
648 		case PEERDESC:
649 			if (!match && word != NULL && wordlen > 0) {
650 				if (strlcpy(res.peerdesc, word,
651 				    sizeof(res.peerdesc)) >=
652 				    sizeof(res.peerdesc))
653 					errx(1, "neighbor description too "
654 					    "long");
655 				match++;
656 				t = &table[i];
657 			}
658 			break;
659 		case RIBNAME:
660 			if (!match && word != NULL && wordlen > 0) {
661 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
662 				    sizeof(res.rib))
663 					errx(1, "rib name too long");
664 				match++;
665 				t = &table[i];
666 			}
667 			break;
668 		case COMMUNICATION:
669 			if (!match && word != NULL && wordlen > 0) {
670 				if (strlcpy(res.reason, word,
671 				    sizeof(res.reason)) >=
672 				    sizeof(res.reason))
673 					errx(1, "shutdown reason too long");
674 				match++;
675 				t = &table[i];
676 			}
677 			break;
678 		case COMMUNITY:
679 			if (word != NULL && strncmp(word, table[i].keyword,
680 			    wordlen) == 0 && argc > 1) {
681 				parsecommunity(&res.community, argv[1]);
682 				*argsused += 1;
683 
684 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
685 					err(1, NULL);
686 				fs->type = ACTION_SET_COMMUNITY;
687 				fs->action.community = res.community;
688 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
689 
690 				match++;
691 				t = &table[i];
692 			}
693 			break;
694 		case LRGCOMMUNITY:
695 			if (word != NULL && strncmp(word, table[i].keyword,
696 			    wordlen) == 0 && argc > 1) {
697 				parselargecommunity(&res.community, argv[1]);
698 				*argsused += 1;
699 
700 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
701 					err(1, NULL);
702 				fs->type = ACTION_SET_COMMUNITY;
703 				fs->action.community = res.community;
704 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
705 
706 				match++;
707 				t = &table[i];
708 			}
709 			break;
710 		case EXTCOMMUNITY:
711 			if (word != NULL && strncmp(word, table[i].keyword,
712 			    wordlen) == 0 && argc > 2) {
713 				parseextcommunity(&res.community,
714 				    argv[1], argv[2]);
715 				*argsused += 2;
716 
717 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
718 					err(1, NULL);
719 				fs->type = ACTION_SET_COMMUNITY;
720 				fs->action.community = res.community;
721 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
722 
723 				match++;
724 				t = &table[i];
725 			}
726 			break;
727 		case RD:
728 			if (word != NULL && wordlen > 0) {
729 				char *p = strdup(word);
730 				struct community ext = { 0 };
731 				uint64_t rd;
732 
733 				if (p == NULL)
734 					err(1, NULL);
735 				parseextcommunity(&ext, "rt", p);
736 				free(p);
737 
738 				switch (ext.data3 >> 8) {
739 				case EXT_COMMUNITY_TRANS_TWO_AS:
740 					rd = (0ULL << 48);
741 					rd |= ((uint64_t)ext.data1 & 0xffff)
742 					    << 32;
743 					rd |= (uint64_t)ext.data2;
744 					break;
745 				case EXT_COMMUNITY_TRANS_IPV4:
746 					rd = (1ULL << 48);
747 					rd |= (uint64_t)ext.data1 << 16;
748 					rd |= (uint64_t)ext.data2 & 0xffff;
749 					break;
750 				case EXT_COMMUNITY_TRANS_FOUR_AS:
751 					rd = (2ULL << 48);
752 					rd |= (uint64_t)ext.data1 << 16;
753 					rd |= (uint64_t)ext.data2 & 0xffff;
754 					break;
755 				default:
756 					errx(1, "bad encoding of rd");
757 				}
758 				res.rd = htobe64(rd);
759 				match++;
760 				t = &table[i];
761 			}
762 			break;
763 		case LOCALPREF:
764 		case MED:
765 		case PREPNBR:
766 		case PREPSELF:
767 		case WEIGHT:
768 		case RTABLE:
769 		case PATHID:
770 			if (word != NULL && strncmp(word, table[i].keyword,
771 			    wordlen) == 0 && argc > 1 &&
772 			    parse_number(argv[1], &res, table[i].type)) {
773 				*argsused += 1;
774 				match++;
775 				t = &table[i];
776 			}
777 			break;
778 		case NEXTHOP:
779 			if (word != NULL && wordlen > 0 &&
780 			    parse_nexthop(word, &res)) {
781 				match++;
782 				t = &table[i];
783 			}
784 			break;
785 		case PFTABLE:
786 			if (word != NULL && wordlen > 0) {
787 				if ((fs = calloc(1,
788 				    sizeof(struct filter_set))) == NULL)
789 					err(1, NULL);
790 				if (strlcpy(fs->action.pftable, word,
791 				    sizeof(fs->action.pftable)) >=
792 				    sizeof(fs->action.pftable))
793 					errx(1, "pftable name too long");
794 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
795 				match++;
796 				t = &table[i];
797 			}
798 			break;
799 		case FILENAME:
800 			if (word != NULL && wordlen > 0) {
801 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
802 					/*
803 					 * ignore error if path has no / and
804 					 * does not exist. In hope to print
805 					 * usage.
806 					 */
807 					if (errno == ENOENT &&
808 					    !strchr(word, '/'))
809 						break;
810 					err(1, "mrt open(%s)", word);
811 				}
812 				match++;
813 				t = &table[i];
814 			}
815 			break;
816 		case FLOW_SRCPORT:
817 		case FLOW_DSTPORT:
818 		case FLOW_PROTO:
819 		case FLOW_ICMPTYPE:
820 		case FLOW_ICMPCODE:
821 		case FLOW_LENGTH:
822 		case FLOW_DSCP:
823 			if (word != NULL && strncmp(word, table[i].keyword,
824 			    wordlen) == 0 && argc > 1) {
825 				*argsused += parse_flow_numop(argc, argv, &res,
826 				    table[i].type);
827 
828 				match++;
829 				t = &table[i];
830 			}
831 			break;
832 		case FLOW_FLAGS:
833 		case FLOW_FRAGS:
834 			if (word != NULL && strncmp(word, table[i].keyword,
835 			    wordlen) == 0) {
836 				errx(1, "%s not yet implemented", word);
837 			}
838 			break;
839 		case ENDTOKEN:
840 			break;
841 		}
842 	}
843 
844 	if (match != 1) {
845 		if (word == NULL)
846 			fprintf(stderr, "missing argument:\n");
847 		else if (match > 1)
848 			fprintf(stderr, "ambiguous argument: %s\n", word);
849 		else if (match < 1)
850 			fprintf(stderr, "unknown argument: %s\n", word);
851 		return (NULL);
852 	}
853 
854 	return (t);
855 }
856 
857 void
858 show_valid_args(const struct token table[])
859 {
860 	int	i;
861 
862 	if (prevtable != NULL) {
863 		const struct token *t = prevtable;
864 		prevtable = NULL;
865 		show_valid_args(t);
866 		fprintf(stderr, "or any of\n");
867 	}
868 
869 	for (i = 0; table[i].type != ENDTOKEN; i++) {
870 		switch (table[i].type) {
871 		case NOTOKEN:
872 			fprintf(stderr, "  <cr>\n");
873 			break;
874 		case ANYTOKEN:
875 			break;
876 		case KEYWORD:
877 		case FLAG:
878 		case ASTYPE:
879 			fprintf(stderr, "  %s\n", table[i].keyword);
880 			break;
881 		case ADDRESS:
882 		case PEERADDRESS:
883 			fprintf(stderr, "  <address>\n");
884 			break;
885 		case PREFIX:
886 		case FLOW_SRC:
887 		case FLOW_DST:
888 			fprintf(stderr, "  <address>[/<len>]\n");
889 			break;
890 		case ASNUM:
891 			fprintf(stderr, "  <asnum>\n");
892 			break;
893 		case GROUPDESC:
894 		case PEERDESC:
895 			fprintf(stderr, "  <neighbor description>\n");
896 			break;
897 		case RIBNAME:
898 			fprintf(stderr, "  <rib name>\n");
899 			break;
900 		case COMMUNICATION:
901 			fprintf(stderr, "  <reason>\n");
902 			break;
903 		case COMMUNITY:
904 			fprintf(stderr, "  %s <community>\n",
905 			    table[i].keyword);
906 			break;
907 		case LRGCOMMUNITY:
908 			fprintf(stderr, "  %s <large-community>\n",
909 			    table[i].keyword);
910 			break;
911 		case EXTCOMMUNITY:
912 			fprintf(stderr, "  %s <extended-community>\n",
913 			    table[i].keyword);
914 			break;
915 		case RD:
916 			fprintf(stderr, "  <route-distinguisher>\n");
917 			break;
918 		case LOCALPREF:
919 		case MED:
920 		case PREPNBR:
921 		case PREPSELF:
922 		case WEIGHT:
923 		case RTABLE:
924 		case PATHID:
925 			fprintf(stderr, "  %s <number>\n", table[i].keyword);
926 			break;
927 		case NEXTHOP:
928 			fprintf(stderr, "  <address>\n");
929 			break;
930 		case PFTABLE:
931 			fprintf(stderr, "  <pftable>\n");
932 			break;
933 		case FAMILY:
934 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | "
935 			    "VPNv4 | VPNv6 ]\n");
936 			break;
937 		case FILENAME:
938 			fprintf(stderr, "  <filename>\n");
939 			break;
940 		case FLOW_SRCPORT:
941 		case FLOW_DSTPORT:
942 		case FLOW_PROTO:
943 		case FLOW_ICMPTYPE:
944 		case FLOW_ICMPCODE:
945 		case FLOW_LENGTH:
946 		case FLOW_DSCP:
947 			fprintf(stderr, "  %s <numberspec>\n",
948 			    table[i].keyword);
949 			break;
950 		case FLOW_FLAGS:
951 		case FLOW_FRAGS:
952 			fprintf(stderr, "  %s <flagspec>\n",
953 			    table[i].keyword);
954 			break;
955 		case ENDTOKEN:
956 			break;
957 		}
958 	}
959 }
960 
961 int
962 parse_addr(const char *word, struct bgpd_addr *addr)
963 {
964 	struct in_addr	ina;
965 	struct addrinfo	hints, *r;
966 
967 	if (word == NULL)
968 		return (0);
969 
970 	memset(&ina, 0, sizeof(ina));
971 
972 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
973 		memset(addr, 0, sizeof(*addr));
974 		addr->aid = AID_INET;
975 		addr->v4 = ina;
976 		return (1);
977 	}
978 
979 	memset(&hints, 0, sizeof(hints));
980 	hints.ai_family = AF_INET6;
981 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
982 	hints.ai_flags = AI_NUMERICHOST;
983 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
984 		sa2addr(r->ai_addr, addr, NULL);
985 		freeaddrinfo(r);
986 		return (1);
987 	}
988 
989 	return (0);
990 }
991 
992 int
993 parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr,
994     uint8_t *prefixlen)
995 {
996 	struct bgpd_addr tmp;
997 	char		*p, *ps;
998 	const char	*errstr;
999 	int		 mask = -1;
1000 
1001 	if (word == NULL)
1002 		return (0);
1003 
1004 	memset(&tmp, 0, sizeof(tmp));
1005 
1006 	if ((p = strrchr(word, '/')) != NULL) {
1007 		size_t plen = strlen(p);
1008 		mask = strtonum(p + 1, 0, 128, &errstr);
1009 		if (errstr)
1010 			errx(1, "netmask %s", errstr);
1011 
1012 		if ((ps = malloc(wordlen - plen + 1)) == NULL)
1013 			err(1, "parse_prefix: malloc");
1014 		strlcpy(ps, word, wordlen - plen + 1);
1015 
1016 		if (parse_addr(ps, &tmp) == 0) {
1017 			free(ps);
1018 			return (0);
1019 		}
1020 
1021 		free(ps);
1022 	} else
1023 		if (parse_addr(word, &tmp) == 0)
1024 			return (0);
1025 
1026 	switch (tmp.aid) {
1027 	case AID_INET:
1028 		if (mask == -1)
1029 			mask = 32;
1030 		if (mask > 32)
1031 			errx(1, "invalid netmask: too large");
1032 		break;
1033 	case AID_INET6:
1034 		if (mask == -1)
1035 			mask = 128;
1036 		break;
1037 	default:
1038 		return (0);
1039 	}
1040 
1041 	applymask(addr, &tmp, mask);
1042 	*prefixlen = mask;
1043 	return (1);
1044 }
1045 
1046 int
1047 parse_asnum(const char *word, size_t wordlen, uint32_t *asnum)
1048 {
1049 	const char	*errstr;
1050 	char		*dot, *parseword;
1051 	uint32_t	 uval, uvalh = 0;
1052 
1053 	if (word == NULL)
1054 		return (0);
1055 
1056 	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
1057 		return (0);
1058 
1059 	parseword = strdup(word);
1060 	if ((dot = strchr(parseword, '.')) != NULL) {
1061 		*dot++ = '\0';
1062 		uvalh = strtonum(parseword, 0, USHRT_MAX, &errstr);
1063 		if (errstr)
1064 			errx(1, "AS number is %s: %s", errstr, word);
1065 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
1066 		if (errstr)
1067 			errx(1, "AS number is %s: %s", errstr, word);
1068 	} else {
1069 		uval = strtonum(parseword, 0, UINT_MAX, &errstr);
1070 		if (errstr)
1071 			errx(1, "AS number is %s: %s", errstr, word);
1072 	}
1073 
1074 	free(parseword);
1075 	*asnum = uval | (uvalh << 16);
1076 	return (1);
1077 }
1078 
1079 int
1080 parse_number(const char *word, struct parse_result *r, enum token_type type)
1081 {
1082 	struct filter_set	*fs;
1083 	const char		*errstr;
1084 	u_int			 uval;
1085 
1086 	if (word == NULL)
1087 		return (0);
1088 
1089 	uval = strtonum(word, 0, UINT_MAX, &errstr);
1090 	if (errstr)
1091 		errx(1, "number is %s: %s", errstr, word);
1092 
1093 	/* number was parseable */
1094 	switch (type) {
1095 	case RTABLE:
1096 		r->rtableid = uval;
1097 		return (1);
1098 	case PATHID:
1099 		r->pathid = uval;
1100 		r->flags |= F_CTL_HAS_PATHID;
1101 		return (1);
1102 	default:
1103 		break;
1104 	}
1105 
1106 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1107 		err(1, NULL);
1108 	switch (type) {
1109 	case LOCALPREF:
1110 		fs->type = ACTION_SET_LOCALPREF;
1111 		fs->action.metric = uval;
1112 		break;
1113 	case MED:
1114 		fs->type = ACTION_SET_MED;
1115 		fs->action.metric = uval;
1116 		break;
1117 	case PREPNBR:
1118 		if (uval > 128) {
1119 			free(fs);
1120 			return (0);
1121 		}
1122 		fs->type = ACTION_SET_PREPEND_PEER;
1123 		fs->action.prepend = uval;
1124 		break;
1125 	case PREPSELF:
1126 		if (uval > 128) {
1127 			free(fs);
1128 			return (0);
1129 		}
1130 		fs->type = ACTION_SET_PREPEND_SELF;
1131 		fs->action.prepend = uval;
1132 		break;
1133 	case WEIGHT:
1134 		fs->type = ACTION_SET_WEIGHT;
1135 		fs->action.metric = uval;
1136 		break;
1137 	default:
1138 		errx(1, "king bula sez bad things happen");
1139 	}
1140 
1141 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1142 	return (1);
1143 }
1144 
1145 static void
1146 getcommunity(char *s, int large, uint32_t *val, uint32_t *flag)
1147 {
1148 	long long	 max = USHRT_MAX;
1149 	const char	*errstr;
1150 
1151 	*flag = 0;
1152 	*val = 0;
1153 	if (strcmp(s, "*") == 0) {
1154 		*flag = COMMUNITY_ANY;
1155 		return;
1156 	} else if (strcmp(s, "neighbor-as") == 0) {
1157 		*flag = COMMUNITY_NEIGHBOR_AS;
1158 		return;
1159 	} else if (strcmp(s, "local-as") == 0) {
1160 		*flag =  COMMUNITY_LOCAL_AS;
1161 		return;
1162 	}
1163 	if (large)
1164 		max = UINT_MAX;
1165 	*val = strtonum(s, 0, max, &errstr);
1166 	if (errstr)
1167 		errx(1, "Community %s is %s (max: %llu)", s, errstr, max);
1168 }
1169 
1170 static void
1171 setcommunity(struct community *c, uint32_t as, uint32_t data,
1172     uint32_t asflag, uint32_t dataflag)
1173 {
1174 	c->flags = COMMUNITY_TYPE_BASIC;
1175 	c->flags |= asflag << 8;
1176 	c->flags |= dataflag << 16;
1177 	c->data1 = as;
1178 	c->data2 = data;
1179 	c->data3 = 0;
1180 }
1181 
1182 void
1183 parsecommunity(struct community *c, char *s)
1184 {
1185 	char *p;
1186 	uint32_t as, data, asflag, dataflag;
1187 
1188 	/* Well-known communities */
1189 	if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) {
1190 		setcommunity(c, COMMUNITY_WELLKNOWN,
1191 		    COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0);
1192 		return;
1193 	} else if (strcasecmp(s, "NO_EXPORT") == 0) {
1194 		setcommunity(c, COMMUNITY_WELLKNOWN,
1195 		    COMMUNITY_NO_EXPORT, 0, 0);
1196 		return;
1197 	} else if (strcasecmp(s, "NO_ADVERTISE") == 0) {
1198 		setcommunity(c, COMMUNITY_WELLKNOWN,
1199 		    COMMUNITY_NO_ADVERTISE, 0, 0);
1200 		return;
1201 	} else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) {
1202 		setcommunity(c, COMMUNITY_WELLKNOWN,
1203 		    COMMUNITY_NO_EXPSUBCONFED, 0, 0);
1204 		return;
1205 	} else if (strcasecmp(s, "NO_PEER") == 0) {
1206 		setcommunity(c, COMMUNITY_WELLKNOWN,
1207 		    COMMUNITY_NO_PEER, 0, 0);
1208 		return;
1209 	} else if (strcasecmp(s, "BLACKHOLE") == 0) {
1210 		setcommunity(c, COMMUNITY_WELLKNOWN,
1211 		    COMMUNITY_BLACKHOLE, 0, 0);
1212 		return;
1213 	}
1214 
1215 	if ((p = strchr(s, ':')) == NULL)
1216 		errx(1, "Bad community syntax");
1217 	*p++ = 0;
1218 
1219 	getcommunity(s, 0, &as, &asflag);
1220 	getcommunity(p, 0, &data, &dataflag);
1221 	setcommunity(c, as, data, asflag, dataflag);
1222 }
1223 
1224 void
1225 parselargecommunity(struct community *c, char *s)
1226 {
1227 	char *p, *q;
1228 	uint32_t dflag1, dflag2, dflag3;
1229 
1230 	if ((p = strchr(s, ':')) == NULL)
1231 		errx(1, "Bad community syntax");
1232 	*p++ = 0;
1233 
1234 	if ((q = strchr(p, ':')) == NULL)
1235 		errx(1, "Bad community syntax");
1236 	*q++ = 0;
1237 
1238 	getcommunity(s, 1, &c->data1, &dflag1);
1239 	getcommunity(p, 1, &c->data2, &dflag2);
1240 	getcommunity(q, 1, &c->data3, &dflag3);
1241 
1242 	c->flags = COMMUNITY_TYPE_LARGE;
1243 	c->flags |= dflag1 << 8;
1244 	c->flags |= dflag2 << 16;
1245 	c->flags |= dflag3 << 24;
1246 }
1247 
1248 static int
1249 parsesubtype(const char *name, int *type, int *subtype)
1250 {
1251 	const struct ext_comm_pairs *cp;
1252 	int found = 0;
1253 
1254 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1255 		if (strcmp(name, cp->subname) == 0) {
1256 			if (found == 0) {
1257 				*type = cp->type;
1258 				*subtype = cp->subtype;
1259 			}
1260 			found++;
1261 		}
1262 	}
1263 	if (found > 1)
1264 		*type = -1;
1265 	return (found);
1266 }
1267 
1268 static int
1269 parseextvalue(int type, char *s, uint32_t *v, uint32_t *flag)
1270 {
1271 	const char	*errstr;
1272 	char		*p;
1273 	struct in_addr	 ip;
1274 	uint32_t	 uvalh, uval;
1275 
1276 	if (type != -1) {
1277 		/* nothing */
1278 	} else if (strcmp(s, "neighbor-as") == 0) {
1279 		*flag = COMMUNITY_NEIGHBOR_AS;
1280 		*v = 0;
1281 		return EXT_COMMUNITY_TRANS_TWO_AS;
1282 	} else if (strcmp(s, "local-as") == 0) {
1283 		*flag = COMMUNITY_LOCAL_AS;
1284 		*v = 0;
1285 		return EXT_COMMUNITY_TRANS_TWO_AS;
1286 	} else if ((p = strchr(s, '.')) == NULL) {
1287 		/* AS_PLAIN number (4 or 2 byte) */
1288 		strtonum(s, 0, USHRT_MAX, &errstr);
1289 		if (errstr == NULL)
1290 			type = EXT_COMMUNITY_TRANS_TWO_AS;
1291 		else
1292 			type = EXT_COMMUNITY_TRANS_FOUR_AS;
1293 	} else if (strchr(p + 1, '.') == NULL) {
1294 		/* AS_DOT number (4-byte) */
1295 		type = EXT_COMMUNITY_TRANS_FOUR_AS;
1296 	} else {
1297 		/* more than one dot -> IP address */
1298 		type = EXT_COMMUNITY_TRANS_IPV4;
1299 	}
1300 
1301 	switch (type & EXT_COMMUNITY_VALUE) {
1302 	case EXT_COMMUNITY_TRANS_TWO_AS:
1303 		uval = strtonum(s, 0, USHRT_MAX, &errstr);
1304 		if (errstr)
1305 			errx(1, "Bad ext-community %s is %s", s, errstr);
1306 		*v = uval;
1307 		break;
1308 	case EXT_COMMUNITY_TRANS_FOUR_AS:
1309 		if ((p = strchr(s, '.')) == NULL) {
1310 			uval = strtonum(s, 0, UINT_MAX, &errstr);
1311 			if (errstr)
1312 				errx(1, "Bad ext-community %s is %s", s,
1313 				    errstr);
1314 			*v = uval;
1315 			break;
1316 		}
1317 		*p++ = '\0';
1318 		uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
1319 		if (errstr)
1320 			errx(1, "Bad ext-community %s is %s", s, errstr);
1321 		uval = strtonum(p, 0, USHRT_MAX, &errstr);
1322 		if (errstr)
1323 			errx(1, "Bad ext-community %s is %s", p, errstr);
1324 		*v = uval | (uvalh << 16);
1325 		break;
1326 	case EXT_COMMUNITY_TRANS_IPV4:
1327 		if (inet_aton(s, &ip) == 0)
1328 			errx(1, "Bad ext-community %s not parseable", s);
1329 		*v = ntohl(ip.s_addr);
1330 		break;
1331 	default:
1332 		errx(1, "%s: unexpected type %d", __func__, type);
1333 	}
1334 	return (type);
1335 }
1336 
1337 void
1338 parseextcommunity(struct community *c, const char *t, char *s)
1339 {
1340 	const struct ext_comm_pairs *cp;
1341 	char		*p, *ep;
1342 	uint64_t	 ullval;
1343 	uint32_t	 uval, uval2, dflag1 = 0, dflag2 = 0;
1344 	int		 type = 0, subtype = 0;
1345 
1346 	if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) {
1347 		c->flags = COMMUNITY_TYPE_EXT;
1348 		c->flags |= COMMUNITY_ANY << 24;
1349 		return;
1350 	}
1351 	if (parsesubtype(t, &type, &subtype) == 0)
1352 		errx(1, "Bad ext-community unknown type");
1353 
1354 	switch (type) {
1355 	case EXT_COMMUNITY_TRANS_TWO_AS:
1356 	case EXT_COMMUNITY_TRANS_FOUR_AS:
1357 	case EXT_COMMUNITY_TRANS_IPV4:
1358 	case EXT_COMMUNITY_GEN_TWO_AS:
1359 	case EXT_COMMUNITY_GEN_FOUR_AS:
1360 	case EXT_COMMUNITY_GEN_IPV4:
1361 	case -1:
1362 		if (strcmp(s, "*") == 0) {
1363 			dflag1 = COMMUNITY_ANY;
1364 			break;
1365 		}
1366 		if ((p = strchr(s, ':')) == NULL)
1367 			errx(1, "Bad ext-community %s", s);
1368 		*p++ = '\0';
1369 		type = parseextvalue(type, s, &uval, &dflag1);
1370 
1371 		switch (type) {
1372 		case EXT_COMMUNITY_TRANS_TWO_AS:
1373 		case EXT_COMMUNITY_GEN_TWO_AS:
1374 			getcommunity(p, 1, &uval2, &dflag2);
1375 			break;
1376 		case EXT_COMMUNITY_TRANS_IPV4:
1377 		case EXT_COMMUNITY_TRANS_FOUR_AS:
1378 		case EXT_COMMUNITY_GEN_IPV4:
1379 		case EXT_COMMUNITY_GEN_FOUR_AS:
1380 			getcommunity(p, 0, &uval2, &dflag2);
1381 			break;
1382 		default:
1383 			errx(1, "parseextcommunity: unexpected result");
1384 		}
1385 
1386 		c->data1 = uval;
1387 		c->data2 = uval2;
1388 		break;
1389 	case EXT_COMMUNITY_TRANS_OPAQUE:
1390 	case EXT_COMMUNITY_TRANS_EVPN:
1391 		if (strcmp(s, "*") == 0) {
1392 			dflag1 = COMMUNITY_ANY;
1393 			break;
1394 		}
1395 		errno = 0;
1396 		ullval = strtoull(s, &ep, 0);
1397 		if (s[0] == '\0' || *ep != '\0')
1398 			errx(1, "Bad ext-community bad value");
1399 		if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX)
1400 			errx(1, "Bad ext-community value too big");
1401 		c->data1 = ullval >> 32;
1402 		c->data2 = ullval;
1403 		break;
1404 	case EXT_COMMUNITY_NON_TRANS_OPAQUE:
1405 		if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
1406 			if (strcmp(s, "valid") == 0) {
1407 				c->data2 = EXT_COMMUNITY_OVS_VALID;
1408 				break;
1409 			} else if (strcmp(s, "invalid") == 0) {
1410 				c->data2 = EXT_COMMUNITY_OVS_INVALID;
1411 				break;
1412 			} else if (strcmp(s, "not-found") == 0) {
1413 				c->data2 = EXT_COMMUNITY_OVS_NOTFOUND;
1414 				break;
1415 			} else if (strcmp(s, "*") == 0) {
1416 				dflag1 = COMMUNITY_ANY;
1417 				break;
1418 			}
1419 		}
1420 		errx(1, "Bad ext-community %s", s);
1421 	}
1422 
1423 	c->data3 = type << 8 | subtype;
1424 
1425 	/* special handling of ext-community rt * since type is not known */
1426 	if (dflag1 == COMMUNITY_ANY && type == -1) {
1427 		c->flags = COMMUNITY_TYPE_EXT;
1428 		c->flags |= dflag1 << 8;
1429 		return;
1430 	}
1431 
1432 	/* verify type/subtype combo */
1433 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1434 		if (cp->type == type && cp->subtype == subtype) {
1435 			c->flags = COMMUNITY_TYPE_EXT;
1436 			c->flags |= dflag1 << 8;
1437 			c->flags |= dflag2 << 16;
1438 			return;
1439 		}
1440 	}
1441 
1442 	errx(1, "Bad ext-community bad format for type");
1443 }
1444 
1445 int
1446 parse_nexthop(const char *word, struct parse_result *r)
1447 {
1448 	struct filter_set	*fs;
1449 
1450 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
1451 		err(1, NULL);
1452 
1453 	if (strcmp(word, "blackhole") == 0)
1454 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
1455 	else if (strcmp(word, "reject") == 0)
1456 		fs->type = ACTION_SET_NEXTHOP_REJECT;
1457 	else if (strcmp(word, "no-modify") == 0)
1458 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
1459 	else if (parse_addr(word, &fs->action.nexthop)) {
1460 		fs->type = ACTION_SET_NEXTHOP;
1461 	} else {
1462 		free(fs);
1463 		return (0);
1464 	}
1465 
1466 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
1467 	return (1);
1468 }
1469 
1470 static int
1471 unary_op(const char *op)
1472 {
1473 	if (strcmp(op, "=") == 0)
1474 		return FLOWSPEC_OP_NUM_EQ;
1475 	if (strcmp(op, "!=") == 0)
1476 		return FLOWSPEC_OP_NUM_NOT;
1477 	if (strcmp(op, ">") == 0)
1478 		return FLOWSPEC_OP_NUM_GT;
1479 	if (strcmp(op, ">=") == 0)
1480 		return FLOWSPEC_OP_NUM_GE;
1481 	if (strcmp(op, "<") == 0)
1482 		return FLOWSPEC_OP_NUM_LT;
1483 	if (strcmp(op, "<=") == 0)
1484 		return FLOWSPEC_OP_NUM_LE;
1485 	return -1;
1486 }
1487 
1488 static enum comp_ops
1489 binary_op(const char *op)
1490 {
1491 	if (strcmp(op, "-") == 0)
1492 		return OP_RANGE;
1493 	if (strcmp(op, "><") == 0)
1494 		return OP_XRANGE;
1495 	return OP_NONE;
1496 }
1497 
1498 static void
1499 push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val)
1500 {
1501 	uint8_t *comp;
1502 	void *data;
1503 	uint32_t u32;
1504 	uint16_t u16;
1505 	uint8_t u8, flag = 0;
1506 	int len, complen;
1507 
1508 	flag |= op;
1509 	if (and)
1510 		flag |= FLOWSPEC_OP_AND;
1511 
1512 	if (val < 0 || val > 0xffffffff) {
1513 		errx(1, "unsupported value for flowspec num_op");
1514 	} else if (val <= 255) {
1515 		len = 1;
1516 		u8 = val;
1517 		data = &u8;
1518 	} else if (val <= 0xffff) {
1519 		len = 2;
1520 		u16 = htons(val);
1521 		data = &u16;
1522 		flag |= 1 << FLOWSPEC_OP_LEN_SHIFT;
1523 	} else {
1524 		len = 4;
1525 		u32 = htonl(val);
1526 		data = &u32;
1527 		flag |= 2 << FLOWSPEC_OP_LEN_SHIFT;
1528 	}
1529 
1530 	complen = r->flow.complen[type];
1531 	comp = realloc(r->flow.components[type], complen + len + 1);
1532 	if (comp == NULL)
1533 		err(1, NULL);
1534 
1535 	comp[complen++] = flag;
1536 	memcpy(comp + complen, data, len);
1537 	complen += len;
1538 	r->flow.complen[type] = complen;
1539 	r->flow.components[type] = comp;
1540 }
1541 
1542 int
1543 parse_flow_numop(int argc, char *argv[], struct parse_result *r,
1544     enum token_type toktype)
1545 {
1546 	const char *errstr;
1547 	long long val, val2;
1548 	int numargs, type;
1549 	int is_list = 0;
1550 	int op;
1551 
1552 	switch (toktype) {
1553 	case FLOW_PROTO:
1554 		type = FLOWSPEC_TYPE_PROTO;
1555 		break;
1556 	case FLOW_SRCPORT:
1557 		type = FLOWSPEC_TYPE_SRC_PORT;
1558 		break;
1559 	case FLOW_DSTPORT:
1560 		type = FLOWSPEC_TYPE_DST_PORT;
1561 		break;
1562 	case FLOW_ICMPTYPE:
1563 		type = FLOWSPEC_TYPE_ICMP_TYPE;
1564 		break;
1565 	case FLOW_ICMPCODE:
1566 		type = FLOWSPEC_TYPE_ICMP_CODE;
1567 		break;
1568 	case FLOW_LENGTH:
1569 		type = FLOWSPEC_TYPE_PKT_LEN;
1570 		break;
1571 	case FLOW_DSCP:
1572 		type = FLOWSPEC_TYPE_DSCP;
1573 		break;
1574 	default:
1575 		errx(1, "parse_flow_numop called with unsupported type");
1576 	}
1577 
1578 	/* skip keyword (which is already accounted for) */
1579 	argc--;
1580 	argv++;
1581 	numargs = argc;
1582 
1583 	while (argc > 0) {
1584 		if (strcmp(argv[0], "{") == 0) {
1585 			is_list = 1;
1586 			argc--;
1587 			argv++;
1588 		} else if (is_list && strcmp(argv[0], "}") == 0) {
1589 			is_list = 0;
1590 			argc--;
1591 			argv++;
1592 		} else if ((op = unary_op(argv[0])) != -1) {
1593 			if (argc < 2)
1594 				errx(1, "missing argument in flowspec "
1595 				    "definition");
1596 
1597 			val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr);
1598 			if (errstr)
1599 				errx(1, "\"%s\" invalid number: %s", argv[0],
1600 				    errstr);
1601 			push_numop(r, type, op, 0, val);
1602 			argc -= 2;
1603 			argv += 2;
1604 		} else {
1605 			val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr);
1606 			if (errstr)
1607 				errx(1, "\"%s\" invalid number: %s", argv[0],
1608 				    errstr);
1609 			if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) {
1610 				val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX,
1611 				    &errstr);
1612 				if (errstr)
1613 					errx(1, "\"%s\" invalid number: %s",
1614 					    argv[2], errstr);
1615 				switch (op) {
1616 				case OP_RANGE:
1617 					push_numop(r, type, FLOWSPEC_OP_NUM_GE,
1618 					    0, val);
1619 					push_numop(r, type, FLOWSPEC_OP_NUM_LE,
1620 					    1, val2);
1621 					break;
1622 				case OP_XRANGE:
1623 					push_numop(r, type, FLOWSPEC_OP_NUM_LT,
1624 					    0, val);
1625 					push_numop(r, type, FLOWSPEC_OP_NUM_GT,
1626 					    0, val2);
1627 					break;
1628 				}
1629 				argc -= 3;
1630 				argv += 3;
1631 			} else {
1632 				push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val);
1633 				argc--;
1634 				argv++;
1635 			}
1636 		}
1637 		if (is_list == 0)
1638 			break;
1639 	}
1640 
1641 	return numargs - argc;
1642 }
1643