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