Skip to content

Commit a8380a9

Browse files
authored
Add support for dynamic NAT rules and dynamic portmap. (#31)
1 parent 67709d2 commit a8380a9

32 files changed

+1017
-559
lines changed

src/kern/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ CFLAGS+= -DNDEBUG
4141
OBJDIR= build
4242
endif
4343

44-
LDFLAGS+= -lpthread -lnv -lpcap -lqsbr -lthmap -llpm -lcdb -lbpfjit
44+
LDFLAGS+= -lpthread -lnv -lqsbr -lthmap -llpm -lcdb -lbpfjit
4545
ifeq ($(SYSNAME),Linux)
4646
LDFLAGS+= -ljemalloc
4747
endif

src/kern/files.npf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ file net/npf/npf_conndb.c npf
2828
file net/npf/npf_state.c npf
2929
file net/npf/npf_state_tcp.c npf
3030
file net/npf/npf_nat.c npf
31+
file net/npf/npf_portmap.c npf
3132
file net/npf/npf_alg.c npf
3233
file net/npf/npf_sendpkt.c npf
3334
file net/npf/npf_worker.c npf

src/kern/npf-params.7

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ Timeout for the TCP time-wait state.
9898
Default: 240.
9999
.El
100100
.\" ---
101+
.It Li portmap.min_port
102+
Lower bound of the port range used when selecting the port for dynamic NAT
103+
with port translation enabled.
104+
Default: 1024 (also the lowest allowed value).
105+
.It Li portmap.max_port
106+
Upper bound of the port range as described above.
107+
Default: 65535 (also the highest allowed value).
108+
.\" ---
101109
.El
102110
.\" -----
103111
.Sh EXAMPLES

src/kern/npf.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ npf_create(int flags, const npf_mbufops_t *mbufops, const npf_ifops_t *ifops)
8080
npf_state_sysinit(npf);
8181
npf_ifmap_init(npf, ifops);
8282
npf_conn_init(npf, flags);
83+
npf_portmap_init(npf);
8384
npf_alg_init(npf);
8485
npf_ext_init(npf);
8586

@@ -100,6 +101,7 @@ npf_destroy(npf_t *npf)
100101
/* Finally, safe to destroy the subsystems. */
101102
npf_ext_fini(npf);
102103
npf_alg_fini(npf);
104+
npf_portmap_fini(npf);
103105
npf_conn_fini(npf);
104106
npf_ifmap_fini(npf);
105107
npf_state_sysfini(npf);

src/kern/npf_conf.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ npf_config_load(npf_t *npf, npf_ruleset_t *rset, npf_tableset_t *tset,
169169
/* Synchronise: drain all references. */
170170
pserialize_perform(npf->qsbr);
171171
if (flush) {
172+
npf_portmap_flush(npf);
172173
npf_ifmap_flush(npf);
173174
}
174175

src/kern/npf_conndb.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
*
3333
* Warning (not applicable for the userspace npfkern):
3434
*
35-
* The thmap data is partially lock-free data structure that uses its
36-
* own spin-locks on the writer side (insert/delete operations).
35+
* thmap is partially lock-free data structure that uses its own
36+
* spin-locks on the writer side (insert/delete operations).
3737
*
3838
* The relevant interrupt priority level (IPL) must be set and the
3939
* kernel preemption disabled across the critical paths to prevent
@@ -93,8 +93,8 @@ typedef struct {
9393
void
9494
npf_conndb_sysinit(npf_t *npf)
9595
{
96-
const size_t len = sizeof(npf_conndb_params_t);
97-
npf_conndb_params_t *params = kmem_zalloc(len, KM_SLEEP);
96+
npf_conndb_params_t *params = npf_param_allocgroup(npf,
97+
NPF_PARAMS_CONNDB, sizeof(npf_conndb_params_t));
9898
npf_param_t param_map[] = {
9999
{
100100
"gc.step",
@@ -104,14 +104,13 @@ npf_conndb_sysinit(npf_t *npf)
104104
}
105105
};
106106
npf_param_register(npf, param_map, __arraycount(param_map));
107-
npf->params[NPF_PARAMS_CONNDB] = params;
108107
}
109108

110109
void
111110
npf_conndb_sysfini(npf_t *npf)
112111
{
113112
const size_t len = sizeof(npf_conndb_params_t);
114-
kmem_free(npf->params[NPF_PARAMS_CONNDB], len);
113+
npf_param_freegroup(npf, NPF_PARAMS_CONNDB, len);
115114
}
116115

117116
npf_conndb_t *

src/kern/npf_ctl.c

Lines changed: 74 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,58 @@ npf_mk_rules(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
453453
}
454454

455455
static int __noinline
456-
npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
457-
npf_tableset_t *tblset, npf_ruleset_t **ntsetp)
456+
npf_mk_singlenat(npf_t *npf, const nvlist_t *nat, npf_ruleset_t *ntset,
457+
npf_tableset_t *tblset, nvlist_t *errdict, npf_rule_t **rlp)
458+
{
459+
npf_rule_t *rl = NULL;
460+
npf_natpolicy_t *np;
461+
int error;
462+
463+
/*
464+
* NAT rules are standard rules, plus the translation policy.
465+
* We first construct the rule structure.
466+
*/
467+
error = npf_mk_singlerule(npf, nat, NULL, &rl, errdict);
468+
if (error) {
469+
return error;
470+
}
471+
KASSERT(rl != NULL);
472+
*rlp = rl;
473+
474+
/* If rule is named, it is a group with NAT policies. */
475+
if (dnvlist_get_string(nat, "name", NULL)) {
476+
return 0;
477+
}
478+
479+
/* Check the table ID. */
480+
if (nvlist_exists_number(nat, "nat-table-id")) {
481+
unsigned tid = nvlist_get_number(nat, "nat-table-id");
482+
483+
if (!npf_tableset_getbyid(tblset, tid)) {
484+
NPF_ERR_DEBUG(errdict);
485+
error = EINVAL;
486+
goto out;
487+
}
488+
}
489+
490+
/* Allocate a new NAT policy and assign it to the rule. */
491+
np = npf_nat_newpolicy(npf, nat, ntset);
492+
if (np == NULL) {
493+
NPF_ERR_DEBUG(errdict);
494+
error = ENOMEM;
495+
goto out;
496+
}
497+
npf_rule_setnat(rl, np);
498+
out:
499+
if (error) {
500+
npf_rule_free(rl);
501+
}
502+
return error;
503+
}
504+
505+
static int __noinline
506+
npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, npf_tableset_t *tblset,
507+
nvlist_t *errdict, npf_ruleset_t **ntsetp)
458508
{
459509
const nvlist_t * const *nat_rules;
460510
npf_ruleset_t *ntset;
@@ -478,42 +528,12 @@ npf_mk_natlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict,
478528
for (unsigned i = 0; i < nitems; i++) {
479529
const nvlist_t *nat = nat_rules[i];
480530
npf_rule_t *rl = NULL;
481-
npf_natpolicy_t *np;
482531

483-
/*
484-
* NAT policies are standard rules, plus the additional
485-
* translation information. First, make a rule.
486-
*/
487-
error = npf_mk_singlerule(npf, nat, NULL, &rl, errdict);
532+
error = npf_mk_singlenat(npf, nat, ntset, tblset, errdict, &rl);
488533
if (error) {
489534
break;
490535
}
491536
npf_ruleset_insert(ntset, rl);
492-
493-
/* If rule is named, it is a group with NAT policies. */
494-
if (dnvlist_get_string(nat, "name", NULL)) {
495-
continue;
496-
}
497-
498-
/* Check the table ID. */
499-
if (nvlist_exists_number(nat, "nat-table-id")) {
500-
unsigned tid = nvlist_get_number(nat, "nat-table-id");
501-
502-
if (!npf_tableset_getbyid(tblset, tid)) {
503-
NPF_ERR_DEBUG(errdict);
504-
error = EINVAL;
505-
break;
506-
}
507-
}
508-
509-
/* Allocate a new NAT policy and assign to the rule. */
510-
np = npf_nat_newpolicy(npf, nat, ntset);
511-
if (np == NULL) {
512-
NPF_ERR_DEBUG(errdict);
513-
error = ENOMEM;
514-
break;
515-
}
516-
npf_rule_setnat(rl, np);
517537
}
518538
*ntsetp = ntset;
519539
return error;
@@ -593,7 +613,7 @@ npfctl_load_nvlist(npf_t *npf, nvlist_t *npf_dict, nvlist_t *errdict)
593613
if (error) {
594614
goto fail;
595615
}
596-
error = npf_mk_natlist(npf, npf_dict, errdict, tblset, &ntset);
616+
error = npf_mk_natlist(npf, npf_dict, tblset, errdict, &ntset);
597617
if (error) {
598618
goto fail;
599619
}
@@ -747,31 +767,42 @@ npfctl_rule(npf_t *npf, u_long cmd, void *data)
747767
const char *ruleset_name;
748768
uint32_t rcmd;
749769
int error = 0;
770+
bool natset;
750771

751772
error = npf_nvlist_copyin(npf, data, &npf_rule);
752773
if (error) {
753774
return error;
754775
}
755776
rcmd = dnvlist_get_number(npf_rule, "command", 0);
777+
natset = dnvlist_get_bool(npf_rule, "nat-rule", false);
756778
ruleset_name = dnvlist_get_string(npf_rule, "ruleset-name", NULL);
757779
if (!ruleset_name) {
758780
error = EINVAL;
759781
goto out;
760782
}
761783

762-
if (rcmd == NPF_CMD_RULE_ADD) {
784+
npf_config_enter(npf);
785+
rlset = natset ? npf_config_natset(npf) : npf_config_ruleset(npf);
786+
switch (rcmd) {
787+
case NPF_CMD_RULE_ADD: {
763788
retdict = nvlist_create(0);
764-
error = npf_mk_singlerule(npf, npf_rule, NULL, &rl, retdict);
789+
if (natset) {
790+
/*
791+
* Translation rule.
792+
*/
793+
error = npf_mk_singlenat(npf, npf_rule, rlset,
794+
npf_config_tableset(npf), retdict, &rl);
795+
} else {
796+
/*
797+
* Standard rule.
798+
*/
799+
error = npf_mk_singlerule(npf, npf_rule, NULL,
800+
&rl, retdict);
801+
}
765802
if (error) {
803+
npf_config_exit(npf);
766804
goto out;
767805
}
768-
}
769-
770-
npf_config_enter(npf);
771-
rlset = npf_config_ruleset(npf);
772-
773-
switch (rcmd) {
774-
case NPF_CMD_RULE_ADD: {
775806
if ((error = npf_ruleset_add(rlset, ruleset_name, rl)) == 0) {
776807
/* Success. */
777808
uint64_t id = npf_rule_getid(rl);

src/kern/npf_impl.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*-
2-
* Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
2+
* Copyright (c) 2009-2019 The NetBSD Foundation, Inc.
33
* All rights reserved.
44
*
55
* This material is based upon work partially supported by The
@@ -72,12 +72,14 @@
7272
struct npf_ruleset;
7373
struct npf_rule;
7474
struct npf_rprocset;
75+
struct npf_portmap;
7576
struct npf_nat;
7677
struct npf_conn;
7778
struct npf_config;
7879

7980
typedef struct npf_ruleset npf_ruleset_t;
8081
typedef struct npf_rule npf_rule_t;
82+
typedef struct npf_portmap npf_portmap_t;
8183
typedef struct npf_nat npf_nat_t;
8284
typedef struct npf_rprocset npf_rprocset_t;
8385
typedef struct npf_alg npf_alg_t;
@@ -179,12 +181,13 @@ typedef struct {
179181
int max;
180182
} npf_param_t;
181183

182-
enum {
184+
typedef enum {
183185
NPF_PARAMS_CONNDB = 0,
184186
NPF_PARAMS_GENERIC_STATE,
185187
NPF_PARAMS_TCP_STATE,
188+
NPF_PARAMS_PORTMAP,
186189
NPF_PARAMS_COUNT
187-
};
190+
} npf_paramgroup_t;
188191

189192
/*
190193
* NPF INSTANCE (CONTEXT) STRUCTURE AND AUXILIARY OPERATIONS.
@@ -214,7 +217,8 @@ struct npf {
214217
npf_conndb_t * conn_db;
215218
pool_cache_t conn_cache[2];
216219

217-
/* ALGs. */
220+
/* NAT and ALGs. */
221+
npf_portmap_t * portmap;
218222
npf_algset_t * algset;
219223

220224
/* Interface mapping. */
@@ -297,6 +301,8 @@ void npf_stats_dec(npf_t *, npf_stats_t);
297301
void npf_param_init(npf_t *);
298302
void npf_param_fini(npf_t *);
299303
void npf_param_register(npf_t *, npf_param_t *, unsigned);
304+
void * npf_param_allocgroup(npf_t *, npf_paramgroup_t, size_t);
305+
void npf_param_freegroup(npf_t *, npf_paramgroup_t, size_t);
300306
int npf_param_check(npf_t *, const char *, int);
301307

302308
void npf_ifmap_init(npf_t *, const npf_ifops_t *);
@@ -340,6 +346,7 @@ void npf_addr_mask(const npf_addr_t *, const npf_netmask_t,
340346
const int, npf_addr_t *);
341347
void npf_addr_bitor(const npf_addr_t *, const npf_netmask_t,
342348
const int, npf_addr_t *);
349+
int npf_netmask_check(const int, npf_netmask_t);
343350

344351
int npf_tcpsaw(const npf_cache_t *, tcp_seq *, tcp_seq *,
345352
uint32_t *);
@@ -390,7 +397,6 @@ void npf_ruleset_destroy(npf_ruleset_t *);
390397
void npf_ruleset_insert(npf_ruleset_t *, npf_rule_t *);
391398
void npf_ruleset_reload(npf_t *, npf_ruleset_t *,
392399
npf_ruleset_t *, bool);
393-
npf_rule_t * npf_ruleset_sharepm(npf_ruleset_t *, npf_natpolicy_t *);
394400
npf_natpolicy_t *npf_ruleset_findnat(npf_ruleset_t *, uint64_t);
395401
void npf_ruleset_freealg(npf_ruleset_t *, npf_alg_t *);
396402
int npf_ruleset_export(npf_t *, const npf_ruleset_t *,
@@ -451,14 +457,22 @@ void npf_state_tcp_sysfini(npf_t *);
451457
bool npf_state_tcp(npf_cache_t *, npf_state_t *, int);
452458
int npf_state_tcp_timeout(npf_t *, const npf_state_t *);
453459

460+
/* Portmap. */
461+
void npf_portmap_init(npf_t *);
462+
void npf_portmap_fini(npf_t *);
463+
464+
in_port_t npf_portmap_get(npf_t *, int, const npf_addr_t *);
465+
bool npf_portmap_take(npf_t *, int, const npf_addr_t *, in_port_t);
466+
void npf_portmap_put(npf_t *, int, const npf_addr_t *, in_port_t);
467+
void npf_portmap_flush(npf_t *);
468+
454469
/* NAT. */
455470
void npf_nat_sysinit(void);
456471
void npf_nat_sysfini(void);
457472
npf_natpolicy_t *npf_nat_newpolicy(npf_t *, const nvlist_t *, npf_ruleset_t *);
458473
int npf_nat_policyexport(const npf_natpolicy_t *, nvlist_t *);
459474
void npf_nat_freepolicy(npf_natpolicy_t *);
460475
bool npf_nat_cmppolicy(npf_natpolicy_t *, npf_natpolicy_t *);
461-
bool npf_nat_sharepm(npf_natpolicy_t *, npf_natpolicy_t *);
462476
void npf_nat_setid(npf_natpolicy_t *, uint64_t);
463477
uint64_t npf_nat_getid(const npf_natpolicy_t *);
464478
void npf_nat_freealg(npf_natpolicy_t *, npf_alg_t *);

src/kern/npf_inet.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,26 @@ npf_addr_cmp(const npf_addr_t *addr1, const npf_netmask_t mask1,
221221
return memcmp(addr1, addr2, alen);
222222
}
223223

224+
int
225+
npf_netmask_check(const int alen, npf_netmask_t mask)
226+
{
227+
switch (alen) {
228+
case sizeof(struct in_addr):
229+
if (__predict_false(mask > 32 && mask != NPF_NO_NETMASK)) {
230+
return EINVAL;
231+
}
232+
break;
233+
case sizeof(struct in6_addr):
234+
if (__predict_false(mask > 128 && mask != NPF_NO_NETMASK)) {
235+
return EINVAL;
236+
}
237+
break;
238+
default:
239+
return EINVAL;
240+
}
241+
return 0;
242+
}
243+
224244
/*
225245
* npf_tcpsaw: helper to fetch SEQ, ACK, WIN and return TCP data length.
226246
*

0 commit comments

Comments
 (0)