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