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.
This commit is contained in:
Julian Elischer 1996-07-10 19:44:30 +00:00
parent 265c33c027
commit 93e0e11657
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=17072
17 changed files with 829 additions and 93 deletions

View File

@ -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

View File

@ -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>.

View File

@ -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] */

144
share/man/man4/divert.4 Normal file
View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

365
sys/netinet/ip_divert.c Normal file
View File

@ -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);
}

View File

@ -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
}

View File

@ -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). */

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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,