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