]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ipfw/ipfw.c
Disallow ipfw "tee" rules until it is actually implemented.
[FreeBSD/FreeBSD.git] / sbin / ipfw / ipfw.c
1 /*
2  * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
3  * Copyright (c) 1994 Ugen J.S.Antsilevich
4  *
5  * Idea and grammar partially left from:
6  * Copyright (c) 1993 Daniel Boulet
7  *
8  * Redistribution and use in source forms, with and without modification,
9  * are permitted provided that this entire comment appears intact.
10  *
11  * Redistribution in binary form may occur without any restrictions.
12  * Obviously, it would be nice if you gave credit where credit is due
13  * but requiring it would be too onerous.
14  *
15  * This software is provided ``AS IS'' without any warranties of any kind.
16  *
17  * NEW command line interface for IP firewall facility
18  *
19  * $Id: ipfw.c,v 1.61 1998/11/23 10:54:28 joerg Exp $
20  *
21  */
22
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 #include <sys/sockio.h>
27 #include <sys/time.h>
28 #include <sys/wait.h>
29
30 #include <ctype.h>
31 #include <err.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <netdb.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <string.h>
40 #include <sysexits.h>
41 #include <time.h>
42 #include <unistd.h>
43
44 #include <net/if.h>
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip_var.h>
48 #include <netinet/ip.h>
49 #include <netinet/ip_icmp.h>
50 #include <netinet/ip_fw.h>
51 #include <netinet/tcp.h>
52 #include <arpa/inet.h>
53
54 int             lineno = -1;
55
56 int             s;                              /* main RAW socket         */
57 int             do_resolv=0;                    /* Would try to resolv all */
58 int             do_acct=0;                      /* Show packet/byte count  */
59 int             do_time=0;                      /* Show time stamps        */
60 int             do_quiet=0;                     /* Be quiet in add and flush  */
61 int             do_force=0;                     /* Don't ask for confirmation */
62
63 struct icmpcode {
64         int     code;
65         char    *str;
66 };
67
68 static struct icmpcode icmpcodes[] = {
69       { ICMP_UNREACH_NET,               "net" },
70       { ICMP_UNREACH_HOST,              "host" },
71       { ICMP_UNREACH_PROTOCOL,          "protocol" },
72       { ICMP_UNREACH_PORT,              "port" },
73       { ICMP_UNREACH_NEEDFRAG,          "needfrag" },
74       { ICMP_UNREACH_SRCFAIL,           "srcfail" },
75       { ICMP_UNREACH_NET_UNKNOWN,       "net-unknown" },
76       { ICMP_UNREACH_HOST_UNKNOWN,      "host-unknown" },
77       { ICMP_UNREACH_ISOLATED,          "isolated" },
78       { ICMP_UNREACH_NET_PROHIB,        "net-prohib" },
79       { ICMP_UNREACH_HOST_PROHIB,       "host-prohib" },
80       { ICMP_UNREACH_TOSNET,            "tosnet" },
81       { ICMP_UNREACH_TOSHOST,           "toshost" },
82       { ICMP_UNREACH_FILTER_PROHIB,     "filter-prohib" },
83       { ICMP_UNREACH_HOST_PRECEDENCE,   "host-precedence" },
84       { ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" },
85       { 0, NULL }
86 };
87
88 static void show_usage(const char *fmt, ...);
89
90 static int
91 mask_bits(struct in_addr m_ad)
92 {
93         int h_fnd=0,h_num=0,i;
94         u_long mask;
95
96         mask=ntohl(m_ad.s_addr);
97         for (i=0;i<sizeof(u_long)*CHAR_BIT;i++) {
98                 if (mask & 1L) {
99                         h_fnd=1;
100                         h_num++;
101                 } else {
102                         if (h_fnd)
103                                 return -1;
104                 }
105                 mask=mask>>1;
106         }
107         return h_num;
108 }                         
109
110 static void
111 print_port(prot, port, comma)
112         u_char  prot;
113         u_short port;
114         const char *comma;
115 {
116         struct servent *se;
117         struct protoent *pe;
118         const char *protocol;
119         int printed = 0;
120
121         if (do_resolv) {
122                 pe = getprotobynumber(prot);
123                 if (pe)
124                         protocol = pe->p_name;
125                 else
126                         protocol = NULL;
127
128                 se = getservbyport(htons(port), protocol);
129                 if (se) {
130                         printf("%s%s", comma, se->s_name);
131                         printed = 1;
132                 }
133         } 
134         if (!printed)
135                 printf("%s%d",comma,port);
136 }
137
138 static void
139 print_iface(char *key, union ip_fw_if *un, int byname)
140 {
141         char ifnb[FW_IFNLEN+1];
142
143         if (byname) {
144                 strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN);
145                 ifnb[FW_IFNLEN]='\0';
146                 if (un->fu_via_if.unit == -1)
147                         printf(" %s %s*", key, ifnb);
148                 else 
149                         printf(" %s %s%d", key, ifnb, un->fu_via_if.unit);
150         } else if (un->fu_via_ip.s_addr != 0) {
151                 printf(" %s %s", key, inet_ntoa(un->fu_via_ip));
152         } else
153                 printf(" %s any", key);
154 }
155
156 static void
157 print_reject_code(int code)
158 {
159         struct icmpcode *ic;
160
161         for (ic = icmpcodes; ic->str; ic++)
162                 if (ic->code == code) {
163                         printf("%s", ic->str);
164                         return;
165                 }
166         printf("%u", code);
167 }
168
169 static void
170 show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
171 {
172         char *comma;
173         u_long adrt;
174         struct hostent *he;
175         struct protoent *pe;
176         int i, mb;
177         int nsp = IP_FW_GETNSRCP(chain);
178         int ndp = IP_FW_GETNDSTP(chain);
179
180         if (do_resolv)
181                 setservent(1/*stayopen*/);
182
183         printf("%05u ", chain->fw_number);
184
185         if (do_acct) 
186                 printf("%*qu %*qu ",pcwidth,chain->fw_pcnt,bcwidth,chain->fw_bcnt);
187
188         if (do_time)
189         {
190                 if (chain->timestamp)
191                 {
192                         char timestr[30];
193
194                         strcpy(timestr, ctime((time_t *)&chain->timestamp));
195                         *strchr(timestr, '\n') = '\0';
196                         printf("%s ", timestr);
197                 }
198                 else
199                         printf("                         ");
200         }
201
202         switch (chain->fw_flg & IP_FW_F_COMMAND)
203         {
204                 case IP_FW_F_ACCEPT:
205                         printf("allow");
206                         break;
207                 case IP_FW_F_DENY:
208                         printf("deny");
209                         break;
210                 case IP_FW_F_COUNT:
211                         printf("count");
212                         break;
213                 case IP_FW_F_DIVERT:
214                         printf("divert %u", chain->fw_divert_port);
215                         break;
216                 case IP_FW_F_TEE:
217                         printf("tee %u", chain->fw_divert_port);
218                         break;
219                 case IP_FW_F_SKIPTO:
220                         printf("skipto %u", chain->fw_skipto_rule);
221                         break;
222                 case IP_FW_F_REJECT:
223                         if (chain->fw_reject_code == IP_FW_REJECT_RST)
224                                 printf("reset");
225                         else {
226                                 printf("unreach ");
227                                 print_reject_code(chain->fw_reject_code);
228                         }
229                         break;
230                 case IP_FW_F_FWD:
231                         printf("fwd %s", inet_ntoa(chain->fw_fwd_ip.sin_addr));
232                         if(chain->fw_fwd_ip.sin_port)
233                                 printf(",%d", chain->fw_fwd_ip.sin_port);
234                         break;
235                 default:
236                         errx(EX_OSERR, "impossible");
237         }
238    
239         if (chain->fw_flg & IP_FW_F_PRN)
240                 printf(" log");
241
242         pe = getprotobynumber(chain->fw_prot);
243         if (pe)
244                 printf(" %s", pe->p_name);
245         else
246                 printf(" %u", chain->fw_prot);
247
248         printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
249
250         adrt=ntohl(chain->fw_smsk.s_addr);
251         if (adrt==ULONG_MAX && do_resolv) {
252                 adrt=(chain->fw_src.s_addr);
253                 he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
254                 if (he==NULL) {
255                         printf(inet_ntoa(chain->fw_src));
256                 } else
257                         printf("%s",he->h_name);
258         } else {
259                 if (adrt!=ULONG_MAX) {
260                         mb=mask_bits(chain->fw_smsk);
261                         if (mb == 0) {
262                                 printf("any");
263                         } else {
264                                 if (mb > 0) {
265                                         printf(inet_ntoa(chain->fw_src));
266                                         printf("/%d",mb);
267                                 } else {
268                                         printf(inet_ntoa(chain->fw_src));
269                                         printf(":");
270                                         printf(inet_ntoa(chain->fw_smsk));
271                                 }
272                         }
273                 } else
274                         printf(inet_ntoa(chain->fw_src));
275         }
276
277         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
278                 comma = " ";
279                 for (i = 0; i < nsp; i++) {
280                         print_port(chain->fw_prot, chain->fw_uar.fw_pts[i], comma);
281                         if (i==0 && (chain->fw_flg & IP_FW_F_SRNG))
282                                 comma = "-";
283                         else
284                                 comma = ",";
285                 }
286         }
287
288         printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
289
290         adrt=ntohl(chain->fw_dmsk.s_addr);
291         if (adrt==ULONG_MAX && do_resolv) {
292                 adrt=(chain->fw_dst.s_addr);
293                 he=gethostbyaddr((char *)&adrt,sizeof(u_long),AF_INET);
294                 if (he==NULL) {
295                         printf(inet_ntoa(chain->fw_dst));
296                 } else
297                         printf("%s",he->h_name);
298         } else {
299                 if (adrt!=ULONG_MAX) {
300                         mb=mask_bits(chain->fw_dmsk);
301                         if (mb == 0) {
302                                 printf("any");
303                         } else {
304                                 if (mb > 0) {
305                                         printf(inet_ntoa(chain->fw_dst));
306                                         printf("/%d",mb);
307                                 } else {
308                                         printf(inet_ntoa(chain->fw_dst));
309                                         printf(":");
310                                         printf(inet_ntoa(chain->fw_dmsk));
311                                 }
312                         }
313                 } else
314                         printf(inet_ntoa(chain->fw_dst));
315         }
316
317         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
318                 comma = " ";
319                 for (i = 0; i < ndp; i++) {
320                         print_port(chain->fw_prot, chain->fw_uar.fw_pts[nsp+i], comma);
321                         if (i==0 && (chain->fw_flg & IP_FW_F_DRNG))
322                                 comma = "-";
323                         else
324                                 comma = ",";
325                 }
326         }
327
328         /* Direction */
329         if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
330                 printf(" in");
331         if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
332                 printf(" out");
333
334         /* Handle hack for "via" backwards compatibility */
335         if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
336                 print_iface("via",
337                     &chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME);
338         } else {
339                 /* Receive interface specified */
340                 if (chain->fw_flg & IP_FW_F_IIFACE)
341                         print_iface("recv", &chain->fw_in_if,
342                             chain->fw_flg & IP_FW_F_IIFNAME);
343                 /* Transmit interface specified */
344                 if (chain->fw_flg & IP_FW_F_OIFACE)
345                         print_iface("xmit", &chain->fw_out_if,
346                             chain->fw_flg & IP_FW_F_OIFNAME);
347         }
348
349         if (chain->fw_flg & IP_FW_F_FRAG)
350                 printf(" frag");
351
352         if (chain->fw_ipopt || chain->fw_ipnopt) {
353                 int     _opt_printed = 0;
354 #define PRINTOPT(x)     {if (_opt_printed) printf(",");\
355                         printf(x); _opt_printed = 1;}
356
357                 printf(" ipopt ");
358                 if (chain->fw_ipopt  & IP_FW_IPOPT_SSRR) PRINTOPT("ssrr");
359                 if (chain->fw_ipnopt & IP_FW_IPOPT_SSRR) PRINTOPT("!ssrr");
360                 if (chain->fw_ipopt  & IP_FW_IPOPT_LSRR) PRINTOPT("lsrr");
361                 if (chain->fw_ipnopt & IP_FW_IPOPT_LSRR) PRINTOPT("!lsrr");
362                 if (chain->fw_ipopt  & IP_FW_IPOPT_RR)   PRINTOPT("rr");
363                 if (chain->fw_ipnopt & IP_FW_IPOPT_RR)   PRINTOPT("!rr");
364                 if (chain->fw_ipopt  & IP_FW_IPOPT_TS)   PRINTOPT("ts");
365                 if (chain->fw_ipnopt & IP_FW_IPOPT_TS)   PRINTOPT("!ts");
366         } 
367
368         if (chain->fw_tcpf & IP_FW_TCPF_ESTAB) 
369                 printf(" established");
370         else if (chain->fw_tcpf == IP_FW_TCPF_SYN &&
371             chain->fw_tcpnf == IP_FW_TCPF_ACK)
372                 printf(" setup");
373         else if (chain->fw_tcpf || chain->fw_tcpnf) {
374                 int     _flg_printed = 0;
375 #define PRINTFLG(x)     {if (_flg_printed) printf(",");\
376                         printf(x); _flg_printed = 1;}
377
378                 printf(" tcpflg ");
379                 if (chain->fw_tcpf  & IP_FW_TCPF_FIN)  PRINTFLG("fin");
380                 if (chain->fw_tcpnf & IP_FW_TCPF_FIN)  PRINTFLG("!fin");
381                 if (chain->fw_tcpf  & IP_FW_TCPF_SYN)  PRINTFLG("syn");
382                 if (chain->fw_tcpnf & IP_FW_TCPF_SYN)  PRINTFLG("!syn");
383                 if (chain->fw_tcpf  & IP_FW_TCPF_RST)  PRINTFLG("rst");
384                 if (chain->fw_tcpnf & IP_FW_TCPF_RST)  PRINTFLG("!rst");
385                 if (chain->fw_tcpf  & IP_FW_TCPF_PSH)  PRINTFLG("psh");
386                 if (chain->fw_tcpnf & IP_FW_TCPF_PSH)  PRINTFLG("!psh");
387                 if (chain->fw_tcpf  & IP_FW_TCPF_ACK)  PRINTFLG("ack");
388                 if (chain->fw_tcpnf & IP_FW_TCPF_ACK)  PRINTFLG("!ack");
389                 if (chain->fw_tcpf  & IP_FW_TCPF_URG)  PRINTFLG("urg");
390                 if (chain->fw_tcpnf & IP_FW_TCPF_URG)  PRINTFLG("!urg");
391         } 
392         if (chain->fw_flg & IP_FW_F_ICMPBIT) {
393                 int type_index;
394                 int first = 1;
395
396                 printf(" icmptype");
397
398                 for (type_index = 0; type_index < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
399                         if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] & 
400                                 (1U << (type_index % (sizeof(unsigned) * 8)))) {
401                                 printf("%c%d", first == 1 ? ' ' : ',', type_index);
402                                 first = 0;
403                         }
404         }
405         printf("\n");
406         if (do_resolv)
407                 endservent();
408 }
409
410 static void
411 list(ac, av)
412         int     ac;
413         char    **av;
414 {
415         struct ip_fw *r;
416         struct ip_fw rules[1024];
417         int l,i,bytes;
418         unsigned long rulenum;
419         int pcwidth = 0;
420         int bcwidth = 0;
421
422         /* extract rules from kernel */
423         memset(rules,0,sizeof rules);
424         bytes = sizeof rules;
425         i = getsockopt(s, IPPROTO_IP, IP_FW_GET, rules, &bytes);
426         if (i < 0)
427                 err(2,"getsockopt(IP_FW_GET)");
428         if (do_acct)
429                 /* find the maximum packet/byte counter widths */
430                 for (r=rules, l = bytes; l >= sizeof rules[0]; 
431                          r++, l-=sizeof rules[0]) {
432                         char temp[32];
433                         int width;
434
435                         /* packet counter */
436                         width = sprintf(temp, "%qu", r->fw_pcnt);
437                         if (width > pcwidth)
438                                 pcwidth = width;
439
440                         /* byte counter */
441                         width = sprintf(temp, "%qu", r->fw_bcnt);
442                         if (width > bcwidth)
443                                 bcwidth = width;
444                 }
445         if (!ac) {
446                 /* display all rules */
447                 for (r = rules, l = bytes; l >= sizeof rules[0]; 
448                          r++, l-=sizeof rules[0])
449                         show_ipfw(r, pcwidth, bcwidth);
450         }
451         else {
452                 /* display specific rules requested on command line */
453                 int exitval = EX_OK;
454
455                 while (ac--) {
456                         char *endptr;
457                         int seen;
458
459                         /* convert command line rule # */
460                         rulenum = strtoul(*av++, &endptr, 10);
461                         if (*endptr) {
462                                 exitval = EX_USAGE;
463                                 warn("invalid rule number: %s", *(av - 1));
464                                 continue;
465                         }
466                         seen = 0;
467                         for (r = rules, l = bytes; 
468                                  l >= sizeof rules[0] && r->fw_number <= rulenum;
469                                  r++, l-=sizeof rules[0])
470                                 if (rulenum == r->fw_number) {
471                                         show_ipfw(r, pcwidth, bcwidth);
472                                         seen = 1;
473                                 }
474                         if (!seen) {
475                                 /* give precedence to other error(s) */
476                                 if (exitval == EX_OK)
477                                         exitval = EX_UNAVAILABLE;
478                                 warnx("rule %lu does not exist", rulenum);
479                         }
480                 }
481                 if (exitval != EX_OK)
482                         exit(exitval);
483         }
484 }
485
486 static void
487 show_usage(const char *fmt, ...)
488 {
489         if (fmt) {
490                 char buf[100];
491                 va_list args;
492
493                 va_start(args, fmt);
494                 vsnprintf(buf, sizeof(buf), fmt, args);
495                 va_end(args);
496                 warnx("error: %s", buf);
497         }
498         fprintf(stderr, "usage: ipfw [options]\n"
499 "    flush\n"
500 "    add [number] rule\n"
501 "    delete number ...\n"
502 "    list [number ...]\n"
503 "    show [number ...]\n"
504 "    zero [number ...]\n"
505 "  rule:  action proto src dst extras...\n"
506 "    action:\n"
507 "      {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
508 "       reset|count|skipto num|divert port|tee port|fwd ip} [log]\n"
509 "    proto: {ip|tcp|udp|icmp|<number>}\n"
510 "    src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
511 "    dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
512 "  extras:\n"
513 "    fragment     (may not be used with ports or tcpflags)\n"
514 "    in\n"
515 "    out\n"
516 "    {xmit|recv|via} {iface|ip|any}\n"
517 "    {established|setup}\n"
518 "    tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
519 "    ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
520 "    icmptypes {type[,type]}...\n");
521
522         exit(EX_USAGE);
523 }
524
525 static int
526 lookup_host (host, ipaddr)
527         char *host;
528         struct in_addr *ipaddr;
529 {
530         struct hostent *he = gethostbyname(host);
531
532         if (!he)
533                 return(-1);
534
535         *ipaddr = *(struct in_addr *)he->h_addr_list[0];
536
537         return(0);
538 }
539
540 static void
541 fill_ip(ipno, mask, acp, avp)
542         struct in_addr *ipno, *mask;
543         int *acp;
544         char ***avp;
545 {
546         int ac = *acp;
547         char **av = *avp;
548         char *p = 0, md = 0;
549
550         if (ac && !strncmp(*av,"any",strlen(*av))) {
551                 ipno->s_addr = mask->s_addr = 0; av++; ac--;
552         } else {
553                 p = strchr(*av, '/');
554                 if (!p) 
555                         p = strchr(*av, ':');
556                 if (p) {
557                         md = *p;
558                         *p++ = '\0'; 
559                 }
560
561                 if (lookup_host(*av, ipno) != 0)
562                         show_usage("hostname ``%s'' unknown", *av);
563                 switch (md) {
564                         case ':':
565                                 if (!inet_aton(p,mask))
566                                         show_usage("bad netmask ``%s''", p);
567                                 break;
568                         case '/':
569                                 if (atoi(p) == 0) {
570                                         mask->s_addr = 0;
571                                 } else if (atoi(p) > 32) {
572                                         show_usage("bad width ``%s''", p);
573                                 } else {
574                                         mask->s_addr =
575                                             htonl(~0 << (32 - atoi(p)));
576                                 }
577                                 break;
578                         default:
579                                 mask->s_addr = htonl(~0);
580                                 break;
581                 }
582                 ipno->s_addr &= mask->s_addr;
583                 av++;
584                 ac--;
585         }
586         *acp = ac;
587         *avp = av;
588 }
589
590 static void
591 fill_reject_code(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 = icmpcodes; ic->str; ic++)
603                 if (!strcasecmp(str, ic->str)) {
604                         *codep = ic->code;
605                         return;
606                 }
607         show_usage("unknown ICMP unreachable code ``%s''", str);
608 }
609
610 static void
611 add_port(cnt, ptr, off, port)
612         u_short *cnt, *ptr, off, port;
613 {
614         if (off + *cnt >= IP_FW_MAX_PORTS)
615                 errx(EX_USAGE, "too many ports (max is %d)", IP_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(EX_DATAERR, "unknown port ``%s''", arg);
637                         }
638                         val = -1;
639                 }
640         } else {
641                 if (val < 0 || val > 0xffff) {
642                         if (!test) {
643                                 errx(EX_DATAERR, "port ``%s'' out of range", arg);
644                         }
645                         val = -1;
646                 }
647         }
648         return(val);
649 }
650
651 static int
652 fill_port(cnt, ptr, off, arg)
653         u_short *cnt, *ptr, off;
654         char *arg;
655 {
656         char *s;
657         int initial_range = 0;
658
659         s = arg + strcspn(arg, "-,");   /* first port name can't have a dash */
660         if (*s == '-') {
661                 *s++ = '\0';
662                 if (strchr(arg, ','))
663                         errx(EX_USAGE, "port range must be first in list");
664                 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0x0000);
665                 arg = s;
666                 s = strchr(arg,',');
667                 if (s)
668                         *s++ = '\0';
669                 add_port(cnt, ptr, off, *arg ? lookup_port(arg, 0, 0) : 0xffff);
670                 arg = s;
671                 initial_range = 1;
672         }
673         while (arg != NULL) {
674                 s = strchr(arg,',');
675                 if (s)
676                         *s++ = '\0';
677                 add_port(cnt, ptr, off, lookup_port(arg, 0, 0));
678                 arg = s;
679         }
680         return initial_range;
681 }
682
683 static void
684 fill_tcpflag(set, reset, vp)
685         u_char *set, *reset;
686         char **vp;
687 {
688         char *p = *vp,*q;
689         u_char *d;
690
691         while (p && *p) {
692                 struct tpcflags {
693                         char * name;
694                         u_char value;
695                 } flags[] = {
696                         { "syn", IP_FW_TCPF_SYN },
697                         { "fin", IP_FW_TCPF_FIN },
698                         { "ack", IP_FW_TCPF_ACK },
699                         { "psh", IP_FW_TCPF_PSH },
700                         { "rst", IP_FW_TCPF_RST },
701                         { "urg", IP_FW_TCPF_URG }
702                 };
703                 int i;
704
705                 if (*p == '!') {
706                         p++;
707                         d = reset;
708                 } else {
709                         d = set;
710                 }
711                 q = strchr(p, ',');
712                 if (q) 
713                         *q++ = '\0';
714                 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
715                         if (!strncmp(p, flags[i].name, strlen(p))) {
716                                 *d |= flags[i].value;
717                                 break;
718                         }
719                 if (i == sizeof(flags) / sizeof(flags[0]))
720                         show_usage("invalid tcp flag ``%s''", p);
721                 p = q;
722         }
723 }
724
725 static void
726 fill_ipopt(u_char *set, u_char *reset, char **vp)
727 {
728         char *p = *vp,*q;
729         u_char *d;
730
731         while (p && *p) {
732                 if (*p == '!') {
733                         p++;
734                         d = reset;
735                 } else {
736                         d = set;
737                 }
738                 q = strchr(p, ',');
739                 if (q) 
740                         *q++ = '\0';
741                 if (!strncmp(p,"ssrr",strlen(p))) *d |= IP_FW_IPOPT_SSRR;
742                 if (!strncmp(p,"lsrr",strlen(p))) *d |= IP_FW_IPOPT_LSRR;
743                 if (!strncmp(p,"rr",strlen(p)))   *d |= IP_FW_IPOPT_RR;
744                 if (!strncmp(p,"ts",strlen(p)))   *d |= IP_FW_IPOPT_TS;
745                 p = q;
746         }
747 }
748
749 static void
750 fill_icmptypes(types, vp, fw_flg)
751         u_long *types;
752         char **vp;
753         u_int *fw_flg;
754 {
755         char *c = *vp;
756
757         while (*c)
758         {
759                 unsigned long icmptype;
760
761                 if ( *c == ',' )
762                         ++c;
763
764                 icmptype = strtoul(c, &c, 0);
765
766                 if ( *c != ',' && *c != '\0' )
767                         show_usage("invalid ICMP type");
768
769                 if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
770                         show_usage("ICMP type out of range");
771
772                 types[icmptype / (sizeof(unsigned) * 8)] |= 
773                         1 << (icmptype % (sizeof(unsigned) * 8));
774                 *fw_flg |= IP_FW_F_ICMPBIT;
775         }
776 }
777
778 static void
779 delete(ac,av)
780         int ac;
781         char **av;
782 {
783         struct ip_fw rule;
784         int i;
785         int exitval = EX_OK;
786
787         memset(&rule, 0, sizeof rule);
788
789         av++; ac--;
790
791         /* Rule number */
792         while (ac && isdigit(**av)) {
793                 rule.fw_number = atoi(*av); av++; ac--;
794                 i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule);
795                 if (i) {
796                         exitval = EX_UNAVAILABLE;
797                         warn("rule %u: setsockopt(%s)", rule.fw_number, "IP_FW_DEL");
798                 }
799         }
800         if (exitval != EX_OK)
801                 exit(exitval);
802 }
803
804 static void
805 verify_interface(union ip_fw_if *ifu)
806 {
807         struct ifreq ifr;
808
809         /*
810          *      If a unit was specified, check for that exact interface.
811          *      If a wildcard was specified, check for unit 0.
812          */
813         snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", 
814                          ifu->fu_via_if.name,
815                          ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit);
816
817         if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
818                 warnx("warning: interface ``%s'' does not exist", ifr.ifr_name);
819 }
820
821 static void
822 fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg)
823 {
824         if (!ac)
825             show_usage("missing argument for ``%s''", which);
826
827         /* Parse the interface or address */
828         if (!strcmp(arg, "any")) {
829                 ifu->fu_via_ip.s_addr = 0;
830                 *byname = 0;
831         } else if (!isdigit(*arg)) {
832                 char *q;
833
834                 *byname = 1;
835                 strncpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
836                 ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0';
837                 for (q = ifu->fu_via_if.name;
838                     *q && !isdigit(*q) && *q != '*'; q++)
839                         continue;
840                 ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q);
841                 *q = '\0';
842                 verify_interface(ifu);
843         } else if (!inet_aton(arg, &ifu->fu_via_ip)) {
844                 show_usage("bad ip address ``%s''", arg);
845         } else
846                 *byname = 0;
847 }
848
849 static void
850 add(ac,av)
851         int ac;
852         char **av;
853 {
854         struct ip_fw rule;
855         int i;
856         u_char proto;
857         struct protoent *pe;
858         int saw_xmrc = 0, saw_via = 0;
859         
860         memset(&rule, 0, sizeof rule);
861
862         av++; ac--;
863
864         /* Rule number */
865         if (ac && isdigit(**av)) {
866                 rule.fw_number = atoi(*av); av++; ac--;
867         }
868
869         /* Action */
870         if (ac == 0)
871                 show_usage("missing action");
872         if (!strncmp(*av,"accept",strlen(*av))
873                     || !strncmp(*av,"pass",strlen(*av))
874                     || !strncmp(*av,"allow",strlen(*av))
875                     || !strncmp(*av,"permit",strlen(*av))) {
876                 rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
877         } else if (!strncmp(*av,"count",strlen(*av))) {
878                 rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
879         } else if (!strncmp(*av,"divert",strlen(*av))) {
880                 rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
881                 if (!ac)
882                         show_usage("missing %s port", "divert");
883                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
884                 if (rule.fw_divert_port == 0) {
885                         struct servent *s;
886                         setservent(1);
887                         s = getservbyname(av[-1], "divert");
888                         if (s != NULL)
889                                 rule.fw_divert_port = ntohs(s->s_port);
890                         else
891                                 show_usage("illegal %s port", "divert");
892                 }
893         } else if (!strncmp(*av,"tee",strlen(*av))) {
894                 rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
895                 if (!ac)
896                         show_usage("missing %s port", "tee divert");
897                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
898                 if (rule.fw_divert_port == 0) {
899                         struct servent *s;
900                         setservent(1);
901                         s = getservbyname(av[-1], "divert");
902                         if (s != NULL)
903                                 rule.fw_divert_port = ntohs(s->s_port);
904                         else
905                                 show_usage("illegal %s port", "tee divert");
906                 }
907 #ifndef IPFW_TEE_IS_FINALLY_IMPLEMENTED
908                 err(EX_USAGE, "the ``tee'' action is not implemented");
909 #endif
910         } else if (!strncmp(*av,"fwd",strlen(*av)) ||
911                    !strncmp(*av,"forward",strlen(*av))) {
912                 struct in_addr dummyip;
913                 char *pp;
914                 rule.fw_flg |= IP_FW_F_FWD; av++; ac--;
915                 if (!ac)
916                         show_usage("missing forwarding IP address");
917                 rule.fw_fwd_ip.sin_len = sizeof(struct sockaddr_in);
918                 rule.fw_fwd_ip.sin_family = AF_INET;
919                 rule.fw_fwd_ip.sin_port = 0;
920                 pp = strchr(*av, ':');
921                 if(pp == NULL)
922                         pp = strchr(*av, ',');
923                 if(pp != NULL)
924                 {
925                         *(pp++) = '\0';
926                         rule.fw_fwd_ip.sin_port = lookup_port(pp, 1, 1);
927                         if(rule.fw_fwd_ip.sin_port == (unsigned int)-1)
928                                 show_usage("illegal forwarding port");
929                 }
930                 fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av);
931                 if (rule.fw_fwd_ip.sin_addr.s_addr == 0)
932                         show_usage("illegal forwarding IP address");
933
934         } else if (!strncmp(*av,"skipto",strlen(*av))) {
935                 rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
936                 if (!ac)
937                         show_usage("missing skipto rule number");
938                 rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--;
939         } else if ((!strncmp(*av,"deny",strlen(*av))
940                     || !strncmp(*av,"drop",strlen(*av)))) {
941                 rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
942         } else if (!strncmp(*av,"reject",strlen(*av))) {
943                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
944                 rule.fw_reject_code = ICMP_UNREACH_HOST;
945         } else if (!strncmp(*av,"reset",strlen(*av))) {
946                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
947                 rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */
948         } else if (!strncmp(*av,"unreach",strlen(*av))) {
949                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
950                 fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
951         } else {
952                 show_usage("invalid action ``%s''", *av);
953         }
954
955         /* [log] */
956         if (ac && !strncmp(*av,"log",strlen(*av))) {
957                 rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
958         }
959
960         /* protocol */
961         if (ac == 0)
962                 show_usage("missing protocol");
963         if ((proto = atoi(*av)) > 0) {
964                 rule.fw_prot = proto; av++; ac--;
965         } else if (!strncmp(*av,"all",strlen(*av))) {
966                 rule.fw_prot = IPPROTO_IP; av++; ac--;
967         } else if ((pe = getprotobyname(*av)) != NULL) {
968                 rule.fw_prot = pe->p_proto; av++; ac--;
969         } else {
970                 show_usage("invalid protocol ``%s''", *av);
971         }
972
973         if (rule.fw_prot != IPPROTO_TCP
974             && (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
975             && rule.fw_reject_code == IP_FW_REJECT_RST)
976                 show_usage("``reset'' is only valid for tcp packets");
977
978         /* from */
979         if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; }
980         else
981                 show_usage("missing ``from''");
982
983         if (ac && !strncmp(*av,"not",strlen(*av))) {
984                 rule.fw_flg |= IP_FW_F_INVSRC;
985                 av++; ac--;
986         }
987         if (!ac)
988                 show_usage("missing arguments");
989
990         fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
991
992         if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
993                 u_short nports = 0;
994
995                 if (fill_port(&nports, rule.fw_uar.fw_pts, 0, *av))
996                         rule.fw_flg |= IP_FW_F_SRNG;
997                 IP_FW_SETNSRCP(&rule, nports);
998                 av++; ac--;
999         }
1000
1001         /* to */
1002         if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; }
1003         else
1004                 show_usage("missing ``to''");
1005
1006         if (ac && !strncmp(*av,"not",strlen(*av))) {
1007                 rule.fw_flg |= IP_FW_F_INVDST;
1008                 av++; ac--;
1009         }
1010         if (!ac)
1011                 show_usage("missing arguments");
1012
1013         fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
1014
1015         if (ac && (isdigit(**av) || lookup_port(*av, 1, 1) >= 0)) {
1016                 u_short nports = 0;
1017
1018                 if (fill_port(&nports,
1019                     rule.fw_uar.fw_pts, IP_FW_GETNSRCP(&rule), *av))
1020                         rule.fw_flg |= IP_FW_F_DRNG;
1021                 IP_FW_SETNDSTP(&rule, nports);
1022                 av++; ac--;
1023         }
1024
1025         if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
1026             && (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) {
1027                 show_usage("only TCP and UDP protocols are valid"
1028                     " with port specifications");
1029         }
1030
1031         while (ac) {
1032                 if (!strncmp(*av,"in",strlen(*av))) { 
1033                         rule.fw_flg |= IP_FW_F_IN;
1034                         av++; ac--; continue;
1035                 }
1036                 if (!strncmp(*av,"out",strlen(*av))) { 
1037                         rule.fw_flg |= IP_FW_F_OUT;
1038                         av++; ac--; continue;
1039                 }
1040                 if (ac && !strncmp(*av,"xmit",strlen(*av))) {
1041                         union ip_fw_if ifu;
1042                         int byname;
1043
1044                         if (saw_via) {
1045 badviacombo:
1046                                 show_usage("``via'' is incompatible"
1047                                     " with ``xmit'' and ``recv''");
1048                         }
1049                         saw_xmrc = 1;
1050                         av++; ac--; 
1051                         fill_iface("xmit", &ifu, &byname, ac, *av);
1052                         rule.fw_out_if = ifu;
1053                         rule.fw_flg |= IP_FW_F_OIFACE;
1054                         if (byname)
1055                                 rule.fw_flg |= IP_FW_F_OIFNAME;
1056                         av++; ac--; continue;
1057                 }
1058                 if (ac && !strncmp(*av,"recv",strlen(*av))) {
1059                         union ip_fw_if ifu;
1060                         int byname;
1061
1062                         if (saw_via)
1063                                 goto badviacombo;
1064                         saw_xmrc = 1;
1065                         av++; ac--; 
1066                         fill_iface("recv", &ifu, &byname, ac, *av);
1067                         rule.fw_in_if = ifu;
1068                         rule.fw_flg |= IP_FW_F_IIFACE;
1069                         if (byname)
1070                                 rule.fw_flg |= IP_FW_F_IIFNAME;
1071                         av++; ac--; continue;
1072                 }
1073                 if (ac && !strncmp(*av,"via",strlen(*av))) {
1074                         union ip_fw_if ifu;
1075                         int byname = 0;
1076
1077                         if (saw_xmrc)
1078                                 goto badviacombo;
1079                         saw_via = 1;
1080                         av++; ac--; 
1081                         fill_iface("via", &ifu, &byname, ac, *av);
1082                         rule.fw_out_if = rule.fw_in_if = ifu;
1083                         if (byname)
1084                                 rule.fw_flg |=
1085                                     (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME);
1086                         av++; ac--; continue;
1087                 }
1088                 if (!strncmp(*av,"fragment",strlen(*av))) {
1089                         rule.fw_flg |= IP_FW_F_FRAG;
1090                         av++; ac--; continue;
1091                 }
1092                 if (!strncmp(*av,"ipoptions",strlen(*av))) { 
1093                         av++; ac--; 
1094                         if (!ac)
1095                                 show_usage("missing argument"
1096                                     " for ``ipoptions''");
1097                         fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
1098                         av++; ac--; continue;
1099                 }
1100                 if (rule.fw_prot == IPPROTO_TCP) {
1101                         if (!strncmp(*av,"established",strlen(*av))) { 
1102                                 rule.fw_tcpf  |= IP_FW_TCPF_ESTAB;
1103                                 av++; ac--; continue;
1104                         }
1105                         if (!strncmp(*av,"setup",strlen(*av))) { 
1106                                 rule.fw_tcpf  |= IP_FW_TCPF_SYN;
1107                                 rule.fw_tcpnf  |= IP_FW_TCPF_ACK;
1108                                 av++; ac--; continue;
1109                         }
1110                         if (!strncmp(*av,"tcpflags",strlen(*av))) { 
1111                                 av++; ac--; 
1112                                 if (!ac)
1113                                         show_usage("missing argument"
1114                                             " for ``tcpflags''");
1115                                 fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av);
1116                                 av++; ac--; continue;
1117                         }
1118                 }
1119                 if (rule.fw_prot == IPPROTO_ICMP) {
1120                         if (!strncmp(*av,"icmptypes",strlen(*av))) {
1121                                 av++; ac--;
1122                                 if (!ac)
1123                                         show_usage("missing argument"
1124                                             " for ``icmptypes''");
1125                                 fill_icmptypes(rule.fw_uar.fw_icmptypes,
1126                                     av, &rule.fw_flg);
1127                                 av++; ac--; continue;
1128                         }
1129                 }
1130                 show_usage("unknown argument ``%s''", *av);
1131         }
1132
1133         /* No direction specified -> do both directions */
1134         if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN)))
1135                 rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN);
1136
1137         /* Sanity check interface check, but handle "via" case separately */
1138         if (saw_via) {
1139                 if (rule.fw_flg & IP_FW_F_IN)
1140                         rule.fw_flg |= IP_FW_F_IIFACE;
1141                 if (rule.fw_flg & IP_FW_F_OUT)
1142                         rule.fw_flg |= IP_FW_F_OIFACE;
1143         } else if ((rule.fw_flg & IP_FW_F_OIFACE) && (rule.fw_flg & IP_FW_F_IN))
1144                 show_usage("can't check xmit interface of incoming packets");
1145
1146         /* frag may not be used in conjunction with ports or TCP flags */
1147         if (rule.fw_flg & IP_FW_F_FRAG) {
1148                 if (rule.fw_tcpf || rule.fw_tcpnf)
1149                         show_usage("can't mix 'frag' and tcpflags");
1150
1151                 if (rule.fw_nports)
1152                         show_usage("can't mix 'frag' and port specifications");
1153         }
1154
1155         if (!do_quiet)
1156                 show_ipfw(&rule, 10, 10);
1157         i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule);
1158         if (i)
1159                 err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ADD");
1160 }
1161
1162 static void
1163 zero (ac, av)
1164         int ac;
1165         char **av;
1166 {
1167         av++; ac--;
1168
1169         if (!ac) {
1170                 /* clear all entries */
1171                 if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0)
1172                         err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
1173                 if (!do_quiet)
1174                         printf("Accounting cleared.\n");
1175         } else {
1176                 struct ip_fw rule;
1177                 int failed = EX_OK;
1178
1179                 memset(&rule, 0, sizeof rule);
1180                 while (ac) {
1181                         /* Rule number */
1182                         if (isdigit(**av)) {
1183                                 rule.fw_number = atoi(*av); av++; ac--;
1184                                 if (setsockopt(s, IPPROTO_IP,
1185                                     IP_FW_ZERO, &rule, sizeof rule)) {
1186                                         warn("rule %u: setsockopt(%s)", rule.fw_number,
1187                                                  "IP_FW_ZERO");
1188                                         failed = EX_UNAVAILABLE;
1189                                 }
1190                                 else if (!do_quiet)
1191                                         printf("Entry %d cleared\n",
1192                                             rule.fw_number);
1193                         } else
1194                                 show_usage("invalid rule number ``%s''", *av);
1195                 }
1196                 if (failed != EX_OK)
1197                         exit(failed);
1198         }
1199 }
1200
1201 static int
1202 ipfw_main(ac,av)
1203         int     ac;
1204         char    **av;
1205 {
1206
1207         int             ch;
1208         extern int      optreset; /* XXX should be declared in <unistd.h> */
1209
1210         if ( ac == 1 ) {
1211                 show_usage(NULL);
1212         }
1213
1214         /* Set the force flag for non-interactive processes */
1215         do_force = !isatty(STDIN_FILENO);
1216
1217         optind = optreset = 1;
1218         while ((ch = getopt(ac, av, "afqtN")) != -1)
1219         switch(ch) {
1220                 case 'a':
1221                         do_acct=1;
1222                         break;
1223                 case 'f':
1224                         do_force=1;
1225                         break;
1226                 case 'q':
1227                         do_quiet=1;
1228                         break;
1229                 case 't':
1230                         do_time=1;
1231                         break;
1232                 case 'N':
1233                         do_resolv=1;
1234                         break;
1235                 default:
1236                         show_usage(NULL);
1237         }
1238
1239         ac -= optind;
1240         if (*(av+=optind)==NULL) {
1241                  show_usage("Bad arguments");
1242         }
1243
1244         if (!strncmp(*av, "add", strlen(*av))) {
1245                 add(ac,av);
1246         } else if (!strncmp(*av, "delete", strlen(*av))) {
1247                 delete(ac,av);
1248         } else if (!strncmp(*av, "flush", strlen(*av))) {
1249                 int do_flush = 0;
1250
1251                 if ( do_force || do_quiet )
1252                         do_flush = 1;
1253                 else {
1254                         int c;
1255
1256                         /* Ask the user */
1257                         printf("Are you sure? [yn] ");
1258                         do {
1259                                 fflush(stdout);
1260                                 c = toupper(getc(stdin));
1261                                 while (c != '\n' && getc(stdin) != '\n')
1262                                         if (feof(stdin))
1263                                                 return (0);
1264                         } while (c != 'Y' && c != 'N');
1265                         printf("\n");
1266                         if (c == 'Y') 
1267                                 do_flush = 1;
1268                 }
1269                 if ( do_flush ) {
1270                         if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0) < 0)
1271                                 err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_FLUSH");
1272                         if (!do_quiet)
1273                                 printf("Flushed all rules.\n");
1274                 }
1275         } else if (!strncmp(*av, "zero", strlen(*av))) {
1276                 zero(ac,av);
1277         } else if (!strncmp(*av, "print", strlen(*av))) {
1278                 list(--ac,++av);
1279         } else if (!strncmp(*av, "list", strlen(*av))) {
1280                 list(--ac,++av);
1281         } else if (!strncmp(*av, "show", strlen(*av))) {
1282                 do_acct++;
1283                 list(--ac,++av);
1284         } else {
1285                 show_usage("Bad arguments");
1286         }
1287         return 0;
1288 }
1289
1290 int 
1291 main(ac, av)
1292         int     ac;
1293         char    **av;
1294 {
1295 #define MAX_ARGS        32
1296 #define WHITESP         " \t\f\v\n\r"
1297         char    buf[BUFSIZ];
1298         char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
1299         char    linename[10];
1300         int     i, c, qflag, pflag, status;
1301         FILE    *f = NULL;
1302         pid_t   preproc = 0;
1303
1304         s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW );
1305         if ( s < 0 )
1306                 err(EX_UNAVAILABLE, "socket");
1307
1308         setbuf(stdout,0);
1309
1310         if (ac > 1 && access(av[ac - 1], R_OK) == 0) {
1311                 qflag = pflag = i = 0;
1312                 lineno = 0;
1313
1314                 while ((c = getopt(ac, av, "D:U:p:q")) != -1)
1315                         switch(c) {
1316                         case 'D':
1317                                 if (!pflag)
1318                                         errx(EX_USAGE, "-D requires -p");
1319                                 if (i > MAX_ARGS - 2)
1320                                         errx(EX_USAGE,
1321                                              "too many -D or -U options");
1322                                 args[i++] = "-D";
1323                                 args[i++] = optarg;
1324                                 break;
1325
1326                         case 'U':
1327                                 if (!pflag)
1328                                         errx(EX_USAGE, "-U requires -p");
1329                                 if (i > MAX_ARGS - 2)
1330                                         errx(EX_USAGE,
1331                                              "too many -D or -U options");
1332                                 args[i++] = "-U";
1333                                 args[i++] = optarg;
1334                                 break;
1335
1336                         case 'p':
1337                                 pflag = 1;
1338                                 cmd = optarg;
1339                                 args[0] = cmd;
1340                                 i = 1;
1341                                 break;
1342
1343                         case 'q':
1344                                 qflag = 1;
1345                                 break;
1346
1347                         default:
1348                                 show_usage(NULL);
1349                         }
1350
1351                 av += optind;
1352                 ac -= optind;
1353                 if (ac != 1)
1354                         show_usage("extraneous filename arguments");
1355
1356                 if ((f = fopen(av[0], "r")) == NULL)
1357                         err(EX_UNAVAILABLE, "fopen: %s", av[0]);
1358
1359                 if (pflag) {
1360                         /* pipe through preprocessor (cpp or m4) */
1361                         int pipedes[2];
1362
1363                         args[i] = 0;
1364
1365                         if (pipe(pipedes) == -1)
1366                                 err(EX_OSERR, "cannot create pipe");
1367
1368                         switch((preproc = fork())) {
1369                         case -1:
1370                                 err(EX_OSERR, "cannot fork");
1371
1372                         case 0:
1373                                 /* child */
1374                                 if (dup2(fileno(f), 0) == -1 ||
1375                                     dup2(pipedes[1], 1) == -1)
1376                                         err(EX_OSERR, "dup2()");
1377                                 fclose(f);
1378                                 close(pipedes[1]);
1379                                 close(pipedes[0]);
1380                                 execvp(cmd, args);
1381                                 err(EX_OSERR, "execvp(%s) failed", cmd);
1382
1383                         default:
1384                                 /* parent */
1385                                 fclose(f);
1386                                 close(pipedes[1]);
1387                                 if ((f = fdopen(pipedes[0], "r")) == NULL) {
1388                                         int savederrno = errno;
1389
1390                                         (void)kill(preproc, SIGTERM);
1391                                         errno = savederrno;
1392                                         err(EX_OSERR, "fdopen()");
1393                                 }
1394                         }
1395                 }
1396
1397                 while (fgets(buf, BUFSIZ, f)) {
1398                         lineno++;
1399                         sprintf(linename, "Line %d", lineno);
1400                         args[0] = linename;
1401
1402                         if (*buf == '#')
1403                                 continue;
1404                         if ((p = strchr(buf, '#')) != NULL)
1405                                 *p = '\0';
1406                         i=1;
1407                         if (qflag) args[i++]="-q";
1408                         for (a = strtok(buf, WHITESP);
1409                             a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
1410                                 args[i] = a;
1411                         if (i == (qflag? 2: 1))
1412                                 continue;
1413                         if (i == MAX_ARGS)
1414                                 errx(EX_USAGE, "%s: too many arguments", linename);
1415                         args[i] = NULL;
1416
1417                         ipfw_main(i, args); 
1418                 }
1419                 fclose(f);
1420                 if (pflag) {
1421                         if (waitpid(preproc, &status, 0) != -1) {
1422                                 if (WIFEXITED(status)) {
1423                                         if (WEXITSTATUS(status) != EX_OK)
1424                                                 errx(EX_UNAVAILABLE,
1425                                                      "preprocessor exited with status %d",
1426                                                      WEXITSTATUS(status));
1427                                 } else if (WIFSIGNALED(status)) {
1428                                         errx(EX_UNAVAILABLE,
1429                                              "preprocessor exited with signal %d",
1430                                              WTERMSIG(status));
1431                                 }
1432                         }
1433                 }
1434
1435         } else
1436                 ipfw_main(ac,av);
1437         return EX_OK;
1438 }