diff options
author | bz <bz@FreeBSD.org> | 2011-08-20 17:05:11 +0000 |
---|---|---|
committer | bz <bz@FreeBSD.org> | 2011-08-20 17:05:11 +0000 |
commit | eccbdd061bd53b771dcd87b1708dfbc43cfb72a7 (patch) | |
tree | f3e27acb3ae7873708cae6d77a7b90d7a8d9e68c /sys/netinet | |
parent | fa01a4aee0c4b294791a3b7515a6776750af5f04 (diff) | |
download | FreeBSD-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.h | 10 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw2.c | 18 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_log.c | 17 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_pfil.c | 37 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_private.h | 1 | ||||
-rw-r--r-- | sys/netinet/ipfw/ip_fw_sockopt.c | 11 | ||||
-rw-r--r-- | sys/netinet/tcp_input.c | 69 | ||||
-rw-r--r-- | sys/netinet/udp_usrreq.c | 60 |
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"]; |