GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/eigrpd/parse.y Lines: 0 370 0.0 %
Date: 2017-11-07 Branches: 0 281 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.22 2017/02/22 14:24:50 renato Exp $ */
2
3
/*
4
 * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
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 <net/route.h>
30
31
#include <arpa/inet.h>
32
#include <ctype.h>
33
#include <err.h>
34
#include <ifaddrs.h>
35
#include <limits.h>
36
#include <stdio.h>
37
#include <syslog.h>
38
#include <unistd.h>
39
40
#include "eigrpd.h"
41
#include "eigrpe.h"
42
#include "log.h"
43
44
struct file {
45
	TAILQ_ENTRY(file)	 entry;
46
	FILE			*stream;
47
	char			*name;
48
	int			 lineno;
49
	int			 errors;
50
};
51
TAILQ_HEAD(files, file);
52
53
struct sym {
54
	TAILQ_ENTRY(sym)	 entry;
55
	int			 used;
56
	int			 persist;
57
	char			*nam;
58
	char			*val;
59
};
60
TAILQ_HEAD(symhead, sym);
61
62
struct config_defaults {
63
	uint8_t		kvalues[6];
64
	uint16_t	active_timeout;
65
	uint8_t		maximum_hops;
66
	uint8_t		maximum_paths;
67
	uint8_t		variance;
68
	struct redist_metric *dflt_metric;
69
	uint16_t	hello_interval;
70
	uint16_t	hello_holdtime;
71
	uint32_t	delay;
72
	uint32_t	bandwidth;
73
	uint8_t		splithorizon;
74
};
75
76
typedef struct {
77
	union {
78
		int64_t			 number;
79
		char			*string;
80
		struct redistribute	*redist;
81
		struct redist_metric	*redist_metric;
82
	} v;
83
	int lineno;
84
} YYSTYPE;
85
86
#define MAXPUSHBACK	128
87
88
static int		 yyerror(const char *, ...)
89
    __attribute__((__format__ (printf, 1, 2)))
90
    __attribute__((__nonnull__ (1)));
91
static int		 kw_cmp(const void *, const void *);
92
static int		 lookup(char *);
93
static int		 lgetc(int);
94
static int		 lungetc(int);
95
static int		 findeol(void);
96
static int		 yylex(void);
97
static int		 check_file_secrecy(int, const char *);
98
static struct file	*pushfile(const char *, int);
99
static int		 popfile(void);
100
static int		 yyparse(void);
101
static int		 symset(const char *, const char *, int);
102
static char		*symget(const char *);
103
static struct eigrp	*conf_get_instance(uint16_t);
104
static struct eigrp_iface *conf_get_if(struct kif *);
105
int			 conf_check_rdomain(unsigned int);
106
static void		 clear_config(struct eigrpd_conf *xconf);
107
static uint32_t	 get_rtr_id(void);
108
static int		 get_prefix(const char *, union eigrpd_addr *, uint8_t *);
109
110
static struct file		*file, *topfile;
111
static struct files		 files = TAILQ_HEAD_INITIALIZER(files);
112
static struct symhead		 symhead = TAILQ_HEAD_INITIALIZER(symhead);
113
static struct eigrpd_conf	*conf;
114
static int			 errors;
115
116
static int			 af;
117
static struct eigrp		*eigrp;
118
static struct eigrp_iface	*ei;
119
120
static struct config_defaults	 globaldefs;
121
static struct config_defaults	 afdefs;
122
static struct config_defaults	 asdefs;
123
static struct config_defaults	 ifacedefs;
124
static struct config_defaults	*defs;
125
126
static unsigned char		*parsebuf;
127
static int			 parseindex;
128
static unsigned char		 pushback_buffer[MAXPUSHBACK];
129
static int			 pushback_index;
130
131
%}
132
133
%token	ROUTERID AS FIBUPDATE RDOMAIN REDISTRIBUTE METRIC DFLTMETRIC
134
%token	MAXHOPS MAXPATHS VARIANCE FIBPRIORITY_INT FIBPRIORITY_EXT
135
%token	FIBPRIORITY_SUMM SUMMARY_ADDR
136
%token	AF IPV4 IPV6 HELLOINTERVAL HOLDTIME KVALUES ACTIVETIMEOUT
137
%token	INTERFACE PASSIVE DELAY BANDWIDTH SPLITHORIZON
138
%token	YES NO
139
%token	INCLUDE
140
%token	ERROR
141
%token	<v.string>	STRING
142
%token	<v.number>	NUMBER
143
%type	<v.number>	yesno no eigrp_af
144
%type	<v.string>	string
145
%type	<v.redist>	redistribute
146
%type	<v.redist_metric> redist_metric opt_red_metric
147
148
%%
149
150
grammar		: /* empty */
151
		| grammar include '\n'
152
		| grammar '\n'
153
		| grammar conf_main '\n'
154
		| grammar varset '\n'
155
		| grammar af '\n'
156
		| grammar error '\n'		{ file->errors++; }
157
		;
158
159
include		: INCLUDE STRING {
160
			struct file	*nfile;
161
162
			if ((nfile = pushfile($2, 1)) == NULL) {
163
				yyerror("failed to include file %s", $2);
164
				free($2);
165
				YYERROR;
166
			}
167
			free($2);
168
169
			file = nfile;
170
			lungetc('\n');
171
		}
