Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 16 Nov 2001 12:45:48 -0800
From:      Julian Elischer <julian@vicor-nb.com>
To:        net@freebsd.org
Cc:        Chrisy Luke <chrisy@flix.net>
Subject:   RFC: ipfirewall_forward patch #2
Message-ID:  <3BF57AFC.B2082EE8@vicor-nb.com>
References:  <3BF30699.E8CC9857@vicor-nb.com> <3BF306D2.3A50C4AF@vicor-nb.com> <20011115001610.A6212@flix.net> <20011115002741.B6212@flirble.org>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------7FD9C58AA4767C8D0B1BF5D8
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit


Here is a fixed version of this patch. last one used an unitialised
variable.

Note: the whole ipfw/fwd scheme is non re-entrant. to fix it and some
other parts
of the code will require that we gain teh capability of associating
extra state with a packet.. e.g. "this packet has been diverted"
or "being forwarded to address 1.2.3.4:99"

presently these things are stored in global variables and rely on the
fact that
only one packet is traversing the IP code at a time. 
This patch is no more or less re-entrant than that which was already
there, but it
is a weakness I noted.

------previously mentionned stuff --------------

 The following patch allows the forwarding of INCOMING packets to an 
arbitrary next hop controlled by the ipfw fwd command..

Ipfw 'fwd' at present has the following restriction:

Only packets already leaving the system can be hijacked and forwarded
to a 2nd machine. Incoming packets can only be forwarded to local
addresses/port combinations.

This patch would allow a sequence of machines to hijack
a particular conforming packet and pass it along a chain of
these machines to make it fall out somewhere else..

the particular example I have in mind:

Incoming tcp sessions being transparently load shared by 4 machines.

[internet]---[A.b.c.d]-------[10.2.3.4]
                   \---------[10.2.3.5]
                   \---------[10.2.3.6]
                   \---------[10.2.3.7]

on A.b.c.d:
ipfw add 1000 fwd 10.2.3.4 tcp from 0.0.0.0:0.0.3.0 to A.b.c.d 80 in
recv fxp0
ipfw add 1000 fwd 10.2.3.5 tcp from 0.0.1.0:0.0.3.0 to A.b.c.d 80 in
recv fxp0
ipfw add 1000 fwd 10.2.3.6 tcp from 0.0.2.0:0.0.3.0 to A.b.c.d 80 in
recv fxp0
ipfw add 1000 fwd 10.2.3.7 tcp from 0.0.3.0:0.0.3.0 to A.b.c.d 80 in
recv fxp0

and on each of the four machines:
ipfw add 1000 fwd localhost tcp from any to A.b.c.d 80 in recv fxp0

This is a 4 -way load sharing setup sharing 
http load across 4 machines (in our case proxies)

implemented in 5 lines of ipfw.

To the external world it would look as if all requests are being
satisfied 
by A.b.c.d:80.
----------End previous mail------------------------------

An actual machine testing this has the following firewall ruleset:


