diff --git a/sbin/ipfw/Makefile b/sbin/ipfw/Makefile index 3effc23dd42e..1bc6fa465f98 100644 --- a/sbin/ipfw/Makefile +++ b/sbin/ipfw/Makefile @@ -1,5 +1,7 @@ PROG= ipfw +COPTS+= -Wall + MAN8= ipfw.8 .include diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 6bfa4cd12c19..7198d049561c 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
.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 12547214c0fc..7a426ec68972 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 #include #include +#include #include #include #include #include #include +#include +#include +#include +#include #include #include #include -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;ifw_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;ifw_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|}}\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 -); +" %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|}\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); - - fprintf(stderr,"See man %s(8) for proper usage.\n",progname); - exit (1); + 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 */ - - return(0); /* interface exists */ + warnx("warning: interface ``%s'' does not exist", ifr.ifr_name); } -void +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; +} + +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"); - } - av++; ac--; - continue; - } - if (!strncmp(*av,"fragment",strlen(*av))) { - rule.fw_flg |= IP_FW_F_FRAG; av++; ac--; continue; - } if (!strncmp(*av,"in",strlen(*av))) { - rule.fw_flg |= IP_FW_F_IN; av++; ac--; continue; + 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; + rule.fw_flg |= IP_FW_F_OUT; + av++; ac--; continue; } - if (ac > 1 && !strncmp(*av,"ipoptions",strlen(*av))) { + 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--; + 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 (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 (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,"fragment",strlen(*av))) { + rule.fw_flg |= IP_FW_F_FRAG; + av++; ac--; continue; + } + 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); diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c index dab18649f1a6..0c941a382c5c 100644 --- a/sys/netinet/ip_divert.c +++ b/sys/netinet/ip_divert.c @@ -30,7 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: ip_divert.c,v 1.10 1997/05/26 03:33:48 peter Exp $ + * $Id: ip_divert.c,v 1.11 1997/06/01 15:58:44 peter Exp $ */ #include @@ -124,13 +124,20 @@ div_init(void) void div_input(struct mbuf *m, int hlen) { - register struct ip *ip = mtod(m, struct ip *); - register struct inpcb *inp; - register struct socket *sa; + struct ip *ip; + struct inpcb *inp; + struct socket *sa; /* Sanity check */ if (ip_divert_port == 0) - panic("div_input"); + panic("div_input: port is 0"); + + /* Assure header */ + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == 0) { + return; + } + ip = mtod(m, struct ip *); /* Record divert port */ divsrc.sin_port = htons(ip_divert_port); @@ -145,6 +152,12 @@ div_input(struct mbuf *m, int hlen) if (hlen) { struct ifaddr *ifa; +#ifdef DIAGNOSTIC + /* Sanity check */ + if (!(m->m_flags & M_PKTHDR)) + panic("div_input: no pkt hdr"); +#endif + /* More fields affected by ip_input() */ HTONS(ip->ip_id); diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c index 71fa62c54742..0886a438122b 100644 --- a/sys/netinet/ip_fw.c +++ b/sys/netinet/ip_fw.c @@ -12,7 +12,7 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id: ip_fw.c,v 1.56 1997/04/06 11:09:03 dufault Exp $ + * $Id: ip_fw.c,v 1.3 1997/05/15 04:20:17 archie Exp $ */ /* @@ -37,11 +37,14 @@ #include #include #include -#include -#include #include #include #include +#include +#include +#include +#include +#include static int fw_debug = 1; #ifdef IPFIREWALL_VERBOSE @@ -74,24 +77,25 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit, CTLFLAG_RW, &fw_verbose_lim #define dprint_ip(a) if (!fw_debug); else print_ip(a) static int add_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); -static int del_entry __P((struct ip_fw_head *chainptr, struct ip_fw *frwl)); +static int del_entry __P((struct ip_fw_head *chainptr, u_short number)); static int zero_entry __P((struct mbuf *m)); -static struct ip_fw * - check_ipfw_struct __P(( struct mbuf *m)); +static struct ip_fw *check_ipfw_struct __P((struct ip_fw *m)); +static struct ip_fw *check_ipfw_mbuf __P((struct mbuf *fw)); static int ipopts_match __P((struct ip *ip, struct ip_fw *f)); static int port_match __P((u_short *portptr, int nports, u_short port, int range_flag)); static int tcpflg_match __P((struct tcphdr *tcp, struct ip_fw *f)); static int icmptype_match __P((struct icmp * icmp, struct ip_fw * f)); -static void ipfw_report __P((char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif)); +static void ipfw_report __P((struct ip_fw *f, struct ip *ip, + struct ifnet *rif, struct ifnet *oif)); #ifdef IPFIREWALL_MODULE static ip_fw_chk_t *old_chk_ptr; static ip_fw_ctl_t *old_ctl_ptr; #endif -static int ip_fw_chk __P((struct ip **pip, int hlen, struct ifnet *rif, - int dirport, struct mbuf **m)); +static int ip_fw_chk __P((struct ip **pip, int hlen, + struct ifnet *oif, int ignport, struct mbuf **m)); static int ip_fw_ctl __P((int stage, struct mbuf **mm)); static char err_prefix[] = "ip_fw_ctl:"; @@ -214,17 +218,88 @@ ipopts_match(struct ip *ip, struct ip_fw *f) return 0; } -static void -ipfw_report(char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif) +static inline int +iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname) { - struct tcphdr *tcp = (struct tcphdr *) ((u_long *) ip + ip->ip_hl); - struct udphdr *udp = (struct udphdr *) ((u_long *) ip + ip->ip_hl); - struct icmp *icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); - if (!fw_verbose) + /* Check by name or by IP address */ + if (byname) { + /* Check unit number (-1 is wildcard) */ + if (ifu->fu_via_if.unit != -1 + && ifp->if_unit != ifu->fu_via_if.unit) + return(0); + /* Check name */ + if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN)) + return(0); + return(1); + } else if (ifu->fu_via_ip.s_addr != 0) { /* Zero == wildcard */ + struct ifaddr *ia; + + for (ia = ifp->if_addrhead.tqh_first; + ia != NULL; ia = ia->ifa_link.tqe_next) { + if (ia->ifa_addr == NULL) + continue; + if (ia->ifa_addr->sa_family != AF_INET) + continue; + if (ifu->fu_via_ip.s_addr != ((struct sockaddr_in *) + (ia->ifa_addr))->sin_addr.s_addr) + continue; + return(1); + } + return(0); + } + return(1); +} + +static void +ipfw_report(struct ip_fw *f, struct ip *ip, + struct ifnet *rif, struct ifnet *oif) +{ + static int counter; + struct tcphdr *const tcp = (struct tcphdr *) ((u_long *) ip+ ip->ip_hl); + struct udphdr *const udp = (struct udphdr *) ((u_long *) ip+ ip->ip_hl); + struct icmp *const icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); + char *cmd; + int count; + + /* Print command name */ + printf("ipfw: %d ", f ? f->fw_number : -1); + if (!f) + printf("Refuse"); + else + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_DENY: + printf("Deny"); + break; + case IP_FW_F_REJECT: + if (f->fw_reject_code == IP_FW_REJECT_RST) + printf("Reset"); + else + printf("Unreach"); + break; + case IP_FW_F_ACCEPT: + printf("Accept"); + break; + case IP_FW_F_COUNT: + printf("Count"); + break; + case IP_FW_F_DIVERT: + printf("Divert %d", f->fw_divert_port); + break; + case IP_FW_F_TEE: + printf("Tee %d", f->fw_divert_port); + break; + case IP_FW_F_SKIPTO: + printf("SkipTo %d", f->fw_skipto_rule); + break; + default: + printf("UNKNOWN"); + break; + } + printf(" "); + + count = f ? f->fw_pcnt : ++counter; + if (fw_verbose_limit != 0 && count > fw_verbose_limit) return; - if (fw_verbose_limit != 0 && counter > fw_verbose_limit) - return; - printf("ipfw: %d %s ",rule, txt); switch (ip->ip_p) { case IPPROTO_TCP: printf("TCP "); @@ -253,66 +328,57 @@ ipfw_report(char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif) print_ip(ip->ip_dst); break; } - printf(" via %s%d", rif->if_name, rif->if_unit); + if (oif) + printf(" out via %s%d", oif->if_name, oif->if_unit); + else if (rif) + printf(" in via %s%d", rif->if_name, rif->if_unit); if ((ip->ip_off & IP_OFFMASK)) printf(" Fragment = %d",ip->ip_off & IP_OFFMASK); printf("\n"); - if (fw_verbose_limit != 0 && counter == fw_verbose_limit) - printf("ipfw: limit reached on rule #%d\n", rule); + if (fw_verbose_limit != 0 && count == fw_verbose_limit) + printf("ipfw: limit reached on rule #%d\n", + f ? f->fw_number : -1); } /* - * We overload the "dirport" parameter: + * Parameters: * - * If dirport is negative, packet is outgoing; otherwise incoming. - * The low order 16 bits of dirport, if non-zero, indicate that - * we should ignore all ``divert '' rules, where is - * the low order 16 bits. + * ip Pointer to packet header (struct ip *) + * hlen Packet header length + * oif Outgoing interface, or NULL if packet is incoming + * ignport Ignore all divert/tee rules to this port (if non-zero) + * *m The packet; we set to NULL when/if we nuke it. * * Return value: * - * -1 The packet was denied/rejected and has been dropped - * 0 The packet is to be accepted; route normally - * Divert the packet to divert , if any socket - * is bound to it; otherwise just drop it. + * 0 The packet is to be accepted and routed normally OR + * the packet was denied/rejected and has been dropped; + * in the latter case, *m is equal to NULL upon return. + * port Divert the packet to port. */ static int ip_fw_chk(struct ip **pip, int hlen, - struct ifnet *rif, int dirport, struct mbuf **m) + struct ifnet *oif, int ignport, struct mbuf **m) { struct ip_fw_chain *chain; - register struct ip_fw *f = NULL; + struct ip_fw *rule; struct ip *ip = *pip; - struct tcphdr *tcp = (struct tcphdr *) ((u_long *) ip + ip->ip_hl); - struct udphdr *udp = (struct udphdr *) ((u_long *) ip + ip->ip_hl); - struct icmp *icmp = (struct icmp *) ((u_long *) ip + ip->ip_hl); - struct ifaddr *ia = NULL, *ia_p; - struct in_addr src, dst, ia_i; + struct ifnet *const rif = (*m)->m_pkthdr.rcvif; u_short src_port, dst_port, offset; - src = ip->ip_src; - dst = ip->ip_dst; - - /* - * If we got interface from which packet came-store pointer to it's - * first adress - */ - if (rif != NULL) - ia = rif->if_addrhead.tqh_first; - /* * Go down the chain, looking for enlightment */ for (chain=ip_fw_chain.lh_first; chain; chain = chain->chain.le_next) { - f = chain->rule; + register struct ip_fw *const f = chain->rule; /* Check direction inbound */ - if (dirport >= 0 && !(f->fw_flg & IP_FW_F_IN)) + if (!oif && !(f->fw_flg & IP_FW_F_IN)) continue; /* Check direction outbound */ - if (dirport < 0 && !(f->fw_flg & IP_FW_F_OUT)) + if (oif && !(f->fw_flg & IP_FW_F_OUT)) continue; /* Fragments */ @@ -321,169 +387,234 @@ ip_fw_chk(struct ip **pip, int hlen, /* If src-addr doesn't match, not this rule. */ if ((f->fw_flg & IP_FW_F_INVSRC) != 0 - ^ (src.s_addr & f->fw_smsk.s_addr) != f->fw_src.s_addr) + ^ (ip->ip_src.s_addr & f->fw_smsk.s_addr) != f->fw_src.s_addr) continue; /* If dest-addr doesn't match, not this rule. */ if ((f->fw_flg & IP_FW_F_INVDST) != 0 - ^ (dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr) + ^ (ip->ip_dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr) continue; - /* If a i/f name was specified, and we don't know */ - if ((f->fw_flg & IP_FW_F_IFNAME) && !rif) - continue; + /* Interface check */ + if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { + struct ifnet *const iface = oif ? oif : rif; - /* If a i/f name was specified, check it */ - if ((f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_name[0]) { - - /* Not same unit, don't match */ - if (!(f->fw_flg & IP_FW_F_IFUWILD) && rif->if_unit != f->fw_via_unit) + /* Backwards compatibility hack for "via" */ + if (!iface || !iface_match(iface, + &f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME)) continue; - - /* Not same name */ - if (strncmp(rif->if_name, f->fw_via_name, FW_IFNLEN)) + } else { + /* Check receive interface */ + if ((f->fw_flg & IP_FW_F_IIFACE) + && (!rif || !iface_match(rif, + &f->fw_in_if, f->fw_flg & IP_FW_F_IIFNAME))) + continue; + /* Check outgoing interface */ + if ((f->fw_flg & IP_FW_F_OIFACE) + && (!oif || !iface_match(oif, + &f->fw_out_if, f->fw_flg & IP_FW_F_OIFNAME))) continue; } - /* If a i/f addr was specified, check it */ - if (!(f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_ip.s_addr) { - int match = 0; - - for (ia_p = ia; ia_p != NULL; - ia_p = ia_p->ifa_link.tqe_next) { - if ((ia_p->ifa_addr == NULL)) - continue; - if (ia_p->ifa_addr->sa_family != AF_INET) - continue; - ia_i.s_addr = - ((struct sockaddr_in *) - (ia_p->ifa_addr))->sin_addr.s_addr; - if (ia_i.s_addr != f->fw_via_ip.s_addr) - continue; - match = 1; - break; - } - if (!match) - continue; - } - - /* - * Check IP options - */ - if (f->fw_ipopt != f->fw_ipnopt) - if (!ipopts_match(ip, f)) - continue; + /* Check IP options */ + if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f)) + continue; - /* If wildcard, match */ + /* Check protocol; if wildcard, match */ if (f->fw_prot == IPPROTO_IP) goto got_match; - /* If different, dont match */ + /* If different, don't match */ if (ip->ip_p != f->fw_prot) continue; + /* Get fragment offset (if any) */ + offset = (ip->ip_off & IP_OFFMASK); + +#define PULLUP_TO(len) do { \ + if ((*m)->m_len < (len) \ + && (*m = m_pullup(*m, (len))) == 0) { \ + goto bogusfrag; \ + } \ + *pip = ip = mtod(*m, struct ip *); \ + } while (0) + + /* Protocol specific checks */ switch (ip->ip_p) { case IPPROTO_TCP: - offset = ip->ip_off & IP_OFFMASK; - if (offset == 1) { - static int frag_counter = 0; - ++frag_counter; - ipfw_report("Refuse", -1, ip, frag_counter, rif); - m_freem(*m); - return -1; - } - if ((offset == 0) && - (f->fw_tcpf != f->fw_tcpnf) && - !tcpflg_match(tcp, f)) - continue; + { + struct tcphdr *tcp; + if (offset == 1) /* cf. RFC 1858 */ + goto bogusfrag; + if (offset != 0) /* Flags, ports aren't valid */ + break; + PULLUP_TO(hlen + 14); + tcp = (struct tcphdr *) ((u_long *)ip + ip->ip_hl); + if (f->fw_tcpf != f->fw_tcpnf && !tcpflg_match(tcp, f)) + continue; src_port = ntohs(tcp->th_sport); dst_port = ntohs(tcp->th_dport); goto check_ports; + } case IPPROTO_UDP: + { + struct udphdr *udp; + + if (offset != 0) /* Ports aren't valid */ + break; + PULLUP_TO(hlen + 4); + udp = (struct udphdr *) ((u_long *)ip + ip->ip_hl); src_port = ntohs(udp->uh_sport); dst_port = ntohs(udp->uh_dport); - check_ports: - if (!port_match(&f->fw_pts[0], f->fw_nsp, - src_port, f->fw_flg & IP_FW_F_SRNG)) + if (!port_match(&f->fw_pts[0], + IP_FW_GETNSRCP(f), src_port, + f->fw_flg & IP_FW_F_SRNG)) continue; - if (!port_match(&f->fw_pts[f->fw_nsp], f->fw_ndp, - dst_port, f->fw_flg & IP_FW_F_DRNG)) + if (!port_match(&f->fw_pts[IP_FW_GETNSRCP(f)], + IP_FW_GETNDSTP(f), dst_port, + f->fw_flg & IP_FW_F_DRNG)) continue; break; + } case IPPROTO_ICMP: + { + struct icmp *icmp; + + if (offset != 0) /* Type isn't valid */ + break; + PULLUP_TO(hlen + 2); + icmp = (struct icmp *) ((u_long *)ip + ip->ip_hl); if (!icmptype_match(icmp, f)) continue; - goto got_match; - - default: break; + } + +bogusfrag: + if (fw_verbose) + ipfw_report(NULL, ip, rif, oif); + goto dropit; } got_match: - f->fw_pcnt++; - f->fw_bcnt+=ip->ip_len; - f->timestamp = time.tv_sec; - if (f->fw_flg & IP_FW_F_PRN) { - if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_ACCEPT) { - ipfw_report("Allow", - f->fw_number, ip, f->fw_pcnt, rif); - } else if ((f->fw_flg & IP_FW_F_COMMAND) - == IP_FW_F_DIVERT) { - if (f->fw_divert_port != (dirport & 0xffff)) - ipfw_report("Divert", f->fw_number, - ip, f->fw_pcnt, rif); - } else if ((f->fw_flg & IP_FW_F_COMMAND) - == IP_FW_F_COUNT) { - ipfw_report("Count", - f->fw_number, ip, f->fw_pcnt, rif); - } else { - ipfw_report("Deny", - f->fw_number, ip, f->fw_pcnt, rif); - } + /* Ignore divert/tee rule if socket port is "ignport" */ + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_DIVERT: + case IP_FW_F_TEE: + if (f->fw_divert_port == ignport) + continue; /* ignore this rule */ + break; } + /* Update statistics */ + f->fw_pcnt += 1; + f->fw_bcnt += ip->ip_len; + f->timestamp = time.tv_sec; + + /* Log to console if desired */ + if ((f->fw_flg & IP_FW_F_PRN) && fw_verbose) + ipfw_report(f, ip, rif, oif); + /* Take appropriate action */ - if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_ACCEPT) { - return 0; - } else if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_COUNT) { + switch (f->fw_flg & IP_FW_F_COMMAND) { + case IP_FW_F_ACCEPT: + return(0); + case IP_FW_F_COUNT: continue; - } else if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DIVERT) { - if (f->fw_divert_port == (dirport & 0xffff)) - continue; /* ignore this rule */ - return (f->fw_divert_port); - } else - break; /* ie, deny/reject */ + case IP_FW_F_DIVERT: + return(f->fw_divert_port); + case IP_FW_F_TEE: + /* + * XXX someday tee packet here, but beware that you + * can't use m_copym() or m_copypacket() because + * the divert input routine modifies the mbuf + * (and these routines only increment reference + * counts in the case of mbuf clusters), so need + * to write custom routine. + */ + continue; + case IP_FW_F_SKIPTO: +#ifdef DIAGNOSTIC + while (chain->chain.le_next + && chain->chain.le_next->rule->fw_number + < f->fw_skipto_rule) +#else + while (chain->chain.le_next->rule->fw_number + < f->fw_skipto_rule) +#endif + chain = chain->chain.le_next; + continue; + } + + /* Deny/reject this packet using this rule */ + rule = f; + break; } #ifdef DIAGNOSTIC - if (!chain) /* rule 65535 should always be there */ + /* Rule 65535 should always be there and should always match */ + if (!chain) panic("ip_fw: chain"); - if (!f) - panic("ip_fw: entry"); #endif /* * At this point, we're going to drop the packet. - * Send an ICMP only if all of the following are true: + * Send a reject notice if all of the following are true: * - * - The packet is an incoming packet - * - The packet matched a deny rule + * - The packet matched a reject rule * - The packet is not an ICMP packet - * - The rule has the special ICMP reply flag set + * - The packet is not a multicast or broadcast packet */ - if (dirport >= 0 - && (f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DENY - && (ip->ip_p != IPPROTO_ICMP) - && (f->fw_flg & IP_FW_F_ICMPRPL)) { - icmp_error(*m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0L, 0); - return -1; + if ((rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_REJECT + && ip->ip_p != IPPROTO_ICMP + && !((*m)->m_flags & (M_BCAST|M_MCAST)) + && !IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) { + switch (rule->fw_reject_code) { + case IP_FW_REJECT_RST: + { + struct tcphdr *const tcp = + (struct tcphdr *) ((u_long *)ip + ip->ip_hl); + struct tcpiphdr ti; + + if (offset != 0 || (tcp->th_flags & TH_RST)) + break; + ti.ti_i = *((struct ipovly *) ip); + ti.ti_t = *tcp; + NTOHL(ti.ti_seq); + NTOHL(ti.ti_ack); + ti.ti_len = ip->ip_len - hlen - (ti.ti_off << 2); + if (tcp->th_flags & TH_ACK) { + tcp_respond(NULL, &ti, *m, + (tcp_seq)0, ntohl(tcp->th_ack), TH_RST); + } else { + if (tcp->th_flags & TH_SYN) + ti.ti_len++; + tcp_respond(NULL, &ti, *m, ti.ti_seq + + ti.ti_len, (tcp_seq)0, TH_RST|TH_ACK); + } + *m = NULL; + break; + } + default: /* Send an ICMP unreachable using code */ + icmp_error(*m, ICMP_UNREACH, + rule->fw_reject_code, 0L, 0); + *m = NULL; + break; + } } - m_freem(*m); - return -1; + +dropit: + /* + * Finally, drop the packet. + */ + if (*m) { + m_freem(*m); + *m = NULL; + } + return(0); } static int @@ -518,6 +649,7 @@ add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) if (fwc) free(fwc, M_IPFW); if (ftmp) free(ftmp, M_IPFW); splx(s); + dprintf(("%s bad rule number\n", err_prefix)); return (EINVAL); } @@ -553,7 +685,7 @@ add_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) } static int -del_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) +del_entry(struct ip_fw_head *chainptr, u_short number) { struct ip_fw_chain *fcp; int s; @@ -561,9 +693,9 @@ del_entry(struct ip_fw_head *chainptr, struct ip_fw *frwl) s = splnet(); fcp = chainptr->lh_first; - if (frwl->fw_number != (u_short)-1) { + if (number != (u_short)-1) { for (; fcp; fcp = fcp->chain.le_next) { - if (fcp->rule->fw_number == frwl->fw_number) { + if (fcp->rule->fw_number == number) { LIST_REMOVE(fcp, chain); splx(s); free(fcp->rule, M_IPFW); @@ -585,10 +717,9 @@ zero_entry(struct mbuf *m) int s; if (m) { - frwl = check_ipfw_struct(m); - - if (!frwl) + if (m->m_len != sizeof(struct ip_fw)) return(EINVAL); + frwl = mtod(m, struct ip_fw *); } else frwl = NULL; @@ -606,56 +737,80 @@ zero_entry(struct mbuf *m) } splx(s); +#if 0 if ( frwl ) printf("ipfw: Entry %d cleared.\n", frwl->fw_number); else printf("ipfw: Accounting cleared.\n"); +#endif return(0); } static struct ip_fw * -check_ipfw_struct(struct mbuf *m) +check_ipfw_mbuf(struct mbuf *m) { - struct ip_fw *frwl; - + /* Check length */ if (m->m_len != sizeof(struct ip_fw)) { dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, sizeof(struct ip_fw))); return (NULL); } - frwl = mtod(m, struct ip_fw *); + return(check_ipfw_struct(mtod(m, struct ip_fw *))); +} +static struct ip_fw * +check_ipfw_struct(struct ip_fw *frwl) +{ + /* Check for invalid flag bits */ if ((frwl->fw_flg & ~IP_FW_F_MASK) != 0) { dprintf(("%s undefined flag bits set (flags=%x)\n", err_prefix, frwl->fw_flg)); return (NULL); } - - /* If neither In nor Out, then both */ - if (!(frwl->fw_flg & (IP_FW_F_IN | IP_FW_F_OUT))) - frwl->fw_flg |= IP_FW_F_IN | IP_FW_F_OUT; - - if ((frwl->fw_flg & IP_FW_F_SRNG) && frwl->fw_nsp < 2) { + /* Must apply to incoming or outgoing (or both) */ + if (!(frwl->fw_flg & (IP_FW_F_IN | IP_FW_F_OUT))) { + dprintf(("%s neither in nor out\n", err_prefix)); + return (NULL); + } + /* Empty interface name is no good */ + if (((frwl->fw_flg & IP_FW_F_IIFNAME) + && !*frwl->fw_in_if.fu_via_if.name) + || ((frwl->fw_flg & IP_FW_F_OIFNAME) + && !*frwl->fw_out_if.fu_via_if.name)) { + dprintf(("%s empty interface name\n", err_prefix)); + return (NULL); + } + /* Sanity check interface matching */ + if ((frwl->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) { + ; /* allow "via" backwards compatibility */ + } else if ((frwl->fw_flg & IP_FW_F_IN) + && (frwl->fw_flg & IP_FW_F_OIFACE)) { + dprintf(("%s outgoing interface check on incoming\n", + err_prefix)); + return (NULL); + } + /* Sanity check port ranges */ + if ((frwl->fw_flg & IP_FW_F_SRNG) && IP_FW_GETNSRCP(frwl) < 2) { dprintf(("%s src range set but n_src_p=%d\n", - err_prefix, frwl->fw_nsp)); + err_prefix, IP_FW_GETNSRCP(frwl))); return (NULL); } - if ((frwl->fw_flg & IP_FW_F_DRNG) && frwl->fw_ndp < 2) { + if ((frwl->fw_flg & IP_FW_F_DRNG) && IP_FW_GETNDSTP(frwl) < 2) { dprintf(("%s dst range set but n_dst_p=%d\n", - err_prefix, frwl->fw_ndp)); + err_prefix, IP_FW_GETNDSTP(frwl))); return (NULL); } - if (frwl->fw_nsp + frwl->fw_ndp > IP_FW_MAX_PORTS) { + if (IP_FW_GETNSRCP(frwl) + IP_FW_GETNDSTP(frwl) > IP_FW_MAX_PORTS) { dprintf(("%s too many ports (%d+%d)\n", - err_prefix, frwl->fw_nsp, frwl->fw_ndp)); + err_prefix, IP_FW_GETNSRCP(frwl), IP_FW_GETNDSTP(frwl))); return (NULL); } /* - * ICMP protocol doesn't use port range + * Protocols other than TCP/UDP don't use port range */ if ((frwl->fw_prot != IPPROTO_TCP) && (frwl->fw_prot != IPPROTO_UDP) && - (frwl->fw_nsp || frwl->fw_ndp)) { + (IP_FW_GETNSRCP(frwl) || IP_FW_GETNDSTP(frwl))) { dprintf(("%s port(s) specified for non TCP/UDP rule\n", err_prefix)); return(NULL); @@ -672,12 +827,34 @@ check_ipfw_struct(struct mbuf *m) return(NULL); } - /* Diverting to port zero is illegal */ - if ((frwl->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DIVERT - && frwl->fw_divert_port == 0) { - dprintf(("ip_fw_ctl: can't divert to port 0\n")); - return (NULL); + /* Check command specific stuff */ + switch (frwl->fw_flg & IP_FW_F_COMMAND) + { + case IP_FW_F_REJECT: + if (frwl->fw_reject_code >= 0x100 + && !(frwl->fw_prot == IPPROTO_TCP + && frwl->fw_reject_code == IP_FW_REJECT_RST)) { + dprintf(("%s unknown reject code\n", err_prefix)); + return(NULL); + } + break; + case IP_FW_F_DIVERT: /* Diverting to port zero is invalid */ + case IP_FW_F_TEE: + if (frwl->fw_divert_port == 0) { + dprintf(("%s can't divert to port 0\n", err_prefix)); + return (NULL); + } + break; + case IP_FW_F_DENY: + case IP_FW_F_ACCEPT: + case IP_FW_F_COUNT: + case IP_FW_F_SKIPTO: + break; + default: + dprintf(("%s invalid command\n", err_prefix)); + return(NULL); } + return frwl; } @@ -728,21 +905,31 @@ ip_fw_ctl(int stage, struct mbuf **mm) return (EINVAL); } - if (stage == IP_FW_ADD || stage == IP_FW_DEL) { - struct ip_fw *frwl = check_ipfw_struct(m); + if (stage == IP_FW_ADD) { + struct ip_fw *frwl = check_ipfw_mbuf(m); - if (!frwl) { - if (m) (void)m_free(m); - return (EINVAL); - } - - if (stage == IP_FW_ADD) - error = add_entry(&ip_fw_chain, frwl); + if (!frwl) + error = EINVAL; else - error = del_entry(&ip_fw_chain, frwl); + error = add_entry(&ip_fw_chain, frwl); if (m) (void)m_free(m); return error; } + if (stage == IP_FW_DEL) { + if (m->m_len != sizeof(struct ip_fw)) { + dprintf(("%s len=%d, want %d\n", err_prefix, m->m_len, + sizeof(struct ip_fw))); + error = EINVAL; + } else if (mtod(m, struct ip_fw *)->fw_number == (u_short)-1) { + dprintf(("%s can't delete rule 65535\n", err_prefix)); + error = EINVAL; + } else + error = del_entry(&ip_fw_chain, + mtod(m, struct ip_fw *)->fw_number); + if (m) (void)m_free(m); + return error; + } + dprintf(("%s unknown request %d\n", err_prefix, stage)); if (m) (void)m_free(m); return (EINVAL); @@ -760,8 +947,10 @@ ip_fw_init(void) bzero(&deny, sizeof deny); deny.fw_prot = IPPROTO_IP; deny.fw_number = (u_short)-1; - deny.fw_flg = IP_FW_F_IN | IP_FW_F_OUT; - add_entry(&ip_fw_chain, &deny); + deny.fw_flg |= IP_FW_F_DENY; + deny.fw_flg |= IP_FW_F_IN | IP_FW_F_OUT; + if (check_ipfw_struct(&deny) == NULL || add_entry(&ip_fw_chain, &deny)) + panic(__FUNCTION__); printf("IP packet filtering initialized, " #ifdef IPDIVERT diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h index b3ae6ad4bd61..96d735766da5 100644 --- a/sys/netinet/ip_fw.h +++ b/sys/netinet/ip_fw.h @@ -11,51 +11,84 @@ * * This software is provided ``AS IS'' without any warranties of any kind. * - * $Id$ + * $Id: ip_fw.h,v 1.1 1997/05/09 17:46:45 archie Exp $ */ +#ifndef _IP_FW_H +#define _IP_FW_H + +/* + * This union structure identifies an interface, either explicitly + * by name or implicitly by IP address. The flags IP_FW_F_IIFNAME + * and IP_FW_F_OIFNAME say how to interpret this structure. An + * interface unit number of -1 matches any unit number, while an + * IP address of 0.0.0.0 indicates matches any interface. + * + * The receive and transmit interfaces are only compared against the + * the packet if the corresponding bit (IP_FW_F_IIFACE or IP_FW_F_OIFACE) + * is set. Note some packets lack a receive or transmit interface + * (in which case the missing "interface" never matches). + */ + +union ip_fw_if { + struct in_addr fu_via_ip; /* Specified by IP address */ + struct { /* Specified by interface name */ +#define FW_IFNLEN 6 /* To keep structure on 2^x boundary */ + char name[FW_IFNLEN]; + short unit; /* -1 means match any unit */ + } fu_via_if; +}; + /* * Format of an IP firewall descriptor * * fw_src, fw_dst, fw_smsk, fw_dmsk are always stored in network byte order. * fw_flg and fw_n*p are stored in host byte order (of course). * Port numbers are stored in HOST byte order. + * Warning: setsockopt() will fail if sizeof(struct ip_fw) > MLEN (108) */ -#ifndef _IP_FW_H -#define _IP_FW_H struct ip_fw { u_long fw_pcnt,fw_bcnt; /* Packet and byte counters */ struct in_addr fw_src, fw_dst; /* Source and destination IP addr */ struct in_addr fw_smsk, fw_dmsk; /* Mask for src and dest IP addr */ - union { - struct in_addr fu_via_ip; /* Specified by IP address */ - struct { /* Specified by interface name */ -#define FW_IFNLEN 6 /* To keep structure on 2^x boundary */ - char fu_via_name[FW_IFNLEN]; - short fu_via_unit; - } fu_via_if; - } fu_via_un; -#define fw_via_ip fu_via_un.fu_via_ip -#define fw_via_name fu_via_un.fu_via_if.fu_via_name -#define fw_via_unit fu_via_un.fu_via_if.fu_via_unit - u_short fw_number; + u_short fw_number; /* Rule number */ u_short fw_flg; /* Flags word */ - u_short fw_nsp, fw_ndp; /* N'of src ports and # of dst ports */ - /* in ports array (dst ports follow */ - /* src ports; max of 10 ports in all; */ - /* count of 0 means match all ports) */ -#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */ - u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ +#define IP_FW_MAX_PORTS 10 /* A reasonable maximum */ + u_short fw_pts[IP_FW_MAX_PORTS]; /* Array of port numbers to match */ u_char fw_ipopt,fw_ipnopt; /* IP options set/unset */ u_char fw_tcpf,fw_tcpnf; /* TCP flags set/unset */ #define IP_FW_ICMPTYPES_DIM (256 / (sizeof(unsigned) * 8)) unsigned fw_icmptypes[IP_FW_ICMPTYPES_DIM]; /* ICMP types bitmap */ - long timestamp; /* timestamp (tv_sec) of last match */ - u_short fw_divert_port; /* Divert port (options IPDIVERT) */ + long timestamp; /* timestamp (tv_sec) of last match */ + union ip_fw_if fw_in_if, fw_out_if; /* Incoming and outgoing interfaces */ + union { + u_short fu_divert_port; /* Divert/tee port (options IPDIVERT) */ + u_short fu_skipto_rule; /* SKIPTO command rule number */ + u_short fu_reject_code; /* REJECT response code */ + } fw_un; u_char fw_prot; /* IP protocol */ + u_char fw_nports; /* N'of src ports and # of dst ports */ + /* in ports array (dst ports follow */ + /* src ports; max of 10 ports in all; */ + /* count of 0 means match all ports) */ }; +#define IP_FW_GETNSRCP(rule) ((rule)->fw_nports & 0x0f) +#define IP_FW_SETNSRCP(rule, n) do { \ + (rule)->fw_nports &= ~0x0f; \ + (rule)->fw_nports |= (n); \ + } while (0) +#define IP_FW_GETNDSTP(rule) ((rule)->fw_nports >> 4) +#define IP_FW_SETNDSTP(rule, n) do { \ + (rule)->fw_nports &= ~0xf0; \ + (rule)->fw_nports |= (n) << 4;\ + } while (0) + +#define fw_divert_port fw_un.fu_divert_port +#define fw_skipto_rule fw_un.fu_skipto_rule +#define fw_reject_code fw_un.fu_reject_code + struct ip_fw_chain { LIST_ENTRY(ip_fw_chain) chain; struct ip_fw *rule; @@ -64,37 +97,55 @@ struct ip_fw_chain { /* * Values for "flags" field . */ -#define IP_FW_F_INVSRC 0x0001 /* Invert sense of src check */ -#define IP_FW_F_INVDST 0x0002 /* Invert sense of dst check */ -#define IP_FW_F_IN 0x0004 /* Inbound */ -#define IP_FW_F_OUT 0x0008 /* Outbound */ +#define IP_FW_F_IN 0x0001 /* Check inbound packets */ +#define IP_FW_F_OUT 0x0002 /* Check outbound packets */ +#define IP_FW_F_IIFACE 0x0004 /* Apply inbound interface test */ +#define IP_FW_F_OIFACE 0x0008 /* Apply outbound interface test */ -#define IP_FW_F_COMMAND 0x0030 /* Mask for type of chain entry: */ -#define IP_FW_F_ACCEPT 0x0010 /* This is an accept rule */ -#define IP_FW_F_COUNT 0x0020 /* This is a count rule */ -#define IP_FW_F_DIVERT 0x0030 /* This is a divert rule */ -#define IP_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IP_FW_F_COMMAND 0x0070 /* Mask for type of chain entry: */ +#define IP_FW_F_DENY 0x0000 /* This is a deny rule */ +#define IP_FW_F_REJECT 0x0010 /* Deny and send a response packet */ +#define IP_FW_F_ACCEPT 0x0020 /* This is an accept rule */ +#define IP_FW_F_COUNT 0x0030 /* This is a count rule */ +#define IP_FW_F_DIVERT 0x0040 /* This is a divert rule */ +#define IP_FW_F_TEE 0x0050 /* This is a tee rule */ +#define IP_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ -#define IP_FW_F_PRN 0x0040 /* Print if this rule matches */ -#define IP_FW_F_ICMPRPL 0x0080 /* Send back icmp unreachable packet */ +#define IP_FW_F_PRN 0x0080 /* Print if this rule matches */ -#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min * - * and max range (stored in host byte * - * order). */ +#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min * + * and max range (stored in host byte * + * order). */ -#define IP_FW_F_DRNG 0x0200 /* The first two dst ports are a min * - * and max range (stored in host byte * - * order). */ +#define IP_FW_F_DRNG 0x0200 /* The first two dst ports are a min * + * and max range (stored in host byte * + * order). */ -#define IP_FW_F_IFNAME 0x0400 /* Use interface name/unit (not IP) */ +#define IP_FW_F_IIFNAME 0x0400 /* In interface by name/unit (not IP) */ +#define IP_FW_F_OIFNAME 0x0800 /* Out interface by name/unit (not IP) */ -#define IP_FW_F_FRAG 0x0800 /* Fragment */ +#define IP_FW_F_INVSRC 0x1000 /* Invert sense of src check */ +#define IP_FW_F_INVDST 0x2000 /* Invert sense of dst check */ -#define IP_FW_F_ICMPBIT 0x1000 /* ICMP type bitmap is valid */ +#define IP_FW_F_FRAG 0x4000 /* Fragment */ -#define IP_FW_F_IFUWILD 0x2000 /* Match all interface units */ +#define IP_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */ -#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */ +#define IP_FW_F_MASK 0xFFFF /* All possible flag bits mask */ + +/* + * For backwards compatibility with rules specifying "via iface" but + * not restricted to only "in" or "out" packets, we define this combination + * of bits to represent this configuration. + */ + +#define IF_FW_F_VIAHACK (IP_FW_F_IN|IP_FW_F_OUT|IP_FW_F_IIFACE|IP_FW_F_OIFACE) + +/* + * Definitions for REJECT response codes. + * Values less than 256 correspond to ICMP unreachable codes. + */ +#define IP_FW_REJECT_RST 0x0100 /* TCP packets: send RST */ /* * Definitions for IP option names. diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 538d1c859503..903adaf19f34 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)ip_input.c 8.2 (Berkeley) 1/4/94 - * $Id: ip_input.c,v 1.61 1997/04/03 10:47:10 darrenr Exp $ + * $Id: ip_input.c,v 1.62 1997/05/11 18:05:37 tegge Exp $ * $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $ */ @@ -334,25 +334,24 @@ ip_input(struct mbuf *m) #endif #ifdef COMPAT_IPFW if (ip_fw_chk_ptr) { - int action; +#ifdef IPDIVERT + u_short port; -#ifdef IPDIVERT - action = (*ip_fw_chk_ptr)(&ip, hlen, - m->m_pkthdr.rcvif, ip_divert_ignore, &m); + port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, ip_divert_ignore, &m); ip_divert_ignore = 0; -#else - action = (*ip_fw_chk_ptr)(&ip, hlen, m->m_pkthdr.rcvif, 0, &m); -#endif - if (action == -1) - return; - if (action != 0) { -#ifdef IPDIVERT - frag_divert_port = action; + if (port) { /* Divert packet */ + frag_divert_port = port; goto ours; -#else - goto bad; /* ipfw said divert but we can't */ -#endif } +#else + /* If ipfw says divert, we have to just drop packet */ + if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, 0, &m)) { + m_freem(m); + m = NULL; + } +#endif + if (!m) + return; } if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN)) @@ -521,9 +520,10 @@ ip_input(struct mbuf *m) #ifdef IPDIVERT /* - * Divert packets here to the divert protocol if required + * Divert reassembled packets to the divert protocol if required */ if (frag_divert_port) { + ipstat.ips_delivered++; ip_divert_port = frag_divert_port; frag_divert_port = 0; (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen); diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index defc06a4d430..4401f6bf8a94 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)ip_output.c 8.3 (Berkeley) 1/21/94 - * $Id: ip_output.c,v 1.55 1997/04/27 20:01:07 wollman Exp $ + * $Id: ip_output.c,v 1.56 1997/05/06 21:22:04 fenner Exp $ */ #define _IP_VHL @@ -357,7 +357,7 @@ ip_output(m0, opt, ro, flags, imo) /* * IpHack's section. * - Xlate: translate packet's addr/port (NAT). - * - Firewall: deny/allow + * - Firewall: deny/allow/etc. * - Wrap: fake packet's addr/port * - Encapsulate: put it in another IP and send out. */ @@ -372,27 +372,24 @@ ip_output(m0, opt, ro, flags, imo) * Check with the firewall... */ if (ip_fw_chk_ptr) { - int action; - #ifdef IPDIVERT - action = (*ip_fw_chk_ptr)(&ip, - hlen, ifp, (~0 << 16) | ip_divert_ignore, &m); + ip_divert_port = (*ip_fw_chk_ptr)(&ip, + hlen, ifp, ip_divert_ignore, &m); ip_divert_ignore = 0; -#else - action = (*ip_fw_chk_ptr)(&ip, hlen, ifp, (~0 << 16), &m); -#endif - if (action == -1) { - error = EACCES; /* XXX is this appropriate? */ - goto done; - } else if (action != 0) { -#ifdef IPDIVERT - ip_divert_port = action; /* divert to port */ + if (ip_divert_port) { /* Divert packet */ (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0); goto done; + } #else - m_freem(m); /* ipfw says divert, but we can't */ + /* If ipfw says divert, we have to just drop packet */ + if ((*ip_fw_chk_ptr)(&ip, hlen, ifp, 0, &m)) { + m_freem(m); goto done; + } #endif + if (!m) { + error = EACCES; + goto done; } } #endif /* COMPAT_IPFW */