summaryrefslogtreecommitdiff
path: root/usr.sbin/ospfd/parse.y
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2005-01-28 14:05:41 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2005-01-28 14:05:41 +0000
commitccc5f735cc0a287cd117c41e690e7bab855d35df (patch)
tree73ce6ba377048b4358b66dcd68c700d22eea33eb /usr.sbin/ospfd/parse.y
parent0dcb4b3a6285e3cc2e96ba56db5f830a086696f7 (diff)
Welcome ospfd
started by Esben Norby some time ago by using the imsg/three process framework of bgpd. He implemented the basic concept plus the ospf finite state machines. Later I joined and helped him cleanup, debug and extend his work. Right now it is not particularly useful, major parts are still missing but is imported to allow more people to work on it. status: The basic protocol works for broadcast networks and the LS database is synchronized and updated. It is not possible to be DR or BDR on a network and other interface types like point-to-point are not yet supported. The shortest path tree is not calculated and so no routing information is exchanged with the kernel FIB. Not yet connected to the builds. OK henning@
Diffstat (limited to 'usr.sbin/ospfd/parse.y')
-rw-r--r--usr.sbin/ospfd/parse.y878
1 files changed, 878 insertions, 0 deletions
diff --git a/usr.sbin/ospfd/parse.y b/usr.sbin/ospfd/parse.y
new file mode 100644
index 00000000000..5cfad9b860c
--- /dev/null
+++ b/usr.sbin/ospfd/parse.y
@@ -0,0 +1,878 @@
+/* $OpenBSD: parse.y,v 1.1 2005/01/28 14:05:40 claudio Exp $ */
+
+/*
+ * Copyright (c) 2004 Esben Norby <esben.norby@ericsson.com>
+ * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
+ * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
+ * Copyright (c) 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
+ * Copyright (c) 2001 Theo de Raadt. All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+%{
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "ospf.h"
+#include "ospfd.h"
+#include "ospfe.h"
+#include "log.h"
+
+static struct ospfd_conf *conf;
+static FILE *fin = NULL;
+static int lineno = 1;
+static int errors = 0;
+static int pdebug = 1;
+char *infile;
+char *start_state;
+
+struct area *area = NULL;
+struct iface *iface = NULL;
+
+int yyerror(const char *, ...);
+int yyparse(void);
+int kw_cmp(const void *, const void *);
+int lookup(char *);
+int lgetc(FILE *);
+int lungetc(int);
+int findeol(void);
+int yylex(void);
+void clear_config(struct ospfd_conf *xconf);
+int check_file_secrecy(int fd, const char *fname);
+
+static struct {
+ u_int32_t dead_interval;
+ u_int16_t transfer_delay;
+ u_int16_t hello_interval;
+ u_int16_t rxmt_interval;
+ u_int16_t metric;
+ u_int8_t priority;
+} defaults;
+
+TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
+struct sym {
+ TAILQ_ENTRY(sym) entries;
+ int used;
+ int persist;
+ char *nam;
+ char *val;
+};
+
+int symset(const char *, const char *, int);
+char *symget(const char *);
+int atoul(char *, u_long *);
+struct area *conf_get_area(struct in_addr);
+struct iface *conf_get_if(char *, unsigned int);
+
+typedef struct {
+ union {
+ u_int32_t number;
+ char *string;
+ } v;
+ int lineno;
+} YYSTYPE;
+
+%}
+
+%token AREA INTERFACE ROUTERID FIBUPDATE
+%token AUTHKEY AUTHTYPE
+%token METRIC PASSIVE
+%token HELLOINTERVAL TRANSMITDELAY
+%token RETRANSMITINTERVAL ROUTERDEADTIME ROUTERPRIORITY
+%token ERROR
+%token <v.string> STRING
+%type <v.number> number yesno
+%type <v.string> string
+
+%%
+
+grammar : /* empty */
+ | grammar '\n'
+ | grammar conf_main '\n'
+ | grammar varset '\n'
+ | grammar area '\n'
+ | grammar error '\n' { errors++; }
+ ;
+
+number : STRING {
+ u_long ulval;
+
+ if (atoul($1, &ulval) == -1) {
+ yyerror("%s is not a number", $1);
+ free($1);
+ YYERROR;
+ } else
+ $$ = ulval;
+ free($1);
+ }
+ ;
+
+string : string STRING {
+ if (asprintf(&$$, "%s %s", $1, $2) == -1) {
+ free($1);
+ free($2);
+ yyerror("string: asprintf");
+ YYERROR;
+ }
+ free($1);
+ free($2);
+ }
+ | STRING
+ ;
+
+yesno : STRING {
+ if (!strcmp($1, "yes"))
+ $$ = 1;
+ else if (!strcmp($1, "no"))
+ $$ = 0;
+ else {
+ free($1);
+ YYERROR;
+ }
+ free($1);
+ }
+ ;
+
+varset : STRING '=' string {
+ if (conf->opts & OSPFD_OPT_VERBOSE)
+ printf("%s = \"%s\"\n", $1, $3);
+ if (symset($1, $3, 0) == -1)
+ fatal("cannot store variable");
+ free($1);
+ free($3);
+ }
+ ;
+
+conf_main : METRIC number {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ defaults.metric = $2;
+ }
+ | ROUTERPRIORITY number {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ defaults.priority = $2;
+ }
+ | ROUTERDEADTIME number {
+ if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
+ yyerror("router-dead-time out of range (%d-%d)",
+ MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
+ YYERROR;
+ }
+ defaults.dead_interval = $2;
+ }
+ | TRANSMITDELAY number {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ defaults.transfer_delay = $2;
+ }
+ | HELLOINTERVAL number {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ defaults.hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL number {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ defaults.rxmt_interval = $2;
+ }
+ | ROUTERID string {
+ if (!inet_aton($2, &conf->rtr_id)) {
+ yyerror("error parsing router-id");
+ free($2);
+ YYERROR;
+ }
+ }
+ | FIBUPDATE yesno {
+ if ($2 == 0)
+ conf->flags |= OSPFD_FLAG_NO_FIB_UPDATE;
+ else
+ conf->flags &= ~OSPFD_FLAG_NO_FIB_UPDATE;
+ }
+ ;
+
+authtype : AUTHTYPE STRING {
+ enum auth_type type;
+
+ if (!strcmp($2, "none"))
+ type = AUTH_NONE;
+ else if (!strcmp($2, "simple"))
+ type = AUTH_SIMPLE;
+ else if (!strcmp($2, "crypt"))
+ type = AUTH_SIMPLE;
+ else {
+ yyerror("unknown auth-type");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ iface->auth_type = type;
+ }
+ ;
+
+authkey : AUTHKEY STRING {
+ if (iface != NULL) {
+ iface->auth_key = $2;
+ /* XXX truncate and warn! */
+ }
+ }
+ ;
+
+optnl : '\n' optnl
+ |
+ ;
+
+nl : '\n' optnl /* one newline or more */
+ ;
+
+area : AREA string {
+ struct in_addr id;
+ if (inet_aton($2, &id) == 0) {
+ yyerror("error parsing area-id");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ area = conf_get_area(id);
+ } optnl '{' optnl {
+
+ } areaopts_l '}' {
+ area = NULL;
+ }
+ ;
+
+areaopts_l : areaopts_l areaoptsl
+ | areaoptsl
+ ;
+
+areaoptsl : interface nl
+ | METRIC number nl {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ area->metric = $2;
+ }
+ | ROUTERPRIORITY number nl {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ area->priority = $2;
+ }
+ | ROUTERDEADTIME number nl {
+ if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
+ yyerror("router-dead-time out of range (%d-%d)",
+ MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
+ YYERROR;
+ }
+ area->dead_interval = $2;
+ }
+ | TRANSMITDELAY number nl {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ area->transfer_delay = $2;
+ }
+ | HELLOINTERVAL number nl {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ area->hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL number nl {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ area->rxmt_interval = $2;
+ }
+ ;
+
+interface : INTERFACE STRING {
+ unsigned int idx;
+
+ if ((idx = if_nametoindex($2)) == 0 ) {
+ yyerror("unknown interface %s", $2);
+ free($2);
+ YYERROR;
+ }
+ iface = conf_get_if($2, idx);
+ free($2);
+ if (iface == NULL)
+ YYERROR;
+ iface->area = area;
+ LIST_INSERT_HEAD(&area->iface_list,
+ iface, entry);
+ iface->rtr_id = conf->rtr_id;
+ iface->passive = false;
+ } optnl '{' optnl {
+
+ } interfaceopts_l '}' {
+ iface = NULL;
+ }
+ ;
+
+interfaceopts_l : interfaceopts_l interfaceoptsl
+ | interfaceoptsl
+ ;
+
+interfaceoptsl : authkey nl
+ | authtype nl
+ | PASSIVE nl { iface->passive = true; }
+ | METRIC number nl {
+ if ($2 < MIN_METRIC || $2 > MAX_METRIC) {
+ yyerror("metric out of range (%d-%d)",
+ MIN_METRIC, MAX_METRIC);
+ YYERROR;
+ }
+ iface->metric = $2;
+ }
+ | ROUTERPRIORITY number nl {
+ if ($2 < MIN_PRIORITY || $2 > MAX_PRIORITY) {
+ yyerror("router-priority out of range (%d-%d)",
+ MIN_PRIORITY, MAX_PRIORITY);
+ YYERROR;
+ }
+ iface->priority = $2;
+ }
+ | ROUTERDEADTIME number nl {
+ if ($2 < MIN_RTR_DEAD_TIME || $2 > MAX_RTR_DEAD_TIME) {
+ yyerror("router-dead-time out of range (%d-%d)",
+ MIN_RTR_DEAD_TIME, MAX_RTR_DEAD_TIME);
+ YYERROR;
+ }
+ iface->dead_interval = $2;
+ }
+ | TRANSMITDELAY number nl {
+ if ($2 < MIN_TRANSIT_DELAY || $2 > MAX_TRANSIT_DELAY) {
+ yyerror("transfer-delay out of range (%d-%d)",
+ MIN_TRANSIT_DELAY, MAX_TRANSIT_DELAY);
+ YYERROR;
+ }
+ iface->transfer_delay = $2;
+ }
+ | HELLOINTERVAL number nl {
+ if ($2 < MIN_HELLO_INTERVAL ||
+ $2 > MAX_HELLO_INTERVAL) {
+ yyerror("hello-interval out of range (%d-%d)",
+ MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
+ YYERROR;
+ }
+ iface->hello_interval = $2;
+ }
+ | RETRANSMITINTERVAL number nl {
+ if ($2 < MIN_RXMT_INTERVAL || $2 > MAX_RXMT_INTERVAL) {
+ yyerror("retransmit-interval out of range "
+ "(%d-%d)", MIN_RXMT_INTERVAL,
+ MAX_RXMT_INTERVAL);
+ YYERROR;
+ }
+ iface->rxmt_interval = $2;
+ }
+ ;
+
+%%
+
+struct keywords {
+ const char *k_name;
+ int k_val;
+};
+
+int
+yyerror(const char *fmt, ...)
+{
+ va_list ap;
+
+ errors = 1;
+ va_start(ap, fmt);
+ fprintf(stderr, "%s:%d: ", infile, yylval.lineno);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ return (0);
+}
+
+int
+kw_cmp(const void *k, const void *e)
+{
+
+ return (strcmp(k, ((const struct keywords *)e)->k_name));
+}
+
+int
+lookup(char *s)
+{
+ /* this has to be sorted always */
+ static const struct keywords keywords[] = {
+ {"area", AREA},
+ {"auth-key", AUTHKEY},
+ {"auth-type", AUTHTYPE},
+ {"fibupdate", FIBUPDATE},
+ {"hello-interval", HELLOINTERVAL},
+ {"interface", INTERFACE},
+ {"metric", METRIC},
+ {"passive", PASSIVE},
+ {"retransmit-interval", RETRANSMITINTERVAL},
+ {"router-dead-time", ROUTERDEADTIME},
+ {"router-id", ROUTERID},
+ {"router-priority", ROUTERPRIORITY},
+ {"transmit-delay", TRANSMITDELAY},
+ };
+ const struct keywords *p;
+
+ p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+
+ if (p) {
+ if (pdebug > 1)
+ fprintf(stderr, "%s: %d\n", s, p->k_val);
+ return (p->k_val);
+ } else {
+ if (pdebug > 1)
+ fprintf(stderr, "string: %s\n", s);
+ return (STRING);
+ }
+}
+
+#define MAXPUSHBACK 128
+
+char *parsebuf;
+int parseindex;
+char pushback_buffer[MAXPUSHBACK];
+int pushback_index = 0;
+
+int
+lgetc(FILE *f)
+{
+ int c, next;
+
+ if (parsebuf) {
+ /* Read character from the parsebuffer instead of input. */
+ if (parseindex >= 0) {
+ c = parsebuf[parseindex++];
+ if (c != '\0')
+ return (c);
+ parsebuf = NULL;
+ } else
+ parseindex++;
+ }
+
+ if (pushback_index)
+ return (pushback_buffer[--pushback_index]);
+
+ while ((c = getc(f)) == '\\') {
+ next = getc(f);
+ if (next != '\n') {
+ if (isspace(next))
+ yyerror("whitespace after \\");
+ ungetc(next, f);
+ break;
+ }
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == '\t' || c == ' ') {
+ /* Compress blanks to a single space. */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+ ungetc(c, f);
+ c = ' ';
+ }
+
+ return (c);
+}
+
+int
+lungetc(int c)
+{
+ if (c == EOF)
+ return (EOF);
+ if (parsebuf) {
+ parseindex--;
+ if (parseindex >= 0)
+ return (c);
+ }
+ if (pushback_index < MAXPUSHBACK-1)
+ return (pushback_buffer[pushback_index++] = c);
+ else
+ return (EOF);
+}
+
+int
+findeol(void)
+{
+ int c;
+
+ parsebuf = NULL;
+ pushback_index = 0;
+
+ /* skip to either EOF or the first real EOL */
+ while (1) {
+ c = lgetc(fin);
+ if (c == '\n') {
+ lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ }
+ return (ERROR);
+}
+
+int
+yylex(void)
+{
+ char buf[8096];
+ char *p, *val;
+ int endc, c;
+ int token;
+
+top:
+ p = buf;
+ while ((c = lgetc(fin)) == ' ')
+ ; /* nothing */
+
+ yylval.lineno = lineno;
+ if (c == '#')
+ while ((c = lgetc(fin)) != '\n' && c != EOF)
+ ; /* nothing */
+ if (c == '$' && parsebuf == NULL) {
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ if (isalnum(c) || c == '_') {
+ *p++ = (char)c;
+ continue;
+ }
+ *p = '\0';
+ lungetc(c);
+ break;
+ }
+ val = symget(buf);
+ if (val == NULL) {
+ yyerror("macro '%s' not defined", buf);
+ return (findeol());
+ }
+ parsebuf = val;
+ parseindex = 0;
+ goto top;
+ }
+
+ switch (c) {
+ case '\'':
+ case '"':
+ endc = c;
+ while (1) {
+ if ((c = lgetc(fin)) == EOF)
+ return (0);
+ if (c == endc) {
+ *p = '\0';
+ break;
+ }
+ if (c == '\n') {
+ lineno++;
+ continue;
+ }
+ if (p + 1 >= buf + sizeof(buf) - 1) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ *p++ = (char)c;
+ }
+ yylval.v.string = strdup(buf);
+ if (yylval.v.string == NULL)
+ errx(1, "yylex: strdup");
+ return (STRING);
+ }
+
+#define allowed_in_string(x) \
+ (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
+ x != '{' && x != '}' && \
+ x != '!' && x != '=' && x != '#' && \
+ x != ','))
+
+ if (isalnum(c) || c == ':' || c == '_') {
+ do {
+ *p++ = c;
+ if ((unsigned)(p-buf) >= sizeof(buf)) {
+ yyerror("string too long");
+ return (findeol());
+ }
+ } while ((c = lgetc(fin)) != EOF && (allowed_in_string(c)));
+ lungetc(c);
+ *p = '\0';
+ if ((token = lookup(buf)) == STRING)
+ if ((yylval.v.string = strdup(buf)) == NULL)
+ err(1, "yylex: strdup");
+ return (token);
+ }
+ if (c == '\n') {
+ yylval.lineno = lineno;
+ lineno++;
+ }
+ if (c == EOF)
+ return (0);
+ return (c);
+}
+
+struct ospfd_conf *
+parse_config(char *filename, int opts)
+{
+ struct sym *sym, *next;
+
+ if ((conf = calloc(1, sizeof(struct ospfd_conf))) == NULL) {
+ errx(1, "parse_config calloc");
+ return (NULL);
+ }
+
+ defaults.dead_interval = DEFAULT_RTR_DEAD_TIME;
+ defaults.transfer_delay = DEFAULT_TRANSIT_DELAY;
+ defaults.hello_interval = DEFAULT_HELLO_INTERVAL;
+ defaults.rxmt_interval = DEFAULT_RXMT_INTERVAL;
+ defaults.metric = DEFAULT_METRIC;
+ defaults.priority = DEFAULT_PRIORITY;
+
+ conf->options = OSPF_OPTION_E;
+
+ if ((fin = fopen(filename, "r")) == NULL) {
+ warn("%s", filename);
+ free(conf);
+ return (NULL);
+ }
+ infile = filename;
+
+ conf->opts = opts;
+ LIST_INIT(&conf->area_list);
+
+ if (check_file_secrecy(fileno(fin), filename)) {
+ fclose(fin);
+ free(conf);
+ return (NULL);
+ }
+
+ yyparse();
+
+ fclose(fin);
+
+ /* Free macros and check which have not been used. */
+ for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
+ next = TAILQ_NEXT(sym, entries);
+ if ((conf->opts & OSPFD_OPT_VERBOSE2) && !sym->used)
+ fprintf(stderr, "warning: macro '%s' not "
+ "used\n", sym->nam);
+ if (!sym->persist) {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+
+ if (errors) {
+ clear_config(conf);
+ return (NULL);
+ }
+
+ return (conf);
+}
+
+int
+symset(const char *nam, const char *val, int persist)
+{
+ struct sym *sym;
+
+ for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
+ sym = TAILQ_NEXT(sym, entries))
+ ; /* nothing */
+
+ if (sym != NULL) {
+ if (sym->persist == 1)
+ return (0);
+ else {
+ free(sym->nam);
+ free(sym->val);
+ TAILQ_REMOVE(&symhead, sym, entries);
+ free(sym);
+ }
+ }
+ if ((sym = calloc(1, sizeof(*sym))) == NULL)
+ return (-1);
+
+ sym->nam = strdup(nam);
+ if (sym->nam == NULL) {
+ free(sym);
+ return (-1);
+ }
+ sym->val = strdup(val);
+ if (sym->val == NULL) {
+ free(sym->nam);
+ free(sym);
+ return (-1);
+ }
+ sym->used = 0;
+ sym->persist = persist;
+ TAILQ_INSERT_TAIL(&symhead, sym, entries);
+ return (0);
+}
+
+int
+cmdline_symset(char *s)
+{
+ char *sym, *val;
+ int ret;
+ size_t len;
+
+ if ((val = strrchr(s, '=')) == NULL)
+ return (-1);
+
+ len = strlen(s) - strlen(val) + 1;
+ if ((sym = malloc(len)) == NULL)
+ errx(1, "cmdline_symset: malloc");
+
+ strlcpy(sym, s, len);
+
+ ret = symset(sym, val + 1, 1);
+ free(sym);
+
+ return (ret);
+}
+
+char *
+symget(const char *nam)
+{
+ struct sym *sym;
+
+ TAILQ_FOREACH(sym, &symhead, entries)
+ if (strcmp(nam, sym->nam) == 0) {
+ sym->used = 1;
+ return (sym->val);
+ }
+ return (NULL);
+}
+
+int
+atoul(char *s, u_long *ulvalp)
+{
+ u_long ulval;
+ char *ep;
+
+ errno = 0;
+ ulval = strtoul(s, &ep, 0);
+ if (s[0] == '\0' || *ep != '\0')
+ return (-1);
+ if (errno == ERANGE && ulval == ULONG_MAX)
+ return (-1);
+ *ulvalp = ulval;
+ return (0);
+}
+
+struct area *
+conf_get_area(struct in_addr id)
+{
+ struct area *a;
+
+ a = area_find(conf, id);
+ if (a)
+ return (a);
+ a = area_new();
+ LIST_INSERT_HEAD(&conf->area_list, a, entry);
+
+ a->dead_interval = defaults.dead_interval;
+ a->transfer_delay = defaults.transfer_delay;
+ a->hello_interval = defaults.hello_interval;
+ a->rxmt_interval = defaults.rxmt_interval;
+ a->metric = defaults.metric;
+ a->priority = defaults.priority;
+
+ a->id.s_addr = id.s_addr;
+
+ return (a);
+}
+
+struct iface *
+conf_get_if(char *name, unsigned int idx)
+{
+ struct area *a;
+ struct iface *i;
+
+ LIST_FOREACH(a, &conf->area_list, entry)
+ LIST_FOREACH(i, &a->iface_list, entry)
+ if (i->ifindex == idx) {
+ yyerror("interface %s already configured",
+ name);
+ return (NULL);
+ }
+
+ i = if_new(name, idx);
+ i->dead_interval = area->dead_interval;
+ i->transfer_delay = area->transfer_delay;
+ i->hello_interval = area->hello_interval;
+ i->rxmt_interval = area->rxmt_interval;
+ i->metric = area->metric;
+ i->priority = area->priority;
+
+ return (i);
+}
+
+void
+clear_config(struct ospfd_conf *xconf)
+{
+ /* XXX clear conf */
+ /* ... */
+}