xref: /freebsd/sys/netpfil/ipfw/nat64/nat64lsn_control.c (revision 4f52dfbb8d6c4d446500c5b097e3806ec219fbd4)
1 /*-
2  * Copyright (c) 2015 Yandex LLC
3  * Copyright (c) 2015 Alexander V. Chernikov <melifaro@FreeBSD.org>
4  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/counter.h>
35 #include <sys/errno.h>
36 #include <sys/kernel.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/module.h>
41 #include <sys/rmlock.h>
42 #include <sys/rwlock.h>
43 #include <sys/socket.h>
44 #include <sys/sockopt.h>
45 #include <sys/queue.h>
46 
47 #include <net/if.h>
48 #include <net/pfil.h>
49 
50 #include <netinet/in.h>
51 #include <netinet/ip.h>
52 #include <netinet/ip_var.h>
53 #include <netinet/ip_fw.h>
54 
55 #include <netpfil/ipfw/ip_fw_private.h>
56 #include <netpfil/ipfw/nat64/ip_fw_nat64.h>
57 #include <netpfil/ipfw/nat64/nat64lsn.h>
58 #include <netinet6/ip_fw_nat64.h>
59 
60 VNET_DEFINE(uint16_t, nat64lsn_eid) = 0;
61 
62 static struct nat64lsn_cfg *
63 nat64lsn_find(struct namedobj_instance *ni, const char *name, uint8_t set)
64 {
65 	struct nat64lsn_cfg *cfg;
66 
67 	cfg = (struct nat64lsn_cfg *)ipfw_objhash_lookup_name_type(ni, set,
68 	    IPFW_TLV_NAT64LSN_NAME, name);
69 
70 	return (cfg);
71 }
72 
73 static void
74 nat64lsn_default_config(ipfw_nat64lsn_cfg *uc)
75 {
76 
77 	if (uc->max_ports == 0)
78 		uc->max_ports = NAT64LSN_MAX_PORTS;
79 	else
80 		uc->max_ports = roundup(uc->max_ports, NAT64_CHUNK_SIZE);
81 	if (uc->max_ports > NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR)
82 		uc->max_ports = NAT64_CHUNK_SIZE * NAT64LSN_MAXPGPTR;
83 	if (uc->jmaxlen == 0)
84 		uc->jmaxlen = NAT64LSN_JMAXLEN;
85 	if (uc->jmaxlen > 65536)
86 		uc->jmaxlen = 65536;
87 	if (uc->nh_delete_delay == 0)
88 		uc->nh_delete_delay = NAT64LSN_HOST_AGE;
89 	if (uc->pg_delete_delay == 0)
90 		uc->pg_delete_delay = NAT64LSN_PG_AGE;
91 	if (uc->st_syn_ttl == 0)
92 		uc->st_syn_ttl = NAT64LSN_TCP_SYN_AGE;
93 	if (uc->st_close_ttl == 0)
94 		uc->st_close_ttl = NAT64LSN_TCP_FIN_AGE;
95 	if (uc->st_estab_ttl == 0)
96 		uc->st_estab_ttl = NAT64LSN_TCP_EST_AGE;
97 	if (uc->st_udp_ttl == 0)
98 		uc->st_udp_ttl = NAT64LSN_UDP_AGE;
99 	if (uc->st_icmp_ttl == 0)
100 		uc->st_icmp_ttl = NAT64LSN_ICMP_AGE;
101 }
102 
103 /*
104  * Creates new nat64lsn instance.
105  * Data layout (v0)(current):
106  * Request: [ ipfw_obj_lheader ipfw_nat64lsn_cfg ]
107  *
108  * Returns 0 on success
109  */
110 static int
111 nat64lsn_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
112     struct sockopt_data *sd)
113 {
114 	ipfw_obj_lheader *olh;
115 	ipfw_nat64lsn_cfg *uc;
116 	struct nat64lsn_cfg *cfg;
117 	struct namedobj_instance *ni;
118 	uint32_t addr4, mask4;
119 
120 	if (sd->valsize != sizeof(*olh) + sizeof(*uc))
121 		return (EINVAL);
122 
123 	olh = (ipfw_obj_lheader *)sd->kbuf;
124 	uc = (ipfw_nat64lsn_cfg *)(olh + 1);
125 
126 	if (ipfw_check_object_name_generic(uc->name) != 0)
127 		return (EINVAL);
128 
129 	if (uc->agg_prefix_len > 127 || uc->set >= IPFW_MAX_SETS)
130 		return (EINVAL);
131 
132 	if (uc->plen4 > 32)
133 		return (EINVAL);
134 	if (uc->plen6 > 128 || ((uc->plen6 % 8) != 0))
135 		return (EINVAL);
136 
137 	/* XXX: Check prefix4 to be global */
138 	addr4 = ntohl(uc->prefix4.s_addr);
139 	mask4 = ~((1 << (32 - uc->plen4)) - 1);
140 	if ((addr4 & mask4) != addr4)
141 		return (EINVAL);
142 
143 	/* XXX: Check prefix6 */
144 	if (uc->min_port == 0)
145 		uc->min_port = NAT64_MIN_PORT;
146 	if (uc->max_port == 0)
147 		uc->max_port = 65535;
148 	if (uc->min_port > uc->max_port)
149 		return (EINVAL);
150 	uc->min_port = roundup(uc->min_port, NAT64_CHUNK_SIZE);
151 	uc->max_port = roundup(uc->max_port, NAT64_CHUNK_SIZE);
152 
153 	nat64lsn_default_config(uc);
154 
155 	ni = CHAIN_TO_SRV(ch);
156 	IPFW_UH_RLOCK(ch);
157 	if (nat64lsn_find(ni, uc->name, uc->set) != NULL) {
158 		IPFW_UH_RUNLOCK(ch);
159 		return (EEXIST);
160 	}
161 	IPFW_UH_RUNLOCK(ch);
162 
163 	cfg = nat64lsn_init_instance(ch, 1 << (32 - uc->plen4));
164 	strlcpy(cfg->name, uc->name, sizeof(cfg->name));
165 	cfg->no.name = cfg->name;
166 	cfg->no.etlv = IPFW_TLV_NAT64LSN_NAME;
167 	cfg->no.set = uc->set;
168 
169 	cfg->prefix4 = addr4;
170 	cfg->pmask4 = addr4 | ~mask4;
171 	/* XXX: Copy 96 bits */
172 	cfg->plen6 = 96;
173 	memcpy(&cfg->prefix6, &uc->prefix6, cfg->plen6 / 8);
174 	cfg->plen4 = uc->plen4;
175 	cfg->flags = uc->flags & NAT64LSN_FLAGSMASK;
176 	cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE;
177 	cfg->agg_prefix_len = uc->agg_prefix_len;
178 	cfg->agg_prefix_max = uc->agg_prefix_max;
179 
180 	cfg->min_chunk = uc->min_port / NAT64_CHUNK_SIZE;
181 	cfg->max_chunk = uc->max_port / NAT64_CHUNK_SIZE;
182 
183 	cfg->jmaxlen = uc->jmaxlen;
184 	cfg->nh_delete_delay = uc->nh_delete_delay;
185 	cfg->pg_delete_delay = uc->pg_delete_delay;
186 	cfg->st_syn_ttl = uc->st_syn_ttl;
187 	cfg->st_close_ttl = uc->st_close_ttl;
188 	cfg->st_estab_ttl = uc->st_estab_ttl;
189 	cfg->st_udp_ttl = uc->st_udp_ttl;
190 	cfg->st_icmp_ttl = uc->st_icmp_ttl;
191 
192 	cfg->nomatch_verdict = IP_FW_DENY;
193 
194 	IPFW_UH_WLOCK(ch);
195 
196 	if (nat64lsn_find(ni, uc->name, uc->set) != NULL) {
197 		IPFW_UH_WUNLOCK(ch);
198 		nat64lsn_destroy_instance(cfg);
199 		return (EEXIST);
200 	}
201 
202 	if (ipfw_objhash_alloc_idx(CHAIN_TO_SRV(ch), &cfg->no.kidx) != 0) {
203 		IPFW_UH_WUNLOCK(ch);
204 		nat64lsn_destroy_instance(cfg);
205 		return (ENOSPC);
206 	}
207 	ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
208 
209 	/* Okay, let's link data */
210 	SRV_OBJECT(ch, cfg->no.kidx) = cfg;
211 	nat64lsn_start_instance(cfg);
212 
213 	IPFW_UH_WUNLOCK(ch);
214 	return (0);
215 }
216 
217 static void
218 nat64lsn_detach_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg)
219 {
220 
221 	IPFW_UH_WLOCK_ASSERT(ch);
222 
223 	ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
224 	ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
225 }
226 
227 /*
228  * Destroys nat64 instance.
229  * Data layout (v0)(current):
230  * Request: [ ipfw_obj_header ]
231  *
232  * Returns 0 on success
233  */
234 static int
235 nat64lsn_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
236     struct sockopt_data *sd)
237 {
238 	struct nat64lsn_cfg *cfg;
239 	ipfw_obj_header *oh;
240 
241 	if (sd->valsize != sizeof(*oh))
242 		return (EINVAL);
243 
244 	oh = (ipfw_obj_header *)op3;
245 
246 	IPFW_UH_WLOCK(ch);
247 	cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
248 	if (cfg == NULL) {
249 		IPFW_UH_WUNLOCK(ch);
250 		return (ESRCH);
251 	}
252 
253 	if (cfg->no.refcnt > 0) {
254 		IPFW_UH_WUNLOCK(ch);
255 		return (EBUSY);
256 	}
257 
258 	SRV_OBJECT(ch, cfg->no.kidx) = NULL;
259 	nat64lsn_detach_config(ch, cfg);
260 	IPFW_UH_WUNLOCK(ch);
261 
262 	nat64lsn_destroy_instance(cfg);
263 	return (0);
264 }
265 
266 #define	__COPY_STAT_FIELD(_cfg, _stats, _field)	\
267 	(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->stats, _field)
268 static void
269 export_stats(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg,
270     struct ipfw_nat64lsn_stats *stats)
271 {
272 
273 	__COPY_STAT_FIELD(cfg, stats, opcnt64);
274 	__COPY_STAT_FIELD(cfg, stats, opcnt46);
275 	__COPY_STAT_FIELD(cfg, stats, ofrags);
276 	__COPY_STAT_FIELD(cfg, stats, ifrags);
277 	__COPY_STAT_FIELD(cfg, stats, oerrors);
278 	__COPY_STAT_FIELD(cfg, stats, noroute4);
279 	__COPY_STAT_FIELD(cfg, stats, noroute6);
280 	__COPY_STAT_FIELD(cfg, stats, nomatch4);
281 	__COPY_STAT_FIELD(cfg, stats, noproto);
282 	__COPY_STAT_FIELD(cfg, stats, nomem);
283 	__COPY_STAT_FIELD(cfg, stats, dropped);
284 
285 	__COPY_STAT_FIELD(cfg, stats, jcalls);
286 	__COPY_STAT_FIELD(cfg, stats, jrequests);
287 	__COPY_STAT_FIELD(cfg, stats, jhostsreq);
288 	__COPY_STAT_FIELD(cfg, stats, jportreq);
289 	__COPY_STAT_FIELD(cfg, stats, jhostfails);
290 	__COPY_STAT_FIELD(cfg, stats, jportfails);
291 	__COPY_STAT_FIELD(cfg, stats, jmaxlen);
292 	__COPY_STAT_FIELD(cfg, stats, jnomem);
293 	__COPY_STAT_FIELD(cfg, stats, jreinjected);
294 	__COPY_STAT_FIELD(cfg, stats, screated);
295 	__COPY_STAT_FIELD(cfg, stats, sdeleted);
296 	__COPY_STAT_FIELD(cfg, stats, spgcreated);
297 	__COPY_STAT_FIELD(cfg, stats, spgdeleted);
298 
299 	stats->hostcount = cfg->ihcount;
300 	stats->tcpchunks = cfg->protochunks[NAT_PROTO_TCP];
301 	stats->udpchunks = cfg->protochunks[NAT_PROTO_UDP];
302 	stats->icmpchunks = cfg->protochunks[NAT_PROTO_ICMP];
303 }
304 #undef	__COPY_STAT_FIELD
305 
306 static void
307 nat64lsn_export_config(struct ip_fw_chain *ch, struct nat64lsn_cfg *cfg,
308     ipfw_nat64lsn_cfg *uc)
309 {
310 
311 	uc->flags = cfg->flags & NAT64LSN_FLAGSMASK;
312 	uc->max_ports = cfg->max_chunks * NAT64_CHUNK_SIZE;
313 	uc->agg_prefix_len = cfg->agg_prefix_len;
314 	uc->agg_prefix_max = cfg->agg_prefix_max;
315 
316 	uc->jmaxlen = cfg->jmaxlen;
317 	uc->nh_delete_delay = cfg->nh_delete_delay;
318 	uc->pg_delete_delay = cfg->pg_delete_delay;
319 	uc->st_syn_ttl = cfg->st_syn_ttl;
320 	uc->st_close_ttl = cfg->st_close_ttl;
321 	uc->st_estab_ttl = cfg->st_estab_ttl;
322 	uc->st_udp_ttl = cfg->st_udp_ttl;
323 	uc->st_icmp_ttl = cfg->st_icmp_ttl;
324 	uc->prefix4.s_addr = htonl(cfg->prefix4);
325 	uc->prefix6 = cfg->prefix6;
326 	uc->plen4 = cfg->plen4;
327 	uc->plen6 = cfg->plen6;
328 	uc->set = cfg->no.set;
329 	strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
330 }
331 
332 struct nat64_dump_arg {
333 	struct ip_fw_chain *ch;
334 	struct sockopt_data *sd;
335 };
336 
337 static int
338 export_config_cb(struct namedobj_instance *ni, struct named_object *no,
339     void *arg)
340 {
341 	struct nat64_dump_arg *da = (struct nat64_dump_arg *)arg;
342 	ipfw_nat64lsn_cfg *uc;
343 
344 	uc = (struct _ipfw_nat64lsn_cfg *)ipfw_get_sopt_space(da->sd,
345 	    sizeof(*uc));
346 	nat64lsn_export_config(da->ch, (struct nat64lsn_cfg *)no, uc);
347 	return (0);
348 }
349 
350 /*
351  * Lists all nat64 lsn instances currently available in kernel.
352  * Data layout (v0)(current):
353  * Request: [ ipfw_obj_lheader ]
354  * Reply: [ ipfw_obj_lheader ipfw_nat64lsn_cfg x N ]
355  *
356  * Returns 0 on success
357  */
358 static int
359 nat64lsn_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
360     struct sockopt_data *sd)
361 {
362 	ipfw_obj_lheader *olh;
363 	struct nat64_dump_arg da;
364 
365 	/* Check minimum header size */
366 	if (sd->valsize < sizeof(ipfw_obj_lheader))
367 		return (EINVAL);
368 
369 	olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
370 
371 	IPFW_UH_RLOCK(ch);
372 	olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
373 	    IPFW_TLV_NAT64LSN_NAME);
374 	olh->objsize = sizeof(ipfw_nat64lsn_cfg);
375 	olh->size = sizeof(*olh) + olh->count * olh->objsize;
376 
377 	if (sd->valsize < olh->size) {
378 		IPFW_UH_RUNLOCK(ch);
379 		return (ENOMEM);
380 	}
381 	memset(&da, 0, sizeof(da));
382 	da.ch = ch;
383 	da.sd = sd;
384 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb, &da,
385 	    IPFW_TLV_NAT64LSN_NAME);
386 	IPFW_UH_RUNLOCK(ch);
387 
388 	return (0);
389 }
390 
391 /*
392  * Change existing nat64lsn instance configuration.
393  * Data layout (v0)(current):
394  * Request: [ ipfw_obj_header ipfw_nat64lsn_cfg ]
395  * Reply: [ ipfw_obj_header ipfw_nat64lsn_cfg ]
396  *
397  * Returns 0 on success
398  */
399 static int
400 nat64lsn_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
401     struct sockopt_data *sd)
402 {
403 	ipfw_obj_header *oh;
404 	ipfw_nat64lsn_cfg *uc;
405 	struct nat64lsn_cfg *cfg;
406 	struct namedobj_instance *ni;
407 
408 	if (sd->valsize != sizeof(*oh) + sizeof(*uc))
409 		return (EINVAL);
410 
411 	oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
412 	    sizeof(*oh) + sizeof(*uc));
413 	uc = (ipfw_nat64lsn_cfg *)(oh + 1);
414 
415 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
416 	    oh->ntlv.set >= IPFW_MAX_SETS)
417 		return (EINVAL);
418 
419 	ni = CHAIN_TO_SRV(ch);
420 	if (sd->sopt->sopt_dir == SOPT_GET) {
421 		IPFW_UH_RLOCK(ch);
422 		cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set);
423 		if (cfg == NULL) {
424 			IPFW_UH_RUNLOCK(ch);
425 			return (EEXIST);
426 		}
427 		nat64lsn_export_config(ch, cfg, uc);
428 		IPFW_UH_RUNLOCK(ch);
429 		return (0);
430 	}
431 
432 	nat64lsn_default_config(uc);
433 
434 	IPFW_UH_WLOCK(ch);
435 	cfg = nat64lsn_find(ni, oh->ntlv.name, oh->ntlv.set);
436 	if (cfg == NULL) {
437 		IPFW_UH_WUNLOCK(ch);
438 		return (EEXIST);
439 	}
440 
441 	/*
442 	 * For now allow to change only following values:
443 	 *  jmaxlen, nh_del_age, pg_del_age, tcp_syn_age, tcp_close_age,
444 	 *  tcp_est_age, udp_age, icmp_age, flags, max_ports.
445 	 */
446 
447 	cfg->max_chunks = uc->max_ports / NAT64_CHUNK_SIZE;
448 	cfg->jmaxlen = uc->jmaxlen;
449 	cfg->nh_delete_delay = uc->nh_delete_delay;
450 	cfg->pg_delete_delay = uc->pg_delete_delay;
451 	cfg->st_syn_ttl = uc->st_syn_ttl;
452 	cfg->st_close_ttl = uc->st_close_ttl;
453 	cfg->st_estab_ttl = uc->st_estab_ttl;
454 	cfg->st_udp_ttl = uc->st_udp_ttl;
455 	cfg->st_icmp_ttl = uc->st_icmp_ttl;
456 	cfg->flags = uc->flags & NAT64LSN_FLAGSMASK;
457 
458 	IPFW_UH_WUNLOCK(ch);
459 
460 	return (0);
461 }
462 
463 /*
464  * Get nat64lsn statistics.
465  * Data layout (v0)(current):
466  * Request: [ ipfw_obj_header ]
467  * Reply: [ ipfw_obj_header ipfw_counter_tlv ]
468  *
469  * Returns 0 on success
470  */
471 static int
472 nat64lsn_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
473     struct sockopt_data *sd)
474 {
475 	struct ipfw_nat64lsn_stats stats;
476 	struct nat64lsn_cfg *cfg;
477 	ipfw_obj_header *oh;
478 	ipfw_obj_ctlv *ctlv;
479 	size_t sz;
480 
481 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
482 	if (sd->valsize % sizeof(uint64_t))
483 		return (EINVAL);
484 	if (sd->valsize < sz)
485 		return (ENOMEM);
486 	oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
487 	if (oh == NULL)
488 		return (EINVAL);
489 	memset(&stats, 0, sizeof(stats));
490 
491 	IPFW_UH_RLOCK(ch);
492 	cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
493 	if (cfg == NULL) {
494 		IPFW_UH_RUNLOCK(ch);
495 		return (ESRCH);
496 	}
497 
498 	export_stats(ch, cfg, &stats);
499 	IPFW_UH_RUNLOCK(ch);
500 
501 	ctlv = (ipfw_obj_ctlv *)(oh + 1);
502 	memset(ctlv, 0, sizeof(*ctlv));
503 	ctlv->head.type = IPFW_TLV_COUNTERS;
504 	ctlv->head.length = sz - sizeof(ipfw_obj_header);
505 	ctlv->count = sizeof(stats) / sizeof(uint64_t);
506 	ctlv->objsize = sizeof(uint64_t);
507 	ctlv->version = IPFW_NAT64_VERSION;
508 	memcpy(ctlv + 1, &stats, sizeof(stats));
509 	return (0);
510 }
511 
512 /*
513  * Reset nat64lsn statistics.
514  * Data layout (v0)(current):
515  * Request: [ ipfw_obj_header ]
516  *
517  * Returns 0 on success
518  */
519 static int
520 nat64lsn_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
521     struct sockopt_data *sd)
522 {
523 	struct nat64lsn_cfg *cfg;
524 	ipfw_obj_header *oh;
525 
526 	if (sd->valsize != sizeof(*oh))
527 		return (EINVAL);
528 	oh = (ipfw_obj_header *)sd->kbuf;
529 	if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
530 	    oh->ntlv.set >= IPFW_MAX_SETS)
531 		return (EINVAL);
532 
533 	IPFW_UH_WLOCK(ch);
534 	cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
535 	if (cfg == NULL) {
536 		IPFW_UH_WUNLOCK(ch);
537 		return (ESRCH);
538 	}
539 	COUNTER_ARRAY_ZERO(cfg->stats.stats, NAT64STATS);
540 	IPFW_UH_WUNLOCK(ch);
541 	return (0);
542 }
543 
544 /*
545  * Reply: [ ipfw_obj_header ipfw_obj_data [ ipfw_nat64lsn_stg
546  *	ipfw_nat64lsn_state x count, ... ] ]
547  */
548 static int
549 export_pg_states(struct nat64lsn_cfg *cfg, struct nat64lsn_portgroup *pg,
550     ipfw_nat64lsn_stg *stg, struct sockopt_data *sd)
551 {
552 	ipfw_nat64lsn_state *ste;
553 	struct nat64lsn_state *st;
554 	int i, count;
555 
556 	NAT64_LOCK(pg->host);
557 	count = 0;
558 	for (i = 0; i < 64; i++) {
559 		if (PG_IS_BUSY_IDX(pg, i))
560 			count++;
561 	}
562 	DPRINTF(DP_STATE, "EXPORT PG %d, count %d", pg->idx, count);
563 
564 	if (count == 0) {
565 		stg->count = 0;
566 		NAT64_UNLOCK(pg->host);
567 		return (0);
568 	}
569 	ste = (ipfw_nat64lsn_state *)ipfw_get_sopt_space(sd,
570 	    count * sizeof(ipfw_nat64lsn_state));
571 	if (ste == NULL) {
572 		NAT64_UNLOCK(pg->host);
573 		return (1);
574 	}
575 
576 	stg->alias4.s_addr = pg->aaddr;
577 	stg->proto = nat64lsn_rproto_map[pg->nat_proto];
578 	stg->flags = 0;
579 	stg->host6 = pg->host->addr;
580 	stg->count = count;
581 	for (i = 0; i < 64; i++) {
582 		if (PG_IS_FREE_IDX(pg, i))
583 			continue;
584 		st = &pg->states[i];
585 		ste->daddr.s_addr = st->u.s.faddr;
586 		ste->dport = st->u.s.fport;
587 		ste->aport = pg->aport + i;
588 		ste->sport = st->u.s.lport;
589 		ste->flags = st->flags; /* XXX filter flags */
590 		ste->idle = GET_AGE(st->timestamp);
591 		ste++;
592 	}
593 	NAT64_UNLOCK(pg->host);
594 
595 	return (0);
596 }
597 
598 static int
599 get_next_idx(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto,
600     uint16_t *port)
601 {
602 
603 	if (*port < 65536 - NAT64_CHUNK_SIZE) {
604 		*port += NAT64_CHUNK_SIZE;
605 		return (0);
606 	}
607 	*port = 0;
608 
609 	if (*nat_proto < NAT_MAX_PROTO - 1) {
610 		*nat_proto += 1;
611 		return (0);
612 	}
613 	*nat_proto = 1;
614 
615 	if (*addr < cfg->pmask4) {
616 		*addr += 1;
617 		return (0);
618 	}
619 
620 	/* End of space. */
621 	return (1);
622 }
623 
624 #define	PACK_IDX(addr, proto, port)	\
625 	((uint64_t)addr << 32) | ((uint32_t)port << 16) | (proto << 8)
626 #define	UNPACK_IDX(idx, addr, proto, port)		\
627 	(addr) = (uint32_t)((idx) >> 32);		\
628 	(port) = (uint16_t)(((idx) >> 16) & 0xFFFF);	\
629 	(proto) = (uint8_t)(((idx) >> 8) & 0xFF)
630 
631 static struct nat64lsn_portgroup *
632 get_next_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto,
633   uint16_t *port)
634 {
635 	struct nat64lsn_portgroup *pg;
636 	uint64_t pre_pack, post_pack;
637 
638 	pg = NULL;
639 	pre_pack = PACK_IDX(*addr, *nat_proto, *port);
640 	for (;;) {
641 		if (get_next_idx(cfg, addr, nat_proto, port) != 0) {
642 			/* End of states */
643 			return (pg);
644 		}
645 
646 		pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port);
647 		if (pg != NULL)
648 			break;
649 	}
650 
651 	post_pack = PACK_IDX(*addr, *nat_proto, *port);
652 	if (pre_pack == post_pack)
653 		DPRINTF(DP_STATE, "XXX: PACK_IDX %u %d %d",
654 		    *addr, *nat_proto, *port);
655 	return (pg);
656 }
657 
658 static NAT64NOINLINE struct nat64lsn_portgroup *
659 get_first_pg(struct nat64lsn_cfg *cfg, uint32_t *addr, uint8_t *nat_proto,
660   uint16_t *port)
661 {
662 	struct nat64lsn_portgroup *pg;
663 
664 	pg = GET_PORTGROUP(cfg, *addr, *nat_proto, *port);
665 	if (pg == NULL)
666 		pg = get_next_pg(cfg, addr, nat_proto, port);
667 
668 	return (pg);
669 }
670 
671 /*
672  * Lists nat64lsn states.
673  * Data layout (v0)(current):
674  * Request: [ ipfw_obj_header ipfw_obj_data [ uint64_t ]]
675  * Reply: [ ipfw_obj_header ipfw_obj_data [
676  *		ipfw_nat64lsn_stg ipfw_nat64lsn_state x N] ]
677  *
678  * Returns 0 on success
679  */
680 static int
681 nat64lsn_states(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
682     struct sockopt_data *sd)
683 {
684 	ipfw_obj_header *oh;
685 	ipfw_obj_data *od;
686 	ipfw_nat64lsn_stg *stg;
687 	struct nat64lsn_cfg *cfg;
688 	struct nat64lsn_portgroup *pg, *pg_next;
689 	uint64_t next_idx;
690 	size_t sz;
691 	uint32_t addr, states;
692 	uint16_t port;
693 	uint8_t nat_proto;
694 
695 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) +
696 	    sizeof(uint64_t);
697 	/* Check minimum header size */
698 	if (sd->valsize < sz)
699 		return (EINVAL);
700 
701 	oh = (ipfw_obj_header *)sd->kbuf;
702 	od = (ipfw_obj_data *)(oh + 1);
703 	if (od->head.type != IPFW_TLV_OBJDATA ||
704 	    od->head.length != sz - sizeof(ipfw_obj_header))
705 		return (EINVAL);
706 
707 	next_idx = *(uint64_t *)(od + 1);
708 	/* Translate index to the request position to start from */
709 	UNPACK_IDX(next_idx, addr, nat_proto, port);
710 	if (nat_proto >= NAT_MAX_PROTO)
711 		return (EINVAL);
712 	if (nat_proto == 0 && addr != 0)
713 		return (EINVAL);
714 
715 	IPFW_UH_RLOCK(ch);
716 	cfg = nat64lsn_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
717 	if (cfg == NULL) {
718 		IPFW_UH_RUNLOCK(ch);
719 		return (ESRCH);
720 	}
721 	/* Fill in starting point */
722 	if (addr == 0) {
723 		addr = cfg->prefix4;
724 		nat_proto = 1;
725 		port = 0;
726 	}
727 	if (addr < cfg->prefix4 || addr > cfg->pmask4) {
728 		IPFW_UH_RUNLOCK(ch);
729 		DPRINTF(DP_GENERIC | DP_STATE, "XXX: %ju %u %u",
730 		    (uintmax_t)next_idx, addr, cfg->pmask4);
731 		return (EINVAL);
732 	}
733 
734 	sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_data) +
735 	    sizeof(ipfw_nat64lsn_stg);
736 	if (sd->valsize < sz)
737 		return (ENOMEM);
738 	oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd, sz);
739 	od = (ipfw_obj_data *)(oh + 1);
740 	od->head.type = IPFW_TLV_OBJDATA;
741 	od->head.length = sz - sizeof(ipfw_obj_header);
742 	stg = (ipfw_nat64lsn_stg *)(od + 1);
743 
744 	pg = get_first_pg(cfg, &addr, &nat_proto, &port);
745 	if (pg == NULL) {
746 		/* No states */
747 		stg->next_idx = 0xFF;
748 		stg->count = 0;
749 		IPFW_UH_RUNLOCK(ch);
750 		return (0);
751 	}
752 	states = 0;
753 	pg_next = NULL;
754 	while (pg != NULL) {
755 		pg_next = get_next_pg(cfg, &addr, &nat_proto, &port);
756 		if (pg_next == NULL)
757 			stg->next_idx = 0xFF;
758 		else
759 			stg->next_idx = PACK_IDX(addr, nat_proto, port);
760 
761 		if (export_pg_states(cfg, pg, stg, sd) != 0) {
762 			IPFW_UH_RUNLOCK(ch);
763 			return (states == 0 ? ENOMEM: 0);
764 		}
765 		states += stg->count;
766 		od->head.length += stg->count * sizeof(ipfw_nat64lsn_state);
767 		sz += stg->count * sizeof(ipfw_nat64lsn_state);
768 		if (pg_next != NULL) {
769 			sz += sizeof(ipfw_nat64lsn_stg);
770 			if (sd->valsize < sz)
771 				break;
772 			stg = (ipfw_nat64lsn_stg *)ipfw_get_sopt_space(sd,
773 			    sizeof(ipfw_nat64lsn_stg));
774 		}
775 		pg = pg_next;
776 	}
777 	IPFW_UH_RUNLOCK(ch);
778 	return (0);
779 }
780 
781 static struct ipfw_sopt_handler	scodes[] = {
782 	{ IP_FW_NAT64LSN_CREATE, 0,	HDIR_BOTH,	nat64lsn_create },
783 	{ IP_FW_NAT64LSN_DESTROY,0,	HDIR_SET,	nat64lsn_destroy },
784 	{ IP_FW_NAT64LSN_CONFIG, 0,	HDIR_BOTH,	nat64lsn_config },
785 	{ IP_FW_NAT64LSN_LIST,	 0,	HDIR_GET,	nat64lsn_list },
786 	{ IP_FW_NAT64LSN_STATS,	 0,	HDIR_GET,	nat64lsn_stats },
787 	{ IP_FW_NAT64LSN_RESET_STATS,0,	HDIR_SET,	nat64lsn_reset_stats },
788 	{ IP_FW_NAT64LSN_LIST_STATES,0,	HDIR_GET,	nat64lsn_states },
789 };
790 
791 static int
792 nat64lsn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
793 {
794 	ipfw_insn *icmd;
795 
796 	icmd = cmd - 1;
797 	if (icmd->opcode != O_EXTERNAL_ACTION ||
798 	    icmd->arg1 != V_nat64lsn_eid)
799 		return (1);
800 
801 	*puidx = cmd->arg1;
802 	*ptype = 0;
803 	return (0);
804 }
805 
806 static void
807 nat64lsn_update_arg1(ipfw_insn *cmd, uint16_t idx)
808 {
809 
810 	cmd->arg1 = idx;
811 }
812 
813 static int
814 nat64lsn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
815     struct named_object **pno)
816 {
817 	int err;
818 
819 	err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
820 	    IPFW_TLV_NAT64LSN_NAME, pno);
821 	return (err);
822 }
823 
824 static struct named_object *
825 nat64lsn_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
826 {
827 	struct namedobj_instance *ni;
828 	struct named_object *no;
829 
830 	IPFW_UH_WLOCK_ASSERT(ch);
831 	ni = CHAIN_TO_SRV(ch);
832 	no = ipfw_objhash_lookup_kidx(ni, idx);
833 	KASSERT(no != NULL, ("NAT64LSN with index %d not found", idx));
834 
835 	return (no);
836 }
837 
838 static int
839 nat64lsn_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
840     enum ipfw_sets_cmd cmd)
841 {
842 
843 	return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64LSN_NAME,
844 	    set, new_set, cmd));
845 }
846 
847 static struct opcode_obj_rewrite opcodes[] = {
848 	{
849 		.opcode = O_EXTERNAL_INSTANCE,
850 		.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
851 		.classifier = nat64lsn_classify,
852 		.update = nat64lsn_update_arg1,
853 		.find_byname = nat64lsn_findbyname,
854 		.find_bykidx = nat64lsn_findbykidx,
855 		.manage_sets = nat64lsn_manage_sets,
856 	},
857 };
858 
859 static int
860 destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
861     void *arg)
862 {
863 	struct nat64lsn_cfg *cfg;
864 	struct ip_fw_chain *ch;
865 
866 	ch = (struct ip_fw_chain *)arg;
867 	cfg = (struct nat64lsn_cfg *)SRV_OBJECT(ch, no->kidx);
868 	SRV_OBJECT(ch, no->kidx) = NULL;
869 	nat64lsn_detach_config(ch, cfg);
870 	nat64lsn_destroy_instance(cfg);
871 	return (0);
872 }
873 
874 int
875 nat64lsn_init(struct ip_fw_chain *ch, int first)
876 {
877 
878 	if (first != 0)
879 		nat64lsn_init_internal();
880 	V_nat64lsn_eid = ipfw_add_eaction(ch, ipfw_nat64lsn, "nat64lsn");
881 	if (V_nat64lsn_eid == 0)
882 		return (ENXIO);
883 	IPFW_ADD_SOPT_HANDLER(first, scodes);
884 	IPFW_ADD_OBJ_REWRITER(first, opcodes);
885 	return (0);
886 }
887 
888 void
889 nat64lsn_uninit(struct ip_fw_chain *ch, int last)
890 {
891 
892 	IPFW_DEL_OBJ_REWRITER(last, opcodes);
893 	IPFW_DEL_SOPT_HANDLER(last, scodes);
894 	ipfw_del_eaction(ch, V_nat64lsn_eid);
895 	/*
896 	 * Since we already have deregistered external action,
897 	 * our named objects become unaccessible via rules, because
898 	 * all rules were truncated by ipfw_del_eaction().
899 	 * So, we can unlink and destroy our named objects without holding
900 	 * IPFW_WLOCK().
901 	 */
902 	IPFW_UH_WLOCK(ch);
903 	ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
904 	    IPFW_TLV_NAT64LSN_NAME);
905 	V_nat64lsn_eid = 0;
906 	IPFW_UH_WUNLOCK(ch);
907 	if (last != 0)
908 		nat64lsn_uninit_internal();
909 }
910 
911