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