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 memmove(&$$, 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
warn(char * fmt,...)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
yyerror(s)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 *
next_word()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
yylex()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
config_vifs_from_file()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
valid_if(s)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 *
ifconfaddr(a)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