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