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>