Add NAT64 CLAT implementation as defined in RFC6877.
CLAT is customer-side translator that algorithmically translates 1:1 private IPv4 addresses to global IPv6 addresses, and vice versa. It is implemented as part of ipfw_nat64 kernel module. When module is loaded or compiled into the kernel, it registers "nat64clat" external action. External action named instance can be created using `create` command and then used in ipfw rules. The create command accepts two IPv6 prefixes `plat_prefix` and `clat_prefix`. If plat_prefix is ommitted, IPv6 NAT64 Well-Known prefix 64:ff9b::/96 will be used. # ipfw nat64clat CLAT create clat_prefix SRC_PFX plat_prefix DST_PFX # ipfw add nat64clat CLAT ip4 from IPv4_PFX to any out # ipfw add nat64clat CLAT ip6 from DST_PFX to SRC_PFX in Obtained from: Yandex LLC Submitted by: Boris N. Lytochkin MFC after: 1 month Relnotes: yes Sponsored by: Yandex LLC
This commit is contained in:
parent
2770fa04e1
commit
93a7173b74
@ -5,7 +5,7 @@
|
||||
PACKAGE=ipfw
|
||||
PROG= ipfw
|
||||
SRCS= ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
|
||||
SRCS+= nat64lsn.c nat64stl.c nptv6.c
|
||||
SRCS+= nat64clat.c nat64lsn.c nat64stl.c nptv6.c
|
||||
WARNS?= 2
|
||||
|
||||
.if ${MK_PF} != "no"
|
||||
|
@ -136,6 +136,21 @@ in-kernel NAT.
|
||||
.Cm destroy
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64stl Ar name Cm stats Op Cm reset
|
||||
.Ss XLAT464 CLAT IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64clat Ar name Cm create Ar create-options
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64clat Ar name Cm config Ar config-options
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64clat
|
||||
.Brq Ar name | all
|
||||
.Brq Cm list | show
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64clat
|
||||
.Brq Ar name | all
|
||||
.Cm destroy
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nat64clat Ar name Cm stats Op Cm reset
|
||||
.Ss IPv6-to-IPv6 NETWORK PREFIX TRANSLATION
|
||||
.Nm
|
||||
.Oo Cm set Ar N Oc Cm nptv6 Ar name Cm create Ar create-options
|
||||
@ -924,6 +939,11 @@ Pass packet to a stateless NAT64 instance (for IPv6/IPv4 network address and
|
||||
protocol translation): see the
|
||||
.Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
|
||||
Section for further information.
|
||||
.It Cm nat64clat Ar name
|
||||
Pass packet to a CLAT NAT64 instance (for client-side IPv6/IPv4 network address and
|
||||
protocol translation): see the
|
||||
.Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
|
||||
Section for further information.
|
||||
.It Cm nptv6 Ar name
|
||||
Pass packet to a NPTv6 instance (for IPv6-to-IPv6 network prefix translation):
|
||||
see the
|
||||
@ -3482,6 +3502,57 @@ Note that the behavior of stateless translator with respect to not matched
|
||||
packets differs from stateful translator.
|
||||
If corresponding addresses was not found in the lookup tables, the packet
|
||||
will not be dropped and the search continues.
|
||||
.Pp
|
||||
.Pp
|
||||
.Ss XLAT464 CLAT translation
|
||||
XLAT464 CLAT NAT64 translator implements client-side stateless translation as
|
||||
defined in RFC6877 and is very similar to statless NAT64 translator
|
||||
explained above. Instead of lookup tables it uses one-to-one mapping
|
||||
between IPv4 and IPv6 addresses using configured prefixes.
|
||||
This mode can be used as a replacement of DNS64 service for applications
|
||||
that are not using it (e.g. VoIP) allowing them to access IPv4-only Internet
|
||||
over IPv6-only networks with help of remote NAT64 translator.
|
||||
.Pp
|
||||
The CLAT NAT64 configuration command is the following:
|
||||
.Bd -ragged -offset indent
|
||||
.Bk -words
|
||||
.Cm nat64clat
|
||||
.Ar name
|
||||
.Cm create
|
||||
.Ar create-options
|
||||
.Ek
|
||||
.Ed
|
||||
.Pp
|
||||
The following parameters can be configured:
|
||||
.Bl -tag -width indent
|
||||
.It Cm clat_prefix Ar ipv6_prefix/length
|
||||
The IPv6 prefix defines IPv4-embedded IPv6 addresses used by translator
|
||||
to represent source IPv4 addresses.
|
||||
.It Cm plat_prefix Ar ipv6_prefix/length
|
||||
The IPv6 prefix defines IPv4-embedded IPv6 addresses used by translator
|
||||
to represent destination IPv4 addresses. This IPv6 prefix should be configured
|
||||
on a remote NAT64 translator.
|
||||
.It Cm log
|
||||
Turn on logging of all handled packets via BPF through
|
||||
.Ar ipfwlog0
|
||||
interface.
|
||||
.It Cm -log
|
||||
Turn off logging of all handled packets via BPF.
|
||||
.It Cm allow_private
|
||||
Turn on processing private IPv4 addresses. By default
|
||||
.Nm nat64clat
|
||||
instance will not process IPv4 packets with destination address from private
|
||||
ranges as defined in RFC1918.
|
||||
.It Cm -allow_private
|
||||
Turn off private address handling in
|
||||
.Nm nat64clat
|
||||
instance.
|
||||
.El
|
||||
.Pp
|
||||
Note that the behavior of CLAT translator with respect to not matched
|
||||
packets differs from stateful translator.
|
||||
If corresponding addresses were not matched against prefixes configured,
|
||||
the packet will not be dropped and the search continues.
|
||||
.Sh IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)
|
||||
.Nm
|
||||
supports in-kernel IPv6-to-IPv6 network prefix translation as described
|
||||
|
@ -237,6 +237,7 @@ static struct _s_x ether_types[] = {
|
||||
};
|
||||
|
||||
static struct _s_x rule_eactions[] = {
|
||||
{ "nat64clat", TOK_NAT64CLAT },
|
||||
{ "nat64lsn", TOK_NAT64LSN },
|
||||
{ "nat64stl", TOK_NAT64STL },
|
||||
{ "nptv6", TOK_NPTV6 },
|
||||
|
@ -291,6 +291,11 @@ enum tokens {
|
||||
TOK_PRIVATE,
|
||||
TOK_PRIVATEOFF,
|
||||
|
||||
/* NAT64 CLAT tokens */
|
||||
TOK_NAT64CLAT,
|
||||
TOK_PLAT_PREFIX,
|
||||
TOK_CLAT_PREFIX,
|
||||
|
||||
/* NPTv6 tokens */
|
||||
TOK_NPTV6,
|
||||
TOK_INTPREFIX,
|
||||
@ -387,6 +392,7 @@ void ipfw_flush(int force);
|
||||
void ipfw_zero(int ac, char *av[], int optname);
|
||||
void ipfw_list(int ac, char *av[], int show_counters);
|
||||
void ipfw_internal_handler(int ac, char *av[]);
|
||||
void ipfw_nat64clat_handler(int ac, char *av[]);
|
||||
void ipfw_nat64lsn_handler(int ac, char *av[]);
|
||||
void ipfw_nat64stl_handler(int ac, char *av[]);
|
||||
void ipfw_nptv6_handler(int ac, char *av[]);
|
||||
|
@ -429,6 +429,8 @@ ipfw_main(int oldac, char **oldav)
|
||||
if (co.use_set || try_next) {
|
||||
if (_substrcmp(*av, "delete") == 0)
|
||||
ipfw_delete(av);
|
||||
else if (!strncmp(*av, "nat64clat", strlen(*av)))
|
||||
ipfw_nat64clat_handler(ac, av);
|
||||
else if (!strncmp(*av, "nat64stl", strlen(*av)))
|
||||
ipfw_nat64stl_handler(ac, av);
|
||||
else if (!strncmp(*av, "nat64lsn", strlen(*av)))
|
||||
|
535
sbin/ipfw/nat64clat.c
Normal file
535
sbin/ipfw/nat64clat.c
Normal file
@ -0,0 +1,535 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Yandex LLC
|
||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "ipfw2.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <netinet6/ip_fw_nat64.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
typedef int (nat64clat_cb_t)(ipfw_nat64clat_cfg *i, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set,
|
||||
int sort);
|
||||
|
||||
static void nat64clat_create(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64clat_config(const char *name, uint8_t set, int ac, char **av);
|
||||
static void nat64clat_destroy(const char *name, uint8_t set);
|
||||
static void nat64clat_stats(const char *name, uint8_t set);
|
||||
static void nat64clat_reset_stats(const char *name, uint8_t set);
|
||||
static int nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
static int nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name,
|
||||
uint8_t set);
|
||||
|
||||
static struct _s_x nat64cmds[] = {
|
||||
{ "create", TOK_CREATE },
|
||||
{ "config", TOK_CONFIG },
|
||||
{ "destroy", TOK_DESTROY },
|
||||
{ "list", TOK_LIST },
|
||||
{ "show", TOK_LIST },
|
||||
{ "stats", TOK_STATS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
static struct _s_x nat64statscmds[] = {
|
||||
{ "reset", TOK_RESET },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* This one handles all nat64clat-related commands
|
||||
* ipfw [set N] nat64clat NAME {create | config} ...
|
||||
* ipfw [set N] nat64clat NAME stats [reset]
|
||||
* ipfw [set N] nat64clat {NAME | all} destroy
|
||||
* ipfw [set N] nat64clat {NAME | all} {list | show}
|
||||
*/
|
||||
#define nat64clat_check_name table_check_name
|
||||
void
|
||||
ipfw_nat64clat_handler(int ac, char *av[])
|
||||
{
|
||||
const char *name;
|
||||
int tcmd;
|
||||
uint8_t set;
|
||||
|
||||
if (co.use_set != 0)
|
||||
set = co.use_set - 1;
|
||||
else
|
||||
set = 0;
|
||||
ac--; av++;
|
||||
|
||||
NEED1("nat64clat needs instance name");
|
||||
name = *av;
|
||||
if (nat64clat_check_name(name) != 0) {
|
||||
if (strcmp(name, "all") == 0)
|
||||
name = NULL;
|
||||
else
|
||||
errx(EX_USAGE, "nat64clat instance name %s is invalid",
|
||||
name);
|
||||
}
|
||||
ac--; av++;
|
||||
NEED1("nat64clat needs command");
|
||||
|
||||
tcmd = get_token(nat64cmds, *av, "nat64clat command");
|
||||
if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
|
||||
errx(EX_USAGE, "nat64clat instance name required");
|
||||
switch (tcmd) {
|
||||
case TOK_CREATE:
|
||||
ac--; av++;
|
||||
nat64clat_create(name, set, ac, av);
|
||||
break;
|
||||
case TOK_CONFIG:
|
||||
ac--; av++;
|
||||
nat64clat_config(name, set, ac, av);
|
||||
break;
|
||||
case TOK_LIST:
|
||||
nat64clat_foreach(nat64clat_show_cb, name, set, 1);
|
||||
break;
|
||||
case TOK_DESTROY:
|
||||
if (name == NULL)
|
||||
nat64clat_foreach(nat64clat_destroy_cb, NULL, set, 0);
|
||||
else
|
||||
nat64clat_destroy(name, set);
|
||||
break;
|
||||
case TOK_STATS:
|
||||
ac--; av++;
|
||||
if (ac == 0) {
|
||||
nat64clat_stats(name, set);
|
||||
break;
|
||||
}
|
||||
tcmd = get_token(nat64statscmds, *av, "stats command");
|
||||
if (tcmd == TOK_RESET)
|
||||
nat64clat_reset_stats(name, set);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
nat64clat_fill_ntlv(ipfw_obj_ntlv *ntlv, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
ntlv->head.type = IPFW_TLV_EACTION_NAME(1); /* it doesn't matter */
|
||||
ntlv->head.length = sizeof(ipfw_obj_ntlv);
|
||||
ntlv->idx = 1;
|
||||
ntlv->set = set;
|
||||
strlcpy(ntlv->name, name, sizeof(ntlv->name));
|
||||
}
|
||||
|
||||
static struct _s_x nat64newcmds[] = {
|
||||
{ "plat_prefix", TOK_PLAT_PREFIX },
|
||||
{ "clat_prefix", TOK_CLAT_PREFIX },
|
||||
{ "log", TOK_LOG },
|
||||
{ "-log", TOK_LOGOFF },
|
||||
{ "allow_private", TOK_PRIVATE },
|
||||
{ "-allow_private", TOK_PRIVATEOFF },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
/*
|
||||
* Creates new nat64clat instance
|
||||
* ipfw nat64clat <NAME> create clat_prefix <prefix> plat_prefix <prefix>
|
||||
* Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
|
||||
*/
|
||||
#define NAT64CLAT_HAS_CLAT_PREFIX 0x01
|
||||
#define NAT64CLAT_HAS_PLAT_PREFIX 0x02
|
||||
static void
|
||||
nat64clat_create(const char *name, uint8_t set, int ac, char *av[])
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nat64clat_cfg)];
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
ipfw_obj_lheader *olh;
|
||||
int tcmd, flags;
|
||||
char *p;
|
||||
struct in6_addr prefix;
|
||||
uint8_t plen;
|
||||
|
||||
memset(buf, 0, sizeof(buf));
|
||||
olh = (ipfw_obj_lheader *)buf;
|
||||
cfg = (ipfw_nat64clat_cfg *)(olh + 1);
|
||||
|
||||
/* Some reasonable defaults */
|
||||
inet_pton(AF_INET6, "64:ff9b::", &cfg->plat_prefix);
|
||||
cfg->plat_plen = 96;
|
||||
cfg->set = set;
|
||||
flags = NAT64CLAT_HAS_PLAT_PREFIX;
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_PLAT_PREFIX:
|
||||
case TOK_CLAT_PREFIX:
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
NEED1("IPv6 plat_prefix required");
|
||||
} else {
|
||||
NEED1("IPv6 clat_prefix required");
|
||||
}
|
||||
|
||||
if ((p = strchr(*av, '/')) != NULL)
|
||||
*p++ = '\0';
|
||||
if (inet_pton(AF_INET6, *av, &prefix) != 1)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix: %s", *av);
|
||||
plen = strtol(p, NULL, 10);
|
||||
if (ipfw_check_nat64prefix(&prefix, plen) != 0)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix length: %s", p);
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
flags |= NAT64CLAT_HAS_PLAT_PREFIX;
|
||||
cfg->plat_prefix = prefix;
|
||||
cfg->plat_plen = plen;
|
||||
} else {
|
||||
flags |= NAT64CLAT_HAS_CLAT_PREFIX;
|
||||
cfg->clat_prefix = prefix;
|
||||
cfg->clat_plen = plen;
|
||||
}
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check validness */
|
||||
if ((flags & NAT64CLAT_HAS_PLAT_PREFIX) != NAT64CLAT_HAS_PLAT_PREFIX)
|
||||
errx(EX_USAGE, "plat_prefix required");
|
||||
if ((flags & NAT64CLAT_HAS_CLAT_PREFIX) != NAT64CLAT_HAS_CLAT_PREFIX)
|
||||
errx(EX_USAGE, "clat_prefix required");
|
||||
|
||||
olh->count = 1;
|
||||
olh->objsize = sizeof(*cfg);
|
||||
olh->size = sizeof(buf);
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
if (do_set3(IP_FW_NAT64CLAT_CREATE, &olh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64clat instance creation failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Configures existing nat64clat instance
|
||||
* ipfw nat64clat <NAME> config <options>
|
||||
* Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_config(const char *name, uint8_t set, int ac, char **av)
|
||||
{
|
||||
char buf[sizeof(ipfw_obj_header) + sizeof(ipfw_nat64clat_cfg)];
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
char *opt;
|
||||
char *p;
|
||||
size_t sz;
|
||||
int tcmd;
|
||||
struct in6_addr prefix;
|
||||
uint8_t plen;
|
||||
|
||||
if (ac == 0)
|
||||
errx(EX_USAGE, "config options required");
|
||||
memset(&buf, 0, sizeof(buf));
|
||||
oh = (ipfw_obj_header *)buf;
|
||||
cfg = (ipfw_nat64clat_cfg *)(oh + 1);
|
||||
sz = sizeof(buf);
|
||||
|
||||
nat64clat_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, &sz) != 0)
|
||||
err(EX_OSERR, "failed to get config for instance %s", name);
|
||||
|
||||
while (ac > 0) {
|
||||
tcmd = get_token(nat64newcmds, *av, "option");
|
||||
opt = *av;
|
||||
ac--; av++;
|
||||
|
||||
switch (tcmd) {
|
||||
case TOK_PLAT_PREFIX:
|
||||
case TOK_CLAT_PREFIX:
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
NEED1("IPv6 plat_prefix required");
|
||||
} else {
|
||||
NEED1("IPv6 clat_prefix required");
|
||||
}
|
||||
|
||||
if ((p = strchr(*av, '/')) != NULL)
|
||||
*p++ = '\0';
|
||||
if (inet_pton(AF_INET6, *av, &prefix) != 1)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix: %s", *av);
|
||||
plen = strtol(p, NULL, 10);
|
||||
if (ipfw_check_nat64prefix(&prefix, plen) != 0)
|
||||
errx(EX_USAGE,
|
||||
"Bad prefix length: %s", p);
|
||||
if (tcmd == TOK_PLAT_PREFIX) {
|
||||
cfg->plat_prefix = prefix;
|
||||
cfg->plat_plen = plen;
|
||||
} else {
|
||||
cfg->clat_prefix = prefix;
|
||||
cfg->clat_plen = plen;
|
||||
}
|
||||
ac--; av++;
|
||||
break;
|
||||
case TOK_LOG:
|
||||
cfg->flags |= NAT64_LOG;
|
||||
break;
|
||||
case TOK_LOGOFF:
|
||||
cfg->flags &= ~NAT64_LOG;
|
||||
break;
|
||||
case TOK_PRIVATE:
|
||||
cfg->flags |= NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
case TOK_PRIVATEOFF:
|
||||
cfg->flags &= ~NAT64_ALLOW_PRIVATE;
|
||||
break;
|
||||
default:
|
||||
errx(EX_USAGE, "Can't change %s option", opt);
|
||||
}
|
||||
}
|
||||
|
||||
if (do_set3(IP_FW_NAT64CLAT_CONFIG, &oh->opheader, sizeof(buf)) != 0)
|
||||
err(EX_OSERR, "nat64clat instance configuration failed");
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys nat64clat instance.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_destroy(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64clat_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64CLAT_DESTROY, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to destroy nat instance %s", name);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get nat64clat instance statistics.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
|
||||
*/
|
||||
static int
|
||||
nat64clat_get_stats(const char *name, uint8_t set,
|
||||
struct ipfw_nat64clat_stats *stats)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *oc;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
|
||||
oh = calloc(1, sz);
|
||||
nat64clat_fill_ntlv(&oh->ntlv, name, set);
|
||||
if (do_get3(IP_FW_NAT64CLAT_STATS, &oh->opheader, &sz) == 0) {
|
||||
oc = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memcpy(stats, oc + 1, sizeof(*stats));
|
||||
free(oh);
|
||||
return (0);
|
||||
}
|
||||
free(oh);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_stats(const char *name, uint8_t set)
|
||||
{
|
||||
struct ipfw_nat64clat_stats stats;
|
||||
|
||||
if (nat64clat_get_stats(name, set, &stats) != 0)
|
||||
err(EX_OSERR, "Error retrieving stats");
|
||||
|
||||
if (co.use_set != 0 || set != 0)
|
||||
printf("set %u ", set);
|
||||
printf("nat64clat %s\n", name);
|
||||
|
||||
printf("\t%ju packets translated from IPv6 to IPv4\n",
|
||||
(uintmax_t)stats.opcnt64);
|
||||
printf("\t%ju packets translated from IPv4 to IPv6\n",
|
||||
(uintmax_t)stats.opcnt46);
|
||||
printf("\t%ju IPv6 fragments created\n",
|
||||
(uintmax_t)stats.ofrags);
|
||||
printf("\t%ju IPv4 fragments received\n",
|
||||
(uintmax_t)stats.ifrags);
|
||||
printf("\t%ju output packets dropped due to no bufs, etc.\n",
|
||||
(uintmax_t)stats.oerrors);
|
||||
printf("\t%ju output packets discarded due to no IPv4 route\n",
|
||||
(uintmax_t)stats.noroute4);
|
||||
printf("\t%ju output packets discarded due to no IPv6 route\n",
|
||||
(uintmax_t)stats.noroute6);
|
||||
printf("\t%ju packets discarded due to unsupported protocol\n",
|
||||
(uintmax_t)stats.noproto);
|
||||
printf("\t%ju packets discarded due to memory allocation problems\n",
|
||||
(uintmax_t)stats.nomem);
|
||||
printf("\t%ju packets discarded due to some errors\n",
|
||||
(uintmax_t)stats.dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nat64clat instance statistics specified by @oh->ntlv.
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*/
|
||||
static void
|
||||
nat64clat_reset_stats(const char *name, uint8_t set)
|
||||
{
|
||||
ipfw_obj_header oh;
|
||||
|
||||
memset(&oh, 0, sizeof(oh));
|
||||
nat64clat_fill_ntlv(&oh.ntlv, name, set);
|
||||
if (do_set3(IP_FW_NAT64CLAT_RESET_STATS, &oh.opheader, sizeof(oh)) != 0)
|
||||
err(EX_OSERR, "failed to reset stats for instance %s", name);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_show_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
char plat_buf[INET6_ADDRSTRLEN], clat_buf[INET6_ADDRSTRLEN];
|
||||
|
||||
if (name != NULL && strcmp(cfg->name, name) != 0)
|
||||
return (ESRCH);
|
||||
|
||||
if (co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
if (co.use_set != 0 || cfg->set != 0)
|
||||
printf("set %u ", cfg->set);
|
||||
|
||||
inet_ntop(AF_INET6, &cfg->clat_prefix, clat_buf, sizeof(clat_buf));
|
||||
inet_ntop(AF_INET6, &cfg->plat_prefix, plat_buf, sizeof(plat_buf));
|
||||
printf("nat64clat %s clat_prefix %s/%u plat_prefix %s/%u",
|
||||
cfg->name, clat_buf, cfg->clat_plen, plat_buf, cfg->plat_plen);
|
||||
if (cfg->flags & NAT64_LOG)
|
||||
printf(" log");
|
||||
if (cfg->flags & NAT64_ALLOW_PRIVATE)
|
||||
printf(" allow_private");
|
||||
printf("\n");
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_destroy_cb(ipfw_nat64clat_cfg *cfg, const char *name, uint8_t set)
|
||||
{
|
||||
|
||||
if (co.use_set != 0 && cfg->set != set)
|
||||
return (ESRCH);
|
||||
|
||||
nat64clat_destroy(cfg->name, cfg->set);
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Compare nat64clat instances names.
|
||||
* Honor number comparison.
|
||||
*/
|
||||
static int
|
||||
nat64name_cmp(const void *a, const void *b)
|
||||
{
|
||||
ipfw_nat64clat_cfg *ca, *cb;
|
||||
|
||||
ca = (ipfw_nat64clat_cfg *)a;
|
||||
cb = (ipfw_nat64clat_cfg *)b;
|
||||
|
||||
if (ca->set > cb->set)
|
||||
return (1);
|
||||
else if (ca->set < cb->set)
|
||||
return (-1);
|
||||
return (stringnum_cmp(ca->name, cb->name));
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieves nat64clat instance list from kernel,
|
||||
* optionally sorts it and calls requested function for each instance.
|
||||
*
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
|
||||
*/
|
||||
static int
|
||||
nat64clat_foreach(nat64clat_cb_t *f, const char *name, uint8_t set, int sort)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nat64clat_cfg *cfg;
|
||||
size_t sz;
|
||||
int i, error;
|
||||
|
||||
/* Start with reasonable default */
|
||||
sz = sizeof(*olh) + 16 * sizeof(*cfg);
|
||||
for (;;) {
|
||||
if ((olh = calloc(1, sz)) == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
olh->size = sz;
|
||||
if (do_get3(IP_FW_NAT64CLAT_LIST, &olh->opheader, &sz) != 0) {
|
||||
sz = olh->size;
|
||||
free(olh);
|
||||
if (errno != ENOMEM)
|
||||
return (errno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sort != 0)
|
||||
qsort(olh + 1, olh->count, olh->objsize,
|
||||
nat64name_cmp);
|
||||
|
||||
cfg = (ipfw_nat64clat_cfg *)(olh + 1);
|
||||
for (i = 0; i < olh->count; i++) {
|
||||
error = f(cfg, name, set); /* Ignore errors for now */
|
||||
cfg = (ipfw_nat64clat_cfg *)((caddr_t)cfg +
|
||||
olh->objsize);
|
||||
}
|
||||
free(olh);
|
||||
break;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
@ -4393,6 +4393,10 @@ netpfil/ipfw/ip_fw_iface.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_nat.c optional inet ipfirewall_nat
|
||||
netpfil/ipfw/nat64/ip_fw_nat64.c optional inet inet6 ipfirewall \
|
||||
ipfirewall_nat64
|
||||
netpfil/ipfw/nat64/nat64clat.c optional inet inet6 ipfirewall \
|
||||
ipfirewall_nat64
|
||||
netpfil/ipfw/nat64/nat64clat_control.c optional inet inet6 ipfirewall \
|
||||
ipfirewall_nat64
|
||||
netpfil/ipfw/nat64/nat64lsn.c optional inet inet6 ipfirewall \
|
||||
ipfirewall_nat64
|
||||
netpfil/ipfw/nat64/nat64lsn_control.c optional inet inet6 ipfirewall \
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
KMOD= ipfw_nat64
|
||||
SRCS= ip_fw_nat64.c nat64_translate.c
|
||||
SRCS+= nat64clat.c nat64clat_control.c
|
||||
SRCS+= nat64lsn.c nat64lsn_control.c
|
||||
SRCS+= nat64stl.c nat64stl_control.c
|
||||
|
||||
|
@ -134,6 +134,13 @@ typedef struct _ip_fw3_opheader {
|
||||
#define IP_FW_NPTV6_STATS 154 /* Get NPTv6 instance statistics */
|
||||
#define IP_FW_NPTV6_RESET_STATS 155 /* Reset NPTv6 instance statistics */
|
||||
|
||||
#define IP_FW_NAT64CLAT_CREATE 160 /* Create clat NAT64 instance */
|
||||
#define IP_FW_NAT64CLAT_DESTROY 161 /* Destroy clat NAT64 instance */
|
||||
#define IP_FW_NAT64CLAT_CONFIG 162 /* Modify clat NAT64 instance */
|
||||
#define IP_FW_NAT64CLAT_LIST 163 /* List clat NAT64 instances */
|
||||
#define IP_FW_NAT64CLAT_STATS 164 /* Get NAT64CLAT instance statistics */
|
||||
#define IP_FW_NAT64CLAT_RESET_STATS 165 /* Reset NAT64CLAT instance statistics */
|
||||
|
||||
/*
|
||||
* The kernel representation of ipfw rules is made of a list of
|
||||
* 'instructions' (for all practical purposes equivalent to BPF
|
||||
|
@ -45,6 +45,19 @@ struct ipfw_nat64stl_stats {
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
};
|
||||
|
||||
struct ipfw_nat64clat_stats {
|
||||
uint64_t opcnt64; /* 6to4 of packets translated */
|
||||
uint64_t opcnt46; /* 4to6 of packets translated */
|
||||
uint64_t ofrags; /* number of fragments generated */
|
||||
uint64_t ifrags; /* number of fragments received */
|
||||
uint64_t oerrors; /* number of output errors */
|
||||
uint64_t noroute4;
|
||||
uint64_t noroute6;
|
||||
uint64_t noproto; /* Protocol not supported */
|
||||
uint64_t nomem; /* mbuf allocation failed */
|
||||
uint64_t dropped; /* dropped due to some errors */
|
||||
};
|
||||
|
||||
struct ipfw_nat64lsn_stats {
|
||||
uint64_t opcnt64; /* 6to4 of packets translated */
|
||||
uint64_t opcnt46; /* 4to6 of packets translated */
|
||||
@ -95,6 +108,17 @@ typedef struct _ipfw_nat64stl_cfg {
|
||||
uint32_t flags;
|
||||
} ipfw_nat64stl_cfg;
|
||||
|
||||
typedef struct _ipfw_nat64clat_cfg {
|
||||
char name[64]; /* NAT name */
|
||||
struct in6_addr plat_prefix; /* NAT64 (PLAT) prefix */
|
||||
struct in6_addr clat_prefix; /* Client (CLAT) prefix */
|
||||
uint8_t plat_plen; /* PLAT Prefix length */
|
||||
uint8_t clat_plen; /* CLAT Prefix length */
|
||||
uint8_t set; /* Named instance set [0..31] */
|
||||
uint8_t spare;
|
||||
uint32_t flags;
|
||||
} ipfw_nat64clat_cfg;
|
||||
|
||||
/*
|
||||
* NAT64LSN default configuration values
|
||||
*/
|
||||
|
@ -86,9 +86,15 @@ vnet_ipfw_nat64_init(const void *arg __unused)
|
||||
error = nat64stl_init(ch, first);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
error = nat64clat_init(ch, first);
|
||||
if (error != 0) {
|
||||
nat64stl_uninit(ch, first);
|
||||
return (error);
|
||||
}
|
||||
error = nat64lsn_init(ch, first);
|
||||
if (error != 0) {
|
||||
nat64stl_uninit(ch, first);
|
||||
nat64clat_uninit(ch, first);
|
||||
return (error);
|
||||
}
|
||||
return (0);
|
||||
@ -103,6 +109,7 @@ vnet_ipfw_nat64_uninit(const void *arg __unused)
|
||||
ch = &V_layer3_chain;
|
||||
last = IS_DEFAULT_VNET(curvnet) ? 1: 0;
|
||||
nat64stl_uninit(ch, last);
|
||||
nat64clat_uninit(ch, last);
|
||||
nat64lsn_uninit(ch, last);
|
||||
return (0);
|
||||
}
|
||||
|
@ -54,5 +54,7 @@ int nat64stl_init(struct ip_fw_chain *ch, int first);
|
||||
void nat64stl_uninit(struct ip_fw_chain *ch, int last);
|
||||
int nat64lsn_init(struct ip_fw_chain *ch, int first);
|
||||
void nat64lsn_uninit(struct ip_fw_chain *ch, int last);
|
||||
int nat64clat_init(struct ip_fw_chain *ch, int first);
|
||||
void nat64clat_uninit(struct ip_fw_chain *ch, int last);
|
||||
|
||||
#endif /* _IP_FW_NAT64_H_ */
|
||||
|
255
sys/netpfil/ipfw/nat64/nat64clat.c
Normal file
255
sys/netpfil/ipfw/nat64/nat64clat.c
Normal file
@ -0,0 +1,255 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Yandex LLC
|
||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/counter.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rmlock.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/if_pflog.h>
|
||||
#include <net/pfil.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <netinet/ip_var.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/icmp6.h>
|
||||
#include <netinet6/ip_fw_nat64.h>
|
||||
|
||||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
#include <netpfil/pf/pf.h>
|
||||
|
||||
#include "nat64clat.h"
|
||||
|
||||
#define NAT64_LOOKUP(chain, cmd) \
|
||||
(struct nat64clat_cfg *)SRV_OBJECT((chain), (cmd)->arg1)
|
||||
|
||||
static void
|
||||
nat64clat_log(struct pfloghdr *plog, struct mbuf *m, sa_family_t family,
|
||||
uint32_t kidx)
|
||||
{
|
||||
static uint32_t pktid = 0;
|
||||
|
||||
memset(plog, 0, sizeof(*plog));
|
||||
plog->length = PFLOG_REAL_HDRLEN;
|
||||
plog->af = family;
|
||||
plog->action = PF_NAT;
|
||||
plog->dir = PF_IN;
|
||||
plog->rulenr = htonl(kidx);
|
||||
pktid++;
|
||||
plog->subrulenr = htonl(pktid);
|
||||
plog->ruleset[0] = '\0';
|
||||
strlcpy(plog->ifname, "NAT64CLAT", sizeof(plog->ifname));
|
||||
ipfw_bpf_mtap2(plog, PFLOG_HDRLEN, m);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_handle_ip4(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct pfloghdr loghdr, *logdata;
|
||||
struct in6_addr saddr, daddr;
|
||||
struct ip *ip;
|
||||
|
||||
ip = mtod(m, struct ip*);
|
||||
/* source address for CLAT may be private with no harm */
|
||||
if (nat64_check_ip4(ip->ip_src.s_addr) != 0 ||
|
||||
nat64_check_ip4(ip->ip_dst.s_addr) != 0 ||
|
||||
nat64_check_private_ip4(&cfg->base, ip->ip_dst.s_addr) != 0)
|
||||
return (NAT64SKIP);
|
||||
|
||||
memcpy(&saddr, &cfg->base.clat_prefix, sizeof(saddr));
|
||||
nat64_embed_ip4(&saddr, cfg->base.clat_plen, ip->ip_src.s_addr);
|
||||
memcpy(&daddr, &cfg->base.plat_prefix, sizeof(daddr));
|
||||
nat64_embed_ip4(&daddr, cfg->base.plat_plen, ip->ip_dst.s_addr);
|
||||
if (cfg->base.flags & NAT64_LOG) {
|
||||
logdata = &loghdr;
|
||||
nat64clat_log(logdata, m, AF_INET, cfg->no.kidx);
|
||||
} else
|
||||
logdata = NULL;
|
||||
return (nat64_do_handle_ip4(m, &saddr, &daddr, 0, &cfg->base,
|
||||
logdata));
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_handle_ip6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct pfloghdr loghdr, *logdata;
|
||||
struct ip6_hdr *ip6;
|
||||
uint32_t aaddr;
|
||||
|
||||
/*
|
||||
* NOTE: we expect ipfw_chk() did m_pullup() up to upper level
|
||||
* protocol's headers. Also we skip some checks, that ip6_input(),
|
||||
* ip6_forward(), ip6_fastfwd() and ipfw_chk() already did.
|
||||
*/
|
||||
ip6 = mtod(m, struct ip6_hdr *);
|
||||
/* Check ip6_dst matches configured prefix */
|
||||
if (memcmp(&ip6->ip6_dst, &cfg->base.clat_prefix,
|
||||
cfg->base.clat_plen / 8) != 0)
|
||||
return (NAT64SKIP);
|
||||
/* Check ip6_src matches configured prefix */
|
||||
if (memcmp(&ip6->ip6_src, &cfg->base.plat_prefix,
|
||||
cfg->base.plat_plen / 8) != 0)
|
||||
return (NAT64SKIP);
|
||||
|
||||
if (cfg->base.flags & NAT64_LOG) {
|
||||
logdata = &loghdr;
|
||||
nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx);
|
||||
} else
|
||||
logdata = NULL;
|
||||
|
||||
aaddr = nat64_extract_ip4(&ip6->ip6_src, cfg->base.plat_plen);
|
||||
return (nat64_do_handle_ip6(m, aaddr, 0, &cfg->base, logdata));
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_handle_icmp6(struct ip_fw_chain *chain, struct nat64clat_cfg *cfg,
|
||||
struct mbuf *m)
|
||||
{
|
||||
struct pfloghdr loghdr, *logdata;
|
||||
struct nat64_counters *stats;
|
||||
struct ip6_hdr *ip6i;
|
||||
struct icmp6_hdr *icmp6;
|
||||
uint32_t daddr;
|
||||
int hlen, proto;
|
||||
|
||||
hlen = 0;
|
||||
stats = &cfg->base.stats;
|
||||
proto = nat64_getlasthdr(m, &hlen);
|
||||
if (proto != IPPROTO_ICMPV6) {
|
||||
NAT64STAT_INC(stats, dropped);
|
||||
return (NAT64MFREE);
|
||||
}
|
||||
icmp6 = mtodo(m, hlen);
|
||||
switch (icmp6->icmp6_type) {
|
||||
case ICMP6_DST_UNREACH:
|
||||
case ICMP6_PACKET_TOO_BIG:
|
||||
case ICMP6_TIME_EXCEED_TRANSIT:
|
||||
case ICMP6_PARAM_PROB:
|
||||
break;
|
||||
default:
|
||||
NAT64STAT_INC(stats, dropped);
|
||||
return (NAT64MFREE);
|
||||
}
|
||||
hlen += sizeof(struct icmp6_hdr);
|
||||
if (m->m_pkthdr.len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN) {
|
||||
NAT64STAT_INC(stats, dropped);
|
||||
return (NAT64MFREE);
|
||||
}
|
||||
if (m->m_len < hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN)
|
||||
m = m_pullup(m, hlen + sizeof(struct ip6_hdr) + ICMP_MINLEN);
|
||||
if (m == NULL) {
|
||||
NAT64STAT_INC(stats, nomem);
|
||||
return (NAT64RETURN);
|
||||
}
|
||||
/*
|
||||
* Use destination address from inner IPv6 header to determine
|
||||
* IPv4 mapped address.
|
||||
*/
|
||||
ip6i = mtodo(m, hlen);
|
||||
daddr = nat64_extract_ip4(&ip6i->ip6_dst, cfg->base.clat_plen);
|
||||
if (daddr == 0) {
|
||||
NAT64STAT_INC(stats, dropped);
|
||||
return (NAT64MFREE);
|
||||
}
|
||||
if (cfg->base.flags & NAT64_LOG) {
|
||||
logdata = &loghdr;
|
||||
nat64clat_log(logdata, m, AF_INET6, cfg->no.kidx);
|
||||
} else
|
||||
logdata = NULL;
|
||||
return (nat64_handle_icmp6(m, 0, daddr, 0, &cfg->base, logdata));
|
||||
}
|
||||
|
||||
int
|
||||
ipfw_nat64clat(struct ip_fw_chain *chain, struct ip_fw_args *args,
|
||||
ipfw_insn *cmd, int *done)
|
||||
{
|
||||
ipfw_insn *icmd;
|
||||
struct nat64clat_cfg *cfg;
|
||||
int ret;
|
||||
|
||||
IPFW_RLOCK_ASSERT(chain);
|
||||
|
||||
*done = 0; /* try next rule if not matched */
|
||||
icmd = cmd + 1;
|
||||
if (cmd->opcode != O_EXTERNAL_ACTION ||
|
||||
cmd->arg1 != V_nat64clat_eid ||
|
||||
icmd->opcode != O_EXTERNAL_INSTANCE ||
|
||||
(cfg = NAT64_LOOKUP(chain, icmd)) == NULL)
|
||||
return (0);
|
||||
|
||||
switch (args->f_id.addr_type) {
|
||||
case 4:
|
||||
ret = nat64clat_handle_ip4(chain, cfg, args->m);
|
||||
break;
|
||||
case 6:
|
||||
ret = nat64clat_handle_ip6(chain, cfg, args->m);
|
||||
break;
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (ret == NAT64SKIP) {
|
||||
/*
|
||||
* In case when packet is ICMPv6 message from an intermediate
|
||||
* router, the source address of message will not match the
|
||||
* addresses from configured prefixes.
|
||||
*/
|
||||
if (args->f_id.proto != IPPROTO_ICMPV6)
|
||||
return (0);
|
||||
|
||||
ret = nat64clat_handle_icmp6(chain, cfg, args->m);
|
||||
}
|
||||
|
||||
if (ret == NAT64SKIP)
|
||||
return (0);
|
||||
|
||||
*done = 1; /* terminate the search */
|
||||
if (ret == NAT64MFREE)
|
||||
m_freem(args->m);
|
||||
|
||||
args->m = NULL;
|
||||
return (IP_FW_NAT64);
|
||||
}
|
54
sys/netpfil/ipfw/nat64/nat64clat.h
Normal file
54
sys/netpfil/ipfw/nat64/nat64clat.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Yandex LLC
|
||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _IP_FW_NAT64CLAT_H_
|
||||
#define _IP_FW_NAT64CLAT_H_
|
||||
|
||||
#include "ip_fw_nat64.h"
|
||||
#include "nat64_translate.h"
|
||||
|
||||
struct nat64clat_cfg {
|
||||
struct named_object no;
|
||||
|
||||
struct nat64_config base;
|
||||
#define NAT64CLAT_FLAGSMASK \
|
||||
(NAT64_LOG | NAT64_ALLOW_PRIVATE) /* flags to pass to userland */
|
||||
char name[64];
|
||||
};
|
||||
|
||||
VNET_DECLARE(uint16_t, nat64clat_eid);
|
||||
#define V_nat64clat_eid VNET(nat64clat_eid)
|
||||
#define IPFW_TLV_NAT64CLAT_NAME IPFW_TLV_EACTION_NAME(V_nat64clat_eid)
|
||||
|
||||
int ipfw_nat64clat(struct ip_fw_chain *chain, struct ip_fw_args *args,
|
||||
ipfw_insn *cmd, int *done);
|
||||
|
||||
#endif
|
614
sys/netpfil/ipfw/nat64/nat64clat_control.c
Normal file
614
sys/netpfil/ipfw/nat64/nat64clat_control.c
Normal file
@ -0,0 +1,614 @@
|
||||
/*-
|
||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
||||
*
|
||||
* Copyright (c) 2019 Yandex LLC
|
||||
* Copyright (c) 2019 Andrey V. Elsukov <ae@FreeBSD.org>
|
||||
* Copyright (c) 2019 Boris N. Lytochkin <lytboris@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/counter.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/mbuf.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rmlock.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockopt.h>
|
||||
#include <sys/syslog.h>
|
||||
#include <sys/sysctl.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/route.h>
|
||||
#include <net/vnet.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip_var.h>
|
||||
#include <netinet/ip_fw.h>
|
||||
#include <netinet6/in6_var.h>
|
||||
#include <netinet6/ip6_var.h>
|
||||
#include <netinet6/ip_fw_nat64.h>
|
||||
|
||||
#include <netpfil/ipfw/ip_fw_private.h>
|
||||
|
||||
#include "nat64clat.h"
|
||||
|
||||
VNET_DEFINE(uint16_t, nat64clat_eid) = 0;
|
||||
|
||||
static struct nat64clat_cfg *nat64clat_alloc_config(const char *name,
|
||||
uint8_t set);
|
||||
static void nat64clat_free_config(struct nat64clat_cfg *cfg);
|
||||
static struct nat64clat_cfg *nat64clat_find(struct namedobj_instance *ni,
|
||||
const char *name, uint8_t set);
|
||||
|
||||
static struct nat64clat_cfg *
|
||||
nat64clat_alloc_config(const char *name, uint8_t set)
|
||||
{
|
||||
struct nat64clat_cfg *cfg;
|
||||
|
||||
cfg = malloc(sizeof(struct nat64clat_cfg), M_IPFW, M_WAITOK | M_ZERO);
|
||||
COUNTER_ARRAY_ALLOC(cfg->base.stats.cnt, NAT64STATS, M_WAITOK);
|
||||
cfg->no.name = cfg->name;
|
||||
cfg->no.etlv = IPFW_TLV_NAT64CLAT_NAME;
|
||||
cfg->no.set = set;
|
||||
strlcpy(cfg->name, name, sizeof(cfg->name));
|
||||
return (cfg);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_free_config(struct nat64clat_cfg *cfg)
|
||||
{
|
||||
|
||||
COUNTER_ARRAY_FREE(cfg->base.stats.cnt, NAT64STATS);
|
||||
free(cfg, M_IPFW);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_export_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
|
||||
ipfw_nat64clat_cfg *uc)
|
||||
{
|
||||
uc->plat_prefix = cfg->base.plat_prefix;
|
||||
uc->plat_plen = cfg->base.plat_plen;
|
||||
uc->clat_prefix = cfg->base.clat_prefix;
|
||||
uc->clat_plen = cfg->base.clat_plen;
|
||||
uc->flags = cfg->base.flags & NAT64CLAT_FLAGSMASK;
|
||||
uc->set = cfg->no.set;
|
||||
strlcpy(uc->name, cfg->no.name, sizeof(uc->name));
|
||||
}
|
||||
|
||||
struct nat64clat_dump_arg {
|
||||
struct ip_fw_chain *ch;
|
||||
struct sockopt_data *sd;
|
||||
};
|
||||
|
||||
static int
|
||||
export_config_cb(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct nat64clat_dump_arg *da = (struct nat64clat_dump_arg *)arg;
|
||||
ipfw_nat64clat_cfg *uc;
|
||||
|
||||
uc = (ipfw_nat64clat_cfg *)ipfw_get_sopt_space(da->sd, sizeof(*uc));
|
||||
nat64clat_export_config(da->ch, (struct nat64clat_cfg *)no, uc);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct nat64clat_cfg *
|
||||
nat64clat_find(struct namedobj_instance *ni, const char *name, uint8_t set)
|
||||
{
|
||||
struct nat64clat_cfg *cfg;
|
||||
|
||||
cfg = (struct nat64clat_cfg *)ipfw_objhash_lookup_name_type(ni, set,
|
||||
IPFW_TLV_NAT64CLAT_NAME, name);
|
||||
|
||||
return (cfg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates new consumer-side nat64 translator instance.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_lheader ipfw_nat64clat_cfg ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_create(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
ipfw_nat64clat_cfg *uc;
|
||||
struct namedobj_instance *ni;
|
||||
struct nat64clat_cfg *cfg;
|
||||
|
||||
if (sd->valsize != sizeof(*olh) + sizeof(*uc))
|
||||
return (EINVAL);
|
||||
|
||||
olh = (ipfw_obj_lheader *)sd->kbuf;
|
||||
uc = (ipfw_nat64clat_cfg *)(olh + 1);
|
||||
|
||||
if (ipfw_check_object_name_generic(uc->name) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
if (uc->set >= IPFW_MAX_SETS ||
|
||||
nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0 ||
|
||||
nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
ni = CHAIN_TO_SRV(ch);
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (EEXIST);
|
||||
}
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
cfg = nat64clat_alloc_config(uc->name, uc->set);
|
||||
cfg->base.plat_prefix = uc->plat_prefix;
|
||||
cfg->base.plat_plen = uc->plat_plen;
|
||||
cfg->base.clat_prefix = uc->clat_prefix;
|
||||
cfg->base.clat_plen = uc->clat_plen;
|
||||
cfg->base.flags = (uc->flags & NAT64CLAT_FLAGSMASK) |
|
||||
NAT64_CLATPFX | NAT64_PLATPFX;
|
||||
if (IN6_IS_ADDR_WKPFX(&cfg->base.plat_prefix))
|
||||
cfg->base.flags |= NAT64_WKPFX;
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
|
||||
if (nat64clat_find(ni, uc->name, uc->set) != NULL) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
nat64clat_free_config(cfg);
|
||||
return (EEXIST);
|
||||
}
|
||||
|
||||
if (ipfw_objhash_alloc_idx(ni, &cfg->no.kidx) != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
nat64clat_free_config(cfg);
|
||||
return (ENOSPC);
|
||||
}
|
||||
ipfw_objhash_add(CHAIN_TO_SRV(ch), &cfg->no);
|
||||
/* Okay, let's link data */
|
||||
SRV_OBJECT(ch, cfg->no.kidx) = cfg;
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change existing nat64clat instance configuration.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_header ipfw_nat64clat_cfg ]
|
||||
* Reply: [ ipfw_obj_header ipfw_nat64clat_cfg ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_config(struct ip_fw_chain *ch, ip_fw3_opheader *op,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_nat64clat_cfg *uc;
|
||||
struct nat64clat_cfg *cfg;
|
||||
struct namedobj_instance *ni;
|
||||
uint32_t flags;
|
||||
|
||||
if (sd->valsize != sizeof(*oh) + sizeof(*uc))
|
||||
return (EINVAL);
|
||||
|
||||
oh = (ipfw_obj_header *)ipfw_get_sopt_space(sd,
|
||||
sizeof(*oh) + sizeof(*uc));
|
||||
uc = (ipfw_nat64clat_cfg *)(oh + 1);
|
||||
|
||||
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
|
||||
oh->ntlv.set >= IPFW_MAX_SETS)
|
||||
return (EINVAL);
|
||||
|
||||
ni = CHAIN_TO_SRV(ch);
|
||||
if (sd->sopt->sopt_dir == SOPT_GET) {
|
||||
IPFW_UH_RLOCK(ch);
|
||||
cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
|
||||
if (cfg == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ENOENT);
|
||||
}
|
||||
nat64clat_export_config(ch, cfg, uc);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
cfg = nat64clat_find(ni, oh->ntlv.name, oh->ntlv.set);
|
||||
if (cfg == NULL) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* For now allow to change only following values:
|
||||
* plat_prefix, plat_plen, clat_prefix, clat_plen, flags.
|
||||
*/
|
||||
flags = 0;
|
||||
if (uc->plat_plen != cfg->base.plat_plen ||
|
||||
!IN6_ARE_ADDR_EQUAL(&uc->plat_prefix, &cfg->base.plat_prefix)) {
|
||||
if (nat64_check_prefix6(&uc->plat_prefix, uc->plat_plen) != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EINVAL);
|
||||
}
|
||||
flags |= NAT64_PLATPFX;
|
||||
}
|
||||
|
||||
if (uc->clat_plen != cfg->base.clat_plen ||
|
||||
!IN6_ARE_ADDR_EQUAL(&uc->clat_prefix, &cfg->base.clat_prefix)) {
|
||||
if (nat64_check_prefix6(&uc->clat_prefix, uc->clat_plen) != 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EINVAL);
|
||||
}
|
||||
flags |= NAT64_CLATPFX;
|
||||
}
|
||||
|
||||
if (flags != 0) {
|
||||
IPFW_WLOCK(ch);
|
||||
if (flags & NAT64_PLATPFX) {
|
||||
cfg->base.plat_prefix = uc->plat_prefix;
|
||||
cfg->base.plat_plen = uc->plat_plen;
|
||||
}
|
||||
if (flags & NAT64_CLATPFX) {
|
||||
cfg->base.clat_prefix = uc->clat_prefix;
|
||||
cfg->base.clat_plen = uc->clat_plen;
|
||||
}
|
||||
IPFW_WUNLOCK(ch);
|
||||
}
|
||||
|
||||
cfg->base.flags &= ~NAT64CLAT_FLAGSMASK;
|
||||
cfg->base.flags |= uc->flags & NAT64CLAT_FLAGSMASK;
|
||||
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_detach_config(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg)
|
||||
{
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
|
||||
ipfw_objhash_del(CHAIN_TO_SRV(ch), &cfg->no);
|
||||
ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), cfg->no.kidx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Destroys nat64 instance.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_destroy(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_header *oh;
|
||||
struct nat64clat_cfg *cfg;
|
||||
|
||||
if (sd->valsize != sizeof(*oh))
|
||||
return (EINVAL);
|
||||
|
||||
oh = (ipfw_obj_header *)sd->kbuf;
|
||||
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
|
||||
if (cfg == NULL) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (ENOENT);
|
||||
}
|
||||
if (cfg->no.refcnt > 0) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (EBUSY);
|
||||
}
|
||||
|
||||
ipfw_reset_eaction_instance(ch, V_nat64clat_eid, cfg->no.kidx);
|
||||
SRV_OBJECT(ch, cfg->no.kidx) = NULL;
|
||||
nat64clat_detach_config(ch, cfg);
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
|
||||
nat64clat_free_config(cfg);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lists all nat64clat instances currently available in kernel.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_lheader ]
|
||||
* Reply: [ ipfw_obj_lheader ipfw_nat64clat_cfg x N ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_list(struct ip_fw_chain *ch, ip_fw3_opheader *op3,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
ipfw_obj_lheader *olh;
|
||||
struct nat64clat_dump_arg da;
|
||||
|
||||
/* Check minimum header size */
|
||||
if (sd->valsize < sizeof(ipfw_obj_lheader))
|
||||
return (EINVAL);
|
||||
|
||||
olh = (ipfw_obj_lheader *)ipfw_get_sopt_header(sd, sizeof(*olh));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
olh->count = ipfw_objhash_count_type(CHAIN_TO_SRV(ch),
|
||||
IPFW_TLV_NAT64CLAT_NAME);
|
||||
olh->objsize = sizeof(ipfw_nat64clat_cfg);
|
||||
olh->size = sizeof(*olh) + olh->count * olh->objsize;
|
||||
|
||||
if (sd->valsize < olh->size) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ENOMEM);
|
||||
}
|
||||
memset(&da, 0, sizeof(da));
|
||||
da.ch = ch;
|
||||
da.sd = sd;
|
||||
ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), export_config_cb,
|
||||
&da, IPFW_TLV_NAT64CLAT_NAME);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
#define __COPY_STAT_FIELD(_cfg, _stats, _field) \
|
||||
(_stats)->_field = NAT64STAT_FETCH(&(_cfg)->base.stats, _field)
|
||||
static void
|
||||
export_stats(struct ip_fw_chain *ch, struct nat64clat_cfg *cfg,
|
||||
struct ipfw_nat64clat_stats *stats)
|
||||
{
|
||||
|
||||
__COPY_STAT_FIELD(cfg, stats, opcnt64);
|
||||
__COPY_STAT_FIELD(cfg, stats, opcnt46);
|
||||
__COPY_STAT_FIELD(cfg, stats, ofrags);
|
||||
__COPY_STAT_FIELD(cfg, stats, ifrags);
|
||||
__COPY_STAT_FIELD(cfg, stats, oerrors);
|
||||
__COPY_STAT_FIELD(cfg, stats, noroute4);
|
||||
__COPY_STAT_FIELD(cfg, stats, noroute6);
|
||||
__COPY_STAT_FIELD(cfg, stats, noproto);
|
||||
__COPY_STAT_FIELD(cfg, stats, nomem);
|
||||
__COPY_STAT_FIELD(cfg, stats, dropped);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get nat64clat statistics.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_header ]
|
||||
* Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ]]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
struct ipfw_nat64clat_stats stats;
|
||||
struct nat64clat_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
ipfw_obj_ctlv *ctlv;
|
||||
size_t sz;
|
||||
|
||||
sz = sizeof(ipfw_obj_header) + sizeof(ipfw_obj_ctlv) + sizeof(stats);
|
||||
if (sd->valsize % sizeof(uint64_t))
|
||||
return (EINVAL);
|
||||
if (sd->valsize < sz)
|
||||
return (ENOMEM);
|
||||
oh = (ipfw_obj_header *)ipfw_get_sopt_header(sd, sz);
|
||||
if (oh == NULL)
|
||||
return (EINVAL);
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
IPFW_UH_RLOCK(ch);
|
||||
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
|
||||
if (cfg == NULL) {
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
return (ENOENT);
|
||||
}
|
||||
export_stats(ch, cfg, &stats);
|
||||
IPFW_UH_RUNLOCK(ch);
|
||||
|
||||
ctlv = (ipfw_obj_ctlv *)(oh + 1);
|
||||
memset(ctlv, 0, sizeof(*ctlv));
|
||||
ctlv->head.type = IPFW_TLV_COUNTERS;
|
||||
ctlv->head.length = sz - sizeof(ipfw_obj_header);
|
||||
ctlv->count = sizeof(stats) / sizeof(uint64_t);
|
||||
ctlv->objsize = sizeof(uint64_t);
|
||||
ctlv->version = IPFW_NAT64_VERSION;
|
||||
memcpy(ctlv + 1, &stats, sizeof(stats));
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset nat64clat statistics.
|
||||
* Data layout (v0)(current):
|
||||
* Request: [ ipfw_obj_header ]
|
||||
*
|
||||
* Returns 0 on success
|
||||
*/
|
||||
static int
|
||||
nat64clat_reset_stats(struct ip_fw_chain *ch, ip_fw3_opheader *op,
|
||||
struct sockopt_data *sd)
|
||||
{
|
||||
struct nat64clat_cfg *cfg;
|
||||
ipfw_obj_header *oh;
|
||||
|
||||
if (sd->valsize != sizeof(*oh))
|
||||
return (EINVAL);
|
||||
oh = (ipfw_obj_header *)sd->kbuf;
|
||||
if (ipfw_check_object_name_generic(oh->ntlv.name) != 0 ||
|
||||
oh->ntlv.set >= IPFW_MAX_SETS)
|
||||
return (EINVAL);
|
||||
|
||||
IPFW_UH_WLOCK(ch);
|
||||
cfg = nat64clat_find(CHAIN_TO_SRV(ch), oh->ntlv.name, oh->ntlv.set);
|
||||
if (cfg == NULL) {
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (ENOENT);
|
||||
}
|
||||
COUNTER_ARRAY_ZERO(cfg->base.stats.cnt, NAT64STATS);
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static struct ipfw_sopt_handler scodes[] = {
|
||||
|
||||
{ IP_FW_NAT64CLAT_CREATE, 0, HDIR_SET, nat64clat_create },
|
||||
{ IP_FW_NAT64CLAT_DESTROY,0, HDIR_SET, nat64clat_destroy },
|
||||
{ IP_FW_NAT64CLAT_CONFIG, 0, HDIR_BOTH, nat64clat_config },
|
||||
{ IP_FW_NAT64CLAT_LIST, 0, HDIR_GET, nat64clat_list },
|
||||
{ IP_FW_NAT64CLAT_STATS, 0, HDIR_GET, nat64clat_stats },
|
||||
{ IP_FW_NAT64CLAT_RESET_STATS,0, HDIR_SET, nat64clat_reset_stats },
|
||||
};
|
||||
|
||||
static int
|
||||
nat64clat_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype)
|
||||
{
|
||||
ipfw_insn *icmd;
|
||||
|
||||
icmd = cmd - 1;
|
||||
if (icmd->opcode != O_EXTERNAL_ACTION ||
|
||||
icmd->arg1 != V_nat64clat_eid)
|
||||
return (1);
|
||||
|
||||
*puidx = cmd->arg1;
|
||||
*ptype = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
nat64clat_update_arg1(ipfw_insn *cmd, uint16_t idx)
|
||||
{
|
||||
|
||||
cmd->arg1 = idx;
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_findbyname(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||
struct named_object **pno)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = ipfw_objhash_find_type(CHAIN_TO_SRV(ch), ti,
|
||||
IPFW_TLV_NAT64CLAT_NAME, pno);
|
||||
return (err);
|
||||
}
|
||||
|
||||
static struct named_object *
|
||||
nat64clat_findbykidx(struct ip_fw_chain *ch, uint16_t idx)
|
||||
{
|
||||
struct namedobj_instance *ni;
|
||||
struct named_object *no;
|
||||
|
||||
IPFW_UH_WLOCK_ASSERT(ch);
|
||||
ni = CHAIN_TO_SRV(ch);
|
||||
no = ipfw_objhash_lookup_kidx(ni, idx);
|
||||
KASSERT(no != NULL, ("NAT with index %d not found", idx));
|
||||
|
||||
return (no);
|
||||
}
|
||||
|
||||
static int
|
||||
nat64clat_manage_sets(struct ip_fw_chain *ch, uint16_t set, uint8_t new_set,
|
||||
enum ipfw_sets_cmd cmd)
|
||||
{
|
||||
|
||||
return (ipfw_obj_manage_sets(CHAIN_TO_SRV(ch), IPFW_TLV_NAT64CLAT_NAME,
|
||||
set, new_set, cmd));
|
||||
}
|
||||
|
||||
static struct opcode_obj_rewrite opcodes[] = {
|
||||
{
|
||||
.opcode = O_EXTERNAL_INSTANCE,
|
||||
.etlv = IPFW_TLV_EACTION /* just show it isn't table */,
|
||||
.classifier = nat64clat_classify,
|
||||
.update = nat64clat_update_arg1,
|
||||
.find_byname = nat64clat_findbyname,
|
||||
.find_bykidx = nat64clat_findbykidx,
|
||||
.manage_sets = nat64clat_manage_sets,
|
||||
},
|
||||
};
|
||||
|
||||
static int
|
||||
destroy_config_cb(struct namedobj_instance *ni, struct named_object *no,
|
||||
void *arg)
|
||||
{
|
||||
struct nat64clat_cfg *cfg;
|
||||
struct ip_fw_chain *ch;
|
||||
|
||||
ch = (struct ip_fw_chain *)arg;
|
||||
cfg = (struct nat64clat_cfg *)SRV_OBJECT(ch, no->kidx);
|
||||
SRV_OBJECT(ch, no->kidx) = NULL;
|
||||
nat64clat_detach_config(ch, cfg);
|
||||
nat64clat_free_config(cfg);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
nat64clat_init(struct ip_fw_chain *ch, int first)
|
||||
{
|
||||
|
||||
V_nat64clat_eid = ipfw_add_eaction(ch, ipfw_nat64clat, "nat64clat");
|
||||
if (V_nat64clat_eid == 0)
|
||||
return (ENXIO);
|
||||
IPFW_ADD_SOPT_HANDLER(first, scodes);
|
||||
IPFW_ADD_OBJ_REWRITER(first, opcodes);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
nat64clat_uninit(struct ip_fw_chain *ch, int last)
|
||||
{
|
||||
|
||||
IPFW_DEL_OBJ_REWRITER(last, opcodes);
|
||||
IPFW_DEL_SOPT_HANDLER(last, scodes);
|
||||
ipfw_del_eaction(ch, V_nat64clat_eid);
|
||||
/*
|
||||
* Since we already have deregistered external action,
|
||||
* our named objects become unaccessible via rules, because
|
||||
* all rules were truncated by ipfw_del_eaction().
|
||||
* So, we can unlink and destroy our named objects without holding
|
||||
* IPFW_WLOCK().
|
||||
*/
|
||||
IPFW_UH_WLOCK(ch);
|
||||
ipfw_objhash_foreach_type(CHAIN_TO_SRV(ch), destroy_config_cb, ch,
|
||||
IPFW_TLV_NAT64CLAT_NAME);
|
||||
V_nat64clat_eid = 0;
|
||||
IPFW_UH_WUNLOCK(ch);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user