1/* $NetBSD: conffile.c,v 1.13 2020/04/22 23:53:27 joerg Exp $ */
2
3/*
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Mihai Chelaru <kefren@NetBSD.org>
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/mman.h>
33#include <sys/stat.h>
34
35#include <arpa/inet.h>
36#include <netinet/in.h>
37
38#include <ctype.h>
39#include <fcntl.h>
40#include <stdlib.h>
41#include <string.h>
42#include <unistd.h>
43
44#include "conffile.h"
45#include "ldp_errors.h"
46
47#define NextCommand(x) strsep(&x, " ")
48#define LINEMAXSIZE 1024
49
50struct coifs_head coifs_head;
51struct conei_head conei_head;
52
53char *mapped, *nextline;
54size_t mapsize;
55
56extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time, command_port,
57	min_label, max_label, no_default_route, loop_detection;
58struct in_addr conf_ldp_id;
59
60static int conf_dispatch(char*);
61static char * conf_getlinelimit(void);
62static int checkeol(char*);
63static int Fhellotime(char*);
64static int Fport(char*);
65static int Fholddown(char*);
66static int Fkeepalive(char*);
67static int Fmaxlabel(char*);
68static int Fminlabel(char*);
69static int Fldpid(char*);
70static int Fneighbour(char*);
71static int Gneighbour(struct conf_neighbour *, char *);
72static int Fnodefault(char*);
73static int Floopdetection(char*);
74static int Finterface(char*);
75static int Ginterface(struct conf_interface *, char *);
76static int Ipassive(struct conf_interface *, char *);
77static int Itaddr(struct conf_interface *, char *);
78
79struct conf_func {
80	char com[64];
81	int (* func)(char *);
82};
83
84struct intf_func {
85	char com[64];
86	int (* func)(struct conf_interface *, char *);
87};
88
89struct conf_func main_commands[] = {
90	{ "hello-time", Fhellotime },
91	{ "keepalive-time", Fkeepalive },
92	{ "holddown-time", Fholddown },
93	{ "command-port", Fport },
94	{ "min-label", Fminlabel },
95	{ "max-label", Fmaxlabel },
96	{ "ldp-id", Fldpid },
97	{ "neighbor", Fneighbour },
98	{ "neighbour", Fneighbour },
99	{ "no-default-route", Fnodefault },
100	{ "loop-detection", Floopdetection },
101	{ "interface", Finterface },
102	{ "", NULL },
103};
104
105struct intf_func intf_commands[] = {
106	{ "passive", Ipassive },
107	{ "transport-address", Itaddr },
108	{ "", NULL },
109};
110
111static int parseline;
112
113/*
114 * Parses config file
115 */
116int
117conf_parsefile(const char *fname)
118{
119	char line[LINEMAXSIZE+1];
120	struct stat fs;
121
122	SLIST_INIT(&conei_head);
123	SLIST_INIT(&coifs_head);
124	conf_ldp_id.s_addr = 0;
125
126	int confh = open(fname, O_RDONLY, 0);
127
128	if (confh == -1 || fstat(confh, &fs) == -1 ||
129	    (mapped = mmap(NULL, fs.st_size, PROT_READ, MAP_SHARED, confh, 0))
130	    == MAP_FAILED) {
131		if (confh != -1)
132			close(confh);
133		return E_CONF_IO;
134	}
135
136	mapsize = fs.st_size;
137	nextline = mapped;
138	for (parseline = 1; ; parseline++) {
139		char *prev = nextline;
140		if ((nextline = conf_getlinelimit()) == NULL)
141			break;
142		while (isspace((int)*prev) != 0 && prev < nextline)
143			prev++;
144		if (nextline - prev < 2)
145			continue;
146		else if (nextline - prev > LINEMAXSIZE)
147			goto parerr;
148		memcpy(line, prev, nextline - prev);
149		if (line[0] == '#')
150			continue;
151		else
152			line[nextline - prev] = '\0';
153		if (conf_dispatch(line) != 0)
154			goto parerr;
155	}
156	munmap(mapped, mapsize);
157	close(confh);
158	return 0;
159parerr:
160	munmap(mapped, mapsize);
161	close(confh);
162	return parseline;
163}
164
165char *
166conf_getlinelimit(void)
167{
168	char *p = nextline;
169
170	if (nextline < mapped || (size_t)(nextline - mapped) >= mapsize)
171		return NULL;
172
173	for (p = nextline; *p != '\n' && (size_t)(p - mapped) < mapsize; p++);
174	return p + 1;
175}
176
177/*
178 * Looks for a matching command on a line
179 */
180int
181conf_dispatch(char *line)
182{
183	int i, last_match = -1, matched = 0;
184	char *command, *nline = line;
185
186	if (strlen(line) == 0 || line[0] == '#')
187		return E_CONF_OK;
188	command = NextCommand(nline);
189	for (i = 0; main_commands[i].func != NULL; i++)
190		if (strncasecmp(main_commands[i].com, command,
191		    strlen(main_commands[i].com)) == 0) {
192			matched++;
193			last_match = i;
194		}
195	if (matched == 0)
196		return E_CONF_NOMATCH;
197	else if (matched > 1)
198		return E_CONF_AMBIGUOUS;
199
200	if (nline == NULL || checkeol(nline) != 0)
201		return E_CONF_PARAM;
202	return main_commands[last_match].func(nline);
203}
204
205/*
206 * Checks if a line is terminated or else if it contains
207 * a start block bracket. If it's semicolon terminated
208 * then trim it.
209 */
210int
211checkeol(char *line)
212{
213	size_t len = strlen(line);
214	if (len > 0 && line[len - 1] == '\n') {
215		line[len - 1] = '\0';
216		len--;
217	}
218	if (len > 0 && line[len - 1] == ';') {
219		line[len - 1] = '\0';
220		return 0;
221	}
222	for (size_t i = 0; i < len; i++)
223		if (line[i] == '{')
224			return 0;
225	return -1;
226}
227
228/*
229 * Sets hello time
230 */
231int
232Fhellotime(char *line)
233{
234	int ht = atoi(line);
235	if (ht <= 0)
236		return E_CONF_PARAM;
237	ldp_hello_time = ht;
238	return 0;
239}
240
241/*
242 * Sets command port
243 */
244int
245Fport(char *line)
246{
247	int cp = atoi(line);
248	if (cp <= 0 || cp > 65535)
249		return E_CONF_PARAM;
250	command_port = cp;
251	return 0;
252}
253
254/*
255 * Sets neighbour keepalive
256 */
257int
258Fkeepalive(char *line)
259{
260	int kt = atoi(line);
261	if (kt <= 0)
262		return E_CONF_PARAM;
263	ldp_keepalive_time = kt;
264	return 0;
265}
266
267/*
268 * Sets neighbour holddown timer
269 */
270int
271Fholddown(char *line)
272{
273	int hdt = atoi(line);
274	if (hdt <= 0)
275		return E_CONF_PARAM;
276	ldp_holddown_time = hdt;
277	return 0;
278}
279
280int
281Fminlabel(char *line)
282{
283	int ml = atoi(line);
284	if (ml <= 0)
285		return E_CONF_PARAM;
286	min_label = ml;
287	return 0;
288}
289
290int
291Fmaxlabel(char *line)
292{
293	int ml = atoi(line);
294	if (ml <= 0)
295		return E_CONF_PARAM;
296	max_label = ml;
297	return 0;
298}
299
300int
301Fldpid(char *line)
302{
303	if (inet_pton(AF_INET, line, &conf_ldp_id) != 1)
304		return E_CONF_PARAM;
305	return 0;
306}
307
308int
309Fneighbour(char *line)
310{
311	char *peer;
312	struct conf_neighbour *nei;
313	struct in_addr ad;
314	char buf[LINEMAXSIZE];
315
316	peer = NextCommand(line);
317	if (inet_pton(AF_INET, peer, &ad) != 1)
318		return E_CONF_PARAM;
319
320	nei = calloc(1, sizeof(*nei));
321	if (nei == NULL)
322		return E_CONF_MEM;
323	nei->address.s_addr = ad.s_addr;
324	SLIST_INSERT_HEAD(&conei_head, nei, neilist);
325
326	for ( ; ; ) {
327		char *prev = nextline;
328		parseline++;
329		nextline = conf_getlinelimit();
330		if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
331			return -1;
332		while (isspace((int)*prev) != 0 && prev < nextline)
333			prev++;
334		memcpy(buf, prev, nextline - prev);
335		if (nextline - prev < 2 || buf[0] == '#')
336			continue;
337		else if (buf[0] == '}')
338			break;
339		else
340			buf[nextline - prev] = '\0';
341		if (Gneighbour(nei, buf) == -1)
342			return -1;
343	}
344	return -1;
345}
346
347/*
348 * neighbour { } sub-commands
349 */
350int
351Gneighbour(struct conf_neighbour *nei, char *line)
352{
353	if (strncasecmp("authenticate", line, 12) == 0) {
354		nei->authenticate = 1;
355		return 0;
356	}
357	return -1;
358}
359
360int
361Fnodefault(char *line)
362{
363	int nd = atoi(line);
364	if (nd < 0)
365		return E_CONF_PARAM;
366	no_default_route = nd;
367	return 0;
368}
369
370int
371Floopdetection(char *line)
372{
373	int loopd = atoi(line);
374	if (loopd < 0)
375		return E_CONF_PARAM;
376	loop_detection = loopd;
377	return 0;
378}
379
380/*
381 * Interface sub-commands
382 */
383int
384Finterface(char *line)
385{
386	char *ifname;
387	struct conf_interface *conf_if;
388	char buf[LINEMAXSIZE];
389
390	if ((ifname = NextCommand(line)) == NULL)
391		return -1;
392	if ((conf_if = calloc(1, sizeof(*conf_if))) == NULL)
393		return -1;
394
395	strlcpy(conf_if->if_name, ifname, IF_NAMESIZE);
396	SLIST_INSERT_HEAD(&coifs_head, conf_if, iflist);
397
398	for ( ; ; ) {
399		char *prev = nextline;
400		parseline++;
401		nextline = conf_getlinelimit();
402		if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
403			return -1;
404		while (isspace((int)*prev) != 0 && prev < nextline)
405			prev++;
406		memcpy(buf, prev, nextline - prev);
407		if (nextline - prev < 2 || buf[0] == '#')
408			continue;
409		else if (buf[0] == '}')
410			break;
411		else
412			buf[nextline - prev] = '\0';
413		if (Ginterface(conf_if, buf) == -1)
414			return -1;
415	}
416	return 0;
417}
418
419int
420Ginterface(struct conf_interface *conf_if, char *buf)
421{
422	int i;
423
424	for (i = 0; intf_commands[i].func != NULL; i++)
425		if (strncasecmp(buf, intf_commands[i].com,
426		    strlen(intf_commands[i].com)) == 0)
427			return intf_commands[i].func(conf_if, buf +
428			    strlen(intf_commands[i].com) + 1);
429	/* command not found */
430	return -1;
431}
432
433/* sets transport address */
434int
435Itaddr(struct conf_interface *conf_if, char *buf)
436{
437	if (inet_pton(AF_INET, buf, &conf_if->tr_addr) != 1)
438		return -1;
439	return 0;
440}
441
442/* sets passive-interface on */
443int
444Ipassive(struct conf_interface *conf_if, char *buf)
445{
446	conf_if->passive = 1;
447	return 0;
448}
449