172
		;
173
174
string		: string STRING	{
175
			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
176
				free($1);
177
				free($2);
178
				yyerror("string: asprintf");
179
				YYERROR;
180
			}
181
			free($1);
182
			free($2);
183
		}
184
		| STRING
185
		;
186
187
optnl		: '\n' optnl
188
		|
189
		;
190
191
nl		: '\n' optnl		/* one newline or more */
192
		;
193
194
yesno		: YES	{ $$ = 1; }
195
		| NO	{ $$ = 0; }
196
		;
197
198
no		: /* empty */	{ $$ = 0; }
199
		| NO		{ $$ = 1; }
200
		;
201
202
eigrp_af	: IPV4	{ $$ = AF_INET; }
203
		| IPV6	{ $$ = AF_INET6; }
204
		;
205
206
varset		: STRING '=' string {
207
			char *s = $1;
208
			if (global.cmd_opts & EIGRPD_OPT_VERBOSE)
209
				printf("%s = \"%s\"\n", $1, $3);
210
			while (*s++) {
211
				if (isspace((unsigned char)*s)) {
212
					yyerror("macro name cannot contain "
213
					    "whitespace");
214
					YYERROR;
215
				}
216
			}
217
			if (symset($1, $3, 0) == -1)
218
				fatal("cannot store variable");
219
			free($1);
220
			free($3);
221
		}
222
		;
223
224
conf_main	: ROUTERID STRING {
225
			if (!inet_aton($2, &conf->rtr_id)) {
226
				yyerror("error parsing router-id");
227
				free($2);
228
				YYERROR;
229
			}
230
			free($2);
231
			if (bad_addr_v4(conf->rtr_id)) {
232
				yyerror("invalid router-id");
233
				YYERROR;
234
			}
235
		}
236
		| FIBUPDATE yesno {
237
			if ($2 == 0)
238
				conf->flags |= EIGRPD_FLAG_NO_FIB_UPDATE;
239
			else
240
				conf->flags &= ~EIGRPD_FLAG_NO_FIB_UPDATE;
241
		}
242
		| RDOMAIN NUMBER {
243
			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
244
				yyerror("invalid rdomain");
245
				YYERROR;
246
			}
247
			conf->rdomain = $2;
248
		}
249
		| FIBPRIORITY_INT NUMBER {
250
			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
251
				yyerror("invalid fib-priority");
252
				YYERROR;
253
			}
254
			conf->fib_priority_internal = $2;
255
		}
256
		| FIBPRIORITY_EXT NUMBER {
257
			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
258
				yyerror("invalid fib-priority");
259
				YYERROR;
260
			}
261
			conf->fib_priority_external = $2;
262
		}
263
		| FIBPRIORITY_SUMM NUMBER {
264
			if ($2 <= RTP_NONE || $2 > RTP_MAX) {
265
				yyerror("invalid fib-priority");
266
				YYERROR;
267
			}
268
			conf->fib_priority_summary = $2;
269
		}
270
		| defaults
271
		;
272
273
af		: AF eigrp_af {
274
			af = $2;
275
			afdefs = *defs;
276
			defs = &afdefs;
277
		} af_block {
278
			af = AF_UNSPEC;
279
			defs = &globaldefs;
280
		}
281
		;
282
283
af_block	: '{' optnl afopts_l '}'
284
		| '{' optnl '}'
285
		|
286
		;
287
288
afopts_l	: afopts_l afoptsl nl
289
		| afoptsl optnl
290
		;
291
292
afoptsl		: as
293
		| defaults
294
		;
295
296
as		: AS NUMBER {
297
			if ($2 < EIGRP_MIN_AS || $2 > EIGRP_MAX_AS) {
298
				yyerror("invalid autonomous-system");
299
				YYERROR;
300
			}
301
			eigrp = conf_get_instance($2);
302
			if (eigrp == NULL)
303
				YYERROR;
304
305
			asdefs = *defs;
306
			defs = &asdefs;
307
		} as_block {
308
			memcpy(eigrp->kvalues, defs->kvalues,
309
			    sizeof(eigrp->kvalues));
310
			eigrp->active_timeout = defs->active_timeout;
311
			eigrp->maximum_hops = defs->maximum_hops;
312
			eigrp->maximum_paths = defs->maximum_paths;
313
			eigrp->variance = defs->variance;
314
			eigrp->dflt_metric = defs->dflt_metric;
315
			eigrp = NULL;
316
			defs = &afdefs;
317
		}
318
		;
319
320
as_block	: '{' optnl asopts_l '}'
321
		| '{' optnl '}'
322
		|
323
		;
324
325
asopts_l	: asopts_l asoptsl nl
326
		| asoptsl optnl
327
		;
328
329
asoptsl		: interface
330
		| redistribute {
331
			SIMPLEQ_INSERT_TAIL(&eigrp->redist_list, $1, entry);
332
		}
333
		| defaults
334
		;
