xref: /freebsd/sys/netpfil/ipfw/nat64/nat64lsn.c (revision 4a77657cbc011ea657ccb079fff6b58b295eccb0)
1d8caf56eSAndrey V. Elsukov /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3002cae78SAndrey V. Elsukov  *
4*4a77657cSAndrey V. Elsukov  * Copyright (c) 2015-2020 Yandex LLC
5d8caf56eSAndrey V. Elsukov  * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
6*4a77657cSAndrey V. Elsukov  * Copyright (c) 2016-2020 Andrey V. Elsukov <ae@FreeBSD.org>
7d8caf56eSAndrey V. Elsukov  *
8d8caf56eSAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
9d8caf56eSAndrey V. Elsukov  * modification, are permitted provided that the following conditions
10d8caf56eSAndrey V. Elsukov  * are met:
11d8caf56eSAndrey V. Elsukov  *
12d8caf56eSAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
13d8caf56eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
14d8caf56eSAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
15d8caf56eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
16d8caf56eSAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
17d8caf56eSAndrey V. Elsukov  *
18d8caf56eSAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19d8caf56eSAndrey V. Elsukov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20d8caf56eSAndrey V. Elsukov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21d8caf56eSAndrey V. Elsukov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22d8caf56eSAndrey V. Elsukov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23d8caf56eSAndrey V. Elsukov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24d8caf56eSAndrey V. Elsukov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25d8caf56eSAndrey V. Elsukov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26d8caf56eSAndrey V. Elsukov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27d8caf56eSAndrey V. Elsukov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28d8caf56eSAndrey V. Elsukov  */
29d8caf56eSAndrey V. Elsukov 
30d8caf56eSAndrey V. Elsukov #include <sys/param.h>
31d8caf56eSAndrey V. Elsukov #include <sys/systm.h>
32d8caf56eSAndrey V. Elsukov #include <sys/counter.h>
33d18c1f26SAndrey V. Elsukov #include <sys/ck.h>
34d18c1f26SAndrey V. Elsukov #include <sys/epoch.h>
35d8caf56eSAndrey V. Elsukov #include <sys/errno.h>
36d18c1f26SAndrey V. Elsukov #include <sys/hash.h>
37d8caf56eSAndrey V. Elsukov #include <sys/kernel.h>
38d8caf56eSAndrey V. Elsukov #include <sys/lock.h>
39d8caf56eSAndrey V. Elsukov #include <sys/malloc.h>
40d8caf56eSAndrey V. Elsukov #include <sys/mbuf.h>
41d8caf56eSAndrey V. Elsukov #include <sys/module.h>
42d8caf56eSAndrey V. Elsukov #include <sys/rmlock.h>
43d8caf56eSAndrey V. Elsukov #include <sys/socket.h>
44d8caf56eSAndrey V. Elsukov #include <sys/syslog.h>
45d8caf56eSAndrey V. Elsukov #include <sys/sysctl.h>
46d8caf56eSAndrey V. Elsukov 
47d8caf56eSAndrey V. Elsukov #include <net/if.h>
48d8caf56eSAndrey V. Elsukov #include <net/if_var.h>
49d8caf56eSAndrey V. Elsukov #include <net/if_pflog.h>
50d8caf56eSAndrey V. Elsukov #include <net/pfil.h>
51d8caf56eSAndrey V. Elsukov 
52d8caf56eSAndrey V. Elsukov #include <netinet/in.h>
53d8caf56eSAndrey V. Elsukov #include <netinet/ip.h>
54d8caf56eSAndrey V. Elsukov #include <netinet/ip_var.h>
55d8caf56eSAndrey V. Elsukov #include <netinet/ip_fw.h>
56d8caf56eSAndrey V. Elsukov #include <netinet/ip6.h>
57d8caf56eSAndrey V. Elsukov #include <netinet/icmp6.h>
58d8caf56eSAndrey V. Elsukov #include <netinet/ip_icmp.h>
59d8caf56eSAndrey V. Elsukov #include <netinet/tcp.h>
60d8caf56eSAndrey V. Elsukov #include <netinet/udp.h>
61d8caf56eSAndrey V. Elsukov #include <netinet6/in6_var.h>
62d8caf56eSAndrey V. Elsukov #include <netinet6/ip6_var.h>
63d8caf56eSAndrey V. Elsukov #include <netinet6/ip_fw_nat64.h>
64d8caf56eSAndrey V. Elsukov 
65d8caf56eSAndrey V. Elsukov #include <netpfil/ipfw/ip_fw_private.h>
66d8caf56eSAndrey V. Elsukov #include <netpfil/pf/pf.h>
67d8caf56eSAndrey V. Elsukov 
68782360deSAndrey V. Elsukov #include "nat64lsn.h"
69782360deSAndrey V. Elsukov 
70d8caf56eSAndrey V. Elsukov MALLOC_DEFINE(M_NAT64LSN, "NAT64LSN", "NAT64LSN");
71d8caf56eSAndrey V. Elsukov 
72e0b7b6d4SAndrey V. Elsukov #define	NAT64LSN_EPOCH_ENTER(et)  NET_EPOCH_ENTER(et)
73e0b7b6d4SAndrey V. Elsukov #define	NAT64LSN_EPOCH_EXIT(et)   NET_EPOCH_EXIT(et)
74e0b7b6d4SAndrey V. Elsukov #define	NAT64LSN_EPOCH_ASSERT()   NET_EPOCH_ASSERT()
752a4bd982SGleb Smirnoff #define	NAT64LSN_EPOCH_CALL(c, f) NET_EPOCH_CALL((f), (c))
76d18c1f26SAndrey V. Elsukov 
77d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_host_zone;
78d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_pgchunk_zone;
79d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_pg_zone;
80d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_aliaslink_zone;
81d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_state_zone;
82d18c1f26SAndrey V. Elsukov static uma_zone_t nat64lsn_job_zone;
83d18c1f26SAndrey V. Elsukov 
84d8caf56eSAndrey V. Elsukov static void nat64lsn_periodic(void *data);
85d8caf56eSAndrey V. Elsukov #define	PERIODIC_DELAY		4
86d8caf56eSAndrey V. Elsukov #define	NAT64_LOOKUP(chain, cmd)	\
87*4a77657cSAndrey V. Elsukov     (struct nat64lsn_instance *)SRV_OBJECT((chain), insntod(cmd, kidx)->kidx)
88d8caf56eSAndrey V. Elsukov /*
89d8caf56eSAndrey V. Elsukov  * Delayed job queue, used to create new hosts
90d8caf56eSAndrey V. Elsukov  * and new portgroups
91d8caf56eSAndrey V. Elsukov  */
92d8caf56eSAndrey V. Elsukov enum nat64lsn_jtype {
93d8caf56eSAndrey V. Elsukov 	JTYPE_NEWHOST = 1,
94d8caf56eSAndrey V. Elsukov 	JTYPE_NEWPORTGROUP,
95d18c1f26SAndrey V. Elsukov 	JTYPE_DESTROY,
96d8caf56eSAndrey V. Elsukov };
97d8caf56eSAndrey V. Elsukov 
98d8caf56eSAndrey V. Elsukov struct nat64lsn_job_item {
99d18c1f26SAndrey V. Elsukov 	STAILQ_ENTRY(nat64lsn_job_item)	entries;
100d8caf56eSAndrey V. Elsukov 	enum nat64lsn_jtype	jtype;
101d18c1f26SAndrey V. Elsukov 
102d18c1f26SAndrey V. Elsukov 	union {
103d18c1f26SAndrey V. Elsukov 		struct { /* used by JTYPE_NEWHOST, JTYPE_NEWPORTGROUP */
104d6369c2dSAndrey V. Elsukov 			struct mbuf		*m;
105d18c1f26SAndrey V. Elsukov 			struct nat64lsn_host	*host;
106d18c1f26SAndrey V. Elsukov 			struct nat64lsn_state	*state;
107d18c1f26SAndrey V. Elsukov 			uint32_t		src6_hval;
108d18c1f26SAndrey V. Elsukov 			uint32_t		state_hval;
109d6369c2dSAndrey V. Elsukov 			struct ipfw_flow_id	f_id;
110d18c1f26SAndrey V. Elsukov 			in_addr_t		faddr;
111d18c1f26SAndrey V. Elsukov 			uint16_t		port;
112d18c1f26SAndrey V. Elsukov 			uint8_t			proto;
113d18c1f26SAndrey V. Elsukov 			uint8_t			done;
114d18c1f26SAndrey V. Elsukov 		};
115d18c1f26SAndrey V. Elsukov 		struct { /* used by JTYPE_DESTROY */
116d18c1f26SAndrey V. Elsukov 			struct nat64lsn_hosts_slist	hosts;
117d18c1f26SAndrey V. Elsukov 			struct nat64lsn_pg_slist	portgroups;
118d18c1f26SAndrey V. Elsukov 			struct nat64lsn_pgchunk		*pgchunk;
119d18c1f26SAndrey V. Elsukov 			struct epoch_context		epoch_ctx;
120d18c1f26SAndrey V. Elsukov 		};
121d18c1f26SAndrey V. Elsukov 	};
122d8caf56eSAndrey V. Elsukov };
123d8caf56eSAndrey V. Elsukov 
124d8caf56eSAndrey V. Elsukov static struct mtx jmtx;
125d8caf56eSAndrey V. Elsukov #define	JQUEUE_LOCK_INIT()	mtx_init(&jmtx, "qlock", NULL, MTX_DEF)
126d8caf56eSAndrey V. Elsukov #define	JQUEUE_LOCK_DESTROY()	mtx_destroy(&jmtx)
127d8caf56eSAndrey V. Elsukov #define	JQUEUE_LOCK()		mtx_lock(&jmtx)
128d8caf56eSAndrey V. Elsukov #define	JQUEUE_UNLOCK()		mtx_unlock(&jmtx)
129d8caf56eSAndrey V. Elsukov 
130d18c1f26SAndrey V. Elsukov static int nat64lsn_alloc_host(struct nat64lsn_cfg *cfg,
131d18c1f26SAndrey V. Elsukov     struct nat64lsn_job_item *ji);
132d18c1f26SAndrey V. Elsukov static int nat64lsn_alloc_pg(struct nat64lsn_cfg *cfg,
133d18c1f26SAndrey V. Elsukov     struct nat64lsn_job_item *ji);
134d18c1f26SAndrey V. Elsukov static struct nat64lsn_job_item *nat64lsn_create_job(
135d18c1f26SAndrey V. Elsukov     struct nat64lsn_cfg *cfg, int jtype);
136d8caf56eSAndrey V. Elsukov static void nat64lsn_enqueue_job(struct nat64lsn_cfg *cfg,
137d8caf56eSAndrey V. Elsukov     struct nat64lsn_job_item *ji);
138d18c1f26SAndrey V. Elsukov static void nat64lsn_job_destroy(epoch_context_t ctx);
139d18c1f26SAndrey V. Elsukov static void nat64lsn_destroy_host(struct nat64lsn_host *host);
140d18c1f26SAndrey V. Elsukov static void nat64lsn_destroy_pg(struct nat64lsn_pg *pg);
141d8caf56eSAndrey V. Elsukov 
142d8caf56eSAndrey V. Elsukov static int nat64lsn_translate4(struct nat64lsn_cfg *cfg,
143d18c1f26SAndrey V. Elsukov     const struct ipfw_flow_id *f_id, struct mbuf **mp);
144d8caf56eSAndrey V. Elsukov static int nat64lsn_translate6(struct nat64lsn_cfg *cfg,
145d18c1f26SAndrey V. Elsukov     struct ipfw_flow_id *f_id, struct mbuf **mp);
146d18c1f26SAndrey V. Elsukov static int nat64lsn_translate6_internal(struct nat64lsn_cfg *cfg,
147d18c1f26SAndrey V. Elsukov     struct mbuf **mp, struct nat64lsn_state *state, uint8_t flags);
148d8caf56eSAndrey V. Elsukov 
149d18c1f26SAndrey V. Elsukov #define	NAT64_BIT_TCP_FIN	0	/* FIN was seen */
150d18c1f26SAndrey V. Elsukov #define	NAT64_BIT_TCP_SYN	1	/* First syn in->out */
151d18c1f26SAndrey V. Elsukov #define	NAT64_BIT_TCP_ESTAB	2	/* Packet with Ack */
152d18c1f26SAndrey V. Elsukov #define	NAT64_BIT_READY_IPV4	6	/* state is ready for translate4 */
153d18c1f26SAndrey V. Elsukov #define	NAT64_BIT_STALE		7	/* state is going to be expired */
154d8caf56eSAndrey V. Elsukov 
155d18c1f26SAndrey V. Elsukov #define	NAT64_FLAG_FIN		(1 << NAT64_BIT_TCP_FIN)
156d18c1f26SAndrey V. Elsukov #define	NAT64_FLAG_SYN		(1 << NAT64_BIT_TCP_SYN)
157d18c1f26SAndrey V. Elsukov #define	NAT64_FLAG_ESTAB	(1 << NAT64_BIT_TCP_ESTAB)
158d18c1f26SAndrey V. Elsukov #define	NAT64_FLAGS_TCP	(NAT64_FLAG_SYN|NAT64_FLAG_ESTAB|NAT64_FLAG_FIN)
159d8caf56eSAndrey V. Elsukov 
160d18c1f26SAndrey V. Elsukov #define	NAT64_FLAG_READY	(1 << NAT64_BIT_READY_IPV4)
161d18c1f26SAndrey V. Elsukov #define	NAT64_FLAG_STALE	(1 << NAT64_BIT_STALE)
162d8caf56eSAndrey V. Elsukov 
163d8caf56eSAndrey V. Elsukov static inline uint8_t
convert_tcp_flags(uint8_t flags)164d8caf56eSAndrey V. Elsukov convert_tcp_flags(uint8_t flags)
165d8caf56eSAndrey V. Elsukov {
166d8caf56eSAndrey V. Elsukov 	uint8_t result;
167d8caf56eSAndrey V. Elsukov 
168d8caf56eSAndrey V. Elsukov 	result = flags & (TH_FIN|TH_SYN);
169d8caf56eSAndrey V. Elsukov 	result |= (flags & TH_RST) >> 2; /* Treat RST as FIN */
170d8caf56eSAndrey V. Elsukov 	result |= (flags & TH_ACK) >> 2; /* Treat ACK as estab */
171d8caf56eSAndrey V. Elsukov 
172d8caf56eSAndrey V. Elsukov 	return (result);
173d8caf56eSAndrey V. Elsukov }
174d8caf56eSAndrey V. Elsukov 
175d18c1f26SAndrey V. Elsukov static void
nat64lsn_log(struct pfloghdr * plog,struct mbuf * m,sa_family_t family,struct nat64lsn_state * state)176d18c1f26SAndrey V. Elsukov nat64lsn_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family,
177d18c1f26SAndrey V. Elsukov     struct nat64lsn_state *state)
178d18c1f26SAndrey V. Elsukov {
179d18c1f26SAndrey V. Elsukov 
180d18c1f26SAndrey V. Elsukov 	memset(plog, 0, sizeof(*plog));
181*4a77657cSAndrey V. Elsukov 	plog->length = PFLOG_REAL_HDRLEN;
182d18c1f26SAndrey V. Elsukov 	plog->af = family;
183d18c1f26SAndrey V. Elsukov 	plog->action = PF_NAT;
184d18c1f26SAndrey V. Elsukov 	plog->dir = PF_IN;
185d18c1f26SAndrey V. Elsukov 	plog->rulenr = htonl(state->ip_src);
186d18c1f26SAndrey V. Elsukov 	plog->subrulenr = htonl((uint32_t)(state->aport << 16) |
187d18c1f26SAndrey V. Elsukov 	    (state->proto << 8) | (state->ip_dst & 0xff));
188d18c1f26SAndrey V. Elsukov 	plog->ruleset[0] = '\0';
189d18c1f26SAndrey V. Elsukov 	strlcpy(plog->ifname, "NAT64LSN", sizeof(plog->ifname));
190d18c1f26SAndrey V. Elsukov 	ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m);
191d18c1f26SAndrey V. Elsukov }
192d18c1f26SAndrey V. Elsukov 
193d18c1f26SAndrey V. Elsukov #define	HVAL(p, n, s)	jenkins_hash32((const uint32_t *)(p), (n), (s))
194d18c1f26SAndrey V. Elsukov #define	HOST_HVAL(c, a)	HVAL((a),\
195d18c1f26SAndrey V. Elsukov     sizeof(struct in6_addr) / sizeof(uint32_t), (c)->hash_seed)
196d18c1f26SAndrey V. Elsukov #define	HOSTS(c, v)	((c)->hosts_hash[(v) & ((c)->hosts_hashsize - 1)])
197d18c1f26SAndrey V. Elsukov 
198d18c1f26SAndrey V. Elsukov #define	ALIASLINK_HVAL(c, f)	HVAL(&(f)->dst_ip6,\
199d18c1f26SAndrey V. Elsukov     sizeof(struct in6_addr) * 2 / sizeof(uint32_t), (c)->hash_seed)
200d18c1f26SAndrey V. Elsukov #define	ALIAS_BYHASH(c, v)	\
201d18c1f26SAndrey V. Elsukov     ((c)->aliases[(v) & ((1 << (32 - (c)->plen4)) - 1)])
202d18c1f26SAndrey V. Elsukov static struct nat64lsn_aliaslink*
nat64lsn_get_aliaslink(struct nat64lsn_cfg * cfg __unused,struct nat64lsn_host * host,const struct ipfw_flow_id * f_id __unused)203d18c1f26SAndrey V. Elsukov nat64lsn_get_aliaslink(struct nat64lsn_cfg *cfg __unused,
204d18c1f26SAndrey V. Elsukov     struct nat64lsn_host *host, const struct ipfw_flow_id *f_id __unused)
205d18c1f26SAndrey V. Elsukov {
206d18c1f26SAndrey V. Elsukov 
207d18c1f26SAndrey V. Elsukov 	/*
208d18c1f26SAndrey V. Elsukov 	 * We can implement some different algorithms how
209d18c1f26SAndrey V. Elsukov 	 * select an alias address.
210d18c1f26SAndrey V. Elsukov 	 * XXX: for now we use first available.
211d18c1f26SAndrey V. Elsukov 	 */
212d18c1f26SAndrey V. Elsukov 	return (CK_SLIST_FIRST(&host->aliases));
213d18c1f26SAndrey V. Elsukov }
214d18c1f26SAndrey V. Elsukov 
215*4a77657cSAndrey V. Elsukov static struct nat64lsn_alias*
nat64lsn_get_alias(struct nat64lsn_cfg * cfg,const struct ipfw_flow_id * f_id __unused)216*4a77657cSAndrey V. Elsukov nat64lsn_get_alias(struct nat64lsn_cfg *cfg,
217*4a77657cSAndrey V. Elsukov     const struct ipfw_flow_id *f_id __unused)
218*4a77657cSAndrey V. Elsukov {
219*4a77657cSAndrey V. Elsukov 	static uint32_t idx = 0;
220*4a77657cSAndrey V. Elsukov 
221*4a77657cSAndrey V. Elsukov 	/*
222*4a77657cSAndrey V. Elsukov 	 * We can choose alias by number of allocated PGs,
223*4a77657cSAndrey V. Elsukov 	 * not used yet by other hosts, or some static configured
224*4a77657cSAndrey V. Elsukov 	 * by user.
225*4a77657cSAndrey V. Elsukov 	 * XXX: for now we choose it using round robin.
226*4a77657cSAndrey V. Elsukov 	 */
227*4a77657cSAndrey V. Elsukov 	return (&ALIAS_BYHASH(cfg, idx++));
228*4a77657cSAndrey V. Elsukov }
229*4a77657cSAndrey V. Elsukov 
230d18c1f26SAndrey V. Elsukov #define	STATE_HVAL(c, d)	HVAL((d), 2, (c)->hash_seed)
231d18c1f26SAndrey V. Elsukov #define	STATE_HASH(h, v)	\
232d18c1f26SAndrey V. Elsukov     ((h)->states_hash[(v) & ((h)->states_hashsize - 1)])
233d18c1f26SAndrey V. Elsukov #define	STATES_CHUNK(p, v)	\
234d18c1f26SAndrey V. Elsukov     ((p)->chunks_count == 1 ? (p)->states : \
235d18c1f26SAndrey V. Elsukov 	((p)->states_chunk[CHUNK_BY_FADDR(p, v)]))
236d18c1f26SAndrey V. Elsukov 
237d18c1f26SAndrey V. Elsukov #ifdef __LP64__
238d18c1f26SAndrey V. Elsukov #define	FREEMASK_FFSLL(pg, faddr)		\
239d18c1f26SAndrey V. Elsukov     ffsll(*FREEMASK_CHUNK((pg), (faddr)))
240d18c1f26SAndrey V. Elsukov #define	FREEMASK_BTR(pg, faddr, bit)	\
241d18c1f26SAndrey V. Elsukov     ck_pr_btr_64(FREEMASK_CHUNK((pg), (faddr)), (bit))
242d18c1f26SAndrey V. Elsukov #define	FREEMASK_BTS(pg, faddr, bit)	\
243d18c1f26SAndrey V. Elsukov     ck_pr_bts_64(FREEMASK_CHUNK((pg), (faddr)), (bit))
244d18c1f26SAndrey V. Elsukov #define	FREEMASK_ISSET(pg, faddr, bit)	\
245d18c1f26SAndrey V. Elsukov     ISSET64(*FREEMASK_CHUNK((pg), (faddr)), (bit))
246d18c1f26SAndrey V. Elsukov #define	FREEMASK_COPY(pg, n, out)	\
247d18c1f26SAndrey V. Elsukov     (out) = ck_pr_load_64(FREEMASK_CHUNK((pg), (n)))
248d18c1f26SAndrey V. Elsukov #else
249d18c1f26SAndrey V. Elsukov static inline int
freemask_ffsll(uint32_t * freemask)250d18c1f26SAndrey V. Elsukov freemask_ffsll(uint32_t *freemask)
251d18c1f26SAndrey V. Elsukov {
252d18c1f26SAndrey V. Elsukov 	int i;
253d18c1f26SAndrey V. Elsukov 
254d18c1f26SAndrey V. Elsukov 	if ((i = ffsl(freemask[0])) != 0)
255d18c1f26SAndrey V. Elsukov 		return (i);
256d18c1f26SAndrey V. Elsukov 	if ((i = ffsl(freemask[1])) != 0)
257d18c1f26SAndrey V. Elsukov 		return (i + 32);
258d18c1f26SAndrey V. Elsukov 	return (0);
259d18c1f26SAndrey V. Elsukov }
260d18c1f26SAndrey V. Elsukov #define	FREEMASK_FFSLL(pg, faddr)		\
261d18c1f26SAndrey V. Elsukov     freemask_ffsll(FREEMASK_CHUNK((pg), (faddr)))
262d18c1f26SAndrey V. Elsukov #define	FREEMASK_BTR(pg, faddr, bit)	\
263d18c1f26SAndrey V. Elsukov     ck_pr_btr_32(FREEMASK_CHUNK((pg), (faddr)) + (bit) / 32, (bit) % 32)
264d18c1f26SAndrey V. Elsukov #define	FREEMASK_BTS(pg, faddr, bit)	\
265d18c1f26SAndrey V. Elsukov     ck_pr_bts_32(FREEMASK_CHUNK((pg), (faddr)) + (bit) / 32, (bit) % 32)
266d18c1f26SAndrey V. Elsukov #define	FREEMASK_ISSET(pg, faddr, bit)	\
267d18c1f26SAndrey V. Elsukov     ISSET32(*(FREEMASK_CHUNK((pg), (faddr)) + (bit) / 32), (bit) % 32)
268d18c1f26SAndrey V. Elsukov #define	FREEMASK_COPY(pg, n, out)	\
269d18c1f26SAndrey V. Elsukov     (out) = ck_pr_load_32(FREEMASK_CHUNK((pg), (n))) | \
270d18c1f26SAndrey V. Elsukov 	((uint64_t)ck_pr_load_32(FREEMASK_CHUNK((pg), (n)) + 1) << 32)
271d18c1f26SAndrey V. Elsukov #endif /* !__LP64__ */
272d18c1f26SAndrey V. Elsukov 
273*4a77657cSAndrey V. Elsukov 
274*4a77657cSAndrey V. Elsukov #define	NAT64LSN_TRY_PGCNT	36
275d18c1f26SAndrey V. Elsukov static struct nat64lsn_pg*
nat64lsn_get_pg(uint32_t * chunkmask,uint32_t * pgmask,struct nat64lsn_pgchunk ** chunks,uint32_t * pgidx,in_addr_t faddr)276d18c1f26SAndrey V. Elsukov nat64lsn_get_pg(uint32_t *chunkmask, uint32_t *pgmask,
277*4a77657cSAndrey V. Elsukov     struct nat64lsn_pgchunk **chunks, uint32_t *pgidx, in_addr_t faddr)
278d18c1f26SAndrey V. Elsukov {
279*4a77657cSAndrey V. Elsukov 	struct nat64lsn_pg *pg;
280d18c1f26SAndrey V. Elsukov 	uint32_t idx, oldidx;
281d18c1f26SAndrey V. Elsukov 	int cnt;
282d18c1f26SAndrey V. Elsukov 
283*4a77657cSAndrey V. Elsukov 	/* First try last used PG. */
284d18c1f26SAndrey V. Elsukov 	idx = oldidx = ck_pr_load_32(pgidx);
285*4a77657cSAndrey V. Elsukov 	MPASS(idx < 1024);
286*4a77657cSAndrey V. Elsukov 	cnt = 0;
287d18c1f26SAndrey V. Elsukov 	do {
288d18c1f26SAndrey V. Elsukov 		ck_pr_fence_load();
289*4a77657cSAndrey V. Elsukov 		if (idx > 1023 || !ISSET32(*chunkmask, idx / 32)) {
290*4a77657cSAndrey V. Elsukov 			/* If it is first try, reset idx to first PG */
291*4a77657cSAndrey V. Elsukov 			idx = 0;
292d18c1f26SAndrey V. Elsukov 			/* Stop if idx is out of range */
293*4a77657cSAndrey V. Elsukov 			if (cnt > 0)
294d18c1f26SAndrey V. Elsukov 				break;
295*4a77657cSAndrey V. Elsukov 		}
296*4a77657cSAndrey V. Elsukov 		if (ISSET32(pgmask[idx / 32], idx % 32)) {
297d18c1f26SAndrey V. Elsukov 			pg = ck_pr_load_ptr(
298d18c1f26SAndrey V. Elsukov 			    &chunks[idx / 32]->pgptr[idx % 32]);
299*4a77657cSAndrey V. Elsukov 			ck_pr_fence_load();
300*4a77657cSAndrey V. Elsukov 			/*
301*4a77657cSAndrey V. Elsukov 			 * Make sure that pg did not become DEAD.
302*4a77657cSAndrey V. Elsukov 			 */
303*4a77657cSAndrey V. Elsukov 			if ((pg->flags & NAT64LSN_DEADPG) == 0 &&
304*4a77657cSAndrey V. Elsukov 			    FREEMASK_BITCOUNT(pg, faddr) > 0) {
305*4a77657cSAndrey V. Elsukov 				if (cnt > 0)
306*4a77657cSAndrey V. Elsukov 					ck_pr_cas_32(pgidx, oldidx, idx);
307*4a77657cSAndrey V. Elsukov 				return (pg);
308*4a77657cSAndrey V. Elsukov 			}
309*4a77657cSAndrey V. Elsukov 		}
310d18c1f26SAndrey V. Elsukov 		idx++;
311d18c1f26SAndrey V. Elsukov 	} while (++cnt < NAT64LSN_TRY_PGCNT);
312*4a77657cSAndrey V. Elsukov 	if (oldidx != idx)
313d18c1f26SAndrey V. Elsukov 		ck_pr_cas_32(pgidx, oldidx, idx);
314d18c1f26SAndrey V. Elsukov 	return (NULL);
315d18c1f26SAndrey V. Elsukov }
316d18c1f26SAndrey V. Elsukov 
317d18c1f26SAndrey V. Elsukov static struct nat64lsn_state*
nat64lsn_get_state6to4(struct nat64lsn_cfg * cfg,struct nat64lsn_host * host,const struct ipfw_flow_id * f_id,uint32_t hval,in_addr_t faddr,uint16_t port,uint8_t proto)318d18c1f26SAndrey V. Elsukov nat64lsn_get_state6to4(struct nat64lsn_cfg *cfg, struct nat64lsn_host *host,
319d18c1f26SAndrey V. Elsukov     const struct ipfw_flow_id *f_id, uint32_t hval, in_addr_t faddr,
320d18c1f26SAndrey V. Elsukov     uint16_t port, uint8_t proto)
321d18c1f26SAndrey V. Elsukov {
322d18c1f26SAndrey V. Elsukov 	struct nat64lsn_aliaslink *link;
323d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
324d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pg *pg;
325d18c1f26SAndrey V. Elsukov 	int i, offset;
326d18c1f26SAndrey V. Elsukov 
327d18c1f26SAndrey V. Elsukov 	NAT64LSN_EPOCH_ASSERT();
328d18c1f26SAndrey V. Elsukov 
329d18c1f26SAndrey V. Elsukov 	/* Check that we already have state for given arguments */
330d18c1f26SAndrey V. Elsukov 	CK_SLIST_FOREACH(state, &STATE_HASH(host, hval), entries) {
331d18c1f26SAndrey V. Elsukov 		if (state->proto == proto && state->ip_dst == faddr &&
332d18c1f26SAndrey V. Elsukov 		    state->sport == port && state->dport == f_id->dst_port)
333d18c1f26SAndrey V. Elsukov 			return (state);
334d18c1f26SAndrey V. Elsukov 	}
335d18c1f26SAndrey V. Elsukov 
336d18c1f26SAndrey V. Elsukov 	link = nat64lsn_get_aliaslink(cfg, host, f_id);
337d18c1f26SAndrey V. Elsukov 	if (link == NULL)
338d18c1f26SAndrey V. Elsukov 		return (NULL);
339d18c1f26SAndrey V. Elsukov 
340d18c1f26SAndrey V. Elsukov 	switch (proto) {
341d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
342*4a77657cSAndrey V. Elsukov 		pg = nat64lsn_get_pg(&link->alias->tcp_chunkmask,
343*4a77657cSAndrey V. Elsukov 		    link->alias->tcp_pgmask, link->alias->tcp,
344d18c1f26SAndrey V. Elsukov 		    &link->alias->tcp_pgidx, faddr);
345d18c1f26SAndrey V. Elsukov 		break;
346d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
347*4a77657cSAndrey V. Elsukov 		pg = nat64lsn_get_pg(&link->alias->udp_chunkmask,
348*4a77657cSAndrey V. Elsukov 		    link->alias->udp_pgmask, link->alias->udp,
349d18c1f26SAndrey V. Elsukov 		    &link->alias->udp_pgidx, faddr);
350d18c1f26SAndrey V. Elsukov 		break;
351d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
352*4a77657cSAndrey V. Elsukov 		pg = nat64lsn_get_pg(&link->alias->icmp_chunkmask,
353*4a77657cSAndrey V. Elsukov 		    link->alias->icmp_pgmask, link->alias->icmp,
354d18c1f26SAndrey V. Elsukov 		    &link->alias->icmp_pgidx, faddr);
355d18c1f26SAndrey V. Elsukov 		break;
356d18c1f26SAndrey V. Elsukov 	default:
357d18c1f26SAndrey V. Elsukov 		panic("%s: wrong proto %d", __func__, proto);
358d18c1f26SAndrey V. Elsukov 	}
359*4a77657cSAndrey V. Elsukov 	if (pg == NULL || (pg->flags & NAT64LSN_DEADPG) != 0)
360d18c1f26SAndrey V. Elsukov 		return (NULL);
361d18c1f26SAndrey V. Elsukov 
362d18c1f26SAndrey V. Elsukov 	/* Check that PG has some free states */
363d18c1f26SAndrey V. Elsukov 	state = NULL;
364d18c1f26SAndrey V. Elsukov 	i = FREEMASK_BITCOUNT(pg, faddr);
365d18c1f26SAndrey V. Elsukov 	while (i-- > 0) {
366d18c1f26SAndrey V. Elsukov 		offset = FREEMASK_FFSLL(pg, faddr);
367d18c1f26SAndrey V. Elsukov 		if (offset == 0) {
368d18c1f26SAndrey V. Elsukov 			/*
369d18c1f26SAndrey V. Elsukov 			 * We lost the race.
370d18c1f26SAndrey V. Elsukov 			 * No more free states in this PG.
371d18c1f26SAndrey V. Elsukov 			 */
372d18c1f26SAndrey V. Elsukov 			break;
373d18c1f26SAndrey V. Elsukov 		}
374d18c1f26SAndrey V. Elsukov 
375d18c1f26SAndrey V. Elsukov 		/* Lets try to atomically grab the state */
376d18c1f26SAndrey V. Elsukov 		if (FREEMASK_BTR(pg, faddr, offset - 1)) {
377d18c1f26SAndrey V. Elsukov 			state = &STATES_CHUNK(pg, faddr)->state[offset - 1];
378d18c1f26SAndrey V. Elsukov 			/* Initialize */
379d18c1f26SAndrey V. Elsukov 			state->flags = proto != IPPROTO_TCP ? 0 :
380d18c1f26SAndrey V. Elsukov 			    convert_tcp_flags(f_id->_flags);
381d18c1f26SAndrey V. Elsukov 			state->proto = proto;
382d18c1f26SAndrey V. Elsukov 			state->aport = pg->base_port + offset - 1;
383d18c1f26SAndrey V. Elsukov 			state->dport = f_id->dst_port;
384d18c1f26SAndrey V. Elsukov 			state->sport = port;
385d18c1f26SAndrey V. Elsukov 			state->ip6_dst = f_id->dst_ip6;
386d18c1f26SAndrey V. Elsukov 			state->ip_dst = faddr;
387d18c1f26SAndrey V. Elsukov 			state->ip_src = link->alias->addr;
388d18c1f26SAndrey V. Elsukov 			state->hval = hval;
389d18c1f26SAndrey V. Elsukov 			state->host = host;
390d18c1f26SAndrey V. Elsukov 			SET_AGE(state->timestamp);
391d18c1f26SAndrey V. Elsukov 
392d18c1f26SAndrey V. Elsukov 			/* Insert new state into host's hash table */
393d18c1f26SAndrey V. Elsukov 			HOST_LOCK(host);
394*4a77657cSAndrey V. Elsukov 			SET_AGE(host->timestamp);
395d18c1f26SAndrey V. Elsukov 			CK_SLIST_INSERT_HEAD(&STATE_HASH(host, hval),
396d18c1f26SAndrey V. Elsukov 			    state, entries);
397d18c1f26SAndrey V. Elsukov 			host->states_count++;
398d18c1f26SAndrey V. Elsukov 			HOST_UNLOCK(host);
399d18c1f26SAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, screated);
400d18c1f26SAndrey V. Elsukov 			/* Mark the state as ready for translate4 */
401d18c1f26SAndrey V. Elsukov 			ck_pr_fence_store();
402d18c1f26SAndrey V. Elsukov 			ck_pr_bts_32(&state->flags, NAT64_BIT_READY_IPV4);
403d18c1f26SAndrey V. Elsukov 			break;
404d18c1f26SAndrey V. Elsukov 		}
405d18c1f26SAndrey V. Elsukov 	}
406d18c1f26SAndrey V. Elsukov 	return (state);
407d18c1f26SAndrey V. Elsukov }
408d18c1f26SAndrey V. Elsukov 
409d18c1f26SAndrey V. Elsukov /*
410d18c1f26SAndrey V. Elsukov  * Inspects icmp packets to see if the message contains different
411d18c1f26SAndrey V. Elsukov  * packet header so we need to alter @addr and @port.
412d18c1f26SAndrey V. Elsukov  */
413d18c1f26SAndrey V. Elsukov static int
inspect_icmp_mbuf(struct mbuf ** mp,uint8_t * proto,uint32_t * addr,uint16_t * port)414d18c1f26SAndrey V. Elsukov inspect_icmp_mbuf(struct mbuf **mp, uint8_t *proto, uint32_t *addr,
415d18c1f26SAndrey V. Elsukov     uint16_t *port)
416d18c1f26SAndrey V. Elsukov {
417d18c1f26SAndrey V. Elsukov 	struct icmp *icmp;
418d18c1f26SAndrey V. Elsukov 	struct ip *ip;
419d18c1f26SAndrey V. Elsukov 	int off;
420d18c1f26SAndrey V. Elsukov 	uint8_t inner_proto;
421d18c1f26SAndrey V. Elsukov 
422d18c1f26SAndrey V. Elsukov 	ip = mtod(*mp, struct ip *); /* Outer IP header */
423d18c1f26SAndrey V. Elsukov 	off = (ip->ip_hl << 2) + ICMP_MINLEN;
424d18c1f26SAndrey V. Elsukov 	if ((*mp)->m_len < off)
425d18c1f26SAndrey V. Elsukov 		*mp = m_pullup(*mp, off);
426d18c1f26SAndrey V. Elsukov 	if (*mp == NULL)
427d18c1f26SAndrey V. Elsukov 		return (ENOMEM);
428d18c1f26SAndrey V. Elsukov 
429d18c1f26SAndrey V. Elsukov 	ip = mtod(*mp, struct ip *); /* Outer IP header */
430d18c1f26SAndrey V. Elsukov 	icmp = L3HDR(ip, struct icmp *);
431d18c1f26SAndrey V. Elsukov 	switch (icmp->icmp_type) {
432d18c1f26SAndrey V. Elsukov 	case ICMP_ECHO:
433d18c1f26SAndrey V. Elsukov 	case ICMP_ECHOREPLY:
434d18c1f26SAndrey V. Elsukov 		/* Use icmp ID as distinguisher */
435d18c1f26SAndrey V. Elsukov 		*port = ntohs(icmp->icmp_id);
436d18c1f26SAndrey V. Elsukov 		return (0);
437d18c1f26SAndrey V. Elsukov 	case ICMP_UNREACH:
438d18c1f26SAndrey V. Elsukov 	case ICMP_TIMXCEED:
439d18c1f26SAndrey V. Elsukov 		break;
440d18c1f26SAndrey V. Elsukov 	default:
441d18c1f26SAndrey V. Elsukov 		return (EOPNOTSUPP);
442d18c1f26SAndrey V. Elsukov 	}
443d18c1f26SAndrey V. Elsukov 	/*
444d18c1f26SAndrey V. Elsukov 	 * ICMP_UNREACH and ICMP_TIMXCEED contains IP header + 64 bits
445d18c1f26SAndrey V. Elsukov 	 * of ULP header.
446d18c1f26SAndrey V. Elsukov 	 */
447d18c1f26SAndrey V. Elsukov 	if ((*mp)->m_pkthdr.len < off + sizeof(struct ip) + ICMP_MINLEN)
448d18c1f26SAndrey V. Elsukov 		return (EINVAL);
449d18c1f26SAndrey V. Elsukov 	if ((*mp)->m_len < off + sizeof(struct ip) + ICMP_MINLEN)
450d18c1f26SAndrey V. Elsukov 		*mp = m_pullup(*mp, off + sizeof(struct ip) + ICMP_MINLEN);
451d18c1f26SAndrey V. Elsukov 	if (*mp == NULL)
452d18c1f26SAndrey V. Elsukov 		return (ENOMEM);
453d18c1f26SAndrey V. Elsukov 	ip = mtodo(*mp, off); /* Inner IP header */
454d18c1f26SAndrey V. Elsukov 	inner_proto = ip->ip_p;
455d18c1f26SAndrey V. Elsukov 	off += ip->ip_hl << 2; /* Skip inner IP header */
456d18c1f26SAndrey V. Elsukov 	*addr = ntohl(ip->ip_src.s_addr);
457d18c1f26SAndrey V. Elsukov 	if ((*mp)->m_len < off + ICMP_MINLEN)
458d18c1f26SAndrey V. Elsukov 		*mp = m_pullup(*mp, off + ICMP_MINLEN);
459d18c1f26SAndrey V. Elsukov 	if (*mp == NULL)
460d18c1f26SAndrey V. Elsukov 		return (ENOMEM);
461d18c1f26SAndrey V. Elsukov 	switch (inner_proto) {
462d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
463d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
464d18c1f26SAndrey V. Elsukov 		/* Copy source port from the header */
465d18c1f26SAndrey V. Elsukov 		*port = ntohs(*((uint16_t *)mtodo(*mp, off)));
466d18c1f26SAndrey V. Elsukov 		*proto = inner_proto;
467d18c1f26SAndrey V. Elsukov 		return (0);
468d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
469d18c1f26SAndrey V. Elsukov 		/*
470d18c1f26SAndrey V. Elsukov 		 * We will translate only ICMP errors for our ICMP
471d18c1f26SAndrey V. Elsukov 		 * echo requests.
472d18c1f26SAndrey V. Elsukov 		 */
473d18c1f26SAndrey V. Elsukov 		icmp = mtodo(*mp, off);
474d18c1f26SAndrey V. Elsukov 		if (icmp->icmp_type != ICMP_ECHO)
475d18c1f26SAndrey V. Elsukov 			return (EOPNOTSUPP);
476d18c1f26SAndrey V. Elsukov 		*port = ntohs(icmp->icmp_id);
477d18c1f26SAndrey V. Elsukov 		return (0);
478d18c1f26SAndrey V. Elsukov 	};
479d18c1f26SAndrey V. Elsukov 	return (EOPNOTSUPP);
480d18c1f26SAndrey V. Elsukov }
481d18c1f26SAndrey V. Elsukov 
482d18c1f26SAndrey V. Elsukov static struct nat64lsn_state*
nat64lsn_get_state4to6(struct nat64lsn_cfg * cfg,struct nat64lsn_alias * alias,in_addr_t faddr,uint16_t port,uint8_t proto)483d18c1f26SAndrey V. Elsukov nat64lsn_get_state4to6(struct nat64lsn_cfg *cfg, struct nat64lsn_alias *alias,
484d18c1f26SAndrey V. Elsukov     in_addr_t faddr, uint16_t port, uint8_t proto)
485d18c1f26SAndrey V. Elsukov {
486d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
487d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pg *pg;
488d18c1f26SAndrey V. Elsukov 	int chunk_idx, pg_idx, state_idx;
489d18c1f26SAndrey V. Elsukov 
490d18c1f26SAndrey V. Elsukov 	NAT64LSN_EPOCH_ASSERT();
491d18c1f26SAndrey V. Elsukov 
492d18c1f26SAndrey V. Elsukov 	if (port < NAT64_MIN_PORT)
493d18c1f26SAndrey V. Elsukov 		return (NULL);
494d18c1f26SAndrey V. Elsukov 	/*
495d18c1f26SAndrey V. Elsukov 	 * Alias keeps 32 pgchunks for each protocol.
496d18c1f26SAndrey V. Elsukov 	 * Each pgchunk has 32 pointers to portgroup.
497d18c1f26SAndrey V. Elsukov 	 * Each portgroup has 64 states for ports.
498d18c1f26SAndrey V. Elsukov 	 */
499d18c1f26SAndrey V. Elsukov 	port -= NAT64_MIN_PORT;
500d18c1f26SAndrey V. Elsukov 	chunk_idx = port / 2048;
501d18c1f26SAndrey V. Elsukov 
502d18c1f26SAndrey V. Elsukov 	port -= chunk_idx * 2048;
503d18c1f26SAndrey V. Elsukov 	pg_idx = port / 64;
504d18c1f26SAndrey V. Elsukov 	state_idx = port % 64;
505d18c1f26SAndrey V. Elsukov 
506d18c1f26SAndrey V. Elsukov 	/*
507d18c1f26SAndrey V. Elsukov 	 * First check in proto_chunkmask that we have allocated PG chunk.
508d18c1f26SAndrey V. Elsukov 	 * Then check in proto_pgmask that we have valid PG pointer.
509d18c1f26SAndrey V. Elsukov 	 */
510d18c1f26SAndrey V. Elsukov 	pg = NULL;
511d18c1f26SAndrey V. Elsukov 	switch (proto) {
512d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
513d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->tcp_chunkmask, chunk_idx) &&
514d18c1f26SAndrey V. Elsukov 		    ISSET32(alias->tcp_pgmask[chunk_idx], pg_idx)) {
515d18c1f26SAndrey V. Elsukov 			pg = alias->tcp[chunk_idx]->pgptr[pg_idx];
516d18c1f26SAndrey V. Elsukov 			break;
517d18c1f26SAndrey V. Elsukov 		}
518d18c1f26SAndrey V. Elsukov 		return (NULL);
519d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
520d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->udp_chunkmask, chunk_idx) &&
521d18c1f26SAndrey V. Elsukov 		    ISSET32(alias->udp_pgmask[chunk_idx], pg_idx)) {
522d18c1f26SAndrey V. Elsukov 			pg = alias->udp[chunk_idx]->pgptr[pg_idx];
523d18c1f26SAndrey V. Elsukov 			break;
524d18c1f26SAndrey V. Elsukov 		}
525d18c1f26SAndrey V. Elsukov 		return (NULL);
526d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
527d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->icmp_chunkmask, chunk_idx) &&
528d18c1f26SAndrey V. Elsukov 		    ISSET32(alias->icmp_pgmask[chunk_idx], pg_idx)) {
529d18c1f26SAndrey V. Elsukov 			pg = alias->icmp[chunk_idx]->pgptr[pg_idx];
530d18c1f26SAndrey V. Elsukov 			break;
531d18c1f26SAndrey V. Elsukov 		}
532d18c1f26SAndrey V. Elsukov 		return (NULL);
533d18c1f26SAndrey V. Elsukov 	default:
534d18c1f26SAndrey V. Elsukov 		panic("%s: wrong proto %d", __func__, proto);
535d18c1f26SAndrey V. Elsukov 	}
536d18c1f26SAndrey V. Elsukov 	if (pg == NULL)
537d18c1f26SAndrey V. Elsukov 		return (NULL);
538d18c1f26SAndrey V. Elsukov 
539d18c1f26SAndrey V. Elsukov 	if (FREEMASK_ISSET(pg, faddr, state_idx))
540d18c1f26SAndrey V. Elsukov 		return (NULL);
541d18c1f26SAndrey V. Elsukov 
542d18c1f26SAndrey V. Elsukov 	state = &STATES_CHUNK(pg, faddr)->state[state_idx];
543d18c1f26SAndrey V. Elsukov 	ck_pr_fence_load();
544d18c1f26SAndrey V. Elsukov 	if (ck_pr_load_32(&state->flags) & NAT64_FLAG_READY)
545d18c1f26SAndrey V. Elsukov 		return (state);
546d18c1f26SAndrey V. Elsukov 	return (NULL);
547d18c1f26SAndrey V. Elsukov }
548d18c1f26SAndrey V. Elsukov 
549f909db0bSAndrey V. Elsukov /*
550f909db0bSAndrey V. Elsukov  * Reassemble IPv4 fragments, make PULLUP if needed, get some ULP fields
551f909db0bSAndrey V. Elsukov  * that might be unknown until reassembling is completed.
552f909db0bSAndrey V. Elsukov  */
553f909db0bSAndrey V. Elsukov static struct mbuf*
nat64lsn_reassemble4(struct nat64lsn_cfg * cfg,struct mbuf * m,uint16_t * port)554f909db0bSAndrey V. Elsukov nat64lsn_reassemble4(struct nat64lsn_cfg *cfg, struct mbuf *m,
555f909db0bSAndrey V. Elsukov     uint16_t *port)
556f909db0bSAndrey V. Elsukov {
557f909db0bSAndrey V. Elsukov 	struct ip *ip;
558f909db0bSAndrey V. Elsukov 	int len;
559f909db0bSAndrey V. Elsukov 
560f909db0bSAndrey V. Elsukov 	m = ip_reass(m);
561f909db0bSAndrey V. Elsukov 	if (m == NULL)
562f909db0bSAndrey V. Elsukov 		return (NULL);
563f909db0bSAndrey V. Elsukov 	/* IP header must be contigious after ip_reass() */
564f909db0bSAndrey V. Elsukov 	ip = mtod(m, struct ip *);
565f909db0bSAndrey V. Elsukov 	len = ip->ip_hl << 2;
566f909db0bSAndrey V. Elsukov 	switch (ip->ip_p) {
567f909db0bSAndrey V. Elsukov 	case IPPROTO_ICMP:
568*4a77657cSAndrey V. Elsukov 		len += ICMP_MINLEN;
569f909db0bSAndrey V. Elsukov 		break;
570f909db0bSAndrey V. Elsukov 	case IPPROTO_TCP:
571f909db0bSAndrey V. Elsukov 		len += sizeof(struct tcphdr);
572f909db0bSAndrey V. Elsukov 		break;
573f909db0bSAndrey V. Elsukov 	case IPPROTO_UDP:
574f909db0bSAndrey V. Elsukov 		len += sizeof(struct udphdr);
575f909db0bSAndrey V. Elsukov 		break;
576f909db0bSAndrey V. Elsukov 	default:
577f909db0bSAndrey V. Elsukov 		m_freem(m);
578f909db0bSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, noproto);
579f909db0bSAndrey V. Elsukov 		return (NULL);
580f909db0bSAndrey V. Elsukov 	}
581f909db0bSAndrey V. Elsukov 	if (m->m_len < len) {
582f909db0bSAndrey V. Elsukov 		m = m_pullup(m, len);
583f909db0bSAndrey V. Elsukov 		if (m == NULL) {
584f909db0bSAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, nomem);
585f909db0bSAndrey V. Elsukov 			return (NULL);
586f909db0bSAndrey V. Elsukov 		}
587f909db0bSAndrey V. Elsukov 		ip = mtod(m, struct ip *);
588f909db0bSAndrey V. Elsukov 	}
589f909db0bSAndrey V. Elsukov 	switch (ip->ip_p) {
590f909db0bSAndrey V. Elsukov 	case IPPROTO_TCP:
591f909db0bSAndrey V. Elsukov 		*port = ntohs(L3HDR(ip, struct tcphdr *)->th_dport);
592f909db0bSAndrey V. Elsukov 		break;
593f909db0bSAndrey V. Elsukov 	case IPPROTO_UDP:
594f909db0bSAndrey V. Elsukov 		*port = ntohs(L3HDR(ip, struct udphdr *)->uh_dport);
595f909db0bSAndrey V. Elsukov 		break;
596f909db0bSAndrey V. Elsukov 	}
597f909db0bSAndrey V. Elsukov 	return (m);
598f909db0bSAndrey V. Elsukov }
599f909db0bSAndrey V. Elsukov 
600d18c1f26SAndrey V. Elsukov static int
nat64lsn_translate4(struct nat64lsn_cfg * cfg,const struct ipfw_flow_id * f_id,struct mbuf ** mp)601d18c1f26SAndrey V. Elsukov nat64lsn_translate4(struct nat64lsn_cfg *cfg,
602d18c1f26SAndrey V. Elsukov     const struct ipfw_flow_id *f_id, struct mbuf **mp)
603d8caf56eSAndrey V. Elsukov {
604d8caf56eSAndrey V. Elsukov 	struct pfloghdr loghdr, *logdata;
605d8caf56eSAndrey V. Elsukov 	struct in6_addr src6;
606d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
607d18c1f26SAndrey V. Elsukov 	struct nat64lsn_alias *alias;
608d18c1f26SAndrey V. Elsukov 	uint32_t addr, flags;
609d18c1f26SAndrey V. Elsukov 	uint16_t port, ts;
610d8caf56eSAndrey V. Elsukov 	int ret;
611d18c1f26SAndrey V. Elsukov 	uint8_t proto;
612d8caf56eSAndrey V. Elsukov 
613d8caf56eSAndrey V. Elsukov 	addr = f_id->dst_ip;
614d8caf56eSAndrey V. Elsukov 	port = f_id->dst_port;
615d18c1f26SAndrey V. Elsukov 	proto = f_id->proto;
616d8caf56eSAndrey V. Elsukov 	if (addr < cfg->prefix4 || addr > cfg->pmask4) {
617782360deSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, nomatch4);
618d8caf56eSAndrey V. Elsukov 		return (cfg->nomatch_verdict);
619d8caf56eSAndrey V. Elsukov 	}
620d8caf56eSAndrey V. Elsukov 
621f909db0bSAndrey V. Elsukov 	/* Reassemble fragments if needed */
622f909db0bSAndrey V. Elsukov 	ret = ntohs(mtod(*mp, struct ip *)->ip_off);
623f909db0bSAndrey V. Elsukov 	if ((ret & (IP_MF | IP_OFFMASK)) != 0) {
624f909db0bSAndrey V. Elsukov 		*mp = nat64lsn_reassemble4(cfg, *mp, &port);
625f909db0bSAndrey V. Elsukov 		if (*mp == NULL)
626f909db0bSAndrey V. Elsukov 			return (IP_FW_DENY);
627f909db0bSAndrey V. Elsukov 	}
628f909db0bSAndrey V. Elsukov 
629d18c1f26SAndrey V. Elsukov 	/* Check if protocol is supported */
630d18c1f26SAndrey V. Elsukov 	switch (proto) {
631d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
632d18c1f26SAndrey V. Elsukov 		ret = inspect_icmp_mbuf(mp, &proto, &addr, &port);
633d8caf56eSAndrey V. Elsukov 		if (ret != 0) {
634eed30257SAndrey V. Elsukov 			if (ret == ENOMEM) {
635782360deSAndrey V. Elsukov 				NAT64STAT_INC(&cfg->base.stats, nomem);
636eed30257SAndrey V. Elsukov 				return (IP_FW_DENY);
637eed30257SAndrey V. Elsukov 			}
638782360deSAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, noproto);
639d8caf56eSAndrey V. Elsukov 			return (cfg->nomatch_verdict);
640d8caf56eSAndrey V. Elsukov 		}
641d8caf56eSAndrey V. Elsukov 		if (addr < cfg->prefix4 || addr > cfg->pmask4) {
642782360deSAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, nomatch4);
643d8caf56eSAndrey V. Elsukov 			return (cfg->nomatch_verdict);
644d8caf56eSAndrey V. Elsukov 		}
645d18c1f26SAndrey V. Elsukov 		/* FALLTHROUGH */
646d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
647d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
648d18c1f26SAndrey V. Elsukov 		break;
649d18c1f26SAndrey V. Elsukov 	default:
650d18c1f26SAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, noproto);
651d18c1f26SAndrey V. Elsukov 		return (cfg->nomatch_verdict);
652d8caf56eSAndrey V. Elsukov 	}
653d8caf56eSAndrey V. Elsukov 
654d18c1f26SAndrey V. Elsukov 	alias = &ALIAS_BYHASH(cfg, addr);
655d18c1f26SAndrey V. Elsukov 	MPASS(addr == alias->addr);
656d8caf56eSAndrey V. Elsukov 
657d18c1f26SAndrey V. Elsukov 	/* Check that we have state for this port */
658d18c1f26SAndrey V. Elsukov 	state = nat64lsn_get_state4to6(cfg, alias, f_id->src_ip,
659d18c1f26SAndrey V. Elsukov 	    port, proto);
660d18c1f26SAndrey V. Elsukov 	if (state == NULL) {
661782360deSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, nomatch4);
662d8caf56eSAndrey V. Elsukov 		return (cfg->nomatch_verdict);
663d8caf56eSAndrey V. Elsukov 	}
664d8caf56eSAndrey V. Elsukov 
665d8caf56eSAndrey V. Elsukov 	/* TODO: Check flags to see if we need to do some static mapping */
666d8caf56eSAndrey V. Elsukov 
667d18c1f26SAndrey V. Elsukov 	/* Update some state fields if need */
668d18c1f26SAndrey V. Elsukov 	SET_AGE(ts);
669d18c1f26SAndrey V. Elsukov 	if (f_id->proto == IPPROTO_TCP)
670d18c1f26SAndrey V. Elsukov 		flags = convert_tcp_flags(f_id->_flags);
671d8caf56eSAndrey V. Elsukov 	else
672d18c1f26SAndrey V. Elsukov 		flags = 0;
673d18c1f26SAndrey V. Elsukov 	if (state->timestamp != ts)
674d18c1f26SAndrey V. Elsukov 		state->timestamp = ts;
675d18c1f26SAndrey V. Elsukov 	if ((state->flags & flags) != flags)
676d18c1f26SAndrey V. Elsukov 		state->flags |= flags;
677d8caf56eSAndrey V. Elsukov 
678d18c1f26SAndrey V. Elsukov 	port = htons(state->sport);
679d18c1f26SAndrey V. Elsukov 	src6 = state->ip6_dst;
680d8caf56eSAndrey V. Elsukov 
681782360deSAndrey V. Elsukov 	if (cfg->base.flags & NAT64_LOG) {
682d8caf56eSAndrey V. Elsukov 		logdata = &loghdr;
683d18c1f26SAndrey V. Elsukov 		nat64lsn_log(logdata, *mp, AF_INET, state);
684d8caf56eSAndrey V. Elsukov 	} else
685d8caf56eSAndrey V. Elsukov 		logdata = NULL;
686d8caf56eSAndrey V. Elsukov 
687d18c1f26SAndrey V. Elsukov 	/*
688d18c1f26SAndrey V. Elsukov 	 * We already have src6 with embedded address, but it is possible,
689d18c1f26SAndrey V. Elsukov 	 * that src_ip is different than state->ip_dst, this is why we
690d18c1f26SAndrey V. Elsukov 	 * do embedding again.
691d18c1f26SAndrey V. Elsukov 	 */
692b11efc1eSAndrey V. Elsukov 	nat64_embed_ip4(&src6, cfg->base.plat_plen, htonl(f_id->src_ip));
693d18c1f26SAndrey V. Elsukov 	ret = nat64_do_handle_ip4(*mp, &src6, &state->host->addr, port,
694782360deSAndrey V. Elsukov 	    &cfg->base, logdata);
695d8caf56eSAndrey V. Elsukov 	if (ret == NAT64SKIP)
696eed30257SAndrey V. Elsukov 		return (cfg->nomatch_verdict);
697d18c1f26SAndrey V. Elsukov 	if (ret == NAT64RETURN)
698d18c1f26SAndrey V. Elsukov 		*mp = NULL;
699d8caf56eSAndrey V. Elsukov 	return (IP_FW_DENY);
700d8caf56eSAndrey V. Elsukov }
701d8caf56eSAndrey V. Elsukov 
702d8caf56eSAndrey V. Elsukov /*
703d18c1f26SAndrey V. Elsukov  * Check if particular state is stale and should be deleted.
704d8caf56eSAndrey V. Elsukov  * Return 1 if true, 0 otherwise.
705d8caf56eSAndrey V. Elsukov  */
706d8caf56eSAndrey V. Elsukov static int
nat64lsn_check_state(struct nat64lsn_cfg * cfg,struct nat64lsn_state * state)707d18c1f26SAndrey V. Elsukov nat64lsn_check_state(struct nat64lsn_cfg *cfg, struct nat64lsn_state *state)
708d8caf56eSAndrey V. Elsukov {
709d18c1f26SAndrey V. Elsukov 	int age, ttl;
710d8caf56eSAndrey V. Elsukov 
711d18c1f26SAndrey V. Elsukov 	/* State was marked as stale in previous pass. */
712d18c1f26SAndrey V. Elsukov 	if (ISSET32(state->flags, NAT64_BIT_STALE))
713d18c1f26SAndrey V. Elsukov 		return (1);
714d18c1f26SAndrey V. Elsukov 
715d18c1f26SAndrey V. Elsukov 	/* State is not yet initialized, it is going to be READY */
716d18c1f26SAndrey V. Elsukov 	if (!ISSET32(state->flags, NAT64_BIT_READY_IPV4))
717d18c1f26SAndrey V. Elsukov 		return (0);
718d18c1f26SAndrey V. Elsukov 
719d18c1f26SAndrey V. Elsukov 	age = GET_AGE(state->timestamp);
720d18c1f26SAndrey V. Elsukov 	switch (state->proto) {
721d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
722d18c1f26SAndrey V. Elsukov 		if (ISSET32(state->flags, NAT64_BIT_TCP_FIN))
723d8caf56eSAndrey V. Elsukov 			ttl = cfg->st_close_ttl;
724d18c1f26SAndrey V. Elsukov 		else if (ISSET32(state->flags, NAT64_BIT_TCP_ESTAB))
725d8caf56eSAndrey V. Elsukov 			ttl = cfg->st_estab_ttl;
726d18c1f26SAndrey V. Elsukov 		else if (ISSET32(state->flags, NAT64_BIT_TCP_SYN))
727d8caf56eSAndrey V. Elsukov 			ttl = cfg->st_syn_ttl;
728d8caf56eSAndrey V. Elsukov 		else
729d8caf56eSAndrey V. Elsukov 			ttl = cfg->st_syn_ttl;
730d8caf56eSAndrey V. Elsukov 		if (age > ttl)
731d8caf56eSAndrey V. Elsukov 			return (1);
732d6369c2dSAndrey V. Elsukov 		break;
733d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
734d6369c2dSAndrey V. Elsukov 		if (age > cfg->st_udp_ttl)
735d18c1f26SAndrey V. Elsukov 			return (1);
736d6369c2dSAndrey V. Elsukov 		break;
737d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
738d6369c2dSAndrey V. Elsukov 		if (age > cfg->st_icmp_ttl)
739d18c1f26SAndrey V. Elsukov 			return (1);
740d6369c2dSAndrey V. Elsukov 		break;
741d6369c2dSAndrey V. Elsukov 	}
742d6369c2dSAndrey V. Elsukov 	return (0);
743d18c1f26SAndrey V. Elsukov }
744d18c1f26SAndrey V. Elsukov 
745*4a77657cSAndrey V. Elsukov #define	PGCOUNT_ADD(alias, proto, value)			\
746*4a77657cSAndrey V. Elsukov     switch (proto) {						\
747*4a77657cSAndrey V. Elsukov     case IPPROTO_TCP: (alias)->tcp_pgcount += (value); break;	\
748*4a77657cSAndrey V. Elsukov     case IPPROTO_UDP: (alias)->udp_pgcount += (value); break;	\
749*4a77657cSAndrey V. Elsukov     case IPPROTO_ICMP: (alias)->icmp_pgcount += (value); break;	\
750*4a77657cSAndrey V. Elsukov     }
751*4a77657cSAndrey V. Elsukov #define	PGCOUNT_INC(alias, proto)	PGCOUNT_ADD(alias, proto, 1)
752*4a77657cSAndrey V. Elsukov #define	PGCOUNT_DEC(alias, proto)	PGCOUNT_ADD(alias, proto, -1)
753*4a77657cSAndrey V. Elsukov 
754*4a77657cSAndrey V. Elsukov static inline void
nat64lsn_state_cleanup(struct nat64lsn_state * state)755*4a77657cSAndrey V. Elsukov nat64lsn_state_cleanup(struct nat64lsn_state *state)
756*4a77657cSAndrey V. Elsukov {
757*4a77657cSAndrey V. Elsukov 
758*4a77657cSAndrey V. Elsukov 	/*
759*4a77657cSAndrey V. Elsukov 	 * Reset READY flag and wait until it become
760*4a77657cSAndrey V. Elsukov 	 * safe for translate4.
761*4a77657cSAndrey V. Elsukov 	 */
762*4a77657cSAndrey V. Elsukov 	ck_pr_btr_32(&state->flags, NAT64_BIT_READY_IPV4);
763*4a77657cSAndrey V. Elsukov 	/*
764*4a77657cSAndrey V. Elsukov 	 * And set STALE flag for deferred deletion in the
765*4a77657cSAndrey V. Elsukov 	 * next pass of nat64lsn_maintain_pg().
766*4a77657cSAndrey V. Elsukov 	 */
767*4a77657cSAndrey V. Elsukov 	ck_pr_bts_32(&state->flags, NAT64_BIT_STALE);
768*4a77657cSAndrey V. Elsukov 	ck_pr_fence_store();
769*4a77657cSAndrey V. Elsukov }
770*4a77657cSAndrey V. Elsukov 
771d18c1f26SAndrey V. Elsukov static int
nat64lsn_maintain_pg(struct nat64lsn_cfg * cfg,struct nat64lsn_pg * pg)772d18c1f26SAndrey V. Elsukov nat64lsn_maintain_pg(struct nat64lsn_cfg *cfg, struct nat64lsn_pg *pg)
773d18c1f26SAndrey V. Elsukov {
774d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
775d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host;
776d18c1f26SAndrey V. Elsukov 	uint64_t freemask;
777d18c1f26SAndrey V. Elsukov 	int c, i, update_age;
778d18c1f26SAndrey V. Elsukov 
779d18c1f26SAndrey V. Elsukov 	update_age = 0;
780d18c1f26SAndrey V. Elsukov 	for (c = 0; c < pg->chunks_count; c++) {
781d18c1f26SAndrey V. Elsukov 		FREEMASK_COPY(pg, c, freemask);
782d18c1f26SAndrey V. Elsukov 		for (i = 0; i < 64; i++) {
783d18c1f26SAndrey V. Elsukov 			if (ISSET64(freemask, i))
784d18c1f26SAndrey V. Elsukov 				continue;
785d18c1f26SAndrey V. Elsukov 			state = &STATES_CHUNK(pg, c)->state[i];
786d18c1f26SAndrey V. Elsukov 			if (nat64lsn_check_state(cfg, state) == 0) {
787d18c1f26SAndrey V. Elsukov 				update_age = 1;
788d18c1f26SAndrey V. Elsukov 				continue;
789d18c1f26SAndrey V. Elsukov 			}
790d18c1f26SAndrey V. Elsukov 			/*
791d18c1f26SAndrey V. Elsukov 			 * Expire state:
792d18c1f26SAndrey V. Elsukov 			 * 1. Mark as STALE and unlink from host's hash.
793d18c1f26SAndrey V. Elsukov 			 * 2. Set bit in freemask.
794d18c1f26SAndrey V. Elsukov 			 */
795d18c1f26SAndrey V. Elsukov 			if (ISSET32(state->flags, NAT64_BIT_STALE)) {
796d18c1f26SAndrey V. Elsukov 				/*
797d18c1f26SAndrey V. Elsukov 				 * State was marked as STALE in previous
798d18c1f26SAndrey V. Elsukov 				 * pass. Now it is safe to release it.
799d18c1f26SAndrey V. Elsukov 				 */
800d18c1f26SAndrey V. Elsukov 				state->flags = 0;
801d18c1f26SAndrey V. Elsukov 				ck_pr_fence_store();
802d18c1f26SAndrey V. Elsukov 				FREEMASK_BTS(pg, c, i);
803d18c1f26SAndrey V. Elsukov 				NAT64STAT_INC(&cfg->base.stats, sdeleted);
804d18c1f26SAndrey V. Elsukov 				continue;
805d18c1f26SAndrey V. Elsukov 			}
806d18c1f26SAndrey V. Elsukov 			MPASS(state->flags & NAT64_FLAG_READY);
807d18c1f26SAndrey V. Elsukov 
808d18c1f26SAndrey V. Elsukov 			host = state->host;
809d18c1f26SAndrey V. Elsukov 			HOST_LOCK(host);
810d18c1f26SAndrey V. Elsukov 			CK_SLIST_REMOVE(&STATE_HASH(host, state->hval),
811d18c1f26SAndrey V. Elsukov 			    state, nat64lsn_state, entries);
812*4a77657cSAndrey V. Elsukov 			/*
813*4a77657cSAndrey V. Elsukov 			 * Now translate6 will not use this state.
814*4a77657cSAndrey V. Elsukov 			 */
815d18c1f26SAndrey V. Elsukov 			host->states_count--;
816d18c1f26SAndrey V. Elsukov 			HOST_UNLOCK(host);
817*4a77657cSAndrey V. Elsukov 			nat64lsn_state_cleanup(state);
818d18c1f26SAndrey V. Elsukov 		}
819d18c1f26SAndrey V. Elsukov 	}
820d18c1f26SAndrey V. Elsukov 
821d18c1f26SAndrey V. Elsukov 	/*
822d18c1f26SAndrey V. Elsukov 	 * We have some alive states, update timestamp.
823d18c1f26SAndrey V. Elsukov 	 */
824d18c1f26SAndrey V. Elsukov 	if (update_age)
825d18c1f26SAndrey V. Elsukov 		SET_AGE(pg->timestamp);
826d18c1f26SAndrey V. Elsukov 
827d8caf56eSAndrey V. Elsukov 	if (GET_AGE(pg->timestamp) < cfg->pg_delete_delay)
828d8caf56eSAndrey V. Elsukov 		return (0);
829d18c1f26SAndrey V. Elsukov 
830d8caf56eSAndrey V. Elsukov 	return (1);
831d8caf56eSAndrey V. Elsukov }
832d8caf56eSAndrey V. Elsukov 
833d18c1f26SAndrey V. Elsukov static void
nat64lsn_expire_portgroups(struct nat64lsn_cfg * cfg,struct nat64lsn_pg_slist * portgroups)834d18c1f26SAndrey V. Elsukov nat64lsn_expire_portgroups(struct nat64lsn_cfg *cfg,
835d18c1f26SAndrey V. Elsukov     struct nat64lsn_pg_slist *portgroups)
836d18c1f26SAndrey V. Elsukov {
837d18c1f26SAndrey V. Elsukov 	struct nat64lsn_alias *alias;
838*4a77657cSAndrey V. Elsukov 	struct nat64lsn_pg *pg, *tpg;
839d18c1f26SAndrey V. Elsukov 	uint32_t *pgmask, *pgidx;
840d18c1f26SAndrey V. Elsukov 	int i, idx;
841d18c1f26SAndrey V. Elsukov 
842d18c1f26SAndrey V. Elsukov 	for (i = 0; i < 1 << (32 - cfg->plen4); i++) {
843d18c1f26SAndrey V. Elsukov 		alias = &cfg->aliases[i];
844d18c1f26SAndrey V. Elsukov 		CK_SLIST_FOREACH_SAFE(pg, &alias->portgroups, entries, tpg) {
845d18c1f26SAndrey V. Elsukov 			if (nat64lsn_maintain_pg(cfg, pg) == 0)
846d18c1f26SAndrey V. Elsukov 				continue;
847d18c1f26SAndrey V. Elsukov 			/* Always keep first PG */
848d18c1f26SAndrey V. Elsukov 			if (pg->base_port == NAT64_MIN_PORT)
849d18c1f26SAndrey V. Elsukov 				continue;
850d8caf56eSAndrey V. Elsukov 			/*
851*4a77657cSAndrey V. Elsukov 			 * PG expires in two passes:
852*4a77657cSAndrey V. Elsukov 			 * 1. Reset bit in pgmask, mark it as DEAD.
853*4a77657cSAndrey V. Elsukov 			 * 2. Unlink it and schedule for deferred destroying.
854d8caf56eSAndrey V. Elsukov 			 */
855d18c1f26SAndrey V. Elsukov 			idx = (pg->base_port - NAT64_MIN_PORT) / 64;
856d18c1f26SAndrey V. Elsukov 			switch (pg->proto) {
857d18c1f26SAndrey V. Elsukov 			case IPPROTO_TCP:
858d18c1f26SAndrey V. Elsukov 				pgmask = alias->tcp_pgmask;
859d18c1f26SAndrey V. Elsukov 				pgidx = &alias->tcp_pgidx;
860d18c1f26SAndrey V. Elsukov 				break;
861d18c1f26SAndrey V. Elsukov 			case IPPROTO_UDP:
862d18c1f26SAndrey V. Elsukov 				pgmask = alias->udp_pgmask;
863d18c1f26SAndrey V. Elsukov 				pgidx = &alias->udp_pgidx;
864d18c1f26SAndrey V. Elsukov 				break;
865d18c1f26SAndrey V. Elsukov 			case IPPROTO_ICMP:
866d18c1f26SAndrey V. Elsukov 				pgmask = alias->icmp_pgmask;
867d18c1f26SAndrey V. Elsukov 				pgidx = &alias->icmp_pgidx;
868d18c1f26SAndrey V. Elsukov 				break;
869d18c1f26SAndrey V. Elsukov 			}
870*4a77657cSAndrey V. Elsukov 			if (pg->flags & NAT64LSN_DEADPG) {
871d18c1f26SAndrey V. Elsukov 				/* Unlink PG from alias's chain */
872d18c1f26SAndrey V. Elsukov 				ALIAS_LOCK(alias);
873d18c1f26SAndrey V. Elsukov 				CK_SLIST_REMOVE(&alias->portgroups, pg,
874d18c1f26SAndrey V. Elsukov 				    nat64lsn_pg, entries);
875*4a77657cSAndrey V. Elsukov 				PGCOUNT_DEC(alias, pg->proto);
876d18c1f26SAndrey V. Elsukov 				ALIAS_UNLOCK(alias);
877*4a77657cSAndrey V. Elsukov 				/*
878*4a77657cSAndrey V. Elsukov 				 * Link it to job's chain for deferred
879*4a77657cSAndrey V. Elsukov 				 * destroying.
880*4a77657cSAndrey V. Elsukov 				 */
881d18c1f26SAndrey V. Elsukov 				NAT64STAT_INC(&cfg->base.stats, spgdeleted);
882d18c1f26SAndrey V. Elsukov 				CK_SLIST_INSERT_HEAD(portgroups, pg, entries);
883*4a77657cSAndrey V. Elsukov 				continue;
884*4a77657cSAndrey V. Elsukov 			}
885*4a77657cSAndrey V. Elsukov 
886*4a77657cSAndrey V. Elsukov 			/* Reset the corresponding bit in pgmask array. */
887*4a77657cSAndrey V. Elsukov 			ck_pr_btr_32(&pgmask[idx / 32], idx % 32);
888*4a77657cSAndrey V. Elsukov 			pg->flags |= NAT64LSN_DEADPG;
889*4a77657cSAndrey V. Elsukov 			ck_pr_fence_store();
890*4a77657cSAndrey V. Elsukov 			/* If last used PG points to this PG, reset it. */
891*4a77657cSAndrey V. Elsukov 			ck_pr_cas_32(pgidx, idx, 0);
892d18c1f26SAndrey V. Elsukov 		}
893d18c1f26SAndrey V. Elsukov 	}
894d18c1f26SAndrey V. Elsukov }
895d18c1f26SAndrey V. Elsukov 
896d18c1f26SAndrey V. Elsukov static void
nat64lsn_expire_hosts(struct nat64lsn_cfg * cfg,struct nat64lsn_hosts_slist * hosts)897d18c1f26SAndrey V. Elsukov nat64lsn_expire_hosts(struct nat64lsn_cfg *cfg,
898d18c1f26SAndrey V. Elsukov     struct nat64lsn_hosts_slist *hosts)
899d8caf56eSAndrey V. Elsukov {
900d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host, *tmp;
901d18c1f26SAndrey V. Elsukov 	int i;
902d8caf56eSAndrey V. Elsukov 
903d18c1f26SAndrey V. Elsukov 	for (i = 0; i < cfg->hosts_hashsize; i++) {
904d18c1f26SAndrey V. Elsukov 		CK_SLIST_FOREACH_SAFE(host, &cfg->hosts_hash[i],
905d18c1f26SAndrey V. Elsukov 		    entries, tmp) {
906d18c1f26SAndrey V. Elsukov 			/* Is host was marked in previous call? */
907d18c1f26SAndrey V. Elsukov 			if (host->flags & NAT64LSN_DEADHOST) {
908*4a77657cSAndrey V. Elsukov 				if (host->states_count > 0 ||
909*4a77657cSAndrey V. Elsukov 				    GET_AGE(host->timestamp) <
910*4a77657cSAndrey V. Elsukov 				    cfg->host_delete_delay) {
911d18c1f26SAndrey V. Elsukov 					host->flags &= ~NAT64LSN_DEADHOST;
912d18c1f26SAndrey V. Elsukov 					continue;
913d18c1f26SAndrey V. Elsukov 				}
914d18c1f26SAndrey V. Elsukov 				/*
915d18c1f26SAndrey V. Elsukov 				 * Unlink host from hash table and schedule
916d18c1f26SAndrey V. Elsukov 				 * it for deferred destroying.
917d18c1f26SAndrey V. Elsukov 				 */
918d18c1f26SAndrey V. Elsukov 				CFG_LOCK(cfg);
919d18c1f26SAndrey V. Elsukov 				CK_SLIST_REMOVE(&cfg->hosts_hash[i], host,
920d18c1f26SAndrey V. Elsukov 				    nat64lsn_host, entries);
921d18c1f26SAndrey V. Elsukov 				cfg->hosts_count--;
922d18c1f26SAndrey V. Elsukov 				CFG_UNLOCK(cfg);
923d18c1f26SAndrey V. Elsukov 				CK_SLIST_INSERT_HEAD(hosts, host, entries);
924d18c1f26SAndrey V. Elsukov 				continue;
925d18c1f26SAndrey V. Elsukov 			}
926*4a77657cSAndrey V. Elsukov 			if (host->states_count > 0 ||
927*4a77657cSAndrey V. Elsukov 			    GET_AGE(host->timestamp) < cfg->host_delete_delay)
928d18c1f26SAndrey V. Elsukov 				continue;
929d18c1f26SAndrey V. Elsukov 			/* Mark host as going to be expired in next pass */
930d18c1f26SAndrey V. Elsukov 			host->flags |= NAT64LSN_DEADHOST;
931d18c1f26SAndrey V. Elsukov 			ck_pr_fence_store();
932d18c1f26SAndrey V. Elsukov 		}
933d18c1f26SAndrey V. Elsukov 	}
934d8caf56eSAndrey V. Elsukov }
935d8caf56eSAndrey V. Elsukov 
936d18c1f26SAndrey V. Elsukov static struct nat64lsn_pgchunk*
nat64lsn_expire_pgchunk(struct nat64lsn_cfg * cfg)937d18c1f26SAndrey V. Elsukov nat64lsn_expire_pgchunk(struct nat64lsn_cfg *cfg)
938d8caf56eSAndrey V. Elsukov {
939d18c1f26SAndrey V. Elsukov #if 0
940d18c1f26SAndrey V. Elsukov 	struct nat64lsn_alias *alias;
941d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pgchunk *chunk;
942d18c1f26SAndrey V. Elsukov 	uint32_t pgmask;
943d18c1f26SAndrey V. Elsukov 	int i, c;
944d8caf56eSAndrey V. Elsukov 
945d18c1f26SAndrey V. Elsukov 	for (i = 0; i < 1 << (32 - cfg->plen4); i++) {
946d18c1f26SAndrey V. Elsukov 		alias = &cfg->aliases[i];
947d18c1f26SAndrey V. Elsukov 		if (GET_AGE(alias->timestamp) < cfg->pgchunk_delete_delay)
948d6369c2dSAndrey V. Elsukov 			continue;
949d18c1f26SAndrey V. Elsukov 		/* Always keep single chunk allocated */
950d18c1f26SAndrey V. Elsukov 		for (c = 1; c < 32; c++) {
951d18c1f26SAndrey V. Elsukov 			if ((alias->tcp_chunkmask & (1 << c)) == 0)
952d18c1f26SAndrey V. Elsukov 				break;
953d18c1f26SAndrey V. Elsukov 			chunk = ck_pr_load_ptr(&alias->tcp[c]);
954d18c1f26SAndrey V. Elsukov 			if (ck_pr_load_32(&alias->tcp_pgmask[c]) != 0)
955d6369c2dSAndrey V. Elsukov 				continue;
956d18c1f26SAndrey V. Elsukov 			ck_pr_btr_32(&alias->tcp_chunkmask, c);
957d18c1f26SAndrey V. Elsukov 			ck_pr_fence_load();
958d18c1f26SAndrey V. Elsukov 			if (ck_pr_load_32(&alias->tcp_pgmask[c]) != 0)
959d18c1f26SAndrey V. Elsukov 				continue;
960d18c1f26SAndrey V. Elsukov 		}
961d18c1f26SAndrey V. Elsukov 	}
962d18c1f26SAndrey V. Elsukov #endif
963d18c1f26SAndrey V. Elsukov 	return (NULL);
964d8caf56eSAndrey V. Elsukov }
965d8caf56eSAndrey V. Elsukov 
966d18c1f26SAndrey V. Elsukov #if 0
967d18c1f26SAndrey V. Elsukov static void
968d18c1f26SAndrey V. Elsukov nat64lsn_maintain_hosts(struct nat64lsn_cfg *cfg)
969d18c1f26SAndrey V. Elsukov {
970d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *h;
971d18c1f26SAndrey V. Elsukov 	struct nat64lsn_states_slist *hash;
972d18c1f26SAndrey V. Elsukov 	int i, j, hsize;
973d18c1f26SAndrey V. Elsukov 
974d18c1f26SAndrey V. Elsukov 	for (i = 0; i < cfg->hosts_hashsize; i++) {
975d18c1f26SAndrey V. Elsukov 		CK_SLIST_FOREACH(h, &cfg->hosts_hash[i], entries) {
976d18c1f26SAndrey V. Elsukov 			 if (h->states_count / 2 < h->states_hashsize ||
977d18c1f26SAndrey V. Elsukov 			     h->states_hashsize >= NAT64LSN_MAX_HSIZE)
978d18c1f26SAndrey V. Elsukov 				 continue;
979d18c1f26SAndrey V. Elsukov 			 hsize = h->states_hashsize * 2;
980d18c1f26SAndrey V. Elsukov 			 hash = malloc(sizeof(*hash)* hsize, M_NOWAIT);
981d18c1f26SAndrey V. Elsukov 			 if (hash == NULL)
982d18c1f26SAndrey V. Elsukov 				 continue;
983d18c1f26SAndrey V. Elsukov 			 for (j = 0; j < hsize; j++)
984d18c1f26SAndrey V. Elsukov 				CK_SLIST_INIT(&hash[i]);
985d18c1f26SAndrey V. Elsukov 
986d18c1f26SAndrey V. Elsukov 			 ck_pr_bts_32(&h->flags, NAT64LSN_GROWHASH);
987d6369c2dSAndrey V. Elsukov 		}
988d8caf56eSAndrey V. Elsukov 	}
989d18c1f26SAndrey V. Elsukov }
990d18c1f26SAndrey V. Elsukov #endif
991d8caf56eSAndrey V. Elsukov 
992d8caf56eSAndrey V. Elsukov /*
993*4a77657cSAndrey V. Elsukov  * This procedure is used to perform various maintance
994d18c1f26SAndrey V. Elsukov  * on dynamic hash list. Currently it is called every 4 seconds.
995d8caf56eSAndrey V. Elsukov  */
996d8caf56eSAndrey V. Elsukov static void
nat64lsn_periodic(void * data)997d8caf56eSAndrey V. Elsukov nat64lsn_periodic(void *data)
998d8caf56eSAndrey V. Elsukov {
999d18c1f26SAndrey V. Elsukov 	struct nat64lsn_job_item *ji;
1000d8caf56eSAndrey V. Elsukov 	struct nat64lsn_cfg *cfg;
1001d8caf56eSAndrey V. Elsukov 
1002d8caf56eSAndrey V. Elsukov 	cfg = (struct nat64lsn_cfg *) data;
1003d8caf56eSAndrey V. Elsukov 	CURVNET_SET(cfg->vp);
1004d18c1f26SAndrey V. Elsukov 	if (cfg->hosts_count > 0) {
1005d18c1f26SAndrey V. Elsukov 		ji = uma_zalloc(nat64lsn_job_zone, M_NOWAIT);
1006d18c1f26SAndrey V. Elsukov 		if (ji != NULL) {
1007d18c1f26SAndrey V. Elsukov 			ji->jtype = JTYPE_DESTROY;
1008d18c1f26SAndrey V. Elsukov 			CK_SLIST_INIT(&ji->hosts);
1009d18c1f26SAndrey V. Elsukov 			CK_SLIST_INIT(&ji->portgroups);
1010d18c1f26SAndrey V. Elsukov 			nat64lsn_expire_hosts(cfg, &ji->hosts);
1011d18c1f26SAndrey V. Elsukov 			nat64lsn_expire_portgroups(cfg, &ji->portgroups);
1012d18c1f26SAndrey V. Elsukov 			ji->pgchunk = nat64lsn_expire_pgchunk(cfg);
1013d18c1f26SAndrey V. Elsukov 			NAT64LSN_EPOCH_CALL(&ji->epoch_ctx,
1014d18c1f26SAndrey V. Elsukov 			    nat64lsn_job_destroy);
1015d18c1f26SAndrey V. Elsukov 		} else
1016d18c1f26SAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, jnomem);
1017d18c1f26SAndrey V. Elsukov 	}
1018d8caf56eSAndrey V. Elsukov 	callout_schedule(&cfg->periodic, hz * PERIODIC_DELAY);
1019d8caf56eSAndrey V. Elsukov 	CURVNET_RESTORE();
1020d8caf56eSAndrey V. Elsukov }
1021d8caf56eSAndrey V. Elsukov 
1022d18c1f26SAndrey V. Elsukov #define	ALLOC_ERROR(stage, type)	((stage) ? 10 * (type) + (stage): 0)
1023d18c1f26SAndrey V. Elsukov #define	HOST_ERROR(stage)		ALLOC_ERROR(stage, 1)
1024d18c1f26SAndrey V. Elsukov #define	PG_ERROR(stage)			ALLOC_ERROR(stage, 2)
1025d18c1f26SAndrey V. Elsukov static int
nat64lsn_alloc_host(struct nat64lsn_cfg * cfg,struct nat64lsn_job_item * ji)1026d18c1f26SAndrey V. Elsukov nat64lsn_alloc_host(struct nat64lsn_cfg *cfg, struct nat64lsn_job_item *ji)
1027d8caf56eSAndrey V. Elsukov {
1028d18c1f26SAndrey V. Elsukov 	char a[INET6_ADDRSTRLEN];
1029d18c1f26SAndrey V. Elsukov 	struct nat64lsn_aliaslink *link;
1030d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host;
1031d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
1032d18c1f26SAndrey V. Elsukov 	uint32_t hval, data[2];
1033d18c1f26SAndrey V. Elsukov 	int i;
1034d8caf56eSAndrey V. Elsukov 
1035d18c1f26SAndrey V. Elsukov 	/* Check that host was not yet added. */
1036d18c1f26SAndrey V. Elsukov 	NAT64LSN_EPOCH_ASSERT();
1037d18c1f26SAndrey V. Elsukov 	CK_SLIST_FOREACH(host, &HOSTS(cfg, ji->src6_hval), entries) {
1038d18c1f26SAndrey V. Elsukov 		if (IN6_ARE_ADDR_EQUAL(&ji->f_id.src_ip6, &host->addr)) {
1039d18c1f26SAndrey V. Elsukov 			/* The host was allocated in previous call. */
1040d18c1f26SAndrey V. Elsukov 			ji->host = host;
1041d18c1f26SAndrey V. Elsukov 			goto get_state;
1042d18c1f26SAndrey V. Elsukov 		}
1043d7a1cf06SAndrey V. Elsukov 	}
1044d7a1cf06SAndrey V. Elsukov 
1045d18c1f26SAndrey V. Elsukov 	host = ji->host = uma_zalloc(nat64lsn_host_zone, M_NOWAIT);
1046d18c1f26SAndrey V. Elsukov 	if (ji->host == NULL)
1047d18c1f26SAndrey V. Elsukov 		return (HOST_ERROR(1));
1048d18c1f26SAndrey V. Elsukov 
1049d18c1f26SAndrey V. Elsukov 	host->states_hashsize = NAT64LSN_HSIZE;
1050d18c1f26SAndrey V. Elsukov 	host->states_hash = malloc(sizeof(struct nat64lsn_states_slist) *
1051d18c1f26SAndrey V. Elsukov 	    host->states_hashsize, M_NAT64LSN, M_NOWAIT);
1052d18c1f26SAndrey V. Elsukov 	if (host->states_hash == NULL) {
1053d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_host_zone, host);
1054d18c1f26SAndrey V. Elsukov 		return (HOST_ERROR(2));
1055d18c1f26SAndrey V. Elsukov 	}
1056d18c1f26SAndrey V. Elsukov 
1057d18c1f26SAndrey V. Elsukov 	link = uma_zalloc(nat64lsn_aliaslink_zone, M_NOWAIT);
1058d18c1f26SAndrey V. Elsukov 	if (link == NULL) {
1059d18c1f26SAndrey V. Elsukov 		free(host->states_hash, M_NAT64LSN);
1060d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_host_zone, host);
1061d18c1f26SAndrey V. Elsukov 		return (HOST_ERROR(3));
1062d18c1f26SAndrey V. Elsukov 	}
1063d18c1f26SAndrey V. Elsukov 
1064d18c1f26SAndrey V. Elsukov 	/* Initialize */
1065d18c1f26SAndrey V. Elsukov 	HOST_LOCK_INIT(host);
1066d18c1f26SAndrey V. Elsukov 	SET_AGE(host->timestamp);
1067d18c1f26SAndrey V. Elsukov 	host->addr = ji->f_id.src_ip6;
1068d18c1f26SAndrey V. Elsukov 	host->hval = ji->src6_hval;
1069d18c1f26SAndrey V. Elsukov 	host->flags = 0;
1070d18c1f26SAndrey V. Elsukov 	host->states_count = 0;
1071d18c1f26SAndrey V. Elsukov 	CK_SLIST_INIT(&host->aliases);
1072d18c1f26SAndrey V. Elsukov 	for (i = 0; i < host->states_hashsize; i++)
1073d18c1f26SAndrey V. Elsukov 		CK_SLIST_INIT(&host->states_hash[i]);
1074d18c1f26SAndrey V. Elsukov 
1075*4a77657cSAndrey V. Elsukov 	link->alias = nat64lsn_get_alias(cfg, &ji->f_id);
1076d18c1f26SAndrey V. Elsukov 	CK_SLIST_INSERT_HEAD(&host->aliases, link, host_entries);
1077d18c1f26SAndrey V. Elsukov 
1078d18c1f26SAndrey V. Elsukov 	ALIAS_LOCK(link->alias);
1079d18c1f26SAndrey V. Elsukov 	CK_SLIST_INSERT_HEAD(&link->alias->hosts, link, alias_entries);
1080d18c1f26SAndrey V. Elsukov 	link->alias->hosts_count++;
1081d18c1f26SAndrey V. Elsukov 	ALIAS_UNLOCK(link->alias);
1082d18c1f26SAndrey V. Elsukov 
1083d18c1f26SAndrey V. Elsukov 	CFG_LOCK(cfg);
1084d18c1f26SAndrey V. Elsukov 	CK_SLIST_INSERT_HEAD(&HOSTS(cfg, ji->src6_hval), host, entries);
1085d18c1f26SAndrey V. Elsukov 	cfg->hosts_count++;
1086d18c1f26SAndrey V. Elsukov 	CFG_UNLOCK(cfg);
1087d18c1f26SAndrey V. Elsukov 
1088d18c1f26SAndrey V. Elsukov get_state:
1089d18c1f26SAndrey V. Elsukov 	data[0] = ji->faddr;
1090d18c1f26SAndrey V. Elsukov 	data[1] = (ji->f_id.dst_port << 16) | ji->port;
1091d18c1f26SAndrey V. Elsukov 	ji->state_hval = hval = STATE_HVAL(cfg, data);
1092d18c1f26SAndrey V. Elsukov 	state = nat64lsn_get_state6to4(cfg, host, &ji->f_id, hval,
1093d18c1f26SAndrey V. Elsukov 	    ji->faddr, ji->port, ji->proto);
1094d7a1cf06SAndrey V. Elsukov 	/*
1095d18c1f26SAndrey V. Elsukov 	 * We failed to obtain new state, used alias needs new PG.
1096d18c1f26SAndrey V. Elsukov 	 * XXX: or another alias should be used.
1097d7a1cf06SAndrey V. Elsukov 	 */
1098d18c1f26SAndrey V. Elsukov 	if (state == NULL) {
1099d18c1f26SAndrey V. Elsukov 		/* Try to allocate new PG */
1100d18c1f26SAndrey V. Elsukov 		if (nat64lsn_alloc_pg(cfg, ji) != PG_ERROR(0))
1101d18c1f26SAndrey V. Elsukov 			return (HOST_ERROR(4));
1102d18c1f26SAndrey V. Elsukov 		/* We assume that nat64lsn_alloc_pg() got state */
1103d18c1f26SAndrey V. Elsukov 	} else
1104d18c1f26SAndrey V. Elsukov 		ji->state = state;
1105d7a1cf06SAndrey V. Elsukov 
1106d18c1f26SAndrey V. Elsukov 	ji->done = 1;
1107d18c1f26SAndrey V. Elsukov 	DPRINTF(DP_OBJ, "ALLOC HOST %s %p",
1108d18c1f26SAndrey V. Elsukov 	    inet_ntop(AF_INET6, &host->addr, a, sizeof(a)), host);
1109d18c1f26SAndrey V. Elsukov 	return (HOST_ERROR(0));
1110d8caf56eSAndrey V. Elsukov }
1111d8caf56eSAndrey V. Elsukov 
1112d18c1f26SAndrey V. Elsukov static int
nat64lsn_find_pg_place(uint32_t * data)1113d18c1f26SAndrey V. Elsukov nat64lsn_find_pg_place(uint32_t *data)
1114d8caf56eSAndrey V. Elsukov {
1115d18c1f26SAndrey V. Elsukov 	int i;
1116d7a1cf06SAndrey V. Elsukov 
1117d18c1f26SAndrey V. Elsukov 	for (i = 0; i < 32; i++) {
1118d18c1f26SAndrey V. Elsukov 		if (~data[i] == 0)
1119d18c1f26SAndrey V. Elsukov 			continue;
1120d18c1f26SAndrey V. Elsukov 		return (i * 32 + ffs(~data[i]) - 1);
1121d18c1f26SAndrey V. Elsukov 	}
1122d18c1f26SAndrey V. Elsukov 	return (-1);
1123d6369c2dSAndrey V. Elsukov }
1124d6369c2dSAndrey V. Elsukov 
1125d18c1f26SAndrey V. Elsukov static int
nat64lsn_alloc_proto_pg(struct nat64lsn_cfg * cfg,struct nat64lsn_alias * alias,uint32_t * chunkmask,uint32_t * pgmask,struct nat64lsn_pgchunk ** chunks,uint32_t * pgidx,uint8_t proto)1126d18c1f26SAndrey V. Elsukov nat64lsn_alloc_proto_pg(struct nat64lsn_cfg *cfg,
1127*4a77657cSAndrey V. Elsukov     struct nat64lsn_alias *alias, uint32_t *chunkmask, uint32_t *pgmask,
1128*4a77657cSAndrey V. Elsukov     struct nat64lsn_pgchunk **chunks, uint32_t *pgidx, uint8_t proto)
1129d6369c2dSAndrey V. Elsukov {
1130d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pg *pg;
1131d18c1f26SAndrey V. Elsukov 	int i, pg_idx, chunk_idx;
1132d6369c2dSAndrey V. Elsukov 
1133d18c1f26SAndrey V. Elsukov 	/* Find place in pgchunk where PG can be added */
1134d18c1f26SAndrey V. Elsukov 	pg_idx = nat64lsn_find_pg_place(pgmask);
1135d18c1f26SAndrey V. Elsukov 	if (pg_idx < 0)	/* no more PGs */
1136d18c1f26SAndrey V. Elsukov 		return (PG_ERROR(1));
1137d18c1f26SAndrey V. Elsukov 	/* Check that we have allocated pgchunk for given PG index */
1138d18c1f26SAndrey V. Elsukov 	chunk_idx = pg_idx / 32;
1139d18c1f26SAndrey V. Elsukov 	if (!ISSET32(*chunkmask, chunk_idx)) {
1140d18c1f26SAndrey V. Elsukov 		chunks[chunk_idx] = uma_zalloc(nat64lsn_pgchunk_zone,
1141d18c1f26SAndrey V. Elsukov 		    M_NOWAIT);
1142d18c1f26SAndrey V. Elsukov 		if (chunks[chunk_idx] == NULL)
1143d18c1f26SAndrey V. Elsukov 			return (PG_ERROR(2));
1144d18c1f26SAndrey V. Elsukov 		ck_pr_bts_32(chunkmask, chunk_idx);
1145d18c1f26SAndrey V. Elsukov 		ck_pr_fence_store();
1146d18c1f26SAndrey V. Elsukov 	}
1147d18c1f26SAndrey V. Elsukov 	/* Allocate PG and states chunks */
1148d6369c2dSAndrey V. Elsukov 	pg = uma_zalloc(nat64lsn_pg_zone, M_NOWAIT);
1149d6369c2dSAndrey V. Elsukov 	if (pg == NULL)
1150d18c1f26SAndrey V. Elsukov 		return (PG_ERROR(3));
1151d18c1f26SAndrey V. Elsukov 	pg->chunks_count = cfg->states_chunks;
1152d18c1f26SAndrey V. Elsukov 	if (pg->chunks_count > 1) {
1153d18c1f26SAndrey V. Elsukov 		pg->freemask_chunk = malloc(pg->chunks_count *
1154d18c1f26SAndrey V. Elsukov 		    sizeof(uint64_t), M_NAT64LSN, M_NOWAIT);
1155d18c1f26SAndrey V. Elsukov 		if (pg->freemask_chunk == NULL) {
1156d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pg_zone, pg);
1157d18c1f26SAndrey V. Elsukov 			return (PG_ERROR(4));
1158d6369c2dSAndrey V. Elsukov 		}
1159d18c1f26SAndrey V. Elsukov 		pg->states_chunk = malloc(pg->chunks_count *
1160d18c1f26SAndrey V. Elsukov 		    sizeof(struct nat64lsn_states_chunk *), M_NAT64LSN,
1161d18c1f26SAndrey V. Elsukov 		    M_NOWAIT | M_ZERO);
1162d18c1f26SAndrey V. Elsukov 		if (pg->states_chunk == NULL) {
1163d18c1f26SAndrey V. Elsukov 			free(pg->freemask_chunk, M_NAT64LSN);
1164d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pg_zone, pg);
1165d18c1f26SAndrey V. Elsukov 			return (PG_ERROR(5));
1166d18c1f26SAndrey V. Elsukov 		}
1167d18c1f26SAndrey V. Elsukov 		for (i = 0; i < pg->chunks_count; i++) {
1168d18c1f26SAndrey V. Elsukov 			pg->states_chunk[i] = uma_zalloc(
1169d18c1f26SAndrey V. Elsukov 			    nat64lsn_state_zone, M_NOWAIT);
1170d18c1f26SAndrey V. Elsukov 			if (pg->states_chunk[i] == NULL)
1171d18c1f26SAndrey V. Elsukov 				goto states_failed;
1172d18c1f26SAndrey V. Elsukov 		}
1173d18c1f26SAndrey V. Elsukov 		memset(pg->freemask_chunk, 0xff,
1174d18c1f26SAndrey V. Elsukov 		    sizeof(uint64_t) * pg->chunks_count);
1175d18c1f26SAndrey V. Elsukov 	} else {
1176d18c1f26SAndrey V. Elsukov 		pg->states = uma_zalloc(nat64lsn_state_zone, M_NOWAIT);
1177d18c1f26SAndrey V. Elsukov 		if (pg->states == NULL) {
1178d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pg_zone, pg);
1179d18c1f26SAndrey V. Elsukov 			return (PG_ERROR(6));
1180d18c1f26SAndrey V. Elsukov 		}
1181d18c1f26SAndrey V. Elsukov 		memset(&pg->freemask64, 0xff, sizeof(uint64_t));
1182d18c1f26SAndrey V. Elsukov 	}
1183d6369c2dSAndrey V. Elsukov 
1184d18c1f26SAndrey V. Elsukov 	/* Initialize PG and hook it to pgchunk */
1185d18c1f26SAndrey V. Elsukov 	SET_AGE(pg->timestamp);
1186*4a77657cSAndrey V. Elsukov 	pg->flags = 0;
1187d18c1f26SAndrey V. Elsukov 	pg->proto = proto;
1188d18c1f26SAndrey V. Elsukov 	pg->base_port = NAT64_MIN_PORT + 64 * pg_idx;
1189d18c1f26SAndrey V. Elsukov 	ck_pr_store_ptr(&chunks[chunk_idx]->pgptr[pg_idx % 32], pg);
1190d18c1f26SAndrey V. Elsukov 	ck_pr_fence_store();
1191*4a77657cSAndrey V. Elsukov 
1192*4a77657cSAndrey V. Elsukov 	/* Set bit in pgmask and set index of last used PG */
1193*4a77657cSAndrey V. Elsukov 	ck_pr_bts_32(&pgmask[chunk_idx], pg_idx % 32);
1194*4a77657cSAndrey V. Elsukov 	ck_pr_store_32(pgidx, pg_idx);
1195d18c1f26SAndrey V. Elsukov 
1196d18c1f26SAndrey V. Elsukov 	ALIAS_LOCK(alias);
1197d18c1f26SAndrey V. Elsukov 	CK_SLIST_INSERT_HEAD(&alias->portgroups, pg, entries);
1198d18c1f26SAndrey V. Elsukov 	SET_AGE(alias->timestamp);
1199*4a77657cSAndrey V. Elsukov 	PGCOUNT_INC(alias, proto);
1200d18c1f26SAndrey V. Elsukov 	ALIAS_UNLOCK(alias);
1201d18c1f26SAndrey V. Elsukov 	NAT64STAT_INC(&cfg->base.stats, spgcreated);
1202d18c1f26SAndrey V. Elsukov 	return (PG_ERROR(0));
1203d18c1f26SAndrey V. Elsukov 
1204d18c1f26SAndrey V. Elsukov states_failed:
1205d18c1f26SAndrey V. Elsukov 	for (i = 0; i < pg->chunks_count; i++)
1206d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_state_zone, pg->states_chunk[i]);
1207d18c1f26SAndrey V. Elsukov 	free(pg->freemask_chunk, M_NAT64LSN);
1208d18c1f26SAndrey V. Elsukov 	free(pg->states_chunk, M_NAT64LSN);
1209d18c1f26SAndrey V. Elsukov 	uma_zfree(nat64lsn_pg_zone, pg);
1210d18c1f26SAndrey V. Elsukov 	return (PG_ERROR(7));
1211d18c1f26SAndrey V. Elsukov }
1212d18c1f26SAndrey V. Elsukov 
1213d18c1f26SAndrey V. Elsukov static int
nat64lsn_alloc_pg(struct nat64lsn_cfg * cfg,struct nat64lsn_job_item * ji)1214d18c1f26SAndrey V. Elsukov nat64lsn_alloc_pg(struct nat64lsn_cfg *cfg, struct nat64lsn_job_item *ji)
1215d18c1f26SAndrey V. Elsukov {
1216d18c1f26SAndrey V. Elsukov 	struct nat64lsn_aliaslink *link;
1217d18c1f26SAndrey V. Elsukov 	struct nat64lsn_alias *alias;
1218d18c1f26SAndrey V. Elsukov 	int ret;
1219d18c1f26SAndrey V. Elsukov 
1220d18c1f26SAndrey V. Elsukov 	link = nat64lsn_get_aliaslink(cfg, ji->host, &ji->f_id);
1221d18c1f26SAndrey V. Elsukov 	if (link == NULL)
1222d18c1f26SAndrey V. Elsukov 		return (PG_ERROR(1));
1223d18c1f26SAndrey V. Elsukov 
1224d18c1f26SAndrey V. Elsukov 	/*
1225d18c1f26SAndrey V. Elsukov 	 * TODO: check that we did not already allocated PG in
1226d18c1f26SAndrey V. Elsukov 	 *	 previous call.
1227d18c1f26SAndrey V. Elsukov 	 */
1228d18c1f26SAndrey V. Elsukov 
1229d18c1f26SAndrey V. Elsukov 	ret = 0;
1230d18c1f26SAndrey V. Elsukov 	alias = link->alias;
1231d18c1f26SAndrey V. Elsukov 	/* Find place in pgchunk where PG can be added */
1232d18c1f26SAndrey V. Elsukov 	switch (ji->proto) {
1233d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
1234d18c1f26SAndrey V. Elsukov 		ret = nat64lsn_alloc_proto_pg(cfg, alias,
1235d18c1f26SAndrey V. Elsukov 		    &alias->tcp_chunkmask, alias->tcp_pgmask,
1236*4a77657cSAndrey V. Elsukov 		    alias->tcp, &alias->tcp_pgidx, ji->proto);
1237d18c1f26SAndrey V. Elsukov 		break;
1238d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
1239d18c1f26SAndrey V. Elsukov 		ret = nat64lsn_alloc_proto_pg(cfg, alias,
1240d18c1f26SAndrey V. Elsukov 		    &alias->udp_chunkmask, alias->udp_pgmask,
1241*4a77657cSAndrey V. Elsukov 		    alias->udp, &alias->udp_pgidx, ji->proto);
1242d18c1f26SAndrey V. Elsukov 		break;
1243d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMP:
1244d18c1f26SAndrey V. Elsukov 		ret = nat64lsn_alloc_proto_pg(cfg, alias,
1245d18c1f26SAndrey V. Elsukov 		    &alias->icmp_chunkmask, alias->icmp_pgmask,
1246*4a77657cSAndrey V. Elsukov 		    alias->icmp, &alias->icmp_pgidx, ji->proto);
1247d18c1f26SAndrey V. Elsukov 		break;
1248d18c1f26SAndrey V. Elsukov 	default:
1249d18c1f26SAndrey V. Elsukov 		panic("%s: wrong proto %d", __func__, ji->proto);
1250d18c1f26SAndrey V. Elsukov 	}
1251d18c1f26SAndrey V. Elsukov 	if (ret == PG_ERROR(1)) {
1252d18c1f26SAndrey V. Elsukov 		/*
1253d18c1f26SAndrey V. Elsukov 		 * PG_ERROR(1) means that alias lacks free PGs
1254d18c1f26SAndrey V. Elsukov 		 * XXX: try next alias.
1255d18c1f26SAndrey V. Elsukov 		 */
1256d18c1f26SAndrey V. Elsukov 		printf("NAT64LSN: %s: failed to obtain PG\n",
1257d18c1f26SAndrey V. Elsukov 		    __func__);
1258d18c1f26SAndrey V. Elsukov 		return (ret);
1259d18c1f26SAndrey V. Elsukov 	}
1260d18c1f26SAndrey V. Elsukov 	if (ret == PG_ERROR(0)) {
1261d18c1f26SAndrey V. Elsukov 		ji->state = nat64lsn_get_state6to4(cfg, ji->host, &ji->f_id,
1262d18c1f26SAndrey V. Elsukov 		    ji->state_hval, ji->faddr, ji->port, ji->proto);
1263d18c1f26SAndrey V. Elsukov 		if (ji->state == NULL)
1264d18c1f26SAndrey V. Elsukov 			ret = PG_ERROR(8);
1265d18c1f26SAndrey V. Elsukov 		else
1266d18c1f26SAndrey V. Elsukov 			ji->done = 1;
1267d18c1f26SAndrey V. Elsukov 	}
1268d18c1f26SAndrey V. Elsukov 	return (ret);
1269d6369c2dSAndrey V. Elsukov }
1270d6369c2dSAndrey V. Elsukov 
1271d6369c2dSAndrey V. Elsukov static void
nat64lsn_do_request(void * data)1272d6369c2dSAndrey V. Elsukov nat64lsn_do_request(void *data)
1273d6369c2dSAndrey V. Elsukov {
1274d18c1f26SAndrey V. Elsukov 	struct epoch_tracker et;
1275d6369c2dSAndrey V. Elsukov 	struct nat64lsn_job_head jhead;
1276d18c1f26SAndrey V. Elsukov 	struct nat64lsn_job_item *ji, *ji2;
1277d18c1f26SAndrey V. Elsukov 	struct nat64lsn_cfg *cfg;
1278d18c1f26SAndrey V. Elsukov 	int jcount;
1279d18c1f26SAndrey V. Elsukov 	uint8_t flags;
1280d18c1f26SAndrey V. Elsukov 
1281d18c1f26SAndrey V. Elsukov 	cfg = (struct nat64lsn_cfg *)data;
1282d18c1f26SAndrey V. Elsukov 	if (cfg->jlen == 0)
1283d18c1f26SAndrey V. Elsukov 		return;
1284d6369c2dSAndrey V. Elsukov 
1285d8caf56eSAndrey V. Elsukov 	CURVNET_SET(cfg->vp);
1286d18c1f26SAndrey V. Elsukov 	STAILQ_INIT(&jhead);
1287d8caf56eSAndrey V. Elsukov 
1288d8caf56eSAndrey V. Elsukov 	/* Grab queue */
1289d8caf56eSAndrey V. Elsukov 	JQUEUE_LOCK();
1290d18c1f26SAndrey V. Elsukov 	STAILQ_SWAP(&jhead, &cfg->jhead, nat64lsn_job_item);
1291d8caf56eSAndrey V. Elsukov 	jcount = cfg->jlen;
1292d8caf56eSAndrey V. Elsukov 	cfg->jlen = 0;
1293d8caf56eSAndrey V. Elsukov 	JQUEUE_UNLOCK();
1294d8caf56eSAndrey V. Elsukov 
1295d18c1f26SAndrey V. Elsukov 	/* TODO: check if we need to resize hash */
1296d8caf56eSAndrey V. Elsukov 
1297782360deSAndrey V. Elsukov 	NAT64STAT_INC(&cfg->base.stats, jcalls);
1298d8caf56eSAndrey V. Elsukov 	DPRINTF(DP_JQUEUE, "count=%d", jcount);
1299d8caf56eSAndrey V. Elsukov 
1300d8caf56eSAndrey V. Elsukov 	/*
1301d8caf56eSAndrey V. Elsukov 	 * TODO:
1302d8caf56eSAndrey V. Elsukov 	 * What we should do here is to build a hash
1303d8caf56eSAndrey V. Elsukov 	 * to ensure we don't have lots of duplicate requests.
1304d8caf56eSAndrey V. Elsukov 	 * Skip this for now.
1305d8caf56eSAndrey V. Elsukov 	 *
1306d8caf56eSAndrey V. Elsukov 	 * TODO: Limit per-call number of items
1307d8caf56eSAndrey V. Elsukov 	 */
1308d8caf56eSAndrey V. Elsukov 
1309d18c1f26SAndrey V. Elsukov 	NAT64LSN_EPOCH_ENTER(et);
1310d18c1f26SAndrey V. Elsukov 	STAILQ_FOREACH(ji, &jhead, entries) {
1311d8caf56eSAndrey V. Elsukov 		switch (ji->jtype) {
1312d8caf56eSAndrey V. Elsukov 		case JTYPE_NEWHOST:
1313d18c1f26SAndrey V. Elsukov 			if (nat64lsn_alloc_host(cfg, ji) != HOST_ERROR(0))
1314d18c1f26SAndrey V. Elsukov 				NAT64STAT_INC(&cfg->base.stats, jhostfails);
1315d8caf56eSAndrey V. Elsukov 			break;
1316d8caf56eSAndrey V. Elsukov 		case JTYPE_NEWPORTGROUP:
1317d18c1f26SAndrey V. Elsukov 			if (nat64lsn_alloc_pg(cfg, ji) != PG_ERROR(0))
1318d18c1f26SAndrey V. Elsukov 				NAT64STAT_INC(&cfg->base.stats, jportfails);
1319d8caf56eSAndrey V. Elsukov 			break;
1320d8caf56eSAndrey V. Elsukov 		default:
1321d18c1f26SAndrey V. Elsukov 			continue;
1322d18c1f26SAndrey V. Elsukov 		}
1323d18c1f26SAndrey V. Elsukov 		if (ji->done != 0) {
1324d18c1f26SAndrey V. Elsukov 			flags = ji->proto != IPPROTO_TCP ? 0 :
1325d18c1f26SAndrey V. Elsukov 			    convert_tcp_flags(ji->f_id._flags);
1326d18c1f26SAndrey V. Elsukov 			nat64lsn_translate6_internal(cfg, &ji->m,
1327d18c1f26SAndrey V. Elsukov 			    ji->state, flags);
1328d18c1f26SAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, jreinjected);
1329d8caf56eSAndrey V. Elsukov 		}
1330d8caf56eSAndrey V. Elsukov 	}
1331d18c1f26SAndrey V. Elsukov 	NAT64LSN_EPOCH_EXIT(et);
1332d8caf56eSAndrey V. Elsukov 
1333d18c1f26SAndrey V. Elsukov 	ji = STAILQ_FIRST(&jhead);
1334d18c1f26SAndrey V. Elsukov 	while (ji != NULL) {
1335d18c1f26SAndrey V. Elsukov 		ji2 = STAILQ_NEXT(ji, entries);
1336d8caf56eSAndrey V. Elsukov 		/*
1337d18c1f26SAndrey V. Elsukov 		 * In any case we must free mbuf if
1338d18c1f26SAndrey V. Elsukov 		 * translator did not consumed it.
1339d8caf56eSAndrey V. Elsukov 		 */
1340d18c1f26SAndrey V. Elsukov 		m_freem(ji->m);
1341d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_job_zone, ji);
1342d18c1f26SAndrey V. Elsukov 		ji = ji2;
1343d8caf56eSAndrey V. Elsukov 	}
1344d8caf56eSAndrey V. Elsukov 	CURVNET_RESTORE();
1345d8caf56eSAndrey V. Elsukov }
1346d8caf56eSAndrey V. Elsukov 
1347d18c1f26SAndrey V. Elsukov static struct nat64lsn_job_item *
nat64lsn_create_job(struct nat64lsn_cfg * cfg,int jtype)1348d18c1f26SAndrey V. Elsukov nat64lsn_create_job(struct nat64lsn_cfg *cfg, int jtype)
1349d8caf56eSAndrey V. Elsukov {
1350d8caf56eSAndrey V. Elsukov 	struct nat64lsn_job_item *ji;
1351d8caf56eSAndrey V. Elsukov 
1352d8caf56eSAndrey V. Elsukov 	/*
1353d18c1f26SAndrey V. Elsukov 	 * Do not try to lock possibly contested mutex if we're near the
1354d18c1f26SAndrey V. Elsukov 	 * limit. Drop packet instead.
1355d8caf56eSAndrey V. Elsukov 	 */
1356d18c1f26SAndrey V. Elsukov 	ji = NULL;
1357d18c1f26SAndrey V. Elsukov 	if (cfg->jlen >= cfg->jmaxlen)
1358782360deSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, jmaxlen);
1359d18c1f26SAndrey V. Elsukov 	else {
1360d18c1f26SAndrey V. Elsukov 		ji = uma_zalloc(nat64lsn_job_zone, M_NOWAIT);
1361d18c1f26SAndrey V. Elsukov 		if (ji == NULL)
1362d6369c2dSAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, jnomem);
1363d8caf56eSAndrey V. Elsukov 	}
1364d18c1f26SAndrey V. Elsukov 	if (ji == NULL) {
1365d18c1f26SAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, dropped);
1366d18c1f26SAndrey V. Elsukov 		DPRINTF(DP_DROPS, "failed to create job");
1367d18c1f26SAndrey V. Elsukov 	} else {
1368d6369c2dSAndrey V. Elsukov 		ji->jtype = jtype;
1369d18c1f26SAndrey V. Elsukov 		ji->done = 0;
1370d6369c2dSAndrey V. Elsukov 	}
1371d8caf56eSAndrey V. Elsukov 	return (ji);
1372d8caf56eSAndrey V. Elsukov }
1373d8caf56eSAndrey V. Elsukov 
1374d18c1f26SAndrey V. Elsukov static void
nat64lsn_enqueue_job(struct nat64lsn_cfg * cfg,struct nat64lsn_job_item * ji)1375d8caf56eSAndrey V. Elsukov nat64lsn_enqueue_job(struct nat64lsn_cfg *cfg, struct nat64lsn_job_item *ji)
1376d8caf56eSAndrey V. Elsukov {
1377d8caf56eSAndrey V. Elsukov 
1378d8caf56eSAndrey V. Elsukov 	JQUEUE_LOCK();
1379d18c1f26SAndrey V. Elsukov 	STAILQ_INSERT_TAIL(&cfg->jhead, ji, entries);
1380d6369c2dSAndrey V. Elsukov 	NAT64STAT_INC(&cfg->base.stats, jrequests);
1381d18c1f26SAndrey V. Elsukov 	cfg->jlen++;
1382d8caf56eSAndrey V. Elsukov 
1383d8caf56eSAndrey V. Elsukov 	if (callout_pending(&cfg->jcallout) == 0)
1384d8caf56eSAndrey V. Elsukov 		callout_reset(&cfg->jcallout, 1, nat64lsn_do_request, cfg);
1385d8caf56eSAndrey V. Elsukov 	JQUEUE_UNLOCK();
1386d8caf56eSAndrey V. Elsukov }
1387d8caf56eSAndrey V. Elsukov 
1388*4a77657cSAndrey V. Elsukov /*
1389*4a77657cSAndrey V. Elsukov  * This function is used to clean up the result of less likely possible
1390*4a77657cSAndrey V. Elsukov  * race condition, when host object was deleted, but some translation
1391*4a77657cSAndrey V. Elsukov  * state was created before it is destroyed.
1392*4a77657cSAndrey V. Elsukov  *
1393*4a77657cSAndrey V. Elsukov  * Since the state expiration removes state from host's hash table,
1394*4a77657cSAndrey V. Elsukov  * we need to be sure, that there will not any states, that are linked
1395*4a77657cSAndrey V. Elsukov  * with this host entry.
1396*4a77657cSAndrey V. Elsukov  */
1397*4a77657cSAndrey V. Elsukov static void
nat64lsn_host_cleanup(struct nat64lsn_host * host)1398*4a77657cSAndrey V. Elsukov nat64lsn_host_cleanup(struct nat64lsn_host *host)
1399*4a77657cSAndrey V. Elsukov {
1400*4a77657cSAndrey V. Elsukov 	struct nat64lsn_state *state, *ts;
1401*4a77657cSAndrey V. Elsukov 	int i;
1402*4a77657cSAndrey V. Elsukov 
1403*4a77657cSAndrey V. Elsukov 	printf("NAT64LSN: %s: race condition has been detected for host %p\n",
1404*4a77657cSAndrey V. Elsukov 	    __func__, host);
1405*4a77657cSAndrey V. Elsukov 	for (i = 0; i < host->states_hashsize; i++) {
1406*4a77657cSAndrey V. Elsukov 		CK_SLIST_FOREACH_SAFE(state, &host->states_hash[i],
1407*4a77657cSAndrey V. Elsukov 		    entries, ts) {
1408*4a77657cSAndrey V. Elsukov 			/*
1409*4a77657cSAndrey V. Elsukov 			 * We can remove the state without lock,
1410*4a77657cSAndrey V. Elsukov 			 * because this host entry is unlinked and will
1411*4a77657cSAndrey V. Elsukov 			 * be destroyed.
1412*4a77657cSAndrey V. Elsukov 			 */
1413*4a77657cSAndrey V. Elsukov 			CK_SLIST_REMOVE(&host->states_hash[i], state,
1414*4a77657cSAndrey V. Elsukov 			    nat64lsn_state, entries);
1415*4a77657cSAndrey V. Elsukov 			host->states_count--;
1416*4a77657cSAndrey V. Elsukov 			nat64lsn_state_cleanup(state);
1417*4a77657cSAndrey V. Elsukov 		}
1418*4a77657cSAndrey V. Elsukov 	}
1419*4a77657cSAndrey V. Elsukov 	MPASS(host->states_count == 0);
1420*4a77657cSAndrey V. Elsukov }
1421*4a77657cSAndrey V. Elsukov 
1422*4a77657cSAndrey V. Elsukov /*
1423*4a77657cSAndrey V. Elsukov  * This function is used to clean up the result of less likely possible
1424*4a77657cSAndrey V. Elsukov  * race condition, when portgroup was deleted, but some translation state
1425*4a77657cSAndrey V. Elsukov  * was created before it is destroyed.
1426*4a77657cSAndrey V. Elsukov  *
1427*4a77657cSAndrey V. Elsukov  * Since states entries are accessible via host's hash table, we need
1428*4a77657cSAndrey V. Elsukov  * to be sure, that there will not any states from this PG, that are
1429*4a77657cSAndrey V. Elsukov  * linked with any host entries.
1430*4a77657cSAndrey V. Elsukov  */
1431*4a77657cSAndrey V. Elsukov static void
nat64lsn_pg_cleanup(struct nat64lsn_pg * pg)1432*4a77657cSAndrey V. Elsukov nat64lsn_pg_cleanup(struct nat64lsn_pg *pg)
1433*4a77657cSAndrey V. Elsukov {
1434*4a77657cSAndrey V. Elsukov 	struct nat64lsn_state *state;
1435*4a77657cSAndrey V. Elsukov 	uint64_t usedmask;
1436*4a77657cSAndrey V. Elsukov 	int c, i;
1437*4a77657cSAndrey V. Elsukov 
1438*4a77657cSAndrey V. Elsukov 	printf("NAT64LSN: %s: race condition has been detected for pg %p\n",
1439*4a77657cSAndrey V. Elsukov 	    __func__, pg);
1440*4a77657cSAndrey V. Elsukov 	for (c = 0; c < pg->chunks_count; c++) {
1441*4a77657cSAndrey V. Elsukov 		/*
1442*4a77657cSAndrey V. Elsukov 		 * Use inverted freemask to find what state was created.
1443*4a77657cSAndrey V. Elsukov 		 */
1444*4a77657cSAndrey V. Elsukov 		usedmask = ~(*FREEMASK_CHUNK(pg, c));
1445*4a77657cSAndrey V. Elsukov 		if (usedmask == 0)
1446*4a77657cSAndrey V. Elsukov 			continue;
1447*4a77657cSAndrey V. Elsukov 		for (i = 0; i < 64; i++) {
1448*4a77657cSAndrey V. Elsukov 			if (!ISSET64(usedmask, i))
1449*4a77657cSAndrey V. Elsukov 				continue;
1450*4a77657cSAndrey V. Elsukov 			state = &STATES_CHUNK(pg, c)->state[i];
1451*4a77657cSAndrey V. Elsukov 			/*
1452*4a77657cSAndrey V. Elsukov 			 * If we have STALE bit, this means that state
1453*4a77657cSAndrey V. Elsukov 			 * is already unlinked from host's hash table.
1454*4a77657cSAndrey V. Elsukov 			 * Thus we can just reset the bit in mask and
1455*4a77657cSAndrey V. Elsukov 			 * schedule destroying in the next epoch call.
1456*4a77657cSAndrey V. Elsukov 			 */
1457*4a77657cSAndrey V. Elsukov 			if (ISSET32(state->flags, NAT64_BIT_STALE)) {
1458*4a77657cSAndrey V. Elsukov 				FREEMASK_BTS(pg, c, i);
1459*4a77657cSAndrey V. Elsukov 				continue;
1460*4a77657cSAndrey V. Elsukov 			}
1461*4a77657cSAndrey V. Elsukov 			/*
1462*4a77657cSAndrey V. Elsukov 			 * There is  small window, when we have bit
1463*4a77657cSAndrey V. Elsukov 			 * grabbed from freemask, but state is not yet
1464*4a77657cSAndrey V. Elsukov 			 * linked into host's hash table.
1465*4a77657cSAndrey V. Elsukov 			 * Check for READY flag, it is set just after
1466*4a77657cSAndrey V. Elsukov 			 * linking. If it is not set, defer cleanup
1467*4a77657cSAndrey V. Elsukov 			 * for next call.
1468*4a77657cSAndrey V. Elsukov 			 */
1469*4a77657cSAndrey V. Elsukov 			if (ISSET32(state->flags, NAT64_BIT_READY_IPV4)) {
1470*4a77657cSAndrey V. Elsukov 				struct nat64lsn_host *host;
1471*4a77657cSAndrey V. Elsukov 
1472*4a77657cSAndrey V. Elsukov 				host = state->host;
1473*4a77657cSAndrey V. Elsukov 				HOST_LOCK(host);
1474*4a77657cSAndrey V. Elsukov 				CK_SLIST_REMOVE(&STATE_HASH(host,
1475*4a77657cSAndrey V. Elsukov 				    state->hval), state, nat64lsn_state,
1476*4a77657cSAndrey V. Elsukov 				    entries);
1477*4a77657cSAndrey V. Elsukov 				host->states_count--;
1478*4a77657cSAndrey V. Elsukov 				HOST_UNLOCK(host);
1479*4a77657cSAndrey V. Elsukov 				nat64lsn_state_cleanup(state);
1480*4a77657cSAndrey V. Elsukov 			}
1481*4a77657cSAndrey V. Elsukov 		}
1482*4a77657cSAndrey V. Elsukov 	}
1483*4a77657cSAndrey V. Elsukov }
1484*4a77657cSAndrey V. Elsukov 
1485d18c1f26SAndrey V. Elsukov static void
nat64lsn_job_destroy(epoch_context_t ctx)1486d18c1f26SAndrey V. Elsukov nat64lsn_job_destroy(epoch_context_t ctx)
1487d7a1cf06SAndrey V. Elsukov {
1488*4a77657cSAndrey V. Elsukov 	struct nat64lsn_hosts_slist hosts;
1489*4a77657cSAndrey V. Elsukov 	struct nat64lsn_pg_slist portgroups;
1490d7a1cf06SAndrey V. Elsukov 	struct nat64lsn_job_item *ji;
1491d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host;
1492d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pg *pg;
1493d18c1f26SAndrey V. Elsukov 	int i;
1494d7a1cf06SAndrey V. Elsukov 
1495*4a77657cSAndrey V. Elsukov 	CK_SLIST_INIT(&hosts);
1496*4a77657cSAndrey V. Elsukov 	CK_SLIST_INIT(&portgroups);
1497d18c1f26SAndrey V. Elsukov 	ji = __containerof(ctx, struct nat64lsn_job_item, epoch_ctx);
1498d18c1f26SAndrey V. Elsukov 	MPASS(ji->jtype == JTYPE_DESTROY);
1499d18c1f26SAndrey V. Elsukov 	while (!CK_SLIST_EMPTY(&ji->hosts)) {
1500d18c1f26SAndrey V. Elsukov 		host = CK_SLIST_FIRST(&ji->hosts);
1501d18c1f26SAndrey V. Elsukov 		CK_SLIST_REMOVE_HEAD(&ji->hosts, entries);
1502d18c1f26SAndrey V. Elsukov 		if (host->states_count > 0) {
1503d18c1f26SAndrey V. Elsukov 			/*
1504*4a77657cSAndrey V. Elsukov 			 * The state has been created during host deletion.
1505d18c1f26SAndrey V. Elsukov 			 */
1506d18c1f26SAndrey V. Elsukov 			printf("NAT64LSN: %s: destroying host with %d "
1507d18c1f26SAndrey V. Elsukov 			    "states\n", __func__, host->states_count);
1508*4a77657cSAndrey V. Elsukov 			/*
1509*4a77657cSAndrey V. Elsukov 			 * We need to cleanup these states to avoid
1510*4a77657cSAndrey V. Elsukov 			 * possible access to already deleted host in
1511*4a77657cSAndrey V. Elsukov 			 * the state expiration code.
1512*4a77657cSAndrey V. Elsukov 			 */
1513*4a77657cSAndrey V. Elsukov 			nat64lsn_host_cleanup(host);
1514*4a77657cSAndrey V. Elsukov 			CK_SLIST_INSERT_HEAD(&hosts, host, entries);
1515*4a77657cSAndrey V. Elsukov 			/*
1516*4a77657cSAndrey V. Elsukov 			 * Keep host entry for next deferred destroying.
1517*4a77657cSAndrey V. Elsukov 			 * In the next epoch its states will be not
1518*4a77657cSAndrey V. Elsukov 			 * accessible.
1519*4a77657cSAndrey V. Elsukov 			 */
1520*4a77657cSAndrey V. Elsukov 			continue;
1521d18c1f26SAndrey V. Elsukov 		}
1522d18c1f26SAndrey V. Elsukov 		nat64lsn_destroy_host(host);
1523d18c1f26SAndrey V. Elsukov 	}
1524d18c1f26SAndrey V. Elsukov 	while (!CK_SLIST_EMPTY(&ji->portgroups)) {
1525d18c1f26SAndrey V. Elsukov 		pg = CK_SLIST_FIRST(&ji->portgroups);
1526d18c1f26SAndrey V. Elsukov 		CK_SLIST_REMOVE_HEAD(&ji->portgroups, entries);
1527d18c1f26SAndrey V. Elsukov 		for (i = 0; i < pg->chunks_count; i++) {
1528d18c1f26SAndrey V. Elsukov 			if (FREEMASK_BITCOUNT(pg, i) != 64) {
1529d18c1f26SAndrey V. Elsukov 				/*
1530*4a77657cSAndrey V. Elsukov 				 * A state has been created during
1531d18c1f26SAndrey V. Elsukov 				 * PG deletion.
1532d18c1f26SAndrey V. Elsukov 				 */
1533d18c1f26SAndrey V. Elsukov 				printf("NAT64LSN: %s: destroying PG %p "
1534d18c1f26SAndrey V. Elsukov 				    "with non-empty chunk %d\n", __func__,
1535d18c1f26SAndrey V. Elsukov 				    pg, i);
1536*4a77657cSAndrey V. Elsukov 				nat64lsn_pg_cleanup(pg);
1537*4a77657cSAndrey V. Elsukov 				CK_SLIST_INSERT_HEAD(&portgroups,
1538*4a77657cSAndrey V. Elsukov 				    pg, entries);
1539*4a77657cSAndrey V. Elsukov 				i = -1;
1540*4a77657cSAndrey V. Elsukov 				break;
1541d18c1f26SAndrey V. Elsukov 			}
1542d18c1f26SAndrey V. Elsukov 		}
1543*4a77657cSAndrey V. Elsukov 		if (i != -1)
1544d18c1f26SAndrey V. Elsukov 			nat64lsn_destroy_pg(pg);
1545d18c1f26SAndrey V. Elsukov 	}
1546*4a77657cSAndrey V. Elsukov 	if (CK_SLIST_EMPTY(&hosts) &&
1547*4a77657cSAndrey V. Elsukov 	    CK_SLIST_EMPTY(&portgroups)) {
1548d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_pgchunk_zone, ji->pgchunk);
1549d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_job_zone, ji);
1550*4a77657cSAndrey V. Elsukov 		return;
1551*4a77657cSAndrey V. Elsukov 	}
1552*4a77657cSAndrey V. Elsukov 
1553*4a77657cSAndrey V. Elsukov 	/* Schedule job item again */
1554*4a77657cSAndrey V. Elsukov 	CK_SLIST_MOVE(&ji->hosts, &hosts, entries);
1555*4a77657cSAndrey V. Elsukov 	CK_SLIST_MOVE(&ji->portgroups, &portgroups, entries);
1556*4a77657cSAndrey V. Elsukov 	NAT64LSN_EPOCH_CALL(&ji->epoch_ctx, nat64lsn_job_destroy);
1557d18c1f26SAndrey V. Elsukov }
1558d7a1cf06SAndrey V. Elsukov 
1559d18c1f26SAndrey V. Elsukov static int
nat64lsn_request_host(struct nat64lsn_cfg * cfg,const struct ipfw_flow_id * f_id,struct mbuf ** mp,uint32_t hval,in_addr_t faddr,uint16_t port,uint8_t proto)1560d18c1f26SAndrey V. Elsukov nat64lsn_request_host(struct nat64lsn_cfg *cfg,
1561d18c1f26SAndrey V. Elsukov     const struct ipfw_flow_id *f_id, struct mbuf **mp, uint32_t hval,
1562d18c1f26SAndrey V. Elsukov     in_addr_t faddr, uint16_t port, uint8_t proto)
1563d18c1f26SAndrey V. Elsukov {
1564d18c1f26SAndrey V. Elsukov 	struct nat64lsn_job_item *ji;
1565d18c1f26SAndrey V. Elsukov 
1566d18c1f26SAndrey V. Elsukov 	ji = nat64lsn_create_job(cfg, JTYPE_NEWHOST);
1567d18c1f26SAndrey V. Elsukov 	if (ji != NULL) {
1568d18c1f26SAndrey V. Elsukov 		ji->m = *mp;
1569d18c1f26SAndrey V. Elsukov 		ji->f_id = *f_id;
1570d18c1f26SAndrey V. Elsukov 		ji->faddr = faddr;
1571d18c1f26SAndrey V. Elsukov 		ji->port = port;
1572d18c1f26SAndrey V. Elsukov 		ji->proto = proto;
1573d18c1f26SAndrey V. Elsukov 		ji->src6_hval = hval;
1574d18c1f26SAndrey V. Elsukov 
1575d8caf56eSAndrey V. Elsukov 		nat64lsn_enqueue_job(cfg, ji);
1576782360deSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, jhostsreq);
1577d18c1f26SAndrey V. Elsukov 		*mp = NULL;
1578d8caf56eSAndrey V. Elsukov 	}
1579eed30257SAndrey V. Elsukov 	return (IP_FW_DENY);
1580d8caf56eSAndrey V. Elsukov }
1581d8caf56eSAndrey V. Elsukov 
1582d18c1f26SAndrey V. Elsukov static int
nat64lsn_request_pg(struct nat64lsn_cfg * cfg,struct nat64lsn_host * host,const struct ipfw_flow_id * f_id,struct mbuf ** mp,uint32_t hval,in_addr_t faddr,uint16_t port,uint8_t proto)1583d18c1f26SAndrey V. Elsukov nat64lsn_request_pg(struct nat64lsn_cfg *cfg, struct nat64lsn_host *host,
1584d18c1f26SAndrey V. Elsukov     const struct ipfw_flow_id *f_id, struct mbuf **mp, uint32_t hval,
1585d18c1f26SAndrey V. Elsukov     in_addr_t faddr, uint16_t port, uint8_t proto)
1586d8caf56eSAndrey V. Elsukov {
1587d8caf56eSAndrey V. Elsukov 	struct nat64lsn_job_item *ji;
1588d8caf56eSAndrey V. Elsukov 
1589d18c1f26SAndrey V. Elsukov 	ji = nat64lsn_create_job(cfg, JTYPE_NEWPORTGROUP);
1590d18c1f26SAndrey V. Elsukov 	if (ji != NULL) {
1591d18c1f26SAndrey V. Elsukov 		ji->m = *mp;
1592d18c1f26SAndrey V. Elsukov 		ji->f_id = *f_id;
1593d18c1f26SAndrey V. Elsukov 		ji->faddr = faddr;
1594d18c1f26SAndrey V. Elsukov 		ji->port = port;
1595d18c1f26SAndrey V. Elsukov 		ji->proto = proto;
1596d18c1f26SAndrey V. Elsukov 		ji->state_hval = hval;
1597d18c1f26SAndrey V. Elsukov 		ji->host = host;
1598d8caf56eSAndrey V. Elsukov 
1599d8caf56eSAndrey V. Elsukov 		nat64lsn_enqueue_job(cfg, ji);
1600782360deSAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, jportreq);
1601d18c1f26SAndrey V. Elsukov 		*mp = NULL;
1602d8caf56eSAndrey V. Elsukov 	}
1603eed30257SAndrey V. Elsukov 	return (IP_FW_DENY);
1604d8caf56eSAndrey V. Elsukov }
1605d8caf56eSAndrey V. Elsukov 
1606d18c1f26SAndrey V. Elsukov static int
nat64lsn_translate6_internal(struct nat64lsn_cfg * cfg,struct mbuf ** mp,struct nat64lsn_state * state,uint8_t flags)1607d18c1f26SAndrey V. Elsukov nat64lsn_translate6_internal(struct nat64lsn_cfg *cfg, struct mbuf **mp,
1608d18c1f26SAndrey V. Elsukov     struct nat64lsn_state *state, uint8_t flags)
1609d8caf56eSAndrey V. Elsukov {
1610d8caf56eSAndrey V. Elsukov 	struct pfloghdr loghdr, *logdata;
1611d18c1f26SAndrey V. Elsukov 	int ret;
1612d18c1f26SAndrey V. Elsukov 	uint16_t ts;
1613d8caf56eSAndrey V. Elsukov 
1614d18c1f26SAndrey V. Elsukov 	/* Update timestamp and flags if needed */
1615d18c1f26SAndrey V. Elsukov 	SET_AGE(ts);
1616d18c1f26SAndrey V. Elsukov 	if (state->timestamp != ts)
1617d18c1f26SAndrey V. Elsukov 		state->timestamp = ts;
1618d18c1f26SAndrey V. Elsukov 	if ((state->flags & flags) != 0)
1619d18c1f26SAndrey V. Elsukov 		state->flags |= flags;
1620d8caf56eSAndrey V. Elsukov 
1621782360deSAndrey V. Elsukov 	if (cfg->base.flags & NAT64_LOG) {
1622d8caf56eSAndrey V. Elsukov 		logdata = &loghdr;
1623d18c1f26SAndrey V. Elsukov 		nat64lsn_log(logdata, *mp, AF_INET6, state);
1624d8caf56eSAndrey V. Elsukov 	} else
1625d8caf56eSAndrey V. Elsukov 		logdata = NULL;
1626d8caf56eSAndrey V. Elsukov 
1627d18c1f26SAndrey V. Elsukov 	ret = nat64_do_handle_ip6(*mp, htonl(state->ip_src),
1628d18c1f26SAndrey V. Elsukov 	    htons(state->aport), &cfg->base, logdata);
1629d18c1f26SAndrey V. Elsukov 	if (ret == NAT64SKIP)
1630eed30257SAndrey V. Elsukov 		return (cfg->nomatch_verdict);
1631d18c1f26SAndrey V. Elsukov 	if (ret == NAT64RETURN)
1632d18c1f26SAndrey V. Elsukov 		*mp = NULL;
1633d8caf56eSAndrey V. Elsukov 	return (IP_FW_DENY);
1634d8caf56eSAndrey V. Elsukov }
1635d8caf56eSAndrey V. Elsukov 
1636d18c1f26SAndrey V. Elsukov static int
nat64lsn_translate6(struct nat64lsn_cfg * cfg,struct ipfw_flow_id * f_id,struct mbuf ** mp)1637d18c1f26SAndrey V. Elsukov nat64lsn_translate6(struct nat64lsn_cfg *cfg, struct ipfw_flow_id *f_id,
1638d18c1f26SAndrey V. Elsukov     struct mbuf **mp)
1639d18c1f26SAndrey V. Elsukov {
1640d18c1f26SAndrey V. Elsukov 	struct nat64lsn_state *state;
1641d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host;
1642d18c1f26SAndrey V. Elsukov 	struct icmp6_hdr *icmp6;
1643d18c1f26SAndrey V. Elsukov 	uint32_t addr, hval, data[2];
1644d18c1f26SAndrey V. Elsukov 	int offset, proto;
1645d18c1f26SAndrey V. Elsukov 	uint16_t port;
1646d18c1f26SAndrey V. Elsukov 	uint8_t flags;
1647d18c1f26SAndrey V. Elsukov 
1648d18c1f26SAndrey V. Elsukov 	/* Check if protocol is supported */
1649d18c1f26SAndrey V. Elsukov 	port = f_id->src_port;
1650d18c1f26SAndrey V. Elsukov 	proto = f_id->proto;
1651d18c1f26SAndrey V. Elsukov 	switch (f_id->proto) {
1652d18c1f26SAndrey V. Elsukov 	case IPPROTO_ICMPV6:
1653d18c1f26SAndrey V. Elsukov 		/*
1654d18c1f26SAndrey V. Elsukov 		 * For ICMPv6 echo reply/request we use icmp6_id as
1655d18c1f26SAndrey V. Elsukov 		 * local port.
1656d18c1f26SAndrey V. Elsukov 		 */
1657d18c1f26SAndrey V. Elsukov 		offset = 0;
1658d18c1f26SAndrey V. Elsukov 		proto = nat64_getlasthdr(*mp, &offset);
1659d18c1f26SAndrey V. Elsukov 		if (proto < 0) {
1660d18c1f26SAndrey V. Elsukov 			NAT64STAT_INC(&cfg->base.stats, dropped);
1661d18c1f26SAndrey V. Elsukov 			DPRINTF(DP_DROPS, "mbuf isn't contigious");
1662d18c1f26SAndrey V. Elsukov 			return (IP_FW_DENY);
1663d18c1f26SAndrey V. Elsukov 		}
1664d18c1f26SAndrey V. Elsukov 		if (proto == IPPROTO_ICMPV6) {
1665d18c1f26SAndrey V. Elsukov 			icmp6 = mtodo(*mp, offset);
1666d18c1f26SAndrey V. Elsukov 			if (icmp6->icmp6_type == ICMP6_ECHO_REQUEST ||
1667d18c1f26SAndrey V. Elsukov 			    icmp6->icmp6_type == ICMP6_ECHO_REPLY)
1668d18c1f26SAndrey V. Elsukov 				port = ntohs(icmp6->icmp6_id);
1669d18c1f26SAndrey V. Elsukov 		}
1670d18c1f26SAndrey V. Elsukov 		proto = IPPROTO_ICMP;
1671d18c1f26SAndrey V. Elsukov 		/* FALLTHROUGH */
1672d18c1f26SAndrey V. Elsukov 	case IPPROTO_TCP:
1673d18c1f26SAndrey V. Elsukov 	case IPPROTO_UDP:
1674d18c1f26SAndrey V. Elsukov 		break;
1675d18c1f26SAndrey V. Elsukov 	default:
1676d18c1f26SAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, noproto);
1677d18c1f26SAndrey V. Elsukov 		return (cfg->nomatch_verdict);
1678d18c1f26SAndrey V. Elsukov 	}
1679d18c1f26SAndrey V. Elsukov 
1680d18c1f26SAndrey V. Elsukov 	/* Extract IPv4 from destination IPv6 address */
1681d18c1f26SAndrey V. Elsukov 	addr = nat64_extract_ip4(&f_id->dst_ip6, cfg->base.plat_plen);
1682d18c1f26SAndrey V. Elsukov 	if (addr == 0 || nat64_check_private_ip4(&cfg->base, addr) != 0) {
1683d18c1f26SAndrey V. Elsukov 		char a[INET_ADDRSTRLEN];
1684d18c1f26SAndrey V. Elsukov 
1685d18c1f26SAndrey V. Elsukov 		NAT64STAT_INC(&cfg->base.stats, dropped);
1686d18c1f26SAndrey V. Elsukov 		DPRINTF(DP_DROPS, "dropped due to embedded IPv4 address %s",
1687d18c1f26SAndrey V. Elsukov 		    inet_ntop(AF_INET, &addr, a, sizeof(a)));
1688d18c1f26SAndrey V. Elsukov 		return (IP_FW_DENY); /* XXX: add extra stats? */
1689d18c1f26SAndrey V. Elsukov 	}
1690d18c1f26SAndrey V. Elsukov 
1691d18c1f26SAndrey V. Elsukov 	/* Try to find host */
1692d18c1f26SAndrey V. Elsukov 	hval = HOST_HVAL(cfg, &f_id->src_ip6);
1693d18c1f26SAndrey V. Elsukov 	CK_SLIST_FOREACH(host, &HOSTS(cfg, hval), entries) {
1694d18c1f26SAndrey V. Elsukov 		if (IN6_ARE_ADDR_EQUAL(&f_id->src_ip6, &host->addr))
1695d18c1f26SAndrey V. Elsukov 			break;
1696d18c1f26SAndrey V. Elsukov 	}
1697d18c1f26SAndrey V. Elsukov 	/* We use IPv4 address in host byte order */
1698d18c1f26SAndrey V. Elsukov 	addr = ntohl(addr);
1699d18c1f26SAndrey V. Elsukov 	if (host == NULL)
1700d18c1f26SAndrey V. Elsukov 		return (nat64lsn_request_host(cfg, f_id, mp,
1701d18c1f26SAndrey V. Elsukov 		    hval, addr, port, proto));
1702d18c1f26SAndrey V. Elsukov 
1703d18c1f26SAndrey V. Elsukov 	flags = proto != IPPROTO_TCP ? 0 : convert_tcp_flags(f_id->_flags);
1704d18c1f26SAndrey V. Elsukov 
1705d18c1f26SAndrey V. Elsukov 	data[0] = addr;
1706d18c1f26SAndrey V. Elsukov 	data[1] = (f_id->dst_port << 16) | port;
1707d18c1f26SAndrey V. Elsukov 	hval = STATE_HVAL(cfg, data);
1708d18c1f26SAndrey V. Elsukov 	state = nat64lsn_get_state6to4(cfg, host, f_id, hval, addr,
1709d18c1f26SAndrey V. Elsukov 	    port, proto);
1710d18c1f26SAndrey V. Elsukov 	if (state == NULL)
1711d18c1f26SAndrey V. Elsukov 		return (nat64lsn_request_pg(cfg, host, f_id, mp, hval, addr,
1712d18c1f26SAndrey V. Elsukov 		    port, proto));
1713d18c1f26SAndrey V. Elsukov 	return (nat64lsn_translate6_internal(cfg, mp, state, flags));
1714d18c1f26SAndrey V. Elsukov }
1715d18c1f26SAndrey V. Elsukov 
1716d8caf56eSAndrey V. Elsukov /*
1717d8caf56eSAndrey V. Elsukov  * Main dataplane entry point.
1718d8caf56eSAndrey V. Elsukov  */
1719d8caf56eSAndrey V. Elsukov int
ipfw_nat64lsn(struct ip_fw_chain * ch,struct ip_fw_args * args,ipfw_insn * cmd,int * done)1720d8caf56eSAndrey V. Elsukov ipfw_nat64lsn(struct ip_fw_chain *ch, struct ip_fw_args *args,
1721d8caf56eSAndrey V. Elsukov     ipfw_insn *cmd, int *done)
1722d8caf56eSAndrey V. Elsukov {
1723*4a77657cSAndrey V. Elsukov 	struct nat64lsn_instance *i;
1724d18c1f26SAndrey V. Elsukov 	ipfw_insn *icmd;
1725d8caf56eSAndrey V. Elsukov 	int ret;
1726d8caf56eSAndrey V. Elsukov 
1727d8caf56eSAndrey V. Elsukov 	IPFW_RLOCK_ASSERT(ch);
1728d8caf56eSAndrey V. Elsukov 
1729d18c1f26SAndrey V. Elsukov 	*done = 0;	/* continue the search in case of failure */
1730*4a77657cSAndrey V. Elsukov 	icmd = cmd + F_LEN(cmd);
1731d8caf56eSAndrey V. Elsukov 	if (cmd->opcode != O_EXTERNAL_ACTION ||
1732*4a77657cSAndrey V. Elsukov 	    insntod(cmd, kidx)->kidx != V_nat64lsn_eid ||
1733d8caf56eSAndrey V. Elsukov 	    icmd->opcode != O_EXTERNAL_INSTANCE ||
1734*4a77657cSAndrey V. Elsukov 	    (i = NAT64_LOOKUP(ch, icmd)) == NULL)
1735d18c1f26SAndrey V. Elsukov 		return (IP_FW_DENY);
1736d8caf56eSAndrey V. Elsukov 
1737d18c1f26SAndrey V. Elsukov 	*done = 1;	/* terminate the search */
1738d18c1f26SAndrey V. Elsukov 
1739d8caf56eSAndrey V. Elsukov 	switch (args->f_id.addr_type) {
1740d8caf56eSAndrey V. Elsukov 	case 4:
1741*4a77657cSAndrey V. Elsukov 		ret = nat64lsn_translate4(i->cfg, &args->f_id, &args->m);
1742d8caf56eSAndrey V. Elsukov 		break;
1743d8caf56eSAndrey V. Elsukov 	case 6:
1744d18c1f26SAndrey V. Elsukov 		/*
1745d18c1f26SAndrey V. Elsukov 		 * Check that destination IPv6 address matches our prefix6.
1746d18c1f26SAndrey V. Elsukov 		 */
1747*4a77657cSAndrey V. Elsukov 		if ((i->cfg->base.flags & NAT64LSN_ANYPREFIX) == 0 &&
1748*4a77657cSAndrey V. Elsukov 		    memcmp(&args->f_id.dst_ip6, &i->cfg->base.plat_prefix,
1749*4a77657cSAndrey V. Elsukov 		    i->cfg->base.plat_plen / 8) != 0) {
1750*4a77657cSAndrey V. Elsukov 			ret = i->cfg->nomatch_verdict;
1751d18c1f26SAndrey V. Elsukov 			break;
1752d18c1f26SAndrey V. Elsukov 		}
1753*4a77657cSAndrey V. Elsukov 		ret = nat64lsn_translate6(i->cfg, &args->f_id, &args->m);
1754d8caf56eSAndrey V. Elsukov 		break;
1755d8caf56eSAndrey V. Elsukov 	default:
1756*4a77657cSAndrey V. Elsukov 		ret = i->cfg->nomatch_verdict;
1757d18c1f26SAndrey V. Elsukov 	}
1758d18c1f26SAndrey V. Elsukov 
1759d18c1f26SAndrey V. Elsukov 	if (ret != IP_FW_PASS && args->m != NULL) {
1760d18c1f26SAndrey V. Elsukov 		m_freem(args->m);
1761d18c1f26SAndrey V. Elsukov 		args->m = NULL;
1762d8caf56eSAndrey V. Elsukov 	}
1763d8caf56eSAndrey V. Elsukov 	return (ret);
1764d8caf56eSAndrey V. Elsukov }
1765d8caf56eSAndrey V. Elsukov 
1766d8caf56eSAndrey V. Elsukov static int
nat64lsn_state_ctor(void * mem,int size,void * arg,int flags)1767d18c1f26SAndrey V. Elsukov nat64lsn_state_ctor(void *mem, int size, void *arg, int flags)
1768d8caf56eSAndrey V. Elsukov {
1769d18c1f26SAndrey V. Elsukov 	struct nat64lsn_states_chunk *chunk;
1770d18c1f26SAndrey V. Elsukov 	int i;
1771d8caf56eSAndrey V. Elsukov 
1772d18c1f26SAndrey V. Elsukov 	chunk = (struct nat64lsn_states_chunk *)mem;
1773d18c1f26SAndrey V. Elsukov 	for (i = 0; i < 64; i++)
1774d18c1f26SAndrey V. Elsukov 		chunk->state[i].flags = 0;
1775d8caf56eSAndrey V. Elsukov 	return (0);
1776d8caf56eSAndrey V. Elsukov }
1777d8caf56eSAndrey V. Elsukov 
1778d8caf56eSAndrey V. Elsukov void
nat64lsn_init_internal(void)1779d8caf56eSAndrey V. Elsukov nat64lsn_init_internal(void)
1780d8caf56eSAndrey V. Elsukov {
1781d8caf56eSAndrey V. Elsukov 
1782d18c1f26SAndrey V. Elsukov 	nat64lsn_host_zone = uma_zcreate("NAT64LSN hosts",
1783d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_host), NULL, NULL, NULL, NULL,
1784d6369c2dSAndrey V. Elsukov 	    UMA_ALIGN_PTR, 0);
1785d18c1f26SAndrey V. Elsukov 	nat64lsn_pgchunk_zone = uma_zcreate("NAT64LSN portgroup chunks",
1786d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_pgchunk), NULL, NULL, NULL, NULL,
1787d18c1f26SAndrey V. Elsukov 	    UMA_ALIGN_PTR, 0);
1788d18c1f26SAndrey V. Elsukov 	nat64lsn_pg_zone = uma_zcreate("NAT64LSN portgroups",
1789d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_pg), NULL, NULL, NULL, NULL,
1790d18c1f26SAndrey V. Elsukov 	    UMA_ALIGN_PTR, 0);
1791d18c1f26SAndrey V. Elsukov 	nat64lsn_aliaslink_zone = uma_zcreate("NAT64LSN links",
1792d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_aliaslink), NULL, NULL, NULL, NULL,
1793d18c1f26SAndrey V. Elsukov 	    UMA_ALIGN_PTR, 0);
1794d18c1f26SAndrey V. Elsukov 	nat64lsn_state_zone = uma_zcreate("NAT64LSN states",
1795d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_states_chunk), nat64lsn_state_ctor,
1796d18c1f26SAndrey V. Elsukov 	    NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
1797d18c1f26SAndrey V. Elsukov 	nat64lsn_job_zone = uma_zcreate("NAT64LSN jobs",
1798d18c1f26SAndrey V. Elsukov 	    sizeof(struct nat64lsn_job_item), NULL, NULL, NULL, NULL,
1799d18c1f26SAndrey V. Elsukov 	    UMA_ALIGN_PTR, 0);
1800d18c1f26SAndrey V. Elsukov 	JQUEUE_LOCK_INIT();
1801d8caf56eSAndrey V. Elsukov }
1802d8caf56eSAndrey V. Elsukov 
1803d8caf56eSAndrey V. Elsukov void
nat64lsn_uninit_internal(void)1804d8caf56eSAndrey V. Elsukov nat64lsn_uninit_internal(void)
1805d8caf56eSAndrey V. Elsukov {
1806d8caf56eSAndrey V. Elsukov 
1807d18c1f26SAndrey V. Elsukov 	/* XXX: epoch_task drain */
1808d8caf56eSAndrey V. Elsukov 	JQUEUE_LOCK_DESTROY();
1809d8caf56eSAndrey V. Elsukov 	uma_zdestroy(nat64lsn_host_zone);
1810d18c1f26SAndrey V. Elsukov 	uma_zdestroy(nat64lsn_pgchunk_zone);
1811d8caf56eSAndrey V. Elsukov 	uma_zdestroy(nat64lsn_pg_zone);
1812d18c1f26SAndrey V. Elsukov 	uma_zdestroy(nat64lsn_aliaslink_zone);
1813d18c1f26SAndrey V. Elsukov 	uma_zdestroy(nat64lsn_state_zone);
1814d18c1f26SAndrey V. Elsukov 	uma_zdestroy(nat64lsn_job_zone);
1815d8caf56eSAndrey V. Elsukov }
1816d8caf56eSAndrey V. Elsukov 
1817d8caf56eSAndrey V. Elsukov void
nat64lsn_start_instance(struct nat64lsn_cfg * cfg)1818d8caf56eSAndrey V. Elsukov nat64lsn_start_instance(struct nat64lsn_cfg *cfg)
1819d8caf56eSAndrey V. Elsukov {
1820d8caf56eSAndrey V. Elsukov 
1821d18c1f26SAndrey V. Elsukov 	CALLOUT_LOCK(cfg);
1822d8caf56eSAndrey V. Elsukov 	callout_reset(&cfg->periodic, hz * PERIODIC_DELAY,
1823d8caf56eSAndrey V. Elsukov 	    nat64lsn_periodic, cfg);
1824d18c1f26SAndrey V. Elsukov 	CALLOUT_UNLOCK(cfg);
1825d8caf56eSAndrey V. Elsukov }
1826d8caf56eSAndrey V. Elsukov 
1827d8caf56eSAndrey V. Elsukov struct nat64lsn_cfg *
nat64lsn_init_config(struct ip_fw_chain * ch,in_addr_t prefix,int plen)1828*4a77657cSAndrey V. Elsukov nat64lsn_init_config(struct ip_fw_chain *ch, in_addr_t prefix, int plen)
1829d8caf56eSAndrey V. Elsukov {
1830d8caf56eSAndrey V. Elsukov 	struct nat64lsn_cfg *cfg;
1831d18c1f26SAndrey V. Elsukov 	struct nat64lsn_alias *alias;
1832d18c1f26SAndrey V. Elsukov 	int i, naddr;
1833d8caf56eSAndrey V. Elsukov 
1834d18c1f26SAndrey V. Elsukov 	cfg = malloc(sizeof(struct nat64lsn_cfg), M_NAT64LSN,
1835d18c1f26SAndrey V. Elsukov 	    M_WAITOK | M_ZERO);
1836d18c1f26SAndrey V. Elsukov 
1837d18c1f26SAndrey V. Elsukov 	CFG_LOCK_INIT(cfg);
1838d18c1f26SAndrey V. Elsukov 	CALLOUT_LOCK_INIT(cfg);
1839d18c1f26SAndrey V. Elsukov 	STAILQ_INIT(&cfg->jhead);
1840d8caf56eSAndrey V. Elsukov 	cfg->vp = curvnet;
1841782360deSAndrey V. Elsukov 	COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
1842d8caf56eSAndrey V. Elsukov 
1843d18c1f26SAndrey V. Elsukov 	cfg->hash_seed = arc4random();
1844d18c1f26SAndrey V. Elsukov 	cfg->hosts_hashsize = NAT64LSN_HOSTS_HSIZE;
1845d18c1f26SAndrey V. Elsukov 	cfg->hosts_hash = malloc(sizeof(struct nat64lsn_hosts_slist) *
1846d18c1f26SAndrey V. Elsukov 	    cfg->hosts_hashsize, M_NAT64LSN, M_WAITOK | M_ZERO);
1847d18c1f26SAndrey V. Elsukov 	for (i = 0; i < cfg->hosts_hashsize; i++)
1848d18c1f26SAndrey V. Elsukov 		CK_SLIST_INIT(&cfg->hosts_hash[i]);
1849d8caf56eSAndrey V. Elsukov 
1850d18c1f26SAndrey V. Elsukov 	naddr = 1 << (32 - plen);
1851d18c1f26SAndrey V. Elsukov 	cfg->prefix4 = prefix;
1852d18c1f26SAndrey V. Elsukov 	cfg->pmask4 = prefix | (naddr - 1);
1853d18c1f26SAndrey V. Elsukov 	cfg->plen4 = plen;
1854d18c1f26SAndrey V. Elsukov 	cfg->aliases = malloc(sizeof(struct nat64lsn_alias) * naddr,
1855d18c1f26SAndrey V. Elsukov 	    M_NAT64LSN, M_WAITOK | M_ZERO);
1856d18c1f26SAndrey V. Elsukov 	for (i = 0; i < naddr; i++) {
1857d18c1f26SAndrey V. Elsukov 		alias = &cfg->aliases[i];
1858d18c1f26SAndrey V. Elsukov 		alias->addr = prefix + i; /* host byte order */
1859d18c1f26SAndrey V. Elsukov 		CK_SLIST_INIT(&alias->hosts);
1860d18c1f26SAndrey V. Elsukov 		ALIAS_LOCK_INIT(alias);
1861d18c1f26SAndrey V. Elsukov 	}
1862d8caf56eSAndrey V. Elsukov 
1863d18c1f26SAndrey V. Elsukov 	callout_init_mtx(&cfg->periodic, &cfg->periodic_lock, 0);
1864d8caf56eSAndrey V. Elsukov 	callout_init(&cfg->jcallout, CALLOUT_MPSAFE);
1865d8caf56eSAndrey V. Elsukov 
1866d8caf56eSAndrey V. Elsukov 	return (cfg);
1867d8caf56eSAndrey V. Elsukov }
1868d8caf56eSAndrey V. Elsukov 
1869d18c1f26SAndrey V. Elsukov static void
nat64lsn_destroy_pg(struct nat64lsn_pg * pg)1870d18c1f26SAndrey V. Elsukov nat64lsn_destroy_pg(struct nat64lsn_pg *pg)
1871d8caf56eSAndrey V. Elsukov {
1872d8caf56eSAndrey V. Elsukov 	int i;
1873d8caf56eSAndrey V. Elsukov 
1874d18c1f26SAndrey V. Elsukov 	if (pg->chunks_count == 1) {
1875d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_state_zone, pg->states);
1876d18c1f26SAndrey V. Elsukov 	} else {
1877d18c1f26SAndrey V. Elsukov 		for (i = 0; i < pg->chunks_count; i++)
1878d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_state_zone, pg->states_chunk[i]);
1879d18c1f26SAndrey V. Elsukov 		free(pg->states_chunk, M_NAT64LSN);
1880d18c1f26SAndrey V. Elsukov 		free(pg->freemask_chunk, M_NAT64LSN);
1881d8caf56eSAndrey V. Elsukov 	}
1882d18c1f26SAndrey V. Elsukov 	uma_zfree(nat64lsn_pg_zone, pg);
1883d18c1f26SAndrey V. Elsukov }
1884d18c1f26SAndrey V. Elsukov 
1885d18c1f26SAndrey V. Elsukov static void
nat64lsn_destroy_alias(struct nat64lsn_cfg * cfg,struct nat64lsn_alias * alias)1886d18c1f26SAndrey V. Elsukov nat64lsn_destroy_alias(struct nat64lsn_cfg *cfg,
1887d18c1f26SAndrey V. Elsukov     struct nat64lsn_alias *alias)
1888d18c1f26SAndrey V. Elsukov {
1889d18c1f26SAndrey V. Elsukov 	struct nat64lsn_pg *pg;
1890d18c1f26SAndrey V. Elsukov 	int i;
1891d18c1f26SAndrey V. Elsukov 
1892d18c1f26SAndrey V. Elsukov 	while (!CK_SLIST_EMPTY(&alias->portgroups)) {
1893d18c1f26SAndrey V. Elsukov 		pg = CK_SLIST_FIRST(&alias->portgroups);
1894d18c1f26SAndrey V. Elsukov 		CK_SLIST_REMOVE_HEAD(&alias->portgroups, entries);
1895d18c1f26SAndrey V. Elsukov 		nat64lsn_destroy_pg(pg);
1896d18c1f26SAndrey V. Elsukov 	}
1897d18c1f26SAndrey V. Elsukov 	for (i = 0; i < 32; i++) {
1898d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->tcp_chunkmask, i))
1899d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pgchunk_zone, alias->tcp[i]);
1900d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->udp_chunkmask, i))
1901d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pgchunk_zone, alias->udp[i]);
1902d18c1f26SAndrey V. Elsukov 		if (ISSET32(alias->icmp_chunkmask, i))
1903d18c1f26SAndrey V. Elsukov 			uma_zfree(nat64lsn_pgchunk_zone, alias->icmp[i]);
1904d18c1f26SAndrey V. Elsukov 	}
1905d18c1f26SAndrey V. Elsukov 	ALIAS_LOCK_DESTROY(alias);
1906d18c1f26SAndrey V. Elsukov }
1907d18c1f26SAndrey V. Elsukov 
1908d18c1f26SAndrey V. Elsukov static void
nat64lsn_destroy_host(struct nat64lsn_host * host)1909d18c1f26SAndrey V. Elsukov nat64lsn_destroy_host(struct nat64lsn_host *host)
1910d18c1f26SAndrey V. Elsukov {
1911d18c1f26SAndrey V. Elsukov 	struct nat64lsn_aliaslink *link;
1912d18c1f26SAndrey V. Elsukov 
1913d18c1f26SAndrey V. Elsukov 	while (!CK_SLIST_EMPTY(&host->aliases)) {
1914d18c1f26SAndrey V. Elsukov 		link = CK_SLIST_FIRST(&host->aliases);
1915d18c1f26SAndrey V. Elsukov 		CK_SLIST_REMOVE_HEAD(&host->aliases, host_entries);
1916d18c1f26SAndrey V. Elsukov 
1917d18c1f26SAndrey V. Elsukov 		ALIAS_LOCK(link->alias);
1918d18c1f26SAndrey V. Elsukov 		CK_SLIST_REMOVE(&link->alias->hosts, link,
1919d18c1f26SAndrey V. Elsukov 		    nat64lsn_aliaslink, alias_entries);
1920d18c1f26SAndrey V. Elsukov 		link->alias->hosts_count--;
1921d18c1f26SAndrey V. Elsukov 		ALIAS_UNLOCK(link->alias);
1922d18c1f26SAndrey V. Elsukov 
1923d18c1f26SAndrey V. Elsukov 		uma_zfree(nat64lsn_aliaslink_zone, link);
1924d18c1f26SAndrey V. Elsukov 	}
1925d18c1f26SAndrey V. Elsukov 	HOST_LOCK_DESTROY(host);
1926d18c1f26SAndrey V. Elsukov 	free(host->states_hash, M_NAT64LSN);
1927d18c1f26SAndrey V. Elsukov 	uma_zfree(nat64lsn_host_zone, host);
1928d8caf56eSAndrey V. Elsukov }
1929d8caf56eSAndrey V. Elsukov 
1930d8caf56eSAndrey V. Elsukov void
nat64lsn_destroy_config(struct nat64lsn_cfg * cfg)1931*4a77657cSAndrey V. Elsukov nat64lsn_destroy_config(struct nat64lsn_cfg *cfg)
1932d8caf56eSAndrey V. Elsukov {
1933d18c1f26SAndrey V. Elsukov 	struct nat64lsn_host *host;
1934d18c1f26SAndrey V. Elsukov 	int i;
1935d8caf56eSAndrey V. Elsukov 
1936d18c1f26SAndrey V. Elsukov 	CALLOUT_LOCK(cfg);
1937d6369c2dSAndrey V. Elsukov 	callout_drain(&cfg->periodic);
1938d18c1f26SAndrey V. Elsukov 	CALLOUT_UNLOCK(cfg);
1939d18c1f26SAndrey V. Elsukov 	callout_drain(&cfg->jcallout);
1940d8caf56eSAndrey V. Elsukov 
1941d18c1f26SAndrey V. Elsukov 	for (i = 0; i < cfg->hosts_hashsize; i++) {
1942d18c1f26SAndrey V. Elsukov 		while (!CK_SLIST_EMPTY(&cfg->hosts_hash[i])) {
1943d18c1f26SAndrey V. Elsukov 			host = CK_SLIST_FIRST(&cfg->hosts_hash[i]);
1944d18c1f26SAndrey V. Elsukov 			CK_SLIST_REMOVE_HEAD(&cfg->hosts_hash[i], entries);
1945d18c1f26SAndrey V. Elsukov 			nat64lsn_destroy_host(host);
1946d18c1f26SAndrey V. Elsukov 		}
1947d18c1f26SAndrey V. Elsukov 	}
1948d18c1f26SAndrey V. Elsukov 
1949d18c1f26SAndrey V. Elsukov 	for (i = 0; i < (1 << (32 - cfg->plen4)); i++)
1950d18c1f26SAndrey V. Elsukov 		nat64lsn_destroy_alias(cfg, &cfg->aliases[i]);
1951d18c1f26SAndrey V. Elsukov 
1952d18c1f26SAndrey V. Elsukov 	CALLOUT_LOCK_DESTROY(cfg);
1953d18c1f26SAndrey V. Elsukov 	CFG_LOCK_DESTROY(cfg);
1954782360deSAndrey V. Elsukov 	COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
1955d18c1f26SAndrey V. Elsukov 	free(cfg->hosts_hash, M_NAT64LSN);
1956d18c1f26SAndrey V. Elsukov 	free(cfg->aliases, M_NAT64LSN);
1957d18c1f26SAndrey V. Elsukov 	free(cfg, M_NAT64LSN);
1958d8caf56eSAndrey V. Elsukov }
1959*4a77657cSAndrey V. Elsukov 
1960