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