335
336
interface	: INTERFACE STRING {
337
			struct kif	*kif;
338
339
			if ((kif = kif_findname($2)) == NULL) {
340
				yyerror("unknown interface %s", $2);
341
				free($2);
342
				YYERROR;
343
			}
344
			free($2);
345
			ei = conf_get_if(kif);
346
			if (ei == NULL)
347
				YYERROR;
348
349
			ifacedefs = *defs;
350
			defs = &ifacedefs;
351
		} interface_block {
352
			ei->hello_holdtime = defs->hello_holdtime;
353
			ei->hello_interval = defs->hello_interval;
354
			ei->delay = defs->delay;
355
			ei->bandwidth = defs->bandwidth;
356
			ei->splithorizon = defs->splithorizon;
357
			ei = NULL;
358
			defs = &asdefs;
359
		}
360
		;
361
362
interface_block	: '{' optnl interfaceopts_l '}'
363
		| '{' optnl '}'
364
		|
365
		;
366
367
interfaceopts_l	: interfaceopts_l interfaceoptsl nl
368
		| interfaceoptsl optnl
369
		;
370
371
interfaceoptsl	: PASSIVE { ei->passive = 1; }
372
		| SUMMARY_ADDR STRING {
373
			struct summary_addr	*s, *tmp;
374
375
			if ((s = calloc(1, sizeof(*s))) == NULL)
376
				fatal(NULL);
377
			if (get_prefix($2, &s->prefix, &s->prefixlen) < 0) {
378
				yyerror("invalid summary-address");
379
				free($2);
380
				free(s);
381
				YYERROR;
382
			}
383
			free($2);
384
385
			TAILQ_FOREACH(tmp, &ei->summary_list, entry) {
386
				if (eigrp_prefixcmp(af, &s->prefix,
387
				    &tmp->prefix, min(s->prefixlen,
388
				    tmp->prefixlen)) == 0) {
389
					yyerror("summary-address conflicts "
390
					    "with another summary-address "
391
					    "already configured");
392
					YYERROR;
393
				}
394
			}
395
396
			TAILQ_INSERT_TAIL(&ei->summary_list, s, entry);
397
		}
398
		| iface_defaults
399
		;
400
401
redistribute	: no REDISTRIBUTE STRING opt_red_metric {
402
			struct redistribute	*r;
403
404
			if ((r = calloc(1, sizeof(*r))) == NULL)
405
				fatal(NULL);
406
			if (!strcmp($3, "default"))
407
				r->type = REDIST_DEFAULT;
408
			else if (!strcmp($3, "static"))
409
				r->type = REDIST_STATIC;
410
			else if (!strcmp($3, "rip"))
411
				r->type = REDIST_RIP;
412
			else if (!strcmp($3, "ospf"))
413
				r->type = REDIST_OSPF;
414
			else if (!strcmp($3, "connected"))
415
				r->type = REDIST_CONNECTED;
416
			else if (get_prefix($3, &r->addr, &r->prefixlen) >= 0)
417
				r->type = REDIST_ADDR;
418
			else {
419
				yyerror("invalid redistribute");
420
				free($3);
421
				free(r);
422
				YYERROR;
423
			}
424
425
			r->af = af;
426
			if ($1)
427
				r->type |= REDIST_NO;
428
			r->metric = $4;
429
			free($3);
430
			$$ = r;
431
		}
432
		;
433
434
redist_metric	: NUMBER NUMBER NUMBER NUMBER NUMBER {
435
			struct redist_metric	*m;
436
437
			if ($1 < MIN_BANDWIDTH || $1 > MAX_BANDWIDTH) {
438
				yyerror("bandwidth out of range (%d-%d)",
439
				    MIN_BANDWIDTH, MAX_BANDWIDTH);
440
				YYERROR;
441
			}
442
			if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
443
				yyerror("delay out of range (%d-%d)",
444
				    MIN_DELAY, MAX_DELAY);
445
				YYERROR;
446
			}
447
			if ($3 < MIN_RELIABILITY || $3 > MAX_RELIABILITY) {
448
				yyerror("reliability out of range (%d-%d)",
449
				    MIN_RELIABILITY, MAX_RELIABILITY);
450
				YYERROR;
451
			}
452
			if ($4 < MIN_LOAD || $4 > MAX_LOAD) {
453
				yyerror("load out of range (%d-%d)",
454
				    MIN_LOAD, MAX_LOAD);
455
				YYERROR;
456
			}
457
			if ($5 < MIN_MTU || $5 > MAX_MTU) {
458
				yyerror("mtu out of range (%d-%d)",
459
				    MIN_MTU, MAX_MTU);
460
				YYERROR;
461
			}
462
463
			if ((m = calloc(1, sizeof(*m))) == NULL)
464
				fatal(NULL);
465
			m->bandwidth = $1;
466
			m->delay = $2;
467
			m->reliability = $3;
468
			m->load = $4;
469
			m->mtu = $5;
470
471
			$$ = m;
472
		}
473
		;
474
475
opt_red_metric	: /* empty */		{ $$ = NULL; }
476
		| METRIC redist_metric 	{ $$ = $2; }
477
		;
