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