1 %{ 2 /* $NetBSD: cfparse.y,v 1.16 2009/04/17 16:05:43 lukem 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 */ 34 static void fatal(const char *fmt, ...) 35 __attribute__((__format__(__printf__, 1, 2))); 36 static void warn(const char *fmt, ...) 37 __attribute__((__format__(__printf__, 1, 2))); 38 static void yyerror(const char *s); 39 static char * next_word(void); 40 static int yylex(void); 41 static u_int32_t valid_if(char *s); 42 static const char * ifconfaddr(u_int32_t a); 43 int yyparse(void); 44 45 static FILE *f __attribute__((__unused__)); /* XXX egcs */ 46 extern int udp_socket; 47 const 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 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 101 conf : stmts 102 ; 103 104 stmts : /* Empty */ 105 | stmts stmt 106 ; 107 108 stmt : 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 232 tunnelmods : /* empty */ 233 | tunnelmods tunnelmod 234 ; 235 236 tunnelmod : mod 237 | SRCRT { fatal("Source-route tunnels not supported"); } 238 ; 239 240 ifmods : /* empty */ 241 | ifmods ifmod 242 ; 243 244 ifmod : 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 290 mod : 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 340 interface : ADDR { $$ = $1; } 341 | STRING { 342 $$ = valid_if($1); 343 if ($$ == 0) 344 fatal("Invalid interface name %s",$1); 345 } 346 ; 347 348 addrname : 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 362 bound : 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 377 boundary : 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 388 addrmask : ADDRMASK { $$ = $1; } 389 | ADDR { $$.addr = $1; $$.mask = 0; } 390 ; 391 %% 392 static void 393 fatal(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 405 static void 406 warn(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 418 static void 419 yyerror(s) 420 const char *s; 421 { 422 logit(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno); 423 } 424 425 static char * 426 next_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 469 static int 470 yylex() 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 555 void 556 config_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 575 static u_int32_t 576 valid_if(s) 577 char *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 589 static const char * 590 ifconfaddr(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