478
479
defaults	: KVALUES NUMBER NUMBER NUMBER NUMBER NUMBER NUMBER {
480
			if ($2 < MIN_KVALUE || $2 > MAX_KVALUE ||
481
			    $3 < MIN_KVALUE || $3 > MAX_KVALUE ||
482
			    $4 < MIN_KVALUE || $4 > MAX_KVALUE ||
483
			    $5 < MIN_KVALUE || $5 > MAX_KVALUE ||
484
			    $6 < MIN_KVALUE || $6 > MAX_KVALUE ||
485
			    $7 < MIN_KVALUE || $7 > MAX_KVALUE) {
486
				yyerror("k-value out of range (%d-%d)",
487
				    MIN_KVALUE, MAX_KVALUE);
488
				YYERROR;
489
			}
490
			defs->kvalues[0] = $2;
491
			defs->kvalues[1] = $3;
492
			defs->kvalues[2] = $4;
493
			defs->kvalues[3] = $5;
494
			defs->kvalues[4] = $6;
495
			defs->kvalues[5] = $7;
496
		}
497
		| ACTIVETIMEOUT NUMBER {
498
			if ($2 < MIN_ACTIVE_TIMEOUT ||
499
			    $2 > MAX_ACTIVE_TIMEOUT) {
500
				yyerror("active-timeout out of range (%d-%d)",
501
				    MIN_ACTIVE_TIMEOUT, MAX_ACTIVE_TIMEOUT);
502
				YYERROR;
503
			}
504
			defs->active_timeout = $2;
505
		}
506
		| MAXHOPS NUMBER {
507
			if ($2 < MIN_MAXIMUM_HOPS ||
508
			    $2 > MAX_MAXIMUM_HOPS) {
509
				yyerror("maximum-hops out of range (%d-%d)",
510
				    MIN_MAXIMUM_HOPS, MAX_MAXIMUM_HOPS);
511
				YYERROR;
512
			}
513
			defs->maximum_hops = $2;
514
		}
515
		| MAXPATHS NUMBER {
516
			if ($2 < MIN_MAXIMUM_PATHS ||
517
			    $2 > MAX_MAXIMUM_PATHS) {
518
				yyerror("maximum-paths out of range (%d-%d)",
519
				    MIN_MAXIMUM_PATHS, MAX_MAXIMUM_PATHS);
520
				YYERROR;
521
			}
522
			defs->maximum_paths = $2;
523
		}
524
		| VARIANCE NUMBER {
525
			if ($2 < MIN_VARIANCE ||
526
			    $2 > MAX_VARIANCE) {
527
				yyerror("variance out of range (%d-%d)",
528
				    MIN_VARIANCE, MAX_VARIANCE);
529
				YYERROR;
530
			}
531
			defs->variance = $2;
532
		}
533
		| DFLTMETRIC redist_metric {
534
			defs->dflt_metric = $2;
535
		}
536
		| iface_defaults
537
		;
