svn commit: r345264 - in head: sbin/ipfw sys/conf sys/modules/ipfw_nat64 sys/netinet sys/netinet6 sys/netpfil/ipfw/nat64

Andrey V. Elsukov ae at FreeBSD.org
Mon Mar 18 11:44:57 UTC 2019


Author: ae
Date: Mon Mar 18 11:44:53 2019
New Revision: 345264
URL: https://svnweb.freebsd.org/changeset/base/345264

Log:
  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

Added:
  head/sbin/ipfw/nat64clat.c   (contents, props changed)
  head/sys/netpfil/ipfw/nat64/nat64clat.c   (contents, props changed)
  head/sys/netpfil/ipfw/nat64/nat64clat.h   (contents, props changed)
  head/sys/netpfil/ipfw/nat64/nat64clat_control.c   (contents, props changed)
Modified:
  head/sbin/ipfw/Makefile
  head/sbin/ipfw/ipfw.8
  head/sbin/ipfw/ipfw2.c
  head/sbin/ipfw/ipfw2.h
  head/sbin/ipfw/main.c
  head/sys/conf/files
  head/sys/modules/ipfw_nat64/Makefile
  head/sys/netinet/ip_fw.h
  head/sys/netinet6/ip_fw_nat64.h
  head/sys/netpfil/ipfw/nat64/ip_fw_nat64.c
  head/sys/netpfil/ipfw/nat64/ip_fw_nat64.h

Modified: head/sbin/ipfw/Makefile
==============================================================================
--- head/sbin/ipfw/Makefile	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sbin/ipfw/Makefile	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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"

Modified: head/sbin/ipfw/ipfw.8
==============================================================================
--- head/sbin/ipfw/ipfw.8	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sbin/ipfw/ipfw.8	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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/IP
 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 re
 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

Modified: head/sbin/ipfw/ipfw2.c
==============================================================================
--- head/sbin/ipfw/ipfw2.c	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sbin/ipfw/ipfw2.c	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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 },

Modified: head/sbin/ipfw/ipfw2.h
==============================================================================
--- head/sbin/ipfw/ipfw2.h	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sbin/ipfw/ipfw2.h	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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[]);

Modified: head/sbin/ipfw/main.c
==============================================================================
--- head/sbin/ipfw/main.c	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sbin/ipfw/main.c	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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)))

Added: head/sbin/ipfw/nat64clat.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sbin/ipfw/nat64clat.c	Mon Mar 18 11:44:53 2019	(r345264)
@@ -0,0 +1,535 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Yandex LLC
+ * Copyright (c) 2019 Andrey V. Elsukov <ae at FreeBSD.org>
+ * Copyright (c) 2019 Boris N. Lytochkin <lytboris at 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);
+}
+

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/conf/files	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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 \

Modified: head/sys/modules/ipfw_nat64/Makefile
==============================================================================
--- head/sys/modules/ipfw_nat64/Makefile	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/modules/ipfw_nat64/Makefile	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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
 

Modified: head/sys/netinet/ip_fw.h
==============================================================================
--- head/sys/netinet/ip_fw.h	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/netinet/ip_fw.h	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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

Modified: head/sys/netinet6/ip_fw_nat64.h
==============================================================================
--- head/sys/netinet6/ip_fw_nat64.h	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/netinet6/ip_fw_nat64.h	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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 */
@@ -94,6 +107,17 @@ typedef struct _ipfw_nat64stl_cfg {
 	uint8_t		spare[2];
 	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

Modified: head/sys/netpfil/ipfw/nat64/ip_fw_nat64.c
==============================================================================
--- head/sys/netpfil/ipfw/nat64/ip_fw_nat64.c	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/netpfil/ipfw/nat64/ip_fw_nat64.c	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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);
 }

Modified: head/sys/netpfil/ipfw/nat64/ip_fw_nat64.h
==============================================================================
--- head/sys/netpfil/ipfw/nat64/ip_fw_nat64.h	Mon Mar 18 10:50:32 2019	(r345263)
+++ head/sys/netpfil/ipfw/nat64/ip_fw_nat64.h	Mon Mar 18 11:44:53 2019	(r345264)
@@ -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_ */

Added: head/sys/netpfil/ipfw/nat64/nat64clat.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netpfil/ipfw/nat64/nat64clat.c	Mon Mar 18 11:44:53 2019	(r345264)
@@ -0,0 +1,255 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2019 Yandex LLC
+ * Copyright (c) 2019 Andrey V. Elsukov <ae at FreeBSD.org>
+ * Copyright (c) 2019 Boris N. Lytochkin <lytboris at 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);
+	}

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list