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