538
539
iface_defaults	: HELLOINTERVAL NUMBER {
540
			if ($2 < MIN_HELLO_INTERVAL ||
541
			    $2 > MAX_HELLO_INTERVAL) {
542
				yyerror("hello-interval out of range (%d-%d)",
543
				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
544
				YYERROR;
545
			}
546
			defs->hello_interval = $2;
547
		}
548
		| HOLDTIME NUMBER {
549
			if ($2 < MIN_HELLO_HOLDTIME ||
550
			    $2 > MAX_HELLO_HOLDTIME) {
551
				yyerror("hold-timel out of range (%d-%d)",
552
				    MIN_HELLO_HOLDTIME,
553
				    MAX_HELLO_HOLDTIME);
554
				YYERROR;
555
			}
556
			defs->hello_holdtime = $2;
557
		}
558
		| DELAY NUMBER {
559
			if ($2 < MIN_DELAY || $2 > MAX_DELAY) {
560
				yyerror("delay out of range (%d-%d)",
561
				    MIN_DELAY, MAX_DELAY);
562
				YYERROR;
563
			}
564
			defs->delay = $2;
565
		}
566
		| BANDWIDTH NUMBER {
567
			if ($2 < MIN_BANDWIDTH || $2 > MAX_BANDWIDTH) {
568
				yyerror("bandwidth out of range (%d-%d)",
569
				    MIN_BANDWIDTH, MAX_BANDWIDTH);
570
				YYERROR;
571
			}
572
			defs->bandwidth = $2;
573
		}
574
		| SPLITHORIZON yesno {
575
			defs->splithorizon = $2;
576
		}
577
		;
578
579
%%
580
581
struct keywords {
582
	const char	*k_name;
583
	int		 k_val;
584
};
585
586
static int
587
yyerror(const char *fmt, ...)
588
{
589
	va_list		 ap;
590
	char		*msg;
591
592
	file->errors++;
593
	va_start(ap, fmt);
594
	if (vasprintf(&msg, fmt, ap) == -1)
595
		fatalx("yyerror vasprintf");
596
	va_end(ap);
597
	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
598
	free(msg);
599
	return (0);
600
}
601
602
static int
603
kw_cmp(const void *k, const void *e)
604
{
605
	return (strcmp(k, ((const struct keywords *)e)->k_name));
606
}
607
608
static int
609
lookup(char *s)
610
{
611
	/* this has to be sorted always */
612
	static const struct keywords keywords[] = {
613
		{"active-timeout",		ACTIVETIMEOUT},
614
		{"address-family",		AF},
615
		{"autonomous-system",		AS},
616
		{"bandwidth",			BANDWIDTH},
617
		{"default-metric",		DFLTMETRIC},
618
		{"delay",			DELAY},
619
		{"fib-priority-external",	FIBPRIORITY_EXT},
620
		{"fib-priority-internal",	FIBPRIORITY_INT},
621
		{"fib-priority-summary",	FIBPRIORITY_SUMM},
622
		{"fib-update",			FIBUPDATE},
623
		{"hello-interval",		HELLOINTERVAL},
624
		{"holdtime",			HOLDTIME},
625
		{"include",			INCLUDE},
626
		{"interface",			INTERFACE},
627
		{"ipv4",			IPV4},
628
		{"ipv6",			IPV6},
629
		{"k-values",			KVALUES},
630
		{"maximum-hops",		MAXHOPS},
631
		{"maximum-paths",		MAXPATHS},
632
		{"metric",			METRIC},
633
		{"no",				NO},
634
		{"passive",			PASSIVE},
635
		{"rdomain",			RDOMAIN},
636
		{"redistribute",		REDISTRIBUTE},
637
		{"router-id",			ROUTERID},
638
		{"split-horizon",		SPLITHORIZON},
639
		{"summary-address",		SUMMARY_ADDR},
640
		{"variance",			VARIANCE},
641
		{"yes",				YES}
642
	};
643
	const struct keywords	*p;
644
645
	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
646
	    sizeof(keywords[0]), kw_cmp);
647
648
	if (p)
649
		return (p->k_val);
650
	else
651
		return (STRING);
652
}
653
654
static int
655
lgetc(int quotec)
656
{
657
	int		c, next;
658
659
	if (parsebuf) {
660
		/* Read character from the parsebuffer instead of input. */
661
		if (parseindex >= 0) {
662
			c = parsebuf[parseindex++];
663
			if (c != '\0')
664
				return (c);
665
			parsebuf = NULL;
666
		} else
667
			parseindex++;
668
	}
669
670
	if (pushback_index)
671
		return (pushback_buffer[--pushback_index]);
672
673
	if (quotec) {
674
		if ((c = getc(file->stream)) == EOF) {
675
			yyerror("reached end of file while parsing "
676
			    "quoted string");
677
			if (file == topfile || popfile() == EOF)
678
				return (EOF);
679
			return (quotec);
680
		}
681
		return (c);
682
	}
683
684
	while ((c = getc(file->stream)) == '\\') {
685
		next = getc(file->stream);
686
		if (next != '\n') {
687
			c = next;
688
			break;
689
		}
690
		yylval.lineno = file->lineno;
691
		file->lineno++;
692
	}
693
694
	while (c == EOF) {
695
		if (file == topfile || popfile() == EOF)
696
			return (EOF);
697
		c = getc(file->stream);
698
	}
699
	return (c);
700
}
701
702
static int
703
lungetc(int c)
704
{
705
	if (c == EOF)
706
		return (EOF);
707
	if (parsebuf) {
708
		parseindex--;
709
		if (parseindex >= 0)
710
			return (c);
711
	}
712
	if (pushback_index < MAXPUSHBACK-1)
713
		return (pushback_buffer[pushback_index++] = c);
714
	else
715
		return (EOF);
716
}
717
718
static int
719
findeol(void)
720
{
721
	int	c;
722
723
	parsebuf = NULL;
724
725
	/* skip to either EOF or the first real EOL */
726
	while (1) {
727
		if (pushback_index)
728
			c = pushback_buffer[--pushback_index];
729
		else
730
			c = lgetc(0);
731
		if (c == '\n') {
732
			file->lineno++;
733
			break;
734
		}
735
		if (c == EOF)
736
			break;
737
	}
738
	return (ERROR);
739
}
740
741
static int
742
yylex(void)
743
{
744
	unsigned char	 buf[8096];
745
	unsigned char	*p, *val;
746
	int		 quotec, next, c;
747
	int		 token;
748
749
top:
750
	p = buf;
751
	while ((c = lgetc(0)) == ' ' || c == '\t')
752
		; /* nothing */
753
754
	yylval.lineno = file->lineno;
755
	if (c == '#')
756
		while ((c = lgetc(0)) != '\n' && c != EOF)
757
			; /* nothing */
758
	if (c == '$' && parsebuf == NULL) {
759
		while (1) {
760
			if ((c = lgetc(0)) == EOF)
761
				return (0);
762
763
			if (p + 1 >= buf + sizeof(buf) - 1) {
764
				yyerror("string too long");
765
				return (findeol());
766
			}
767
			if (isalnum(c) || c == '_') {
768
				*p++ = c;
769
				continue;
770
			}
771
			*p = '\0';
772
			lungetc(c);
773
			break;
774
		}
775
		val = symget(buf);
776
		if (val == NULL) {
777
			yyerror("macro '%s' not defined", buf);
778
			return (findeol());
779
		}
780
		parsebuf = val;
781
		parseindex = 0;
782
		goto top;
783
	}
784
785
	switch (c) {
786
	case '\'':
787
	case '"':
788
		quotec = c;
789
		while (1) {
790
			if ((c = lgetc(quotec)) == EOF)
791
				return (0);
792
			if (c == '\n') {
793
				file->lineno++;
794
				continue;
795
			} else if (c == '\\') {
796
				if ((next = lgetc(quotec)) == EOF)
797
					return (0);
798
				if (next == quotec || c == ' ' || c == '\t')
799
					c = next;
800
				else if (next == '\n') {
801
					file->lineno++;
802
					continue;
803
				} else
804
					lungetc(next);
805
			} else if (c == quotec) {
806
				*p = '\0';
807
				break;
808
			} else if (c == '\0') {
809
				yyerror("syntax error");
810
				return (findeol());
811
			}
812
			if (p + 1 >= buf + sizeof(buf) - 1) {
813
				yyerror("string too long");
814
				return (findeol());
815
			}
816
			*p++ = c;
817
		}
818
		yylval.v.string = strdup(buf);
819
		if (yylval.v.string == NULL)
820
			err(1, "yylex: strdup");
821
		return (STRING);
822
	}
823
824
#define allowed_to_end_number(x) \
825
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
826
827
	if (c == '-' || isdigit(c)) {
828
		do {
829
			*p++ = c;
830
			if ((unsigned)(p-buf) >= sizeof(buf)) {
831
				yyerror("string too long");
832
				return (findeol());
833
			}
834
		} while ((c = lgetc(0)) != EOF && isdigit(c));
835
		lungetc(c);
836
		if (p == buf + 1 && buf[0] == '-')
837
			goto nodigits;
838
		if (c == EOF || allowed_to_end_number(c)) {
839
			const char *errstr = NULL;
840
841
			*p = '\0';
842
			yylval.v.number = strtonum(buf, LLONG_MIN,
843
			    LLONG_MAX, &errstr);
844
			if (errstr) {
845
				yyerror("\"%s\" invalid number: %s",
846
				    buf, errstr);
847
				return (findeol());
848
			}
849
			return (NUMBER);
850
		} else {
851
nodigits:
852
			while (p > buf + 1)
853
				lungetc(*--p);
854
			c = *--p;
855
			if (c == '-')
856
				return (c);
857
		}
858
	}
859
860
#define allowed_in_string(x) \
861
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
862
	x != '{' && x != '}' && \
863
	x != '!' && x != '=' && x != '#' && \
864
	x != ','))
