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>
This commit is contained in:
julian 1998-07-06 03:20:19 +00:00
parent 28e9d7db55
commit 4483ce4f11
10 changed files with 344 additions and 33 deletions

View File

@ -195,7 +195,31 @@ Send a copy of packets matching this rule to the
.Xr divert 4 .Xr divert 4
socket bound to port socket bound to port
.Ar port . .Ar port .
The search continues with the next rule. The search continues with the next rule. This feature is not yet implemeted.
.It Ar fwd ipaddr Op ,port
Change the next-hop on matching packets to
.Ar ipaddr ,
which can be an IP address in dotted quad or a host name.
If
.Ar ipaddr
is not a directly-reachable address, the route
as found in the local routing table for that IP is used
instead.
If
.Ar ipaddr
is a local address, then on a packet entering the system from a remote
host it will be diverted to
.Ar port
on the local machine, keeping the local address of the socket set
to the original IP address the packet was destined for. This is intended
for use with transparent proxy servers. If the IP is not
a local address then the port number (if specified) is ignored and
the rule only applies to packets leaving the system. This will
also map addresses to local ports when packets are generated locally.
The search terminates if this rule matches. If the port number is not
given then the port number in the packet is used, so that a packet for
an external machine port Y would be forwarded to local port Y. The kernel
must have been compiled with optiions IPFIREWALL_FORWARD.
.It Ar skipto number .It Ar skipto number
Skip all subsequent rules numbered less than Skip all subsequent rules numbered less than
.Ar number . .Ar number .

View File

