xref: /openbsd-src/usr.sbin/ldpd/parse.y (revision f885c9d92adf97ebff462545fd95c5f3a239cc1d)
1*f885c9d9Sflorian /*	$OpenBSD: parse.y,v 1.74 2024/08/22 08:17:54 florian Exp $ */
2ab0c2486Smichele 
3ab0c2486Smichele /*
45dc9330aSrenato  * Copyright (c) 2013, 2015, 2016 Renato Westphal <renato@openbsd.org>
5ab0c2486Smichele  * Copyright (c) 2004, 2005, 2008 Esben Norby <norby@openbsd.org>
6ab0c2486Smichele  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7ab0c2486Smichele  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8ab0c2486Smichele  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9ab0c2486Smichele  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10ab0c2486Smichele  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11ab0c2486Smichele  *
12ab0c2486Smichele  * Permission to use, copy, modify, and distribute this software for any
13ab0c2486Smichele  * purpose with or without fee is hereby granted, provided that the above
14ab0c2486Smichele  * copyright notice and this permission notice appear in all copies.
15ab0c2486Smichele  *
16ab0c2486Smichele  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17ab0c2486Smichele  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18ab0c2486Smichele  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19ab0c2486Smichele  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20ab0c2486Smichele  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21ab0c2486Smichele  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22ab0c2486Smichele  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23ab0c2486Smichele  */
24ab0c2486Smichele 
25ab0c2486Smichele %{
26ab0c2486Smichele #include <sys/stat.h>
277ee91690Sdlg #include <sys/types.h>
287ee91690Sdlg #include <sys/socket.h>
29ab0c2486Smichele #include <arpa/inet.h>
30ab0c2486Smichele #include <ctype.h>
31ab0c2486Smichele #include <err.h>
32ab0c2486Smichele #include <unistd.h>
33ab0c2486Smichele #include <ifaddrs.h>
34af2666a1Sclaudio #include <net/if_types.h>
35ab0c2486Smichele #include <limits.h>
36ab0c2486Smichele #include <stdio.h>
374a1d3eaaSsthen #include <syslog.h>
387ee91690Sdlg #include <errno.h>
397ee91690Sdlg #include <netdb.h>
40ab0c2486Smichele 
41ab0c2486Smichele #include "ldpd.h"
42ab0c2486Smichele #include "ldpe.h"
435411bbb6Srenato #include "lde.h"
44ab0c2486Smichele #include "log.h"
45ab0c2486Smichele 
46c28a25a1Srenato struct file {
47ab0c2486Smichele 	TAILQ_ENTRY(file)	 entry;
48ab0c2486Smichele 	FILE			*stream;
49ab0c2486Smichele 	char			*name;
506dbe86f5Sdenis 	size_t			 ungetpos;
516dbe86f5Sdenis 	size_t			 ungetsize;
526dbe86f5Sdenis 	u_char			*ungetbuf;
536dbe86f5Sdenis 	int			 eof_reached;
54ab0c2486Smichele 	int			 lineno;
55ab0c2486Smichele 	int			 errors;
56c28a25a1Srenato };
57c28a25a1Srenato TAILQ_HEAD(files, file);
58ab0c2486Smichele 
59ab0c2486Smichele struct sym {
60ab0c2486Smichele 	TAILQ_ENTRY(sym)	 entry;
61ab0c2486Smichele 	int			 used;
62ab0c2486Smichele 	int			 persist;
63ab0c2486Smichele 	char			*nam;
64ab0c2486Smichele 	char			*val;
65ab0c2486Smichele };
66c28a25a1Srenato TAILQ_HEAD(symhead, sym);
67ab0c2486Smichele 
68ab0c2486Smichele struct config_defaults {
69a8c39dc0Srenato 	uint16_t	keepalive;
703de94509Srenato 	uint16_t	lhello_holdtime;
713de94509Srenato 	uint16_t	lhello_interval;
723de94509Srenato 	uint16_t	thello_holdtime;
733de94509Srenato 	uint16_t	thello_interval;
74a8c39dc0Srenato 	union ldpd_addr	trans_addr;
75a8c39dc0Srenato 	int		afflags;
763de94509Srenato 	uint8_t		pwflags;
77ab0c2486Smichele };
78ab0c2486Smichele 
79ab0c2486Smichele typedef struct {
80ab0c2486Smichele 	union {
81ab0c2486Smichele 		int64_t		 number;
82ab0c2486Smichele 		char		*string;
8309ad9801Sdlg 		struct in_addr	 routerid;
847ee91690Sdlg 		struct ldp_auth	*auth;
85ab0c2486Smichele 	} v;
86ab0c2486Smichele 	int lineno;
87ab0c2486Smichele } YYSTYPE;
88ab0c2486Smichele 
89c28a25a1Srenato static int		 yyerror(const char *, ...)
90c28a25a1Srenato     __attribute__((__format__ (printf, 1, 2)))
91c28a25a1Srenato     __attribute__((__nonnull__ (1)));
92c28a25a1Srenato static int		 kw_cmp(const void *, const void *);
93c28a25a1Srenato static int		 lookup(char *);
946dbe86f5Sdenis static int		 igetc(void);
95c28a25a1Srenato static int		 lgetc(int);
966dbe86f5Sdenis void			 lungetc(int);
97c28a25a1Srenato static int		 findeol(void);
98c28a25a1Srenato static int		 yylex(void);
99c28a25a1Srenato static int		 check_file_secrecy(int, const char *);
100c28a25a1Srenato static struct file	*pushfile(const char *, int);
101c28a25a1Srenato static int		 popfile(void);
102c28a25a1Srenato static int		 yyparse(void);
103c28a25a1Srenato static int		 symset(const char *, const char *, int);
104c28a25a1Srenato static char		*symget(const char *);
105c28a25a1Srenato static struct iface	*conf_get_if(struct kif *);
106c28a25a1Srenato static struct tnbr	*conf_get_tnbr(union ldpd_addr *);
107c28a25a1Srenato static struct nbr_params *conf_get_nbrp(struct in_addr);
108c28a25a1Srenato static struct l2vpn	*conf_get_l2vpn(char *);
109c28a25a1Srenato static struct l2vpn_if	*conf_get_l2vpn_if(struct l2vpn *, struct kif *);
110c28a25a1Srenato static struct l2vpn_pw	*conf_get_l2vpn_pw(struct l2vpn *, struct kif *);
1118622bd53Srenato int			 conf_check_rdomain(unsigned int);
112c28a25a1Srenato static void		 clear_config(struct ldpd_conf *xconf);
113c28a25a1Srenato static uint32_t		 get_rtr_id(void);
114c28a25a1Srenato static int		 get_address(const char *, union ldpd_addr *);
115c28a25a1Srenato static int		 get_af_address(const char *, int *, union ldpd_addr *);
1167ee91690Sdlg static int		 str2key(char *, const char *, int);
117c28a25a1Srenato 
118c28a25a1Srenato static struct file		*file, *topfile;
119c28a25a1Srenato static struct files		 files = TAILQ_HEAD_INITIALIZER(files);
120c28a25a1Srenato static struct symhead		 symhead = TAILQ_HEAD_INITIALIZER(symhead);
121c28a25a1Srenato static struct ldpd_conf		*conf;
122c28a25a1Srenato static int			 errors;
123c28a25a1Srenato 
124c28a25a1Srenato static int			 af;
125c28a25a1Srenato static struct ldpd_af_conf	*af_conf;
126c28a25a1Srenato static struct iface		*iface;
127c28a25a1Srenato static struct iface_af		*ia;
128c28a25a1Srenato static struct tnbr		*tnbr;
129c28a25a1Srenato static struct nbr_params	*nbrp;
130c28a25a1Srenato static struct l2vpn		*l2vpn;
131c28a25a1Srenato static struct l2vpn_pw		*pw;
132c28a25a1Srenato 
133c28a25a1Srenato static struct config_defaults	 globaldefs;
134c28a25a1Srenato static struct config_defaults	 afdefs;
135c28a25a1Srenato static struct config_defaults	 ifacedefs;
136c28a25a1Srenato static struct config_defaults	 tnbrdefs;
137c28a25a1Srenato static struct config_defaults	 pwdefs;
138c28a25a1Srenato static struct config_defaults	*defs;
139c28a25a1Srenato 
140ab0c2486Smichele %}
141ab0c2486Smichele 
1428622bd53Srenato %token	INTERFACE TNEIGHBOR ROUTERID FIBUPDATE RDOMAIN EXPNULL
14383dcf737Sclaudio %token	LHELLOHOLDTIME LHELLOINTERVAL
14483dcf737Sclaudio %token	THELLOHOLDTIME THELLOINTERVAL
1457ee91690Sdlg %token	THELLOACCEPT AF IPV4 IPV6 INET INET6 GTSMENABLE GTSMHOPS
146a8c39dc0Srenato %token	KEEPALIVE TRANSADDRESS TRANSPREFERENCE DSCISCOINTEROP
1477ee91690Sdlg %token	NEIGHBOR
1487ee91690Sdlg %token	TCP MD5SIG PASSWORD KEY
1496399cec1Srenato %token	L2VPN TYPE VPLS PWTYPE MTU BRIDGE
1506399cec1Srenato %token	ETHERNET ETHERNETTAGGED STATUSTLV CONTROLWORD
151a8c39dc0Srenato %token	PSEUDOWIRE NEIGHBORID NEIGHBORADDR PWID
152fda3abacSclaudio %token	EXTTAG
153ab0c2486Smichele %token	YES NO
15466a1cd4fSrenato %token	INCLUDE
155ab0c2486Smichele %token	ERROR
156ab0c2486Smichele %token	<v.string>	STRING
157ab0c2486Smichele %token	<v.number>	NUMBER
158a8c39dc0Srenato %type	<v.number>	yesno ldp_af l2vpn_type pw_type
159ab0c2486Smichele %type	<v.string>	string
16009ad9801Sdlg %type	<v.routerid>	routerid
1617ee91690Sdlg %type	<v.auth>	auth tcpmd5 optnbrprefix
162ab0c2486Smichele 
163ab0c2486Smichele %%
164ab0c2486Smichele 
165ab0c2486Smichele grammar		: /* empty */
16666a1cd4fSrenato 		| grammar include '\n'
167ab0c2486Smichele 		| grammar '\n'
168ab0c2486Smichele 		| grammar conf_main '\n'
169ab0c2486Smichele 		| grammar varset '\n'
170a8c39dc0Srenato 		| grammar af '\n'
171627846caSrenato 		| grammar neighbor '\n'
1726399cec1Srenato 		| grammar l2vpn '\n'
173ab0c2486Smichele 		| grammar error '\n'		{ file->errors++; }
174ab0c2486Smichele 		;
175ab0c2486Smichele 
17666a1cd4fSrenato include		: INCLUDE STRING		{
17766a1cd4fSrenato 			struct file	*nfile;
17866a1cd4fSrenato 
1796dbe86f5Sdenis 			if ((nfile = pushfile($2,
1806dbe86f5Sdenis 			    !(global.cmd_opts & LDPD_OPT_NOACTION))) == NULL) {
18166a1cd4fSrenato 				yyerror("failed to include file %s", $2);
18266a1cd4fSrenato 				free($2);
18366a1cd4fSrenato 				YYERROR;
18466a1cd4fSrenato 			}
18566a1cd4fSrenato 			free($2);
18666a1cd4fSrenato 
18766a1cd4fSrenato 			file = nfile;
18866a1cd4fSrenato 			lungetc('\n');
18966a1cd4fSrenato 		}
19066a1cd4fSrenato 		;
19166a1cd4fSrenato 
192ab0c2486Smichele string		: string STRING	{
193ab0c2486Smichele 			if (asprintf(&$$, "%s %s", $1, $2) == -1) {
194ab0c2486Smichele 				free($1);
195ab0c2486Smichele 				free($2);
196ab0c2486Smichele 				yyerror("string: asprintf");
197ab0c2486Smichele 				YYERROR;
198ab0c2486Smichele 			}
199ab0c2486Smichele 			free($1);
200ab0c2486Smichele 			free($2);
201ab0c2486Smichele 		}
202ab0c2486Smichele 		| STRING
203ab0c2486Smichele 		;
204ab0c2486Smichele 
20509ad9801Sdlg routerid	: STRING {
206*f885c9d9Sflorian 			if (inet_pton(AF_INET, $1, &$$) != 1) {
20709ad9801Sdlg 				yyerror("%s: error parsing router id", $1);
20809ad9801Sdlg 				free($1);
20909ad9801Sdlg 				YYERROR;
21009ad9801Sdlg 			}
21109ad9801Sdlg 			if (bad_addr_v4($$)) {
21209ad9801Sdlg 				yyerror("%s: invalid router id", $1);
21309ad9801Sdlg 				free($1);
21409ad9801Sdlg 				YYERROR;
21509ad9801Sdlg 			}
21609ad9801Sdlg 			free($1);
21709ad9801Sdlg 
21809ad9801Sdlg 			break;
21909ad9801Sdlg 		}
22009ad9801Sdlg 		;
22109ad9801Sdlg 
222ab0c2486Smichele yesno		: YES	{ $$ = 1; }
223ab0c2486Smichele 		| NO	{ $$ = 0; }
224ab0c2486Smichele 		;
225ab0c2486Smichele 
226a8c39dc0Srenato ldp_af		: IPV4	{ $$ = AF_INET; }
227a8c39dc0Srenato 		| IPV6	{ $$ = AF_INET6; }
228a8c39dc0Srenato 		;
229a8c39dc0Srenato 
2306399cec1Srenato l2vpn_type	: VPLS	{ $$ = L2VPN_TYPE_VPLS; }
2316399cec1Srenato 		;
2326399cec1Srenato 
2336399cec1Srenato pw_type		: ETHERNET		{ $$ = PW_TYPE_ETHERNET; }
2346399cec1Srenato 		| ETHERNETTAGGED	{ $$ = PW_TYPE_ETHERNET_TAGGED; }
2356399cec1Srenato 		;
2366399cec1Srenato 
237ab0c2486Smichele varset		: STRING '=' string {
2380c7b4ca6Sbenno 			char *s = $1;
2393de94509Srenato 			if (global.cmd_opts & LDPD_OPT_VERBOSE)
240ab0c2486Smichele 				printf("%s = \"%s\"\n", $1, $3);
2410c7b4ca6Sbenno 			while (*s++) {
2420c7b4ca6Sbenno 				if (isspace((unsigned char)*s)) {
2430c7b4ca6Sbenno 					yyerror("macro name cannot contain "
2440c7b4ca6Sbenno 					    "whitespace");
24516a0a906Skrw 					free($1);
24616a0a906Skrw 					free($3);
2470c7b4ca6Sbenno 					YYERROR;
2480c7b4ca6Sbenno 				}
2490c7b4ca6Sbenno 			}
250ab0c2486Smichele 			if (symset($1, $3, 0) == -1)
251ab0c2486Smichele 				fatal("cannot store variable");
252ab0c2486Smichele 			free($1);
253ab0c2486Smichele 			free($3);
254ab0c2486Smichele 		}
255ab0c2486Smichele 		;
256ab0c2486Smichele 
25709ad9801Sdlg conf_main	: ROUTERID routerid {
25809ad9801Sdlg 			conf->rtr_id = $2;
259ab0c2486Smichele 		}
260970465c0Sclaudio 		| FIBUPDATE yesno {
261ab0c2486Smichele 			if ($2 == 0)
2623de94509Srenato 				conf->flags |= F_LDPD_NO_FIB_UPDATE;
263ab0c2486Smichele 			else
2643de94509Srenato 				conf->flags &= ~F_LDPD_NO_FIB_UPDATE;
265ab0c2486Smichele 		}
2668622bd53Srenato 		| RDOMAIN NUMBER {
2678622bd53Srenato 			if ($2 < 0 || $2 > RT_TABLEID_MAX) {
2688622bd53Srenato 				yyerror("invalid rdomain");
2698622bd53Srenato 				YYERROR;
2708622bd53Srenato 			}
2718622bd53Srenato 			conf->rdomain = $2;
2728622bd53Srenato 		}
273a8c39dc0Srenato 		| TRANSPREFERENCE ldp_af {
274a8c39dc0Srenato 			conf->trans_pref = $2;
275a8c39dc0Srenato 
276a8c39dc0Srenato 			switch (conf->trans_pref) {
277a8c39dc0Srenato 			case AF_INET:
278a8c39dc0Srenato 				conf->trans_pref = DUAL_STACK_LDPOV4;
279a8c39dc0Srenato 				break;
280a8c39dc0Srenato 			case AF_INET6:
281a8c39dc0Srenato 				conf->trans_pref = DUAL_STACK_LDPOV6;
282a8c39dc0Srenato 				break;
283a8c39dc0Srenato 			default:
284a8c39dc0Srenato 				yyerror("invalid address-family");
285a8c39dc0Srenato 				YYERROR;
286a8c39dc0Srenato 			}
287a8c39dc0Srenato 		}
288a8c39dc0Srenato 		| DSCISCOINTEROP yesno {
289a8c39dc0Srenato 			if ($2 == 1)
290a8c39dc0Srenato 				conf->flags |= F_LDPD_DS_CISCO_INTEROP;
29183dcf737Sclaudio 			else
292a8c39dc0Srenato 				conf->flags &= ~F_LDPD_DS_CISCO_INTEROP;
293a8c39dc0Srenato 		}
2947ee91690Sdlg 		| auth {
2957ee91690Sdlg 			LIST_INSERT_HEAD(&conf->auth_list, $1, entry);
2967ee91690Sdlg 		}
297a8c39dc0Srenato 		| af_defaults
298a8c39dc0Srenato 		| iface_defaults
299a8c39dc0Srenato 		| tnbr_defaults
300a8c39dc0Srenato 		;
301a8c39dc0Srenato 
302a8c39dc0Srenato af		: AF ldp_af {
303a8c39dc0Srenato 			af = $2;
304a8c39dc0Srenato 			switch (af) {
305a8c39dc0Srenato 			case AF_INET:
306a8c39dc0Srenato 				af_conf = &conf->ipv4;
307a8c39dc0Srenato 				break;
308a8c39dc0Srenato 			case AF_INET6:
309a8c39dc0Srenato 				af_conf = &conf->ipv6;
310a8c39dc0Srenato 				break;
311a8c39dc0Srenato 			default:
312a8c39dc0Srenato 				yyerror("invalid address-family");
313a8c39dc0Srenato 				YYERROR;
314a8c39dc0Srenato 			}
315a8c39dc0Srenato 
316a8c39dc0Srenato 			afdefs = *defs;
317a8c39dc0Srenato 			defs = &afdefs;
318a8c39dc0Srenato 		} af_block {
319a8c39dc0Srenato 			af_conf->keepalive = defs->keepalive;
320a8c39dc0Srenato 			af_conf->thello_holdtime = defs->thello_holdtime;
321a8c39dc0Srenato 			af_conf->thello_interval = defs->thello_interval;
322a8c39dc0Srenato 			af_conf->flags = defs->afflags;
323a8c39dc0Srenato 			af_conf->flags |= F_LDPD_AF_ENABLED;
324a8c39dc0Srenato 			af_conf = NULL;
325a8c39dc0Srenato 			af = AF_UNSPEC;
326a8c39dc0Srenato 			defs = &globaldefs;
327a8c39dc0Srenato 		}
328a8c39dc0Srenato 		;
329a8c39dc0Srenato 
330a8c39dc0Srenato af_block	: '{' optnl afopts_l '}'
331a8c39dc0Srenato 		| '{' optnl '}'
332a8c39dc0Srenato 		|
333a8c39dc0Srenato 		;
334a8c39dc0Srenato 
335a8c39dc0Srenato afopts_l	: afopts_l afoptsl nl
336a8c39dc0Srenato 		| afoptsl optnl
337a8c39dc0Srenato 		;
338a8c39dc0Srenato 
339a8c39dc0Srenato afoptsl		:  TRANSADDRESS STRING {
340a8c39dc0Srenato 			if (get_address($2, &af_conf->trans_addr) == -1) {
341a8c39dc0Srenato 				yyerror("error parsing transport-address");
342a8c39dc0Srenato 				free($2);
343a8c39dc0Srenato 				YYERROR;
344a8c39dc0Srenato 			}
345a8c39dc0Srenato 			free($2);
346a8c39dc0Srenato 			if (bad_addr(af, &af_conf->trans_addr)) {
347a8c39dc0Srenato 				yyerror("invalid transport-address");
348a8c39dc0Srenato 				YYERROR;
349a8c39dc0Srenato 			}
350a8c39dc0Srenato 			if (af == AF_INET6 &&
351a8c39dc0Srenato 			   IN6_IS_SCOPE_EMBED(&af_conf->trans_addr.v6)) {
352a8c39dc0Srenato 				yyerror("ipv6 transport-address can not be "
353a8c39dc0Srenato 				    "link-local");
354a8c39dc0Srenato 				YYERROR;
355a8c39dc0Srenato 			}
356a8c39dc0Srenato 		}
3575ff72af8Srenato 		| GTSMENABLE yesno {
3585ff72af8Srenato 			if ($2 == 0)
3595ff72af8Srenato 				defs->afflags |= F_LDPD_AF_NO_GTSM;
3605ff72af8Srenato 		}
361a8c39dc0Srenato 		| af_defaults
362a8c39dc0Srenato 		| iface_defaults
363a8c39dc0Srenato 		| tnbr_defaults
364a8c39dc0Srenato 		| interface
365a8c39dc0Srenato 		| tneighbor
366a8c39dc0Srenato 		;
367a8c39dc0Srenato 
368a8c39dc0Srenato af_defaults	: THELLOACCEPT yesno {
369a8c39dc0Srenato 			if ($2 == 0)
370a8c39dc0Srenato 				defs->afflags &= ~F_LDPD_AF_THELLO_ACCEPT;
371a8c39dc0Srenato 			else
372a8c39dc0Srenato 				defs->afflags |= F_LDPD_AF_THELLO_ACCEPT;
3733de94509Srenato 		}
3743de94509Srenato 		| EXPNULL yesno {
3753de94509Srenato 			if ($2 == 0)
376a8c39dc0Srenato 				defs->afflags &= ~F_LDPD_AF_EXPNULL;
3773de94509Srenato 			else
378a8c39dc0Srenato 				defs->afflags |= F_LDPD_AF_EXPNULL;
37983dcf737Sclaudio 		}
380a7bbe4e3Sclaudio 		| KEEPALIVE NUMBER {
3813d39db89Srenato 			if ($2 < MIN_KEEPALIVE || $2 > MAX_KEEPALIVE) {
382a7bbe4e3Sclaudio 				yyerror("keepalive out of range (%d-%d)",
383a7bbe4e3Sclaudio 				    MIN_KEEPALIVE, MAX_KEEPALIVE);
384a7bbe4e3Sclaudio 				YYERROR;
385a7bbe4e3Sclaudio 			}
386a8c39dc0Srenato 			defs->keepalive = $2;
387a7bbe4e3Sclaudio 		}
388ab0c2486Smichele 		;
38999170248Srenato 
39083dcf737Sclaudio iface_defaults	: LHELLOHOLDTIME NUMBER {
39119fce358Srenato 			if ($2 < MIN_HOLDTIME || $2 > MAX_HOLDTIME) {
39219fce358Srenato 				yyerror("hello-holdtime out of range (%d-%d)",
393ab0c2486Smichele 				    MIN_HOLDTIME, MAX_HOLDTIME);
394ab0c2486Smichele 				YYERROR;
395ab0c2486Smichele 			}
39683dcf737Sclaudio 			defs->lhello_holdtime = $2;
397ab0c2486Smichele 		}
39883dcf737Sclaudio 		| LHELLOINTERVAL NUMBER {
399ab0c2486Smichele 			if ($2 < MIN_HELLO_INTERVAL ||
400ab0c2486Smichele 			    $2 > MAX_HELLO_INTERVAL) {
401ab0c2486Smichele 				yyerror("hello-interval out of range (%d-%d)",
402ab0c2486Smichele 				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
403ab0c2486Smichele 				YYERROR;
404ab0c2486Smichele 			}
40583dcf737Sclaudio 			defs->lhello_interval = $2;
40683dcf737Sclaudio 		}
40783dcf737Sclaudio 		;
40883dcf737Sclaudio 
40983dcf737Sclaudio tnbr_defaults	: THELLOHOLDTIME NUMBER {
41019fce358Srenato 			if ($2 < MIN_HOLDTIME || $2 > MAX_HOLDTIME) {
41119fce358Srenato 				yyerror("hello-holdtime out of range (%d-%d)",
41283dcf737Sclaudio 				    MIN_HOLDTIME, MAX_HOLDTIME);
41383dcf737Sclaudio 				YYERROR;
41483dcf737Sclaudio 			}
41583dcf737Sclaudio 			defs->thello_holdtime = $2;
41683dcf737Sclaudio 		}
41783dcf737Sclaudio 		| THELLOINTERVAL NUMBER {
41883dcf737Sclaudio 			if ($2 < MIN_HELLO_INTERVAL ||
41983dcf737Sclaudio 			    $2 > MAX_HELLO_INTERVAL) {
42083dcf737Sclaudio 				yyerror("hello-interval out of range (%d-%d)",
42183dcf737Sclaudio 				    MIN_HELLO_INTERVAL, MAX_HELLO_INTERVAL);
42283dcf737Sclaudio 				YYERROR;
42383dcf737Sclaudio 			}
42483dcf737Sclaudio 			defs->thello_interval = $2;
425ab0c2486Smichele 		}
426ab0c2486Smichele 		;
427ab0c2486Smichele 
4287ee91690Sdlg tcpmd5		: TCP MD5SIG PASSWORD STRING {
4297ee91690Sdlg 			size_t len;
4307ee91690Sdlg 
4317ee91690Sdlg 			$$ = calloc(1, sizeof(*$$));
4327ee91690Sdlg 			if ($$ == NULL) {
4337ee91690Sdlg 				free($4);
4347ee91690Sdlg 				yyerror("unable to allocate md5 key");
4357ee91690Sdlg 				YYERROR;
4367ee91690Sdlg 			}
4377ee91690Sdlg 
4387ee91690Sdlg 			len = strlen($4);
4397ee91690Sdlg 			if (len > sizeof($$->md5key)) {
4407ee91690Sdlg 				free($$);
4417ee91690Sdlg 				free($4);
4427ee91690Sdlg 				yyerror("tcp md5sig password too long: "
4437ee91690Sdlg 				    "max %zu", sizeof($$->md5key));
4447ee91690Sdlg 				YYERROR;
4457ee91690Sdlg 			}
4467ee91690Sdlg 
4477ee91690Sdlg 			memcpy($$->md5key, $4, len);
4487ee91690Sdlg 			$$->md5key_len = len;
4497ee91690Sdlg 
4507ee91690Sdlg 			free($4);
4517ee91690Sdlg 		}
4527ee91690Sdlg 		| TCP MD5SIG KEY STRING {
4537ee91690Sdlg 			int len;
4547ee91690Sdlg 
4557ee91690Sdlg 			$$ = calloc(1, sizeof(*$$));
4567ee91690Sdlg 			if ($$ == NULL) {
4577ee91690Sdlg 				free($4);
4587ee91690Sdlg 				yyerror("unable to allocate md5 key");
4597ee91690Sdlg 				YYERROR;
4607ee91690Sdlg 			}
4617ee91690Sdlg 
4627ee91690Sdlg 			len = str2key($$->md5key, $4, sizeof($$->md5key));
4637ee91690Sdlg 			if (len == -1) {
4647ee91690Sdlg 				free($$);
4657ee91690Sdlg 				free($4);
4667ee91690Sdlg 				yyerror("invalid hex string");
4677ee91690Sdlg 				YYERROR;
4687ee91690Sdlg 			}
4697ee91690Sdlg 			if ((size_t)len > sizeof($$->md5key_len)) {
4707ee91690Sdlg 				free($$);
4717ee91690Sdlg 				free($4);
4727ee91690Sdlg 				yyerror("tcp md5sig key too long: %d "
4737ee91690Sdlg 				    "max %zu", len, sizeof($$->md5key));
4747ee91690Sdlg 				YYERROR;
4757ee91690Sdlg 			}
4767ee91690Sdlg 
4777ee91690Sdlg 			$$->md5key_len = len;
4787ee91690Sdlg 
4797ee91690Sdlg 			free($4);
4807ee91690Sdlg 		}
4817ee91690Sdlg 		| NO TCP MD5SIG {
4827ee91690Sdlg 			$$ = calloc(1, sizeof(*$$));
4837ee91690Sdlg 			if ($$ == NULL) {
4847ee91690Sdlg 				yyerror("unable to allocate no md5 key");
4857ee91690Sdlg 				YYERROR;
4867ee91690Sdlg 			}
4877ee91690Sdlg 			$$->md5key_len = 0;
4887ee91690Sdlg 		}
4897ee91690Sdlg 		;
4907ee91690Sdlg 
4917ee91690Sdlg optnbrprefix	: STRING {
4927ee91690Sdlg 			$$ = calloc(1, sizeof(*$$));
4937ee91690Sdlg 			if ($$ == NULL) {
4947ee91690Sdlg 				yyerror("unable to allocate auth");
4957ee91690Sdlg 				free($1);
4967ee91690Sdlg 				YYERROR;
4977ee91690Sdlg 			}
4987ee91690Sdlg 
4997ee91690Sdlg 			$$->idlen = inet_net_pton(AF_INET, $1,
5007ee91690Sdlg 			    &$$->id, sizeof($$->id));
5017ee91690Sdlg 			if ($$->idlen == -1) {
5027ee91690Sdlg 				yyerror("%s: %s", $1, strerror(errno));
5037ee91690Sdlg 				free($1);
5047ee91690Sdlg 				YYERROR;
5057ee91690Sdlg 			}
5067ee91690Sdlg 		}
5077ee91690Sdlg 		| /* empty */ {
5087ee91690Sdlg 			$$ = NULL;
5097ee91690Sdlg 		}
5107ee91690Sdlg 		;
5117ee91690Sdlg 
5127ee91690Sdlg auth		: tcpmd5 optnbrprefix {
5137ee91690Sdlg 			$$ = $1;
5147ee91690Sdlg 			if ($2 != NULL) {
5157ee91690Sdlg 				$$->id = $2->id;
5167ee91690Sdlg 				$$->idlen = $2->idlen;
5177ee91690Sdlg 				free($2);
5187ee91690Sdlg 			} else {
5197ee91690Sdlg 				$$->id.s_addr = 0;
5207ee91690Sdlg 				$$->idlen = 0;
5217ee91690Sdlg 			}
5227ee91690Sdlg 		}
5237ee91690Sdlg 		;
5247ee91690Sdlg 
5253d39db89Srenato nbr_opts	: KEEPALIVE NUMBER {
5263d39db89Srenato 			if ($2 < MIN_KEEPALIVE || $2 > MAX_KEEPALIVE) {
5273d39db89Srenato 				yyerror("keepalive out of range (%d-%d)",
5283d39db89Srenato 				    MIN_KEEPALIVE, MAX_KEEPALIVE);
5293d39db89Srenato 				YYERROR;
5303d39db89Srenato 			}
5313d39db89Srenato 			nbrp->keepalive = $2;
5323d39db89Srenato 			nbrp->flags |= F_NBRP_KEEPALIVE;
5333d39db89Srenato 		}
5347ee91690Sdlg 		| tcpmd5 {
5357ee91690Sdlg 			/* this is syntactic sugar... */
5367ee91690Sdlg 			$1->id = nbrp->lsr_id;
5377ee91690Sdlg 			$1->idlen = 32;
5387ee91690Sdlg 			LIST_INSERT_HEAD(&conf->auth_list, $1, entry);
539627846caSrenato 		}
5405ff72af8Srenato 		| GTSMENABLE yesno {
5415ff72af8Srenato 			nbrp->flags |= F_NBRP_GTSM;
5425ff72af8Srenato 			nbrp->gtsm_enabled = $2;
5435ff72af8Srenato 		}
5445ff72af8Srenato 		| GTSMHOPS NUMBER {
5455ff72af8Srenato 			if ($2 < 1 || $2 > 255) {
5465ff72af8Srenato 				yyerror("invalid number of hops %lld", $2);
5475ff72af8Srenato 				YYERROR;
5485ff72af8Srenato 			}
5495ff72af8Srenato 			nbrp->gtsm_hops = $2;
5505ff72af8Srenato 			nbrp->flags |= F_NBRP_GTSM_HOPS;
5515ff72af8Srenato 		}
552627846caSrenato 		;
553627846caSrenato 
5546399cec1Srenato pw_defaults	: STATUSTLV yesno {
555a2da3f43Srenato 			if ($2 == 1)
5566399cec1Srenato 				defs->pwflags |= F_PW_STATUSTLV_CONF;
557a2da3f43Srenato 			else
5586399cec1Srenato 				defs->pwflags &= ~F_PW_STATUSTLV_CONF;
5596399cec1Srenato 		}
5606399cec1Srenato 		| CONTROLWORD yesno {
561a2da3f43Srenato 			if ($2 == 1)
5622d825b92Srenato 				defs->pwflags |= F_PW_CWORD_CONF;
563a2da3f43Srenato 			else
5642d825b92Srenato 				defs->pwflags &= ~F_PW_CWORD_CONF;
5656399cec1Srenato 		}
5666399cec1Srenato 		;
5676399cec1Srenato 
5686399cec1Srenato pwopts		: PWID NUMBER {
5696399cec1Srenato 			if ($2 < MIN_PWID_ID ||
5706399cec1Srenato 			    $2 > MAX_PWID_ID) {
5716399cec1Srenato 				yyerror("pw-id out of range (%d-%d)",
5726399cec1Srenato 				    MIN_PWID_ID, MAX_PWID_ID);
5736399cec1Srenato 				YYERROR;
5746399cec1Srenato 			}
5756399cec1Srenato 
5766399cec1Srenato 			pw->pwid = $2;
5776399cec1Srenato 		}
57809ad9801Sdlg 		| NEIGHBORID routerid {
57909ad9801Sdlg 			pw->lsr_id = $2;
5806399cec1Srenato 		}
581a8c39dc0Srenato 		| NEIGHBORADDR STRING {
582a8c39dc0Srenato 			int		 family;
583a8c39dc0Srenato 			union ldpd_addr	 addr;
584a8c39dc0Srenato 
585a8c39dc0Srenato 			if (get_af_address($2, &family, &addr) == -1) {
586a8c39dc0Srenato 				yyerror("error parsing neighbor address");
587a8c39dc0Srenato 				free($2);
588a8c39dc0Srenato 				YYERROR;
589a8c39dc0Srenato 			}
590a8c39dc0Srenato 			free($2);
591a8c39dc0Srenato 			if (bad_addr(family, &addr)) {
592a8c39dc0Srenato 				yyerror("invalid neighbor address");
593a8c39dc0Srenato 				YYERROR;
594a8c39dc0Srenato 			}
595a8c39dc0Srenato 			if (family == AF_INET6 &&
596a8c39dc0Srenato 			    IN6_IS_SCOPE_EMBED(&addr.v6)) {
597a8c39dc0Srenato 				yyerror("neighbor address can not be "
598a8c39dc0Srenato 				    "link-local");
599a8c39dc0Srenato 				YYERROR;
600a8c39dc0Srenato 			}
601a8c39dc0Srenato 
602a8c39dc0Srenato 			pw->af = family;
603a8c39dc0Srenato 			pw->addr = addr;
604a8c39dc0Srenato 		}
6056399cec1Srenato 		| pw_defaults
6066399cec1Srenato 		;
6076399cec1Srenato 
6086399cec1Srenato pseudowire	: PSEUDOWIRE STRING {
6096399cec1Srenato 			struct kif	*kif;
6106399cec1Srenato 
6116399cec1Srenato 			if ((kif = kif_findname($2)) == NULL) {
6126399cec1Srenato 				yyerror("unknown interface %s", $2);
6136399cec1Srenato 				free($2);
6146399cec1Srenato 				YYERROR;
6156399cec1Srenato 			}
6166399cec1Srenato 			free($2);
6176399cec1Srenato 
61817bf3978Sdlg 			if (kif->if_type != IFT_MPLSTUNNEL &&
61917bf3978Sdlg 			    kmpw_find(kif->ifname) == -1) {
6206399cec1Srenato 				yyerror("unsupported interface type on "
6216399cec1Srenato 				    "interface %s", kif->ifname);
6226399cec1Srenato 				YYERROR;
6236399cec1Srenato 			}
6246399cec1Srenato 
6256399cec1Srenato 			pw = conf_get_l2vpn_pw(l2vpn, kif);
6266399cec1Srenato 			if (pw == NULL)
6276399cec1Srenato 				YYERROR;
6286399cec1Srenato 
6297aa09c5dSrenato 			pwdefs = *defs;
6306399cec1Srenato 			defs = &pwdefs;
6316399cec1Srenato 		} pw_block {
6326399cec1Srenato 			struct l2vpn	*l;
6336399cec1Srenato 			struct l2vpn_pw *p;
6346399cec1Srenato 
63556dfb329Srenato 			/* check for errors */
63656dfb329Srenato 			if (pw->pwid == 0) {
63756dfb329Srenato 				yyerror("missing pseudowire id");
63856dfb329Srenato 				YYERROR;
63956dfb329Srenato 			}
64072bfe95eSrenato 			if (pw->lsr_id.s_addr == INADDR_ANY) {
641a8c39dc0Srenato 				yyerror("missing pseudowire neighbor-id");
64256dfb329Srenato 				YYERROR;
64356dfb329Srenato 			}
64419fce358Srenato 			LIST_FOREACH(l, &conf->l2vpn_list, entry) {
64519fce358Srenato 				LIST_FOREACH(p, &l->pw_list, entry) {
6466399cec1Srenato 					if (pw != p &&
6476399cec1Srenato 					    pw->pwid == p->pwid &&
648a8c39dc0Srenato 					    pw->af == p->af &&
649a8c39dc0Srenato 					    pw->lsr_id.s_addr ==
650a8c39dc0Srenato 					    p->lsr_id.s_addr) {
6516399cec1Srenato 						yyerror("pseudowire already "
6526399cec1Srenato 						    "configured");
6536399cec1Srenato 						YYERROR;
6546399cec1Srenato 					}
65519fce358Srenato 				}
65619fce358Srenato 			}
6576399cec1Srenato 
658a8c39dc0Srenato 			/*
659a8c39dc0Srenato 			 * If the neighbor address is not specified, use the
660a8c39dc0Srenato 			 * neighbor id.
661a8c39dc0Srenato 			 */
662a8c39dc0Srenato 			if (pw->af == AF_UNSPEC) {
663a8c39dc0Srenato 				pw->af = AF_INET;
664a8c39dc0Srenato 				pw->addr.v4 = pw->lsr_id;
665a8c39dc0Srenato 			}
666a8c39dc0Srenato 
6676399cec1Srenato 			pw->flags = defs->pwflags;
6686399cec1Srenato 			pw = NULL;
66919fce358Srenato 			defs = &globaldefs;
6706399cec1Srenato 		}
6716399cec1Srenato 		;
6726399cec1Srenato 
6736399cec1Srenato pw_block	: '{' optnl pwopts_l '}'
6746399cec1Srenato 		| '{' optnl '}'
6756399cec1Srenato 		| /* nothing */
6766399cec1Srenato 		;
6776399cec1Srenato 
6786399cec1Srenato pwopts_l	: pwopts_l pwopts nl
6796399cec1Srenato 		| pwopts optnl
6806399cec1Srenato 		;
6816399cec1Srenato 
6826399cec1Srenato l2vpnopts	: PWTYPE pw_type {
6836399cec1Srenato 			l2vpn->pw_type = $2;
6846399cec1Srenato 		}
6856399cec1Srenato 		| MTU NUMBER {
6866399cec1Srenato 			if ($2 < MIN_L2VPN_MTU ||
6876399cec1Srenato 			    $2 > MAX_L2VPN_MTU) {
6886399cec1Srenato 				yyerror("l2vpn mtu out of range (%d-%d)",
6896399cec1Srenato 				    MIN_L2VPN_MTU, MAX_L2VPN_MTU);
6906399cec1Srenato 				YYERROR;
6916399cec1Srenato 			}
6926399cec1Srenato 			l2vpn->mtu = $2;
6936399cec1Srenato 		}
6946399cec1Srenato 		| pw_defaults
6956399cec1Srenato 		| BRIDGE STRING {
6966399cec1Srenato 			struct l2vpn	 *l;
6976399cec1Srenato 			struct kif	 *kif;
6986399cec1Srenato 
6996399cec1Srenato 			if ((kif = kif_findname($2)) == NULL) {
7006399cec1Srenato 				yyerror("unknown interface %s", $2);
7016399cec1Srenato 				free($2);
7026399cec1Srenato 				YYERROR;
7036399cec1Srenato 			}
7046399cec1Srenato 			free($2);
7056399cec1Srenato 
7066399cec1Srenato 			if (l2vpn->br_ifindex != 0) {
7076399cec1Srenato 				yyerror("bridge interface cannot be "
7086399cec1Srenato 				    "redefined on l2vpn %s", l2vpn->name);
7096399cec1Srenato 				YYERROR;
7106399cec1Srenato 			}
7116399cec1Srenato 
7120f882be7Sstsp 			if (kif->if_type != IFT_BRIDGE) {
7136399cec1Srenato 				yyerror("unsupported interface type on "
7146399cec1Srenato 				    "interface %s", kif->ifname);
7156399cec1Srenato 				YYERROR;
7166399cec1Srenato 			}
7176399cec1Srenato 
7186399cec1Srenato 			LIST_FOREACH(l, &conf->l2vpn_list, entry) {
7196399cec1Srenato 				if (l->br_ifindex == kif->ifindex) {
7206399cec1Srenato 					yyerror("bridge %s is already being "
7216399cec1Srenato 					    "used by l2vpn %s", kif->ifname,
7226399cec1Srenato 					    l->name);
7236399cec1Srenato 					YYERROR;
7246399cec1Srenato 				}
7256399cec1Srenato 			}
7266399cec1Srenato 
7276399cec1Srenato 			l2vpn->br_ifindex = kif->ifindex;
7286399cec1Srenato 			strlcpy(l2vpn->br_ifname, kif->ifname,
7296399cec1Srenato 			    sizeof(l2vpn->br_ifname));
7306399cec1Srenato 		}
7316399cec1Srenato 		| INTERFACE STRING {
7326399cec1Srenato 			struct kif	*kif;
7336399cec1Srenato 			struct l2vpn_if	*lif;
7346399cec1Srenato 
7356399cec1Srenato 			if ((kif = kif_findname($2)) == NULL) {
7366399cec1Srenato 				yyerror("unknown interface %s", $2);
7376399cec1Srenato 				free($2);
7386399cec1Srenato 				YYERROR;
7396399cec1Srenato 			}
7406399cec1Srenato 			free($2);
7416399cec1Srenato 
7426399cec1Srenato 			lif = conf_get_l2vpn_if(l2vpn, kif);
7436399cec1Srenato 			if (lif == NULL)
7446399cec1Srenato 				YYERROR;
7456399cec1Srenato 		}
7466399cec1Srenato 		| pseudowire
7476399cec1Srenato 		;
7486399cec1Srenato 
749ab0c2486Smichele optnl		: '\n' optnl
750ab0c2486Smichele 		|
751ab0c2486Smichele 		;
752ab0c2486Smichele 
753ab0c2486Smichele nl		: '\n' optnl		/* one newline or more */
754ab0c2486Smichele 		;
755ab0c2486Smichele 
756ab0c2486Smichele interface	: INTERFACE STRING	{
757ab0c2486Smichele 			struct kif	*kif;
758ab0c2486Smichele 
759814e607dSclaudio 			if ((kif = kif_findname($2)) == NULL) {
760ab0c2486Smichele 				yyerror("unknown interface %s", $2);
761ab0c2486Smichele 				free($2);
762ab0c2486Smichele 				YYERROR;
763ab0c2486Smichele 			}
764ab0c2486Smichele 			free($2);
76519fce358Srenato 
766814e607dSclaudio 			iface = conf_get_if(kif);
767ab0c2486Smichele 			if (iface == NULL)
768ab0c2486Smichele 				YYERROR;
769ab0c2486Smichele 
770a8c39dc0Srenato 			ia = iface_af_get(iface, af);
771a8c39dc0Srenato 			if (ia->enabled) {
772a8c39dc0Srenato 				yyerror("interface %s already configured for "
773a8c39dc0Srenato 				    "address-family %s", kif->ifname,
774a8c39dc0Srenato 				    af_name(af));
775a8c39dc0Srenato 				YYERROR;
776a8c39dc0Srenato 			}
777a8c39dc0Srenato 			ia->enabled = 1;
778a8c39dc0Srenato 
7797aa09c5dSrenato 			ifacedefs = *defs;
780ab0c2486Smichele 			defs = &ifacedefs;
781ab0c2486Smichele 		} interface_block {
782a8c39dc0Srenato 			ia->hello_holdtime = defs->lhello_holdtime;
783a8c39dc0Srenato 			ia->hello_interval = defs->lhello_interval;
784ab0c2486Smichele 			iface = NULL;
785a8c39dc0Srenato 			defs = &afdefs;
786ab0c2486Smichele 		}
787ab0c2486Smichele 		;
788ab0c2486Smichele 
789ab0c2486Smichele interface_block	: '{' optnl interfaceopts_l '}'
790ab0c2486Smichele 		| '{' optnl '}'
7918d295d64Sclaudio 		| /* nothing */
792ab0c2486Smichele 		;
793ab0c2486Smichele 
79483dcf737Sclaudio interfaceopts_l	: interfaceopts_l iface_defaults nl
79583dcf737Sclaudio 		| iface_defaults optnl
79683dcf737Sclaudio 		;
79783dcf737Sclaudio 
79883dcf737Sclaudio tneighbor	: TNEIGHBOR STRING	{
799a8c39dc0Srenato 			union ldpd_addr	 addr;
80083dcf737Sclaudio 
801a8c39dc0Srenato 			if (get_address($2, &addr) == -1) {
80219fce358Srenato 				yyerror("error parsing targeted-neighbor "
80319fce358Srenato 				    "address");
80483dcf737Sclaudio 				free($2);
80583dcf737Sclaudio 				YYERROR;
80683dcf737Sclaudio 			}
80783dcf737Sclaudio 			free($2);
808a8c39dc0Srenato 			if (bad_addr(af, &addr)) {
80919fce358Srenato 				yyerror("invalid targeted-neighbor address");
810e2fb924eSrenato 				YYERROR;
811e2fb924eSrenato 			}
812a8c39dc0Srenato 			if (af == AF_INET6 &&
813a8c39dc0Srenato 			   IN6_IS_SCOPE_EMBED(&addr.v6)) {
814a8c39dc0Srenato 				yyerror("targeted-neighbor address can not be "
815a8c39dc0Srenato 				    "link-local");
816a8c39dc0Srenato 				YYERROR;
817a8c39dc0Srenato 			}
81883dcf737Sclaudio 
819a8c39dc0Srenato 			tnbr = conf_get_tnbr(&addr);
82083dcf737Sclaudio 			if (tnbr == NULL)
82183dcf737Sclaudio 				YYERROR;
82299170248Srenato 
8237aa09c5dSrenato 			tnbrdefs = *defs;
82483dcf737Sclaudio 			defs = &tnbrdefs;
82583dcf737Sclaudio 		} tneighbor_block {
82683dcf737Sclaudio 			tnbr->hello_holdtime = defs->thello_holdtime;
82783dcf737Sclaudio 			tnbr->hello_interval = defs->thello_interval;
82883dcf737Sclaudio 			tnbr = NULL;
829a8c39dc0Srenato 			defs = &afdefs;
83083dcf737Sclaudio 		}
83183dcf737Sclaudio 		;
83283dcf737Sclaudio 
83383dcf737Sclaudio tneighbor_block	: '{' optnl tneighboropts_l '}'
83483dcf737Sclaudio 		| '{' optnl '}'
83583dcf737Sclaudio 		| /* nothing */
83683dcf737Sclaudio 		;
83783dcf737Sclaudio 
83883dcf737Sclaudio tneighboropts_l	: tneighboropts_l tnbr_defaults nl
83983dcf737Sclaudio 		| tnbr_defaults optnl
840ab0c2486Smichele 		;
841ab0c2486Smichele 
84209ad9801Sdlg neighbor	: NEIGHBOR routerid	{
84309ad9801Sdlg 			nbrp = conf_get_nbrp($2);
844627846caSrenato 			if (nbrp == NULL)
845627846caSrenato 				YYERROR;
846627846caSrenato 		} neighbor_block {
847627846caSrenato 			nbrp = NULL;
848627846caSrenato 		}
849627846caSrenato 		;
850627846caSrenato 
851627846caSrenato neighbor_block	: '{' optnl neighboropts_l '}'
852627846caSrenato 		| '{' optnl '}'
853627846caSrenato 		| /* nothing */
854627846caSrenato 		;
855627846caSrenato 
856627846caSrenato neighboropts_l	: neighboropts_l nbr_opts nl
857627846caSrenato 		| nbr_opts optnl
858627846caSrenato 		;
859627846caSrenato 
8606399cec1Srenato l2vpn		: L2VPN STRING TYPE l2vpn_type {
8616399cec1Srenato 			l2vpn = conf_get_l2vpn($2);
8626399cec1Srenato 			if (l2vpn == NULL)
8636399cec1Srenato 				YYERROR;
8646399cec1Srenato 			l2vpn->type = $4;
8656399cec1Srenato 		} l2vpn_block {
8666399cec1Srenato 			l2vpn = NULL;
8676399cec1Srenato 		}
8686399cec1Srenato 		;
8696399cec1Srenato 
8706399cec1Srenato l2vpn_block	: '{' optnl l2vpnopts_l '}'
8716399cec1Srenato 		| '{' optnl '}'
8726399cec1Srenato 		| /* nothing */
8736399cec1Srenato 		;
8746399cec1Srenato 
8756399cec1Srenato l2vpnopts_l	: l2vpnopts_l l2vpnopts nl
8766399cec1Srenato 		| l2vpnopts optnl
8776399cec1Srenato 		;
8786399cec1Srenato 
879ab0c2486Smichele %%
880ab0c2486Smichele 
881ab0c2486Smichele struct keywords {
882ab0c2486Smichele 	const char	*k_name;
883ab0c2486Smichele 	int		 k_val;
884ab0c2486Smichele };
885ab0c2486Smichele 
886c28a25a1Srenato static int
887ab0c2486Smichele yyerror(const char *fmt, ...)
888ab0c2486Smichele {
889ab0c2486Smichele 	va_list		 ap;
890e3490c9cSbluhm 	char		*msg;
891ab0c2486Smichele 
892ab0c2486Smichele 	file->errors++;
893ab0c2486Smichele 	va_start(ap, fmt);
894e3490c9cSbluhm 	if (vasprintf(&msg, fmt, ap) == -1)
895e3490c9cSbluhm 		fatalx("yyerror vasprintf");
896ab0c2486Smichele 	va_end(ap);
897e3490c9cSbluhm 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
898e3490c9cSbluhm 	free(msg);
899ab0c2486Smichele 	return (0);
900ab0c2486Smichele }
901ab0c2486Smichele 
902c28a25a1Srenato static int
903ab0c2486Smichele kw_cmp(const void *k, const void *e)
904ab0c2486Smichele {
905ab0c2486Smichele 	return (strcmp(k, ((const struct keywords *)e)->k_name));
906ab0c2486Smichele }
907ab0c2486Smichele 
908c28a25a1Srenato static int
909ab0c2486Smichele lookup(char *s)
910ab0c2486Smichele {
911ab0c2486Smichele 	/* this has to be sorted always */
912ab0c2486Smichele 	static const struct keywords keywords[] = {
913a8c39dc0Srenato 		{"address-family",		AF},
9146399cec1Srenato 		{"bridge",			BRIDGE},
9156399cec1Srenato 		{"control-word",		CONTROLWORD},
916a8c39dc0Srenato 		{"ds-cisco-interop",		DSCISCOINTEROP},
9176399cec1Srenato 		{"ethernet",			ETHERNET},
9186399cec1Srenato 		{"ethernet-tagged",		ETHERNETTAGGED},
9193de94509Srenato 		{"explicit-null",		EXPNULL},
920970465c0Sclaudio 		{"fib-update",			FIBUPDATE},
9215ff72af8Srenato 		{"gtsm-enable",			GTSMENABLE},
9225ff72af8Srenato 		{"gtsm-hops",			GTSMHOPS},
92366a1cd4fSrenato 		{"include",			INCLUDE},
9247ee91690Sdlg 		{"inet",			INET},
9257ee91690Sdlg 		{"inet6",			INET6},
926ab0c2486Smichele 		{"interface",			INTERFACE},
927a8c39dc0Srenato 		{"ipv4",			IPV4},
928a8c39dc0Srenato 		{"ipv6",			IPV6},
929ab0c2486Smichele 		{"keepalive",			KEEPALIVE},
9307ee91690Sdlg 		{"key",				KEY},
9316399cec1Srenato 		{"l2vpn",			L2VPN},
93283dcf737Sclaudio 		{"link-hello-holdtime",		LHELLOHOLDTIME},
93383dcf737Sclaudio 		{"link-hello-interval",		LHELLOINTERVAL},
9347ee91690Sdlg 		{"md5sig",			MD5SIG},
9356399cec1Srenato 		{"mtu",				MTU},
936627846caSrenato 		{"neighbor",			NEIGHBOR},
937a8c39dc0Srenato 		{"neighbor-addr",		NEIGHBORADDR},
938a8c39dc0Srenato 		{"neighbor-id",			NEIGHBORID},
9396a647450Sclaudio 		{"no",				NO},
940627846caSrenato 		{"password",			PASSWORD},
9416399cec1Srenato 		{"pseudowire",			PSEUDOWIRE},
9426399cec1Srenato 		{"pw-id",			PWID},
9436399cec1Srenato 		{"pw-type",			PWTYPE},
9448622bd53Srenato 		{"rdomain",			RDOMAIN},
945ab0c2486Smichele 		{"router-id",			ROUTERID},
9466399cec1Srenato 		{"status-tlv",			STATUSTLV},
94783dcf737Sclaudio 		{"targeted-hello-accept",	THELLOACCEPT},
94883dcf737Sclaudio 		{"targeted-hello-holdtime",	THELLOHOLDTIME},
94983dcf737Sclaudio 		{"targeted-hello-interval",	THELLOINTERVAL},
95083dcf737Sclaudio 		{"targeted-neighbor",		TNEIGHBOR},
9517ee91690Sdlg 		{"tcp",				TCP},
95286408800Srenato 		{"transport-address",		TRANSADDRESS},
953a8c39dc0Srenato 		{"transport-preference",	TRANSPREFERENCE},
9546399cec1Srenato 		{"type",			TYPE},
9556399cec1Srenato 		{"vpls",			VPLS},
956ab0c2486Smichele 		{"yes",				YES}
957ab0c2486Smichele 	};
958ab0c2486Smichele 	const struct keywords	*p;
959ab0c2486Smichele 
960ab0c2486Smichele 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
961ab0c2486Smichele 	    sizeof(keywords[0]), kw_cmp);
962ab0c2486Smichele 
963ab0c2486Smichele 	if (p)
964ab0c2486Smichele 		return (p->k_val);
965ab0c2486Smichele 	else
966ab0c2486Smichele 		return (STRING);
967ab0c2486Smichele }
968ab0c2486Smichele 
9696dbe86f5Sdenis #define START_EXPAND	1
9706dbe86f5Sdenis #define DONE_EXPAND	2
9716dbe86f5Sdenis 
9726dbe86f5Sdenis static int	expanding;
9736dbe86f5Sdenis 
9746dbe86f5Sdenis int
9756dbe86f5Sdenis igetc(void)
9766dbe86f5Sdenis {
9776dbe86f5Sdenis 	int	c;
9786dbe86f5Sdenis 
9796dbe86f5Sdenis 	while (1) {
9806dbe86f5Sdenis 		if (file->ungetpos > 0)
9816dbe86f5Sdenis 			c = file->ungetbuf[--file->ungetpos];
9826dbe86f5Sdenis 		else
9836dbe86f5Sdenis 			c = getc(file->stream);
9846dbe86f5Sdenis 
9856dbe86f5Sdenis 		if (c == START_EXPAND)
9866dbe86f5Sdenis 			expanding = 1;
9876dbe86f5Sdenis 		else if (c == DONE_EXPAND)
9886dbe86f5Sdenis 			expanding = 0;
9896dbe86f5Sdenis 		else
9906dbe86f5Sdenis 			break;
9916dbe86f5Sdenis 	}
9926dbe86f5Sdenis 	return (c);
9936dbe86f5Sdenis }
9946dbe86f5Sdenis 
995c28a25a1Srenato static int
996ab0c2486Smichele lgetc(int quotec)
997ab0c2486Smichele {
998ab0c2486Smichele 	int		c, next;
999ab0c2486Smichele 
1000ab0c2486Smichele 	if (quotec) {
10016dbe86f5Sdenis 		if ((c = igetc()) == EOF) {
1002ab0c2486Smichele 			yyerror("reached end of file while parsing "
1003ab0c2486Smichele 			    "quoted string");
1004ab0c2486Smichele 			if (file == topfile || popfile() == EOF)
1005ab0c2486Smichele 				return (EOF);
1006ab0c2486Smichele 			return (quotec);
1007ab0c2486Smichele 		}
1008ab0c2486Smichele 		return (c);
1009ab0c2486Smichele 	}
1010ab0c2486Smichele 
10116dbe86f5Sdenis 	while ((c = igetc()) == '\\') {
10126dbe86f5Sdenis 		next = igetc();
1013ab0c2486Smichele 		if (next != '\n') {
1014ab0c2486Smichele 			c = next;
1015ab0c2486Smichele 			break;
1016ab0c2486Smichele 		}
1017ab0c2486Smichele 		yylval.lineno = file->lineno;
1018ab0c2486Smichele 		file->lineno++;
1019ab0c2486Smichele 	}
1020ab0c2486Smichele 
10216dbe86f5Sdenis 	if (c == EOF) {
10226dbe86f5Sdenis 		/*
10236dbe86f5Sdenis 		 * Fake EOL when hit EOF for the first time. This gets line
10246dbe86f5Sdenis 		 * count right if last line in included file is syntactically
10256dbe86f5Sdenis 		 * invalid and has no newline.
10266dbe86f5Sdenis 		 */
10276dbe86f5Sdenis 		if (file->eof_reached == 0) {
10286dbe86f5Sdenis 			file->eof_reached = 1;
10296dbe86f5Sdenis 			return ('\n');
10306dbe86f5Sdenis 		}
1031ab0c2486Smichele 		while (c == EOF) {
1032ab0c2486Smichele 			if (file == topfile || popfile() == EOF)
1033ab0c2486Smichele 				return (EOF);
10346dbe86f5Sdenis 			c = igetc();
10356dbe86f5Sdenis 		}
1036ab0c2486Smichele 	}
1037ab0c2486Smichele 	return (c);
1038ab0c2486Smichele }
1039ab0c2486Smichele 
10406dbe86f5Sdenis void
1041ab0c2486Smichele lungetc(int c)
1042ab0c2486Smichele {
1043ab0c2486Smichele 	if (c == EOF)
10446dbe86f5Sdenis 		return;
10456dbe86f5Sdenis 
10466dbe86f5Sdenis 	if (file->ungetpos >= file->ungetsize) {
10476dbe86f5Sdenis 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
10486dbe86f5Sdenis 		if (p == NULL)
1049a062aa9dSkrw 			err(1, "%s", __func__);
10506dbe86f5Sdenis 		file->ungetbuf = p;
10516dbe86f5Sdenis 		file->ungetsize *= 2;
1052ab0c2486Smichele 	}
10536dbe86f5Sdenis 	file->ungetbuf[file->ungetpos++] = c;
1054ab0c2486Smichele }
1055ab0c2486Smichele 
1056c28a25a1Srenato static int
1057ab0c2486Smichele findeol(void)
1058ab0c2486Smichele {
1059ab0c2486Smichele 	int	c;
1060ab0c2486Smichele 
1061ab0c2486Smichele 	/* skip to either EOF or the first real EOL */
1062ab0c2486Smichele 	while (1) {
1063ab0c2486Smichele 		c = lgetc(0);
1064ab0c2486Smichele 		if (c == '\n') {
1065ab0c2486Smichele 			file->lineno++;
1066ab0c2486Smichele 			break;
1067ab0c2486Smichele 		}
1068ab0c2486Smichele 		if (c == EOF)
1069ab0c2486Smichele 			break;
1070ab0c2486Smichele 	}
1071ab0c2486Smichele 	return (ERROR);
1072ab0c2486Smichele }
1073ab0c2486Smichele 
1074c28a25a1Srenato static int
1075ab0c2486Smichele yylex(void)
1076ab0c2486Smichele {
107708f6ba19Snaddy 	char	 buf[8096];
107808f6ba19Snaddy 	char	*p, *val;
1079ab0c2486Smichele 	int	 quotec, next, c;
1080ab0c2486Smichele 	int	 token;
1081ab0c2486Smichele 
1082ab0c2486Smichele  top:
1083ab0c2486Smichele 	p = buf;
1084ab0c2486Smichele 	while ((c = lgetc(0)) == ' ' || c == '\t')
1085ab0c2486Smichele 		; /* nothing */
1086ab0c2486Smichele 
1087ab0c2486Smichele 	yylval.lineno = file->lineno;
1088ab0c2486Smichele 	if (c == '#')
1089ab0c2486Smichele 		while ((c = lgetc(0)) != '\n' && c != EOF)
1090ab0c2486Smichele 			; /* nothing */
10916dbe86f5Sdenis 	if (c == '$' && !expanding) {
1092ab0c2486Smichele 		while (1) {
1093ab0c2486Smichele 			if ((c = lgetc(0)) == EOF)
1094ab0c2486Smichele 				return (0);
1095ab0c2486Smichele 
1096ab0c2486Smichele 			if (p + 1 >= buf + sizeof(buf) - 1) {
1097ab0c2486Smichele 				yyerror("string too long");
1098ab0c2486Smichele 				return (findeol());
1099ab0c2486Smichele 			}
1100ab0c2486Smichele 			if (isalnum(c) || c == '_') {
1101015d7b4dSbenno 				*p++ = c;
1102ab0c2486Smichele 				continue;
1103ab0c2486Smichele 			}
1104ab0c2486Smichele 			*p = '\0';
1105ab0c2486Smichele 			lungetc(c);
1106ab0c2486Smichele 			break;
1107ab0c2486Smichele 		}
1108ab0c2486Smichele 		val = symget(buf);
1109ab0c2486Smichele 		if (val == NULL) {
1110ab0c2486Smichele 			yyerror("macro '%s' not defined", buf);
1111ab0c2486Smichele 			return (findeol());
1112ab0c2486Smichele 		}
11136dbe86f5Sdenis 		p = val + strlen(val) - 1;
11146dbe86f5Sdenis 		lungetc(DONE_EXPAND);
11156dbe86f5Sdenis 		while (p >= val) {
111608f6ba19Snaddy 			lungetc((unsigned char)*p);
11176dbe86f5Sdenis 			p--;
11186dbe86f5Sdenis 		}
11196dbe86f5Sdenis 		lungetc(START_EXPAND);
1120ab0c2486Smichele 		goto top;
1121ab0c2486Smichele 	}
1122ab0c2486Smichele 
1123ab0c2486Smichele 	switch (c) {
1124ab0c2486Smichele 	case '\'':
1125ab0c2486Smichele 	case '"':
1126ab0c2486Smichele 		quotec = c;
1127ab0c2486Smichele 		while (1) {
1128ab0c2486Smichele 			if ((c = lgetc(quotec)) == EOF)
1129ab0c2486Smichele 				return (0);
1130ab0c2486Smichele 			if (c == '\n') {
1131ab0c2486Smichele 				file->lineno++;
1132ab0c2486Smichele 				continue;
1133ab0c2486Smichele 			} else if (c == '\\') {
1134ab0c2486Smichele 				if ((next = lgetc(quotec)) == EOF)
1135ab0c2486Smichele 					return (0);
1136a1533359Ssashan 				if (next == quotec || next == ' ' ||
1137a1533359Ssashan 				    next == '\t')
1138ab0c2486Smichele 					c = next;
1139daf24110Shenning 				else if (next == '\n') {
1140daf24110Shenning 					file->lineno++;
1141ab0c2486Smichele 					continue;
1142daf24110Shenning 				} else
1143ab0c2486Smichele 					lungetc(next);
1144ab0c2486Smichele 			} else if (c == quotec) {
1145ab0c2486Smichele 				*p = '\0';
1146ab0c2486Smichele 				break;
114741eef22fSjsg 			} else if (c == '\0') {
114841eef22fSjsg 				yyerror("syntax error");
114941eef22fSjsg 				return (findeol());
1150ab0c2486Smichele 			}
1151ab0c2486Smichele 			if (p + 1 >= buf + sizeof(buf) - 1) {
1152ab0c2486Smichele 				yyerror("string too long");
1153ab0c2486Smichele 				return (findeol());
1154ab0c2486Smichele 			}
1155015d7b4dSbenno 			*p++ = c;
1156ab0c2486Smichele 		}
1157ab0c2486Smichele 		yylval.v.string = strdup(buf);
1158ab0c2486Smichele 		if (yylval.v.string == NULL)
1159a062aa9dSkrw 			err(1, "%s", __func__);
1160ab0c2486Smichele 		return (STRING);
1161ab0c2486Smichele 	}
1162ab0c2486Smichele 
1163ab0c2486Smichele #define allowed_to_end_number(x) \
1164ab0c2486Smichele 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1165ab0c2486Smichele 
1166ab0c2486Smichele 	if (c == '-' || isdigit(c)) {
1167ab0c2486Smichele 		do {
1168ab0c2486Smichele 			*p++ = c;
1169915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1170ab0c2486Smichele 				yyerror("string too long");
1171ab0c2486Smichele 				return (findeol());
1172ab0c2486Smichele 			}
1173ab0c2486Smichele 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1174ab0c2486Smichele 		lungetc(c);
1175ab0c2486Smichele 		if (p == buf + 1 && buf[0] == '-')
1176ab0c2486Smichele 			goto nodigits;
1177ab0c2486Smichele 		if (c == EOF || allowed_to_end_number(c)) {
1178ab0c2486Smichele 			const char *errstr = NULL;
1179ab0c2486Smichele 
1180ab0c2486Smichele 			*p = '\0';
1181ab0c2486Smichele 			yylval.v.number = strtonum(buf, LLONG_MIN,
1182ab0c2486Smichele 			    LLONG_MAX, &errstr);
1183ab0c2486Smichele 			if (errstr) {
1184ab0c2486Smichele 				yyerror("\"%s\" invalid number: %s",
1185ab0c2486Smichele 				    buf, errstr);
1186ab0c2486Smichele 				return (findeol());
1187ab0c2486Smichele 			}
1188ab0c2486Smichele 			return (NUMBER);
1189ab0c2486Smichele 		} else {
1190ab0c2486Smichele  nodigits:
1191ab0c2486Smichele 			while (p > buf + 1)
119208f6ba19Snaddy 				lungetc((unsigned char)*--p);
119308f6ba19Snaddy 			c = (unsigned char)*--p;
1194ab0c2486Smichele 			if (c == '-')
1195ab0c2486Smichele 				return (c);
1196ab0c2486Smichele 		}
1197ab0c2486Smichele 	}
1198ab0c2486Smichele 
1199ab0c2486Smichele #define allowed_in_string(x) \
1200ab0c2486Smichele 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1201ab0c2486Smichele 	x != '{' && x != '}' && \
1202ab0c2486Smichele 	x != '!' && x != '=' && x != '#' && \
1203ab0c2486Smichele 	x != ','))
1204ab0c2486Smichele 
1205ab0c2486Smichele 	if (isalnum(c) || c == ':' || c == '_') {
1206ab0c2486Smichele 		do {
1207ab0c2486Smichele 			*p++ = c;
1208915c3f33Sderaadt 			if ((size_t)(p-buf) >= sizeof(buf)) {
1209ab0c2486Smichele 				yyerror("string too long");
1210ab0c2486Smichele 				return (findeol());
1211ab0c2486Smichele 			}
1212ab0c2486Smichele 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1213ab0c2486Smichele 		lungetc(c);
1214ab0c2486Smichele 		*p = '\0';
1215ab0c2486Smichele 		if ((token = lookup(buf)) == STRING)
1216ab0c2486Smichele 			if ((yylval.v.string = strdup(buf)) == NULL)
1217a062aa9dSkrw 				err(1, "%s", __func__);
1218ab0c2486Smichele 		return (token);
1219ab0c2486Smichele 	}
1220ab0c2486Smichele 	if (c == '\n') {
1221ab0c2486Smichele 		yylval.lineno = file->lineno;
1222ab0c2486Smichele 		file->lineno++;
1223ab0c2486Smichele 	}
1224ab0c2486Smichele 	if (c == EOF)
1225ab0c2486Smichele 		return (0);
1226ab0c2486Smichele 	return (c);
1227ab0c2486Smichele }
1228ab0c2486Smichele 
1229c28a25a1Srenato static int
1230ab0c2486Smichele check_file_secrecy(int fd, const char *fname)
1231ab0c2486Smichele {
1232ab0c2486Smichele 	struct stat	st;
1233ab0c2486Smichele 
1234ab0c2486Smichele 	if (fstat(fd, &st)) {
1235ab0c2486Smichele 		log_warn("cannot stat %s", fname);
1236ab0c2486Smichele 		return (-1);
1237ab0c2486Smichele 	}
1238ab0c2486Smichele 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1239ab0c2486Smichele 		log_warnx("%s: owner not root or current user", fname);
1240ab0c2486Smichele 		return (-1);
1241ab0c2486Smichele 	}
12427140c133Shenning 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
12437140c133Shenning 		log_warnx("%s: group writable or world read/writable", fname);
1244ab0c2486Smichele 		return (-1);
1245ab0c2486Smichele 	}
1246ab0c2486Smichele 	return (0);
1247ab0c2486Smichele }
1248ab0c2486Smichele 
1249c28a25a1Srenato static struct file *
1250ab0c2486Smichele pushfile(const char *name, int secret)
1251ab0c2486Smichele {
1252ab0c2486Smichele 	struct file	*nfile;
1253ab0c2486Smichele 
1254d0812690Smichele 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
12556a3d55f9Skrw 		log_warn("%s", __func__);
1256ab0c2486Smichele 		return (NULL);
1257ab0c2486Smichele 	}
1258d0812690Smichele 	if ((nfile->name = strdup(name)) == NULL) {
12596a3d55f9Skrw 		log_warn("%s", __func__);
1260d0812690Smichele 		free(nfile);
1261d0812690Smichele 		return (NULL);
1262d0812690Smichele 	}
1263ab0c2486Smichele 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
12646a3d55f9Skrw 		log_warn("%s: %s", __func__, nfile->name);
1265ab0c2486Smichele 		free(nfile->name);
1266ab0c2486Smichele 		free(nfile);
1267ab0c2486Smichele 		return (NULL);
1268ab0c2486Smichele 	} else if (secret &&
1269ab0c2486Smichele 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1270ab0c2486Smichele 		fclose(nfile->stream);
1271ab0c2486Smichele 		free(nfile->name);
1272ab0c2486Smichele 		free(nfile);
1273ab0c2486Smichele 		return (NULL);
1274ab0c2486Smichele 	}
12756dbe86f5Sdenis 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
12766dbe86f5Sdenis 	nfile->ungetsize = 16;
12776dbe86f5Sdenis 	nfile->ungetbuf = malloc(nfile->ungetsize);
12786dbe86f5Sdenis 	if (nfile->ungetbuf == NULL) {
12796a3d55f9Skrw 		log_warn("%s", __func__);
12806dbe86f5Sdenis 		fclose(nfile->stream);
12816dbe86f5Sdenis 		free(nfile->name);
12826dbe86f5Sdenis 		free(nfile);
12836dbe86f5Sdenis 		return (NULL);
12846dbe86f5Sdenis 	}
1285ab0c2486Smichele 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1286ab0c2486Smichele 	return (nfile);
1287ab0c2486Smichele }
1288ab0c2486Smichele 
1289c28a25a1Srenato static int
1290ab0c2486Smichele popfile(void)
1291ab0c2486Smichele {
1292ab0c2486Smichele 	struct file	*prev;
1293ab0c2486Smichele 
1294ab0c2486Smichele 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1295ab0c2486Smichele 		prev->errors += file->errors;
1296ab0c2486Smichele 
1297ab0c2486Smichele 	TAILQ_REMOVE(&files, file, entry);
1298ab0c2486Smichele 	fclose(file->stream);
1299ab0c2486Smichele 	free(file->name);
13006dbe86f5Sdenis 	free(file->ungetbuf);
1301ab0c2486Smichele 	free(file);
1302ab0c2486Smichele 	file = prev;
1303ab0c2486Smichele 	return (file ? 0 : EOF);
1304ab0c2486Smichele }
1305ab0c2486Smichele 
1306ab0c2486Smichele struct ldpd_conf *
13073de94509Srenato parse_config(char *filename)
1308ab0c2486Smichele {
1309ab0c2486Smichele 	struct sym	*sym, *next;
1310ab0c2486Smichele 
131116040b47Srenato 	conf = config_new_empty();
13128622bd53Srenato 	conf->rdomain = 0;
1313a8c39dc0Srenato 	conf->trans_pref = DUAL_STACK_LDPOV6;
1314ab0c2486Smichele 
1315ab0c2486Smichele 	defs = &globaldefs;
1316a8c39dc0Srenato 	defs->keepalive = DEFAULT_KEEPALIVE;
131783dcf737Sclaudio 	defs->lhello_holdtime = LINK_DFLT_HOLDTIME;
131883dcf737Sclaudio 	defs->lhello_interval = DEFAULT_HELLO_INTERVAL;
131983dcf737Sclaudio 	defs->thello_holdtime = TARGETED_DFLT_HOLDTIME;
132083dcf737Sclaudio 	defs->thello_interval = DEFAULT_HELLO_INTERVAL;
1321a2da3f43Srenato 	defs->pwflags = F_PW_STATUSTLV_CONF|F_PW_CWORD_CONF;
132273d80685Smichele 
13233de94509Srenato 	if ((file = pushfile(filename,
13243de94509Srenato 	    !(global.cmd_opts & LDPD_OPT_NOACTION))) == NULL) {
1325ab0c2486Smichele 		free(conf);
1326ab0c2486Smichele 		return (NULL);
1327ab0c2486Smichele 	}
1328ab0c2486Smichele 	topfile = file;
1329ab0c2486Smichele 
1330ab0c2486Smichele 	yyparse();
1331ab0c2486Smichele 	errors = file->errors;
1332ab0c2486Smichele 	popfile();
1333ab0c2486Smichele 
1334ab0c2486Smichele 	/* Free macros and check which have not been used. */
133546bca67bSkrw 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
13363de94509Srenato 		if ((global.cmd_opts & LDPD_OPT_VERBOSE2) && !sym->used)
1337ab0c2486Smichele 			fprintf(stderr, "warning: macro '%s' not "
1338ab0c2486Smichele 			    "used\n", sym->nam);
1339ab0c2486Smichele 		if (!sym->persist) {
1340ab0c2486Smichele 			free(sym->nam);
1341ab0c2486Smichele 			free(sym->val);
1342ab0c2486Smichele 			TAILQ_REMOVE(&symhead, sym, entry);
1343ab0c2486Smichele 			free(sym);
1344ab0c2486Smichele 		}
1345ab0c2486Smichele 	}
1346ab0c2486Smichele 
13478622bd53Srenato 	/* check that all interfaces belong to the configured rdomain */
13488622bd53Srenato 	errors += conf_check_rdomain(conf->rdomain);
13498622bd53Srenato 
1350ab0c2486Smichele 	/* free global config defaults */
1351ab0c2486Smichele 	if (errors) {
1352ab0c2486Smichele 		clear_config(conf);
1353ab0c2486Smichele 		return (NULL);
1354ab0c2486Smichele 	}
1355ab0c2486Smichele 
135619fce358Srenato 	if (conf->rtr_id.s_addr == INADDR_ANY)
1357ab0c2486Smichele 		conf->rtr_id.s_addr = get_rtr_id();
1358a8c39dc0Srenato 
1359a8c39dc0Srenato 	/* if the ipv4 transport-address is not set, use the router-id */
1360a8c39dc0Srenato 	if ((conf->ipv4.flags & F_LDPD_AF_ENABLED) &&
1361a8c39dc0Srenato 	    conf->ipv4.trans_addr.v4.s_addr == INADDR_ANY)
1362a8c39dc0Srenato 		conf->ipv4.trans_addr.v4 = conf->rtr_id;
1363ab0c2486Smichele 
1364ab0c2486Smichele 	return (conf);
1365ab0c2486Smichele }
1366ab0c2486Smichele 
1367c28a25a1Srenato static int
1368ab0c2486Smichele symset(const char *nam, const char *val, int persist)
1369ab0c2486Smichele {
1370ab0c2486Smichele 	struct sym	*sym;
1371ab0c2486Smichele 
137254c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
137354c95b7aSkrw 		if (strcmp(nam, sym->nam) == 0)
137454c95b7aSkrw 			break;
137554c95b7aSkrw 	}
1376ab0c2486Smichele 
1377ab0c2486Smichele 	if (sym != NULL) {
1378ab0c2486Smichele 		if (sym->persist == 1)
1379ab0c2486Smichele 			return (0);
1380ab0c2486Smichele 		else {
1381ab0c2486Smichele 			free(sym->nam);
1382ab0c2486Smichele 			free(sym->val);
1383ab0c2486Smichele 			TAILQ_REMOVE(&symhead, sym, entry);
1384ab0c2486Smichele 			free(sym);
1385ab0c2486Smichele 		}
1386ab0c2486Smichele 	}
1387ab0c2486Smichele 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1388ab0c2486Smichele 		return (-1);
1389ab0c2486Smichele 
1390ab0c2486Smichele 	sym->nam = strdup(nam);
1391ab0c2486Smichele 	if (sym->nam == NULL) {
1392ab0c2486Smichele 		free(sym);
1393ab0c2486Smichele 		return (-1);
1394ab0c2486Smichele 	}
1395ab0c2486Smichele 	sym->val = strdup(val);
1396ab0c2486Smichele 	if (sym->val == NULL) {
1397ab0c2486Smichele 		free(sym->nam);
1398ab0c2486Smichele 		free(sym);
1399ab0c2486Smichele 		return (-1);
1400ab0c2486Smichele 	}
1401ab0c2486Smichele 	sym->used = 0;
1402ab0c2486Smichele 	sym->persist = persist;
1403ab0c2486Smichele 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1404ab0c2486Smichele 	return (0);
1405ab0c2486Smichele }
1406ab0c2486Smichele 
1407ab0c2486Smichele int
1408ab0c2486Smichele cmdline_symset(char *s)
1409ab0c2486Smichele {
1410ab0c2486Smichele 	char	*sym, *val;
1411ab0c2486Smichele 	int	ret;
1412ab0c2486Smichele 
1413ab0c2486Smichele 	if ((val = strrchr(s, '=')) == NULL)
1414ab0c2486Smichele 		return (-1);
1415ed1b9eb8Smiko 	sym = strndup(s, val - s);
1416ed1b9eb8Smiko 	if (sym == NULL)
1417ed1b9eb8Smiko 		errx(1, "%s: strndup", __func__);
1418ab0c2486Smichele 	ret = symset(sym, val + 1, 1);
1419ab0c2486Smichele 	free(sym);
1420ab0c2486Smichele 
1421ab0c2486Smichele 	return (ret);
1422ab0c2486Smichele }
1423ab0c2486Smichele 
1424c28a25a1Srenato static char *
1425ab0c2486Smichele symget(const char *nam)
1426ab0c2486Smichele {
1427ab0c2486Smichele 	struct sym	*sym;
1428ab0c2486Smichele 
142954c95b7aSkrw 	TAILQ_FOREACH(sym, &symhead, entry) {
1430ab0c2486Smichele 		if (strcmp(nam, sym->nam) == 0) {
1431ab0c2486Smichele 			sym->used = 1;
1432ab0c2486Smichele 			return (sym->val);
1433ab0c2486Smichele 		}
143454c95b7aSkrw 	}
1435ab0c2486Smichele 	return (NULL);
1436ab0c2486Smichele }
1437ab0c2486Smichele 
1438c28a25a1Srenato static struct iface *
1439814e607dSclaudio conf_get_if(struct kif *kif)
1440ab0c2486Smichele {
1441ab0c2486Smichele 	struct iface	*i;
1442c000763aSrenato 	struct l2vpn	*l;
1443ab0c2486Smichele 
144419fce358Srenato 	if (kif->if_type == IFT_LOOP ||
144519fce358Srenato 	    kif->if_type == IFT_CARP ||
1446c000763aSrenato 	    kif->if_type == IFT_BRIDGE ||
144719fce358Srenato 	    kif->if_type == IFT_MPLSTUNNEL) {
144819fce358Srenato 		yyerror("unsupported interface type on interface %s",
144919fce358Srenato 		    kif->ifname);
145019fce358Srenato 		return (NULL);
145119fce358Srenato 	}
1452ab0c2486Smichele 
1453c000763aSrenato 	LIST_FOREACH(l, &conf->l2vpn_list, entry)
1454c000763aSrenato 		if (l2vpn_if_find(l, kif->ifindex)) {
1455c000763aSrenato 			yyerror("interface %s already configured under "
1456c000763aSrenato 			    "l2vpn %s", kif->ifname, l->name);
1457c000763aSrenato 			return (NULL);
1458c000763aSrenato 		}
1459c000763aSrenato 
1460c000763aSrenato 	LIST_FOREACH(i, &conf->iface_list, entry)
1461c000763aSrenato 		if (i->ifindex == kif->ifindex)
1462c000763aSrenato 			return (i);
1463c000763aSrenato 
146419fce358Srenato 	i = if_new(kif);
146519fce358Srenato 	LIST_INSERT_HEAD(&conf->iface_list, i, entry);
1466ab0c2486Smichele 	return (i);
1467ab0c2486Smichele }
1468ab0c2486Smichele 
1469c28a25a1Srenato static struct tnbr *
1470a8c39dc0Srenato conf_get_tnbr(union ldpd_addr *addr)
147183dcf737Sclaudio {
147283dcf737Sclaudio 	struct tnbr	*t;
147383dcf737Sclaudio 
1474a8c39dc0Srenato 	t = tnbr_find(conf, af, addr);
1475edf37f74Srenato 	if (t) {
147683dcf737Sclaudio 		yyerror("targeted neighbor %s already configured",
1477a8c39dc0Srenato 		    log_addr(af, addr));
147883dcf737Sclaudio 		return (NULL);
147983dcf737Sclaudio 	}
148083dcf737Sclaudio 
1481a8c39dc0Srenato 	t = tnbr_new(conf, af, addr);
148219fce358Srenato 	t->flags |= F_TNBR_CONFIGURED;
148319fce358Srenato 	LIST_INSERT_HEAD(&conf->tnbr_list, t, entry);
148483dcf737Sclaudio 	return (t);
148583dcf737Sclaudio }
148683dcf737Sclaudio 
1487c28a25a1Srenato static struct nbr_params *
14881ce5acabSrenato conf_get_nbrp(struct in_addr lsr_id)
1489627846caSrenato {
1490627846caSrenato 	struct nbr_params	*n;
1491627846caSrenato 
1492627846caSrenato 	LIST_FOREACH(n, &conf->nbrp_list, entry) {
14931ce5acabSrenato 		if (n->lsr_id.s_addr == lsr_id.s_addr) {
1494627846caSrenato 			yyerror("neighbor %s already configured",
14951ce5acabSrenato 			    inet_ntoa(lsr_id));
1496627846caSrenato 			return (NULL);
1497627846caSrenato 		}
1498627846caSrenato 	}
1499627846caSrenato 
15001ce5acabSrenato 	n = nbr_params_new(lsr_id);
150119fce358Srenato 	LIST_INSERT_HEAD(&conf->nbrp_list, n, entry);
1502627846caSrenato 	return (n);
1503627846caSrenato }
1504627846caSrenato 
1505c28a25a1Srenato static struct l2vpn *
15066399cec1Srenato conf_get_l2vpn(char *name)
15076399cec1Srenato {
15086399cec1Srenato 	struct l2vpn	 *l;
15096399cec1Srenato 
15106399cec1Srenato 	if (l2vpn_find(conf, name)) {
15116399cec1Srenato 		yyerror("l2vpn %s already configured", name);
15126399cec1Srenato 		return (NULL);
15136399cec1Srenato 	}
15146399cec1Srenato 
15156399cec1Srenato 	l = l2vpn_new(name);
151619fce358Srenato 	LIST_INSERT_HEAD(&conf->l2vpn_list, l, entry);
15176399cec1Srenato 	return (l);
15186399cec1Srenato }
15196399cec1Srenato 
1520c28a25a1Srenato static struct l2vpn_if *
15216399cec1Srenato conf_get_l2vpn_if(struct l2vpn *l, struct kif *kif)
15226399cec1Srenato {
15236399cec1Srenato 	struct iface	*i;
15246399cec1Srenato 	struct l2vpn	*ltmp;
152519fce358Srenato 	struct l2vpn_if	*f;
15266399cec1Srenato 
1527c000763aSrenato 	if (kif->if_type == IFT_LOOP ||
1528c000763aSrenato 	    kif->if_type == IFT_CARP ||
1529c000763aSrenato 	    kif->if_type == IFT_BRIDGE ||
1530c000763aSrenato 	    kif->if_type == IFT_MPLSTUNNEL) {
1531c000763aSrenato 		yyerror("unsupported interface type on interface %s",
1532c000763aSrenato 		    kif->ifname);
1533c000763aSrenato 		return (NULL);
1534c000763aSrenato 	}
1535c000763aSrenato 
1536c000763aSrenato 	LIST_FOREACH(ltmp, &conf->l2vpn_list, entry)
1537c000763aSrenato 		if (l2vpn_if_find(ltmp, kif->ifindex)) {
1538c000763aSrenato 			yyerror("interface %s already configured under "
1539c000763aSrenato 			    "l2vpn %s", kif->ifname, ltmp->name);
1540c000763aSrenato 			return (NULL);
1541c000763aSrenato 		}
1542c000763aSrenato 
15436399cec1Srenato 	LIST_FOREACH(i, &conf->iface_list, entry) {
15446399cec1Srenato 		if (i->ifindex == kif->ifindex) {
15456399cec1Srenato 			yyerror("interface %s already configured",
15466399cec1Srenato 			    kif->ifname);
15476399cec1Srenato 			return (NULL);
15486399cec1Srenato 		}
15496399cec1Srenato 	}
15506399cec1Srenato 
155119fce358Srenato 	f = l2vpn_if_new(l, kif);
155219fce358Srenato 	LIST_INSERT_HEAD(&l2vpn->if_list, f, entry);
155319fce358Srenato 	return (f);
15546399cec1Srenato }
15556399cec1Srenato 
1556c28a25a1Srenato static struct l2vpn_pw *
15576399cec1Srenato conf_get_l2vpn_pw(struct l2vpn *l, struct kif *kif)
15586399cec1Srenato {
15596399cec1Srenato 	struct l2vpn	*ltmp;
156019fce358Srenato 	struct l2vpn_pw	*p;
15616399cec1Srenato 
15626399cec1Srenato 	LIST_FOREACH(ltmp, &conf->l2vpn_list, entry) {
15636399cec1Srenato 		if (l2vpn_pw_find(ltmp, kif->ifindex)) {
15646399cec1Srenato 			yyerror("pseudowire %s is already being "
15656399cec1Srenato 			    "used by l2vpn %s", kif->ifname, ltmp->name);
15666399cec1Srenato 			return (NULL);
15676399cec1Srenato 		}
15686399cec1Srenato 	}
15696399cec1Srenato 
157019fce358Srenato 	p = l2vpn_pw_new(l, kif);
157119fce358Srenato 	LIST_INSERT_HEAD(&l2vpn->pw_list, p, entry);
157219fce358Srenato 	return (p);
15736399cec1Srenato }
15746399cec1Srenato 
15758622bd53Srenato int
15768622bd53Srenato conf_check_rdomain(unsigned int rdomain)
15778622bd53Srenato {
15788622bd53Srenato 	struct iface	*i;
15798622bd53Srenato 	int		 errs = 0;
15808622bd53Srenato 
15818622bd53Srenato 	LIST_FOREACH(i, &conf->iface_list, entry) {
15828622bd53Srenato 		if (i->rdomain != rdomain) {
15838622bd53Srenato 			logit(LOG_CRIT, "interface %s not in rdomain %u",
15848622bd53Srenato 			    i->name, rdomain);
15858622bd53Srenato 			errs++;
15868622bd53Srenato 		}
15878622bd53Srenato 	}
15888622bd53Srenato 
15898622bd53Srenato 	return (errs);
15908622bd53Srenato }
15918622bd53Srenato 
1592c28a25a1Srenato static void
1593ab0c2486Smichele clear_config(struct ldpd_conf *xconf)
1594ab0c2486Smichele {
1595ab0c2486Smichele 	struct iface		*i;
159617cc0810Srenato 	struct tnbr		*t;
159717cc0810Srenato 	struct nbr_params	*n;
15986399cec1Srenato 	struct l2vpn		*l;
15996399cec1Srenato 	struct l2vpn_if		*f;
16006399cec1Srenato 	struct l2vpn_pw		*p;
1601ab0c2486Smichele 
160217cc0810Srenato 	while ((i = LIST_FIRST(&xconf->iface_list)) != NULL) {
1603ab0c2486Smichele 		LIST_REMOVE(i, entry);
1604eee72fddSrenato 		free(i);
1605ab0c2486Smichele 	}
1606ab0c2486Smichele 
160717cc0810Srenato 	while ((t = LIST_FIRST(&xconf->tnbr_list)) != NULL) {
160817cc0810Srenato 		LIST_REMOVE(t, entry);
1609eee72fddSrenato 		free(t);
161017cc0810Srenato 	}
161117cc0810Srenato 
161217cc0810Srenato 	while ((n = LIST_FIRST(&xconf->nbrp_list)) != NULL) {
161317cc0810Srenato 		LIST_REMOVE(n, entry);
161417cc0810Srenato 		free(n);
161517cc0810Srenato 	}
161617cc0810Srenato 
16176399cec1Srenato 	while ((l = LIST_FIRST(&xconf->l2vpn_list)) != NULL) {
16186399cec1Srenato 		while ((f = LIST_FIRST(&l->if_list)) != NULL) {
16196399cec1Srenato 			LIST_REMOVE(f, entry);
16206399cec1Srenato 			free(f);
16216399cec1Srenato 		}
16226399cec1Srenato 		while ((p = LIST_FIRST(&l->pw_list)) != NULL) {
16236399cec1Srenato 			LIST_REMOVE(p, entry);
16246399cec1Srenato 			free(p);
16256399cec1Srenato 		}
16266399cec1Srenato 		LIST_REMOVE(l, entry);
16276399cec1Srenato 		free(l);
16286399cec1Srenato 	}
16296399cec1Srenato 
1630ab0c2486Smichele 	free(xconf);
1631ab0c2486Smichele }
1632ab0c2486Smichele 
1633c28a25a1Srenato static uint32_t
1634ab0c2486Smichele get_rtr_id(void)
1635ab0c2486Smichele {
1636ab0c2486Smichele 	struct ifaddrs		*ifap, *ifa;
16373de94509Srenato 	uint32_t		 ip = 0, cur, localnet;
1638ab0c2486Smichele 
1639ab0c2486Smichele 	localnet = htonl(INADDR_LOOPBACK & IN_CLASSA_NET);
1640ab0c2486Smichele 
164108964f31Srenato 	if (getifaddrs(&ifap) == -1) {
164208964f31Srenato 		log_warn("getifaddrs");
164308964f31Srenato 		return (0);
164408964f31Srenato 	}
1645ab0c2486Smichele 
1646ab0c2486Smichele 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
1647f2af7f71Sclaudio 		if (strncmp(ifa->ifa_name, "carp", 4) == 0)
1648f2af7f71Sclaudio 			continue;
1649ab0c2486Smichele 		if (ifa->ifa_addr->sa_family != AF_INET)
1650ab0c2486Smichele 			continue;
1651ab0c2486Smichele 		cur = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr;
1652ab0c2486Smichele 		if ((cur & localnet) == localnet)	/* skip 127/8 */
1653ab0c2486Smichele 			continue;
165436dfdbcaSrenato 		if (ntohl(cur) < ntohl(ip) || ip == 0)
1655ab0c2486Smichele 			ip = cur;
1656ab0c2486Smichele 	}
1657ab0c2486Smichele 	freeifaddrs(ifap);
1658ab0c2486Smichele 
1659ab0c2486Smichele 	return (ip);
1660ab0c2486Smichele }
1661ab0c2486Smichele 
1662c28a25a1Srenato static int
1663a8c39dc0Srenato get_address(const char *s, union ldpd_addr *addr)
1664ab0c2486Smichele {
1665a8c39dc0Srenato 	switch (af) {
1666a8c39dc0Srenato 	case AF_INET:
1667a8c39dc0Srenato 		if (inet_pton(AF_INET, s, &addr->v4) != 1)
1668a8c39dc0Srenato 			return (-1);
1669a8c39dc0Srenato 		break;
1670a8c39dc0Srenato 	case AF_INET6:
1671a8c39dc0Srenato 		if (inet_pton(AF_INET6, s, &addr->v6) != 1)
1672a8c39dc0Srenato 			return (-1);
1673a8c39dc0Srenato 		break;
1674a8c39dc0Srenato 	default:
1675a8c39dc0Srenato 		return (-1);
1676a8c39dc0Srenato 	}
1677ab0c2486Smichele 
1678ab0c2486Smichele 	return (0);
1679ab0c2486Smichele }
1680ab0c2486Smichele 
1681c28a25a1Srenato static int
1682a8c39dc0Srenato get_af_address(const char *s, int *family, union ldpd_addr *addr)
1683a8c39dc0Srenato {
1684a8c39dc0Srenato 	if (inet_pton(AF_INET, s, &addr->v4) == 1) {
1685a8c39dc0Srenato 		*family = AF_INET;
1686a8c39dc0Srenato 		return (0);
1687a8c39dc0Srenato 	}
1688ab0c2486Smichele 
1689a8c39dc0Srenato 	if (inet_pton(AF_INET6, s, &addr->v6) == 1) {
1690a8c39dc0Srenato 		*family = AF_INET6;
1691a8c39dc0Srenato 		return (0);
1692a8c39dc0Srenato 	}
1693a8c39dc0Srenato 
1694a8c39dc0Srenato 	return (-1);
1695ab0c2486Smichele }
16967ee91690Sdlg 
16977ee91690Sdlg static int
16987ee91690Sdlg hexchar(int ch)
16997ee91690Sdlg {
17007ee91690Sdlg 	if (ch >= '0' && ch <= '9')
17017ee91690Sdlg 		return (ch - '0');
17027ee91690Sdlg 	if (ch >= 'a' && ch <= 'f')
17037ee91690Sdlg 		return (ch - 'a');
17047ee91690Sdlg 	if (ch >= 'A' && ch <= 'F')
17057ee91690Sdlg 		return (ch - 'A');
17067ee91690Sdlg 
17077ee91690Sdlg 	return (-1);
17087ee91690Sdlg }
17097ee91690Sdlg 
17107ee91690Sdlg static int
17117ee91690Sdlg str2key(char *dst, const char *src, int dstlen)
17127ee91690Sdlg {
17137ee91690Sdlg 	int		i = 0;
17147ee91690Sdlg 	int		digit;
17157ee91690Sdlg 
17167ee91690Sdlg 	while (*src != '\0') {
17177ee91690Sdlg 		digit = hexchar(*src);
17187ee91690Sdlg 		if (digit == -1)
17197ee91690Sdlg 			return (-1);
17207ee91690Sdlg 
17217ee91690Sdlg 		if (i < dstlen)
17227ee91690Sdlg 			*dst = digit << 4;
17237ee91690Sdlg 
17247ee91690Sdlg 		src++;
17257ee91690Sdlg 		if (*src == '\0')
17267ee91690Sdlg 			return (-1);
17277ee91690Sdlg 		digit = hexchar(*src);
17287ee91690Sdlg 		if (digit == -1)
17297ee91690Sdlg 			return (-1);
17307ee91690Sdlg 
17317ee91690Sdlg 		if (i < dstlen)
17327ee91690Sdlg 			*dst |= digit;
17337ee91690Sdlg 
17347ee91690Sdlg 		src++;
17357ee91690Sdlg 		i++;
17367ee91690Sdlg 	}
17377ee91690Sdlg 
17387ee91690Sdlg 	return (i);
17397ee91690Sdlg }
1740