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
50 struct coifs_head coifs_head;
51 struct conei_head conei_head;
52
53 char *mapped, *nextline;
54 size_t mapsize;
55
56 extern int ldp_hello_time, ldp_keepalive_time, ldp_holddown_time, command_port,
57 min_label, max_label, no_default_route, loop_detection;
58 struct in_addr conf_ldp_id;
59
60 static int conf_dispatch(char*);
61 static char * conf_getlinelimit(void);
62 static int checkeol(char*);
63 static int Fhellotime(char*);
64 static int Fport(char*);
65 static int Fholddown(char*);
66 static int Fkeepalive(char*);
67 static int Fmaxlabel(char*);
68 static int Fminlabel(char*);
69 static int Fldpid(char*);
70 static int Fneighbour(char*);
71 static int Gneighbour(struct conf_neighbour *, char *);
72 static int Fnodefault(char*);
73 static int Floopdetection(char*);
74 static int Finterface(char*);
75 static int Ginterface(struct conf_interface *, char *);
76 static int Ipassive(struct conf_interface *, char *);
77 static int Itaddr(struct conf_interface *, char *);
78
79 struct conf_func {
80 char com[64];
81 int (* func)(char *);
82 };
83
84 struct intf_func {
85 char com[64];
86 int (* func)(struct conf_interface *, char *);
87 };
88
89 struct 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
105 struct intf_func intf_commands[] = {
106 { "passive", Ipassive },
107 { "transport-address", Itaddr },
108 { "", NULL },
109 };
110
111 static int parseline;
112
113 /*
114 * Parses config file
115 */
116 int
conf_parsefile(const char * fname)117 conf_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;
159 parerr:
160 munmap(mapped, mapsize);
161 close(confh);
162 return parseline;
163 }
164
165 char *
conf_getlinelimit(void)166 conf_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 */
180 int
conf_dispatch(char * line)181 conf_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 */
210 int
checkeol(char * line)211 checkeol(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 */
231 int
Fhellotime(char * line)232 Fhellotime(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 */
244 int
Fport(char * line)245 Fport(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 */
257 int
Fkeepalive(char * line)258 Fkeepalive(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 */
270 int
Fholddown(char * line)271 Fholddown(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
280 int
Fminlabel(char * line)281 Fminlabel(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
290 int
Fmaxlabel(char * line)291 Fmaxlabel(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
300 int
Fldpid(char * line)301 Fldpid(char *line)
302 {
303 if (inet_pton(AF_INET, line, &conf_ldp_id) != 1)
304 return E_CONF_PARAM;
305 return 0;
306 }
307
308 int
Fneighbour(char * line)309 Fneighbour(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 */
350 int
Gneighbour(struct conf_neighbour * nei,char * line)351 Gneighbour(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
360 int
Fnodefault(char * line)361 Fnodefault(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
370 int
Floopdetection(char * line)371 Floopdetection(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 */
383 int
Finterface(char * line)384 Finterface(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
419 int
Ginterface(struct conf_interface * conf_if,char * buf)420 Ginterface(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 */
434 int
Itaddr(struct conf_interface * conf_if,char * buf)435 Itaddr(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 */
443 int
Ipassive(struct conf_interface * conf_if,char * buf)444 Ipassive(struct conf_interface *conf_if, char *buf)
445 {
446 conf_if->passive = 1;
447 return 0;
448 }
449