Merge from vendor branch NTPD:
[dragonfly.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  * $FreeBSD: src/sbin/ipfw/ipfw.c,v 1.80.2.26 2003/01/14 19:15:58 dillon Exp $
20  * $DragonFly: src/sbin/ipfw/Attic/ipfw.c,v 1.6 2004/02/04 17:39:59 joerg Exp $
21  */
22
23 #include <sys/param.h>
24 #include <sys/mbuf.h>
25 #include <sys/socket.h>
26 #include <sys/sockio.h>
27 #include <sys/sysctl.h>
28 #include <sys/time.h>
29 #include <sys/wait.h>
30
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <grp.h>
35 #include <limits.h>
36 #include <netdb.h>
37 #include <pwd.h>
38 #include <signal.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <stdarg.h>
42 #include <string.h>
43 #include <unistd.h>
44 #include <sysexits.h>
45
46 #include <net/if.h>
47 #include <netinet/in.h>
48 #include <netinet/in_systm.h>
49 #include <netinet/ip.h>
50 #include <netinet/ip_icmp.h>
51 #include <net/ipfw/ip_fw.h>
52 #include <net/route.h> /* def. of struct route */
53 #include <net/dummynet/ip_dummynet.h>
54 #include <netinet/tcp.h>
55 #include <arpa/inet.h>
56
57 int             s,                      /* main RAW socket */
58                 do_resolv,              /* Would try to resolve all */
59                 do_acct,                /* Show packet/byte count */
60                 do_time,                /* Show time stamps */
61                 do_quiet,               /* Be quiet in add and flush */
62                 do_force,               /* Don't ask for confirmation */
63                 do_pipe,                /* this cmd refers to a pipe */
64                 do_sort,                /* field to sort results (0 = no) */
65                 do_dynamic,             /* display dynamic rules */
66                 do_expired,             /* display expired dynamic rules */
67                 verbose;
68
69 struct icmpcode {
70         int     code;
71         char    *str;
72 };
73
74 static struct icmpcode icmpcodes[] = {
75       { ICMP_UNREACH_NET,               "net" },
76       { ICMP_UNREACH_HOST,              "host" },
77       { ICMP_UNREACH_PROTOCOL,          "protocol" },
78       { ICMP_UNREACH_PORT,              "port" },
79       { ICMP_UNREACH_NEEDFRAG,          "needfrag" },
80       { ICMP_UNREACH_SRCFAIL,           "srcfail" },
81       { ICMP_UNREACH_NET_UNKNOWN,       "net-unknown" },
82       { ICMP_UNREACH_HOST_UNKNOWN,      "host-unknown" },
83       { ICMP_UNREACH_ISOLATED,          "isolated" },
84       { ICMP_UNREACH_NET_PROHIB,        "net-prohib" },
85       { ICMP_UNREACH_HOST_PROHIB,       "host-prohib" },
86       { ICMP_UNREACH_TOSNET,            "tosnet" },
87       { ICMP_UNREACH_TOSHOST,           "toshost" },
88       { ICMP_UNREACH_FILTER_PROHIB,     "filter-prohib" },
89       { ICMP_UNREACH_HOST_PRECEDENCE,   "host-precedence" },
90       { ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" },
91       { 0, NULL }
92 };
93
94 static void show_usage(void);
95
96 static int
97 mask_bits(struct in_addr m_ad)
98 {
99         int h_fnd = 0, h_num = 0, i;
100         u_long mask;
101
102         mask = ntohl(m_ad.s_addr);
103         for (i = 0; i < sizeof(u_long)*CHAR_BIT; i++) {
104                 if (mask & 1L) {
105                         h_fnd = 1;
106                         h_num++;
107                 } else {
108                         if (h_fnd)
109                                 return -1;
110                 }
111                 mask = mask >> 1;
112         }
113         return h_num;
114 }
115
116 static void
117 print_port(u_char prot, u_short port, const char comma)
118 {
119         struct servent *se = NULL;
120         struct protoent *pe;
121
122         if (comma == ':') {
123                 printf("%c0x%04x", comma, port);
124                 return;
125         }
126         if (do_resolv) {
127                 pe = getprotobynumber(prot);
128                 se = getservbyport(htons(port), pe ? pe->p_name : NULL);
129         }
130         if (se)
131                 printf("%c%s", comma, se->s_name);
132         else
133                 printf("%c%d", comma, port);
134 }
135
136 static void
137 print_iface(char *key, union ip_fw_if *un, int byname)
138 {
139         if (byname) {
140                 printf(" %s %s", key, un->fu_via_if.name);
141         } else if (un->fu_via_ip.s_addr != 0) {
142                 printf(" %s %s", key, inet_ntoa(un->fu_via_ip));
143         } else
144                 printf(" %s any", key);
145 }
146
147 static void
148 print_reject_code(int code)
149 {
150         struct icmpcode *ic;
151
152         for (ic = icmpcodes; ic->str; ic++)
153                 if (ic->code == code) {
154                         printf("%s", ic->str);
155                         return;
156                 }
157         printf("%u", code);
158 }
159
160 /**
161  * _s_x holds a string-int pair for various lookups.
162  * s=NULL terminates the struct.
163  */
164 struct _s_x { char *s; int x; };
165 static struct _s_x limit_masks[] = {
166         {"src-addr",    DYN_SRC_ADDR},
167         {"src-port",    DYN_SRC_PORT},
168         {"dst-addr",    DYN_DST_ADDR},
169         {"dst-port",    DYN_DST_PORT},
170         {NULL,          0} };
171
172 static void
173 show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
174 {
175         static int twidth = 0;
176         char comma;
177         u_long adrt;
178         struct hostent *he;
179         struct protoent *pe;
180         int i, mb;
181         int nsp = IP_FW_GETNSRCP(chain);
182         int ndp = IP_FW_GETNDSTP(chain);
183
184         if (do_resolv)
185                 setservent(1/*stay open*/);
186
187         printf("%05u ", chain->fw_number);
188
189         if (do_acct)
190                 printf("%*qu %*qu ", pcwidth, chain->fw_pcnt, bcwidth, chain->fw_bcnt);
191
192         if (do_time) {
193                 char timestr[30];
194
195                 if (twidth == 0) {
196                         time_t zerotime = 0;
197
198                         strcpy(timestr, ctime(&zerotime));
199                         *strchr(timestr, '\n') = '\0';
200                         twidth = strlen(timestr);
201                 }
202                 if (chain->timestamp) {
203                         strcpy(timestr, ctime((time_t *)&chain->timestamp));
204                         *strchr(timestr, '\n') = '\0';
205                         printf("%s ", timestr);
206                 } else {
207                         printf("%*s ", twidth, " ");
208                 }
209         }
210         if (chain->fw_flg == IP_FW_F_CHECK_S) {
211                 printf("check-state\n");
212                 goto done;
213         }
214
215         if (chain->fw_flg & IP_FW_F_RND_MATCH) {
216                 double d = 1.0 * chain->dont_match_prob;
217                 d = 1 - (d / 0x7fffffff);
218                 printf("prob %f ", d);
219         }
220
221         switch (chain->fw_flg & IP_FW_F_COMMAND) {
222                 case IP_FW_F_ACCEPT:
223                         printf("allow");
224                         break;
225                 case IP_FW_F_DENY:
226                         printf("deny");
227                         break;
228                 case IP_FW_F_COUNT:
229                         printf("count");
230                         break;
231                 case IP_FW_F_DIVERT:
232                         printf("divert %u", chain->fw_divert_port);
233                         break;
234                 case IP_FW_F_TEE:
235                         printf("tee %u", chain->fw_divert_port);
236                         break;
237                 case IP_FW_F_SKIPTO:
238                         printf("skipto %u", chain->fw_skipto_rule);
239                         break;
240                 case IP_FW_F_PIPE:
241                         printf("pipe %u", chain->fw_skipto_rule);
242                         break;
243                 case IP_FW_F_QUEUE:
244                         printf("queue %u", chain->fw_skipto_rule);
245                         break;
246                 case IP_FW_F_REJECT:
247                         if (chain->fw_reject_code == IP_FW_REJECT_RST)
248                                 printf("reset");
249                         else {
250                                 printf("unreach ");
251                                 print_reject_code(chain->fw_reject_code);
252                         }
253                         break;
254                 case IP_FW_F_FWD:
255                         printf("fwd %s", inet_ntoa(chain->fw_fwd_ip.sin_addr));
256                         if(chain->fw_fwd_ip.sin_port)
257                                 printf(",%d", chain->fw_fwd_ip.sin_port);
258                         break;
259                 default:
260                         errx(EX_OSERR, "impossible");
261         }
262
263         if (chain->fw_flg & IP_FW_F_PRN) {
264                 printf(" log");
265                 if (chain->fw_logamount)
266                         printf(" logamount %d", chain->fw_logamount);
267         }
268
269         pe = getprotobynumber(chain->fw_prot);
270         if (pe)
271                 printf(" %s", pe->p_name);
272         else
273                 printf(" %u", chain->fw_prot);
274
275         printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : "");
276
277         if (chain->fw_flg & IP_FW_F_SME) {
278                 printf("me");
279         } else {
280
281                 adrt = ntohl(chain->fw_smsk.s_addr);
282                 if (adrt == ULONG_MAX && do_resolv) {
283                         adrt = (chain->fw_src.s_addr);
284                         he = gethostbyaddr((char *)&adrt,
285                             sizeof(u_long), AF_INET);
286                         if (he == NULL)
287                                 printf("%s", inet_ntoa(chain->fw_src));
288                         else
289                                 printf("%s", he->h_name);
290                 } else if (adrt != ULONG_MAX) {
291                         mb = mask_bits(chain->fw_smsk);
292                         if (mb == 0) {
293                                 printf("any");
294                         } else if (mb > 0) {
295                                 printf("%s", inet_ntoa(chain->fw_src));
296                                 printf("/%d", mb);
297                         } else {
298                                 printf("%s", inet_ntoa(chain->fw_src));
299                                 printf(":");
300                                 printf("%s", inet_ntoa(chain->fw_smsk));
301                         }
302                 } else {
303                         printf("%s", inet_ntoa(chain->fw_src));
304                 }
305         }
306
307         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
308                 comma = ' ';
309                 for (i = 0; i < nsp; i++) {
310                         print_port(chain->fw_prot,
311                             chain->fw_uar.fw_pts[i], comma);
312                         if (i == 0 && (chain->fw_flg & IP_FW_F_SRNG))
313                                 comma = '-';
314                         else if (i == 0 && (chain->fw_flg & IP_FW_F_SMSK))
315                                 comma = ':';
316                         else
317                                 comma = ',';
318                 }
319         }
320
321         printf(" to %s", chain->fw_flg & IP_FW_F_INVDST ? "not " : "");
322
323         if (chain->fw_flg & IP_FW_F_DME) {
324                 printf("me");
325         } else {
326                 adrt = ntohl(chain->fw_dmsk.s_addr);
327                 if (adrt == ULONG_MAX && do_resolv) {
328                         adrt = (chain->fw_dst.s_addr);
329                         he = gethostbyaddr((char *)&adrt,
330                             sizeof(u_long), AF_INET);
331                         if (he == NULL)
332                                 printf("%s", inet_ntoa(chain->fw_dst));
333                         else
334                                 printf("%s", he->h_name);
335                 } else if (adrt != ULONG_MAX) {
336                         mb = mask_bits(chain->fw_dmsk);
337                         if (mb == 0) {
338                                 printf("any");
339                         } else if (mb > 0) {
340                                 printf("%s", inet_ntoa(chain->fw_dst));
341                                 printf("/%d", mb);
342                         } else {
343                                 printf("%s", inet_ntoa(chain->fw_dst));
344                                 printf(":");
345                                 printf("%s", inet_ntoa(chain->fw_dmsk));
346                         }
347                 } else {
348                         printf("%s", inet_ntoa(chain->fw_dst));
349                 }
350         }
351
352         if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) {
353                 comma = ' ';
354                 for (i = 0; i < ndp; i++) {
355                         print_port(chain->fw_prot,
356                             chain->fw_uar.fw_pts[nsp+i], comma);
357                         if (i == 0 && (chain->fw_flg & IP_FW_F_DRNG))
358                                 comma = '-';
359                         else if (i == 0 && (chain->fw_flg & IP_FW_F_DMSK))
360                                 comma = ':';
361                         else
362                                 comma = ',';
363                 }
364         }
365
366         if (chain->fw_flg & IP_FW_F_UID) {
367                 struct passwd *pwd = getpwuid(chain->fw_uid);
368
369                 if (pwd)
370                         printf(" uid %s", pwd->pw_name);
371                 else
372                         printf(" uid %u", chain->fw_uid);
373         }
374
375         if (chain->fw_flg & IP_FW_F_GID) {
376                 struct group *grp = getgrgid(chain->fw_gid);
377
378                 if (grp)
379                         printf(" gid %s", grp->gr_name);
380                 else
381                         printf(" gid %u", chain->fw_gid);
382         }
383
384         if (chain->fw_flg & IP_FW_F_KEEP_S) {
385                 struct _s_x *p = limit_masks;
386
387                 switch(chain->dyn_type) {
388                 default:
389                         printf(" *** unknown type ***");
390                         break ;
391                 case DYN_KEEP_STATE:
392                         printf(" keep-state");
393                         break;
394                 case DYN_LIMIT:
395                         printf(" limit");
396                         for ( ; p->s != NULL ; p++)
397                                 if (chain->limit_mask & p->x)
398                                         printf(" %s", p->s);
399                         printf(" %d", chain->conn_limit);
400                         break ;
401                 }
402         }
403         /* Direction */
404         if (chain->fw_flg & IP_FW_BRIDGED)
405                 printf(" bridged");
406         if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT))
407                 printf(" in");
408         if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT))
409                 printf(" out");
410
411         /* Handle hack for "via" backwards compatibility */
412         if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
413                 print_iface("via",
414                     &chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME);
415         } else {
416                 /* Receive interface specified */
417                 if (chain->fw_flg & IP_FW_F_IIFACE)
418                         print_iface("recv", &chain->fw_in_if,
419                             chain->fw_flg & IP_FW_F_IIFNAME);
420                 /* Transmit interface specified */
421                 if (chain->fw_flg & IP_FW_F_OIFACE)
422                         print_iface("xmit", &chain->fw_out_if,
423                             chain->fw_flg & IP_FW_F_OIFNAME);
424         }
425
426         if (chain->fw_flg & IP_FW_F_FRAG)
427                 printf(" frag");
428
429         if (chain->fw_ipopt || chain->fw_ipnopt) {
430                 int     _opt_printed = 0;
431 #define PRINTOPT(x)     {if (_opt_printed) printf(",");\
432                         printf(x); _opt_printed = 1;}
433
434                 printf(" ipopt ");
435                 if (chain->fw_ipopt & IP_FW_IPOPT_SSRR)
436                         PRINTOPT("ssrr");
437                 if (chain->fw_ipnopt & IP_FW_IPOPT_SSRR)
438                         PRINTOPT("!ssrr");
439                 if (chain->fw_ipopt & IP_FW_IPOPT_LSRR)
440                         PRINTOPT("lsrr");
441                 if (chain->fw_ipnopt & IP_FW_IPOPT_LSRR)
442                         PRINTOPT("!lsrr");
443                 if (chain->fw_ipopt & IP_FW_IPOPT_RR)
444                         PRINTOPT("rr");
445                 if (chain->fw_ipnopt & IP_FW_IPOPT_RR)
446                         PRINTOPT("!rr");
447                 if (chain->fw_ipopt & IP_FW_IPOPT_TS)
448                         PRINTOPT("ts");
449                 if (chain->fw_ipnopt & IP_FW_IPOPT_TS)
450                         PRINTOPT("!ts");
451         }
452
453         if (chain->fw_ipflg & IP_FW_IF_TCPEST)
454                 printf(" established");
455         else if (chain->fw_tcpf == IP_FW_TCPF_SYN &&
456             chain->fw_tcpnf == IP_FW_TCPF_ACK)
457                 printf(" setup");
458         else if (chain->fw_tcpf || chain->fw_tcpnf) {
459                 int     _flg_printed = 0;
460 #define PRINTFLG(x)     {if (_flg_printed) printf(",");\
461                         printf(x); _flg_printed = 1;}
462
463                 printf(" tcpflags ");
464                 if (chain->fw_tcpf & IP_FW_TCPF_FIN)
465                         PRINTFLG("fin");
466                 if (chain->fw_tcpnf & IP_FW_TCPF_FIN)
467                         PRINTFLG("!fin");
468                 if (chain->fw_tcpf & IP_FW_TCPF_SYN)
469                         PRINTFLG("syn");
470                 if (chain->fw_tcpnf & IP_FW_TCPF_SYN)
471                         PRINTFLG("!syn");
472                 if (chain->fw_tcpf & IP_FW_TCPF_RST)
473                         PRINTFLG("rst");
474                 if (chain->fw_tcpnf & IP_FW_TCPF_RST)
475                         PRINTFLG("!rst");
476                 if (chain->fw_tcpf & IP_FW_TCPF_PSH)
477                         PRINTFLG("psh");
478                 if (chain->fw_tcpnf & IP_FW_TCPF_PSH)
479                         PRINTFLG("!psh");
480                 if (chain->fw_tcpf & IP_FW_TCPF_ACK)
481                         PRINTFLG("ack");
482                 if (chain->fw_tcpnf & IP_FW_TCPF_ACK)
483                         PRINTFLG("!ack");
484                 if (chain->fw_tcpf  & IP_FW_TCPF_URG)
485                         PRINTFLG("urg");
486                 if (chain->fw_tcpnf & IP_FW_TCPF_URG)
487                         PRINTFLG("!urg");
488         }
489         if (chain->fw_tcpopt || chain->fw_tcpnopt) {
490                 int     _opt_printed = 0;
491 #define PRINTTOPT(x)    {if (_opt_printed) printf(",");\
492                         printf(x); _opt_printed = 1;}
493
494                 printf(" tcpoptions ");
495                 if (chain->fw_tcpopt & IP_FW_TCPOPT_MSS)
496                         PRINTTOPT("mss");
497                 if (chain->fw_tcpnopt & IP_FW_TCPOPT_MSS)
498                         PRINTTOPT("!mss");
499                 if (chain->fw_tcpopt & IP_FW_TCPOPT_WINDOW)
500                         PRINTTOPT("window");
501                 if (chain->fw_tcpnopt & IP_FW_TCPOPT_WINDOW)
502                         PRINTTOPT("!window");
503                 if (chain->fw_tcpopt & IP_FW_TCPOPT_SACK)
504                         PRINTTOPT("sack");
505                 if (chain->fw_tcpnopt & IP_FW_TCPOPT_SACK)
506                         PRINTTOPT("!sack");
507                 if (chain->fw_tcpopt & IP_FW_TCPOPT_TS)
508                         PRINTTOPT("ts");
509                 if (chain->fw_tcpnopt & IP_FW_TCPOPT_TS)
510                         PRINTTOPT("!ts");
511                 if (chain->fw_tcpopt & IP_FW_TCPOPT_CC)
512                         PRINTTOPT("cc");
513                 if (chain->fw_tcpnopt & IP_FW_TCPOPT_CC)
514                         PRINTTOPT("!cc");
515         }
516
517         if (chain->fw_flg & IP_FW_F_ICMPBIT) {
518                 int type_index;
519                 int first = 1;
520
521                 printf(" icmptype");
522
523                 for (type_index = 0; type_index < IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8; ++type_index)
524                         if (chain->fw_uar.fw_icmptypes[type_index / (sizeof(unsigned) * 8)] &
525                                 (1U << (type_index % (sizeof(unsigned) * 8)))) {
526                                 printf("%c%d", first == 1 ? ' ' : ',', type_index);
527                                 first = 0;
528                         }
529         }
530         printf("\n");
531 done:
532         if (do_resolv)
533                 endservent();
534 }
535
536 int
537 sort_q(const void *pa, const void *pb)
538 {
539         int rev = (do_sort < 0);
540         int field = rev ? -do_sort : do_sort;
541         long long res = 0;
542         const struct dn_flow_queue *a = pa;
543         const struct dn_flow_queue *b = pb;
544
545         switch (field) {
546         case 1: /* pkts */
547                 res = a->len - b->len;
548                 break;
549         case 2: /* bytes */
550                 res = a->len_bytes - b->len_bytes;
551                 break;
552
553         case 3: /* tot pkts */
554                 res = a->tot_pkts - b->tot_pkts;
555                 break;
556
557         case 4: /* tot bytes */
558                 res = a->tot_bytes - b->tot_bytes;
559                 break;
560         }
561         if (res < 0)
562                 res = -1;
563         if (res > 0)
564                 res = 1;
565         return (int)(rev ? res : -res);
566 }
567
568 static void
569 list_queues(struct dn_flow_set *fs, struct dn_flow_queue *q)
570 {
571         int l;
572
573         printf("    mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
574             fs->flow_mask.proto,
575             fs->flow_mask.src_ip, fs->flow_mask.src_port,
576             fs->flow_mask.dst_ip, fs->flow_mask.dst_port);
577         if (fs->rq_elements == 0)
578                 return;
579
580         printf("BKT Prot ___Source IP/port____ "
581             "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
582         if (do_sort != 0)
583                 heapsort(q, fs->rq_elements, sizeof *q, sort_q);
584         for (l = 0; l < fs->rq_elements; l++) {
585                 struct in_addr ina;
586                 struct protoent *pe;
587
588                 ina.s_addr = htonl(q[l].id.src_ip);
589                 printf("%3d ", q[l].hash_slot);
590                 pe = getprotobynumber(q[l].id.proto);
591                 if (pe)
592                         printf("%-4s ", pe->p_name);
593                 else
594                         printf("%4u ", q[l].id.proto);
595                 printf("%15s/%-5d ",
596                     inet_ntoa(ina), q[l].id.src_port);
597                 ina.s_addr = htonl(q[l].id.dst_ip);
598                 printf("%15s/%-5d ",
599                     inet_ntoa(ina), q[l].id.dst_port);
600                 printf("%4qu %8qu %2u %4u %3u\n",
601                     q[l].tot_pkts, q[l].tot_bytes,
602                     q[l].len, q[l].len_bytes, q[l].drops);
603                 if (verbose)
604                         printf("   S %20qd  F %20qd\n",
605                             q[l].S, q[l].F);
606         }
607 }
608
609 static void
610 print_flowset_parms(struct dn_flow_set *fs, char *prefix)
611 {
612         int l;
613         char qs[30];
614         char plr[30];
615         char red[90];   /* Display RED parameters */
616
617         l = fs->qsize;
618         if (fs->flags_fs & DN_QSIZE_IS_BYTES) {
619                 if (l >= 8192)
620                         sprintf(qs, "%d KB", l / 1024);
621                 else
622                         sprintf(qs, "%d B", l);
623         } else
624                 sprintf(qs, "%3d sl.", l);
625         if (fs->plr)
626                 sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff));
627         else
628                 plr[0] = '\0';
629         if (fs->flags_fs & DN_IS_RED)   /* RED parameters */
630                 sprintf(red,
631                     "\n\t  %cRED w_q %f min_th %d max_th %d max_p %f",
632                     (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ',
633                     1.0 * fs->w_q / (double)(1 << SCALE_RED),
634                     SCALE_VAL(fs->min_th),
635                     SCALE_VAL(fs->max_th),
636                     1.0 * fs->max_p / (double)(1 << SCALE_RED));
637         else
638                 sprintf(red, "droptail");
639
640         printf("%s %s%s %d queues (%d buckets) %s\n",
641             prefix, qs, plr, fs->rq_elements, fs->rq_size, red);
642 }
643
644 static void
645 sysctl_handler(int ac, char *av[], int which)
646 {
647         ac--;
648         av++;
649
650         if (*av == NULL) {
651                 warnx("missing keyword to enable/disable\n");
652         } else if (strncmp(*av, "firewall", strlen(*av)) == 0) {
653                 sysctlbyname("net.inet.ip.fw.enable", NULL, 0,
654                     &which, sizeof(which));
655         } else if (strncmp(*av, "one_pass", strlen(*av)) == 0) {
656                 sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0,
657                     &which, sizeof(which));
658         } else if (strncmp(*av, "debug", strlen(*av)) == 0) {
659                 sysctlbyname("net.inet.ip.fw.debug", NULL, 0,
660                     &which, sizeof(which));
661         } else if (strncmp(*av, "verbose", strlen(*av)) == 0) {
662                 sysctlbyname("net.inet.ip.fw.verbose", NULL, 0,
663                     &which, sizeof(which));
664         } else {
665                 warnx("unrecognize enable/disable keyword: %s\n", *av);
666         }
667 }
668
669 static void
670 list(int ac, char *av[])
671 {
672         struct ip_fw *rules;
673         struct dn_pipe *pipes;
674         void *data = NULL;
675         int pcwidth = 0;
676         int bcwidth = 0;
677         int n, num = 0;
678         int nbytes;
679
680         /* get rules or pipes from kernel, resizing array as necessary */
681         {
682                 const int unit = do_pipe ? sizeof(*pipes) : sizeof(*rules);
683                 const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET;
684                 int nalloc = unit;
685                 nbytes = nalloc;
686
687                 while (nbytes >= nalloc) {
688                         nalloc = nalloc * 2 + 200;
689                         nbytes = nalloc;
690                         if ((data = realloc(data, nbytes)) == NULL)
691                                 err(EX_OSERR, "realloc");
692                         if (getsockopt(s, IPPROTO_IP, ocmd, data, &nbytes) < 0)
693                                 err(EX_OSERR, "getsockopt(IP_%s_GET)",
694                                     do_pipe ? "DUMMYNET" : "FW");
695                 }
696         }
697
698         /* display requested pipes */
699         if (do_pipe) {
700                 u_long rulenum;
701                 void *next = data;
702                 struct dn_pipe *p = (struct dn_pipe *) data;
703                 struct dn_flow_set *fs;
704                 struct dn_flow_queue *q;
705                 int l;
706
707                 if (ac > 0)
708                         rulenum = strtoul(*av++, NULL, 10);
709                 else
710                         rulenum = 0;
711                 for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) {
712                         double b = p->bandwidth;
713                         char buf[30];
714                         char prefix[80];
715
716                         if (p->next != (struct dn_pipe *)DN_IS_PIPE)
717                                 break;
718                         l = sizeof(*p) + p->fs.rq_elements * sizeof(*q);
719                         next = (void *)p + l;
720                         nbytes -= l;
721                         q = (struct dn_flow_queue *)(p+1);
722
723                         if (rulenum != 0 && rulenum != p->pipe_nr)
724                                 continue;
725                         if (p->if_name[0] != '\0')
726                                 sprintf(buf, "%s", p->if_name);
727                         else if (b == 0)
728                                 sprintf(buf, "unlimited");
729                         else if (b >= 1000000)
730                                 sprintf(buf, "%7.3f Mbit/s", b/1000000);
731                         else if (b >= 1000)
732                                 sprintf(buf, "%7.3f Kbit/s", b/1000);
733                         else
734                                 sprintf(buf, "%7.3f bit/s ", b);
735
736                         sprintf(prefix, "%05d: %s %4d ms ",
737                             p->pipe_nr, buf, p->delay);
738                         print_flowset_parms(&(p->fs), prefix);
739                         if (verbose)
740                                 printf("   V %20qd\n", p->V >> MY_M);
741                         list_queues(&(p->fs), q);
742                 }
743                 fs = (struct dn_flow_set *) next;
744                 for (; nbytes >= sizeof *fs; fs = (struct dn_flow_set *)next) {
745                         char prefix[80];
746
747                         if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE)
748                                 break;
749                         l = sizeof(*fs) + fs->rq_elements * sizeof(*q);
750                         next = (void *)fs + l;
751                         nbytes -= l;
752                         q = (struct dn_flow_queue *)(fs+1);
753                         sprintf(prefix, "q%05d: weight %d pipe %d ",
754                             fs->fs_nr, fs->weight, fs->parent_nr);
755                         print_flowset_parms(fs, prefix);
756                         list_queues(fs, q);
757                 }
758                 free(data);
759                 return;
760         }
761
762         rules = (struct ip_fw *)data;
763         /* determine num more accurately */
764         num = 0;
765         while (rules[num].fw_number < 65535)
766                 num++;
767         num++; /* counting starts from 0 ... */
768         /* if showing stats, figure out column widths ahead of time */
769         if (do_acct) {
770                 for (n = 0; n < num; n++) {
771                         struct ip_fw *const r = &rules[n];
772                         char temp[32];
773                         int width;
774
775                         /* packet counter */
776                         width = sprintf(temp, "%qu", r->fw_pcnt);
777                         if (width > pcwidth)
778                                 pcwidth = width;
779
780                         /* byte counter */
781                         width = sprintf(temp, "%qu", r->fw_bcnt);
782                         if (width > bcwidth)
783                                 bcwidth = width;
784                 }
785         }
786         if (ac == 0) {
787                 /* display all rules */
788                 for (n = 0; n < num; n++) {
789                         struct ip_fw *const r = &rules[n];
790
791                         show_ipfw(r, pcwidth, bcwidth);
792                 }
793         } else {
794                 /* display specific rules requested on command line */
795                 int exitval = EX_OK;
796
797                 while (ac--) {
798                         u_long rnum;
799                         char *endptr;
800                         int seen;
801
802                         /* convert command line rule # */
803                         rnum = strtoul(*av++, &endptr, 10);
804                         if (*endptr) {
805                                 exitval = EX_USAGE;
806                                 warnx("invalid rule number: %s", *(av - 1));
807                                 continue;
808                         }
809                         do_dynamic = 0;
810                         for (seen = n = 0; n < num; n++) {
811                                 struct ip_fw *const r = &rules[n];
812
813                                 if (r->fw_number > rnum)
814                                         break;
815                                 if (r->fw_number == rnum) {
816                                         show_ipfw(r, pcwidth, bcwidth);
817                                         seen = 1;
818                                 }
819                         }
820                         if (!seen) {
821                                 /* give precedence to other error(s) */
822                                 if (exitval == EX_OK)
823                                         exitval = EX_UNAVAILABLE;
824                                 warnx("rule %lu does not exist", rnum);
825                         }
826                 }
827                 if (exitval != EX_OK)
828                         exit(exitval);
829         }
830         /*
831          * show dynamic rules
832         */
833         if (do_dynamic && num * sizeof (rules[0]) != nbytes) {
834                 struct ipfw_dyn_rule *d =
835                     (struct ipfw_dyn_rule *)&rules[num];
836                 struct in_addr a;
837                 struct protoent *pe;
838
839             printf("## Dynamic rules:\n");
840             for (;; d++) {
841                 if (d->expire == 0 && !do_expired) {
842                         if (d->next == NULL)
843                                 break;
844                         continue;
845                 }
846
847                 printf("%05d %qu %qu (T %d, slot %d)",
848                     (int)(d->rule),
849                     d->pcnt, d->bcnt,
850                     d->expire,
851                     d->bucket);
852                 switch (d->dyn_type) {
853                 case DYN_LIMIT_PARENT:
854                         printf(" PARENT %d", d->count);
855                         break;
856                 case DYN_LIMIT:
857                         printf(" LIMIT");
858                         break;
859                 case DYN_KEEP_STATE: /* bidir, no mask */
860                         printf(" <->");
861                         break;
862                 }
863
864                 pe = getprotobynumber(d->id.proto);
865                 if (pe)
866                         printf(" %s,", pe->p_name);
867                 else
868                         printf(" %u,", d->id.proto);
869                 a.s_addr = htonl(d->id.src_ip);
870                 printf(" %s %d", inet_ntoa(a), d->id.src_port);
871                 a.s_addr = htonl(d->id.dst_ip);
872                 printf("<-> %s %d", inet_ntoa(a), d->id.dst_port);
873                 printf("\n");
874                 if (d->next == NULL)
875                     break;
876             }
877         }
878
879         free(data);
880 }
881
882 static void
883 show_usage(void)
884 {
885         fprintf(stderr, "usage: ipfw [options]\n"
886 "    [pipe] flush\n"
887 "    add [number] rule\n"
888 "    [pipe] delete number ...\n"
889 "    [pipe] list [number ...]\n"
890 "    [pipe] show [number ...]\n"
891 "    zero [number ...]\n"
892 "    resetlog [number ...]\n"
893 "    pipe number config [pipeconfig]\n"
894 "  rule: [prob <match_probability>] action proto src dst extras...\n"
895 "    action:\n"
896 "      {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
897 "       reset|count|skipto num|divert port|tee port|fwd ip|\n"
898 "       pipe num} [log [logamount count]]\n"
899 "    proto: {ip|tcp|udp|icmp|<number>}\n"
900 "    src: from [not] {me|any|ip[{/bits|:mask}]} [{port[-port]}, [port], ...]\n"
901 "    dst: to [not] {me|any|ip[{/bits|:mask}]} [{port[-port]}, [port], ...]\n"
902 "  extras:\n"
903 "    uid {user id}\n"
904 "    gid {group id}\n"
905 "    fragment     (may not be used with ports or tcpflags)\n"
906 "    in\n"
907 "    out\n"
908 "    {xmit|recv|via} {iface|ip|any}\n"
909 "    {established|setup}\n"
910 "    tcpflags [!]{syn|fin|rst|ack|psh|urg}, ...\n"
911 "    ipoptions [!]{ssrr|lsrr|rr|ts}, ...\n"
912 "    tcpoptions [!]{mss|window|sack|ts|cc}, ...\n"
913 "    icmptypes {type[, type]}...\n"
914 "    keep-state [method]\n"
915 "  pipeconfig:\n"
916 "    {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
917 "    {bw|bandwidth} interface_name\n"
918 "    delay <milliseconds>\n"
919 "    queue <size>{packets|Bytes|KBytes}\n"
920 "    plr <fraction>\n"
921 "    mask {all| [dst-ip|src-ip|dst-port|src-port|proto] <number>}\n"
922 "    buckets <number>}\n"
923 "    {red|gred} <fraction>/<number>/<number>/<fraction>\n"
924 "    droptail\n"
925 );
926
927         exit(EX_USAGE);
928 }
929
930 static int
931 lookup_host (char *host, struct in_addr *ipaddr)
932 {
933         struct hostent *he;
934
935         if (!inet_aton(host, ipaddr)) {
936                 if ((he = gethostbyname(host)) == NULL)
937                         return(-1);
938                 *ipaddr = *(struct in_addr *)he->h_addr_list[0];
939         }
940         return(0);
941 }
942
943 static void
944 fill_ip(struct in_addr *ipno, struct in_addr *mask, int *acp, char ***avp)
945 {
946         int ac = *acp;
947         char **av = *avp;
948         char *p = 0, md = 0;
949
950         if (ac && !strncmp(*av, "any", strlen(*av))) {
951                 ipno->s_addr = mask->s_addr = 0; av++; ac--;
952         } else {
953                 p = strchr(*av, '/');
954                 if (!p)
955                         p = strchr(*av, ':');
956                 if (p) {
957                         md = *p;
958                         *p++ = '\0';
959                 }
960
961                 if (lookup_host(*av, ipno) != 0)
962                         errx(EX_NOHOST, "hostname ``%s'' unknown", *av);
963                 switch (md) {
964                         case ':':
965                                 if (!inet_aton(p, mask))
966                                         errx(EX_DATAERR, "bad netmask ``%s''", p);
967                                 break;
968                         case '/':
969                                 if (atoi(p) == 0) {
970                                         mask->s_addr = 0;
971                                 } else if (atoi(p) > 32) {
972                                         errx(EX_DATAERR, "bad width ``%s''", p);
973                                 } else {
974                                         mask->s_addr =
975                                             htonl(~0 << (32 - atoi(p)));
976                                 }
977                                 break;
978                         default:
979                                 mask->s_addr = htonl(~0);
980                                 break;
981                 }
982                 ipno->s_addr &= mask->s_addr;
983                 av++;
984                 ac--;
985         }
986         *acp = ac;
987         *avp = av;
988 }
989
990 static void
991 fill_reject_code(u_short *codep, char *str)
992 {
993         struct icmpcode *ic;
994         u_long val;
995         char *s;
996
997         if (str == '\0')
998                 errx(EX_DATAERR, "missing unreachable code");
999         val = strtoul(str, &s, 0);
1000         if (s != str && *s == '\0' && val < 0x100) {
1001                 *codep = val;
1002                 return;
1003         }
1004         for (ic = icmpcodes; ic->str; ic++)
1005                 if (!strcasecmp(str, ic->str)) {
1006                         *codep = ic->code;
1007                         return;
1008                 }
1009         errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str);
1010 }
1011
1012 static void
1013 add_port(u_short *cnt, u_short *ptr, u_short off, u_short port)
1014 {
1015         if (off + *cnt >= IP_FW_MAX_PORTS)
1016                 errx(EX_USAGE, "too many ports (max is %d)", IP_FW_MAX_PORTS);
1017         ptr[off+*cnt] = port;
1018         (*cnt)++;
1019 }
1020
1021 static int
1022 lookup_port(const char *arg, int proto, int test, int nodash)
1023 {
1024         int             val;
1025         char            *earg, buf[32];
1026         struct servent  *s;
1027         char            *p, *q;
1028
1029         snprintf(buf, sizeof(buf), "%s", arg);
1030
1031         for (p = q = buf; *p; *q++ = *p++) {
1032                 if (*p == '\\') {
1033                         if (*(p+1))
1034                                 p++;
1035                 } else {
1036                         if (*p == ',' || (nodash && *p == '-'))
1037                                 break;
1038                 }
1039         }
1040         *q = '\0';
1041
1042         val = (int) strtoul(buf, &earg, 0);
1043         if (!*buf || *earg) {
1044                 char *protocol = NULL;
1045
1046                 if (proto != 0) {
1047                         struct protoent *pe = getprotobynumber(proto);
1048
1049                         if (pe)
1050                                 protocol = pe->p_name;
1051                 }
1052
1053                 setservent(1);
1054                 if ((s = getservbyname(buf, protocol))) {
1055                         val = htons(s->s_port);
1056                 } else {
1057                         if (!test)
1058                                 errx(EX_DATAERR, "unknown port ``%s''", buf);
1059                         val = -1;
1060                 }
1061         } else {
1062                 if (val < 0 || val > 0xffff) {
1063                         if (!test)
1064                                 errx(EX_DATAERR,
1065                                     "port ``%s'' out of range", buf);
1066                         val = -1;
1067                 }
1068         }
1069         return(val);
1070 }
1071
1072 /*
1073  * return: 0 normally, 1 if first pair is a range,
1074  * 2 if first pair is a port+mask
1075  */
1076 static int
1077 fill_port(u_short *cnt, u_short *ptr, u_short off, char *arg, int proto)
1078 {
1079         char *s;
1080         int initial_range = 0;
1081
1082         for (s = arg; *s && *s != ',' && *s != '-' && *s != ':'; s++) {
1083                 if (*s == '\\' && *(s+1))
1084                         s++;
1085         }
1086         if (*s == ':') {
1087                 *s++ = '\0';
1088                 if (strchr(arg, ','))
1089                         errx(EX_USAGE, "port/mask must be first in list");
1090                 add_port(cnt, ptr, off,
1091                     *arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
1092                 arg = s;
1093                 s = strchr(arg, ',');
1094                 if (s)
1095                         *s++ = '\0';
1096                 add_port(cnt, ptr, off,
1097                     *arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
1098                 arg = s;
1099                 initial_range = 2;
1100         } else
1101         if (*s == '-') {
1102                 *s++ = '\0';
1103                 if (strchr(arg, ','))
1104                         errx(EX_USAGE, "port range must be first in list");
1105                 add_port(cnt, ptr, off,
1106                     *arg ? lookup_port(arg, proto, 0, 0) : 0x0000);
1107                 arg = s;
1108                 s = strchr(arg, ',');
1109                 if (s)
1110                         *s++ = '\0';
1111                 add_port(cnt, ptr, off,
1112                     *arg ? lookup_port(arg, proto, 0, 0) : 0xffff);
1113                 arg = s;
1114                 initial_range = 1;
1115         }
1116         while (arg != NULL) {
1117                 s = strchr(arg, ',');
1118                 if (s)
1119                         *s++ = '\0';
1120                 add_port(cnt, ptr, off, lookup_port(arg, proto, 0, 0));
1121                 arg = s;
1122         }
1123         return initial_range;
1124 }
1125
1126 static void
1127 fill_tcpflag(u_char *set, u_char *reset, char **vp)
1128 {
1129         char *p = *vp, *q;
1130         u_char *d;
1131
1132         while (p && *p) {
1133                 struct tpcflags {
1134                         char * name;
1135                         u_char value;
1136                 } flags[] = {
1137                         { "syn", IP_FW_TCPF_SYN },
1138                         { "fin", IP_FW_TCPF_FIN },
1139                         { "ack", IP_FW_TCPF_ACK },
1140                         { "psh", IP_FW_TCPF_PSH },
1141                         { "rst", IP_FW_TCPF_RST },
1142                         { "urg", IP_FW_TCPF_URG }
1143                 };
1144                 int i;
1145
1146                 if (*p == '!') {
1147                         p++;
1148                         d = reset;
1149                 } else {
1150                         d = set;
1151                 }
1152                 q = strchr(p, ',');
1153                 if (q)
1154                         *q++ = '\0';
1155                 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
1156                         if (!strncmp(p, flags[i].name, strlen(p))) {
1157                                 *d |= flags[i].value;
1158                                 break;
1159                         }
1160                 if (i == sizeof(flags) / sizeof(flags[0]))
1161                         errx(EX_DATAERR, "invalid tcp flag ``%s''", p);
1162                 p = q;
1163         }
1164 }
1165
1166 static void
1167 fill_tcpopts(u_char *set, u_char *reset, char **vp)
1168 {
1169         char *p = *vp, *q;
1170         u_char *d;
1171
1172         while (p && *p) {
1173                 struct tpcopts {
1174                         const char * name;
1175                         u_char value;
1176                 } opts[] = {
1177                         { "mss", IP_FW_TCPOPT_MSS },
1178                         { "window", IP_FW_TCPOPT_WINDOW },
1179                         { "sack", IP_FW_TCPOPT_SACK },
1180                         { "ts", IP_FW_TCPOPT_TS },
1181                         { "cc", IP_FW_TCPOPT_CC },
1182                 };
1183                 int i;
1184
1185                 if (*p == '!') {
1186                         p++;
1187                         d = reset;
1188                 } else {
1189                         d = set;
1190                 }
1191                 q = strchr(p, ',');
1192                 if (q)
1193                         *q++ = '\0';
1194                 for (i = 0; i < sizeof(opts) / sizeof(opts[0]); ++i)
1195                         if (!strncmp(p, opts[i].name, strlen(p))) {
1196                                 *d |= opts[i].value;
1197                                 break;
1198                         }
1199                 if (i == sizeof(opts) / sizeof(opts[0]))
1200                         errx(EX_DATAERR, "invalid tcp option ``%s''", p);
1201                 p = q;
1202         }
1203 }
1204
1205 static void
1206 fill_ipopt(u_char *set, u_char *reset, char **vp)
1207 {
1208         char *p = *vp, *q;
1209         u_char *d;
1210
1211         while (p && *p) {
1212                 if (*p == '!') {
1213                         p++;
1214                         d = reset;
1215                 } else {
1216                         d = set;
1217                 }
1218                 q = strchr(p, ',');
1219                 if (q)
1220                         *q++ = '\0';
1221                 if (!strncmp(p, "ssrr", strlen(p))) *d |= IP_FW_IPOPT_SSRR;
1222                 if (!strncmp(p, "lsrr", strlen(p))) *d |= IP_FW_IPOPT_LSRR;
1223                 if (!strncmp(p, "rr", strlen(p)))   *d |= IP_FW_IPOPT_RR;
1224                 if (!strncmp(p, "ts", strlen(p)))   *d |= IP_FW_IPOPT_TS;
1225                 p = q;
1226         }
1227 }
1228
1229 static void
1230 fill_icmptypes(unsigned *types, char **vp, u_int *fw_flg)
1231 {
1232         unsigned long icmptype;
1233         char *c = *vp;
1234
1235         while (*c) {
1236                 if (*c == ',')
1237                         ++c;
1238
1239                 icmptype = strtoul(c, &c, 0);
1240
1241                 if (*c != ',' && *c != '\0')
1242                         errx(EX_DATAERR, "invalid ICMP type");
1243
1244                 if (icmptype >= IP_FW_ICMPTYPES_DIM * sizeof(unsigned) * 8)
1245                         errx(EX_DATAERR, "ICMP type out of range");
1246
1247                 types[icmptype / (sizeof(unsigned) * 8)] |=
1248                         1 << (icmptype % (sizeof(unsigned) * 8));
1249                 *fw_flg |= IP_FW_F_ICMPBIT;
1250         }
1251 }
1252
1253 static void
1254 delete(int ac, char *av[])
1255 {
1256         struct ip_fw rule;
1257         struct dn_pipe pipe;
1258         int i;
1259         int exitval = EX_OK;
1260
1261         memset(&rule, 0, sizeof rule);
1262         memset(&pipe, 0, sizeof pipe);
1263
1264         av++; ac--;
1265
1266         /* Rule number */
1267         while (ac && isdigit(**av)) {
1268                 i = atoi(*av); av++; ac--;
1269                 if (do_pipe) {
1270                         if (do_pipe == 1)
1271                                 pipe.pipe_nr = i;
1272                         else
1273                                 pipe.fs.fs_nr = i;
1274                         i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_DEL,
1275                             &pipe, sizeof pipe);
1276                         if (i) {
1277                                 exitval = 1;
1278                                 warn("rule %u: setsockopt(IP_DUMMYNET_DEL)",
1279                                     do_pipe == 1 ? pipe.pipe_nr :
1280                                     pipe.fs.fs_nr);
1281                         }
1282                 } else {
1283                         rule.fw_number = i;
1284                         i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule,
1285                             sizeof rule);
1286                         if (i) {
1287                                 exitval = EX_UNAVAILABLE;
1288                                 warn("rule %u: setsockopt(IP_FW_DEL)",
1289                                     rule.fw_number);
1290                         }
1291                 }
1292         }
1293         if (exitval != EX_OK)
1294                 exit(exitval);
1295 }
1296
1297 static void
1298 verify_interface(union ip_fw_if *ifu)
1299 {
1300         struct ifreq ifr;
1301
1302         strlcpy(ifr.ifr_name, ifu->fu_via_if.name, sizeof(ifr.ifr_name));
1303
1304         if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
1305                 warnx("warning: interface ``%s'' does not exist",
1306                     ifr.ifr_name);
1307 }
1308
1309 static void
1310 fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg)
1311 {
1312         if (!ac)
1313             errx(EX_USAGE, "missing argument for ``%s''", which);
1314
1315         /* Parse the interface or address */
1316         if (!strcmp(arg, "any")) {
1317                 ifu->fu_via_ip.s_addr = 0;
1318                 *byname = 0;
1319         } else if (!isdigit(*arg)) {
1320                 *byname = 1;
1321                 strlcpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name));
1322                 /*
1323                  * We assume that strings containing '*', '?', or '['
1324                  * are meant to be shell pattern.
1325                  */
1326                 if (strpbrk(arg, "*?[") != NULL) {
1327                         ifu->fu_via_if.glob = 1;
1328                 } else {
1329                         ifu->fu_via_if.glob = 0;
1330                         verify_interface(ifu);
1331                 }
1332         } else if (!inet_aton(arg, &ifu->fu_via_ip)) {
1333                 errx(EX_DATAERR, "bad ip address ``%s''", arg);
1334         } else
1335                 *byname = 0;
1336 }
1337
1338 static unsigned long
1339 getbw(const char *str, u_short *flags, int kb)
1340 {
1341     char *end;
1342     unsigned long val;
1343     int inbytes = 0;
1344
1345     val = strtoul(str, &end, 0);
1346     if (*end == 'k' || *end == 'K') {
1347         ++end;
1348         val *= kb;
1349     } else if (*end == 'm' || *end == 'M') {
1350         ++end;
1351         val *= kb * kb;
1352     }
1353
1354     /*
1355      * Deal with bits or bytes or b(bits) or B(bytes).  If there is no
1356      * trailer assume bits.
1357      */
1358     if (strncasecmp(end, "bit", 3) == 0) {
1359         ;
1360     } else if (strncasecmp(end, "byte", 4) == 0) {
1361         inbytes = 1;
1362     } else if (*end == 'b') {
1363         ;
1364     } else if (*end == 'B') {
1365         inbytes = 1;
1366     }
1367
1368     /*
1369      * Return in bits if flags is NULL, else flag bits
1370      * or bytes in flags and return the unconverted value.
1371      */
1372     if (inbytes && flags)
1373         *flags |= DN_QSIZE_IS_BYTES;
1374     else if (inbytes && flags == NULL)
1375         val *= 8;
1376     return(val);
1377 }
1378
1379 static void
1380 config_pipe(int ac, char **av)
1381 {
1382         struct dn_pipe pipe;
1383         int i;
1384         char *end;
1385
1386         memset(&pipe, 0, sizeof pipe);
1387
1388         av++; ac--;
1389         /* Pipe number */
1390         if (ac && isdigit(**av)) {
1391                 i = atoi(*av); av++; ac--;
1392                 if (do_pipe == 1)
1393                         pipe.pipe_nr = i;
1394                 else
1395                         pipe.fs.fs_nr = i;
1396         }
1397         while (ac > 1) {
1398                 if (!strncmp(*av, "plr", strlen(*av))) {
1399
1400                         double d = strtod(av[1], NULL);
1401                         if (d > 1)
1402                                 d = 1;
1403                         else if (d < 0)
1404                                 d = 0;
1405                         pipe.fs.plr = (int)(d*0x7fffffff);
1406                         av += 2;
1407                         ac -= 2;
1408                 } else if (!strncmp(*av, "queue", strlen(*av))) {
1409                         pipe.fs.qsize = getbw(av[1], &pipe.fs.flags_fs, 1024);
1410                         av += 2;
1411                         ac -= 2;
1412                 } else if (!strncmp(*av, "buckets", strlen(*av))) {
1413                         pipe.fs.rq_size = strtoul(av[1], NULL, 0);
1414                         av += 2;
1415                         ac -= 2;
1416                 } else if (!strncmp(*av, "mask", strlen(*av))) {
1417                         /* per-flow queue, mask is dst_ip, dst_port,
1418                          * src_ip, src_port, proto measured in bits
1419                          */
1420                         u_int32_t a;
1421                         void *par = NULL;
1422
1423                         pipe.fs.flow_mask.dst_ip = 0;
1424                         pipe.fs.flow_mask.src_ip = 0;
1425                         pipe.fs.flow_mask.dst_port = 0;
1426                         pipe.fs.flow_mask.src_port = 0;
1427                         pipe.fs.flow_mask.proto = 0;
1428                         end = NULL;
1429                         av++; ac--;
1430                         if (ac >= 1 && !strncmp(*av, "all", strlen(*av))) {
1431                                 /* special case -- all bits are significant */
1432                                 pipe.fs.flow_mask.dst_ip = ~0;
1433                                 pipe.fs.flow_mask.src_ip = ~0;
1434                                 pipe.fs.flow_mask.dst_port = ~0;
1435                                 pipe.fs.flow_mask.src_port = ~0;
1436                                 pipe.fs.flow_mask.proto = ~0;
1437                                 pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
1438                                 av++;
1439                                 ac--;
1440                                 continue;
1441                         }
1442                         while (ac >= 1) {
1443                                 int len = strlen(*av);
1444
1445                                 if (!strncmp(*av, "dst-ip", len))
1446                                         par = &pipe.fs.flow_mask.dst_ip;
1447                                 else if (!strncmp(*av, "src-ip", len))
1448                                         par = &pipe.fs.flow_mask.src_ip;
1449                                 else if (!strncmp(*av, "dst-port", len))
1450                                         par = &pipe.fs.flow_mask.dst_port;
1451                                 else if (!strncmp(*av, "src-port", len))
1452                                         par = &pipe.fs.flow_mask.src_port;
1453                                 else if (!strncmp(*av, "proto", len))
1454                                         par = &pipe.fs.flow_mask.proto;
1455                                 else
1456                                         break;
1457                                 if (ac < 2)
1458                                         errx(EX_USAGE, "mask: %s value"
1459                                             " missing", *av);
1460                                 if (*av[1] == '/') {
1461                                         a = strtoul(av[1]+1, &end, 0);
1462                                         if (a == 32) /* special case... */
1463                                                 a = ~0;
1464                                         else
1465                                                 a = (1 << a) - 1;
1466                                         fprintf(stderr, " mask is 0x%08x\n", a);
1467                                 } else {
1468                                         a = strtoul(av[1], &end, 0);
1469                                 }
1470                                 if (par == &pipe.fs.flow_mask.src_port
1471                                     || par == &pipe.fs.flow_mask.dst_port) {
1472                                         if (a >= (1 << 16))
1473                                                 errx(EX_DATAERR, "mask: %s"
1474                                                     " must be 16 bit, not"
1475                                                     " 0x%08x", *av, a);
1476                                         *((u_int16_t *)par) = (u_int16_t)a;
1477                                 } else if (par == &pipe.fs.flow_mask.proto) {
1478                                         if (a >= (1 << 8))
1479                                                 errx(EX_DATAERR, "mask: %s"
1480                                                     " must be"
1481                                                     " 8 bit, not 0x%08x",
1482                                                     *av, a);
1483                                         *((u_int8_t *)par) = (u_int8_t)a;
1484                                 } else
1485                                         *((u_int32_t *)par) = a;
1486                                 if (a != 0)
1487                                         pipe.fs.flags_fs |= DN_HAVE_FLOW_MASK;
1488                                 av += 2;
1489                                 ac -= 2;
1490                         } /* end for */
1491                 } else if (!strncmp(*av, "red", strlen(*av))
1492                     || !strncmp(*av, "gred", strlen(*av))) {
1493                         /* RED enabled */
1494                         pipe.fs.flags_fs |= DN_IS_RED;
1495                         if (*av[0] == 'g')
1496                                 pipe.fs.flags_fs |= DN_IS_GENTLE_RED;
1497                         if ((end = strsep(&av[1], "/"))) {
1498                                 double w_q = strtod(end, NULL);
1499                                 if (w_q > 1 || w_q <= 0)
1500                                         errx(EX_DATAERR, "w_q %f must be "
1501                                             "0 < x <= 1", w_q);
1502                                 pipe.fs.w_q = (int) (w_q * (1 << SCALE_RED));
1503                         }
1504                         if ((end = strsep(&av[1], "/"))) {
1505                                 pipe.fs.min_th = strtoul(end, &end, 0);
1506                                 if (*end == 'K' || *end == 'k')
1507                                         pipe.fs.min_th *= 1024;
1508                         }
1509                         if ((end = strsep(&av[1], "/"))) {
1510                                 pipe.fs.max_th = strtoul(end, &end, 0);
1511                                 if (*end == 'K' || *end == 'k')
1512                                         pipe.fs.max_th *= 1024;
1513                         }
1514                         if ((end = strsep(&av[1], "/"))) {
1515                                 double max_p = strtod(end, NULL);
1516                                 if (max_p > 1 || max_p <= 0)
1517                                         errx(EX_DATAERR, "max_p %f must be "
1518                                             "0 < x <= 1", max_p);
1519                                 pipe.fs.max_p =
1520                                     (int)(max_p * (1 << SCALE_RED));
1521                         }
1522                         av += 2;
1523                         ac -= 2;
1524                 } else if (!strncmp(*av, "droptail", strlen(*av))) {
1525                         /* DROPTAIL */
1526                         pipe.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED);
1527                         av += 1;
1528                         ac -= 1;
1529                 } else {
1530                         int len = strlen(*av);
1531                         if (do_pipe == 1) {
1532                                 /* some commands are only good for pipes. */
1533                                 if (!strncmp(*av, "bw", len)
1534                                     || !strncmp(*av, "bandwidth", len)) {
1535                                         if (av[1][0] >= 'a'
1536                                             && av[1][0] <= 'z') {
1537                                                 int l = sizeof(pipe.if_name)-1;
1538                                                 /* interface name */
1539                                                 strncpy(pipe.if_name, av[1], l);
1540                                                 pipe.if_name[l] = '\0';
1541                                                 pipe.bandwidth = 0;
1542                                         } else {
1543                                                 pipe.if_name[0] = '\0';
1544                                                 pipe.bandwidth = 
1545                                                     getbw(av[1], NULL, 1000);
1546                                         }
1547                                         if (pipe.bandwidth < 0)
1548                                                 errx(EX_DATAERR,
1549                                                     "bandwidth too large");
1550                                         av += 2;
1551                                         ac -= 2;
1552                                 } else if (!strncmp(*av, "delay", len)) {
1553                                         pipe.delay = strtoul(av[1], NULL, 0);
1554                                         av += 2;
1555                                         ac -= 2;
1556                                 } else {
1557                                         errx(EX_DATAERR, "unrecognised pipe"
1558                                             " option ``%s''", *av);
1559                                 }
1560                         } else { /* this refers to a queue */
1561                                 if (!strncmp(*av, "weight", len)) {
1562                                         pipe.fs.weight =
1563                                             strtoul(av[1], &end, 0);
1564                                         av += 2;
1565                                         ac -= 2;
1566                                 } else if (!strncmp(*av, "pipe", len)) {
1567                                         pipe.fs.parent_nr =
1568                                             strtoul(av[1], &end, 0);
1569                                         av += 2;
1570                                         ac -= 2;
1571                                 } else {
1572                                         errx(EX_DATAERR, "unrecognised option "
1573                                             "``%s''", *av);
1574                                 }
1575                         }
1576                 }
1577         }
1578         if (do_pipe == 1) {
1579                 if (pipe.pipe_nr == 0)
1580                         errx(EX_DATAERR, "pipe_nr %d must be > 0",
1581                             pipe.pipe_nr);
1582                 if (pipe.delay > 10000)
1583                         errx(EX_DATAERR, "delay %d must be < 10000",
1584                             pipe.delay);
1585         } else { /* do_pipe == 2, queue */
1586                 if (pipe.fs.parent_nr == 0)
1587                         errx(EX_DATAERR, "pipe %d must be > 0",
1588                             pipe.fs.parent_nr);
1589                 if (pipe.fs.weight >100)
1590                         errx(EX_DATAERR, "weight %d must be <= 100",
1591                             pipe.fs.weight);
1592         }
1593         if (pipe.fs.flags_fs & DN_QSIZE_IS_BYTES) {
1594                 if (pipe.fs.qsize > 1024*1024)
1595                         errx(EX_DATAERR, "queue size %d, must be < 1MB",
1596                             pipe.fs.qsize);
1597         } else {
1598                 if (pipe.fs.qsize > 100)
1599                         errx(EX_DATAERR, "queue size %d, must be"
1600                             " 2 <= x <= 100", pipe.fs.qsize);
1601         }
1602         if (pipe.fs.flags_fs & DN_IS_RED) {
1603                 size_t len;
1604                 int lookup_depth, avg_pkt_size;
1605                 double s, idle, weight, w_q;
1606                 struct clockinfo clock;
1607                 int t;
1608
1609                 if (pipe.fs.min_th >= pipe.fs.max_th)
1610                         errx(EX_DATAERR, "min_th %d must be < than max_th %d",
1611                             pipe.fs.min_th, pipe.fs.max_th);
1612                 if (pipe.fs.max_th == 0)
1613                         errx(EX_DATAERR, "max_th must be > 0");
1614
1615                 len = sizeof(int);
1616                 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
1617                             &lookup_depth, &len, NULL, 0) == -1)
1618
1619                 errx(1, "sysctlbyname(\"%s\")",
1620                     "net.inet.ip.dummynet.red_lookup_depth");
1621                 if (lookup_depth == 0)
1622                         errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth"
1623                             " must be greater than zero");
1624
1625                 len = sizeof(int);
1626                 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
1627                             &avg_pkt_size, &len, NULL, 0) == -1)
1628
1629                         errx(1, "sysctlbyname(\"%s\")",
1630                             "net.inet.ip.dummynet.red_avg_pkt_size");
1631                 if (avg_pkt_size == 0)
1632                         errx(EX_DATAERR,
1633                             "net.inet.ip.dummynet.red_avg_pkt_size must"
1634                             " be greater than zero");
1635
1636                 len = sizeof(struct clockinfo);
1637                 if (sysctlbyname("kern.clockrate", &clock, &len, NULL, 0) == -1)
1638                         errx(1, "sysctlbyname(\"%s\")",
1639                             "kern.clockrate");
1640
1641                 /*
1642                  * Ticks needed for sending a medium-sized packet.
1643                  * Unfortunately, when we are configuring a WF2Q+ queue, we
1644                  * do not have bandwidth information, because that is stored
1645                  * in the parent pipe, and also we have multiple queues
1646                  * competing for it. So we set s=0, which is not very
1647                  * correct. But on the other hand, why do we want RED with
1648                  * WF2Q+ ?
1649                  */
1650                 if (pipe.bandwidth==0) /* this is a WF2Q+ queue */
1651                         s = 0;
1652                 else
1653                         s = clock.hz * avg_pkt_size * 8 / pipe.bandwidth;
1654
1655                 /*
1656                  * max idle time (in ticks) before avg queue size
1657                  * becomes 0.
1658                  * NOTA:  (3/w_q) is approx the value x so that
1659                  * (1-w_q)^x < 10^-3.
1660                  */
1661                 w_q = ((double)pipe.fs.w_q) / (1 << SCALE_RED);
1662                 idle = s * 3. / w_q;
1663                 pipe.fs.lookup_step = (int)idle / lookup_depth;
1664                 if (!pipe.fs.lookup_step)
1665                         pipe.fs.lookup_step = 1;
1666                 weight = 1 - w_q;
1667                 for (t = pipe.fs.lookup_step; t > 0; --t)
1668                         weight *= weight;
1669                 pipe.fs.lookup_weight = (int)(weight * (1 << SCALE_RED));
1670         }
1671         i = setsockopt(s, IPPROTO_IP, IP_DUMMYNET_CONFIGURE, &pipe,
1672             sizeof pipe);
1673         if (i)
1674                 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
1675 }
1676
1677 static void
1678 add(int ac, char *av[])
1679 {
1680         struct ip_fw rule;
1681         int i;
1682         u_char proto;
1683         struct protoent *pe;
1684         int saw_xmrc = 0, saw_via = 0;
1685
1686         memset(&rule, 0, sizeof rule);
1687
1688         av++; ac--;
1689
1690         /* Rule number */
1691         if (ac && isdigit(**av)) {
1692                 rule.fw_number = atoi(*av); av++; ac--;
1693         }
1694
1695         /* Action */
1696         if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) {
1697                 double d = strtod(av[1], NULL);
1698                 if (d <= 0 || d > 1)
1699                         errx(EX_DATAERR, "illegal match prob. %s", av[1]);
1700                 if (d != 1) { /* 1 means always match */
1701                         rule.fw_flg |= IP_FW_F_RND_MATCH;
1702                         rule.dont_match_prob = (long)((1 - d) * 0x7fffffff);
1703                 }
1704                 av += 2; ac -= 2;
1705         }
1706
1707         if (ac == 0)
1708                 errx(EX_USAGE, "missing action");
1709         if (!strncmp(*av, "accept", strlen(*av))
1710                     || !strncmp(*av, "pass", strlen(*av))
1711                     || !strncmp(*av, "allow", strlen(*av))
1712                     || !strncmp(*av, "permit", strlen(*av))) {
1713                 rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
1714         } else if (!strncmp(*av, "count", strlen(*av))) {
1715                 rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
1716         } else if (!strncmp(*av, "pipe", strlen(*av))) {
1717                 rule.fw_flg |= IP_FW_F_PIPE; av++; ac--;
1718                 if (!ac)
1719                         errx(EX_USAGE, "missing pipe number");
1720                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
1721         } else if (!strncmp(*av, "queue", strlen(*av))) {
1722                 rule.fw_flg |= IP_FW_F_QUEUE; av++; ac--;
1723                 if (!ac)
1724                         errx(EX_USAGE, "missing queue number");
1725                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
1726         } else if (!strncmp(*av, "divert", strlen(*av))) {
1727                 rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--;
1728                 if (!ac)
1729                         errx(EX_USAGE, "missing %s port", "divert");
1730                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
1731                 if (rule.fw_divert_port == 0) {
1732                         struct servent *s;
1733                         setservent(1);
1734                         s = getservbyname(av[-1], "divert");
1735                         if (s != NULL)
1736                                 rule.fw_divert_port = ntohs(s->s_port);
1737                         else
1738                                 errx(EX_DATAERR, "illegal %s port", "divert");
1739                 }
1740         } else if (!strncmp(*av, "tee", strlen(*av))) {
1741                 rule.fw_flg |= IP_FW_F_TEE; av++; ac--;
1742                 if (!ac)
1743                         errx(EX_USAGE, "missing %s port", "tee divert");
1744                 rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--;
1745                 if (rule.fw_divert_port == 0) {
1746                         struct servent *s;
1747                         setservent(1);
1748                         s = getservbyname(av[-1], "divert");
1749                         if (s != NULL)
1750                                 rule.fw_divert_port = ntohs(s->s_port);
1751                         else
1752                                 errx(EX_DATAERR, "illegal %s port",
1753                                     "tee divert");
1754                 }
1755         } else if (!strncmp(*av, "fwd", strlen(*av))
1756             || !strncmp(*av, "forward", strlen(*av))) {
1757                 struct in_addr dummyip;
1758                 char *pp;
1759                 rule.fw_flg |= IP_FW_F_FWD; av++; ac--;
1760                 if (!ac)
1761                         errx(EX_USAGE, "missing forwarding IP address");
1762                 rule.fw_fwd_ip.sin_len = sizeof(struct sockaddr_in);
1763                 rule.fw_fwd_ip.sin_family = AF_INET;
1764                 rule.fw_fwd_ip.sin_port = 0;
1765                 pp = strchr(*av, ':');
1766                 if(pp == NULL)
1767                         pp = strchr(*av, ',');
1768                 if(pp != NULL)
1769                 {
1770                         *(pp++) = '\0';
1771                         i = lookup_port(pp, 0, 1, 0);
1772                         if (i == -1)
1773                                 errx(EX_DATAERR, "illegal forwarding"
1774                                     " port ``%s''", pp);
1775                         else
1776                                 rule.fw_fwd_ip.sin_port = (u_short)i;
1777                 }
1778                 fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av);
1779                 if (rule.fw_fwd_ip.sin_addr.s_addr == 0)
1780                         errx(EX_DATAERR, "illegal forwarding IP address");
1781
1782         } else if (!strncmp(*av, "skipto", strlen(*av))) {
1783                 rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
1784                 if (!ac)
1785                         errx(EX_USAGE, "missing skipto rule number");
1786                 rule.fw_skipto_rule = strtoul(*av, NULL, 10); av++; ac--;
1787         } else if ((!strncmp(*av, "deny", strlen(*av))
1788                     || !strncmp(*av, "drop", strlen(*av)))) {
1789                 rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
1790         } else if (!strncmp(*av, "reject", strlen(*av))) {
1791                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
1792                 rule.fw_reject_code = ICMP_UNREACH_HOST;
1793         } else if (!strncmp(*av, "reset", strlen(*av))) {
1794                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
1795                 rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */
1796         } else if (!strncmp(*av, "unreach", strlen(*av))) {
1797                 rule.fw_flg |= IP_FW_F_REJECT; av++; ac--;
1798                 fill_reject_code(&rule.fw_reject_code, *av); av++; ac--;
1799         } else if (!strncmp(*av, "check-state", strlen(*av))) {
1800                 rule.fw_flg |= IP_FW_F_CHECK_S; av++; ac--;
1801                 goto done;
1802         } else {
1803                 errx(EX_DATAERR, "invalid action ``%s''", *av);
1804         }
1805
1806         /* [log] */
1807         if (ac && !strncmp(*av, "log", strlen(*av))) {
1808                 rule.fw_flg |= IP_FW_F_PRN; av++; ac--;
1809         }
1810         if (ac && !strncmp(*av, "logamount", strlen(*av))) {
1811                 if (!(rule.fw_flg & IP_FW_F_PRN))
1812                         errx(EX_USAGE, "``logamount'' not valid without"
1813                              " ``log''");
1814                 ac--; av++;
1815                 if (!ac)
1816                         errx(EX_USAGE, "``logamount'' requires argument");
1817                 rule.fw_logamount = atoi(*av);
1818                 if (rule.fw_logamount < 0)
1819                         errx(EX_DATAERR, "``logamount'' argument must be"
1820                              " positive");
1821                 if (rule.fw_logamount == 0)
1822                         rule.fw_logamount = -1;
1823                 ac--; av++;
1824         }
1825
1826         /* protocol */
1827         if (ac == 0)
1828                 errx(EX_USAGE, "missing protocol");
1829         if ((proto = atoi(*av)) > 0) {
1830                 rule.fw_prot = proto; av++; ac--;
1831         } else if (!strncmp(*av, "all", strlen(*av))) {
1832                 rule.fw_prot = IPPROTO_IP; av++; ac--;
1833         } else if ((pe = getprotobyname(*av)) != NULL) {
1834                 rule.fw_prot = pe->p_proto; av++; ac--;
1835         } else {
1836                 errx(EX_DATAERR, "invalid protocol ``%s''", *av);
1837         }
1838
1839         if (rule.fw_prot != IPPROTO_TCP
1840             && (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT
1841             && rule.fw_reject_code == IP_FW_REJECT_RST)
1842                 errx(EX_DATAERR, "``reset'' is only valid for tcp packets");
1843
1844         /* from */
1845         if (ac && !strncmp(*av, "from", strlen(*av))) { av++; ac--; }
1846         else
1847                 errx(EX_USAGE, "missing ``from''");
1848
1849         if (ac && !strncmp(*av, "not", strlen(*av))) {
1850                 rule.fw_flg |= IP_FW_F_INVSRC;
1851                 av++; ac--;
1852         }
1853         if (!ac)
1854                 errx(EX_USAGE, "missing arguments");
1855
1856         if (ac && !strncmp(*av, "me", strlen(*av))) {
1857                 rule.fw_flg |= IP_FW_F_SME;
1858                 av++; ac--;
1859         } else {
1860                 fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av);
1861         }
1862
1863         if (ac && (isdigit(**av)
1864             || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
1865                 u_short nports = 0;
1866                 int retval;
1867
1868                 retval = fill_port(&nports, rule.fw_uar.fw_pts,
1869                     0, *av, rule.fw_prot);
1870                 if (retval == 1)
1871                         rule.fw_flg |= IP_FW_F_SRNG;
1872                 else if (retval == 2)
1873                         rule.fw_flg |= IP_FW_F_SMSK;
1874                 IP_FW_SETNSRCP(&rule, nports);
1875                 av++; ac--;
1876         }
1877
1878         /* to */
1879         if (ac && !strncmp(*av, "to", strlen(*av))) { av++; ac--; }
1880         else
1881                 errx(EX_USAGE, "missing ``to''");
1882
1883         if (ac && !strncmp(*av, "not", strlen(*av))) {
1884                 rule.fw_flg |= IP_FW_F_INVDST;
1885                 av++; ac--;
1886         }
1887         if (!ac)
1888                 errx(EX_USAGE, "missing arguments");
1889
1890
1891         if (ac && !strncmp(*av, "me", strlen(*av))) {
1892                 rule.fw_flg |= IP_FW_F_DME;
1893                 av++; ac--;
1894         } else {
1895                 fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av);
1896         }
1897
1898         if (ac && (isdigit(**av)
1899             || lookup_port(*av, rule.fw_prot, 1, 1) >= 0)) {
1900                 u_short nports = 0;
1901                 int retval;
1902
1903                 retval = fill_port(&nports, rule.fw_uar.fw_pts,
1904                     IP_FW_GETNSRCP(&rule), *av, rule.fw_prot);
1905                 if (retval == 1)
1906                         rule.fw_flg |= IP_FW_F_DRNG;
1907                 else if (retval == 2)
1908                         rule.fw_flg |= IP_FW_F_DMSK;
1909                 IP_FW_SETNDSTP(&rule, nports);
1910                 av++; ac--;
1911         }
1912
1913         if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP)
1914             && (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) {
1915                 errx(EX_USAGE, "only TCP and UDP protocols are valid"
1916                     " with port specifications");
1917         }
1918
1919         while (ac) {
1920                 if (!strncmp(*av, "uid", strlen(*av))) {
1921                         struct passwd *pwd;
1922                         char *end;
1923                         uid_t uid;
1924
1925                         rule.fw_flg |= IP_FW_F_UID;
1926                         ac--; av++;
1927                         if (!ac)
1928                                 errx(EX_USAGE, "``uid'' requires argument");
1929
1930                         uid = strtoul(*av, &end, 0);
1931                         if (*end == '\0')
1932                                 pwd = getpwuid(uid);
1933                         else
1934                                 pwd = getpwnam(*av);
1935                         if (pwd == NULL)
1936                                 errx(EX_DATAERR, "uid \"%s\" is"
1937                                      " nonexistent", *av);
1938                         rule.fw_uid = pwd->pw_uid;
1939                         ac--; av++;
1940                 } else if (!strncmp(*av, "gid", strlen(*av))) {
1941                         struct group *grp;
1942                         char *end;
1943                         gid_t gid;
1944
1945                         rule.fw_flg |= IP_FW_F_GID;
1946                         ac--; av++;
1947                         if (!ac)
1948                                 errx(EX_USAGE, "``gid'' requires argument");
1949
1950                         gid = strtoul(*av, &end, 0);
1951                         if (*end == '\0')
1952                                 grp = getgrgid(gid);
1953                         else
1954                                 grp = getgrnam(*av);
1955                         if (grp == NULL)
1956                                 errx(EX_DATAERR, "gid \"%s\" is"
1957                                      " nonexistent", *av);
1958                         rule.fw_gid = grp->gr_gid;
1959                         ac--; av++;
1960                 } else if (!strncmp(*av, "in", strlen(*av))) {
1961                         rule.fw_flg |= IP_FW_F_IN;
1962                         av++; ac--;
1963                 } else if (!strncmp(*av,"limit",strlen(*av))) {
1964                         /* dyn. rule used to limit number of connections. */
1965                         rule.fw_flg |= IP_FW_F_KEEP_S;
1966                         rule.dyn_type = DYN_LIMIT ;
1967                         rule.limit_mask = 0 ;
1968                         av++; ac--;
1969                         for (; ac >1 ;) {
1970                             struct _s_x *p = limit_masks;
1971                             for ( ; p->s != NULL ; p++)
1972                                 if (!strncmp(*av, p->s, strlen(*av))) {
1973                                     rule.limit_mask |= p->x ;
1974                                     av++; ac-- ;
1975                                     break ;
1976                                 }
1977                             if (p->s == NULL)
1978                                 break ;
1979                         }
1980                         if (ac < 1)
1981                             errx(EX_USAGE,
1982                                 "limit needs mask and # of connections");
1983                         rule.conn_limit = atoi(*av);
1984                         if (rule.conn_limit == 0)
1985                             errx(EX_USAGE, "limit: limit must be >0");
1986                         if (rule.limit_mask == 0)
1987                             errx(EX_USAGE, "missing limit mask");
1988                         av++; ac--;
1989                 } else if (!strncmp(*av, "keep-state", strlen(*av))) {
1990                         u_long type;
1991                         rule.fw_flg |= IP_FW_F_KEEP_S;
1992
1993                         av++; ac--;
1994                         if (ac > 0 && (type = atoi(*av)) != 0) {
1995                                 rule.dyn_type = type;
1996                                 av++; ac--;
1997                         }
1998                 } else if (!strncmp(*av, "bridged", strlen(*av))) {
1999                         rule.fw_flg |= IP_FW_BRIDGED;
2000                         av++; ac--;
2001                 } else if (!strncmp(*av, "out", strlen(*av))) {
2002                         rule.fw_flg |= IP_FW_F_OUT;
2003                         av++; ac--;
2004                 } else if (ac && !strncmp(*av, "xmit", strlen(*av))) {
2005                         union ip_fw_if ifu;
2006                         int byname;
2007
2008                         if (saw_via) {
2009 badviacombo:
2010                                 errx(EX_USAGE, "``via'' is incompatible"
2011                                     " with ``xmit'' and ``recv''");
2012                         }
2013                         saw_xmrc = 1;
2014                         av++; ac--;
2015                         fill_iface("xmit", &ifu, &byname, ac, *av);
2016                         rule.fw_out_if = ifu;
2017                         rule.fw_flg |= IP_FW_F_OIFACE;
2018                         if (byname)
2019                                 rule.fw_flg |= IP_FW_F_OIFNAME;
2020                         av++; ac--;
2021                 } else if (ac && !strncmp(*av, "recv", strlen(*av))) {
2022                         union ip_fw_if ifu;
2023                         int byname;
2024
2025                         if (saw_via)
2026                                 goto badviacombo;
2027                         saw_xmrc = 1;
2028                         av++; ac--;
2029                         fill_iface("recv", &ifu, &byname, ac, *av);
2030                         rule.fw_in_if = ifu;
2031                         rule.fw_flg |= IP_FW_F_IIFACE;
2032                         if (byname)
2033                                 rule.fw_flg |= IP_FW_F_IIFNAME;
2034                         av++; ac--;
2035                 } else if (ac && !strncmp(*av, "via", strlen(*av))) {
2036                         union ip_fw_if ifu;
2037                         int byname = 0;
2038
2039                         if (saw_xmrc)
2040                                 goto badviacombo;
2041                         saw_via = 1;
2042                         av++; ac--;
2043                         fill_iface("via", &ifu, &byname, ac, *av);
2044                         rule.fw_out_if = rule.fw_in_if = ifu;
2045                         if (byname)
2046                                 rule.fw_flg |=
2047                                     (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME);
2048                         av++; ac--;
2049                 } else if (!strncmp(*av, "fragment", strlen(*av))) {
2050                         rule.fw_flg |= IP_FW_F_FRAG;
2051                         av++; ac--;
2052                 } else if (!strncmp(*av, "ipoptions", strlen(*av))) {
2053                         av++; ac--;
2054                         if (!ac)
2055                                 errx(EX_USAGE, "missing argument"
2056                                     " for ``ipoptions''");
2057                         fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av);
2058                         av++; ac--;
2059                 } else if (rule.fw_prot == IPPROTO_TCP) {
2060                         if (!strncmp(*av, "established", strlen(*av))) {
2061                                 rule.fw_ipflg |= IP_FW_IF_TCPEST;
2062                                 av++; ac--;
2063                         } else if (!strncmp(*av, "setup", strlen(*av))) {
2064                                 rule.fw_tcpf  |= IP_FW_TCPF_SYN;
2065                                 rule.fw_tcpnf  |= IP_FW_TCPF_ACK;
2066                                 av++; ac--;
2067                         } else if (!strncmp(*av, "tcpflags", strlen(*av))
2068                             || !strncmp(*av, "tcpflgs", strlen(*av))) {
2069                                 av++; ac--;
2070                                 if (!ac)
2071                                         errx(EX_USAGE, "missing argument"
2072                                             " for ``tcpflags''");
2073                                 fill_tcpflag(&rule.fw_tcpf,
2074                                     &rule.fw_tcpnf, av);
2075                                 av++; ac--;
2076                         } else if (!strncmp(*av, "tcpoptions", strlen(*av))
2077                             || !strncmp(*av, "tcpopts", strlen(*av))) {
2078                                 av++; ac--;
2079                                 if (!ac)
2080                                         errx(EX_USAGE, "missing argument"
2081                                             " for ``tcpoptions''");
2082                                 fill_tcpopts(&rule.fw_tcpopt,
2083                                     &rule.fw_tcpnopt, av);
2084                                 av++; ac--;
2085                         } else {
2086                                 errx(EX_USAGE, "unknown or out of order"
2087                                      " argument ``%s''", *av);
2088                         }
2089                 } else if (rule.fw_prot == IPPROTO_ICMP) {
2090                         if (!strncmp(*av, "icmptypes", strlen(*av))) {
2091                                 av++; ac--;
2092                                 if (!ac)
2093                                         errx(EX_USAGE, "missing argument"
2094                                             " for ``icmptypes''");
2095                                 fill_icmptypes(rule.fw_uar.fw_icmptypes,
2096                                     av, &rule.fw_flg);
2097                                 av++; ac--;
2098                         } else {
2099                                 errx(EX_USAGE, "unknown or out of order"
2100                                      " argument ``%s''", *av);
2101                         }
2102                 } else {
2103                         errx(EX_USAGE, "unknown argument ``%s''", *av);
2104                 }
2105         }
2106
2107         /* No direction specified -> do both directions */
2108         if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN)))
2109                 rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN);
2110
2111         /* Sanity check interface check, but handle "via" case separately */
2112         if (saw_via) {
2113                 if (rule.fw_flg & IP_FW_F_IN)
2114                         rule.fw_flg |= IP_FW_F_IIFACE;
2115                 if (rule.fw_flg & IP_FW_F_OUT)
2116                         rule.fw_flg |= IP_FW_F_OIFACE;
2117         } else if ((rule.fw_flg & IP_FW_F_OIFACE)
2118             && (rule.fw_flg & IP_FW_F_IN)) {
2119                 errx(EX_DATAERR, "can't check xmit interface of incoming"
2120                      " packets");
2121         }
2122
2123         /* frag may not be used in conjunction with ports or TCP flags */
2124         if (rule.fw_flg & IP_FW_F_FRAG) {
2125                 if (rule.fw_tcpf || rule.fw_tcpnf)
2126                         errx(EX_DATAERR, "can't mix 'frag' and tcpflags");
2127
2128                 if (rule.fw_nports)
2129                         errx(EX_DATAERR, "can't mix 'frag' and port"
2130                              " specifications");
2131         }
2132         if (rule.fw_flg & IP_FW_F_PRN) {
2133                 if (!rule.fw_logamount) {
2134                         size_t len = sizeof(int);
2135
2136                         if (sysctlbyname("net.inet.ip.fw.verbose_limit",
2137                             &rule.fw_logamount, &len, NULL, 0) == -1)
2138                                 errx(1, "sysctlbyname(\"%s\")",
2139                                     "net.inet.ip.fw.verbose_limit");
2140                 } else if (rule.fw_logamount == -1)
2141                         rule.fw_logamount = 0;
2142                 rule.fw_loghighest = rule.fw_logamount;
2143         }
2144 done:
2145         i = sizeof(rule);
2146         if (getsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, &i) == -1)
2147                 err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD");
2148         if (!do_quiet)
2149                 show_ipfw(&rule, 10, 10);
2150 }
2151
2152 static void
2153 zero (int ac, char *av[])
2154 {
2155         struct ip_fw rule;
2156         int failed = EX_OK;
2157
2158         av++; ac--;
2159
2160         if (!ac) {
2161                 /* clear all entries */
2162                 if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, NULL, 0) < 0)
2163                         err(EX_UNAVAILABLE, "setsockopt(%s)", "IP_FW_ZERO");
2164                 if (!do_quiet)
2165                         printf("Accounting cleared.\n");
2166
2167                 return;
2168         }
2169
2170         memset(&rule, 0, sizeof rule);
2171         while (ac) {
2172                 /* Rule number */
2173                 if (isdigit(**av)) {
2174                         rule.fw_number = atoi(*av); av++; ac--;
2175                         if (setsockopt(s, IPPROTO_IP,
2176                             IP_FW_ZERO, &rule, sizeof rule)) {
2177                                 warn("rule %u: setsockopt(IP_FW_ZERO)",
2178                                     rule.fw_number);
2179                                 failed = EX_UNAVAILABLE;
2180                         } else if (!do_quiet)
2181                                 printf("Entry %d cleared\n",
2182                                     rule.fw_number);
2183                 } else {
2184                         errx(EX_USAGE, "invalid rule number ``%s''", *av);
2185                 }
2186         }
2187         if (failed != EX_OK)
2188                 exit(failed);
2189 }
2190
2191 static void
2192 resetlog (int ac, char *av[])
2193 {
2194         struct ip_fw rule;
2195         int failed = EX_OK;
2196
2197         av++; ac--;
2198
2199         if (!ac) {
2200                 /* clear all entries */
2201                 if (setsockopt(s, IPPROTO_IP, IP_FW_RESETLOG, NULL, 0) < 0)
2202                         err(EX_UNAVAILABLE, "setsockopt(IP_FW_RESETLOG)");
2203                 if (!do_quiet)
2204                         printf("Logging counts reset.\n");
2205
2206                 return;
2207         }
2208
2209         memset(&rule, 0, sizeof rule);
2210         while (ac) {
2211                 /* Rule number */
2212                 if (isdigit(**av)) {
2213                         rule.fw_number = atoi(*av); av++; ac--;
2214                         if (setsockopt(s, IPPROTO_IP,
2215                             IP_FW_RESETLOG, &rule, sizeof rule)) {
2216                                 warn("rule %u: setsockopt(IP_FW_RESETLOG)",
2217                                     rule.fw_number);
2218                                 failed = EX_UNAVAILABLE;
2219                         } else if (!do_quiet)
2220                                 printf("Entry %d logging count reset\n",
2221                                     rule.fw_number);
2222                 } else {
2223                         errx(EX_DATAERR, "invalid rule number ``%s''", *av);
2224                 }
2225         }
2226         if (failed != EX_OK)
2227                 exit(failed);
2228 }
2229
2230 static int
2231 ipfw_main(int ac, char **av)
2232 {
2233         int ch;
2234
2235         if (ac == 1)
2236                 show_usage();
2237
2238         /* Initialize globals. */
2239         do_resolv = do_acct = do_time = do_quiet =
2240         do_pipe = do_sort = verbose = 0;
2241
2242         /* Set the force flag for non-interactive processes */
2243         do_force = !isatty(STDIN_FILENO);
2244
2245         optind = optreset = 1;
2246         while ((ch = getopt(ac, av, "s:adefNqtv")) != -1)
2247         switch (ch) {
2248         case 's': /* sort */
2249                 do_sort = atoi(optarg);
2250                 break;
2251         case 'a':
2252                 do_acct = 1;
2253                 break;
2254         case 'd':
2255                 do_dynamic = 1;
2256                 break;
2257         case 'e':
2258                 do_expired = 1;
2259                 break;
2260         case 'f':
2261                 do_force = 1;
2262                 break;
2263         case 'N':
2264                 do_resolv = 1;
2265                 break;
2266         case 'q':
2267                 do_quiet = 1;
2268                 break;
2269         case 't':
2270                 do_time = 1;
2271                 break;
2272         case 'v': /* verbose */
2273                 verbose++;
2274                 break;
2275         default:
2276                 show_usage();
2277         }
2278
2279         ac -= optind;
2280         if (*(av += optind) == NULL)
2281                  errx(EX_USAGE, "bad arguments, for usage summary ``ipfw''");
2282
2283         if (!strncmp(*av, "pipe", strlen(*av))) {
2284                 do_pipe = 1;
2285                 ac--;
2286                 av++;
2287         } else if (!strncmp(*av, "queue", strlen(*av))) {
2288                 do_pipe = 2;
2289                 ac--;
2290                 av++;
2291         }
2292         if (!ac)
2293                 errx(EX_USAGE, "pipe requires arguments");
2294
2295         /* allow argument swapping */
2296         if (ac > 1 && *av[0] >= '0' && *av[0] <= '9') {
2297                 char *p = av[0];
2298                 av[0] = av[1];
2299                 av[1] = p;
2300         }
2301         if (!strncmp(*av, "add", strlen(*av))) {
2302                 add(ac, av);
2303         } else if (do_pipe && !strncmp(*av, "config", strlen(*av))) {
2304                 config_pipe(ac, av);
2305         } else if (!strncmp(*av, "delete", strlen(*av))) {
2306                 delete(ac, av);
2307         } else if (!strncmp(*av, "flush", strlen(*av))) {
2308                 int do_flush = 0;
2309
2310                 if (do_force || do_quiet)
2311                         do_flush = 1;
2312                 else {
2313                         int c;
2314
2315                         /* Ask the user */
2316                         printf("Are you sure? [yn] ");
2317                         fflush(stdout);
2318                         do {
2319                                 c = toupper(getc(stdin));
2320                                 while (c != '\n' && getc(stdin) != '\n')
2321                                         if (feof(stdin))
2322                                                 return (0);
2323                         } while (c != 'Y' && c != 'N');
2324                         printf("\n");
2325                         if (c == 'Y')
2326                                 do_flush = 1;
2327                 }
2328                 if (do_flush) {
2329                         if (setsockopt(s, IPPROTO_IP,
2330                             do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH,
2331                             NULL, 0) < 0)
2332                                 err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)",
2333                                     do_pipe ? "DUMMYNET" : "FW");
2334                         if (!do_quiet)
2335                                 printf("Flushed all %s.\n",
2336                                     do_pipe ? "pipes" : "rules");
2337                 }
2338         } else if (!strncmp(*av, "zero", strlen(*av))) {
2339                 zero(ac, av);
2340         } else if (!strncmp(*av, "resetlog", strlen(*av))) {
2341                 resetlog(ac, av);
2342         } else if (!strncmp(*av, "print", strlen(*av))) {
2343                 list(--ac, ++av);
2344         } else if (!strncmp(*av, "enable", strlen(*av))) {
2345                 sysctl_handler(ac, av, 1);
2346         } else if (!strncmp(*av, "disable", strlen(*av))) {
2347                 sysctl_handler(ac, av, 0);
2348         } else if (!strncmp(*av, "list", strlen(*av))) {
2349                 list(--ac, ++av);
2350         } else if (!strncmp(*av, "show", strlen(*av))) {
2351                 do_acct++;
2352                 list(--ac, ++av);
2353         } else {
2354                 errx(EX_USAGE, "bad arguments, for usage summary ``ipfw''");
2355         }
2356         return 0;
2357 }
2358
2359 int
2360 main(int ac, char *av[])
2361 {
2362 #define MAX_ARGS        32
2363 #define WHITESP         " \t\f\v\n\r"
2364         char    buf[BUFSIZ];
2365         char    *a, *p, *args[MAX_ARGS], *cmd = NULL;
2366         char    linename[10];
2367         int     i, c, lineno, qflag, pflag, status;
2368         FILE    *f = NULL;
2369         pid_t   preproc = 0;
2370
2371         s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
2372         if (s < 0)
2373                 err(EX_UNAVAILABLE, "socket");
2374
2375         setbuf(stdout, 0);
2376
2377         /*
2378          * Only interpret the last command line argument as a file to
2379          * be preprocessed if it is specified as an absolute pathname.
2380          */
2381
2382         if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) {
2383                 qflag = pflag = i = 0;
2384                 lineno = 0;
2385
2386                 while ((c = getopt(ac, av, "D:U:p:q")) != -1)
2387                         switch(c) {
2388                         case 'D':
2389                                 if (!pflag)
2390                                         errx(EX_USAGE, "-D requires -p");
2391                                 if (i > MAX_ARGS - 2)
2392                                         errx(EX_USAGE,
2393                                              "too many -D or -U options");
2394                                 args[i++] = "-D";
2395                                 args[i++] = optarg;
2396                                 break;
2397
2398                         case 'U':
2399                                 if (!pflag)
2400                                         errx(EX_USAGE, "-U requires -p");
2401                                 if (i > MAX_ARGS - 2)
2402                                         errx(EX_USAGE,
2403                                              "too many -D or -U options");
2404                                 args[i++] = "-U";
2405                                 args[i++] = optarg;
2406                                 break;
2407
2408                         case 'p':
2409                                 pflag = 1;
2410                                 cmd = optarg;
2411                                 args[0] = cmd;
2412                                 i = 1;
2413                                 break;
2414
2415                         case 'q':
2416                                 qflag = 1;
2417                                 break;
2418
2419                         default:
2420                                 errx(EX_USAGE, "bad arguments, for usage"
2421                                      " summary ``ipfw''");
2422                         }
2423
2424                 av += optind;
2425                 ac -= optind;
2426                 if (ac != 1)
2427                         errx(EX_USAGE, "extraneous filename arguments");
2428
2429                 if ((f = fopen(av[0], "r")) == NULL)
2430                         err(EX_UNAVAILABLE, "fopen: %s", av[0]);
2431
2432                 if (pflag) {
2433                         /* pipe through preprocessor (cpp or m4) */
2434                         int pipedes[2];
2435
2436                         args[i] = 0;
2437
2438                         if (pipe(pipedes) == -1)
2439                                 err(EX_OSERR, "cannot create pipe");
2440
2441                         switch((preproc = fork())) {
2442                         case -1:
2443                                 err(EX_OSERR, "cannot fork");
2444
2445                         case 0:
2446                                 /* child */
2447                                 if (dup2(fileno(f), 0) == -1
2448                                     || dup2(pipedes[1], 1) == -1)
2449                                         err(EX_OSERR, "dup2()");
2450                                 fclose(f);
2451                                 close(pipedes[1]);
2452                                 close(pipedes[0]);
2453                                 execvp(cmd, args);
2454                                 err(EX_OSERR, "execvp(%s) failed", cmd);
2455
2456                         default:
2457                                 /* parent */
2458                                 fclose(f);
2459                                 close(pipedes[1]);
2460                                 if ((f = fdopen(pipedes[0], "r")) == NULL) {
2461                                         int savederrno = errno;
2462
2463                                         (void)kill(preproc, SIGTERM);
2464                                         errno = savederrno;
2465                                         err(EX_OSERR, "fdopen()");
2466                                 }
2467                         }
2468                 }
2469
2470                 while (fgets(buf, BUFSIZ, f)) {
2471                         lineno++;
2472                         sprintf(linename, "Line %d", lineno);
2473                         args[0] = linename;
2474
2475                         if (*buf == '#')
2476                                 continue;
2477                         if ((p = strchr(buf, '#')) != NULL)
2478                                 *p = '\0';
2479                         i = 1;
2480                         if (qflag)
2481                                 args[i++] = "-q";
2482                         for (a = strtok(buf, WHITESP);
2483                             a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++)
2484                                 args[i] = a;
2485                         if (i == (qflag? 2: 1))
2486                                 continue;
2487                         if (i == MAX_ARGS)
2488                                 errx(EX_USAGE, "%s: too many arguments",
2489                                     linename);
2490                         args[i] = NULL;
2491
2492                         ipfw_main(i, args);
2493                 }
2494                 fclose(f);
2495                 if (pflag) {
2496                         if (waitpid(preproc, &status, 0) == -1)
2497                                 errx(EX_OSERR, "waitpid()");
2498                         if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK)
2499                                 errx(EX_UNAVAILABLE,
2500                                     "preprocessor exited with status %d",
2501                                     WEXITSTATUS(status));
2502                         else if (WIFSIGNALED(status))
2503                                 errx(EX_UNAVAILABLE,
2504                                     "preprocessor exited with signal %d",
2505                                     WTERMSIG(status));
2506                 }
2507         } else {
2508                 ipfw_main(ac, av);
2509         }
2510         return EX_OK;
2511 }