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