Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netpfil/ipfw/nat64/nat64clat_control.c
39536 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/param.h>
31
#include <sys/systm.h>
32
#include <sys/counter.h>
33
#include <sys/errno.h>
34
#include <sys/kernel.h>
35
#include <sys/lock.h>
36
#include <sys/malloc.h>
37
#include <sys/mbuf.h>
38
#include <sys/module.h>
39
#include <sys/rmlock.h>
40
#include <sys/rwlock.h>
41
#include <sys/socket.h>
42
#include <sys/sockopt.h>
43
#include <sys/syslog.h>
44
#include <sys/sysctl.h>
45
46
#include <net/if.h>
47
#include <net/if_var.h>
48
#include <net/route.h>
49
#include <net/vnet.h>
50
51
#include <netinet/in.h>
52
#include <netinet/ip_var.h>
53
#include <netinet/ip_fw.h>
54
#include <netinet6/in6_var.h>
55
#include <netinet6/ip6_var.h>
56
#include <netinet6/ip_fw_nat64.h>
57
58
#include <netpfil/ipfw/ip_fw_private.h>
59
60
#include "nat64clat.h"
61
62
VNET_DEFINE(uint32_t, nat64clat_eid) = 0;
63
64
static struct nat64clat_cfg *nat64clat_alloc_config(const char *name,
65
uint8_t set);
66
static void nat64clat_free_config(struct nat64clat_cfg *cfg);
67
static struct nat64clat_cfg *nat64clat_find(struct namedobj_instance *ni,
68
const char *name, uint8_t set);
69
70
static struct nat64clat_cfg *
71
nat64clat_alloc_config(const char *name, uint8_t set)
72
{
73
struct nat64clat_cfg *cfg;
74
75
cfg = malloc(sizeof(struct nat64clat_cfg), M_IPFW, M_WAITOK | M_ZERO);
76
COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
77
cfg->no.name = cfg->name;
78
cfg->no.etlv = IPFW_TLV_NAT64CLAT_NAME;
79
cfg->no.set = set;
80
strlcpy(cfg->name, name, sizeof(cfg->name));
81
return (cfg);
82
}
83
84
static void
85
nat64clat_free_config(struct nat64clat_cfg *cfg)
86
{
87
88
COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
89
free(cfg, M_IPFW);
90
}
91
92
static void
93
nat64clat_export_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
94
ipfw_nat64clat_cfg *uc)
95
{
96
uc->plat_prefix = cfg->base.plat_prefix;
97
uc->plat_plen = cfg->base.plat_plen;
98
uc->clat_prefix = cfg->base.clat_prefix;
99
uc->clat_plen = cfg->base.clat_plen;
100
uc->flags = cfg->base.flags & NAT64CLAT_FLAGSMASK;
101
uc->set = cfg->no.set;
102
strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
103
}
104
105
struct nat64clat_dump_arg {
106
struct ip_fw_chain *ch;
107
struct sockopt_data *sd;
108
};
109
110
static int
111
export_config_cb(struct namedobj_instance *ni, struct named_object *no,
112
void *arg)
113
{
114
struct nat64clat_dump_arg *da = (struct nat64clat_dump_arg *)arg;
115
ipfw_nat64clat_cfg *uc;
116
117
uc = (ipfw_nat64clat_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
118
nat64clat_export_config(da->ch, (struct nat64clat_cfg *)no, uc);
119
return (0);
120
}
121
122
static struct nat64clat_cfg *
123
nat64clat_find(struct namedobj_instance *ni, const char *name, uint8_t set)
124
{
125
struct nat64clat_cfg *cfg;
126
127
cfg = (struct nat64clat_cfg *)ipfw_objhash_lookup_name_type(ni, set,
128
IPFW_TLV_NAT64CLAT_NAME, name);
129
130
return (cfg);
131
}
132
133
/*
134
* Creates new consumer-side nat64 translator instance.
135
* Data layout (v0)(current):
136
* Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
137
*
138
* Returns 0 on success
139
*/
140
static int
141
nat64clat_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
142
struct sockopt_data *sd)
143
{
144
ipfw_obj_lheader *olh;
145
ipfw_nat64clat_cfg *uc;
146
struct namedobj_instance *ni;
147
struct nat64clat_cfg *cfg;
148
149
if (sd->valsize != sizeof(*olh) + sizeof(*uc))
150
return (EINVAL);
151
152
olh = (ipfw_obj_lheader *)sd->kbuf;
153
uc = (ipfw_nat64clat_cfg *)(olh + 1);
154
155
if (ipfw_check_object_name_generic(uc->name) != 0)
156
return (EINVAL);
157
158
if (uc->set >= IPFW_MAX_SETS ||
159
nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0 ||
160
nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0)
161
return (EINVAL);
162
163
ni = CHAIN_TO_SRV(ch);
164
165
IPFW_UH_RLOCK(ch);
166
if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
167
IPFW_UH_RUNLOCK(ch);
168
return (EEXIST);
169
}
170
IPFW_UH_RUNLOCK(ch);
171
172
cfg = nat64clat_alloc_config(uc->name, uc->set);
173
cfg->base.plat_prefix = uc->plat_prefix;
174
cfg->base.plat_plen = uc->plat_plen;
175
cfg->base.clat_prefix = uc->clat_prefix;
176
cfg->base.clat_plen = uc->clat_plen;
177
cfg->base.flags = (uc->flags & NAT64CLAT_FLAGSMASK) |
178
NAT64_CLATPFX | NAT64_PLATPFX;
179
if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))
180
cfg->base.flags |= NAT64_WKPFX;
181
182
IPFW_UH_WLOCK(ch);
183
184
if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
185
IPFW_UH_WUNLOCK(ch);
186
nat64clat_free_config(cfg);
187
return (EEXIST);
188
}
189
190
if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) {
191
IPFW_UH_WUNLOCK(ch);
192
nat64clat_free_config(cfg);
193
return (ENOSPC);
194
}
195
ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
196
/* Okay, let's link data */
197
SRV_OBJECT(ch, cfg->no.kidx) = cfg;
198
IPFW_UH_WUNLOCK(ch);
199
200
return (0);
201
}
202
203
/*
204
* Change existing nat64clat instance configuration.
205
* Data layout (v0)(current):
206
* Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
207
* Reply: [ ipfw_obj_header ipfw_nat64clat_cfg ]
208
*
209
* Returns 0 on success
210
*/
211
static int
212
nat64clat_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
213
struct sockopt_data *sd)
214
{
215
ipfw_obj_header *oh;
216
ipfw_nat64clat_cfg *uc;
217
struct nat64clat_cfg *cfg;
218
struct namedobj_instance *ni;
219
uint32_t flags;
220
221
if (sd->valsize != sizeof(*oh) + sizeof(*uc))
222
return (EINVAL);
223
224
oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
225
sizeof(*oh) + sizeof(*uc));
226
uc = (ipfw_nat64clat_cfg *)(oh + 1);
227
228
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
229
oh->ntlv.set >= IPFW_MAX_SETS)
230
return (EINVAL);
231
232
ni = CHAIN_TO_SRV(ch);
233
if (sd->sopt->sopt_dir == SOPT_GET) {
234
IPFW_UH_RLOCK(ch);
235
cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
236
if (cfg == NULL) {
237
IPFW_UH_RUNLOCK(ch);
238
return (ENOENT);
239
}
240
nat64clat_export_config(ch, cfg, uc);
241
IPFW_UH_RUNLOCK(ch);
242
return (0);
243
}
244
245
IPFW_UH_WLOCK(ch);
246
cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
247
if (cfg == NULL) {
248
IPFW_UH_WUNLOCK(ch);
249
return (ENOENT);
250
}
251
252
/*
253
* For now allow to change only following values:
254
* plat_prefix, plat_plen, clat_prefix, clat_plen, flags.
255
*/
256
flags = 0;
257
if (uc->plat_plen != cfg->base.plat_plen ||
258
!IN6_ARE_ADDR_EQUAL(&uc->plat_prefix, &cfg->base.plat_prefix)) {
259
if (nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0) {
260
IPFW_UH_WUNLOCK(ch);
261
return (EINVAL);
262
}
263
flags |= NAT64_PLATPFX;
264
}
265
266
if (uc->clat_plen != cfg->base.clat_plen ||
267
!IN6_ARE_ADDR_EQUAL(&uc->clat_prefix, &cfg->base.clat_prefix)) {
268
if (nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) {
269
IPFW_UH_WUNLOCK(ch);
270
return (EINVAL);
271
}
272
flags |= NAT64_CLATPFX;
273
}
274
275
if (flags != 0) {
276
IPFW_WLOCK(ch);
277
if (flags & NAT64_PLATPFX) {
278
cfg->base.plat_prefix = uc->plat_prefix;
279
cfg->base.plat_plen = uc->plat_plen;
280
}
281
if (flags & NAT64_CLATPFX) {
282
cfg->base.clat_prefix = uc->clat_prefix;
283
cfg->base.clat_plen = uc->clat_plen;
284
}
285
IPFW_WUNLOCK(ch);
286
}
287
288
cfg->base.flags &= ~NAT64CLAT_FLAGSMASK;
289
cfg->base.flags |= uc->flags & NAT64CLAT_FLAGSMASK;
290
291
IPFW_UH_WUNLOCK(ch);
292
return (0);
293
}
294
295
static void
296
nat64clat_detach_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg)
297
{
298
299
IPFW_UH_WLOCK_ASSERT(ch);
300
301
ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
302
ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
303
}
304
305
/*
306
* Destroys nat64 instance.
307
* Data layout (v0)(current):
308
* Request: [ ipfw_obj_header ]
309
*
310
* Returns 0 on success
311
*/
312
static int
313
nat64clat_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
314
struct sockopt_data *sd)
315
{
316
ipfw_obj_header *oh;
317
struct nat64clat_cfg *cfg;
318
319
if (sd->valsize != sizeof(*oh))
320
return (EINVAL);
321
322
oh = (ipfw_obj_header *)sd->kbuf;
323
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
324
return (EINVAL);
325
326
IPFW_UH_WLOCK(ch);
327
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
328
if (cfg == NULL) {
329
IPFW_UH_WUNLOCK(ch);
330
return (ENOENT);
331
}
332
if (cfg->no.refcnt > 0) {
333
IPFW_UH_WUNLOCK(ch);
334
return (EBUSY);
335
}
336
337
ipfw_reset_eaction_instance(ch, V_nat64clat_eid, cfg->no.kidx);
338
SRV_OBJECT(ch, cfg->no.kidx) = NULL;
339
nat64clat_detach_config(ch, cfg);
340
IPFW_UH_WUNLOCK(ch);
341
342
nat64clat_free_config(cfg);
343
return (0);
344
}
345
346
/*
347
* Lists all nat64clat instances currently available in kernel.
348
* Data layout (v0)(current):
349
* Request: [ ipfw_obj_lheader ]
350
* Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
351
*
352
* Returns 0 on success
353
*/
354
static int
355
nat64clat_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
356
struct sockopt_data *sd)
357
{
358
ipfw_obj_lheader *olh;
359
struct nat64clat_dump_arg da;
360
361
/* Check minimum header size */
362
if (sd->valsize < sizeof(ipfw_obj_lheader))
363
return (EINVAL);
364
365
olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
366
367
IPFW_UH_RLOCK(ch);
368
olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
369
IPFW_TLV_NAT64CLAT_NAME);
370
olh->objsize = sizeof(ipfw_nat64clat_cfg);
371
olh->size = sizeof(*olh) + olh->count * olh->objsize;
372
373
if (sd->valsize < olh->size) {
374
IPFW_UH_RUNLOCK(ch);
375
return (ENOMEM);
376
}
377
memset(&da, 0, sizeof(da));
378
da.ch = ch;
379
da.sd = sd;
380
ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
381
&da, IPFW_TLV_NAT64CLAT_NAME);
382
IPFW_UH_RUNLOCK(ch);
383
384
return (0);
385
}
386
387
#define __COPY_STAT_FIELD(_cfg, _stats, _field) \
388
(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)
389
static void
390
export_stats(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
391
struct ipfw_nat64clat_stats *stats)
392
{
393
394
__COPY_STAT_FIELD(cfg, stats, opcnt64);
395
__COPY_STAT_FIELD(cfg, stats, opcnt46);
396
__COPY_STAT_FIELD(cfg, stats, ofrags);
397
__COPY_STAT_FIELD(cfg, stats, ifrags);
398
__COPY_STAT_FIELD(cfg, stats, oerrors);
399
__COPY_STAT_FIELD(cfg, stats, noroute4);
400
__COPY_STAT_FIELD(cfg, stats, noroute6);
401
__COPY_STAT_FIELD(cfg, stats, noproto);
402
__COPY_STAT_FIELD(cfg, stats, nomem);
403
__COPY_STAT_FIELD(cfg, stats, dropped);
404
}
405
406
/*
407
* Get nat64clat statistics.
408
* Data layout (v0)(current):
409
* Request: [ ipfw_obj_header ]
410
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
411
*
412
* Returns 0 on success
413
*/
414
static int
415
nat64clat_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
416
struct sockopt_data *sd)
417
{
418
struct ipfw_nat64clat_stats stats;
419
struct nat64clat_cfg *cfg;
420
ipfw_obj_header *oh;
421
ipfw_obj_ctlv *ctlv;
422
size_t sz;
423
424
sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
425
if (sd->valsize % sizeof(uint64_t))
426
return (EINVAL);
427
if (sd->valsize < sz)
428
return (ENOMEM);
429
oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
430
if (oh == NULL)
431
return (EINVAL);
432
memset(&stats, 0, sizeof(stats));
433
434
IPFW_UH_RLOCK(ch);
435
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
436
if (cfg == NULL) {
437
IPFW_UH_RUNLOCK(ch);
438
return (ENOENT);
439
}
440
export_stats(ch, cfg, &stats);
441
IPFW_UH_RUNLOCK(ch);
442
443
ctlv = (ipfw_obj_ctlv *)(oh + 1);
444
memset(ctlv, 0, sizeof(*ctlv));
445
ctlv->head.type = IPFW_TLV_COUNTERS;
446
ctlv->head.length = sz - sizeof(ipfw_obj_header);
447
ctlv->count = sizeof(stats) / sizeof(uint64_t);
448
ctlv->objsize = sizeof(uint64_t);
449
ctlv->version = IPFW_NAT64_VERSION;
450
memcpy(ctlv + 1, &stats, sizeof(stats));
451
return (0);
452
}
453
454
/*
455
* Reset nat64clat statistics.
456
* Data layout (v0)(current):
457
* Request: [ ipfw_obj_header ]
458
*
459
* Returns 0 on success
460
*/
461
static int
462
nat64clat_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
463
struct sockopt_data *sd)
464
{
465
struct nat64clat_cfg *cfg;
466
ipfw_obj_header *oh;
467
468
if (sd->valsize != sizeof(*oh))
469
return (EINVAL);
470
oh = (ipfw_obj_header *)sd->kbuf;
471
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
472
oh->ntlv.set >= IPFW_MAX_SETS)
473
return (EINVAL);
474
475
IPFW_UH_WLOCK(ch);
476
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
477
if (cfg == NULL) {
478
IPFW_UH_WUNLOCK(ch);
479
return (ENOENT);
480
}
481
COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);
482
IPFW_UH_WUNLOCK(ch);
483
return (0);
484
}
485
486
static struct ipfw_sopt_handler scodes[] = {
487
{ IP_FW_NAT64CLAT_CREATE, IP_FW3_OPVER, HDIR_SET, nat64clat_create },
488
{ IP_FW_NAT64CLAT_DESTROY, IP_FW3_OPVER, HDIR_SET, nat64clat_destroy },
489
{ IP_FW_NAT64CLAT_CONFIG, IP_FW3_OPVER, HDIR_BOTH, nat64clat_config },
490
{ IP_FW_NAT64CLAT_LIST, IP_FW3_OPVER, HDIR_GET, nat64clat_list },
491
{ IP_FW_NAT64CLAT_STATS, IP_FW3_OPVER, HDIR_GET, nat64clat_stats },
492
{ IP_FW_NAT64CLAT_RESET_STATS, IP_FW3_OPVER, HDIR_SET, nat64clat_reset_stats },
493
};
494
495
static int
496
nat64clat_manage_sets(struct ip_fw_chain *ch, uint32_t set, uint8_t new_set,
497
enum ipfw_sets_cmd cmd)
498
{
499
500
return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch),
501
IPFW_TLV_NAT64CLAT_NAME, set, new_set, cmd));
502
}
503
NAT64_DEFINE_OPCODE_REWRITER(nat64clat, NAT64CLAT, opcodes);
504
505
static int
506
destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
507
void *arg)
508
{
509
struct nat64clat_cfg *cfg;
510
struct ip_fw_chain *ch;
511
512
ch = (struct ip_fw_chain *)arg;
513
cfg = (struct nat64clat_cfg *)SRV_OBJECT(ch, no->kidx);
514
SRV_OBJECT(ch, no->kidx) = NULL;
515
nat64clat_detach_config(ch, cfg);
516
nat64clat_free_config(cfg);
517
return (0);
518
}
519
520
int
521
nat64clat_init(struct ip_fw_chain *ch, int first)
522
{
523
524
V_nat64clat_eid = ipfw_add_eaction(ch, ipfw_nat64clat, "nat64clat");
525
if (V_nat64clat_eid == 0)
526
return (ENXIO);
527
IPFW_ADD_SOPT_HANDLER(first, scodes);
528
IPFW_ADD_OBJ_REWRITER(first, opcodes);
529
return (0);
530
}
531
532
void
533
nat64clat_uninit(struct ip_fw_chain *ch, int last)
534
{
535
536
IPFW_DEL_OBJ_REWRITER(last, opcodes);
537
IPFW_DEL_SOPT_HANDLER(last, scodes);
538
ipfw_del_eaction(ch, V_nat64clat_eid);
539
/*
540
* Since we already have deregistered external action,
541
* our named objects become unaccessible via rules, because
542
* all rules were truncated by ipfw_del_eaction().
543
* So, we can unlink and destroy our named objects without holding
544
* IPFW_WLOCK().
545
*/
546
IPFW_UH_WLOCK(ch);
547
ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
548
IPFW_TLV_NAT64CLAT_NAME);
549
V_nat64clat_eid = 0;
550
IPFW_UH_WUNLOCK(ch);
551
}
552
553