@ -16,7 +16,7 @@
* *
* NEW command line interface for IP firewall facility * NEW command line interface for IP firewall facility
* *
* $Id: ipfw.c,v 1.56 1998/04/22 06:20:20 phk Exp $ * $Id: ipfw.c,v 1.57 1998/05/15 12:38:07 danny Exp $
* *
*/ */
@ -224,6 +224,11 @@ show_ipfw(struct ip_fw *chain, int pcwidth, int bcwidth)
print_reject_code(chain->fw_reject_code); print_reject_code(chain->fw_reject_code);
} }
break; break;
case IP_FW_F_FWD:
printf("fwd %s", inet_ntoa(chain->fw_fwd_ip.sin_addr));
if(chain->fw_fwd_ip.sin_port)
printf(",%d", chain->fw_fwd_ip.sin_port);
break;
default: default:
errx(EX_OSERR, "impossible"); errx(EX_OSERR, "impossible");
} }
@ -497,7 +502,7 @@ show_usage(const char *fmt, ...)
" rule: action proto src dst extras...\n" " rule: action proto src dst extras...\n"
" action:\n" " action:\n"
" {allow|permit|accept|pass|deny|drop|reject|unreach code|\n" " {allow|permit|accept|pass|deny|drop|reject|unreach code|\n"
" reset|count|skipto num|divert port|tee port} [log]\n" " reset|count|skipto num|divert port|tee port|fwd ip} [log]\n"
" proto: {ip|tcp|udp|icmp|<number>}\n" " proto: {ip|tcp|udp|icmp|<number>}\n"
" src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" " src: from [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
" dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n" " dst: to [not] {any|ip[{/bits|:mask}]} [{port|port-port},[port],...]\n"
@ -896,6 +901,30 @@ add(ac,av)
else else
show_usage("illegal divert port"); show_usage("illegal divert port");
} }
} else if (!strncmp(*av,"fwd",strlen(*av)) ||
!strncmp(*av,"forward",strlen(*av))) {
struct in_addr dummyip;
char *pp;
rule.fw_flg |= IP_FW_F_FWD; av++; ac--;
if (!ac)
show_usage("missing forwarding IP address");
rule.fw_fwd_ip.sin_len = sizeof(struct sockaddr_in);
rule.fw_fwd_ip.sin_family = AF_INET;
rule.fw_fwd_ip.sin_port = 0;
pp = strchr(*av, ':');
if(pp == NULL)
pp = strchr(*av, ',');
if(pp != NULL)
{
*(pp++) = '\0';
rule.fw_fwd_ip.sin_port = lookup_port(pp, 1, 1);
if(rule.fw_fwd_ip.sin_port == (unsigned int)-1)
show_usage("illegal forwarding port");
}
fill_ip(&(rule.fw_fwd_ip.sin_addr), &dummyip, &ac, &av);
if (rule.fw_fwd_ip.sin_addr.s_addr == 0)
show_usage("illegal forwarding IP address");
} else if (!strncmp(*av,"skipto",strlen(*av))) { } else if (!strncmp(*av,"skipto",strlen(*av))) {
rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--; rule.fw_flg |= IP_FW_F_SKIPTO; av++; ac--;
if (!ac) if (!ac)

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)in.h 8.3 (Berkeley) 1/3/94 * @(#)in.h 8.3 (Berkeley) 1/3/94
* $Id: in.h,v 1.34 1998/06/06 19:39:08 julian Exp $ * $Id: in.h,v 1.35 1998/06/06 20:45:25 julian Exp $
*/ */
#ifndef _NETINET_IN_H_ #ifndef _NETINET_IN_H_
@ -431,7 +431,7 @@ char *inet_ntoa __P((struct in_addr)); /* in libkern */
/* Firewall hooks */ /* Firewall hooks */
struct ip; struct ip;
typedef int ip_fw_chk_t __P((struct ip**, int, struct ifnet*, u_int16_t*, struct mbuf**)); typedef int ip_fw_chk_t __P((struct ip**, int, struct ifnet*, u_int16_t*, struct mbuf**, struct sockaddr_in**));
typedef int ip_fw_ctl_t __P((int, struct mbuf**)); typedef int ip_fw_ctl_t __P((int, struct mbuf**));
extern ip_fw_chk_t *ip_fw_chk_ptr; extern ip_fw_chk_t *ip_fw_chk_ptr;
extern ip_fw_ctl_t *ip_fw_ctl_ptr; extern ip_fw_ctl_t *ip_fw_ctl_ptr;

View File

@ -12,7 +12,7 @@
* *
* This software is provided ``AS IS'' without any warranties of any kind. * This software is provided ``AS IS'' without any warranties of any kind.
* *
* $Id: ip_fw.c,v 1.90 1998/06/21 14:53:30 bde Exp $ * $Id: ip_fw.c,v 1.91 1998/07/02 05:49:08 julian Exp $
*/ */
/* /*
@ -103,7 +103,8 @@ static ip_fw_ctl_t *old_ctl_ptr;
#endif #endif
static int ip_fw_chk __P((struct ip **pip, int hlen, static int ip_fw_chk __P((struct ip **pip, int hlen,
struct ifnet *oif, u_int16_t *cookie, struct mbuf **m)); struct ifnet *oif, u_int16_t *cookie, struct mbuf **m,
struct sockaddr_in **next_hop));
static int ip_fw_ctl __P((int stage, struct mbuf **mm)); static int ip_fw_ctl __P((int stage, struct mbuf **mm));
static char err_prefix[] = "ip_fw_ctl:"; static char err_prefix[] = "ip_fw_ctl:";
@ -320,6 +321,14 @@ ipfw_report(struct ip_fw *f, struct ip *ip,
case IP_FW_F_SKIPTO: case IP_FW_F_SKIPTO:
printf("SkipTo %d", f->fw_skipto_rule); printf("SkipTo %d", f->fw_skipto_rule);
break; break;
#ifdef IPFIREWALL_FORWARD
case IP_FW_F_FWD:
printf("Forward to ");
print_ip(f->fw_fwd_ip.sin_addr);
if (f->fw_fwd_ip.sin_port)
printf(":%d", f->fw_fwd_ip.sin_port);
break;
#endif
default: default:
printf("UNKNOWN"); printf("UNKNOWN");
break; break;
@ -393,7 +402,8 @@ ipfw_report(struct ip_fw *f, struct ip *ip,
static int static int
ip_fw_chk(struct ip **pip, int hlen, ip_fw_chk(struct ip **pip, int hlen,
struct ifnet *oif, u_int16_t *cookie, struct mbuf **m) struct ifnet *oif, u_int16_t *cookie, struct mbuf **m,
struct sockaddr_in **next_hop)
{ {
struct ip_fw_chain *chain; struct ip_fw_chain *chain;
struct ip_fw *rule = NULL; struct ip_fw *rule = NULL;
@ -606,11 +616,28 @@ ip_fw_chk(struct ip **pip, int hlen,
#endif #endif
chain = LIST_NEXT(chain, chain); chain = LIST_NEXT(chain, chain);
continue; continue;
#ifdef IPFIREWALL_FORWARD
case IP_FW_F_FWD:
/* Change the next-hop address for this packet.
* Initially we'll only worry about directly
* reachable next-hop's, but ultimately
* we will work out for next-hops that aren't
* direct the route we would take for it. We
* [cs]ould leave this latter problem to
* ip_output.c. We hope to high [name the abode of
* your favourite deity] that ip_output doesn't modify
* the new value of next_hop (which is dst there)
*/
if (next_hop != NULL) /* Make sure, first... */
*next_hop = &(f->fw_fwd_ip);
return(0); /* Allow the packet */
#endif
} }
/* Deny/reject this packet using this rule */ /* Deny/reject this packet using this rule */
rule = f; rule = f;
break; break;
} }
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
@ -950,6 +977,9 @@ check_ipfw_struct(struct ip_fw *frwl)
case IP_FW_F_ACCEPT: case IP_FW_F_ACCEPT:
case IP_FW_F_COUNT: case IP_FW_F_COUNT:
case IP_FW_F_SKIPTO: case IP_FW_F_SKIPTO:
#ifdef IPFIREWALL_FORWARD
case IP_FW_F_FWD:
#endif
break; break;
default: default:
dprintf(("%s invalid command\n", err_prefix)); dprintf(("%s invalid command\n", err_prefix));
@ -982,7 +1012,7 @@ ip_fw_ctl(int stage, struct mbuf **mm)
if (m == NULL) if (m == NULL)
return (ENOBUFS); return (ENOBUFS);
MCLGET(m, M_WAIT); MCLGET(m, M_WAIT);
if(!(m->m_flags & M_EXT)) { if (!(m->m_flags & M_EXT)) {
abort: m_freem(*mm); abort: m_freem(*mm);
*mm = NULL; *mm = NULL;
return (ENOBUFS); return (ENOBUFS);
@ -990,7 +1020,7 @@ abort: m_freem(*mm);
m->m_len = 0; m->m_len = 0;
for (; fcp; fcp = LIST_NEXT(fcp, chain)) { for (; fcp; fcp = LIST_NEXT(fcp, chain)) {
/* Will we need a new cluster? */ /* Will we need a new cluster? */
if((m->m_len + sizeof *(fcp->rule)) > MCLBYTES) { if ((m->m_len + sizeof *(fcp->rule)) > MCLBYTES) {
m = m->m_next = m_get(M_WAIT, MT_SOOPTS); m = m->m_next = m_get(M_WAIT, MT_SOOPTS);
if (m == NULL) { if (m == NULL) {
goto abort; goto abort;
@ -1094,6 +1124,11 @@ ip_fw_init(void)
#else #else
"divert disabled, "); "divert disabled, ");
#endif #endif
#ifdef IPFIREWALL_FORWARD
printf("rule-based forwarding enabled, ");
#else
printf("rule-based forwarding disabled, ");
#endif
#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT #ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
printf("default to accept, "); printf("default to accept, ");
#endif #endif

View File

@ -11,7 +11,7 @@
* *
* This software is provided ``AS IS'' without any warranties of any kind. * This software is provided ``AS IS'' without any warranties of any kind.
* *
* $Id: ip_fw.h,v 1.31 1998/01/08 03:03:54 alex Exp $ * $Id: ip_fw.h,v 1.32 1998/02/03 22:15:03 bde Exp $
*/ */
#ifndef _IP_FW_H #ifndef _IP_FW_H
@ -71,6 +71,7 @@ struct ip_fw {
u_short fu_divert_port; /* Divert/tee port (options IPDIVERT) */ u_short fu_divert_port; /* Divert/tee port (options IPDIVERT) */
u_short fu_skipto_rule; /* SKIPTO command rule number */ u_short fu_skipto_rule; /* SKIPTO command rule number */
u_short fu_reject_code; /* REJECT response code */ u_short fu_reject_code; /* REJECT response code */
struct sockaddr_in fu_fwd_ip;
} fw_un; } fw_un;
u_char fw_prot; /* IP protocol */ u_char fw_prot; /* IP protocol */
u_char fw_nports; /* N'of src ports and # of dst ports */ u_char fw_nports; /* N'of src ports and # of dst ports */
@ -93,6 +94,7 @@ struct ip_fw {
#define fw_divert_port fw_un.fu_divert_port #define fw_divert_port fw_un.fu_divert_port
#define fw_skipto_rule fw_un.fu_skipto_rule #define fw_skipto_rule fw_un.fu_skipto_rule
#define fw_reject_code fw_un.fu_reject_code #define fw_reject_code fw_un.fu_reject_code
#define fw_fwd_ip fw_un.fu_fwd_ip
struct ip_fw_chain { struct ip_fw_chain {
LIST_ENTRY(ip_fw_chain) chain; LIST_ENTRY(ip_fw_chain) chain;
@ -115,6 +117,7 @@ struct ip_fw_chain {
#define IP_FW_F_DIVERT 0x0040 /* This is a divert rule */ #define IP_FW_F_DIVERT 0x0040 /* This is a divert rule */
#define IP_FW_F_TEE 0x0050 /* This is a tee rule */ #define IP_FW_F_TEE 0x0050 /* This is a tee rule */
#define IP_FW_F_SKIPTO 0x0060 /* This is a skipto rule */ #define IP_FW_F_SKIPTO 0x0060 /* This is a skipto rule */
#define IP_FW_F_FWD 0x0070 /* This is a "change forwarding address" rule */
#define IP_FW_F_PRN 0x0080 /* Print if this rule matches */ #define IP_FW_F_PRN 0x0080 /* Print if this rule matches */

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)ip_input.c 8.2 (Berkeley) 1/4/94 * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
* $Id: ip_input.c,v 1.90 1998/06/12 03:48:16 julian Exp $ * $Id: ip_input.c,v 1.91 1998/07/02 05:49:12 julian Exp $
* $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $ * $ANA: ip_input.c,v 1.5 1996/09/18 14:34:59 wollman Exp $
*/ */
@ -181,6 +181,8 @@ static struct ip_srcrt {
static u_short frag_divert_port; static u_short frag_divert_port;
#endif #endif
struct sockaddr_in *ip_fw_fwd_addr;
static void save_rte __P((u_char *, struct in_addr)); static void save_rte __P((u_char *, struct in_addr));
static void ip_deq __P((struct ipasfrag *)); static void ip_deq __P((struct ipasfrag *));
static int ip_dooptions __P((struct mbuf *)); static int ip_dooptions __P((struct mbuf *));
@ -354,30 +356,43 @@ ip_input(struct mbuf *m)
#endif #endif
#ifdef COMPAT_IPFW #ifdef COMPAT_IPFW
if (ip_fw_chk_ptr) { if (ip_fw_chk_ptr) {
#ifdef IPDIVERT u_int16_t port;
u_short port;
port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &ip_divert_cookie, &m); #ifdef IPFIREWALL_FORWARD
/*
* If we've been forwarded from the output side, then
* skip the firewall a second time
*/
if (ip_fw_fwd_addr)
goto ours;
#endif /* IPFIREWALL_FORWARD */
#ifdef IPDIVERT
port = (*ip_fw_chk_ptr)(&ip, hlen, NULL, &ip_divert_cookie,
&m, &ip_fw_fwd_addr);
if (port) { if (port) {
/* Divert packet */ /* Divert packet */
frag_divert_port = port; frag_divert_port = port;
goto ours; goto ours;
} }
#else #else /* !DIVERT */
u_int16_t dummy = 0; /*
/* If ipfw says divert, we have to just drop packet */ * If ipfw says divert, we have to just drop packet */
if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, &dummy, &m)) { * Use port as a dummy argument.
*/
port = 0;
if ((*ip_fw_chk_ptr)(&ip, hlen, NULL, &port,
&m, &ip_fw_fwd_addr)) {
m_freem(m); m_freem(m);
m = NULL; m = NULL;
} }
#endif #endif /* !DIVERT */
if (!m) if (!m)
return; return;
} }
if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN)) if (ip_nat_ptr && !(*ip_nat_ptr)(&ip, &m, m->m_pkthdr.rcvif, IP_NAT_IN))
return; return;
#endif #endif /* !COMPAT_IPFW */
/* /*
* Process options and, if not destined for us, * Process options and, if not destined for us,
@ -401,7 +416,8 @@ ip_input(struct mbuf *m)
/* /*
* Check our list of addresses, to see if the packet is for us. * Check our list of addresses, to see if the packet is for us.
*/ */
for (ia = in_ifaddrhead.tqh_first; ia; ia = ia->ia_link.tqe_next) { for (ia = TAILQ_FIRST(in_ifaddrhead); ia;
ia = TAILQ_NEXT(ia, ia_link)) {
#define satosin(sa) ((struct sockaddr_in *)(sa)) #define satosin(sa) ((struct sockaddr_in *)(sa))
if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr) if (IA_SIN(ia)->sin_addr.s_addr == ip->ip_dst.s_addr)
@ -409,6 +425,16 @@ ip_input(struct mbuf *m)
#ifdef BOOTP_COMPAT #ifdef BOOTP_COMPAT
if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY) if (IA_SIN(ia)->sin_addr.s_addr == INADDR_ANY)
goto ours; goto ours;
#endif
#ifdef IPFIREWALL_FORWARD
/*
* If the addr to forward to is one of ours, we pretend to
* be the destination for this packet.
*/
if (ip_fw_fwd_addr != NULL &&
IA_SIN(ia)->sin_addr.s_addr ==
ip_fw_fwd_addr->sin_addr.s_addr)
goto ours;
#endif #endif
if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) { if (ia->ia_ifp && ia->ia_ifp->if_flags & IFF_BROADCAST) {
if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr == if (satosin(&ia->ia_broadaddr)->sin_addr.s_addr ==
@ -548,8 +574,12 @@ ip_input(struct mbuf *m)
if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) { if (((struct ipasfrag *)ip)->ipf_mff & 1 || ip->ip_off) {
ipstat.ips_fragments++; ipstat.ips_fragments++;
ip = ip_reass((struct ipasfrag *)ip, fp, &ipq[sum]); ip = ip_reass((struct ipasfrag *)ip, fp, &ipq[sum]);
if (ip == 0) if (ip == 0) {
#ifdef IPFIREWALL_FORWARD
ip_fw_fwd_addr = NULL;
#endif
return; return;
}
/* Get the length of the reassembled packets header */ /* Get the length of the reassembled packets header */
hlen = IP_VHL_HL(ip->ip_vhl) << 2; hlen = IP_VHL_HL(ip->ip_vhl) << 2;
ipstat.ips_reassembled++; ipstat.ips_reassembled++;
@ -599,8 +629,14 @@ ip_input(struct mbuf *m)
*/ */
ipstat.ips_delivered++; ipstat.ips_delivered++;
(*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen); (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen);
#ifdef IPFIREWALL_FORWARD
ip_fw_fwd_addr = NULL; /* tcp needed it */
#endif
return; return;
bad: bad:
#ifdef IPFIREWALL_FORWARD
ip_fw_fwd_addr = NULL;
#endif
m_freem(m); m_freem(m);
} }

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)ip_output.c 8.3 (Berkeley) 1/21/94 * @(#)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 #define _IP_VHL
@ -71,6 +71,13 @@ static MALLOC_DEFINE(M_IPMOPTS, "ip_moptions", "internet multicast options");
#undef COMPAT_IPFW #undef COMPAT_IPFW
#endif #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; u_short ip_id;
static struct mbuf *ip_insertoptions __P((struct mbuf *, struct mbuf *, int *)); 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 sockaddr_in *dst;
struct in_ifaddr *ia; struct in_ifaddr *ia;
int isbroadcast; int isbroadcast;
#ifdef IPFIREWALL_FORWARD
int fwd_rewrite_src = 0;
#endif
#ifdef DIAGNOSTIC #ifdef DIAGNOSTIC
if ((m->m_flags & M_PKTHDR) == 0) 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 * If source address not specified yet, use address
* of outgoing interface. * 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; 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 * Verify that we have any chance at all of being able to queue
* the packet or packet fragments * the packet or packet fragments
@ -369,28 +388,134 @@ ip_output(m0, opt, ro, flags, imo)
* Check with the firewall... * Check with the firewall...
*/ */
if (ip_fw_chk_ptr) { if (ip_fw_chk_ptr) {
#ifdef IPFIREWALL_FORWARD
struct sockaddr_in *old;
old = dst;
#endif
#ifdef IPDIVERT #ifdef IPDIVERT
ip_divert_port = (*ip_fw_chk_ptr)(&ip, 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 */ if (ip_divert_port) { /* Divert packet */
(*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0); (*inetsw[ip_protox[IPPROTO_DIVERT]].pr_input)(m, 0);
goto done; goto done;
} }
#else #else /* !IPDIVERT */
u_int16_t dummy = 0; u_int16_t dummy = 0;
/* If ipfw says divert, we have to just drop packet */ /* 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); m_freem(m);
goto done; goto done;
} }
#endif #endif /* !IPDIVERT */
if (!m) { if (!m) {
error = EACCES; error = EACCES;
goto done; 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 */ #endif /* COMPAT_IPFW */
/* /*
* If small enough for interface, can just send directly. * If small enough for interface, can just send directly.
*/ */

View File

@ -31,7 +31,7 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)ip_var.h 8.2 (Berkeley) 1/9/95 * @(#)ip_var.h 8.2 (Berkeley) 1/9/95
* $Id: ip_var.h,v 1.40 1998/06/06 19:39:10 julian Exp $ * $Id: ip_var.h,v 1.41 1998/06/08 09:47:42 bde Exp $
*/ */
#ifndef _NETINET_IP_VAR_H_ #ifndef _NETINET_IP_VAR_H_
@ -211,6 +211,9 @@ extern u_short ip_divert_port;
extern u_short ip_divert_cookie; extern u_short ip_divert_cookie;
#endif /* IPDIVERT */ #endif /* IPDIVERT */
struct sockaddr_in;
extern struct sockaddr_in *ip_fw_fwd_addr;
#endif /* KERNEL */ #endif /* KERNEL */
#endif /* _NETINET_IP_VAR_H_ */ #endif /* _NETINET_IP_VAR_H_ */

View File

@ -31,9 +31,10 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95
* $Id: tcp_input.c,v 1.77 1998/05/18 17:11:24 guido Exp $ * $Id: tcp_input.c,v 1.78 1998/05/31 18:42:49 peter Exp $
*/ */
#include "opt_ipfw.h" /* for ipfw_fwd */
#include "opt_tcpdebug.h" #include "opt_tcpdebug.h"
#include <sys/param.h> #include <sys/param.h>
@ -339,6 +340,33 @@ tcp_input(m, iphlen)
* Locate pcb for segment. * Locate pcb for segment.
*/ */
findpcb: findpcb:
#ifdef IPFIREWALL_FORWARD
if (ip_fw_fwd_addr != NULL) {
/*
* Diverted. Pretend to be the destination.
* already got one like this?
*/
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport, 0);
if (!inp) {
/*
* No, then it's new. Try find the ambushing socket
*/
if (!ip_fw_fwd_addr->sin_port) {
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src,
ti->ti_sport, ip_fw_fwd_addr->sin_addr,
ti->ti_dport, 1);
} else {
inp = in_pcblookup_hash(&tcbinfo,
ti->ti_src, ti->ti_sport,
ip_fw_fwd_addr->sin_addr,
ntohs(ip_fw_fwd_addr->sin_port), 1);
}
}
ip_fw_fwd_addr = NULL;
} else
#endif /* IPFIREWALL_FORWARD */
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport, inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport, 1); ti->ti_dst, ti->ti_dport, 1);

View File

@ -31,9 +31,10 @@
* SUCH DAMAGE. * SUCH DAMAGE.
* *
* @(#)tcp_input.c 8.12 (Berkeley) 5/24/95 * @(#)tcp_input.c 8.12 (Berkeley) 5/24/95
* $Id: tcp_input.c,v 1.77 1998/05/18 17:11:24 guido Exp $ * $Id: tcp_input.c,v 1.78 1998/05/31 18:42:49 peter Exp $
*/ */
#include "opt_ipfw.h" /* for ipfw_fwd */
#include "opt_tcpdebug.h" #include "opt_tcpdebug.h"
#include <sys/param.h> #include <sys/param.h>
@ -339,6 +340,33 @@ tcp_input(m, iphlen)
* Locate pcb for segment. * Locate pcb for segment.
*/ */
findpcb: findpcb:
#ifdef IPFIREWALL_FORWARD
if (ip_fw_fwd_addr != NULL) {
/*
* Diverted. Pretend to be the destination.
* already got one like this?
*/
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport, 0);
if (!inp) {
/*
* No, then it's new. Try find the ambushing socket
*/
if (!ip_fw_fwd_addr->sin_port) {
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src,
ti->ti_sport, ip_fw_fwd_addr->sin_addr,
ti->ti_dport, 1);
} else {
inp = in_pcblookup_hash(&tcbinfo,
ti->ti_src, ti->ti_sport,
ip_fw_fwd_addr->sin_addr,
ntohs(ip_fw_fwd_addr->sin_port), 1);
}
}
ip_fw_fwd_addr = NULL;
} else
#endif /* IPFIREWALL_FORWARD */
inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport, inp = in_pcblookup_hash(&tcbinfo, ti->ti_src, ti->ti_sport,
ti->ti_dst, ti->ti_dport, 1); ti->ti_dst, ti->ti_dport, 1);