1%{
2/*	$NetBSD: cfparse.y,v 1.15 2003/08/18 05:39:53 itojun Exp $	*/
3
4/*
5 * Configuration file parser for mrouted.
6 *
7 * Written by Bill Fenner, NRL, 1994
8 * Copyright (c) 1994
9 * Naval Research Laboratory (NRL/CCS)
10 *                    and the
11 * Defense Advanced Research Projects Agency (DARPA)
12 *
13 * All Rights Reserved.
14 *
15 * Permission to use, copy, modify and distribute this software and its
16 * documentation is hereby granted, provided that both the copyright notice and
17 * this permission notice appear in all copies of the software, derivative
18 * works or modified versions, and any portions thereof, and that both notices
19 * appear in supporting documentation.
20 *
21 * NRL AND DARPA ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND
22 * DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM
23 * THE USE OF THIS SOFTWARE.
24 */
25#include <stdio.h>
26#include <stdarg.h>
27#include "defs.h"
28#include <netdb.h>
29#include <ifaddrs.h>
30
31/*
32 * Local function declarations
33 */
34static void		fatal(const char *fmt, ...)
35    __attribute__((__format__(__printf__, 1, 2)));
36static void		warn(const char *fmt, ...)
37        __attribute__((__format__(__printf__, 1, 2)));
38static void		yyerror(const char *s);
39static char *		next_word(void);
40static int		yylex(void);
41static u_int32_t	valid_if(char *s);
42static const char *	ifconfaddr(u_int32_t a);
43int			yyparse(void);
44
45static FILE *f __attribute__((__unused__));	/* XXX egcs */
46extern int udp_socket;
47const char *configfilename = _PATH_MROUTED_CONF;
48
49extern int cache_lifetime;
50extern int max_prune_lifetime;
51
52static int lineno;
53
54static struct uvif *v;
55
56static int order;
57
58struct addrmask {
59	u_int32_t	addr;
60	int	mask;
61};
62
63struct boundnam {
64	char		*name;
65	struct addrmask	 bound;
66};
67
68#define MAXBOUNDS 20
69
70struct boundnam boundlist[MAXBOUNDS];	/* Max. of 20 named boundaries */
71int numbounds = 0;			/* Number of named boundaries */
72
73%}
74
75%union
76{
77	int num;
78	char *ptr;
79	struct addrmask addrmask;
80	u_int32_t addr;
81};
82
83%token CACHE_LIFETIME PRUNING
84%token PHYINT TUNNEL NAME
85%token DISABLE IGMPV1 SRCRT
86%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET
87%token SYSNAM SYSCONTACT SYSVERSION SYSLOCATION
88%token <num> BOOLEAN
89%token <num> NUMBER
90%token <ptr> STRING
91%token <addrmask> ADDRMASK
92%token <addr> ADDR
93
94%type <addr> interface addrname
95%type <addrmask> bound boundary addrmask
96
97%start conf
98
99%%
100
101conf	: stmts
102	;
103
104stmts	: /* Empty */
105	| stmts stmt
106	;
107
108stmt	: error
109	| PHYINT interface 		{
110
111			vifi_t vifi;
112
113			if (order)
114			    fatal("phyints must appear before tunnels");
115
116			for (vifi = 0, v = uvifs;
117			     vifi < numvifs;
118			     ++vifi, ++v)
119			    if (!(v->uv_flags & VIFF_TUNNEL) &&
120				$2 == v->uv_lcl_addr)
121				break;
122
123			if (vifi == numvifs)
124			    fatal("%s is not a configured interface",
125				inet_fmt($2));
126
127					}
128		ifmods
129	| TUNNEL interface addrname	{
130			const char *ifname;
131			struct ifreq ffr;
132			vifi_t vifi;
133
134			order++;
135
136			ifname = ifconfaddr($2);
137			if (ifname == 0)
138			    fatal("Tunnel local address %s is not mine",
139				inet_fmt($2));
140
141			strncpy(ffr.ifr_name, ifname, sizeof(ffr.ifr_name));
142			if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
143			    fatal("ioctl SIOCGIFFLAGS on %s",ffr.ifr_name);
144			if (ffr.ifr_flags & IFF_LOOPBACK)
145			    fatal("Tunnel local address %s is a loopback interface",
146				inet_fmt($2));
147
148			if (ifconfaddr($3) != 0)
149			    fatal("Tunnel remote address %s is one of mine",
150				inet_fmt($3));
151
152			for (vifi = 0, v = uvifs;
153			     vifi < numvifs;
154			     ++vifi, ++v)
155			    if (v->uv_flags & VIFF_TUNNEL) {
156				if ($3 == v->uv_rmt_addr)
157				    fatal("Duplicate tunnel to %s",
158					inet_fmt($3));
159			    } else if (!(v->uv_flags & VIFF_DISABLED)) {
160				if (($3 & v->uv_subnetmask) == v->uv_subnet)
161				    fatal("Unnecessary tunnel to %s",
162					inet_fmt($3));
163			    }
164
165			if (numvifs == MAXVIFS)
166			    fatal("too many vifs");
167
168			v = &uvifs[numvifs];
169			v->uv_flags	= VIFF_TUNNEL;
170			v->uv_metric	= DEFAULT_METRIC;
171			v->uv_rate_limit= DEFAULT_TUN_RATE_LIMIT;
172			v->uv_threshold	= DEFAULT_THRESHOLD;
173			v->uv_lcl_addr	= $2;
174			v->uv_rmt_addr	= $3;
175			v->uv_subnet	= 0;
176			v->uv_subnetmask= 0;
177			v->uv_subnetbcast= 0;
178			strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
179			v->uv_groups	= NULL;
180			v->uv_neighbors	= NULL;
181			v->uv_acl	= NULL;
182			v->uv_addrs	= NULL;
183
184			if (!(ffr.ifr_flags & IFF_UP)) {
185			    v->uv_flags |= VIFF_DOWN;
186			    vifs_down = TRUE;
187			}
188					}
189		tunnelmods
190					{
191			logit(LOG_INFO, 0,
192			    "installing tunnel from %s to %s as vif #%u - rate=%d",
193			    inet_fmt($2),
194			    inet_fmt($3),
195			    numvifs, v->uv_rate_limit);
196
197			++numvifs;
198					}
199	| PRUNING BOOLEAN	    { pruning = $2; }
200	| CACHE_LIFETIME NUMBER     { cache_lifetime = $2;
201				      max_prune_lifetime = cache_lifetime * 2;
202				    }
203	| NAME STRING boundary	    { if (numbounds >= MAXBOUNDS) {
204					fatal("Too many named boundaries (max %d)", MAXBOUNDS);
205				      }
206
207				      boundlist[numbounds].name = strdup($2);
208				      boundlist[numbounds++].bound = $3;
209				    }
210	| SYSNAM STRING    {
211#ifdef SNMP
212			    set_sysName($2);
213#endif /* SNMP */
214			    }
215	| SYSCONTACT STRING {
216#ifdef SNMP
217			    set_sysContact($2);
218#endif /* SNMP */
219			    }
220        | SYSVERSION STRING {
221#ifdef SNMP
222			    set_sysVersion($2);
223#endif /* SNMP */
224			    }
225	| SYSLOCATION STRING {
226#ifdef SNMP
227			    set_sysLocation($2);
228#endif /* SNMP */
229			    }
230	;
231
232tunnelmods	: /* empty */
233	| tunnelmods tunnelmod
234	;
235
236tunnelmod	: mod
237	| SRCRT			{ fatal("Source-route tunnels not supported"); }
238	;
239
240ifmods	: /* empty */
241	| ifmods ifmod
242	;
243
244ifmod	: mod
245	| DISABLE		{ v->uv_flags |= VIFF_DISABLED; }
246	| IGMPV1		{ v->uv_flags |= VIFF_IGMPV1; }
247	| NETMASK addrname	{
248				  u_int32_t subnet, mask;
249
250				  mask = $2;
251				  subnet = v->uv_lcl_addr & mask;
252				  if (!inet_valid_subnet(subnet, mask))
253					fatal("Invalid netmask");
254				  v->uv_subnet = subnet;
255				  v->uv_subnetmask = mask;
256				  v->uv_subnetbcast = subnet | ~mask;
257				}
258	| NETMASK		{
259
260		    warn("Expected address after netmask keyword, ignored");
261
262				}
263	| ALTNET addrmask	{
264
265		    struct phaddr *ph;
266
267		    ph = (struct phaddr *)malloc(sizeof(struct phaddr));
268		    if (ph == NULL)
269			fatal("out of memory");
270		    if ($2.mask) {
271			VAL_TO_MASK(ph->pa_subnetmask, $2.mask);
272		    } else
273			ph->pa_subnetmask = v->uv_subnetmask;
274		    ph->pa_subnet = $2.addr & ph->pa_subnetmask;
275		    ph->pa_subnetbcast = ph->pa_subnet | ~ph->pa_subnetmask;
276		    if ($2.addr & ~ph->pa_subnetmask)
277			warn("Extra subnet %s/%d has host bits set",
278				inet_fmt($2.addr), $2.mask);
279		    ph->pa_next = v->uv_addrs;
280		    v->uv_addrs = ph;
281
282				}
283	| ALTNET		{
284
285		    warn("Expected address after altnet keyword, ignored");
286
287				}
288	;
289
290mod	: THRESHOLD NUMBER	{ if ($2 < 1 || $2 > 255)
291				    fatal("Invalid threshold %d",$2);
292				  v->uv_threshold = $2;
293				}
294	| THRESHOLD		{
295
296		    warn("Expected number after threshold keyword, ignored");
297
298				}
299	| METRIC NUMBER		{ if ($2 < 1 || $2 > UNREACHABLE)
300				    fatal("Invalid metric %d",$2);
301				  v->uv_metric = $2;
302				}
303	| METRIC		{
304
305		    warn("Expected number after metric keyword, ignored");
306
307				}
308	| RATE_LIMIT NUMBER	{ if ($2 > MAX_RATE_LIMIT)
309				    fatal("Invalid rate_limit %d",$2);
310				  v->uv_rate_limit = $2;
311				}
312	| RATE_LIMIT		{
313
314		    warn("Expected number after rate_limit keyword, ignored");
315
316				}
317	| BOUNDARY bound	{
318
319		    struct vif_acl *v_acl;
320
321		    v_acl = (struct vif_acl *)malloc(sizeof(struct vif_acl));
322		    if (v_acl == NULL)
323			fatal("out of memory");
324		    VAL_TO_MASK(v_acl->acl_mask, $2.mask);
325		    v_acl->acl_addr = $2.addr & v_acl->acl_mask;
326		    if ($2.addr & ~v_acl->acl_mask)
327			warn("Boundary spec %s/%d has host bits set",
328				inet_fmt($2.addr),$2.mask);
329		    v_acl->acl_next = v->uv_acl;
330		    v->uv_acl = v_acl;
331
332				}
333	| BOUNDARY		{
334
335		warn("Expected boundary spec after boundary keyword, ignored");
336
337				}
338	;
339
340interface	: ADDR		{ $$ = $1; }
341	| STRING		{
342				  $$ = valid_if($1);
343				  if ($$ == 0)
344					fatal("Invalid interface name %s",$1);
345				}
346	;
347
348addrname	: ADDR		{ $$ = $1; }
349	| STRING		{ struct hostent *hp;
350
351				  if ((hp = gethostbyname($1)) == NULL)
352				    fatal("No such host %s", $1);
353
354				  if (hp->h_addr_list[1])
355				    fatal("Hostname %s does not %s",
356					$1, "map to a unique address");
357
358				  bcopy(hp->h_addr_list[0], &$$,
359					    hp->h_length);
360				}
361
362bound	: boundary		{ $$ = $1; }
363	| STRING		{ int i;
364
365				  for (i=0; i < numbounds; i++) {
366				    if (!strcmp(boundlist[i].name, $1)) {
367					$$ = boundlist[i].bound;
368					break;
369				    }
370				  }
371				  if (i == numbounds) {
372				    fatal("Invalid boundary name %s",$1);
373				  }
374				}
375	;
376
377boundary	: ADDRMASK	{
378
379			if ((ntohl($1.addr) & 0xff000000) != 0xef000000) {
380			    fatal("Boundaries must be 239.x.x.x, not %s/%d",
381				inet_fmt($1.addr), $1.mask);
382			}
383			$$ = $1;
384
385				}
386	;
387
388addrmask	: ADDRMASK	{ $$ = $1; }
389	| ADDR			{ $$.addr = $1; $$.mask = 0; }
390	;
391%%
392static void
393fatal(const char *fmt, ...)
394{
395	va_list ap;
396	char buf[200];
397
398	va_start(ap, fmt);
399	vsnprintf(buf, sizeof(buf), fmt, ap);
400	va_end(ap);
401
402	logit(LOG_ERR,0,"%s: %s near line %d", configfilename, buf, lineno);
403}
404
405static void
406warn(const char *fmt, ...)
407{
408	va_list ap;
409	char buf[200];
410
411	va_start(ap, fmt);
412	vsnprintf(buf, sizeof(buf), fmt, ap);
413	va_end(ap);
414
415	logit(LOG_WARNING,0,"%s: %s near line %d", configfilename, buf, lineno);
416}
417
418static void
419yyerror(s)
420const char *s;
421{
422	logit(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno);
423}
424
425static char *
426next_word()
427{
428	static char buf[1024];
429	static char *p=NULL;
430	extern FILE *f;
431	char *q;
432
433	while (1) {
434	    if (!p || !*p) {
435		lineno++;
436		if (fgets(buf, sizeof(buf), f) == NULL)
437		    return NULL;
438		p = buf;
439	    }
440	    while (*p && (*p == ' ' || *p == '\t'))	/* skip whitespace */
441		p++;
442	    if (*p == '#') {
443		p = NULL;		/* skip comments */
444		continue;
445	    }
446	    q = p;
447#ifdef SNMP
448       if (*p == '"') {
449          p++;
450	       while (*p && *p != '"' && *p != '\n')
451		      p++;		/* find next whitespace */
452          if (*p == '"')
453             p++;
454       } else
455#endif
456	    while (*p && *p != ' ' && *p != '\t' && *p != '\n')
457		p++;		/* find next whitespace */
458	    *p++ = '\0';	/* null-terminate string */
459
460	    if (!*q) {
461		p = NULL;
462		continue;	/* if 0-length string, read another line */
463	    }
464
465	    return q;
466	}
467}
468
469static int
470yylex()
471{
472	int n;
473	u_int32_t addr;
474	char *q;
475	char c;
476
477	if ((q = next_word()) == NULL) {
478		return 0;
479	}
480
481	if (!strcmp(q,"cache_lifetime"))
482		return CACHE_LIFETIME;
483	if (!strcmp(q,"pruning"))
484		return PRUNING;
485	if (!strcmp(q,"phyint"))
486		return PHYINT;
487	if (!strcmp(q,"tunnel"))
488		return TUNNEL;
489	if (!strcmp(q,"disable"))
490		return DISABLE;
491	if (!strcmp(q,"metric"))
492		return METRIC;
493	if (!strcmp(q,"threshold"))
494		return THRESHOLD;
495	if (!strcmp(q,"rate_limit"))
496		return RATE_LIMIT;
497	if (!strcmp(q,"srcrt") || !strcmp(q,"sourceroute"))
498		return SRCRT;
499	if (!strcmp(q,"boundary"))
500		return BOUNDARY;
501	if (!strcmp(q,"netmask"))
502		return NETMASK;
503	if (!strcmp(q,"igmpv1"))
504		return IGMPV1;
505	if (!strcmp(q,"altnet"))
506		return ALTNET;
507	if (!strcmp(q,"name"))
508		return NAME;
509	if (!strcmp(q,"on") || !strcmp(q,"yes")) {
510		yylval.num = 1;
511		return BOOLEAN;
512	}
513	if (!strcmp(q,"off") || !strcmp(q,"no")) {
514		yylval.num = 0;
515		return BOOLEAN;
516	}
517	if ((addr = inet_parse(q, &n)) != 0xffffffff) {
518		yylval.addrmask.mask = n;
519		yylval.addrmask.addr = addr;
520		return ADDRMASK;
521	}
522	if ((addr = inet_parse(q,0)) != 0xffffffff &&
523	    inet_valid_host(addr)) {
524		yylval.addr = addr;
525		return ADDR;
526	}
527	if (sscanf(q,"0x%8x%c",&n,&c) == 1) {
528		yylval.addr = n;
529		return ADDR;
530	}
531	if (sscanf(q,"%d%c",&n,&c) == 1) {
532		yylval.num = n;
533		return NUMBER;
534	}
535#ifdef SNMP
536	if (!strcmp(q,"sysName"))
537		return SYSNAM;
538	if (!strcmp(q,"sysContact"))
539		return SYSCONTACT;
540	if (!strcmp(q,"sysVersion"))
541		return SYSVERSION;
542	if (!strcmp(q,"sysLocation"))
543		return SYSLOCATION;
544   if (*q=='"') {
545      if (q[ strlen(q)-1 ]=='"')
546         q[ strlen(q)-1 ]='\0'; /* trash trailing quote */
547      yylval.ptr = q+1;
548      return STRING;
549   }
550#endif
551	yylval.ptr = q;
552	return STRING;
553}
554
555void
556config_vifs_from_file()
557{
558	extern FILE *f;
559
560	order = 0;
561	numbounds = 0;
562	lineno = 0;
563
564	if ((f = fopen(configfilename, "r")) == NULL) {
565	    if (errno != ENOENT)
566		logit(LOG_ERR, errno, "can't open %s", configfilename);
567	    return;
568	}
569
570	yyparse();
571
572	fclose(f);
573}
574
575static u_int32_t
576valid_if(s)
577char *s;
578{
579	vifi_t vifi;
580	struct uvif *uv;
581
582	for (vifi=0, uv=uvifs; vifi<numvifs; vifi++, uv++)
583	    if (!strcmp(uv->uv_name, s))
584		return uv->uv_lcl_addr;
585
586	return 0;
587}
588
589static const char *
590ifconfaddr(a)
591    u_int32_t a;
592{
593    static char ifname[IFNAMSIZ];
594    struct ifaddrs *ifap, *ifa;
595
596    if (getifaddrs(&ifap) != 0)
597	return (NULL);
598
599    for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
600	if (ifa->ifa_addr->sa_family == AF_INET &&
601	    ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr == a) {
602	    strlcpy(ifname, ifa->ifa_name, sizeof(ifname));
603	    freeifaddrs(ifap);
604	    return (ifname);
605	}
606    }
607
608    freeifaddrs(ifap);
609    return (NULL);
610}
611