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