865
866
	if (isalnum(c) || c == ':' || c == '_') {
867
		do {
868
			*p++ = c;
869
			if ((unsigned)(p-buf) >= sizeof(buf)) {
870
				yyerror("string too long");
871
				return (findeol());
872
			}
873
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
874
		lungetc(c);
875
		*p = '\0';
876
		if ((token = lookup(buf)) == STRING)
877
			if ((yylval.v.string = strdup(buf)) == NULL)
878
				err(1, "yylex: strdup");
879
		return (token);
880
	}
881
	if (c == '\n') {
882
		yylval.lineno = file->lineno;
883
		file->lineno++;
884
	}
885
	if (c == EOF)
886
		return (0);
887
	return (c);
888
}
889
890
static int
891
check_file_secrecy(int fd, const char *fname)
892
{
893
	struct stat	st;
894
895
	if (fstat(fd, &st)) {
896
		log_warn("cannot stat %s", fname);
897
		return (-1);
898
	}
899
	if (st.st_uid != 0 && st.st_uid != getuid()) {
900
		log_warnx("%s: owner not root or current user", fname);
901
		return (-1);
902
	}
903
	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
904
		log_warnx("%s: group writable or world read/writable", fname);
905
		return (-1);
906
	}
907
	return (0);
908
}
909
910
static struct file *
911
pushfile(const char *name, int secret)
912
{
913
	struct file	*nfile;
914
915
	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
916
		log_warn("calloc");
917
		return (NULL);
918
	}
919
	if ((nfile->name = strdup(name)) == NULL) {
920
		log_warn("strdup");
921
		free(nfile);
922
		return (NULL);
923
	}
924
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
925
		log_warn("%s", nfile->name);
926
		free(nfile->name);
927
		free(nfile);
928
		return (NULL);
929
	} else if (secret &&
930
	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
931
		fclose(nfile->stream);
932
		free(nfile->name);
933
		free(nfile);
934
		return (NULL);
935
	}
936
	nfile->lineno = 1;
937
	TAILQ_INSERT_TAIL(&files, nfile, entry);
938
	return (nfile);
939
}
940
941
static int
942
popfile(void)
943
{
944
	struct file	*prev;
945
946
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
947
		prev->errors += file->errors;
948
949
	TAILQ_REMOVE(&files, file, entry);
950
	fclose(file->stream);
951
	free(file->name);
952
	free(file);
953
	file = prev;
954
	return (file ? 0 : EOF);
955
}
956
957
struct eigrpd_conf *
958
parse_config(char *filename)
959
{
960
	struct sym	*sym, *next;
961
962
	conf = config_new_empty();
963
	conf->rdomain = 0;
964
	conf->fib_priority_internal = RTP_EIGRP;
965
	conf->fib_priority_external = RTP_EIGRP;
966
	conf->fib_priority_summary = RTP_EIGRP;
967
968
	defs = &globaldefs;
969
	defs->kvalues[0] = defs->kvalues[2] = 1;
970
	defs->active_timeout = DEFAULT_ACTIVE_TIMEOUT;
971
	defs->maximum_hops = DEFAULT_MAXIMUM_HOPS;
972
	defs->maximum_paths = DEFAULT_MAXIMUM_PATHS;
973
	defs->variance = DEFAULT_VARIANCE;
974
	defs->hello_holdtime = DEFAULT_HELLO_HOLDTIME;
975
	defs->hello_interval = DEFAULT_HELLO_INTERVAL;
976
	defs->delay = DEFAULT_DELAY;
977
	defs->bandwidth = DEFAULT_BANDWIDTH;
978
	defs->splithorizon = 1;
979
980
	if ((file = pushfile(filename,
981
	    !(global.cmd_opts & EIGRPD_OPT_NOACTION))) == NULL) {
982
		free(conf);
983
		return (NULL);
984
	}
985
	topfile = file;
986
987
	yyparse();
988
	errors = file->errors;
989
	popfile();
990
991
	/* Free macros and check which have not been used. */
992
	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
993
		if ((global.cmd_opts & EIGRPD_OPT_VERBOSE2) && !sym->used)
994
			fprintf(stderr, "warning: macro '%s' not "
995
			    "used\n", sym->nam);
996
		if (!sym->persist) {
997
			free(sym->nam);
998
			free(sym->val);
999
			TAILQ_REMOVE(&symhead, sym, entry);
1000
			free(sym);
1001
		}
1002
	}
