Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sbin/ipfw/nat64clat.c
39475 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2019 Yandex LLC
5
* Copyright (c) 2019 Andrey V. Elsukov <[email protected]>
6
* Copyright (c) 2019 Boris N. Lytochkin <[email protected]>
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
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
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
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
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
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
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
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
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
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
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
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
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
538