svn commit: r303012 - in head: sbin/ipfw sys/conf sys/modules sys/modules/ipfw_nptv6 sys/netinet sys/netinet6 sys/netpfil/ipfw/nptv6

Andrey V. Elsukov ae at FreeBSD.org
Mon Jul 18 19:46:33 UTC 2016


Author: ae
Date: Mon Jul 18 19:46:31 2016
New Revision: 303012
URL: https://svnweb.freebsd.org/changeset/base/303012

Log:
  Add ipfw_nptv6 module that implements Network Prefix Translation for IPv6
  as defined in RFC 6296. The module works together with ipfw(4) and
  implemented as its external action module. When it is loaded, it registers
  as eaction and can be used in rules. The usage pattern is similar to
  ipfw_nat(4). All matched by rule traffic goes to the NPT module.
  
  Reviewed by:	hrs
  Obtained from:	Yandex LLC
  MFC after:	1 month
  Relnotes:	yes
  Sponsored by:	Yandex LLC
  Differential Revision:	https://reviews.freebsd.org/D6420

Added:
  head/sbin/ipfw/nptv6.c   (contents, props changed)
  head/sys/modules/ipfw_nptv6/
  head/sys/modules/ipfw_nptv6/Makefile   (contents, props changed)
  head/sys/netinet6/ip_fw_nptv6.h   (contents, props changed)
  head/sys/netpfil/ipfw/nptv6/
  head/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c   (contents, props changed)
  head/sys/netpfil/ipfw/nptv6/nptv6.c   (contents, props changed)
  head/sys/netpfil/ipfw/nptv6/nptv6.h   (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/NOTES
  head/sys/conf/files
  head/sys/conf/options
  head/sys/modules/Makefile
  head/sys/netinet/ip_fw.h

Modified: head/sbin/ipfw/Makefile
==============================================================================
--- head/sbin/ipfw/Makefile	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sbin/ipfw/Makefile	Mon Jul 18 19:46:31 2016	(r303012)
@@ -5,6 +5,7 @@
 PACKAGE=ipfw
 PROG=	ipfw
 SRCS=	ipfw2.c dummynet.c ipv6.c main.c nat.c tables.c
+SRCS+=	nptv6.c
 WARNS?=	2
 
 .if ${MK_PF} != "no"

Modified: head/sbin/ipfw/ipfw.8
==============================================================================
--- head/sbin/ipfw/ipfw.8	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sbin/ipfw/ipfw.8	Mon Jul 18 19:46:31 2016	(r303012)
@@ -1,7 +1,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd May 26, 2016
+.Dd July 18, 2016
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -113,6 +113,19 @@ in-kernel NAT.
 .Oc
 .Oc
 .Ar pathname
+.Ss IPv6-to-IPv6 NETWORK PREFIX TRANSLATION
+.Nm
+.Oo Cm set Ar N Oc Cm nptv6 Ar name Cm create Ar create-options
+.Nm
+.Oo Cm set Ar N Oc Cm nptv6
+.Brq Ar name | all
+.Brq Cm list | show
+.Nm
+.Oo Cm set Ar N Oc Cm nptv6
+.Brq Ar name | all
+.Cm destroy
+.Nm
+.Oo Cm set Ar N Oc Cm nptv6 Ar name Cm stats
 .Ss INTERNAL DIAGNOSTICS
 .Nm
 .Cm internal iflist
@@ -813,6 +826,11 @@ nat instance
 see the
 .Sx NETWORK ADDRESS TRANSLATION (NAT)
 Section for further information.
+.It Cm nptv6 Ar name
+Pass packet to a NPTv6 instance (for IPv6-to-IPv6 network prefix translation):
+see the
+.Sx IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)
+Section for further information.
 .It Cm pipe Ar pipe_nr
 Pass packet to a
 .Nm dummynet
@@ -2885,6 +2903,47 @@ instances.
 See
 .Sx SYSCTL VARIABLES
 for more info.
