From f12c626daaef8cb7b5368e5911f218e694e34003 Mon Sep 17 00:00:00 2001 From: ae Date: Sun, 14 Apr 2019 12:34:30 +0000 Subject: [PATCH] MFC r345262: Modify struct nat64_config. Add second IPv6 prefix to generic config structure and rename another fields to conform to RFC6877. Now it contains two prefixes and length: PLAT is provider-side translator that translates N:1 global IPv6 addresses to global IPv4 addresses. CLAT is customer-side translator (XLAT) that algorithmically translates 1:1 IPv4 addresses to global IPv6 addresses. Use PLAT prefix in stateless (nat64stl) and stateful (nat64lsn) translators. Modify nat64_extract_ip4() and nat64_embed_ip4() functions to accept prefix length and use plat_plen to specify prefix length. Retire net.inet.ip.fw.nat64_allow_private sysctl variable. Add NAT64_ALLOW_PRIVATE flag and use "allow_private" config option to configure this ability separately for each NAT64 instance. Obtained from: Yandex LLC Sponsored by: Yandex LLC --- sbin/ipfw/ipfw.8 | 28 +++--- sbin/ipfw/ipfw2.h | 2 + sbin/ipfw/nat64lsn.c | 16 +++ sbin/ipfw/nat64stl.c | 16 +++ sys/netinet6/ip_fw_nat64.h | 10 +- sys/netpfil/ipfw/ip_fw_pfil.c | 4 +- sys/netpfil/ipfw/nat64/ip_fw_nat64.c | 4 - sys/netpfil/ipfw/nat64/ip_fw_nat64.h | 2 - sys/netpfil/ipfw/nat64/nat64_translate.c | 115 +++++++++++++--------- sys/netpfil/ipfw/nat64/nat64_translate.h | 38 ++++--- sys/netpfil/ipfw/nat64/nat64lsn.c | 8 +- sys/netpfil/ipfw/nat64/nat64lsn.h | 2 +- sys/netpfil/ipfw/nat64/nat64lsn_control.c | 12 +-- sys/netpfil/ipfw/nat64/nat64stl.c | 14 ++- sys/netpfil/ipfw/nat64/nat64stl.h | 3 +- sys/netpfil/ipfw/nat64/nat64stl_control.c | 12 +-- 16 files changed, 183 insertions(+), 103 deletions(-) diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8 index 58bf4ddf6a1..f1552554d04 100644 --- a/sbin/ipfw/ipfw.8 +++ b/sbin/ipfw/ipfw.8 @@ -1,7 +1,7 @@ .\" .\" $FreeBSD$ .\" -.Dd March 1, 2019 +.Dd March 18, 2019 .Dt IPFW 8 .Os .Sh NAME @@ -3408,6 +3408,14 @@ With you are able to see each handled packet before and after translation. .It Cm -log Turn off logging of all handled packets via BPF. +.It Cm allow_private +Turn on processing private IPv4 addresses. By default IPv6 packets with +destinations mapped to private address ranges defined by RFC1918 are not +processed. +.It Cm -allow_private +Turn off private address handling in +.Nm nat64 +instance. .El .Pp To inspect a states table of stateful NAT64 the following command can be used: @@ -3455,6 +3463,14 @@ Turn on logging of all handled packets via BPF through interface. .It Cm -log Turn off logging of all handled packets via BPF. +.It Cm allow_private +Turn on processing private IPv4 addresses. By default IPv6 packets with +destinations mapped to private address ranges defined by RFC1918 are not +processed. +.It Cm -allow_private +Turn off private address handling in +.Nm nat64 +instance. .El .Pp Note that the behavior of stateless translator with respect to not matched @@ -3934,16 +3950,6 @@ Default is no. Controls whether bridged packets are passed to .Nm . Default is no. -.It Va net.inet.ip.fw.nat64_allow_private : No 0 -Defines how -.Nm nat64 -handles private IPv4 addresses: -.Bl -tag -width indent -.It Cm 0 -Packets with private IPv4 will not be handled by translator -.It Cm 1 -Translator will accept and process packets with private IPv4 addresses. -.El .It Va net.inet.ip.fw.nat64_debug : No 0 Controls debugging messages produced by .Nm ipfw_nat64 diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h index 13bf40ae8c8..7596cdf8653 100644 --- a/sbin/ipfw/ipfw2.h +++ b/sbin/ipfw/ipfw2.h @@ -288,6 +288,8 @@ enum tokens { TOK_UDP_AGE, TOK_ICMP_AGE, TOK_LOGOFF, + TOK_PRIVATE, + TOK_PRIVATEOFF, /* NPTv6 tokens */ TOK_NPTV6, diff --git a/sbin/ipfw/nat64lsn.c b/sbin/ipfw/nat64lsn.c index a364f017855..776613c4718 100644 --- a/sbin/ipfw/nat64lsn.c +++ b/sbin/ipfw/nat64lsn.c @@ -377,6 +377,8 @@ static struct _s_x nat64newcmds[] = { { "icmp_age", TOK_ICMP_AGE }, { "log", TOK_LOG }, { "-log", TOK_LOGOFF }, + { "allow_private", TOK_PRIVATE }, + { "-allow_private", TOK_PRIVATEOFF }, { NULL, 0 } }; @@ -522,6 +524,12 @@ nat64lsn_create(const char *name, uint8_t set, int ac, char **av) case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; + case TOK_PRIVATE: + cfg->flags |= NAT64_ALLOW_PRIVATE; + break; + case TOK_PRIVATEOFF: + cfg->flags &= ~NAT64_ALLOW_PRIVATE; + break; } } @@ -627,6 +635,12 @@ nat64lsn_config(const char *name, uint8_t set, int ac, char **av) case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; + case TOK_PRIVATE: + cfg->flags |= NAT64_ALLOW_PRIVATE; + break; + case TOK_PRIVATEOFF: + cfg->flags &= ~NAT64_ALLOW_PRIVATE; + break; default: errx(EX_USAGE, "Can't change %s option", opt); } @@ -801,6 +815,8 @@ nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set) printf(" icmp_age %u", cfg->st_icmp_ttl); if (cfg->flags & NAT64_LOG) printf(" log"); + if (cfg->flags & NAT64_ALLOW_PRIVATE) + printf(" allow_private"); printf("\n"); return (0); } diff --git a/sbin/ipfw/nat64stl.c b/sbin/ipfw/nat64stl.c index 27653aa5b4d..867f3a6629f 100644 --- a/sbin/ipfw/nat64stl.c +++ b/sbin/ipfw/nat64stl.c @@ -196,6 +196,8 @@ static struct _s_x nat64newcmds[] = { { "prefix6", TOK_PREFIX6 }, { "log", TOK_LOG }, { "-log", TOK_LOGOFF }, + { "allow_private", TOK_PRIVATE }, + { "-allow_private", TOK_PRIVATEOFF }, { NULL, 0 } }; @@ -263,6 +265,12 @@ nat64stl_create(const char *name, uint8_t set, int ac, char *av[]) case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; + case TOK_PRIVATE: + cfg->flags |= NAT64_ALLOW_PRIVATE; + break; + case TOK_PRIVATEOFF: + cfg->flags &= ~NAT64_ALLOW_PRIVATE; + break; } } @@ -332,6 +340,12 @@ nat64stl_config(const char *name, uint8_t set, int ac, char **av) case TOK_LOGOFF: cfg->flags &= ~NAT64_LOG; break; + case TOK_PRIVATE: + cfg->flags |= NAT64_ALLOW_PRIVATE; + break; + case TOK_PRIVATEOFF: + cfg->flags &= ~NAT64_ALLOW_PRIVATE; + break; default: errx(EX_USAGE, "Can't change %s option", opt); } @@ -451,6 +465,8 @@ nat64stl_show_cb(ipfw_nat64stl_cfg *cfg, const char *name, uint8_t set) printf(" prefix6 %s/%u", abuf, cfg->plen6); if (cfg->flags & NAT64_LOG) printf(" log"); + if (cfg->flags & NAT64_ALLOW_PRIVATE) + printf(" allow_private"); printf("\n"); return (0); } diff --git a/sys/netinet6/ip_fw_nat64.h b/sys/netinet6/ip_fw_nat64.h index a5c38b2aa65..b179f30e91a 100644 --- a/sys/netinet6/ip_fw_nat64.h +++ b/sys/netinet6/ip_fw_nat64.h @@ -40,7 +40,7 @@ struct ipfw_nat64stl_stats { uint64_t noroute4; uint64_t noroute6; uint64_t noproto; /* Protocol not supported */ - uint64_t nomem; /* mbuf allocation filed */ + uint64_t nomem; /* mbuf allocation failed */ uint64_t dropped; /* dropped due to some errors */ }; @@ -53,7 +53,7 @@ struct ipfw_nat64lsn_stats { uint64_t noroute4; uint64_t noroute6; uint64_t noproto; /* Protocol not supported */ - uint64_t nomem; /* mbuf allocation filed */ + uint64_t nomem; /* mbuf allocation failed */ uint64_t dropped; /* dropped due to some errors */ uint64_t nomatch4; /* No addr/port match */ @@ -79,8 +79,10 @@ struct ipfw_nat64lsn_stats { uint64_t _reserved[4]; }; -#define NAT64_LOG 0x0001 /* Enable logging via BPF */ - +#define NAT64_LOG 0x0001 /* Enable logging via BPF */ +#define NAT64_ALLOW_PRIVATE 0x0002 /* Allow private IPv4 address + * translation + */ typedef struct _ipfw_nat64stl_cfg { char name[64]; /* NAT name */ ipfw_obj_ntlv ntlv6; /* object name tlv */ diff --git a/sys/netpfil/ipfw/ip_fw_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c index f9428c4fd33..d8be4acc56c 100644 --- a/sys/netpfil/ipfw/ip_fw_pfil.c +++ b/sys/netpfil/ipfw/ip_fw_pfil.c @@ -150,8 +150,8 @@ ipfw_check_packet(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir, ipfw = ipfw_chk(&args); *m0 = args.m; - KASSERT(*m0 != NULL || ipfw == IP_FW_DENY, ("%s: m0 is NULL", - __func__)); + KASSERT(*m0 != NULL || ipfw == IP_FW_DENY || + ipfw == IP_FW_NAT64, ("%s: m0 is NULL", __func__)); /* breaking out of the switch means drop */ switch (ipfw) { diff --git a/sys/netpfil/ipfw/nat64/ip_fw_nat64.c b/sys/netpfil/ipfw/nat64/ip_fw_nat64.c index 4093600cc98..3ed88afa539 100644 --- a/sys/netpfil/ipfw/nat64/ip_fw_nat64.c +++ b/sys/netpfil/ipfw/nat64/ip_fw_nat64.c @@ -51,14 +51,10 @@ __FBSDID("$FreeBSD$"); #include "nat64_translate.h" VNET_DEFINE(int, nat64_debug) = 0; -VNET_DEFINE(int, nat64_allow_private) = 0; SYSCTL_DECL(_net_inet_ip_fw); SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, nat64_debug, CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nat64_debug), 0, "Debug level for NAT64 module"); -SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, nat64_allow_private, - CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nat64_allow_private), 0, - "Allow use of non-global IPv4 addresses with NAT64"); static int sysctl_direct_output(SYSCTL_HANDLER_ARGS) diff --git a/sys/netpfil/ipfw/nat64/ip_fw_nat64.h b/sys/netpfil/ipfw/nat64/ip_fw_nat64.h index 30c46e17e27..15df086666b 100644 --- a/sys/netpfil/ipfw/nat64/ip_fw_nat64.h +++ b/sys/netpfil/ipfw/nat64/ip_fw_nat64.h @@ -41,9 +41,7 @@ #define DP_ALL 0xFFFF VNET_DECLARE(int, nat64_debug); -VNET_DECLARE(int, nat64_allow_private); #define V_nat64_debug VNET(nat64_debug) -#define V_nat64_allow_private VNET(nat64_allow_private) #if 0 #define NAT64NOINLINE __noinline diff --git a/sys/netpfil/ipfw/nat64/nat64_translate.c b/sys/netpfil/ipfw/nat64/nat64_translate.c index 2373b777fca..6b87b4861cb 100644 --- a/sys/netpfil/ipfw/nat64/nat64_translate.c +++ b/sys/netpfil/ipfw/nat64/nat64_translate.c @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include @@ -241,7 +242,7 @@ nat64_output_one(struct mbuf *m, struct nat64_counters *stats, void *logdata) * Returns zero on success, otherwise EINVAL. */ int -nat64_check_prefix6(const struct in6_addr *prefix, int length) +nat64_check_prefixlen(int length) { switch (length) { @@ -250,29 +251,40 @@ nat64_check_prefix6(const struct in6_addr *prefix, int length) case 48: case 56: case 64: - /* Well-known prefix has 96 prefix length */ - if (IN6_IS_ADDR_WKPFX(prefix)) - return (EINVAL); - /* FALLTHROUGH */ case 96: - /* Bits 64 to 71 must be set to zero */ - if (prefix->__u6_addr.__u6_addr8[8] != 0) - return (EINVAL); - /* Some extra checks */ - if (IN6_IS_ADDR_MULTICAST(prefix) || - IN6_IS_ADDR_UNSPECIFIED(prefix) || - IN6_IS_ADDR_LOOPBACK(prefix)) - return (EINVAL); return (0); } return (EINVAL); } +int +nat64_check_prefix6(const struct in6_addr *prefix, int length) +{ + + if (nat64_check_prefixlen(length) != 0) + return (EINVAL); + + /* Well-known prefix has 96 prefix length */ + if (IN6_IS_ADDR_WKPFX(prefix) && length != 96) + return (EINVAL); + + /* Bits 64 to 71 must be set to zero */ + if (prefix->__u6_addr.__u6_addr8[8] != 0) + return (EINVAL); + + /* Some extra checks */ + if (IN6_IS_ADDR_MULTICAST(prefix) || + IN6_IS_ADDR_UNSPECIFIED(prefix) || + IN6_IS_ADDR_LOOPBACK(prefix)) + return (EINVAL); + return (0); +} + int nat64_check_private_ip4(const struct nat64_config *cfg, in_addr_t ia) { - if (V_nat64_allow_private) + if (cfg->flags & NAT64_ALLOW_PRIVATE) return (0); /* WKPFX must not be used to represent non-global IPv4 addresses */ @@ -301,29 +313,34 @@ nat64_check_private_ip4(const struct nat64_config *cfg, in_addr_t ia) return (0); } +/* + * Embed @ia IPv4 address into @ip6 IPv6 address. + * Place to embedding determined from prefix length @plen. + */ void -nat64_embed_ip4(const struct nat64_config *cfg, in_addr_t ia, - struct in6_addr *ip6) +nat64_embed_ip4(struct in6_addr *ip6, int plen, in_addr_t ia) { - /* assume the prefix6 is properly filled with zeros */ - bcopy(&cfg->prefix6, ip6, sizeof(*ip6)); - switch (cfg->plen6) { + switch (plen) { case 32: case 96: - ip6->s6_addr32[cfg->plen6 / 32] = ia; + ip6->s6_addr32[plen / 32] = ia; break; case 40: case 48: case 56: + /* + * Preserve prefix bits. + * Since suffix bits should be zero and reserved for future + * use, we just overwrite the whole word, where they are. + */ + ip6->s6_addr32[1] &= 0xffffffff << (32 - plen % 32); #if BYTE_ORDER == BIG_ENDIAN - ip6->s6_addr32[1] = cfg->prefix6.s6_addr32[1] | - (ia >> (cfg->plen6 % 32)); - ip6->s6_addr32[2] = ia << (24 - cfg->plen6 % 32); + ip6->s6_addr32[1] |= ia >> (plen % 32); + ip6->s6_addr32[2] = ia << (24 - plen % 32); #elif BYTE_ORDER == LITTLE_ENDIAN - ip6->s6_addr32[1] = cfg->prefix6.s6_addr32[1] | - (ia << (cfg->plen6 % 32)); - ip6->s6_addr32[2] = ia >> (24 - cfg->plen6 % 32); + ip6->s6_addr32[1] |= ia << (plen % 32); + ip6->s6_addr32[2] = ia >> (24 - plen % 32); #endif break; case 64: @@ -336,13 +353,18 @@ nat64_embed_ip4(const struct nat64_config *cfg, in_addr_t ia, #endif break; default: - panic("Wrong plen6"); + panic("Wrong plen: %d", plen); }; + /* + * Bits 64 to 71 of the address are reserved for compatibility + * with the host identifier format defined in the IPv6 addressing + * architecture [RFC4291]. These bits MUST be set to zero. + */ ip6->s6_addr8[8] = 0; } in_addr_t -nat64_extract_ip4(const struct nat64_config *cfg, const struct in6_addr *ip6) +nat64_extract_ip4(const struct in6_addr *ip6, int plen) { in_addr_t ia; @@ -353,7 +375,7 @@ nat64_extract_ip4(const struct nat64_config *cfg, const struct in6_addr *ip6) * The suffix bits are reserved for future extensions and SHOULD * be set to zero. */ - switch (cfg->plen6) { + switch (plen) { case 32: if (ip6->s6_addr32[3] != 0 || ip6->s6_addr32[2] != 0) goto badip6; @@ -377,20 +399,20 @@ nat64_extract_ip4(const struct nat64_config *cfg, const struct in6_addr *ip6) (ip6->s6_addr32[3] & htonl(0x00ffffff)) != 0) goto badip6; }; - switch (cfg->plen6) { + switch (plen) { case 32: case 96: - ia = ip6->s6_addr32[cfg->plen6 / 32]; + ia = ip6->s6_addr32[plen / 32]; break; case 40: case 48: case 56: #if BYTE_ORDER == BIG_ENDIAN - ia = (ip6->s6_addr32[1] << (cfg->plen6 % 32)) | - (ip6->s6_addr32[2] >> (24 - cfg->plen6 % 32)); + ia = (ip6->s6_addr32[1] << (plen % 32)) | + (ip6->s6_addr32[2] >> (24 - plen % 32)); #elif BYTE_ORDER == LITTLE_ENDIAN - ia = (ip6->s6_addr32[1] >> (cfg->plen6 % 32)) | - (ip6->s6_addr32[2] << (24 - cfg->plen6 % 32)); + ia = (ip6->s6_addr32[1] >> (plen % 32)) | + (ip6->s6_addr32[2] << (24 - plen % 32)); #endif break; case 64: @@ -403,12 +425,9 @@ nat64_extract_ip4(const struct nat64_config *cfg, const struct in6_addr *ip6) default: return (0); }; - if (nat64_check_ip4(ia) != 0 || - nat64_check_private_ip4(cfg, ia) != 0) - goto badip4; + if (nat64_check_ip4(ia) == 0) + return (ia); - return (ia); -badip4: DPRINTF(DP_GENERIC | DP_DROPS, "invalid destination address: %08x", ia); return (0); @@ -435,7 +454,7 @@ nat64_extract_ip4(const struct nat64_config *cfg, const struct in6_addr *ip6) * IPv6 to IPv4: HC' = cksum_add(HC, result) * IPv4 to IPv6: HC' = cksum_add(HC, ~result) */ -static NAT64NOINLINE uint16_t +static uint16_t nat64_cksum_convert(struct ip6_hdr *ip6, struct ip *ip) { uint32_t sum; @@ -455,7 +474,7 @@ nat64_cksum_convert(struct ip6_hdr *ip6, struct ip *ip) return (sum); } -static NAT64NOINLINE void +static void nat64_init_ip4hdr(const struct ip6_hdr *ip6, const struct ip6_frag *frag, uint16_t plen, uint8_t proto, struct ip *ip) { @@ -1025,9 +1044,11 @@ nat64_icmp_translate(struct mbuf *m, struct ip6_hdr *ip6, uint16_t icmpid, /* Construct new inner IPv6 header */ eip6 = mtodo(n, offset + sizeof(struct icmp6_hdr)); eip6->ip6_src = ip6->ip6_dst; - /* Use the fact that we have single /96 prefix for IPv4 map */ + + /* Use the same prefix that we have in outer header */ eip6->ip6_dst = ip6->ip6_src; - nat64_embed_ip4(cfg, ip.ip_dst.s_addr, &eip6->ip6_dst); + MPASS(cfg->flags & NAT64_PLATPFX); + nat64_embed_ip4(&eip6->ip6_dst, cfg->plat_plen, ip.ip_dst.s_addr); eip6->ip6_flow = htonl(ip.ip_tos << 20); eip6->ip6_vfc |= IPV6_VERSION; @@ -1450,7 +1471,9 @@ nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport, /* Now we need to make a fake IPv4 packet to generate ICMP message */ ip.ip_dst.s_addr = aaddr; - ip.ip_src.s_addr = nat64_extract_ip4(cfg, &ip6i->ip6_src); + ip.ip_src.s_addr = nat64_extract_ip4(&ip6i->ip6_src, cfg->plat_plen); + if (ip.ip_src.s_addr == 0) + goto fail; /* XXX: Make fake ulp header */ if (V_nat64out == &nat64_direct) /* init_ip4hdr will decrement it */ ip6i->ip6_hlim += IPV6_HLIMDEC; @@ -1503,7 +1526,7 @@ nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, return (NAT64MFREE); } - ip.ip_dst.s_addr = nat64_extract_ip4(cfg, &ip6->ip6_dst); + ip.ip_dst.s_addr = nat64_extract_ip4(&ip6->ip6_dst, cfg->plat_plen); if (ip.ip_dst.s_addr == 0) { NAT64STAT_INC(&cfg->stats, dropped); return (NAT64MFREE); diff --git a/sys/netpfil/ipfw/nat64/nat64_translate.h b/sys/netpfil/ipfw/nat64/nat64_translate.h index ca7b3537459..2aac8904d2c 100644 --- a/sys/netpfil/ipfw/nat64/nat64_translate.h +++ b/sys/netpfil/ipfw/nat64/nat64_translate.h @@ -46,12 +46,12 @@ struct nat64_stats { * unsupported/etc. */ - uint64_t jrequests; /* number of jobs requests queued */ - uint64_t jcalls; /* number of jobs handler calls */ - uint64_t jhostsreq; /* number of hosts requests */ - uint64_t jportreq; - uint64_t jhostfails; - uint64_t jportfails; + uint64_t jrequests; /* jobs requests queued */ + uint64_t jcalls; /* jobs handler calls */ + uint64_t jhostsreq; /* hosts requests */ + uint64_t jportreq; /* PG allocation requests */ + uint64_t jhostfails; /* hosts requests failed */ + uint64_t jportfails; /* PG allocation failed */ uint64_t jmaxlen; uint64_t jnomem; uint64_t jreinjected; @@ -85,11 +85,24 @@ struct nat64_counters { #define NAT64RETURN 1 #define NAT64MFREE -1 +/* + * According to RFC6877: + * PLAT is provider-side translator (XLAT) that translates N:1 global + * IPv6 addresses to global IPv4 addresses, and vice versa. + * + * CLAT is customer-side translator (XLAT) that algorithmically + * translates 1:1 private IPv4 addresses to global IPv6 addresses, + * and vice versa. + */ struct nat64_config { + struct in6_addr clat_prefix; + struct in6_addr plat_prefix; uint32_t flags; -#define NAT64_WKPFX 0x00010000 /* prefix6 is WKPFX */ - struct in6_addr prefix6; - uint8_t plen6; +#define NAT64_WKPFX 0x00010000 /* prefix is well-known */ +#define NAT64_CLATPFX 0x00020000 /* dst prefix is configured */ +#define NAT64_PLATPFX 0x00040000 /* src prefix is configured */ + uint8_t clat_plen; + uint8_t plat_plen; struct nat64_counters stats; }; @@ -128,6 +141,7 @@ nat64_check_ip4(in_addr_t ia) (a)->s6_addr32[1] == 0 && (a)->s6_addr32[2] == 0) int nat64_check_private_ip4(const struct nat64_config *cfg, in_addr_t ia); +int nat64_check_prefixlen(int length); int nat64_check_prefix6(const struct in6_addr *prefix, int length); int nat64_getlasthdr(struct mbuf *m, int *offset); int nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr, @@ -137,10 +151,8 @@ int nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport, struct nat64_config *cfg, void *logdata); int nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport, struct nat64_config *cfg, void *logdata); -void nat64_embed_ip4(const struct nat64_config *cfg, in_addr_t ia, - struct in6_addr *ip6); -in_addr_t nat64_extract_ip4(const struct nat64_config *cfg, - const struct in6_addr *ip6); +void nat64_embed_ip4(struct in6_addr *ip6, int plen, in_addr_t ia); +in_addr_t nat64_extract_ip4(const struct in6_addr *ip6, int plen); void nat64_set_output_method(int); int nat64_get_output_method(void); diff --git a/sys/netpfil/ipfw/nat64/nat64lsn.c b/sys/netpfil/ipfw/nat64/nat64lsn.c index a5dbb838135..6fc53cfba6d 100644 --- a/sys/netpfil/ipfw/nat64/nat64lsn.c +++ b/sys/netpfil/ipfw/nat64/nat64lsn.c @@ -407,7 +407,7 @@ nat64lsn_translate4(struct nat64lsn_cfg *cfg, const struct ipfw_flow_id *f_id, } else logdata = NULL; - nat64_embed_ip4(&cfg->base, htonl(f_id->src_ip), &src6); + nat64_embed_ip4(&src6, cfg->base.plat_plen, htonl(f_id->src_ip)); ret = nat64_do_handle_ip4(*pm, &src6, &nh->addr, lport, &cfg->base, logdata); @@ -1481,8 +1481,10 @@ nat64lsn_translate6(struct nat64lsn_cfg *cfg, struct ipfw_flow_id *f_id, return (nat64lsn_request_host(cfg, f_id, pm)); /* Fill-in on-stack state structure */ - kst.u.s.faddr = nat64_extract_ip4(&cfg->base, &f_id->dst_ip6); - if (kst.u.s.faddr == 0) { + kst.u.s.faddr = nat64_extract_ip4(&f_id->dst_ip6, + cfg->base.plat_plen); + if (kst.u.s.faddr == 0 || + nat64_check_private_ip4(&cfg->base, kst.u.s.faddr) != 0) { NAT64STAT_INC(&cfg->base.stats, dropped); goto drop; } diff --git a/sys/netpfil/ipfw/nat64/nat64lsn.h b/sys/netpfil/ipfw/nat64/nat64lsn.h index 59ed1cc841b..6491ba0096b 100644 --- a/sys/netpfil/ipfw/nat64/nat64lsn.h +++ b/sys/netpfil/ipfw/nat64/nat64lsn.h @@ -216,7 +216,7 @@ struct nat64lsn_cfg { uint16_t st_icmp_ttl; /* ICMP expire */ uint32_t protochunks[NAT_MAX_PROTO];/* Number of chunks used */ struct nat64_config base; -#define NAT64LSN_FLAGSMASK (NAT64_LOG) +#define NAT64LSN_FLAGSMASK (NAT64_LOG | NAT64_ALLOW_PRIVATE) struct callout periodic; struct callout jcallout; diff --git a/sys/netpfil/ipfw/nat64/nat64lsn_control.c b/sys/netpfil/ipfw/nat64/nat64lsn_control.c index a1ef8da6ba5..d998ce55b1d 100644 --- a/sys/netpfil/ipfw/nat64/nat64lsn_control.c +++ b/sys/netpfil/ipfw/nat64/nat64lsn_control.c @@ -164,10 +164,10 @@ nat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME; cfg->no.set = uc->set; - cfg->base.prefix6 = uc->prefix6; - cfg->base.plen6 = uc->plen6; - cfg->base.flags = uc->flags & NAT64LSN_FLAGSMASK; - if (IN6_IS_ADDR_WKPFX(&cfg->base.prefix6)) + cfg->base.plat_prefix = uc->prefix6; + cfg->base.plat_plen = uc->plen6; + cfg->base.flags = (uc->flags & NAT64LSN_FLAGSMASK) | NAT64_PLATPFX; + if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) cfg->base.flags |= NAT64_WKPFX; cfg->prefix4 = addr4; @@ -324,9 +324,9 @@ nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg, uc->st_udp_ttl = cfg->st_udp_ttl; uc->st_icmp_ttl = cfg->st_icmp_ttl; uc->prefix4.s_addr = htonl(cfg->prefix4); - uc->prefix6 = cfg->base.prefix6; + uc->prefix6 = cfg->base.plat_prefix; uc->plen4 = cfg->plen4; - uc->plen6 = cfg->base.plen6; + uc->plen6 = cfg->base.plat_plen; uc->set = cfg->no.set; strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); } diff --git a/sys/netpfil/ipfw/nat64/nat64stl.c b/sys/netpfil/ipfw/nat64/nat64stl.c index 8770b94623d..701bd9e8b85 100644 --- a/sys/netpfil/ipfw/nat64/nat64stl.c +++ b/sys/netpfil/ipfw/nat64/nat64stl.c @@ -99,7 +99,9 @@ nat64stl_handle_ip4(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, daddr = TARG_VAL(chain, tablearg, nh6); if (nat64_check_ip6(&daddr) != 0) return (NAT64MFREE); - nat64_embed_ip4(&cfg->base, ip->ip_src.s_addr, &saddr); + + saddr = cfg->base.plat_prefix; + nat64_embed_ip4(&saddr, cfg->base.plat_plen, ip->ip_src.s_addr); if (cfg->base.flags & NAT64_LOG) { logdata = &loghdr; nat64stl_log(logdata, m, AF_INET, cfg->no.kidx); @@ -118,7 +120,10 @@ nat64stl_handle_ip6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, uint32_t aaddr; aaddr = htonl(TARG_VAL(chain, tablearg, nh4)); - + if (nat64_check_private_ip4(&cfg->base, aaddr) != 0) { + NAT64STAT_INC(&cfg->base.stats, dropped); + return (NAT64MFREE); + } /* * NOTE: we expect ipfw_chk() did m_pullup() up to upper level * protocol's headers. Also we skip some checks, that ip6_input(), @@ -126,7 +131,8 @@ nat64stl_handle_ip6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, */ ip6 = mtod(m, struct ip6_hdr *); /* Check ip6_dst matches configured prefix */ - if (bcmp(&ip6->ip6_dst, &cfg->base.prefix6, cfg->base.plen6 / 8) != 0) + if (memcmp(&ip6->ip6_dst, &cfg->base.plat_prefix, + cfg->base.plat_plen / 8) != 0) return (NAT64SKIP); if (cfg->base.flags & NAT64_LOG) { @@ -254,7 +260,7 @@ ipfw_nat64stl(struct ip_fw_chain *chain, struct ip_fw_args *args, if (ret == NAT64MFREE) m_freem(args->m); args->m = NULL; - return (IP_FW_DENY); + return (IP_FW_NAT64); } diff --git a/sys/netpfil/ipfw/nat64/nat64stl.h b/sys/netpfil/ipfw/nat64/nat64stl.h index 42d70e8905a..2df771e7d62 100644 --- a/sys/netpfil/ipfw/nat64/nat64stl.h +++ b/sys/netpfil/ipfw/nat64/nat64stl.h @@ -43,7 +43,8 @@ struct nat64stl_cfg { #define NAT64STL_KIDX 0x0100 #define NAT64STL_46T 0x0200 #define NAT64STL_64T 0x0400 -#define NAT64STL_FLAGSMASK (NAT64_LOG) /* flags to pass to userland */ + /* flags to pass to userland */ +#define NAT64STL_FLAGSMASK (NAT64_LOG | NAT64_ALLOW_PRIVATE) char name[64]; }; diff --git a/sys/netpfil/ipfw/nat64/nat64stl_control.c b/sys/netpfil/ipfw/nat64/nat64stl_control.c index 307c6c3ec45..1762ebde864 100644 --- a/sys/netpfil/ipfw/nat64/nat64stl_control.c +++ b/sys/netpfil/ipfw/nat64/nat64stl_control.c @@ -99,8 +99,8 @@ nat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg, { struct named_object *no; - uc->prefix6 = cfg->base.prefix6; - uc->plen6 = cfg->base.plen6; + uc->prefix6 = cfg->base.plat_prefix; + uc->plen6 = cfg->base.plat_plen; uc->flags = cfg->base.flags & NAT64STL_FLAGSMASK; uc->set = cfg->no.set; strlcpy(uc->name, cfg->no.name, sizeof(uc->name)); @@ -206,10 +206,10 @@ nat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3, IPFW_UH_RUNLOCK(ch); cfg = nat64stl_alloc_config(uc->name, uc->set); - cfg->base.prefix6 = uc->prefix6; - cfg->base.plen6 = uc->plen6; - cfg->base.flags = uc->flags & NAT64STL_FLAGSMASK; - if (IN6_IS_ADDR_WKPFX(&cfg->base.prefix6)) + cfg->base.plat_prefix = uc->prefix6; + cfg->base.plat_plen = uc->plen6; + cfg->base.flags = (uc->flags & NAT64STL_FLAGSMASK) | NAT64_PLATPFX; + if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix)) cfg->base.flags |= NAT64_WKPFX; IPFW_UH_WLOCK(ch); -- 2.45.0