[NET]------[192.168.105.11#192.168.101.1]------[192.168.101.2]
                                           \---[192.168.101.3]

00100     0        0 allow ip from any to any via lo0
00200     0        0 deny ip from any to 127.0.0.0/8
00300     0        0 deny ip from 127.0.0.0/8 to any
00700 14781 20110386 allow ip from 192.168.105.11 to any out xmit fxp1
00750 14603 20084922 allow tcp from 192.168.105.11 80 to any in recv
fxp0
00990  5541   288132 fwd 192.168.101.3 tcp from 0.0.0.0:0.0.0.1 to
192.168.105.11 80 in recv fxp1
01000  5701   228040 fwd 192.168.101.2 tcp from 0.0.0.1:0.0.0.1 to
192.168.105.11 80 in recv fxp1
02000   193     8288 divert 8668 ip from any to any in recv fxp1
02000     0        0 divert 8668 ip from any to any out xmit fxp1
65000   193     8288 allow ip from any to any

This includes efficient rules for divert to natd (efficient means I
don't divert packets that could not be nat'd and natd will just leave
untouched)
It also leaves unforwarded and un-nat'd packets returning from the
proxies..
with 2wo proxies runnung flat out, load on the load-shareing machine was
about 3%
of cpu, shared between interrupt and system time. Notice that the 
natd is almost inactive.

All requests to 192.168.105.11 80 are actually satisfied on
192.168.101.2
and 192.168.101.3

the  proxies have the additional ipfw line:

01000 fwd localhost tcp from any to 192.168.105.11 80 in recv fxp1

I would certainly like some people with knowledge of the IP code to
check the patch.

Also, anyone with ideas on how to make ipfw, divert and 
ipfw-fwd to be more re-entrant, I'd like to hear from you..
--------------7FD9C58AA4767C8D0B1BF5D8
Content-Type: text/plain; charset=us-ascii;
 name="thediff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="thediff"

Index: ip_input.c
===================================================================
RCS file: /usr/home/cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.130.2.25
diff -u -r1.130.2.25 ip_input.c
--- ip_input.c	2001/08/29 21:41:37	1.130.2.25
+++ ip_input.c	2001/11/14 23:41:20
@@ -1587,21 +1587,29 @@
 	int error, type = 0, code = 0;
 	struct mbuf *mcopy;
 	n_long dest;
+	struct in_addr pkt_dst;
 	struct ifnet *destifp;
 #ifdef IPSEC
 	struct ifnet dummyifp;
 #endif
 
 	dest = 0;
+	/*
+	 * Cache the destination address of the packet; this may be
+	 * changed by use of 'ipfw fwd'.
+	 */
+	pkt_dst = ip_fw_fwd_addr == NULL ?
+	    ip->ip_dst : ip_fw_fwd_addr->sin_addr;
+
 #ifdef DIAGNOSTIC
 	if (ipprintfs)
 		printf("forward: src %lx dst %lx ttl %x\n",
-		    (u_long)ip->ip_src.s_addr, (u_long)ip->ip_dst.s_addr,
+		    (u_long)ip->ip_src.s_addr, (u_long)pkt_dst.s_addr,
 		    ip->ip_ttl);
 #endif
 
 
-	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(ip->ip_dst) == 0) {
+	if (m->m_flags & (M_BCAST|M_MCAST) || in_canforward(pkt_dst) == 0) {
 		ipstat.ips_cantforward++;
 		m_freem(m);
 		return;
@@ -1620,14 +1628,14 @@
 
 	sin = (struct sockaddr_in *)&ipforward_rt.ro_dst;
 	if ((rt = ipforward_rt.ro_rt) == 0 ||
-	    ip->ip_dst.s_addr != sin->sin_addr.s_addr) {
+	    pkt_dst.s_addr != sin->sin_addr.s_addr) {
 		if (ipforward_rt.ro_rt) {
 			RTFREE(ipforward_rt.ro_rt);
 			ipforward_rt.ro_rt = 0;
 		}
 		sin->sin_family = AF_INET;
 		sin->sin_len = sizeof(*sin);
-		sin->sin_addr = ip->ip_dst;
+		sin->sin_addr = pkt_dst;
 
 		rtalloc_ign(&ipforward_rt, RTF_PRCLONING);
 		if (ipforward_rt.ro_rt == 0) {
@@ -1674,7 +1682,7 @@
 	if (rt->rt_ifp == m->m_pkthdr.rcvif &&
 	    (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0 &&
 	    satosin(rt_key(rt))->sin_addr.s_addr != 0 &&
-	    ipsendredirects && !srcrt) {
+	    ipsendredirects && !srcrt && !ip_fw_fwd_addr) {
 #define	RTA(rt)	((struct in_ifaddr *)(rt->rt_ifa))
 		u_long src = ntohl(ip->ip_src.s_addr);
 
@@ -1683,7 +1691,7 @@
 		    if (rt->rt_flags & RTF_GATEWAY)
 			dest = satosin(rt->rt_gateway)->sin_addr.s_addr;
 		    else
-			dest = ip->ip_dst.s_addr;
+			dest = pkt_dst.s_addr;
 		    /* Router requirements says to only send host redirects */
 		    type = ICMP_REDIRECT;
 		    code = ICMP_REDIRECT_HOST;
Index: ip_output.c
===================================================================
RCS file: /usr/home/cvs/src/sys/netinet/ip_output.c,v
retrieving revision 1.99.2.16
diff -u -r1.99.2.16 ip_output.c
--- ip_output.c	2001/07/19 06:37:26	1.99.2.16
+++ ip_output.c	2001/11/15 23:35:53
@@ -135,6 +135,7 @@
 	struct sockaddr_in *dst;
 	struct in_ifaddr *ia = NULL;
 	int isbroadcast, sw_csum;
+	struct in_addr pkt_dst;
 #ifdef IPSEC
 	struct route iproute;
 	struct socket *so = NULL;
@@ -204,6 +205,9 @@
 		hlen = len;
 	}
 	ip = mtod(m, struct ip *);
+	pkt_dst = ip_fw_fwd_addr == NULL
+		? ip->ip_dst : ip_fw_fwd_addr->sin_addr;
+
 	/*
 	 * Fill in IP header.
 	 */
@@ -227,14 +231,14 @@
 	 * and is still up.  If not, free it and try again.
 	 */
 	if (ro->ro_rt && ((ro->ro_rt->rt_flags & RTF_UP) == 0 ||
-	   dst->sin_addr.s_addr != ip->ip_dst.s_addr)) {
+	   dst->sin_addr.s_addr != pkt_dst.s_addr)) {
 		RTFREE(ro->ro_rt);
 		ro->ro_rt = (struct rtentry *)0;
 	}
 	if (ro->ro_rt == 0) {
 		dst->sin_family = AF_INET;
 		dst->sin_len = sizeof(*dst);
-		dst->sin_addr = ip->ip_dst;
+		dst->sin_addr = pkt_dst;
 	}
 	/*
 	 * If routing to interface only,
@@ -279,7 +283,7 @@
 		else
 			isbroadcast = in_broadcast(dst->sin_addr, ifp);
 	}
-	if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr))) {
+	if (IN_MULTICAST(ntohl(pkt_dst.s_addr))) {
 		struct in_multi *inm;
 
 		m->m_flags |= M_MCAST;
@@ -325,7 +329,7 @@
 				}
 		}
 
-		IN_LOOKUP_MULTI(ip->ip_dst, ifp, inm);
+		IN_LOOKUP_MULTI(pkt_dst, ifp, inm);
 		if (inm != NULL &&
 		   (imo == NULL || imo->imo_multicast_loop)) {
 			/*
@@ -575,8 +579,9 @@
 
 	/*
 	 * Check with the firewall...
+	 * but not if we are already being fwd'd from a firewall.
 	 */
-	if (fw_enable && ip_fw_chk_ptr) {
+	if (fw_enable && ip_fw_chk_ptr && !ip_fw_fwd_addr) {
 		struct sockaddr_in *old = dst;
 
 		off = (*ip_fw_chk_ptr)(&ip,
@@ -767,6 +772,7 @@
                 goto done;
 	}
 
+	ip_fw_fwd_addr = NULL;
 pass:
 	m->m_pkthdr.csum_flags |= CSUM_IP;
 	sw_csum = m->m_pkthdr.csum_flags & ~ifp->if_hwassist;

--------------7FD9C58AA4767C8D0B1BF5D8--


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-net" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?3BF57AFC.B2082EE8>