summaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
authorbz <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
committerbz <bz@FreeBSD.org>2011-08-20 17:05:11 +0000
commiteccbdd061bd53b771dcd87b1708dfbc43cfb72a7 (patch)
treef3e27acb3ae7873708cae6d77a7b90d7a8d9e68c /sys/netinet
parentfa01a4aee0c4b294791a3b7515a6776750af5f04 (diff)
downloadFreeBSD-src-eccbdd061bd53b771dcd87b1708dfbc43cfb72a7.zip
FreeBSD-src-eccbdd061bd53b771dcd87b1708dfbc43cfb72a7.tar.gz
Add support for IPv6 to ipfw fwd:
Distinguish IPv4 and IPv6 addresses and optional port numbers in user space to set the option for the correct protocol family. Add support in the kernel for carrying the new IPv6 destination address and port. Add support to TCP and UDP for IPv6 and fix UDP IPv4 to not change the address in the IP header. Add support for IPv6 forwarding to a non-local destination. Add a regession test uitilizing VIMAGE to check all 20 possible combinations I could think of. Obtained from: David Dolson at Sandvine Incorporated (original version for ipfw fwd IPv6 support) Sponsored by: Sandvine Incorporated PR: bin/117214 MFC after: 4 weeks Approved by: re (kib)
Diffstat (limited to 'sys/netinet')
-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
8 files changed, 168 insertions, 55 deletions
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"];
OpenPOWER on IntegriCloud