1345264Sae/*- 2345264Sae * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3345264Sae * 4345264Sae * Copyright (c) 2019 Yandex LLC 5345264Sae * Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org> 6345264Sae * Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com> 7345264Sae * 8345264Sae * Redistribution and use in source and binary forms, with or without 9345264Sae * modification, are permitted provided that the following conditions 10345264Sae * are met: 11345264Sae * 12345264Sae * 1. Redistributions of source code must retain the above copyright 13345264Sae * notice, this list of conditions and the following disclaimer. 14345264Sae * 2. Redistributions in binary form must reproduce the above copyright 15345264Sae * notice, this list of conditions and the following disclaimer in the 16345264Sae * documentation and/or other materials provided with the distribution. 17345264Sae * 18345264Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19345264Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20345264Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21345264Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22345264Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23345264Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24345264Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25345264Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26345264Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27345264Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28345264Sae */ 29345264Sae 30345264Sae#include <sys/cdefs.h> 31345264Sae__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64clat.c 346212 2019-04-14 12:39:09Z ae $"); 32345264Sae 33345264Sae#include <sys/param.h> 34345264Sae#include <sys/systm.h> 35345264Sae#include <sys/counter.h> 36345264Sae#include <sys/kernel.h> 37345264Sae#include <sys/lock.h> 38345264Sae#include <sys/mbuf.h> 39345264Sae#include <sys/module.h> 40345264Sae#include <sys/rmlock.h> 41345264Sae#include <sys/rwlock.h> 42345264Sae#include <sys/socket.h> 43345264Sae#include <sys/sysctl.h> 44345264Sae 45345264Sae#include <net/if.h> 46345264Sae#include <net/if_var.h> 47345264Sae#include <net/if_pflog.h> 48345264Sae#include <net/pfil.h> 49345264Sae 50345264Sae#include <netinet/in.h> 51345264Sae#include <netinet/ip.h> 52345264Sae#include <netinet/ip_icmp.h> 53345264Sae#include <netinet/ip_var.h> 54345264Sae#include <netinet/ip_fw.h> 55345264Sae#include <netinet/ip6.h> 56345264Sae#include <netinet/icmp6.h> 57345264Sae#include <netinet6/ip_fw_nat64.h> 58345264Sae 59345264Sae#include <netpfil/ipfw/ip_fw_private.h> 60345264Sae#include <netpfil/pf/pf.h> 61345264Sae 62345264Sae#include "nat64clat.h" 63345264Sae 64345264Sae#define NAT64_LOOKUP(chain, cmd) \ 65345264Sae (struct nat64clat_cfg *)SRV_OBJECT((chain), (cmd)->arg1) 66345264Sae 67345264Saestatic void 68345264Saenat64clat_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family, 69345264Sae uint32_t kidx) 70345264Sae{ 71345264Sae static uint32_t pktid = 0; 72345264Sae 73345264Sae memset(plog, 0, sizeof(*plog)); 74345264Sae plog->length = PFLOG_REAL_HDRLEN; 75345264Sae plog->af = family; 76345264Sae plog->action = PF_NAT; 77345264Sae plog->dir = PF_IN; 78345264Sae plog->rulenr = htonl(kidx); 79345264Sae pktid++; 80345264Sae plog->subrulenr = htonl(pktid); 81345264Sae plog->ruleset[0] = '\0'; 82345264Sae strlcpy(plog->ifname, "NAT64CLAT", sizeof(plog->ifname)); 83345264Sae ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m); 84345264Sae} 85345264Sae 86345264Saestatic int 87345264Saenat64clat_handle_ip4(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, 88345264Sae struct mbuf *m) 89345264Sae{ 90345264Sae struct pfloghdr loghdr, *logdata; 91345264Sae struct in6_addr saddr, daddr; 92345264Sae struct ip *ip; 93345264Sae 94345264Sae ip = mtod(m, struct ip*); 95345264Sae /* source address for CLAT may be private with no harm */ 96345264Sae if (nat64_check_ip4(ip->ip_src.s_addr) != 0 || 97345264Sae nat64_check_ip4(ip->ip_dst.s_addr) != 0 || 98345264Sae nat64_check_private_ip4(&cfg->base, ip->ip_dst.s_addr) != 0) 99345264Sae return (NAT64SKIP); 100345264Sae 101345264Sae memcpy(&saddr, &cfg->base.clat_prefix, sizeof(saddr)); 102345264Sae nat64_embed_ip4(&saddr, cfg->base.clat_plen, ip->ip_src.s_addr); 103345264Sae memcpy(&daddr, &cfg->base.plat_prefix, sizeof(daddr)); 104345264Sae nat64_embed_ip4(&daddr, cfg->base.plat_plen, ip->ip_dst.s_addr); 105345264Sae if (cfg->base.flags & NAT64_LOG) { 106345264Sae logdata = &loghdr; 107345264Sae nat64clat_log(logdata, m, AF_INET, cfg->no.kidx); 108345264Sae } else 109345264Sae logdata = NULL; 110345264Sae return (nat64_do_handle_ip4(m, &saddr, &daddr, 0, &cfg->base, 111345264Sae logdata)); 112345264Sae} 113345264Sae 114345264Saestatic int 115345264Saenat64clat_handle_ip6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, 116345264Sae struct mbuf *m) 117345264Sae{ 118345264Sae struct pfloghdr loghdr, *logdata; 119345264Sae struct ip6_hdr *ip6; 120345264Sae uint32_t aaddr; 121345264Sae 122345264Sae /* 123345264Sae * NOTE: we expect ipfw_chk() did m_pullup() up to upper level 124345264Sae * protocol's headers. Also we skip some checks, that ip6_input(), 125345264Sae * ip6_forward(), ip6_fastfwd() and ipfw_chk() already did. 126345264Sae */ 127345264Sae ip6 = mtod(m, struct ip6_hdr *); 128345264Sae /* Check ip6_dst matches configured prefix */ 129345264Sae if (memcmp(&ip6->ip6_dst, &cfg->base.clat_prefix, 130345264Sae cfg->base.clat_plen / 8) != 0) 131345264Sae return (NAT64SKIP); 132345264Sae /* Check ip6_src matches configured prefix */ 133345264Sae if (memcmp(&ip6->ip6_src, &cfg->base.plat_prefix, 134345264Sae cfg->base.plat_plen / 8) != 0) 135345264Sae return (NAT64SKIP); 136345264Sae 137345264Sae if (cfg->base.flags & NAT64_LOG) { 138345264Sae logdata = &loghdr; 139345264Sae nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx); 140345264Sae } else 141345264Sae logdata = NULL; 142345264Sae 143345264Sae aaddr = nat64_extract_ip4(&ip6->ip6_src, cfg->base.plat_plen); 144345264Sae return (nat64_do_handle_ip6(m, aaddr, 0, &cfg->base, logdata)); 145345264Sae} 146345264Sae 147345264Saestatic int 148345264Saenat64clat_handle_icmp6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg, 149345264Sae struct mbuf *m) 150345264Sae{ 151345264Sae struct pfloghdr loghdr, *logdata; 152345264Sae struct nat64_counters *stats; 153345264Sae struct ip6_hdr *ip6i; 154345264Sae struct icmp6_hdr *icmp6; 155345264Sae uint32_t daddr; 156345264Sae int hlen, proto; 157345264Sae 158345264Sae hlen = 0; 159345264Sae stats = &cfg->base.stats; 160345264Sae proto = nat64_getlasthdr(m, &hlen); 161345264Sae if (proto != IPPROTO_ICMPV6) { 162345264Sae NAT64STAT_INC(stats, dropped); 163345264Sae return (NAT64MFREE); 164345264Sae } 165345264Sae icmp6 = mtodo(m, hlen); 166345264Sae switch (icmp6->icmp6_type) { 167345264Sae case ICMP6_DST_UNREACH: 168345264Sae case ICMP6_PACKET_TOO_BIG: 169345264Sae case ICMP6_TIME_EXCEED_TRANSIT: 170345264Sae case ICMP6_PARAM_PROB: 171345264Sae break; 172345264Sae default: 173345264Sae NAT64STAT_INC(stats, dropped); 174345264Sae return (NAT64MFREE); 175345264Sae } 176345264Sae hlen += sizeof(struct icmp6_hdr); 177345264Sae if (m->m_pkthdr.len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN) { 178345264Sae NAT64STAT_INC(stats, dropped); 179345264Sae return (NAT64MFREE); 180345264Sae } 181345264Sae if (m->m_len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN) 182345264Sae m = m_pullup(m, hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN); 183345264Sae if (m == NULL) { 184345264Sae NAT64STAT_INC(stats, nomem); 185345264Sae return (NAT64RETURN); 186345264Sae } 187345264Sae /* 188345264Sae * Use destination address from inner IPv6 header to determine 189345264Sae * IPv4 mapped address. 190345264Sae */ 191345264Sae ip6i = mtodo(m, hlen); 192345264Sae daddr = nat64_extract_ip4(&ip6i->ip6_dst, cfg->base.clat_plen); 193345264Sae if (daddr == 0) { 194345264Sae NAT64STAT_INC(stats, dropped); 195345264Sae return (NAT64MFREE); 196345264Sae } 197345264Sae if (cfg->base.flags & NAT64_LOG) { 198345264Sae logdata = &loghdr; 199345264Sae nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx); 200345264Sae } else 201345264Sae logdata = NULL; 202345264Sae return (nat64_handle_icmp6(m, 0, daddr, 0, &cfg->base, logdata)); 203345264Sae} 204345264Sae 205345264Saeint 206345264Saeipfw_nat64clat(struct ip_fw_chain *chain, struct ip_fw_args *args, 207345264Sae ipfw_insn *cmd, int *done) 208345264Sae{ 209345264Sae ipfw_insn *icmd; 210345264Sae struct nat64clat_cfg *cfg; 211345264Sae int ret; 212345264Sae 213345264Sae IPFW_RLOCK_ASSERT(chain); 214345264Sae 215345264Sae *done = 0; /* try next rule if not matched */ 216345264Sae icmd = cmd + 1; 217345264Sae if (cmd->opcode != O_EXTERNAL_ACTION || 218345264Sae cmd->arg1 != V_nat64clat_eid || 219345264Sae icmd->opcode != O_EXTERNAL_INSTANCE || 220345264Sae (cfg = NAT64_LOOKUP(chain, icmd)) == NULL) 221345264Sae return (0); 222345264Sae 223345264Sae switch (args->f_id.addr_type) { 224345264Sae case 4: 225345264Sae ret = nat64clat_handle_ip4(chain, cfg, args->m); 226345264Sae break; 227345264Sae case 6: 228345264Sae ret = nat64clat_handle_ip6(chain, cfg, args->m); 229345264Sae break; 230345264Sae default: 231345264Sae return (0); 232345264Sae } 233345264Sae 234345264Sae if (ret == NAT64SKIP) { 235345264Sae /* 236345264Sae * In case when packet is ICMPv6 message from an intermediate 237345264Sae * router, the source address of message will not match the 238345264Sae * addresses from configured prefixes. 239345264Sae */ 240345264Sae if (args->f_id.proto != IPPROTO_ICMPV6) 241345264Sae return (0); 242345264Sae 243345264Sae ret = nat64clat_handle_icmp6(chain, cfg, args->m); 244345264Sae } 245345264Sae 246345264Sae if (ret == NAT64SKIP) 247345264Sae return (0); 248345264Sae 249345264Sae *done = 1; /* terminate the search */ 250345264Sae if (ret == NAT64MFREE) 251345264Sae m_freem(args->m); 252345264Sae 253345264Sae args->m = NULL; 254345264Sae return (IP_FW_NAT64); 255345264Sae} 256