1 /* $KAME: ip6fw.c,v 1.13 2001/06/22 05:51:16 itojun Exp $ */ 2 3 /* 4 * Copyright (C) 1998, 1999, 2000 and 2001 WIDE Project. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the project nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 34 * Copyright (c) 1994 Ugen J.S.Antsilevich 35 * 36 * Idea and grammar partially left from: 37 * Copyright (c) 1993 Daniel Boulet 38 * 39 * Redistribution and use in source forms, with and without modification, 40 * are permitted provided that this entire comment appears intact. 41 * 42 * Redistribution in binary form may occur without any restrictions. 43 * Obviously, it would be nice if you gave credit where credit is due 44 * but requiring it would be too onerous. 45 * 46 * This software is provided ``AS IS'' without any warranties of any kind. 47 * 48 * NEW command line interface for IP firewall facility 49 * 50 * $Id: ip6fw.c,v 1.1.2.2.2.2 1999/05/14 05:13:50 shin Exp $ 51 * $FreeBSD: src/sbin/ip6fw/ip6fw.c,v 1.1.2.9 2003/04/05 10:54:51 ume Exp $ 52 * $DragonFly: src/sbin/ip6fw/ip6fw.c,v 1.9 2005/11/06 12:29:11 swildner Exp $ 53 */ 54 55 #include <sys/types.h> 56 #include <sys/queue.h> 57 #include <sys/socket.h> 58 #include <sys/sockio.h> 59 #include <sys/time.h> 60 #include <sys/ioctl.h> 61 #include <sys/wait.h> 62 63 #include <ctype.h> 64 #include <err.h> 65 #include <limits.h> 66 #include <netdb.h> 67 #include <stdio.h> 68 #include <stdlib.h> 69 #include <stdarg.h> 70 #include <string.h> 71 #include <time.h> 72 #include <unistd.h> 73 #include <errno.h> 74 #include <signal.h> 75 #include <sysexits.h> 76 77 #include <net/if.h> 78 #include <netinet/in.h> 79 #include <netinet/in_systm.h> 80 #include <netinet/ip6.h> 81 #include <netinet/icmp6.h> 82 #include <net/ip6fw/ip6_fw.h> 83 #include <netinet/tcp.h> 84 #include <arpa/inet.h> 85 86 int lineno = -1; 87 88 int s; /* main RAW socket */ 89 int do_resolv=0; /* Would try to resolv all */ 90 int do_acct=0; /* Show packet/byte count */ 91 int do_time=0; /* Show time stamps */ 92 int do_quiet=0; /* Be quiet in add and flush */ 93 int do_force=0; /* Don't ask for confirmation */ 94 95 struct icmpcode { 96 int code; 97 char *str; 98 }; 99 100 static struct icmpcode icmp6codes[] = { 101 { ICMP6_DST_UNREACH_NOROUTE, "noroute" }, 102 { ICMP6_DST_UNREACH_ADMIN, "admin" }, 103 { ICMP6_DST_UNREACH_NOTNEIGHBOR, "notneighbor" }, 104 { ICMP6_DST_UNREACH_ADDR, "addr" }, 105 { ICMP6_DST_UNREACH_NOPORT, "noport" }, 106 { 0, NULL } 107 }; 108 109 static char ntop_buf[INET6_ADDRSTRLEN]; 110 111 static void show_usage(const char *fmt, ...); 112 113 static int 114 mask_bits(u_char *m_ad, int m_len) 115 { 116 int h_num = 0,i; 117 118 for (i = 0; i < m_len; i++, m_ad++) { 119 if (*m_ad != 0xff) 120 break; 121 h_num += 8; 122 } 123 if (i < m_len) { 124 switch (*m_ad) { 125 #define MASKLEN(m, l) case m: h_num += l; break 126 MASKLEN(0xfe, 7); 127 MASKLEN(0xfc, 6); 128 MASKLEN(0xf8, 5); 129 MASKLEN(0xf0, 4); 130 MASKLEN(0xe0, 3); 131 MASKLEN(0xc0, 2); 132 MASKLEN(0x80, 1); 133 #undef MASKLEN 134 } 135 } 136 return h_num; 137 } 138 139 static int pl2m[9] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; 140 141 struct in6_addr * 142 plen2mask(int n) 143 { 144 static struct in6_addr ia; 145 u_char *p; 146 int i; 147 148 memset(&ia, 0, sizeof(struct in6_addr)); 149 p = (u_char *)&ia; 150 for (i = 0; i < 16; i++, p++, n -= 8) { 151 if (n >= 8) { 152 *p = 0xff; 153 continue; 154 } 155 *p = pl2m[n]; 156 break; 157 } 158 return &ia; 159 } 160 161 void 162 print_port(u_char prot, u_short port, const char *comma) 163 { 164 struct servent *se; 165 struct protoent *pe; 166 const char *protocol; 167 int printed = 0; 168 169 if (do_resolv) { 170 pe = getprotobynumber(prot); 171 if (pe) 172 protocol = pe->p_name; 173 else 174 protocol = NULL; 175 176 se = getservbyport(htons(port), protocol); 177 if (se) { 178 printf("%s%s", comma, se->s_name); 179 printed = 1; 180 } 181 } 182 if (!printed) 183 printf("%s%d",comma,port); 184 } 185 186 static void 187 print_iface(char *key, union ip6_fw_if *un, int byname) 188 { 189 190 if (byname) { 191 printf(" %s %s", key, un->fu_via_if.name); 192 } else if (!IN6_IS_ADDR_UNSPECIFIED(&un->fu_via_ip6)) { 193 printf(" %s %s", key, inet_ntop(AF_INET6,&un->fu_via_ip6,ntop_buf,sizeof(ntop_buf))); 194 } else 195 printf(" %s any", key); 196 } 197 198 static void 199 print_reject_code(int code) 200 { 201 struct icmpcode *ic; 202 203 for (ic = icmp6codes; ic->str; ic++) 204 if (ic->code == code) { 205 printf("%s", ic->str); 206 return; 207 } 208 printf("%u", code); 209 } 210 211 static void 212 show_ip6fw(struct ip6_fw *chain) 213 { 214 char *comma; 215 struct hostent *he; 216 struct protoent *pe; 217 int i, mb; 218 int nsp = IPV6_FW_GETNSRCP(chain); 219 int ndp = IPV6_FW_GETNDSTP(chain); 220 221 if (do_resolv) 222 setservent(1/*stayopen*/); 223 224 printf("%05u ", chain->fw_number); 225 226 if (do_acct) 227 printf("%10lu %10lu ",chain->fw_pcnt,chain->fw_bcnt); 228 229 if (do_time) 230 { 231 if (chain->timestamp) 232 { 233 char timestr[30]; 234 235 strcpy(timestr, ctime((time_t *)&chain->timestamp)); 236 *strchr(timestr, '\n') = '\0'; 237 printf("%s ", timestr); 238 } 239 else 240 printf(" "); 241 } 242 243 switch (chain->fw_flg & IPV6_FW_F_COMMAND) 244 { 245 case IPV6_FW_F_ACCEPT: 246 printf("allow"); 247 break; 248 case IPV6_FW_F_DENY: 249 printf("deny"); 250 break; 251 case IPV6_FW_F_COUNT: 252 printf("count"); 253 break; 254 case IPV6_FW_F_DIVERT: 255 printf("divert %u", chain->fw_divert_port); 256 break; 257 case IPV6_FW_F_TEE: 258 printf("tee %u", chain->fw_divert_port); 259 break; 260 case IPV6_FW_F_SKIPTO: 261 printf("skipto %u", chain->fw_skipto_rule); 262 break; 263 case IPV6_FW_F_REJECT: 264 if (chain->fw_reject_code == IPV6_FW_REJECT_RST) 265 printf("reset"); 266 else { 267 printf("unreach "); 268 print_reject_code(chain->fw_reject_code); 269 } 270 break; 271 default: 272 errx(EX_OSERR, "impossible"); 273 } 274 275 if (chain->fw_flg & IPV6_FW_F_PRN) 276 printf(" log"); 277 278 pe = getprotobynumber(chain->fw_prot); 279 if (pe) 280 printf(" %s", pe->p_name); 281 else 282 printf(" %u", chain->fw_prot); 283 284 printf(" from %s", chain->fw_flg & IPV6_FW_F_INVSRC ? "not " : ""); 285 286 mb=mask_bits((u_char *)&chain->fw_smsk,sizeof(chain->fw_smsk)); 287 if (mb==128 && do_resolv) { 288 he=gethostbyaddr(&(chain->fw_src),sizeof(chain->fw_src),AF_INET6); 289 if (he==NULL) { 290 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf))); 291 } else 292 printf("%s",he->h_name); 293 } else { 294 if (mb!=128) { 295 if (mb == 0) { 296 printf("any"); 297 } else { 298 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf))); 299 printf("/%d",mb); 300 } 301 } else 302 printf(inet_ntop(AF_INET6,&chain->fw_src,ntop_buf,sizeof(ntop_buf))); 303 } 304 305 if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { 306 comma = " "; 307 for (i = 0; i < nsp; i++) { 308 print_port(chain->fw_prot, chain->fw_pts[i], comma); 309 if (i==0 && (chain->fw_flg & IPV6_FW_F_SRNG)) 310 comma = "-"; 311 else 312 comma = ","; 313 } 314 } 315 316 printf(" to %s", chain->fw_flg & IPV6_FW_F_INVDST ? "not " : ""); 317 318 mb=mask_bits((u_char *)&chain->fw_dmsk,sizeof(chain->fw_dmsk)); 319 if (mb==128 && do_resolv) { 320 he=gethostbyaddr(&(chain->fw_dst),sizeof(chain->fw_dst),AF_INET6); 321 if (he==NULL) { 322 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf))); 323 } else 324 printf("%s",he->h_name); 325 } else { 326 if (mb!=128) { 327 if (mb == 0) { 328 printf("any"); 329 } else { 330 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf))); 331 printf("/%d",mb); 332 } 333 } else 334 printf(inet_ntop(AF_INET6,&chain->fw_dst,ntop_buf,sizeof(ntop_buf))); 335 } 336 337 if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { 338 comma = " "; 339 for (i = 0; i < ndp; i++) { 340 print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma); 341 if (i==0 && (chain->fw_flg & IPV6_FW_F_DRNG)) 342 comma = "-"; 343 else 344 comma = ","; 345 } 346 } 347 348 /* Direction */ 349 if ((chain->fw_flg & IPV6_FW_F_IN) && !(chain->fw_flg & IPV6_FW_F_OUT)) 350 printf(" in"); 351 if (!(chain->fw_flg & IPV6_FW_F_IN) && (chain->fw_flg & IPV6_FW_F_OUT)) 352 printf(" out"); 353 354 /* Handle hack for "via" backwards compatibility */ 355 if ((chain->fw_flg & IF6_FW_F_VIAHACK) == IF6_FW_F_VIAHACK) { 356 print_iface("via", 357 &chain->fw_in_if, chain->fw_flg & IPV6_FW_F_IIFNAME); 358 } else { 359 /* Receive interface specified */ 360 if (chain->fw_flg & IPV6_FW_F_IIFACE) 361 print_iface("recv", &chain->fw_in_if, 362 chain->fw_flg & IPV6_FW_F_IIFNAME); 363 /* Transmit interface specified */ 364 if (chain->fw_flg & IPV6_FW_F_OIFACE) 365 print_iface("xmit", &chain->fw_out_if, 366 chain->fw_flg & IPV6_FW_F_OIFNAME); 367 } 368 369 if (chain->fw_flg & IPV6_FW_F_FRAG) 370 printf(" frag"); 371 372 if (chain->fw_ip6opt || chain->fw_ip6nopt) { 373 int _opt_printed = 0; 374 #define PRINTOPT(x) {if (_opt_printed) printf(",");\ 375 printf(x); _opt_printed = 1;} 376 377 printf(" ip6opt "); 378 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("hopopt"); 379 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_HOPOPT) PRINTOPT("!hopopt"); 380 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("route"); 381 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ROUTE) PRINTOPT("!route"); 382 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("frag"); 383 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_FRAG) PRINTOPT("!frag"); 384 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_ESP) PRINTOPT("esp"); 385 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_ESP) PRINTOPT("!esp"); 386 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_AH) PRINTOPT("ah"); 387 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_AH) PRINTOPT("!ah"); 388 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("nonxt"); 389 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_NONXT) PRINTOPT("!nonxt"); 390 if (chain->fw_ip6opt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("opts"); 391 if (chain->fw_ip6nopt & IPV6_FW_IP6OPT_OPTS) PRINTOPT("!opts"); 392 } 393 394 if (chain->fw_ipflg & IPV6_FW_IF_TCPEST) 395 printf(" established"); 396 else if (chain->fw_tcpf == IPV6_FW_TCPF_SYN && 397 chain->fw_tcpnf == IPV6_FW_TCPF_ACK) 398 printf(" setup"); 399 else if (chain->fw_tcpf || chain->fw_tcpnf) { 400 int _flg_printed = 0; 401 #define PRINTFLG(x) {if (_flg_printed) printf(",");\ 402 printf(x); _flg_printed = 1;} 403 404 printf(" tcpflg "); 405 if (chain->fw_tcpf & IPV6_FW_TCPF_FIN) PRINTFLG("fin"); 406 if (chain->fw_tcpnf & IPV6_FW_TCPF_FIN) PRINTFLG("!fin"); 407 if (chain->fw_tcpf & IPV6_FW_TCPF_SYN) PRINTFLG("syn"); 408 if (chain->fw_tcpnf & IPV6_FW_TCPF_SYN) PRINTFLG("!syn"); 409 if (chain->fw_tcpf & IPV6_FW_TCPF_RST) PRINTFLG("rst"); 410 if (chain->fw_tcpnf & IPV6_FW_TCPF_RST) PRINTFLG("!rst"); 411 if (chain->fw_tcpf & IPV6_FW_TCPF_PSH) PRINTFLG("psh"); 412 if (chain->fw_tcpnf & IPV6_FW_TCPF_PSH) PRINTFLG("!psh"); 413 if (chain->fw_tcpf & IPV6_FW_TCPF_ACK) PRINTFLG("ack"); 414 if (chain->fw_tcpnf & IPV6_FW_TCPF_ACK) PRINTFLG("!ack"); 415 if (chain->fw_tcpf & IPV6_FW_TCPF_URG) PRINTFLG("urg"); 416 if (chain->fw_tcpnf & IPV6_FW_TCPF_URG) PRINTFLG("!urg"); 417 } 418 if (chain->fw_flg & IPV6_FW_F_ICMPBIT) { 419 int type_index; 420 int first = 1; 421 422 printf(" icmptype"); 423 424 for (type_index = 0; type_index < IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index) 425 if (chain->fw_icmp6types[type_index / (sizeof(unsigned) * 8)] & 426 (1U << (type_index % (sizeof(unsigned) * 8)))) { 427 printf("%c%d", first == 1 ? ' ' : ',', type_index); 428 first = 0; 429 } 430 } 431 printf("\n"); 432 if (do_resolv) 433 endservent(); 434 } 435 436 void 437 list(int ac, char **av) 438 { 439 struct ip6_fw *r, *rules, *n; 440 int l,i; 441 unsigned long rulenum; 442 int nalloc, bytes, maxbytes; 443 444 /* extract rules from kernel, resizing array as necessary */ 445 rules = NULL; 446 nalloc = sizeof *rules; 447 bytes = nalloc; 448 maxbytes = 65536 * sizeof *rules; 449 while (bytes >= nalloc) { 450 if ((n = realloc(rules, nalloc * 2 + 200)) == NULL) 451 err(EX_OSERR, "realloc"); 452 bytes = nalloc = nalloc * 2 + 200; 453 rules = n; 454 i = getsockopt(s, IPPROTO_IPV6, IPV6_FW_GET, rules, &bytes); 455 if ((i < 0 && errno != EINVAL) || nalloc > maxbytes) 456 err(EX_OSERR, "getsockopt(IPV6_FW_GET)"); 457 } 458 if (!ac) { 459 /* display all rules */ 460 for (r = rules, l = bytes; l >= sizeof rules[0]; 461 r++, l-=sizeof rules[0]) 462 show_ip6fw(r); 463 } 464 else { 465 /* display specific rules requested on command line */ 466 int exitval = 0; 467 468 while (ac--) { 469 char *endptr; 470 int seen; 471 472 /* convert command line rule # */ 473 rulenum = strtoul(*av++, &endptr, 10); 474 if (*endptr) { 475 exitval = 1; 476 warn("invalid rule number: %s", av[-1]); 477 continue; 478 } 479 seen = 0; 480 for (r = rules, l = bytes; 481 l >= sizeof rules[0] && r->fw_number <= rulenum; 482 r++, l-=sizeof rules[0]) 483 if (rulenum == r->fw_number) { 484 show_ip6fw(r); 485 seen = 1; 486 } 487 if (!seen) { 488 exitval = 1; 489 warnx("rule %lu does not exist", rulenum); 490 } 491 } 492 if (exitval != 0) 493 exit(exitval); 494 } 495 } 496 497 static void 498 show_usage(const char *fmt, ...) 499 { 500 if (fmt) { 501 char buf[100]; 502 va_list args; 503 504 va_start(args, fmt); 505 vsnprintf(buf, sizeof(buf), fmt, args); 506 va_end(args); 507 warnx("error: %s", buf); 508 } 509 fprintf(stderr, "usage: ip6fw [options]\n" 510 " flush\n" 511 " add [number] rule\n" 512 " delete number ...\n" 513 " list [number ...]\n" 514 " show [number ...]\n" 515 " zero [number ...]\n" 516 " rule: action proto src dst extras...\n" 517 " action:\n" 518 " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n" 519 " reset|count|skipto num} [log]\n" 520 " proto: {ipv6|tcp|udp|ipv6-icmp|<number>}\n" 521 " src: from [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n" 522 " dst: to [not] {any|ipv6[/prefixlen]} [{port|port-port},[port],...]\n" 523 " extras:\n" 524 " fragment (may not be used with ports or tcpflags)\n" 525 " in\n" 526 " out\n" 527 " {xmit|recv|via} {iface|ipv6|any}\n" 528 " {established|setup}\n" 529 " tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n" 530 " ipv6options [!]{hopopt|route|frag|esp|ah|nonxt|opts},...\n" 531 " icmptypes {type[,type]}...\n"); 532 533 exit(1); 534 } 535 536 static int 537 lookup_host (const char *host, u_char *addr, int family) 538 { 539 struct hostent *he; 540 541 if (inet_pton(family, host, addr) != 1) { 542 if ((he = gethostbyname2(host, family)) == NULL) 543 return(-1); 544 memcpy(addr, he->h_addr_list[0], he->h_length); 545 } 546 return(0); 547 } 548 549 void 550 fill_ip6(struct in6_addr *ipno, struct in6_addr *mask, int *acp, char ***avp) 551 { 552 int ac = *acp; 553 char **av = *avp; 554 char *p = 0, md = 0; 555 int i; 556 557 if (ac && !strncmp(*av,"any",strlen(*av))) { 558 *ipno = *mask = in6addr_any; av++; ac--; 559 } else { 560 p = strchr(*av, '/'); 561 if (p) { 562 md = *p; 563 *p++ = '\0'; 564 } 565 566 if (lookup_host(*av, (u_char *)ipno, AF_INET6) != 0) 567 show_usage("hostname ``%s'' unknown", *av); 568 switch (md) { 569 case '/': 570 if (atoi(p) == 0) { 571 *mask = in6addr_any; 572 } else if (atoi(p) > 128) { 573 show_usage("bad width ``%s''", p); 574 } else { 575 *mask = *(plen2mask(atoi(p))); 576 } 577 break; 578 default: 579 *mask = *(plen2mask(128)); 580 break; 581 } 582 for (i = 0; i < sizeof(*ipno); i++) 583 ipno->s6_addr[i] &= mask->s6_addr[i]; 584 av++; 585 ac--; 586 } 587 *acp = ac; 588 *avp = av; 589 } 590 591 static void 592 fill_reject_code6(u_short *codep, char *str) 593 { 594 struct icmpcode *ic; 595 u_long val; 596 char *s; 597 598 val = strtoul(str, &s, 0); 599 if (s != str && *s == '\0' && val < 0x100) { 600 *codep = val; 601 return; 602 } 603 for (ic = icmp6codes; ic->str; ic++) 604 if (!strcasecmp(str, ic->str)) { 605 *codep = ic->code; 606 return; 607 } 608 show_usage("unknown ICMP6 unreachable code ``%s''", str); 609 } 610 611 static void 612 add_port(u_short *cnt, u_short *ptr, u_short off, u_short port) 613 { 614 if (off + *cnt >= IPV6_FW_MAX_PORTS) 615 errx(1, "too many ports (max is %d)", IPV6_FW_MAX_PORTS); 616 ptr[off+*cnt] = port; 617 (*cnt)++; 618 } 619 620 static int 621 lookup_port(const char *arg, int test, int nodash) 622 { 623 int val; 624 char *earg, buf[32]; 625 struct servent *s; 626 627 snprintf(buf, sizeof(buf), "%s", arg); 628 buf[strcspn(arg, nodash ? "-," : ",")] = 0; 629 val = (int) strtoul(buf, &earg, 0); 630 if (!*buf || *earg) { 631 setservent(1); 632 if ((s = getservbyname(buf, NULL))) { 633 val = htons(s->s_port); 634 } else { 635 if (!test) { 636 errx(1, "unknown port ``%s''", arg); 637 } 638 val = -1; 639 } 640 } else { 641 if (val < 0 || val > 0xffff) { 642 if (!test) { 643 errx(1, "port ``%s'' out of range", arg); 644 } 645 val = -1; 646 } 647 } 648 return(val); 649 } 650 651 int 652 fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg) 653 { 654 char *s; 655 int initial_range = 0; 656 657 s = arg + strcspn(arg, "-,"); /* first port name can't have a dash */ 658 if (*s == '-') { 659 *s++ = '\0'; 660 if (strchr(arg, ',')) 661 errx(1, "port range must be first in list"); 662 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000); 663 arg = s; 664 s = strchr(arg,','); 665 if (s) 666 *s++ = '\0'; 667 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff); 668 arg = s; 669 initial_range = 1; 670 } 671 while (arg != NULL) { 672 s = strchr(arg,','); 673 if (s) 674 *s++ = '\0'; 675 add_port(cnt, ptr, off, lookup_port(arg, 0, 0)); 676 arg = s; 677 } 678 return initial_range; 679 } 680 681 void 682 fill_tcpflag(u_char *set, u_char *reset, char **vp) 683 { 684 char *p = *vp,*q; 685 u_char *d; 686 687 while (p && *p) { 688 struct tpcflags { 689 char * name; 690 u_char value; 691 } flags[] = { 692 { "syn", IPV6_FW_TCPF_SYN }, 693 { "fin", IPV6_FW_TCPF_FIN }, 694 { "ack", IPV6_FW_TCPF_ACK }, 695 { "psh", IPV6_FW_TCPF_PSH }, 696 { "rst", IPV6_FW_TCPF_RST }, 697 { "urg", IPV6_FW_TCPF_URG } 698 }; 699 int i; 700 701 if (*p == '!') { 702 p++; 703 d = reset; 704 } else { 705 d = set; 706 } 707 q = strchr(p, ','); 708 if (q) 709 *q++ = '\0'; 710 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) 711 if (!strncmp(p, flags[i].name, strlen(p))) { 712 *d |= flags[i].value; 713 break; 714 } 715 if (i == sizeof(flags) / sizeof(flags[0])) 716 show_usage("invalid tcp flag ``%s''", p); 717 p = q; 718 } 719 } 720 721 static void 722 fill_ip6opt(u_char *set, u_char *reset, char **vp) 723 { 724 char *p = *vp,*q; 725 u_char *d; 726 727 while (p && *p) { 728 if (*p == '!') { 729 p++; 730 d = reset; 731 } else { 732 d = set; 733 } 734 q = strchr(p, ','); 735 if (q) 736 *q++ = '\0'; 737 if (!strncmp(p,"hopopt",strlen(p))) *d |= IPV6_FW_IP6OPT_HOPOPT; 738 if (!strncmp(p,"route",strlen(p))) *d |= IPV6_FW_IP6OPT_ROUTE; 739 if (!strncmp(p,"frag",strlen(p))) *d |= IPV6_FW_IP6OPT_FRAG; 740 if (!strncmp(p,"esp",strlen(p))) *d |= IPV6_FW_IP6OPT_ESP; 741 if (!strncmp(p,"ah",strlen(p))) *d |= IPV6_FW_IP6OPT_AH; 742 if (!strncmp(p,"nonxt",strlen(p))) *d |= IPV6_FW_IP6OPT_NONXT; 743 if (!strncmp(p,"opts",strlen(p))) *d |= IPV6_FW_IP6OPT_OPTS; 744 p = q; 745 } 746 } 747 748 void 749 fill_icmptypes(unsigned *types, char **vp, u_short *fw_flg) 750 { 751 char *c = *vp; 752 753 while (*c) 754 { 755 unsigned long icmptype; 756 757 if ( *c == ',' ) 758 ++c; 759 760 icmptype = strtoul(c, &c, 0); 761 762 if ( *c != ',' && *c != '\0' ) 763 show_usage("invalid ICMP6 type"); 764 765 if (icmptype >= IPV6_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8) 766 show_usage("ICMP6 type out of range"); 767 768 types[icmptype / (sizeof(unsigned) * 8)] |= 769 1 << (icmptype % (sizeof(unsigned) * 8)); 770 *fw_flg |= IPV6_FW_F_ICMPBIT; 771 } 772 } 773 774 void 775 delete(int ac, char **av) 776 { 777 struct ip6_fw rule; 778 int i; 779 int exitval = 0; 780 781 memset(&rule, 0, sizeof rule); 782 783 av++; ac--; 784 785 /* Rule number */ 786 while (ac && isdigit(**av)) { 787 rule.fw_number = atoi(*av); av++; ac--; 788 i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_DEL, &rule, sizeof rule); 789 if (i) { 790 exitval = 1; 791 warn("rule %u: setsockopt(%s)", rule.fw_number, "IPV6_FW_DEL"); 792 } 793 } 794 if (exitval != 0) 795 exit(exitval); 796 } 797 798 static void 799 verify_interface(union ip6_fw_if *ifu) 800 { 801 struct ifreq ifr; 802 803 strlcpy(ifr.ifr_name, ifu->fu_via_if.name, sizeof(ifr.ifr_name)); 804 805 if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) 806 warnx("warning: interface ``%s'' does not exist", ifr.ifr_name); 807 } 808 809 static void 810 fill_iface(char *which, union ip6_fw_if *ifu, int *byname, int ac, char *arg) 811 { 812 if (!ac) 813 show_usage("missing argument for ``%s''", which); 814 815 /* Parse the interface or address */ 816 if (!strcmp(arg, "any")) { 817 ifu->fu_via_ip6 = in6addr_any; 818 *byname = 0; 819 } else if (!isdigit(*arg)) { 820 *byname = 1; 821 strlcpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name)); 822 /* 823 * We assume that strings containing '*', '?', or '[' 824 * are ment to be shell pattern. 825 */ 826 if (strpbrk(arg, "*?[") != NULL) { 827 ifu->fu_via_if.glob = 1; 828 } else { 829 ifu->fu_via_if.glob = 0; 830 verify_interface(ifu); 831 } 832 } else if (inet_pton(AF_INET6, arg, &ifu->fu_via_ip6) != 1) { 833 show_usage("bad ip6 address ``%s''", arg); 834 } else 835 *byname = 0; 836 } 837 838 static void 839 add(int ac, char **av) 840 { 841 struct ip6_fw rule; 842 int i; 843 u_char proto; 844 struct protoent *pe; 845 int saw_xmrc = 0, saw_via = 0; 846 847 memset(&rule, 0, sizeof rule); 848 849 av++; ac--; 850 851 /* Rule number */ 852 if (ac && isdigit(**av)) { 853 rule.fw_number = atoi(*av); av++; ac--; 854 } 855 856 /* Action */ 857 if (ac == 0) 858 show_usage("missing action"); 859 if (!strncmp(*av,"accept",strlen(*av)) 860 || !strncmp(*av,"pass",strlen(*av)) 861 || !strncmp(*av,"allow",strlen(*av)) 862 || !strncmp(*av,"permit",strlen(*av))) { 863 rule.fw_flg |= IPV6_FW_F_ACCEPT; av++; ac--; 864 } else if (!strncmp(*av,"count",strlen(*av))) { 865 rule.fw_flg |= IPV6_FW_F_COUNT; av++; ac--; 866 } 867 #if 0 868 else if (!strncmp(*av,"divert",strlen(*av))) { 869 rule.fw_flg |= IPV6_FW_F_DIVERT; av++; ac--; 870 if (!ac) 871 show_usage("missing divert port"); 872 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; 873 if (rule.fw_divert_port == 0) { 874 struct servent *s; 875 setservent(1); 876 s = getservbyname(av[-1], "divert"); 877 if (s != NULL) 878 rule.fw_divert_port = ntohs(s->s_port); 879 else 880 show_usage("illegal divert port"); 881 } 882 } else if (!strncmp(*av,"tee",strlen(*av))) { 883 rule.fw_flg |= IPV6_FW_F_TEE; av++; ac--; 884 if (!ac) 885 show_usage("missing divert port"); 886 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; 887 if (rule.fw_divert_port == 0) { 888 struct servent *s; 889 setservent(1); 890 s = getservbyname(av[-1], "divert"); 891 if (s != NULL) 892 rule.fw_divert_port = ntohs(s->s_port); 893 else 894 show_usage("illegal divert port"); 895 } 896 } 897 #endif 898 else if (!strncmp(*av,"skipto",strlen(*av))) { 899 rule.fw_flg |= IPV6_FW_F_SKIPTO; av++; ac--; 900 if (!ac) 901 show_usage("missing skipto rule number"); 902 rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--; 903 } else if ((!strncmp(*av,"deny",strlen(*av)) 904 || !strncmp(*av,"drop",strlen(*av)))) { 905 rule.fw_flg |= IPV6_FW_F_DENY; av++; ac--; 906 } else if (!strncmp(*av,"reject",strlen(*av))) { 907 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--; 908 rule.fw_reject_code = ICMP6_DST_UNREACH_NOROUTE; 909 } else if (!strncmp(*av,"reset",strlen(*av))) { 910 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--; 911 rule.fw_reject_code = IPV6_FW_REJECT_RST; /* check TCP later */ 912 } else if (!strncmp(*av,"unreach",strlen(*av))) { 913 rule.fw_flg |= IPV6_FW_F_REJECT; av++; ac--; 914 fill_reject_code6(&rule.fw_reject_code, *av); av++; ac--; 915 } else { 916 show_usage("invalid action ``%s''", *av); 917 } 918 919 /* [log] */ 920 if (ac && !strncmp(*av,"log",strlen(*av))) { 921 rule.fw_flg |= IPV6_FW_F_PRN; av++; ac--; 922 } 923 924 /* protocol */ 925 if (ac == 0) 926 show_usage("missing protocol"); 927 if ((proto = atoi(*av)) > 0) { 928 rule.fw_prot = proto; av++; ac--; 929 } else if (!strncmp(*av,"all",strlen(*av))) { 930 rule.fw_prot = IPPROTO_IPV6; av++; ac--; 931 } else if ((pe = getprotobyname(*av)) != NULL) { 932 rule.fw_prot = pe->p_proto; av++; ac--; 933 } else { 934 show_usage("invalid protocol ``%s''", *av); 935 } 936 937 if (rule.fw_prot != IPPROTO_TCP 938 && (rule.fw_flg & IPV6_FW_F_COMMAND) == IPV6_FW_F_REJECT 939 && rule.fw_reject_code == IPV6_FW_REJECT_RST) 940 show_usage("``reset'' is only valid for tcp packets"); 941 942 /* from */ 943 if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; } 944 else 945 show_usage("missing ``from''"); 946 947 if (ac && !strncmp(*av,"not",strlen(*av))) { 948 rule.fw_flg |= IPV6_FW_F_INVSRC; 949 av++; ac--; 950 } 951 if (!ac) 952 show_usage("missing arguments"); 953 954 fill_ip6(&rule.fw_src, &rule.fw_smsk, &ac, &av); 955 956 if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) { 957 u_short nports = 0; 958 959 if (fill_port(&nports, rule.fw_pts, 0, *av)) 960 rule.fw_flg |= IPV6_FW_F_SRNG; 961 IPV6_FW_SETNSRCP(&rule, nports); 962 av++; ac--; 963 } 964 965 /* to */ 966 if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; } 967 else 968 show_usage("missing ``to''"); 969 970 if (ac && !strncmp(*av,"not",strlen(*av))) { 971 rule.fw_flg |= IPV6_FW_F_INVDST; 972 av++; ac--; 973 } 974 if (!ac) 975 show_usage("missing arguments"); 976 977 fill_ip6(&rule.fw_dst, &rule.fw_dmsk, &ac, &av); 978 979 if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) { 980 u_short nports = 0; 981 982 if (fill_port(&nports, 983 rule.fw_pts, IPV6_FW_GETNSRCP(&rule), *av)) 984 rule.fw_flg |= IPV6_FW_F_DRNG; 985 IPV6_FW_SETNDSTP(&rule, nports); 986 av++; ac--; 987 } 988 989 if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP) 990 && (IPV6_FW_GETNSRCP(&rule) || IPV6_FW_GETNDSTP(&rule))) { 991 show_usage("only TCP and UDP protocols are valid" 992 " with port specifications"); 993 } 994 995 while (ac) { 996 if (!strncmp(*av,"in",strlen(*av))) { 997 rule.fw_flg |= IPV6_FW_F_IN; 998 av++; ac--; continue; 999 } 1000 if (!strncmp(*av,"out",strlen(*av))) { 1001 rule.fw_flg |= IPV6_FW_F_OUT; 1002 av++; ac--; continue; 1003 } 1004 if (ac && !strncmp(*av,"xmit",strlen(*av))) { 1005 union ip6_fw_if ifu; 1006 int byname; 1007 1008 if (saw_via) { 1009 badviacombo: 1010 show_usage("``via'' is incompatible" 1011 " with ``xmit'' and ``recv''"); 1012 } 1013 saw_xmrc = 1; 1014 av++; ac--; 1015 fill_iface("xmit", &ifu, &byname, ac, *av); 1016 rule.fw_out_if = ifu; 1017 rule.fw_flg |= IPV6_FW_F_OIFACE; 1018 if (byname) 1019 rule.fw_flg |= IPV6_FW_F_OIFNAME; 1020 av++; ac--; continue; 1021 } 1022 if (ac && !strncmp(*av,"recv",strlen(*av))) { 1023 union ip6_fw_if ifu; 1024 int byname; 1025 1026 if (saw_via) 1027 goto badviacombo; 1028 saw_xmrc = 1; 1029 av++; ac--; 1030 fill_iface("recv", &ifu, &byname, ac, *av); 1031 rule.fw_in_if = ifu; 1032 rule.fw_flg |= IPV6_FW_F_IIFACE; 1033 if (byname) 1034 rule.fw_flg |= IPV6_FW_F_IIFNAME; 1035 av++; ac--; continue; 1036 } 1037 if (ac && !strncmp(*av,"via",strlen(*av))) { 1038 union ip6_fw_if ifu; 1039 int byname = 0; 1040 1041 if (saw_xmrc) 1042 goto badviacombo; 1043 saw_via = 1; 1044 av++; ac--; 1045 fill_iface("via", &ifu, &byname, ac, *av); 1046 rule.fw_out_if = rule.fw_in_if = ifu; 1047 if (byname) 1048 rule.fw_flg |= 1049 (IPV6_FW_F_IIFNAME | IPV6_FW_F_OIFNAME); 1050 av++; ac--; continue; 1051 } 1052 if (!strncmp(*av,"fragment",strlen(*av))) { 1053 rule.fw_flg |= IPV6_FW_F_FRAG; 1054 av++; ac--; continue; 1055 } 1056 if (!strncmp(*av,"ipv6options",strlen(*av))) { 1057 av++; ac--; 1058 if (!ac) 1059 show_usage("missing argument" 1060 " for ``ipv6options''"); 1061 fill_ip6opt(&rule.fw_ip6opt, &rule.fw_ip6nopt, av); 1062 av++; ac--; continue; 1063 } 1064 if (rule.fw_prot == IPPROTO_TCP) { 1065 if (!strncmp(*av,"established",strlen(*av))) { 1066 rule.fw_ipflg |= IPV6_FW_IF_TCPEST; 1067 av++; ac--; continue; 1068 } 1069 if (!strncmp(*av,"setup",strlen(*av))) { 1070 rule.fw_tcpf |= IPV6_FW_TCPF_SYN; 1071 rule.fw_tcpnf |= IPV6_FW_TCPF_ACK; 1072 av++; ac--; continue; 1073 } 1074 if (!strncmp(*av,"tcpflags",strlen(*av))) { 1075 av++; ac--; 1076 if (!ac) 1077 show_usage("missing argument" 1078 " for ``tcpflags''"); 1079 fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av); 1080 av++; ac--; continue; 1081 } 1082 } 1083 if (rule.fw_prot == IPPROTO_ICMPV6) { 1084 if (!strncmp(*av,"icmptypes",strlen(*av))) { 1085 av++; ac--; 1086 if (!ac) 1087 show_usage("missing argument" 1088 " for ``icmptypes''"); 1089 fill_icmptypes(rule.fw_icmp6types, 1090 av, &rule.fw_flg); 1091 av++; ac--; continue; 1092 } 1093 } 1094 show_usage("unknown argument ``%s''", *av); 1095 } 1096 1097 /* No direction specified -> do both directions */ 1098 if (!(rule.fw_flg & (IPV6_FW_F_OUT|IPV6_FW_F_IN))) 1099 rule.fw_flg |= (IPV6_FW_F_OUT|IPV6_FW_F_IN); 1100 1101 /* Sanity check interface check, but handle "via" case separately */ 1102 if (saw_via) { 1103 if (rule.fw_flg & IPV6_FW_F_IN) 1104 rule.fw_flg |= IPV6_FW_F_IIFACE; 1105 if (rule.fw_flg & IPV6_FW_F_OUT) 1106 rule.fw_flg |= IPV6_FW_F_OIFACE; 1107 } else if ((rule.fw_flg & IPV6_FW_F_OIFACE) && (rule.fw_flg & IPV6_FW_F_IN)) 1108 show_usage("can't check xmit interface of incoming packets"); 1109 1110 /* frag may not be used in conjunction with ports or TCP flags */ 1111 if (rule.fw_flg & IPV6_FW_F_FRAG) { 1112 if (rule.fw_tcpf || rule.fw_tcpnf) 1113 show_usage("can't mix 'frag' and tcpflags"); 1114 1115 if (rule.fw_nports) 1116 show_usage("can't mix 'frag' and port specifications"); 1117 } 1118 1119 if (!do_quiet) 1120 show_ip6fw(&rule); 1121 i = setsockopt(s, IPPROTO_IPV6, IPV6_FW_ADD, &rule, sizeof rule); 1122 if (i) 1123 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ADD"); 1124 } 1125 1126 static void 1127 zero (int ac, char **av) 1128 { 1129 av++; ac--; 1130 1131 if (!ac) { 1132 /* clear all entries */ 1133 if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_ZERO,NULL,0)<0) 1134 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_ZERO"); 1135 if (!do_quiet) 1136 printf("Accounting cleared.\n"); 1137 } else { 1138 struct ip6_fw rule; 1139 int failed = 0; 1140 1141 memset(&rule, 0, sizeof rule); 1142 while (ac) { 1143 /* Rule number */ 1144 if (isdigit(**av)) { 1145 rule.fw_number = atoi(*av); av++; ac--; 1146 if (setsockopt(s, IPPROTO_IPV6, 1147 IPV6_FW_ZERO, &rule, sizeof rule)) { 1148 warn("rule %u: setsockopt(%s)", rule.fw_number, 1149 "IPV6_FW_ZERO"); 1150 failed = 1; 1151 } 1152 else if (!do_quiet) 1153 printf("Entry %d cleared\n", 1154 rule.fw_number); 1155 } else 1156 show_usage("invalid rule number ``%s''", *av); 1157 } 1158 if (failed != 0) 1159 exit(failed); 1160 } 1161 } 1162 1163 int 1164 ip6fw_main(int ac, char **av) 1165 { 1166 int ch; 1167 extern int optind; 1168 1169 /* init optind to 1 */ 1170 optind = 1; 1171 1172 if ( ac == 1 ) { 1173 show_usage(NULL); 1174 } 1175 1176 /* Set the force flag for non-interactive processes */ 1177 do_force = !isatty(STDIN_FILENO); 1178 1179 while ((ch = getopt(ac, av ,"afqtN")) != -1) 1180 switch(ch) { 1181 case 'a': 1182 do_acct=1; 1183 break; 1184 case 'f': 1185 do_force=1; 1186 break; 1187 case 'q': 1188 do_quiet=1; 1189 break; 1190 case 't': 1191 do_time=1; 1192 break; 1193 case 'N': 1194 do_resolv=1; 1195 break; 1196 default: 1197 show_usage(NULL); 1198 } 1199 1200 ac -= optind; 1201 if (*(av+=optind)==NULL) { 1202 show_usage("Bad arguments"); 1203 } 1204 1205 if (!strncmp(*av, "add", strlen(*av))) { 1206 add(ac,av); 1207 } else if (!strncmp(*av, "delete", strlen(*av))) { 1208 delete(ac,av); 1209 } else if (!strncmp(*av, "flush", strlen(*av))) { 1210 int do_flush = 0; 1211 1212 if ( do_force || do_quiet ) 1213 do_flush = 1; 1214 else { 1215 int c; 1216 1217 /* Ask the user */ 1218 printf("Are you sure? [yn] "); 1219 do { 1220 fflush(stdout); 1221 c = toupper(getc(stdin)); 1222 while (c != '\n' && getc(stdin) != '\n') 1223 if (feof(stdin)) 1224 return (0); 1225 } while (c != 'Y' && c != 'N'); 1226 printf("\n"); 1227 if (c == 'Y') 1228 do_flush = 1; 1229 } 1230 if ( do_flush ) { 1231 if (setsockopt(s,IPPROTO_IPV6,IPV6_FW_FLUSH,NULL,0) < 0) 1232 err(EX_UNAVAILABLE, "setsockopt(%s)", "IPV6_FW_FLUSH"); 1233 if (!do_quiet) 1234 printf("Flushed all rules.\n"); 1235 } 1236 } else if (!strncmp(*av, "zero", strlen(*av))) { 1237 zero(ac,av); 1238 } else if (!strncmp(*av, "print", strlen(*av))) { 1239 list(--ac,++av); 1240 } else if (!strncmp(*av, "list", strlen(*av))) { 1241 list(--ac,++av); 1242 } else if (!strncmp(*av, "show", strlen(*av))) { 1243 do_acct++; 1244 list(--ac,++av); 1245 } else { 1246 show_usage("Bad arguments"); 1247 } 1248 return 0; 1249 } 1250 1251 int 1252 main(int ac, char **av) 1253 { 1254 #define MAX_ARGS 32 1255 #define WHITESP " \t\f\v\n\r" 1256 char buf[BUFSIZ]; 1257 char *a, *p, *args[MAX_ARGS], *cmd = NULL; 1258 char linename[10]; 1259 int i, c, lineno, qflag, pflag, status; 1260 FILE *f = NULL; 1261 pid_t preproc = 0; 1262 1263 s = socket( AF_INET6, SOCK_RAW, IPPROTO_RAW ); 1264 if ( s < 0 ) 1265 err(EX_UNAVAILABLE, "socket"); 1266 1267 setbuf(stdout,0); 1268 1269 /* 1270 * Only interpret the last command line argument as a file to 1271 * be preprocessed if it is specified as an absolute pathname. 1272 */ 1273 1274 if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) { 1275 qflag = pflag = i = 0; 1276 lineno = 0; 1277 1278 while ((c = getopt(ac, av, "D:U:p:q")) != -1) 1279 switch(c) { 1280 case 'D': 1281 if (!pflag) 1282 errx(EX_USAGE, "-D requires -p"); 1283 if (i > MAX_ARGS - 2) 1284 errx(EX_USAGE, 1285 "too many -D or -U options"); 1286 args[i++] = "-D"; 1287 args[i++] = optarg; 1288 break; 1289 1290 case 'U': 1291 if (!pflag) 1292 errx(EX_USAGE, "-U requires -p"); 1293 if (i > MAX_ARGS - 2) 1294 errx(EX_USAGE, 1295 "too many -D or -U options"); 1296 args[i++] = "-U"; 1297 args[i++] = optarg; 1298 break; 1299 1300 case 'p': 1301 pflag = 1; 1302 cmd = optarg; 1303 args[0] = cmd; 1304 i = 1; 1305 break; 1306 1307 case 'q': 1308 qflag = 1; 1309 break; 1310 1311 default: 1312 show_usage(NULL); 1313 } 1314 1315 av += optind; 1316 ac -= optind; 1317 if (ac != 1) 1318 show_usage("extraneous filename arguments"); 1319 1320 if ((f = fopen(av[0], "r")) == NULL) 1321 err(EX_UNAVAILABLE, "fopen: %s", av[0]); 1322 1323 if (pflag) { 1324 /* pipe through preprocessor (cpp or m4) */ 1325 int pipedes[2]; 1326 1327 args[i] = 0; 1328 1329 if (pipe(pipedes) == -1) 1330 err(EX_OSERR, "cannot create pipe"); 1331 1332 switch((preproc = fork())) { 1333 case -1: 1334 err(EX_OSERR, "cannot fork"); 1335 1336 case 0: 1337 /* child */ 1338 if (dup2(fileno(f), 0) == -1 || 1339 dup2(pipedes[1], 1) == -1) 1340 err(EX_OSERR, "dup2()"); 1341 fclose(f); 1342 close(pipedes[1]); 1343 close(pipedes[0]); 1344 execvp(cmd, args); 1345 err(EX_OSERR, "execvp(%s) failed", cmd); 1346 1347 default: 1348 /* parent */ 1349 fclose(f); 1350 close(pipedes[1]); 1351 if ((f = fdopen(pipedes[0], "r")) == NULL) { 1352 int savederrno = errno; 1353 1354 kill(preproc, SIGTERM); 1355 errno = savederrno; 1356 err(EX_OSERR, "fdopen()"); 1357 } 1358 } 1359 } 1360 1361 while (fgets(buf, BUFSIZ, f)) { 1362 lineno++; 1363 sprintf(linename, "Line %d", lineno); 1364 args[0] = linename; 1365 1366 if (*buf == '#') 1367 continue; 1368 if ((p = strchr(buf, '#')) != NULL) 1369 *p = '\0'; 1370 i=1; 1371 if (qflag) args[i++]="-q"; 1372 for (a = strtok(buf, WHITESP); 1373 a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++) 1374 args[i] = a; 1375 if (i == (qflag? 2: 1)) 1376 continue; 1377 if (i == MAX_ARGS) 1378 errx(EX_USAGE, "%s: too many arguments", linename); 1379 args[i] = NULL; 1380 1381 ip6fw_main(i, args); 1382 } 1383 fclose(f); 1384 if (pflag) { 1385 if (waitpid(preproc, &status, 0) != -1) { 1386 if (WIFEXITED(status)) { 1387 if (WEXITSTATUS(status) != EX_OK) 1388 errx(EX_UNAVAILABLE, 1389 "preprocessor exited with status %d", 1390 WEXITSTATUS(status)); 1391 } else if (WIFSIGNALED(status)) { 1392 errx(EX_UNAVAILABLE, 1393 "preprocessor exited with signal %d", 1394 WTERMSIG(status)); 1395 } 1396 } 1397 } 1398 } else 1399 ip6fw_main(ac,av); 1400 return 0; 1401 } 1402