xref: /openbsd-src/usr.sbin/ripd/parse.y (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: parse.y,v 1.28 2010/08/03 18:42:41 henning Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Michele Marchetto <mydecay@openbeer.it>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 %{
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <unistd.h>
35 #include <ifaddrs.h>
36 #include <limits.h>
37 #include <stdarg.h>
38 #include <stdio.h>
39 #include <string.h>
40 
41 #include "ripd.h"
42 #include "rip.h"
43 #include "ripe.h"
44 #include "log.h"
45 
46 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
47 static struct file {
48 	TAILQ_ENTRY(file)	 entry;
49 	FILE			*stream;
50 	char			*name;
51 	int			 lineno;
52 	int			 errors;
53 } *file, *topfile;
54 struct file	*pushfile(const char *, int);
55 int		 popfile(void);
56 int		 yyparse(void);
57 int		 yylex(void);
58 int		 yyerror(const char *, ...);
59 int		 kw_cmp(const void *, const void *);
60 int		 lookup(char *);
61 int		 lgetc(int);
62 int		 lungetc(int);
63 int		 findeol(void);
64 
65 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
66 struct sym {
67 	TAILQ_ENTRY(sym)	 entry;
68 	int			 used;
69 	int			 persist;
70 	char			*nam;
71 	char			*val;
72 };
73 int		 symset(const char *, const char *, int);
74 char		*symget(const char *);
75 
76 static struct {
77 	u_int8_t		 auth_key[MAX_SIMPLE_AUTH_LEN];
78 	struct auth_md_head	 md_list;
79 	enum auth_type		 auth_type;
80 	u_int8_t		 auth_keyid;
81 	u_int8_t		 cost;
82 } *defs, globaldefs, ifacedefs;
83 
84 struct iface	*iface = NULL;
85 static struct ripd_conf	*conf;
86 static int		 errors = 0;
87 
88 struct iface	*conf_get_if(struct kif *);
89 void		 clear_config(struct ripd_conf *);
90 int		 check_file_secrecy(int, const char *);
91 u_int32_t	 get_rtr_id(void);
92 int		 host(const char *, struct in_addr *, struct in_addr *);
93 
94 typedef struct {
95 	union {
96 		int64_t		 number;
97 		char		*string;
98 	} v;
99 	int lineno;
100 } YYSTYPE;
101 
102 %}
103 
104 %token	SPLIT_HORIZON TRIGGERED_UPDATES FIBUPDATE REDISTRIBUTE RDOMAIN
105 %token	AUTHKEY AUTHTYPE AUTHMD AUTHMDKEYID
106 %token	INTERFACE RTLABEL
107 %token	COST PASSIVE
108 %token	YES NO
109 %token	DEMOTE
110 %token	ERROR
111 %token	<v.string>	STRING
112 %token	<v.number>	NUMBER
113 %type	<v.number>	yesno no
114 %type	<v.string>	string
115 
116 %%
117 
118 grammar		: /* empty */
119 		| grammar '\n'
120 		| grammar conf_main '\n'
121 		| grammar varset '\n'
122 		| grammar interface '\n'
123 		| grammar error '\n'		{ file->errors++; }
124 		;
125 
126 string		: string STRING {
127 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
128 				free($1);
129 				free($2);
130 				yyerror("string: asprintf");
131 				YYERROR;
132 			}
133 			free($1);
134 			free($2);
135 		}
136 		| STRING
137 		;
138 
139 yesno		: YES	{ $$ = 1; }
140 		| NO	{ $$ = 0; }
141 		;
142 
143 no		: /* empty */	{ $$ = 0; }
144 		| NO		{ $$ = 1; }
145 
146 varset		: STRING '=' string {
147 			if (conf->opts & RIPD_OPT_VERBOSE)
148 				printf("%s = \"%s\"\n", $1, $3);
149 			if (symset($1, $3, 0) == -1)
150 				fatal("cannot store variable");
151 			free($1);
152 			free($3);
153 		}
154 		;
155 
156 conf_main	: SPLIT_HORIZON STRING {
157 			/* clean flags first */
158 			conf->options &= ~(OPT_SPLIT_HORIZON |
159 			    OPT_SPLIT_POISONED);
160 			if (!strcmp($2, "none"))
161 				/* nothing */ ;
162 			else if (!strcmp($2, "simple"))
163 				conf->options |= OPT_SPLIT_HORIZON;
164 			else if (!strcmp($2, "poisoned"))
165 				conf->options |= OPT_SPLIT_POISONED;
166 			else {
167 				yyerror("unknown split horizon type");
168 				free($2);
169 				YYERROR;
170 			}
171 			free($2);
172 		}
173 		| TRIGGERED_UPDATES yesno {
174 			if ($2 == 1)
175 				conf->options |= OPT_TRIGGERED_UPDATES;
176 			else
177 				conf->options &= ~OPT_TRIGGERED_UPDATES;
178 		}
179 		| RDOMAIN NUMBER {
180 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
181 				yyerror("invalid rdomain");
182 				YYERROR;
183 			}
184 			conf->rdomain = $2;
185 		}
186 		| FIBUPDATE yesno {
187 			if ($2 == 0)
188 				conf->flags |= RIPD_FLAG_NO_FIB_UPDATE;
189 			else
190 				conf->flags &= ~RIPD_FLAG_NO_FIB_UPDATE;
191 		}
192 		| no REDISTRIBUTE STRING {
193 			struct redistribute	*r;
194 
195 			if ((r = calloc(1, sizeof(*r))) == NULL)
196 				fatal(NULL);
197 			if (!strcmp($3, "static"))
198 				r->type = REDIST_STATIC;
199 			else if (!strcmp($3, "connected"))
200 				r->type = REDIST_CONNECTED;
201 			else if (!strcmp($3, "default"))
202 				r->type = REDIST_DEFAULT;
203 			else if (host($3, &r->addr, &r->mask))
204 				r->type = REDIST_ADDR;
205 			else {
206 				yyerror("unknown redistribute type");
207 				free($3);
208 				free(r);
209 				YYERROR;
210 			}
211 
212 			if ($1)
213 				r->type |= REDIST_NO;
214 
215 			SIMPLEQ_INSERT_TAIL(&conf->redist_list, r,
216 			    entry);
217 
218 			conf->redistribute |= REDISTRIBUTE_ON;
219 			free($3);
220 		}
221 		| no REDISTRIBUTE RTLABEL STRING {
222 			struct redistribute	*r;
223 
224 			if ((r = calloc(1, sizeof(*r))) == NULL)
225 				fatal(NULL);
226 			r->type = REDIST_LABEL;
227 			r->label = rtlabel_name2id($4);
228 			if ($1)
229 				r->type |= REDIST_NO;
230 			free($4);
231 
232 			SIMPLEQ_INSERT_TAIL(&conf->redist_list, r, entry);
233 			conf->redistribute |= REDISTRIBUTE_ON;
234 		}
235 		| defaults
236 		;
237 
238 authmd		: AUTHMD NUMBER STRING {
239 			if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
240 				yyerror("auth-md key-id out of range "
241 				    "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
242 				free($3);
243 				YYERROR;
244 			}
245 			if (md_list_add(&defs->md_list, $2, $3) == -1) {
246 				yyerror("auth-md key length out of range "
247 				    "(max length %d)", MD5_DIGEST_LENGTH);
248 				free($3);
249 				YYERROR;
250 			}
251 			free($3);
252 		}
253 
254 authmdkeyid	: AUTHMDKEYID NUMBER {
255 			if ($2 < MIN_MD_ID || $2 > MAX_MD_ID) {
256 				yyerror("auth-md-keyid out of range "
257 				    "(%d-%d)", MIN_MD_ID, MAX_MD_ID);
258 				YYERROR;
259 			}
260 			defs->auth_keyid = $2;
261 		}
262 
263 authtype	: AUTHTYPE STRING {
264 			enum auth_type	type;
265 
266 			if (!strcmp($2, "none"))
267 				type = AUTH_NONE;
268 			else if (!strcmp($2, "simple"))
269 				type = AUTH_SIMPLE;
270 			else if (!strcmp($2, "crypt"))
271 				type = AUTH_CRYPT;
272 			else {
273 				yyerror("unknown auth-type");
274 				free($2);
275 				YYERROR;
276 			}
277 			free($2);
278 			defs->auth_type = type;
279 		}
280 		;
281 
282 authkey		: AUTHKEY STRING {
283 			if (strlen($2) > MAX_SIMPLE_AUTH_LEN) {
284 				yyerror("auth-key too long (max length %d)",
285 				    MAX_SIMPLE_AUTH_LEN);
286 				free($2);
287 				YYERROR;
288 			}
289 			bzero(defs->auth_key, MAX_SIMPLE_AUTH_LEN);
290 			memcpy(defs->auth_key, $2, strlen($2));
291 			free($2);
292 		}
293 		;
294 
295 defaults	: COST NUMBER {
296 			if ($2 < 1 || $2 > INFINITY) {
297 				yyerror("cost out of range (%d-%d)", 1,
298 				    INFINITY);
299 				YYERROR;
300 			}
301 			defs->cost = $2;
302 		}
303 		| authtype
304 		| authkey
305 		| authmdkeyid
306 		| authmd
307 		;
308 
309 optnl		: '\n' optnl
310 		|
311 		;
312 
313 nl		: '\n' optnl
314 		;
315 
316 interface	: INTERFACE STRING {
317 			struct kif *kif;
318 
319 			if ((kif = kif_findname($2)) == NULL) {
320 				yyerror("unknown interface %s", $2);
321 				free($2);
322 				YYERROR;
323 			}
324 			free($2);
325 			iface = conf_get_if(kif);
326 			if (iface == NULL)
327 				YYERROR;
328 			LIST_INSERT_HEAD(&conf->iface_list, iface, entry);
329 			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
330 			md_list_copy(&ifacedefs.md_list, &defs->md_list);
331 			defs = &ifacedefs;
332 		} interface_block {
333 			iface->cost = defs->cost;
334 			iface->auth_type = defs->auth_type;
335 			iface->auth_keyid = defs->auth_keyid;
336 			memcpy(iface->auth_key, defs->auth_key,
337 			    sizeof(iface->auth_key));
338 			md_list_copy(&iface->auth_md_list, &defs->md_list);
339 			md_list_clr(&defs->md_list);
340 			defs = &globaldefs;
341 		}
342 		;
343 
344 interface_block	: '{' optnl interfaceopts_l '}'
345 		| '{' optnl '}'
346 		;
347 
348 interfaceopts_l	: interfaceopts_l interfaceoptsl nl
349 		| interfaceoptsl optnl
350 		;
351 
352 interfaceoptsl	: PASSIVE		{ iface->passive = 1; }
353 		| DEMOTE STRING		{
354 			if (strlcpy(iface->demote_group, $2,
355 			    sizeof(iface->demote_group)) >=
356 			    sizeof(iface->demote_group)) {
357 				yyerror("demote group name \"%s\" too long");
358 				free($2);
359 				YYERROR;
360 			}
361 			free($2);
362 			if (carp_demote_init(iface->demote_group,
363 			    conf->opts & RIPD_OPT_FORCE_DEMOTE) == -1) {
364 				yyerror("error initializing group \"%s\"",
365 				    iface->demote_group);
366 				YYERROR;
367 			}
368 		}
369 		| defaults
370 		;
371 %%
372 
373 struct keywords {
374 	const char	*k_name;
375 	int		 k_val;
376 };
377 
378 int
379 yyerror(const char *fmt, ...)
380 {
381 	va_list	ap;
382 
383 	file->errors++;
384 	va_start(ap, fmt);
385 	fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
386 	vfprintf(stderr, fmt, ap);
387 	fprintf(stderr, "\n");
388 	va_end(ap);
389 	return (0);
390 }
391 
392 int
393 kw_cmp(const void *k, const void *e)
394 {
395 	return (strcmp(k, ((const struct keywords *)e)->k_name));
396 }
397 
398 int
399 lookup(char *s)
400 {
401 	/* this has to be sorted always */
402 	static const struct keywords keywords[] = {
403 	    {"auth-key",		AUTHKEY},
404 	    {"auth-md",			AUTHMD},
405 	    {"auth-md-keyid",		AUTHMDKEYID},
406 	    {"auth-type",		AUTHTYPE},
407 	    {"cost",			COST},
408 	    {"demote",			DEMOTE},
409 	    {"fib-update",		FIBUPDATE},
410 	    {"interface",		INTERFACE},
411 	    {"no",			NO},
412 	    {"passive",			PASSIVE},
413 	    {"rdomain",			RDOMAIN},
414 	    {"redistribute",		REDISTRIBUTE},
415 	    {"rtlabel",			RTLABEL},
416 	    {"split-horizon",		SPLIT_HORIZON},
417 	    {"triggered-updates",	TRIGGERED_UPDATES},
418 	    {"yes",			YES}
419 	};
420 	const struct keywords	*p;
421 
422 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
423 	    sizeof(keywords[0]), kw_cmp);
424 
425 	if (p)
426 		return (p->k_val);
427 	else
428 		return (STRING);
429 }
430 
431 #define MAXPUSHBACK	128
432 
433 char	*parsebuf;
434 int	 parseindex;
435 char	 pushback_buffer[MAXPUSHBACK];
436 int	 pushback_index = 0;
437 
438 int
439 lgetc(int quotec)
440 {
441 	int		c, next;
442 
443 	if (parsebuf) {
444 		/* Read character from the parsebuffer instead of input. */
445 		if (parseindex >= 0) {
446 			c = parsebuf[parseindex++];
447 			if (c != '\0')
448 				return (c);
449 			parsebuf = NULL;
450 		} else
451 			parseindex++;
452 	}
453 
454 	if (pushback_index)
455 		return (pushback_buffer[--pushback_index]);
456 
457 	if (quotec) {
458 		if ((c = getc(file->stream)) == EOF) {
459 			yyerror("reached end of file while parsing "
460 			    "quoted string");
461 			if (file == topfile || popfile() == EOF)
462 				return (EOF);
463 			return (quotec);
464 		}
465 		return (c);
466 	}
467 
468 	while ((c = getc(file->stream)) == '\\') {
469 		next = getc(file->stream);
470 		if (next != '\n') {
471 			c = next;
472 			break;
473 		}
474 		yylval.lineno = file->lineno;
475 		file->lineno++;
476 	}
477 
478 	while (c == EOF) {
479 		if (file == topfile || popfile() == EOF)
480 			return (EOF);
481 		c = getc(file->stream);
482 	}
483 	return (c);
484 }
485 
486 int
487 lungetc(int c)
488 {
489 	if (c == EOF)
490 		return (EOF);
491 	if (parsebuf) {
492 		parseindex--;
493 		if (parseindex >= 0)
494 			return (c);
495 	}
496 	if (pushback_index < MAXPUSHBACK-1)
497 		return (pushback_buffer[pushback_index++] = c);
498 	else
499 		return (EOF);
500 }
501 
502 int
503 findeol(void)
504 {
505 	int	c;
506 
507 	parsebuf = NULL;
508 
509 	/* skip to either EOF or the first real EOL */
510 	while (1) {
511 		if (pushback_index)
512 			c = pushback_buffer[--pushback_index];
513 		else
514 			c = lgetc(0);
515 		if (c == '\n') {
516 			file->lineno++;
517 			break;
518 		}
519 		if (c == EOF)
520 			break;
521 	}
522 	return (ERROR);
523 }
524 
525 int
526 yylex(void)
527 {
528 	char	 buf[8096];
529 	char	*p, *val;
530 	int	 quotec, next, c;
531 	int	 token;
532 
533 top:
534 	p = buf;
535 	while ((c = lgetc(0)) == ' ' || c == '\t')
536 		; /* nothing */
537 
538 	yylval.lineno = file->lineno;
539 	if (c == '#')
540 		while ((c = lgetc(0)) != '\n' && c != EOF)
541 			; /* nothing */
542 	if (c == '$' && parsebuf == NULL) {
543 		while (1) {
544 			if ((c = lgetc(0)) == EOF)
545 				return (0);
546 
547 			if (p + 1 >= buf + sizeof(buf) - 1) {
548 				yyerror("string too long");
549 				return (findeol());
550 			}
551 			if (isalnum(c) || c == '_') {
552 				*p++ = (char)c;
553 				continue;
554 			}
555 			*p = '\0';
556 			lungetc(c);
557 			break;
558 		}
559 		val = symget(buf);
560 		if (val == NULL) {
561 			yyerror("macro '%s' not defined", buf);
562 			return (findeol());
563 		}
564 		parsebuf = val;
565 		parseindex = 0;
566 		goto top;
567 	}
568 
569 	switch (c) {
570 	case '\'':
571 	case '"':
572 		quotec = c;
573 		while (1) {
574 			if ((c = lgetc(quotec)) == EOF)
575 				return (0);
576 			if (c == '\n') {
577 				file->lineno++;
578 				continue;
579 			} else if (c == '\\') {
580 				if ((next = lgetc(quotec)) == EOF)
581 					return (0);
582 				if (next == quotec || c == ' ' || c == '\t')
583 					c = next;
584 				else if (next == '\n') {
585 					file->lineno++;
586 					continue;
587 				} else
588 					lungetc(next);
589 			} else if (c == quotec) {
590 				*p = '\0';
591 				break;
592 			}
593 			if (p + 1 >= buf + sizeof(buf) - 1) {
594 				yyerror("string too long");
595 				return (findeol());
596 			}
597 			*p++ = (char)c;
598 		}
599 		yylval.v.string = strdup(buf);
600 		if (yylval.v.string == NULL)
601 			err(1, "yylex: strdup");
602 		return (STRING);
603 	}
604 
605 #define allowed_to_end_number(x) \
606 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
607 
608 	if (c == '-' || isdigit(c)) {
609 		do {
610 			*p++ = c;
611 			if ((unsigned)(p-buf) >= sizeof(buf)) {
612 				yyerror("string too long");
613 				return (findeol());
614 			}
615 		} while ((c = lgetc(0)) != EOF && isdigit(c));
616 		lungetc(c);
617 		if (p == buf + 1 && buf[0] == '-')
618 			goto nodigits;
619 		if (c == EOF || allowed_to_end_number(c)) {
620 			const char *errstr = NULL;
621 
622 			*p = '\0';
623 			yylval.v.number = strtonum(buf, LLONG_MIN,
624 			    LLONG_MAX, &errstr);
625 			if (errstr) {
626 				yyerror("\"%s\" invalid number: %s",
627 				    buf, errstr);
628 				return (findeol());
629 			}
630 			return (NUMBER);
631 		} else {
632 nodigits:
633 			while (p > buf + 1)
634 				lungetc(*--p);
635 			c = *--p;
636 			if (c == '-')
637 				return (c);
638 		}
639 	}
640 
641 #define allowed_in_string(x) \
642 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
643 	x != '{' && x != '}' && \
644 	x != '!' && x != '=' && x != '#' && \
645 	x != ','))
646 
647 	if (isalnum(c) || c == ':' || c == '_') {
648 		do {
649 			*p++ = c;
650 			if ((unsigned)(p-buf) >= sizeof(buf)) {
651 				yyerror("string too long");
652 				return (findeol());
653 			}
654 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
655 		lungetc(c);
656 		*p = '\0';
657 		if ((token = lookup(buf)) == STRING)
658 			if ((yylval.v.string = strdup(buf)) == NULL)
659 				err(1, "yylex: strdup");
660 		return (token);
661 	}
662 	if (c == '\n') {
663 		yylval.lineno = file->lineno;
664 		file->lineno++;
665 	}
666 	if (c == EOF)
667 		return (0);
668 	return (c);
669 }
670 
671 int
672 check_file_secrecy(int fd, const char *fname)
673 {
674 	struct stat	st;
675 
676 	if (fstat(fd, &st)) {
677 		log_warn("cannot stat %s", fname);
678 		return (-1);
679 	}
680 	if (st.st_uid != 0 && st.st_uid != getuid()) {
681 		log_warnx("%s: owner not root or current user", fname);
682 		return (-1);
683 	}
684 	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
685 		log_warnx("%s: group/world readable/writeable", fname);
686 		return (-1);
687 	}
688 	return (0);
689 }
690 
691 struct file *
692 pushfile(const char *name, int secret)
693 {
694 	struct file	*nfile;
695 
696 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
697 		log_warn("malloc");
698 		return (NULL);
699 	}
700 	if ((nfile->name = strdup(name)) == NULL) {
701 		log_warn("malloc");
702 		free(nfile);
703 		return (NULL);
704 	}
705 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
706 		log_warn("%s", nfile->name);
707 		free(nfile->name);
708 		free(nfile);
709 		return (NULL);
710 	} else if (secret &&
711 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
712 		fclose(nfile->stream);
713 		free(nfile->name);
714 		free(nfile);
715 		return (NULL);
716 	}
717 	nfile->lineno = 1;
718 	TAILQ_INSERT_TAIL(&files, nfile, entry);
719 	return (nfile);
720 }
721 
722 int
723 popfile(void)
724 {
725 	struct file	*prev;
726 
727 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
728 		prev->errors += file->errors;
729 
730 	TAILQ_REMOVE(&files, file, entry);
731 	fclose(file->stream);
732 	free(file->name);
733 	free(file);
734 	file = prev;
735 	return (file ? 0 : EOF);
736 }
737 
738 struct ripd_conf *
739 parse_config(char *filename, int opts)
740 {
741 	struct sym	*sym, *next;
742 
743 	if ((conf = calloc(1, sizeof(struct ripd_conf))) == NULL)
744 		fatal("parse_config");
745 
746 	bzero(&globaldefs, sizeof(globaldefs));
747 	defs = &globaldefs;
748 	TAILQ_INIT(&defs->md_list);
749 	defs->cost = DEFAULT_COST;
750 	defs->auth_type = AUTH_NONE;
751 	conf->opts = opts;
752 	conf->options = OPT_SPLIT_POISONED;
753 	SIMPLEQ_INIT(&conf->redist_list);
754 
755 	if ((file = pushfile(filename, !(conf->opts & RIPD_OPT_NOACTION))) == NULL) {
756 		free(conf);
757 		return (NULL);
758 	}
759 	topfile = file;
760 
761 	yyparse();
762 	errors = file->errors;
763 	popfile();
764 
765 	/* Free macros and check which have not been used. */
766 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
767 		next = TAILQ_NEXT(sym, entry);
768 		if ((conf->opts & RIPD_OPT_VERBOSE2) && !sym->used)
769 			fprintf(stderr, "warning: macro '%s' not "
770 			    "used\n", sym->nam);
771 		if (!sym->persist) {
772 			free(sym->nam);
773 			free(sym->val);
774 			TAILQ_REMOVE(&symhead, sym, entry);
775 			free(sym);
776 		}
777 	}
778 
779 	/* free global config defaults */
780 	md_list_clr(&globaldefs.md_list);
781 
782 	if (errors) {
783 		clear_config(conf);
784 		return (NULL);
785 	}
786 
787 	return (conf);
788 }
789 
790 int
791 symset(const char *nam, const char *val, int persist)
792 {
793 	struct sym	*sym;
794 
795 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
796 	    sym = TAILQ_NEXT(sym, entry))
797 		;	/* nothing */
798 
799 	if (sym != NULL) {
800 		if (sym->persist == 1)
801 			return (0);
802 		else {
803 			free(sym->nam);
804 			free(sym->val);
805 			TAILQ_REMOVE(&symhead, sym, entry);
806 			free(sym);
807 		}
808 	}
809 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
810 		return (-1);
811 
812 	sym->nam = strdup(nam);
813 	if (sym->nam == NULL) {
814 		free(sym);
815 		return (-1);
816 	}
817 	sym->val = strdup(val);
818 	if (sym->val == NULL) {
819 		free(sym->nam);
820 		free(sym);
821 		return (-1);
822 	}
823 	sym->used = 0;
824 	sym->persist = persist;
825 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
826 	return (0);
827 }
828 
829 int
830 cmdline_symset(char *s)
831 {
832 	char	*sym, *val;
833 	int	ret;
834 	size_t	len;
835 
836 	if ((val = strrchr(s, '=')) == NULL)
837 		return (-1);
838 
839 	len = strlen(s) - strlen(val) + 1;
840 	if ((sym = malloc(len)) == NULL)
841 		errx(1, "cmdline_symset: malloc");
842 
843 	strlcpy(sym, s, len);
844 
845 	ret = symset(sym, val + 1, 1);
846 	free(sym);
847 
848 	return (ret);
849 }
850 
851 char *
852 symget(const char *nam)
853 {
854 	struct sym	*sym;
855 
856 	TAILQ_FOREACH(sym, &symhead, entry)
857 		if (strcmp(nam, sym->nam) == 0) {
858 			sym->used = 1;
859 			return (sym->val);
860 		}
861 	return (NULL);
862 }
863 
864 struct iface *
865 conf_get_if(struct kif *kif)
866 {
867 	struct iface	*i;
868 
869 	LIST_FOREACH(i, &conf->iface_list, entry)
870 		if (i->ifindex == kif->ifindex) {
871 			yyerror("interface %s already configured",
872 			    kif->ifname);
873 			return (NULL);
874 		}
875 
876 	i = if_new(kif);
877 	i->auth_keyid = 1;
878 	i->passive = 0;
879 
880 	return (i);
881 }
882 
883 void
884 clear_config(struct ripd_conf *xconf)
885 {
886 	struct iface	*i;
887 
888 	while ((i = LIST_FIRST(&conf->iface_list)) != NULL) {
889 		LIST_REMOVE(i, entry);
890 		if_del(i);
891 	}
892 
893 	free(xconf);
894 }
895 
896 int
897 host(const char *s, struct in_addr *addr, struct in_addr *mask)
898 {
899 	struct in_addr		 ina;
900 	int			 bits = 32;
901 
902 	bzero(&ina, sizeof(struct in_addr));
903 	if (strrchr(s, '/') != NULL) {
904 		if ((bits = inet_net_pton(AF_INET, s, &ina, sizeof(ina))) == -1)
905 			return (0);
906 	} else {
907 		if (inet_pton(AF_INET, s, &ina) != 1)
908 			return (0);
909 	}
910 
911 	addr->s_addr = ina.s_addr;
912 	mask->s_addr = prefixlen2mask(bits);
913 
914 	return (1);
915 }
916