2 * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp
3 * Copyright (c) 1994 Ugen J.S.Antsilevich
5 * Idea and grammar partially left from:
6 * Copyright (c) 1993 Daniel Boulet
8 * Redistribution and use in source forms, with and without modification,
9 * are permitted provided that this entire comment appears intact.
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.
15 * This software is provided ``AS IS'' without any warranties of any kind.
17 * NEW command line interface for IP firewall facility
19 * $Id: ipfw.c,v 1.2 2000/06/07 04:22:47 lindak Exp $
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <sys/socket.h>
26 #include <sys/sockio.h>
45 #include <netinet/in.h>
46 #include <netinet/in_systm.h>
47 #include <netinet/ip_var.h>
48 #include <netinet/ip.h>
49 #include <netinet/ip_icmp.h>
50 #include <netinet/ip_fw.h>
51 #include <net/route.h> /* def. of struct route */
52 #include <sys/param.h>
54 #include <netinet/ip_dummynet.h>
55 #include <netinet/tcp.h>
56 #include <arpa/inet.h>
60 int s
; /* main RAW socket */
61 int do_resolv
=0; /* Would try to resolve all */
62 int do_acct
=0; /* Show packet/byte count */
63 int do_time
=0; /* Show time stamps */
64 int do_quiet
=0; /* Be quiet in add and flush */
65 int do_force
=0; /* Don't ask for confirmation */
66 int do_pipe
=0; /* this cmd refers to a pipe */
73 static struct icmpcode icmpcodes
[] = {
74 { ICMP_UNREACH_NET
, "net" },
75 { ICMP_UNREACH_HOST
, "host" },
76 { ICMP_UNREACH_PROTOCOL
, "protocol" },
77 { ICMP_UNREACH_PORT
, "port" },
78 { ICMP_UNREACH_NEEDFRAG
, "needfrag" },
79 { ICMP_UNREACH_SRCFAIL
, "srcfail" },
80 { ICMP_UNREACH_NET_UNKNOWN
, "net-unknown" },
81 { ICMP_UNREACH_HOST_UNKNOWN
, "host-unknown" },
82 { ICMP_UNREACH_ISOLATED
, "isolated" },
83 { ICMP_UNREACH_NET_PROHIB
, "net-prohib" },
84 { ICMP_UNREACH_HOST_PROHIB
, "host-prohib" },
85 { ICMP_UNREACH_TOSNET
, "tosnet" },
86 { ICMP_UNREACH_TOSHOST
, "toshost" },
87 { ICMP_UNREACH_FILTER_PROHIB
, "filter-prohib" },
88 { ICMP_UNREACH_HOST_PRECEDENCE
, "host-precedence" },
89 { ICMP_UNREACH_PRECEDENCE_CUTOFF
, "precedence-cutoff" },
93 static void show_usage(const char *fmt
, ...);
96 mask_bits(struct in_addr m_ad
)
98 int h_fnd
=0,h_num
=0,i
;
101 mask
=ntohl(m_ad
.s_addr
);
102 for (i
=0;i
<sizeof(u_long
)*CHAR_BIT
;i
++) {
116 print_port(prot
, port
, comma
)
123 const char *protocol
;
127 pe
= getprotobynumber(prot
);
129 protocol
= pe
->p_name
;
133 se
= getservbyport(htons(port
), protocol
);
135 printf("%s%s", comma
, se
->s_name
);
140 printf("%s%d",comma
,port
);
144 print_iface(char *key
, union ip_fw_if
*un
, int byname
)
146 char ifnb
[FW_IFNLEN
+1];
149 strncpy(ifnb
, un
->fu_via_if
.name
, FW_IFNLEN
);
150 ifnb
[FW_IFNLEN
]='\0';
151 if (un
->fu_via_if
.unit
== -1)
152 printf(" %s %s*", key
, ifnb
);
154 printf(" %s %s%d", key
, ifnb
, un
->fu_via_if
.unit
);
155 } else if (un
->fu_via_ip
.s_addr
!= 0) {
156 printf(" %s %s", key
, inet_ntoa(un
->fu_via_ip
));
158 printf(" %s any", key
);
162 print_reject_code(int code
)
166 for (ic
= icmpcodes
; ic
->str
; ic
++)
167 if (ic
->code
== code
) {
168 printf("%s", ic
->str
);
175 show_ipfw(struct ip_fw
*chain
, int pcwidth
, int bcwidth
)
182 int nsp
= IP_FW_GETNSRCP(chain
);
183 int ndp
= IP_FW_GETNDSTP(chain
);
186 setservent(1/*stayopen*/);
188 printf("%05u ", chain
->fw_number
);
191 printf("%*qu %*qu ",pcwidth
,chain
->fw_pcnt
,bcwidth
,chain
->fw_bcnt
);
195 if (chain
->timestamp
)
199 strcpy(timestr
, ctime((time_t *)&chain
->timestamp
));
200 *strchr(timestr
, '\n') = '\0';
201 printf("%s ", timestr
);
207 switch (chain
->fw_flg
& IP_FW_F_COMMAND
)
219 printf("divert %u", chain
->fw_divert_port
);
222 printf("tee %u", chain
->fw_divert_port
);
225 printf("skipto %u", chain
->fw_skipto_rule
);
228 printf("pipe %u", chain
->fw_skipto_rule
);
231 if (chain
->fw_reject_code
== IP_FW_REJECT_RST
)
235 print_reject_code(chain
->fw_reject_code
);
239 printf("fwd %s", inet_ntoa(chain
->fw_fwd_ip
.sin_addr
));
240 if(chain
->fw_fwd_ip
.sin_port
)
241 printf(",%d", chain
->fw_fwd_ip
.sin_port
);
244 errx(EX_OSERR
, "impossible");
247 if (chain
->fw_flg
& IP_FW_F_PRN
)
250 pe
= getprotobynumber(chain
->fw_prot
);
252 printf(" %s", pe
->p_name
);
254 printf(" %u", chain
->fw_prot
);
256 printf(" from %s", chain
->fw_flg
& IP_FW_F_INVSRC
? "not " : "");
258 adrt
=ntohl(chain
->fw_smsk
.s_addr
);
259 if (adrt
==ULONG_MAX
&& do_resolv
) {
260 adrt
=(chain
->fw_src
.s_addr
);
261 he
=gethostbyaddr((char *)&adrt
,sizeof(u_long
),AF_INET
);
263 printf(inet_ntoa(chain
->fw_src
));
265 printf("%s",he
->h_name
);
267 if (adrt
!=ULONG_MAX
) {
268 mb
=mask_bits(chain
->fw_smsk
);
273 printf(inet_ntoa(chain
->fw_src
));
276 printf(inet_ntoa(chain
->fw_src
));
278 printf(inet_ntoa(chain
->fw_smsk
));
282 printf(inet_ntoa(chain
->fw_src
));
285 if (chain
->fw_prot
== IPPROTO_TCP
|| chain
->fw_prot
== IPPROTO_UDP
) {
287 for (i
= 0; i
< nsp
; i
++) {
288 print_port(chain
->fw_prot
, chain
->fw_uar
.fw_pts
[i
], comma
);
289 if (i
==0 && (chain
->fw_flg
& IP_FW_F_SRNG
))
296 printf(" to %s", chain
->fw_flg
& IP_FW_F_INVDST
? "not " : "");
298 adrt
=ntohl(chain
->fw_dmsk
.s_addr
);
299 if (adrt
==ULONG_MAX
&& do_resolv
) {
300 adrt
=(chain
->fw_dst
.s_addr
);
301 he
=gethostbyaddr((char *)&adrt
,sizeof(u_long
),AF_INET
);
303 printf(inet_ntoa(chain
->fw_dst
));
305 printf("%s",he
->h_name
);
307 if (adrt
!=ULONG_MAX
) {
308 mb
=mask_bits(chain
->fw_dmsk
);
313 printf(inet_ntoa(chain
->fw_dst
));
316 printf(inet_ntoa(chain
->fw_dst
));
318 printf(inet_ntoa(chain
->fw_dmsk
));
322 printf(inet_ntoa(chain
->fw_dst
));
325 if (chain
->fw_prot
== IPPROTO_TCP
|| chain
->fw_prot
== IPPROTO_UDP
) {
327 for (i
= 0; i
< ndp
; i
++) {
328 print_port(chain
->fw_prot
, chain
->fw_uar
.fw_pts
[nsp
+i
], comma
);
329 if (i
==0 && (chain
->fw_flg
& IP_FW_F_DRNG
))
337 if ((chain
->fw_flg
& IP_FW_F_IN
) && !(chain
->fw_flg
& IP_FW_F_OUT
))
339 if (!(chain
->fw_flg
& IP_FW_F_IN
) && (chain
->fw_flg
& IP_FW_F_OUT
))
342 /* Handle hack for "via" backwards compatibility */
343 if ((chain
->fw_flg
& IF_FW_F_VIAHACK
) == IF_FW_F_VIAHACK
) {
345 &chain
->fw_in_if
, chain
->fw_flg
& IP_FW_F_IIFNAME
);
347 /* Receive interface specified */
348 if (chain
->fw_flg
& IP_FW_F_IIFACE
)
349 print_iface("recv", &chain
->fw_in_if
,
350 chain
->fw_flg
& IP_FW_F_IIFNAME
);
351 /* Transmit interface specified */
352 if (chain
->fw_flg
& IP_FW_F_OIFACE
)
353 print_iface("xmit", &chain
->fw_out_if
,
354 chain
->fw_flg
& IP_FW_F_OIFNAME
);
357 if (chain
->fw_flg
& IP_FW_F_FRAG
)
360 if (chain
->fw_ipopt
|| chain
->fw_ipnopt
) {
361 int _opt_printed
= 0;
362 #define PRINTOPT(x) {if (_opt_printed) printf(",");\
363 printf(x); _opt_printed = 1;}
366 if (chain
->fw_ipopt
& IP_FW_IPOPT_SSRR
) PRINTOPT("ssrr");
367 if (chain
->fw_ipnopt
& IP_FW_IPOPT_SSRR
) PRINTOPT("!ssrr");
368 if (chain
->fw_ipopt
& IP_FW_IPOPT_LSRR
) PRINTOPT("lsrr");
369 if (chain
->fw_ipnopt
& IP_FW_IPOPT_LSRR
) PRINTOPT("!lsrr");
370 if (chain
->fw_ipopt
& IP_FW_IPOPT_RR
) PRINTOPT("rr");
371 if (chain
->fw_ipnopt
& IP_FW_IPOPT_RR
) PRINTOPT("!rr");
372 if (chain
->fw_ipopt
& IP_FW_IPOPT_TS
) PRINTOPT("ts");
373 if (chain
->fw_ipnopt
& IP_FW_IPOPT_TS
) PRINTOPT("!ts");
376 if (chain
->fw_tcpf
& IP_FW_TCPF_ESTAB
)
377 printf(" established");
378 else if (chain
->fw_tcpf
== IP_FW_TCPF_SYN
&&
379 chain
->fw_tcpnf
== IP_FW_TCPF_ACK
)
381 else if (chain
->fw_tcpf
|| chain
->fw_tcpnf
) {
382 int _flg_printed
= 0;
383 #define PRINTFLG(x) {if (_flg_printed) printf(",");\
384 printf(x); _flg_printed = 1;}
387 if (chain
->fw_tcpf
& IP_FW_TCPF_FIN
) PRINTFLG("fin");
388 if (chain
->fw_tcpnf
& IP_FW_TCPF_FIN
) PRINTFLG("!fin");
389 if (chain
->fw_tcpf
& IP_FW_TCPF_SYN
) PRINTFLG("syn");
390 if (chain
->fw_tcpnf
& IP_FW_TCPF_SYN
) PRINTFLG("!syn");
391 if (chain
->fw_tcpf
& IP_FW_TCPF_RST
) PRINTFLG("rst");
392 if (chain
->fw_tcpnf
& IP_FW_TCPF_RST
) PRINTFLG("!rst");
393 if (chain
->fw_tcpf
& IP_FW_TCPF_PSH
) PRINTFLG("psh");
394 if (chain
->fw_tcpnf
& IP_FW_TCPF_PSH
) PRINTFLG("!psh");
395 if (chain
->fw_tcpf
& IP_FW_TCPF_ACK
) PRINTFLG("ack");
396 if (chain
->fw_tcpnf
& IP_FW_TCPF_ACK
) PRINTFLG("!ack");
397 if (chain
->fw_tcpf
& IP_FW_TCPF_URG
) PRINTFLG("urg");
398 if (chain
->fw_tcpnf
& IP_FW_TCPF_URG
) PRINTFLG("!urg");
400 if (chain
->fw_flg
& IP_FW_F_ICMPBIT
) {
406 for (type_index
= 0; type_index
< IP_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8; ++type_index
)
407 if (chain
->fw_uar
.fw_icmptypes
[type_index
/ (sizeof(unsigned) * 8)] &
408 (1U << (type_index
% (sizeof(unsigned) * 8)))) {
409 printf("%c%d", first
== 1 ? ' ' : ',', type_index
);
424 struct dn_pipe
*pipes
;
430 /* get rules or pipes from kernel, resizing array as necessary */
432 const int unit
= do_pipe
? sizeof(*pipes
) : sizeof(*rules
);
433 const int ocmd
= do_pipe
? IP_DUMMYNET_GET
: IP_FW_GET
;
437 while (num
>= nalloc
) {
438 nalloc
= nalloc
* 2 + 200;
439 nbytes
= nalloc
* unit
;
440 if ((data
= realloc(data
, nbytes
)) == NULL
)
441 err(EX_OSERR
, "realloc");
442 if (getsockopt(s
, IPPROTO_IP
, ocmd
, data
, &nbytes
) < 0)
443 err(EX_OSERR
, "getsockopt(IP_%s_GET)",
444 do_pipe
? "DUMMYNET" : "FW");
449 /* display requested pipes */
453 pipes
= (struct dn_pipe
*) data
;
455 rulenum
= strtoul(*av
++, NULL
, 10);
458 for (n
= 0; n
< num
; n
++) {
459 struct dn_pipe
*const p
= &pipes
[n
];
460 double b
= p
->bandwidth
;
466 if (rulenum
!= 0 && rulenum
!= p
->pipe_nr
)
469 sprintf(buf
, "unlimited");
470 else if (b
>= 1000000)
471 sprintf(buf
, "%7.3f Mbit/s", b
/1000000 );
473 sprintf(buf
, "%7.3f Kbit/s", b
/1000 );
475 sprintf(buf
, "%7.3f bit/s ", b
);
477 if ( (l
= p
->queue_size_bytes
) != 0 ) {
479 sprintf(qs
,"%d KB", l
/ 1024);
481 sprintf(qs
,"%d B", l
);
483 sprintf(qs
,"%3d sl.", p
->queue_size
);
485 sprintf(plr
,"plr %f", 1.0*p
->plr
/(double)(0x7fffffff));
489 printf("%05d: %s %4d ms %s %s -- %d pkts (%d B) %d drops\n",
490 p
->pipe_nr
, buf
, p
->delay
, qs
, plr
,
491 p
->r_len
, p
->r_len_bytes
, p
->r_drops
);
497 /* if showing stats, figure out column widths ahead of time */
498 rules
= (struct ip_fw
*) data
;
500 for (n
= 0; n
< num
; n
++) {
501 struct ip_fw
*const r
= &rules
[n
];
506 width
= sprintf(temp
, "%qu", r
->fw_pcnt
);
511 width
= sprintf(temp
, "%qu", r
->fw_bcnt
);
517 /* display all rules */
518 for (n
= 0; n
< num
; n
++) {
519 struct ip_fw
*const r
= &rules
[n
];
521 show_ipfw(r
, pcwidth
, bcwidth
);
524 /* display specific rules requested on command line */
532 /* convert command line rule # */
533 rnum
= strtoul(*av
++, &endptr
, 10);
536 warnx("invalid rule number: %s", *(av
- 1));
539 for (seen
= n
= 0; n
< num
; n
++) {
540 struct ip_fw
*const r
= &rules
[n
];
542 if (r
->fw_number
> rnum
)
544 if (r
->fw_number
== rnum
) {
545 show_ipfw(r
, pcwidth
, bcwidth
);
550 /* give precedence to other error(s) */
551 if (exitval
== EX_OK
)
552 exitval
= EX_UNAVAILABLE
;
553 warnx("rule %lu does not exist", rnum
);
556 if (exitval
!= EX_OK
)
563 show_usage(const char *fmt
, ...)
570 vsnprintf(buf
, sizeof(buf
), fmt
, args
);
572 warnx("error: %s", buf
);
574 fprintf(stderr
, "usage: ipfw [options]\n"
576 " add [number] rule\n"
577 " delete number ...\n"
578 " list [number ...]\n"
579 " show [number ...]\n"
580 " zero [number ...]\n"
581 " rule: action proto src dst extras...\n"
583 " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
584 " reset|count|skipto num|divert port|tee port|fwd ip} [log]\n"
585 " proto: {ip|tcp|udp|icmp|<number>}\n"
586 " src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
587 " dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
589 " fragment (may not be used with ports or tcpflags)\n"
592 " {xmit|recv|via} {iface|ip|any}\n"
593 " {established|setup}\n"
594 " tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n"
595 " ipoptions [!]{ssrr|lsrr|rr|ts},...\n"
596 " icmptypes {type[,type]}...\n");
602 lookup_host (host
, ipaddr
)
604 struct in_addr
*ipaddr
;
606 struct hostent
*he
= gethostbyname(host
);
609 if (inet_aton(host
, ipaddr
))
614 *ipaddr
= *(struct in_addr
*)he
->h_addr_list
[0];
620 fill_ip(ipno
, mask
, acp
, avp
)
621 struct in_addr
*ipno
, *mask
;
629 if (ac
&& !strncmp(*av
,"any",strlen(*av
))) {
630 ipno
->s_addr
= mask
->s_addr
= 0; av
++; ac
--;
632 p
= strchr(*av
, '/');
634 p
= strchr(*av
, ':');
640 if (lookup_host(*av
, ipno
) != 0)
641 show_usage("hostname ``%s'' unknown", *av
);
644 if (!inet_aton(p
,mask
))
645 show_usage("bad netmask ``%s''", p
);
650 } else if (atoi(p
) > 32) {
651 show_usage("bad width ``%s''", p
);
654 htonl(~0 << (32 - atoi(p
)));
658 mask
->s_addr
= htonl(~0);
661 ipno
->s_addr
&= mask
->s_addr
;
670 fill_reject_code(u_short
*codep
, char *str
)
676 val
= strtoul(str
, &s
, 0);
677 if (s
!= str
&& *s
== '\0' && val
< 0x100) {
681 for (ic
= icmpcodes
; ic
->str
; ic
++)
682 if (!strcasecmp(str
, ic
->str
)) {
686 show_usage("unknown ICMP unreachable code ``%s''", str
);
690 add_port(cnt
, ptr
, off
, port
)
691 u_short
*cnt
, *ptr
, off
, port
;
693 if (off
+ *cnt
>= IP_FW_MAX_PORTS
)
694 errx(EX_USAGE
, "too many ports (max is %d)", IP_FW_MAX_PORTS
);
695 ptr
[off
+*cnt
] = port
;
700 lookup_port(const char *arg
, int test
, int nodash
)
706 snprintf(buf
, sizeof(buf
), "%s", arg
);
707 buf
[strcspn(arg
, nodash
? "-," : ",")] = 0;
708 val
= (int) strtoul(buf
, &earg
, 0);
709 if (!*buf
|| *earg
) {
711 if ((s
= getservbyname(buf
, NULL
))) {
712 val
= htons(s
->s_port
);
715 errx(EX_DATAERR
, "unknown port ``%s''", arg
);
720 if (val
< 0 || val
> 0xffff) {
722 errx(EX_DATAERR
, "port ``%s'' out of range", arg
);
731 fill_port(cnt
, ptr
, off
, arg
)
732 u_short
*cnt
, *ptr
, off
;
736 int initial_range
= 0;
738 s
= arg
+ strcspn(arg
, "-,"); /* first port name can't have a dash */
741 if (strchr(arg
, ','))
742 errx(EX_USAGE
, "port range must be first in list");
743 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, 0, 0) : 0x0000);
748 add_port(cnt
, ptr
, off
, *arg
? lookup_port(arg
, 0, 0) : 0xffff);
752 while (arg
!= NULL
) {
756 add_port(cnt
, ptr
, off
, lookup_port(arg
, 0, 0));
759 return initial_range
;
763 fill_tcpflag(set
, reset
, vp
)
775 { "syn", IP_FW_TCPF_SYN
},
776 { "fin", IP_FW_TCPF_FIN
},
777 { "ack", IP_FW_TCPF_ACK
},
778 { "psh", IP_FW_TCPF_PSH
},
779 { "rst", IP_FW_TCPF_RST
},
780 { "urg", IP_FW_TCPF_URG
}
793 for (i
= 0; i
< sizeof(flags
) / sizeof(flags
[0]); ++i
)
794 if (!strncmp(p
, flags
[i
].name
, strlen(p
))) {
795 *d
|= flags
[i
].value
;
798 if (i
== sizeof(flags
) / sizeof(flags
[0]))
799 show_usage("invalid tcp flag ``%s''", p
);
805 fill_ipopt(u_char
*set
, u_char
*reset
, char **vp
)
820 if (!strncmp(p
,"ssrr",strlen(p
))) *d
|= IP_FW_IPOPT_SSRR
;
821 if (!strncmp(p
,"lsrr",strlen(p
))) *d
|= IP_FW_IPOPT_LSRR
;
822 if (!strncmp(p
,"rr",strlen(p
))) *d
|= IP_FW_IPOPT_RR
;
823 if (!strncmp(p
,"ts",strlen(p
))) *d
|= IP_FW_IPOPT_TS
;
829 fill_icmptypes(types
, vp
, fw_flg
)
838 unsigned long icmptype
;
843 icmptype
= strtoul(c
, &c
, 0);
845 if ( *c
!= ',' && *c
!= '\0' )
846 show_usage("invalid ICMP type");
848 if (icmptype
>= IP_FW_ICMPTYPES_DIM
* sizeof(unsigned) * 8)
849 show_usage("ICMP type out of range");
851 types
[icmptype
/ (sizeof(unsigned) * 8)] |=
852 1 << (icmptype
% (sizeof(unsigned) * 8));
853 *fw_flg
|= IP_FW_F_ICMPBIT
;
867 memset(&rule
, 0, sizeof rule
);
868 memset(&pipe
, 0, sizeof pipe
);
873 while (ac
&& isdigit(**av
)) {
875 pipe
.pipe_nr
= atoi(*av
); av
++; ac
--;
876 i
= setsockopt(s
, IPPROTO_IP
, IP_DUMMYNET_DEL
,
880 warn("rule %u: setsockopt(%s)", pipe
.pipe_nr
, "IP_DUMMYNET_DEL");
883 rule
.fw_number
= atoi(*av
); av
++; ac
--;
884 i
= setsockopt(s
, IPPROTO_IP
, IP_FW_DEL
, &rule
, sizeof rule
);
886 exitval
= EX_UNAVAILABLE
;
887 warn("rule %u: setsockopt(%s)", rule
.fw_number
, "IP_FW_DEL");
891 if (exitval
!= EX_OK
)
896 verify_interface(union ip_fw_if
*ifu
)
901 * If a unit was specified, check for that exact interface.
902 * If a wildcard was specified, check for unit 0.
904 snprintf(ifr
.ifr_name
, sizeof(ifr
.ifr_name
), "%s%d",
906 ifu
->fu_via_if
.unit
== -1 ? 0 : ifu
->fu_via_if
.unit
);
908 if (ioctl(s
, SIOCGIFFLAGS
, &ifr
) < 0)
909 warnx("warning: interface ``%s'' does not exist", ifr
.ifr_name
);
913 fill_iface(char *which
, union ip_fw_if
*ifu
, int *byname
, int ac
, char *arg
)
916 show_usage("missing argument for ``%s''", which
);
918 /* Parse the interface or address */
919 if (!strcmp(arg
, "any")) {
920 ifu
->fu_via_ip
.s_addr
= 0;
922 } else if (!isdigit(*arg
)) {
926 strncpy(ifu
->fu_via_if
.name
, arg
, sizeof(ifu
->fu_via_if
.name
));
927 ifu
->fu_via_if
.name
[sizeof(ifu
->fu_via_if
.name
) - 1] = '\0';
928 for (q
= ifu
->fu_via_if
.name
;
929 *q
&& !isdigit(*q
) && *q
!= '*'; q
++)
931 ifu
->fu_via_if
.unit
= (*q
== '*') ? -1 : atoi(q
);
933 verify_interface(ifu
);
934 } else if (!inet_aton(arg
, &ifu
->fu_via_ip
)) {
935 show_usage("bad ip address ``%s''", arg
);
941 config_pipe(int ac
, char **av
)
947 memset(&pipe
, 0, sizeof pipe
);
951 if (ac
&& isdigit(**av
)) {
952 pipe
.pipe_nr
= atoi(*av
); av
++; ac
--;
955 if (!strncmp(*av
,"bw",strlen(*av
)) ||
956 ! strncmp(*av
,"bandwidth",strlen(*av
))) {
957 pipe
.bandwidth
= strtoul(av
[1], &end
, 0);
959 end
++, pipe
.bandwidth
*= 1000 ;
960 else if (*end
== 'M')
961 end
++, pipe
.bandwidth
*= 1000000 ;
963 pipe
.bandwidth
*= 8 ;
965 } else if (!strncmp(*av
,"delay",strlen(*av
)) ) {
966 pipe
.delay
= strtoul(av
[1], NULL
, 0);
968 } else if (!strncmp(*av
,"plr",strlen(*av
)) ) {
970 double d
= strtod(av
[1], NULL
);
971 pipe
.plr
= (int)(d
*0x7fffffff) ;
973 } else if (!strncmp(*av
,"queue",strlen(*av
)) ) {
975 pipe
.queue_size
= strtoul(av
[1], &end
, 0);
977 pipe
.queue_size_bytes
= pipe
.queue_size
*1024 ;
978 pipe
.queue_size
= 0 ;
979 } else if (*end
== 'B') {
980 pipe
.queue_size_bytes
= pipe
.queue_size
;
981 pipe
.queue_size
= 0 ;
985 show_usage("unrecognised option ``%s''", *av
);
987 if (pipe
.pipe_nr
== 0 )
988 show_usage("pipe_nr %d be > 0", pipe
.pipe_nr
);
989 if (pipe
.queue_size
> 100 )
990 show_usage("queue size %d must be 2 <= x <= 100", pipe
.queue_size
);
991 if (pipe
.delay
> 10000 )
992 show_usage("delay %d must be < 10000", pipe
.delay
);
994 printf("configuring pipe %d bw %d delay %d size %d\n",
995 pipe
.pipe_nr
, pipe
.bandwidth
, pipe
.delay
, pipe
.queue_size
);
997 i
= setsockopt(s
,IPPROTO_IP
, IP_DUMMYNET_CONFIGURE
, &pipe
,sizeof pipe
);
999 err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE");
1011 struct protoent
*pe
;
1012 int saw_xmrc
= 0, saw_via
= 0;
1014 memset(&rule
, 0, sizeof rule
);
1019 if (ac
&& isdigit(**av
)) {
1020 rule
.fw_number
= atoi(*av
); av
++; ac
--;
1025 show_usage("missing action");
1026 if (!strncmp(*av
,"accept",strlen(*av
))
1027 || !strncmp(*av
,"pass",strlen(*av
))
1028 || !strncmp(*av
,"allow",strlen(*av
))
1029 || !strncmp(*av
,"permit",strlen(*av
))) {
1030 rule
.fw_flg
|= IP_FW_F_ACCEPT
; av
++; ac
--;
1031 } else if (!strncmp(*av
,"count",strlen(*av
))) {
1032 rule
.fw_flg
|= IP_FW_F_COUNT
; av
++; ac
--;
1033 } else if (!strncmp(*av
,"pipe",strlen(*av
))) {
1034 rule
.fw_flg
|= IP_FW_F_PIPE
; av
++; ac
--;
1036 show_usage("missing pipe number");
1037 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1038 } else if (!strncmp(*av
,"divert",strlen(*av
))) {
1039 rule
.fw_flg
|= IP_FW_F_DIVERT
; av
++; ac
--;
1041 show_usage("missing %s port", "divert");
1042 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1043 if (rule
.fw_divert_port
== 0) {
1046 s
= getservbyname(av
[-1], "divert");
1048 rule
.fw_divert_port
= ntohs(s
->s_port
);
1050 show_usage("illegal %s port", "divert");
1052 } else if (!strncmp(*av
,"tee",strlen(*av
))) {
1053 rule
.fw_flg
|= IP_FW_F_TEE
; av
++; ac
--;
1055 show_usage("missing %s port", "tee divert");
1056 rule
.fw_divert_port
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1057 if (rule
.fw_divert_port
== 0) {
1060 s
= getservbyname(av
[-1], "divert");
1062 rule
.fw_divert_port
= ntohs(s
->s_port
);
1064 show_usage("illegal %s port", "tee divert");
1066 #ifndef IPFW_TEE_IS_FINALLY_IMPLEMENTED
1067 err(EX_USAGE
, "the ``tee'' action is not implemented");
1069 } else if (!strncmp(*av
,"fwd",strlen(*av
)) ||
1070 !strncmp(*av
,"forward",strlen(*av
))) {
1071 struct in_addr dummyip
;
1073 rule
.fw_flg
|= IP_FW_F_FWD
; av
++; ac
--;
1075 show_usage("missing forwarding IP address");
1076 rule
.fw_fwd_ip
.sin_len
= sizeof(struct sockaddr_in
);
1077 rule
.fw_fwd_ip
.sin_family
= AF_INET
;
1078 rule
.fw_fwd_ip
.sin_port
= 0;
1079 pp
= strchr(*av
, ':');
1081 pp
= strchr(*av
, ',');
1085 rule
.fw_fwd_ip
.sin_port
= lookup_port(pp
, 1, 1);
1086 if(rule
.fw_fwd_ip
.sin_port
== (unsigned int)-1)
1087 show_usage("illegal forwarding port");
1089 fill_ip(&(rule
.fw_fwd_ip
.sin_addr
), &dummyip
, &ac
, &av
);
1090 if (rule
.fw_fwd_ip
.sin_addr
.s_addr
== 0)
1091 show_usage("illegal forwarding IP address");
1093 } else if (!strncmp(*av
,"skipto",strlen(*av
))) {
1094 rule
.fw_flg
|= IP_FW_F_SKIPTO
; av
++; ac
--;
1096 show_usage("missing skipto rule number");
1097 rule
.fw_skipto_rule
= strtoul(*av
, NULL
, 0); av
++; ac
--;
1098 } else if ((!strncmp(*av
,"deny",strlen(*av
))
1099 || !strncmp(*av
,"drop",strlen(*av
)))) {
1100 rule
.fw_flg
|= IP_FW_F_DENY
; av
++; ac
--;
1101 } else if (!strncmp(*av
,"reject",strlen(*av
))) {
1102 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1103 rule
.fw_reject_code
= ICMP_UNREACH_HOST
;
1104 } else if (!strncmp(*av
,"reset",strlen(*av
))) {
1105 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1106 rule
.fw_reject_code
= IP_FW_REJECT_RST
; /* check TCP later */
1107 } else if (!strncmp(*av
,"unreach",strlen(*av
))) {
1108 rule
.fw_flg
|= IP_FW_F_REJECT
; av
++; ac
--;
1109 fill_reject_code(&rule
.fw_reject_code
, *av
); av
++; ac
--;
1111 show_usage("invalid action ``%s''", *av
);
1115 if (ac
&& !strncmp(*av
,"log",strlen(*av
))) {
1116 rule
.fw_flg
|= IP_FW_F_PRN
; av
++; ac
--;
1121 show_usage("missing protocol");
1122 if ((proto
= atoi(*av
)) > 0) {
1123 rule
.fw_prot
= proto
; av
++; ac
--;
1124 } else if (!strncmp(*av
,"all",strlen(*av
))) {
1125 rule
.fw_prot
= IPPROTO_IP
; av
++; ac
--;
1126 } else if ((pe
= getprotobyname(*av
)) != NULL
) {
1127 rule
.fw_prot
= pe
->p_proto
; av
++; ac
--;
1129 show_usage("invalid protocol ``%s''", *av
);
1132 if (rule
.fw_prot
!= IPPROTO_TCP
1133 && (rule
.fw_flg
& IP_FW_F_COMMAND
) == IP_FW_F_REJECT
1134 && rule
.fw_reject_code
== IP_FW_REJECT_RST
)
1135 show_usage("``reset'' is only valid for tcp packets");
1138 if (ac
&& !strncmp(*av
,"from",strlen(*av
))) { av
++; ac
--; }
1140 show_usage("missing ``from''");
1142 if (ac
&& !strncmp(*av
,"not",strlen(*av
))) {
1143 rule
.fw_flg
|= IP_FW_F_INVSRC
;
1147 show_usage("missing arguments");
1149 fill_ip(&rule
.fw_src
, &rule
.fw_smsk
, &ac
, &av
);
1151 if (ac
&& (isdigit(**av
) || lookup_port(*av
, 1, 1) >= 0)) {
1154 if (fill_port(&nports
, rule
.fw_uar
.fw_pts
, 0, *av
))
1155 rule
.fw_flg
|= IP_FW_F_SRNG
;
1156 IP_FW_SETNSRCP(&rule
, nports
);
1161 if (ac
&& !strncmp(*av
,"to",strlen(*av
))) { av
++; ac
--; }
1163 show_usage("missing ``to''");
1165 if (ac
&& !strncmp(*av
,"not",strlen(*av
))) {
1166 rule
.fw_flg
|= IP_FW_F_INVDST
;
1170 show_usage("missing arguments");
1172 fill_ip(&rule
.fw_dst
, &rule
.fw_dmsk
, &ac
, &av
);
1174 if (ac
&& (isdigit(**av
) || lookup_port(*av
, 1, 1) >= 0)) {
1177 if (fill_port(&nports
,
1178 rule
.fw_uar
.fw_pts
, IP_FW_GETNSRCP(&rule
), *av
))
1179 rule
.fw_flg
|= IP_FW_F_DRNG
;
1180 IP_FW_SETNDSTP(&rule
, nports
);
1184 if ((rule
.fw_prot
!= IPPROTO_TCP
) && (rule
.fw_prot
!= IPPROTO_UDP
)
1185 && (IP_FW_GETNSRCP(&rule
) || IP_FW_GETNDSTP(&rule
))) {
1186 show_usage("only TCP and UDP protocols are valid"
1187 " with port specifications");
1191 if (!strncmp(*av
,"in",strlen(*av
))) {
1192 rule
.fw_flg
|= IP_FW_F_IN
;
1193 av
++; ac
--; continue;
1195 if (!strncmp(*av
,"out",strlen(*av
))) {
1196 rule
.fw_flg
|= IP_FW_F_OUT
;
1197 av
++; ac
--; continue;
1199 if (ac
&& !strncmp(*av
,"xmit",strlen(*av
))) {
1205 show_usage("``via'' is incompatible"
1206 " with ``xmit'' and ``recv''");
1210 fill_iface("xmit", &ifu
, &byname
, ac
, *av
);
1211 rule
.fw_out_if
= ifu
;
1212 rule
.fw_flg
|= IP_FW_F_OIFACE
;
1214 rule
.fw_flg
|= IP_FW_F_OIFNAME
;
1215 av
++; ac
--; continue;
1217 if (ac
&& !strncmp(*av
,"recv",strlen(*av
))) {
1225 fill_iface("recv", &ifu
, &byname
, ac
, *av
);
1226 rule
.fw_in_if
= ifu
;
1227 rule
.fw_flg
|= IP_FW_F_IIFACE
;
1229 rule
.fw_flg
|= IP_FW_F_IIFNAME
;
1230 av
++; ac
--; continue;
1232 if (ac
&& !strncmp(*av
,"via",strlen(*av
))) {
1240 fill_iface("via", &ifu
, &byname
, ac
, *av
);
1241 rule
.fw_out_if
= rule
.fw_in_if
= ifu
;
1244 (IP_FW_F_IIFNAME
| IP_FW_F_OIFNAME
);
1245 av
++; ac
--; continue;
1247 if (!strncmp(*av
,"fragment",strlen(*av
))) {
1248 rule
.fw_flg
|= IP_FW_F_FRAG
;
1249 av
++; ac
--; continue;
1251 if (!strncmp(*av
,"ipoptions",strlen(*av
))) {
1254 show_usage("missing argument"
1255 " for ``ipoptions''");
1256 fill_ipopt(&rule
.fw_ipopt
, &rule
.fw_ipnopt
, av
);
1257 av
++; ac
--; continue;
1259 if (rule
.fw_prot
== IPPROTO_TCP
) {
1260 if (!strncmp(*av
,"established",strlen(*av
))) {
1261 rule
.fw_tcpf
|= IP_FW_TCPF_ESTAB
;
1262 av
++; ac
--; continue;
1264 if (!strncmp(*av
,"setup",strlen(*av
))) {
1265 rule
.fw_tcpf
|= IP_FW_TCPF_SYN
;
1266 rule
.fw_tcpnf
|= IP_FW_TCPF_ACK
;
1267 av
++; ac
--; continue;
1269 if (!strncmp(*av
,"tcpflags",strlen(*av
))) {
1272 show_usage("missing argument"
1273 " for ``tcpflags''");
1274 fill_tcpflag(&rule
.fw_tcpf
, &rule
.fw_tcpnf
, av
);
1275 av
++; ac
--; continue;
1278 if (rule
.fw_prot
== IPPROTO_ICMP
) {
1279 if (!strncmp(*av
,"icmptypes",strlen(*av
))) {
1282 show_usage("missing argument"
1283 " for ``icmptypes''");
1284 fill_icmptypes(rule
.fw_uar
.fw_icmptypes
,
1286 av
++; ac
--; continue;
1289 show_usage("unknown argument ``%s''", *av
);
1292 /* No direction specified -> do both directions */
1293 if (!(rule
.fw_flg
& (IP_FW_F_OUT
|IP_FW_F_IN
)))
1294 rule
.fw_flg
|= (IP_FW_F_OUT
|IP_FW_F_IN
);
1296 /* Sanity check interface check, but handle "via" case separately */
1298 if (rule
.fw_flg
& IP_FW_F_IN
)
1299 rule
.fw_flg
|= IP_FW_F_IIFACE
;
1300 if (rule
.fw_flg
& IP_FW_F_OUT
)
1301 rule
.fw_flg
|= IP_FW_F_OIFACE
;
1302 } else if ((rule
.fw_flg
& IP_FW_F_OIFACE
) && (rule
.fw_flg
& IP_FW_F_IN
))
1303 show_usage("can't check xmit interface of incoming packets");
1305 /* frag may not be used in conjunction with ports or TCP flags */
1306 if (rule
.fw_flg
& IP_FW_F_FRAG
) {
1307 if (rule
.fw_tcpf
|| rule
.fw_tcpnf
)
1308 show_usage("can't mix 'frag' and tcpflags");
1311 show_usage("can't mix 'frag' and port specifications");
1315 show_ipfw(&rule
, 10, 10);
1316 i
= setsockopt(s
, IPPROTO_IP
, IP_FW_ADD
, &rule
, sizeof rule
);
1318 err(EX_UNAVAILABLE
, "setsockopt(%s)", "IP_FW_ADD");
1329 /* clear all entries */
1330 if (setsockopt(s
,IPPROTO_IP
,IP_FW_ZERO
,NULL
,0)<0)
1331 err(EX_UNAVAILABLE
, "setsockopt(%s)", "IP_FW_ZERO");
1333 printf("Accounting cleared.\n");
1338 memset(&rule
, 0, sizeof rule
);
1341 if (isdigit(**av
)) {
1342 rule
.fw_number
= atoi(*av
); av
++; ac
--;
1343 if (setsockopt(s
, IPPROTO_IP
,
1344 IP_FW_ZERO
, &rule
, sizeof rule
)) {
1345 warn("rule %u: setsockopt(%s)", rule
.fw_number
,
1347 failed
= EX_UNAVAILABLE
;
1350 printf("Entry %d cleared\n",
1353 show_usage("invalid rule number ``%s''", *av
);
1355 if (failed
!= EX_OK
)
1367 extern int optreset
; /* XXX should be declared in <unistd.h> */
1373 /* Set the force flag for non-interactive processes */
1374 do_force
= !isatty(STDIN_FILENO
);
1376 optind
= optreset
= 1;
1377 while ((ch
= getopt(ac
, av
, "afqtN")) != -1)
1399 if (*(av
+=optind
)==NULL
) {
1400 show_usage("Bad arguments");
1403 if (!strncmp(*av
, "pipe", strlen(*av
))) {
1409 show_usage("pipe requires arguments");
1411 /* allow argument swapping */
1412 if (ac
> 1 && *av
[0]>='0' && *av
[0]<='9') {
1417 if (!strncmp(*av
, "add", strlen(*av
))) {
1419 } else if (do_pipe
&& !strncmp(*av
, "config", strlen(*av
))) {
1421 } else if (!strncmp(*av
, "delete", strlen(*av
))) {
1423 } else if (!strncmp(*av
, "flush", strlen(*av
))) {
1426 if ( do_force
|| do_quiet
)
1432 printf("Are you sure? [yn] ");
1435 c
= toupper(getc(stdin
));
1436 while (c
!= '\n' && getc(stdin
) != '\n')
1439 } while (c
!= 'Y' && c
!= 'N');
1445 if (setsockopt(s
,IPPROTO_IP
,IP_FW_FLUSH
,NULL
,0) < 0)
1446 err(EX_UNAVAILABLE
, "setsockopt(%s)", "IP_FW_FLUSH");
1448 printf("Flushed all rules.\n");
1450 } else if (!strncmp(*av
, "zero", strlen(*av
))) {
1452 } else if (!strncmp(*av
, "print", strlen(*av
))) {
1454 } else if (!strncmp(*av
, "list", strlen(*av
))) {
1456 } else if (!strncmp(*av
, "show", strlen(*av
))) {
1460 show_usage("Bad arguments");
1471 #define WHITESP " \t\f\v\n\r"
1473 char *a
, *p
, *args
[MAX_ARGS
], *cmd
= NULL
;
1475 int i
, c
, qflag
, pflag
, status
;
1479 s
= socket( AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
1481 err(EX_UNAVAILABLE
, "socket");
1485 if (ac
> 1 && access(av
[ac
- 1], R_OK
) == 0) {
1486 qflag
= pflag
= i
= 0;
1489 while ((c
= getopt(ac
, av
, "D:U:p:q")) != -1)
1493 errx(EX_USAGE
, "-D requires -p");
1494 if (i
> MAX_ARGS
- 2)
1496 "too many -D or -U options");
1503 errx(EX_USAGE
, "-U requires -p");
1504 if (i
> MAX_ARGS
- 2)
1506 "too many -D or -U options");
1529 show_usage("extraneous filename arguments");
1531 if ((f
= fopen(av
[0], "r")) == NULL
)
1532 err(EX_UNAVAILABLE
, "fopen: %s", av
[0]);
1535 /* pipe through preprocessor (cpp or m4) */
1540 if (pipe(pipedes
) == -1)
1541 err(EX_OSERR
, "cannot create pipe");
1543 switch((preproc
= fork())) {
1545 err(EX_OSERR
, "cannot fork");
1549 if (dup2(fileno(f
), 0) == -1 ||
1550 dup2(pipedes
[1], 1) == -1)
1551 err(EX_OSERR
, "dup2()");
1556 err(EX_OSERR
, "execvp(%s) failed", cmd
);
1562 if ((f
= fdopen(pipedes
[0], "r")) == NULL
) {
1563 int savederrno
= errno
;
1565 (void)kill(preproc
, SIGTERM
);
1567 err(EX_OSERR
, "fdopen()");
1572 while (fgets(buf
, BUFSIZ
, f
)) {
1574 sprintf(linename
, "Line %d", lineno
);
1579 if ((p
= strchr(buf
, '#')) != NULL
)
1582 if (qflag
) args
[i
++]="-q";
1583 for (a
= strtok(buf
, WHITESP
);
1584 a
&& i
< MAX_ARGS
; a
= strtok(NULL
, WHITESP
), i
++)
1586 if (i
== (qflag
? 2: 1))
1589 errx(EX_USAGE
, "%s: too many arguments", linename
);
1596 if (waitpid(preproc
, &status
, 0) != -1) {
1597 if (WIFEXITED(status
)) {
1598 if (WEXITSTATUS(status
) != EX_OK
)
1599 errx(EX_UNAVAILABLE
,
1600 "preprocessor exited with status %d",
1601 WEXITSTATUS(status
));
1602 } else if (WIFSIGNALED(status
)) {
1603 errx(EX_UNAVAILABLE
,
1604 "preprocessor exited with signal %d",