summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/ipfw/ipfw.84
-rw-r--r--sbin/ipfw/ipfw2.c108
-rw-r--r--sys/netinet/ip_fw.h10
-rw-r--r--sys/netinet/ipfw/ip_fw2.c18
-rw-r--r--sys/netinet/ipfw/ip_fw_log.c17
-rw-r--r--sys/netinet/ipfw/ip_fw_pfil.c37
-rw-r--r--sys/netinet/ipfw/ip_fw_private.h1
-rw-r--r--sys/netinet/ipfw/ip_fw_sockopt.c11
-rw-r--r--sys/netinet/tcp_input.c69
-rw-r--r--sys/netinet/udp_usrreq.c60
-rw-r--r--sys/netinet6/ip6_forward.c70
-rw-r--r--sys/netinet6/ip6_input.c31
-rw-r--r--sys/netinet6/ip6_output.c36
-rw-r--r--sys/netinet6/udp6_usrreq.c44
-rwxr-xr-xtools/regression/ipfw/fwd/vimage-fwd.sh369
15 files changed, 800 insertions, 85 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 6fc12c8..9d00287 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd June 29, 2011
+.Dd August 20, 2011
.Dt IPFW 8
.Os
.Sh NAME
@@ -726,7 +726,7 @@ The search terminates.
Change the next-hop on matching packets to
.Ar ipaddr ,
which can be an IP address or a host name.
-The next hop can also be supplied by the last table
+For IPv4, the next hop can also be supplied by the last table
looked up for the packet by using the
.Cm tablearg
keyword instead of an explicit address.
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index e389e99..615c4d1 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -1111,6 +1111,18 @@ show_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth)
}
break;
+ case O_FORWARD_IP6:
+ {
+ char buf[4 + INET6_ADDRSTRLEN + 1];
+ ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd;
+
+ printf("fwd %s", inet_ntop(AF_INET6, &s->sa.sin6_addr,
+ buf, sizeof(buf)));
+ if (s->sa.sin6_port)
+ printf(",%d", s->sa.sin6_port);
+ }
+ break;
+
case O_LOG: /* O_LOG is printed last */
logptr = (ipfw_insn_log *)cmd;
break;
@@ -2809,40 +2821,96 @@ chkarg:
break;
case TOK_FORWARD: {
- ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+ /*
+ * Locate the address-port separator (':' or ',').
+ * Could be one of the following:
+ * hostname:port
+ * IPv4 a.b.c.d,port
+ * IPv4 a.b.c.d:port
+ * IPv6 w:x:y::z,port
+ * The ':' can only be used with hostname and IPv4 address.
+ * XXX-BZ Should we also support [w:x:y::z]:port?
+ */
+ struct sockaddr_storage result;
+ struct addrinfo *res;
char *s, *end;
+ int family;
+ u_short port_number;
NEED1("missing forward address[:port]");
- action->opcode = O_FORWARD_IP;
- action->len = F_INSN_SIZE(ipfw_insn_sa);
-
- /*
- * In the kernel we assume AF_INET and use only
- * sin_port and sin_addr. Remember to set sin_len as
- * the routing code seems to use it too.
- */
- p->sa.sin_family = AF_INET;
- p->sa.sin_len = sizeof(struct sockaddr_in);
- p->sa.sin_port = 0;
/*
* locate the address-port separator (':' or ',')
*/
- s = strchr(*av, ':');
- if (s == NULL)
- s = strchr(*av, ',');
+ s = strchr(*av, ',');
+ if (s == NULL) {
+ /* Distinguish between IPv4:port and IPv6 cases. */
+ s = strchr(*av, ':');
+ if (s && strchr(s+1, ':'))
+ s = NULL; /* no port */
+ }
+
+ port_number = 0;
if (s != NULL) {
+ /* Terminate host portion and set s to start of port. */
*(s++) = '\0';
i = strtoport(s, &end, 0 /* base */, 0 /* proto */);
if (s == end)
errx(EX_DATAERR,
"illegal forwarding port ``%s''", s);
- p->sa.sin_port = (u_short)i;
+ port_number = (u_short)i;
+ }
+
+ if (_substrcmp(*av, "tablearg") == 0) {
+ family = PF_INET;
+ ((struct sockaddr_in*)&result)->sin_addr.s_addr =
+ INADDR_ANY;
+ } else {
+ /*
+ * Resolve the host name or address to a family and a
+ * network representation of the addres.
+ */
+ if (getaddrinfo(*av, NULL, NULL, &res))
+ errx(EX_DATAERR, NULL);
+ /* Just use the first host in the answer. */
+ family = res->ai_family;
+ memcpy(&result, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ if (family == PF_INET) {
+ ipfw_insn_sa *p = (ipfw_insn_sa *)action;
+
+ action->opcode = O_FORWARD_IP;
+ action->len = F_INSN_SIZE(ipfw_insn_sa);
+
+ /*
+ * In the kernel we assume AF_INET and use only
+ * sin_port and sin_addr. Remember to set sin_len as
+ * the routing code seems to use it too.
+ */
+ p->sa.sin_len = sizeof(struct sockaddr_in);
+ p->sa.sin_family = AF_INET;
+ p->sa.sin_port = port_number;
+ p->sa.sin_addr.s_addr =
+ ((struct sockaddr_in *)&result)->sin_addr.s_addr;
+ } else if (family == PF_INET6) {
+ ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action;
+
+ action->opcode = O_FORWARD_IP6;
+ action->len = F_INSN_SIZE(ipfw_insn_sa6);
+
+ p->sa.sin6_len = sizeof(struct sockaddr_in6);
+ p->sa.sin6_family = AF_INET6;
+ p->sa.sin6_port = port_number;
+ p->sa.sin6_flowinfo = 0;
+ p->sa.sin6_scope_id = 0;
+ /* No table support for v6 yet. */
+ bcopy(&((struct sockaddr_in6*)&result)->sin6_addr,
+ &p->sa.sin6_addr, sizeof(p->sa.sin6_addr));
+ } else {
+ errx(EX_DATAERR, "Invalid address family in forward action");
}
- if (_substrcmp(*av, "tablearg") == 0)
- p->sa.sin_addr.s_addr = INADDR_ANY;
- else
- lookup_host(*av, &(p->sa.sin_addr));
av++;
break;
}
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index ff3a67f..f6f8fcd 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -203,6 +203,8 @@ enum ipfw_opcodes { /* arguments (4 byte each) */
O_CALLRETURN, /* arg1=called rule number */
+ O_FORWARD_IP6, /* fwd sockaddr_in6 */
+
O_LAST_OPCODE /* not an opcode! */
};
@@ -299,6 +301,14 @@ typedef struct _ipfw_insn_sa {
} ipfw_insn_sa;
/*
+ * This is used to forward to a given address (ipv6).
+ */
+typedef struct _ipfw_insn_sa6 {
+ ipfw_insn o;
+ struct sockaddr_in6 sa;
+} ipfw_insn_sa6;
+
+/*
* This is used for MAC addr-mask pairs.
*/
typedef struct _ipfw_insn_mac {
diff --git a/sys/netinet/ipfw/ip_fw2.c b/sys/netinet/ipfw/ip_fw2.c
index e4e64e4..c3123df 100644
--- a/sys/netinet/ipfw/ip_fw2.c
+++ b/sys/netinet/ipfw/ip_fw2.c
@@ -796,6 +796,7 @@ set_match(struct ip_fw_args *args, int slot,
*
* args->rule Pointer to the last matching rule (in/out)
* args->next_hop Socket we are forwarding to (out).
+ * args->next_hop6 IPv6 next hop we are forwarding to (out).
* args->f_id Addresses grabbed from the packet (out)
* args->rule.info a cookie depending on rule action
*
@@ -2281,6 +2282,23 @@ do { \
done = 1; /* exit outer loop */
break;
+#ifdef INET6
+ case O_FORWARD_IP6:
+ if (args->eh) /* not valid on layer2 pkts */
+ break;
+ if (q == NULL || q->rule != f ||
+ dyn_dir == MATCH_FORWARD) {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = &(((ipfw_insn_sa6 *)cmd)->sa);
+ args->next_hop6 = sin6;
+ }
+ retval = IP_FW_PASS;
+ l = 0; /* exit inner loop */
+ done = 1; /* exit outer loop */
+ break;
+#endif
+
case O_NETGRAPH:
case O_NGTEE:
set_match(args, f_pos, chain);
diff --git a/sys/netinet/ipfw/ip_fw_log.c b/sys/netinet/ipfw/ip_fw_log.c
index edfa07d..6454813 100644
--- a/sys/netinet/ipfw/ip_fw_log.c
+++ b/sys/netinet/ipfw/ip_fw_log.c
@@ -167,7 +167,7 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
{
char *action;
int limit_reached = 0;
- char action2[40], proto[128], fragment[32];
+ char action2[92], proto[128], fragment[32];
if (V_fw_verbose == 0) {
#ifndef WITHOUT_BPF
@@ -290,6 +290,21 @@ ipfw_log(struct ip_fw *f, u_int hlen, struct ip_fw_args *args,
sa->sa.sin_port);
}
break;
+#ifdef INET6
+ case O_FORWARD_IP6: {
+ char buf[INET6_ADDRSTRLEN];
+ ipfw_insn_sa6 *sa = (ipfw_insn_sa6 *)cmd;
+ int len;
+
+ len = snprintf(SNPARGS(action2, 0), "Forward to [%s]",
+ ip6_sprintf(buf, &sa->sa.sin6_addr));
+
+ if (sa->sa.sin6_port)
+ snprintf(SNPARGS(action2, len), ":%u",
+ sa->sa.sin6_port);
+ }
+ break;
+#endif
case O_NETGRAPH:
snprintf(SNPARGS(action2, 0), "Netgraph %d",
cmd->arg1);
diff --git a/sys/netinet/ipfw/ip_fw_pfil.c b/sys/netinet/ipfw/ip_fw_pfil.c
index 736615b..2aaa845 100644
--- a/sys/netinet/ipfw/ip_fw_pfil.c
+++ b/sys/netinet/ipfw/ip_fw_pfil.c
@@ -152,13 +152,26 @@ again:
switch (ipfw) {
case IP_FW_PASS:
/* next_hop may be set by ipfw_chk */
- if (args.next_hop == NULL)
+ if (args.next_hop == NULL && args.next_hop6 == NULL)
break; /* pass */
#ifndef IPFIREWALL_FORWARD
ret = EACCES;
#else
{
struct m_tag *fwd_tag;
+ size_t len;
+
+ KASSERT(args.next_hop == NULL || args.next_hop6 == NULL,
+ ("%s: both next_hop=%p and next_hop6=%p not NULL", __func__,
+ args.next_hop, args.next_hop6));
+#ifdef INET6
+ if (args.next_hop6 != NULL)
+ len = sizeof(struct sockaddr_in6);
+#endif
+#ifdef INET
+ if (args.next_hop != NULL)
+ len = sizeof(struct sockaddr_in);
+#endif
/* Incoming packets should not be tagged so we do not
* m_tag_find. Outgoing packets may be tagged, so we
@@ -169,18 +182,28 @@ again:
if (fwd_tag != NULL) {
m_tag_unlink(*m0, fwd_tag);
} else {
- fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
- sizeof(struct sockaddr_in), M_NOWAIT);
+ fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD, len,
+ M_NOWAIT);
if (fwd_tag == NULL) {
ret = EACCES;
break; /* i.e. drop */
}
}
- bcopy(args.next_hop, (fwd_tag+1), sizeof(struct sockaddr_in));
+#ifdef INET6
+ if (args.next_hop6 != NULL) {
+ bcopy(args.next_hop6, (fwd_tag+1), len);
+ if (in6_localip(&args.next_hop6->sin6_addr))
+ (*m0)->m_flags |= M_FASTFWD_OURS;
+ }
+#endif
+#ifdef INET
+ if (args.next_hop != NULL) {
+ bcopy(args.next_hop, (fwd_tag+1), len);
+ if (in_localip(args.next_hop->sin_addr))
+ (*m0)->m_flags |= M_FASTFWD_OURS;
+ }
+#endif
m_tag_prepend(*m0, fwd_tag);
-
- if (in_localip(args.next_hop->sin_addr))
- (*m0)->m_flags |= M_FASTFWD_OURS;
}
#endif
break;
diff --git a/sys/netinet/ipfw/ip_fw_private.h b/sys/netinet/ipfw/ip_fw_private.h
index 16ef46d..fdb2b77 100644
--- a/sys/netinet/ipfw/ip_fw_private.h
+++ b/sys/netinet/ipfw/ip_fw_private.h
@@ -86,6 +86,7 @@ struct ip_fw_args {
struct mbuf *m; /* the mbuf chain */
struct ifnet *oif; /* output interface */
struct sockaddr_in *next_hop; /* forward address */
+ struct sockaddr_in6 *next_hop6; /* ipv6 forward address */
/*
* On return, it points to the matching rule.
diff --git a/sys/netinet/ipfw/ip_fw_sockopt.c b/sys/netinet/ipfw/ip_fw_sockopt.c
index 1432858..375b9c5 100644
--- a/sys/netinet/ipfw/ip_fw_sockopt.c
+++ b/sys/netinet/ipfw/ip_fw_sockopt.c
@@ -723,6 +723,17 @@ check_ipfw_struct(struct ip_fw *rule, int size)
return EINVAL;
#endif
+#ifdef INET6
+ case O_FORWARD_IP6:
+#ifdef IPFIREWALL_FORWARD
+ if (cmdlen != F_INSN_SIZE(ipfw_insn_sa6))
+ goto bad_size;
+ goto check_action;
+#else
+ return (EINVAL);
+#endif
+#endif /* INET6 */
+
case O_DIVERT:
case O_TEE:
if (ip_divert_ptr == NULL)
diff --git a/sys/netinet/tcp_input.c b/sys/netinet/tcp_input.c
index e3e9aa6..24680c2 100644
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -573,11 +573,9 @@ tcp_input(struct mbuf *m, int off0)
uint8_t sig_checked = 0;
#endif
uint8_t iptos = 0;
-#ifdef INET
#ifdef IPFIREWALL_FORWARD
struct m_tag *fwd_tag;
#endif
-#endif /* INET */
#ifdef INET6
struct ip6_hdr *ip6 = NULL;
int isipv6;
@@ -776,14 +774,55 @@ findpcb:
}
#endif
-#ifdef INET
#ifdef IPFIREWALL_FORWARD
/*
* Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
*/
fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
+#endif /* IPFIREWALL_FORWARD */
- if (fwd_tag != NULL && isipv6 == 0) { /* IPv6 support is not yet */
+#ifdef INET6
+#ifdef IPFIREWALL_FORWARD
+ if (isipv6 && fwd_tag != NULL) {
+ struct sockaddr_in6 *next_hop6;
+
+ next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1);
+ /*
+ * Transparently forwarded. Pretend to be the destination.
+ * Already got one like this?
+ */
+ inp = in6_pcblookup_mbuf(&V_tcbinfo,
+ &ip6->ip6_src, th->th_sport, &ip6->ip6_dst, th->th_dport,
+ INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif, m);
+ if (!inp) {
+ /*
+ * It's new. Try to find the ambushing socket.
+ * Because we've rewritten the destination address,
+ * any hardware-generated hash is ignored.
+ */
+ inp = in6_pcblookup(&V_tcbinfo, &ip6->ip6_src,
+ th->th_sport, &next_hop6->sin6_addr,
+ next_hop6->sin6_port ? ntohs(next_hop6->sin6_port) :
+ th->th_dport, INPLOOKUP_WILDCARD |
+ INPLOOKUP_WLOCKPCB, m->m_pkthdr.rcvif);
+ }
+ /* Remove the tag from the packet. We don't need it anymore. */
+ m_tag_delete(m, fwd_tag);
+ } else
+#endif /* IPFIREWALL_FORWARD */
+ if (isipv6) {
+ inp = in6_pcblookup_mbuf(&V_tcbinfo, &ip6->ip6_src,
+ th->th_sport, &ip6->ip6_dst, th->th_dport,
+ INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB,
+ m->m_pkthdr.rcvif, m);
+ }
+#endif /* INET6 */
+#if defined(INET6) && defined(INET)
+ else
+#endif
+#ifdef INET
+#ifdef IPFIREWALL_FORWARD
+ if (fwd_tag != NULL) {
struct sockaddr_in *next_hop;
next_hop = (struct sockaddr_in *)(fwd_tag+1);
@@ -810,25 +849,11 @@ findpcb:
m_tag_delete(m, fwd_tag);
} else
#endif /* IPFIREWALL_FORWARD */
+ inp = in_pcblookup_mbuf(&V_tcbinfo, ip->ip_src,
+ th->th_sport, ip->ip_dst, th->th_dport,
+ INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB,
+ m->m_pkthdr.rcvif, m);
#endif /* INET */
- {
-#ifdef INET6
- if (isipv6)
- inp = in6_pcblookup_mbuf(&V_tcbinfo, &ip6->ip6_src,
- th->th_sport, &ip6->ip6_dst, th->th_dport,
- INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB,
- m->m_pkthdr.rcvif, m);
-#endif
-#if defined(INET) && defined(INET6)
- else
-#endif
-#ifdef INET
- inp = in_pcblookup_mbuf(&V_tcbinfo, ip->ip_src,
- th->th_sport, ip->ip_dst, th->th_dport,
- INPLOOKUP_WILDCARD | INPLOOKUP_WLOCKPCB,
- m->m_pkthdr.rcvif, m);
-#endif
- }
/*
* If the INPCB does not exist then all data in the incoming
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index 28eb8fd..701bc35 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -441,28 +441,6 @@ udp_input(struct mbuf *m, int off)
} else
UDPSTAT_INC(udps_nosum);
-#ifdef IPFIREWALL_FORWARD
- /*
- * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
- */
- fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
- if (fwd_tag != NULL) {
- struct sockaddr_in *next_hop;
-
- /*
- * Do the hack.
- */
- next_hop = (struct sockaddr_in *)(fwd_tag + 1);
- ip->ip_dst = next_hop->sin_addr;
- uh->uh_dport = ntohs(next_hop->sin_port);
-
- /*
- * Remove the tag from the packet. We don't need it anymore.
- */
- m_tag_delete(m, fwd_tag);
- }
-#endif
-
if (IN_MULTICAST(ntohl(ip->ip_dst.s_addr)) ||
in_broadcast(ip->ip_dst, ifp)) {
struct inpcb *last;
@@ -568,9 +546,41 @@ udp_input(struct mbuf *m, int off)
/*
* Locate pcb for datagram.
*/
- inp = in_pcblookup_mbuf(&V_udbinfo, ip->ip_src, uh->uh_sport,
- ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB,
- ifp, m);
+#ifdef IPFIREWALL_FORWARD
+ /*
+ * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
+ */
+ fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
+ if (fwd_tag != NULL) {
+ struct sockaddr_in *next_hop;
+
+ next_hop = (struct sockaddr_in *)(fwd_tag + 1);
+
+ /*
+ * Transparently forwarded. Pretend to be the destination.
+ * Already got one like this?
+ */
+ inp = in_pcblookup_mbuf(&V_udbinfo, ip->ip_src, uh->uh_sport,
+ ip->ip_dst, uh->uh_dport, INPLOOKUP_RLOCKPCB, ifp, m);
+ if (!inp) {
+ /*
+ * It's new. Try to find the ambushing socket.
+ * Because we've rewritten the destination address,
+ * any hardware-generated hash is ignored.
+ */
+ inp = in_pcblookup(&V_udbinfo, ip->ip_src,
+ uh->uh_sport, next_hop->sin_addr,
+ next_hop->sin_port ? htons(next_hop->sin_port) :
+ uh->uh_dport, INPLOOKUP_WILDCARD |
+ INPLOOKUP_RLOCKPCB, ifp);
+ }
+ /* Remove the tag from the packet. We don't need it anymore. */
+ m_tag_delete(m, fwd_tag);
+ } else
+#endif /* IPFIREWALL_FORWARD */
+ inp = in_pcblookup_mbuf(&V_udbinfo, ip->ip_src, uh->uh_sport,
+ ip->ip_dst, uh->uh_dport, INPLOOKUP_WILDCARD |
+ INPLOOKUP_RLOCKPCB, ifp, m);
if (inp == NULL) {
if (udp_log_in_vain) {
char buf[4*sizeof "123"];
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
index cff29e1..77cb926 100644
--- a/sys/netinet6/ip6_forward.c
+++ b/sys/netinet6/ip6_forward.c
@@ -34,6 +34,7 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_ipfw.h"
#include "opt_ipsec.h"
#include "opt_ipstealth.h"
@@ -50,6 +51,7 @@ __FBSDID("$FreeBSD$");
#include <sys/syslog.h>
#include <net/if.h>
+#include <net/netisr.h>
#include <net/route.h>
#include <net/pfil.h>
@@ -98,11 +100,17 @@ ip6_forward(struct mbuf *m, int srcrt)
struct mbuf *mcopy = NULL;
struct ifnet *origifp; /* maybe unnecessary */
u_int32_t inzone, outzone;
- struct in6_addr src_in6, dst_in6;
+ struct in6_addr src_in6, dst_in6, odst;
#ifdef IPSEC
struct secpolicy *sp = NULL;
int ipsecrt = 0;
#endif
+#ifdef SCTP
+ int sw_csum;
+#endif
+#ifdef IPFIREWALL_FORWARD
+ struct m_tag *fwd_tag;
+#endif
char ip6bufs[INET6_ADDRSTRLEN], ip6bufd[INET6_ADDRSTRLEN];
#ifdef IPSEC
@@ -345,13 +353,15 @@ ip6_forward(struct mbuf *m, int srcrt)
goto skip_routing;
skip_ipsec:
#endif
-
+again:
bzero(&rin6, sizeof(struct route_in6));
dst = (struct sockaddr_in6 *)&rin6.ro_dst;
dst->sin6_len = sizeof(struct sockaddr_in6);
dst->sin6_family = AF_INET6;
dst->sin6_addr = ip6->ip6_dst;
-
+#ifdef IPFIREWALL_FORWARD
+again2:
+#endif
rin6.ro_rt = rtalloc1((struct sockaddr *)dst, 0, 0);
if (rin6.ro_rt != NULL)
RT_UNLOCK(rin6.ro_rt);
@@ -554,6 +564,7 @@ skip_routing:
if (!PFIL_HOOKED(&V_inet6_pfil_hook))
goto pass;
+ odst = ip6->ip6_dst;
/* Run through list of hooks for output packets. */
error = pfil_run_hooks(&V_inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT, NULL);
if (error != 0)
@@ -562,6 +573,59 @@ skip_routing:
goto freecopy;
ip6 = mtod(m, struct ip6_hdr *);
+ /* See if destination IP address was changed by packet filter. */
+ if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {
+ m->m_flags |= M_SKIP_FIREWALL;
+ /* If destination is now ourself drop to ip6_input(). */
+ if (in6_localip(&ip6->ip6_dst)) {
+ m->m_flags |= M_FASTFWD_OURS;
+ if (m->m_pkthdr.rcvif == NULL)
+ m->m_pkthdr.rcvif = V_loif;
+ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+ m->m_pkthdr.csum_flags |=
+ CSUM_IP_CHECKED | CSUM_IP_VALID;
+#ifdef SCTP
+ if (m->m_pkthdr.csum_flags & CSUM_SCTP)
+ m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+#endif
+ error = netisr_queue(NETISR_IPV6, m);
+ goto out;
+ } else
+ goto again; /* Redo the routing table lookup. */
+ }
+
+#ifdef IPFIREWALL_FORWARD
+ /* See if local, if yes, send it to netisr. */
+ if (m->m_flags & M_FASTFWD_OURS) {
+ if (m->m_pkthdr.rcvif == NULL)
+ m->m_pkthdr.rcvif = V_loif;
+ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+#ifdef SCTP
+ if (m->m_pkthdr.csum_flags & CSUM_SCTP)
+ m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+#endif
+ error = netisr_queue(NETISR_IPV6, m);
+ goto out;
+ }
+ /* Or forward to some other address? */
+ fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
+ if (fwd_tag) {
+ dst = (struct sockaddr_in6 *)&rin6.ro_dst;
+ bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6));
+ m->m_flags |= M_SKIP_FIREWALL;
+ m_tag_delete(m, fwd_tag);
+ goto again2;
+ }
+#endif /* IPFIREWALL_FORWARD */
+
pass:
error = nd6_output(rt->rt_ifp, origifp, m, dst, rt);
if (error) {
diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c
index de3a622..1fdde16 100644
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_ipfw.h"
#include "opt_ipsec.h"
#include "opt_route.h"
@@ -91,6 +92,7 @@ __FBSDID("$FreeBSD$");
#include <net/vnet.h>
#include <netinet/in.h>
+#include <netinet/ip_var.h>
#include <netinet/in_systm.h>
#include <net/if_llatbl.h>
#ifdef INET
@@ -357,6 +359,17 @@ ip6_input(struct mbuf *m)
*/
ip6_delaux(m);
+ if (m->m_flags & M_FASTFWD_OURS) {
+ /*
+ * Firewall changed destination to local.
+ */
+ m->m_flags &= ~M_FASTFWD_OURS;
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ ip6 = mtod(m, struct ip6_hdr *);
+ goto hbhcheck;
+ }
+
/*
* mbuf statistics
*/
@@ -533,6 +546,24 @@ ip6_input(struct mbuf *m)
ip6 = mtod(m, struct ip6_hdr *);
srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst);
+#ifdef IPFIREWALL_FORWARD
+ if (m->m_flags & M_FASTFWD_OURS) {
+ m->m_flags &= ~M_FASTFWD_OURS;
+ ours = 1;
+ deliverifp = m->m_pkthdr.rcvif;
+ goto hbhcheck;
+ }
+ if (m_tag_find(m, PACKET_TAG_IPFORWARD, NULL) != NULL) {
+ /*
+ * Directly ship the packet on. This allows forwarding
+ * packets originally destined to us to some other directly
+ * connected host.
+ */
+ ip6_forward(m, 1);
+ goto out;
+ }
+#endif /* IPFIREWALL_FORWARD */
+
passin:
/*
* Disambiguate address scope zones (if there is ambiguity).
diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c
index 973689c..8516124 100644
--- a/sys/netinet6/ip6_output.c
+++ b/sys/netinet6/ip6_output.c
@@ -65,6 +65,7 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_ipfw.h"
#include "opt_ipsec.h"
#include "opt_sctp.h"
#include "opt_route.h"
@@ -90,6 +91,7 @@ __FBSDID("$FreeBSD$");
#include <netinet/in.h>
#include <netinet/in_var.h>
+#include <netinet/ip_var.h>
#include <netinet6/in6_var.h>
#include <netinet/ip6.h>
#include <netinet/icmp6.h>
@@ -229,6 +231,9 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt,
int segleft_org = 0;
struct secpolicy *sp = NULL;
#endif /* IPSEC */
+#ifdef IPFIREWALL_FORWARD
+ struct m_tag *fwd_tag;
+#endif
ip6 = mtod(m, struct ip6_hdr *);
if (ip6 == NULL) {
@@ -850,7 +855,8 @@ again:
if (!IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst)) {
m->m_flags |= M_SKIP_FIREWALL;
/* If destination is now ourself drop to ip6_input(). */
- if (in6_localaddr(&ip6->ip6_dst)) {
+ if (in6_localip(&ip6->ip6_dst)) {
+ m->m_flags |= M_FASTFWD_OURS;
if (m->m_pkthdr.rcvif == NULL)
m->m_pkthdr.rcvif = V_loif;
if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
@@ -870,7 +876,33 @@ again:
goto again; /* Redo the routing table lookup. */
}
- /* XXX: IPFIREWALL_FORWARD */
+#ifdef IPFIREWALL_FORWARD
+ /* See if local, if yes, send it to netisr. */
+ if (m->m_flags & M_FASTFWD_OURS) {
+ if (m->m_pkthdr.rcvif == NULL)
+ m->m_pkthdr.rcvif = V_loif;
+ if (m->m_pkthdr.csum_flags & CSUM_DELAY_DATA) {
+ m->m_pkthdr.csum_flags |=
+ CSUM_DATA_VALID | CSUM_PSEUDO_HDR;
+ m->m_pkthdr.csum_data = 0xffff;
+ }
+#ifdef SCTP
+ if (m->m_pkthdr.csum_flags & CSUM_SCTP)
+ m->m_pkthdr.csum_flags |= CSUM_SCTP_VALID;
+#endif
+ error = netisr_queue(NETISR_IPV6, m);
+ goto done;
+ }
+ /* Or forward to some other address? */
+ fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
+ if (fwd_tag) {
+ dst = (struct sockaddr_in6 *)&ro->ro_dst;
+ bcopy((fwd_tag+1), dst, sizeof(struct sockaddr_in6));
+ m->m_flags |= M_SKIP_FIREWALL;
+ m_tag_delete(m, fwd_tag);
+ goto again;
+ }
+#endif /* IPFIREWALL_FORWARD */
passout:
/*
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 6723007..2a4c0a5 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -71,6 +71,7 @@ __FBSDID("$FreeBSD$");
#include "opt_inet.h"
#include "opt_inet6.h"
+#include "opt_ipfw.h"
#include "opt_ipsec.h"
#include <sys/param.h>
@@ -181,6 +182,9 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
int off = *offp;
int plen, ulen;
struct sockaddr_in6 fromsa;
+#ifdef IPFIREWALL_FORWARD
+ struct m_tag *fwd_tag;
+#endif
ifp = m->m_pkthdr.rcvif;
ip6 = mtod(m, struct ip6_hdr *);
@@ -377,9 +381,43 @@ udp6_input(struct mbuf **mp, int *offp, int proto)
/*
* Locate pcb for datagram.
*/
- inp = in6_pcblookup_mbuf(&V_udbinfo, &ip6->ip6_src, uh->uh_sport,
- &ip6->ip6_dst, uh->uh_dport, INPLOOKUP_WILDCARD |
- INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m);
+#ifdef IPFIREWALL_FORWARD
+ /*
+ * Grab info from PACKET_TAG_IPFORWARD tag prepended to the chain.
+ */
+ fwd_tag = m_tag_find(m, PACKET_TAG_IPFORWARD, NULL);
+ if (fwd_tag != NULL) {
+ struct sockaddr_in6 *next_hop6;
+
+ next_hop6 = (struct sockaddr_in6 *)(fwd_tag + 1);
+
+ /*
+ * Transparently forwarded. Pretend to be the destination.
+ * Already got one like this?
+ */
+ inp = in6_pcblookup_mbuf(&V_udbinfo,
+ &ip6->ip6_src, uh->uh_sport, &ip6->ip6_dst, uh->uh_dport,
+ INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif, m);
+ if (!inp) {
+ /*
+ * It's new. Try to find the ambushing socket.
+ * Because we've rewritten the destination address,
+ * any hardware-generated hash is ignored.
+ */
+ inp = in6_pcblookup(&V_udbinfo, &ip6->ip6_src,
+ uh->uh_sport, &next_hop6->sin6_addr,
+ next_hop6->sin6_port ? htons(next_hop6->sin6_port) :
+ uh->uh_dport, INPLOOKUP_WILDCARD |
+ INPLOOKUP_RLOCKPCB, m->m_pkthdr.rcvif);
+ }
+ /* Remove the tag from the packet. We don't need it anymore. */
+ m_tag_delete(m, fwd_tag);
+ } else
+#endif /* IPFIREWALL_FORWARD */
+ inp = in6_pcblookup_mbuf(&V_udbinfo, &ip6->ip6_src,
+ uh->uh_sport, &ip6->ip6_dst, uh->uh_dport,
+ INPLOOKUP_WILDCARD | INPLOOKUP_RLOCKPCB,
+ m->m_pkthdr.rcvif, m);
if (inp == NULL) {
if (udp_log_in_vain) {
char ip6bufs[INET6_ADDRSTRLEN];
diff --git a/tools/regression/ipfw/fwd/vimage-fwd.sh b/tools/regression/ipfw/fwd/vimage-fwd.sh
new file mode 100755
index 0000000..5dbfcd7
--- /dev/null
+++ b/tools/regression/ipfw/fwd/vimage-fwd.sh
@@ -0,0 +1,369 @@
+#!/bin/sh
+#-
+# Copyright (c) 2010, "Bjoern A. Zeeb" <bz@FreeBSD.org>
+# Copyright (c) 2011, Sandvine Incorporated ULC.
+# 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.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+#
+
+#
+# Test ipfw fwd for IPv4 and IPv6 using VIMAGE, testing that as well.
+# For no test the packet header contents must be changed but always
+# keeping the original destination.
+#
+
+case `id -u` in
+0) ;;
+*) echo "ERROR: Must be run as superuser." >&2
+ exit 2
+esac
+
+epair_base()
+{
+ local ep
+
+ ep=`ifconfig epair create`
+ expr ${ep} : '\(.*\).'
+}
+
+debug_err()
+{
+ local _p
+ _p="$1"
+
+ case "${DEBUG}" in
+ "") ;;
+ *)
+ echo " ~~ start of debug ~~"
+ echo " ~~ left:"
+ jexec ${ljid} /sbin/ipfw show
+ echo " ~~ middle:"
+ jexec ${mjid} /sbin/ipfw show
+ echo " ~~ right:"
+ jexec ${rjid} /sbin/ipfw show
+ echo " ~~ result file:"
+ cat ${_p}.1
+ echo " ~~ log file:"
+ cat ${_p}
+ echo " ~~ end of debug ~~"
+ ;;
+ esac
+}
+
+check_cleanup_result_file()
+{
+ local _p
+ _p="$1"
+
+ if test ! -s ${_p}.1; then
+ echo "FAIL (output file empty)."
+ debug_err ${_p}
+ else
+ read line < ${_p}.1
+ # Netcat adds 'X's in udp mode.
+ l="/${line#*/}"
+ if test "${l}" = "${_p}"; then
+ echo "PASS."
+ else
+ echo "FAIL (expected: '${_p}' got '${l}')."
+ debug_err ${_p}
+ fi
+ fi
+
+ rm -f ${_p}.1
+ rm -f ${_p}
+}
+
+# Transparent proxy scenario (local address).
+run_test_tp()
+{
+ local _descr
+ local _sip _dip _fip _fport _dport _p
+ local _nc_af _nc_p
+ local _lport
+ descr="$1"
+ _sip="$2"
+ _dip="$3"
+ _fip="$4"
+ _fport="$5"
+ _dport="$6"
+ _p="$7"
+ _nc_af="$8"
+
+ _lport=${_dport}
+ case "${_fport}" in
+ "") _lport="${_dport}" ;;
+ *) _lport="${_fport#,}" ;;
+ esac
+
+ case "${_p}" in
+ udp) _nc_p="-u" ;;
+ esac
+
+ OUT=`mktemp -t "ipfwfwd$$-XXXXXX"`
+ echo -n "${descr} (${OUT}).."
+ (
+ jexec ${ljid} /sbin/ipfw -f flush
+ jexec ${ljid} /sbin/ipfw -f zero
+ jexec ${mjid} /sbin/ipfw -f flush
+ jexec ${mjid} /sbin/ipfw -f zero
+ jexec ${rjid} /sbin/ipfw -f flush
+ jexec ${rjid} /sbin/ipfw -f zero
+ jexec ${mjid} /sbin/ipfw add 100 fwd ${_fip}${_fport} ${_p} from ${_sip} to ${_dip}
+
+ jexec ${mjid} /bin/sh -c "nc -w 10 ${_nc_af} -n ${_nc_p} -l ${_fip} ${_lport} > ${OUT}.1 &"
+ jexec ${rjid} /bin/sh -c "echo '${OUT}' | nc -w 1 -v ${_nc_af} -n ${_nc_p} ${_dip} ${_dport}"
+ ) > ${OUT} 2>&1
+ check_cleanup_result_file "${OUT}"
+}
+
+# Transparent redirect scenario (non-local address).
+run_test_nh()
+{
+ local _descr
+ local _sip _dip _fip _fport _dport _p
+ local _nc_af _nc_p
+ local _lport
+ descr="$1"
+ _sip="$2"
+ _dip="$3"
+ _fip="$4"
+ _fport="$5"
+ _dport="$6"
+ _p="$7"
+ _nc_af="$8"
+
+ _lport=${_dport}
+ case "${_fport}" in
+ "") _lport="${_dport}" ;;
+ *) _lport="${_fport#,}" ;;
+ esac
+
+ case "${_p}" in
+ udp) _nc_p="-u" ;;
+ esac
+
+ OUT=`mktemp -t "ipfwfwd$$-XXXXXX"`
+ echo -n "${descr} (${OUT}).."
+ (
+ jexec ${ljid} /sbin/ipfw -f flush
+ jexec ${ljid} /sbin/ipfw -f zero
+ jexec ${mjid} /sbin/ipfw -f flush
+ jexec ${mjid} /sbin/ipfw -f zero
+ jexec ${rjid} /sbin/ipfw -f flush
+ jexec ${rjid} /sbin/ipfw -f zero
+ jexec ${mjid} /sbin/ipfw add 100 fwd ${_fip} ${_p} from ${_sip} to ${_dip}
+
+ jexec ${ljid} /bin/sh -c "nc -w 10 ${_nc_af} -n ${_nc_p} -l ${_dip} ${_lport} > ${OUT}.1 &"
+ jexec ${rjid} /bin/sh -c "echo '${OUT}' | nc -w 1 -v ${_nc_af} -n ${_nc_p} ${_dip} ${_dport}"
+ ) > ${OUT} 2>&1
+ check_cleanup_result_file "${OUT}"
+}
+
+echo "==> Setting up test network"
+kldload -q ipfw > /dev/null 2>&1
+
+# Start left (sender) jail.
+ljid=`jail -i -c -n lef$$ host.hostname=left.example.net vnet persist`
+
+# Start middle (ipfw) jail.
+mjid=`jail -i -c -n mid$$ host.hostname=center.example.net vnet persist`
+
+# Start right (non-local ip redirects go to here) jail.
+rjid=`jail -i -c -n right$$ host.hostname=right.example.net vnet persist`
+
+echo "left ${ljid} middle ${mjid} right ${rjid}"
+
+# Create networking.
+#
+# jail: left middle right
+# ifaces: lmep:a ---- lmep:b mrep:a ---- mrep:b
+#
+
+jexec ${mjid} sysctl net.inet.ip.forwarding=1
+jexec ${mjid} sysctl net.inet6.ip6.forwarding=1
+jexec ${mjid} sysctl net.inet6.ip6.accept_rtadv=0
+
+lmep=$(epair_base)
+ifconfig ${lmep}a vnet ${ljid}
+ifconfig ${lmep}b vnet ${mjid}
+
+jexec ${ljid} ifconfig lo0 inet 127.0.0.1/8
+jexec ${ljid} ifconfig lo0 inet 192.0.2.5/32 alias # Test 9-10
+jexec ${ljid} ifconfig lo0 inet6 2001:db8:1::1/128 alias # Test 11-12
+jexec ${ljid} ifconfig ${lmep}a inet 192.0.2.1/30 up
+jexec ${ljid} ifconfig ${lmep}a inet6 2001:db8::1/64 alias
+
+jexec ${ljid} route add default 192.0.2.2
+jexec ${ljid} route add -inet6 default 2001:db8::2
+
+jexec ${mjid} ifconfig lo0 inet 127.0.0.1/8
+jexec ${mjid} ifconfig lo0 inet 192.0.2.255/32 alias # Test 1-4
+jexec ${mjid} ifconfig lo0 inet6 2001:db8:ffff::1/128 alias # Test 5-8
+jexec ${mjid} ifconfig ${lmep}b inet 192.0.2.2/30 up
+jexec ${mjid} ifconfig ${lmep}b inet6 2001:db8::2/64 alias
+jexec ${mjid} route add default 192.0.2.1
+
+mrep=$(epair_base)
+ifconfig ${mrep}a vnet ${mjid}
+ifconfig ${mrep}b vnet ${rjid}
+
+jexec ${mjid} ifconfig ${mrep}a inet 192.0.2.5/30 up
+jexec ${mjid} ifconfig ${mrep}a inet6 2001:db8:1::1/64 alias
+
+jexec ${rjid} ifconfig lo0 inet 127.0.0.1/8
+jexec ${rjid} ifconfig ${mrep}b inet 192.0.2.6/30 up
+jexec ${rjid} ifconfig ${mrep}b inet6 2001:db8:1::2/64 alias
+
+jexec ${rjid} route add default 192.0.2.5
+jexec ${rjid} route add -inet6 default 2001:db8:1::1
+
+# ------------------------------------------------------------------------------
+# Tests
+#
+# The jails are not chrooted to they all share the same base filesystem.
+# This means we can put results into /tmp and just collect them from here.
+#
+echo "==> Running tests"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=1
+run_test_tp "TEST ${i} IPv4 UDP redirect local to other local address, same port" \
+ 192.0.2.6 192.0.2.5 192.0.2.255 "" 12345 udp "-4"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 UDP redirect local to other local address, different port" \
+ 192.0.2.6 192.0.2.5 192.0.2.255 ",65534" 12345 udp "-4"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 TCP redirect local to other local address, same port" \
+ 192.0.2.6 192.0.2.5 192.0.2.255 "" 12345 tcp "-4"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 TCP redirect local to other local address, different port" \
+ 192.0.2.6 192.0.2.5 192.0.2.255 ",65534" 12345 tcp "-4"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 UDP redirect foreign to local address, same port" \
+ 192.0.2.6 192.0.2.1 192.0.2.255 "" 12345 udp "-4"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 UDP redirect foreign to local address, different port" \
+ 192.0.2.6 192.0.2.1 192.0.2.255 ",65534" 12345 udp "-4"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 TCP redirect foreign to local address, same port" \
+ 192.0.2.6 192.0.2.1 192.0.2.255 "" 12345 tcp "-4"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv4 TCP redirect foreign to local address, different port" \
+ 192.0.2.6 192.0.2.1 192.0.2.255 ",65534" 12345 tcp "-4"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 UDP redirect local to other local address, same port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 udp "-6"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 UDP redirect local to other local address, different port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 udp "-6"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 TCP redirect local to other local address, same port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 tcp "-6"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 TCP redirect local to other local address, different port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 tcp "-6"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 UDP redirect foreign to local address, same port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 udp "-6"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 UDP redirect foreign to local address, different port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 udp "-6"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 TCP redirect foreign to local address, same port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 "" 12345 tcp "-6"
+
+i=$((i + 1))
+run_test_tp "TEST ${i} IPv6 TCP redirect foreign to local address, different port" \
+ 2001:db8:1::2 2001:db8::1 2001:db8:ffff::1 ",65534" 12345 tcp "-6"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_nh "TEST ${i} IPv4 UDP redirect to foreign address" \
+ 192.0.2.6 192.0.2.5 192.0.2.1 "" 12345 udp "-4"
+
+i=$((i + 1))
+run_test_nh "TEST ${i} IPv4 TCP redirect to foreign address" \
+ 192.0.2.6 192.0.2.5 192.0.2.1 "" 12345 tcp "-4"
+
+#- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+i=$((i + 1))
+run_test_nh "TEST ${i} IPv6 UDP redirect to foreign address" \
+ 2001:db8:1::2 2001:db8:1::1 2001:db8::1 "" 12345 udp "-6"
+
+i=$((i + 1))
+run_test_nh "TEST ${i} IPv6 TCP redirect to foreign address" \
+ 2001:db8:1::2 2001:db8:1::1 2001:db8::1 "" 12345 tcp "-6"
+
+################################################################################
+#
+# Cleanup
+#
+echo "==> Cleaning up in 3 seconds"
+# Let VIMAGE network stacks settle to avoid panics while still "experimental".
+sleep 3
+
+jail -r ${rjid}
+jail -r ${mjid}
+jail -r ${ljid}
+
+for jid in ${rjid} ${mjid} ${ljid}; do
+ while : ; do
+ x=`jls -as -j ${jid} jid 2>/dev/null`
+ case "${x}" in
+ jid=*) echo "Waiting for jail ${jid} to stop." >&2
+ sleep 1
+ continue
+ ;;
+ esac
+ break
+ done
+done
+
+ifconfig ${lmep}a destroy
+ifconfig ${mrep}a destroy
+
+# end
OpenPOWER on IntegriCloud