xref: /openbsd-src/usr.sbin/ospfctl/parser.c (revision 213432118bd827963ed7a3911e52f4007b431711)
1*21343211Sremi /*	$OpenBSD: parser.c,v 1.21 2019/05/16 21:07:33 remi Exp $ */
2b49634deSclaudio 
3b49634deSclaudio /*
42090e587Snorby  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
5b49634deSclaudio  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6b49634deSclaudio  *
7b49634deSclaudio  * Permission to use, copy, modify, and distribute this software for any
8b49634deSclaudio  * purpose with or without fee is hereby granted, provided that the above
9b49634deSclaudio  * copyright notice and this permission notice appear in all copies.
10b49634deSclaudio  *
11b49634deSclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12b49634deSclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13b49634deSclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14b49634deSclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15b49634deSclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16b49634deSclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17b49634deSclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18b49634deSclaudio  */
19b49634deSclaudio 
20b49634deSclaudio #include <sys/types.h>
21b49634deSclaudio #include <sys/socket.h>
22b49634deSclaudio #include <netinet/in.h>
23b49634deSclaudio #include <arpa/inet.h>
24b49634deSclaudio #include <err.h>
25b49634deSclaudio #include <errno.h>
26b49634deSclaudio #include <limits.h>
27b49634deSclaudio #include <stdio.h>
28b49634deSclaudio #include <stdlib.h>
29b49634deSclaudio #include <string.h>
30b49634deSclaudio 
31c47b7f65Sclaudio #include "ospfd.h"
32c47b7f65Sclaudio 
33b49634deSclaudio #include "parser.h"
34b49634deSclaudio 
35b49634deSclaudio enum token_type {
36b49634deSclaudio 	NOTOKEN,
37b49634deSclaudio 	ENDTOKEN,
38b49634deSclaudio 	KEYWORD,
39b49634deSclaudio 	ADDRESS,
40b49634deSclaudio 	FLAG,
41b49634deSclaudio 	PREFIX,
42*21343211Sremi 	IFNAME,
43*21343211Sremi 	AREA
44b49634deSclaudio };
45b49634deSclaudio 
46b49634deSclaudio struct token {
47b49634deSclaudio 	enum token_type		 type;
48b49634deSclaudio 	const char		*keyword;
49b49634deSclaudio 	int			 value;
50b49634deSclaudio 	const struct token	*next;
51b49634deSclaudio };
52b49634deSclaudio 
53b49634deSclaudio static const struct token t_main[];
5473a141beSclaudio static const struct token t_fib[];
55b49634deSclaudio static const struct token t_show[];
56b49634deSclaudio static const struct token t_show_iface[];
57b49634deSclaudio static const struct token t_show_db[];
58b49634deSclaudio static const struct token t_show_area[];
59b49634deSclaudio static const struct token t_show_nbr[];
6037355230Snorby static const struct token t_show_rib[];
61c47b7f65Sclaudio static const struct token t_show_fib[];
62c3319070Sclaudio static const struct token t_log[];
63b49634deSclaudio 
64b49634deSclaudio static const struct token t_main[] = {
65247faaa3Sclaudio 	{KEYWORD,	"reload",	RELOAD,		NULL},
6673a141beSclaudio 	{KEYWORD,	"fib",		FIB,		t_fib},
67b49634deSclaudio 	{KEYWORD,	"show",		SHOW,		t_show},
68c3319070Sclaudio 	{KEYWORD,	"log",		NONE,		t_log},
69b49634deSclaudio 	{ENDTOKEN,	"",		NONE,		NULL}
70b49634deSclaudio };
71b49634deSclaudio 
7273a141beSclaudio static const struct token t_fib[] = {
7373a141beSclaudio 	{ KEYWORD,	"couple",	FIB_COUPLE,	NULL},
7473a141beSclaudio 	{ KEYWORD,	"decouple",	FIB_DECOUPLE,	NULL},
75119f0f1dSdlg 	{ KEYWORD,	"reload",	FIB_RELOAD,	NULL},
7673a141beSclaudio 	{ ENDTOKEN,	"",		NONE,		NULL}
7773a141beSclaudio };
7873a141beSclaudio 
79b49634deSclaudio static const struct token t_show[] = {
80b49634deSclaudio 	{NOTOKEN,	"",		NONE,		NULL},
81b49634deSclaudio 	{KEYWORD,	"interfaces",	SHOW_IFACE,	t_show_iface},
82b49634deSclaudio 	{KEYWORD,	"database",	SHOW_DB,	t_show_db},
83b49634deSclaudio 	{KEYWORD,	"neighbor",	SHOW_NBR,	t_show_nbr},
8437355230Snorby 	{KEYWORD,	"rib",		SHOW_RIB,	t_show_rib},
85c47b7f65Sclaudio 	{KEYWORD,	"fib",		SHOW_FIB,	t_show_fib},
8659df52c7Snorby 	{KEYWORD,	"summary",	SHOW_SUM,	NULL},
87b49634deSclaudio 	{ENDTOKEN,	"",		NONE,		NULL}
88b49634deSclaudio };
89b49634deSclaudio 
90b49634deSclaudio static const struct token t_show_iface[] = {
91b49634deSclaudio 	{NOTOKEN,	"",		NONE,			NULL},
9212bb42a0Snorby 	{KEYWORD,	"detail",	SHOW_IFACE_DTAIL,	NULL},
9312bb42a0Snorby 	{IFNAME,	"",		SHOW_IFACE_DTAIL,	NULL},
94b49634deSclaudio 	{ENDTOKEN,	"",		NONE,			NULL}
95b49634deSclaudio };
96b49634deSclaudio 
97b49634deSclaudio static const struct token t_show_db[] = {
98b49634deSclaudio 	{NOTOKEN,	"",			NONE,		NULL},
99b49634deSclaudio 	{KEYWORD,	"area",			SHOW_DBBYAREA,	t_show_area},
10032286112Snorby 	{KEYWORD,	"asbr",			SHOW_DBASBR,	NULL},
10132286112Snorby 	{KEYWORD,	"external",		SHOW_DBEXT,	NULL},
10232286112Snorby 	{KEYWORD,	"network",		SHOW_DBNET,	NULL},
10332286112Snorby 	{KEYWORD,	"router",		SHOW_DBRTR,	NULL},
10432286112Snorby 	{KEYWORD,	"self-originated",	SHOW_DBSELF,	NULL},
10532286112Snorby 	{KEYWORD,	"summary",		SHOW_DBSUM,	NULL},
10697494e25Sclaudio 	{KEYWORD,	"opaque",		SHOW_DBOPAQ,	NULL},
107b49634deSclaudio 	{ENDTOKEN,	"",			NONE,		NULL}
108b49634deSclaudio };
109b49634deSclaudio 
110b49634deSclaudio static const struct token t_show_area[] = {
111*21343211Sremi 	{AREA,		"",		NONE,		NULL},
112b49634deSclaudio 	{ENDTOKEN,	"",		NONE,		NULL}
113b49634deSclaudio };
114b49634deSclaudio 
115b49634deSclaudio static const struct token t_show_nbr[] = {
116b49634deSclaudio 	{NOTOKEN,	"",		NONE,		NULL},
117b49634deSclaudio 	{KEYWORD,	"detail",	SHOW_NBR_DTAIL,	NULL},
118b49634deSclaudio 	{ENDTOKEN,	"",		NONE,		NULL}
119b49634deSclaudio };
120b49634deSclaudio 
12137355230Snorby static const struct token t_show_rib[] = {
12237355230Snorby 	{NOTOKEN,	"",		NONE,		NULL},
12337355230Snorby 	{KEYWORD,	"detail",	SHOW_RIB_DTAIL,	NULL},
12437355230Snorby 	{ENDTOKEN,	"",		NONE,		NULL}
12537355230Snorby };
12637355230Snorby 
127c47b7f65Sclaudio static const struct token t_show_fib[] = {
128c47b7f65Sclaudio 	{NOTOKEN,	"",		NONE,			NULL},
1294c561fb6Sclaudio 	{KEYWORD,	"interface",	SHOW_FIB_IFACE,		t_show_iface},
130c47b7f65Sclaudio 	{FLAG,		"connected",	F_CONNECTED,		t_show_fib},
131c47b7f65Sclaudio 	{FLAG,		"static",	F_STATIC,		t_show_fib},
132c47b7f65Sclaudio 	{FLAG,		"ospf",		F_OSPFD_INSERTED,	t_show_fib},
133c47b7f65Sclaudio 	{ADDRESS,	"",		NONE,			NULL},
134c47b7f65Sclaudio 	{ENDTOKEN,	"",		NONE,			NULL}
135c47b7f65Sclaudio };
136c47b7f65Sclaudio 
137c3319070Sclaudio static const struct token t_log[] = {
138c3319070Sclaudio 	{KEYWORD,	"verbose",	LOG_VERBOSE,		NULL},
139c3319070Sclaudio 	{KEYWORD,	"brief",	LOG_BRIEF,		NULL},
140c3319070Sclaudio 	{ENDTOKEN,	"",		NONE,			NULL}
141c3319070Sclaudio };
142c3319070Sclaudio 
1436be5843eStedu static const struct token *match_token(const char *, const struct token *,
1446be5843eStedu     struct parse_result *);
1456be5843eStedu static void show_valid_args(const struct token *);
146b49634deSclaudio 
147b49634deSclaudio struct parse_result *
parse(int argc,char * argv[])148b49634deSclaudio parse(int argc, char *argv[])
149b49634deSclaudio {
1506be5843eStedu 	static struct parse_result	res;
151b49634deSclaudio 	const struct token	*table = t_main;
152b49634deSclaudio 	const struct token	*match;
153b49634deSclaudio 
154b49634deSclaudio 	bzero(&res, sizeof(res));
155b49634deSclaudio 
156532600dcSclaudio 	while (argc >= 0) {
1576be5843eStedu 		if ((match = match_token(argv[0], table, &res)) == NULL) {
158b49634deSclaudio 			fprintf(stderr, "valid commands/args:\n");
159b49634deSclaudio 			show_valid_args(table);
160b49634deSclaudio 			return (NULL);
161b49634deSclaudio 		}
162b49634deSclaudio 
163b49634deSclaudio 		argc--;
164b49634deSclaudio 		argv++;
165b49634deSclaudio 
166b49634deSclaudio 		if (match->type == NOTOKEN || match->next == NULL)
167b49634deSclaudio 			break;
168b49634deSclaudio 
169b49634deSclaudio 		table = match->next;
170b49634deSclaudio 	}
171b49634deSclaudio 
172b49634deSclaudio 	if (argc > 0) {
173b49634deSclaudio 		fprintf(stderr, "superfluous argument: %s\n", argv[0]);
174b49634deSclaudio 		return (NULL);
175b49634deSclaudio 	}
176b49634deSclaudio 
177b49634deSclaudio 	return (&res);
178b49634deSclaudio }
179b49634deSclaudio 
1806be5843eStedu static const struct token *
match_token(const char * word,const struct token * table,struct parse_result * res)1816be5843eStedu match_token(const char *word, const struct token *table,
1826be5843eStedu     struct parse_result *res)
183b49634deSclaudio {
184b49634deSclaudio 	u_int			 i, match;
185b49634deSclaudio 	const struct token	*t = NULL;
186b49634deSclaudio 
187b49634deSclaudio 	match = 0;
188b49634deSclaudio 
189b49634deSclaudio 	for (i = 0; table[i].type != ENDTOKEN; i++) {
190b49634deSclaudio 		switch (table[i].type) {
191b49634deSclaudio 		case NOTOKEN:
192b49634deSclaudio 			if (word == NULL || strlen(word) == 0) {
193b49634deSclaudio 				match++;
194b49634deSclaudio 				t = &table[i];
195b49634deSclaudio 			}
196b49634deSclaudio 			break;
197b49634deSclaudio 		case KEYWORD:
198b49634deSclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
199b49634deSclaudio 			    strlen(word)) == 0) {
200b49634deSclaudio 				match++;
201b49634deSclaudio 				t = &table[i];
202b49634deSclaudio 				if (t->value)
2036be5843eStedu 					res->action = t->value;
204b49634deSclaudio 			}
205b49634deSclaudio 			break;
206b49634deSclaudio 		case FLAG:
207b49634deSclaudio 			if (word != NULL && strncmp(word, table[i].keyword,
208b49634deSclaudio 			    strlen(word)) == 0) {
209b49634deSclaudio 				match++;
210b49634deSclaudio 				t = &table[i];
2116be5843eStedu 				res->flags |= t->value;
212b49634deSclaudio 			}
213b49634deSclaudio 			break;
214b49634deSclaudio 		case ADDRESS:
2156be5843eStedu 			if (parse_addr(word, &res->addr)) {
216b49634deSclaudio 				match++;
217b49634deSclaudio 				t = &table[i];
218b49634deSclaudio 				if (t->value)
2196be5843eStedu 					res->action = t->value;
220b49634deSclaudio 			}
221b49634deSclaudio 			break;
222*21343211Sremi 		case AREA:
223*21343211Sremi 			if (parse_area(word, &res->addr)) {
224*21343211Sremi 				match++;
225*21343211Sremi 				t = &table[i];
226*21343211Sremi 				if (t->value)
227*21343211Sremi 					res->action = t->value;
228*21343211Sremi 			}
229*21343211Sremi 			break;
230b49634deSclaudio 		case PREFIX:
2316be5843eStedu 			if (parse_prefix(word, &res->addr, &res->prefixlen)) {
232b49634deSclaudio 				match++;
233b49634deSclaudio 				t = &table[i];
234b49634deSclaudio 				if (t->value)
2356be5843eStedu 					res->action = t->value;
236b49634deSclaudio 			}
237b49634deSclaudio 			break;
238b49634deSclaudio 		case IFNAME:
239b49634deSclaudio 			if (!match && word != NULL && strlen(word) > 0) {
2406be5843eStedu 				if (strlcpy(res->ifname, word,
2416be5843eStedu 				    sizeof(res->ifname)) >=
2426be5843eStedu 				    sizeof(res->ifname))
243b49634deSclaudio 					err(1, "interface name too long");
244b49634deSclaudio 				match++;
245b49634deSclaudio 				t = &table[i];
24612bb42a0Snorby 				if (t->value)
2476be5843eStedu 					res->action = t->value;
248b49634deSclaudio 			}
249b49634deSclaudio 			break;
250b49634deSclaudio 
251b49634deSclaudio 		case ENDTOKEN:
252b49634deSclaudio 			break;
253b49634deSclaudio 		}
254b49634deSclaudio 	}
255b49634deSclaudio 
256b49634deSclaudio 	if (match != 1) {
257532600dcSclaudio 		if (word == NULL)
258532600dcSclaudio 			fprintf(stderr, "missing argument:\n");
259532600dcSclaudio 		else if (match > 1)
260b49634deSclaudio 			fprintf(stderr, "ambiguous argument: %s\n", word);
261532600dcSclaudio 		else if (match < 1)
262b49634deSclaudio 			fprintf(stderr, "unknown argument: %s\n", word);
263b49634deSclaudio 		return (NULL);
264b49634deSclaudio 	}
265b49634deSclaudio 
266b49634deSclaudio 	return (t);
267b49634deSclaudio }
268b49634deSclaudio 
2696be5843eStedu static void
show_valid_args(const struct token * table)270e331d0fcSjsg show_valid_args(const struct token *table)
271b49634deSclaudio {
272b49634deSclaudio 	int	i;
273b49634deSclaudio 
274b49634deSclaudio 	for (i = 0; table[i].type != ENDTOKEN; i++) {
275b49634deSclaudio 		switch (table[i].type) {
276b49634deSclaudio 		case NOTOKEN:
277b49634deSclaudio 			fprintf(stderr, "  <cr>\n");
278b49634deSclaudio 			break;
279b49634deSclaudio 		case KEYWORD:
280b49634deSclaudio 		case FLAG:
281b49634deSclaudio 			fprintf(stderr, "  %s\n", table[i].keyword);
282b49634deSclaudio 			break;
283b49634deSclaudio 		case ADDRESS:
284b49634deSclaudio 			fprintf(stderr, "  <address>\n");
285b49634deSclaudio 			break;
286*21343211Sremi 		case AREA:
287*21343211Sremi 			fprintf(stderr, "  <area>\n");
288*21343211Sremi 			break;
289b49634deSclaudio 		case PREFIX:
290b49634deSclaudio 			fprintf(stderr, "  <address>[/<len>]\n");
291b49634deSclaudio 			break;
292b49634deSclaudio 		case IFNAME:
293b49634deSclaudio 			fprintf(stderr, "  <interface>\n");
294c5ba8b41Sderaadt 			break;
295b49634deSclaudio 		case ENDTOKEN:
296b49634deSclaudio 			break;
297b49634deSclaudio 		}
298b49634deSclaudio 	}
299b49634deSclaudio }
300b49634deSclaudio 
301b49634deSclaudio int
parse_addr(const char * word,struct in_addr * addr)302b49634deSclaudio parse_addr(const char *word, struct in_addr *addr)
303b49634deSclaudio {
304b49634deSclaudio 	struct in_addr	ina;
305b49634deSclaudio 
306b49634deSclaudio 	if (word == NULL)
307b49634deSclaudio 		return (0);
308b49634deSclaudio 
309b49634deSclaudio 	bzero(addr, sizeof(struct in_addr));
310b49634deSclaudio 	bzero(&ina, sizeof(ina));
311b49634deSclaudio 
312b49634deSclaudio 	if (inet_pton(AF_INET, word, &ina)) {
313b49634deSclaudio 		addr->s_addr = ina.s_addr;
314b49634deSclaudio 		return (1);
315b49634deSclaudio 	}
316b49634deSclaudio 
317b49634deSclaudio 	return (0);
318b49634deSclaudio }
319b49634deSclaudio 
320b49634deSclaudio int
parse_area(const char * word,struct in_addr * addr)321*21343211Sremi parse_area(const char *word, struct in_addr *addr)
322*21343211Sremi {
323*21343211Sremi 	struct in_addr	 ina;
324*21343211Sremi 	const char	*errstr;
325*21343211Sremi 
326*21343211Sremi 	if (word == NULL)
327*21343211Sremi 		return (0);
328*21343211Sremi 
329*21343211Sremi 	bzero(addr, sizeof(struct in_addr));
330*21343211Sremi 	bzero(&ina, sizeof(ina));
331*21343211Sremi 
332*21343211Sremi 	if (inet_pton(AF_INET, word, &ina)) {
333*21343211Sremi 		addr->s_addr = ina.s_addr;
334*21343211Sremi 		return (1);
335*21343211Sremi 	}
336*21343211Sremi 
337*21343211Sremi 	ina.s_addr = htonl(strtonum(word, 0, 0xffffffff, &errstr));
338*21343211Sremi 	if (errstr == NULL) {
339*21343211Sremi 		addr->s_addr = ina.s_addr;
340*21343211Sremi 		return (1);
341*21343211Sremi 	}
342*21343211Sremi 
343*21343211Sremi 	return (0);
344*21343211Sremi }
345*21343211Sremi 
346*21343211Sremi int
parse_prefix(const char * word,struct in_addr * addr,u_int8_t * prefixlen)347b49634deSclaudio parse_prefix(const char *word, struct in_addr *addr, u_int8_t *prefixlen)
348b49634deSclaudio {
349b49634deSclaudio 	struct in_addr	 ina;
350b49634deSclaudio 	int		 bits = 32;
351b49634deSclaudio 
352b49634deSclaudio 	if (word == NULL)
353b49634deSclaudio 		return (0);
354b49634deSclaudio 
355b49634deSclaudio 	bzero(addr, sizeof(struct in_addr));
356b49634deSclaudio 	bzero(&ina, sizeof(ina));
357b49634deSclaudio 
358b49634deSclaudio 	if (strrchr(word, '/') != NULL) {
359b49634deSclaudio 		if ((bits = inet_net_pton(AF_INET, word,
360b49634deSclaudio 		    &ina, sizeof(ina))) == -1)
361b49634deSclaudio 			return (0);
3627d92a97aShenning 		addr->s_addr = ina.s_addr & htonl(prefixlen2mask(bits));
363b49634deSclaudio 		*prefixlen = bits;
364b49634deSclaudio 		return (1);
365c5ba8b41Sderaadt 	}
366b49634deSclaudio 	*prefixlen = 32;
367b49634deSclaudio 	return (parse_addr(word, addr));
368b49634deSclaudio }
369b49634deSclaudio 
3707d92a97aShenning /* XXX local copy from kroute.c, should go to shared file */
3717d92a97aShenning in_addr_t
prefixlen2mask(u_int8_t prefixlen)3727d92a97aShenning prefixlen2mask(u_int8_t prefixlen)
3737d92a97aShenning {
3747d92a97aShenning 	if (prefixlen == 0)
3757d92a97aShenning 		return (0);
3767d92a97aShenning 
3777d92a97aShenning 	return (0xffffffff << (32 - prefixlen));
3787d92a97aShenning }
379