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