2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
24 * Copyright (c) 1994 Ugen J.S.Antsilevich
26 * Idea and grammar partially left from:
27 * Copyright (c) 1993 Daniel Boulet
29 * Redistribution and use in source forms, with and without modification,
30 * are permitted provided that this entire comment appears intact.
32 * Redistribution in binary form may occur without any restrictions.
33 * Obviously, it would be nice if you gave credit where credit is due
34 * but requiring it would be too onerous.
36 * This software is provided ``AS IS'' without any warranties of any kind.
38 * NEW command line interface for IP firewall facility
44 #include <sys/param.h>
46 #include <sys/socket.h>
47 #include <sys/sockio.h>
48 #include <sys/sysctl.h>
51 #include <sys/ioctl.h>
69 #include <netinet/in.h>
70 #include <netinet/in_systm.h>
71 #include <netinet/ip.h>
72 #include <netinet/ip_icmp.h>
73 #include <netinet/ip_fw.h>
74 #include <net/route.h> /* def. of struct route */
76 #include <netinet/ip_dummynet.h>
78 #include <netinet/tcp.h>
79 #include <arpa/inet.h>
81 int s
, /* main RAW socket */
82 do_resolv
, /* Would try to resolve all */
83 do_acct
, /* Show packet/byte count */
84 do_time
, /* Show time stamps */
85 do_quiet
, /* Be quiet in add and flush */
86 do_force
, /* Don't ask for confirmation */
88 do_pipe
, /* this cmd refers to a pipe */
90 do_sort
, /* field to sort results (0 = no) */
98 static struct icmpcode icmpcodes
[] = {
99 { ICMP_UNREACH_NET
, "net" },
100 { ICMP_UNREACH_HOST
, "host" },
101 { ICMP_UNREACH_PROTOCOL
, "protocol" },
102 { ICMP_UNREACH_PORT
, "port" },
103 { ICMP_UNREACH_NEEDFRAG
, "needfrag" },
104 { ICMP_UNREACH_SRCFAIL
, "srcfail" },
105 { ICMP_UNREACH_NET_UNKNOWN
, "net-unknown" },
106 { ICMP_UNREACH_HOST_UNKNOWN
, "host-unknown" },
107 { ICMP_UNREACH_ISOLATED
, "isolated" },
108 { ICMP_UNREACH_NET_PROHIB
, "net-prohib" },
109 { ICMP_UNREACH_HOST_PROHIB
, "host-prohib" },
110 { ICMP_UNREACH_TOSNET
, "tosnet" },
111 { ICMP_UNREACH_TOSHOST
, "toshost" },
112 { ICMP_UNREACH_FILTER_PROHIB
, "filter-prohib" },
113 { ICMP_UNREACH_HOST_PRECEDENCE
, "host-precedence" },
114 { ICMP_UNREACH_PRECEDENCE_CUTOFF
, "precedence-cutoff" },
118 static void show_usage(const char *fmt
, ...);
121 mask_bits(struct in_addr m_ad
)
123 int h_fnd
= 0, h_num
= 0, i
;
126 mask
= ntohl(m_ad
.s_addr
);
127 for (i
= 0; i
< sizeof(u_long
) * CHAR_BIT
; i
++) {
141 print_port(prot
, port
, comma
)
148 const char *protocol
;
151 if (!strcmp(comma
, ":")) {
152 printf("%s0x%04x", comma
, port
);
156 pe
= getprotobynumber(prot
);
158 protocol
= pe
->p_name
;
162 se
= getservbyport(htons(port
), protocol
);
164 printf("%s%s", comma
, se
->s_name
);
169 printf("%s%d", comma
, port
);
173 print_iface(char *key
, union ip_fw_if
*un
, int byname
)
175 char ifnb
[FW_IFNLEN
+1];
178 strncpy(ifnb
, un
->fu_via_if
.name
, FW_IFNLEN
);
179 ifnb
[FW_IFNLEN
] = '\0';
180 if (un
->fu_via_if
.unit
== -1)
181 printf(" %s %s*", key
, ifnb
);
183 printf(" %s %s%d", key
, ifnb
, un
->fu_via_if
.unit
);
184 } else if (un
->fu_via_ip
.s_addr
!= 0) {
185 printf(" %s %s", key
, inet_ntoa(un
->fu_via_ip
));
187 printf(" %s any", key
);
191 print_reject_code(int code
)
195 for (ic
= icmpcodes
; ic
->str
; ic
++)
196 if (ic
->code
== code
) {
197 printf("%s", ic
->str
);
204 show_ipfw(struct ip_fw
*chain
, int pcwidth
, int bcwidth
)
211 int nsp
= IP_FW_GETNSRCP(chain
);
212 int ndp
= IP_FW_GETNDSTP(chain
);
215 setservent(1/*stay open*/);
217 printf("%05u ", chain
->fw_number
);
220 printf("%*qu %*qu ", pcwidth
, chain
->fw_pcnt
, bcwidth
, chain
->fw_bcnt
);
223 if (chain
->timestamp
) {
226 strcpy(timestr
, ctime((time_t *)&chain
->timestamp
));
227 *strchr(timestr
, '\n') = '\0';
228 printf("%s ", timestr
);
233 if (chain
->fw_flg
== IP_FW_F_CHECK_S
) {
234 printf("check-state\n");
238 if (chain
->fw_flg
& IP_FW_F_RND_MATCH
) {
239 double d
= 1.0 * (int)(chain
->pipe_ptr
);
240 d
= 1 - (d
/ 0x7fffffff);
241 printf("prob %f ", d
);
244 switch (chain
->fw_flg
& IP_FW_F_COMMAND
) {
255 printf("divert %u", chain
->fw_divert_port
);
258 printf("tee %u", chain
->fw_divert_port
);
261 printf("skipto %u", chain
->fw_skipto_rule
);
265 printf("pipe %u", chain
->fw_skipto_rule
);
268 printf("queue %u", chain
->fw_skipto_rule
);
271 if (chain
->fw_reject_code
== IP_FW_REJECT_RST
)
275 print_reject_code(chain
->fw_reject_code
);
279 printf("fwd %s", inet_ntoa(chain
->fw_fwd_ip
.sin_addr
));
280 if(chain
->fw_fwd_ip
.sin_port
)
281 printf(",%d", chain
->fw_fwd_ip
.sin_port
);
284 errx(EX_OSERR
, "impossible");
287 if (chain
->fw_flg
& IP_FW_F_PRN
) {
289 if (chain
->fw_logamount
)
290 printf(" logamount %d", chain
->fw_logamount
);
293 pe
= getprotobynumber(chain
->fw_prot
);
295 printf(" %s", pe
->p_name
);
297 printf(" %u", chain
->fw_prot
);
299 if (chain
->fw_flg
& IP_FW_F_SME
) {
302 printf(" from %s", chain
->fw_flg
& IP_FW_F_INVSRC
? "not " : "");
304 adrt
= ntohl(chain
->fw_smsk
.s_addr
);
305 if (adrt
== ULONG_MAX
&& do_resolv
) {
306 adrt
= (chain
->fw_src
.s_addr
);
307 he
= gethostbyaddr((char *)&adrt
,
308 sizeof(u_long
), AF_INET
);
310 printf("%s", inet_ntoa(chain
->fw_src
));
312 printf("%s", he
->h_name
);
314 if (adrt
!= ULONG_MAX
) {
315 mb
= mask_bits(chain
->fw_smsk
);
320 printf("%s", inet_ntoa(chain
->fw_src
));
323 printf("%s", inet_ntoa(chain
->fw_src
));
325 printf("%s", inet_ntoa(chain
->fw_smsk
));
329 printf("%s", inet_ntoa(chain
->fw_src
));
333 if (chain
->fw_prot
== IPPROTO_TCP
|| chain
->fw_prot
== IPPROTO_UDP
) {
335 for (i
= 0; i
< nsp
; i
++) {
336 print_port(chain
->fw_prot
, chain
->fw_uar
.fw_pts
[i
], comma
);
337 if (i
== 0 && (chain
->fw_flg
& IP_FW_F_SRNG
))
339 else if (i
== 0 && (chain
->fw_flg
& IP_FW_F_SMSK
))
346 if (chain
->fw_flg
& IP_FW_F_DME
) {
349 printf(" to %s", chain
->fw_flg
& IP_FW_F_INVDST
? "not " : "");
351 adrt
= ntohl(chain
->fw_dmsk
.s_addr
);
352 if (adrt
== ULONG_MAX
&& do_resolv
) {
353 adrt
= (chain
->fw_dst
.s_addr
);
354 he
= gethostbyaddr((char *)&adrt
,
355 sizeof(u_long
), AF_INET
);
357 printf("%s", inet_ntoa(chain
->fw_dst
));
359 printf("%s", he
->h_name
);
361 if (adrt
!= ULONG_MAX
) {
362 mb
= mask_bits(chain
->fw_dmsk
);
367 printf("%s", inet_ntoa(chain
->fw_dst
));
370 printf("%s", inet_ntoa(chain
->fw_dst
));
372 printf("%s", inet_ntoa(chain
->fw_dmsk
));
376 printf("%s", inet_ntoa(chain
->fw_dst
));
380 if (chain
->fw_prot
== IPPROTO_TCP
|| chain
->fw_prot
== IPPROTO_UDP
) {
382 for (i
= 0; i
< ndp
; i
++) {
383 print_port(chain
->fw_prot
, chain
->fw_uar
.fw_pts
[nsp
+i
], comma
);
384 if (i
== 0 && (chain
->fw_flg
& IP_FW_F_DRNG
))
386 else if (i
== 0 && (chain
->fw_flg
& IP_FW_F_DMSK
))
393 if (chain
->fw_flg
& IP_FW_F_UID
) {
394 struct passwd
*pwd
= getpwuid(chain
->fw_uid
);
397 printf(" uid %s", pwd
->pw_name
);
399 printf(" uid %u", chain
->fw_uid
);
402 if (chain
->fw_flg
& IP_FW_F_KEEP_S
) {
403 if (chain
->next_rule_ptr
)
404 printf(" keep-state %d", (int)chain
->next_rule_ptr
);
406 printf(" keep-state");
409 if (chain
->fw_flg
& IP_FW_BRIDGED
)
411 if ((chain
->fw_flg
& IP_FW_F_IN
) && !(chain
->fw_flg
& IP_FW_F_OUT
))
413 if (!(chain
->fw_flg
& IP_FW_F_IN
) && (chain
->fw_flg
& IP_FW_F_OUT
))
416 /* Handle hack for "via" backwards compatibility */
417 if ((chain
->fw_flg
& IF_FW_F_VIAHACK
) == IF_FW_F_VIAHACK
) {
419 &chain
->fw_in_if
, chain
->fw_flg
& IP_FW_F_IIFNAME
);
421 /* Receive interface specified */
422 if (chain
->fw_flg
& IP_FW_F_IIFACE
)
423 print_iface("recv", &chain
->fw_in_if
,
424 chain
->fw_flg
& IP_FW_F_IIFNAME
);
425 /* Transmit interface specified */
426 if (chain
->fw_flg
& IP_FW_F_OIFACE
)
427 print_iface("xmit", &chain
->fw_out_if
,
428 chain
->fw_flg
& IP_FW_F_OIFNAME
);
431 if (chain
->fw_flg
& IP_FW_F_FRAG
)
434 if (chain
->fw_ipopt
|| chain
->fw_ipnopt
) {
435 int _opt_printed
= 0;
436 #define PRINTOPT(x) {if (_opt_printed) printf(",");\
437 printf(x); _opt_printed = 1;}
440 if (chain
->fw_ipopt
& IP_FW_IPOPT_SSRR
) PRINTOPT("ssrr");
441 if (chain
->fw_ipnopt
& IP_FW_IPOPT_SSRR
) PRINTOPT("!ssrr");
442 if (chain
->fw_ipopt
& IP_FW_IPOPT_LSRR
) PRINTOPT("lsrr");
443 if (chain
->fw_ipnopt
& IP_FW_IPOPT_LSRR
) PRINTOPT("!lsrr");
444 if (chain
->fw_ipopt
& IP_FW_IPOPT_RR
) PRINTOPT("rr");
445 if (chain
->fw_ipnopt
& IP_FW_IPOPT_RR
) PRINTOPT("!rr");
446 if (chain
->fw_ipopt
& IP_FW_IPOPT_TS
) PRINTOPT("ts");
447 if (chain
->fw_ipnopt
& IP_FW_IPOPT_TS
) PRINTOPT("!ts");
450 if (chain
->fw_ipflg
& IP_FW_IF_TCPEST
)
451 printf(" established");
452 else if (chain
->fw_tcpf
== IP_FW_TCPF_SYN
&&
453 chain
->fw_tcpnf
== IP_FW_TCPF_ACK
)
455 else if (chain
->fw_tcpf
|| chain
->fw_tcpnf
) {
456 int _flg_printed
= 0;
457 #define PRINTFLG(x) {if (_flg_printed) printf(",");\
458 printf(x); _flg_printed = 1;}
460 printf(" tcpflags ");
461 if (chain
->fw_tcpf
& IP_FW_TCPF_FIN
) PRINTFLG("fin");
462 if (chain
->fw_tcpnf
& IP_FW_TCPF_FIN
) PRINTFLG("!fin");
463 if (chain
->fw_tcpf
& IP_FW_TCPF_SYN
) PRINTFLG("syn");
464 if (chain
->fw_tcpnf
& IP_FW_TCPF_SYN
) PRINTFLG("!syn");
465 if (chain
->fw_tcpf
& IP_FW_TCPF_RST
) PRINTFLG("rst");
466 if (chain
->fw_tcpnf
& IP_FW_TCPF_RST
) PRINTFLG("!rst");
467 if (chain
->fw_tcpf
& IP_FW_TCPF_PSH
) PRINTFLG("psh");
468 if (chain
->fw_tcpnf
& IP_FW_TCPF_PSH
) PRINTFLG("!psh");
469 if (chain
->fw_tcpf
& IP_FW_TCPF_ACK
) PRINTFLG("ack");
470 if (chain
->fw_tcpnf
& IP_FW_TCPF_ACK
) PRINTFLG("!ack");
471 if (chain
->fw_tcpf
& IP_FW_TCPF_URG
) PRINTFLG("urg");
472 if (chain
->fw_tcpnf
& IP_FW_TCPF_URG
) PRINTFLG("!urg");
474 if (chain
->fw_tcpopt
|| chain
->fw_tcpnopt
) {
475 int _opt_printed
= 0;
476 #define PRINTTOPT(x) {if (_opt_printed) printf(",");\
477 printf(x); _opt_printed = 1;}
479 printf(" tcpoptions ");
480 if (chain
->fw_tcpopt
& IP_FW_TCPOPT_MSS
) PRINTTOPT("mss");
481 if (chain
->fw_tcpnopt
& IP_FW_TCPOPT_MSS
) PRINTTOPT("!mss");
482 if (chain
->fw_tcpopt
& IP_FW_TCPOPT_WINDOW
) PRINTTOPT("window");
483 if (chain
->fw_tcpnopt
& IP_FW_TCPOPT_WINDOW
) PRINTTOPT("!window");
484 if (chain
->fw_tcpopt
& IP_FW_TCPOPT_SACK
) PRINTTOPT("sack");
485 if (chain
->fw_tcpnopt
& IP_FW_TCPOPT_SACK
) PRINTTOPT("!sack");
486 if (chain
->fw_tcpopt
& IP_FW_TCPOPT_TS
) PRINTTOPT("ts");
487 if (chain
->fw_tcpnopt
& IP_FW_TCPOPT_TS
) PRINTTOPT("!ts");
488 if (chain
->fw_tcpopt
& IP_FW_TCPOPT_CC
) PRINTTOPT("cc");
489 if (chain
->fw_tcpnopt
& IP_FW_TCPOPT_CC
) PRINTTOPT("!cc");
492 if (chain
->fw_flg
& IP_FW_F_ICMPBIT
) {
498 for (type_index
= 0; type_index
< IP_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8; ++type_index
)
499 if (chain
->fw_uar
.fw_icmptypes
[type_index
/ (sizeof(unsigned) * 8)] &
500 (1U << (type_index
% (sizeof(unsigned) * 8)))) {
501 printf("%c%d", first
== 1 ? ' ' : ',', type_index
);
513 sort_q(const void *pa
, const void *pb
)
515 int rev
= (do_sort
< 0);
516 int field
= rev
? -do_sort
: do_sort
;
518 const struct dn_flow_queue
*a
= pa
;
519 const struct dn_flow_queue
*b
= pb
;
523 res
= a
->len
- b
->len
;
526 res
= a
->len_bytes
- b
->len_bytes
;
529 case 3 : /* tot pkts */
530 res
= a
->tot_pkts
- b
->tot_pkts
;
533 case 4 : /* tot bytes */
534 res
= a
->tot_bytes
- b
->tot_bytes
;
541 return (int)(rev
? res
: -res
);
545 list_queues(struct dn_flow_set
*fs
, struct dn_flow_queue
*q
)
549 printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n",
551 fs
->flow_mask
.src_ip
, fs
->flow_mask
.src_port
,
552 fs
->flow_mask
.dst_ip
, fs
->flow_mask
.dst_port
);
553 if (fs
->rq_elements
== 0)
556 printf("BKT Prot ___Source IP/port____ "
557 "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n");
559 heapsort(q
, fs
->rq_elements
, sizeof(*q
), sort_q
);
560 for (l
= 0; l
< fs
->rq_elements
; l
++) {
564 ina
.s_addr
= htonl(q
[l
].id
.src_ip
);
565 printf("%3d ", q
[l
].hash_slot
);
566 pe
= getprotobynumber(q
[l
].id
.proto
);
568 printf("%-4s ", pe
->p_name
);
570 printf("%4u ", q
[l
].id
.proto
);
571 printf("%15s/%-5d ", inet_ntoa(ina
), q
[l
].id
.src_port
);
572 ina
.s_addr
= htonl(q
[l
].id
.dst_ip
);
574 inet_ntoa(ina
), q
[l
].id
.dst_port
);
575 printf("%4qu %8qu %2u %4u %3u\n",
576 q
[l
].tot_pkts
, q
[l
].tot_bytes
,
577 q
[l
].len
, q
[l
].len_bytes
, q
[l
].drops
);
579 printf(" S %20qd F %20qd\n", q
[l
].S
, q
[l
].F
);
584 print_flowset_parms(struct dn_flow_set
*fs
, char *prefix
)
589 char red
[90]; /* Display RED parameters */
592 if (fs
->flags_fs
& DN_QSIZE_IS_BYTES
) {
594 sprintf(qs
, "%d KB", l
/ 1024);
596 sprintf(qs
, "%d B", l
);
598 sprintf(qs
, "%3d sl.", l
);
600 sprintf(plr
, "plr %f", 1.0*fs
->plr
/(double)(0x7fffffff));
603 if (fs
->flags_fs
& DN_IS_RED
) /* RED parameters */
605 "\n %cRED w_q %f min_th %d max_th %d max_p %f",
606 (fs
->flags_fs
& DN_IS_GENTLE_RED
)? 'G' : ' ',
607 1.0 * fs
->w_q
/ (double)(1 << SCALE_RED
),
608 SCALE_VAL(fs
->min_th
),
609 SCALE_VAL(fs
->max_th
),
610 1.0 * fs
->max_p
/ (double)(1 << SCALE_RED
) ) ;
612 sprintf(red
, "droptail");
614 printf("%s %s%s %d queues (%d buckets) %s\n", prefix
, qs
, plr
,
615 fs
->rq_elements
, fs
->rq_size
, red
);
617 #endif /* DUMMYNET */
626 struct dn_pipe
*pipes
;
627 #endif /* DUMMYNET */
634 /* get rules or pipes from kernel, resizing array as necessary */
637 const int unit
= do_pipe
? sizeof(*pipes
) : sizeof(*rules
);
638 const int ocmd
= do_pipe
? IP_DUMMYNET_GET
: IP_FW_GET
;
640 const int unit
= sizeof(*rules
);
641 const int ocmd
= IP_FW_GET
;
642 #endif /* DUMMYNET */
646 while (nbytes
>= nalloc
) {
647 nalloc
= nalloc
* 2 + 200;
649 if ((data
= realloc(data
, nbytes
)) == NULL
)
650 err(EX_OSERR
, "realloc");
652 rules
->version
= IP_FW_CURRENT_API_VERSION
;
653 if (getsockopt(s
, IPPROTO_IP
, ocmd
, data
, &nbytes
) < 0)
655 err(EX_OSERR
, "getsockopt(IP_%s_GET)",
656 do_pipe
? "DUMMYNET" : "FW");
658 err(EX_OSERR
, "getsockopt(IP_FW_GET)");
659 #endif /* DUMMYNET */
663 /* display requested pipes */
668 struct dn_pipe
*p
= (struct dn_pipe
*) data
;
669 struct dn_flow_set
*fs
;
670 struct dn_flow_queue
*q
;
674 rulenum
= strtoul(*av
++, NULL
, 10);
677 for (; nbytes
>= sizeof(*p
); p
= (struct dn_pipe
*)next
) {
678 double b
= p
->bandwidth
;
682 if (p
->next
!= (struct dn_pipe
*)DN_IS_PIPE
)
684 l
= sizeof(*p
) + p
->fs
.rq_elements
* sizeof(*q
);
685 next
= (void *)p
+ l
;
687 q
= (struct dn_flow_queue
*)(p
+1);
689 if (rulenum
!= 0 && rulenum
!= p
->pipe_nr
)
691 if (p
->if_name
[0] != '\0')
692 sprintf(buf
, "%s", p
->if_name
);
694 sprintf(buf
, "unlimited");
695 else if (b
>= 1000000)
696 sprintf(buf
, "%7.3f Mbit/s", b
/1000000);
698 sprintf(buf
, "%7.3f Kbit/s", b
/1000);
700 sprintf(buf
, "%7.3f bit/s ", b
);
702 sprintf(prefix
, "%05d: %s %4d ms ",
703 p
->pipe_nr
, buf
, p
->delay
);
704 print_flowset_parms(&(p
->fs
), prefix
);
706 printf(" V %20qd\n", p
->V
>> MY_M
);
707 list_queues(&(p
->fs
), q
);
709 fs
= (struct dn_flow_set
*) next
;
710 for (; nbytes
>= sizeof(*fs
); fs
= (struct dn_flow_set
*)next
) {
713 if (fs
->next
!= (struct dn_flow_set
*)DN_IS_QUEUE
)
715 l
= sizeof(*fs
) + fs
->rq_elements
* sizeof(*q
);
716 next
= (void *)fs
+ l
;
718 q
= (struct dn_flow_queue
*)(fs
+1);
719 sprintf(prefix
, "q%05d: weight %d pipe %d ",
720 fs
->fs_nr
, fs
->weight
, fs
->parent_nr
);
721 print_flowset_parms(fs
, prefix
);
727 #endif /* DUMMYNET */
729 rules
= (struct ip_fw
*) data
;
730 /* determine num more accurately */
732 while (rules
[num
].fw_number
< 65535)
734 num
++; /* counting starts from 0 ... */
735 /* if showing stats, figure out column widths ahead of time */
737 for (n
= 0; n
< num
; n
++) {
738 struct ip_fw
*const r
= &rules
[n
];
743 width
= sprintf(temp
, "%qu", r
->fw_pcnt
);
748 width
= sprintf(temp
, "%qu", r
->fw_bcnt
);
754 /* display all rules */
755 for (n
= 0; n
< num
; n
++) {
756 struct ip_fw
*const r
= &rules
[n
];
758 show_ipfw(r
, pcwidth
, bcwidth
);
761 /* display specific rules requested on command line */
769 /* convert command line rule # */
770 rnum
= strtoul(*av
++, &endptr
, 10);
773 warnx("invalid rule number: %s", *(av
- 1));
776 for (seen
= n
= 0; n
< num
; n
++) {
777 struct ip_fw
*const r
= &rules
[n
];
779 if (r
->fw_number
> rnum
)
781 if (r
->fw_number
== rnum
) {
782 show_ipfw(r
, pcwidth
, bcwidth
);
787 /* give precedence to other error(s) */
788 if (exitval
== EX_OK
)
789 exitval
= EX_UNAVAILABLE
;
790 warnx("rule %lu does not exist", rnum
);
793 if (exitval
!= EX_OK
)
799 if (num
* sizeof (rules
[0]) != nbytes
) {
800 struct ipfw_dyn_rule
*d
=
801 (struct ipfw_dyn_rule
*)&rules
[num
];
805 printf("## Dynamic rules:\n");
807 printf("%05d %qu %qu (T %d, # %d) ty %d",
813 pe
= getprotobynumber(d
->id
.proto
);
815 printf(" %s,", pe
->p_name
);
817 printf(" %u,", d
->id
.proto
);
818 a
.s_addr
= htonl(d
->id
.src_ip
);
819 printf(" %s", inet_ntoa(a
));
820 printf(" %d", d
->id
.src_port
);
822 default: /* bidir, no mask */
826 a
.s_addr
= htonl(d
->id
.dst_ip
);
827 printf(" %s", inet_ntoa(a
));
828 printf(" %d", d
->id
.dst_port
);
839 show_usage(const char *fmt
, ...)
846 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
848 warnx("error: %s", buf
);
850 fprintf(stderr
, "usage: ipfw [options]\n"
853 #endif /* DUMMYNET */
854 " add [number] rule\n"
856 " [pipe] delete number ...\n"
857 " [pipe] list [number ...]\n"
858 " [pipe] show [number ...]\n"
859 #endif /* DUMMYNET */
860 " zero [number ...]\n"
861 " resetlog [number ...]\n"
863 " pipe number config [pipeconfig]\n"
864 #endif /* DUMMYNET */
865 " rule: [prob <match_probability>] action proto src dst extras...\n"
867 " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
868 " reset|count|skipto num|divert port|tee port|fwd ip|\n"
871 #endif /* DUMMYNET */
872 "} [log [logamount count]]\n"
873 " proto: {ip|tcp|udp|icmp|<number>}\n"
874 " src: from [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
875 " dst: to [not] {me|any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
878 " fragment (may not be used with ports or tcpflags)\n"
881 " {xmit|recv|via} {iface|ip|any}\n"
882 " {established|setup}\n"
883 " tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
884 " ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
885 " tcpoptions [!]{mss|window|sack|ts|cc},...\n"
886 " icmptypes {type[,type]}...\n"
889 " {bw|bandwidth} <number>{bit/s|Kbit/s|Mbit/s|Bytes/s|KBytes/s|MBytes/s}\n"
890 " {bw|bandwidth} interface_name\n"
891 " delay <milliseconds>\n"
892 " queue <size>{packets|Bytes|KBytes}\n"
894 " mask {all| [dst-ip|src-ip|dst-port|src-port|proto] <number>}\n"
895 " buckets <number>}\n"
896 " {red|gred} <fraction>/<number>/<number>/<fraction>\n"
898 #endif /* DUMMYNET */
905 lookup_host (host
, ipaddr
)
907 struct in_addr
*ipaddr
;
911 if (!inet_aton(host
, ipaddr
)) {
912 if ((he
= gethostbyname(host
)) == NULL
)
914 *ipaddr
= *(struct in_addr
*)he
->h_addr_list
[0];
920 fill_ip(ipno
, mask
, acp
, avp
)
921 struct in_addr
*ipno
, *mask
;
929 if (ac
&& !strncmp(*av
, "any", strlen(*av
))) {
930 ipno
->s_addr
= mask
->s_addr
= 0; av
++; ac
--;
932 p
= strchr(*av
, '/');
934 p
= strchr(*av
, ':');
940 if (lookup_host(*av
, ipno
) != 0)
941 show_usage("hostname ``%s'' unknown", *av
);
944 if (!inet_aton(p
, mask
))
945 show_usage("bad netmask ``%s''", p
);
950 } else if (atoi(p
) > 32) {
951 show_usage("bad width ``%s''", p
);
954 htonl(~0 << (32 - atoi(p
)));
958 mask
->s_addr
= htonl(~0);
961 ipno
->s_addr
&= mask
->s_addr
;
970 fill_reject_code(u_short
*codep
, char *str
)
976 val
= strtoul(str
, &s
, 0);
977 if (s
!= str
&& *s
== '\0' && val
< 0x100) {
981 for (ic
= icmpcodes
; ic
->str
; ic
++)
982 if (!strcasecmp(str
, ic
->str
)) {
986 show_usage("unknown ICMP unreachable code ``%s''", str
);
990 add_port(cnt
, ptr
, off
, port
)
991 u_short
*cnt
, *ptr
, off
, port
;
993 if (off
+ *cnt
>= IP_FW_MAX_PORTS
)
994 errx(EX_USAGE
, "too many ports (max is %d)", IP_FW_MAX_PORTS
);
995 ptr
[off
+*cnt
] = port
;
1000 lookup_port(const char *arg
, int proto
, int test
, int nodash
)
1003 char *earg
, buf
[32];
1007 snprintf(buf
, sizeof(buf
), "%s", arg
);
1009 for (p
= q
= buf
; *p
; *q
++ = *p
++) {
1014 if (*p
== ',' || (nodash
&& *p
== '-'))
1020 val
= (int) strtoul(buf
, &earg
, 0);
1021 if (!*buf
|| *earg
) {
1022 char *protocol
= NULL
;
1025 struct protoent
*pe
= getprotobynumber(proto
);
1028 protocol
= pe
->p_name
;
1032 if ((s
= getservbyname(buf
, protocol
))) {
1033 val
= htons(s
->s_port
);
1036 errx(EX_DATAERR
, "unknown port ``%s''", buf
);
1041 if (val
< 0 || val
> 0xffff) {
1043 errx(EX_DATAERR
, "port ``%s'' out of range", buf
);
1052 * return: 0 normally, 1 if first pair is a range,
1053 * 2 if first pair is a port+mask
1056 fill_port(u_short
*cnt
, u_short
*ptr
, u_short off
, char *arg
, int proto
)
1059 int initial_range
= 0;
1061 for (s
= arg
; *s
&& *s
!= ',' && *s
!= '-' && *s
!= ':'; s
++) {
1062 if (*s
== '\\' && *(s
+1))
1067 if (strchr(arg
, ','))
1068 errx(EX_USAGE
, "port/mask must be first in list");
1069 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, proto
, 0, 0) : 0x0000);
1071 s
= strchr(arg
,',');
1074 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, proto
, 0, 0) : 0xffff);
1080 if (strchr(arg
, ','))
1081 errx(EX_USAGE
, "port range must be first in list");
1082 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, proto
, 0, 0) : 0x0000);
1084 s
= strchr(arg
,',');
1087 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, proto
, 0, 0) : 0xffff);
1091 while (arg
!= NULL
) {
1092 s
= strchr(arg
,',');
1095 add_port(cnt
, ptr
, off
, lookup_port(arg
, proto
, 0, 0));
1098 return initial_range
;
1102 fill_tcpflag(u_char
*set
, u_char
*reset
, char **vp
)
1112 { "syn", IP_FW_TCPF_SYN
},
1113 { "fin", IP_FW_TCPF_FIN
},
1114 { "ack", IP_FW_TCPF_ACK
},
1115 { "psh", IP_FW_TCPF_PSH
},
1116 { "rst", IP_FW_TCPF_RST
},
1117 { "urg", IP_FW_TCPF_URG
}
1130 for (i
= 0; i
< sizeof(flags
) / sizeof(flags
[0]); ++i
)
1131 if (!strncmp(p
, flags
[i
].name
, strlen(p
))) {
1132 *d
|= flags
[i
].value
;
1135 if (i
== sizeof(flags
) / sizeof(flags
[0]))
1136 show_usage("invalid tcp flag ``%s''", p
);
1142 fill_tcpopts(u_char
*set
, u_char
*reset
, char **vp
)
1152 { "mss", IP_FW_TCPOPT_MSS
},
1153 { "window", IP_FW_TCPOPT_WINDOW
},
1154 { "sack", IP_FW_TCPOPT_SACK
},
1155 { "ts", IP_FW_TCPOPT_TS
},
1156 { "cc", IP_FW_TCPOPT_CC
},
1169 for (i
= 0; i
< sizeof(opts
) / sizeof(opts
[0]); ++i
)
1170 if (!strncmp(p
, opts
[i
].name
, strlen(p
))) {
1171 *d
|= opts
[i
].value
;
1174 if (i
== sizeof(opts
) / sizeof(opts
[0]))
1175 show_usage("invalid tcp option ``%s''", p
);
1181 fill_ipopt(u_char
*set
, u_char
*reset
, char **vp
)
1196 if (!strncmp(p
, "ssrr", strlen(p
))) *d
|= IP_FW_IPOPT_SSRR
;
1197 if (!strncmp(p
, "lsrr", strlen(p
))) *d
|= IP_FW_IPOPT_LSRR
;
1198 if (!strncmp(p
, "rr", strlen(p
))) *d
|= IP_FW_IPOPT_RR
;
1199 if (!strncmp(p
, "ts", strlen(p
))) *d
|= IP_FW_IPOPT_TS
;
1205 fill_icmptypes(types
, vp
, fw_flg
)
1214 unsigned long icmptype
;
1219 icmptype
= strtoul(c
, &c
, 0);
1221 if (*c
!= ',' && *c
!= '\0')
1222 show_usage("invalid ICMP type");
1224 if (icmptype
>= IP_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8)
1225 show_usage("ICMP type out of range");
1227 types
[icmptype
/ (sizeof(unsigned) * 8)] |=
1228 1 << (icmptype
% (sizeof(unsigned) * 8));
1229 *fw_flg
|= IP_FW_F_ICMPBIT
;
1240 struct dn_pipe pipe
;
1241 #endif /* DUMMYNET */
1243 int exitval
= EX_OK
;
1245 memset(&rule
, 0, sizeof rule
);
1247 memset(&pipe
, 0, sizeof pipe
);
1248 #endif /* DUMMYNET */
1249 rule
.version
= IP_FW_CURRENT_API_VERSION
;
1254 while (ac
&& isdigit(**av
))
1256 i
= atoi(*av
); av
++; ac
--;
1261 if (do_pipe
== 1) pipe
.pipe_nr
= i
;
1262 else pipe
.fs
.fs_nr
= i
;
1264 i
= setsockopt(s
, IPPROTO_IP
, IP_DUMMYNET_DEL
, &pipe
, sizeof pipe
);
1268 warn("rule %u: setsockopt(%s)", do_pipe
== 1 ? pipe
.pipe_nr
: pipe
.fs
.fs_nr
,
1273 #endif /* DUMMYNET */
1276 i
= setsockopt(s
, IPPROTO_IP
, IP_FW_DEL
, &rule
, sizeof rule
);
1279 exitval
= EX_UNAVAILABLE
;
1280 warn("rule %u: setsockopt(%s)", rule
.fw_number
, "IP_FW_DEL");
1285 if (exitval
!= EX_OK
)
1290 verify_interface(union ip_fw_if
*ifu
)
1295 * If a unit was specified, check for that exact interface.
1296 * If a wildcard was specified, check for unit 0.
1298 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s%d",
1299 ifu
->fu_via_if
.name
,
1300 ifu
->fu_via_if
.unit
== -1 ? 0 : ifu
->fu_via_if
.unit
);
1302 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) < 0)
1303 warnx("warning: interface ``%s'' does not exist", ifr
.ifr_name
);
1307 fill_iface(char *which
, union ip_fw_if
*ifu
, int *byname
, int ac
, char *arg
)
1310 show_usage("missing argument for ``%s''", which
);
1312 /* Parse the interface or address */
1313 if (!strcmp(arg
, "any")) {
1314 ifu
->fu_via_ip
.s_addr
= 0;
1316 } else if (!isdigit(*arg
)) {
1320 strncpy(ifu
->fu_via_if
.name
, arg
, sizeof(ifu
->fu_via_if
.name
));
1321 ifu
->fu_via_if
.name
[sizeof(ifu
->fu_via_if
.name
) - 1] = '\0';
1322 for (q
= ifu
->fu_via_if
.name
;
1323 *q
&& !isdigit(*q
) && *q
!= '*'; q
++)
1325 ifu
->fu_via_if
.unit
= (*q
== '*') ? -1 : atoi(q
);
1327 verify_interface(ifu
);
1328 } else if (!inet_aton(arg
, &ifu
->fu_via_ip
)) {
1329 show_usage("bad ip address ``%s''", arg
);
1336 config_pipe(int ac
, char **av
)
1338 struct dn_pipe pipe
;
1342 memset(&pipe
, 0, sizeof pipe
);
1346 if (ac
&& isdigit(**av
)) {
1347 i
= atoi(*av
); av
++; ac
--;
1354 if (!strncmp(*av
, "plr", strlen(*av
))) {
1356 double d
= strtod(av
[1], NULL
);
1361 pipe
.fs
.plr
= (int)(d
*0x7fffffff);
1363 } else if (!strncmp(*av
, "queue", strlen(*av
))) {
1365 pipe
.fs
.qsize
= strtoul(av
[1], &end
, 0);
1366 if (*end
== 'K' || *end
== 'k') {
1367 pipe
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
1368 pipe
.fs
.qsize
*= 1024;
1369 } else if (*end
== 'B' || !strncmp(end
, "by", 2)) {
1370 pipe
.fs
.flags_fs
|= DN_QSIZE_IS_BYTES
;
1373 } else if (!strncmp(*av
, "buckets", strlen(*av
))) {
1374 pipe
.fs
.rq_size
= strtoul(av
[1], NULL
, 0);
1376 } else if (!strncmp(*av
, "mask", strlen(*av
))) {
1377 /* per-flow queue, mask is dst_ip, dst_port,
1378 * src_ip, src_port, proto measured in bits
1381 u_int32_t
*par
= NULL
;
1383 pipe
.fs
.flow_mask
.dst_ip
= 0;
1384 pipe
.fs
.flow_mask
.src_ip
= 0;
1385 pipe
.fs
.flow_mask
.dst_port
= 0;
1386 pipe
.fs
.flow_mask
.src_port
= 0;
1387 pipe
.fs
.flow_mask
.proto
= 0;
1390 if (ac
>= 1 && !strncmp(*av
, "all", strlen(*av
))) {
1391 /* special case -- all bits are significant */
1392 pipe
.fs
.flow_mask
.dst_ip
= ~0;
1393 pipe
.fs
.flow_mask
.src_ip
= ~0;
1394 pipe
.fs
.flow_mask
.dst_port
= ~0;
1395 pipe
.fs
.flow_mask
.src_port
= ~0;
1396 pipe
.fs
.flow_mask
.proto
= ~0;
1397 pipe
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
1403 if (!strncmp(*av
, "dst-ip", strlen(*av
)))
1404 par
= &(pipe
.fs
.flow_mask
.dst_ip
);
1405 else if (!strncmp(*av
, "src-ip", strlen(*av
)))
1406 par
= &(pipe
.fs
.flow_mask
.src_ip
);
1407 else if (!strncmp(*av
, "dst-port", strlen(*av
)))
1408 (u_int16_t
*)par
= &(pipe
.fs
.flow_mask
.dst_port
);
1409 else if (!strncmp(*av
, "src-port", strlen(*av
)))
1410 (u_int16_t
*)par
= &(pipe
.fs
.flow_mask
.src_port
);
1411 else if (!strncmp(*av
, "proto", strlen(*av
)))
1412 (u_int8_t
*)par
= &(pipe
.fs
.flow_mask
.proto
);
1416 show_usage("mask: %s value missing", *av
);
1417 if (*av
[1] == '/') {
1418 a
= strtoul(av
[1]+1, &end
, 0);
1419 if (a
== 32) /* special case... */
1423 fprintf(stderr
, " mask is 0x%08x\n", a
);
1425 a
= strtoul(av
[1], &end
, 0);
1426 if ((u_int16_t
*)par
== &(pipe
.fs
.flow_mask
.src_port
) ||
1427 (u_int16_t
*)par
== &(pipe
.fs
.flow_mask
.dst_port
)) {
1429 show_usage("mask: %s must be 16 bit, not 0x%08x",
1431 *((u_int16_t
*)par
) = (u_int16_t
) a
;
1432 } else if ((u_int8_t
*)par
== &(pipe
.fs
.flow_mask
.proto
)) {
1434 show_usage("mask: %s must be 8 bit, not 0x%08x",
1436 *((u_int8_t
*)par
) = (u_int8_t
) a
;
1440 pipe
.fs
.flags_fs
|= DN_HAVE_FLOW_MASK
;
1444 } else if (!strncmp(*av
, "red", strlen(*av
)) ||
1445 !strncmp(*av
, "gred", strlen(*av
))) { /* RED enabled */
1446 pipe
.fs
.flags_fs
|= DN_IS_RED
;
1448 pipe
.fs
.flags_fs
|= DN_IS_GENTLE_RED
;
1449 if ((end
= strsep(&av
[1],"/"))) {
1450 double w_q
= strtod(end
, NULL
);
1451 if (w_q
> 1 || w_q
<= 0)
1452 show_usage("w_q %f must be 0 < x <= 1", w_q
);
1453 pipe
.fs
.w_q
= (int) (w_q
* (1 << SCALE_RED
));
1455 if ((end
= strsep(&av
[1],"/"))) {
1456 pipe
.fs
.min_th
= strtoul(end
, &end
, 0);
1457 if (*end
== 'K' || *end
== 'k')
1458 pipe
.fs
.min_th
*= 1024;
1460 if ((end
= strsep(&av
[1],"/"))) {
1461 pipe
.fs
.max_th
= strtoul(end
, &end
, 0);
1462 if (*end
== 'K' || *end
== 'k')
1463 pipe
.fs
.max_th
*= 1024;
1465 if ((end
= strsep(&av
[1],"/"))) {
1466 double max_p
= strtod(end
, NULL
);
1467 if (max_p
> 1 || max_p
<= 0)
1468 show_usage("max_p %f must be 0 < x <= 1", max_p
);
1469 pipe
.fs
.max_p
= (int) (max_p
* (1 << SCALE_RED
));
1472 } else if (!strncmp(*av
, "droptail", strlen(*av
))) { /* DROPTAIL */
1473 pipe
.fs
.flags_fs
&= ~(DN_IS_RED
|DN_IS_GENTLE_RED
);
1477 /* some commands are only good for pipes. */
1478 if (!strncmp(*av
, "bw", strlen(*av
)) ||
1479 ! strncmp(*av
, "bandwidth", strlen(*av
))) {
1480 if (av
[1][0] >= 'a' && av
[1][0] <= 'z') {
1481 int l
= sizeof(pipe
.if_name
)-1;
1482 /* interface name */
1483 strncpy(pipe
.if_name
, av
[1], l
);
1484 pipe
.if_name
[l
] = '\0';
1487 pipe
.if_name
[0] = '\0';
1488 pipe
.bandwidth
= strtoul(av
[1], &end
, 0);
1489 if (*end
== 'K' || *end
== 'k')
1490 end
++, pipe
.bandwidth
*= 1000;
1491 else if (*end
== 'M')
1492 end
++, pipe
.bandwidth
*= 1000000;
1493 if (*end
== 'B' || !strncmp(end
, "by", 2))
1494 pipe
.bandwidth
*= 8;
1497 } else if (!strncmp(*av
, "delay", strlen(*av
))) {
1498 pipe
.delay
= strtoul(av
[1], NULL
, 0);
1501 show_usage("unrecognised pipe option ``%s''", *av
);
1502 } else { /* this refers to a queue */
1503 if (!strncmp(*av
, "weight", strlen(*av
))) {
1504 pipe
.fs
.weight
= strtoul(av
[1], &end
, 0);
1507 } else if (!strncmp(*av
, "pipe", strlen(*av
))) {
1508 pipe
.fs
.parent_nr
= strtoul(av
[1], &end
, 0);
1512 show_usage("unrecognised option ``%s''", *av
);
1517 if (pipe
.pipe_nr
== 0)
1518 show_usage("pipe_nr %d must be > 0", pipe
.pipe_nr
);
1519 if (pipe
.delay
> 10000)
1520 show_usage("delay %d must be < 10000", pipe
.delay
);
1521 } else { /* do_pipe == 2, queue */
1522 if (pipe
.fs
.parent_nr
== 0)
1523 show_usage("pipe %d must be > 0", pipe
.fs
.parent_nr
);
1524 if (pipe
.fs
.weight
>100)
1525 show_usage("weight %d must be <= 100", pipe
.fs
.weight
);
1527 if (pipe
.fs
.flags_fs
& DN_QSIZE_IS_BYTES
) {
1528 if (pipe
.fs
.qsize
> 1024*1024)
1529 show_usage("queue size %d, must be < 1MB",
1532 if (pipe
.fs
.qsize
> 100)
1533 show_usage("queue size %d, must be 2 <= x <= 100",
1536 if (pipe
.fs
.flags_fs
& DN_IS_RED
) {
1537 if (pipe
.fs
.min_th
>= pipe
.fs
.max_th
)
1538 show_usage("min_th %d must be < than max_th %d",
1539 pipe
.fs
.min_th
, pipe
.fs
.max_th
);
1540 if (pipe
.fs
.max_th
== 0)
1541 show_usage("max_th must be > 0");
1542 if (pipe
.bandwidth
) {
1544 int lookup_depth
, avg_pkt_size
;
1545 double s
, idle
, weight
, w_q
;
1546 struct clockinfo clock
;
1550 if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth",
1551 &lookup_depth
, &len
, NULL
, 0) == -1)
1553 errx(1, "sysctlbyname(\"%s\")",
1554 "net.inet.ip.dummynet.red_lookup_depth");
1555 if (lookup_depth
== 0)
1556 show_usage("net.inet.ip.dummynet.red_lookup_depth must"
1557 "greater than zero");
1560 if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size",
1561 &avg_pkt_size
, &len
, NULL
, 0) == -1)
1563 errx(1, "sysctlbyname(\"%s\")",
1564 "net.inet.ip.dummynet.red_avg_pkt_size");
1565 if (avg_pkt_size
== 0)
1566 show_usage("net.inet.ip.dummynet.red_avg_pkt_size must"
1567 "greater than zero");
1569 len
= sizeof(struct clockinfo
);
1570 if (sysctlbyname("kern.clockrate",
1571 &clock
, &len
, NULL
, 0) == -1)
1572 errx(1, "sysctlbyname(\"%s\")", "kern.clockrate");
1574 /* ticks needed for sending a medium-sized packet */
1575 s
= clock
.hz
* avg_pkt_size
* 8 / pipe
.bandwidth
;
1578 * max idle time (in ticks) before avg queue size becomes 0.
1579 * NOTA: (3/w_q) is approx the value x so that
1580 * (1-w_q)^x < 10^-3.
1582 w_q
= ((double) pipe
.fs
.w_q
) / (1 << SCALE_RED
);
1583 idle
= s
* 3. / w_q
;
1584 pipe
.fs
.lookup_step
= (int) idle
/ lookup_depth
;
1585 if (!pipe
.fs
.lookup_step
)
1586 pipe
.fs
.lookup_step
= 1;
1588 for (t
= pipe
.fs
.lookup_step
; t
> 0; --t
)
1590 pipe
.fs
.lookup_weight
= (int) (weight
* (1 << SCALE_RED
));
1594 printf("configuring pipe %d bw %d delay %d size %d\n",
1595 pipe
.pipe_nr
, pipe
.bandwidth
, pipe
.delay
, pipe
.queue_size
);
1597 i
= setsockopt(s
,IPPROTO_IP
, IP_DUMMYNET_CONFIGURE
, &pipe
, sizeof pipe
);
1599 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
1602 #endif /* DUMMYNET */
1612 struct protoent
*pe
;
1613 int saw_xmrc
= 0, saw_via
= 0;
1615 memset(&rule
, 0, sizeof rule
);
1616 rule
.version
= IP_FW_CURRENT_API_VERSION
;
1621 if (ac
&& isdigit(**av
)) {
1622 rule
.fw_number
= atoi(*av
); av
++; ac
--;
1626 if (ac
> 1 && !strncmp(*av
, "prob", strlen(*av
))) {
1627 double d
= strtod(av
[1], NULL
);
1628 if (d
<= 0 || d
> 1)
1629 show_usage("illegal match prob. %s", av
[1]);
1630 if (d
!= 1) { /* 1 means always match */
1631 rule
.fw_flg
|= IP_FW_F_RND_MATCH
;
1632 /* we really store dont_match probability */
1633 (long)rule
.pipe_ptr
= (long)((1 - d
) * 0x7fffffff);
1639 show_usage("missing action");
1640 if (!strncmp(*av
, "accept", strlen(*av
))
1641 || !strncmp(*av
, "pass" ,strlen(*av
))
1642 || !strncmp(*av
, "allow", strlen(*av
))
1643 || !strncmp(*av
, "permit", strlen(*av
))) {
1644 rule
.fw_flg
|= IP_FW_F_ACCEPT
; av
++; ac
--;
1645 } else if (!strncmp(*av
, "count", strlen(*av
))) {
1646 rule
.fw_flg
|= IP_FW_F_COUNT
; av
++; ac
--;
1649 else if (!strncmp(*av
, "pipe", strlen(*av
))) {
1650 rule
.fw_flg
|= IP_FW_F_PIPE
; av
++; ac
--;
1652 show_usage("missing pipe number");
1653 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1654 } else if (!strncmp(*av
, "queue", strlen(*av
))) {
1655 rule
.fw_flg
|= IP_FW_F_QUEUE
; av
++; ac
--;
1657 show_usage("missing queue number");
1658 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1660 #endif /* DUMMYNET */
1661 else if (!strncmp(*av
, "divert", strlen(*av
))) {
1662 rule
.fw_flg
|= IP_FW_F_DIVERT
; av
++; ac
--;
1664 show_usage("missing %s port", "divert");
1665 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1666 if (rule
.fw_divert_port
== 0) {
1669 s
= getservbyname(av
[-1], "divert");
1671 rule
.fw_divert_port
= ntohs(s
->s_port
);
1673 show_usage("illegal %s port", "divert");
1675 } else if (!strncmp(*av
, "tee", strlen(*av
))) {
1676 rule
.fw_flg
|= IP_FW_F_TEE
; av
++; ac
--;
1678 show_usage("missing %s port", "tee divert");
1679 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1680 if (rule
.fw_divert_port
== 0) {
1683 s
= getservbyname(av
[-1], "divert");
1685 rule
.fw_divert_port
= ntohs(s
->s_port
);
1687 show_usage("illegal %s port", "tee divert");
1689 } else if (!strncmp(*av
, "fwd", strlen(*av
)) ||
1690 !strncmp(*av
, "forward", strlen(*av
))) {
1691 struct in_addr dummyip
;
1693 rule
.fw_flg
|= IP_FW_F_FWD
; av
++; ac
--;
1695 show_usage("missing forwarding IP address");
1696 rule
.fw_fwd_ip
.sin_len
= sizeof(struct sockaddr_in
);
1697 rule
.fw_fwd_ip
.sin_family
= AF_INET
;
1698 rule
.fw_fwd_ip
.sin_port
= 0;
1699 pp
= strchr(*av
, ':');
1701 pp
= strchr(*av
, ',');
1705 i
= lookup_port(pp
, 0, 1, 0);
1707 show_usage("illegal forwarding port ``%s''", pp
);
1709 rule
.fw_fwd_ip
.sin_port
= (u_short
)i
;
1711 fill_ip(&(rule
.fw_fwd_ip
.sin_addr
), &dummyip
, &ac
, &av
);
1712 if (rule
.fw_fwd_ip
.sin_addr
.s_addr
== 0)
1713 show_usage("illegal forwarding IP address");
1715 } else if (!strncmp(*av
, "skipto", strlen(*av
))) {
1716 rule
.fw_flg
|= IP_FW_F_SKIPTO
; av
++; ac
--;
1718 show_usage("missing skipto rule number");
1719 rule
.fw_skipto_rule
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1720 } else if ((!strncmp(*av
, "deny", strlen(*av
))
1721 || !strncmp(*av
, "drop", strlen(*av
)))) {
1722 rule
.fw_flg
|= IP_FW_F_DENY
; av
++; ac
--;
1723 } else if (!strncmp(*av
, "reject", strlen(*av
))) {
1724 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1725 rule
.fw_reject_code
= ICMP_UNREACH_HOST
;
1726 } else if (!strncmp(*av
, "reset", strlen(*av
))) {
1727 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1728 rule
.fw_reject_code
= IP_FW_REJECT_RST
; /* check TCP later */
1729 } else if (!strncmp(*av
, "unreach", strlen(*av
))) {
1730 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1731 fill_reject_code(&rule
.fw_reject_code
, *av
); av
++; ac
--;
1732 } else if (!strncmp(*av
, "check-state", strlen(*av
))) {
1733 rule
.fw_flg
|= IP_FW_F_CHECK_S
; av
++; ac
--;
1736 show_usage("invalid action ``%s''", *av
);
1740 if (ac
&& !strncmp(*av
, "log", strlen(*av
))) {
1741 rule
.fw_flg
|= IP_FW_F_PRN
; av
++; ac
--;
1743 if (ac
&& !strncmp(*av
, "logamount", strlen(*av
))) {
1744 if (!(rule
.fw_flg
& IP_FW_F_PRN
))
1745 show_usage("``logamount'' not valid without ``log''");
1748 show_usage("``logamount'' requires argument");
1749 rule
.fw_logamount
= atoi(*av
);
1750 if (rule
.fw_logamount
< 0)
1751 show_usage("``logamount'' argument must be positive");
1752 if (rule
.fw_logamount
== 0)
1753 rule
.fw_logamount
= -1;
1759 show_usage("missing protocol");
1760 if ((proto
= atoi(*av
)) > 0) {
1761 rule
.fw_prot
= proto
; av
++; ac
--;
1762 } else if (!strncmp(*av
, "all", strlen(*av
))) {
1763 rule
.fw_prot
= IPPROTO_IP
; av
++; ac
--;
1764 } else if ((pe
= getprotobyname(*av
)) != NULL
) {
1765 rule
.fw_prot
= pe
->p_proto
; av
++; ac
--;
1767 show_usage("invalid protocol ``%s''", *av
);
1770 if (rule
.fw_prot
!= IPPROTO_TCP
1771 && (rule
.fw_flg
& IP_FW_F_COMMAND
) == IP_FW_F_REJECT
1772 && rule
.fw_reject_code
== IP_FW_REJECT_RST
)
1773 show_usage("``reset'' is only valid for tcp packets");
1776 if (ac
&& !strncmp(*av
, "from", strlen(*av
))) { av
++; ac
--; }
1778 show_usage("missing ``from''");
1780 if (ac
&& !strncmp(*av
, "not", strlen(*av
))) {
1781 rule
.fw_flg
|= IP_FW_F_INVSRC
;
1785 show_usage("missing arguments");
1787 if (ac
&& !strncmp(*av
, "me", strlen(*av
))) {
1788 rule
.fw_flg
|= IP_FW_F_SME
;
1791 fill_ip(&rule
.fw_src
, &rule
.fw_smsk
, &ac
, &av
);
1794 if (ac
&& (isdigit(**av
) || lookup_port(*av
, rule
.fw_prot
, 1, 1) >= 0)) {
1798 retval
= fill_port(&nports
, rule
.fw_uar
.fw_pts
, 0, *av
, rule
.fw_prot
);
1800 rule
.fw_flg
|= IP_FW_F_SRNG
;
1801 else if (retval
== 2)
1802 rule
.fw_flg
|= IP_FW_F_SMSK
;
1803 IP_FW_SETNSRCP(&rule
, nports
);
1808 if (ac
&& !strncmp(*av
, "to", strlen(*av
))) { av
++; ac
--; }
1810 show_usage("missing ``to''");
1812 if (ac
&& !strncmp(*av
, "not", strlen(*av
))) {
1813 rule
.fw_flg
|= IP_FW_F_INVDST
;
1817 show_usage("missing arguments");
1819 if (ac
&& !strncmp(*av
, "me", strlen(*av
))) {
1820 rule
.fw_flg
|= IP_FW_F_DME
;
1823 fill_ip(&rule
.fw_dst
, &rule
.fw_dmsk
, &ac
, &av
);
1826 if (ac
&& (isdigit(**av
) || lookup_port(*av
, rule
.fw_prot
, 1, 1) >= 0)) {
1830 retval
= fill_port(&nports
,
1831 rule
.fw_uar
.fw_pts
, IP_FW_GETNSRCP(&rule
), *av
, rule
.fw_prot
);
1833 rule
.fw_flg
|= IP_FW_F_DRNG
;
1834 else if (retval
== 2)
1835 rule
.fw_flg
|= IP_FW_F_DMSK
;
1836 IP_FW_SETNDSTP(&rule
, nports
);
1840 if ((rule
.fw_prot
!= IPPROTO_TCP
) && (rule
.fw_prot
!= IPPROTO_UDP
)
1841 && (IP_FW_GETNSRCP(&rule
) || IP_FW_GETNDSTP(&rule
))) {
1842 show_usage("only TCP and UDP protocols are valid"
1843 " with port specifications");
1847 if (!strncmp(*av
, "uid", strlen(*av
))) {
1852 rule
.fw_flg
|= IP_FW_F_UID
;
1855 show_usage("``uid'' requires argument");
1857 uid
= strtoul(*av
, &end
, 0);
1859 pwd
= getpwuid(uid
);
1861 pwd
= getpwnam(*av
);
1863 show_usage("uid \"%s\" is nonexistant", *av
);
1864 rule
.fw_uid
= pwd
->pw_uid
;
1868 if (!strncmp(*av
, "in", strlen(*av
))) {
1869 rule
.fw_flg
|= IP_FW_F_IN
;
1870 av
++; ac
--; continue;
1872 if (!strncmp(*av
, "keep-state", strlen(*av
))) {
1874 rule
.fw_flg
|= IP_FW_F_KEEP_S
;
1877 if (ac
> 0 && (type
= atoi(*av
)) != 0) {
1878 (int)rule
.next_rule_ptr
= type
;
1883 if (!strncmp(*av
, "bridged", strlen(*av
))) {
1884 rule
.fw_flg
|= IP_FW_BRIDGED
;
1885 av
++; ac
--; continue;
1887 if (!strncmp(*av
, "out", strlen(*av
))) {
1888 rule
.fw_flg
|= IP_FW_F_OUT
;
1889 av
++; ac
--; continue;
1891 if (ac
&& !strncmp(*av
, "xmit", strlen(*av
))) {
1897 show_usage("``via'' is incompatible"
1898 " with ``xmit'' and ``recv''");
1902 fill_iface("xmit", &ifu
, &byname
, ac
, *av
);
1903 rule
.fw_out_if
= ifu
;
1904 rule
.fw_flg
|= IP_FW_F_OIFACE
;
1906 rule
.fw_flg
|= IP_FW_F_OIFNAME
;
1907 av
++; ac
--; continue;
1909 if (ac
&& !strncmp(*av
, "recv", strlen(*av
))) {
1917 fill_iface("recv", &ifu
, &byname
, ac
, *av
);
1918 rule
.fw_in_if
= ifu
;
1919 rule
.fw_flg
|= IP_FW_F_IIFACE
;
1921 rule
.fw_flg
|= IP_FW_F_IIFNAME
;
1922 av
++; ac
--; continue;
1924 if (ac
&& !strncmp(*av
, "via", strlen(*av
))) {
1932 fill_iface("via", &ifu
, &byname
, ac
, *av
);
1933 rule
.fw_out_if
= rule
.fw_in_if
= ifu
;
1936 (IP_FW_F_IIFNAME
| IP_FW_F_OIFNAME
);
1937 av
++; ac
--; continue;
1939 if (!strncmp(*av
, "fragment", strlen(*av
))) {
1940 rule
.fw_flg
|= IP_FW_F_FRAG
;
1941 av
++; ac
--; continue;
1943 if (!strncmp(*av
, "ipoptions", strlen(*av
))) {
1946 show_usage("missing argument"
1947 " for ``ipoptions''");
1948 fill_ipopt(&rule
.fw_ipopt
, &rule
.fw_ipnopt
, av
);
1949 av
++; ac
--; continue;
1951 if (rule
.fw_prot
== IPPROTO_TCP
) {
1952 if (!strncmp(*av
, "established", strlen(*av
))) {
1953 rule
.fw_ipflg
|= IP_FW_IF_TCPEST
;
1954 av
++; ac
--; continue;
1956 if (!strncmp(*av
, "setup", strlen(*av
))) {
1957 rule
.fw_tcpf
|= IP_FW_TCPF_SYN
;
1958 rule
.fw_tcpnf
|= IP_FW_TCPF_ACK
;
1959 av
++; ac
--; continue;
1961 if (!strncmp(*av
, "tcpflags", strlen(*av
)) ||
1962 !strncmp(*av
, "tcpflgs", strlen(*av
))) {
1965 show_usage("missing argument"
1966 " for ``tcpflags''");
1967 fill_tcpflag(&rule
.fw_tcpf
, &rule
.fw_tcpnf
, av
);
1968 av
++; ac
--; continue;
1970 if (!strncmp(*av
, "tcpoptions", strlen(*av
)) ||
1971 !strncmp(*av
, "tcpopts", strlen(*av
))) {
1974 show_usage("missing argument"
1975 " for ``tcpoptions''");
1976 fill_tcpopts(&rule
.fw_tcpopt
, &rule
.fw_tcpnopt
, av
);
1977 av
++; ac
--; continue;
1980 if (rule
.fw_prot
== IPPROTO_ICMP
) {
1981 if (!strncmp(*av
, "icmptypes", strlen(*av
))) {
1984 show_usage("missing argument"
1985 " for ``icmptypes''");
1986 fill_icmptypes(rule
.fw_uar
.fw_icmptypes
,
1988 av
++; ac
--; continue;
1991 show_usage("unknown argument ``%s''", *av
);
1994 /* No direction specified -> do both directions */
1995 if (!(rule
.fw_flg
& (IP_FW_F_OUT
|IP_FW_F_IN
)))
1996 rule
.fw_flg
|= (IP_FW_F_OUT
|IP_FW_F_IN
);
1998 /* Sanity check interface check, but handle "via" case separately */
2000 if (rule
.fw_flg
& IP_FW_F_IN
)
2001 rule
.fw_flg
|= IP_FW_F_IIFACE
;
2002 if (rule
.fw_flg
& IP_FW_F_OUT
)
2003 rule
.fw_flg
|= IP_FW_F_OIFACE
;
2004 } else if ((rule
.fw_flg
& IP_FW_F_OIFACE
) && (rule
.fw_flg
& IP_FW_F_IN
))
2005 show_usage("can't check xmit interface of incoming packets");
2007 /* frag may not be used in conjunction with ports or TCP flags */
2008 if (rule
.fw_flg
& IP_FW_F_FRAG
) {
2009 if (rule
.fw_tcpf
|| rule
.fw_tcpnf
)
2010 show_usage("can't mix 'frag' and tcpflags");
2013 show_usage("can't mix 'frag' and port specifications");
2015 if (rule
.fw_flg
& IP_FW_F_PRN
) {
2016 if (!rule
.fw_logamount
) {
2017 size_t len
= sizeof(int);
2019 if (sysctlbyname("net.inet.ip.fw.verbose_limit",
2020 &rule
.fw_logamount
, &len
, NULL
, 0) == -1)
2021 errx(1, "sysctlbyname(\"%s\")",
2022 "net.inet.ip.fw.verbose_limit");
2023 } else if (rule
.fw_logamount
== -1)
2024 rule
.fw_logamount
= 0;
2025 rule
.fw_loghighest
= rule
.fw_logamount
;
2029 if (getsockopt(s
, IPPROTO_IP
, IP_FW_ADD
, &rule
, &i
) == -1)
2030 err(EX_UNAVAILABLE
, "getsockopt(%s)", "IP_FW_ADD");
2032 show_ipfw(&rule
, 10, 10);
2041 memset(&rule
, 0, sizeof rule
);
2042 rule
.version
= IP_FW_CURRENT_API_VERSION
;
2047 /* clear all entries */
2048 if (setsockopt(s
, IPPROTO_IP
, IP_FW_ZERO
, &rule
, sizeof rule
) < 0)
2049 err(EX_UNAVAILABLE
, "setsockopt(%s)", "IP_FW_ZERO");
2051 printf("Accounting cleared.\n");
2057 if (isdigit(**av
)) {
2058 rule
.fw_number
= atoi(*av
); av
++; ac
--;
2059 if (setsockopt(s
, IPPROTO_IP
,
2060 IP_FW_ZERO
, &rule
, sizeof rule
)) {
2061 warn("rule %u: setsockopt(%s)", rule
.fw_number
,
2063 failed
= EX_UNAVAILABLE
;
2066 printf("Entry %d cleared\n",
2069 show_usage("invalid rule number ``%s''", *av
);
2071 if (failed
!= EX_OK
)
2082 memset(&rule
, 0, sizeof rule
);
2083 rule
.version
= IP_FW_CURRENT_API_VERSION
;
2088 /* clear all entries */
2089 if (setsockopt(s
, IPPROTO_IP
, IP_FW_RESETLOG
, &rule
, sizeof rule
) < 0)
2090 err(EX_UNAVAILABLE
, "setsockopt(%s)", "IP_FW_RESETLOG");
2092 printf("Logging counts reset.\n");
2098 if (isdigit(**av
)) {
2099 rule
.fw_number
= atoi(*av
); av
++; ac
--;
2100 if (setsockopt(s
, IPPROTO_IP
,
2101 IP_FW_RESETLOG
, &rule
, sizeof rule
)) {
2102 warn("rule %u: setsockopt(%s)", rule
.fw_number
,
2104 failed
= EX_UNAVAILABLE
;
2107 printf("Entry %d logging count reset\n",
2110 show_usage("invalid rule number ``%s''", *av
);
2112 if (failed
!= EX_OK
)
2129 /* Set the force flag for non-interactive processes */
2130 do_force
= !isatty(STDIN_FILENO
);
2132 optind
= optreset
= 1;
2133 while ((ch
= getopt(ac
, av
, "s:afqtvN")) != -1)
2135 case 's': /* sort */
2136 do_sort
= atoi(optarg
);
2150 case 'v': /* verbose */
2161 if (*(av
+=optind
)==NULL
) {
2162 show_usage("bad arguments");
2166 if (!strncmp(*av
, "pipe", strlen(*av
))) {
2170 } else if (!strncmp(*av
, "queue", strlen(*av
))) {
2176 show_usage("pipe requires arguments");
2178 #endif /* DUMMYNET */
2180 /* allow argument swapping */
2181 if (ac
> 1 && *av
[0] >= '0' && *av
[0] <= '9') {
2186 if (!strncmp(*av
, "add", strlen(*av
))) {
2190 else if (do_pipe
&& !strncmp(*av
, "config", strlen(*av
))) {
2191 config_pipe(ac
, av
);
2193 #endif /* DUMMYNET */
2194 else if (!strncmp(*av
, "delete", strlen(*av
))) {
2196 } else if (!strncmp(*av
, "flush", strlen(*av
))) {
2199 if (do_force
|| do_quiet
)
2205 printf("Are you sure? [yn] ");
2208 c
= toupper(getc(stdin
));
2209 while (c
!= '\n' && getc(stdin
) != '\n')
2212 } while (c
!= 'Y' && c
!= 'N');
2223 error
= setsockopt(s
, IPPROTO_IP
, IP_DUMMYNET_FLUSH
, NULL
, 0);
2226 #endif /* DUMMYNET */
2229 memset(&rule
, 0, sizeof rule
);
2230 rule
.version
= IP_FW_CURRENT_API_VERSION
;
2231 error
= setsockopt(s
, IPPROTO_IP
, IP_FW_FLUSH
, &rule
, sizeof rule
);
2236 err(EX_UNAVAILABLE
, "setsockopt(IP_%s_FLUSH)",
2237 do_pipe
? "DUMMYNET" : "FW");
2238 #else /* DUMMYNET */
2239 err(EX_UNAVAILABLE
, "setsockopt(IP_FW_FLUSH)");
2240 #endif /* DUMMYNET */
2243 printf("Flushed all %s.\n",
2244 do_pipe
? "pipes" : "rules");
2245 #else /* DUMMYNET */
2246 printf("Flushed all rules.\n");
2247 #endif /* DUMMYNET */
2249 } else if (!strncmp(*av
, "zero", strlen(*av
))) {
2251 } else if (!strncmp(*av
, "resetlog", strlen(*av
))) {
2253 } else if (!strncmp(*av
, "print", strlen(*av
))) {
2255 } else if (!strncmp(*av
, "list", strlen(*av
))) {
2257 } else if (!strncmp(*av
, "show", strlen(*av
))) {
2261 show_usage("bad arguments");
2272 #define WHITESP " \t\f\v\n\r"
2274 char *a
, *p
, *args
[MAX_ARGS
], *cmd
= NULL
;
2276 int i
, c
, lineno
, qflag
, pflag
, status
;
2280 s
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
2282 err(EX_UNAVAILABLE
, "socket");
2287 * this is a nasty check on the last argument!!!
2288 * If there happens to be a filename matching a keyword in the current
2289 * directory, things will fail miserably.
2292 if (ac
> 1 && av
[ac
- 1][0] == '/' && access(av
[ac
- 1], R_OK
) == 0) {
2293 qflag
= pflag
= i
= 0;
2296 while ((c
= getopt(ac
, av
, "D:U:p:q")) != -1)
2300 errx(EX_USAGE
, "-D requires -p");
2301 if (i
> MAX_ARGS
- 2)
2303 "too many -D or -U options");
2310 errx(EX_USAGE
, "-U requires -p");
2311 if (i
> MAX_ARGS
- 2)
2313 "too many -D or -U options");
2336 show_usage("extraneous filename arguments");
2338 if ((f
= fopen(av
[0], "r")) == NULL
)
2339 err(EX_UNAVAILABLE
, "fopen: %s", av
[0]);
2342 /* pipe through preprocessor (cpp or m4) */
2347 if (pipe(pipedes
) == -1)
2348 err(EX_OSERR
, "cannot create pipe");
2350 switch((preproc
= fork())) {
2352 err(EX_OSERR
, "cannot fork");
2356 if (dup2(fileno(f
), 0) == -1
2357 || dup2(pipedes
[1], 1) == -1)
2358 err(EX_OSERR
, "dup2()");
2363 err(EX_OSERR
, "execvp(%s) failed", cmd
);
2369 if ((f
= fdopen(pipedes
[0], "r")) == NULL
) {
2370 int savederrno
= errno
;
2372 (void)kill(preproc
, SIGTERM
);
2374 err(EX_OSERR
, "fdopen()");
2379 while (fgets(buf
, BUFSIZ
, f
)) {
2381 sprintf(linename
, "Line %d", lineno
);
2386 if ((p
= strchr(buf
, '#')) != NULL
)
2391 for (a
= strtok(buf
, WHITESP
);
2392 a
&& i
< MAX_ARGS
; a
= strtok(NULL
, WHITESP
), i
++)
2394 if (i
== (qflag
? 2: 1))
2397 errx(EX_USAGE
, "%s: too many arguments",
2405 if (waitpid(preproc
, &status
, 0) == -1)
2406 errx(EX_OSERR
, "waitpid()");
2407 if (WIFEXITED(status
) && WEXITSTATUS(status
) != EX_OK
)
2408 errx(EX_UNAVAILABLE
,
2409 "preprocessor exited with status %d",
2410 WEXITSTATUS(status
));
2411 else if (WIFSIGNALED(status
))
2412 errx(EX_UNAVAILABLE
,
2413 "preprocessor exited with signal %d",