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