diff options
-rw-r--r-- | etc/protocols | 3 | ||||
-rw-r--r-- | sbin/ipfw/ipfw.8 | 54 | ||||
-rw-r--r-- | sbin/ipfw/ipfw.c | 58 | ||||
-rw-r--r-- | share/man/man4/divert.4 | 144 | ||||
-rw-r--r-- | sys/conf/NOTES | 5 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/i386/conf/LINT | 5 | ||||
-rw-r--r-- | sys/i386/conf/NOTES | 5 | ||||
-rw-r--r-- | sys/netinet/in.h | 3 | ||||
-rw-r--r-- | sys/netinet/in_proto.c | 12 | ||||
-rw-r--r-- | sys/netinet/ip_divert.c | 365 | ||||
-rw-r--r-- | sys/netinet/ip_fw.c | 126 | ||||
-rw-r--r-- | sys/netinet/ip_fw.h | 15 | ||||
-rw-r--r-- | sys/netinet/ip_input.c | 71 | ||||
-rw-r--r-- | sys/netinet/ip_output.c | 33 | ||||
-rw-r--r-- | sys/netinet/ip_var.h | 16 | ||||
-rw-r--r-- | usr.bin/netstat/main.c | 6 |
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, |