1/*	$OpenBSD: parse.y,v 1.52 2023/07/04 02:56:11 dlg Exp $ */
2
3/*
4 * Copyright (c) 2004, 2005 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 <net/route.h>
29#include <netinet/in.h>
30#include <arpa/inet.h>
31
32#include <ctype.h>
33#include <err.h>
34#include <errno.h>
35#include <unistd.h>
36#include <ifaddrs.h>
37#include <limits.h>
38#include <netdb.h>
39#include <stdarg.h>
40#include <stdio.h>
41#include <string.h>
42#include <syslog.h>
43
44#include "ospf6.h"
45#include "ospf6d.h"
46#include "ospfe.h"
47#include "log.h"
48
49TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
50static struct file {
51	TAILQ_ENTRY(file)	 entry;
52	FILE			*stream;
53	char			*name;
54	size_t			 ungetpos;
55	size_t			 ungetsize;
56	u_char			*ungetbuf;
57	int			 eof_reached;
58	int			 lineno;
59	int			 errors;
60} *file, *topfile;
61struct file	*pushfile(const char *, int);
62int		 popfile(void);
63int		 check_file_secrecy(int, const char *);
64int		 yyparse(void);
65int		 yylex(void);
66int		 yyerror(const char *, ...)
67    __attribute__((__format__ (printf, 1, 2)))
68    __attribute__((__nonnull__ (1)));
69int		 kw_cmp(const void *, const void *);
70int		 lookup(char *);
71int		 igetc(void);
72int		 lgetc(int);
73void		 lungetc(int);
74int		 findeol(void);
75
76TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
77struct sym {
78	TAILQ_ENTRY(sym)	 entry;
79	int			 used;
80	int			 persist;
81	char			*nam;
82	char			*val;
83};
84int		 symset(const char *, const char *, int);
85char		*symget(const char *);
86
87void		 clear_config(struct ospfd_conf *xconf);
88u_int32_t	 get_rtr_id(void);
89int	 host(const char *, struct in6_addr *);
90int	 prefix(const char *, struct in6_addr *, u_int8_t *);
91
92static struct ospfd_conf	*conf;
93static int			 errors = 0;
94
95struct area	*area = NULL;
96struct iface	*iface = NULL;
97
98struct config_defaults {
99	u_int16_t	dead_interval;
100	u_int16_t	transmit_delay;
101	u_int16_t	hello_interval;
102	u_int16_t	rxmt_interval;
103	u_int16_t	metric;
104	u_int8_t	priority;
105	u_int8_t	p2p;
106};
107
108struct config_defaults	 globaldefs;
109struct config_defaults	 areadefs;
110struct config_defaults	 ifacedefs;
111struct config_defaults	*defs;
112
113struct area	*conf_get_area(struct in_addr);
114int		 conf_check_rdomain(u_int);
115
116typedef struct {
117	union {
118		int64_t		 number;
119		char		*string;
120		struct redistribute *redist;
121		struct in_addr	 id;
122	} v;
123	int lineno;
124} YYSTYPE;
125
126%}
127
128%token	AREA INTERFACE ROUTERID FIBPRIORITY FIBUPDATE REDISTRIBUTE RTLABEL
129%token	RDOMAIN STUB ROUTER SPFDELAY SPFHOLDTIME EXTTAG
130%token	METRIC P2P PASSIVE
131%token	HELLOINTERVAL TRANSMITDELAY
132%token	RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
133%token	SET TYPE
134%token	YES NO
135%token	DEMOTE
136%token	INCLUDE
137%token	ERROR
138%token	DEPEND ON
139%token	<v.string>	STRING
140%token	<v.number>	NUMBER
141%type	<v.number>	yesno no optlist, optlist_l option demotecount
142%type	<v.string>	string dependon
143%type	<v.redist>	redistribute
144%type	<v.id>		areaid
145
146%%
147
148grammar		: /* empty */
149		| grammar include '\n'
150		| grammar '\n'
151		| grammar conf_main '\n'
152		| grammar varset '\n'
153		| grammar area '\n'
154		| grammar error '\n'		{ file->errors++; }
155		;
156
157include		: INCLUDE STRING		{
158			struct file	*nfile;
159
160			if ((nfile = pushfile($2,
161			    !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) {
162				yyerror("failed to include file %s", $2);
163				free($2);
164				YYERROR;
165			}
166			free($2);
167
168			file = nfile;
169			lungetc('\n');
170		}
171		;
172
173string		: string STRING	{
174			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
175				free($1);
176				free($2);
177				yyerror("string: asprintf");
178				YYERROR;
179			}
180			free($1);
181			free($2);
182		}
183		| STRING
184		;
185
186yesno		: YES	{ $$ = 1; }
187		| NO	{ $$ = 0; }
188		;
189
190no		: /* empty */	{ $$ = 0; }
191		| NO		{ $$ = 1; }
192
193varset		: STRING '=' string		{
194			char *s = $1;
195			if (conf->opts & OSPFD_OPT_VERBOSE)
196				printf("%s = \"%s\"\n", $1, $3);
197			while (*s++) {
198				if (isspace((unsigned char)*s)) {
199					yyerror("macro name cannot contain "
200					    "whitespace");
201					free($1);
202					free($3);
203					YYERROR;
204				}
205			}
206			if (symset($1, $3, 0) == -1)
207				fatal("cannot store variable");
208			free($1);
209			free($3);
210		}
211		;
212
213conf_main	: ROUTERID STRING {
214			if (!inet_aton($2, &conf->rtr_id)) {
215				yyerror("error parsing router-id");
216				free($2);
217				YYERROR;
218			}
219			free($2);
220		}
221		| FIBPRIORITY NUMBER {
222			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
223				yyerror("invalid fib-priority");
224				YYERROR;
225			}
226			conf->fib_priority = $2;
227		}
228		| FIBUPDATE yesno {
229			if ($2 == 0)
230				conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
231			else
232				conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
233		}
234		| redistribute {
235			SIMPLEQ_INSERT_TAIL(&conf->redist_list, $1, entry);
236			conf->redistribute = 1;
237		}
238		| RTLABEL STRING EXTTAG NUMBER {
239			if ($4 < 0 || $4 > UINT_MAX) {
240				yyerror("invalid external route tag");
241				free($2);
242				YYERROR;
243			}
244			rtlabel_tag(rtlabel_name2id($2), $4);
245			free($2);
246		}
247		| RDOMAIN NUMBER {
248			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
249				yyerror("invalid rdomain");
250				YYERROR;
251			}
252			conf->rdomain = $2;
253		}
254		| SPFDELAY NUMBER {
255			if ($2 < MIN_SPF_DELAY || $2 > MAX_SPF_DELAY) {
256				yyerror("spf-delay out of range "
257				    "(%d-%d)", MIN_SPF_DELAY,
258				    MAX_SPF_DELAY);
259				YYERROR;
260			}
261			conf->spf_delay = $2;
262		}
263		| SPFHOLDTIME NUMBER {
264			if ($2 < MIN_SPF_HOLDTIME || $2 > MAX_SPF_HOLDTIME) {
265				yyerror("spf-holdtime out of range "
266				    "(%d-%d)", MIN_SPF_HOLDTIME,
267				    MAX_SPF_HOLDTIME);
268				YYERROR;
269			}
270			conf->spf_hold_time = $2;
271		}
272		| STUB ROUTER yesno {
273			if ($3)
274				conf->flags |= OSPFD_FLAG_STUB_ROUTER;
275			else
276				/* allow to force non stub mode */
277				conf->flags &= ~OSPFD_FLAG_STUB_ROUTER;
278		}
279		| defaults
280		;
281
282redistribute	: no REDISTRIBUTE STRING optlist dependon {
283			struct redistribute	*r;
284
285			if ((r = calloc(1, sizeof(*r))) == NULL)
286				fatal(NULL);
287			if (!strcmp($3, "default"))
288				r->type = REDIST_DEFAULT;
289			else if (!strcmp($3, "static"))
290				r->type = REDIST_STATIC;
291			else if (!strcmp($3, "connected"))
292				r->type = REDIST_CONNECTED;
293			else if (prefix($3, &r->addr, &r->prefixlen)) {
294				r->type = REDIST_ADDR;
295				conf->redist_label_or_prefix = !$1;
296			}
297			else {
298				yyerror("unknown redistribute type");
299				free($3);
300				free(r);
301				YYERROR;
302			}
303
304			if ($1)
305				r->type |= REDIST_NO;
306			r->metric = $4;
307			if ($5)
308				strlcpy(r->dependon, $5, sizeof(r->dependon));
309			else
310				r->dependon[0] = '\0';
311			free($3);
312			free($5);
313			$$ = r;
314		}
315		| no REDISTRIBUTE RTLABEL STRING optlist dependon {
316			struct redistribute	*r;
317
318			if ((r = calloc(1, sizeof(*r))) == NULL)
319				fatal(NULL);
320			r->type = REDIST_LABEL;
321			r->label = rtlabel_name2id($4);
322			if ($1)
323				r->type |= REDIST_NO;
324			else
325				conf->redist_label_or_prefix = 1;
326			r->metric = $5;
327			if ($6)
328				strlcpy(r->dependon, $6, sizeof(r->dependon));
329			else
330				r->dependon[0] = '\0';
331			free($4);
332			free($6);
333			$$ = r;
334		}
335		;
336
337optlist		: /* empty */			{ $$ = DEFAULT_REDIST_METRIC; }
338		| SET option			{
339			$$ = $2;
340			if (($$ & LSA_METRIC_MASK) == 0)
341				$$ |= DEFAULT_REDIST_METRIC;
342		}
343		| SET optnl '{' optnl optlist_l optnl '}'	{
344			$$ = $5;
345			if (($$ & LSA_METRIC_MASK) == 0)
346				$$ |= DEFAULT_REDIST_METRIC;
347		}
348		;
349
350optlist_l	: optlist_l comma option {
351			if ($1 & LSA_ASEXT_E_FLAG && $3 & LSA_ASEXT_E_FLAG) {
352				yyerror("redistribute type already defined");
353				YYERROR;
354			}
355			if ($1 & LSA_METRIC_MASK && $3 & LSA_METRIC_MASK) {
356				yyerror("redistribute metric already defined");
357				YYERROR;
358			}
359			$$ = $1 | $3;
360		}
361		| option { $$ = $1; }
362		;
363
364option		: METRIC NUMBER {
365			if ($2 == 0 || $2 > MAX_METRIC) {
366				yyerror("invalid redistribute metric");
367				YYERROR;
368			}
369			$$ = $2;
370		}
371		| TYPE NUMBER {
372			switch ($2) {
373			case 1:
374				$$ = 0;
375				break;
376			case 2:
377				$$ = LSA_ASEXT_E_FLAG;
378				break;
379			default:
380				yyerror("only external type 1 and 2 allowed");
381				YYERROR;
382			}
383		}
384		;
385
386dependon	: /* empty */		{ $$ = NULL; }
387		| DEPEND ON STRING	{
388			if (strlen($3) >= IFNAMSIZ) {
389				yyerror("interface name %s too long", $3);
390				free($3);
391				YYERROR;
392			}
393			if ((if_findname($3)) == NULL) {
394				yyerror("unknown interface %s", $3);
395				free($3);
396				YYERROR;
397			}
398			$$ = $3;
399		}
400		;
401
402defaults	: METRIC NUMBER {
403			if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
404				yyerror("metric out of range (%d-%d)",
405				    MIN_METRIC, MAX_METRIC);
406				YYERROR;
407			}
408			defs->metric = $2;
409		}
410		| ROUTERPRIORITY NUMBER {
411			if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
412				yyerror("router-priority out of range (%d-%d)",
413				    MIN_PRIORITY, MAX_PRIORITY);
414				YYERROR;
415			}
416			defs->priority = $2;
417		}
418		| ROUTERDEADTIME NUMBER {
419			if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
420				yyerror("router-dead-time out of range (%d-%d)",
421				    MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
422				YYERROR;
423			}
424			defs->dead_interval = $2;
425		}
426		| TRANSMITDELAY NUMBER {
427			if ($2 < MIN_TRANSMIT_DELAY ||
428			    $2 > MAX_TRANSMIT_DELAY) {
429				yyerror("transmit-delay out of range (%d-%d)",
430				    MIN_TRANSMIT_DELAY, MAX_TRANSMIT_DELAY);
431				YYERROR;
432			}
433			defs->transmit_delay = $2;
434		}
435		| HELLOINTERVAL NUMBER {
436			if ($2 < MIN_HELLO_INTERVAL ||
437			    $2 > MAX_HELLO_INTERVAL) {
438				yyerror("hello-interval out of range (%d-%d)",
439				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
440				YYERROR;
441			}
442			defs->hello_interval = $2;
443		}
444		| RETRANSMITINTERVAL NUMBER {
445			if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
446				yyerror("retransmit-interval out of range "
447				    "(%d-%d)", MIN_RXMT_INTERVAL,
448				    MAX_RXMT_INTERVAL);
449				YYERROR;
450			}
451			defs->rxmt_interval = $2;
452		}
453		| TYPE P2P		{
454			defs->p2p = 1;
455		}
456		;
457
458optnl		: '\n' optnl
459		|
460		;
461
462nl		: '\n' optnl		/* one newline or more */
463		;
464
465comma		: ','
466		| /*empty*/
467		;
468
469area		: AREA areaid {
470			area = conf_get_area($2);
471
472			memcpy(&areadefs, defs, sizeof(areadefs));
473			defs = &areadefs;
474		} '{' optnl areaopts_l '}' {
475			area = NULL;
476			defs = &globaldefs;
477		}
478		;
479
480demotecount	: NUMBER	{ $$ = $1; }
481		| /*empty*/	{ $$ = 1; }
482		;
483
484areaid		: NUMBER {
485			if ($1 < 0 || $1 > 0xffffffff) {
486				yyerror("invalid area id");
487				YYERROR;
488			}
489			$$.s_addr = htonl($1);
490		}
491		| STRING {
492			if (inet_aton($1, &$$) == 0) {
493				yyerror("error parsing area");
494				free($1);
495				YYERROR;
496			}
497			free($1);
498		}
499		;
500
501areaopts_l	: areaopts_l areaoptsl nl
502		| areaoptsl optnl
503		;
504
505areaoptsl	: interface
506		| DEMOTE STRING	demotecount {
507			if ($3 < 1 || $3 > 255) {
508				yyerror("demote count out of range (1-255)");
509				free($2);
510				YYERROR;
511			}
512			area->demote_level = $3;
513			if (strlcpy(area->demote_group, $2,
514			    sizeof(area->demote_group)) >=
515			    sizeof(area->demote_group)) {
516				yyerror("demote group name \"%s\" too long",
517				    $2);
518				free($2);
519				YYERROR;
520			}
521			free($2);
522			if (carp_demote_init(area->demote_group,
523			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
524				yyerror("error initializing group \"%s\"",
525				    area->demote_group);
526				YYERROR;
527			}
528		}
529		| defaults
530		;
531
532interface	: INTERFACE STRING	{
533			if ((iface = if_findname($2)) == NULL) {
534				yyerror("unknown interface %s", $2);
535				free($2);
536				YYERROR;
537			}
538			if (IN6_IS_ADDR_UNSPECIFIED(&iface->addr)) {
539				yyerror("unnumbered interface %s", $2);
540				free($2);
541				YYERROR;
542			}
543			free($2);
544			iface->area = area;
545			LIST_INSERT_HEAD(&area->iface_list, iface, entry);
546
547			memcpy(&ifacedefs, defs, sizeof(ifacedefs));
548			defs = &ifacedefs;
549		} interface_block {
550			iface->dead_interval = defs->dead_interval;
551			iface->transmit_delay = defs->transmit_delay;
552			iface->hello_interval = defs->hello_interval;
553			iface->rxmt_interval = defs->rxmt_interval;
554			iface->metric = defs->metric;
555			iface->priority = defs->priority;
556			iface->cflags |= F_IFACE_CONFIGURED;
557			if (defs->p2p == 1)
558				iface->type = IF_TYPE_POINTOPOINT;
559			iface = NULL;
560			/* interface is always part of an area */
561			defs = &areadefs;
562		}
563		;
564
565interface_block	: '{' optnl interfaceopts_l '}'
566		| '{' optnl '}'
567		|
568		;
569
570interfaceopts_l	: interfaceopts_l interfaceoptsl nl
571		| interfaceoptsl optnl
572		;
573
574interfaceoptsl	: PASSIVE		{ iface->cflags |= F_IFACE_PASSIVE; }
575		| DEMOTE STRING		{
576			if (strlcpy(iface->demote_group, $2,
577			    sizeof(iface->demote_group)) >=
578			    sizeof(iface->demote_group)) {
579				yyerror("demote group name \"%s\" too long",
580				    $2);
581				free($2);
582				YYERROR;
583			}
584			free($2);
585			if (carp_demote_init(iface->demote_group,
586			    conf->opts & OSPFD_OPT_FORCE_DEMOTE) == -1) {
587				yyerror("error initializing group \"%s\"",
588				    iface->demote_group);
589				YYERROR;
590			}
591		}
592		| dependon {
593			struct iface	*depend_if = NULL;
594
595			if ($1) {
596				strlcpy(iface->dependon, $1,
597				    sizeof(iface->dependon));
598				depend_if = if_findname($1);
599				iface->depend_ok = ifstate_is_up(depend_if);
600			} else {
601				iface->dependon[0] = '\0';
602				iface->depend_ok = 1;
603			}
604		}
605		| defaults
606		;
607
608%%
609
610struct keywords {
611	const char	*k_name;
612	int		 k_val;
613};
614
615int
616yyerror(const char *fmt, ...)
617{
618	va_list		 ap;
619	char		*msg;
620
621	file->errors++;
622	va_start(ap, fmt);
623	if (vasprintf(&msg, fmt, ap) == -1)
624		fatalx("yyerror vasprintf");
625	va_end(ap);
626	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
627	free(msg);
628	return (0);
629}
630
631int
632kw_cmp(const void *k, const void *e)
633{
634	return (strcmp(k, ((const struct keywords *)e)->k_name));
635}
636
637int
638lookup(char *s)
639{
640	/* this has to be sorted always */
641	static const struct keywords keywords[] = {
642		{"area",		AREA},
643		{"demote",		DEMOTE},
644		{"depend",		DEPEND},
645		{"external-tag",	EXTTAG},
646		{"fib-priority",	FIBPRIORITY},
647		{"fib-update",		FIBUPDATE},
648		{"hello-interval",	HELLOINTERVAL},
649		{"include",		INCLUDE},
650		{"interface",		INTERFACE},
651		{"metric",		METRIC},
652		{"no",			NO},
653		{"on",			ON},
654		{"p2p",			P2P},
655		{"passive",		PASSIVE},
656		{"rdomain",		RDOMAIN},
657		{"redistribute",	REDISTRIBUTE},
658		{"retransmit-interval",	RETRANSMITINTERVAL},
659		{"router",		ROUTER},
660		{"router-dead-time",	ROUTERDEADTIME},
661		{"router-id",		ROUTERID},
662		{"router-priority",	ROUTERPRIORITY},
663		{"rtlabel",		RTLABEL},
664		{"set",			SET},
665		{"spf-delay",		SPFDELAY},
666		{"spf-holdtime",	SPFHOLDTIME},
667		{"stub",		STUB},
668		{"transmit-delay",	TRANSMITDELAY},
669		{"type",		TYPE},
670		{"yes",			YES}
671	};
672	const struct keywords	*p;
673
674	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
675	    sizeof(keywords[0]), kw_cmp);
676
677	if (p)
678		return (p->k_val);
679	else
680		return (STRING);
681}
682
683#define START_EXPAND	1
684#define DONE_EXPAND	2
685
686static int	expanding;
687
688int
689igetc(void)
690{
691	int	c;
692
693	while (1) {
694		if (file->ungetpos > 0)
695			c = file->ungetbuf[--file->ungetpos];
696		else
697			c = getc(file->stream);
698
699		if (c == START_EXPAND)
700			expanding = 1;
701		else if (c == DONE_EXPAND)
702			expanding = 0;
703		else
704			break;
705	}
706	return (c);
707}
708
709int
710lgetc(int quotec)
711{
712	int		c, next;
713
714	if (quotec) {
715		if ((c = igetc()) == EOF) {
716			yyerror("reached end of file while parsing "
717			    "quoted string");
718			if (file == topfile || popfile() == EOF)
719				return (EOF);
720			return (quotec);
721		}
722		return (c);
723	}
724
725	while ((c = igetc()) == '\\') {
726		next = igetc();
727		if (next != '\n') {
728			c = next;
729			break;
730		}
731		yylval.lineno = file->lineno;
732		file->lineno++;
733	}
734
735	if (c == EOF) {
736		/*
737		 * Fake EOL when hit EOF for the first time. This gets line
738		 * count right if last line in included file is syntactically
739		 * invalid and has no newline.
740		 */
741		if (file->eof_reached == 0) {
742			file->eof_reached = 1;
743			return ('\n');
744		}
745		while (c == EOF) {
746			if (file == topfile || popfile() == EOF)
747				return (EOF);
748			c = igetc();
749		}
750	}
751	return (c);
752}
753
754void
755lungetc(int c)
756{
757	if (c == EOF)
758		return;
759
760	if (file->ungetpos >= file->ungetsize) {
761		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
762		if (p == NULL)
763			err(1, "%s", __func__);
764		file->ungetbuf = p;
765		file->ungetsize *= 2;
766	}
767	file->ungetbuf[file->ungetpos++] = c;
768}
769
770int
771findeol(void)
772{
773	int	c;
774
775	/* skip to either EOF or the first real EOL */
776	while (1) {
777		c = lgetc(0);
778		if (c == '\n') {
779			file->lineno++;
780			break;
781		}
782		if (c == EOF)
783			break;
784	}
785	return (ERROR);
786}
787
788int
789yylex(void)
790{
791	char	 buf[8096];
792	char	*p, *val;
793	int	 quotec, next, c;
794	int	 token;
795
796top:
797	p = buf;
798	while ((c = lgetc(0)) == ' ' || c == '\t')
799		; /* nothing */
800
801	yylval.lineno = file->lineno;
802	if (c == '#')
803		while ((c = lgetc(0)) != '\n' && c != EOF)
804			; /* nothing */
805	if (c == '$' && !expanding) {
806		while (1) {
807			if ((c = lgetc(0)) == EOF)
808				return (0);
809
810			if (p + 1 >= buf + sizeof(buf) - 1) {
811				yyerror("string too long");
812				return (findeol());
813			}
814			if (isalnum(c) || c == '_') {
815				*p++ = c;
816				continue;
817			}
818			*p = '\0';
819			lungetc(c);
820			break;
821		}
822		val = symget(buf);
823		if (val == NULL) {
824			yyerror("macro '%s' not defined", buf);
825			return (findeol());
826		}
827		p = val + strlen(val) - 1;
828		lungetc(DONE_EXPAND);
829		while (p >= val) {
830			lungetc((unsigned char)*p);
831			p--;
832		}
833		lungetc(START_EXPAND);
834		goto top;
835	}
836
837	switch (c) {
838	case '\'':
839	case '"':
840		quotec = c;
841		while (1) {
842			if ((c = lgetc(quotec)) == EOF)
843				return (0);
844			if (c == '\n') {
845				file->lineno++;
846				continue;
847			} else if (c == '\\') {
848				if ((next = lgetc(quotec)) == EOF)
849					return (0);
850				if (next == quotec || next == ' ' ||
851				    next == '\t')
852					c = next;
853				else if (next == '\n') {
854					file->lineno++;
855					continue;
856				} else
857					lungetc(next);
858			} else if (c == quotec) {
859				*p = '\0';
860				break;
861			} else if (c == '\0') {
862				yyerror("syntax error");
863				return (findeol());
864			}
865			if (p + 1 >= buf + sizeof(buf) - 1) {
866				yyerror("string too long");
867				return (findeol());
868			}
869			*p++ = c;
870		}
871		yylval.v.string = strdup(buf);
872		if (yylval.v.string == NULL)
873			err(1, "%s", __func__);
874		return (STRING);
875	}
876
877#define allowed_to_end_number(x) \
878	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
879
880	if (c == '-' || isdigit(c)) {
881		do {
882			*p++ = c;
883			if ((size_t)(p-buf) >= sizeof(buf)) {
884				yyerror("string too long");
885				return (findeol());
886			}
887		} while ((c = lgetc(0)) != EOF && isdigit(c));
888		lungetc(c);
889		if (p == buf + 1 && buf[0] == '-')
890			goto nodigits;
891		if (c == EOF || allowed_to_end_number(c)) {
892			const char *errstr = NULL;
893
894			*p = '\0';
895			yylval.v.number = strtonum(buf, LLONG_MIN,
896			    LLONG_MAX, &errstr);
897			if (errstr) {
898				yyerror("\"%s\" invalid number: %s",
899				    buf, errstr);
900				return (findeol());
901			}
902			return (NUMBER);
903		} else {
904nodigits:
905			while (p > buf + 1)
906				lungetc((unsigned char)*--p);
907			c = (unsigned char)*--p;
908			if (c == '-')
909				return (c);
910		}
911	}
912
913#define allowed_in_string(x) \
914	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
915	x != '{' && x != '}' && \
916	x != '!' && x != '=' && x != '#' && \
917	x != ','))
918
919	if (isalnum(c) || c == ':' || c == '_') {
920		do {
921			*p++ = c;
922			if ((size_t)(p-buf) >= sizeof(buf)) {
923				yyerror("string too long");
924				return (findeol());
925			}
926		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
927		lungetc(c);
928		*p = '\0';
929		if ((token = lookup(buf)) == STRING)
930			if ((yylval.v.string = strdup(buf)) == NULL)
931				err(1, "%s", __func__);
932		return (token);
933	}
934	if (c == '\n') {
935		yylval.lineno = file->lineno;
936		file->lineno++;
937	}
938	if (c == EOF)
939		return (0);
940	return (c);
941}
942
943int
944check_file_secrecy(int fd, const char *fname)
945{
946	struct stat	st;
947
948	if (fstat(fd, &st)) {
949		log_warn("cannot stat %s", fname);
950		return (-1);
951	}
952	if (st.st_uid != 0 && st.st_uid != getuid()) {
953		log_warnx("%s: owner not root or current user", fname);
954		return (-1);
955	}
956	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
957		log_warnx("%s: group writable or world read/writable", fname);
958		return (-1);
959	}
960	return (0);
961}
962
963struct file *
964pushfile(const char *name, int secret)
965{
966	struct file	*nfile;
967
968	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
969		log_warn("%s", __func__);
970		return (NULL);
971	}
972	if ((nfile->name = strdup(name)) == NULL) {
973		log_warn("%s", __func__);
974		free(nfile);
975		return (NULL);
976	}
977	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
978		log_warn("%s: %s", __func__, nfile->name);
979		free(nfile->name);
980		free(nfile);
981		return (NULL);
982	} else if (secret &&
983	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
984		fclose(nfile->stream);
985		free(nfile->name);
986		free(nfile);
987		return (NULL);
988	}
989	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
990	nfile->ungetsize = 16;
991	nfile->ungetbuf = malloc(nfile->ungetsize);
992	if (nfile->ungetbuf == NULL) {
993		log_warn("%s", __func__);
994		fclose(nfile->stream);
995		free(nfile->name);
996		free(nfile);
997		return (NULL);
998	}
999	TAILQ_INSERT_TAIL(&files, nfile, entry);
1000	return (nfile);
1001}
1002
1003int
1004popfile(void)
1005{
1006	struct file	*prev;
1007
1008	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1009		prev->errors += file->errors;
1010
1011	TAILQ_REMOVE(&files, file, entry);
1012	fclose(file->stream);
1013	free(file->name);
1014	free(file->ungetbuf);
1015	free(file);
1016	file = prev;
1017	return (file ? 0 : EOF);
1018}
1019
1020struct ospfd_conf *
1021parse_config(char *filename, int opts)
1022{
1023	struct sym	*sym, *next;
1024
1025	if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL)
1026		fatal("parse_config");
1027	conf->opts = opts;
1028	if (conf->opts & OSPFD_OPT_STUB_ROUTER)
1029		conf->flags |= OSPFD_FLAG_STUB_ROUTER;
1030
1031	bzero(&globaldefs, sizeof(globaldefs));
1032	defs = &globaldefs;
1033	defs->dead_interval = DEFAULT_RTR_DEAD_TIME;
1034	defs->transmit_delay = DEFAULT_TRANSMIT_DELAY;
1035	defs->hello_interval = DEFAULT_HELLO_INTERVAL;
1036	defs->rxmt_interval = DEFAULT_RXMT_INTERVAL;
1037	defs->metric = DEFAULT_METRIC;
1038	defs->priority = DEFAULT_PRIORITY;
1039	defs->p2p = 0;
1040
1041	conf->spf_delay = DEFAULT_SPF_DELAY;
1042	conf->spf_hold_time = DEFAULT_SPF_HOLDTIME;
1043	conf->spf_state = SPF_IDLE;
1044	conf->fib_priority = RTP_OSPF;
1045
1046	if ((file = pushfile(filename,
1047	    !(conf->opts & OSPFD_OPT_NOACTION))) == NULL) {
1048		free(conf);
1049		return (NULL);
1050	}
1051	topfile = file;
1052
1053	LIST_INIT(&conf->area_list);
1054	LIST_INIT(&conf->cand_list);
1055	SIMPLEQ_INIT(&conf->redist_list);
1056
1057	yyparse();
1058	errors = file->errors;
1059	popfile();
1060
1061	/* Free macros and check which have not been used. */
1062	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1063		if ((conf->opts & OSPFD_OPT_VERBOSE2) && !sym->used)
1064			fprintf(stderr, "warning: macro '%s' not "
1065			    "used\n", sym->nam);
1066		if (!sym->persist) {
1067			free(sym->nam);
1068			free(sym->val);
1069			TAILQ_REMOVE(&symhead, sym, entry);
1070			free(sym);
1071		}
1072	}
1073
1074	/* check that all interfaces belong to the configured rdomain */
1075	errors += conf_check_rdomain(conf->rdomain);
1076
1077	if (errors) {
1078		clear_config(conf);
1079		return (NULL);
1080	}
1081
1082	if (conf->rtr_id.s_addr == 0)
1083		conf->rtr_id.s_addr = get_rtr_id();
1084
1085	return (conf);
1086}
1087
1088int
1089symset(const char *nam, const char *val, int persist)
1090{
1091	struct sym	*sym;
1092
1093	TAILQ_FOREACH(sym, &symhead, entry) {
1094		if (strcmp(nam, sym->nam) == 0)
1095			break;
1096	}
1097
1098	if (sym != NULL) {
1099		if (sym->persist == 1)
1100			return (0);
1101		else {
1102			free(sym->nam);
1103			free(sym->val);
1104			TAILQ_REMOVE(&symhead, sym, entry);
1105			free(sym);
1106		}
1107	}
1108	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1109		return (-1);
1110
1111	sym->nam = strdup(nam);
1112	if (sym->nam == NULL) {
1113		free(sym);
1114		return (-1);
1115	}
1116	sym->val = strdup(val);
1117	if (sym->val == NULL) {
1118		free(sym->nam);
1119		free(sym);
1120		return (-1);
1121	}
1122	sym->used = 0;
1123	sym->persist = persist;
1124	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1125	return (0);
1126}
1127
1128int
1129cmdline_symset(char *s)
1130{
1131	char	*sym, *val;
1132	int	ret;
1133
1134	if ((val = strrchr(s, '=')) == NULL)
1135		return (-1);
1136	sym = strndup(s, val - s);
1137	if (sym == NULL)
1138		errx(1, "%s: strndup", __func__);
1139	ret = symset(sym, val + 1, 1);
1140	free(sym);
1141
1142	return (ret);
1143}
1144
1145char *
1146symget(const char *nam)
1147{
1148	struct sym	*sym;
1149
1150	TAILQ_FOREACH(sym, &symhead, entry) {
1151		if (strcmp(nam, sym->nam) == 0) {
1152			sym->used = 1;
1153			return (sym->val);
1154		}
1155	}
1156	return (NULL);
1157}
1158
1159struct area *
1160conf_get_area(struct in_addr id)
1161{
1162	struct area	*a;
1163
1164	a = area_find(conf, id);
1165	if (a)
1166		return (a);
1167	a = area_new();
1168	LIST_INSERT_HEAD(&conf->area_list, a, entry);
1169
1170	a->id.s_addr = id.s_addr;
1171
1172	return (a);
1173}
1174
1175int
1176conf_check_rdomain(u_int rdomain)
1177{
1178	struct area		*a;
1179	struct iface		*i, *idep;
1180	struct redistribute	*r;
1181	int			 errs = 0;
1182
1183	SIMPLEQ_FOREACH(r, &conf->redist_list, entry)
1184		if (r->dependon[0] != '\0') {
1185			idep = if_findname(r->dependon);
1186			if (idep->rdomain != rdomain) {
1187				logit(LOG_CRIT,
1188				    "depend on %s: interface not in rdomain %u",
1189				    idep->name, rdomain);
1190				errs++;
1191			}
1192		}
1193
1194	LIST_FOREACH(a, &conf->area_list, entry)
1195		LIST_FOREACH(i, &a->iface_list, entry) {
1196			if (i->rdomain != rdomain) {
1197				logit(LOG_CRIT,
1198				    "interface %s not in rdomain %u",
1199				    i->name, rdomain);
1200				errs++;
1201			}
1202			if (i->dependon[0] != '\0') {
1203				idep = if_findname(i->dependon);
1204				if (idep->rdomain != rdomain) {
1205					logit(LOG_CRIT,
1206					    "depend on %s: interface not in "
1207					    "rdomain %u",
1208					    idep->name, rdomain);
1209					errs++;
1210				}
1211			}
1212		}
1213
1214	return (errs);
1215}
1216
1217void
1218conf_clear_redist_list(struct redist_list *rl)
1219{
1220	struct redistribute *r;
1221	while ((r = SIMPLEQ_FIRST(rl)) != NULL) {
1222		SIMPLEQ_REMOVE_HEAD(rl, entry);
1223		free(r);
1224	}
1225}
1226
1227void
1228clear_config(struct ospfd_conf *xconf)
1229{
1230	struct area	*a;
1231
1232	while ((a = LIST_FIRST(&xconf->area_list)) != NULL) {
1233		LIST_REMOVE(a, entry);
1234		area_del(a);
1235	}
1236
1237	conf_clear_redist_list(&xconf->redist_list);
1238
1239	free(xconf);
1240}
1241
1242u_int32_t
1243get_rtr_id(void)
1244{
1245	struct ifaddrs		*ifap, *ifa;
1246	u_int32_t		 ip = 0, cur, localnet;
1247
1248	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1249
1250	if (getifaddrs(&ifap) == -1)
1251		fatal("getifaddrs");
1252
1253	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1254		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1255			continue;
1256		if (ifa->ifa_addr == NULL ||
1257		    ifa->ifa_addr->sa_family != AF_INET)
1258			continue;
1259		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1260		if ((cur & localnet) == localnet)	/* skip 127/8 */
1261			continue;
1262		if (ntohl(cur) < ntohl(ip) || ip == 0)
1263			ip = cur;
1264	}
1265	freeifaddrs(ifap);
1266
1267	if (ip == 0)
1268		fatal("router-id is 0.0.0.0");
1269
1270	return (ip);
1271}
1272
1273int
1274host(const char *s, struct in6_addr *addr)
1275{
1276	struct addrinfo	hints, *r;
1277
1278	if (s == NULL)
1279		return (0);
1280
1281	bzero(addr, sizeof(struct in6_addr));
1282	bzero(&hints, sizeof(hints));
1283	hints.ai_family = AF_INET6;
1284	hints.ai_socktype = SOCK_DGRAM; /*dummy*/
1285	hints.ai_flags = AI_NUMERICHOST;
1286	if (getaddrinfo(s, "0", &hints, &r) == 0) {
1287		*addr = ((struct sockaddr_in6 *)r->ai_addr)->sin6_addr;
1288		/* XXX address scope !!! */
1289		/* ((struct sockaddr_in6 *)r->ai_addr)->sin6_scope_id */
1290		freeaddrinfo(r);
1291		return (1);
1292	}
1293	return (0);
1294}
1295
1296int
1297prefix(const char *s, struct in6_addr *addr, u_int8_t *plen)
1298{
1299	char		*p, *ps;
1300	const char	*errstr;
1301	int		 mask;
1302
1303	if (s == NULL)
1304		return (0);
1305
1306	if ((p = strrchr(s, '/')) != NULL) {
1307		mask = strtonum(p + 1, 0, 128, &errstr);
1308		if (errstr)
1309			errx(1, "invalid netmask: %s", errstr);
1310
1311		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
1312			err(1, "%s", __func__);
1313		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
1314
1315		if (host(ps, addr) == 0) {
1316			free(ps);
1317			return (0);
1318		}
1319
1320		inet6applymask(addr, addr, mask);
1321		*plen = mask;
1322		return (1);
1323	}
1324	*plen = 128;
1325	return (host(s, addr));
1326}
1327