conffile.c revision 1.9
1/* $NetBSD: conffile.c,v 1.9 2013/11/13 21:20:21 christos 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 <arpa/inet.h>
33#include <netinet/in.h>
34#include <sys/mman.h>
35#include <ctype.h>
36#include <fcntl.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include "conffile.h"
42#include "ldp_errors.h"
43
44#define NextCommand(x) strsep(&x, " ")
45#define LINEMAXSIZE 1024
46
47char *mapped, *nextline;
48size_t mapsize;
49
50extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time, command_port,
51	min_label, max_label, no_default_route, loop_detection;
52struct in_addr conf_ldp_id;
53
54static int conf_dispatch(char*);
55static char * conf_getlinelimit(void);
56static int checkeol(char*);
57static int Fhellotime(char*);
58static int Fport(char*);
59static int Fholddown(char*);
60static int Fkeepalive(char*);
61static int Fmaxlabel(char*);
62static int Fminlabel(char*);
63static int Fldpid(char*);
64static int Fneighbour(char*);
65static int Gneighbour(struct conf_neighbour *, char *);
66static int Fnodefault(char*);
67static int Floopdetection(char*);
68static int Finterface(char*);
69static int Ginterface(struct conf_interface *, char *);
70static int Ipassive(struct conf_interface *, char *);
71static int Itaddr(struct conf_interface *, char *);
72
73struct conf_func {
74	char com[64];
75	int (* func)(char *);
76};
77
78struct intf_func {
79	char com[64];
80	int (* func)(struct conf_interface *, char *);
81};
82
83struct conf_func main_commands[] = {
84	{ "hello-time", Fhellotime },
85	{ "keepalive-time", Fkeepalive },
86	{ "holddown-time", Fholddown },
87	{ "command-port", Fport },
88	{ "min-label", Fminlabel },
89	{ "max-label", Fmaxlabel },
90	{ "ldp-id", Fldpid },
91	{ "neighbor", Fneighbour },
92	{ "neighbour", Fneighbour },
93	{ "no-default-route", Fnodefault },
94	{ "loop-detection", Floopdetection },
95	{ "interface", Finterface },
96	{ "", NULL },
97};
98
99struct intf_func intf_commands[] = {
100	{ "passive", Ipassive },
101	{ "transport-address", Itaddr },
102	{ "", NULL },
103};
104
105static int parseline;
106
107/*
108 * Parses config file
109 */
110int
111conf_parsefile(const char *fname)
112{
113	char line[LINEMAXSIZE+1];
114	struct stat fs;
115
116	SLIST_INIT(&conei_head);
117	SLIST_INIT(&coifs_head);
118	conf_ldp_id.s_addr = 0;
119
120	int confh = open(fname, O_RDONLY, 0);
121
122	if (confh == -1 || fstat(confh, &fs) == -1 ||
123	    (mapped = mmap(NULL, fs.st_size, PROT_READ, MAP_SHARED, confh, 0))
124	    == MAP_FAILED) {
125		close(confh);
126		return E_CONF_IO;
127	}
128
129	mapsize = fs.st_size;
130	nextline = mapped;
131	for (parseline = 1; ; parseline++) {
132		char *prev = nextline;
133		if ((nextline = conf_getlinelimit()) == NULL)
134			break;
135		while (isspace((int)*prev) != 0 && prev < nextline)
136			prev++;
137		if (nextline - prev < 2)
138			continue;
139		else if (nextline - prev > LINEMAXSIZE)
140			goto parerr;
141		memcpy(line, prev, nextline - prev);
142		if (line[0] == '#')
143			continue;
144		else
145			line[nextline - prev] = '\0';
146		if (conf_dispatch(line) != 0)
147			goto parerr;
148	}
149	munmap(mapped, mapsize);
150	close(confh);
151	return 0;
152parerr:
153	munmap(mapped, mapsize);
154	close(confh);
155	return parseline;
156}
157
158char *
159conf_getlinelimit(void)
160{
161	char *p = nextline;
162
163	if (nextline < mapped || (size_t)(nextline - mapped) >= mapsize)
164		return NULL;
165
166	for (p = nextline; *p != '\n' && (size_t)(p - mapped) < mapsize; p++);
167	return p + 1;
168}
169
170/*
171 * Looks for a matching command on a line
172 */
173int
174conf_dispatch(char *line)
175{
176	int i, last_match = -1, matched = 0;
177	char *command, *nline = line;
178
179	if (strlen(line) == 0 || line[0] == '#')
180		return E_CONF_OK;
181	command = NextCommand(nline);
182	for (i = 0; main_commands[i].func != NULL; i++)
183		if (strncasecmp(main_commands[i].com, command,
184		    strlen(main_commands[i].com)) == 0) {
185			matched++;
186			last_match = i;
187		}
188	if (matched == 0)
189		return E_CONF_NOMATCH;
190	else if (matched > 1)
191		return E_CONF_AMBIGUOUS;
192
193	if (nline == NULL || checkeol(nline) != 0)
194		return E_CONF_PARAM;
195	return main_commands[last_match].func(nline);
196}
197
198/*
199 * Checks if a line is terminated or else if it contains
200 * a start block bracket. If it's semicolon terminated
201 * then trim it.
202 */
203int
204checkeol(char *line)
205{
206	size_t len = strlen(line);
207	if (len > 0 && line[len - 1] == '\n') {
208		line[len - 1] = '\0';
209		len--;
210	}
211	if (len > 0 && line[len - 1] == ';') {
212		line[len - 1] = '\0';
213		return 0;
214	}
215	for (size_t i = 0; i < len; i++)
216		if (line[i] == '{')
217			return 0;
218	return -1;
219}
220
221/*
222 * Sets hello time
223 */
224int
225Fhellotime(char *line)
226{
227	int ht = atoi(line);
228	if (ht <= 0)
229		return E_CONF_PARAM;
230	ldp_hello_time = ht;
231	return 0;
232}
233
234/*
235 * Sets command port
236 */
237int
238Fport(char *line)
239{
240	int cp = atoi(line);
241	if (cp <= 0 || cp > 65535)
242		return E_CONF_PARAM;
243	command_port = cp;
244	return 0;
245}
246
247/*
248 * Sets neighbour keepalive
249 */
250int
251Fkeepalive(char *line)
252{
253	int kt = atoi(line);
254	if (kt <= 0)
255		return E_CONF_PARAM;
256	ldp_keepalive_time = kt;
257	return 0;
258}
259
260/*
261 * Sets neighbour holddown timer
262 */
263int
264Fholddown(char *line)
265{
266	int hdt = atoi(line);
267	if (hdt <= 0)
268		return E_CONF_PARAM;
269	ldp_holddown_time = hdt;
270	return 0;
271}
272
273int
274Fminlabel(char *line)
275{
276	int ml = atoi(line);
277	if (ml <= 0)
278		return E_CONF_PARAM;
279	min_label = ml;
280	return 0;
281}
282
283int
284Fmaxlabel(char *line)
285{
286	int ml = atoi(line);
287	if (ml <= 0)
288		return E_CONF_PARAM;
289	max_label = ml;
290	return 0;
291}
292
293int
294Fldpid(char *line)
295{
296	if (inet_pton(AF_INET, line, &conf_ldp_id) != 1)
297		return E_CONF_PARAM;
298	return 0;
299}
300
301int
302Fneighbour(char *line)
303{
304	char *peer;
305	struct conf_neighbour *nei;
306	struct in_addr ad;
307	char buf[LINEMAXSIZE];
308
309	peer = NextCommand(line);
310	if (inet_pton(AF_INET, peer, &ad) != 1)
311		return E_CONF_PARAM;
312
313	nei = calloc(1, sizeof(*nei));
314	if (nei == NULL)
315		return E_CONF_MEM;
316	nei->address.s_addr = ad.s_addr;
317	SLIST_INSERT_HEAD(&conei_head, nei, neilist);
318
319	for ( ; ; ) {
320		char *prev = nextline;
321		parseline++;
322		nextline = conf_getlinelimit();
323		if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
324			return -1;
325		while (isspace((int)*prev) != 0 && prev < nextline)
326			prev++;
327		memcpy(buf, prev, nextline - prev);
328		if (nextline - prev < 2 || buf[0] == '#')
329			continue;
330		else if (buf[0] == '}')
331			break;
332		else
333			buf[nextline - prev] = '\0';
334		if (Gneighbour(nei, buf) == -1)
335			return -1;
336	}
337	return -1;
338}
339
340/*
341 * neighbour { } sub-commands
342 */
343int
344Gneighbour(struct conf_neighbour *nei, char *line)
345{
346	if (strncasecmp("authenticate", line, 12) == 0) {
347		nei->authenticate = 1;
348		return 0;
349	}
350	return -1;
351}
352
353int
354Fnodefault(char *line)
355{
356	int nd = atoi(line);
357	if (nd < 0)
358		return E_CONF_PARAM;
359	no_default_route = nd;
360	return 0;
361}
362
363int
364Floopdetection(char *line)
365{
366	int loopd = atoi(line);
367	if (loopd < 0)
368		return E_CONF_PARAM;
369	loop_detection = loopd;
370	return 0;
371}
372
373/*
374 * Interface sub-commands
375 */
376int
377Finterface(char *line)
378{
379	char *ifname;
380	struct conf_interface *conf_if = calloc(1, sizeof(*conf_if));
381	char buf[LINEMAXSIZE];
382
383	ifname = NextCommand(line);
384	if (conf_if == NULL || ifname == NULL)
385		return -1;
386	strlcpy(conf_if->if_name, ifname, IF_NAMESIZE);
387	SLIST_INSERT_HEAD(&coifs_head, conf_if, iflist);
388
389	for ( ; ; ) {
390		char *prev = nextline;
391		parseline++;
392		nextline = conf_getlinelimit();
393		if (nextline == NULL || (size_t)(nextline - prev) > LINEMAXSIZE)
394			return -1;
395		while (isspace((int)*prev) != 0 && prev < nextline)
396			prev++;
397		memcpy(buf, prev, nextline - prev);
398		if (nextline - prev < 2 || buf[0] == '#')
399			continue;
400		else if (buf[0] == '}')
401			break;
402		else
403			buf[nextline - prev] = '\0';
404		if (Ginterface(conf_if, buf) == -1)
405			return -1;
406	}
407	return 0;
408}
409
410int
411Ginterface(struct conf_interface *conf_if, char *buf)
412{
413	int i;
414
415	for (i = 0; intf_commands[i].func != NULL; i++)
416		if (strncasecmp(buf, intf_commands[i].com,
417		    strlen(intf_commands[i].com)) == 0)
418			return intf_commands[i].func(conf_if, buf +
419			    strlen(intf_commands[i].com) + 1);
420	/* command not found */
421	return -1;
422}
423
424/* sets transport address */
425int
426Itaddr(struct conf_interface *conf_if, char *buf)
427{
428	if (inet_pton(AF_INET, buf, &conf_if->tr_addr) != 1)
429		return -1;
430	return 0;
431}
432
433/* sets passive-interface on */
434int
435Ipassive(struct conf_interface *conf_if, char *buf)
436{
437	conf_if->passive = 1;
438	return 0;
439}
440