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