xref: /freebsd/sbin/ipfw/nptv6.c (revision 32e86a82f54826f14ea381affa6674db3aa3b5ae)
1b867e84eSAndrey V. Elsukov /*-
2b867e84eSAndrey V. Elsukov  * Copyright (c) 2016 Yandex LLC
3b867e84eSAndrey V. Elsukov  * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
4b867e84eSAndrey V. Elsukov  * All rights reserved.
5b867e84eSAndrey V. Elsukov  *
6b867e84eSAndrey V. Elsukov  * Redistribution and use in source and binary forms, with or without
7b867e84eSAndrey V. Elsukov  * modification, are permitted provided that the following conditions
8b867e84eSAndrey V. Elsukov  * are met:
9b867e84eSAndrey V. Elsukov  *
10b867e84eSAndrey V. Elsukov  * 1. Redistributions of source code must retain the above copyright
11b867e84eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer.
12b867e84eSAndrey V. Elsukov  * 2. Redistributions in binary form must reproduce the above copyright
13b867e84eSAndrey V. Elsukov  *    notice, this list of conditions and the following disclaimer in the
14b867e84eSAndrey V. Elsukov  *    documentation and/or other materials provided with the distribution.
15b867e84eSAndrey V. Elsukov  *
16b867e84eSAndrey V. Elsukov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17b867e84eSAndrey V. Elsukov  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18b867e84eSAndrey V. Elsukov  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19b867e84eSAndrey V. Elsukov  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20b867e84eSAndrey V. Elsukov  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21b867e84eSAndrey V. Elsukov  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22b867e84eSAndrey V. Elsukov  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23b867e84eSAndrey V. Elsukov  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24b867e84eSAndrey V. Elsukov  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25b867e84eSAndrey V. Elsukov  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26b867e84eSAndrey V. Elsukov  */
27b867e84eSAndrey V. Elsukov 
28b867e84eSAndrey V. Elsukov #include <sys/param.h>
29b867e84eSAndrey V. Elsukov #include <sys/socket.h>
30b867e84eSAndrey V. Elsukov 
31b867e84eSAndrey V. Elsukov #include "ipfw2.h"
32b867e84eSAndrey V. Elsukov 
33b867e84eSAndrey V. Elsukov #include <ctype.h>
34b867e84eSAndrey V. Elsukov #include <err.h>
35b867e84eSAndrey V. Elsukov #include <errno.h>
36b867e84eSAndrey V. Elsukov #include <inttypes.h>
37b867e84eSAndrey V. Elsukov #include <stdio.h>
38b867e84eSAndrey V. Elsukov #include <stdlib.h>
39b867e84eSAndrey V. Elsukov #include <string.h>
40b867e84eSAndrey V. Elsukov #include <sysexits.h>
41b867e84eSAndrey V. Elsukov 
42b867e84eSAndrey V. Elsukov #include <net/if.h>
43b867e84eSAndrey V. Elsukov #include <netinet/in.h>
44b867e84eSAndrey V. Elsukov #include <netinet/ip_fw.h>
45b867e84eSAndrey V. Elsukov #include <netinet6/ip_fw_nptv6.h>
46b867e84eSAndrey V. Elsukov #include <arpa/inet.h>
47b867e84eSAndrey V. Elsukov 
48b867e84eSAndrey V. Elsukov 
49b867e84eSAndrey V. Elsukov typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set);
50b867e84eSAndrey V. Elsukov static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set,
51b867e84eSAndrey V. Elsukov     int sort);
52b867e84eSAndrey V. Elsukov 
53b867e84eSAndrey V. Elsukov static void nptv6_create(const char *name, uint8_t set, int ac, char **av);
54b867e84eSAndrey V. Elsukov static void nptv6_destroy(const char *name, uint8_t set);
55b867e84eSAndrey V. Elsukov static void nptv6_stats(const char *name, uint8_t set);
5657fb3b7aSAndrey V. Elsukov static void nptv6_reset_stats(const char *name, uint8_t set);
57b867e84eSAndrey V. Elsukov static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
58b867e84eSAndrey V. Elsukov static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
59b867e84eSAndrey V. Elsukov 
60b867e84eSAndrey V. Elsukov static struct _s_x nptv6cmds[] = {
61b867e84eSAndrey V. Elsukov       { "create",	TOK_CREATE },
62b867e84eSAndrey V. Elsukov       { "destroy",	TOK_DESTROY },
63b867e84eSAndrey V. Elsukov       { "list",		TOK_LIST },
64b867e84eSAndrey V. Elsukov       { "show",		TOK_LIST },
65b867e84eSAndrey V. Elsukov       { "stats",	TOK_STATS },
66b867e84eSAndrey V. Elsukov       { NULL, 0 }
67b867e84eSAndrey V. Elsukov };
68b867e84eSAndrey V. Elsukov 
6957fb3b7aSAndrey V. Elsukov static struct _s_x nptv6statscmds[] = {
7057fb3b7aSAndrey V. Elsukov       { "reset",	TOK_RESET },
7157fb3b7aSAndrey V. Elsukov       { NULL, 0 }
7257fb3b7aSAndrey V. Elsukov };
7357fb3b7aSAndrey V. Elsukov 
74b867e84eSAndrey V. Elsukov /*
75b867e84eSAndrey V. Elsukov  * This one handles all NPTv6-related commands
76b867e84eSAndrey V. Elsukov  *	ipfw [set N] nptv6 NAME {create | config} ...
7757fb3b7aSAndrey V. Elsukov  *	ipfw [set N] nptv6 NAME stats [reset]
78b867e84eSAndrey V. Elsukov  *	ipfw [set N] nptv6 {NAME | all} destroy
79b867e84eSAndrey V. Elsukov  *	ipfw [set N] nptv6 {NAME | all} {list | show}
80b867e84eSAndrey V. Elsukov  */
81b867e84eSAndrey V. Elsukov #define	nptv6_check_name	table_check_name
82b867e84eSAndrey V. Elsukov void
ipfw_nptv6_handler(int ac,char * av[])83b867e84eSAndrey V. Elsukov ipfw_nptv6_handler(int ac, char *av[])
84b867e84eSAndrey V. Elsukov {
85b867e84eSAndrey V. Elsukov 	const char *name;
86b867e84eSAndrey V. Elsukov 	int tcmd;
87b867e84eSAndrey V. Elsukov 	uint8_t set;
88b867e84eSAndrey V. Elsukov 
8956707beeSMark Johnston 	if (g_co.use_set != 0)
9056707beeSMark Johnston 		set = g_co.use_set - 1;
91b867e84eSAndrey V. Elsukov 	else
92b867e84eSAndrey V. Elsukov 		set = 0;
93b867e84eSAndrey V. Elsukov 	ac--; av++;
94b867e84eSAndrey V. Elsukov 
95b867e84eSAndrey V. Elsukov 	NEED1("nptv6 needs instance name");
96b867e84eSAndrey V. Elsukov 	name = *av;
97b867e84eSAndrey V. Elsukov 	if (nptv6_check_name(name) != 0) {
98b867e84eSAndrey V. Elsukov 		if (strcmp(name, "all") == 0) {
99b867e84eSAndrey V. Elsukov 			name = NULL;
100b867e84eSAndrey V. Elsukov 		} else
101b867e84eSAndrey V. Elsukov 			errx(EX_USAGE, "nptv6 instance name %s is invalid",
102b867e84eSAndrey V. Elsukov 			    name);
103b867e84eSAndrey V. Elsukov 	}
104b867e84eSAndrey V. Elsukov 	ac--; av++;
105b867e84eSAndrey V. Elsukov 	NEED1("nptv6 needs command");
106b867e84eSAndrey V. Elsukov 
107b867e84eSAndrey V. Elsukov 	tcmd = get_token(nptv6cmds, *av, "nptv6 command");
108b867e84eSAndrey V. Elsukov 	if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
109b867e84eSAndrey V. Elsukov 		errx(EX_USAGE, "nptv6 instance name required");
110b867e84eSAndrey V. Elsukov 	switch (tcmd) {
111b867e84eSAndrey V. Elsukov 	case TOK_CREATE:
112b867e84eSAndrey V. Elsukov 		ac--; av++;
113b867e84eSAndrey V. Elsukov 		nptv6_create(name, set, ac, av);
114b867e84eSAndrey V. Elsukov 		break;
115b867e84eSAndrey V. Elsukov 	case TOK_LIST:
116b867e84eSAndrey V. Elsukov 		nptv6_foreach(nptv6_show_cb, name, set, 1);
117b867e84eSAndrey V. Elsukov 		break;
118b867e84eSAndrey V. Elsukov 	case TOK_DESTROY:
119b867e84eSAndrey V. Elsukov 		if (name == NULL)
120b867e84eSAndrey V. Elsukov 			nptv6_foreach(nptv6_destroy_cb, NULL, set, 0);
121b867e84eSAndrey V. Elsukov 		else
122b867e84eSAndrey V. Elsukov 			nptv6_destroy(name, set);
123b867e84eSAndrey V. Elsukov 		break;
124b867e84eSAndrey V. Elsukov 	case TOK_STATS:
12557fb3b7aSAndrey V. Elsukov 		ac--; av++;
12657fb3b7aSAndrey V. Elsukov 		if (ac == 0) {
127b867e84eSAndrey V. Elsukov 			nptv6_stats(name, set);
12857fb3b7aSAndrey V. Elsukov 			break;
12957fb3b7aSAndrey V. Elsukov 		}
13057fb3b7aSAndrey V. Elsukov 		tcmd = get_token(nptv6statscmds, *av, "stats command");
13157fb3b7aSAndrey V. Elsukov 		if (tcmd == TOK_RESET)
13257fb3b7aSAndrey V. Elsukov 			nptv6_reset_stats(name, set);
133b867e84eSAndrey V. Elsukov 	}
134b867e84eSAndrey V. Elsukov }
135b867e84eSAndrey V. Elsukov 
136b867e84eSAndrey V. Elsukov 
137b867e84eSAndrey V. Elsukov static void
nptv6_fill_ntlv(ipfw_obj_ntlv * ntlv,const char * name,uint8_t set)138b867e84eSAndrey V. Elsukov nptv6_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
139b867e84eSAndrey V. Elsukov {
140b867e84eSAndrey V. Elsukov 
141b867e84eSAndrey V. Elsukov 	ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
142b867e84eSAndrey V. Elsukov 	ntlv->head.length = sizeof(ipfw_obj_ntlv);
143b867e84eSAndrey V. Elsukov 	ntlv->idx = 1;
144b867e84eSAndrey V. Elsukov 	ntlv->set = set;
145b867e84eSAndrey V. Elsukov 	strlcpy(ntlv->name, name, sizeof(ntlv->name));
146b867e84eSAndrey V. Elsukov }
147b867e84eSAndrey V. Elsukov 
148b867e84eSAndrey V. Elsukov static struct _s_x nptv6newcmds[] = {
149b867e84eSAndrey V. Elsukov       { "int_prefix",	TOK_INTPREFIX },
150b867e84eSAndrey V. Elsukov       { "ext_prefix",	TOK_EXTPREFIX },
151b867e84eSAndrey V. Elsukov       { "prefixlen",	TOK_PREFIXLEN },
152b2b56606SAndrey V. Elsukov       { "ext_if",	TOK_EXTIF },
153b867e84eSAndrey V. Elsukov       { NULL, 0 }
154b867e84eSAndrey V. Elsukov };
155b867e84eSAndrey V. Elsukov 
156b867e84eSAndrey V. Elsukov 
157b867e84eSAndrey V. Elsukov static void
nptv6_parse_prefix(const char * arg,struct in6_addr * prefix,int * len)158b867e84eSAndrey V. Elsukov nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
159b867e84eSAndrey V. Elsukov {
160b867e84eSAndrey V. Elsukov 	char *p, *l;
161b867e84eSAndrey V. Elsukov 
162b867e84eSAndrey V. Elsukov 	p = strdup(arg);
163b867e84eSAndrey V. Elsukov 	if (p == NULL)
164b867e84eSAndrey V. Elsukov 		err(EX_OSERR, NULL);
165b867e84eSAndrey V. Elsukov 	if ((l = strchr(p, '/')) != NULL)
166b867e84eSAndrey V. Elsukov 		*l++ = '\0';
167b867e84eSAndrey V. Elsukov 	if (inet_pton(AF_INET6, p, prefix) != 1)
168b867e84eSAndrey V. Elsukov 		errx(EX_USAGE, "Bad prefix: %s", p);
169b867e84eSAndrey V. Elsukov 	if (l != NULL) {
170b867e84eSAndrey V. Elsukov 		*len = (int)strtol(l, &l, 10);
171b867e84eSAndrey V. Elsukov 		if (*l != '\0' || *len <= 0 || *len > 64)
172b867e84eSAndrey V. Elsukov 			errx(EX_USAGE, "Bad prefix length: %s", arg);
173b867e84eSAndrey V. Elsukov 	} else
174b867e84eSAndrey V. Elsukov 		*len = 0;
175b867e84eSAndrey V. Elsukov 	free(p);
176b867e84eSAndrey V. Elsukov }
177b867e84eSAndrey V. Elsukov /*
178b867e84eSAndrey V. Elsukov  * Creates new nptv6 instance
179b867e84eSAndrey V. Elsukov  * ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix>
180b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
181b867e84eSAndrey V. Elsukov  */
182b867e84eSAndrey V. Elsukov #define	NPTV6_HAS_INTPREFIX	0x01
183b867e84eSAndrey V. Elsukov #define	NPTV6_HAS_EXTPREFIX	0x02
184b867e84eSAndrey V. Elsukov #define	NPTV6_HAS_PREFIXLEN	0x04
185b867e84eSAndrey V. Elsukov static void
nptv6_create(const char * name,uint8_t set,int ac,char * av[])186b867e84eSAndrey V. Elsukov nptv6_create(const char *name, uint8_t set, int ac, char *av[])
187b867e84eSAndrey V. Elsukov {
188b867e84eSAndrey V. Elsukov 	char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)];
189b867e84eSAndrey V. Elsukov 	struct in6_addr mask;
190b867e84eSAndrey V. Elsukov 	ipfw_nptv6_cfg *cfg;
191b867e84eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
192b867e84eSAndrey V. Elsukov 	int tcmd, flags, plen;
19356707beeSMark Johnston 	char *p;
194b867e84eSAndrey V. Elsukov 
195b867e84eSAndrey V. Elsukov 	plen = 0;
196b867e84eSAndrey V. Elsukov 	memset(buf, 0, sizeof(buf));
197b867e84eSAndrey V. Elsukov 	olh = (ipfw_obj_lheader *)buf;
198b867e84eSAndrey V. Elsukov 	cfg = (ipfw_nptv6_cfg *)(olh + 1);
199b867e84eSAndrey V. Elsukov 	cfg->set = set;
200b867e84eSAndrey V. Elsukov 	flags = 0;
201b867e84eSAndrey V. Elsukov 	while (ac > 0) {
202b867e84eSAndrey V. Elsukov 		tcmd = get_token(nptv6newcmds, *av, "option");
203b867e84eSAndrey V. Elsukov 		ac--; av++;
204b867e84eSAndrey V. Elsukov 
205b867e84eSAndrey V. Elsukov 		switch (tcmd) {
206b867e84eSAndrey V. Elsukov 		case TOK_INTPREFIX:
207b867e84eSAndrey V. Elsukov 			NEED1("IPv6 prefix required");
208b867e84eSAndrey V. Elsukov 			nptv6_parse_prefix(*av, &cfg->internal, &plen);
209b867e84eSAndrey V. Elsukov 			flags |= NPTV6_HAS_INTPREFIX;
210b867e84eSAndrey V. Elsukov 			if (plen > 0)
211b867e84eSAndrey V. Elsukov 				goto check_prefix;
212b867e84eSAndrey V. Elsukov 			ac--; av++;
213b867e84eSAndrey V. Elsukov 			break;
214b867e84eSAndrey V. Elsukov 		case TOK_EXTPREFIX:
215b2b56606SAndrey V. Elsukov 			if (flags & NPTV6_HAS_EXTPREFIX)
216b2b56606SAndrey V. Elsukov 				errx(EX_USAGE,
217b2b56606SAndrey V. Elsukov 				    "Only one ext_prefix or ext_if allowed");
218b867e84eSAndrey V. Elsukov 			NEED1("IPv6 prefix required");
219b867e84eSAndrey V. Elsukov 			nptv6_parse_prefix(*av, &cfg->external, &plen);
220b867e84eSAndrey V. Elsukov 			flags |= NPTV6_HAS_EXTPREFIX;
221b867e84eSAndrey V. Elsukov 			if (plen > 0)
222b867e84eSAndrey V. Elsukov 				goto check_prefix;
223b867e84eSAndrey V. Elsukov 			ac--; av++;
224b867e84eSAndrey V. Elsukov 			break;
225b2b56606SAndrey V. Elsukov 		case TOK_EXTIF:
226b2b56606SAndrey V. Elsukov 			if (flags & NPTV6_HAS_EXTPREFIX)
227b2b56606SAndrey V. Elsukov 				errx(EX_USAGE,
228b2b56606SAndrey V. Elsukov 				    "Only one ext_prefix or ext_if allowed");
229b2b56606SAndrey V. Elsukov 			NEED1("Interface name required");
230b2b56606SAndrey V. Elsukov 			if (strlen(*av) >= sizeof(cfg->if_name))
231b2b56606SAndrey V. Elsukov 				errx(EX_USAGE, "Invalid interface name");
232b2b56606SAndrey V. Elsukov 			flags |= NPTV6_HAS_EXTPREFIX;
233b2b56606SAndrey V. Elsukov 			cfg->flags |= NPTV6_DYNAMIC_PREFIX;
234b2b56606SAndrey V. Elsukov 			strncpy(cfg->if_name, *av, sizeof(cfg->if_name));
235b2b56606SAndrey V. Elsukov 			ac--; av++;
236b2b56606SAndrey V. Elsukov 			break;
237b867e84eSAndrey V. Elsukov 		case TOK_PREFIXLEN:
238b867e84eSAndrey V. Elsukov 			NEED1("IPv6 prefix length required");
239b867e84eSAndrey V. Elsukov 			plen = strtol(*av, &p, 10);
240b867e84eSAndrey V. Elsukov check_prefix:
241b867e84eSAndrey V. Elsukov 			if (*p != '\0' || plen < 8 || plen > 64)
242b867e84eSAndrey V. Elsukov 				errx(EX_USAGE, "wrong prefix length: %s", *av);
243b867e84eSAndrey V. Elsukov 			/* RFC 6296 Sec. 3.1 */
244b867e84eSAndrey V. Elsukov 			if (cfg->plen > 0 && cfg->plen != plen) {
245b867e84eSAndrey V. Elsukov 				warnx("Prefix length mismatch (%d vs %d).  "
246b867e84eSAndrey V. Elsukov 				    "It was extended up to %d",
247b867e84eSAndrey V. Elsukov 				    cfg->plen, plen, MAX(plen, cfg->plen));
248b867e84eSAndrey V. Elsukov 				plen = MAX(plen, cfg->plen);
249b867e84eSAndrey V. Elsukov 			}
250b867e84eSAndrey V. Elsukov 			cfg->plen = plen;
251b867e84eSAndrey V. Elsukov 			flags |= NPTV6_HAS_PREFIXLEN;
252b867e84eSAndrey V. Elsukov 			ac--; av++;
253b867e84eSAndrey V. Elsukov 			break;
254b867e84eSAndrey V. Elsukov 		}
255b867e84eSAndrey V. Elsukov 	}
256b867e84eSAndrey V. Elsukov 
257b867e84eSAndrey V. Elsukov 	/* Check validness */
258b867e84eSAndrey V. Elsukov 	if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
259b867e84eSAndrey V. Elsukov 		errx(EX_USAGE, "int_prefix required");
260b867e84eSAndrey V. Elsukov 	if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX)
261b2b56606SAndrey V. Elsukov 		errx(EX_USAGE, "ext_prefix or ext_if required");
262b867e84eSAndrey V. Elsukov 	if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN)
263b867e84eSAndrey V. Elsukov 		errx(EX_USAGE, "prefixlen required");
264b867e84eSAndrey V. Elsukov 
265b867e84eSAndrey V. Elsukov 	n2mask(&mask, cfg->plen);
266b867e84eSAndrey V. Elsukov 	APPLY_MASK(&cfg->internal, &mask);
267b2b56606SAndrey V. Elsukov 	if ((cfg->flags & NPTV6_DYNAMIC_PREFIX) == 0)
268b867e84eSAndrey V. Elsukov 		APPLY_MASK(&cfg->external, &mask);
269b867e84eSAndrey V. Elsukov 
270b867e84eSAndrey V. Elsukov 	olh->count = 1;
271b867e84eSAndrey V. Elsukov 	olh->objsize = sizeof(*cfg);
272b867e84eSAndrey V. Elsukov 	olh->size = sizeof(buf);
273b867e84eSAndrey V. Elsukov 	strlcpy(cfg->name, name, sizeof(cfg->name));
274b867e84eSAndrey V. Elsukov 	if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0)
275b867e84eSAndrey V. Elsukov 		err(EX_OSERR, "nptv6 instance creation failed");
276b867e84eSAndrey V. Elsukov }
277b867e84eSAndrey V. Elsukov 
278b867e84eSAndrey V. Elsukov /*
279b867e84eSAndrey V. Elsukov  * Destroys NPTv6 instance.
280b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
281b867e84eSAndrey V. Elsukov  */
282b867e84eSAndrey V. Elsukov static void
nptv6_destroy(const char * name,uint8_t set)283b867e84eSAndrey V. Elsukov nptv6_destroy(const char *name, uint8_t set)
284b867e84eSAndrey V. Elsukov {
285b867e84eSAndrey V. Elsukov 	ipfw_obj_header oh;
286b867e84eSAndrey V. Elsukov 
287b867e84eSAndrey V. Elsukov 	memset(&oh, 0, sizeof(oh));
288b867e84eSAndrey V. Elsukov 	nptv6_fill_ntlv(&oh.ntlv, name, set);
289b867e84eSAndrey V. Elsukov 	if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0)
290b867e84eSAndrey V. Elsukov 		err(EX_OSERR, "failed to destroy nat instance %s", name);
291b867e84eSAndrey V. Elsukov }
292b867e84eSAndrey V. Elsukov 
293b867e84eSAndrey V. Elsukov /*
294b867e84eSAndrey V. Elsukov  * Get NPTv6 instance statistics.
295b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
296b867e84eSAndrey V. Elsukov  * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
297b867e84eSAndrey V. Elsukov  */
298b867e84eSAndrey V. Elsukov static int
nptv6_get_stats(const char * name,uint8_t set,struct ipfw_nptv6_stats * stats)299b867e84eSAndrey V. Elsukov nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats)
300b867e84eSAndrey V. Elsukov {
301b867e84eSAndrey V. Elsukov 	ipfw_obj_header *oh;
302b867e84eSAndrey V. Elsukov 	ipfw_obj_ctlv *oc;
303b867e84eSAndrey V. Elsukov 	size_t sz;
304b867e84eSAndrey V. Elsukov 
305b867e84eSAndrey V. Elsukov 	sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
306b867e84eSAndrey V. Elsukov 	oh = calloc(1, sz);
307b867e84eSAndrey V. Elsukov 	nptv6_fill_ntlv(&oh->ntlv, name, set);
308b867e84eSAndrey V. Elsukov 	if (do_get3(IP_FW_NPTV6_STATS, &oh->opheader, &sz) == 0) {
309b867e84eSAndrey V. Elsukov 		oc = (ipfw_obj_ctlv *)(oh + 1);
310b867e84eSAndrey V. Elsukov 		memcpy(stats, oc + 1, sizeof(*stats));
311b867e84eSAndrey V. Elsukov 		free(oh);
312b867e84eSAndrey V. Elsukov 		return (0);
313b867e84eSAndrey V. Elsukov 	}
314b867e84eSAndrey V. Elsukov 	free(oh);
315b867e84eSAndrey V. Elsukov 	return (-1);
316b867e84eSAndrey V. Elsukov }
317b867e84eSAndrey V. Elsukov 
318b867e84eSAndrey V. Elsukov static void
nptv6_stats(const char * name,uint8_t set)319b867e84eSAndrey V. Elsukov nptv6_stats(const char *name, uint8_t set)
320b867e84eSAndrey V. Elsukov {
321b867e84eSAndrey V. Elsukov 	struct ipfw_nptv6_stats stats;
322b867e84eSAndrey V. Elsukov 
323b867e84eSAndrey V. Elsukov 	if (nptv6_get_stats(name, set, &stats) != 0)
324b867e84eSAndrey V. Elsukov 		err(EX_OSERR, "Error retrieving stats");
325b867e84eSAndrey V. Elsukov 
32656707beeSMark Johnston 	if (g_co.use_set != 0 || set != 0)
327c5e85276SAndrey V. Elsukov 		printf("set %u ", set);
328c5e85276SAndrey V. Elsukov 	printf("nptv6 %s\n", name);
329c5e85276SAndrey V. Elsukov 	printf("\t%ju packets translated (internal to external)\n",
330b867e84eSAndrey V. Elsukov 	    (uintmax_t)stats.in2ex);
331c5e85276SAndrey V. Elsukov 	printf("\t%ju packets translated (external to internal)\n",
332b867e84eSAndrey V. Elsukov 	    (uintmax_t)stats.ex2in);
333c5e85276SAndrey V. Elsukov 	printf("\t%ju packets dropped due to some error\n",
334b867e84eSAndrey V. Elsukov 	    (uintmax_t)stats.dropped);
335b867e84eSAndrey V. Elsukov }
336b867e84eSAndrey V. Elsukov 
33757fb3b7aSAndrey V. Elsukov /*
33857fb3b7aSAndrey V. Elsukov  * Reset NPTv6 instance statistics specified by @oh->ntlv.
33957fb3b7aSAndrey V. Elsukov  * Request: [ ipfw_obj_header ]
34057fb3b7aSAndrey V. Elsukov  */
34157fb3b7aSAndrey V. Elsukov static void
nptv6_reset_stats(const char * name,uint8_t set)34257fb3b7aSAndrey V. Elsukov nptv6_reset_stats(const char *name, uint8_t set)
34357fb3b7aSAndrey V. Elsukov {
34457fb3b7aSAndrey V. Elsukov 	ipfw_obj_header oh;
34557fb3b7aSAndrey V. Elsukov 
34657fb3b7aSAndrey V. Elsukov 	memset(&oh, 0, sizeof(oh));
34757fb3b7aSAndrey V. Elsukov 	nptv6_fill_ntlv(&oh.ntlv, name, set);
34857fb3b7aSAndrey V. Elsukov 	if (do_set3(IP_FW_NPTV6_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
34957fb3b7aSAndrey V. Elsukov 		err(EX_OSERR, "failed to reset stats for instance %s", name);
35057fb3b7aSAndrey V. Elsukov }
35157fb3b7aSAndrey V. Elsukov 
352b867e84eSAndrey V. Elsukov static int
nptv6_show_cb(ipfw_nptv6_cfg * cfg,const char * name,uint8_t set)353b867e84eSAndrey V. Elsukov nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
354b867e84eSAndrey V. Elsukov {
355b867e84eSAndrey V. Elsukov 	char abuf[INET6_ADDRSTRLEN];
356b867e84eSAndrey V. Elsukov 
357b867e84eSAndrey V. Elsukov 	if (name != NULL && strcmp(cfg->name, name) != 0)
358b867e84eSAndrey V. Elsukov 		return (ESRCH);
359b867e84eSAndrey V. Elsukov 
36056707beeSMark Johnston 	if (g_co.use_set != 0 && cfg->set != set)
361b867e84eSAndrey V. Elsukov 		return (ESRCH);
362b867e84eSAndrey V. Elsukov 
36356707beeSMark Johnston 	if (g_co.use_set != 0 || cfg->set != 0)
364b867e84eSAndrey V. Elsukov 		printf("set %u ", cfg->set);
365b867e84eSAndrey V. Elsukov 	inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf));
366b867e84eSAndrey V. Elsukov 	printf("nptv6 %s int_prefix %s ", cfg->name, abuf);
367b2b56606SAndrey V. Elsukov 	if (cfg->flags & NPTV6_DYNAMIC_PREFIX)
368b2b56606SAndrey V. Elsukov 		printf("ext_if %s ", cfg->if_name);
369b2b56606SAndrey V. Elsukov 	else {
370b867e84eSAndrey V. Elsukov 		inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
371b2b56606SAndrey V. Elsukov 		printf("ext_prefix %s ", abuf);
372b2b56606SAndrey V. Elsukov 	}
373b2b56606SAndrey V. Elsukov 	printf("prefixlen %u\n", cfg->plen);
374b867e84eSAndrey V. Elsukov 	return (0);
375b867e84eSAndrey V. Elsukov }
376b867e84eSAndrey V. Elsukov 
377b867e84eSAndrey V. Elsukov static int
nptv6_destroy_cb(ipfw_nptv6_cfg * cfg,const char * name __unused,uint8_t set)37856707beeSMark Johnston nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name __unused, uint8_t set)
379b867e84eSAndrey V. Elsukov {
380b867e84eSAndrey V. Elsukov 
38156707beeSMark Johnston 	if (g_co.use_set != 0 && cfg->set != set)
382b867e84eSAndrey V. Elsukov 		return (ESRCH);
383b867e84eSAndrey V. Elsukov 
384b867e84eSAndrey V. Elsukov 	nptv6_destroy(cfg->name, cfg->set);
385b867e84eSAndrey V. Elsukov 	return (0);
386b867e84eSAndrey V. Elsukov }
387b867e84eSAndrey V. Elsukov 
388b867e84eSAndrey V. Elsukov 
389b867e84eSAndrey V. Elsukov /*
390b867e84eSAndrey V. Elsukov  * Compare NPTv6 instances names.
391b867e84eSAndrey V. Elsukov  * Honor number comparison.
392b867e84eSAndrey V. Elsukov  */
393b867e84eSAndrey V. Elsukov static int
nptv6name_cmp(const void * a,const void * b)394b867e84eSAndrey V. Elsukov nptv6name_cmp(const void *a, const void *b)
395b867e84eSAndrey V. Elsukov {
39656707beeSMark Johnston 	const ipfw_nptv6_cfg *ca, *cb;
397b867e84eSAndrey V. Elsukov 
39856707beeSMark Johnston 	ca = (const ipfw_nptv6_cfg *)a;
39956707beeSMark Johnston 	cb = (const ipfw_nptv6_cfg *)b;
400b867e84eSAndrey V. Elsukov 
401b867e84eSAndrey V. Elsukov 	if (ca->set > cb->set)
402b867e84eSAndrey V. Elsukov 		return (1);
403b867e84eSAndrey V. Elsukov 	else if (ca->set < cb->set)
404b867e84eSAndrey V. Elsukov 		return (-1);
405b867e84eSAndrey V. Elsukov 	return (stringnum_cmp(ca->name, cb->name));
406b867e84eSAndrey V. Elsukov }
407b867e84eSAndrey V. Elsukov 
408b867e84eSAndrey V. Elsukov /*
409b867e84eSAndrey V. Elsukov  * Retrieves NPTv6 instance list from kernel,
410b867e84eSAndrey V. Elsukov  * Request: [ ipfw_obj_lheader ]
411b867e84eSAndrey V. Elsukov  * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ]
412b867e84eSAndrey V. Elsukov  */
413b867e84eSAndrey V. Elsukov static int
nptv6_foreach(nptv6_cb_t * f,const char * name,uint8_t set,int sort)414b867e84eSAndrey V. Elsukov nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort)
415b867e84eSAndrey V. Elsukov {
416b867e84eSAndrey V. Elsukov 	ipfw_obj_lheader *olh;
417b867e84eSAndrey V. Elsukov 	ipfw_nptv6_cfg *cfg;
418b867e84eSAndrey V. Elsukov 	size_t sz;
41956707beeSMark Johnston 	uint32_t i;
420b867e84eSAndrey V. Elsukov 
421b867e84eSAndrey V. Elsukov 	/* Start with reasonable default */
422b867e84eSAndrey V. Elsukov 	sz = sizeof(*olh) + 16 * sizeof(*cfg);
423b867e84eSAndrey V. Elsukov 	for (;;) {
424b867e84eSAndrey V. Elsukov 		if ((olh = calloc(1, sz)) == NULL)
425b867e84eSAndrey V. Elsukov 			return (ENOMEM);
426b867e84eSAndrey V. Elsukov 
427b867e84eSAndrey V. Elsukov 		olh->size = sz;
428b867e84eSAndrey V. Elsukov 		if (do_get3(IP_FW_NPTV6_LIST, &olh->opheader, &sz) != 0) {
429b867e84eSAndrey V. Elsukov 			sz = olh->size;
430b867e84eSAndrey V. Elsukov 			free(olh);
431b867e84eSAndrey V. Elsukov 			if (errno != ENOMEM)
432b867e84eSAndrey V. Elsukov 				return (errno);
433b867e84eSAndrey V. Elsukov 			continue;
434b867e84eSAndrey V. Elsukov 		}
435b867e84eSAndrey V. Elsukov 
436b867e84eSAndrey V. Elsukov 		if (sort != 0)
437b867e84eSAndrey V. Elsukov 			qsort(olh + 1, olh->count, olh->objsize, nptv6name_cmp);
438b867e84eSAndrey V. Elsukov 
439b867e84eSAndrey V. Elsukov 		cfg = (ipfw_nptv6_cfg *)(olh + 1);
440b867e84eSAndrey V. Elsukov 		for (i = 0; i < olh->count; i++) {
441*f5e73306SJohn Baldwin 			(void)f(cfg, name, set);
442b867e84eSAndrey V. Elsukov 			cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize);
443b867e84eSAndrey V. Elsukov 		}
444b867e84eSAndrey V. Elsukov 		free(olh);
445b867e84eSAndrey V. Elsukov 		break;
446b867e84eSAndrey V. Elsukov 	}
447b867e84eSAndrey V. Elsukov 	return (0);
448b867e84eSAndrey V. Elsukov }
449b867e84eSAndrey V. Elsukov 
450