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