Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netpfil/ipfw/nat64/nat64_translate.c
Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <net/if.h> | #include <net/if.h> | ||||
#include <net/if_var.h> | #include <net/if_var.h> | ||||
#include <net/if_pflog.h> | #include <net/if_pflog.h> | ||||
#include <net/pfil.h> | #include <net/pfil.h> | ||||
#include <net/netisr.h> | #include <net/netisr.h> | ||||
#include <net/route.h> | #include <net/route.h> | ||||
#include <net/route/nhop.h> | |||||
#include <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_fib.h> | #include <netinet/in_fib.h> | ||||
#include <netinet/in_var.h> | |||||
#include <netinet/ip.h> | #include <netinet/ip.h> | ||||
#include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
#include <netinet/ip_fw.h> | #include <netinet/ip_fw.h> | ||||
#include <netinet/ip6.h> | #include <netinet/ip6.h> | ||||
#include <netinet/icmp6.h> | #include <netinet/icmp6.h> | ||||
#include <netinet/ip_icmp.h> | #include <netinet/ip_icmp.h> | ||||
#include <netinet/tcp.h> | #include <netinet/tcp.h> | ||||
#include <netinet/udp.h> | #include <netinet/udp.h> | ||||
Show All 10 Lines | |||||
#include "nat64_translate.h" | #include "nat64_translate.h" | ||||
typedef int (*nat64_output_t)(struct ifnet *, struct mbuf *, | typedef int (*nat64_output_t)(struct ifnet *, struct mbuf *, | ||||
struct sockaddr *, struct nat64_counters *, void *); | struct sockaddr *, struct nat64_counters *, void *); | ||||
typedef int (*nat64_output_one_t)(struct mbuf *, struct nat64_counters *, | typedef int (*nat64_output_one_t)(struct mbuf *, struct nat64_counters *, | ||||
void *); | void *); | ||||
static int nat64_find_route4(struct nhop4_basic *, struct sockaddr_in *, | static struct nhop_object *nat64_find_route4(struct sockaddr_in *, | ||||
struct mbuf *); | struct mbuf *); | ||||
static int nat64_find_route6(struct nhop6_basic *, struct sockaddr_in6 *, | static struct nhop_object *nat64_find_route6(struct sockaddr_in6 *, | ||||
struct mbuf *); | struct mbuf *); | ||||
static int nat64_output_one(struct mbuf *, struct nat64_counters *, void *); | static int nat64_output_one(struct mbuf *, struct nat64_counters *, void *); | ||||
static int nat64_output(struct ifnet *, struct mbuf *, struct sockaddr *, | static int nat64_output(struct ifnet *, struct mbuf *, struct sockaddr *, | ||||
struct nat64_counters *, void *); | struct nat64_counters *, void *); | ||||
static int nat64_direct_output_one(struct mbuf *, struct nat64_counters *, | static int nat64_direct_output_one(struct mbuf *, struct nat64_counters *, | ||||
void *); | void *); | ||||
static int nat64_direct_output(struct ifnet *, struct mbuf *, | static int nat64_direct_output(struct ifnet *, struct mbuf *, | ||||
struct sockaddr *, struct nat64_counters *, void *); | struct sockaddr *, struct nat64_counters *, void *); | ||||
▲ Show 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | if (error != 0) | ||||
NAT64STAT_INC(stats, oerrors); | NAT64STAT_INC(stats, oerrors); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
nat64_direct_output_one(struct mbuf *m, struct nat64_counters *stats, | nat64_direct_output_one(struct mbuf *m, struct nat64_counters *stats, | ||||
void *logdata) | void *logdata) | ||||
{ | { | ||||
struct nhop6_basic nh6; | struct nhop_object *nh4 = NULL; | ||||
struct nhop4_basic nh4; | struct nhop_object *nh6 = NULL; | ||||
struct sockaddr_in6 dst6; | struct sockaddr_in6 dst6; | ||||
struct sockaddr_in dst4; | struct sockaddr_in dst4; | ||||
struct sockaddr *dst; | struct sockaddr *dst; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct ip *ip4; | struct ip *ip4; | ||||
struct ifnet *ifp; | struct ifnet *ifp; | ||||
int error; | int error; | ||||
ip4 = mtod(m, struct ip *); | ip4 = mtod(m, struct ip *); | ||||
error = 0; | |||||
switch (ip4->ip_v) { | switch (ip4->ip_v) { | ||||
case IPVERSION: | case IPVERSION: | ||||
dst4.sin_addr = ip4->ip_dst; | dst4.sin_addr = ip4->ip_dst; | ||||
error = nat64_find_route4(&nh4, &dst4, m); | nh4 = nat64_find_route4(&dst4, m); | ||||
if (error != 0) | if (nh4 == NULL) { | ||||
NAT64STAT_INC(stats, noroute4); | NAT64STAT_INC(stats, noroute4); | ||||
else { | error = EHOSTUNREACH; | ||||
ifp = nh4.nh_ifp; | } else { | ||||
ifp = nh4->nh_ifp; | |||||
dst = (struct sockaddr *)&dst4; | dst = (struct sockaddr *)&dst4; | ||||
} | } | ||||
break; | break; | ||||
case (IPV6_VERSION >> 4): | case (IPV6_VERSION >> 4): | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
dst6.sin6_addr = ip6->ip6_dst; | dst6.sin6_addr = ip6->ip6_dst; | ||||
error = nat64_find_route6(&nh6, &dst6, m); | nh6 = nat64_find_route6(&dst6, m); | ||||
if (error != 0) | if (nh6 == NULL) { | ||||
NAT64STAT_INC(stats, noroute6); | NAT64STAT_INC(stats, noroute6); | ||||
else { | error = EHOSTUNREACH; | ||||
ifp = nh6.nh_ifp; | } else { | ||||
ifp = nh6->nh_ifp; | |||||
dst = (struct sockaddr *)&dst6; | dst = (struct sockaddr *)&dst6; | ||||
} | } | ||||
break; | break; | ||||
default: | default: | ||||
m_freem(m); | m_freem(m); | ||||
NAT64STAT_INC(stats, dropped); | NAT64STAT_INC(stats, dropped); | ||||
DPRINTF(DP_DROPS, "dropped due to unknown IP version"); | DPRINTF(DP_DROPS, "dropped due to unknown IP version"); | ||||
return (EAFNOSUPPORT); | return (EAFNOSUPPORT); | ||||
▲ Show 20 Lines • Show All 396 Lines • ▼ Show 20 Lines | if (m != NULL) | ||||
m_freem(m); | m_freem(m); | ||||
if (n != NULL) | if (n != NULL) | ||||
m_freem(n); | m_freem(n); | ||||
mbufq_drain(mq); | mbufq_drain(mq); | ||||
NAT64STAT_INC(stats, nomem); | NAT64STAT_INC(stats, nomem); | ||||
return (ENOMEM); | return (ENOMEM); | ||||
} | } | ||||
static NAT64NOINLINE int | static struct nhop_object * | ||||
nat64_find_route6(struct nhop6_basic *pnh, struct sockaddr_in6 *dst, | nat64_find_route6(struct sockaddr_in6 *dst, struct mbuf *m) | ||||
struct mbuf *m) | |||||
{ | { | ||||
struct nhop_object *nh; | |||||
if (fib6_lookup_nh_basic(M_GETFIB(m), &dst->sin6_addr, 0, 0, 0, | NET_EPOCH_ASSERT(); | ||||
pnh) != 0) | nh = fib6_lookup(M_GETFIB(m), &dst->sin6_addr, 0, 0, 0); | ||||
return (EHOSTUNREACH); | if (nh == NULL) | ||||
if (pnh->nh_flags & (NHF_BLACKHOLE | NHF_REJECT)) | return NULL; | ||||
return (EHOSTUNREACH); | if (nh->nh_flags & (NHF_BLACKHOLE | NHF_REJECT)) | ||||
return NULL; | |||||
/* | /* | ||||
* XXX: we need to use destination address with embedded scope | * XXX: we need to use destination address with embedded scope | ||||
* zone id, because LLTABLE uses such form of addresses for lookup. | * zone id, because LLTABLE uses such form of addresses for lookup. | ||||
*/ | */ | ||||
dst->sin6_family = AF_INET6; | dst->sin6_family = AF_INET6; | ||||
dst->sin6_len = sizeof(*dst); | dst->sin6_len = sizeof(*dst); | ||||
dst->sin6_addr = pnh->nh_addr; | dst->sin6_addr = ifatoia6(nh->nh_ifa)->ia_addr.sin6_addr; | ||||
if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) | if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) | ||||
dst->sin6_addr.s6_addr16[1] = | dst->sin6_addr.s6_addr16[1] = | ||||
htons(pnh->nh_ifp->if_index & 0xffff); | htons(nh->nh_ifp->if_index & 0xffff); | ||||
dst->sin6_port = 0; | dst->sin6_port = 0; | ||||
dst->sin6_scope_id = 0; | dst->sin6_scope_id = 0; | ||||
dst->sin6_flowinfo = 0; | dst->sin6_flowinfo = 0; | ||||
return (0); | return nh; | ||||
} | } | ||||
#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; | ||||
▲ Show 20 Lines • Show All 111 Lines • ▼ Show 20 Lines | nat64_icmp6_reflect(struct mbuf *m, uint8_t type, uint8_t code, uint32_t mtu, | ||||
m_freem(m); | m_freem(m); | ||||
V_nat64out->output_one(n, stats, logdata); | V_nat64out->output_one(n, stats, logdata); | ||||
return; | return; | ||||
freeit: | freeit: | ||||
NAT64STAT_INC(stats, dropped); | NAT64STAT_INC(stats, dropped); | ||||
m_freem(m); | m_freem(m); | ||||
} | } | ||||
static NAT64NOINLINE int | static struct nhop_object * | ||||
nat64_find_route4(struct nhop4_basic *pnh, struct sockaddr_in *dst, | nat64_find_route4(struct sockaddr_in *dst, struct mbuf *m) | ||||
struct mbuf *m) | |||||
{ | { | ||||
struct nhop_object *nh; | |||||
if (fib4_lookup_nh_basic(M_GETFIB(m), dst->sin_addr, 0, 0, pnh) != 0) | NET_EPOCH_ASSERT(); | ||||
return (EHOSTUNREACH); | nh = fib4_lookup(M_GETFIB(m), dst->sin_addr, 0, 0, 0); | ||||
if (pnh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST | NHF_REJECT)) | if (nh == NULL) | ||||
return (EHOSTUNREACH); | return NULL; | ||||
if (nh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST | NHF_REJECT)) | |||||
return NULL; | |||||
dst->sin_family = AF_INET; | dst->sin_family = AF_INET; | ||||
dst->sin_len = sizeof(*dst); | dst->sin_len = sizeof(*dst); | ||||
dst->sin_addr = pnh->nh_addr; | dst->sin_addr = IA_SIN(nh->nh_ifa)->sin_addr; | ||||
dst->sin_port = 0; | dst->sin_port = 0; | ||||
return (0); | return nh; | ||||
} | } | ||||
#define NAT64_ICMP_PLEN 64 | #define NAT64_ICMP_PLEN 64 | ||||
static NAT64NOINLINE void | static NAT64NOINLINE void | ||||
nat64_icmp_reflect(struct mbuf *m, uint8_t type, | nat64_icmp_reflect(struct mbuf *m, uint8_t type, | ||||
uint8_t code, uint16_t mtu, struct nat64_counters *stats, void *logdata) | uint8_t code, uint16_t mtu, struct nat64_counters *stats, void *logdata) | ||||
{ | { | ||||
struct icmp *icmp; | struct icmp *icmp; | ||||
▲ Show 20 Lines • Show All 426 Lines • ▼ Show 20 Lines | nat64_getlasthdr(struct mbuf *m, int *offset) | ||||
return (proto); | return (proto); | ||||
} | } | ||||
int | int | ||||
nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, | nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, | ||||
struct in6_addr *daddr, uint16_t lport, struct nat64_config *cfg, | struct in6_addr *daddr, uint16_t lport, struct nat64_config *cfg, | ||||
void *logdata) | void *logdata) | ||||
{ | { | ||||
struct nhop6_basic nh; | struct nhop_object *nh; | ||||
struct ip6_hdr ip6; | struct ip6_hdr ip6; | ||||
struct sockaddr_in6 dst; | struct sockaddr_in6 dst; | ||||
struct ip *ip; | struct ip *ip; | ||||
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; | ||||
Show All 26 Lines | nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, | ||||
/* Fragmented ICMP is unsupported */ | /* Fragmented ICMP is unsupported */ | ||||
if (proto == IPPROTO_ICMP && ip_off != 0) { | if (proto == IPPROTO_ICMP && ip_off != 0) { | ||||
DPRINTF(DP_DROPS, "dropped due to fragmented ICMP"); | DPRINTF(DP_DROPS, "dropped due to fragmented ICMP"); | ||||
NAT64STAT_INC(&cfg->stats, dropped); | NAT64STAT_INC(&cfg->stats, dropped); | ||||
return (NAT64MFREE); | return (NAT64MFREE); | ||||
} | } | ||||
dst.sin6_addr = ip6.ip6_dst; | dst.sin6_addr = ip6.ip6_dst; | ||||
if (nat64_find_route6(&nh, &dst, m) != 0) { | nh = nat64_find_route6(&dst, m); | ||||
if (nh == NULL) { | |||||
NAT64STAT_INC(&cfg->stats, noroute6); | NAT64STAT_INC(&cfg->stats, noroute6); | ||||
nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, | nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, | ||||
&cfg->stats, logdata); | &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
if (nh.nh_mtu < plen + sizeof(ip6) && | if (nh->nh_mtu < plen + sizeof(ip6) && | ||||
(ip->ip_off & htons(IP_DF)) != 0) { | (ip->ip_off & htons(IP_DF)) != 0) { | ||||
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; | ||||
if (*V_nat64ipstealth == 0) | if (*V_nat64ipstealth == 0) | ||||
ip6.ip6_hlim -= IPTTLDEC; | ip6.ip6_hlim -= IPTTLDEC; | ||||
Show All 22 Lines | nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, | ||||
case IPPROTO_ICMP: | case IPPROTO_ICMP: | ||||
m = nat64_icmp_translate(m, &ip6, lport, hlen, cfg); | m = nat64_icmp_translate(m, &ip6, lport, hlen, cfg); | ||||
if (m == NULL) /* stats already accounted */ | if (m == NULL) /* stats already accounted */ | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
m_adj(m, hlen); | m_adj(m, hlen); | ||||
mbufq_init(&mq, 255); | mbufq_init(&mq, 255); | ||||
nat64_fragment6(&cfg->stats, &ip6, &mq, m, nh.nh_mtu, ip_id, ip_off); | nat64_fragment6(&cfg->stats, &ip6, &mq, m, nh->nh_mtu, ip_id, ip_off); | ||||
while ((m = mbufq_dequeue(&mq)) != NULL) { | while ((m = mbufq_dequeue(&mq)) != NULL) { | ||||
if (V_nat64out->output(nh.nh_ifp, m, (struct sockaddr *)&dst, | if (V_nat64out->output(nh->nh_ifp, m, (struct sockaddr *)&dst, | ||||
&cfg->stats, logdata) != 0) | &cfg->stats, logdata) != 0) | ||||
break; | break; | ||||
NAT64STAT_INC(&cfg->stats, opcnt46); | NAT64STAT_INC(&cfg->stats, opcnt46); | ||||
} | } | ||||
mbufq_drain(&mq); | mbufq_drain(&mq); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 218 Lines • ▼ Show 20 Lines | fail: | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
int | int | ||||
nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, | nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, | ||||
struct nat64_config *cfg, void *logdata) | struct nat64_config *cfg, void *logdata) | ||||
{ | { | ||||
struct ip ip; | struct ip ip; | ||||
struct nhop4_basic nh; | struct nhop_object *nh; | ||||
struct sockaddr_in dst; | struct sockaddr_in dst; | ||||
struct ip6_frag *frag; | struct ip6_frag *frag; | ||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
uint16_t *csum; | uint16_t *csum; | ||||
int plen, hlen, proto; | int plen, hlen, proto; | ||||
/* | /* | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, | ||||
if (proto == IPPROTO_ICMPV6) { | if (proto == IPPROTO_ICMPV6) { | ||||
icmp6 = mtodo(m, hlen); | icmp6 = mtodo(m, hlen); | ||||
if (icmp6->icmp6_type != ICMP6_ECHO_REQUEST && | if (icmp6->icmp6_type != ICMP6_ECHO_REQUEST && | ||||
icmp6->icmp6_type != ICMP6_ECHO_REPLY) | icmp6->icmp6_type != ICMP6_ECHO_REPLY) | ||||
return (nat64_handle_icmp6(m, hlen, aaddr, aport, | return (nat64_handle_icmp6(m, hlen, aaddr, aport, | ||||
cfg, logdata)); | cfg, logdata)); | ||||
} | } | ||||
dst.sin_addr.s_addr = ip.ip_dst.s_addr; | dst.sin_addr.s_addr = ip.ip_dst.s_addr; | ||||
if (nat64_find_route4(&nh, &dst, m) != 0) { | nh = nat64_find_route4(&dst, m); | ||||
if (nh == NULL) { | |||||
NAT64STAT_INC(&cfg->stats, noroute4); | NAT64STAT_INC(&cfg->stats, noroute4); | ||||
nat64_icmp6_reflect(m, ICMP6_DST_UNREACH, | nat64_icmp6_reflect(m, ICMP6_DST_UNREACH, | ||||
ICMP6_DST_UNREACH_NOROUTE, 0, &cfg->stats, logdata); | ICMP6_DST_UNREACH_NOROUTE, 0, &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
if (nh.nh_mtu < plen + sizeof(ip)) { | if (nh->nh_mtu < plen + sizeof(ip)) { | ||||
nat64_icmp6_reflect(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu, | nat64_icmp6_reflect(m, ICMP6_PACKET_TOO_BIG, 0, nh->nh_mtu, | ||||
&cfg->stats, logdata); | &cfg->stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
nat64_init_ip4hdr(ip6, frag, plen, proto, &ip); | nat64_init_ip4hdr(ip6, frag, plen, proto, &ip); | ||||
/* 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; | ||||
Show All 31 Lines | if (aport != 0) { | ||||
icmp6->icmp6_id = aport; | icmp6->icmp6_id = aport; | ||||
*csum = cksum_adjust(*csum, old_id, aport); | *csum = cksum_adjust(*csum, old_id, aport); | ||||
} | } | ||||
break; | break; | ||||
}; | }; | ||||
m_adj(m, hlen - sizeof(ip)); | m_adj(m, hlen - sizeof(ip)); | ||||
bcopy(&ip, mtod(m, void *), sizeof(ip)); | bcopy(&ip, mtod(m, void *), sizeof(ip)); | ||||
if (V_nat64out->output(nh.nh_ifp, m, (struct sockaddr *)&dst, | if (V_nat64out->output(nh->nh_ifp, m, (struct sockaddr *)&dst, | ||||
&cfg->stats, logdata) == 0) | &cfg->stats, logdata) == 0) | ||||
NAT64STAT_INC(&cfg->stats, opcnt64); | NAT64STAT_INC(&cfg->stats, opcnt64); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||