Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/nat64/nat64_translate.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#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 <netinet/in.h> | #include <netinet/in.h> | ||||
#include <netinet/in_fib.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> | ||||
#include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
#include <netinet6/in6_fib.h> | |||||
#include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
#include <netpfil/pf/pf.h> | #include <netpfil/pf/pf.h> | ||||
#include <netpfil/ipfw/ip_fw_private.h> | #include <netpfil/ipfw/ip_fw_private.h> | ||||
#include <netpfil/ipfw/nat64/ip_fw_nat64.h> | #include <netpfil/ipfw/nat64/ip_fw_nat64.h> | ||||
#include <netpfil/ipfw/nat64/nat64_translate.h> | #include <netpfil/ipfw/nat64/nat64_translate.h> | ||||
#include <machine/in_cksum.h> | #include <machine/in_cksum.h> | ||||
static void | static void | ||||
nat64_log(struct pfloghdr *logdata, struct mbuf *m, sa_family_t family) | nat64_log(struct pfloghdr *logdata, struct mbuf *m, sa_family_t family) | ||||
{ | { | ||||
logdata->dir = PF_OUT; | logdata->dir = PF_OUT; | ||||
logdata->af = family; | logdata->af = family; | ||||
ipfw_bpf_mtap2(logdata, PFLOG_HDRLEN, m); | ipfw_bpf_mtap2(logdata, PFLOG_HDRLEN, m); | ||||
} | } | ||||
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT | #ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT | ||||
static NAT64NOINLINE struct sockaddr* nat64_find_route4(struct route *ro, | static NAT64NOINLINE int nat64_find_route4(struct nhop4_basic *, | ||||
in_addr_t dest, struct mbuf *m); | struct sockaddr_in *, struct mbuf *); | ||||
static NAT64NOINLINE struct sockaddr* nat64_find_route6(struct route_in6 *ro, | static NAT64NOINLINE int nat64_find_route6(struct nhop6_basic *, | ||||
struct in6_addr *dest, struct mbuf *m); | struct sockaddr_in6 *, struct mbuf *); | ||||
static NAT64NOINLINE int | static NAT64NOINLINE int | ||||
nat64_output(struct ifnet *ifp, struct mbuf *m, | nat64_output(struct ifnet *ifp, struct mbuf *m, | ||||
struct sockaddr *dst, struct route *ro, nat64_stats_block *stats, | struct sockaddr *dst, struct route *ro, nat64_stats_block *stats, | ||||
void *logdata) | void *logdata) | ||||
{ | { | ||||
int error; | int error; | ||||
if (logdata != NULL) | if (logdata != NULL) | ||||
nat64_log(logdata, m, dst->sa_family); | nat64_log(logdata, m, dst->sa_family); | ||||
error = (*ifp->if_output)(ifp, m, dst, ro); | error = (*ifp->if_output)(ifp, m, dst, ro); | ||||
if (error != 0) | if (error != 0) | ||||
NAT64STAT_INC(stats, oerrors); | NAT64STAT_INC(stats, oerrors); | ||||
return (error); | return (error); | ||||
} | } | ||||
static NAT64NOINLINE int | static NAT64NOINLINE int | ||||
nat64_output_one(struct mbuf *m, nat64_stats_block *stats, void *logdata) | nat64_output_one(struct mbuf *m, nat64_stats_block *stats, void *logdata) | ||||
{ | { | ||||
struct route_in6 ro6; | struct nhop6_basic nh6; | ||||
struct route ro4, *ro; | struct nhop4_basic nh4; | ||||
struct sockaddr_in6 dst6; | |||||
struct sockaddr_in dst4; | |||||
struct sockaddr *dst; | struct sockaddr *dst; | ||||
struct ifnet *ifp; | |||||
struct ip6_hdr *ip6; | struct ip6_hdr *ip6; | ||||
struct ip *ip4; | struct ip *ip4; | ||||
struct ifnet *ifp; | |||||
int error; | int error; | ||||
ip4 = mtod(m, struct ip *); | ip4 = mtod(m, struct ip *); | ||||
switch (ip4->ip_v) { | switch (ip4->ip_v) { | ||||
case IPVERSION: | case IPVERSION: | ||||
ro = &ro4; | dst4.sin_addr = ip4->ip_dst; | ||||
dst = nat64_find_route4(&ro4, ip4->ip_dst.s_addr, m); | error = nat64_find_route4(&nh4, &dst4, m); | ||||
if (dst == NULL) | if (error != 0) | ||||
NAT64STAT_INC(stats, noroute4); | NAT64STAT_INC(stats, noroute4); | ||||
else { | |||||
ifp = nh4.nh_ifp; | |||||
dst = (struct sockaddr *)&dst4; | |||||
} | |||||
break; | break; | ||||
case (IPV6_VERSION >> 4): | case (IPV6_VERSION >> 4): | ||||
ip6 = (struct ip6_hdr *)ip4; | ip6 = mtod(m, struct ip6_hdr *); | ||||
ro = (struct route *)&ro6; | dst6.sin6_addr = ip6->ip6_dst; | ||||
dst = nat64_find_route6(&ro6, &ip6->ip6_dst, m); | error = nat64_find_route6(&nh6, &dst6, m); | ||||
if (dst == NULL) | if (error != 0) | ||||
NAT64STAT_INC(stats, noroute6); | NAT64STAT_INC(stats, noroute6); | ||||
else { | |||||
ifp = nh6.nh_ifp; | |||||
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); | ||||
} | } | ||||
if (dst == NULL) { | if (error != 0) { | ||||
FREE_ROUTE(ro); | |||||
m_freem(m); | m_freem(m); | ||||
return (EHOSTUNREACH); | return (EHOSTUNREACH); | ||||
} | } | ||||
if (logdata != NULL) | if (logdata != NULL) | ||||
nat64_log(logdata, m, dst->sa_family); | nat64_log(logdata, m, dst->sa_family); | ||||
ifp = ro->ro_rt->rt_ifp; | error = (*ifp->if_output)(ifp, m, dst, NULL); | ||||
error = (*ifp->if_output)(ifp, m, dst, ro); | |||||
if (error != 0) | if (error != 0) | ||||
NAT64STAT_INC(stats, oerrors); | NAT64STAT_INC(stats, oerrors); | ||||
FREE_ROUTE(ro); | |||||
return (error); | return (error); | ||||
} | } | ||||
#else /* !IPFIREWALL_NAT64_DIRECT_OUTPUT */ | #else /* !IPFIREWALL_NAT64_DIRECT_OUTPUT */ | ||||
static NAT64NOINLINE int | static NAT64NOINLINE int | ||||
nat64_output(struct ifnet *ifp, struct mbuf *m, | nat64_output(struct ifnet *ifp, struct mbuf *m, | ||||
struct sockaddr *dst, struct route *ro, nat64_stats_block *stats, | struct sockaddr *dst, struct route *ro, nat64_stats_block *stats, | ||||
void *logdata) | void *logdata) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 313 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); | ||||
} | } | ||||
#if __FreeBSD_version < 1100000 | static NAT64NOINLINE int | ||||
#define rt_expire rt_rmx.rmx_expire | nat64_find_route6(struct nhop6_basic *pnh, struct sockaddr_in6 *dst, | ||||
#define rt_mtu rt_rmx.rmx_mtu | struct mbuf *m) | ||||
#endif | |||||
static NAT64NOINLINE struct sockaddr* | |||||
nat64_find_route6(struct route_in6 *ro, struct in6_addr *dest, struct mbuf *m) | |||||
{ | { | ||||
struct sockaddr_in6 *dst; | |||||
struct rtentry *rt; | |||||
bzero(ro, sizeof(*ro)); | if (fib6_lookup_nh_basic(M_GETFIB(m), &dst->sin6_addr, 0, 0, 0, | ||||
dst = (struct sockaddr_in6 *)&ro->ro_dst; | pnh) != 0) | ||||
return (EHOSTUNREACH); | |||||
if (pnh->nh_flags & (NHF_BLACKHOLE | NHF_REJECT)) | |||||
return (EHOSTUNREACH); | |||||
/* | |||||
* XXX: we need to use destination address with embedded scope | |||||
* 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 = *dest; | dst->sin6_addr = pnh->nh_addr; | ||||
IN6_LOOKUP_ROUTE(ro, M_GETFIB(m)); | if (IN6_IS_SCOPE_LINKLOCAL(&dst->sin6_addr)) | ||||
rt = ro->ro_rt; | dst->sin6_addr.s6_addr16[1] = | ||||
if (rt && (rt->rt_flags & RTF_UP) && | htons(pnh->nh_ifp->if_index & 0xffff); | ||||
(rt->rt_ifp->if_flags & IFF_UP) && | dst->sin6_port = 0; | ||||
(rt->rt_ifp->if_drv_flags & IFF_DRV_RUNNING)) { | dst->sin6_scope_id = 0; | ||||
if (rt->rt_flags & RTF_GATEWAY) | dst->sin6_flowinfo = 0; | ||||
dst = (struct sockaddr_in6 *)rt->rt_gateway; | |||||
} else | return (0); | ||||
return (NULL); | |||||
if (((rt->rt_flags & RTF_REJECT) && | |||||
(rt->rt_expire == 0 || | |||||
time_uptime < rt->rt_expire)) || | |||||
rt->rt_ifp->if_link_state == LINK_STATE_DOWN) | |||||
return (NULL); | |||||
return ((struct sockaddr *)dst); | |||||
} | } | ||||
#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, | ||||
nat64_stats_block *stats, void *logdata) | nat64_stats_block *stats, void *logdata) | ||||
{ | { | ||||
struct icmp6_hdr *icmp6; | struct icmp6_hdr *icmp6; | ||||
▲ Show 20 Lines • Show All 84 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); | ||||
nat64_output_one(n, stats, logdata); | nat64_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 struct sockaddr* | static NAT64NOINLINE int | ||||
nat64_find_route4(struct route *ro, in_addr_t dest, struct mbuf *m) | nat64_find_route4(struct nhop4_basic *pnh, struct sockaddr_in *dst, | ||||
struct mbuf *m) | |||||
{ | { | ||||
struct sockaddr_in *dst; | |||||
struct rtentry *rt; | |||||
bzero(ro, sizeof(*ro)); | if (fib4_lookup_nh_basic(M_GETFIB(m), dst->sin_addr, 0, 0, pnh) != 0) | ||||
dst = (struct sockaddr_in *)&ro->ro_dst; | return (EHOSTUNREACH); | ||||
if (pnh->nh_flags & (NHF_BLACKHOLE | NHF_BROADCAST | NHF_REJECT)) | |||||
return (EHOSTUNREACH); | |||||
dst->sin_family = AF_INET; | dst->sin_family = AF_INET; | ||||
dst->sin_len = sizeof(*dst); | dst->sin_len = sizeof(*dst); | ||||
dst->sin_addr.s_addr = dest; | dst->sin_addr = pnh->nh_addr; | ||||
IN_LOOKUP_ROUTE(ro, M_GETFIB(m)); | dst->sin_port = 0; | ||||
rt = ro->ro_rt; | return (0); | ||||
if (rt && (rt->rt_flags & RTF_UP) && | |||||
(rt->rt_ifp->if_flags & IFF_UP) && | |||||
(rt->rt_ifp->if_drv_flags & IFF_DRV_RUNNING)) { | |||||
if (rt->rt_flags & RTF_GATEWAY) | |||||
dst = (struct sockaddr_in *)rt->rt_gateway; | |||||
} else | |||||
return (NULL); | |||||
if (((rt->rt_flags & RTF_REJECT) && | |||||
(rt->rt_expire == 0 || | |||||
time_uptime < rt->rt_expire)) || | |||||
rt->rt_ifp->if_link_state == LINK_STATE_DOWN) | |||||
return (NULL); | |||||
return ((struct sockaddr *)dst); | |||||
} | } | ||||
#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, nat64_stats_block *stats, void *logdata) | uint8_t code, uint16_t mtu, nat64_stats_block *stats, void *logdata) | ||||
{ | { | ||||
struct icmp *icmp; | struct icmp *icmp; | ||||
▲ Show 20 Lines • Show All 424 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, nat64_stats_block *stats, | struct in6_addr *daddr, uint16_t lport, nat64_stats_block *stats, | ||||
void *logdata) | void *logdata) | ||||
{ | { | ||||
struct route_in6 ro; | struct nhop6_basic nh; | ||||
struct ip6_hdr ip6; | struct ip6_hdr ip6; | ||||
struct ifnet *ifp; | struct sockaddr_in6 dst; | ||||
struct ip *ip; | struct ip *ip; | ||||
struct mbufq mq; | struct mbufq mq; | ||||
struct sockaddr *dst; | |||||
uint32_t mtu; | |||||
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 (ip->ip_ttl <= IPTTLDEC) { | ||||
Show All 21 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(stats, dropped); | NAT64STAT_INC(stats, dropped); | ||||
return (NAT64MFREE); | return (NAT64MFREE); | ||||
} | } | ||||
dst = nat64_find_route6(&ro, &ip6.ip6_dst, m); | dst.sin6_addr = ip6.ip6_dst; | ||||
if (dst == NULL) { | if (nat64_find_route6(&nh, &dst, m) != 0) { | ||||
FREE_ROUTE(&ro); | |||||
NAT64STAT_INC(stats, noroute6); | NAT64STAT_INC(stats, noroute6); | ||||
nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, | nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_HOST, 0, | ||||
stats, logdata); | stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
ifp = ro.ro_rt->rt_ifp; | if (nh.nh_mtu < plen + sizeof(ip6) && | ||||
if (ro.ro_rt->rt_mtu != 0) | (ip->ip_off & htons(IP_DF)) != 0) { | ||||
mtu = min(ro.ro_rt->rt_mtu, ifp->if_mtu); | |||||
else | |||||
mtu = ifp->if_mtu; | |||||
if (mtu < plen + sizeof(ip6) && (ip->ip_off & htons(IP_DF)) != 0) { | |||||
FREE_ROUTE(&ro); | |||||
nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, | nat64_icmp_reflect(m, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG, | ||||
FRAGSZ(mtu) + sizeof(struct ip), stats, logdata); | FRAGSZ(nh.nh_mtu) + sizeof(struct ip), 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; | ||||
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT | #ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT | ||||
ip6.ip6_hlim = ip->ip_ttl - IPTTLDEC; | ip6.ip6_hlim = ip->ip_ttl - IPTTLDEC; | ||||
#else | #else | ||||
Show All 19 Lines | if (lport != 0) { | ||||
struct udphdr *udp = UDP(mtodo(m, hlen)); | struct udphdr *udp = UDP(mtodo(m, hlen)); | ||||
*csum = cksum_adjust(*csum, udp->uh_dport, lport); | *csum = cksum_adjust(*csum, udp->uh_dport, lport); | ||||
udp->uh_dport = lport; | udp->uh_dport = lport; | ||||
} | } | ||||
*csum = cksum_add(*csum, ~nat64_cksum_convert(&ip6, ip)); | *csum = cksum_add(*csum, ~nat64_cksum_convert(&ip6, ip)); | ||||
break; | break; | ||||
case IPPROTO_ICMP: | case IPPROTO_ICMP: | ||||
m = nat64_icmp_translate(m, &ip6, lport, hlen, stats); | m = nat64_icmp_translate(m, &ip6, lport, hlen, stats); | ||||
if (m == NULL) { | if (m == NULL) /* stats already accounted */ | ||||
FREE_ROUTE(&ro); | |||||
/* 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(stats, &ip6, &mq, m, mtu, ip_id, ip_off); | nat64_fragment6(stats, &ip6, &mq, m, nh.nh_mtu, ip_id, ip_off); | ||||
while ((m = mbufq_dequeue(&mq)) != NULL) { | while ((m = mbufq_dequeue(&mq)) != NULL) { | ||||
if (nat64_output(ifp, m, dst, (struct route *)&ro, stats, | if (nat64_output(nh.nh_ifp, m, (struct sockaddr *)&dst, | ||||
logdata) != 0) | NULL, stats, logdata) != 0) | ||||
break; | break; | ||||
NAT64STAT_INC(stats, opcnt46); | NAT64STAT_INC(stats, opcnt46); | ||||
} | } | ||||
mbufq_drain(&mq); | mbufq_drain(&mq); | ||||
FREE_ROUTE(&ro); | |||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
int | int | ||||
nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport, | nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport, | ||||
nat64_stats_block *stats, void *logdata) | nat64_stats_block *stats, void *logdata) | ||||
{ | { | ||||
struct ip ip; | struct ip ip; | ||||
▲ Show 20 Lines • Show All 210 Lines • ▼ Show 20 Lines | fail: | ||||
NAT64STAT_INC(stats, dropped); | NAT64STAT_INC(stats, dropped); | ||||
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, | ||||
nat64_stats_block *stats, void *logdata) | nat64_stats_block *stats, void *logdata) | ||||
{ | { | ||||
struct route ro; | |||||
struct ip ip; | struct ip ip; | ||||
struct ifnet *ifp; | struct nhop4_basic nh; | ||||
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; | ||||
struct sockaddr *dst; | |||||
uint16_t *csum; | uint16_t *csum; | ||||
uint32_t mtu; | |||||
int plen, hlen, proto; | int plen, hlen, proto; | ||||
/* | /* | ||||
* XXX: we expect ipfw_chk() did m_pullup() up to upper level | * XXX: we expect ipfw_chk() did m_pullup() up to upper level | ||||
* protocol's headers. Also we skip some checks, that ip6_input(), | * protocol's headers. Also we skip some checks, that ip6_input(), | ||||
* ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. | * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. | ||||
*/ | */ | ||||
ip6 = mtod(m, struct ip6_hdr *); | ip6 = mtod(m, struct ip6_hdr *); | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, | ||||
icmp6 = NULL; /* Make gcc happy */ | icmp6 = NULL; /* Make gcc happy */ | ||||
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, | ||||
stats, logdata)); | stats, logdata)); | ||||
} | } | ||||
dst = nat64_find_route4(&ro, ip.ip_dst.s_addr, m); | dst.sin_addr.s_addr = ip.ip_dst.s_addr; | ||||
if (dst == NULL) { | if (nat64_find_route4(&nh, &dst, m) != 0) { | ||||
FREE_ROUTE(&ro); | |||||
NAT64STAT_INC(stats, noroute4); | NAT64STAT_INC(stats, noroute4); | ||||
nat64_icmp6_reflect(m, ICMP6_DST_UNREACH, | nat64_icmp6_reflect(m, ICMP6_DST_UNREACH, | ||||
ICMP6_DST_UNREACH_NOROUTE, 0, stats, logdata); | ICMP6_DST_UNREACH_NOROUTE, 0, stats, logdata); | ||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||
if (nh.nh_mtu < plen + sizeof(ip)) { | |||||
ifp = ro.ro_rt->rt_ifp; | nat64_icmp6_reflect(m, ICMP6_PACKET_TOO_BIG, 0, nh.nh_mtu, | ||||
if (ro.ro_rt->rt_mtu != 0) | stats, logdata); | ||||
mtu = min(ro.ro_rt->rt_mtu, ifp->if_mtu); | |||||
else | |||||
mtu = ifp->if_mtu; | |||||
if (mtu < plen + sizeof(ip)) { | |||||
FREE_ROUTE(&ro); | |||||
nat64_icmp6_reflect(m, ICMP6_PACKET_TOO_BIG, 0, mtu, 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; | ||||
if (aport != 0) { | if (aport != 0) { | ||||
Show All 13 Lines | case IPPROTO_UDP: | ||||
*csum = cksum_add(*csum, nat64_cksum_convert(ip6, &ip)); | *csum = cksum_add(*csum, nat64_cksum_convert(ip6, &ip)); | ||||
break; | break; | ||||
case IPPROTO_ICMPV6: | case IPPROTO_ICMPV6: | ||||
/* Checksum in ICMPv6 covers pseudo header */ | /* Checksum in ICMPv6 covers pseudo header */ | ||||
csum = &icmp6->icmp6_cksum; | csum = &icmp6->icmp6_cksum; | ||||
*csum = cksum_add(*csum, in6_cksum_pseudo(ip6, plen, | *csum = cksum_add(*csum, in6_cksum_pseudo(ip6, plen, | ||||
IPPROTO_ICMPV6, 0)); | IPPROTO_ICMPV6, 0)); | ||||
/* Convert ICMPv6 types to ICMP */ | /* Convert ICMPv6 types to ICMP */ | ||||
mtu = *(uint16_t *)icmp6; /* save old word for cksum_adjust */ | proto = *(uint16_t *)icmp6; /* save old word for cksum_adjust */ | ||||
if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) | if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST) | ||||
icmp6->icmp6_type = ICMP_ECHO; | icmp6->icmp6_type = ICMP_ECHO; | ||||
else /* ICMP6_ECHO_REPLY */ | else /* ICMP6_ECHO_REPLY */ | ||||
icmp6->icmp6_type = ICMP_ECHOREPLY; | icmp6->icmp6_type = ICMP_ECHOREPLY; | ||||
*csum = cksum_adjust(*csum, (uint16_t)mtu, *(uint16_t *)icmp6); | *csum = cksum_adjust(*csum, (uint16_t)proto, | ||||
*(uint16_t *)icmp6); | |||||
if (aport != 0) { | if (aport != 0) { | ||||
uint16_t old_id = icmp6->icmp6_id; | uint16_t old_id = icmp6->icmp6_id; | ||||
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 (nat64_output(ifp, m, dst, &ro, stats, logdata) == 0) | if (nat64_output(nh.nh_ifp, m, (struct sockaddr *)&dst, NULL, | ||||
stats, logdata) == 0) | |||||
NAT64STAT_INC(stats, opcnt64); | NAT64STAT_INC(stats, opcnt64); | ||||
FREE_ROUTE(&ro); | |||||
return (NAT64RETURN); | return (NAT64RETURN); | ||||
} | } | ||||