xref: /freebsd/sys/netpfil/ipfw/nat64/nat64stl_control.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
1d8caf56eSAndrey V. Elsukov /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3002cae78SAndrey V. Elsukov  *
4002cae78SAndrey V. Elsukov  * Copyright (c) 2015-2019 Yandex LLC
5d8caf56eSAndrey V. Elsukov  * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
6002cae78SAndrey V. Elsukov  * Copyright (c) 2015-2019 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>
33d8caf56eSAndrey V. Elsukov #include <sys/errno.h>
34d8caf56eSAndrey V. Elsukov #include <sys/kernel.h>
35d8caf56eSAndrey V. Elsukov #include <sys/lock.h>
36d8caf56eSAndrey V. Elsukov #include <sys/malloc.h>
37d8caf56eSAndrey V. Elsukov #include <sys/mbuf.h>
38d8caf56eSAndrey V. Elsukov #include <sys/module.h>
39d8caf56eSAndrey V. Elsukov #include <sys/rmlock.h>
40d8caf56eSAndrey V. Elsukov #include <sys/rwlock.h>
41d8caf56eSAndrey V. Elsukov #include <sys/socket.h>
42d8caf56eSAndrey V. Elsukov #include <sys/sockopt.h>
43d8caf56eSAndrey V. Elsukov #include <sys/queue.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/pfil.h>
50d8caf56eSAndrey V. Elsukov #include <net/route.h>
51d8caf56eSAndrey V. Elsukov #include <net/vnet.h>
52d8caf56eSAndrey V. Elsukov 
53d8caf56eSAndrey V. Elsukov #include <netinet/in.h>
54d8caf56eSAndrey V. Elsukov #include <netinet/ip_var.h>
55d8caf56eSAndrey V. Elsukov #include <netinet/ip_fw.h>
56d8caf56eSAndrey V. Elsukov #include <netinet6/in6_var.h>
57d8caf56eSAndrey V. Elsukov #include <netinet6/ip6_var.h>
58782360deSAndrey V. Elsukov #include <netinet6/ip_fw_nat64.h>
59d8caf56eSAndrey V. Elsukov 
60d8caf56eSAndrey V. Elsukov #include <netpfil/ipfw/ip_fw_private.h>
61782360deSAndrey V. Elsukov 
62782360deSAndrey V. Elsukov #include "nat64stl.h"
63d8caf56eSAndrey V. Elsukov 
64d8caf56eSAndrey V. Elsukov VNET_DEFINE(uint16_t, nat64stl_eid) = 0;
65d8caf56eSAndrey V. Elsukov 
66782360deSAndrey V. Elsukov static struct nat64stl_cfg *nat64stl_alloc_config(const char *name,
67782360deSAndrey V. Elsukov     uint8_t set);
68d8caf56eSAndrey V. Elsukov static void nat64stl_free_config(struct nat64stl_cfg *cfg);
69d8caf56eSAndrey V. Elsukov static struct nat64stl_cfg *nat64stl_find(struct namedobj_instance *ni,
70d8caf56eSAndrey V. Elsukov     const char *name, uint8_t set);
71d8caf56eSAndrey V. Elsukov 
72d8caf56eSAndrey V. Elsukov static struct nat64stl_cfg *
nat64stl_alloc_config(const char * name,uint8_t set)73d8caf56eSAndrey V. Elsukov nat64stl_alloc_config(const char *name, uint8_t set)
74d8caf56eSAndrey V. Elsukov {
75d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
76d8caf56eSAndrey V. Elsukov 
77d8caf56eSAndrey V. Elsukov 	cfg = malloc(sizeof(struct nat64stl_cfg), M_IPFW, M_WAITOK | M_ZERO);
78782360deSAndrey V. Elsukov 	COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
79d8caf56eSAndrey V. Elsukov 	cfg->no.name = cfg->name;
80d8caf56eSAndrey V. Elsukov 	cfg->no.etlv = IPFW_TLV_NAT64STL_NAME;
81d8caf56eSAndrey V. Elsukov 	cfg->no.set = set;
82d8caf56eSAndrey V. Elsukov 	strlcpy(cfg->name, name, sizeof(cfg->name));
83d8caf56eSAndrey V. Elsukov 	return (cfg);
84d8caf56eSAndrey V. Elsukov }
85d8caf56eSAndrey V. Elsukov 
86d8caf56eSAndrey V. Elsukov static void
nat64stl_free_config(struct nat64stl_cfg * cfg)87d8caf56eSAndrey V. Elsukov nat64stl_free_config(struct nat64stl_cfg *cfg)
88d8caf56eSAndrey V. Elsukov {
89d8caf56eSAndrey V. Elsukov 
90782360deSAndrey V. Elsukov 	COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
91d8caf56eSAndrey V. Elsukov 	free(cfg, M_IPFW);
92d8caf56eSAndrey V. Elsukov }
93d8caf56eSAndrey V. Elsukov 
94d8caf56eSAndrey V. Elsukov static void
nat64stl_export_config(struct ip_fw_chain * ch,struct nat64stl_cfg * cfg,ipfw_nat64stl_cfg * uc)95d8caf56eSAndrey V. Elsukov nat64stl_export_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
96d8caf56eSAndrey V. Elsukov     ipfw_nat64stl_cfg *uc)
97d8caf56eSAndrey V. Elsukov {
98d8caf56eSAndrey V. Elsukov 	struct named_object *no;
99d8caf56eSAndrey V. Elsukov 
100b11efc1eSAndrey V. Elsukov 	uc->prefix6 = cfg->base.plat_prefix;
101b11efc1eSAndrey V. Elsukov 	uc->plen6 = cfg->base.plat_plen;
102782360deSAndrey V. Elsukov 	uc->flags = cfg->base.flags & NAT64STL_FLAGSMASK;
103d8caf56eSAndrey V. Elsukov 	uc->set = cfg->no.set;
104d8caf56eSAndrey V. Elsukov 	strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
105d8caf56eSAndrey V. Elsukov 
106d8caf56eSAndrey V. Elsukov 	no = ipfw_objhash_lookup_table_kidx(ch, cfg->map64);
107d8caf56eSAndrey V. Elsukov 	ipfw_export_obj_ntlv(no, &uc->ntlv6);
108d8caf56eSAndrey V. Elsukov 	no = ipfw_objhash_lookup_table_kidx(ch, cfg->map46);
109d8caf56eSAndrey V. Elsukov 	ipfw_export_obj_ntlv(no, &uc->ntlv4);
110d8caf56eSAndrey V. Elsukov }
111d8caf56eSAndrey V. Elsukov 
112d8caf56eSAndrey V. Elsukov struct nat64stl_dump_arg {
113d8caf56eSAndrey V. Elsukov 	struct ip_fw_chain *ch;
114d8caf56eSAndrey V. Elsukov 	struct sockopt_data *sd;
115d8caf56eSAndrey V. Elsukov };
116d8caf56eSAndrey V. Elsukov 
117d8caf56eSAndrey V. Elsukov static int
export_config_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)118d8caf56eSAndrey V. Elsukov export_config_cb(struct namedobj_instance *ni, struct named_object *no,
119d8caf56eSAndrey V. Elsukov     void *arg)
120d8caf56eSAndrey V. Elsukov {
121d8caf56eSAndrey V. Elsukov 	struct nat64stl_dump_arg *da = (struct nat64stl_dump_arg *)arg;
122d8caf56eSAndrey V. Elsukov 	ipfw_nat64stl_cfg *uc;
123d8caf56eSAndrey V. Elsukov 
124d8caf56eSAndrey V. Elsukov 	uc = (ipfw_nat64stl_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
125d8caf56eSAndrey V. Elsukov 	nat64stl_export_config(da->ch, (struct nat64stl_cfg *)no, uc);
126d8caf56eSAndrey V. Elsukov 	return (0);
127d8caf56eSAndrey V. Elsukov }
128d8caf56eSAndrey V. Elsukov 
129d8caf56eSAndrey V. Elsukov static struct nat64stl_cfg *
nat64stl_find(struct namedobj_instance * ni,const char * name,uint8_t set)130d8caf56eSAndrey V. Elsukov nat64stl_find(struct namedobj_instance *ni, const char *name, uint8_t set)
131d8caf56eSAndrey V. Elsukov {
132d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
133d8caf56eSAndrey V. Elsukov 
134d8caf56eSAndrey V. Elsukov 	cfg = (struct nat64stl_cfg *)ipfw_objhash_lookup_name_type(ni, set,
135d8caf56eSAndrey V. Elsukov 	    IPFW_TLV_NAT64STL_NAME, name);
136d8caf56eSAndrey V. Elsukov 
137d8caf56eSAndrey V. Elsukov 	return (cfg);
138d8caf56eSAndrey V. Elsukov }
139d8caf56eSAndrey V. Elsukov 
140d8caf56eSAndrey V. Elsukov static int
nat64stl_create_internal(struct ip_fw_chain * ch,struct nat64stl_cfg * cfg,ipfw_nat64stl_cfg * i)141d8caf56eSAndrey V. Elsukov nat64stl_create_internal(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
142d8caf56eSAndrey V. Elsukov     ipfw_nat64stl_cfg *i)
143d8caf56eSAndrey V. Elsukov {
144d8caf56eSAndrey V. Elsukov 
145d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
146d8caf56eSAndrey V. Elsukov 
147d8caf56eSAndrey V. Elsukov 	if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0)
148d8caf56eSAndrey V. Elsukov 		return (ENOSPC);
149782360deSAndrey V. Elsukov 	cfg->base.flags |= NAT64STL_KIDX;
150d8caf56eSAndrey V. Elsukov 
151d8caf56eSAndrey V. Elsukov 	if (ipfw_ref_table(ch, &i->ntlv4, &cfg->map46) != 0)
152d8caf56eSAndrey V. Elsukov 		return (EINVAL);
153782360deSAndrey V. Elsukov 	cfg->base.flags |= NAT64STL_46T;
154d8caf56eSAndrey V. Elsukov 
155d8caf56eSAndrey V. Elsukov 	if (ipfw_ref_table(ch, &i->ntlv6, &cfg->map64) != 0)
156d8caf56eSAndrey V. Elsukov 		return (EINVAL);
157782360deSAndrey V. Elsukov 	cfg->base.flags |= NAT64STL_64T;
158d8caf56eSAndrey V. Elsukov 
159d8caf56eSAndrey V. Elsukov 	ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
160d8caf56eSAndrey V. Elsukov 
161d8caf56eSAndrey V. Elsukov 	return (0);
162d8caf56eSAndrey V. Elsukov }
163d8caf56eSAndrey V. Elsukov 
164d8caf56eSAndrey V. Elsukov /*
165d8caf56eSAndrey V. Elsukov  * Creates new nat64 instance.
166d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
167d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ipfw_nat64stl_cfg ]
168d8caf56eSAndrey V. Elsukov  *
169d8caf56eSAndrey V. Elsukov  * Returns 0 on success
170d8caf56eSAndrey V. Elsukov  */
171d8caf56eSAndrey V. Elsukov static int
nat64stl_create(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)172d8caf56eSAndrey V. Elsukov nat64stl_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
173d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
174d8caf56eSAndrey V. Elsukov {
175d8caf56eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
176d8caf56eSAndrey V. Elsukov 	ipfw_nat64stl_cfg *uc;
177d8caf56eSAndrey V. Elsukov 	struct namedobj_instance *ni;
178d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
179d8caf56eSAndrey V. Elsukov 	int error;
180d8caf56eSAndrey V. Elsukov 
181d8caf56eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*olh) + sizeof(*uc))
182d8caf56eSAndrey V. Elsukov 		return (EINVAL);
183d8caf56eSAndrey V. Elsukov 
184d8caf56eSAndrey V. Elsukov 	olh = (ipfw_obj_lheader *)sd->kbuf;
185d8caf56eSAndrey V. Elsukov 	uc = (ipfw_nat64stl_cfg *)(olh + 1);
186d8caf56eSAndrey V. Elsukov 
187d8caf56eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(uc->name) != 0)
188d8caf56eSAndrey V. Elsukov 		return (EINVAL);
189782360deSAndrey V. Elsukov 	if (uc->set >= IPFW_MAX_SETS ||
190782360deSAndrey V. Elsukov 	    nat64_check_prefix6(&uc->prefix6, uc->plen6) != 0)
191d8caf56eSAndrey V. Elsukov 		return (EINVAL);
192d8caf56eSAndrey V. Elsukov 
193d8caf56eSAndrey V. Elsukov 	/* XXX: check types of tables */
194d8caf56eSAndrey V. Elsukov 
195d8caf56eSAndrey V. Elsukov 	ni = CHAIN_TO_SRV(ch);
196d8caf56eSAndrey V. Elsukov 	error = 0;
197d8caf56eSAndrey V. Elsukov 
198d8caf56eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
199d8caf56eSAndrey V. Elsukov 	if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
200d8caf56eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
201d8caf56eSAndrey V. Elsukov 		return (EEXIST);
202d8caf56eSAndrey V. Elsukov 	}
203d8caf56eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
204d8caf56eSAndrey V. Elsukov 
205d8caf56eSAndrey V. Elsukov 	cfg = nat64stl_alloc_config(uc->name, uc->set);
206b11efc1eSAndrey V. Elsukov 	cfg->base.plat_prefix = uc->prefix6;
207b11efc1eSAndrey V. Elsukov 	cfg->base.plat_plen = uc->plen6;
208b11efc1eSAndrey V. Elsukov 	cfg->base.flags = (uc->flags & NAT64STL_FLAGSMASK) | NAT64_PLATPFX;
209b11efc1eSAndrey V. Elsukov 	if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))
210782360deSAndrey V. Elsukov 		cfg->base.flags |= NAT64_WKPFX;
211d8caf56eSAndrey V. Elsukov 
212d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
213d8caf56eSAndrey V. Elsukov 
214d8caf56eSAndrey V. Elsukov 	if (nat64stl_find(ni, uc->name, uc->set) != NULL) {
215d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
216d8caf56eSAndrey V. Elsukov 		nat64stl_free_config(cfg);
217d8caf56eSAndrey V. Elsukov 		return (EEXIST);
218d8caf56eSAndrey V. Elsukov 	}
219d8caf56eSAndrey V. Elsukov 	error = nat64stl_create_internal(ch, cfg, uc);
220d8caf56eSAndrey V. Elsukov 	if (error == 0) {
221d8caf56eSAndrey V. Elsukov 		/* Okay, let's link data */
222d8caf56eSAndrey V. Elsukov 		SRV_OBJECT(ch, cfg->no.kidx) = cfg;
223d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
224d8caf56eSAndrey V. Elsukov 		return (0);
225d8caf56eSAndrey V. Elsukov 	}
226d8caf56eSAndrey V. Elsukov 
227782360deSAndrey V. Elsukov 	if (cfg->base.flags & NAT64STL_KIDX)
228d8caf56eSAndrey V. Elsukov 		ipfw_objhash_free_idx(ni, cfg->no.kidx);
229782360deSAndrey V. Elsukov 	if (cfg->base.flags & NAT64STL_46T)
230d8caf56eSAndrey V. Elsukov 		ipfw_unref_table(ch, cfg->map46);
231782360deSAndrey V. Elsukov 	if (cfg->base.flags & NAT64STL_64T)
232d8caf56eSAndrey V. Elsukov 		ipfw_unref_table(ch, cfg->map64);
233d8caf56eSAndrey V. Elsukov 
234d8caf56eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
235d8caf56eSAndrey V. Elsukov 	nat64stl_free_config(cfg);
236d8caf56eSAndrey V. Elsukov 	return (error);
237d8caf56eSAndrey V. Elsukov }
238d8caf56eSAndrey V. Elsukov 
239d8caf56eSAndrey V. Elsukov /*
240d8caf56eSAndrey V. Elsukov  * Change existing nat64stl instance configuration.
241d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
242d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ipfw_nat64stl_cfg ]
243d8caf56eSAndrey V. Elsukov  * Reply: [ ipfw_obj_header ipfw_nat64stl_cfg ]
244d8caf56eSAndrey V. Elsukov  *
245d8caf56eSAndrey V. Elsukov  * Returns 0 on success
246d8caf56eSAndrey V. Elsukov  */
247d8caf56eSAndrey V. Elsukov static int
nat64stl_config(struct ip_fw_chain * ch,ip_fw3_opheader * op,struct sockopt_data * sd)248d8caf56eSAndrey V. Elsukov nat64stl_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
249d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
250d8caf56eSAndrey V. Elsukov {
251d8caf56eSAndrey V. Elsukov 	ipfw_obj_header *oh;
252d8caf56eSAndrey V. Elsukov 	ipfw_nat64stl_cfg *uc;
253d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
254d8caf56eSAndrey V. Elsukov 	struct namedobj_instance *ni;
255d8caf56eSAndrey V. Elsukov 
256d8caf56eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*oh) + sizeof(*uc))
257d8caf56eSAndrey V. Elsukov 		return (EINVAL);
258d8caf56eSAndrey V. Elsukov 
259d8caf56eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
260d8caf56eSAndrey V. Elsukov 	    sizeof(*oh) + sizeof(*uc));
261d8caf56eSAndrey V. Elsukov 	uc = (ipfw_nat64stl_cfg *)(oh + 1);
262d8caf56eSAndrey V. Elsukov 
263d8caf56eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
264d8caf56eSAndrey V. Elsukov 	    oh->ntlv.set >= IPFW_MAX_SETS)
265d8caf56eSAndrey V. Elsukov 		return (EINVAL);
266d8caf56eSAndrey V. Elsukov 
267d8caf56eSAndrey V. Elsukov 	ni = CHAIN_TO_SRV(ch);
268d8caf56eSAndrey V. Elsukov 	if (sd->sopt->sopt_dir == SOPT_GET) {
269d8caf56eSAndrey V. Elsukov 		IPFW_UH_RLOCK(ch);
270d8caf56eSAndrey V. Elsukov 		cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
271d8caf56eSAndrey V. Elsukov 		if (cfg == NULL) {
272d8caf56eSAndrey V. Elsukov 			IPFW_UH_RUNLOCK(ch);
273d8caf56eSAndrey V. Elsukov 			return (EEXIST);
274d8caf56eSAndrey V. Elsukov 		}
275d8caf56eSAndrey V. Elsukov 		nat64stl_export_config(ch, cfg, uc);
276d8caf56eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
277d8caf56eSAndrey V. Elsukov 		return (0);
278d8caf56eSAndrey V. Elsukov 	}
279d8caf56eSAndrey V. Elsukov 
280d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
281d8caf56eSAndrey V. Elsukov 	cfg = nat64stl_find(ni, oh->ntlv.name, oh->ntlv.set);
282d8caf56eSAndrey V. Elsukov 	if (cfg == NULL) {
283d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
284d8caf56eSAndrey V. Elsukov 		return (EEXIST);
285d8caf56eSAndrey V. Elsukov 	}
286d8caf56eSAndrey V. Elsukov 
287d8caf56eSAndrey V. Elsukov 	/*
288d8caf56eSAndrey V. Elsukov 	 * For now allow to change only following values:
289d8caf56eSAndrey V. Elsukov 	 *  flags.
290d8caf56eSAndrey V. Elsukov 	 */
291782360deSAndrey V. Elsukov 	cfg->base.flags &= ~NAT64STL_FLAGSMASK;
292782360deSAndrey V. Elsukov 	cfg->base.flags |= uc->flags & NAT64STL_FLAGSMASK;
293782360deSAndrey V. Elsukov 
294d8caf56eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
295d8caf56eSAndrey V. Elsukov 	return (0);
296d8caf56eSAndrey V. Elsukov }
297d8caf56eSAndrey V. Elsukov 
298d8caf56eSAndrey V. Elsukov static void
nat64stl_detach_config(struct ip_fw_chain * ch,struct nat64stl_cfg * cfg)299d8caf56eSAndrey V. Elsukov nat64stl_detach_config(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg)
300d8caf56eSAndrey V. Elsukov {
301d8caf56eSAndrey V. Elsukov 
302d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
303d8caf56eSAndrey V. Elsukov 
304d8caf56eSAndrey V. Elsukov 	ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
305d8caf56eSAndrey V. Elsukov 	ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
306d8caf56eSAndrey V. Elsukov 	ipfw_unref_table(ch, cfg->map46);
307d8caf56eSAndrey V. Elsukov 	ipfw_unref_table(ch, cfg->map64);
308d8caf56eSAndrey V. Elsukov }
309d8caf56eSAndrey V. Elsukov 
310d8caf56eSAndrey V. Elsukov /*
311d8caf56eSAndrey V. Elsukov  * Destroys nat64 instance.
312d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
313d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
314d8caf56eSAndrey V. Elsukov  *
315d8caf56eSAndrey V. Elsukov  * Returns 0 on success
316d8caf56eSAndrey V. Elsukov  */
317d8caf56eSAndrey V. Elsukov static int
nat64stl_destroy(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)318d8caf56eSAndrey V. Elsukov nat64stl_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
319d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
320d8caf56eSAndrey V. Elsukov {
321d8caf56eSAndrey V. Elsukov 	ipfw_obj_header *oh;
322d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
323d8caf56eSAndrey V. Elsukov 
324d8caf56eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*oh))
325d8caf56eSAndrey V. Elsukov 		return (EINVAL);
326d8caf56eSAndrey V. Elsukov 
327d8caf56eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)sd->kbuf;
328d8caf56eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
329d8caf56eSAndrey V. Elsukov 		return (EINVAL);
330d8caf56eSAndrey V. Elsukov 
331d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
332d8caf56eSAndrey V. Elsukov 	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
333d8caf56eSAndrey V. Elsukov 	if (cfg == NULL) {
334d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
335d8caf56eSAndrey V. Elsukov 		return (ESRCH);
336d8caf56eSAndrey V. Elsukov 	}
337d8caf56eSAndrey V. Elsukov 	if (cfg->no.refcnt > 0) {
338d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
339d8caf56eSAndrey V. Elsukov 		return (EBUSY);
340d8caf56eSAndrey V. Elsukov 	}
341d8caf56eSAndrey V. Elsukov 
342cefe3d67SAndrey V. Elsukov 	ipfw_reset_eaction_instance(ch, V_nat64stl_eid, cfg->no.kidx);
343d8caf56eSAndrey V. Elsukov 	SRV_OBJECT(ch, cfg->no.kidx) = NULL;
344d8caf56eSAndrey V. Elsukov 	nat64stl_detach_config(ch, cfg);
345d8caf56eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
346d8caf56eSAndrey V. Elsukov 
347d8caf56eSAndrey V. Elsukov 	nat64stl_free_config(cfg);
348d8caf56eSAndrey V. Elsukov 	return (0);
349d8caf56eSAndrey V. Elsukov }
350d8caf56eSAndrey V. Elsukov 
351d8caf56eSAndrey V. Elsukov /*
352d8caf56eSAndrey V. Elsukov  * Lists all nat64stl instances currently available in kernel.
353d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
354d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ]
355d8caf56eSAndrey V. Elsukov  * Reply: [ ipfw_obj_lheader ipfw_nat64stl_cfg x N ]
356d8caf56eSAndrey V. Elsukov  *
357d8caf56eSAndrey V. Elsukov  * Returns 0 on success
358d8caf56eSAndrey V. Elsukov  */
359d8caf56eSAndrey V. Elsukov static int
nat64stl_list(struct ip_fw_chain * ch,ip_fw3_opheader * op3,struct sockopt_data * sd)360d8caf56eSAndrey V. Elsukov nat64stl_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
361d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
362d8caf56eSAndrey V. Elsukov {
363d8caf56eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
364d8caf56eSAndrey V. Elsukov 	struct nat64stl_dump_arg da;
365d8caf56eSAndrey V. Elsukov 
366d8caf56eSAndrey V. Elsukov 	/* Check minimum header size */
367d8caf56eSAndrey V. Elsukov 	if (sd->valsize < sizeof(ipfw_obj_lheader))
368d8caf56eSAndrey V. Elsukov 		return (EINVAL);
369d8caf56eSAndrey V. Elsukov 
370d8caf56eSAndrey V. Elsukov 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
371d8caf56eSAndrey V. Elsukov 
372d8caf56eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
373d8caf56eSAndrey V. Elsukov 	olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
374d8caf56eSAndrey V. Elsukov 	    IPFW_TLV_NAT64STL_NAME);
375d8caf56eSAndrey V. Elsukov 	olh->objsize = sizeof(ipfw_nat64stl_cfg);
376d8caf56eSAndrey V. Elsukov 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
377d8caf56eSAndrey V. Elsukov 
378d8caf56eSAndrey V. Elsukov 	if (sd->valsize < olh->size) {
379d8caf56eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
380d8caf56eSAndrey V. Elsukov 		return (ENOMEM);
381d8caf56eSAndrey V. Elsukov 	}
382d8caf56eSAndrey V. Elsukov 	memset(&da, 0, sizeof(da));
383d8caf56eSAndrey V. Elsukov 	da.ch = ch;
384d8caf56eSAndrey V. Elsukov 	da.sd = sd;
385d8caf56eSAndrey V. Elsukov 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
386d8caf56eSAndrey V. Elsukov 	    &da, IPFW_TLV_NAT64STL_NAME);
387d8caf56eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
388d8caf56eSAndrey V. Elsukov 
389d8caf56eSAndrey V. Elsukov 	return (0);
390d8caf56eSAndrey V. Elsukov }
391d8caf56eSAndrey V. Elsukov 
392d8caf56eSAndrey V. Elsukov #define	__COPY_STAT_FIELD(_cfg, _stats, _field)	\
393782360deSAndrey V. Elsukov 	(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)
394d8caf56eSAndrey V. Elsukov static void
export_stats(struct ip_fw_chain * ch,struct nat64stl_cfg * cfg,struct ipfw_nat64stl_stats * stats)395d8caf56eSAndrey V. Elsukov export_stats(struct ip_fw_chain *ch, struct nat64stl_cfg *cfg,
396d8caf56eSAndrey V. Elsukov     struct ipfw_nat64stl_stats *stats)
397d8caf56eSAndrey V. Elsukov {
398d8caf56eSAndrey V. Elsukov 
399d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, opcnt64);
400d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, opcnt46);
401d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, ofrags);
402d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, ifrags);
403d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, oerrors);
404d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, noroute4);
405d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, noroute6);
406d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, noproto);
407d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, nomem);
408d8caf56eSAndrey V. Elsukov 	__COPY_STAT_FIELD(cfg, stats, dropped);
409d8caf56eSAndrey V. Elsukov }
410d8caf56eSAndrey V. Elsukov 
411d8caf56eSAndrey V. Elsukov /*
412d8caf56eSAndrey V. Elsukov  * Get nat64stl statistics.
413d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
414d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
415d8caf56eSAndrey V. Elsukov  * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
416d8caf56eSAndrey V. Elsukov  *
417d8caf56eSAndrey V. Elsukov  * Returns 0 on success
418d8caf56eSAndrey V. Elsukov  */
419d8caf56eSAndrey V. Elsukov static int
nat64stl_stats(struct ip_fw_chain * ch,ip_fw3_opheader * op,struct sockopt_data * sd)420d8caf56eSAndrey V. Elsukov nat64stl_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
421d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
422d8caf56eSAndrey V. Elsukov {
423d8caf56eSAndrey V. Elsukov 	struct ipfw_nat64stl_stats stats;
424d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
425d8caf56eSAndrey V. Elsukov 	ipfw_obj_header *oh;
426d8caf56eSAndrey V. Elsukov 	ipfw_obj_ctlv *ctlv;
427d8caf56eSAndrey V. Elsukov 	size_t sz;
428d8caf56eSAndrey V. Elsukov 
429d8caf56eSAndrey V. Elsukov 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
430d8caf56eSAndrey V. Elsukov 	if (sd->valsize % sizeof(uint64_t))
431d8caf56eSAndrey V. Elsukov 		return (EINVAL);
432d8caf56eSAndrey V. Elsukov 	if (sd->valsize < sz)
433d8caf56eSAndrey V. Elsukov 		return (ENOMEM);
434d8caf56eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
435d8caf56eSAndrey V. Elsukov 	if (oh == NULL)
436d8caf56eSAndrey V. Elsukov 		return (EINVAL);
437d8caf56eSAndrey V. Elsukov 	memset(&stats, 0, sizeof(stats));
438d8caf56eSAndrey V. Elsukov 
439d8caf56eSAndrey V. Elsukov 	IPFW_UH_RLOCK(ch);
440d8caf56eSAndrey V. Elsukov 	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
441d8caf56eSAndrey V. Elsukov 	if (cfg == NULL) {
442d8caf56eSAndrey V. Elsukov 		IPFW_UH_RUNLOCK(ch);
443d8caf56eSAndrey V. Elsukov 		return (ESRCH);
444d8caf56eSAndrey V. Elsukov 	}
445d8caf56eSAndrey V. Elsukov 	export_stats(ch, cfg, &stats);
446d8caf56eSAndrey V. Elsukov 	IPFW_UH_RUNLOCK(ch);
447d8caf56eSAndrey V. Elsukov 
448d8caf56eSAndrey V. Elsukov 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
449d8caf56eSAndrey V. Elsukov 	memset(ctlv, 0, sizeof(*ctlv));
450d8caf56eSAndrey V. Elsukov 	ctlv->head.type = IPFW_TLV_COUNTERS;
451d8caf56eSAndrey V. Elsukov 	ctlv->head.length = sz - sizeof(ipfw_obj_header);
452d8caf56eSAndrey V. Elsukov 	ctlv->count = sizeof(stats) / sizeof(uint64_t);
453d8caf56eSAndrey V. Elsukov 	ctlv->objsize = sizeof(uint64_t);
454d8caf56eSAndrey V. Elsukov 	ctlv->version = IPFW_NAT64_VERSION;
455d8caf56eSAndrey V. Elsukov 	memcpy(ctlv + 1, &stats, sizeof(stats));
456d8caf56eSAndrey V. Elsukov 	return (0);
457d8caf56eSAndrey V. Elsukov }
458d8caf56eSAndrey V. Elsukov 
459d8caf56eSAndrey V. Elsukov /*
460d8caf56eSAndrey V. Elsukov  * Reset nat64stl statistics.
461d8caf56eSAndrey V. Elsukov  * Data layout (v0)(current):
462d8caf56eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
463d8caf56eSAndrey V. Elsukov  *
464d8caf56eSAndrey V. Elsukov  * Returns 0 on success
465d8caf56eSAndrey V. Elsukov  */
466d8caf56eSAndrey V. Elsukov static int
nat64stl_reset_stats(struct ip_fw_chain * ch,ip_fw3_opheader * op,struct sockopt_data * sd)467d8caf56eSAndrey V. Elsukov nat64stl_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
468d8caf56eSAndrey V. Elsukov     struct sockopt_data *sd)
469d8caf56eSAndrey V. Elsukov {
470d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
471d8caf56eSAndrey V. Elsukov 	ipfw_obj_header *oh;
472d8caf56eSAndrey V. Elsukov 
473d8caf56eSAndrey V. Elsukov 	if (sd->valsize != sizeof(*oh))
474d8caf56eSAndrey V. Elsukov 		return (EINVAL);
475d8caf56eSAndrey V. Elsukov 	oh = (ipfw_obj_header *)sd->kbuf;
476d8caf56eSAndrey V. Elsukov 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
477d8caf56eSAndrey V. Elsukov 	    oh->ntlv.set >= IPFW_MAX_SETS)
478d8caf56eSAndrey V. Elsukov 		return (EINVAL);
479d8caf56eSAndrey V. Elsukov 
480d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
481d8caf56eSAndrey V. Elsukov 	cfg = nat64stl_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
482d8caf56eSAndrey V. Elsukov 	if (cfg == NULL) {
483d8caf56eSAndrey V. Elsukov 		IPFW_UH_WUNLOCK(ch);
484d8caf56eSAndrey V. Elsukov 		return (ESRCH);
485d8caf56eSAndrey V. Elsukov 	}
486782360deSAndrey V. Elsukov 	COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);
487d8caf56eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
488d8caf56eSAndrey V. Elsukov 	return (0);
489d8caf56eSAndrey V. Elsukov }
490d8caf56eSAndrey V. Elsukov 
491d8caf56eSAndrey V. Elsukov static struct ipfw_sopt_handler	scodes[] = {
492d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_CREATE, 0,	HDIR_SET,	nat64stl_create },
493d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_DESTROY,0,	HDIR_SET,	nat64stl_destroy },
494d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_CONFIG, 0,	HDIR_BOTH,	nat64stl_config },
495d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_LIST,   0,	HDIR_GET,	nat64stl_list },
496d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_STATS,  0,	HDIR_GET,	nat64stl_stats },
497d8caf56eSAndrey V. Elsukov 	{ IP_FW_NAT64STL_RESET_STATS,0,	HDIR_SET,	nat64stl_reset_stats },
498d8caf56eSAndrey V. Elsukov };
499d8caf56eSAndrey V. Elsukov 
500d8caf56eSAndrey V. Elsukov static int
nat64stl_classify(ipfw_insn * cmd,uint16_t * puidx,uint8_t * ptype)501d8caf56eSAndrey V. Elsukov nat64stl_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
502d8caf56eSAndrey V. Elsukov {
503d8caf56eSAndrey V. Elsukov 	ipfw_insn *icmd;
504d8caf56eSAndrey V. Elsukov 
505d8caf56eSAndrey V. Elsukov 	icmd = cmd - 1;
506d8caf56eSAndrey V. Elsukov 	if (icmd->opcode != O_EXTERNAL_ACTION ||
507d8caf56eSAndrey V. Elsukov 	    icmd->arg1 != V_nat64stl_eid)
508d8caf56eSAndrey V. Elsukov 		return (1);
509d8caf56eSAndrey V. Elsukov 
510d8caf56eSAndrey V. Elsukov 	*puidx = cmd->arg1;
511d8caf56eSAndrey V. Elsukov 	*ptype = 0;
512d8caf56eSAndrey V. Elsukov 	return (0);
513d8caf56eSAndrey V. Elsukov }
514d8caf56eSAndrey V. Elsukov 
515d8caf56eSAndrey V. Elsukov static void
nat64stl_update_arg1(ipfw_insn * cmd,uint16_t idx)516d8caf56eSAndrey V. Elsukov nat64stl_update_arg1(ipfw_insn *cmd, uint16_t idx)
517d8caf56eSAndrey V. Elsukov {
518d8caf56eSAndrey V. Elsukov 
519d8caf56eSAndrey V. Elsukov 	cmd->arg1 = idx;
520d8caf56eSAndrey V. Elsukov }
521d8caf56eSAndrey V. Elsukov 
522d8caf56eSAndrey V. Elsukov static int
nat64stl_findbyname(struct ip_fw_chain * ch,struct tid_info * ti,struct named_object ** pno)523d8caf56eSAndrey V. Elsukov nat64stl_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
524d8caf56eSAndrey V. Elsukov     struct named_object **pno)
525d8caf56eSAndrey V. Elsukov {
526d8caf56eSAndrey V. Elsukov 	int err;
527d8caf56eSAndrey V. Elsukov 
528d8caf56eSAndrey V. Elsukov 	err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
529d8caf56eSAndrey V. Elsukov 	    IPFW_TLV_NAT64STL_NAME, pno);
530d8caf56eSAndrey V. Elsukov 	return (err);
531d8caf56eSAndrey V. Elsukov }
532d8caf56eSAndrey V. Elsukov 
533d8caf56eSAndrey V. Elsukov static struct named_object *
nat64stl_findbykidx(struct ip_fw_chain * ch,uint16_t idx)534d8caf56eSAndrey V. Elsukov nat64stl_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
535d8caf56eSAndrey V. Elsukov {
536d8caf56eSAndrey V. Elsukov 	struct namedobj_instance *ni;
537d8caf56eSAndrey V. Elsukov 	struct named_object *no;
538d8caf56eSAndrey V. Elsukov 
539d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK_ASSERT(ch);
540d8caf56eSAndrey V. Elsukov 	ni = CHAIN_TO_SRV(ch);
541d8caf56eSAndrey V. Elsukov 	no = ipfw_objhash_lookup_kidx(ni, idx);
542d8caf56eSAndrey V. Elsukov 	KASSERT(no != NULL, ("NAT with index %d not found", idx));
543d8caf56eSAndrey V. Elsukov 
544d8caf56eSAndrey V. Elsukov 	return (no);
545d8caf56eSAndrey V. Elsukov }
546d8caf56eSAndrey V. Elsukov 
547d8caf56eSAndrey V. Elsukov static int
nat64stl_manage_sets(struct ip_fw_chain * ch,uint16_t set,uint8_t new_set,enum ipfw_sets_cmd cmd)548d8caf56eSAndrey V. Elsukov nat64stl_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
549d8caf56eSAndrey V. Elsukov     enum ipfw_sets_cmd cmd)
550d8caf56eSAndrey V. Elsukov {
551d8caf56eSAndrey V. Elsukov 
552d8caf56eSAndrey V. Elsukov 	return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64STL_NAME,
553d8caf56eSAndrey V. Elsukov 	    set, new_set, cmd));
554d8caf56eSAndrey V. Elsukov }
555d8caf56eSAndrey V. Elsukov 
556d8caf56eSAndrey V. Elsukov static struct opcode_obj_rewrite opcodes[] = {
557d8caf56eSAndrey V. Elsukov 	{
558d8caf56eSAndrey V. Elsukov 		.opcode = O_EXTERNAL_INSTANCE,
559d8caf56eSAndrey V. Elsukov 		.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
560d8caf56eSAndrey V. Elsukov 		.classifier = nat64stl_classify,
561d8caf56eSAndrey V. Elsukov 		.update = nat64stl_update_arg1,
562d8caf56eSAndrey V. Elsukov 		.find_byname = nat64stl_findbyname,
563d8caf56eSAndrey V. Elsukov 		.find_bykidx = nat64stl_findbykidx,
564d8caf56eSAndrey V. Elsukov 		.manage_sets = nat64stl_manage_sets,
565d8caf56eSAndrey V. Elsukov 	},
566d8caf56eSAndrey V. Elsukov };
567d8caf56eSAndrey V. Elsukov 
568d8caf56eSAndrey V. Elsukov static int
destroy_config_cb(struct namedobj_instance * ni,struct named_object * no,void * arg)569d8caf56eSAndrey V. Elsukov destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
570d8caf56eSAndrey V. Elsukov     void *arg)
571d8caf56eSAndrey V. Elsukov {
572d8caf56eSAndrey V. Elsukov 	struct nat64stl_cfg *cfg;
573d8caf56eSAndrey V. Elsukov 	struct ip_fw_chain *ch;
574d8caf56eSAndrey V. Elsukov 
575d8caf56eSAndrey V. Elsukov 	ch = (struct ip_fw_chain *)arg;
576d8caf56eSAndrey V. Elsukov 	cfg = (struct nat64stl_cfg *)SRV_OBJECT(ch, no->kidx);
577d8caf56eSAndrey V. Elsukov 	SRV_OBJECT(ch, no->kidx) = NULL;
578d8caf56eSAndrey V. Elsukov 	nat64stl_detach_config(ch, cfg);
579d8caf56eSAndrey V. Elsukov 	nat64stl_free_config(cfg);
580d8caf56eSAndrey V. Elsukov 	return (0);
581d8caf56eSAndrey V. Elsukov }
582d8caf56eSAndrey V. Elsukov 
583d8caf56eSAndrey V. Elsukov int
nat64stl_init(struct ip_fw_chain * ch,int first)584d8caf56eSAndrey V. Elsukov nat64stl_init(struct ip_fw_chain *ch, int first)
585d8caf56eSAndrey V. Elsukov {
586d8caf56eSAndrey V. Elsukov 
587d8caf56eSAndrey V. Elsukov 	V_nat64stl_eid = ipfw_add_eaction(ch, ipfw_nat64stl, "nat64stl");
588d8caf56eSAndrey V. Elsukov 	if (V_nat64stl_eid == 0)
589d8caf56eSAndrey V. Elsukov 		return (ENXIO);
590d8caf56eSAndrey V. Elsukov 	IPFW_ADD_SOPT_HANDLER(first, scodes);
591d8caf56eSAndrey V. Elsukov 	IPFW_ADD_OBJ_REWRITER(first, opcodes);
592d8caf56eSAndrey V. Elsukov 	return (0);
593d8caf56eSAndrey V. Elsukov }
594d8caf56eSAndrey V. Elsukov 
595d8caf56eSAndrey V. Elsukov void
nat64stl_uninit(struct ip_fw_chain * ch,int last)596d8caf56eSAndrey V. Elsukov nat64stl_uninit(struct ip_fw_chain *ch, int last)
597d8caf56eSAndrey V. Elsukov {
598d8caf56eSAndrey V. Elsukov 
599d8caf56eSAndrey V. Elsukov 	IPFW_DEL_OBJ_REWRITER(last, opcodes);
600d8caf56eSAndrey V. Elsukov 	IPFW_DEL_SOPT_HANDLER(last, scodes);
601d8caf56eSAndrey V. Elsukov 	ipfw_del_eaction(ch, V_nat64stl_eid);
602d8caf56eSAndrey V. Elsukov 	/*
603d8caf56eSAndrey V. Elsukov 	 * Since we already have deregistered external action,
604d8caf56eSAndrey V. Elsukov 	 * our named objects become unaccessible via rules, because
605d8caf56eSAndrey V. Elsukov 	 * all rules were truncated by ipfw_del_eaction().
606d8caf56eSAndrey V. Elsukov 	 * So, we can unlink and destroy our named objects without holding
607d8caf56eSAndrey V. Elsukov 	 * IPFW_WLOCK().
608d8caf56eSAndrey V. Elsukov 	 */
609d8caf56eSAndrey V. Elsukov 	IPFW_UH_WLOCK(ch);
610d8caf56eSAndrey V. Elsukov 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
611d8caf56eSAndrey V. Elsukov 	    IPFW_TLV_NAT64STL_NAME);
612d8caf56eSAndrey V. Elsukov 	V_nat64stl_eid = 0;
613d8caf56eSAndrey V. Elsukov 	IPFW_UH_WUNLOCK(ch);
614d8caf56eSAndrey V. Elsukov }
615