nat64stl.c revision 346210
155714Skris/*- 255714Skris * Copyright (c) 2015-2016 Yandex LLC 355714Skris * Copyright (c) 2015-2016 Andrey V. Elsukov <ae@FreeBSD.org> 455714Skris * All rights reserved. 555714Skris * 655714Skris * Redistribution and use in source and binary forms, with or without 755714Skris * modification, are permitted provided that the following conditions 855714Skris * are met: 955714Skris * 1055714Skris * 1. Redistributions of source code must retain the above copyright 1155714Skris * notice, this list of conditions and the following disclaimer. 1255714Skris * 2. Redistributions in binary form must reproduce the above copyright 1355714Skris * notice, this list of conditions and the following disclaimer in the 1455714Skris * documentation and/or other materials provided with the distribution. 1555714Skris * 1655714Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1755714Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1855714Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1955714Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2055714Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2155714Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2255714Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2355714Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2455714Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2555714Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2655714Skris */ 2755714Skris 2855714Skris#include <sys/cdefs.h> 2955714Skris__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64stl.c 346210 2019-04-14 12:34:30Z ae $"); 3055714Skris 3155714Skris#include <sys/param.h> 3255714Skris#include <sys/systm.h> 3355714Skris#include <sys/counter.h> 3455714Skris#include <sys/kernel.h> 3555714Skris#include <sys/lock.h> 3655714Skris#include <sys/mbuf.h> 3755714Skris#include <sys/module.h> 3855714Skris#include <sys/rmlock.h> 3955714Skris#include <sys/rwlock.h> 4055714Skris#include <sys/socket.h> 4155714Skris#include <sys/sysctl.h> 4255714Skris 4355714Skris#include <net/if.h> 4455714Skris#include <net/if_var.h> 4555714Skris#include <net/if_pflog.h> 4655714Skris#include <net/pfil.h> 4755714Skris 4855714Skris#include <netinet/in.h> 4955714Skris#include <netinet/ip.h> 5055714Skris#include <netinet/ip_icmp.h> 5155714Skris#include <netinet/ip_var.h> 5255714Skris#include <netinet/ip_fw.h> 5355714Skris#include <netinet/ip6.h> 5455714Skris#include <netinet/icmp6.h> 5555714Skris#include <netinet6/ip_fw_nat64.h> 5655714Skris 5755714Skris#include <netpfil/ipfw/ip_fw_private.h> 5855714Skris#include <netpfil/pf/pf.h> 5955714Skris 6055714Skris#include "nat64stl.h" 6155714Skris 6255714Skris#define NAT64_LOOKUP(chain, cmd) \ 6355714Skris (struct nat64stl_cfg *)SRV_OBJECT((chain), (cmd)->arg1) 6455714Skris 6555714Skrisstatic void 6655714Skrisnat64stl_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, 6755714Skris uint32_t kidx) 6859191Skris{ 6955714Skris static uint32_t pktid = 0; 70279264Sdelphij 71238405Sjkim memset(plog, 0, sizeof(*plog)); 72279264Sdelphij plog->length = PFLOG_REAL_HDRLEN; 73109998Smarkm plog->af = family; 7455714Skris plog->action = PF_NAT; 7555714Skris plog->dir = PF_IN; 7655714Skris plog->rulenr = htonl(kidx); 7755714Skris pktid++; 7855714Skris plog->subrulenr = htonl(pktid); 7955714Skris plog->ruleset[0] = '\0'; 8055714Skris strlcpy(plog->ifname, "NAT64STL", sizeof(plog->ifname)); 8155714Skris ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); 8255714Skris} 8355714Skris 84109998Smarkmstatic int 85109998Smarkmnat64stl_handle_ip4(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, 86109998Smarkm struct mbuf *m, uint32_t tablearg) 87109998Smarkm{ 88109998Smarkm struct pfloghdr loghdr, *logdata; 89109998Smarkm struct in6_addr saddr, daddr; 90109998Smarkm struct ip *ip; 91109998Smarkm 92109998Smarkm ip = mtod(m, struct ip*); 93109998Smarkm if (nat64_check_ip4(ip->ip_src.s_addr) != 0 || 94109998Smarkm nat64_check_ip4(ip->ip_dst.s_addr) != 0 || 95109998Smarkm nat64_check_private_ip4(&cfg->base, ip->ip_src.s_addr) != 0 || 96109998Smarkm nat64_check_private_ip4(&cfg->base, ip->ip_dst.s_addr) != 0) 97109998Smarkm return (NAT64SKIP); 98109998Smarkm 99109998Smarkm daddr = TARG_VAL(chain, tablearg, nh6); 100109998Smarkm if (nat64_check_ip6(&daddr) != 0) 101109998Smarkm return (NAT64MFREE); 10259191Skris 10359191Skris saddr = cfg->base.plat_prefix; 10455714Skris nat64_embed_ip4(&saddr, cfg->base.plat_plen, ip->ip_src.s_addr); 10555714Skris if (cfg->base.flags & NAT64_LOG) { 10659191Skris logdata = &loghdr; 107109998Smarkm nat64stl_log(logdata, m, AF_INET, cfg->no.kidx); 10855714Skris } else 10955714Skris logdata = NULL; 11055714Skris return (nat64_do_handle_ip4(m, &saddr, &daddr, 0, &cfg->base, 11155714Skris logdata)); 112109998Smarkm} 113109998Smarkm 11459191Skrisstatic int 11559191Skrisnat64stl_handle_ip6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, 11659191Skris struct mbuf *m, uint32_t tablearg) 117142425Snectar{ 11855714Skris struct pfloghdr loghdr, *logdata; 119238405Sjkim struct ip6_hdr *ip6; 120238405Sjkim uint32_t aaddr; 121238405Sjkim 122238405Sjkim aaddr = htonl(TARG_VAL(chain, tablearg, nh4)); 12359191Skris if (nat64_check_private_ip4(&cfg->base, aaddr) != 0) { 12455714Skris NAT64STAT_INC(&cfg->base.stats, dropped); 125160814Ssimon return (NAT64MFREE); 12655714Skris } 12755714Skris /* 128100928Snectar * NOTE: we expect ipfw_chk() did m_pullup() up to upper level 129100928Snectar * protocol's headers. Also we skip some checks, that ip6_input(), 130111147Snectar * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. 131109998Smarkm */ 132111147Snectar ip6 = mtod(m, struct ip6_hdr *); 133142425Snectar /* Check ip6_dst matches configured prefix */ 134194206Ssimon if (memcmp(&ip6->ip6_dst, &cfg->base.plat_prefix, 13555714Skris cfg->base.plat_plen / 8) != 0) 13655714Skris return (NAT64SKIP); 13755714Skris 13855714Skris if (cfg->base.flags & NAT64_LOG) { 13955714Skris logdata = &loghdr; 14055714Skris nat64stl_log(logdata, m, AF_INET6, cfg->no.kidx); 14155714Skris } else 142109998Smarkm logdata = NULL; 143109998Smarkm return (nat64_do_handle_ip6(m, aaddr, 0, &cfg->base, logdata)); 144109998Smarkm} 14555714Skris 146109998Smarkmstatic int 14755714Skrisnat64stl_handle_icmp6(struct ip_fw_chain *chain, struct nat64stl_cfg *cfg, 14855714Skris struct mbuf *m) 149238405Sjkim{ 150238405Sjkim struct pfloghdr loghdr, *logdata; 151238405Sjkim struct nat64_counters *stats; 152238405Sjkim struct ip6_hdr *ip6i; 15355714Skris struct icmp6_hdr *icmp6; 15455714Skris uint32_t tablearg; 155238405Sjkim int hlen, proto; 156238405Sjkim 157238405Sjkim hlen = 0; 158238405Sjkim stats = &cfg->base.stats; 15955714Skris proto = nat64_getlasthdr(m, &hlen); 160238405Sjkim if (proto != IPPROTO_ICMPV6) { 16155714Skris NAT64STAT_INC(stats, dropped); 16255714Skris return (NAT64MFREE); 16355714Skris } 16455714Skris icmp6 = mtodo(m, hlen); 16555714Skris switch (icmp6->icmp6_type) { 16655714Skris case ICMP6_DST_UNREACH: 16755714Skris case ICMP6_PACKET_TOO_BIG: 16855714Skris case ICMP6_TIME_EXCEED_TRANSIT: 16955714Skris case ICMP6_PARAM_PROB: 17055714Skris break; 17155714Skris default: 17255714Skris NAT64STAT_INC(stats, dropped); 17355714Skris return (NAT64MFREE); 17455714Skris } 17555714Skris hlen += sizeof(struct icmp6_hdr); 17655714Skris if (m->m_pkthdr.len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN) { 17755714Skris NAT64STAT_INC(stats, dropped); 17855714Skris return (NAT64MFREE); 17955714Skris } 18055714Skris if (m->m_len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN) 18155714Skris m = m_pullup(m, hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN); 18259191Skris if (m == NULL) { 18359191Skris NAT64STAT_INC(stats, nomem); 18459191Skris return (NAT64RETURN); 18559191Skris } 18659191Skris /* 187111147Snectar * Use destination address from inner IPv6 header to determine 188109998Smarkm * IPv4 mapped address. 189109998Smarkm */ 190109998Smarkm ip6i = mtodo(m, hlen); 191109998Smarkm if (ipfw_lookup_table(chain, cfg->map64, 192109998Smarkm sizeof(struct in6_addr), &ip6i->ip6_dst, &tablearg) == 0) { 193111147Snectar m_freem(m); 19455714Skris return (NAT64RETURN); 19555714Skris } 19655714Skris if (cfg->base.flags & NAT64_LOG) { 19755714Skris logdata = &loghdr; 19855714Skris nat64stl_log(logdata, m, AF_INET6, cfg->no.kidx); 19955714Skris } else 200109998Smarkm logdata = NULL; 201109998Smarkm return (nat64_handle_icmp6(m, 0, 20259191Skris htonl(TARG_VAL(chain, tablearg, nh4)), 0, &cfg->base, logdata)); 20359191Skris} 20459191Skris 20559191Skrisint 20659191Skrisipfw_nat64stl(struct ip_fw_chain *chain, struct ip_fw_args *args, 20755714Skris ipfw_insn *cmd, int *done) 20855714Skris{ 20955714Skris ipfw_insn *icmd; 21055714Skris struct nat64stl_cfg *cfg; 21155714Skris in_addr_t dst4; 21255714Skris uint32_t tablearg; 21355714Skris int ret; 21455714Skris 21555714Skris IPFW_RLOCK_ASSERT(chain); 216238405Sjkim 217238405Sjkim *done = 0; /* try next rule if not matched */ 218238405Sjkim icmd = cmd + 1; 219238405Sjkim if (cmd->opcode != O_EXTERNAL_ACTION || 22055714Skris cmd->arg1 != V_nat64stl_eid || 22155714Skris icmd->opcode != O_EXTERNAL_INSTANCE || 22255714Skris (cfg = NAT64_LOOKUP(chain, icmd)) == NULL) 22355714Skris return (0); 22455714Skris 22555714Skris switch (args->f_id.addr_type) { 22655714Skris case 4: 22755714Skris dst4 = htonl(args->f_id.dst_ip); 22855714Skris ret = ipfw_lookup_table(chain, cfg->map46, sizeof(in_addr_t), 22955714Skris &dst4, &tablearg); 23055714Skris break; 23155714Skris case 6: 23255714Skris ret = ipfw_lookup_table(chain, cfg->map64, 23355714Skris sizeof(struct in6_addr), &args->f_id.src_ip6, &tablearg); 23455714Skris break; 23555714Skris default: 23655714Skris return (0); 23755714Skris } 23855714Skris if (ret == 0) { 23955714Skris /* 24055714Skris * In case when packet is ICMPv6 message from an intermediate 24155714Skris * router, the source address of message will not match the 24255714Skris * addresses from our map64 table. 24355714Skris */ 24455714Skris if (args->f_id.proto != IPPROTO_ICMPV6) 24555714Skris return (0); 246205128Ssimon 247205128Ssimon ret = nat64stl_handle_icmp6(chain, cfg, args->m); 248205128Ssimon } else { 249205128Ssimon if (args->f_id.addr_type == 4) 250205128Ssimon ret = nat64stl_handle_ip4(chain, cfg, args->m, 251205128Ssimon tablearg); 25255714Skris else 25355714Skris ret = nat64stl_handle_ip6(chain, cfg, args->m, 25455714Skris tablearg); 25555714Skris } 25655714Skris if (ret == NAT64SKIP) 25755714Skris return (0); 25855714Skris 25955714Skris *done = 1; /* terminate the search */ 26055714Skris if (ret == NAT64MFREE) 26155714Skris m_freem(args->m); 26255714Skris args->m = NULL; 26355714Skris return (IP_FW_NAT64); 26455714Skris} 26555714Skris 26655714Skris 26755714Skris