+.Sh IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)
+.Nm
+support in-kernel IPv6-to-IPv6 network prefix translation as described
+in RFC6296.
+The kernel module
+.Cm ipfw_nptv6
+should be loaded or kernel should has
+.Cm options IPFIREWALL_NPTV6
+to be able use NPTv6 translator.
+.Pp
+The NPTv6 configuration command is the following:
+.Bd -ragged -offset indent
+.Bk -words
+.Cm nptv6
+.Ar name
+.Cm create
+.Ar create-options
+.Ek
+.Ed
+.Pp
+The following parameters can be configured:
+.Bl -tag -width indent
+.It Cm int_prefix Ar ipv6_prefix
+IPv6 prefix used in internal network.
+NPTv6 module translates source address when it matches this prefix.
+.It Cm ext_prefix Ar ipv6_prefix
+IPv6 prefix used in external network.
+NPTv6 module translates destination address when it matches this prefix.
+.It Cm prefixlen Ar length
+The length of specified IPv6 prefixes. It must be in range from 8 to 64.
+.El
+.Pp
+Note that the prefix translation rules are silently ignored when IPv6 packet
+forwarding is disabled.
+To enable the packet forwarding, set the sysctl variable
+.Va net.inet6.ip6.forwarding
+to 1.
+.Pp
+To let the packet continue after being translated, set the sysctl variable
+.Va net.inet.ip.fw.one_pass
+to 0.
 .Sh LOADER TUNABLES
 Tunables can be set in
 .Xr loader 8

Modified: head/sbin/ipfw/ipfw2.c
==============================================================================
--- head/sbin/ipfw/ipfw2.c	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sbin/ipfw/ipfw2.c	Mon Jul 18 19:46:31 2016	(r303012)
@@ -235,6 +235,7 @@ static struct _s_x ether_types[] = {
 };
 
 static struct _s_x rule_eactions[] = {
+	{ "nptv6",		TOK_NPTV6 },
 	{ NULL, 0 }	/* terminator */
 };
 

