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