1003
1004
	/* check that all interfaces belong to the configured rdomain */
1005
	errors += conf_check_rdomain(conf->rdomain);
1006
1007
	if (errors) {
1008
		clear_config(conf);
1009
		return (NULL);
1010
	}
1011
1012
	if (conf->rtr_id.s_addr == 0)
1013
		conf->rtr_id.s_addr = get_rtr_id();
1014
1015
	return (conf);
1016
}
1017
1018
static int
1019
symset(const char *nam, const char *val, int persist)
1020
{
1021
	struct sym	*sym;
1022
1023
	TAILQ_FOREACH(sym, &symhead, entry) {
1024
		if (strcmp(nam, sym->nam) == 0)
1025
			break;
1026
	}
1027
1028
	if (sym != NULL) {
1029
		if (sym->persist == 1)
1030
			return (0);
1031
		else {
1032
			free(sym->nam);
1033
			free(sym->val);
1034
			TAILQ_REMOVE(&symhead, sym, entry);
1035
			free(sym);
1036
		}
1037
	}
1038
	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1039
		return (-1);
1040
1041
	sym->nam = strdup(nam);
1042
	if (sym->nam == NULL) {
1043
		free(sym);
1044
		return (-1);
1045
	}
1046
	sym->val = strdup(val);
1047
	if (sym->val == NULL) {
1048
		free(sym->nam);
1049
		free(sym);
1050
		return (-1);
1051
	}
1052
	sym->used = 0;
1053
	sym->persist = persist;
1054
	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1055
	return (0);
1056
}
1057
1058
int
1059
cmdline_symset(char *s)
1060
{
1061
	char	*sym, *val;
1062
	int	ret;
1063
	size_t	len;
1064
1065
	if ((val = strrchr(s, '=')) == NULL)
1066
		return (-1);
1067
1068
	len = strlen(s) - strlen(val) + 1;
1069
	if ((sym = malloc(len)) == NULL)
1070
		errx(1, "cmdline_symset: malloc");
1071
1072
	strlcpy(sym, s, len);
1073
1074
	ret = symset(sym, val + 1, 1);
1075
	free(sym);
1076
1077
	return (ret);
1078
}
1079
1080
static char *
1081
symget(const char *nam)
1082
{
1083
	struct sym	*sym;
1084
1085
	TAILQ_FOREACH(sym, &symhead, entry) {
1086
		if (strcmp(nam, sym->nam) == 0) {
1087
			sym->used = 1;
1088
			return (sym->val);
1089
		}
1090
	}
1091
	return (NULL);
1092
}
1093
1094
static struct eigrp *
1095
conf_get_instance(uint16_t as)
1096
{
1097
	struct eigrp	*e, *tmp;
1098
1099
	if (eigrp_find(conf, af, as)) {
1100
		yyerror("autonomous-system %u already configured"
1101
		    "for address-family %s", as, af_name(af));
1102
		return (NULL);
1103
	}
1104
1105
	e = calloc(1, sizeof(struct eigrp));
1106
	if (e == NULL)
1107
		fatal(NULL);
1108
1109
	e->af = af;
1110
	e->as = as;
1111
	SIMPLEQ_INIT(&e->redist_list);
1112
	TAILQ_INIT(&e->ei_list);
1113
	RB_INIT(&e->nbrs);
1114
	RB_INIT(&e->topology);
1115
1116
	/* start local sequence number used by RTP */
1117
	e->seq_num = 1;
1118
1119
	/* order by address-family and then by autonomous-system */
1120
	TAILQ_FOREACH(tmp, &conf->instances, entry)
1121
		if (tmp->af > e->af ||
1122
		    (tmp->af == e->af && tmp->as > e->as))
1123
			break;
1124
	if (tmp)
1125
		TAILQ_INSERT_BEFORE(tmp, e, entry);
1126
	else
1127
		TAILQ_INSERT_TAIL(&conf->instances, e, entry);
1128
1129
	return (e);
1130
}
1131
1132
static struct eigrp_iface *
1133
conf_get_if(struct kif *kif)
1134
{
1135
	struct eigrp_iface	*e;
1136
1137
	TAILQ_FOREACH(e, &eigrp->ei_list, e_entry)
1138
		if (e->iface->ifindex == kif->ifindex) {
1139
			yyerror("interface %s already configured "
1140
			    "for address-family %s and "
1141
			    "autonomous-system %u", kif->ifname,
1142
			    af_name(af), eigrp->as);
1143
			return (NULL);
1144
		}
1145
1146
	e = eigrp_if_new(conf, eigrp, kif);
1147
1148
	return (e);
1149
}
1150
1151
int
1152
conf_check_rdomain(unsigned int rdomain)
1153
{
1154
	struct iface	*iface;
1155
	int		 errs = 0;
1156
1157
	TAILQ_FOREACH(iface, &conf->iface_list, entry) {
1158
		if (iface->rdomain != rdomain) {
1159
			logit(LOG_CRIT, "interface %s not in rdomain %u",
1160
			    iface->name, rdomain);
1161
			errs++;
1162
		}
1163
	}
1164
1165
	return (errs);
1166
}
1167
1168
static void
1169
clear_config(struct eigrpd_conf *xconf)
1170
{
1171
	struct eigrp		*e;
1172
	struct redistribute	*r;
1173
	struct eigrp_iface	*i;
1174
	struct summary_addr	*s;
1175
1176
	while ((e = TAILQ_FIRST(&xconf->instances)) != NULL) {
1177
		while (!SIMPLEQ_EMPTY(&e->redist_list)) {
1178
			r = SIMPLEQ_FIRST(&e->redist_list);
1179
			SIMPLEQ_REMOVE_HEAD(&e->redist_list, entry);
1180
			free(r);
1181
		}
1182
1183
		while ((i = TAILQ_FIRST(&e->ei_list)) != NULL) {
1184
			RB_REMOVE(iface_id_head, &ifaces_by_id, i);
1185
			TAILQ_REMOVE(&e->ei_list, i, e_entry);
1186
			TAILQ_REMOVE(&e->ei_list, i, i_entry);
1187
			while ((s = TAILQ_FIRST(&i->summary_list)) != NULL) {
1188
				TAILQ_REMOVE(&i->summary_list, s, entry);
1189
				free(s);
1190
			}
1191
			if (TAILQ_EMPTY(&i->iface->ei_list)) {
1192
				TAILQ_REMOVE(&xconf->iface_list, i->iface, entry);
1193
				free(i->iface);
1194
			}
1195
			free(i);
1196
		}
1197
1198
		TAILQ_REMOVE(&xconf->instances, e, entry);
1199
		free(e);
1200
	}
1201
1202
	free(xconf);
1203
}
1204
1205
static uint32_t
1206
get_rtr_id(void)
1207
{
1208
	struct ifaddrs		*ifap, *ifa;
1209
	uint32_t		 ip = 0, cur, localnet;
1210
1211
	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1212
1213
	if (getifaddrs(&ifap) == -1)
1214
		fatal("getifaddrs");
1215
1216
	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1217
		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1218
			continue;
1219
		if (ifa->ifa_addr->sa_family != AF_INET)
1220
			continue;
1221
		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1222
		if ((cur & localnet) == localnet)	/* skip 127/8 */
1223
			continue;
1224
		if (ntohl(cur) < ntohl(ip) || ip == 0)
1225
			ip = cur;
1226
	}
