diff options
| author | Julian Elischer <julian@FreeBSD.org> | 1996-07-10 19:44:30 +0000 |
|---|---|---|
| committer | Julian Elischer <julian@FreeBSD.org> | 1996-07-10 19:44:30 +0000 |
| commit | 93e0e11657c61975fdd909f535d89bc6f0eaeceb (patch) | |
| tree | 5affe14a214c46b4bd58b410a49350e34137ed18 /sys | |
| parent | 265c33c02735b31902caed1f0bc5fe3bdfc644d3 (diff) | |
Notes
Diffstat (limited to 'sys')
| -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 |
12 files changed, 597 insertions, 60 deletions
diff --git a/sys/conf/NOTES b/sys/conf/NOTES index d4884f6e71b7c..586ac17b33d69 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 1a4752e08e496..fd3d22bf233e2 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 d4884f6e71b7c..586ac17b33d69 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 d4884f6e71b7c..586ac17b33d69 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 f19aaeb2d988b..437489f1541a3 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 ab9591bd6e8de..fef71acc53bac 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 0000000000000..7720f8484268c --- /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 c8b34ff08b38b..4308250bd7184 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 a8b11a811b678..b63bc74aa6283 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 5db23ec79546e..2a17342017f30 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 7777114bbbf7a..bb30a9fc8589f 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 01d42d146792c..91b3bd83dddee 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 |
