2 * Copyright (c) 2016 Yandex LLC
3 * Copyright (c) 2016 Andrey V. Elsukov <ae@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
32 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <netinet/ip_fw.h>
48 #include <netinet6/ip_fw_nptv6.h>
49 #include <arpa/inet.h>
52 typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set);
53 static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set,
56 static void nptv6_create(const char *name, uint8_t set, int ac, char **av);
57 static void nptv6_destroy(const char *name, uint8_t set);
58 static void nptv6_stats(const char *name, uint8_t set);
59 static void nptv6_reset_stats(const char *name, uint8_t set);
60 static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
61 static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
63 static struct _s_x nptv6cmds[] = {
64 { "create", TOK_CREATE },
65 { "destroy", TOK_DESTROY },
68 { "stats", TOK_STATS },
72 static struct _s_x nptv6statscmds[] = {
73 { "reset", TOK_RESET },
78 * This one handles all NPTv6-related commands
79 * ipfw [set N] nptv6 NAME {create | config} ...
80 * ipfw [set N] nptv6 NAME stats [reset]
81 * ipfw [set N] nptv6 {NAME | all} destroy
82 * ipfw [set N] nptv6 {NAME | all} {list | show}
84 #define nptv6_check_name table_check_name
86 ipfw_nptv6_handler(int ac, char *av[])
98 NEED1("nptv6 needs instance name");
100 if (nptv6_check_name(name) != 0) {
101 if (strcmp(name, "all") == 0) {
104 errx(EX_USAGE, "nptv6 instance name %s is invalid",
108 NEED1("nptv6 needs command");
110 tcmd = get_token(nptv6cmds, *av, "nptv6 command");
111 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
112 errx(EX_USAGE, "nptv6 instance name required");
116 nptv6_create(name, set, ac, av);
119 nptv6_foreach(nptv6_show_cb, name, set, 1);
123 nptv6_foreach(nptv6_destroy_cb, NULL, set, 0);
125 nptv6_destroy(name, set);
130 nptv6_stats(name, set);
133 tcmd = get_token(nptv6statscmds, *av, "stats command");
134 if (tcmd == TOK_RESET)
135 nptv6_reset_stats(name, set);
141 nptv6_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
144 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
145 ntlv->head.length = sizeof(ipfw_obj_ntlv);
148 strlcpy(ntlv->name, name, sizeof(ntlv->name));
151 static struct _s_x nptv6newcmds[] = {
152 { "int_prefix", TOK_INTPREFIX },
153 { "ext_prefix", TOK_EXTPREFIX },
154 { "prefixlen", TOK_PREFIXLEN },
160 nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
167 if ((l = strchr(p, '/')) != NULL)
169 if (inet_pton(AF_INET6, p, prefix) != 1)
170 errx(EX_USAGE, "Bad prefix: %s", p);
172 *len = (int)strtol(l, &l, 10);
173 if (*l != '\0' || *len <= 0 || *len > 64)
174 errx(EX_USAGE, "Bad prefix length: %s", arg);
180 * Creates new nptv6 instance
181 * ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix>
182 * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
184 #define NPTV6_HAS_INTPREFIX 0x01
185 #define NPTV6_HAS_EXTPREFIX 0x02
186 #define NPTV6_HAS_PREFIXLEN 0x04
188 nptv6_create(const char *name, uint8_t set, int ac, char *av[])
190 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)];
191 struct in6_addr mask;
193 ipfw_obj_lheader *olh;
194 int tcmd, flags, plen;
198 memset(buf, 0, sizeof(buf));
199 olh = (ipfw_obj_lheader *)buf;
200 cfg = (ipfw_nptv6_cfg *)(olh + 1);
204 tcmd = get_token(nptv6newcmds, *av, "option");
209 NEED1("IPv6 prefix required");
210 nptv6_parse_prefix(*av, &cfg->internal, &plen);
211 flags |= NPTV6_HAS_INTPREFIX;
217 NEED1("IPv6 prefix required");
218 nptv6_parse_prefix(*av, &cfg->external, &plen);
219 flags |= NPTV6_HAS_EXTPREFIX;
225 NEED1("IPv6 prefix length required");
226 plen = strtol(*av, &p, 10);
228 if (*p != '\0' || plen < 8 || plen > 64)
229 errx(EX_USAGE, "wrong prefix length: %s", *av);
230 /* RFC 6296 Sec. 3.1 */
231 if (cfg->plen > 0 && cfg->plen != plen) {
232 warnx("Prefix length mismatch (%d vs %d). "
233 "It was extended up to %d",
234 cfg->plen, plen, MAX(plen, cfg->plen));
235 plen = MAX(plen, cfg->plen);
238 flags |= NPTV6_HAS_PREFIXLEN;
244 /* Check validness */
245 if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
246 errx(EX_USAGE, "int_prefix required");
247 if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX)
248 errx(EX_USAGE, "ext_prefix required");
249 if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN)
250 errx(EX_USAGE, "prefixlen required");
252 n2mask(&mask, cfg->plen);
253 APPLY_MASK(&cfg->internal, &mask);
254 APPLY_MASK(&cfg->external, &mask);
257 olh->objsize = sizeof(*cfg);
258 olh->size = sizeof(buf);
259 strlcpy(cfg->name, name, sizeof(cfg->name));
260 if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0)
261 err(EX_OSERR, "nptv6 instance creation failed");
265 * Destroys NPTv6 instance.
266 * Request: [ ipfw_obj_header ]
269 nptv6_destroy(const char *name, uint8_t set)
273 memset(&oh, 0, sizeof(oh));
274 nptv6_fill_ntlv(&oh.ntlv, name, set);
275 if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0)
276 err(EX_OSERR, "failed to destroy nat instance %s", name);
280 * Get NPTv6 instance statistics.
281 * Request: [ ipfw_obj_header ]
282 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
285 nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats)
291 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
293 nptv6_fill_ntlv(&oh->ntlv, name, set);
294 if (do_get3(IP_FW_NPTV6_STATS, &oh->opheader, &sz) == 0) {
295 oc = (ipfw_obj_ctlv *)(oh + 1);
296 memcpy(stats, oc + 1, sizeof(*stats));
305 nptv6_stats(const char *name, uint8_t set)
307 struct ipfw_nptv6_stats stats;
309 if (nptv6_get_stats(name, set, &stats) != 0)
310 err(EX_OSERR, "Error retrieving stats");
312 if (co.use_set != 0 || set != 0)
313 printf("set %u ", set);
314 printf("nptv6 %s\n", name);
315 printf("\t%ju packets translated (internal to external)\n",
316 (uintmax_t)stats.in2ex);
317 printf("\t%ju packets translated (external to internal)\n",
318 (uintmax_t)stats.ex2in);
319 printf("\t%ju packets dropped due to some error\n",
320 (uintmax_t)stats.dropped);
324 * Reset NPTv6 instance statistics specified by @oh->ntlv.
325 * Request: [ ipfw_obj_header ]
328 nptv6_reset_stats(const char *name, uint8_t set)
332 memset(&oh, 0, sizeof(oh));
333 nptv6_fill_ntlv(&oh.ntlv, name, set);
334 if (do_set3(IP_FW_NPTV6_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
335 err(EX_OSERR, "failed to reset stats for instance %s", name);
339 nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
341 char abuf[INET6_ADDRSTRLEN];
343 if (name != NULL && strcmp(cfg->name, name) != 0)
346 if (co.use_set != 0 && cfg->set != set)
349 if (co.use_set != 0 || cfg->set != 0)
350 printf("set %u ", cfg->set);
351 inet_ntop(AF_INET6, &cfg->internal, abuf, sizeof(abuf));
352 printf("nptv6 %s int_prefix %s ", cfg->name, abuf);
353 inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
354 printf("ext_prefix %s prefixlen %u\n", abuf, cfg->plen);
359 nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
362 if (co.use_set != 0 && cfg->set != set)
365 nptv6_destroy(cfg->name, cfg->set);
371 * Compare NPTv6 instances names.
372 * Honor number comparison.
375 nptv6name_cmp(const void *a, const void *b)
377 ipfw_nptv6_cfg *ca, *cb;
379 ca = (ipfw_nptv6_cfg *)a;
380 cb = (ipfw_nptv6_cfg *)b;
382 if (ca->set > cb->set)
384 else if (ca->set < cb->set)
386 return (stringnum_cmp(ca->name, cb->name));
390 * Retrieves NPTv6 instance list from kernel,
391 * Request: [ ipfw_obj_lheader ]
392 * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ]
395 nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort)
397 ipfw_obj_lheader *olh;
402 /* Start with reasonable default */
403 sz = sizeof(*olh) + 16 * sizeof(*cfg);
405 if ((olh = calloc(1, sz)) == NULL)
409 if (do_get3(IP_FW_NPTV6_LIST, &olh->opheader, &sz) != 0) {
418 qsort(olh + 1, olh->count, olh->objsize, nptv6name_cmp);
420 cfg = (ipfw_nptv6_cfg *)(olh + 1);
421 for (i = 0; i < olh->count; i++) {
422 error = f(cfg, name, set);
423 cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize);