1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2019 Yandex LLC
5 * Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
6 * Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
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/types.h>
31 #include <sys/socket.h>
32
33 #include "ipfw2.h"
34
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <inttypes.h>
39 #include <netdb.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <sysexits.h>
44
45 #include <net/if.h>
46 #include <netinet/in.h>
47 #include <netinet/ip_fw.h>
48 #include <netinet6/ip_fw_nat64.h>
49 #include <arpa/inet.h>
50
51 typedef int (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name,
52 uint8_t set);
53 static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set,
54 int sort);
55
56 static void nat64clat_create(const char *name, uint8_t set, int ac, char **av);
57 static void nat64clat_config(const char *name, uint8_t set, int ac, char **av);
58 static void nat64clat_destroy(const char *name, uint8_t set);
59 static void nat64clat_stats(const char *name, uint8_t set);
60 static void nat64clat_reset_stats(const char *name, uint8_t set);
61 static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name,
62 uint8_t set);
63 static int nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name,
64 uint8_t set);
65
66 static struct _s_x nat64cmds[] = {
67 { "create", TOK_CREATE },
68 { "config", TOK_CONFIG },
69 { "destroy", TOK_DESTROY },
70 { "list", TOK_LIST },
71 { "show", TOK_LIST },
72 { "stats", TOK_STATS },
73 { NULL, 0 }
74 };
75
76 static struct _s_x nat64statscmds[] = {
77 { "reset", TOK_RESET },
78 { NULL, 0 }
79 };
80
81 /*
82 * This one handles all nat64clat-related commands
83 * ipfw [set N] nat64clat NAME {create | config} ...
84 * ipfw [set N] nat64clat NAME stats [reset]
85 * ipfw [set N] nat64clat {NAME | all} destroy
86 * ipfw [set N] nat64clat {NAME | all} {list | show}
87 */
88 #define nat64clat_check_name table_check_name
89 void
ipfw_nat64clat_handler(int ac,char * av[])90 ipfw_nat64clat_handler(int ac, char *av[])
91 {
92 const char *name;
93 int tcmd;
94 uint8_t set;
95
96 if (g_co.use_set != 0)
97 set = g_co.use_set - 1;
98 else
99 set = 0;
100 ac--; av++;
101
102 NEED1("nat64clat needs instance name");
103 name = *av;
104 if (nat64clat_check_name(name) != 0) {
105 if (strcmp(name, "all") == 0)
106 name = NULL;
107 else
108 errx(EX_USAGE, "nat64clat instance name %s is invalid",
109 name);
110 }
111 ac--; av++;
112 NEED1("nat64clat needs command");
113
114 tcmd = get_token(nat64cmds, *av, "nat64clat command");
115 if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
116 errx(EX_USAGE, "nat64clat instance name required");
117 switch (tcmd) {
118 case TOK_CREATE:
119 ac--; av++;
120 nat64clat_create(name, set, ac, av);
121 break;
122 case TOK_CONFIG:
123 ac--; av++;
124 nat64clat_config(name, set, ac, av);
125 break;
126 case TOK_LIST:
127 nat64clat_foreach(nat64clat_show_cb, name, set, 1);
128 break;
129 case TOK_DESTROY:
130 if (name == NULL)
131 nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0);
132 else
133 nat64clat_destroy(name, set);
134 break;
135 case TOK_STATS:
136 ac--; av++;
137 if (ac == 0) {
138 nat64clat_stats(name, set);
139 break;
140 }
141 tcmd = get_token(nat64statscmds, *av, "stats command");
142 if (tcmd == TOK_RESET)
143 nat64clat_reset_stats(name, set);
144 }
145 }
146
147
148 static void
nat64clat_fill_ntlv(ipfw_obj_ntlv * ntlv,const char * name,uint8_t set)149 nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
150 {
151
152 ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
153 ntlv->head.length = sizeof(ipfw_obj_ntlv);
154 ntlv->idx = 1;
155 ntlv->set = set;
156 strlcpy(ntlv->name, name, sizeof(ntlv->name));
157 }
158
159 static struct _s_x nat64newcmds[] = {
160 { "plat_prefix", TOK_PLAT_PREFIX },
161 { "clat_prefix", TOK_CLAT_PREFIX },
162 { "log", TOK_LOG },
163 { "-log", TOK_LOGOFF },
164 { "allow_private", TOK_PRIVATE },
165 { "-allow_private", TOK_PRIVATEOFF },
166 { NULL, 0 }
167 };
168
169 /*
170 * Creates new nat64clat instance
171 * ipfw nat64clat <NAME> create clat_prefix <prefix> plat_prefix <prefix>
172 * Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
173 */
174 #define NAT64CLAT_HAS_CLAT_PREFIX 0x01
175 #define NAT64CLAT_HAS_PLAT_PREFIX 0x02
176 static void
nat64clat_create(const char * name,uint8_t set,int ac,char * av[])177 nat64clat_create(const char *name, uint8_t set, int ac, char *av[])
178 {
179 char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)];
180 ipfw_nat64clat_cfg *cfg;
181 ipfw_obj_lheader *olh;
182 int tcmd, flags;
183 char *p;
184 struct in6_addr prefix;
185 uint8_t plen;
186
187 memset(buf, 0, sizeof(buf));
188 olh = (ipfw_obj_lheader *)buf;
189 cfg = (ipfw_nat64clat_cfg *)(olh + 1);
190
191 /* Some reasonable defaults */
192 inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix);
193 cfg->plat_plen = 96;
194 cfg->set = set;
195 flags = NAT64CLAT_HAS_PLAT_PREFIX;
196 while (ac > 0) {
197 tcmd = get_token(nat64newcmds, *av, "option");
198 ac--; av++;
199
200 switch (tcmd) {
201 case TOK_PLAT_PREFIX:
202 case TOK_CLAT_PREFIX:
203 if (tcmd == TOK_PLAT_PREFIX) {
204 NEED1("IPv6 plat_prefix required");
205 } else {
206 NEED1("IPv6 clat_prefix required");
207 }
208
209 if ((p = strchr(*av, '/')) != NULL)
210 *p++ = '\0';
211 if (inet_pton(AF_INET6, *av, &prefix) != 1)
212 errx(EX_USAGE,
213 "Bad prefix: %s", *av);
214 plen = strtol(p, NULL, 10);
215 if (ipfw_check_nat64prefix(&prefix, plen) != 0)
216 errx(EX_USAGE,
217 "Bad prefix length: %s", p);
218 if (tcmd == TOK_PLAT_PREFIX) {
219 flags |= NAT64CLAT_HAS_PLAT_PREFIX;
220 cfg->plat_prefix = prefix;
221 cfg->plat_plen = plen;
222 } else {
223 flags |= NAT64CLAT_HAS_CLAT_PREFIX;
224 cfg->clat_prefix = prefix;
225 cfg->clat_plen = plen;
226 }
227 ac--; av++;
228 break;
229 case TOK_LOG:
230 cfg->flags |= NAT64_LOG;
231 break;
232 case TOK_LOGOFF:
233 cfg->flags &= ~NAT64_LOG;
234 break;
235 case TOK_PRIVATE:
236 cfg->flags |= NAT64_ALLOW_PRIVATE;
237 break;
238 case TOK_PRIVATEOFF:
239 cfg->flags &= ~NAT64_ALLOW_PRIVATE;
240 break;
241 }
242 }
243
244 /* Check validness */
245 if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX)
246 errx(EX_USAGE, "plat_prefix required");
247 if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX)
248 errx(EX_USAGE, "clat_prefix required");
249
250 olh->count = 1;
251 olh->objsize = sizeof(*cfg);
252 olh->size = sizeof(buf);
253 strlcpy(cfg->name, name, sizeof(cfg->name));
254 if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0)
255 err(EX_OSERR, "nat64clat instance creation failed");
256 }
257
258 /*
259 * Configures existing nat64clat instance
260 * ipfw nat64clat <NAME> config <options>
261 * Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
262 */
263 static void
nat64clat_config(const char * name,uint8_t set,int ac,char ** av)264 nat64clat_config(const char *name, uint8_t set, int ac, char **av)
265 {
266 char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)];
267 ipfw_nat64clat_cfg *cfg;
268 ipfw_obj_header *oh;
269 char *opt;
270 char *p;
271 size_t sz;
272 int tcmd;
273 struct in6_addr prefix;
274 uint8_t plen;
275
276 if (ac == 0)
277 errx(EX_USAGE, "config options required");
278 memset(&buf, 0, sizeof(buf));
279 oh = (ipfw_obj_header *)buf;
280 cfg = (ipfw_nat64clat_cfg *)(oh + 1);
281 sz = sizeof(buf);
282
283 nat64clat_fill_ntlv(&oh->ntlv, name, set);
284 if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0)
285 err(EX_OSERR, "failed to get config for instance %s", name);
286
287 while (ac > 0) {
288 tcmd = get_token(nat64newcmds, *av, "option");
289 opt = *av;
290 ac--; av++;
291
292 switch (tcmd) {
293 case TOK_PLAT_PREFIX:
294 case TOK_CLAT_PREFIX:
295 if (tcmd == TOK_PLAT_PREFIX) {
296 NEED1("IPv6 plat_prefix required");
297 } else {
298 NEED1("IPv6 clat_prefix required");
299 }
300
301 if ((p = strchr(*av, '/')) != NULL)
302 *p++ = '\0';
303 else
304 errx(EX_USAGE,
305 "Prefix length required: %s", *av);
306 if (inet_pton(AF_INET6, *av, &prefix) != 1)
307 errx(EX_USAGE,
308 "Bad prefix: %s", *av);
309 plen = strtol(p, NULL, 10);
310 if (ipfw_check_nat64prefix(&prefix, plen) != 0)
311 errx(EX_USAGE,
312 "Bad prefix length: %s", p);
313 if (tcmd == TOK_PLAT_PREFIX) {
314 cfg->plat_prefix = prefix;
315 cfg->plat_plen = plen;
316 } else {
317 cfg->clat_prefix = prefix;
318 cfg->clat_plen = plen;
319 }
320 ac--; av++;
321 break;
322 case TOK_LOG:
323 cfg->flags |= NAT64_LOG;
324 break;
325 case TOK_LOGOFF:
326 cfg->flags &= ~NAT64_LOG;
327 break;
328 case TOK_PRIVATE:
329 cfg->flags |= NAT64_ALLOW_PRIVATE;
330 break;
331 case TOK_PRIVATEOFF:
332 cfg->flags &= ~NAT64_ALLOW_PRIVATE;
333 break;
334 default:
335 errx(EX_USAGE, "Can't change %s option", opt);
336 }
337 }
338
339 if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0)
340 err(EX_OSERR, "nat64clat instance configuration failed");
341 }
342
343 /*
344 * Destroys nat64clat instance.
345 * Request: [ ipfw_obj_header ]
346 */
347 static void
nat64clat_destroy(const char * name,uint8_t set)348 nat64clat_destroy(const char *name, uint8_t set)
349 {
350 ipfw_obj_header oh;
351
352 memset(&oh, 0, sizeof(oh));
353 nat64clat_fill_ntlv(&oh.ntlv, name, set);
354 if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0)
355 err(EX_OSERR, "failed to destroy nat instance %s", name);
356 }
357
358 /*
359 * Get nat64clat instance statistics.
360 * Request: [ ipfw_obj_header ]
361 * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
362 */
363 static int
nat64clat_get_stats(const char * name,uint8_t set,struct ipfw_nat64clat_stats * stats)364 nat64clat_get_stats(const char *name, uint8_t set,
365 struct ipfw_nat64clat_stats *stats)
366 {
367 ipfw_obj_header *oh;
368 ipfw_obj_ctlv *oc;
369 size_t sz;
370
371 sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
372 oh = calloc(1, sz);
373 nat64clat_fill_ntlv(&oh->ntlv, name, set);
374 if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) {
375 oc = (ipfw_obj_ctlv *)(oh + 1);
376 memcpy(stats, oc + 1, sizeof(*stats));
377 free(oh);
378 return (0);
379 }
380 free(oh);
381 return (-1);
382 }
383
384 static void
nat64clat_stats(const char * name,uint8_t set)385 nat64clat_stats(const char *name, uint8_t set)
386 {
387 struct ipfw_nat64clat_stats stats;
388
389 if (nat64clat_get_stats(name, set, &stats) != 0)
390 err(EX_OSERR, "Error retrieving stats");
391
392 if (g_co.use_set != 0 || set != 0)
393 printf("set %u ", set);
394 printf("nat64clat %s\n", name);
395
396 printf("\t%ju packets translated from IPv6 to IPv4\n",
397 (uintmax_t)stats.opcnt64);
398 printf("\t%ju packets translated from IPv4 to IPv6\n",
399 (uintmax_t)stats.opcnt46);
400 printf("\t%ju IPv6 fragments created\n",
401 (uintmax_t)stats.ofrags);
402 printf("\t%ju IPv4 fragments received\n",
403 (uintmax_t)stats.ifrags);
404 printf("\t%ju output packets dropped due to no bufs, etc.\n",
405 (uintmax_t)stats.oerrors);
406 printf("\t%ju output packets discarded due to no IPv4 route\n",
407 (uintmax_t)stats.noroute4);
408 printf("\t%ju output packets discarded due to no IPv6 route\n",
409 (uintmax_t)stats.noroute6);
410 printf("\t%ju packets discarded due to unsupported protocol\n",
411 (uintmax_t)stats.noproto);
412 printf("\t%ju packets discarded due to memory allocation problems\n",
413 (uintmax_t)stats.nomem);
414 printf("\t%ju packets discarded due to some errors\n",
415 (uintmax_t)stats.dropped);
416 }
417
418 /*
419 * Reset nat64clat instance statistics specified by @oh->ntlv.
420 * Request: [ ipfw_obj_header ]
421 */
422 static void
nat64clat_reset_stats(const char * name,uint8_t set)423 nat64clat_reset_stats(const char *name, uint8_t set)
424 {
425 ipfw_obj_header oh;
426
427 memset(&oh, 0, sizeof(oh));
428 nat64clat_fill_ntlv(&oh.ntlv, name, set);
429 if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
430 err(EX_OSERR, "failed to reset stats for instance %s", name);
431 }
432
433 static int
nat64clat_show_cb(ipfw_nat64clat_cfg * cfg,const char * name,uint8_t set)434 nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set)
435 {
436 char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN];
437
438 if (name != NULL && strcmp(cfg->name, name) != 0)
439 return (ESRCH);
440
441 if (g_co.use_set != 0 && cfg->set != set)
442 return (ESRCH);
443
444 if (g_co.use_set != 0 || cfg->set != 0)
445 printf("set %u ", cfg->set);
446
447 inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf));
448 inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf));
449 printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u",
450 cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen);
451 if (cfg->flags & NAT64_LOG)
452 printf(" log");
453 if (cfg->flags & NAT64_ALLOW_PRIVATE)
454 printf(" allow_private");
455 printf("\n");
456 return (0);
457 }
458
459 static int
nat64clat_destroy_cb(ipfw_nat64clat_cfg * cfg,const char * name __unused,uint8_t set)460 nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name __unused,
461 uint8_t set)
462 {
463
464 if (g_co.use_set != 0 && cfg->set != set)
465 return (ESRCH);
466
467 nat64clat_destroy(cfg->name, cfg->set);
468 return (0);
469 }
470
471
472 /*
473 * Compare nat64clat instances names.
474 * Honor number comparison.
475 */
476 static int
nat64name_cmp(const void * a,const void * b)477 nat64name_cmp(const void *a, const void *b)
478 {
479 const ipfw_nat64clat_cfg *ca, *cb;
480
481 ca = (const ipfw_nat64clat_cfg *)a;
482 cb = (const ipfw_nat64clat_cfg *)b;
483
484 if (ca->set > cb->set)
485 return (1);
486 else if (ca->set < cb->set)
487 return (-1);
488 return (stringnum_cmp(ca->name, cb->name));
489 }
490
491 /*
492 * Retrieves nat64clat instance list from kernel,
493 * optionally sorts it and calls requested function for each instance.
494 *
495 * Request: [ ipfw_obj_lheader ]
496 * Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
497 */
498 static int
nat64clat_foreach(nat64clat_cb_t * f,const char * name,uint8_t set,int sort)499 nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort)
500 {
501 ipfw_obj_lheader *olh;
502 ipfw_nat64clat_cfg *cfg;
503 size_t sz;
504 uint32_t i;
505
506 /* Start with reasonable default */
507 sz = sizeof(*olh) + 16 * sizeof(*cfg);
508 for (;;) {
509 if ((olh = calloc(1, sz)) == NULL)
510 return (ENOMEM);
511
512 olh->size = sz;
513 if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) {
514 sz = olh->size;
515 free(olh);
516 if (errno != ENOMEM)
517 return (errno);
518 continue;
519 }
520
521 if (sort != 0)
522 qsort(olh + 1, olh->count, olh->objsize,
523 nat64name_cmp);
524
525 cfg = (ipfw_nat64clat_cfg *)(olh + 1);
526 for (i = 0; i < olh->count; i++) {
527 (void)f(cfg, name, set); /* Ignore errors for now */
528 cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg +
529 olh->objsize);
530 }
531 free(olh);
532 break;
533 }
534 return (0);
535 }
536
537