1345264Sae/*-
2345264Sae * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3345264Sae *
4345264Sae * Copyright (c) 2019 Yandex LLC
5345264Sae * Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
6345264Sae * Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
7345264Sae *
8345264Sae * Redistribution and use in source and binary forms, with or without
9345264Sae * modification, are permitted provided that the following conditions
10345264Sae * are met:
11345264Sae *
12345264Sae * 1. Redistributions of source code must retain the above copyright
13345264Sae *    notice, this list of conditions and the following disclaimer.
14345264Sae * 2. Redistributions in binary form must reproduce the above copyright
15345264Sae *    notice, this list of conditions and the following disclaimer in the
16345264Sae *    documentation and/or other materials provided with the distribution.
17345264Sae *
18345264Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19345264Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20345264Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21345264Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22345264Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23345264Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24345264Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25345264Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26345264Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27345264Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28345264Sae */
29345264Sae
30345264Sae#include <sys/cdefs.h>
31345264Sae__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64clat_control.c 346212 2019-04-14 12:39:09Z ae $");
32345264Sae
33345264Sae#include <sys/param.h>
34345264Sae#include <sys/systm.h>
35345264Sae#include <sys/counter.h>
36345264Sae#include <sys/errno.h>
37345264Sae#include <sys/kernel.h>
38345264Sae#include <sys/lock.h>
39345264Sae#include <sys/malloc.h>
40345264Sae#include <sys/mbuf.h>
41345264Sae#include <sys/module.h>
42345264Sae#include <sys/rmlock.h>
43345264Sae#include <sys/rwlock.h>
44345264Sae#include <sys/socket.h>
45345264Sae#include <sys/sockopt.h>
46345264Sae#include <sys/syslog.h>
47345264Sae#include <sys/sysctl.h>
48345264Sae
49345264Sae#include <net/if.h>
50345264Sae#include <net/if_var.h>
51345264Sae#include <net/route.h>
52345264Sae#include <net/vnet.h>
53346212Sae#include <net/pfil.h>
54345264Sae
55345264Sae#include <netinet/in.h>
56345264Sae#include <netinet/ip_var.h>
57345264Sae#include <netinet/ip_fw.h>
58345264Sae#include <netinet6/in6_var.h>
59345264Sae#include <netinet6/ip6_var.h>
60345264Sae#include <netinet6/ip_fw_nat64.h>
61345264Sae
62345264Sae#include <netpfil/ipfw/ip_fw_private.h>
63345264Sae
64345264Sae#include "nat64clat.h"
65345264Sae
66345264SaeVNET_DEFINE(uint16_t, nat64clat_eid) = 0;
67345264Sae
68345264Saestatic struct nat64clat_cfg *nat64clat_alloc_config(const char *name,
69345264Sae    uint8_t set);
70345264Saestatic void nat64clat_free_config(struct nat64clat_cfg *cfg);
71345264Saestatic struct nat64clat_cfg *nat64clat_find(struct namedobj_instance *ni,
72345264Sae    const char *name, uint8_t set);
73345264Sae
74345264Saestatic struct nat64clat_cfg *
75345264Saenat64clat_alloc_config(const char *name, uint8_t set)
76345264Sae{
77345264Sae	struct nat64clat_cfg *cfg;
78345264Sae
79345264Sae	cfg = malloc(sizeof(struct nat64clat_cfg), M_IPFW, M_WAITOK | M_ZERO);
80345264Sae	COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
81345264Sae	cfg->no.name = cfg->name;
82345264Sae	cfg->no.etlv = IPFW_TLV_NAT64CLAT_NAME;
83345264Sae	cfg->no.set = set;
84345264Sae	strlcpy(cfg->name, name, sizeof(cfg->name));
85345264Sae	return (cfg);
86345264Sae}
87345264Sae
88345264Saestatic void
89345264Saenat64clat_free_config(struct nat64clat_cfg *cfg)
90345264Sae{
91345264Sae
92345264Sae	COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
93345264Sae	free(cfg, M_IPFW);
94345264Sae}
95345264Sae
96345264Saestatic void
97345264Saenat64clat_export_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
98345264Sae    ipfw_nat64clat_cfg *uc)
99345264Sae{
100345264Sae	uc->plat_prefix = cfg->base.plat_prefix;
101345264Sae	uc->plat_plen = cfg->base.plat_plen;
102345264Sae	uc->clat_prefix = cfg->base.clat_prefix;
103345264Sae	uc->clat_plen = cfg->base.clat_plen;
104345264Sae	uc->flags = cfg->base.flags & NAT64CLAT_FLAGSMASK;
105345264Sae	uc->set = cfg->no.set;
106345264Sae	strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
107345264Sae}
108345264Sae
109345264Saestruct nat64clat_dump_arg {
110345264Sae	struct ip_fw_chain *ch;
111345264Sae	struct sockopt_data *sd;
112345264Sae};
113345264Sae
114345264Saestatic int
115345264Saeexport_config_cb(struct namedobj_instance *ni, struct named_object *no,
116345264Sae    void *arg)
117345264Sae{
118345264Sae	struct nat64clat_dump_arg *da = (struct nat64clat_dump_arg *)arg;
119345264Sae	ipfw_nat64clat_cfg *uc;
120345264Sae
121345264Sae	uc = (ipfw_nat64clat_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
122345264Sae	nat64clat_export_config(da->ch, (struct nat64clat_cfg *)no, uc);
123345264Sae	return (0);
124345264Sae}
125345264Sae
126345264Saestatic struct nat64clat_cfg *
127345264Saenat64clat_find(struct namedobj_instance *ni, const char *name, uint8_t set)
128345264Sae{
129345264Sae	struct nat64clat_cfg *cfg;
130345264Sae
131345264Sae	cfg = (struct nat64clat_cfg *)ipfw_objhash_lookup_name_type(ni, set,
132345264Sae	    IPFW_TLV_NAT64CLAT_NAME, name);
133345264Sae
134345264Sae	return (cfg);
135345264Sae}
136345264Sae
137345264Sae/*
138345264Sae * Creates new consumer-side nat64 translator instance.
139345264Sae * Data layout (v0)(current):
140345264Sae * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
141345264Sae *
142345264Sae * Returns 0 on success
143345264Sae */
144345264Saestatic int
145345264Saenat64clat_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
146345264Sae    struct sockopt_data *sd)
147345264Sae{
148345264Sae	ipfw_obj_lheader *olh;
149345264Sae	ipfw_nat64clat_cfg *uc;
150345264Sae	struct namedobj_instance *ni;
151345264Sae	struct nat64clat_cfg *cfg;
152345264Sae
153345264Sae	if (sd->valsize != sizeof(*olh) + sizeof(*uc))
154345264Sae		return (EINVAL);
155345264Sae
156345264Sae	olh = (ipfw_obj_lheader *)sd->kbuf;
157345264Sae	uc = (ipfw_nat64clat_cfg *)(olh + 1);
158345264Sae
159345264Sae	if (ipfw_check_object_name_generic(uc->name) != 0)
160345264Sae		return (EINVAL);
161345264Sae
162345264Sae	if (uc->set >= IPFW_MAX_SETS ||
163345264Sae	    nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0 ||
164345264Sae	    nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0)
165345264Sae		return (EINVAL);
166345264Sae
167345264Sae	ni = CHAIN_TO_SRV(ch);
168345264Sae
169345264Sae	IPFW_UH_RLOCK(ch);
170345264Sae	if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
171345264Sae		IPFW_UH_RUNLOCK(ch);
172345264Sae		return (EEXIST);
173345264Sae	}
174345264Sae	IPFW_UH_RUNLOCK(ch);
175345264Sae
176345264Sae	cfg = nat64clat_alloc_config(uc->name, uc->set);
177345264Sae	cfg->base.plat_prefix = uc->plat_prefix;
178345264Sae	cfg->base.plat_plen = uc->plat_plen;
179345264Sae	cfg->base.clat_prefix = uc->clat_prefix;
180345264Sae	cfg->base.clat_plen = uc->clat_plen;
181345264Sae	cfg->base.flags = (uc->flags & NAT64CLAT_FLAGSMASK) |
182345264Sae	    NAT64_CLATPFX | NAT64_PLATPFX;
183345264Sae	if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))
184345264Sae		cfg->base.flags |= NAT64_WKPFX;
185345264Sae
186345264Sae	IPFW_UH_WLOCK(ch);
187345264Sae
188345264Sae	if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
189345264Sae		IPFW_UH_WUNLOCK(ch);
190345264Sae		nat64clat_free_config(cfg);
191345264Sae		return (EEXIST);
192345264Sae	}
193345264Sae
194345264Sae	if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) {
195345264Sae		IPFW_UH_WUNLOCK(ch);
196345264Sae		nat64clat_free_config(cfg);
197345264Sae		return (ENOSPC);
198345264Sae	}
199345264Sae	ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
200345264Sae	/* Okay, let's link data */
201345264Sae	SRV_OBJECT(ch, cfg->no.kidx) = cfg;
202345264Sae	IPFW_UH_WUNLOCK(ch);
203345264Sae
204345264Sae	return (0);
205345264Sae}
206345264Sae
207345264Sae/*
208345264Sae * Change existing nat64clat instance configuration.
209345264Sae * Data layout (v0)(current):
210345264Sae * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
211345264Sae * Reply: [ ipfw_obj_header ipfw_nat64clat_cfg ]
212345264Sae *
213345264Sae * Returns 0 on success
214345264Sae */
215345264Saestatic int
216345264Saenat64clat_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
217345264Sae    struct sockopt_data *sd)
218345264Sae{
219345264Sae	ipfw_obj_header *oh;
220345264Sae	ipfw_nat64clat_cfg *uc;
221345264Sae	struct nat64clat_cfg *cfg;
222345264Sae	struct namedobj_instance *ni;
223345264Sae	uint32_t flags;
224345264Sae
225345264Sae	if (sd->valsize != sizeof(*oh) + sizeof(*uc))
226345264Sae		return (EINVAL);
227345264Sae
228345264Sae	oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
229345264Sae	    sizeof(*oh) + sizeof(*uc));
230345264Sae	uc = (ipfw_nat64clat_cfg *)(oh + 1);
231345264Sae
232345264Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
233345264Sae	    oh->ntlv.set >= IPFW_MAX_SETS)
234345264Sae		return (EINVAL);
235345264Sae
236345264Sae	ni = CHAIN_TO_SRV(ch);
237345264Sae	if (sd->sopt->sopt_dir == SOPT_GET) {
238345264Sae		IPFW_UH_RLOCK(ch);
239345264Sae		cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
240345264Sae		if (cfg == NULL) {
241345264Sae			IPFW_UH_RUNLOCK(ch);
242345264Sae			return (ENOENT);
243345264Sae		}
244345264Sae		nat64clat_export_config(ch, cfg, uc);
245345264Sae		IPFW_UH_RUNLOCK(ch);
246345264Sae		return (0);
247345264Sae	}
248345264Sae
249345264Sae	IPFW_UH_WLOCK(ch);
250345264Sae	cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
251345264Sae	if (cfg == NULL) {
252345264Sae		IPFW_UH_WUNLOCK(ch);
253345264Sae		return (ENOENT);
254345264Sae	}
255345264Sae
256345264Sae	/*
257345264Sae	 * For now allow to change only following values:
258345264Sae	 *  plat_prefix, plat_plen, clat_prefix, clat_plen, flags.
259345264Sae	 */
260345264Sae	flags = 0;
261345264Sae	if (uc->plat_plen != cfg->base.plat_plen ||
262345264Sae	    !IN6_ARE_ADDR_EQUAL(&uc->plat_prefix, &cfg->base.plat_prefix)) {
263345264Sae		if (nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0) {
264345264Sae			IPFW_UH_WUNLOCK(ch);
265345264Sae			return (EINVAL);
266345264Sae		}
267345264Sae		flags |= NAT64_PLATPFX;
268345264Sae	}
269345264Sae
270345264Sae	if (uc->clat_plen != cfg->base.clat_plen ||
271345264Sae	    !IN6_ARE_ADDR_EQUAL(&uc->clat_prefix, &cfg->base.clat_prefix)) {
272345264Sae		if (nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) {
273345264Sae			IPFW_UH_WUNLOCK(ch);
274345264Sae			return (EINVAL);
275345264Sae		}
276345264Sae		flags |= NAT64_CLATPFX;
277345264Sae	}
278345264Sae
279345264Sae	if (flags != 0) {
280345264Sae		IPFW_WLOCK(ch);
281345264Sae		if (flags & NAT64_PLATPFX) {
282345264Sae			cfg->base.plat_prefix = uc->plat_prefix;
283345264Sae			cfg->base.plat_plen = uc->plat_plen;
284345264Sae		}
285345264Sae		if (flags & NAT64_CLATPFX) {
286345264Sae			cfg->base.clat_prefix = uc->clat_prefix;
287345264Sae			cfg->base.clat_plen = uc->clat_plen;
288345264Sae		}
289345264Sae		IPFW_WUNLOCK(ch);
290345264Sae	}
291345264Sae
292345264Sae	cfg->base.flags &= ~NAT64CLAT_FLAGSMASK;
293345264Sae	cfg->base.flags |= uc->flags & NAT64CLAT_FLAGSMASK;
294345264Sae
295345264Sae	IPFW_UH_WUNLOCK(ch);
296345264Sae	return (0);
297345264Sae}
298345264Sae
299345264Saestatic void
300345264Saenat64clat_detach_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg)
301345264Sae{
302345264Sae
303345264Sae	IPFW_UH_WLOCK_ASSERT(ch);
304345264Sae
305345264Sae	ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
306345264Sae	ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
307345264Sae}
308345264Sae
309345264Sae/*
310345264Sae * Destroys nat64 instance.
311345264Sae * Data layout (v0)(current):
312345264Sae * Request: [ ipfw_obj_header ]
313345264Sae *
314345264Sae * Returns 0 on success
315345264Sae */
316345264Saestatic int
317345264Saenat64clat_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
318345264Sae    struct sockopt_data *sd)
319345264Sae{
320345264Sae	ipfw_obj_header *oh;
321345264Sae	struct nat64clat_cfg *cfg;
322345264Sae
323345264Sae	if (sd->valsize != sizeof(*oh))
324345264Sae		return (EINVAL);
325345264Sae
326345264Sae	oh = (ipfw_obj_header *)sd->kbuf;
327345264Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
328345264Sae		return (EINVAL);
329345264Sae
330345264Sae	IPFW_UH_WLOCK(ch);
331345264Sae	cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
332345264Sae	if (cfg == NULL) {
333345264Sae		IPFW_UH_WUNLOCK(ch);
334345264Sae		return (ENOENT);
335345264Sae	}
336345264Sae	if (cfg->no.refcnt > 0) {
337345264Sae		IPFW_UH_WUNLOCK(ch);
338345264Sae		return (EBUSY);
339345264Sae	}
340345264Sae
341345264Sae	ipfw_reset_eaction_instance(ch, V_nat64clat_eid, cfg->no.kidx);
342345264Sae	SRV_OBJECT(ch, cfg->no.kidx) = NULL;
343345264Sae	nat64clat_detach_config(ch, cfg);
344345264Sae	IPFW_UH_WUNLOCK(ch);
345345264Sae
346345264Sae	nat64clat_free_config(cfg);
347345264Sae	return (0);
348345264Sae}
349345264Sae
350345264Sae/*
351345264Sae * Lists all nat64clat instances currently available in kernel.
352345264Sae * Data layout (v0)(current):
353345264Sae * Request: [ ipfw_obj_lheader ]
354345264Sae * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
355345264Sae *
356345264Sae * Returns 0 on success
357345264Sae */
358345264Saestatic int
359345264Saenat64clat_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
360345264Sae    struct sockopt_data *sd)
361345264Sae{
362345264Sae	ipfw_obj_lheader *olh;
363345264Sae	struct nat64clat_dump_arg da;
364345264Sae
365345264Sae	/* Check minimum header size */
366345264Sae	if (sd->valsize < sizeof(ipfw_obj_lheader))
367345264Sae		return (EINVAL);
368345264Sae
369345264Sae	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
370345264Sae
371345264Sae	IPFW_UH_RLOCK(ch);
372345264Sae	olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
373345264Sae	    IPFW_TLV_NAT64CLAT_NAME);
374345264Sae	olh->objsize = sizeof(ipfw_nat64clat_cfg);
375345264Sae	olh->size = sizeof(*olh) + olh->count * olh->objsize;
376345264Sae
377345264Sae	if (sd->valsize < olh->size) {
378345264Sae		IPFW_UH_RUNLOCK(ch);
379345264Sae		return (ENOMEM);
380345264Sae	}
381345264Sae	memset(&da, 0, sizeof(da));
382345264Sae	da.ch = ch;
383345264Sae	da.sd = sd;
384345264Sae	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
385345264Sae	    &da, IPFW_TLV_NAT64CLAT_NAME);
386345264Sae	IPFW_UH_RUNLOCK(ch);
387345264Sae
388345264Sae	return (0);
389345264Sae}
390345264Sae
391345264Sae#define	__COPY_STAT_FIELD(_cfg, _stats, _field)	\
392345264Sae	(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)
393345264Saestatic void
394345264Saeexport_stats(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
395345264Sae    struct ipfw_nat64clat_stats *stats)
396345264Sae{
397345264Sae
398345264Sae	__COPY_STAT_FIELD(cfg, stats, opcnt64);
399345264Sae	__COPY_STAT_FIELD(cfg, stats, opcnt46);
400345264Sae	__COPY_STAT_FIELD(cfg, stats, ofrags);
401345264Sae	__COPY_STAT_FIELD(cfg, stats, ifrags);
402345264Sae	__COPY_STAT_FIELD(cfg, stats, oerrors);
403345264Sae	__COPY_STAT_FIELD(cfg, stats, noroute4);
404345264Sae	__COPY_STAT_FIELD(cfg, stats, noroute6);
405345264Sae	__COPY_STAT_FIELD(cfg, stats, noproto);
406345264Sae	__COPY_STAT_FIELD(cfg, stats, nomem);
407345264Sae	__COPY_STAT_FIELD(cfg, stats, dropped);
408345264Sae}
409345264Sae
410345264Sae/*
411345264Sae * Get nat64clat statistics.
412345264Sae * Data layout (v0)(current):
413345264Sae * Request: [ ipfw_obj_header ]
414345264Sae * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
415345264Sae *
416345264Sae * Returns 0 on success
417345264Sae */
418345264Saestatic int
419345264Saenat64clat_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
420345264Sae    struct sockopt_data *sd)
421345264Sae{
422345264Sae	struct ipfw_nat64clat_stats stats;
423345264Sae	struct nat64clat_cfg *cfg;
424345264Sae	ipfw_obj_header *oh;
425345264Sae	ipfw_obj_ctlv *ctlv;
426345264Sae	size_t sz;
427345264Sae
428345264Sae	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
429345264Sae	if (sd->valsize % sizeof(uint64_t))
430345264Sae		return (EINVAL);
431345264Sae	if (sd->valsize < sz)
432345264Sae		return (ENOMEM);
433345264Sae	oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
434345264Sae	if (oh == NULL)
435345264Sae		return (EINVAL);
436345264Sae	memset(&stats, 0, sizeof(stats));
437345264Sae
438345264Sae	IPFW_UH_RLOCK(ch);
439345264Sae	cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
440345264Sae	if (cfg == NULL) {
441345264Sae		IPFW_UH_RUNLOCK(ch);
442345264Sae		return (ENOENT);
443345264Sae	}
444345264Sae	export_stats(ch, cfg, &stats);
445345264Sae	IPFW_UH_RUNLOCK(ch);
446345264Sae
447345264Sae	ctlv = (ipfw_obj_ctlv *)(oh + 1);
448345264Sae	memset(ctlv, 0, sizeof(*ctlv));
449345264Sae	ctlv->head.type = IPFW_TLV_COUNTERS;
450345264Sae	ctlv->head.length = sz - sizeof(ipfw_obj_header);
451345264Sae	ctlv->count = sizeof(stats) / sizeof(uint64_t);
452345264Sae	ctlv->objsize = sizeof(uint64_t);
453345264Sae	ctlv->version = IPFW_NAT64_VERSION;
454345264Sae	memcpy(ctlv + 1, &stats, sizeof(stats));
455345264Sae	return (0);
456345264Sae}
457345264Sae
458345264Sae/*
459345264Sae * Reset nat64clat statistics.
460345264Sae * Data layout (v0)(current):
461345264Sae * Request: [ ipfw_obj_header ]
462345264Sae *
463345264Sae * Returns 0 on success
464345264Sae */
465345264Saestatic int
466345264Saenat64clat_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
467345264Sae    struct sockopt_data *sd)
468345264Sae{
469345264Sae	struct nat64clat_cfg *cfg;
470345264Sae	ipfw_obj_header *oh;
471345264Sae
472345264Sae	if (sd->valsize != sizeof(*oh))
473345264Sae		return (EINVAL);
474345264Sae	oh = (ipfw_obj_header *)sd->kbuf;
475345264Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
476345264Sae	    oh->ntlv.set >= IPFW_MAX_SETS)
477345264Sae		return (EINVAL);
478345264Sae
479345264Sae	IPFW_UH_WLOCK(ch);
480345264Sae	cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
481345264Sae	if (cfg == NULL) {
482345264Sae		IPFW_UH_WUNLOCK(ch);
483345264Sae		return (ENOENT);
484345264Sae	}
485345264Sae	COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);
486345264Sae	IPFW_UH_WUNLOCK(ch);
487345264Sae	return (0);
488345264Sae}
489345264Sae
490345264Saestatic struct ipfw_sopt_handler	scodes[] = {
491345264Sae
492345264Sae	{ IP_FW_NAT64CLAT_CREATE, 0,	HDIR_SET,	nat64clat_create },
493345264Sae	{ IP_FW_NAT64CLAT_DESTROY,0,	HDIR_SET,	nat64clat_destroy },
494345264Sae	{ IP_FW_NAT64CLAT_CONFIG, 0,	HDIR_BOTH,	nat64clat_config },
495345264Sae	{ IP_FW_NAT64CLAT_LIST,   0,	HDIR_GET,	nat64clat_list },
496345264Sae	{ IP_FW_NAT64CLAT_STATS,  0,	HDIR_GET,	nat64clat_stats },
497345264Sae	{ IP_FW_NAT64CLAT_RESET_STATS,0,	HDIR_SET,	nat64clat_reset_stats },
498345264Sae};
499345264Sae
500345264Saestatic int
501345264Saenat64clat_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
502345264Sae{
503345264Sae	ipfw_insn *icmd;
504345264Sae
505345264Sae	icmd = cmd - 1;
506345264Sae	if (icmd->opcode != O_EXTERNAL_ACTION ||
507345264Sae	    icmd->arg1 != V_nat64clat_eid)
508345264Sae		return (1);
509345264Sae
510345264Sae	*puidx = cmd->arg1;
511345264Sae	*ptype = 0;
512345264Sae	return (0);
513345264Sae}
514345264Sae
515345264Saestatic void
516345264Saenat64clat_update_arg1(ipfw_insn *cmd, uint16_t idx)
517345264Sae{
518345264Sae
519345264Sae	cmd->arg1 = idx;
520345264Sae}
521345264Sae
522345264Saestatic int
523345264Saenat64clat_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
524345264Sae    struct named_object **pno)
525345264Sae{
526345264Sae	int err;
527345264Sae
528345264Sae	err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
529345264Sae	    IPFW_TLV_NAT64CLAT_NAME, pno);
530345264Sae	return (err);
531345264Sae}
532345264Sae
533345264Saestatic struct named_object *
534345264Saenat64clat_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
535345264Sae{
536345264Sae	struct namedobj_instance *ni;
537345264Sae	struct named_object *no;
538345264Sae
539345264Sae	IPFW_UH_WLOCK_ASSERT(ch);
540345264Sae	ni = CHAIN_TO_SRV(ch);
541345264Sae	no = ipfw_objhash_lookup_kidx(ni, idx);
542345264Sae	KASSERT(no != NULL, ("NAT with index %d not found", idx));
543345264Sae
544345264Sae	return (no);
545345264Sae}
546345264Sae
547345264Saestatic int
548345264Saenat64clat_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
549345264Sae    enum ipfw_sets_cmd cmd)
550345264Sae{
551345264Sae
552345264Sae	return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64CLAT_NAME,
553345264Sae	    set, new_set, cmd));
554345264Sae}
555345264Sae
556345264Saestatic struct opcode_obj_rewrite opcodes[] = {
557345264Sae	{
558345264Sae		.opcode = O_EXTERNAL_INSTANCE,
559345264Sae		.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
560345264Sae		.classifier = nat64clat_classify,
561345264Sae		.update = nat64clat_update_arg1,
562345264Sae		.find_byname = nat64clat_findbyname,
563345264Sae		.find_bykidx = nat64clat_findbykidx,
564345264Sae		.manage_sets = nat64clat_manage_sets,
565345264Sae	},
566345264Sae};
567345264Sae
568345264Saestatic int
569345264Saedestroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
570345264Sae    void *arg)
571345264Sae{
572345264Sae	struct nat64clat_cfg *cfg;
573345264Sae	struct ip_fw_chain *ch;
574345264Sae
575345264Sae	ch = (struct ip_fw_chain *)arg;
576345264Sae	cfg = (struct nat64clat_cfg *)SRV_OBJECT(ch, no->kidx);
577345264Sae	SRV_OBJECT(ch, no->kidx) = NULL;
578345264Sae	nat64clat_detach_config(ch, cfg);
579345264Sae	nat64clat_free_config(cfg);
580345264Sae	return (0);
581345264Sae}
582345264Sae
583345264Saeint
584345264Saenat64clat_init(struct ip_fw_chain *ch, int first)
585345264Sae{
586345264Sae
587345264Sae	V_nat64clat_eid = ipfw_add_eaction(ch, ipfw_nat64clat, "nat64clat");
588345264Sae	if (V_nat64clat_eid == 0)
589345264Sae		return (ENXIO);
590345264Sae	IPFW_ADD_SOPT_HANDLER(first, scodes);
591345264Sae	IPFW_ADD_OBJ_REWRITER(first, opcodes);
592345264Sae	return (0);
593345264Sae}
594345264Sae
595345264Saevoid
596345264Saenat64clat_uninit(struct ip_fw_chain *ch, int last)
597345264Sae{
598345264Sae
599345264Sae	IPFW_DEL_OBJ_REWRITER(last, opcodes);
600345264Sae	IPFW_DEL_SOPT_HANDLER(last, scodes);
601345264Sae	ipfw_del_eaction(ch, V_nat64clat_eid);
602345264Sae	/*
603345264Sae	 * Since we already have deregistered external action,
604345264Sae	 * our named objects become unaccessible via rules, because
605345264Sae	 * all rules were truncated by ipfw_del_eaction().
606345264Sae	 * So, we can unlink and destroy our named objects without holding
607345264Sae	 * IPFW_WLOCK().
608345264Sae	 */
609345264Sae	IPFW_UH_WLOCK(ch);
610345264Sae	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
611345264Sae	    IPFW_TLV_NAT64CLAT_NAME);
612345264Sae	V_nat64clat_eid = 0;
613345264Sae	IPFW_UH_WUNLOCK(ch);
614345264Sae}
615345264Sae
616