summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjulian <julian@FreeBSD.org>1996-07-10 19:44:30 +0000
committerjulian <julian@FreeBSD.org>1996-07-10 19:44:30 +0000
commit9277e63302140b5062d96a9394cdec2b83b2e70a (patch)
tree5affe14a214c46b4bd58b410a49350e34137ed18
parent366bddd7f5f46d8b7d51ff94c668b40ccecbbc23 (diff)
downloadFreeBSD-src-9277e63302140b5062d96a9394cdec2b83b2e70a.zip
FreeBSD-src-9277e63302140b5062d96a9394cdec2b83b2e70a.tar.gz
Adding changes to ipfw and the kernel to support ip packet diversion..
This stuff should not be too destructive if the IPDIVERT is not compiled in.. be aware that this changes the size of the ip_fw struct so ipfw needs to be recompiled to use it.. more changes coming to clean this up.
-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 33a1666..c451b67 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 a217131..a430113 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 63dc4c3..6a49587 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 0000000..651cb57
--- /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 d4884f6..586ac17 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 1a4752e..fd3d22b 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 d4884f6..586ac17 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 d4884f6..586ac17 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 f19aaeb..437489f 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 ab9591b..fef71ac 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 0000000..7720f84
--- /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 c8b34ff..4308250 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 a8b11a8..b63bc74 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 5db23ec..2a17342 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 7777114..bb30a9f 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 01d42d1..91b3bd8 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 0a6294c..4bf8719 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,
OpenPOWER on IntegriCloud