1227
	freeifaddrs(ifap);
1228
1229
	if (ip == 0)
1230
		fatal("router-id is 0.0.0.0");
1231
1232
	return (ip);
1233
}
1234
1235
static int
1236
get_prefix(const char *s, union eigrpd_addr *addr, uint8_t *plen)
1237
{
1238
	char			*p, *ps;
1239
	const char		*errstr;
1240
	int			 maxplen;
1241
1242
	switch (af) {
1243
	case AF_INET:
1244
		maxplen = 32;
1245
		break;
1246
	case AF_INET6:
1247
		maxplen = 128;
1248
		break;
1249
	default:
1250
		return (-1);
1251
	}
1252
1253
	if ((p = strrchr(s, '/')) != NULL) {
1254
		*plen = strtonum(p + 1, 0, maxplen, &errstr);
1255
		if (errstr) {
1256
			log_warnx("prefixlen is %s: %s", errstr, p + 1);
1257
			return (-1);
1258
		}
1259
		if ((ps = malloc(strlen(s) - strlen(p) + 1)) == NULL)
1260
			fatal("get_prefix: malloc");
1261
		strlcpy(ps, s, strlen(s) - strlen(p) + 1);
1262
	} else {
1263
		if ((ps = strdup(s)) == NULL)
1264
			fatal("get_prefix: strdup");
1265
		*plen = maxplen;
1266
	}
1267
1268
	memset(addr, 0, sizeof(union eigrpd_addr));
1269
	switch (af) {
1270
	case AF_INET:
1271
		if (inet_pton(AF_INET, ps, &addr->v4) != 1) {
1272
			free(ps);
1273
			return (-1);
1274
		}
1275
		break;
1276
	case AF_INET6:
1277
		if (inet_pton(AF_INET6, ps, &addr->v6) != 1) {
1278
			free(ps);
1279
			return (-1);
1280
		}
1281
		break;
1282
	default:
1283
		free(ps);
1284
		return (-1);
1285
	}
1286
	eigrp_applymask(af, addr, addr, *plen);
1287
	free(ps);
1288
1289
	return (0);
1290
}