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