aboutsummaryrefslogtreecommitdiff
path: root/sbin/ipfw
diff options
context:
space:
mode:
authorAndrey V. Elsukov <ae@FreeBSD.org>2019-03-19 10:57:03 +0000
committerAndrey V. Elsukov <ae@FreeBSD.org>2019-03-19 10:57:03 +0000
commitd18c1f26a4bbd5c3871bb67660a5899d28f5efa5 (patch)
tree7b90ad4e40d767c5d50b9b23303d7b4648229ae1 /sbin/ipfw
parentc5be49da01dc36e7e681026bd1a9b271929d2bd7 (diff)
downloadsrc-d18c1f26a4bbd5c3871bb67660a5899d28f5efa5.tar.gz
src-d18c1f26a4bbd5c3871bb67660a5899d28f5efa5.zip
Reapply r345274 with build fixes for 32-bit architectures.
Update NAT64LSN implementation: o most of data structures and relations were modified to be able support large number of translation states. Now each supported protocol can use full ports range. Ports groups now are belongs to IPv4 alias addresses, not hosts. Each ports group can keep several states chunks. This is controlled with new `states_chunks` config option. States chunks allow to have several translation states for single alias address and port, but for different destination addresses. o by default all hash tables now use jenkins hash. o ConcurrencyKit and epoch(9) is used to make NAT64LSN lockless on fast path. o one NAT64LSN instance now can be used to handle several IPv6 prefixes, special prefix "::" value should be used for this purpose when instance is created. o due to modified internal data structures relations, the socket opcode that does states listing was changed. Obtained from: Yandex LLC MFC after: 1 month Sponsored by: Yandex LLC
Notes
Notes: svn path=/head/; revision=345293
Diffstat (limited to 'sbin/ipfw')
-rw-r--r--sbin/ipfw/ipfw.830
-rw-r--r--sbin/ipfw/ipfw2.h1
-rw-r--r--sbin/ipfw/nat64lsn.c124
3 files changed, 73 insertions, 82 deletions
diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 31448aff92bb..f02ec3e148cd 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd March 18, 2019
+.Dd March 19, 2019
.Dt IPFW 8
.Os
.Sh NAME
@@ -3300,6 +3300,7 @@ See
.Sx SYSCTL VARIABLES
for more info.
.Sh IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
+.Ss Stateful translation
.Nm
supports in-kernel IPv6/IPv4 network address and protocol translation.
Stateful NAT64 translation allows IPv6-only clients to contact IPv4 servers
@@ -3317,7 +3318,8 @@ to be able use stateful NAT64 translator.
Stateful NAT64 uses a bunch of memory for several types of objects.
When IPv6 client initiates connection, NAT64 translator creates a host entry
in the states table.
-Each host entry has a number of ports group entries allocated on demand.
+Each host entry uses preallocated IPv4 alias entry.
+Each alias entry has a number of ports group entries allocated on demand.
Ports group entries contains connection state entries.
There are several options to control limits and lifetime for these objects.
.Pp
@@ -3337,6 +3339,11 @@ First time an original packet is handled and consumed by translator,
and then it is handled again as translated packet.
This behavior can be changed by sysctl variable
.Va net.inet.ip.fw.nat64_direct_output .
+Also translated packet can be tagged using
+.Cm tag
+rule action, and then matched by
+.Cm tagged
+opcode to avoid loops and extra overhead.
.Pp
The stateful NAT64 configuration command is the following:
.Bd -ragged -offset indent
@@ -3364,15 +3371,16 @@ to represent IPv4 addresses. This IPv6 prefix should be configured in DNS64.
The translator implementation follows RFC6052, that restricts the length of
prefixes to one of following: 32, 40, 48, 56, 64, or 96.
The Well-Known IPv6 Prefix 64:ff9b:: must be 96 bits long.
-.It Cm max_ports Ar number
-Maximum number of ports reserved for upper level protocols to one IPv6 client.
-All reserved ports are divided into chunks between supported protocols.
-The number of connections from one IPv6 client is limited by this option.
-Note that closed TCP connections still remain in the list of connections until
-.Cm tcp_close_age
-interval will not expire.
-Default value is
-.Ar 2048 .
+The special
+.Ar ::/length
+prefix can be used to handle several IPv6 prefixes with one NAT64 instance.
+The NAT64 instance will determine a destination IPv4 address from prefix
+.Ar length .
+.It Cm states_chunks Ar number
+The number of states chunks in single ports group.
+Each ports group by default can keep 64 state entries in single chunk.
+The above value affects the maximum number of states that can be associated with single IPv4 alias address and port.
+The value must be power of 2, and up to 128.
.It Cm host_del_age Ar seconds
The number of seconds until the host entry for a IPv6 client will be deleted
and all its resources will be released due to inactivity.
diff --git a/sbin/ipfw/ipfw2.h b/sbin/ipfw/ipfw2.h
index ff6990ae1c06..2b562734d15f 100644
--- a/sbin/ipfw/ipfw2.h
+++ b/sbin/ipfw/ipfw2.h
@@ -278,6 +278,7 @@ enum tokens {
TOK_AGG_LEN,
TOK_AGG_COUNT,
TOK_MAX_PORTS,
+ TOK_STATES_CHUNKS,
TOK_JMAXLEN,
TOK_PORT_RANGE,
TOK_HOST_DEL_AGE,
diff --git a/sbin/ipfw/nat64lsn.c b/sbin/ipfw/nat64lsn.c
index c6a892572818..4a6d7a7914c3 100644
--- a/sbin/ipfw/nat64lsn.c
+++ b/sbin/ipfw/nat64lsn.c
@@ -87,68 +87,70 @@ nat64lsn_print_states(void *buf)
char sflags[4], *sf, *proto;
ipfw_obj_header *oh;
ipfw_obj_data *od;
- ipfw_nat64lsn_stg *stg;
- ipfw_nat64lsn_state *ste;
+ ipfw_nat64lsn_stg_v1 *stg;
+ ipfw_nat64lsn_state_v1 *ste;
uint64_t next_idx;
int i, sz;
oh = (ipfw_obj_header *)buf;
od = (ipfw_obj_data *)(oh + 1);
- stg = (ipfw_nat64lsn_stg *)(od + 1);
+ stg = (ipfw_nat64lsn_stg_v1 *)(od + 1);
sz = od->head.length - sizeof(*od);
next_idx = 0;
while (sz > 0 && next_idx != 0xFF) {
- next_idx = stg->next_idx;
+ next_idx = stg->next.index;
sz -= sizeof(*stg);
if (stg->count == 0) {
stg++;
continue;
}
- switch (stg->proto) {
- case IPPROTO_TCP:
- proto = "TCP";
- break;
- case IPPROTO_UDP:
- proto = "UDP";
- break;
- case IPPROTO_ICMPV6:
- proto = "ICMPv6";
- break;
- }
- inet_ntop(AF_INET6, &stg->host6, s, sizeof(s));
+ /*
+ * NOTE: addresses are in network byte order,
+ * ports are in host byte order.
+ */
inet_ntop(AF_INET, &stg->alias4, a, sizeof(a));
- ste = (ipfw_nat64lsn_state *)(stg + 1);
+ ste = (ipfw_nat64lsn_state_v1 *)(stg + 1);
for (i = 0; i < stg->count && sz > 0; i++) {
sf = sflags;
+ inet_ntop(AF_INET6, &ste->host6, s, sizeof(s));
inet_ntop(AF_INET, &ste->daddr, f, sizeof(f));
- if (stg->proto == IPPROTO_TCP) {
+ switch (ste->proto) {
+ case IPPROTO_TCP:
+ proto = "TCP";
if (ste->flags & 0x02)
*sf++ = 'S';
if (ste->flags & 0x04)
*sf++ = 'E';
if (ste->flags & 0x01)
*sf++ = 'F';
+ break;
+ case IPPROTO_UDP:
+ proto = "UDP";
+ break;
+ case IPPROTO_ICMP:
+ proto = "ICMPv6";
+ break;
}
*sf = '\0';
- switch (stg->proto) {
+ switch (ste->proto) {
case IPPROTO_TCP:
case IPPROTO_UDP:
printf("%s:%d\t%s:%d\t%s\t%s\t%d\t%s:%d\n",
s, ste->sport, a, ste->aport, proto,
sflags, ste->idle, f, ste->dport);
break;
- case IPPROTO_ICMPV6:
+ case IPPROTO_ICMP:
printf("%s\t%s\t%s\t\t%d\t%s\n",
s, a, proto, ste->idle, f);
break;
default:
printf("%s\t%s\t%d\t\t%d\t%s\n",
- s, a, stg->proto, ste->idle, f);
+ s, a, ste->proto, ste->idle, f);
}
ste++;
sz -= sizeof(*ste);
}
- stg = (ipfw_nat64lsn_stg *)ste;
+ stg = (ipfw_nat64lsn_stg_v1 *)ste;
}
return (next_idx);
}
@@ -174,6 +176,7 @@ nat64lsn_states_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set)
err(EX_OSERR, NULL);
do {
oh = (ipfw_obj_header *)buf;
+ oh->opheader.version = 1; /* Force using ov new API */
od = (ipfw_obj_data *)(oh + 1);
nat64lsn_fill_ntlv(&oh->ntlv, cfg->name, set);
od->head.type = IPFW_TLV_OBJDATA;
@@ -363,12 +366,8 @@ nat64lsn_parse_int(const char *arg, const char *desc)
static struct _s_x nat64newcmds[] = {
{ "prefix6", TOK_PREFIX6 },
- { "agg_len", TOK_AGG_LEN }, /* not yet */
- { "agg_count", TOK_AGG_COUNT }, /* not yet */
- { "port_range", TOK_PORT_RANGE }, /* not yet */
{ "jmaxlen", TOK_JMAXLEN },
{ "prefix4", TOK_PREFIX4 },
- { "max_ports", TOK_MAX_PORTS },
{ "host_del_age", TOK_HOST_DEL_AGE },
{ "pg_del_age", TOK_PG_DEL_AGE },
{ "tcp_syn_age", TOK_TCP_SYN_AGE },
@@ -376,10 +375,13 @@ static struct _s_x nat64newcmds[] = {
{ "tcp_est_age", TOK_TCP_EST_AGE },
{ "udp_age", TOK_UDP_AGE },
{ "icmp_age", TOK_ICMP_AGE },
+ { "states_chunks",TOK_STATES_CHUNKS },
{ "log", TOK_LOG },
{ "-log", TOK_LOGOFF },
{ "allow_private", TOK_PRIVATE },
{ "-allow_private", TOK_PRIVATEOFF },
+ /* for compatibility with old configurations */
+ { "max_ports", TOK_MAX_PORTS }, /* unused */
{ NULL, 0 }
};
@@ -436,42 +438,17 @@ nat64lsn_create(const char *name, uint8_t set, int ac, char **av)
nat64lsn_parse_prefix(*av, AF_INET6, &cfg->prefix6,
&cfg->plen6);
if (ipfw_check_nat64prefix(&cfg->prefix6,
- cfg->plen6) != 0)
+ cfg->plen6) != 0 &&
+ !IN6_IS_ADDR_UNSPECIFIED(&cfg->prefix6))
errx(EX_USAGE, "Bad prefix6 %s", *av);
ac--; av++;
break;
-#if 0
- case TOK_AGG_LEN:
- NEED1("Aggregation prefix len required");
- cfg->agg_prefix_len = nat64lsn_parse_int(*av, opt);
- ac--; av++;
- break;
- case TOK_AGG_COUNT:
- NEED1("Max per-prefix count required");
- cfg->agg_prefix_max = nat64lsn_parse_int(*av, opt);
- ac--; av++;
- break;
- case TOK_PORT_RANGE:
- NEED1("port range x[:y] required");
- if ((p = strchr(*av, ':')) == NULL)
- cfg->min_port = (uint16_t)nat64lsn_parse_int(
- *av, opt);
- else {
- *p++ = '\0';
- cfg->min_port = (uint16_t)nat64lsn_parse_int(
- *av, opt);
- cfg->max_port = (uint16_t)nat64lsn_parse_int(
- p, opt);
- }
- ac--; av++;
- break;
case TOK_JMAXLEN:
NEED1("job queue length required");
cfg->jmaxlen = nat64lsn_parse_int(*av, opt);
ac--; av++;
break;
-#endif
case TOK_MAX_PORTS:
NEED1("Max per-user ports required");
cfg->max_ports = nat64lsn_parse_int(*av, opt);
@@ -519,6 +496,12 @@ nat64lsn_create(const char *name, uint8_t set, int ac, char **av)
*av, opt);
ac--; av++;
break;
+ case TOK_STATES_CHUNKS:
+ NEED1("number of chunks required");
+ cfg->states_chunks = (uint8_t)nat64lsn_parse_int(
+ *av, opt);
+ ac--; av++;
+ break;
case TOK_LOG:
cfg->flags |= NAT64_LOG;
break;
@@ -630,6 +613,12 @@ nat64lsn_config(const char *name, uint8_t set, int ac, char **av)
*av, opt);
ac--; av++;
break;
+ case TOK_STATES_CHUNKS:
+ NEED1("number of chunks required");
+ cfg->states_chunks = (uint8_t)nat64lsn_parse_int(
+ *av, opt);
+ ac--; av++;
+ break;
case TOK_LOG:
cfg->flags |= NAT64_LOG;
break;
@@ -789,31 +778,24 @@ nat64lsn_show_cb(ipfw_nat64lsn_cfg *cfg, const char *name, uint8_t set)
printf("nat64lsn %s prefix4 %s/%u", cfg->name, abuf, cfg->plen4);
inet_ntop(AF_INET6, &cfg->prefix6, abuf, sizeof(abuf));
printf(" prefix6 %s/%u", abuf, cfg->plen6);
-#if 0
- printf("agg_len %u agg_count %u ", cfg->agg_prefix_len,
- cfg->agg_prefix_max);
- if (cfg->min_port != NAT64LSN_PORT_MIN ||
- cfg->max_port != NAT64LSN_PORT_MAX)
- printf(" port_range %u:%u", cfg->min_port, cfg->max_port);
- if (cfg->jmaxlen != NAT64LSN_JMAXLEN)
- printf(" jmaxlen %u ", cfg->jmaxlen);
-#endif
- if (cfg->max_ports != NAT64LSN_MAX_PORTS)
- printf(" max_ports %u", cfg->max_ports);
- if (cfg->nh_delete_delay != NAT64LSN_HOST_AGE)
+ if (co.verbose || cfg->states_chunks > 1)
+ printf(" states_chunks %u", cfg->states_chunks);
+ if (co.verbose || cfg->nh_delete_delay != NAT64LSN_HOST_AGE)
printf(" host_del_age %u", cfg->nh_delete_delay);
- if (cfg->pg_delete_delay != NAT64LSN_PG_AGE)
+ if (co.verbose || cfg->pg_delete_delay != NAT64LSN_PG_AGE)
printf(" pg_del_age %u ", cfg->pg_delete_delay);
- if (cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE)
+ if (co.verbose || cfg->st_syn_ttl != NAT64LSN_TCP_SYN_AGE)
printf(" tcp_syn_age %u", cfg->st_syn_ttl);
- if (cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE)
+ if (co.verbose || cfg->st_close_ttl != NAT64LSN_TCP_FIN_AGE)
printf(" tcp_close_age %u", cfg->st_close_ttl);
- if (cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE)
+ if (co.verbose || cfg->st_estab_ttl != NAT64LSN_TCP_EST_AGE)
printf(" tcp_est_age %u", cfg->st_estab_ttl);
- if (cfg->st_udp_ttl != NAT64LSN_UDP_AGE)
+ if (co.verbose || cfg->st_udp_ttl != NAT64LSN_UDP_AGE)
printf(" udp_age %u", cfg->st_udp_ttl);
- if (cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE)
+ if (co.verbose || cfg->st_icmp_ttl != NAT64LSN_ICMP_AGE)
printf(" icmp_age %u", cfg->st_icmp_ttl);
+ if (co.verbose || cfg->jmaxlen != NAT64LSN_JMAXLEN)
+ printf(" jmaxlen %u ", cfg->jmaxlen);
if (cfg->flags & NAT64_LOG)
printf(" log");
if (cfg->flags & NAT64_ALLOW_PRIVATE)