Modified: head/sbin/ipfw/ipfw2.h
==============================================================================
--- head/sbin/ipfw/ipfw2.h	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sbin/ipfw/ipfw2.h	Mon Jul 18 19:46:31 2016	(r303012)
@@ -254,6 +254,13 @@ enum tokens {
 	TOK_UNLOCK,
 	TOK_VLIST,
 	TOK_OLIST,
+	TOK_STATS,
+
+	/* NPTv6 tokens */
+	TOK_NPTV6,
+	TOK_INTPREFIX,
+	TOK_EXTPREFIX,
+	TOK_PREFIXLEN,
 };
 
 /*
@@ -340,6 +347,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_nptv6_handler(int ac, char *av[]);
 int ipfw_check_object_name(const char *name);
 
 #ifdef PF

Modified: head/sbin/ipfw/main.c
==============================================================================
--- head/sbin/ipfw/main.c	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sbin/ipfw/main.c	Mon Jul 18 19:46:31 2016	(r303012)
@@ -425,6 +425,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, "nptv6", strlen(*av)))
+			ipfw_nptv6_handler(ac, av);
 		else if (_substrcmp(*av, "flush") == 0)
 			ipfw_flush(co.do_force);
 		else if (_substrcmp(*av, "zero") == 0)

Added: head/sbin/ipfw/nptv6.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sbin/ipfw/nptv6.c	Mon Jul 18 19:46:31 2016	(r303012)
@@ -0,0 +1,399 @@
+/*-
+ * Copyright (c) 2016 Yandex LLC
+ * Copyright (c) 2016 Andrey V. Elsukov <ae at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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/socket.h>
+
+#include "ipfw2.h"
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.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_nptv6.h>
+#include <arpa/inet.h>
+
+
+typedef int (nptv6_cb_t)(ipfw_nptv6_cfg *i, const char *name, uint8_t set);
+static int nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set,
+    int sort);
+
+static void nptv6_create(const char *name, uint8_t set, int ac, char **av);
+static void nptv6_destroy(const char *name, uint8_t set);
+static void nptv6_stats(const char *name, uint8_t set);
+static int nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
+static int nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set);
+
+static struct _s_x nptv6cmds[] = {
+      { "create",	TOK_CREATE },
+      { "destroy",	TOK_DESTROY },
+      { "list",		TOK_LIST },
+      { "show",		TOK_LIST },
+      { "stats",	TOK_STATS },
+      { NULL, 0 }
+};
+
+/*
+ * This one handles all NPTv6-related commands
+ *	ipfw [set N] nptv6 NAME {create | config} ...
+ *	ipfw [set N] nptv6 NAME stats
+ *	ipfw [set N] nptv6 {NAME | all} destroy
+ *	ipfw [set N] nptv6 {NAME | all} {list | show}
+ */
+#define	nptv6_check_name	table_check_name
+void
+ipfw_nptv6_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("nptv6 needs instance name");
+	name = *av;
+	if (nptv6_check_name(name) != 0) {
+		if (strcmp(name, "all") == 0) {
+			name = NULL;
+		} else
+			errx(EX_USAGE, "nptv6 instance name %s is invalid",
+			    name);
+	}
+	ac--; av++;
+	NEED1("nptv6 needs command");
+
+	tcmd = get_token(nptv6cmds, *av, "nptv6 command");
+	if (name == NULL && tcmd != TOK_DESTROY && tcmd != TOK_LIST)
+		errx(EX_USAGE, "nptv6 instance name required");
+	switch (tcmd) {
+	case TOK_CREATE:
+		ac--; av++;
+		nptv6_create(name, set, ac, av);
+		break;
+	case TOK_LIST:
+		nptv6_foreach(nptv6_show_cb, name, set, 1);
+		break;
+	case TOK_DESTROY:
+		if (name == NULL)
+			nptv6_foreach(nptv6_destroy_cb, NULL, set, 0);
+		else
+			nptv6_destroy(name, set);
+		break;
+	case TOK_STATS:
+		nptv6_stats(name, set);
+	}
+}
+
+
+static void
+nptv6_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 nptv6newcmds[] = {
+      { "int_prefix",	TOK_INTPREFIX },
+      { "ext_prefix",	TOK_EXTPREFIX },
+      { "prefixlen",	TOK_PREFIXLEN },
+      { NULL, 0 }
+};
+
+
+static void
+nptv6_parse_prefix(const char *arg, struct in6_addr *prefix, int *len)
+{
+	char *p, *l;
+
+	p = strdup(arg);
+	if (p == NULL)
+		err(EX_OSERR, NULL);
+	if ((l = strchr(p, '/')) != NULL)
+		*l++ = '\0';
+	if (inet_pton(AF_INET6, p, prefix) != 1)
+		errx(EX_USAGE, "Bad prefix: %s", p);
+	if (l != NULL) {
+		*len = (int)strtol(l, &l, 10);
+		if (*l != '\0' || *len <= 0 || *len > 64)
+			errx(EX_USAGE, "Bad prefix length: %s", arg);
+	} else
+		*len = 0;
+	free(p);
+}
+/*
+ * Creates new nptv6 instance
+ * ipfw nptv6 <NAME> create int_prefix <prefix> ext_prefix <prefix>
+ * Request: [ ipfw_obj_lheader ipfw_nptv6_cfg ]
+ */
+#define	NPTV6_HAS_INTPREFIX	0x01
+#define	NPTV6_HAS_EXTPREFIX	0x02
+#define	NPTV6_HAS_PREFIXLEN	0x04
+static void
+nptv6_create(const char *name, uint8_t set, int ac, char *av[])
+{
+	char buf[sizeof(ipfw_obj_lheader) + sizeof(ipfw_nptv6_cfg)];
+	struct in6_addr mask;
+	ipfw_nptv6_cfg *cfg;
+	ipfw_obj_lheader *olh;
+	int tcmd, flags, plen;
+	char *p = "\0";
+
+	plen = 0;
+	memset(buf, 0, sizeof(buf));
+	olh = (ipfw_obj_lheader *)buf;
+	cfg = (ipfw_nptv6_cfg *)(olh + 1);
+	cfg->set = set;
+	flags = 0;
+	while (ac > 0) {
+		tcmd = get_token(nptv6newcmds, *av, "option");
+		ac--; av++;
+
+		switch (tcmd) {
+		case TOK_INTPREFIX:
+			NEED1("IPv6 prefix required");
+			nptv6_parse_prefix(*av, &cfg->internal, &plen);
+			flags |= NPTV6_HAS_INTPREFIX;
+			if (plen > 0)
+				goto check_prefix;
+			ac--; av++;
+			break;
+		case TOK_EXTPREFIX:
+			NEED1("IPv6 prefix required");
+			nptv6_parse_prefix(*av, &cfg->external, &plen);
+			flags |= NPTV6_HAS_EXTPREFIX;
+			if (plen > 0)
+				goto check_prefix;
+			ac--; av++;
+			break;
+		case TOK_PREFIXLEN:
+			NEED1("IPv6 prefix length required");
+			plen = strtol(*av, &p, 10);
+check_prefix:
+			if (*p != '\0' || plen < 8 || plen > 64)
+				errx(EX_USAGE, "wrong prefix length: %s", *av);
+			/* RFC 6296 Sec. 3.1 */
+			if (cfg->plen > 0 && cfg->plen != plen) {
+				warnx("Prefix length mismatch (%d vs %d).  "
+				    "It was extended up to %d",
+				    cfg->plen, plen, MAX(plen, cfg->plen));
+				plen = MAX(plen, cfg->plen);
+			}
+			cfg->plen = plen;
+			flags |= NPTV6_HAS_PREFIXLEN;
+			ac--; av++;
+			break;
+		}
+	}
+
+	/* Check validness */
+	if ((flags & NPTV6_HAS_INTPREFIX) != NPTV6_HAS_INTPREFIX)
+		errx(EX_USAGE, "int_prefix required");
+	if ((flags & NPTV6_HAS_EXTPREFIX) != NPTV6_HAS_EXTPREFIX)
+		errx(EX_USAGE, "ext_prefix required");
+	if ((flags & NPTV6_HAS_PREFIXLEN) != NPTV6_HAS_PREFIXLEN)
+		errx(EX_USAGE, "prefixlen required");
+
+	n2mask(&mask, cfg->plen);
+	APPLY_MASK(&cfg->internal, &mask);
+	APPLY_MASK(&cfg->external, &mask);
+
+	olh->count = 1;
+	olh->objsize = sizeof(*cfg);
+	olh->size = sizeof(buf);
+	strlcpy(cfg->name, name, sizeof(cfg->name));
+	if (do_set3(IP_FW_NPTV6_CREATE, &olh->opheader, sizeof(buf)) != 0)
+		err(EX_OSERR, "nptv6 instance creation failed");
+}
+
+/*
+ * Destroys NPTv6 instance.
+ * Request: [ ipfw_obj_header ]
+ */
+static void
+nptv6_destroy(const char *name, uint8_t set)
+{
+	ipfw_obj_header oh;
+
+	memset(&oh, 0, sizeof(oh));
+	nptv6_fill_ntlv(&oh.ntlv, name, set);
+	if (do_set3(IP_FW_NPTV6_DESTROY, &oh.opheader, sizeof(oh)) != 0)
+		err(EX_OSERR, "failed to destroy nat instance %s", name);
+}
+
+/*
+ * Get NPTv6 instance statistics.
+ * Request: [ ipfw_obj_header ]
+ * Reply: [ ipfw_obj_header ipfw_obj_ctlv [ uint64_t x N ] ]
+ */
+static int
+nptv6_get_stats(const char *name, uint8_t set, struct ipfw_nptv6_stats *stats)
+{
+	ipfw_obj_header *oh;
+	ipfw_obj_ctlv *oc;
+	size_t sz;
+
+	sz = sizeof(*oh) + sizeof(*oc) + sizeof(*stats);
+	oh = calloc(1, sz);
+	nptv6_fill_ntlv(&oh->ntlv, name, set);
+	if (do_get3(IP_FW_NPTV6_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
+nptv6_stats(const char *name, uint8_t set)
+{
+	struct ipfw_nptv6_stats stats;
+
+	if (nptv6_get_stats(name, set, &stats) != 0)
+		err(EX_OSERR, "Error retrieving stats");
+
+	printf("Number of packets translated (internal to external): %ju\n",
+	    (uintmax_t)stats.in2ex);
+	printf("Number of packets translated (external to internal): %ju\n",
+	    (uintmax_t)stats.ex2in);
+	printf("Number of packets dropped due to some error: %ju\n",
+	    (uintmax_t)stats.dropped);
+}
+
+static int
+nptv6_show_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
+{
+	char abuf[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->internal, abuf, sizeof(abuf));
+	printf("nptv6 %s int_prefix %s ", cfg->name, abuf);
+	inet_ntop(AF_INET6, &cfg->external, abuf, sizeof(abuf));
+	printf("ext_prefix %s prefixlen %u\n", abuf, cfg->plen);
+	return (0);
+}
+
+static int
+nptv6_destroy_cb(ipfw_nptv6_cfg *cfg, const char *name, uint8_t set)
+{
+
+	if (co.use_set != 0 && cfg->set != set)
+		return (ESRCH);
+
+	nptv6_destroy(cfg->name, cfg->set);
+	return (0);
+}
+
+
+/*
+ * Compare NPTv6 instances names.
+ * Honor number comparison.
+ */
+static int
+nptv6name_cmp(const void *a, const void *b)
+{
+	ipfw_nptv6_cfg *ca, *cb;
+
+	ca = (ipfw_nptv6_cfg *)a;
+	cb = (ipfw_nptv6_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 NPTv6 instance list from kernel,
+ * Request: [ ipfw_obj_lheader ]
+ * Reply: [ ipfw_obj_lheader ipfw_nptv6_cfg x N ]
+ */
+static int
+nptv6_foreach(nptv6_cb_t *f, const char *name, uint8_t set, int sort)
+{
+	ipfw_obj_lheader *olh;
+	ipfw_nptv6_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_NPTV6_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, nptv6name_cmp);
+
+		cfg = (ipfw_nptv6_cfg *)(olh + 1);
+		for (i = 0; i < olh->count; i++) {
+			error = f(cfg, name, set);
+			cfg = (ipfw_nptv6_cfg *)((caddr_t)cfg + olh->objsize);
+		}
+		free(olh);
+		break;
+	}
+	return (0);
+}
+

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sys/conf/NOTES	Mon Jul 18 19:46:31 2016	(r303012)
@@ -965,6 +965,8 @@ device		lagg
 # IPFIREWALL_NAT adds support for in kernel nat in ipfw, and it requires
 # LIBALIAS.
 #
+# IPFIREWALL_NPTV6 adds support for in kernel NPTv6 in ipfw.
+#
 # IPSTEALTH enables code to support stealth forwarding (i.e., forwarding
 # packets without touching the TTL).  This can be useful to hide firewalls
 # from traceroute and similar tools.
@@ -986,6 +988,7 @@ options 	IPFIREWALL_VERBOSE	#enable logg
 options 	IPFIREWALL_VERBOSE_LIMIT=100	#limit verbosity
 options 	IPFIREWALL_DEFAULT_TO_ACCEPT	#allow everything by default
 options 	IPFIREWALL_NAT		#ipfw kernel nat support
+options 	IPFIREWALL_NPTV6	#ipfw kernel IPv6 NPT support
 options 	IPDIVERT		#divert sockets
 options 	IPFILTER		#ipfilter support
 options 	IPFILTER_LOG		#ipfilter logging

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sys/conf/files	Mon Jul 18 19:46:31 2016	(r303012)
@@ -3859,6 +3859,10 @@ netpfil/ipfw/ip_fw_table_algo.c	optional
 netpfil/ipfw/ip_fw_table_value.c	optional inet ipfirewall
 netpfil/ipfw/ip_fw_iface.c	optional inet ipfirewall
 netpfil/ipfw/ip_fw_nat.c	optional inet ipfirewall_nat
+netpfil/ipfw/nptv6/ip_fw_nptv6.c	optional inet inet6 ipfirewall \
+	ipfirewall_nptv6
+netpfil/ipfw/nptv6/nptv6.c	optional inet inet6 ipfirewall \
+	ipfirewall_nptv6
 netpfil/pf/if_pflog.c		optional pflog pf inet
 netpfil/pf/if_pfsync.c		optional pfsync pf inet
 netpfil/pf/pf.c			optional pf inet

Modified: head/sys/conf/options
==============================================================================
--- head/sys/conf/options	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sys/conf/options	Mon Jul 18 19:46:31 2016	(r303012)
@@ -417,6 +417,7 @@ IPFILTER_LOOKUP		opt_ipfilter.h
 IPFIREWALL		opt_ipfw.h
 IPFIREWALL_DEFAULT_TO_ACCEPT	opt_ipfw.h
 IPFIREWALL_NAT		opt_ipfw.h
+IPFIREWALL_NPTV6	opt_ipfw.h
 IPFIREWALL_VERBOSE	opt_ipfw.h
 IPFIREWALL_VERBOSE_LIMIT	opt_ipfw.h
 IPSEC			opt_ipsec.h

Modified: head/sys/modules/Makefile
==============================================================================
--- head/sys/modules/Makefile	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sys/modules/Makefile	Mon Jul 18 19:46:31 2016	(r303012)
@@ -167,6 +167,7 @@ SUBDIR=	\
 	${_ipfilter} \
 	${_ipfw} \
 	ipfw_nat \
+	${_ipfw_nptv6} \
 	${_ipmi} \
 	ip6_mroute_mod \
 	ip_mroute_mod \
@@ -460,6 +461,10 @@ _ipdivert=	ipdivert
 _ipfw=		ipfw
 .endif
 
+.if ${MK_INET6_SUPPORT} != "no" || defined(ALL_MODULES)
+_ipfw_nptv6=	ipfw_nptv6
+.endif
+
 .if ${MK_IPFILTER} != "no" || defined(ALL_MODULES)
 _ipfilter=	ipfilter
 .endif

Added: head/sys/modules/ipfw_nptv6/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/modules/ipfw_nptv6/Makefile	Mon Jul 18 19:46:31 2016	(r303012)
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../netpfil/ipfw/nptv6
+
+KMOD=	ipfw_nptv6
+SRCS=	ip_fw_nptv6.c nptv6.c opt_ipfw.h
+
+.include <bsd.kmod.mk>

Modified: head/sys/netinet/ip_fw.h
==============================================================================
--- head/sys/netinet/ip_fw.h	Mon Jul 18 19:20:49 2016	(r303011)
+++ head/sys/netinet/ip_fw.h	Mon Jul 18 19:46:31 2016	(r303012)
@@ -109,6 +109,12 @@ typedef struct _ip_fw3_opheader {
 #define	IP_FW_DUMP_SOPTCODES	116	/* Dump available sopts/versions */
 #define	IP_FW_DUMP_SRVOBJECTS	117	/* Dump existing named objects */
 
+#define	IP_FW_NPTV6_CREATE	150	/* Create NPTv6 instance */
+#define	IP_FW_NPTV6_DESTROY	151	/* Destroy NPTv6 instance */
+#define	IP_FW_NPTV6_CONFIG	152	/* Modify NPTv6 instance */
+#define	IP_FW_NPTV6_LIST	153	/* List NPTv6 instances */
+#define	IP_FW_NPTV6_STATS	154	/* Get NPTv6 instance statistics */
+
 /*
  * The kernel representation of ipfw rules is made of a list of
  * 'instructions' (for all practical purposes equivalent to BPF
@@ -783,6 +789,7 @@ typedef struct  _ipfw_obj_tlv {
 #define	IPFW_TLV_TBLENT_LIST	8
 #define	IPFW_TLV_RANGE		9
 #define	IPFW_TLV_EACTION	10
+#define	IPFW_TLV_COUNTERS	11
 
 #define	IPFW_TLV_EACTION_BASE	1000
 #define	IPFW_TLV_EACTION_NAME(arg)	(IPFW_TLV_EACTION_BASE + (arg))

Added: head/sys/netinet6/ip_fw_nptv6.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netinet6/ip_fw_nptv6.h	Mon Jul 18 19:46:31 2016	(r303012)
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2016 Yandex LLC
+ * Copyright (c) 2016 Andrey V. Elsukov <ae at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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	_NETINET6_IP_FW_NPTV6_H_
+#define	_NETINET6_IP_FW_NPTV6_H_
+
+struct ipfw_nptv6_stats {
+	uint64_t	in2ex;		/* Int->Ext packets translated */
+	uint64_t	ex2in;		/* Ext->Int packets translated */
+	uint64_t	dropped;	/* dropped due to some errors */
+	uint64_t	reserved[5];
+};
+
+typedef struct _ipfw_nptv6_cfg {
+	char		name[64];	/* NPTv6 instance name */
+	struct in6_addr	internal;	/* NPTv6 internal prefix */
+	struct in6_addr	external;	/* NPTv6 external prefix */
+	uint8_t		plen;		/* Prefix length */
+	uint8_t		set;		/* Named instance set [0..31] */
+	uint8_t		spare[2];
+	uint32_t	flags;
+} ipfw_nptv6_cfg;
+
+#endif /* _NETINET6_IP_FW_NPTV6_H_ */
+

Added: head/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netpfil/ipfw/nptv6/ip_fw_nptv6.c	Mon Jul 18 19:46:31 2016	(r303012)
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 2016 Yandex LLC
+ * Copyright (c) 2016 Andrey V. Elsukov <ae at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/rwlock.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_fw.h>
+
+#include <netpfil/ipfw/ip_fw_private.h>
+#include <netpfil/ipfw/nptv6/nptv6.h>
+
+static int
+vnet_ipfw_nptv6_init(const void *arg __unused)
+{
+
+	return (nptv6_init(&V_layer3_chain, IS_DEFAULT_VNET(curvnet)));
+}
+
+static int
+vnet_ipfw_nptv6_uninit(const void *arg __unused)
+{
+
+	nptv6_uninit(&V_layer3_chain, IS_DEFAULT_VNET(curvnet));
+	return (0);
+}
+
+static int
+ipfw_nptv6_modevent(module_t mod, int type, void *unused)
+{
+
+	switch (type) {
+	case MOD_LOAD:
+	case MOD_UNLOAD:
+		break;
+	default:
+		return (EOPNOTSUPP);
+	}
+	return (0);
+}
+
+static moduledata_t ipfw_nptv6_mod = {
+	"ipfw_nptv6",
+	ipfw_nptv6_modevent,
+	0
+};
+
+/* Define startup order. */
+#define	IPFW_NPTV6_SI_SUB_FIREWALL	SI_SUB_PROTO_IFATTACHDOMAIN
+#define	IPFW_NPTV6_MODEVENT_ORDER	(SI_ORDER_ANY - 128) /* after ipfw */
+#define	IPFW_NPTV6_MODULE_ORDER		(IPFW_NPTV6_MODEVENT_ORDER + 1)
+#define	IPFW_NPTV6_VNET_ORDER		(IPFW_NPTV6_MODEVENT_ORDER + 2)
+
+DECLARE_MODULE(ipfw_nptv6, ipfw_nptv6_mod, IPFW_NPTV6_SI_SUB_FIREWALL,
+    IPFW_NPTV6_MODULE_ORDER);
+MODULE_DEPEND(ipfw_nptv6, ipfw, 3, 3, 3);
+MODULE_VERSION(ipfw_nptv6, 1);
+
+VNET_SYSINIT(vnet_ipfw_nptv6_init, IPFW_NPTV6_SI_SUB_FIREWALL,
+    IPFW_NPTV6_VNET_ORDER, vnet_ipfw_nptv6_init, NULL);
+VNET_SYSUNINIT(vnet_ipfw_nptv6_uninit, IPFW_NPTV6_SI_SUB_FIREWALL,
+    IPFW_NPTV6_VNET_ORDER, vnet_ipfw_nptv6_uninit, NULL);

Added: head/sys/netpfil/ipfw/nptv6/nptv6.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/netpfil/ipfw/nptv6/nptv6.c	Mon Jul 18 19:46:31 2016	(r303012)
@@ -0,0 +1,856 @@
+/*-
+ * Copyright (c) 2016 Yandex LLC
+ * Copyright (c) 2016 Andrey V. Elsukov <ae at FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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/queue.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/netisr.h>
+#include <net/pfil.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_var.h>
+#include <netinet/ip_fw.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+
+#include <netpfil/ipfw/ip_fw_private.h>
+#include <netpfil/ipfw/nptv6/nptv6.h>
+
+static VNET_DEFINE(uint16_t, nptv6_eid) = 0;
+#define	V_nptv6_eid	VNET(nptv6_eid)
+#define	IPFW_TLV_NPTV6_NAME	IPFW_TLV_EACTION_NAME(V_nptv6_eid)
+
+static struct nptv6_cfg *nptv6_alloc_config(const char *name, uint8_t set);
+static void nptv6_free_config(struct nptv6_cfg *cfg);
+static struct nptv6_cfg *nptv6_find(struct namedobj_instance *ni,
+    const char *name, uint8_t set);
+static int nptv6_rewrite_internal(struct nptv6_cfg *cfg, struct mbuf **mp,
+    int offset);
+static int nptv6_rewrite_external(struct nptv6_cfg *cfg, struct mbuf **mp,
+    int offset);
+
+#define	NPTV6_LOOKUP(chain, cmd)	\
+    (struct nptv6_cfg *)SRV_OBJECT((chain), (cmd)->arg1)
+
+#ifndef IN6_MASK_ADDR
+#define IN6_MASK_ADDR(a, m)	do { \
+	(a)->s6_addr32[0] &= (m)->s6_addr32[0]; \
+	(a)->s6_addr32[1] &= (m)->s6_addr32[1]; \
+	(a)->s6_addr32[2] &= (m)->s6_addr32[2]; \
+	(a)->s6_addr32[3] &= (m)->s6_addr32[3]; \
+} while (0)
+#endif
+#ifndef IN6_ARE_MASKED_ADDR_EQUAL
+#define IN6_ARE_MASKED_ADDR_EQUAL(d, a, m)	(	\
+	(((d)->s6_addr32[0] ^ (a)->s6_addr32[0]) & (m)->s6_addr32[0]) == 0 && \
+	(((d)->s6_addr32[1] ^ (a)->s6_addr32[1]) & (m)->s6_addr32[1]) == 0 && \
+	(((d)->s6_addr32[2] ^ (a)->s6_addr32[2]) & (m)->s6_addr32[2]) == 0 && \
+	(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
+#endif
+
+#if 0
+#define	NPTV6_DEBUG(fmt, ...)	do {			\
+	printf("%s: " fmt "\n", __func__, ## __VA_ARGS__);	\
+} while (0)
+#define	NPTV6_IPDEBUG(fmt, ...)	do {			\
+	char _s[INET6_ADDRSTRLEN], _d[INET6_ADDRSTRLEN];	\
+	printf("%s: " fmt "\n", __func__, ## __VA_ARGS__);	\
+} while (0)
+#else
+#define	NPTV6_DEBUG(fmt, ...)
+#define	NPTV6_IPDEBUG(fmt, ...)
+#endif
+
+static int
+nptv6_getlasthdr(struct nptv6_cfg *cfg, struct mbuf *m, int *offset)
+{
+	struct ip6_hdr *ip6;
+	struct ip6_hbh *hbh;
+	int proto, hlen;
+
+	hlen = (offset == NULL) ? 0: *offset;
+	if (m->m_len < hlen)
+		return (-1);
+	ip6 = mtodo(m, hlen);
+	hlen += sizeof(*ip6);
+	proto = ip6->ip6_nxt;
+	while (proto == IPPROTO_HOPOPTS || proto == IPPROTO_ROUTING ||
+	    proto == IPPROTO_DSTOPTS) {
+		hbh = mtodo(m, hlen);
+		if (m->m_len < hlen)
+			return (-1);
+		proto = hbh->ip6h_nxt;
+		hlen += hbh->ip6h_len << 3;
+	}
+	if (offset != NULL)
+		*offset = hlen;
+	return (proto);
+}
+
+static int
+nptv6_translate_icmpv6(struct nptv6_cfg *cfg, struct mbuf **mp, int offset)
+{
+	struct icmp6_hdr *icmp6;
+	struct ip6_hdr *ip6;
+	struct mbuf *m;
+
+	m = *mp;
+	if (offset > m->m_len)
+		return (-1);
+	icmp6 = mtodo(m, offset);
+	NPTV6_DEBUG("ICMPv6 type %d", icmp6->icmp6_type);
+	switch (icmp6->icmp6_type) {
+	case ICMP6_DST_UNREACH:
+	case ICMP6_PACKET_TOO_BIG:
+	case ICMP6_TIME_EXCEEDED:
+	case ICMP6_PARAM_PROB:
+		break;
+	case ICMP6_ECHO_REQUEST:
+	case ICMP6_ECHO_REPLY:
+		/* nothing to translate */
+		return (0);
+	default:
+		/*
+		 * XXX: We can add some checks to not translate NDP and MLD
+		 * messages. Currently user must explicitly allow these message
+		 * types, otherwise packets will be dropped.
+		 */
+		return (-1);
+	}
+	offset += sizeof(*icmp6);
+	if (offset + sizeof(*ip6) > m->m_pkthdr.len)
+		return (-1);
+	if (offset + sizeof(*ip6) > m->m_len)
+		*mp = m = m_pullup(m, offset + sizeof(*ip6));
+	if (m == NULL)
+		return (-1);
+	ip6 = mtodo(m, offset);
+	NPTV6_IPDEBUG("offset %d, %s -> %s %d", offset,
+	    inet_ntop(AF_INET6, &ip6->ip6_src, _s, sizeof(_s)),
+	    inet_ntop(AF_INET6, &ip6->ip6_dst, _d, sizeof(_d)),
+	    ip6->ip6_nxt);
+	if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_src,
+	    &cfg->external, &cfg->mask))
+		return (nptv6_rewrite_external(cfg, mp, offset));
+	else if (IN6_ARE_MASKED_ADDR_EQUAL(&ip6->ip6_dst,
+	    &cfg->internal, &cfg->mask))
+		return (nptv6_rewrite_internal(cfg, mp, offset));
+	/*
+	 * Addresses in the inner IPv6 header doesn't matched to
+	 * our prefixes.
+	 */
+	return (-1);
+}
+
+static int
+nptv6_search_index(struct nptv6_cfg *cfg, struct in6_addr *a)
+{
+	int idx;
+
+	if (cfg->flags & NPTV6_48PLEN)
+		return (3);

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


More information about the svn-src-all mailing list