Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netpfil/ipfw/nat64/nat64_translate.c
Show All 23 Lines | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_ipstealth.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/counter.h> | #include <sys/counter.h> | ||||
#include <sys/errno.h> | #include <sys/errno.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mbuf.h> | #include <sys/mbuf.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
static const struct nat64_methods nat64_netisr = { | static const struct nat64_methods nat64_netisr = { | ||||
.output = nat64_output, | .output = nat64_output, | ||||
.output_one = nat64_output_one | .output_one = nat64_output_one | ||||
}; | }; | ||||
static const struct nat64_methods nat64_direct = { | static const struct nat64_methods nat64_direct = { | ||||
.output = nat64_direct_output, | .output = nat64_direct_output, | ||||
.output_one = nat64_direct_output_one | .output_one = nat64_direct_output_one | ||||
}; | }; | ||||
VNET_DEFINE_STATIC(const struct nat64_methods *, nat64out) = &nat64_netisr; | |||||
/* These variables should be initialized explicitly on module loading */ | |||||
VNET_DEFINE_STATIC(const struct nat64_methods *, nat64out); | |||||
VNET_DEFINE_STATIC(const int *, nat64ipstealth); | |||||
VNET_DEFINE_STATIC(const int *, nat64ip6stealth); | |||||
#define V_nat64out VNET(nat64out) | #define V_nat64out VNET(nat64out) | ||||
#define V_nat64ipstealth VNET(nat64ipstealth) | |||||
#define V_nat64ip6stealth VNET(nat64ip6stealth) | |||||
static const int stealth_on = 1; | |||||
#ifndef IPSTEALTH | |||||
static const int stealth_off = 0; | |||||
#endif | |||||
void | void | ||||
nat64_set_output_method(int direct) | nat64_set_output_method(int direct) | ||||
{ | { | ||||
V_nat64out = direct != 0 ? &nat64_direct: &nat64_netisr; | if (direct != 0) { | ||||
V_nat64out = &nat64_direct; | |||||
#ifdef IPSTEALTH | |||||
/* Honor corresponding variables, if IPSTEALTH is defined */ | |||||
V_nat64ipstealth = &V_ipstealth; | |||||
V_nat64ip6stealth = &V_ip6stealth; | |||||
#else | |||||
/* otherwise we need to decrement HLIM/TTL for direct case */ | |||||
V_nat64ipstealth = V_nat64ip6stealth = &stealth_off; | |||||
#endif | |||||
} else { | |||||
V_nat64out = &nat64_netisr; | |||||
/* Leave TTL/HLIM decrementing to forwarding code */ | |||||
V_nat64ipstealth = V_nat64ip6stealth = &stealth_on; | |||||
} | } | ||||
} | |||||
int | int | ||||
nat64_get_output_method(void) | nat64_get_output_method(void) | ||||
{ | { | ||||
return (V_nat64out == &nat64_direct ? 1: 0); | return (V_nat64out == &nat64_direct ? 1: 0); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 360 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
/* assume addresses are already initialized */ | /* assume addresses are already initialized */ | ||||
ip->ip_v = IPVERSION; | ip->ip_v = IPVERSION; | ||||
ip->ip_hl = sizeof(*ip) >> 2; | ip->ip_hl = sizeof(*ip) >> 2; | ||||
ip->ip_tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; | ip->ip_tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; | ||||
ip->ip_len = htons(sizeof(*ip) + plen); | ip->ip_len = htons(sizeof(*ip) + plen); | ||||
ip->ip_ttl = ip6->ip6_hlim; | ip->ip_ttl = ip6->ip6_hlim; | ||||
/* Forwarding code will decrement TTL for netisr based output. */ | if (*V_nat64ip6stealth == 0) | ||||
if (V_nat64out == &nat64_direct) | |||||
ip->ip_ttl -= IPV6_HLIMDEC; | ip->ip_ttl -= IPV6_HLIMDEC; | ||||
ip->ip_sum = 0; | ip->ip_sum = 0; | ||||
ip->ip_p = (proto == IPPROTO_ICMPV6) ? IPPROTO_ICMP: proto; | ip->ip_p = (proto == IPPROTO_ICMPV6) ? IPPROTO_ICMP: proto; | ||||
ip_fillid(ip); | ip_fillid(ip); | ||||
if (frag != NULL) { | if (frag != NULL) { | ||||
ip->ip_off = htons(ntohs(frag->ip6f_offlg) >> 3); | ip->ip_off = htons(ntohs(frag->ip6f_offlg) >> 3); | ||||
if (frag->ip6f_offlg & IP6F_MORE_FRAG) | if (frag->ip6f_offlg & IP6F_MORE_FRAG) | ||||
ip->ip_off |= htons(IP_MF); | ip->ip_off |= htons(IP_MF); | ||||
▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | |||||
#define NAT64_ICMP6_PLEN 64 | #define NAT64_ICMP6_PLEN 64 | ||||
static NAT64NOINLINE void | static NAT64NOINLINE void | ||||
nat64_icmp6_reflect(struct mbuf *m, uint8_t type, uint8_t code, uint32_t mtu, | nat64_icmp6_reflect(struct mbuf *m, uint8_t type, uint8_t code, uint32_t mtu, | ||||
struct nat64_counters *stats, void *logdata) | struct nat64_counters *stats, void *logdata) | ||||
{ | { | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
struct ip6_hdr *ip6, *oip6; | struct ip6_hdr *ip6, *oip6; | ||||
struct mbuf *n; | struct mbuf *n; | ||||
int len, plen; | int len, plen, proto; | ||||
len = 0; | len = 0; | ||||
plen = nat64_getlasthdr(m, &len); | proto = nat64_getlasthdr(m, &len); | ||||
if (plen < 0) { | if (proto < 0) { | ||||
DPRINTF(DP_DROPS, "mbuf isn't contigious"); | DPRINTF(DP_DROPS, "mbuf isn't contigious"); | ||||
goto freeit; | goto freeit; | ||||
} | } | ||||
/* | /* | ||||
* Do not send ICMPv6 in reply to ICMPv6 errors. | * Do not send ICMPv6 in reply to ICMPv6 errors. | ||||
*/ | */ | ||||
if (plen == IPPROTO_ICMPV6) { | if (proto == IPPROTO_ICMPV6) { | ||||
if (m->m_len < len + sizeof(*icmp6)) { | if (m->m_len < len + sizeof(*icmp6)) { | ||||
DPRINTF(DP_DROPS, "mbuf isn't contigious"); | DPRINTF(DP_DROPS, "mbuf isn't contigious"); | ||||
goto freeit; | goto freeit; | ||||
} | } | ||||
icmp6 = mtodo(m, len); | icmp6 = mtodo(m, len); | ||||
if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST || | if (icmp6->icmp6_type < ICMP6_ECHO_REQUEST || | ||||
icmp6->icmp6_type == ND_REDIRECT) { | icmp6->icmp6_type == ND_REDIRECT) { | ||||
DPRINTF(DP_DROPS, "do not send ICMPv6 in reply to " | DPRINTF(DP_DROPS, "do not send ICMPv6 in reply to " | ||||
"ICMPv6 errors"); | "ICMPv6 errors"); | ||||
goto freeit; | goto freeit; | ||||
} | } | ||||
/* | |||||
* If there are extra headers between IPv6 and ICMPv6, | |||||
* strip off them. | |||||
*/ | |||||
if (len > sizeof(struct ip6_hdr)) { | |||||
/* | |||||
* NOTE: ipfw_chk already did m_pullup() and it is | |||||
* expected that data is contigious from the start | |||||
* of IPv6 header up to the end of ICMPv6 header. | |||||
*/ | |||||
bcopy(mtod(m, caddr_t), | |||||
mtodo(m, len - sizeof(struct ip6_hdr)), | |||||
sizeof(struct ip6_hdr)); | |||||
m_adj(m, len - sizeof(struct ip6_hdr)); | |||||
} | } | ||||
} | |||||
/* | /* | ||||
if (icmp6_ratelimit(&ip6->ip6_src, type, code)) | if (icmp6_ratelimit(&ip6->ip6_src, type, code)) | ||||
goto freeit; | goto freeit; | ||||
*/ | */ | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
switch (type) { | switch (type) { | ||||
case ICMP6_DST_UNREACH: | case ICMP6_DST_UNREACH: | ||||
case ICMP6_PACKET_TOO_BIG: | case ICMP6_PACKET_TOO_BIG: | ||||
Show All 24 Lines | nat64_icmp6_reflect(struct mbuf *m, uint8_t type, uint8_t code, uint32_t mtu, | ||||
* in the chain, when we will do M_PREPEND() or make some type of | * in the chain, when we will do M_PREPEND() or make some type of | ||||
* tunneling. | * tunneling. | ||||
*/ | */ | ||||
m_move_pkthdr(n, m); | m_move_pkthdr(n, m); | ||||
M_ALIGN(n, sizeof(struct ip6_hdr) + plen + max_hdr); | M_ALIGN(n, sizeof(struct ip6_hdr) + plen + max_hdr); | ||||
n->m_len = n->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; | n->m_len = n->m_pkthdr.len = sizeof(struct ip6_hdr) + plen; | ||||
oip6 = mtod(n, struct ip6_hdr *); | oip6 = mtod(n, struct ip6_hdr *); | ||||
oip6->ip6_src = ip6->ip6_dst; | /* | ||||
* Make IPv6 source address selection for reflected datagram. | |||||
* nat64_check_ip6() doesn't allow scoped addresses, therefore | |||||
* we use zero scopeid. | |||||
*/ | |||||
if (in6_selectsrc_addr(M_GETFIB(n), &ip6->ip6_src, 0, | |||||
n->m_pkthdr.rcvif, &oip6->ip6_src, NULL) != 0) { | |||||
/* | |||||
* Failed to find proper source address, drop the packet. | |||||
*/ | |||||
m_freem(n); | |||||
goto freeit; | |||||
} | |||||
oip6->ip6_dst = ip6->ip6_src; | oip6->ip6_dst = ip6->ip6_src; | ||||
oip6->ip6_nxt = IPPROTO_ICMPV6; | oip6->ip6_nxt = IPPROTO_ICMPV6; | ||||
oip6->ip6_flow = 0; | oip6->ip6_flow = 0; | ||||
oip6->ip6_vfc |= IPV6_VERSION; | oip6->ip6_vfc |= IPV6_VERSION; | ||||
oip6->ip6_hlim = V_ip6_defhlim; | oip6->ip6_hlim = V_ip6_defhlim; | ||||
oip6->ip6_plen = htons(plen); | oip6->ip6_plen = htons(plen); | ||||
icmp6 = mtodo(n, sizeof(struct ip6_hdr)); | icmp6 = mtodo(n, sizeof(struct ip6_hdr)); | ||||
▲ Show 20 Lines • Show All 478 Lines • ▼ Show 20 Lines | nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, | ||||
struct mbufq mq; | struct mbufq mq; | ||||
uint16_t ip_id, ip_off; | uint16_t ip_id, ip_off; | ||||
uint16_t *csum; | uint16_t *csum; | ||||
int plen, hlen; | int plen, hlen; | ||||
uint8_t proto; | uint8_t proto; | ||||
ip = mtod(m, struct ip*); | ip = mtod(m, struct ip*); | ||||
if (ip->ip_ttl <= IPTTLDEC) { | if (*V_nat64ipstealth == 0 && ip->ip_ttl <= IPTTLDEC) { | ||||
nat64_icmp_reflect(m, ICMP_TIMXCEED, | nat64_icmp_reflect(m, ICMP_TIMXCEED, | ||||
ICMP_TIMXCEED_INTRANS, 0, &cfg->stats, logdata); | ICMP_TIMXCEED_INTRANS, 0, &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
ip6.ip6_dst = *daddr; | ip6.ip6_dst = *daddr; | ||||
ip6.ip6_src = *saddr; | ip6.ip6_src = *saddr; | ||||
Show All 30 Lines | if (nh.nh_mtu < plen + sizeof(ip6) && | ||||
nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, | nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, | ||||
FRAGSZ(nh.nh_mtu) + sizeof(struct ip), &cfg->stats, logdata); | FRAGSZ(nh.nh_mtu) + sizeof(struct ip), &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
ip6.ip6_flow = htonl(ip->ip_tos << 20); | ip6.ip6_flow = htonl(ip->ip_tos << 20); | ||||
ip6.ip6_vfc |= IPV6_VERSION; | ip6.ip6_vfc |= IPV6_VERSION; | ||||
ip6.ip6_hlim = ip->ip_ttl; | ip6.ip6_hlim = ip->ip_ttl; | ||||
/* Forwarding code will decrement TTL for netisr based output. */ | if (*V_nat64ipstealth == 0) | ||||
if (V_nat64out == &nat64_direct) | |||||
ip6.ip6_hlim -= IPTTLDEC; | ip6.ip6_hlim -= IPTTLDEC; | ||||
ip6.ip6_plen = htons(plen); | ip6.ip6_plen = htons(plen); | ||||
ip6.ip6_nxt = (proto == IPPROTO_ICMP) ? IPPROTO_ICMPV6: proto; | ip6.ip6_nxt = (proto == IPPROTO_ICMP) ? IPPROTO_ICMPV6: proto; | ||||
/* Convert checksums. */ | /* Convert checksums. */ | ||||
switch (proto) { | switch (proto) { | ||||
case IPPROTO_TCP: | case IPPROTO_TCP: | ||||
csum = &TCP(mtodo(m, hlen))->th_sum; | csum = &TCP(mtodo(m, hlen))->th_sum; | ||||
if (lport != 0) { | if (lport != 0) { | ||||
▲ Show 20 Lines • Show All 286 Lines • ▼ Show 20 Lines | nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, | ||||
} | } | ||||
ip.ip_dst.s_addr = nat64_extract_ip4(&ip6->ip6_dst, cfg->plat_plen); | ip.ip_dst.s_addr = nat64_extract_ip4(&ip6->ip6_dst, cfg->plat_plen); | ||||
if (ip.ip_dst.s_addr == 0) { | if (ip.ip_dst.s_addr == 0) { | ||||
NAT64STAT_INC(&cfg->stats, dropped); | NAT64STAT_INC(&cfg->stats, dropped); | ||||
return (NAT64MFREE); | return (NAT64MFREE); | ||||
} | } | ||||
if (ip6->ip6_hlim <= IPV6_HLIMDEC) { | if (*V_nat64ip6stealth == 0 && ip6->ip6_hlim <= IPV6_HLIMDEC) { | ||||
nat64_icmp6_reflect(m, ICMP6_TIME_EXCEEDED, | nat64_icmp6_reflect(m, ICMP6_TIME_EXCEEDED, | ||||
ICMP6_TIME_EXCEED_TRANSIT, 0, &cfg->stats, logdata); | ICMP6_TIME_EXCEED_TRANSIT, 0, &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
hlen = 0; | hlen = 0; | ||||
plen = ntohs(ip6->ip6_plen); | plen = ntohs(ip6->ip6_plen); | ||||
proto = nat64_getlasthdr(m, &hlen); | proto = nat64_getlasthdr(m, &hlen); | ||||
▲ Show 20 Lines • Show All 111 Lines • Show Last 20 Lines |