xref: /openbsd-src/usr.sbin/bgpctl/parser.c (revision f885c9d92adf97ebff462545fd95c5f3a239cc1d)
1*f885c9d9Sflorian /*	$OpenBSD: parser.c,v 1.137 2024/08/22 08:17:54 florian Exp $ */
2acd8015aShenning 
3acd8015aShenning /*
4acd8015aShenning  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
5280f24a4Sphessler  * Copyright (c) 2016 Job Snijders <job@instituut.net>
6280f24a4Sphessler  * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org>
7acd8015aShenning  *
8acd8015aShenning  * Permission to use, copy, modify, and distribute this software for any
9acd8015aShenning  * purpose with or without fee is hereby granted, provided that the above
10acd8015aShenning  * copyright notice and this permission notice appear in all copies.
11acd8015aShenning  *
12acd8015aShenning  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13acd8015aShenning  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14acd8015aShenning  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15acd8015aShenning  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16acd8015aShenning  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17acd8015aShenning  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18acd8015aShenning  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19acd8015aShenning  */
20acd8015aShenning 
21fc210fe3Sclaudio #include <sys/types.h>
22fc210fe3Sclaudio 
23cb39b155Sclaudio #include <endian.h>
24d50a3b9fSclaudio #include <err.h>
257b6c56a0Sclaudio #include <errno.h>
26a20554fdSclaudio #include <fcntl.h>
277b6c56a0Sclaudio #include <limits.h>
28fc210fe3Sclaudio #include <netdb.h>
29acd8015aShenning #include <stdio.h>
307b6c56a0Sclaudio #include <stdlib.h>
31acd8015aShenning #include <string.h>
322302052bShenning #include <unistd.h>
33acd8015aShenning 
34acd8015aShenning #include "parser.h"
35acd8015aShenning 
36acd8015aShenning enum token_type {
37acd8015aShenning 	ENDTOKEN,
38d1d41292Sclaudio 	NOTOKEN,
39d1d41292Sclaudio 	ANYTOKEN,
40acd8015aShenning 	KEYWORD,
41acd8015aShenning 	ADDRESS,
42f72661b1Sclaudio 	PEERADDRESS,
437b6c56a0Sclaudio 	FLAG,
447b6c56a0Sclaudio 	ASNUM,
45d50a3b9fSclaudio 	ASTYPE,
46fd71c51dShenning 	PREFIX,
479caec0aeSclaudio 	PEERDESC,
48903de74aSclaudio 	GROUPDESC,
49a9a0337fSclaudio 	RIBNAME,
50a78f83ceSderaadt 	COMMUNICATION,
519caec0aeSclaudio 	COMMUNITY,
52e3bd233eSbenno 	EXTCOMMUNITY,
53bddeed9cSclaudio 	LRGCOMMUNITY,
549caec0aeSclaudio 	LOCALPREF,
559caec0aeSclaudio 	MED,
569caec0aeSclaudio 	NEXTHOP,
579caec0aeSclaudio 	PFTABLE,
589caec0aeSclaudio 	PREPNBR,
599caec0aeSclaudio 	PREPSELF,
60c41f8a59Shenning 	WEIGHT,
61a73789d3Sclaudio 	RD,
622302052bShenning 	FAMILY,
63a20554fdSclaudio 	RTABLE,
6446daf8ccSclaudio 	FILENAME,
6546daf8ccSclaudio 	PATHID,
66e3925e28Sclaudio 	FLOW_PROTO,
67e3925e28Sclaudio 	FLOW_SRC,
68e3925e28Sclaudio 	FLOW_DST,
69e3925e28Sclaudio 	FLOW_SRCPORT,
70e3925e28Sclaudio 	FLOW_DSTPORT,
71e3925e28Sclaudio 	FLOW_ICMPTYPE,
72e3925e28Sclaudio 	FLOW_ICMPCODE,
73e3925e28Sclaudio 	FLOW_LENGTH,
74e3925e28Sclaudio 	FLOW_DSCP,
75e3925e28Sclaudio 	FLOW_FLAGS,
76e3925e28Sclaudio 	FLOW_FRAGS,
772302052bShenning };
782302052bShenning 
79acd8015aShenning struct token {
80acd8015aShenning 	enum token_type		 type;
81acd8015aShenning 	const char		*keyword;
82acd8015aShenning 	int			 value;
83acd8015aShenning 	const struct token	*next;
84acd8015aShenning };
85acd8015aShenning 
86d1d41292Sclaudio static const struct token *prevtable;
87d1d41292Sclaudio 
88acd8015aShenning static const struct token t_main[];
89acd8015aShenning static const struct token t_show[];
9016fcf8d2Shenning static const struct token t_show_summary[];
91acd8015aShenning static const struct token t_show_fib[];
927b6c56a0Sclaudio static const struct token t_show_rib[];
93bfee927aSclaudio static const struct token t_show_avs[];
94854886c9Sdenis static const struct token t_show_ovs[];
95a20554fdSclaudio static const struct token t_show_mrt[];
96a20554fdSclaudio static const struct token t_show_mrt_file[];
97f72661b1Sclaudio static const struct token t_show_rib_neigh[];
98a20554fdSclaudio static const struct token t_show_mrt_neigh[];
99a9a0337fSclaudio static const struct token t_show_rib_rib[];
100acd8015aShenning static const struct token t_show_neighbor[];
101acd8015aShenning static const struct token t_show_neighbor_modifiers[];
102acd8015aShenning static const struct token t_fib[];
103acd8015aShenning static const struct token t_neighbor[];
104acd8015aShenning static const struct token t_neighbor_modifiers[];
105a20554fdSclaudio static const struct token t_show_rib_as[];
106a20554fdSclaudio static const struct token t_show_mrt_as[];
107d50a3b9fSclaudio static const struct token t_show_prefix[];
1087b6c56a0Sclaudio static const struct token t_show_ip[];
1099caec0aeSclaudio static const struct token t_network[];
1105fed6b04Sclaudio static const struct token t_flowspec[];
111e3925e28Sclaudio static const struct token t_flowfamily[];
112e3925e28Sclaudio static const struct token t_flowrule[];
113e3925e28Sclaudio static const struct token t_flowsrc[];
114e3925e28Sclaudio static const struct token t_flowdst[];
115e3925e28Sclaudio static const struct token t_flowsrcport[];
116e3925e28Sclaudio static const struct token t_flowdstport[];
117e3925e28Sclaudio static const struct token t_flowicmp[];
118325664f1Sclaudio static const struct token t_bulk[];
1192fb3b566Shenning static const struct token t_network_show[];
120a4cb7d77Sclaudio static const struct token t_prefix[];
1219caec0aeSclaudio static const struct token t_set[];
1229caec0aeSclaudio static const struct token t_nexthop[];
1239caec0aeSclaudio static const struct token t_pftable[];
124c3319070Sclaudio static const struct token t_log[];
125a78f83ceSderaadt static const struct token t_communication[];
126acd8015aShenning 
127acd8015aShenning static const struct token t_main[] = {
128acd8015aShenning 	{ KEYWORD,	"fib",		FIB,		t_fib},
1295fed6b04Sclaudio 	{ KEYWORD,	"flowspec",	NONE,		t_flowspec},
1303b433cf9Sclaudio 	{ KEYWORD,	"log",		NONE,		t_log},
131acd8015aShenning 	{ KEYWORD,	"neighbor",	NEIGHBOR,	t_neighbor},
1329caec0aeSclaudio 	{ KEYWORD,	"network",	NONE,		t_network},
1333b433cf9Sclaudio 	{ KEYWORD,	"reload",	RELOAD,		t_communication},
1343b433cf9Sclaudio 	{ KEYWORD,	"show",		SHOW,		t_show},
135acd8015aShenning 	{ ENDTOKEN,	"",		NONE,		NULL}
136acd8015aShenning };
137acd8015aShenning 
138acd8015aShenning static const struct token t_show[] = {
139acd8015aShenning 	{ NOTOKEN,	"",		NONE,		NULL},
140acd8015aShenning 	{ KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
1415fed6b04Sclaudio 	{ KEYWORD,	"flowspec",	FLOWSPEC_SHOW,	t_network_show},
142acd8015aShenning 	{ KEYWORD,	"interfaces",	SHOW_INTERFACE,	NULL},
1433b433cf9Sclaudio 	{ KEYWORD,	"ip",		NONE,		t_show_ip},
1443b433cf9Sclaudio 	{ KEYWORD,	"metrics",	SHOW_METRICS,	NULL},
1453b433cf9Sclaudio 	{ KEYWORD,	"mrt",		SHOW_MRT,	t_show_mrt},
146acd8015aShenning 	{ KEYWORD,	"neighbor",	SHOW_NEIGHBOR,	t_show_neighbor},
147c49d37b1Shenning 	{ KEYWORD,	"network",	NETWORK_SHOW,	t_network_show},
148acd8015aShenning 	{ KEYWORD,	"nexthop",	SHOW_NEXTHOP,	NULL},
1497b6c56a0Sclaudio 	{ KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
15069d2b5abSclaudio 	{ KEYWORD,	"rtr",		SHOW_RTR,	NULL},
1513b433cf9Sclaudio 	{ KEYWORD,	"sets",		SHOW_SET,	NULL},
1523b433cf9Sclaudio 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
1533b433cf9Sclaudio 	{ KEYWORD,	"tables",	SHOW_FIB_TABLES, NULL},
15416fcf8d2Shenning 	{ ENDTOKEN,	"",		NONE,		NULL}
15516fcf8d2Shenning };
15616fcf8d2Shenning 
15716fcf8d2Shenning static const struct token t_show_summary[] = {
15816fcf8d2Shenning 	{ NOTOKEN,	"",		NONE,			NULL},
15916fcf8d2Shenning 	{ KEYWORD,	"terse",	SHOW_SUMMARY_TERSE,	NULL},
160acd8015aShenning 	{ ENDTOKEN,	"",		NONE,			NULL}
161acd8015aShenning };
162acd8015aShenning 
163acd8015aShenning static const struct token t_show_fib[] = {
164acd8015aShenning 	{ NOTOKEN,	"",		NONE,		NULL},
165ef0c8610Sclaudio 	{ FLAG,		"bgp",		F_BGPD,		t_show_fib},
1663b433cf9Sclaudio 	{ FLAG,		"connected",	F_CONNECTED,	t_show_fib},
167acd8015aShenning 	{ FLAG,		"nexthop",	F_NEXTHOP,	t_show_fib},
1683b433cf9Sclaudio 	{ FLAG,		"static",	F_STATIC,	t_show_fib},
1697ddd74cdSclaudio 	{ RTABLE,	"table",	NONE,		t_show_fib},
170c41f8a59Shenning 	{ FAMILY,	"",		NONE,		t_show_fib},
171acd8015aShenning 	{ ADDRESS,	"",		NONE,		NULL},
172acd8015aShenning 	{ ENDTOKEN,	"",		NONE,		NULL}
173acd8015aShenning };
174acd8015aShenning 
1757b6c56a0Sclaudio static const struct token t_show_rib[] = {
1767b6c56a0Sclaudio 	{ NOTOKEN,	"",		NONE,		NULL},
177a20554fdSclaudio 	{ ASTYPE,	"as",		AS_ALL,		t_show_rib_as},
178bfee927aSclaudio 	{ KEYWORD,	"avs",		NONE,		t_show_avs},
1793b433cf9Sclaudio 	{ FLAG,		"best",		F_CTL_BEST,	t_show_rib},
180bddeed9cSclaudio 	{ COMMUNITY,	"community",	NONE,		t_show_rib},
1813b433cf9Sclaudio 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_rib},
1824ae31d17Sclaudio 	{ FLAG,		"disqualified",	F_CTL_INELIGIBLE, t_show_rib},
1833b433cf9Sclaudio 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_rib},
1843b433cf9Sclaudio 	{ FLAG,		"error",	F_CTL_INVALID,	t_show_rib},
185bddeed9cSclaudio 	{ EXTCOMMUNITY,	"ext-community", NONE,		t_show_rib},
18609b2f24cSclaudio 	{ FLAG,		"filtered",	F_CTL_FILTERED,	t_show_rib},
1873b433cf9Sclaudio 	{ FLAG,		"in",		F_CTL_ADJ_IN,	t_show_rib},
188bddeed9cSclaudio 	{ LRGCOMMUNITY,	"large-community", NONE,	t_show_rib},
1893b433cf9Sclaudio 	{ FLAG,		"leaked",	F_CTL_LEAKED,	t_show_rib},
1903b433cf9Sclaudio 	{ KEYWORD,	"memory",	SHOW_RIB_MEM,	NULL},
1913b433cf9Sclaudio 	{ KEYWORD,	"neighbor",	NONE,		t_show_rib_neigh},
1923b433cf9Sclaudio 	{ FLAG,		"out",		F_CTL_ADJ_OUT,	t_show_rib},
19346daf8ccSclaudio 	{ KEYWORD,	"ovs",		NONE,		t_show_ovs},
1947ddd74cdSclaudio 	{ PATHID,	"path-id",	NONE,		t_show_rib},
1953b433cf9Sclaudio 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_rib_as},
1963b433cf9Sclaudio 	{ FLAG,		"selected",	F_CTL_BEST,	t_show_rib},
1973b433cf9Sclaudio 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_rib_as},
1983b433cf9Sclaudio 	{ FLAG,		"ssv",		F_CTL_SSV,	t_show_rib},
1995901a216Sclaudio 	{ KEYWORD,	"summary",	SHOW_SUMMARY,	t_show_summary},
2003b433cf9Sclaudio 	{ KEYWORD,	"table",	NONE,		t_show_rib_rib},
2013b433cf9Sclaudio 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_rib_as},
202f72661b1Sclaudio 	{ FAMILY,	"",		NONE,		t_show_rib},
2036ed5da00Sclaudio 	{ PREFIX,	"",		NONE,		t_show_prefix},
2047b6c56a0Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
2057b6c56a0Sclaudio };
2067b6c56a0Sclaudio 
207bfee927aSclaudio static const struct token t_show_avs[] = {
208bfee927aSclaudio 	{ FLAG,		"invalid",	F_CTL_AVS_INVALID,	t_show_rib},
2099a717891Sjob 	{ FLAG,		"unknown",	F_CTL_AVS_UNKNOWN,	t_show_rib},
2103b433cf9Sclaudio 	{ FLAG,		"valid"	,	F_CTL_AVS_VALID,	t_show_rib},
211bfee927aSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
212bfee927aSclaudio };
213bfee927aSclaudio 
214854886c9Sdenis static const struct token t_show_ovs[] = {
215854886c9Sdenis 	{ FLAG,		"invalid",	F_CTL_OVS_INVALID,	t_show_rib},
216854886c9Sdenis 	{ FLAG,		"not-found",	F_CTL_OVS_NOTFOUND,	t_show_rib},
2173b433cf9Sclaudio 	{ FLAG,		"valid"	,	F_CTL_OVS_VALID,	t_show_rib},
218854886c9Sdenis 	{ ENDTOKEN,	"",		NONE,		NULL}
219854886c9Sdenis };
220a20554fdSclaudio 
221a20554fdSclaudio static const struct token t_show_mrt[] = {
222a20554fdSclaudio 	{ NOTOKEN,	"",		NONE,		NULL},
223a20554fdSclaudio 	{ ASTYPE,	"as",		AS_ALL,		t_show_mrt_as},
224a20554fdSclaudio 	{ FLAG,		"detail",	F_CTL_DETAIL,	t_show_mrt},
2253b433cf9Sclaudio 	{ ASTYPE,	"empty-as",	AS_EMPTY,	t_show_mrt},
226a20554fdSclaudio 	{ KEYWORD,	"file",		NONE,		t_show_mrt_file},
2273b433cf9Sclaudio 	{ KEYWORD,	"neighbor",	NONE,		t_show_mrt_neigh},
2283b433cf9Sclaudio 	{ ASTYPE,	"peer-as",	AS_PEER,	t_show_mrt_as},
2293b433cf9Sclaudio 	{ FLAG,		"peers",	F_CTL_NEIGHBORS,t_show_mrt},
2303b433cf9Sclaudio 	{ ASTYPE,	"source-as",	AS_SOURCE,	t_show_mrt_as},
2313b433cf9Sclaudio 	{ FLAG,		"ssv",		F_CTL_SSV,	t_show_mrt},
2323b433cf9Sclaudio 	{ ASTYPE,	"transit-as",	AS_TRANSIT,	t_show_mrt_as},
233a20554fdSclaudio 	{ FAMILY,	"",		NONE,		t_show_mrt},
234a20554fdSclaudio 	{ PREFIX,	"",		NONE,		t_show_prefix},
235a20554fdSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
236a20554fdSclaudio };
237a20554fdSclaudio 
238a20554fdSclaudio static const struct token t_show_mrt_file[] = {
239a20554fdSclaudio 	{ FILENAME,	"",		NONE,		t_show_mrt},
240a20554fdSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
241a20554fdSclaudio };
242a20554fdSclaudio 
243903de74aSclaudio static const struct token t_show_rib_neigh_group[] = {
244903de74aSclaudio 	{ GROUPDESC,	"",		NONE,	t_show_rib},
245903de74aSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
246903de74aSclaudio };
247903de74aSclaudio 
248f72661b1Sclaudio static const struct token t_show_rib_neigh[] = {
249903de74aSclaudio 	{ KEYWORD,	"group",	NONE,	t_show_rib_neigh_group},
250f72661b1Sclaudio 	{ PEERADDRESS,	"",		NONE,	t_show_rib},
251f72661b1Sclaudio 	{ PEERDESC,	"",		NONE,	t_show_rib},
252f72661b1Sclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
253f72661b1Sclaudio };
254f72661b1Sclaudio 
255a20554fdSclaudio static const struct token t_show_mrt_neigh[] = {
256a20554fdSclaudio 	{ PEERADDRESS,	"",		NONE,	t_show_mrt},
257a20554fdSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
258a20554fdSclaudio };
259a20554fdSclaudio 
260a9a0337fSclaudio static const struct token t_show_rib_rib[] = {
261a9a0337fSclaudio 	{ RIBNAME,	"",		NONE,	t_show_rib},
262a9a0337fSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
263a9a0337fSclaudio };
264a9a0337fSclaudio 
265acd8015aShenning static const struct token t_show_neighbor_modifiers[] = {
266acd8015aShenning 	{ NOTOKEN,	"",		NONE,			NULL},
267acd8015aShenning 	{ KEYWORD,	"messages",	SHOW_NEIGHBOR,		NULL},
2685901a216Sclaudio 	{ KEYWORD,	"terse",	SHOW_NEIGHBOR_TERSE,	NULL},
2693b433cf9Sclaudio 	{ KEYWORD,	"timers",	SHOW_NEIGHBOR_TIMERS,	NULL},
270acd8015aShenning 	{ ENDTOKEN,	"",		NONE,			NULL}
271acd8015aShenning };
272acd8015aShenning 
273903de74aSclaudio static const struct token t_show_neighbor_group[] = {
274903de74aSclaudio 	{ GROUPDESC,	"",		NONE,	t_show_neighbor_modifiers},
275903de74aSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
276903de74aSclaudio };
277903de74aSclaudio 
278903de74aSclaudio static const struct token t_show_neighbor[] = {
279903de74aSclaudio 	{ NOTOKEN,	"",		NONE,	NULL},
280903de74aSclaudio 	{ KEYWORD,	"group",	NONE,	t_show_neighbor_group},
281903de74aSclaudio 	{ PEERADDRESS,	"",		NONE,	t_show_neighbor_modifiers},
282903de74aSclaudio 	{ PEERDESC,	"",		NONE,	t_show_neighbor_modifiers},
283903de74aSclaudio 	{ ENDTOKEN,	"",		NONE,	NULL}
284903de74aSclaudio };
285903de74aSclaudio 
286acd8015aShenning static const struct token t_fib[] = {
287acd8015aShenning 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
288acd8015aShenning 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
2897ddd74cdSclaudio 	{ RTABLE,	"table",	NONE,		t_fib},
290acd8015aShenning 	{ ENDTOKEN,	"",		NONE,		NULL}
291acd8015aShenning };
292acd8015aShenning 
293903de74aSclaudio static const struct token t_neighbor_group[] = {
294903de74aSclaudio 	{ GROUPDESC,	"",		NONE,		t_neighbor_modifiers},
295903de74aSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
296903de74aSclaudio };
297903de74aSclaudio 
298acd8015aShenning static const struct token t_neighbor[] = {
299903de74aSclaudio 	{ KEYWORD,	"group",	NONE,		t_neighbor_group},
300f72661b1Sclaudio 	{ PEERADDRESS,	"",		NONE,		t_neighbor_modifiers},
301fd71c51dShenning 	{ PEERDESC,	"",		NONE,		t_neighbor_modifiers},
302acd8015aShenning 	{ ENDTOKEN,	"",		NONE,		NULL}
303acd8015aShenning };
304acd8015aShenning 
305a78f83ceSderaadt static const struct token t_communication[] = {
3060561b344Sphessler 	{ NOTOKEN,	"",		NONE,		NULL},
307a78f83ceSderaadt 	{ COMMUNICATION, "",		NONE,		NULL},
3080561b344Sphessler 	{ ENDTOKEN,	"",		NONE,		NULL}
3090561b344Sphessler };
3100561b344Sphessler 
311acd8015aShenning static const struct token t_neighbor_modifiers[] = {
312a78f83ceSderaadt 	{ KEYWORD,	"clear",	NEIGHBOR_CLEAR,	t_communication},
31307820ff6Sclaudio 	{ KEYWORD,	"destroy",	NEIGHBOR_DESTROY,	NULL},
3143b433cf9Sclaudio 	{ KEYWORD,	"down",		NEIGHBOR_DOWN,	t_communication},
3153b433cf9Sclaudio 	{ KEYWORD,	"refresh",	NEIGHBOR_RREFRESH,	NULL},
3163b433cf9Sclaudio 	{ KEYWORD,	"up",		NEIGHBOR_UP,		NULL},
317acd8015aShenning 	{ ENDTOKEN,	"",		NONE,			NULL}
318acd8015aShenning };
319acd8015aShenning 
320a20554fdSclaudio static const struct token t_show_rib_as[] = {
321f72661b1Sclaudio 	{ ASNUM,	"",		NONE,		t_show_rib},
3227b6c56a0Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
3237b6c56a0Sclaudio };
3247b6c56a0Sclaudio 
325a20554fdSclaudio static const struct token t_show_mrt_as[] = {
326a20554fdSclaudio 	{ ASNUM,	"",		NONE,		t_show_mrt},
327a20554fdSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
328a20554fdSclaudio };
329a20554fdSclaudio 
330d50a3b9fSclaudio static const struct token t_show_prefix[] = {
331d1d41292Sclaudio 	{ FLAG,		"all",		F_LONGER,	t_show_rib},
332d1d41292Sclaudio 	{ FLAG,		"longer-prefixes", F_LONGER,	t_show_rib},
333d1d41292Sclaudio 	{ FLAG,		"or-longer",	F_LONGER,	t_show_rib},
334d1d41292Sclaudio 	{ FLAG,		"or-shorter",	F_SHORTER,	t_show_rib},
335d1d41292Sclaudio 	{ ANYTOKEN,	"",		NONE,		t_show_rib},
336d50a3b9fSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
337d50a3b9fSclaudio };
338d50a3b9fSclaudio 
3397b6c56a0Sclaudio static const struct token t_show_ip[] = {
3407b6c56a0Sclaudio 	{ KEYWORD,	"bgp",		SHOW_RIB,	t_show_rib},
3417b6c56a0Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
3427b6c56a0Sclaudio };
3437b6c56a0Sclaudio 
3449caec0aeSclaudio static const struct token t_network[] = {
345a4cb7d77Sclaudio 	{ KEYWORD,	"add",		NETWORK_ADD,	t_prefix},
3463b433cf9Sclaudio 	{ KEYWORD,	"bulk",		NONE,		t_bulk},
347a4cb7d77Sclaudio 	{ KEYWORD,	"delete",	NETWORK_REMOVE,	t_prefix},
348a4cb7d77Sclaudio 	{ KEYWORD,	"flush",	NETWORK_FLUSH,	NULL},
349f486926aSclaudio 	{ KEYWORD,	"mrt",		NETWORK_MRT,	t_show_mrt},
3503b433cf9Sclaudio 	{ KEYWORD,	"show",		NETWORK_SHOW,	t_network_show},
351325664f1Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
352325664f1Sclaudio };
353325664f1Sclaudio 
3545fed6b04Sclaudio static const struct token t_flowspec[] = {
355e3925e28Sclaudio 	{ KEYWORD,	"add",		FLOWSPEC_ADD,	t_flowfamily},
356e3925e28Sclaudio 	{ KEYWORD,	"delete",	FLOWSPEC_REMOVE,t_flowfamily},
3575fed6b04Sclaudio 	{ KEYWORD,	"flush",	FLOWSPEC_FLUSH,	NULL},
3585fed6b04Sclaudio 	{ KEYWORD,	"show",		FLOWSPEC_SHOW,	t_network_show},
3595fed6b04Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
3605fed6b04Sclaudio };
3615fed6b04Sclaudio 
362e3925e28Sclaudio static const struct token t_flowfamily[] = {
363e3925e28Sclaudio 	{ FAMILY,	"",		NONE,		t_flowrule},
364e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
365e3925e28Sclaudio };
366e3925e28Sclaudio 
367e3925e28Sclaudio static const struct token t_flowrule[] = {
368e3925e28Sclaudio 	{ NOTOKEN,	"",		NONE,		NULL},
369e3925e28Sclaudio 	{ FLOW_FLAGS,	"flags",	NONE,		t_flowrule},
370e3925e28Sclaudio 	{ FLOW_FRAGS,	"fragment",	NONE,		t_flowrule},
371e3925e28Sclaudio 	{ KEYWORD,	"from",		NONE,		t_flowsrc},
372e3925e28Sclaudio 	{ FLOW_ICMPTYPE,"icmp-type",	NONE,		t_flowicmp},
373e3925e28Sclaudio 	{ FLOW_LENGTH,	"length",	NONE,		t_flowrule},
374e3925e28Sclaudio 	{ FLOW_PROTO,	"proto",	NONE,		t_flowrule},
375e3925e28Sclaudio 	{ KEYWORD,	"set",		NONE,		t_set},
376e3925e28Sclaudio 	{ KEYWORD,	"to",		NONE,		t_flowdst},
377e3925e28Sclaudio 	{ FLOW_DSCP,	"dscp",		NONE,		t_flowrule},
378e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
379e3925e28Sclaudio };
380e3925e28Sclaudio 
381e3925e28Sclaudio static const struct token t_flowsrc[] = {
382e3925e28Sclaudio 	{ KEYWORD,	"any",		NONE,		t_flowsrcport},
383e3925e28Sclaudio 	{ FLOW_SRC,	"",		NONE,		t_flowsrcport},
384e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
385e3925e28Sclaudio };
386e3925e28Sclaudio 
387e3925e28Sclaudio static const struct token t_flowdst[] = {
388e3925e28Sclaudio 	{ KEYWORD,	"any",		NONE,		t_flowdstport},
389e3925e28Sclaudio 	{ FLOW_DST,	"",		NONE,		t_flowdstport},
390e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
391e3925e28Sclaudio };
392e3925e28Sclaudio 
393e3925e28Sclaudio static const struct token t_flowsrcport[] = {
394e3925e28Sclaudio 	{ FLOW_SRCPORT,	"port",		NONE,		t_flowrule},
395e3925e28Sclaudio 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
396e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
397e3925e28Sclaudio };
398e3925e28Sclaudio 
399e3925e28Sclaudio static const struct token t_flowdstport[] = {
400e3925e28Sclaudio 	{ FLOW_DSTPORT,	"port",		NONE,		t_flowrule},
401e3925e28Sclaudio 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
402e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
403e3925e28Sclaudio };
404e3925e28Sclaudio 
405e3925e28Sclaudio static const struct token t_flowicmp[] = {
406e3925e28Sclaudio 	{ FLOW_ICMPCODE,"code",		NONE,		t_flowrule},
407e3925e28Sclaudio 	{ ANYTOKEN,	"",		NONE,		t_flowrule},
408e3925e28Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
409e3925e28Sclaudio };
410e3925e28Sclaudio 
411325664f1Sclaudio static const struct token t_bulk[] = {
412325664f1Sclaudio 	{ KEYWORD,	"add",		NETWORK_BULK_ADD,	t_set},
413325664f1Sclaudio 	{ KEYWORD,	"delete",	NETWORK_BULK_REMOVE,	NULL},
414a4cb7d77Sclaudio 	{ ENDTOKEN,	"",		NONE,			NULL}
415a4cb7d77Sclaudio };
416a4cb7d77Sclaudio 
417a4cb7d77Sclaudio static const struct token t_prefix[] = {
4189caec0aeSclaudio 	{ PREFIX,	"",		NONE,		t_set},
419a4cb7d77Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
420a4cb7d77Sclaudio };
421a4cb7d77Sclaudio 
4222fb3b566Shenning static const struct token t_network_show[] = {
4232fb3b566Shenning 	{ NOTOKEN,	"",		NONE,		NULL},
4242fb3b566Shenning 	{ FAMILY,	"",		NONE,		NULL},
4252fb3b566Shenning 	{ ENDTOKEN,	"",		NONE,		NULL}
4262fb3b566Shenning };
4272fb3b566Shenning 
428a73789d3Sclaudio static const struct token t_rd[] = {
429a73789d3Sclaudio 	{ RD,		"",			NONE,	t_set},
430a73789d3Sclaudio 	{ ENDTOKEN,	"",			NONE,	NULL}
431a73789d3Sclaudio };
432a73789d3Sclaudio 
4339caec0aeSclaudio static const struct token t_set[] = {
4349caec0aeSclaudio 	{ NOTOKEN,	"",			NONE,	NULL},
435bddeed9cSclaudio 	{ COMMUNITY,	"community",		NONE,	t_set},
436bddeed9cSclaudio 	{ EXTCOMMUNITY,	"ext-community",	NONE,	t_set},
437bddeed9cSclaudio 	{ LRGCOMMUNITY,	"large-community",	NONE,	t_set},
4387ddd74cdSclaudio 	{ LOCALPREF,	"localpref",		NONE,	t_set},
4397ddd74cdSclaudio 	{ MED,		"med",			NONE,	t_set},
4407ddd74cdSclaudio 	{ MED,		"metric",		NONE,	t_set},
4419caec0aeSclaudio 	{ KEYWORD,	"nexthop",		NONE,	t_nexthop},
4429caec0aeSclaudio 	{ KEYWORD,	"pftable",		NONE,	t_pftable},
4437ddd74cdSclaudio 	{ PREPNBR,	"prepend-neighbor",	NONE,	t_set},
4447ddd74cdSclaudio 	{ PREPSELF,	"prepend-self",		NONE,	t_set},
445a73789d3Sclaudio 	{ KEYWORD,	"rd",			NONE,	t_rd},
4467ddd74cdSclaudio 	{ WEIGHT,	"weight",		NONE,	t_set},
4479caec0aeSclaudio 	{ ENDTOKEN,	"",			NONE,	NULL}
4489caec0aeSclaudio };
4499caec0aeSclaudio 
4509caec0aeSclaudio static const struct token t_nexthop[] = {
4519caec0aeSclaudio 	{ NEXTHOP,	"",			NONE,	t_set},
4529caec0aeSclaudio 	{ ENDTOKEN,	"",			NONE,	NULL}
4539caec0aeSclaudio };
4549caec0aeSclaudio 
4559caec0aeSclaudio static const struct token t_pftable[] = {
4569caec0aeSclaudio 	{ PFTABLE,	"",			NONE,	t_set},
4579caec0aeSclaudio 	{ ENDTOKEN,	"",			NONE,	NULL}
4589caec0aeSclaudio };
4599caec0aeSclaudio 
460c3319070Sclaudio static const struct token t_log[] = {
461c3319070Sclaudio 	{ KEYWORD,	"brief",	LOG_BRIEF,	NULL},
4623b433cf9Sclaudio 	{ KEYWORD,	"verbose",	LOG_VERBOSE,	NULL},
463c3319070Sclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
464c3319070Sclaudio };
465c3319070Sclaudio 
466acd8015aShenning static struct parse_result	res;
467acd8015aShenning 
46822c4f340Sclaudio const struct token	*match_token(int, char *[], const struct token [],
46922c4f340Sclaudio 			    int *);
470acd8015aShenning void			 show_valid_args(const struct token []);
471a73789d3Sclaudio 
472acd8015aShenning int	parse_addr(const char *, struct bgpd_addr *);
47359154960Sclaudio int	parse_asnum(const char *, size_t, uint32_t *);
474a73789d3Sclaudio int	parse_number(const char *, struct parse_result *, enum token_type);
475bddeed9cSclaudio void	parsecommunity(struct community *c, char *s);
476bddeed9cSclaudio void	parselargecommunity(struct community *c, char *s);
4770ca99656Sclaudio void	parseextcommunity(struct community *c, const char *t, char *s);
4789caec0aeSclaudio int	parse_nexthop(const char *, struct parse_result *);
479e3925e28Sclaudio int	parse_flow_numop(int, char *[], struct parse_result *, enum token_type);
480acd8015aShenning 
481acd8015aShenning struct parse_result *
482acd8015aShenning parse(int argc, char *argv[])
483acd8015aShenning {
484acd8015aShenning 	const struct token	*table = t_main;
485acd8015aShenning 	const struct token	*match;
48622c4f340Sclaudio 	int			 used;
487acd8015aShenning 
4884a99c744Sclaudio 	memset(&res, 0, sizeof(res));
4895faf3abbSbenno 	res.rtableid = getrtable();
49038cb626eSfgsch 	TAILQ_INIT(&res.set);
491acd8015aShenning 
49263aa252eSclaudio 	while (argc >= 0) {
49322c4f340Sclaudio 		if ((match = match_token(argc, argv, table, &used)) == NULL) {
494acd8015aShenning 			fprintf(stderr, "valid commands/args:\n");
495acd8015aShenning 			show_valid_args(table);
496acd8015aShenning 			return (NULL);
497acd8015aShenning 		}
498d1d41292Sclaudio 		if (match->type == ANYTOKEN) {
499d1d41292Sclaudio 			if (prevtable == NULL)
500d1d41292Sclaudio 				prevtable = table;
501d1d41292Sclaudio 			table = match->next;
502d1d41292Sclaudio 			continue;
503d1d41292Sclaudio 		}
504acd8015aShenning 
50522c4f340Sclaudio 		argc -= used;
50622c4f340Sclaudio 		argv += used;
507acd8015aShenning 
5084e9d3417Shenning 		if (match->type == NOTOKEN || match->next == NULL)
509acd8015aShenning 			break;
510acd8015aShenning 		table = match->next;
511acd8015aShenning 	}
512acd8015aShenning 
5134e9d3417Shenning 	if (argc > 0) {
5144e9d3417Shenning 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
515acd8015aShenning 		return (NULL);
516acd8015aShenning 	}
517acd8015aShenning 
518acd8015aShenning 	return (&res);
519acd8015aShenning }
520acd8015aShenning 
521acd8015aShenning const struct token *
52222c4f340Sclaudio match_token(int argc, char *argv[], const struct token table[], int *argsused)
523acd8015aShenning {
524acd8015aShenning 	u_int			 i, match;
525acd8015aShenning 	const struct token	*t = NULL;
5269caec0aeSclaudio 	struct filter_set	*fs;
52722c4f340Sclaudio 	const char		*word = argv[0];
52884cd1f5fStedu 	size_t			 wordlen = 0;
529acd8015aShenning 
53022c4f340Sclaudio 	*argsused = 1;
531acd8015aShenning 	match = 0;
53284cd1f5fStedu 	if (word != NULL)
53384cd1f5fStedu 		wordlen = strlen(word);
534acd8015aShenning 	for (i = 0; table[i].type != ENDTOKEN; i++) {
535acd8015aShenning 		switch (table[i].type) {
536acd8015aShenning 		case NOTOKEN:
53784cd1f5fStedu 			if (word == NULL || wordlen == 0) {
538acd8015aShenning 				match++;
539acd8015aShenning 				t = &table[i];
540acd8015aShenning 			}
541acd8015aShenning 			break;
542d1d41292Sclaudio 		case ANYTOKEN:
543d1d41292Sclaudio 			/* match anything if nothing else matched before */
544d1d41292Sclaudio 			if (match == 0) {
545d1d41292Sclaudio 				match++;
546d1d41292Sclaudio 				t = &table[i];
547d1d41292Sclaudio 			}
548d1d41292Sclaudio 			break;
549acd8015aShenning 		case KEYWORD:
550acd8015aShenning 			if (word != NULL && strncmp(word, table[i].keyword,
55184cd1f5fStedu 			    wordlen) == 0) {
552acd8015aShenning 				match++;
553acd8015aShenning 				t = &table[i];
554acd8015aShenning 				if (t->value)
555acd8015aShenning 					res.action = t->value;
556acd8015aShenning 			}
557acd8015aShenning 			break;
558acd8015aShenning 		case FLAG:
559acd8015aShenning 			if (word != NULL && strncmp(word, table[i].keyword,
56084cd1f5fStedu 			    wordlen) == 0) {
561acd8015aShenning 				match++;
562acd8015aShenning 				t = &table[i];
563acd8015aShenning 				res.flags |= t->value;
564acd8015aShenning 			}
565acd8015aShenning 			break;
566c41f8a59Shenning 		case FAMILY:
567c41f8a59Shenning 			if (word == NULL)
568c41f8a59Shenning 				break;
569e6cfebe9Sclaudio 			if (!strcmp(word, "inet") ||
570e6cfebe9Sclaudio 			    !strcasecmp(word, "IPv4")) {
571c41f8a59Shenning 				match++;
572c41f8a59Shenning 				t = &table[i];
573536c61dcSclaudio 				res.aid = AID_INET;
574c41f8a59Shenning 			}
575e6cfebe9Sclaudio 			if (!strcmp(word, "inet6") ||
576e6cfebe9Sclaudio 			    !strcasecmp(word, "IPv6")) {
577c41f8a59Shenning 				match++;
578c41f8a59Shenning 				t = &table[i];
579536c61dcSclaudio 				res.aid = AID_INET6;
580c41f8a59Shenning 			}
581e6cfebe9Sclaudio 			if (!strcasecmp(word, "VPNv4")) {
582e6cfebe9Sclaudio 				match++;
583e6cfebe9Sclaudio 				t = &table[i];
584e6cfebe9Sclaudio 				res.aid = AID_VPN_IPv4;
585e6cfebe9Sclaudio 			}
5863eb11dfcSclaudio 			if (!strcasecmp(word, "VPNv6")) {
5873eb11dfcSclaudio 				match++;
5883eb11dfcSclaudio 				t = &table[i];
5893eb11dfcSclaudio 				res.aid = AID_VPN_IPv6;
5903eb11dfcSclaudio 			}
591c41f8a59Shenning 			break;
592acd8015aShenning 		case ADDRESS:
593acd8015aShenning 			if (parse_addr(word, &res.addr)) {
594acd8015aShenning 				match++;
595acd8015aShenning 				t = &table[i];
596acd8015aShenning 			}
597acd8015aShenning 			break;
598f72661b1Sclaudio 		case PEERADDRESS:
599f72661b1Sclaudio 			if (parse_addr(word, &res.peeraddr)) {
600f72661b1Sclaudio 				match++;
601f72661b1Sclaudio 				t = &table[i];
602f72661b1Sclaudio 			}
603f72661b1Sclaudio 			break;
604e3925e28Sclaudio 		case FLOW_SRC:
605e3925e28Sclaudio 			if (parse_prefix(word, wordlen, &res.flow.src,
606e3925e28Sclaudio 			    &res.flow.srclen)) {
607e3925e28Sclaudio 				match++;
608e3925e28Sclaudio 				t = &table[i];
609e3925e28Sclaudio 				if (res.aid != res.flow.src.aid)
610e3925e28Sclaudio 					errx(1, "wrong address family in "
611e3925e28Sclaudio 					    "flowspec rule");
612e3925e28Sclaudio 			}
613e3925e28Sclaudio 			break;
614e3925e28Sclaudio 		case FLOW_DST:
615e3925e28Sclaudio 			if (parse_prefix(word, wordlen, &res.flow.dst,
616e3925e28Sclaudio 			    &res.flow.dstlen)) {
617e3925e28Sclaudio 				match++;
618e3925e28Sclaudio 				t = &table[i];
619e3925e28Sclaudio 				if (res.aid != res.flow.dst.aid)
620e3925e28Sclaudio 					errx(1, "wrong address family in "
621e3925e28Sclaudio 					    "flowspec rule");
622e3925e28Sclaudio 			}
623e3925e28Sclaudio 			break;
624d50a3b9fSclaudio 		case PREFIX:
62559154960Sclaudio 			if (parse_prefix(word, wordlen, &res.addr,
62659154960Sclaudio 			    &res.prefixlen)) {
627d50a3b9fSclaudio 				match++;
628d50a3b9fSclaudio 				t = &table[i];
629d50a3b9fSclaudio 			}
630d50a3b9fSclaudio 			break;
6317b6c56a0Sclaudio 		case ASTYPE:
6327b6c56a0Sclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
63384cd1f5fStedu 			    wordlen) == 0) {
6347b6c56a0Sclaudio 				match++;
6357b6c56a0Sclaudio 				t = &table[i];
6367b6c56a0Sclaudio 				res.as.type = t->value;
6377b6c56a0Sclaudio 			}
6387b6c56a0Sclaudio 			break;
6397b6c56a0Sclaudio 		case ASNUM:
6403fdbeaafSclaudio 			if (parse_asnum(word, wordlen, &res.as.as_min)) {
6417ab130d7Sclaudio 				res.as.as_max = res.as.as_min;
6427b6c56a0Sclaudio 				match++;
6437b6c56a0Sclaudio 				t = &table[i];
6447b6c56a0Sclaudio 			}
6457b6c56a0Sclaudio 			break;
646903de74aSclaudio 		case GROUPDESC:
647903de74aSclaudio 			res.is_group = 1;
648903de74aSclaudio 			/* FALLTHROUGH */
649fd71c51dShenning 		case PEERDESC:
65084cd1f5fStedu 			if (!match && word != NULL && wordlen > 0) {
651fd71c51dShenning 				if (strlcpy(res.peerdesc, word,
652fd71c51dShenning 				    sizeof(res.peerdesc)) >=
653fd71c51dShenning 				    sizeof(res.peerdesc))
654f186116dSclaudio 					errx(1, "neighbor description too "
655f186116dSclaudio 					    "long");
656fd71c51dShenning 				match++;
657fd71c51dShenning 				t = &table[i];
658fd71c51dShenning 			}
659fd71c51dShenning 			break;
660a9a0337fSclaudio 		case RIBNAME:
66184cd1f5fStedu 			if (!match && word != NULL && wordlen > 0) {
662a9a0337fSclaudio 				if (strlcpy(res.rib, word, sizeof(res.rib)) >=
663a9a0337fSclaudio 				    sizeof(res.rib))
664a9a0337fSclaudio 					errx(1, "rib name too long");
665a9a0337fSclaudio 				match++;
666a9a0337fSclaudio 				t = &table[i];
667a9a0337fSclaudio 			}
668a9a0337fSclaudio 			break;
669a78f83ceSderaadt 		case COMMUNICATION:
6700561b344Sphessler 			if (!match && word != NULL && wordlen > 0) {
671a78f83ceSderaadt 				if (strlcpy(res.reason, word,
672a78f83ceSderaadt 				    sizeof(res.reason)) >=
673a78f83ceSderaadt 				    sizeof(res.reason))
6740561b344Sphessler 					errx(1, "shutdown reason too long");
6750561b344Sphessler 				match++;
6760561b344Sphessler 				t = &table[i];
6770561b344Sphessler 			}
6780561b344Sphessler 			break;
6799caec0aeSclaudio 		case COMMUNITY:
680bddeed9cSclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
68122c4f340Sclaudio 			    wordlen) == 0 && argc > 1) {
68222c4f340Sclaudio 				parsecommunity(&res.community, argv[1]);
68322c4f340Sclaudio 				*argsused += 1;
684a73789d3Sclaudio 
685a73789d3Sclaudio 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
686a73789d3Sclaudio 					err(1, NULL);
687a73789d3Sclaudio 				fs->type = ACTION_SET_COMMUNITY;
688a73789d3Sclaudio 				fs->action.community = res.community;
689a73789d3Sclaudio 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
690a73789d3Sclaudio 
6919caec0aeSclaudio 				match++;
6929caec0aeSclaudio 				t = &table[i];
6939caec0aeSclaudio 			}
6949caec0aeSclaudio 			break;
695bddeed9cSclaudio 		case LRGCOMMUNITY:
696e3bd233eSbenno 			if (word != NULL && strncmp(word, table[i].keyword,
69722c4f340Sclaudio 			    wordlen) == 0 && argc > 1) {
69822c4f340Sclaudio 				parselargecommunity(&res.community, argv[1]);
69922c4f340Sclaudio 				*argsused += 1;
700bddeed9cSclaudio 
701bddeed9cSclaudio 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
702bddeed9cSclaudio 					err(1, NULL);
703bddeed9cSclaudio 				fs->type = ACTION_SET_COMMUNITY;
704bddeed9cSclaudio 				fs->action.community = res.community;
705bddeed9cSclaudio 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
706bddeed9cSclaudio 
707e3bd233eSbenno 				match++;
708e3bd233eSbenno 				t = &table[i];
709e3bd233eSbenno 			}
710e3bd233eSbenno 			break;
711e3bd233eSbenno 		case EXTCOMMUNITY:
712bddeed9cSclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
71322c4f340Sclaudio 			    wordlen) == 0 && argc > 2) {
714a73789d3Sclaudio 				parseextcommunity(&res.community,
71522c4f340Sclaudio 				    argv[1], argv[2]);
71622c4f340Sclaudio 				*argsused += 2;
717a73789d3Sclaudio 
718a73789d3Sclaudio 				if ((fs = calloc(1, sizeof(*fs))) == NULL)
719a73789d3Sclaudio 					err(1, NULL);
720a73789d3Sclaudio 				fs->type = ACTION_SET_COMMUNITY;
721a73789d3Sclaudio 				fs->action.community = res.community;
722a73789d3Sclaudio 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
723a73789d3Sclaudio 
724e3bd233eSbenno 				match++;
725e3bd233eSbenno 				t = &table[i];
726e3bd233eSbenno 			}
727e3bd233eSbenno 			break;
728a73789d3Sclaudio 		case RD:
729a73789d3Sclaudio 			if (word != NULL && wordlen > 0) {
730a73789d3Sclaudio 				char *p = strdup(word);
731f320e759Smbuhl 				struct community ext = { 0 };
73259154960Sclaudio 				uint64_t rd;
733a73789d3Sclaudio 
734a73789d3Sclaudio 				if (p == NULL)
735a73789d3Sclaudio 					err(1, NULL);
736a73789d3Sclaudio 				parseextcommunity(&ext, "rt", p);
737a73789d3Sclaudio 				free(p);
738a73789d3Sclaudio 
7390ca99656Sclaudio 				switch (ext.data3 >> 8) {
740a73789d3Sclaudio 				case EXT_COMMUNITY_TRANS_TWO_AS:
741a73789d3Sclaudio 					rd = (0ULL << 48);
74259154960Sclaudio 					rd |= ((uint64_t)ext.data1 & 0xffff)
7430ca99656Sclaudio 					    << 32;
74459154960Sclaudio 					rd |= (uint64_t)ext.data2;
745a73789d3Sclaudio 					break;
746a73789d3Sclaudio 				case EXT_COMMUNITY_TRANS_IPV4:
747a73789d3Sclaudio 					rd = (1ULL << 48);
74859154960Sclaudio 					rd |= (uint64_t)ext.data1 << 16;
74959154960Sclaudio 					rd |= (uint64_t)ext.data2 & 0xffff;
750a73789d3Sclaudio 					break;
751a73789d3Sclaudio 				case EXT_COMMUNITY_TRANS_FOUR_AS:
752a73789d3Sclaudio 					rd = (2ULL << 48);
75359154960Sclaudio 					rd |= (uint64_t)ext.data1 << 16;
75459154960Sclaudio 					rd |= (uint64_t)ext.data2 & 0xffff;
755a73789d3Sclaudio 					break;
756a73789d3Sclaudio 				default:
757a73789d3Sclaudio 					errx(1, "bad encoding of rd");
758a73789d3Sclaudio 				}
759a73789d3Sclaudio 				res.rd = htobe64(rd);
760280f24a4Sphessler 				match++;
761280f24a4Sphessler 				t = &table[i];
762280f24a4Sphessler 			}
763280f24a4Sphessler 			break;
7649caec0aeSclaudio 		case LOCALPREF:
7659caec0aeSclaudio 		case MED:
7669caec0aeSclaudio 		case PREPNBR:
7679caec0aeSclaudio 		case PREPSELF:
7689caec0aeSclaudio 		case WEIGHT:
7697f410e5eSclaudio 		case RTABLE:
77046daf8ccSclaudio 		case PATHID:
7717ddd74cdSclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
7727ddd74cdSclaudio 			    wordlen) == 0 && argc > 1 &&
7737ddd74cdSclaudio 			    parse_number(argv[1], &res, table[i].type)) {
7747ddd74cdSclaudio 				*argsused += 1;
7759caec0aeSclaudio 				match++;
7769caec0aeSclaudio 				t = &table[i];
7779caec0aeSclaudio 			}
7789caec0aeSclaudio 			break;
7799caec0aeSclaudio 		case NEXTHOP:
78084cd1f5fStedu 			if (word != NULL && wordlen > 0 &&
7819caec0aeSclaudio 			    parse_nexthop(word, &res)) {
7829caec0aeSclaudio 				match++;
7839caec0aeSclaudio 				t = &table[i];
7849caec0aeSclaudio 			}
7859caec0aeSclaudio 			break;
7869caec0aeSclaudio 		case PFTABLE:
78784cd1f5fStedu 			if (word != NULL && wordlen > 0) {
7889caec0aeSclaudio 				if ((fs = calloc(1,
7899caec0aeSclaudio 				    sizeof(struct filter_set))) == NULL)
7909caec0aeSclaudio 					err(1, NULL);
7919caec0aeSclaudio 				if (strlcpy(fs->action.pftable, word,
7929caec0aeSclaudio 				    sizeof(fs->action.pftable)) >=
793f186116dSclaudio 				    sizeof(fs->action.pftable))
794f186116dSclaudio 					errx(1, "pftable name too long");
79538cb626eSfgsch 				TAILQ_INSERT_TAIL(&res.set, fs, entry);
7969caec0aeSclaudio 				match++;
7979caec0aeSclaudio 				t = &table[i];
7989caec0aeSclaudio 			}
7999caec0aeSclaudio 			break;
800a20554fdSclaudio 		case FILENAME:
80184cd1f5fStedu 			if (word != NULL && wordlen > 0) {
802a20554fdSclaudio 				if ((res.mrtfd = open(word, O_RDONLY)) == -1) {
803a20554fdSclaudio 					/*
804a20554fdSclaudio 					 * ignore error if path has no / and
805a20554fdSclaudio 					 * does not exist. In hope to print
806a20554fdSclaudio 					 * usage.
807a20554fdSclaudio 					 */
808a20554fdSclaudio 					if (errno == ENOENT &&
809a20554fdSclaudio 					    !strchr(word, '/'))
810a20554fdSclaudio 						break;
811a20554fdSclaudio 					err(1, "mrt open(%s)", word);
812a20554fdSclaudio 				}
813a20554fdSclaudio 				match++;
814a20554fdSclaudio 				t = &table[i];
815a20554fdSclaudio 			}
816a20554fdSclaudio 			break;
817e3925e28Sclaudio 		case FLOW_SRCPORT:
818e3925e28Sclaudio 		case FLOW_DSTPORT:
819e3925e28Sclaudio 		case FLOW_PROTO:
820e3925e28Sclaudio 		case FLOW_ICMPTYPE:
821e3925e28Sclaudio 		case FLOW_ICMPCODE:
822e3925e28Sclaudio 		case FLOW_LENGTH:
823e3925e28Sclaudio 		case FLOW_DSCP:
824e3925e28Sclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
825e3925e28Sclaudio 			    wordlen) == 0 && argc > 1) {
826e3925e28Sclaudio 				*argsused += parse_flow_numop(argc, argv, &res,
827e3925e28Sclaudio 				    table[i].type);
828e3925e28Sclaudio 
829e3925e28Sclaudio 				match++;
830e3925e28Sclaudio 				t = &table[i];
831e3925e28Sclaudio 			}
832e3925e28Sclaudio 			break;
833e3925e28Sclaudio 		case FLOW_FLAGS:
834e3925e28Sclaudio 		case FLOW_FRAGS:
835e3925e28Sclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
836e3925e28Sclaudio 			    wordlen) == 0) {
837e3925e28Sclaudio 				errx(1, "%s not yet implemented", word);
838e3925e28Sclaudio 			}
839e3925e28Sclaudio 			break;
840acd8015aShenning 		case ENDTOKEN:
841acd8015aShenning 			break;
842acd8015aShenning 		}
843acd8015aShenning 	}
844acd8015aShenning 
845acd8015aShenning 	if (match != 1) {
84663aa252eSclaudio 		if (word == NULL)
84763aa252eSclaudio 			fprintf(stderr, "missing argument:\n");
84863aa252eSclaudio 		else if (match > 1)
849acd8015aShenning 			fprintf(stderr, "ambiguous argument: %s\n", word);
85063aa252eSclaudio 		else if (match < 1)
851acd8015aShenning 			fprintf(stderr, "unknown argument: %s\n", word);
852acd8015aShenning 		return (NULL);
853acd8015aShenning 	}
854acd8015aShenning 
855acd8015aShenning 	return (t);
856acd8015aShenning }
857acd8015aShenning 
858acd8015aShenning void
859acd8015aShenning show_valid_args(const struct token table[])
860acd8015aShenning {
861acd8015aShenning 	int	i;
862acd8015aShenning 
863d1d41292Sclaudio 	if (prevtable != NULL) {
864d1d41292Sclaudio 		const struct token *t = prevtable;
865d1d41292Sclaudio 		prevtable = NULL;
866d1d41292Sclaudio 		show_valid_args(t);
867d1d41292Sclaudio 		fprintf(stderr, "or any of\n");
868d1d41292Sclaudio 	}
869d1d41292Sclaudio 
870acd8015aShenning 	for (i = 0; table[i].type != ENDTOKEN; i++) {
871acd8015aShenning 		switch (table[i].type) {
8727b6c56a0Sclaudio 		case NOTOKEN:
873f45db661Shenning 			fprintf(stderr, "  <cr>\n");
874acd8015aShenning 			break;
875d1d41292Sclaudio 		case ANYTOKEN:
876d1d41292Sclaudio 			break;
877acd8015aShenning 		case KEYWORD:
878acd8015aShenning 		case FLAG:
8797b6c56a0Sclaudio 		case ASTYPE:
880acd8015aShenning 			fprintf(stderr, "  %s\n", table[i].keyword);
881acd8015aShenning 			break;
882acd8015aShenning 		case ADDRESS:
883f72661b1Sclaudio 		case PEERADDRESS:
884acd8015aShenning 			fprintf(stderr, "  <address>\n");
885acd8015aShenning 			break;
886d50a3b9fSclaudio 		case PREFIX:
887e3925e28Sclaudio 		case FLOW_SRC:
888e3925e28Sclaudio 		case FLOW_DST:
889d50a3b9fSclaudio 			fprintf(stderr, "  <address>[/<len>]\n");
890d50a3b9fSclaudio 			break;
8917b6c56a0Sclaudio 		case ASNUM:
8927b6c56a0Sclaudio 			fprintf(stderr, "  <asnum>\n");
8937b6c56a0Sclaudio 			break;
894903de74aSclaudio 		case GROUPDESC:
895fd71c51dShenning 		case PEERDESC:
896fd71c51dShenning 			fprintf(stderr, "  <neighbor description>\n");
897fd71c51dShenning 			break;
898a9a0337fSclaudio 		case RIBNAME:
899a9a0337fSclaudio 			fprintf(stderr, "  <rib name>\n");
900a9a0337fSclaudio 			break;
901a78f83ceSderaadt 		case COMMUNICATION:
902a78f83ceSderaadt 			fprintf(stderr, "  <reason>\n");
9030561b344Sphessler 			break;
9049caec0aeSclaudio 		case COMMUNITY:
905bddeed9cSclaudio 			fprintf(stderr, "  %s <community>\n",
906bddeed9cSclaudio 			    table[i].keyword);
9079caec0aeSclaudio 			break;
908bddeed9cSclaudio 		case LRGCOMMUNITY:
909bddeed9cSclaudio 			fprintf(stderr, "  %s <large-community>\n",
910bddeed9cSclaudio 			    table[i].keyword);
911a73789d3Sclaudio 			break;
912e3bd233eSbenno 		case EXTCOMMUNITY:
913bddeed9cSclaudio 			fprintf(stderr, "  %s <extended-community>\n",
914bddeed9cSclaudio 			    table[i].keyword);
915e3bd233eSbenno 			break;
916a73789d3Sclaudio 		case RD:
917a73789d3Sclaudio 			fprintf(stderr, "  <route-distinguisher>\n");
918280f24a4Sphessler 			break;
9199caec0aeSclaudio 		case LOCALPREF:
9209caec0aeSclaudio 		case MED:
9219caec0aeSclaudio 		case PREPNBR:
9229caec0aeSclaudio 		case PREPSELF:
9239caec0aeSclaudio 		case WEIGHT:
9247f410e5eSclaudio 		case RTABLE:
9257ddd74cdSclaudio 		case PATHID:
9267ddd74cdSclaudio 			fprintf(stderr, "  %s <number>\n", table[i].keyword);
9277f410e5eSclaudio 			break;
9289caec0aeSclaudio 		case NEXTHOP:
9299caec0aeSclaudio 			fprintf(stderr, "  <address>\n");
9309caec0aeSclaudio 			break;
9319caec0aeSclaudio 		case PFTABLE:
9329caec0aeSclaudio 			fprintf(stderr, "  <pftable>\n");
9339caec0aeSclaudio 			break;
934c41f8a59Shenning 		case FAMILY:
9353eb11dfcSclaudio 			fprintf(stderr, "  [ inet | inet6 | IPv4 | IPv6 | "
9363eb11dfcSclaudio 			    "VPNv4 | VPNv6 ]\n");
9377f0943bdSderaadt 			break;
938a20554fdSclaudio 		case FILENAME:
939a20554fdSclaudio 			fprintf(stderr, "  <filename>\n");
940a20554fdSclaudio 			break;
941e3925e28Sclaudio 		case FLOW_SRCPORT:
942e3925e28Sclaudio 		case FLOW_DSTPORT:
943e3925e28Sclaudio 		case FLOW_PROTO:
944e3925e28Sclaudio 		case FLOW_ICMPTYPE:
945e3925e28Sclaudio 		case FLOW_ICMPCODE:
946e3925e28Sclaudio 		case FLOW_LENGTH:
947e3925e28Sclaudio 		case FLOW_DSCP:
948e3925e28Sclaudio 			fprintf(stderr, "  %s <numberspec>\n",
949e3925e28Sclaudio 			    table[i].keyword);
950e3925e28Sclaudio 			break;
951e3925e28Sclaudio 		case FLOW_FLAGS:
952e3925e28Sclaudio 		case FLOW_FRAGS:
953e3925e28Sclaudio 			fprintf(stderr, "  %s <flagspec>\n",
954e3925e28Sclaudio 			    table[i].keyword);
955e3925e28Sclaudio 			break;
956acd8015aShenning 		case ENDTOKEN:
957acd8015aShenning 			break;
958acd8015aShenning 		}
959acd8015aShenning 	}
960acd8015aShenning }
961acd8015aShenning 
962acd8015aShenning int
963acd8015aShenning parse_addr(const char *word, struct bgpd_addr *addr)
964acd8015aShenning {
965acd8015aShenning 	struct in_addr	ina;
966fc210fe3Sclaudio 	struct addrinfo	hints, *r;
967acd8015aShenning 
968acd8015aShenning 	if (word == NULL)
969acd8015aShenning 		return (0);
970acd8015aShenning 
9714a99c744Sclaudio 	memset(&ina, 0, sizeof(ina));
972acd8015aShenning 
973adf6c067Sclaudio 	if (inet_net_pton(AF_INET, word, &ina, sizeof(ina)) != -1) {
97404fd5bf5Sclaudio 		memset(addr, 0, sizeof(*addr));
975b26dd4adSclaudio 		addr->aid = AID_INET;
976acd8015aShenning 		addr->v4 = ina;
977acd8015aShenning 		return (1);
978acd8015aShenning 	}
979acd8015aShenning 
9804a99c744Sclaudio 	memset(&hints, 0, sizeof(hints));
981fc210fe3Sclaudio 	hints.ai_family = AF_INET6;
982fc210fe3Sclaudio 	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
983fc210fe3Sclaudio 	hints.ai_flags = AI_NUMERICHOST;
984fc210fe3Sclaudio 	if (getaddrinfo(word, "0", &hints, &r) == 0) {
985a6311673Sclaudio 		sa2addr(r->ai_addr, addr, NULL);
986fc210fe3Sclaudio 		freeaddrinfo(r);
987fc210fe3Sclaudio 		return (1);
988fc210fe3Sclaudio 	}
989fc210fe3Sclaudio 
990acd8015aShenning 	return (0);
991acd8015aShenning }
9927b6c56a0Sclaudio 
9937b6c56a0Sclaudio int
99459154960Sclaudio parse_prefix(const char *word, size_t wordlen, struct bgpd_addr *addr,
99559154960Sclaudio     uint8_t *prefixlen)
996d50a3b9fSclaudio {
99704fd5bf5Sclaudio 	struct bgpd_addr tmp;
998fc210fe3Sclaudio 	char		*p, *ps;
999fc210fe3Sclaudio 	const char	*errstr;
1000fc210fe3Sclaudio 	int		 mask = -1;
1001d50a3b9fSclaudio 
1002d50a3b9fSclaudio 	if (word == NULL)
1003d50a3b9fSclaudio 		return (0);
1004d50a3b9fSclaudio 
100504fd5bf5Sclaudio 	memset(&tmp, 0, sizeof(tmp));
1006d50a3b9fSclaudio 
1007fc210fe3Sclaudio 	if ((p = strrchr(word, '/')) != NULL) {
100884cd1f5fStedu 		size_t plen = strlen(p);
1009fc210fe3Sclaudio 		mask = strtonum(p + 1, 0, 128, &errstr);
1010fc210fe3Sclaudio 		if (errstr)
1011a20554fdSclaudio 			errx(1, "netmask %s", errstr);
1012fc210fe3Sclaudio 
101384cd1f5fStedu 		if ((ps = malloc(wordlen - plen + 1)) == NULL)
10143323994cSclaudio 			err(1, "parse_prefix: malloc");
101584cd1f5fStedu 		strlcpy(ps, word, wordlen - plen + 1);
1016fc210fe3Sclaudio 
101704fd5bf5Sclaudio 		if (parse_addr(ps, &tmp) == 0) {
101817aad25cSclaudio 			free(ps);
1019d50a3b9fSclaudio 			return (0);
102017aad25cSclaudio 		}
1021fc210fe3Sclaudio 
1022fc210fe3Sclaudio 		free(ps);
1023fc210fe3Sclaudio 	} else
102404fd5bf5Sclaudio 		if (parse_addr(word, &tmp) == 0)
1025fc210fe3Sclaudio 			return (0);
1026fc210fe3Sclaudio 
102704fd5bf5Sclaudio 	switch (tmp.aid) {
1028b26dd4adSclaudio 	case AID_INET:
1029fc210fe3Sclaudio 		if (mask == -1)
1030fc210fe3Sclaudio 			mask = 32;
1031fc210fe3Sclaudio 		if (mask > 32)
1032fc210fe3Sclaudio 			errx(1, "invalid netmask: too large");
1033fc210fe3Sclaudio 		break;
1034b26dd4adSclaudio 	case AID_INET6:
1035fc210fe3Sclaudio 		if (mask == -1)
1036fc210fe3Sclaudio 			mask = 128;
1037fc210fe3Sclaudio 		break;
1038fc210fe3Sclaudio 	default:
1039fc210fe3Sclaudio 		return (0);
1040d50a3b9fSclaudio 	}
1041d50a3b9fSclaudio 
104204fd5bf5Sclaudio 	applymask(addr, &tmp, mask);
1043fc210fe3Sclaudio 	*prefixlen = mask;
1044fc210fe3Sclaudio 	return (1);
1045d50a3b9fSclaudio }
1046d50a3b9fSclaudio 
1047d50a3b9fSclaudio int
104859154960Sclaudio parse_asnum(const char *word, size_t wordlen, uint32_t *asnum)
10497b6c56a0Sclaudio {
1050d04c8312Sclaudio 	const char	*errstr;
1051066ab39dSderaadt 	char		*dot, *parseword;
105259154960Sclaudio 	uint32_t	 uval, uvalh = 0;
10537b6c56a0Sclaudio 
10547b6c56a0Sclaudio 	if (word == NULL)
10557b6c56a0Sclaudio 		return (0);
10567b6c56a0Sclaudio 
105784cd1f5fStedu 	if (wordlen < 1 || word[0] < '0' || word[0] > '9')
105870b1a84dShenning 		return (0);
105970b1a84dShenning 
1060066ab39dSderaadt 	parseword = strdup(word);
1061066ab39dSderaadt 	if ((dot = strchr(parseword, '.')) != NULL) {
1062d18b99bcSclaudio 		*dot++ = '\0';
1063066ab39dSderaadt 		uvalh = strtonum(parseword, 0, USHRT_MAX, &errstr);
1064d18b99bcSclaudio 		if (errstr)
1065d18b99bcSclaudio 			errx(1, "AS number is %s: %s", errstr, word);
1066d18b99bcSclaudio 		uval = strtonum(dot, 0, USHRT_MAX, &errstr);
1067d18b99bcSclaudio 		if (errstr)
1068d18b99bcSclaudio 			errx(1, "AS number is %s: %s", errstr, word);
1069d18b99bcSclaudio 	} else {
1070066ab39dSderaadt 		uval = strtonum(parseword, 0, UINT_MAX, &errstr);
1071d04c8312Sclaudio 		if (errstr)
1072d04c8312Sclaudio 			errx(1, "AS number is %s: %s", errstr, word);
1073d18b99bcSclaudio 	}
1074d04c8312Sclaudio 
1075066ab39dSderaadt 	free(parseword);
1076d18b99bcSclaudio 	*asnum = uval | (uvalh << 16);
10777b6c56a0Sclaudio 	return (1);
10787b6c56a0Sclaudio }
10799caec0aeSclaudio 
10809caec0aeSclaudio int
10819caec0aeSclaudio parse_number(const char *word, struct parse_result *r, enum token_type type)
10829caec0aeSclaudio {
10839caec0aeSclaudio 	struct filter_set	*fs;
1084d04c8312Sclaudio 	const char		*errstr;
1085d04c8312Sclaudio 	u_int			 uval;
10869caec0aeSclaudio 
10879caec0aeSclaudio 	if (word == NULL)
10889caec0aeSclaudio 		return (0);
10899caec0aeSclaudio 
1090d04c8312Sclaudio 	uval = strtonum(word, 0, UINT_MAX, &errstr);
1091d04c8312Sclaudio 	if (errstr)
1092d04c8312Sclaudio 		errx(1, "number is %s: %s", errstr, word);
10939caec0aeSclaudio 
10949caec0aeSclaudio 	/* number was parseable */
109546daf8ccSclaudio 	switch (type) {
109646daf8ccSclaudio 	case RTABLE:
10977f410e5eSclaudio 		r->rtableid = uval;
10987f410e5eSclaudio 		return (1);
109946daf8ccSclaudio 	case PATHID:
110046daf8ccSclaudio 		r->pathid = uval;
110146daf8ccSclaudio 		r->flags |= F_CTL_HAS_PATHID;
110246daf8ccSclaudio 		return (1);
110346daf8ccSclaudio 	default:
110446daf8ccSclaudio 		break;
11057f410e5eSclaudio 	}
11067f410e5eSclaudio 
11079caec0aeSclaudio 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
11089caec0aeSclaudio 		err(1, NULL);
11099caec0aeSclaudio 	switch (type) {
11109caec0aeSclaudio 	case LOCALPREF:
11119caec0aeSclaudio 		fs->type = ACTION_SET_LOCALPREF;
1112d04c8312Sclaudio 		fs->action.metric = uval;
11139caec0aeSclaudio 		break;
11149caec0aeSclaudio 	case MED:
11159caec0aeSclaudio 		fs->type = ACTION_SET_MED;
1116d04c8312Sclaudio 		fs->action.metric = uval;
11179caec0aeSclaudio 		break;
11189caec0aeSclaudio 	case PREPNBR:
1119d04c8312Sclaudio 		if (uval > 128) {
11209caec0aeSclaudio 			free(fs);
11219caec0aeSclaudio 			return (0);
11229caec0aeSclaudio 		}
11239caec0aeSclaudio 		fs->type = ACTION_SET_PREPEND_PEER;
1124d04c8312Sclaudio 		fs->action.prepend = uval;
11259caec0aeSclaudio 		break;
11269caec0aeSclaudio 	case PREPSELF:
1127d04c8312Sclaudio 		if (uval > 128) {
11289caec0aeSclaudio 			free(fs);
11299caec0aeSclaudio 			return (0);
11309caec0aeSclaudio 		}
11319caec0aeSclaudio 		fs->type = ACTION_SET_PREPEND_SELF;
1132d04c8312Sclaudio 		fs->action.prepend = uval;
11339caec0aeSclaudio 		break;
11349caec0aeSclaudio 	case WEIGHT:
11359caec0aeSclaudio 		fs->type = ACTION_SET_WEIGHT;
1136d04c8312Sclaudio 		fs->action.metric = uval;
11379caec0aeSclaudio 		break;
11389caec0aeSclaudio 	default:
11399caec0aeSclaudio 		errx(1, "king bula sez bad things happen");
11409caec0aeSclaudio 	}
11419caec0aeSclaudio 
114238cb626eSfgsch 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
11439caec0aeSclaudio 	return (1);
11449caec0aeSclaudio }
11459caec0aeSclaudio 
1146a73789d3Sclaudio static void
114759154960Sclaudio getcommunity(char *s, int large, uint32_t *val, uint32_t *flag)
11489caec0aeSclaudio {
11498b0355c9Sclaudio 	long long	 max = USHRT_MAX;
1150d04c8312Sclaudio 	const char	*errstr;
11519caec0aeSclaudio 
1152187619b8Sclaudio 	*flag = 0;
1153a73789d3Sclaudio 	*val = 0;
1154a73789d3Sclaudio 	if (strcmp(s, "*") == 0) {
1155a73789d3Sclaudio 		*flag = COMMUNITY_ANY;
1156a73789d3Sclaudio 		return;
1157a73789d3Sclaudio 	} else if (strcmp(s, "neighbor-as") == 0) {
1158a73789d3Sclaudio 		*flag = COMMUNITY_NEIGHBOR_AS;
1159a73789d3Sclaudio 		return;
1160a73789d3Sclaudio 	} else if (strcmp(s, "local-as") == 0) {
1161a73789d3Sclaudio 		*flag =  COMMUNITY_LOCAL_AS;
1162a73789d3Sclaudio 		return;
1163a73789d3Sclaudio 	}
1164a73789d3Sclaudio 	if (large)
1165a73789d3Sclaudio 		max = UINT_MAX;
1166a73789d3Sclaudio 	*val = strtonum(s, 0, max, &errstr);
1167a73789d3Sclaudio 	if (errstr)
1168a73789d3Sclaudio 		errx(1, "Community %s is %s (max: %llu)", s, errstr, max);
11699caec0aeSclaudio }
11709caec0aeSclaudio 
1171a73789d3Sclaudio static void
117259154960Sclaudio setcommunity(struct community *c, uint32_t as, uint32_t data,
117359154960Sclaudio     uint32_t asflag, uint32_t dataflag)
11749caec0aeSclaudio {
11750ca99656Sclaudio 	c->flags = COMMUNITY_TYPE_BASIC;
11760ca99656Sclaudio 	c->flags |= asflag << 8;
11770ca99656Sclaudio 	c->flags |= dataflag << 16;
11780ca99656Sclaudio 	c->data1 = as;
11790ca99656Sclaudio 	c->data2 = data;
11800ca99656Sclaudio 	c->data3 = 0;
11819caec0aeSclaudio }
11829caec0aeSclaudio 
1183a73789d3Sclaudio void
1184bddeed9cSclaudio parsecommunity(struct community *c, char *s)
1185a73789d3Sclaudio {
1186a73789d3Sclaudio 	char *p;
118759154960Sclaudio 	uint32_t as, data, asflag, dataflag;
1188a73789d3Sclaudio 
1189a73789d3Sclaudio 	/* Well-known communities */
1190a73789d3Sclaudio 	if (strcasecmp(s, "GRACEFUL_SHUTDOWN") == 0) {
1191a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1192a73789d3Sclaudio 		    COMMUNITY_GRACEFUL_SHUTDOWN, 0, 0);
1193a73789d3Sclaudio 		return;
1194a73789d3Sclaudio 	} else if (strcasecmp(s, "NO_EXPORT") == 0) {
1195a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1196a73789d3Sclaudio 		    COMMUNITY_NO_EXPORT, 0, 0);
1197a73789d3Sclaudio 		return;
1198a73789d3Sclaudio 	} else if (strcasecmp(s, "NO_ADVERTISE") == 0) {
1199a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1200a73789d3Sclaudio 		    COMMUNITY_NO_ADVERTISE, 0, 0);
1201a73789d3Sclaudio 		return;
1202a73789d3Sclaudio 	} else if (strcasecmp(s, "NO_EXPORT_SUBCONFED") == 0) {
1203a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1204a73789d3Sclaudio 		    COMMUNITY_NO_EXPSUBCONFED, 0, 0);
1205a73789d3Sclaudio 		return;
1206a73789d3Sclaudio 	} else if (strcasecmp(s, "NO_PEER") == 0) {
1207a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1208a73789d3Sclaudio 		    COMMUNITY_NO_PEER, 0, 0);
1209a73789d3Sclaudio 		return;
1210a73789d3Sclaudio 	} else if (strcasecmp(s, "BLACKHOLE") == 0) {
1211a73789d3Sclaudio 		setcommunity(c, COMMUNITY_WELLKNOWN,
1212a73789d3Sclaudio 		    COMMUNITY_BLACKHOLE, 0, 0);
1213a73789d3Sclaudio 		return;
1214a73789d3Sclaudio 	}
1215a73789d3Sclaudio 
1216a73789d3Sclaudio 	if ((p = strchr(s, ':')) == NULL)
1217a73789d3Sclaudio 		errx(1, "Bad community syntax");
1218a73789d3Sclaudio 	*p++ = 0;
1219a73789d3Sclaudio 
1220a73789d3Sclaudio 	getcommunity(s, 0, &as, &asflag);
1221a73789d3Sclaudio 	getcommunity(p, 0, &data, &dataflag);
1222a73789d3Sclaudio 	setcommunity(c, as, data, asflag, dataflag);
1223a73789d3Sclaudio }
1224a73789d3Sclaudio 
1225bddeed9cSclaudio void
1226bddeed9cSclaudio parselargecommunity(struct community *c, char *s)
1227bddeed9cSclaudio {
1228bddeed9cSclaudio 	char *p, *q;
1229bddeed9cSclaudio 	uint32_t dflag1, dflag2, dflag3;
1230bddeed9cSclaudio 
1231bddeed9cSclaudio 	if ((p = strchr(s, ':')) == NULL)
1232bddeed9cSclaudio 		errx(1, "Bad community syntax");
1233bddeed9cSclaudio 	*p++ = 0;
1234bddeed9cSclaudio 
1235bddeed9cSclaudio 	if ((q = strchr(p, ':')) == NULL)
1236bddeed9cSclaudio 		errx(1, "Bad community syntax");
1237bddeed9cSclaudio 	*q++ = 0;
1238bddeed9cSclaudio 
1239bddeed9cSclaudio 	getcommunity(s, 1, &c->data1, &dflag1);
1240bddeed9cSclaudio 	getcommunity(p, 1, &c->data2, &dflag2);
1241bddeed9cSclaudio 	getcommunity(q, 1, &c->data3, &dflag3);
1242bddeed9cSclaudio 
1243bddeed9cSclaudio 	c->flags = COMMUNITY_TYPE_LARGE;
1244bddeed9cSclaudio 	c->flags |= dflag1 << 8;
1245bddeed9cSclaudio 	c->flags |= dflag2 << 16;
1246bddeed9cSclaudio 	c->flags |= dflag3 << 24;
1247bddeed9cSclaudio }
1248bddeed9cSclaudio 
1249a73789d3Sclaudio static int
1250a73789d3Sclaudio parsesubtype(const char *name, int *type, int *subtype)
1251e3bd233eSbenno {
1252e3bd233eSbenno 	const struct ext_comm_pairs *cp;
1253e3bd233eSbenno 	int found = 0;
1254e3bd233eSbenno 
1255e3bd233eSbenno 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1256e3bd233eSbenno 		if (strcmp(name, cp->subname) == 0) {
1257e3bd233eSbenno 			if (found == 0) {
1258e3bd233eSbenno 				*type = cp->type;
1259e3bd233eSbenno 				*subtype = cp->subtype;
1260e3bd233eSbenno 			}
1261e3bd233eSbenno 			found++;
1262e3bd233eSbenno 		}
1263e3bd233eSbenno 	}
1264e3bd233eSbenno 	if (found > 1)
1265e3bd233eSbenno 		*type = -1;
1266e3bd233eSbenno 	return (found);
1267e3bd233eSbenno }
1268e3bd233eSbenno 
1269a73789d3Sclaudio static int
127059154960Sclaudio parseextvalue(int type, char *s, uint32_t *v, uint32_t *flag)
1271e3bd233eSbenno {
1272e3bd233eSbenno 	const char	*errstr;
1273e3bd233eSbenno 	char		*p;
1274e3bd233eSbenno 	struct in_addr	 ip;
127559154960Sclaudio 	uint32_t	 uvalh, uval;
1276e3bd233eSbenno 
1277a73789d3Sclaudio 	if (type != -1) {
1278a73789d3Sclaudio 		/* nothing */
12790ca99656Sclaudio 	} else if (strcmp(s, "neighbor-as") == 0) {
12800ca99656Sclaudio 		*flag = COMMUNITY_NEIGHBOR_AS;
12810ca99656Sclaudio 		*v = 0;
12825aca612eSclaudio 		return EXT_COMMUNITY_TRANS_TWO_AS;
12830ca99656Sclaudio 	} else if (strcmp(s, "local-as") == 0) {
12840ca99656Sclaudio 		*flag = COMMUNITY_LOCAL_AS;
12850ca99656Sclaudio 		*v = 0;
12865aca612eSclaudio 		return EXT_COMMUNITY_TRANS_TWO_AS;
1287a73789d3Sclaudio 	} else if ((p = strchr(s, '.')) == NULL) {
1288e3bd233eSbenno 		/* AS_PLAIN number (4 or 2 byte) */
1289a73789d3Sclaudio 		strtonum(s, 0, USHRT_MAX, &errstr);
1290a73789d3Sclaudio 		if (errstr == NULL)
1291a73789d3Sclaudio 			type = EXT_COMMUNITY_TRANS_TWO_AS;
1292e3bd233eSbenno 		else
1293a73789d3Sclaudio 			type = EXT_COMMUNITY_TRANS_FOUR_AS;
1294e3bd233eSbenno 	} else if (strchr(p + 1, '.') == NULL) {
1295e3bd233eSbenno 		/* AS_DOT number (4-byte) */
1296a73789d3Sclaudio 		type = EXT_COMMUNITY_TRANS_FOUR_AS;
1297e3bd233eSbenno 	} else {
1298e3bd233eSbenno 		/* more than one dot -> IP address */
1299a73789d3Sclaudio 		type = EXT_COMMUNITY_TRANS_IPV4;
1300e3bd233eSbenno 	}
1301e3bd233eSbenno 
1302c2371130Sclaudio 	switch (type & EXT_COMMUNITY_VALUE) {
1303a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_TWO_AS:
1304a73789d3Sclaudio 		uval = strtonum(s, 0, USHRT_MAX, &errstr);
1305a73789d3Sclaudio 		if (errstr)
1306a73789d3Sclaudio 			errx(1, "Bad ext-community %s is %s", s, errstr);
1307a73789d3Sclaudio 		*v = uval;
1308a73789d3Sclaudio 		break;
1309a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_FOUR_AS:
1310a73789d3Sclaudio 		if ((p = strchr(s, '.')) == NULL) {
1311a73789d3Sclaudio 			uval = strtonum(s, 0, UINT_MAX, &errstr);
1312a73789d3Sclaudio 			if (errstr)
1313a73789d3Sclaudio 				errx(1, "Bad ext-community %s is %s", s,
1314a73789d3Sclaudio 				    errstr);
1315a73789d3Sclaudio 			*v = uval;
1316a73789d3Sclaudio 			break;
1317a73789d3Sclaudio 		}
1318a73789d3Sclaudio 		*p++ = '\0';
1319a73789d3Sclaudio 		uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
1320a73789d3Sclaudio 		if (errstr)
1321a73789d3Sclaudio 			errx(1, "Bad ext-community %s is %s", s, errstr);
1322a73789d3Sclaudio 		uval = strtonum(p, 0, USHRT_MAX, &errstr);
1323a73789d3Sclaudio 		if (errstr)
1324a73789d3Sclaudio 			errx(1, "Bad ext-community %s is %s", p, errstr);
1325a73789d3Sclaudio 		*v = uval | (uvalh << 16);
1326a73789d3Sclaudio 		break;
1327a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_IPV4:
1328*f885c9d9Sflorian 		if (inet_pton(AF_INET, s, &ip) != 1)
1329a73789d3Sclaudio 			errx(1, "Bad ext-community %s not parseable", s);
1330a73789d3Sclaudio 		*v = ntohl(ip.s_addr);
1331a73789d3Sclaudio 		break;
1332a73789d3Sclaudio 	default:
1333a73789d3Sclaudio 		errx(1, "%s: unexpected type %d", __func__, type);
1334a73789d3Sclaudio 	}
1335a73789d3Sclaudio 	return (type);
1336a73789d3Sclaudio }
1337a73789d3Sclaudio 
13380ca99656Sclaudio void
13390ca99656Sclaudio parseextcommunity(struct community *c, const char *t, char *s)
1340e3bd233eSbenno {
1341e3bd233eSbenno 	const struct ext_comm_pairs *cp;
1342e3bd233eSbenno 	char		*p, *ep;
134359154960Sclaudio 	uint64_t	 ullval;
134459154960Sclaudio 	uint32_t	 uval, uval2, dflag1 = 0, dflag2 = 0;
13458b0355c9Sclaudio 	int		 type = 0, subtype = 0;
1346e3bd233eSbenno 
13470ca99656Sclaudio 	if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) {
13480ca99656Sclaudio 		c->flags = COMMUNITY_TYPE_EXT;
13490ca99656Sclaudio 		c->flags |= COMMUNITY_ANY << 24;
13500ca99656Sclaudio 		return;
13510ca99656Sclaudio 	}
1352a73789d3Sclaudio 	if (parsesubtype(t, &type, &subtype) == 0)
1353a73789d3Sclaudio 		errx(1, "Bad ext-community unknown type");
1354e3bd233eSbenno 
1355e3bd233eSbenno 	switch (type) {
1356a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_TWO_AS:
1357a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_FOUR_AS:
1358a73789d3Sclaudio 	case EXT_COMMUNITY_TRANS_IPV4:
1359c2371130Sclaudio 	case EXT_COMMUNITY_GEN_TWO_AS:
1360c2371130Sclaudio 	case EXT_COMMUNITY_GEN_FOUR_AS:
1361c2371130Sclaudio 	case EXT_COMMUNITY_GEN_IPV4:
1362a73789d3Sclaudio 	case -1:
13630ca99656Sclaudio 		if (strcmp(s, "*") == 0) {
13640ca99656Sclaudio 			dflag1 = COMMUNITY_ANY;
13650ca99656Sclaudio 			break;
13660ca99656Sclaudio 		}
1367a73789d3Sclaudio 		if ((p = strchr(s, ':')) == NULL)
1368a73789d3Sclaudio 			errx(1, "Bad ext-community %s", s);
1369e3bd233eSbenno 		*p++ = '\0';
13700ca99656Sclaudio 		type = parseextvalue(type, s, &uval, &dflag1);
13710ca99656Sclaudio 
1372e3bd233eSbenno 		switch (type) {
1373e3bd233eSbenno 		case EXT_COMMUNITY_TRANS_TWO_AS:
1374c2371130Sclaudio 		case EXT_COMMUNITY_GEN_TWO_AS:
13750ca99656Sclaudio 			getcommunity(p, 1, &uval2, &dflag2);
1376e3bd233eSbenno 			break;
1377e3bd233eSbenno 		case EXT_COMMUNITY_TRANS_IPV4:
1378e3bd233eSbenno 		case EXT_COMMUNITY_TRANS_FOUR_AS:
1379c2371130Sclaudio 		case EXT_COMMUNITY_GEN_IPV4:
1380c2371130Sclaudio 		case EXT_COMMUNITY_GEN_FOUR_AS:
13810ca99656Sclaudio 			getcommunity(p, 0, &uval2, &dflag2);
1382e3bd233eSbenno 			break;
1383e3bd233eSbenno 		default:
1384a73789d3Sclaudio 			errx(1, "parseextcommunity: unexpected result");
1385e3bd233eSbenno 		}
13860ca99656Sclaudio 
13870ca99656Sclaudio 		c->data1 = uval;
13880ca99656Sclaudio 		c->data2 = uval2;
1389e3bd233eSbenno 		break;
1390e3bd233eSbenno 	case EXT_COMMUNITY_TRANS_OPAQUE:
1391e3bd233eSbenno 	case EXT_COMMUNITY_TRANS_EVPN:
13920ca99656Sclaudio 		if (strcmp(s, "*") == 0) {
13930ca99656Sclaudio 			dflag1 = COMMUNITY_ANY;
13940ca99656Sclaudio 			break;
13950ca99656Sclaudio 		}
1396e3bd233eSbenno 		errno = 0;
1397a73789d3Sclaudio 		ullval = strtoull(s, &ep, 0);
1398a73789d3Sclaudio 		if (s[0] == '\0' || *ep != '\0')
1399a73789d3Sclaudio 			errx(1, "Bad ext-community bad value");
1400a73789d3Sclaudio 		if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX)
1401a73789d3Sclaudio 			errx(1, "Bad ext-community value too big");
14020ca99656Sclaudio 		c->data1 = ullval >> 32;
14030ca99656Sclaudio 		c->data2 = ullval;
1404e3bd233eSbenno 		break;
1405e3bd233eSbenno 	case EXT_COMMUNITY_NON_TRANS_OPAQUE:
14060ca99656Sclaudio 		if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
14070ca99656Sclaudio 			if (strcmp(s, "valid") == 0) {
14080ca99656Sclaudio 				c->data2 = EXT_COMMUNITY_OVS_VALID;
14090ca99656Sclaudio 				break;
14100ca99656Sclaudio 			} else if (strcmp(s, "invalid") == 0) {
14110ca99656Sclaudio 				c->data2 = EXT_COMMUNITY_OVS_INVALID;
14120ca99656Sclaudio 				break;
14130ca99656Sclaudio 			} else if (strcmp(s, "not-found") == 0) {
14140ca99656Sclaudio 				c->data2 = EXT_COMMUNITY_OVS_NOTFOUND;
14150ca99656Sclaudio 				break;
14160ca99656Sclaudio 			} else if (strcmp(s, "*") == 0) {
14170ca99656Sclaudio 				dflag1 = COMMUNITY_ANY;
1418e3bd233eSbenno 				break;
1419e3bd233eSbenno 			}
14200ca99656Sclaudio 		}
14210ca99656Sclaudio 		errx(1, "Bad ext-community %s", s);
14220ca99656Sclaudio 	}
14230ca99656Sclaudio 
14240ca99656Sclaudio 	c->data3 = type << 8 | subtype;
14250ca99656Sclaudio 
14260ca99656Sclaudio 	/* special handling of ext-community rt * since type is not known */
14270ca99656Sclaudio 	if (dflag1 == COMMUNITY_ANY && type == -1) {
14280ca99656Sclaudio 		c->flags = COMMUNITY_TYPE_EXT;
14290ca99656Sclaudio 		c->flags |= dflag1 << 8;
14300ca99656Sclaudio 		return;
14310ca99656Sclaudio 	}
1432e3bd233eSbenno 
1433e3bd233eSbenno 	/* verify type/subtype combo */
1434e3bd233eSbenno 	for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
1435a73789d3Sclaudio 		if (cp->type == type && cp->subtype == subtype) {
14360ca99656Sclaudio 			c->flags = COMMUNITY_TYPE_EXT;
14370ca99656Sclaudio 			c->flags |= dflag1 << 8;
14380ca99656Sclaudio 			c->flags |= dflag2 << 16;
14390ca99656Sclaudio 			return;
1440e3bd233eSbenno 		}
1441280f24a4Sphessler 	}
1442280f24a4Sphessler 
1443a73789d3Sclaudio 	errx(1, "Bad ext-community bad format for type");
1444280f24a4Sphessler }
1445280f24a4Sphessler 
14469caec0aeSclaudio int
14479caec0aeSclaudio parse_nexthop(const char *word, struct parse_result *r)
14489caec0aeSclaudio {
14499caec0aeSclaudio 	struct filter_set	*fs;
14509caec0aeSclaudio 
14519caec0aeSclaudio 	if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
14529caec0aeSclaudio 		err(1, NULL);
14539caec0aeSclaudio 
14549caec0aeSclaudio 	if (strcmp(word, "blackhole") == 0)
14559caec0aeSclaudio 		fs->type = ACTION_SET_NEXTHOP_BLACKHOLE;
14569caec0aeSclaudio 	else if (strcmp(word, "reject") == 0)
14579caec0aeSclaudio 		fs->type = ACTION_SET_NEXTHOP_REJECT;
14589caec0aeSclaudio 	else if (strcmp(word, "no-modify") == 0)
14599caec0aeSclaudio 		fs->type = ACTION_SET_NEXTHOP_NOMODIFY;
14609caec0aeSclaudio 	else if (parse_addr(word, &fs->action.nexthop)) {
14619caec0aeSclaudio 		fs->type = ACTION_SET_NEXTHOP;
14629caec0aeSclaudio 	} else {
14639caec0aeSclaudio 		free(fs);
14649caec0aeSclaudio 		return (0);
14659caec0aeSclaudio 	}
14669caec0aeSclaudio 
146738cb626eSfgsch 	TAILQ_INSERT_TAIL(&r->set, fs, entry);
14689caec0aeSclaudio 	return (1);
14699caec0aeSclaudio }
1470e3925e28Sclaudio 
1471e3925e28Sclaudio static int
1472e3925e28Sclaudio unary_op(const char *op)
1473e3925e28Sclaudio {
1474e3925e28Sclaudio 	if (strcmp(op, "=") == 0)
1475e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_EQ;
1476e3925e28Sclaudio 	if (strcmp(op, "!=") == 0)
1477e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_NOT;
1478e3925e28Sclaudio 	if (strcmp(op, ">") == 0)
1479e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_GT;
1480e3925e28Sclaudio 	if (strcmp(op, ">=") == 0)
1481e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_GE;
1482e3925e28Sclaudio 	if (strcmp(op, "<") == 0)
1483e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_LT;
1484e3925e28Sclaudio 	if (strcmp(op, "<=") == 0)
1485e3925e28Sclaudio 		return FLOWSPEC_OP_NUM_LE;
1486e3925e28Sclaudio 	return -1;
1487e3925e28Sclaudio }
1488e3925e28Sclaudio 
1489e3925e28Sclaudio static enum comp_ops
1490e3925e28Sclaudio binary_op(const char *op)
1491e3925e28Sclaudio {
1492e3925e28Sclaudio 	if (strcmp(op, "-") == 0)
1493e3925e28Sclaudio 		return OP_RANGE;
1494e3925e28Sclaudio 	if (strcmp(op, "><") == 0)
1495e3925e28Sclaudio 		return OP_XRANGE;
1496e3925e28Sclaudio 	return OP_NONE;
1497e3925e28Sclaudio }
1498e3925e28Sclaudio 
1499e3925e28Sclaudio static void
1500e3925e28Sclaudio push_numop(struct parse_result *r, int type, uint8_t op, int and, long long val)
1501e3925e28Sclaudio {
1502e3925e28Sclaudio 	uint8_t *comp;
1503e3925e28Sclaudio 	void *data;
1504e3925e28Sclaudio 	uint32_t u32;
1505e3925e28Sclaudio 	uint16_t u16;
1506e3925e28Sclaudio 	uint8_t u8, flag = 0;
1507e3925e28Sclaudio 	int len, complen;
1508e3925e28Sclaudio 
1509e3925e28Sclaudio 	flag |= op;
1510e3925e28Sclaudio 	if (and)
1511e3925e28Sclaudio 		flag |= FLOWSPEC_OP_AND;
1512e3925e28Sclaudio 
1513e3925e28Sclaudio 	if (val < 0 || val > 0xffffffff) {
1514e3925e28Sclaudio 		errx(1, "unsupported value for flowspec num_op");
1515e3925e28Sclaudio 	} else if (val <= 255) {
1516e3925e28Sclaudio 		len = 1;
1517e3925e28Sclaudio 		u8 = val;
1518e3925e28Sclaudio 		data = &u8;
1519e3925e28Sclaudio 	} else if (val <= 0xffff) {
1520e3925e28Sclaudio 		len = 2;
1521e3925e28Sclaudio 		u16 = htons(val);
1522e3925e28Sclaudio 		data = &u16;
1523e3925e28Sclaudio 		flag |= 1 << FLOWSPEC_OP_LEN_SHIFT;
1524e3925e28Sclaudio 	} else {
1525e3925e28Sclaudio 		len = 4;
1526e3925e28Sclaudio 		u32 = htonl(val);
1527e3925e28Sclaudio 		data = &u32;
1528e3925e28Sclaudio 		flag |= 2 << FLOWSPEC_OP_LEN_SHIFT;
1529e3925e28Sclaudio 	}
1530e3925e28Sclaudio 
1531e3925e28Sclaudio 	complen = r->flow.complen[type];
1532e3925e28Sclaudio 	comp = realloc(r->flow.components[type], complen + len + 1);
1533e3925e28Sclaudio 	if (comp == NULL)
1534e3925e28Sclaudio 		err(1, NULL);
1535e3925e28Sclaudio 
1536e3925e28Sclaudio 	comp[complen++] = flag;
1537e3925e28Sclaudio 	memcpy(comp + complen, data, len);
1538e3925e28Sclaudio 	complen += len;
1539e3925e28Sclaudio 	r->flow.complen[type] = complen;
1540e3925e28Sclaudio 	r->flow.components[type] = comp;
1541e3925e28Sclaudio }
1542e3925e28Sclaudio 
1543e3925e28Sclaudio int
1544e3925e28Sclaudio parse_flow_numop(int argc, char *argv[], struct parse_result *r,
1545e3925e28Sclaudio     enum token_type toktype)
1546e3925e28Sclaudio {
1547e3925e28Sclaudio 	const char *errstr;
1548e3925e28Sclaudio 	long long val, val2;
1549e3925e28Sclaudio 	int numargs, type;
1550e3925e28Sclaudio 	int is_list = 0;
1551e3925e28Sclaudio 	int op;
1552e3925e28Sclaudio 
1553e3925e28Sclaudio 	switch (toktype) {
1554e3925e28Sclaudio 	case FLOW_PROTO:
1555e3925e28Sclaudio 		type = FLOWSPEC_TYPE_PROTO;
1556e3925e28Sclaudio 		break;
1557e3925e28Sclaudio 	case FLOW_SRCPORT:
1558e3925e28Sclaudio 		type = FLOWSPEC_TYPE_SRC_PORT;
1559e3925e28Sclaudio 		break;
1560e3925e28Sclaudio 	case FLOW_DSTPORT:
1561e3925e28Sclaudio 		type = FLOWSPEC_TYPE_DST_PORT;
1562e3925e28Sclaudio 		break;
1563e3925e28Sclaudio 	case FLOW_ICMPTYPE:
1564e3925e28Sclaudio 		type = FLOWSPEC_TYPE_ICMP_TYPE;
1565e3925e28Sclaudio 		break;
1566e3925e28Sclaudio 	case FLOW_ICMPCODE:
1567e3925e28Sclaudio 		type = FLOWSPEC_TYPE_ICMP_CODE;
1568e3925e28Sclaudio 		break;
1569e3925e28Sclaudio 	case FLOW_LENGTH:
1570e3925e28Sclaudio 		type = FLOWSPEC_TYPE_PKT_LEN;
1571e3925e28Sclaudio 		break;
1572e3925e28Sclaudio 	case FLOW_DSCP:
1573e3925e28Sclaudio 		type = FLOWSPEC_TYPE_DSCP;
1574e3925e28Sclaudio 		break;
1575e3925e28Sclaudio 	default:
1576e3925e28Sclaudio 		errx(1, "parse_flow_numop called with unsupported type");
1577e3925e28Sclaudio 	}
1578e3925e28Sclaudio 
1579e3925e28Sclaudio 	/* skip keyword (which is already accounted for) */
1580e3925e28Sclaudio 	argc--;
1581e3925e28Sclaudio 	argv++;
1582e3925e28Sclaudio 	numargs = argc;
1583e3925e28Sclaudio 
1584e3925e28Sclaudio 	while (argc > 0) {
1585e3925e28Sclaudio 		if (strcmp(argv[0], "{") == 0) {
1586e3925e28Sclaudio 			is_list = 1;
1587e3925e28Sclaudio 			argc--;
1588e3925e28Sclaudio 			argv++;
1589e3925e28Sclaudio 		} else if (is_list && strcmp(argv[0], "}") == 0) {
1590e3925e28Sclaudio 			is_list = 0;
1591e3925e28Sclaudio 			argc--;
1592e3925e28Sclaudio 			argv++;
1593e3925e28Sclaudio 		} else if ((op = unary_op(argv[0])) != -1) {
1594e3925e28Sclaudio 			if (argc < 2)
1595e3925e28Sclaudio 				errx(1, "missing argument in flowspec "
1596e3925e28Sclaudio 				    "definition");
1597e3925e28Sclaudio 
1598e3925e28Sclaudio 			val = strtonum(argv[1], LLONG_MIN, LLONG_MAX, &errstr);
1599e3925e28Sclaudio 			if (errstr)
1600e3925e28Sclaudio 				errx(1, "\"%s\" invalid number: %s", argv[0],
1601e3925e28Sclaudio 				    errstr);
1602e3925e28Sclaudio 			push_numop(r, type, op, 0, val);
1603e3925e28Sclaudio 			argc -= 2;
1604e3925e28Sclaudio 			argv += 2;
1605e3925e28Sclaudio 		} else {
1606e3925e28Sclaudio 			val = strtonum(argv[0], LLONG_MIN, LLONG_MAX, &errstr);
1607e3925e28Sclaudio 			if (errstr)
1608e3925e28Sclaudio 				errx(1, "\"%s\" invalid number: %s", argv[0],
1609e3925e28Sclaudio 				    errstr);
1610e3925e28Sclaudio 			if (argc >= 3 && (op = binary_op(argv[1])) != OP_NONE) {
1611e3925e28Sclaudio 				val2 = strtonum(argv[2], LLONG_MIN, LLONG_MAX,
1612e3925e28Sclaudio 				    &errstr);
1613e3925e28Sclaudio 				if (errstr)
1614e3925e28Sclaudio 					errx(1, "\"%s\" invalid number: %s",
1615e3925e28Sclaudio 					    argv[2], errstr);
1616e3925e28Sclaudio 				switch (op) {
1617e3925e28Sclaudio 				case OP_RANGE:
1618e3925e28Sclaudio 					push_numop(r, type, FLOWSPEC_OP_NUM_GE,
1619e3925e28Sclaudio 					    0, val);
1620e3925e28Sclaudio 					push_numop(r, type, FLOWSPEC_OP_NUM_LE,
1621e3925e28Sclaudio 					    1, val2);
1622e3925e28Sclaudio 					break;
1623e3925e28Sclaudio 				case OP_XRANGE:
1624e3925e28Sclaudio 					push_numop(r, type, FLOWSPEC_OP_NUM_LT,
1625e3925e28Sclaudio 					    0, val);
1626e3925e28Sclaudio 					push_numop(r, type, FLOWSPEC_OP_NUM_GT,
1627e3925e28Sclaudio 					    0, val2);
1628e3925e28Sclaudio 					break;
1629e3925e28Sclaudio 				}
1630e3925e28Sclaudio 				argc -= 3;
1631e3925e28Sclaudio 				argv += 3;
1632e3925e28Sclaudio 			} else {
1633e3925e28Sclaudio 				push_numop(r, type, FLOWSPEC_OP_NUM_EQ, 0, val);
1634e3925e28Sclaudio 				argc--;
1635e3925e28Sclaudio 				argv++;
1636e3925e28Sclaudio 			}
1637e3925e28Sclaudio 		}
1638e3925e28Sclaudio 		if (is_list == 0)
1639e3925e28Sclaudio 			break;
1640e3925e28Sclaudio 	}
1641e3925e28Sclaudio 
1642e3925e28Sclaudio 	return numargs - argc;
1643e3925e28Sclaudio }
1644