aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/protocols3
-rw-r--r--sbin/ipfw/ipfw.854
-rw-r--r--sbin/ipfw/ipfw.c58
-rw-r--r--share/man/man4/divert.4144
-rw-r--r--sys/conf/NOTES5
-rw-r--r--sys/conf/files1
-rw-r--r--sys/i386/conf/LINT5
-rw-r--r--sys/i386/conf/NOTES5
-rw-r--r--sys/netinet/in.h3
-rw-r--r--sys/netinet/in_proto.c12
-rw-r--r--sys/netinet/ip_divert.c365
-rw-r--r--sys/netinet/ip_fw.c126
-rw-r--r--sys/netinet/ip_fw.h15
-rw-r--r--sys/netinet/ip_input.c71
-rw-r--r--sys/netinet/ip_output.c33
-rw-r--r--sys/netinet/ip_var.h16
-rw-r--r--usr.bin/netstat/main.c6
17 files changed, 829 insertions, 93 deletions
diff --git a/etc/protocols b/etc/protocols
index 33a1666a8974..c451b671678f 100644
--- a/etc/protocols
+++ b/etc/protocols
@@ -1,7 +1,7 @@
#
# Internet (IP) protocols
#
-# $Id: protocols,v 1.2 1993/11/07 00:31:22 wollman Exp $
+# $Id: protocols,v 1.3 1995/08/29 19:29:35 wollman Exp $
# from: @(#)protocols 5.1 (Berkeley) 4/17/89
#
# Updated for FreeBSD based on RFC 1340, Assigned Numbers (July 1992).
@@ -27,3 +27,4 @@ vmtp 81 VMTP # Versatile Message Transport
ospf 89 OSPFIGP # Open Shortest Path First IGP
ipip 94 IPIP # Yet Another IP encapsulation
encap 98 ENCAP # Yet Another IP encapsulation
+divert 254 DIVERT # Divert pseudo-protocol
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index a217131ef76b..a43011397a5c 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -52,24 +52,38 @@ command.
.Pp
The ipfw code works by going through the rule-list for each packet,
until a match is found.
-All rules have two counters associated with them, a packet count and
+All rules have two associated counters, a packet count and
a byte count.
These counters are updated when a packet matches the rule.
.Pp
-The rules are ordered by a ``line-number'' that is used to order and
-delete rules.
-If a rule is added without a number, it is put at the end, just before
-the terminal ``policy-rule'', and numbered 100 higher than the previous
-rule.
+The rules are ordered by a ``line-number'' from 1 to 65534 that is used
+to order and delete rules. Rules are tried in increasing order, and the
+first rule that matches a packet applies.
+Multiple rules may share the same number and apply in
+the order in which they were added.
+.Pp
+If a rule is added without a number, it numbered 100 higher
+than the previous rule. If the highest defined rule number is
+greater than 65434, new rules are appended to the last rule.
+.Pp
+The delete operation deletes the first rule with number
+.Ar number ,
+if any.
+.Pp
+The list command prints out the current rule set.
+.Pp
+The zero operation zeroes the counters associated with rule number
+.Ar number .
+.Pp
+The flush operation removes all rules.
.Pp
One rule is always present:
.Bd -literal -offset center
65535 deny all from any to any
.Ed
-this rule is the default policy, ie. don't allow anything at all.
-Your job in setting up rules is to modify this policy to match your
-needs.
+This rule is the default policy, i.e., don't allow anything at all.
+Your job in setting up rules is to modify this policy to match your needs.
.Pp
The following options are available:
.Bl -tag -width flag
@@ -93,12 +107,16 @@ Same as allow.
Same as allow.
.It Nm count
Update counters for all packets that match rule.
-The search continues with next rule.
+The search continues with the next rule.
.It Nm deny
Discard packets that match this rule.
The search terminates.
.It Nm reject
-Discard packets that match this rule, try to send ICMP notice.
+Discard packets that match this rule, and try to send an ICMP notice.
+The search terminates.
+.It Nm divert port
+Divert packets that match this rule to the divert socket bound to port
+.Ar port .
The search terminates.
.El
.Pp
@@ -145,7 +163,7 @@ Only this exact ip number match the rule.
An ipnumber with a mask width of the form 1.2.3.4/24.
In this case all ip numbers from 1.2.3.0 to 1.2.3.255 will match.
.It Ar ipno:mask
-An ipnumber with a mask width of the form 1.2.3.4:255.255.240.0
+An ipnumber with a mask width of the form 1.2.3.4:255.255.240.0.
In this case all ip numbers from 1.2.0.0 to 1.2.15.255 will match.
.El
.Pp
@@ -270,6 +288,12 @@ ipfw flush
.Ed
in similar surroundings is also a bad idea.
+.Sh PACKET DIVERSION
+A divert socket bound to the specified port will receive all packets diverted
+to that port; see
+.Xr divert 4 .
+If no socket is bound to the destination port, or if the kernel
+wasn't compiled with divert socket support, diverted packets are dropped.
.Sh EXAMPLES
This command adds an entry which denies all tcp packets from
.Em hacker.evil.org
@@ -292,12 +316,16 @@ or in short form
.Pp
.Dl ipfw -a l
.Pp
+This rule diverts all incoming packets from 192.168.2.0/24 to divert port 5000:
+.Pp
+.Dl ipfw divert 5000 all from 192.168.2.0/24 to any in
.Sh SEE ALSO
.Xr gethostbyname 3 ,
.Xr getservbyport 3 ,
.Xr ip 4 ,
.Xr ipfirewall 4 ,
.Xr ipaccounting 4 ,
+.Xr divert 4 ,
.Xr reboot 8 ,
.Xr syslogd 8
.Sh BUGS
@@ -323,3 +351,5 @@ The FreeBSD version is written completely by:
.Pp
This has all been extensively rearranged by Poul-Henning Kamp and
Alex Nash.
+.Pp
+Packet diversion added by Archie Cobbs <archie@whistle.com>.
diff --git a/sbin/ipfw/ipfw.c b/sbin/ipfw/ipfw.c
index 63dc4c3b1fd5..6a49587fa1b5 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.27 1996/06/23 20:47:51 alex Exp $
+ * $Id: ipfw.c,v 1.28 1996/06/29 01:28:19 alex Exp $
*
*/
@@ -130,14 +130,27 @@ show_ipfw(chain)
printf(" ");
}
- if (chain->fw_flg & IP_FW_F_ACCEPT)
- printf("allow");
- else if (chain->fw_flg & IP_FW_F_ICMPRPL)
- printf("reject");
- else if (chain->fw_flg & IP_FW_F_COUNT)
- printf("count");
- else
- printf("deny");
+ switch (chain->fw_flg & IP_FW_F_COMMAND)
+ {
+ case IP_FW_F_ACCEPT:
+ printf("allow");
+ break;
+ case IP_FW_F_DIVERT:
+ printf("divert %u", chain->fw_divert_port);
+ 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");
+ break;
+ default:
+ errx(1, "impossible");
+ }
+
if (chain->fw_flg & IP_FW_F_PRN)
printf(" log");
@@ -330,7 +343,6 @@ list(ac, av)
i = getsockopt(s, IPPROTO_IP, IP_FW_GET, rules, &l);
if (i < 0)
err(2,"getsockopt(IP_FW_GET)");
- printf("FireWall chain entries: %d %d\n",l,i);
for (r=rules; l >= sizeof rules[0]; r++, l-=sizeof rules[0])
show_ipfw(r);
}
@@ -350,7 +362,7 @@ show_usage(str)
"\t\tlist [number]\n"
"\t\tzero [number]\n"
"\trule:\taction proto src dst extras...\n"
-"\t\taction: {allow|deny|reject|count} [log]\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"
@@ -611,20 +623,26 @@ add(ac,av)
}
/* Action */
- if (ac && !strncmp(*av,"accept",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
- } else if (ac && !strncmp(*av,"allow",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
- } else if (ac && !strncmp(*av,"pass",strlen(*av))) {
+ if (ac && (!strncmp(*av,"accept",strlen(*av))
+ || !strncmp(*av,"pass",strlen(*av))
+ || !strncmp(*av,"allow",strlen(*av))
+ || !strncmp(*av,"permit",strlen(*av)))) {
rule.fw_flg |= IP_FW_F_ACCEPT; av++; ac--;
} else if (ac && !strncmp(*av,"count",strlen(*av))) {
rule.fw_flg |= IP_FW_F_COUNT; av++; ac--;
- } else if (ac && !strncmp(*av,"deny",strlen(*av))) {
- av++; ac--;
+ } else if (ac && !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)))) {
+ rule.fw_flg |= IP_FW_F_DENY; av++; ac--;
} else if (ac && !strncmp(*av,"reject",strlen(*av))) {
- rule.fw_flg |= IP_FW_F_ICMPRPL; av++; ac--;
+ rule.fw_flg |= IP_FW_F_DENY|IP_FW_F_ICMPRPL; av++; ac--;
} else {
- show_usage("missing action\n");
+ show_usage("missing/unrecognized action\n");
}
/* [log] */
diff --git a/share/man/man4/divert.4 b/share/man/man4/divert.4
new file mode 100644
index 000000000000..651cb570cce5
--- /dev/null
+++ b/share/man/man4/divert.4
@@ -0,0 +1,144 @@
+.Dd June 18, 1996
+.Dt DIVERT 4
+.Os FreeBSD
+.Sh NAME
+.Nm divert
+.Nd kernel packet diversion mechanism
+.Sh SYNOPSIS
+.Fd #include <sys/socket.h>
+.Fd #include <netinet/in.h>
+.Ft int
+.Fn socket PF_INET SOCK_RAW IPPROTO_DIVERT
+.Sh DESCRIPTION
+.Pp
+Divert sockets are similar to raw IP sockets, except that they
+can be bound to a specific
+.Nm
+port via the
+.Xr bind 2
+system call. The IP address in the bind is ignored; only the port
+number is significant.
+A divert socket bound to a divert port will receive all packets diverted
+to that port by some (here unspecified) kernel mechanism(s).
+Packets may also be written to a divert port, in which case they
+re-enter kernel IP packet processing.
+.Pp
+Divert sockets are normally used in conjunction with
+FreeBSD's packet filtering implementation and the
+.Xr ipfw 8
+program. By reading from and writing to a divert socket, matching packets
+can be passed through an arbitrary ``filter'' as they travel through
+the host machine, special routing tricks can be done, etc.
+.Sh READING PACKETS
+Packets are diverted either as they are ``incoming'' or ``outgoing.''
+Incoming packets are diverted after reception on an IP interface,
+whereas outgoing packets are diverted before next hop forwarding.
+.Pp
+Diverted packets may be read unaltered via
+.Xr read 2 ,
+.Xr recv 2 ,
+or
+.Xr recvfrom 2 .
+In the latter case, the address returned will have its port set to
+the divert port and the IP address set to the (first) address of
+the interface on which the packet was recieved (if the packet
+was incoming) or
+.Dv INADDR_ANY
+(if the packet was outgoing).
+.Sh WRITING PACKETS
+Writing to a divert socket is similar to writing to a raw IP socket;
+the packet is injected ``as is'' into the normal kernel IP packet
+processing and minimal error checking is done.
+Packets are written as either incoming or outgoing:
+if
+.Xr write 2
+or
+.Xr send 2
+is used to deliver the packet, or if
+.Xr sendto 2
+is used with a destination IP address of
+.Dv INADDR_ANY ,
+then the packet is treated as if it were outgoing, i.e., destined
+for a non-local address. Otherwise, the packet is assumed to be
+incoming and full packet routing is done.
+.Pp
+In the latter case, the
+IP address specified must match the address of some local interface.
+This is to indicate on which interface the packet ``arrived.''
+.Pp
+Normally, packets read as incoming should be written as incoming;
+similarly for outgoing packets. When reading and then writing back
+packets, passing the same socket address supplied by
+.Xr recvfrom 2
+unmodified to
+.Xr sendto 2
+simplifies things.
+.Sh LOOP AVOIDANCE
+To avoid having a packet sent from a divert socket rediverted back
+to the same socket, use the
+.Xr sendto 2
+system call supplying any non-zero destination port number.
+This indicates to
+.Xr ipfw 8
+and other diverting mechanisms to not divert the packet back
+to the same socket it was written from.
+.Pp
+Since
+.Xr ipfw
+checks incoming as well as outgoing packets,
+a packet written as incoming may get checked twice.
+Loop avoidance will be enabled for both checks.
+.Sh DETAILS
+To enable divert sockets, your kernel must be compiled with the option
+.Dv IPDIVERT .
+.Pp
+If a packet is diverted but no socket is bound to the
+port, or if
+.Dv IPDIVERT
+is not enabled in the kernel, the packet is dropped.
+.Pp
+Incoming packet fragments which get diverted are fully reassembled
+before delivery; the diversion of any one fragment causes the entire
+packet to get diverted.
+If different fragments divert to different ports,
+then which port ultimately gets chosen is unpredictable.
+.Pp
+Packets are recieved and sent unchanged, with two exceptions:
+read as incoming will have their IP header checksum zeroed,
+and packets written as outgoing have their IP header checksums overwritten
+with the correct value.
+Packets written as incoming and having incorrect checksums will be dropped.
+Otherwise, all header fields are unchanged (and therefore in network order).
+.Pp
+Binding to port numbers less than 1024 requires super-user access.
+.Sh ERRORS
+Writing to a divert socket can return these errors, along with
+the usual errors possible when writing raw packets:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The packet had an invalid header, or the IP options in the packet
+and the socket options set were incompatible.
+.It Bq Er EADDRNOTAVAIL
+The destination address contained an IP address not equal to
+.Dv INADDR_ANY
+that was not associated with any interface.
+.El
+.Sh SEE ALSO
+.Xr ipfw 8 ,
+.Xr socket 2 ,
+.Xr bind 2 ,
+.Xr sendto 2 .
+.Xr recvfrom 2 ,
+.Sh BUGS
+This is an attempt to provide a clean way for user mode processes
+to implement various IP tricks like address translation, but it
+could be cleaner, and it's too dependent on
+.Xr ipfw 8 .
+.Pp
+It's questionable whether incoming fragments should be reassembled
+before being diverted. For example, if only some fragments of a
+packet destined for another machine don't get routed through the
+local machine, the packet is lost. This should probably be
+a settable socket option in any case.
+.Sh AUTHOR
+Archie Cobbs <archie@whistle.com>, Whistle Communications Corp.
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index d4884f6e71b7..586ac17b33d6 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.272 1996/06/26 19:42:06 gpalmer Exp $
+# $Id: LINT,v 1.273 1996/07/05 18:48:59 jhay Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -233,6 +233,8 @@ pseudo-device tun 1 #Tunnel driver(user process ppp)
# logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT
# limits the number of times a matching entry can be logged.
#
+# IPDIVERT enables the divert IP sockets, used by ``ipfw divert''
+#
# TCPDEBUG is undocumented.
#
options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs
@@ -241,6 +243,7 @@ options IPFIREWALL #firewall
options IPFIREWALL_VERBOSE #print information about
# dropped packets
options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity
+options IPDIVERT #divert sockets
options TCPDEBUG
diff --git a/sys/conf/files b/sys/conf/files
index 1a4752e08e49..fd3d22bf233e 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -198,6 +198,7 @@ netinet/ip_icmp.c optional inet
netinet/ip_input.c optional inet
netinet/ip_mroute.c optional inet
netinet/ip_output.c optional inet
+netinet/ip_divert.c optional ipdivert
netinet/raw_ip.c optional inet
netinet/ip_fw.c optional ipfirewall
netinet/tcp_debug.c optional tcpdebug
diff --git a/sys/i386/conf/LINT b/sys/i386/conf/LINT
index d4884f6e71b7..586ac17b33d6 100644
--- a/sys/i386/conf/LINT
+++ b/sys/i386/conf/LINT
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.272 1996/06/26 19:42:06 gpalmer Exp $
+# $Id: LINT,v 1.273 1996/07/05 18:48:59 jhay Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -233,6 +233,8 @@ pseudo-device tun 1 #Tunnel driver(user process ppp)
# logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT
# limits the number of times a matching entry can be logged.
#
+# IPDIVERT enables the divert IP sockets, used by ``ipfw divert''
+#
# TCPDEBUG is undocumented.
#
options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs
@@ -241,6 +243,7 @@ options IPFIREWALL #firewall
options IPFIREWALL_VERBOSE #print information about
# dropped packets
options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity
+options IPDIVERT #divert sockets
options TCPDEBUG
diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES
index d4884f6e71b7..586ac17b33d6 100644
--- a/sys/i386/conf/NOTES
+++ b/sys/i386/conf/NOTES
@@ -2,7 +2,7 @@
# LINT -- config file for checking all the sources, tries to pull in
# as much of the source tree as it can.
#
-# $Id: LINT,v 1.272 1996/06/26 19:42:06 gpalmer Exp $
+# $Id: LINT,v 1.273 1996/07/05 18:48:59 jhay Exp $
#
# NB: You probably don't want to try running a kernel built from this
# file. Instead, you should start from GENERIC, and add options from
@@ -233,6 +233,8 @@ pseudo-device tun 1 #Tunnel driver(user process ppp)
# logged packets to the system logger. IPFIREWALL_VERBOSE_LIMIT
# limits the number of times a matching entry can be logged.
#
+# IPDIVERT enables the divert IP sockets, used by ``ipfw divert''
+#
# TCPDEBUG is undocumented.
#
options "TCP_COMPAT_42" #emulate 4.2BSD TCP bugs
@@ -241,6 +243,7 @@ options IPFIREWALL #firewall
options IPFIREWALL_VERBOSE #print information about
# dropped packets
options "IPFIREWALL_VERBOSE_LIMIT=100" #limit verbosity
+options IPDIVERT #divert sockets
options TCPDEBUG
diff --git a/sys/netinet/in.h b/sys/netinet/in.h
index f19aaeb2d988..437489f1541a 100644
--- a/sys/netinet/in.h
+++ b/sys/netinet/in.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)in.h 8.3 (Berkeley) 1/3/94
- * $Id: in.h,v 1.16 1996/03/14 16:59:18 fenner Exp $
+ * $Id: in.h,v 1.17 1996/04/03 13:52:11 phk Exp $
*/
#ifndef _NETINET_IN_H_
@@ -60,6 +60,7 @@
#define IPPROTO_EON 80 /* ISO cnlp */
#define IPPROTO_ENCAP 98 /* encapsulation header */
+#define IPPROTO_DIVERT 254 /* divert pseudo-protocol */
#define IPPROTO_RAW 255 /* raw IP packet */
#define IPPROTO_MAX 256
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index ab9591bd6e8d..fef71acc53ba 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)in_proto.c 8.2 (Berkeley) 2/9/95
- * $Id: in_proto.c,v 1.30 1996/05/08 04:34:03 gpalmer Exp $
+ * $Id: in_proto.c,v 1.31 1996/06/20 17:52:32 fenner Exp $
*/
#include <sys/param.h>
@@ -132,6 +132,13 @@ struct protosw inetsw[] = {
rip_usrreq,
0, 0, 0, 0,
},
+#ifdef IPDIVERT
+{ SOCK_RAW, &inetdomain, IPPROTO_DIVERT, PR_ATOMIC|PR_ADDR,
+ div_input, 0, 0, ip_ctloutput,
+ div_usrreq,
+ div_init, 0, 0, 0,
+},
+#endif
#ifdef TPIP
{ SOCK_SEQPACKET,&inetdomain, IPPROTO_TP, PR_CONNREQUIRED|PR_WANTRCVD,
tpip_input, 0, tpip_ctlinput, tp_ctloutput,
@@ -187,4 +194,7 @@ SYSCTL_NODE(_net_inet, IPPROTO_ICMP, icmp, CTLFLAG_RW, 0, "ICMP");
SYSCTL_NODE(_net_inet, IPPROTO_UDP, udp, CTLFLAG_RW, 0, "UDP");
SYSCTL_NODE(_net_inet, IPPROTO_TCP, tcp, CTLFLAG_RW, 0, "TCP");
SYSCTL_NODE(_net_inet, IPPROTO_IGMP, igmp, CTLFLAG_RW, 0, "IGMP");
+#ifdef IPDIVERT
+SYSCTL_NODE(_net_inet, IPPROTO_DIVERT, div, CTLFLAG_RW, 0, "DIVERT");
+#endif
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
new file mode 100644
index 000000000000..7720f8484268
--- /dev/null
+++ b/sys/netinet/ip_divert.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ip_divert.c,v 1.2 1996/06/14 00:28:38 archie Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/socketvar.h>
+#include <sys/errno.h>
+#include <sys/systm.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
+
+/*
+ * Divert sockets
+ */
+
+/*
+ * Allocate enough space to hold a full IP packet
+ */
+#define DIVSNDQ (65536 + 100)
+#define DIVRCVQ (65536 + 100)
+
+/* Global variables */
+
+/*
+ * ip_input() and ip_output() set this secret value before calling us to
+ * let us know which divert port to divert a packet to; this is done so
+ * we can use the existing prototype for struct protosw's pr_input().
+ * This is stored in host order.
+ */
+u_short ip_divert_port;
+
+/*
+ * We set this value to a non-zero port number when we want the call to
+ * ip_fw_chk() in ip_input() or ip_output() to ignore ``divert <port>''
+ * chain entries. This is stored in host order.
+ */
+u_short ip_divert_ignore;
+
+/* Internal variables */
+
+static struct inpcbhead divcb;
+static struct inpcbinfo divcbinfo;
+
+static u_long div_sendspace = DIVSNDQ; /* XXX sysctl ? */
+static u_long div_recvspace = DIVRCVQ; /* XXX sysctl ? */
+
+/* Optimization: have this preinitialized */
+static struct sockaddr_in divsrc = { sizeof(divsrc), AF_INET };
+
+/* Internal functions */
+
+static int div_output(struct socket *so,
+ struct mbuf *m, struct mbuf *addr, struct mbuf *control);
+
+/*
+ * Initialize divert connection block queue.
+ */
+void
+div_init(void)
+{
+ LIST_INIT(&divcb);
+ divcbinfo.listhead = &divcb;
+ /*
+ * XXX We don't use the hash list for divert IP, but it's easier
+ * to allocate a one entry hash list than it is to check all
+ * over the place for hashbase == NULL.
+ */
+ divcbinfo.hashbase = phashinit(1, M_PCB, &divcbinfo.hashsize);
+}
+
+/*
+ * Setup generic address and protocol structures
+ * for div_input routine, then pass them along with
+ * mbuf chain. ip->ip_len is assumed to have had
+ * the header length (hlen) subtracted out already.
+ * We tell whether the packet was incoming or outgoing
+ * by seeing if hlen == 0, which is a hack.
+ */
+void
+div_input(struct mbuf *m, int hlen)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ register struct inpcb *inp;
+ register struct socket *sa;
+
+ /* Sanity check */
+ if (ip_divert_port == 0)
+ panic("div_input");
+
+ /* Record divert port */
+ divsrc.sin_port = htons(ip_divert_port);
+
+ /* Restore packet header fields */
+ ip->ip_len += hlen;
+ HTONS(ip->ip_len);
+ HTONS(ip->ip_off);
+
+ /* Record receive interface address, if any */
+ divsrc.sin_addr.s_addr = 0;
+ if (hlen) {
+ struct ifaddr *ifa;
+
+ /* More fields affected by ip_input() */
+ HTONS(ip->ip_id);
+
+ /* Find IP address for recieve interface */
+ for (ifa = m->m_pkthdr.rcvif->if_addrlist;
+ ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ divsrc.sin_addr =
+ ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr;
+ break;
+ }
+ }
+
+ /* Put packet on socket queue, if any */
+ sa = NULL;
+ for (inp = divcb.lh_first; inp != NULL; inp = inp->inp_list.le_next) {
+ if (inp->inp_lport == htons(ip_divert_port))
+ sa = inp->inp_socket;
+ }
+ if (sa) {
+ if (sbappendaddr(&sa->so_rcv, (struct sockaddr *)&divsrc,
+ m, (struct mbuf *)0) == 0)
+ m_freem(m);
+ else
+ sorwakeup(sa);
+ } else {
+ m_freem(m);
+ ipstat.ips_noproto++;
+ ipstat.ips_delivered--;
+ }
+}
+
+/*
+ * Deliver packet back into the IP processing machinery.
+ *
+ * If no address specified, or address is 0.0.0.0, send to ip_output();
+ * otherwise, send to ip_input() and mark as having been received on
+ * the interface with that address.
+ *
+ * If no address specified, or dest port is 0, allow packet to divert
+ * back to this socket; otherwise, don't.
+ */
+static int
+div_output(so, m, addr, control)
+ struct socket *so;
+ register struct mbuf *m;
+ struct mbuf *addr, *control;
+{
+ register struct inpcb *const inp = sotoinpcb(so);
+ register struct ip *const ip = mtod(m, struct ip *);
+ struct sockaddr_in *sin = NULL;
+ int error = 0;
+
+ if (control)
+ m_freem(control); /* XXX */
+ if (addr)
+ sin = mtod(addr, struct sockaddr_in *);
+
+ /* Loopback avoidance option */
+ if (sin && sin->sin_port)
+ ip_divert_ignore = ntohs(inp->inp_lport);
+
+ /* Reinject packet into the system as incoming or outgoing */
+ if (!sin || sin->sin_addr.s_addr == 0) {
+ /* Don't allow both user specified and setsockopt options,
+ and don't allow packet length sizes that will crash */
+ if (((ip->ip_hl != (sizeof (*ip) >> 2)) && inp->inp_options) ||
+ ((u_short)ntohs(ip->ip_len) > m->m_pkthdr.len)) {
+ error = EINVAL;
+ goto cantsend;
+ }
+
+ /* Convert fields to host order for ip_output() */
+ NTOHS(ip->ip_len);
+ NTOHS(ip->ip_off);
+
+ /* Send packet to output processing */
+ ipstat.ips_rawout++; /* XXX */
+ error = ip_output(m, inp->inp_options, &inp->inp_route,
+ (so->so_options & SO_DONTROUTE) |
+ IP_ALLOWBROADCAST | IP_RAWOUTPUT, inp->inp_moptions);
+ } else {
+ struct ifaddr *ifa;
+
+ /* Find receive interface with the given IP address */
+ sin->sin_port = 0;
+ if ((ifa = ifa_ifwithaddr((struct sockaddr *) sin)) == 0) {
+ error = EADDRNOTAVAIL;
+ goto cantsend;
+ }
+ m->m_pkthdr.rcvif = ifa->ifa_ifp;
+
+ /* Send packet to input processing */
+ ip_input(m);
+ }
+
+ /* Reset for next time (and other packets) */
+ ip_divert_ignore = 0;
+ return error;
+
+cantsend:
+ ip_divert_ignore = 0;
+ m_freem(m);
+ return error;
+}
+
+/*ARGSUSED*/
+int
+div_usrreq(so, req, m, nam, control)
+ register struct socket *so;
+ int req;
+ struct mbuf *m, *nam, *control;
+{
+ register int error = 0;
+ register struct inpcb *inp = sotoinpcb(so);
+ int s = 0;
+
+ if (inp == NULL && req != PRU_ATTACH) {
+ error = EINVAL;
+ goto release;
+ }
+ switch (req) {
+
+ case PRU_ATTACH:
+ if (inp)
+ panic("div_attach");
+ if ((so->so_state & SS_PRIV) == 0) {
+ error = EACCES;
+ break;
+ }
+ if ((error = soreserve(so, div_sendspace, div_recvspace)) ||
+ (error = in_pcballoc(so, &divcbinfo)))
+ break;
+ inp = (struct inpcb *)so->so_pcb;
+ inp->inp_ip.ip_p = (int)nam; /* XXX */
+ inp->inp_flags |= INP_HDRINCL;
+ /* The socket is always "connected" because
+ we always know "where" to send the packet */
+ so->so_state |= SS_ISCONNECTED;
+ break;
+
+ case PRU_DISCONNECT:
+ if ((so->so_state & SS_ISCONNECTED) == 0) {
+ error = ENOTCONN;
+ break;
+ }
+ /* FALLTHROUGH */
+ case PRU_ABORT:
+ soisdisconnected(so);
+ /* FALLTHROUGH */
+ case PRU_DETACH:
+ if (inp == 0)
+ panic("div_detach");
+ in_pcbdetach(inp);
+ break;
+
+ case PRU_BIND:
+ s = splnet();
+ error = in_pcbbind(inp, nam);
+ splx(s);
+ break;
+
+ /*
+ * Mark the connection as being incapable of further input.
+ */
+ case PRU_SHUTDOWN:
+ socantsendmore(so);
+ break;
+
+ case PRU_SEND:
+ /* Packet must have a header (but that's about it) */
+ if (m->m_len < sizeof (struct ip) ||
+ (m = m_pullup(m, sizeof (struct ip))) == 0) {
+ ipstat.ips_toosmall++;
+ error = EINVAL;
+ break;
+ }
+
+ /* Send packet */
+ error = div_output(so, m, nam, control);
+ m = NULL;
+ break;
+
+ case PRU_SOCKADDR:
+ in_setsockaddr(inp, nam);
+ break;
+
+ case PRU_SENSE:
+ /*
+ * stat: don't bother with a blocksize.
+ */
+ return (0);
+
+ /*
+ * Not supported.
+ */
+ case PRU_CONNECT:
+ case PRU_CONNECT2:
+ case PRU_CONTROL:
+ case PRU_RCVOOB:
+ case PRU_RCVD:
+ case PRU_LISTEN:
+ case PRU_ACCEPT:
+ case PRU_SENDOOB:
+ case PRU_PEERADDR:
+ error = EOPNOTSUPP;
+ break;
+
+ default:
+ panic("div_usrreq");
+ }
+release:
+ if (m)
+ m_freem(m);
+ return (error);
+}
diff --git a/sys/netinet/ip_fw.c b/sys/netinet/ip_fw.c
index c8b34ff08b38..4308250bd718 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.43 1996/06/29 03:33:20 alex Exp $
+ * $Id: ip_fw.c,v 1.44 1996/07/09 20:49:38 nate Exp $
*/
/*
@@ -32,11 +32,13 @@
#include <sys/time.h>
#include <sys/sysctl.h>
#include <net/if.h>
+#include <net/route.h>
#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>
@@ -88,7 +90,7 @@ static ip_fw_ctl_t *old_ctl_ptr;
#endif
static int ip_fw_chk __P((struct ip **pip, int hlen, struct ifnet *rif,
- int dir, struct mbuf **m));
+ int dirport, struct mbuf **m));
static int ip_fw_ctl __P((int stage, struct mbuf **mm));
@@ -257,11 +259,24 @@ ipfw_report(char *txt, int rule, struct ip *ip, int counter)
}
/*
- * Returns 1 if it should be accepted, 0 otherwise.
+ * We overload the "dirport" parameter:
+ *
+ * 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.
+ *
+ * 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.
*/
static int
-ip_fw_chk(struct ip **pip, int hlen, struct ifnet *rif, int dir, struct mbuf **m)
+ip_fw_chk(struct ip **pip, int hlen,
+ struct ifnet *rif, int dirport, struct mbuf **m)
{
struct ip_fw_chain *chain;
register struct ip_fw *f = NULL;
@@ -284,7 +299,7 @@ ip_fw_chk(struct ip **pip, int hlen, struct ifnet *rif, int dir, struct mbuf **m
++frag_counter;
ipfw_report("Refuse", -1, ip, frag_counter);
m_freem(*m);
- return 0;
+ return -1;
}
src = ip->ip_src;
@@ -331,11 +346,11 @@ ip_fw_chk(struct ip **pip, int hlen, struct ifnet *rif, int dir, struct mbuf **m
f = chain->rule;
/* Check direction inbound */
- if (!dir && !(f->fw_flg & IP_FW_F_IN))
+ if (dirport >= 0 && !(f->fw_flg & IP_FW_F_IN))
continue;
/* Check direction outbound */
- if (dir && !(f->fw_flg & IP_FW_F_OUT))
+ if (dirport < 0 && !(f->fw_flg & IP_FW_F_OUT))
continue;
/* Fragments */
@@ -437,41 +452,65 @@ got_match:
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_ACCEPT)
- ipfw_report("Allow", f->fw_number, ip, f->fw_pcnt);
- else if (f->fw_flg & IP_FW_F_COUNT)
- ipfw_report("Count", f->fw_number, ip, f->fw_pcnt);
- else
- ipfw_report("Deny", f->fw_number, ip, f->fw_pcnt);
+ if ((f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_ACCEPT) {
+ ipfw_report("Accept",
+ f->fw_number, ip, f->fw_pcnt);
+ } 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);
+ } else if ((f->fw_flg & IP_FW_F_COMMAND)
+ == IP_FW_F_COUNT) {
+ ipfw_report("Count",
+ f->fw_number, ip, f->fw_pcnt);
+ } else {
+ ipfw_report("Deny",
+ f->fw_number, ip, f->fw_pcnt);
+ }
}
- if (f->fw_flg & IP_FW_F_ACCEPT)
- return 1;
- if (f->fw_flg & IP_FW_F_COUNT)
- continue;
- break;
+ /* 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) {
+ 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 */
}
+#ifdef DIAGNOSTIC
+ if (!chain) /* rule 65535 should always be there */
+ panic("ip_fw: chain");
+ if (!f)
+ panic("ip_fw: entry");
+#endif
+
/*
- * Don't icmp outgoing packets at all
+ * At this point, we're going to drop the packet.
+ * Send an ICMP only if all of the following are true:
+ *
+ * - The packet is an incoming packet
+ * - The packet matched a deny rule
+ * - The packet is not an ICMP packet
+ * - The rule has the special ICMP reply flag set
*/
- if (f != NULL && !dir) {
- /*
- * Do not ICMP reply to icmp packets....:) or to packets
- * rejected by entry without the special ICMP reply flag.
- */
- if ((f_prt != IP_FW_F_ICMP) && (f->fw_flg & IP_FW_F_ICMPRPL)) {
- if (f_prt == IP_FW_F_ALL)
- icmp_error(*m, ICMP_UNREACH,
- ICMP_UNREACH_HOST, 0L, 0);
- else
- icmp_error(*m, ICMP_UNREACH,
- ICMP_UNREACH_PORT, 0L, 0);
- return 0;
- }
+ if (dirport >= 0
+ && (f->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_DENY
+ && (f_prt != IP_FW_F_ICMP)
+ && (f->fw_flg & IP_FW_F_ICMPRPL)) {
+ if (f_prt == IP_FW_F_ALL)
+ icmp_error(*m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0L, 0);
+ else
+ icmp_error(*m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0L, 0);
+ return -1;
}
m_freem(*m);
- return 0;
+ return -1;
}
static int
@@ -659,6 +698,13 @@ check_ipfw_struct(struct mbuf *m)
dprintf(("ip_fw_ctl: rule never matches\n"));
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);
+ }
return frwl;
}
@@ -742,15 +788,21 @@ ip_fw_init(void)
deny.fw_flg = IP_FW_F_ALL;
deny.fw_number = (u_short)-1;
add_entry(&ip_fw_chain, &deny);
-
- printf("IP firewall initialized, ");
+
+ printf("IP packet filtering initialized, "
+#ifdef IPDIVERT
+ "divert enabled, ");
+#else
+ "divert disabled, ");
+#endif
#ifndef IPFIREWALL_VERBOSE
printf("logging disabled\n");
#else
if (fw_verbose_limit == 0)
printf("unlimited logging\n");
else
- printf("logging limited to %d packets/entry\n", fw_verbose_limit);
+ printf("logging limited to %d packets/entry\n",
+ fw_verbose_limit);
#endif
}
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index a8b11a811b67..b63bc74aa628 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -11,7 +11,7 @@
*
* This software is provided ``AS IS'' without any warranties of any kind.
*
- * $Id: ip_fw.h,v 1.19 1996/06/02 00:14:50 gpalmer Exp $
+ * $Id: ip_fw.h,v 1.20 1996/06/09 23:46:21 alex Exp $
*/
/*
@@ -29,8 +29,8 @@ struct ip_fw {
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;
- struct {
+ 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;
@@ -52,6 +52,7 @@ struct ip_fw {
#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) */
};
struct ip_fw_chain {
@@ -72,11 +73,15 @@ struct ip_fw_chain {
#define IP_FW_F_IN 0x0004 /* Inbound */
#define IP_FW_F_OUT 0x0008 /* Outbound */
+#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 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_PRN 0x0040 /* Print if this rule matches */
#define IP_FW_F_ICMPRPL 0x0080 /* Send back icmp unreachable packet */
-
+
#define IP_FW_F_SRNG 0x0100 /* The first two src ports are a min *
* and max range (stored in host byte *
* order). */
diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c
index 5db23ec79546..2a17342017f3 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.43 1996/06/08 08:18:57 bde Exp $
+ * $Id: ip_input.c,v 1.44 1996/06/12 19:34:33 gpalmer Exp $
*/
#include "opt_ipfw.h"
@@ -129,6 +129,15 @@ static struct ip_srcrt {
struct in_addr route[MAX_IPOPTLEN/sizeof(struct in_addr)];
} ip_srcrt;
+#ifdef IPDIVERT
+/*
+ * Shared variable between ip_input() and ip_reass() to communicate
+ * about which packets, once assembled from fragments, get diverted,
+ * and to which port.
+ */
+static u_short frag_divert_port;
+#endif
+
static void save_rte __P((u_char *, struct in_addr));
static void ip_deq __P((struct ipasfrag *));
static int ip_dooptions __P((struct mbuf *));
@@ -255,14 +264,31 @@ ip_input(struct mbuf *m)
* Right now when no processing on packet has done
* and it is still fresh out of network we do our black
* deals with it.
- * - Firewall: deny/allow
+ * - Firewall: deny/allow/divert
* - Wrap: fake packet's addr/port <unimpl.>
* - Encapsulate: put it in another IP and send out. <unimp.>
*/
- if (ip_fw_chk_ptr &&
- !(*ip_fw_chk_ptr)(&ip, hlen, m->m_pkthdr.rcvif, 0, &m))
- return;
+ if (ip_fw_chk_ptr) {
+ int action;
+
+#ifdef IPDIVERT
+ action = (*ip_fw_chk_ptr)(&ip, hlen,
+ m->m_pkthdr.rcvif, ip_divert_ignore, &m);
+#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;
+ goto ours;
+#else
+ goto bad; /* ipfw said divert but we can't */
+#endif
+ }
+ }
/*
* Process options and, if not destined for us,
@@ -386,6 +412,9 @@ ours:
if (m->m_flags & M_EXT) { /* XXX */
if ((m = m_pullup(m, sizeof (struct ip))) == 0) {
ipstat.ips_toosmall++;
+#ifdef IPDIVERT
+ frag_divert_port = 0;
+#endif
return;
}
ip = mtod(m, struct ip *);
@@ -432,6 +461,18 @@ found:
} else
ip->ip_len -= hlen;
+#ifdef IPDIVERT
+ /*
+ * Divert packets here to the divert protocol if required
+ */
+ if (frag_divert_port) {
+ ip_divert_port = frag_divert_port;
+ frag_divert_port = 0;
+ (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, hlen);
+ return;
+ }
+#endif
+
/*
* Switch out to protocol's input routine.
*/
@@ -501,6 +542,9 @@ ip_reass(ip, fp)
fp->ipq_next = fp->ipq_prev = (struct ipasfrag *)fp;
fp->ipq_src = ((struct ip *)ip)->ip_src;
fp->ipq_dst = ((struct ip *)ip)->ip_dst;
+#ifdef IPDIVERT
+ fp->ipq_divert = 0;
+#endif
q = (struct ipasfrag *)fp;
goto insert;
}
@@ -546,6 +590,16 @@ ip_reass(ip, fp)
}
insert:
+
+#ifdef IPDIVERT
+ /*
+ * Any fragment diverting causes the whole packet to divert
+ */
+ if (frag_divert_port != 0)
+ fp->ipq_divert = frag_divert_port;
+ frag_divert_port = 0;
+#endif
+
/*
* Stick new segment in its place;
* check for complete reassembly.
@@ -575,6 +629,13 @@ insert:
m_cat(m, t);
}
+#ifdef IPDIVERT
+ /*
+ * Record divert port for packet, if any
+ */
+ frag_divert_port = fp->ipq_divert;
+#endif
+
/*
* Create header for new ip packet by
* modifying header of first packet;
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c
index 7777114bbbf7..bb30a9fc8589 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.39 1996/05/22 17:23:08 wollman Exp $
+ * $Id: ip_output.c,v 1.40 1996/06/08 08:18:59 bde Exp $
*/
#define _IP_VHL
@@ -80,6 +80,8 @@ static int ip_pcbopts __P((struct mbuf **, struct mbuf *));
static int ip_setmoptions
__P((int, struct ip_moptions **, struct mbuf *));
+extern struct protosw inetsw[];
+
/*
* IP output. The packet in mbuf chain m contains a skeletal IP
* header (with len, off, ttl, proto, tos, src, dst).
@@ -329,15 +331,34 @@ ip_output(m0, opt, ro, flags, imo)
}
sendit:
+#ifdef COMPAT_IPFW
/*
* Check with the firewall...
*/
-#ifdef COMPAT_IPFW
- if (ip_fw_chk_ptr && !(*ip_fw_chk_ptr)(&ip, hlen, ifp, 1, &m)) {
- error = EACCES;
- goto done;
- }
+ if (ip_fw_chk_ptr) {
+ int action;
+
+#ifdef IPDIVERT
+ action = (*ip_fw_chk_ptr)(&ip,
+ hlen, ifp, (~0 << 16) | ip_divert_ignore, &m);
+#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 */
+ (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0);
+ goto done;
+#else
+ m_freem(m); /* ipfw says divert, but we can't */
+ goto done;
+#endif
+ }
+ }
+#endif /* COMPAT_IPFW */
/*
* If small enough for interface, can just send directly.
diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h
index 01d42d146792..91b3bd83ddde 100644
--- a/sys/netinet/ip_var.h
+++ b/sys/netinet/ip_var.h
@@ -31,7 +31,7 @@
* SUCH DAMAGE.
*
* @(#)ip_var.h 8.2 (Berkeley) 1/9/95
- * $Id: ip_var.h,v 1.19 1996/01/30 22:58:27 mpp Exp $
+ * $Id: ip_var.h,v 1.20 1996/03/26 19:16:45 fenner Exp $
*/
#ifndef _NETINET_IP_VAR_H_
@@ -63,6 +63,9 @@ struct ipq {
struct ipasfrag *ipq_next,*ipq_prev;
/* to ip headers of fragments */
struct in_addr ipq_src,ipq_dst;
+#ifdef IPDIVERT
+ u_short ipq_divert; /* divert protocol port */
+#endif
};
/*
@@ -189,6 +192,15 @@ int ip_rsvp_vif_init __P((struct socket *, struct mbuf *));
int ip_rsvp_vif_done __P((struct socket *, struct mbuf *));
void ip_rsvp_force_done __P((struct socket *));
-#endif
+#ifdef IPDIVERT
+void div_init __P((void));
+void div_input __P((struct mbuf *, int));
+int div_usrreq __P((struct socket *,
+ int, struct mbuf *, struct mbuf *, struct mbuf *));
+extern u_short ip_divert_port;
+extern u_short ip_divert_ignore;
+#endif /* IPDIVERT */
+
+#endif /* KERNEL */
#endif
diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c
index 0a6294cc53f5..4bf871977420 100644
--- a/usr.bin/netstat/main.c
+++ b/usr.bin/netstat/main.c
@@ -137,6 +137,10 @@ struct nlist nl[] = {
{ "_ddpstat"},
#define N_DDPCB 36
{ "_ddpcb"},
+#define N_DIVPCB 37
+ { "_divcb"},
+#define N_DIVSTAT 38
+ { "_divstat"},
{ "" },
};
@@ -152,6 +156,8 @@ struct protox {
tcp_stats, "tcp" },
{ N_UDB, N_UDPSTAT, 1, protopr,
udp_stats, "udp" },
+ { N_DIVPCB, N_DIVSTAT, 1, protopr,
+ NULL, "divert" }, /* no stat structure yet */
{ -1, N_IPSTAT, 1, 0,
ip_stats, "ip" },
{ -1, N_ICMPSTAT, 1, 0,