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