diff options
Diffstat (limited to 'sbin/ipfw')
-rw-r--r-- | sbin/ipfw/Makefile | 2 | ||||
-rw-r--r-- | sbin/ipfw/ipfw.8 | 183 | ||||
-rw-r--r-- | sbin/ipfw/ipfw.c | 622 |
3 files changed, 557 insertions, 250 deletions
diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile index 3effc23..1bc6fa4 100644 --- a/sbin/ipfw/Makefile +++ b/sbin/ipfw/Makefile @@ -1,5 +1,7 @@ PROG= ipfw +COPTS+= -Wall + MAN8= ipfw.8 .include <bsd.prog.mk> diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 6bfa4cd..7198d04 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -19,10 +19,10 @@ flush .Fl q .Oc zero -.Op Ar number +.Op Ar number ... .Nm ipfw delete -.Ar number +.Ar number ... .Nm ipfw .Op Fl aftN list @@ -122,41 +122,101 @@ Try to resolve addresses and service names in output. .Bl -hang -offset flag -width 1234567890123456 .It Ar allow Allow packets that match rule. -The search terminates. -.It Ar pass -Same as allow. -.It Ar accept -Same as allow. -.It Ar count -Update counters for all packets that match rule. -The search continues with the next rule. +The search terminates. Aliases are +.Ar pass , +.Ar permit , +and +.Ar accept . .It Ar deny Discard packets that match this rule. The search terminates. +.Ar Drop +is an alias for +.Ar deny . .It Ar reject -Discard packets that match this rule, and try to send an ICMP notice. +(Deprecated.) Discard packets that match this rule, and try to send an ICMP +host unreachable notice. The search terminates. +.It Ar unreach code +Discard packets that match this rule, and try to send an ICMP +unreachable notice with code +.Ar code , +where +.Ar code +is a number from zero to 255, or one of these aliases: +.Ar net , +.Ar host , +.Ar protocol , +.Ar port , +.Ar needfrag , +.Ar srcfail , +.Ar net-unknown , +.Ar host-unknown , +.Ar isolated , +.Ar net-prohib , +.Ar host-prohib , +.Ar tosnet , +.Ar toshost , +.Ar filter-prohib , +.Ar host-precedence , +or +.Ar precedence-cutoff . +The search terminates. +.It Ar reset +TCP packets only. Discard packets that match this rule, +and try to send a TCP reset (RST) notice. +The search terminates. +.It Ar count +Update counters for all packets that match rule. +The search continues with the next rule. .It Ar divert port -Divert packets that match this rule to the divert socket bound to port +Divert packets that match this rule to the +.Xr divert 4 +socket bound to port .Ar port . The search terminates. +.It Ar tee port +Send a copy of packets matching this rule to the +.Xr divert 4 +socket bound to port +.Ar port . +The search continues with the next rule. +.It Ar skipto number +Skip all subsequent rules numbered less than +.Ar number . +The search continues with the first rule numbered +.Ar number +or higher. .El .Pp -When a packet matches a rule with the ``log'' -keyword, a message will be printed on the console. +If a packet matches more than one +.Ar divert +and/or +.Ar tee +rule, all but the last are ignored. +.Pp +If the kernel was compiled with +.Dv IPFIREWALL_VERBOSE , +then when a packet matches a rule with the ``log'' +keyword a message will be printed on the console. If the kernel was compiled with the -.Dv IP_FIREWALL_VERBOSE_LIMIT +.Dv IPFIREWALL_VERBOSE_LIMIT option, then logging will cease after the number of packets specified by the option are received for that particular chain entry. Logging may then be re-enabled by clearing the packet counter for that entry. .Pp +Console logging and the log limit are adjustable dynamically +through the +.Xr sysctl 8 +interface. +.Pp .Ar proto : .Bl -hang -offset flag -width 1234567890123456 .It Ar ip -All packets match. -.It Ar all -All packets match. +All packets match. The alias +.Ar all +has the same effect. .It Ar tcp Only TCP packets match. .It Ar udp @@ -172,7 +232,6 @@ for a complete list). .Ar src and .Ar dst : -.Pp .Bl -hang -offset flag .It Ar <address/mask> .Op Ar ports @@ -217,20 +276,72 @@ and the port list is limited to .Pa /usr/src/sys/netinet/ip_fw.h ) ports. .Pp -If ``via'' -.Ar name -is specified, only packets received via or on their way out of an interface -matching -.Ar name -will match this rule. +Rules can apply to packets when they are incoming, or outgoing, or both. +The +.Ar in +keyword indicates the rule should only match incoming packets. +The +.Ar out +keyword indicates the rule should only match outgoing packets. .Pp -If ``via'' -.Ar ipno -is specified, only packets received via or on their way out of an interface -having the address -.Ar ipno -will match this rule. +To match packets going through a certain interface, specify +the interface using +.Ar via : +.Bl -hang -offset flag -width 1234567890123456 +.It Ar via ifX +Packet must be going through interface +.Ar ifX. +.It Ar via if* +Packet must be going through interface +.Ar ifX , +where X is any unit number. +.It Ar via any +Packet must be going through +.Em some +interface. +.It Ar via ipno +Packet must be going through the interface having IP address +.Ar ipno . +.El +.Pp +The +.Ar via +keyword causes the interface to always be checked. +If +.Ar recv +or +.Ar xmit +is used instead of +.Ar via , +then the only receive or transmit interface (respectively) is checked. +By specifying both, it is possible to match packets based on both receive +and transmit interface, e.g.: +.Pp +.Dl "ipfw add 100 deny ip from any to any out recv ed0 xmit ed1" .Pp +The +.Ar recv +interface can be tested on either incoming or outgoing packets, while the +.Ar xmit +interface can only be tested on outgoing packets. So +.Ar out +is required (and +.Ar in +invalid) whenver +.Ar xmit +is used. Specifying +.Ar via +together with +.Ar xmit +or +.Ar recv +is invalid. +.Pp +A packet may not have a receive or transmit interface: packets originating +from the local host have no receive interface. while packets destined for +the local host have no transmit interface. +.Pp +Additional .Ar options : .Bl -hang -offset flag -width 1234567890123456 .It frag @@ -350,11 +461,11 @@ This rule diverts all incoming packets from 192.168.2.0/24 to divert port 5000: .Sh SEE ALSO .Xr divert 4 , .Xr ip 4 , -.Xr ipfirewall 4 , .Xr protocols 5 , .Xr services 5 , .Xr reboot 8 , -.Xr syslogd 8 +.Xr syslogd 8 , +.Xr sysctl 8 .Sh BUGS .Pp .Em WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!!WARNING!! @@ -367,6 +478,12 @@ do anything you don't understand. .Pp When manipulating/adding chain entries, service and protocol names are not accepted. +.Pp +Incoming packet fragments diverted by +.Ar divert +are reassembled before delivery to the socket, whereas fragments diverted via +.Ar tee +are not. .Sh AUTHORS Ugen J. S. Antsilevich, Poul-Henning Kamp, diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c index 1254721..7a426ec 100644 --- a/sbin/ipfw/ipfw.c +++ b/sbin/ipfw/ipfw.c @@ -16,7 +16,7 @@ * * NEW command line interface for IP firewall facility * - * $Id: ipfw.c,v 1.41 1997/03/05 12:08:44 bde Exp $ + * $Id: ipfw.c,v 1.42 1997/03/29 03:32:28 imp Exp $ * */ @@ -32,18 +32,23 @@ #include <netdb.h> #include <stdio.h> #include <stdlib.h> +#include <stdarg.h> #include <string.h> #include <time.h> #include <unistd.h> #include <net/if.h> #include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/ip_var.h> +#include <netinet/ip.h> +#include <netinet/ip_icmp.h> #include <netinet/ip_fw.h> #include <netinet/tcp.h> #include <arpa/inet.h> -int lineno = -1; -char progname[BUFSIZ]; /* Program name for errors */ +extern char *__progname; +int lineno = -1; int s; /* main RAW socket */ int do_resolv=0; /* Would try to resolv all */ @@ -52,9 +57,33 @@ int do_time=0; /* Show time stamps */ int do_quiet=0; /* Be quiet in add and flush */ int do_force=0; /* Don't ask for confirmation */ -int -mask_bits(m_ad) - struct in_addr m_ad; +struct icmpcode { + int code; + char *str; +}; + +static struct icmpcode icmpcodes[] = { + { ICMP_UNREACH_NET, "net" }, + { ICMP_UNREACH_HOST, "host" }, + { ICMP_UNREACH_PROTOCOL, "protocol" }, + { ICMP_UNREACH_PORT, "port" }, + { ICMP_UNREACH_NEEDFRAG, "needfrag" }, + { ICMP_UNREACH_SRCFAIL, "srcfail" }, + { ICMP_UNREACH_NET_UNKNOWN, "net-unknown" }, + { ICMP_UNREACH_HOST_UNKNOWN, "host-unknown" }, + { ICMP_UNREACH_ISOLATED, "isolated" }, + { ICMP_UNREACH_NET_PROHIB, "net-prohib" }, + { ICMP_UNREACH_HOST_PROHIB, "host-prohib" }, + { ICMP_UNREACH_TOSNET, "tosnet" }, + { ICMP_UNREACH_TOSHOST, "toshost" }, + { ICMP_UNREACH_FILTER_PROHIB, "filter-prohib" }, + { ICMP_UNREACH_HOST_PRECEDENCE, "host-precedence" }, + { ICMP_UNREACH_PRECEDENCE_CUTOFF, "precedence-cutoff" }, + { 0, NULL } +}; + +static int +mask_bits(struct in_addr m_ad) { int h_fnd=0,h_num=0,i; u_long mask; @@ -101,15 +130,47 @@ print_port(prot, port, comma) printf("%s%d",comma,port); } -void -show_ipfw(chain) - struct ip_fw *chain; +static void +print_iface(char *key, union ip_fw_if *un, int byname) +{ + char ifnb[FW_IFNLEN+1]; + + if (byname) { + strncpy(ifnb, un->fu_via_if.name, FW_IFNLEN); + ifnb[FW_IFNLEN]='\0'; + if (un->fu_via_if.unit == -1) + printf(" %s %s*", key, ifnb); + else + printf(" %s %s%d", key, ifnb, un->fu_via_if.unit); + } else if (un->fu_via_ip.s_addr != 0) { + printf(" %s %s", key, inet_ntoa(un->fu_via_ip)); + } else + printf(" %s any", key); +} + +static void +print_reject_code(int code) +{ + struct icmpcode *ic; + + for (ic = icmpcodes; ic->str; ic++) + if (ic->code == code) { + printf("%s", ic->str); + return; + } + printf("%u", code); +} + +static void +show_ipfw(struct ip_fw *chain) { char *comma; u_long adrt; struct hostent *he; struct protoent *pe; int i, mb; + int nsp = IP_FW_GETNSRCP(chain); + int ndp = IP_FW_GETNDSTP(chain); if (do_resolv) setservent(1/*stayopen*/); @@ -138,17 +199,28 @@ show_ipfw(chain) case IP_FW_F_ACCEPT: printf("allow"); break; - case IP_FW_F_DIVERT: - printf("divert %u", chain->fw_divert_port); + case IP_FW_F_DENY: + printf("deny"); break; case IP_FW_F_COUNT: printf("count"); break; - case IP_FW_F_DENY: - if (chain->fw_flg & IP_FW_F_ICMPRPL) - printf("reject"); - else - printf("deny"); + case IP_FW_F_DIVERT: + printf("divert %u", chain->fw_divert_port); + break; + case IP_FW_F_TEE: + printf("tee %u", chain->fw_divert_port); + break; + case IP_FW_F_SKIPTO: + printf("skipto %u", chain->fw_skipto_rule); + break; + case IP_FW_F_REJECT: + if (chain->fw_reject_code == IP_FW_REJECT_RST) + printf("reset"); + else { + printf("unreach "); + print_reject_code(chain->fw_reject_code); + } break; default: errx(1, "impossible"); @@ -161,7 +233,7 @@ show_ipfw(chain) if (pe) printf(" %s", pe->p_name); else - printf("%u", chain->fw_prot); + printf(" %u", chain->fw_prot); printf(" from %s", chain->fw_flg & IP_FW_F_INVSRC ? "not " : ""); @@ -192,9 +264,9 @@ show_ipfw(chain) printf(inet_ntoa(chain->fw_src)); } - if (chain->fw_prot != IPPROTO_IP) { + if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { comma = " "; - for (i=0;i<chain->fw_nsp; i++ ) { + for (i = 0; i < nsp; i++) { print_port(chain->fw_prot, chain->fw_pts[i], comma); if (i==0 && (chain->fw_flg & IP_FW_F_SRNG)) comma = "-"; @@ -232,35 +304,36 @@ show_ipfw(chain) printf(inet_ntoa(chain->fw_dst)); } - comma = " "; - for (i=0;i<chain->fw_ndp;i++) { - print_port(chain->fw_prot, chain->fw_pts[chain->fw_nsp+i], - comma); - if (i==0 && (chain->fw_flg & IP_FW_F_DRNG)) - comma = "-"; - else - comma = ","; - } + if (chain->fw_prot == IPPROTO_TCP || chain->fw_prot == IPPROTO_UDP) { + comma = " "; + for (i = 0; i < ndp; i++) { + print_port(chain->fw_prot, chain->fw_pts[nsp+i], comma); + if (i==0 && (chain->fw_flg & IP_FW_F_DRNG)) + comma = "-"; + else + comma = ","; + } + } - if ((chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT)) - ; - else if (chain->fw_flg & IP_FW_F_IN) + /* Direction */ + if ((chain->fw_flg & IP_FW_F_IN) && !(chain->fw_flg & IP_FW_F_OUT)) printf(" in"); - else if (chain->fw_flg & IP_FW_F_OUT) + if (!(chain->fw_flg & IP_FW_F_IN) && (chain->fw_flg & IP_FW_F_OUT)) printf(" out"); - if (chain->fw_flg&IP_FW_F_IFNAME && chain->fw_via_name[0]) { - char ifnb[FW_IFNLEN+1]; - printf(" via "); - strncpy(ifnb,chain->fw_via_name,FW_IFNLEN); - ifnb[FW_IFNLEN]='\0'; - if (chain->fw_flg & IP_FW_F_IFUWILD) - printf("%s*",ifnb); - else - printf("%s%d",ifnb,chain->fw_via_unit); - } else if (chain->fw_via_ip.s_addr) { - printf(" via "); - printf(inet_ntoa(chain->fw_via_ip)); + /* Handle hack for "via" backwards compatibility */ + if ((chain->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { + print_iface("via", + &chain->fw_in_if, chain->fw_flg & IP_FW_F_IIFNAME); + } else { + /* Receive interface specified */ + if (chain->fw_flg & IP_FW_F_IIFACE) + print_iface("recv", &chain->fw_in_if, + chain->fw_flg & IP_FW_F_IIFNAME); + /* Transmit interface specified */ + if (chain->fw_flg & IP_FW_F_OIFACE) + print_iface("xmit", &chain->fw_out_if, + chain->fw_flg & IP_FW_F_OIFNAME); } if (chain->fw_flg & IP_FW_F_FRAG) @@ -342,44 +415,50 @@ list(ac, av) show_ipfw(r); } -void -show_usage(str) - char *str; +static void +show_usage(const char *fmt, ...) { - if (str) - fprintf(stderr,"%s: ERROR - %s\n",progname,str); + if (fmt) { + char buf[100]; + va_list args; + + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + warnx("error: %s", buf); + } else + warnx("error"); fprintf(stderr, "Usage:\n" -"\t%s [options]\n" -"\t\tflush\n" -"\t\tadd [number] rule\n" -"\t\tdelete number\n" -"\t\tlist [number]\n" -"\t\tshow [number]\n" -"\t\tzero [number]\n" -"\trule:\taction proto src dst extras...\n" -"\t\taction: {allow|deny|reject|count|divert port} [log]\n" -"\t\tproto: {ip|tcp|udp|icmp|<number>}}\n" -"\t\tsrc: from {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" -"\t\tdst: to {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" -"\textras:\n" -"\t\tfragment\n" -"\t\t{in|out|inout}\n" -"\t\tvia {ifname|ip}\n" -"\t\t{established|setup}\n" -"\t\ttcpflags [!]{syn|fin|rst|ack|psh|urg},...\n" -"\t\tipoptions [!]{ssrr|lsrr|rr|ts},...\n" -"\t\ticmptypes {type},...\n" -"\t\tproto {ipproto},...\n" -, progname -); - - - fprintf(stderr,"See man %s(8) for proper usage.\n",progname); - exit (1); +" %s [options]\n" +" flush\n" +" add [number] rule\n" +" delete number ...\n" +" list [number]\n" +" show [number]\n" +" zero [number ...]\n" +" rule: action proto src dst extras...\n" +" action:\n" +" {allow|permit|accept|pass|deny|drop|reject|unreach code|\n" +" reset|count|skipto num|divert port|tee port} [log]\n" +" proto: {ip|tcp|udp|icmp|<number>}\n" +" src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" +" dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" +" extras:\n" +" fragment\n" +" in\n" +" out\n" +" {xmit|recv|via} {iface|ip|any}\n" +" {established|setup}\n" +" tcpflags [!]{syn|fin|rst|ack|psh|urg},...\n" +" ipoptions [!]{ssrr|lsrr|rr|ts},...\n" +" icmptypes {type[,type]}...\n", +__progname); + + errx(1, "see man %s(8) for proper usage.", __progname); } -int +static int lookup_host (host, ipaddr) char *host; struct in_addr *ipaddr; @@ -415,22 +494,25 @@ fill_ip(ipno, mask, acp, avp) *p++ = '\0'; } - if (lookup_host(*av,ipno) != 0) - show_usage("ip number\n"); + if (lookup_host(*av, ipno) != 0) + show_usage("hostname ``%s'' unknown", *av); switch (md) { case ':': if (!inet_aton(p,mask)) - show_usage("ip number\n"); + show_usage("bad netmask ``%s''", p); break; case '/': if (atoi(p) == 0) { mask->s_addr = 0; + } else if (atoi(p) > 32) { + show_usage("bad width ``%s''", p); } else { - mask->s_addr = htonl(0xffffffff << (32 - atoi(p))); + mask->s_addr = + htonl(~0 << (32 - atoi(p))); } break; default: - mask->s_addr = htonl(0xffffffff); + mask->s_addr = htonl(~0); break; } ipno->s_addr &= mask->s_addr; @@ -441,7 +523,27 @@ fill_ip(ipno, mask, acp, avp) *avp = av; } -void +static void +fill_reject_code(u_short *codep, char *str) +{ + struct icmpcode *ic; + u_long val; + char *s; + + val = strtoul(str, &s, 0); + if (s != str && *s == '\0' && val < 0x100) { + *codep = val; + return; + } + for (ic = icmpcodes; ic->str; ic++) + if (!strcasecmp(str, ic->str)) { + *codep = ic->code; + return; + } + show_usage("unknown ICMP unreachable code ``%s''", str); +} + +static void add_port(cnt, ptr, off, port) u_short *cnt, *ptr, off, port; { @@ -520,15 +622,13 @@ fill_tcpflag(set, reset, vp) break; } if (i == sizeof(flags) / sizeof(flags[0])) - show_usage("invalid tcp flag\n"); + show_usage("invalid tcp flag ``%s''", p); p = q; } } -void -fill_ipopt(set, reset, vp) - u_char *set, *reset; - char **vp; +static void +fill_ipopt(u_char *set, u_char *reset, char **vp) { char *p = *vp,*q; u_char *d; @@ -593,18 +693,16 @@ delete(ac,av) av++; ac--; /* Rule number */ - if (ac && isdigit(**av)) { + while (ac && isdigit(**av)) { rule.fw_number = atoi(*av); av++; ac--; + i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule); + if (i) + warn("setsockopt(%s)", "IP_FW_DEL"); } - - i = setsockopt(s, IPPROTO_IP, IP_FW_DEL, &rule, sizeof rule); - if (i) - err(1,"setsockopt(IP_FW_DEL)"); } -int -verify_interface(rule) - struct ip_fw *rule; +static void +verify_interface(union ip_fw_if *ifu) { struct ifreq ifr; @@ -613,16 +711,42 @@ verify_interface(rule) * If a wildcard was specified, check for unit 0. */ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", - rule->fw_via_name, - rule->fw_flg & IP_FW_F_IFUWILD ? 0 : rule->fw_via_unit); + ifu->fu_via_if.name, + ifu->fu_via_if.unit == -1 ? 0 : ifu->fu_via_if.unit); if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0) - return(-1); /* interface isn't recognized by the kernel */ + warnx("warning: interface ``%s'' does not exist", ifr.ifr_name); +} - return(0); /* interface exists */ +static void +fill_iface(char *which, union ip_fw_if *ifu, int *byname, int ac, char *arg) +{ + if (!ac) + show_usage("missing argument for ``%s''", which); + + /* Parse the interface or address */ + if (!strcmp(arg, "any")) { + ifu->fu_via_ip.s_addr = 0; + *byname = 0; + } else if (!isdigit(*arg)) { + char *q; + + *byname = 1; + strncpy(ifu->fu_via_if.name, arg, sizeof(ifu->fu_via_if.name)); + ifu->fu_via_if.name[sizeof(ifu->fu_via_if.name) - 1] = '\0'; + for (q = ifu->fu_via_if.name; + *q && !isdigit(*q) && *q != '*'; q++) + continue; + ifu->fu_via_if.unit = (*q == '*') ? -1 : atoi(q); + *q = '\0'; + verify_interface(ifu); + } else if (!inet_aton(arg, &ifu->fu_via_ip)) { + show_usage("bad ip address ``%s''", arg); + } else + *byname = 0; } -void +static void add(ac,av) int ac; char **av; @@ -631,6 +755,7 @@ add(ac,av) int i; u_char proto; struct protoent *pe; + int saw_xmrc = 0, saw_via = 0; memset(&rule, 0, sizeof rule); @@ -642,26 +767,48 @@ add(ac,av) } /* Action */ - if (ac && (!strncmp(*av,"accept",strlen(*av)) + if (ac == 0) + show_usage("missing action"); + if (!strncmp(*av,"accept",strlen(*av)) || !strncmp(*av,"pass",strlen(*av)) || !strncmp(*av,"allow",strlen(*av)) - || !strncmp(*av,"permit",strlen(*av)))) { + || !strncmp(*av,"permit",strlen(*av))) { rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--; - } else if (ac && !strncmp(*av,"count",strlen(*av))) { + } else if (!strncmp(*av,"count",strlen(*av))) { rule.fw_flg |= IP_FW_F_COUNT; av++; ac--; - } else if (ac && !strncmp(*av,"divert",strlen(*av))) { + } else if (!strncmp(*av,"divert",strlen(*av))) { rule.fw_flg |= IP_FW_F_DIVERT; av++; ac--; if (!ac) show_usage("missing divert port"); rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; if (rule.fw_divert_port == 0) show_usage("illegal divert port"); - } else if (ac && (!strncmp(*av,"deny",strlen(*av)))) { + } else if (!strncmp(*av,"tee",strlen(*av))) { + rule.fw_flg |= IP_FW_F_TEE; av++; ac--; + if (!ac) + show_usage("missing divert port"); + rule.fw_divert_port = strtoul(*av, NULL, 0); av++; ac--; + if (rule.fw_divert_port == 0) + show_usage("illegal divert port"); + } else if (!strncmp(*av,"skipto",strlen(*av))) { + rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--; + if (!ac) + show_usage("missing skipto rule number"); + rule.fw_skipto_rule = strtoul(*av, NULL, 0); av++; ac--; + } else if ((!strncmp(*av,"deny",strlen(*av)) + || !strncmp(*av,"drop",strlen(*av)))) { rule.fw_flg |= IP_FW_F_DENY; av++; ac--; - } else if (ac && !strncmp(*av,"reject",strlen(*av))) { - rule.fw_flg |= IP_FW_F_DENY|IP_FW_F_ICMPRPL; av++; ac--; + } else if (!strncmp(*av,"reject",strlen(*av))) { + rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; + rule.fw_reject_code = ICMP_UNREACH_HOST; + } else if (!strncmp(*av,"reset",strlen(*av))) { + rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; + rule.fw_reject_code = IP_FW_REJECT_RST; /* check TCP later */ + } else if (!strncmp(*av,"unreach",strlen(*av))) { + rule.fw_flg |= IP_FW_F_REJECT; av++; ac--; + fill_reject_code(&rule.fw_reject_code, *av); av++; ac--; } else { - show_usage("missing/unrecognized action\n"); + show_usage("invalid action ``%s''", *av); } /* [log] */ @@ -670,103 +817,142 @@ add(ac,av) } /* protocol */ - if (ac) { - if ((proto = atoi(*av)) > 0) { - rule.fw_prot = proto; av++; ac--; - } else if (!strncmp(*av,"all",strlen(*av))) { - rule.fw_prot = IPPROTO_IP; av++; ac--; - } else if ((pe = getprotobyname(*av)) != NULL) { - rule.fw_prot = pe->p_proto; av++; ac--; - } else { - show_usage("invalid protocol\n"); - } - } else - show_usage("missing protocol\n"); + if (ac == 0) + show_usage("missing protocol"); + if ((proto = atoi(*av)) > 0) { + rule.fw_prot = proto; av++; ac--; + } else if (!strncmp(*av,"all",strlen(*av))) { + rule.fw_prot = IPPROTO_IP; av++; ac--; + } else if ((pe = getprotobyname(*av)) != NULL) { + rule.fw_prot = pe->p_proto; av++; ac--; + } else { + show_usage("invalid protocol ``%s''", *av); + } + + if (rule.fw_prot != IPPROTO_TCP + && (rule.fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT + && rule.fw_reject_code == IP_FW_REJECT_RST) + show_usage("``reset'' is only valid for tcp packets"); /* from */ if (ac && !strncmp(*av,"from",strlen(*av))) { av++; ac--; } - else show_usage("missing ``from''\n"); + else + show_usage("missing ``from''"); if (ac && !strncmp(*av,"not",strlen(*av))) { rule.fw_flg |= IP_FW_F_INVSRC; av++; ac--; } - if (!ac) show_usage("Missing arguments\n"); + if (!ac) + show_usage("missing arguments"); fill_ip(&rule.fw_src, &rule.fw_smsk, &ac, &av); if (ac && isdigit(**av)) { - if (fill_port(&rule.fw_nsp, &rule.fw_pts, 0, *av, 0)) + u_short nports = 0; + + if (fill_port(&nports, rule.fw_pts, 0, *av)) rule.fw_flg |= IP_FW_F_SRNG; + IP_FW_SETNSRCP(&rule, nports); av++; ac--; } /* to */ if (ac && !strncmp(*av,"to",strlen(*av))) { av++; ac--; } - else show_usage("missing ``to''\n"); + else + show_usage("missing ``to''"); if (ac && !strncmp(*av,"not",strlen(*av))) { rule.fw_flg |= IP_FW_F_INVDST; av++; ac--; } - if (!ac) show_usage("Missing arguments\n"); + if (!ac) + show_usage("missing arguments"); fill_ip(&rule.fw_dst, &rule.fw_dmsk, &ac, &av); if (ac && isdigit(**av)) { - if (fill_port(&rule.fw_ndp, &rule.fw_pts, rule.fw_nsp, *av, 0)) + u_short nports = 0; + + if (fill_port(&nports, + rule.fw_pts, IP_FW_GETNSRCP(&rule), *av)) rule.fw_flg |= IP_FW_F_DRNG; + IP_FW_SETNDSTP(&rule, nports); av++; ac--; } - if ((rule.fw_prot != IPPROTO_TCP) && - (rule.fw_prot != IPPROTO_UDP) && - (rule.fw_nsp || rule.fw_ndp)) { - show_usage("only TCP and UDP protocols are valid with port specifications"); + if ((rule.fw_prot != IPPROTO_TCP) && (rule.fw_prot != IPPROTO_UDP) + && (IP_FW_GETNSRCP(&rule) || IP_FW_GETNDSTP(&rule))) { + show_usage("only TCP and UDP protocols are valid" + " with port specifications"); } while (ac) { - if (ac && !strncmp(*av,"via",strlen(*av))) { - if (rule.fw_via_ip.s_addr || (rule.fw_flg & IP_FW_F_IFNAME)) { - show_usage("multiple 'via' options specified"); - } - - av++; ac--; - if (!ac) { - show_usage("'via' option specified with no interface."); - } - if (!isdigit(**av)) { - char *q; - - strncpy(rule.fw_via_name, *av, sizeof(rule.fw_via_name)); - rule.fw_via_name[sizeof(rule.fw_via_name) - 1] = '\0'; - for (q = rule.fw_via_name; *q && !isdigit(*q) && *q != '*'; q++) - continue; - if (*q == '*') - rule.fw_flg |= IP_FW_F_IFUWILD; - else - rule.fw_via_unit = atoi(q); - *q = '\0'; - rule.fw_flg |= IP_FW_F_IFNAME; - if (verify_interface(&rule) != 0) - fprintf(stderr, "Warning: interface does not exist\n"); - } else if (inet_aton(*av,&rule.fw_via_ip) == INADDR_NONE) { - show_usage("bad IP# after via\n"); + if (!strncmp(*av,"in",strlen(*av))) { + rule.fw_flg |= IP_FW_F_IN; + av++; ac--; continue; + } + if (!strncmp(*av,"out",strlen(*av))) { + rule.fw_flg |= IP_FW_F_OUT; + av++; ac--; continue; + } + if (ac && !strncmp(*av,"xmit",strlen(*av))) { + union ip_fw_if ifu; + int byname; + + if (saw_via) { +badviacombo: + show_usage("``via'' is incompatible" + " with ``xmit'' and ``recv''"); } + saw_xmrc = 1; av++; ac--; - continue; + fill_iface("xmit", &ifu, &byname, ac, *av); + rule.fw_out_if = ifu; + rule.fw_flg |= IP_FW_F_OIFACE; + if (byname) + rule.fw_flg |= IP_FW_F_OIFNAME; + av++; ac--; continue; } - if (!strncmp(*av,"fragment",strlen(*av))) { - rule.fw_flg |= IP_FW_F_FRAG; av++; ac--; continue; + if (ac && !strncmp(*av,"recv",strlen(*av))) { + union ip_fw_if ifu; + int byname; + + if (saw_via) + goto badviacombo; + saw_xmrc = 1; + av++; ac--; + fill_iface("recv", &ifu, &byname, ac, *av); + rule.fw_in_if = ifu; + rule.fw_flg |= IP_FW_F_IIFACE; + if (byname) + rule.fw_flg |= IP_FW_F_IIFNAME; + av++; ac--; continue; } - if (!strncmp(*av,"in",strlen(*av))) { - rule.fw_flg |= IP_FW_F_IN; av++; ac--; continue; + if (ac && !strncmp(*av,"via",strlen(*av))) { + union ip_fw_if ifu; + int byname = 0; + + if (saw_xmrc) + goto badviacombo; + saw_via = 1; + av++; ac--; + fill_iface("via", &ifu, &byname, ac, *av); + rule.fw_out_if = rule.fw_in_if = ifu; + if (byname) + rule.fw_flg |= + (IP_FW_F_IIFNAME | IP_FW_F_OIFNAME); + av++; ac--; continue; } - if (!strncmp(*av,"out",strlen(*av))) { - rule.fw_flg |= IP_FW_F_OUT; av++; ac--; continue; + if (!strncmp(*av,"fragment",strlen(*av))) { + rule.fw_flg |= IP_FW_F_FRAG; + av++; ac--; continue; } - if (ac > 1 && !strncmp(*av,"ipoptions",strlen(*av))) { + if (!strncmp(*av,"ipoptions",strlen(*av))) { av++; ac--; + if (!ac) + show_usage("missing argument" + " for ``ipoptions''"); fill_ipopt(&rule.fw_ipopt, &rule.fw_ipnopt, av); av++; ac--; continue; } @@ -780,31 +966,50 @@ add(ac,av) rule.fw_tcpnf |= IP_FW_TCPF_ACK; av++; ac--; continue; } - if (ac > 1 && !strncmp(*av,"tcpflags",strlen(*av))) { + if (!strncmp(*av,"tcpflags",strlen(*av))) { av++; ac--; + if (!ac) + show_usage("missing argument" + " for ``tcpflags''"); fill_tcpflag(&rule.fw_tcpf, &rule.fw_tcpnf, av); av++; ac--; continue; } } if (rule.fw_prot == IPPROTO_ICMP) { - if (ac > 1 && !strncmp(*av,"icmptypes",strlen(*av))) { + if (!strncmp(*av,"icmptypes",strlen(*av))) { av++; ac--; - fill_icmptypes(rule.fw_icmptypes, av, &rule.fw_flg); + if (!ac) + show_usage("missing argument" + " for ``icmptypes''"); + fill_icmptypes(rule.fw_icmptypes, + av, &rule.fw_flg); av++; ac--; continue; } } - printf("%d %s\n",ac,*av); - show_usage("Unknown argument\n"); + show_usage("unknown argument ``%s''", *av); } + /* No direction specified -> do both directions */ + if (!(rule.fw_flg & (IP_FW_F_OUT|IP_FW_F_IN))) + rule.fw_flg |= (IP_FW_F_OUT|IP_FW_F_IN); + + /* Sanity check interface check, but handle "via" case separately */ + if (saw_via) { + if (rule.fw_flg & IP_FW_F_IN) + rule.fw_flg |= IP_FW_F_IIFACE; + if (rule.fw_flg & IP_FW_F_OUT) + rule.fw_flg |= IP_FW_F_OIFACE; + } else if ((rule.fw_flg & IP_FW_F_OIFACE) && (rule.fw_flg & IP_FW_F_IN)) + show_usage("can't check xmit interface of incoming packets"); + if (!do_quiet) show_ipfw(&rule); i = setsockopt(s, IPPROTO_IP, IP_FW_ADD, &rule, sizeof rule); if (i) - err(1,"setsockopt(IP_FW_ADD)"); + err(1, "setsockopt(%s)", "IP_FW_ADD"); } -void +static void zero (ac, av) int ac; char **av; @@ -813,28 +1018,26 @@ zero (ac, av) if (!ac) { /* clear all entries */ - if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0) { - fprintf(stderr,"%s: setsockopt failed.\n",progname); - exit(1); - } + if (setsockopt(s,IPPROTO_IP,IP_FW_ZERO,NULL,0)<0) + err(1, "setsockopt(%s)", "IP_FW_ZERO"); if (!do_quiet) printf("Accounting cleared.\n"); } else { - /* clear a specific entry */ struct ip_fw rule; memset(&rule, 0, sizeof rule); - - /* Rule number */ - if (isdigit(**av)) { - rule.fw_number = atoi(*av); av++; ac--; - - if (setsockopt(s, IPPROTO_IP, IP_FW_ZERO, &rule, sizeof rule)) - err(1, "setsockopt(Zero)"); - printf("Entry %d cleared\n", rule.fw_number); - } - else { - show_usage("expected number"); + while (ac) { + /* Rule number */ + if (isdigit(**av)) { + rule.fw_number = atoi(*av); av++; ac--; + if (setsockopt(s, IPPROTO_IP, + IP_FW_ZERO, &rule, sizeof rule)) + warn("setsockopt(%s)", "IP_FW_ZERO"); + else + printf("Entry %d cleared\n", + rule.fw_number); + } else + show_usage("invalid rule number ``%s''", *av); } } } @@ -874,7 +1077,7 @@ ipfw_main(ac,av) do_resolv=1; break; default: - show_usage("Unrecognised switch"); + show_usage("invalid flag ``-%c''", ch); } ac -= optind; @@ -908,10 +1111,8 @@ ipfw_main(ac,av) do_flush = 1; } if ( do_flush ) { - if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0)<0) { - fprintf(stderr,"%s: setsockopt failed.\n",progname); - exit(1); - } + if (setsockopt(s,IPPROTO_IP,IP_FW_FLUSH,NULL,0) < 0) + err(1, "setsockopt(%s)", "IP_FW_FLUSH"); if (!do_quiet) printf("Flushed all rules.\n"); } @@ -936,47 +1137,34 @@ main(ac, av) char **av; { #define MAX_ARGS 32 +#define WHITESP " \t\f\v\n\r" char buf[BUFSIZ]; - char *args[MAX_ARGS]; + char *a, *args[MAX_ARGS]; char linename[10]; int i; FILE *f; - strncpy(progname,*av, sizeof(progname)); - progname[sizeof(progname) - 1] = '\0'; - s = socket( AF_INET, SOCK_RAW, IPPROTO_RAW ); - if ( s < 0 ) { - fprintf(stderr,"%s: Can't open raw socket.\n" - "Must be root to use this program.\n",progname); - exit(1); - } + if ( s < 0 ) + err(1, "socket"); setbuf(stdout,0); if (av[1] && !access(av[1], R_OK)) { lineno = 0; - f = fopen(av[1], "r"); + if ((f = fopen(av[1], "r")) == NULL) + err(1, "fopen: %s", av[1]); while (fgets(buf, BUFSIZ, f)) { - if (buf[strlen(buf)-1]=='\n') - buf[strlen(buf)-1] = 0; lineno++; sprintf(linename, "Line %d", lineno); args[0] = linename; - args[1] = buf; - while(*args[1] == ' ') - args[1]++; - i = 2; - while((args[i] = strchr(args[i-1],' '))) { - *(args[i]++) = 0; - while(*args[i] == ' ') - args[i]++; - i++; - } - if (*args[i-1] == 0) - i--; + for (i = 1, a = strtok(buf, WHITESP); + a && i < MAX_ARGS; a = strtok(NULL, WHITESP), i++) + args[i] = a; + if (i == MAX_ARGS) + errx(1, "%s: too many arguments", linename); args[i] = NULL; ipfw_main(i, args); |