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