diff options
author | Julian Elischer <julian@FreeBSD.org> | 1998-07-06 03:20:19 +0000 |
---|---|---|
committer | Julian Elischer <julian@FreeBSD.org> | 1998-07-06 03:20:19 +0000 |
commit | f9e354df42e8d939ec8114b373b7c416c11a20bd (patch) | |
tree | 2ee1b18e79b6b0b95a22ae91807e501dcae3ef57 /sys/netinet/ip_output.c | |
parent | 6312920c2029288900017977fea63cbfd4197f87 (diff) | |
download | src-f9e354df42e8d939ec8114b373b7c416c11a20bd.tar.gz src-f9e354df42e8d939ec8114b373b7c416c11a20bd.zip |
Support for IPFW based transparent forwarding.
Any packet that can be matched by a ipfw rule can be redirected
transparently to another port or machine. Redirection to another port
mostly makes sense with tcp, where a session can be set up
between a proxy and an unsuspecting client. Redirection to another machine
requires that the other machine also be expecting to receive the forwarded
packets, as their headers will not have been modified.
/sbin/ipfw must be recompiled!!!
Reviewed by: Peter Wemm <peter@freebsd.org>
Submitted by: Chrisy Luke <chrisy@flix.net>
Notes
Notes:
svn path=/head/; revision=37409
Diffstat (limited to 'sys/netinet/ip_output.c')
-rw-r--r-- | sys/netinet/ip_output.c | 139 |
1 files changed, 132 insertions, 7 deletions
diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index c142887cd9bf..d85f36ba5c7f 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.74 1998/06/15 00:35:47 julian Exp $ + * $Id: ip_output.c,v 1.75 1998/06/21 14:53:32 bde Exp $ */ #define _IP_VHL @@ -71,6 +71,13 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options"); #undef COMPAT_IPFW #endif +#ifdef IPFIREWALL_FORWARD_DEBUG +#define print_ip(a) printf("%ld.%ld.%ld.%ld",(ntohl(a.s_addr)>>24)&0xFF,\ + (ntohl(a.s_addr)>>16)&0xFF,\ + (ntohl(a.s_addr)>>8)&0xFF,\ + (ntohl(a.s_addr))&0xFF); +#endif + u_short ip_id; static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); @@ -114,6 +121,9 @@ ip_output(m0, opt, ro, flags, imo) struct sockaddr_in *dst; struct in_ifaddr *ia; int isbroadcast; +#ifdef IPFIREWALL_FORWARD + int fwd_rewrite_src = 0; +#endif #ifdef DIAGNOSTIC if ((m->m_flags & M_PKTHDR) == 0) @@ -304,9 +314,18 @@ ip_output(m0, opt, ro, flags, imo) * If source address not specified yet, use address * of outgoing interface. */ - if (ip->ip_src.s_addr == INADDR_ANY) + if (ip->ip_src.s_addr == INADDR_ANY) { ip->ip_src = IA_SIN(ia)->sin_addr; -#endif +#ifdef IPFIREWALL_FORWARD + /* Keep note that we did this - if the firewall changes + * the next-hop, our interface may change, changing the + * default source IP. It's a shame so much effort happens + * twice. Oh well. + */ + fwd_rewrite_src++; +#endif /* IPFIREWALL_FORWARD */ + } +#endif /* notdef */ /* * Verify that we have any chance at all of being able to queue * the packet or packet fragments @@ -369,28 +388,134 @@ sendit: * Check with the firewall... */ if (ip_fw_chk_ptr) { +#ifdef IPFIREWALL_FORWARD + struct sockaddr_in *old; + old = dst; +#endif #ifdef IPDIVERT ip_divert_port = (*ip_fw_chk_ptr)(&ip, - hlen, ifp, &ip_divert_cookie, &m); + hlen, ifp, &ip_divert_cookie, &m, &dst); if (ip_divert_port) { /* Divert packet */ (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0); goto done; } -#else +#else /* !IPDIVERT */ u_int16_t dummy = 0; /* If ipfw says divert, we have to just drop packet */ - if ((*ip_fw_chk_ptr)(&ip, hlen, ifp, &dummy, &m)) { + if ((*ip_fw_chk_ptr)(&ip, hlen, ifp, &dummy, &m, &dst)) { m_freem(m); goto done; } -#endif +#endif /* !IPDIVERT */ if (!m) { error = EACCES; goto done; } +#ifdef IPFIREWALL_FORWARD + /* Here we check dst to make sure it's directly reachable on the + * interface we previously thought it was. + * If it isn't (which may be likely in some situations) we have + * to re-route it (ie, find a route for the next-hop and the + * associated interface) and set them here. This is nested + * forwarding which in most cases is undesirable, except where + * such control is nigh impossible. So we do it here. + * And I'm babbling. + */ + if (old != dst) { + struct in_ifaddr *ia; + + /* It's changed... */ + /* There must be a better way to do this next line... */ + static struct route sro_fwd, *ro_fwd = &sro_fwd; +#ifdef IPFIREWALL_FORWARD_DEBUG + printf("IPFIREWALL_FORWARD: New dst ip: "); + print_ip(dst->sin_addr); + printf("\n"); +#endif + /* + * We need to figure out if we have been forwarded + * to a local socket. If so then we should somehow + * "loop back" to ip_input, and get directed to the + * PCB as if we had received this packet. This is + * because it may be dificult to identify the packets + * you want to forward until they are being output + * and have selected an interface. (e.g. locally + * initiated packets) If we used the loopback inteface, + * we would not be able to control what happens + * as the packet runs through ip_input() as + * it is done through a ISR. + */ + for (ia = TAILQ_FIRST(in_ifaddrhead); ia; + ia = TAILQ_NEXT(ia, ia_link)) { + /* + * If the addr to forward to is one + * of ours, we pretend to + * be the destination for this packet. + */ + if (IA_SIN(ia)->sin_addr.s_addr == + dst->sin_addr.s_addr) + break; + } + if (ia) { + /* tell ip_input "dont filter" */ + ip_fw_fwd_addr = dst; + if (m->m_pkthdr.rcvif == NULL) + m->m_pkthdr.rcvif = ifunit("lo0"); + ip->ip_len = htons((u_short)ip->ip_len); + ip->ip_off = htons((u_short)ip->ip_off); + ip->ip_sum = 0; + if (ip->ip_vhl == IP_VHL_BORING) { + ip->ip_sum = in_cksum_hdr(ip); + } else { + ip->ip_sum = in_cksum(m, hlen); + } + ip_input(m); + goto done; + } + /* Some of the logic for this was + * nicked from above. + * + * This rewrites the cached route in a local PCB. + * Is this what we want to do? + */ + bcopy(dst, &ro_fwd->ro_dst, sizeof(*dst)); + + ro_fwd->ro_rt = 0; + rtalloc_ign(ro_fwd, RTF_PRCLONING); + + if (ro_fwd->ro_rt == 0) { + ipstat.ips_noroute++; + error = EHOSTUNREACH; + goto bad; + } + + ia = ifatoia(ro_fwd->ro_rt->rt_ifa); + ifp = ro_fwd->ro_rt->rt_ifp; + ro_fwd->ro_rt->rt_use++; + if (ro_fwd->ro_rt->rt_flags & RTF_GATEWAY) + dst = (struct sockaddr_in *)ro_fwd->ro_rt->rt_gateway; + if (ro_fwd->ro_rt->rt_flags & RTF_HOST) + isbroadcast = + (ro_fwd->ro_rt->rt_flags & RTF_BROADCAST); + else + isbroadcast = in_broadcast(dst->sin_addr, ifp); + RTFREE(ro->ro_rt); + ro->ro_rt = ro_fwd->ro_rt; + dst = (struct sockaddr_in *)&ro_fwd->ro_dst; + + /* + * If we added a default src ip earlier, + * which would have been gotten from the-then + * interface, do it again, from the new one. + */ + if (fwd_rewrite_src) + ip->ip_src = IA_SIN(ia)->sin_addr; + } +#endif /* IPFIREWALL_FORWARD */ } #endif /* COMPAT_IPFW */ + /* * If small enough for interface, can just send directly. */ |