1304046Sae/*-
2346211Sae * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3346211Sae *
4346211Sae * Copyright (c) 2015-2019 Yandex LLC
5304046Sae * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
6346211Sae * Copyright (c) 2015-2019 Andrey V. Elsukov <ae@FreeBSD.org>
7304046Sae *
8304046Sae * Redistribution and use in source and binary forms, with or without
9304046Sae * modification, are permitted provided that the following conditions
10304046Sae * are met:
11304046Sae *
12304046Sae * 1. Redistributions of source code must retain the above copyright
13304046Sae *    notice, this list of conditions and the following disclaimer.
14304046Sae * 2. Redistributions in binary form must reproduce the above copyright
15304046Sae *    notice, this list of conditions and the following disclaimer in the
16304046Sae *    documentation and/or other materials provided with the distribution.
17304046Sae *
18304046Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19304046Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20304046Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21304046Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22304046Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23304046Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24304046Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25304046Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26304046Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27304046Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28304046Sae */
29304046Sae
30304046Sae#include <sys/cdefs.h>
31304046Sae__FBSDID("$FreeBSD: stable/11/sys/netpfil/ipfw/nat64/nat64stl_control.c 346211 2019-04-14 12:35:58Z ae $");
32304046Sae
33304046Sae#include <sys/param.h>
34304046Sae#include <sys/systm.h>
35304046Sae#include <sys/counter.h>
36304046Sae#include <sys/errno.h>
37304046Sae#include <sys/kernel.h>
38304046Sae#include <sys/lock.h>
39304046Sae#include <sys/malloc.h>
40304046Sae#include <sys/mbuf.h>
41304046Sae#include <sys/module.h>
42304046Sae#include <sys/rmlock.h>
43304046Sae#include <sys/rwlock.h>
44304046Sae#include <sys/socket.h>
45304046Sae#include <sys/sockopt.h>
46304046Sae#include <sys/queue.h>
47304046Sae#include <sys/syslog.h>
48304046Sae#include <sys/sysctl.h>
49304046Sae
50304046Sae#include <net/if.h>
51304046Sae#include <net/if_var.h>
52304046Sae#include <net/pfil.h>
53304046Sae#include <net/route.h>
54304046Sae#include <net/vnet.h>
55304046Sae
56304046Sae#include <netinet/in.h>
57304046Sae#include <netinet/ip_var.h>
58304046Sae#include <netinet/ip_fw.h>
59304046Sae#include <netinet6/in6_var.h>
60304046Sae#include <netinet6/ip6_var.h>
61334836Sae#include <netinet6/ip_fw_nat64.h>
62304046Sae
63304046Sae#include <netpfil/ipfw/ip_fw_private.h>
64304046Sae
65334836Sae#include "nat64stl.h"
66334836Sae
67304046SaeVNET_DEFINE(uint16_t, nat64stl_eid) = 0;
68304046Sae
69334836Saestatic struct nat64stl_cfg *nat64stl_alloc_config(const char *name,
70334836Sae    uint8_t set);
71304046Saestatic void nat64stl_free_config(struct nat64stl_cfg *cfg);
72304046Saestatic struct nat64stl_cfg *nat64stl_find(struct namedobj_instance *ni,
73304046Sae    const char *name, uint8_t set);
74304046Sae
75304046Saestatic struct nat64stl_cfg *
76304046Saenat64stl_alloc_config(const char *name, uint8_t set)
77304046Sae{
78304046Sae	struct nat64stl_cfg *cfg;
79304046Sae
80304046Sae	cfg = malloc(sizeof(struct nat64stl_cfg), M_IPFW, M_WAITOK | M_ZERO);
81334836Sae	COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
82304046Sae	cfg->no.name = cfg->name;
83304046Sae	cfg->no.etlv = IPFW_TLV_NAT64STL_NAME;
84304046Sae	cfg->no.set = set;
85304046Sae	strlcpy(cfg->name, name, sizeof(cfg->name));
86304046Sae	return (cfg);
87304046Sae}
88304046Sae
89304046Saestatic void
90304046Saenat64stl_free_config(struct nat64stl_cfg *cfg)
91304046Sae{
92304046Sae
93334836Sae	COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
94304046Sae	free(cfg, M_IPFW);
95304046Sae}
96304046Sae
97304046Saestatic void
98304046Saenat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
99304046Sae    ipfw_nat64stl_cfg *uc)
100304046Sae{
101304046Sae	struct named_object *no;
102304046Sae
103346210Sae	uc->prefix6 = cfg->base.plat_prefix;
104346210Sae	uc->plen6 = cfg->base.plat_plen;
105334836Sae	uc->flags = cfg->base.flags & NAT64STL_FLAGSMASK;
106304046Sae	uc->set = cfg->no.set;
107304046Sae	strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
108304046Sae
109304046Sae	no = ipfw_objhash_lookup_table_kidx(ch, cfg->map64);
110304046Sae	ipfw_export_obj_ntlv(no, &uc->ntlv6);
111304046Sae	no = ipfw_objhash_lookup_table_kidx(ch, cfg->map46);
112304046Sae	ipfw_export_obj_ntlv(no, &uc->ntlv4);
113304046Sae}
114304046Sae
115304046Saestruct nat64stl_dump_arg {
116304046Sae	struct ip_fw_chain *ch;
117304046Sae	struct sockopt_data *sd;
118304046Sae};
119304046Sae
120304046Saestatic int
121304046Saeexport_config_cb(struct namedobj_instance *ni, struct named_object *no,
122304046Sae    void *arg)
123304046Sae{
124304046Sae	struct nat64stl_dump_arg *da = (struct nat64stl_dump_arg *)arg;
125304046Sae	ipfw_nat64stl_cfg *uc;
126304046Sae
127304046Sae	uc = (ipfw_nat64stl_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
128304046Sae	nat64stl_export_config(da->ch, (struct nat64stl_cfg *)no, uc);
129304046Sae	return (0);
130304046Sae}
131304046Sae
132304046Saestatic struct nat64stl_cfg *
133304046Saenat64stl_find(struct namedobj_instance *ni, const char *name, uint8_t set)
134304046Sae{
135304046Sae	struct nat64stl_cfg *cfg;
136304046Sae
137304046Sae	cfg = (struct nat64stl_cfg *)ipfw_objhash_lookup_name_type(ni, set,
138304046Sae	    IPFW_TLV_NAT64STL_NAME, name);
139304046Sae
140304046Sae	return (cfg);
141304046Sae}
142304046Sae
143304046Sae
144304046Saestatic int
145304046Saenat64stl_create_internal(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
146304046Sae    ipfw_nat64stl_cfg *i)
147304046Sae{
148304046Sae
149304046Sae	IPFW_UH_WLOCK_ASSERT(ch);
150304046Sae
151304046Sae	if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0)
152304046Sae		return (ENOSPC);
153334836Sae	cfg->base.flags |= NAT64STL_KIDX;
154304046Sae
155304046Sae	if (ipfw_ref_table(ch, &i->ntlv4, &cfg->map46) != 0)
156304046Sae		return (EINVAL);
157334836Sae	cfg->base.flags |= NAT64STL_46T;
158304046Sae
159304046Sae	if (ipfw_ref_table(ch, &i->ntlv6, &cfg->map64) != 0)
160304046Sae		return (EINVAL);
161334836Sae	cfg->base.flags |= NAT64STL_64T;
162304046Sae
163304046Sae	ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
164304046Sae
165304046Sae	return (0);
166304046Sae}
167304046Sae
168304046Sae/*
169304046Sae * Creates new nat64 instance.
170304046Sae * Data layout (v0)(current):
171304046Sae * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ]
172304046Sae *
173304046Sae * Returns 0 on success
174304046Sae */
175304046Saestatic int
176304046Saenat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
177304046Sae    struct sockopt_data *sd)
178304046Sae{
179304046Sae	ipfw_obj_lheader *olh;
180304046Sae	ipfw_nat64stl_cfg *uc;
181304046Sae	struct namedobj_instance *ni;
182304046Sae	struct nat64stl_cfg *cfg;
183304046Sae	int error;
184304046Sae
185304046Sae	if (sd->valsize != sizeof(*olh) + sizeof(*uc))
186304046Sae		return (EINVAL);
187304046Sae
188304046Sae	olh = (ipfw_obj_lheader *)sd->kbuf;
189304046Sae	uc = (ipfw_nat64stl_cfg *)(olh + 1);
190304046Sae
191304046Sae	if (ipfw_check_object_name_generic(uc->name) != 0)
192304046Sae		return (EINVAL);
193334836Sae	if (uc->set >= IPFW_MAX_SETS ||
194334836Sae	    nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0)
195304046Sae		return (EINVAL);
196304046Sae
197304046Sae	/* XXX: check types of tables */
198304046Sae
199304046Sae	ni = CHAIN_TO_SRV(ch);
200304046Sae	error = 0;
201304046Sae
202304046Sae	IPFW_UH_RLOCK(ch);
203304046Sae	if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
204304046Sae		IPFW_UH_RUNLOCK(ch);
205304046Sae		return (EEXIST);
206304046Sae	}
207304046Sae	IPFW_UH_RUNLOCK(ch);
208304046Sae
209304046Sae	cfg = nat64stl_alloc_config(uc->name, uc->set);
210346210Sae	cfg->base.plat_prefix = uc->prefix6;
211346210Sae	cfg->base.plat_plen = uc->plen6;
212346210Sae	cfg->base.flags = (uc->flags & NAT64STL_FLAGSMASK) | NAT64_PLATPFX;
213346210Sae	if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))
214334836Sae		cfg->base.flags |= NAT64_WKPFX;
215304046Sae
216304046Sae	IPFW_UH_WLOCK(ch);
217304046Sae
218304046Sae	if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
219304046Sae		IPFW_UH_WUNLOCK(ch);
220304046Sae		nat64stl_free_config(cfg);
221304046Sae		return (EEXIST);
222304046Sae	}
223304046Sae	error = nat64stl_create_internal(ch, cfg, uc);
224304046Sae	if (error == 0) {
225304046Sae		/* Okay, let's link data */
226304046Sae		SRV_OBJECT(ch, cfg->no.kidx) = cfg;
227304046Sae		IPFW_UH_WUNLOCK(ch);
228304046Sae		return (0);
229304046Sae	}
230304046Sae
231334836Sae	if (cfg->base.flags & NAT64STL_KIDX)
232304046Sae		ipfw_objhash_free_idx(ni, cfg->no.kidx);
233334836Sae	if (cfg->base.flags & NAT64STL_46T)
234304046Sae		ipfw_unref_table(ch, cfg->map46);
235334836Sae	if (cfg->base.flags & NAT64STL_64T)
236304046Sae		ipfw_unref_table(ch, cfg->map64);
237304046Sae
238304046Sae	IPFW_UH_WUNLOCK(ch);
239304046Sae	nat64stl_free_config(cfg);
240304046Sae	return (error);
241304046Sae}
242304046Sae
243304046Sae/*
244304046Sae * Change existing nat64stl instance configuration.
245304046Sae * Data layout (v0)(current):
246304046Sae * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ]
247304046Sae * Reply: [ ipfw_obj_header ipfw_nat64stl_cfg ]
248304046Sae *
249304046Sae * Returns 0 on success
250304046Sae */
251304046Saestatic int
252304046Saenat64stl_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
253304046Sae    struct sockopt_data *sd)
254304046Sae{
255304046Sae	ipfw_obj_header *oh;
256304046Sae	ipfw_nat64stl_cfg *uc;
257304046Sae	struct nat64stl_cfg *cfg;
258304046Sae	struct namedobj_instance *ni;
259304046Sae
260304046Sae	if (sd->valsize != sizeof(*oh) + sizeof(*uc))
261304046Sae		return (EINVAL);
262304046Sae
263304046Sae	oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
264304046Sae	    sizeof(*oh) + sizeof(*uc));
265304046Sae	uc = (ipfw_nat64stl_cfg *)(oh + 1);
266304046Sae
267304046Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
268304046Sae	    oh->ntlv.set >= IPFW_MAX_SETS)
269304046Sae		return (EINVAL);
270304046Sae
271304046Sae	ni = CHAIN_TO_SRV(ch);
272304046Sae	if (sd->sopt->sopt_dir == SOPT_GET) {
273304046Sae		IPFW_UH_RLOCK(ch);
274304046Sae		cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
275304046Sae		if (cfg == NULL) {
276304046Sae			IPFW_UH_RUNLOCK(ch);
277304046Sae			return (EEXIST);
278304046Sae		}
279304046Sae		nat64stl_export_config(ch, cfg, uc);
280304046Sae		IPFW_UH_RUNLOCK(ch);
281304046Sae		return (0);
282304046Sae	}
283304046Sae
284304046Sae	IPFW_UH_WLOCK(ch);
285304046Sae	cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
286304046Sae	if (cfg == NULL) {
287304046Sae		IPFW_UH_WUNLOCK(ch);
288304046Sae		return (EEXIST);
289304046Sae	}
290304046Sae
291304046Sae	/*
292304046Sae	 * For now allow to change only following values:
293304046Sae	 *  flags.
294304046Sae	 */
295334836Sae	cfg->base.flags &= ~NAT64STL_FLAGSMASK;
296334836Sae	cfg->base.flags |= uc->flags & NAT64STL_FLAGSMASK;
297334836Sae
298304046Sae	IPFW_UH_WUNLOCK(ch);
299304046Sae	return (0);
300304046Sae}
301304046Sae
302304046Saestatic void
303304046Saenat64stl_detach_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg)
304304046Sae{
305304046Sae
306304046Sae	IPFW_UH_WLOCK_ASSERT(ch);
307304046Sae
308304046Sae	ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
309304046Sae	ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
310304046Sae	ipfw_unref_table(ch, cfg->map46);
311304046Sae	ipfw_unref_table(ch, cfg->map64);
312304046Sae}
313304046Sae
314304046Sae/*
315304046Sae * Destroys nat64 instance.
316304046Sae * Data layout (v0)(current):
317304046Sae * Request: [ ipfw_obj_header ]
318304046Sae *
319304046Sae * Returns 0 on success
320304046Sae */
321304046Saestatic int
322304046Saenat64stl_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
323304046Sae    struct sockopt_data *sd)
324304046Sae{
325304046Sae	ipfw_obj_header *oh;
326304046Sae	struct nat64stl_cfg *cfg;
327304046Sae
328304046Sae	if (sd->valsize != sizeof(*oh))
329304046Sae		return (EINVAL);
330304046Sae
331304046Sae	oh = (ipfw_obj_header *)sd->kbuf;
332304046Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
333304046Sae		return (EINVAL);
334304046Sae
335304046Sae	IPFW_UH_WLOCK(ch);
336304046Sae	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
337304046Sae	if (cfg == NULL) {
338304046Sae		IPFW_UH_WUNLOCK(ch);
339304046Sae		return (ESRCH);
340304046Sae	}
341304046Sae	if (cfg->no.refcnt > 0) {
342304046Sae		IPFW_UH_WUNLOCK(ch);
343304046Sae		return (EBUSY);
344304046Sae	}
345304046Sae
346346205Sae	ipfw_reset_eaction_instance(ch, V_nat64stl_eid, cfg->no.kidx);
347304046Sae	SRV_OBJECT(ch, cfg->no.kidx) = NULL;
348304046Sae	nat64stl_detach_config(ch, cfg);
349304046Sae	IPFW_UH_WUNLOCK(ch);
350304046Sae
351304046Sae	nat64stl_free_config(cfg);
352304046Sae	return (0);
353304046Sae}
354304046Sae
355304046Sae/*
356304046Sae * Lists all nat64stl instances currently available in kernel.
357304046Sae * Data layout (v0)(current):
358304046Sae * Request: [ ipfw_obj_lheader ]
359304046Sae * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ]
360304046Sae *
361304046Sae * Returns 0 on success
362304046Sae */
363304046Saestatic int
364304046Saenat64stl_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
365304046Sae    struct sockopt_data *sd)
366304046Sae{
367304046Sae	ipfw_obj_lheader *olh;
368304046Sae	struct nat64stl_dump_arg da;
369304046Sae
370304046Sae	/* Check minimum header size */
371304046Sae	if (sd->valsize < sizeof(ipfw_obj_lheader))
372304046Sae		return (EINVAL);
373304046Sae
374304046Sae	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
375304046Sae
376304046Sae	IPFW_UH_RLOCK(ch);
377304046Sae	olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
378304046Sae	    IPFW_TLV_NAT64STL_NAME);
379304046Sae	olh->objsize = sizeof(ipfw_nat64stl_cfg);
380304046Sae	olh->size = sizeof(*olh) + olh->count * olh->objsize;
381304046Sae
382304046Sae	if (sd->valsize < olh->size) {
383304046Sae		IPFW_UH_RUNLOCK(ch);
384304046Sae		return (ENOMEM);
385304046Sae	}
386304046Sae	memset(&da, 0, sizeof(da));
387304046Sae	da.ch = ch;
388304046Sae	da.sd = sd;
389304046Sae	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
390304046Sae	    &da, IPFW_TLV_NAT64STL_NAME);
391304046Sae	IPFW_UH_RUNLOCK(ch);
392304046Sae
393304046Sae	return (0);
394304046Sae}
395304046Sae
396304046Sae#define	__COPY_STAT_FIELD(_cfg, _stats, _field)	\
397334836Sae	(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)
398304046Saestatic void
399304046Saeexport_stats(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
400304046Sae    struct ipfw_nat64stl_stats *stats)
401304046Sae{
402304046Sae
403304046Sae	__COPY_STAT_FIELD(cfg, stats, opcnt64);
404304046Sae	__COPY_STAT_FIELD(cfg, stats, opcnt46);
405304046Sae	__COPY_STAT_FIELD(cfg, stats, ofrags);
406304046Sae	__COPY_STAT_FIELD(cfg, stats, ifrags);
407304046Sae	__COPY_STAT_FIELD(cfg, stats, oerrors);
408304046Sae	__COPY_STAT_FIELD(cfg, stats, noroute4);
409304046Sae	__COPY_STAT_FIELD(cfg, stats, noroute6);
410304046Sae	__COPY_STAT_FIELD(cfg, stats, noproto);
411304046Sae	__COPY_STAT_FIELD(cfg, stats, nomem);
412304046Sae	__COPY_STAT_FIELD(cfg, stats, dropped);
413304046Sae}
414304046Sae
415304046Sae/*
416304046Sae * Get nat64stl statistics.
417304046Sae * Data layout (v0)(current):
418304046Sae * Request: [ ipfw_obj_header ]
419304046Sae * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
420304046Sae *
421304046Sae * Returns 0 on success
422304046Sae */
423304046Saestatic int
424304046Saenat64stl_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
425304046Sae    struct sockopt_data *sd)
426304046Sae{
427304046Sae	struct ipfw_nat64stl_stats stats;
428304046Sae	struct nat64stl_cfg *cfg;
429304046Sae	ipfw_obj_header *oh;
430304046Sae	ipfw_obj_ctlv *ctlv;
431304046Sae	size_t sz;
432304046Sae
433304046Sae	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
434304046Sae	if (sd->valsize % sizeof(uint64_t))
435304046Sae		return (EINVAL);
436304046Sae	if (sd->valsize < sz)
437304046Sae		return (ENOMEM);
438304046Sae	oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
439304046Sae	if (oh == NULL)
440304046Sae		return (EINVAL);
441304046Sae	memset(&stats, 0, sizeof(stats));
442304046Sae
443304046Sae	IPFW_UH_RLOCK(ch);
444304046Sae	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
445304046Sae	if (cfg == NULL) {
446304046Sae		IPFW_UH_RUNLOCK(ch);
447304046Sae		return (ESRCH);
448304046Sae	}
449304046Sae	export_stats(ch, cfg, &stats);
450304046Sae	IPFW_UH_RUNLOCK(ch);
451304046Sae
452304046Sae	ctlv = (ipfw_obj_ctlv *)(oh + 1);
453304046Sae	memset(ctlv, 0, sizeof(*ctlv));
454304046Sae	ctlv->head.type = IPFW_TLV_COUNTERS;
455304046Sae	ctlv->head.length = sz - sizeof(ipfw_obj_header);
456304046Sae	ctlv->count = sizeof(stats) / sizeof(uint64_t);
457304046Sae	ctlv->objsize = sizeof(uint64_t);
458304046Sae	ctlv->version = IPFW_NAT64_VERSION;
459304046Sae	memcpy(ctlv + 1, &stats, sizeof(stats));
460304046Sae	return (0);
461304046Sae}
462304046Sae
463304046Sae/*
464304046Sae * Reset nat64stl statistics.
465304046Sae * Data layout (v0)(current):
466304046Sae * Request: [ ipfw_obj_header ]
467304046Sae *
468304046Sae * Returns 0 on success
469304046Sae */
470304046Saestatic int
471304046Saenat64stl_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
472304046Sae    struct sockopt_data *sd)
473304046Sae{
474304046Sae	struct nat64stl_cfg *cfg;
475304046Sae	ipfw_obj_header *oh;
476304046Sae
477304046Sae	if (sd->valsize != sizeof(*oh))
478304046Sae		return (EINVAL);
479304046Sae	oh = (ipfw_obj_header *)sd->kbuf;
480304046Sae	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
481304046Sae	    oh->ntlv.set >= IPFW_MAX_SETS)
482304046Sae		return (EINVAL);
483304046Sae
484304046Sae	IPFW_UH_WLOCK(ch);
485304046Sae	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
486304046Sae	if (cfg == NULL) {
487304046Sae		IPFW_UH_WUNLOCK(ch);
488304046Sae		return (ESRCH);
489304046Sae	}
490334836Sae	COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);
491304046Sae	IPFW_UH_WUNLOCK(ch);
492304046Sae	return (0);
493304046Sae}
494304046Sae
495304046Saestatic struct ipfw_sopt_handler	scodes[] = {
496304046Sae
497304046Sae	{ IP_FW_NAT64STL_CREATE, 0,	HDIR_SET,	nat64stl_create },
498304046Sae	{ IP_FW_NAT64STL_DESTROY,0,	HDIR_SET,	nat64stl_destroy },
499304046Sae	{ IP_FW_NAT64STL_CONFIG, 0,	HDIR_BOTH,	nat64stl_config },
500304046Sae	{ IP_FW_NAT64STL_LIST,   0,	HDIR_GET,	nat64stl_list },
501304046Sae	{ IP_FW_NAT64STL_STATS,  0,	HDIR_GET,	nat64stl_stats },
502304046Sae	{ IP_FW_NAT64STL_RESET_STATS,0,	HDIR_SET,	nat64stl_reset_stats },
503304046Sae};
504304046Sae
505304046Saestatic int
506304046Saenat64stl_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
507304046Sae{
508304046Sae	ipfw_insn *icmd;
509304046Sae
510304046Sae	icmd = cmd - 1;
511304046Sae	if (icmd->opcode != O_EXTERNAL_ACTION ||
512304046Sae	    icmd->arg1 != V_nat64stl_eid)
513304046Sae		return (1);
514304046Sae
515304046Sae	*puidx = cmd->arg1;
516304046Sae	*ptype = 0;
517304046Sae	return (0);
518304046Sae}
519304046Sae
520304046Saestatic void
521304046Saenat64stl_update_arg1(ipfw_insn *cmd, uint16_t idx)
522304046Sae{
523304046Sae
524304046Sae	cmd->arg1 = idx;
525304046Sae}
526304046Sae
527304046Saestatic int
528304046Saenat64stl_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
529304046Sae    struct named_object **pno)
530304046Sae{
531304046Sae	int err;
532304046Sae
533304046Sae	err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
534304046Sae	    IPFW_TLV_NAT64STL_NAME, pno);
535304046Sae	return (err);
536304046Sae}
537304046Sae
538304046Saestatic struct named_object *
539304046Saenat64stl_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
540304046Sae{
541304046Sae	struct namedobj_instance *ni;
542304046Sae	struct named_object *no;
543304046Sae
544304046Sae	IPFW_UH_WLOCK_ASSERT(ch);
545304046Sae	ni = CHAIN_TO_SRV(ch);
546304046Sae	no = ipfw_objhash_lookup_kidx(ni, idx);
547304046Sae	KASSERT(no != NULL, ("NAT with index %d not found", idx));
548304046Sae
549304046Sae	return (no);
550304046Sae}
551304046Sae
552304046Saestatic int
553304046Saenat64stl_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
554304046Sae    enum ipfw_sets_cmd cmd)
555304046Sae{
556304046Sae
557304046Sae	return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64STL_NAME,
558304046Sae	    set, new_set, cmd));
559304046Sae}
560304046Sae
561304046Saestatic struct opcode_obj_rewrite opcodes[] = {
562304046Sae	{
563304046Sae		.opcode = O_EXTERNAL_INSTANCE,
564304046Sae		.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
565304046Sae		.classifier = nat64stl_classify,
566304046Sae		.update = nat64stl_update_arg1,
567304046Sae		.find_byname = nat64stl_findbyname,
568304046Sae		.find_bykidx = nat64stl_findbykidx,
569304046Sae		.manage_sets = nat64stl_manage_sets,
570304046Sae	},
571304046Sae};
572304046Sae
573304046Saestatic int
574304046Saedestroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
575304046Sae    void *arg)
576304046Sae{
577304046Sae	struct nat64stl_cfg *cfg;
578304046Sae	struct ip_fw_chain *ch;
579304046Sae
580304046Sae	ch = (struct ip_fw_chain *)arg;
581304046Sae	cfg = (struct nat64stl_cfg *)SRV_OBJECT(ch, no->kidx);
582304046Sae	SRV_OBJECT(ch, no->kidx) = NULL;
583304046Sae	nat64stl_detach_config(ch, cfg);
584304046Sae	nat64stl_free_config(cfg);
585304046Sae	return (0);
586304046Sae}
587304046Sae
588304046Saeint
589304046Saenat64stl_init(struct ip_fw_chain *ch, int first)
590304046Sae{
591304046Sae
592304046Sae	V_nat64stl_eid = ipfw_add_eaction(ch, ipfw_nat64stl, "nat64stl");
593304046Sae	if (V_nat64stl_eid == 0)
594304046Sae		return (ENXIO);
595304046Sae	IPFW_ADD_SOPT_HANDLER(first, scodes);
596304046Sae	IPFW_ADD_OBJ_REWRITER(first, opcodes);
597304046Sae	return (0);
598304046Sae}
599304046Sae
600304046Saevoid
601304046Saenat64stl_uninit(struct ip_fw_chain *ch, int last)
602304046Sae{
603304046Sae
604304046Sae	IPFW_DEL_OBJ_REWRITER(last, opcodes);
605304046Sae	IPFW_DEL_SOPT_HANDLER(last, scodes);
606304046Sae	ipfw_del_eaction(ch, V_nat64stl_eid);
607304046Sae	/*
608304046Sae	 * Since we already have deregistered external action,
609304046Sae	 * our named objects become unaccessible via rules, because
610304046Sae	 * all rules were truncated by ipfw_del_eaction().
611304046Sae	 * So, we can unlink and destroy our named objects without holding
612304046Sae	 * IPFW_WLOCK().
613304046Sae	 */
614304046Sae	IPFW_UH_WLOCK(ch);
615304046Sae	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
616304046Sae	    IPFW_TLV_NAT64STL_NAME);
617304046Sae	V_nat64stl_eid = 0;
618304046Sae	IPFW_UH_WUNLOCK(ch);
619304046Sae}
620304046Sae
621