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