summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/Makefile2
-rw-r--r--sbin/ipfw/ipfw.8183
-rw-r--r--sbin/ipfw/ipfw.c622
-rw-r--r--sys/netinet/ip_divert.c23
-rw-r--r--sys/netinet/ip_fw.c581
-rw-r--r--sys/netinet/ip_fw.h141
-rw-r--r--sys/netinet/ip_input.c32
-rw-r--r--sys/netinet/ip_output.c29
8 files changed, 1085 insertions, 528 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);
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
index dab1864..0c941a3 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 <sys/param.h>
@@ -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 71fa62c..0886a43 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 <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
-#include <netinet/tcp.h>
-#include <netinet/udp.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/ip_fw.h>
+#include <netinet/tcp.h>
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#include <netinet/udp.h>
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 inline int
+iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname)
+{
+ /* 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(char *txt, int rule, struct ip *ip, int counter, struct ifnet *rif)
+ipfw_report(struct ip_fw *f, struct ip *ip,
+ struct ifnet *rif, struct ifnet *oif)
{
- 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)
- return;
- if (fw_verbose_limit != 0 && counter > fw_verbose_limit)
+ 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;
- 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 <port>'' rules, where <port> 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
- * <port> Divert the packet to divert <port>, 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)
- continue;
-
- /* If a i/f name was specified, and we don't know */
- if ((f->fw_flg & IP_FW_F_IFNAME) && !rif)
+ ^ (ip->ip_dst.s_addr & f->fw_dmsk.s_addr) != f->fw_dst.s_addr)
continue;
- /* If a i/f name was specified, check it */
- if ((f->fw_flg & IP_FW_F_IFNAME) && f->fw_via_name[0]) {
+ /* Interface check */
+ if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
+ struct ifnet *const iface = oif ? oif : rif;
- /* 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;
- }
-
- /* 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)
+ /* 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;
}
- /*
- * 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;
- }
- m_freem(*m);
- 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;
+ }
+ }
+
+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 (!frwl) {
- if (m) (void)m_free(m);
- return (EINVAL);
- }
+ if (stage == IP_FW_ADD) {
+ struct ip_fw *frwl = check_ipfw_mbuf(m);
- 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 b3ae6ad..96d7357 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 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 0x0080 /* Print if this rule matches */
-#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_SRNG 0x0100 /* The first two src ports are a min *
+ * and max range (stored in host byte *
+ * order). */
-#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_DRNG 0x0200 /* The first two dst 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_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_DRNG 0x0200 /* The first two dst ports are a min *
- * and max range (stored in host byte *
- * order). */
+#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_IFNAME 0x0400 /* Use interface name/unit (not IP) */
+#define IP_FW_F_FRAG 0x4000 /* Fragment */
-#define IP_FW_F_FRAG 0x0800 /* Fragment */
+#define IP_FW_F_ICMPBIT 0x8000 /* ICMP type bitmap is valid */
-#define IP_FW_F_ICMPBIT 0x1000 /* ICMP type bitmap is valid */
+#define IP_FW_F_MASK 0xFFFF /* All possible flag bits mask */
-#define IP_FW_F_IFUWILD 0x2000 /* Match all interface units */
+/*
+ * 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)
-#define IP_FW_F_MASK 0x3FFF /* All possible flag bits mask */
+/*
+ * 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 538d1c8..903adaf 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 @@ tooshort:
#endif
#ifdef COMPAT_IPFW
if (ip_fw_chk_ptr) {
- int action;
-
#ifdef IPDIVERT
- action = (*ip_fw_chk_ptr)(&ip, hlen,
- m->m_pkthdr.rcvif, ip_divert_ignore, &m);
+ u_short port;
+
+ 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
+ /* 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 @@ found:
#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 defc06a..4401f6b 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 @@ sendit:
/*
* IpHack's section.
* - Xlate: translate packet's addr/port (NAT).
- * - Firewall: deny/allow
+ * - Firewall: deny/allow/etc.
* - Wrap: fake packet's addr/port <unimpl.>
* - Encapsulate: put it in another IP and send out. <unimp.>
*/
@@ -372,27 +372,24 @@ sendit:
* 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 */
OpenPOWER on IntegriCloud