Retire IPFIREWALL_NAT64_DIRECT_OUTPUT kernel option. And add ability

to switch the output method in run-time. Also document some sysctl
variables that can by changed for NAT64 module.

NAT64 had compile time option IPFIREWALL_NAT64_DIRECT_OUTPUT to use
if_output directly from nat64 module. By default is used netisr based
output method. Now both methods can be used, but they require different
handling by rules.

Obtained from:	Yandex LLC
MFC after:	3 weeks
Sponsored by:	Yandex LLC
Differential Revision:	https://reviews.freebsd.org/D16647
This commit is contained in:
Andrey V. Elsukov 2018-10-21 16:29:12 +00:00
parent 91e8ea7f5c
commit aa2715612c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=339542
6 changed files with 135 additions and 43 deletions

View File

@ -1,7 +1,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd September 27, 2018
.Dd October 21, 2018
.Dt IPFW 8
.Os
.Sh NAME
@ -3289,9 +3289,14 @@ Make sure that ND6 neighbor solicitation (ICMPv6 type 135) and neighbor
advertisement (ICMPv6 type 136) messages will not be handled by translation
rules.
.Pp
After translation NAT64 translator sends packets through corresponding netisr
queue.
After translation NAT64 translator by default sends packets through
corresponding netisr queue.
Thus translator host should be configured as IPv4 and IPv6 router.
Also this means, that a packet is handled by firewall twice.
First time an original packet is handled and consumed by translator,
and then it is handled again as translated packet.
This behavior can be changed by sysctl variable
.Va net.inet.ip.fw.nat64_direct_output .
.Pp
The stateful NAT64 configuration command is the following:
.Bd -ragged -offset indent
@ -3914,6 +3919,41 @@ Default is no.
Controls whether bridged packets are passed to
.Nm .
Default is no.
.It Va net.inet.ip.fw.nat64_allow_private : No 0
Defines how
.Nm nat64
handles private IPv4 addresses:
.Bl -tag -width indent
.It Cm 0
Packets with private IPv4 will not be handled by translator
.It Cm 1
Translator will accept and process packets with private IPv4 addresses.
.El
.It Va net.inet.ip.fw.nat64_debug : No 0
Controls debugging messages produced by
.Nm ipfw_nat64
module.
.It Va net.inet.ip.fw.nat64_direct_output : No 0
Controls the output method used by
.Nm ipfw_nat64
module:
.Bl -tag -width indent
.It Cm 0
A packet is handled by
.Nm ipfw
twice.
First time an original packet is handled by
.Nm ipfw
and consumed by
.Nm ipfw_nat64
translator.
Then translated packet is queued via netisr to input processing again.
.It Cm 1
A packet is handled by
.Nm ipfw
only once, and after translation it will be pushed directly to outgoing
interface.
.El
.El
.Sh INTERNAL DIAGNOSTICS
There are some commands that may be useful to understand current state

View File

@ -439,7 +439,6 @@ IPFIREWALL opt_ipfw.h
IPFIREWALL_DEFAULT_TO_ACCEPT opt_ipfw.h
IPFIREWALL_NAT opt_ipfw.h
IPFIREWALL_NAT64 opt_ipfw.h
IPFIREWALL_NAT64_DIRECT_OUTPUT opt_ipfw.h
IPFIREWALL_NPTV6 opt_ipfw.h
IPFIREWALL_VERBOSE opt_ipfw.h
IPFIREWALL_VERBOSE_LIMIT opt_ipfw.h

View File

@ -6,8 +6,5 @@ KMOD= ipfw_nat64
SRCS= ip_fw_nat64.c nat64_translate.c
SRCS+= nat64lsn.c nat64lsn_control.c
SRCS+= nat64stl.c nat64stl_control.c
SRCS+= opt_ipfw.h
#CFLAGS+= -DIPFIREWALL_NAT64_DIRECT_OUTPUT
.include <bsd.kmod.mk>

View File

@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <netpfil/ipfw/ip_fw_private.h>
#include "ip_fw_nat64.h"
#include "nat64_translate.h"
VNET_DEFINE(int, nat64_debug) = 0;
VNET_DEFINE(int, nat64_allow_private) = 0;
@ -56,9 +57,27 @@ SYSCTL_DECL(_net_inet_ip_fw);
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, nat64_debug, CTLFLAG_VNET | CTLFLAG_RW,
&VNET_NAME(nat64_debug), 0, "Debug level for NAT64 module");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, nat64_allow_private,
CTLFLAG_VNET |CTLFLAG_RW, &VNET_NAME(nat64_allow_private), 0,
CTLFLAG_VNET | CTLFLAG_RW, &VNET_NAME(nat64_allow_private), 0,
"Allow use of non-global IPv4 addresses with NAT64");
static int
sysctl_direct_output(SYSCTL_HANDLER_ARGS)
{
uint32_t value;
int error;
value = nat64_get_output_method();
error = sysctl_handle_32(oidp, &value, 0, req);
/* Read operation or some error */
if ((error != 0) || (req->newptr == NULL))
return (error);
nat64_set_output_method(value);
return (0);
}
SYSCTL_PROC(_net_inet_ip_fw, OID_AUTO, nat64_direct_output,
CTLFLAG_VNET | CTLTYPE_U32 | CTLFLAG_RW, 0, 0, sysctl_direct_output, "IU",
"Use if_output directly instead of deffered netisr-based processing");
static int
vnet_ipfw_nat64_init(const void *arg __unused)
{

View File

@ -25,8 +25,6 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "opt_ipfw.h"
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
@ -71,6 +69,53 @@ __FBSDID("$FreeBSD$");
#include "ip_fw_nat64.h"
#include "nat64_translate.h"
typedef int (*nat64_output_t)(struct ifnet *, struct mbuf *,
struct sockaddr *, struct nat64_counters *, void *);
typedef int (*nat64_output_one_t)(struct mbuf *, struct nat64_counters *,
void *);
static int nat64_find_route4(struct nhop4_basic *, struct sockaddr_in *,
struct mbuf *);
static int nat64_find_route6(struct nhop6_basic *, struct sockaddr_in6 *,
struct mbuf *);
static int nat64_output_one(struct mbuf *, struct nat64_counters *, void *);
static int nat64_output(struct ifnet *, struct mbuf *, struct sockaddr *,
struct nat64_counters *, void *);
static int nat64_direct_output_one(struct mbuf *, struct nat64_counters *,
void *);
static int nat64_direct_output(struct ifnet *, struct mbuf *,
struct sockaddr *, struct nat64_counters *, void *);
struct nat64_methods {
nat64_output_t output;
nat64_output_one_t output_one;
};
static const struct nat64_methods nat64_netisr = {
.output = nat64_output,
.output_one = nat64_output_one
};
static const struct nat64_methods nat64_direct = {
.output = nat64_direct_output,
.output_one = nat64_direct_output_one
};
VNET_DEFINE_STATIC(const struct nat64_methods *, nat64out) = &nat64_netisr;
#define V_nat64out VNET(nat64out)
void
nat64_set_output_method(int direct)
{
V_nat64out = direct != 0 ? &nat64_direct: &nat64_netisr;
}
int
nat64_get_output_method(void)
{
return (V_nat64out == &nat64_direct ? 1: 0);
}
static void
nat64_log(struct pfloghdr *logdata, struct mbuf *m, sa_family_t family)
{
@ -80,14 +125,8 @@ nat64_log(struct pfloghdr *logdata, struct mbuf *m, sa_family_t family)
ipfw_bpf_mtap2(logdata, PFLOG_HDRLEN, m);
}
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT
static NAT64NOINLINE int nat64_find_route4(struct nhop4_basic *,
struct sockaddr_in *, struct mbuf *);
static NAT64NOINLINE int nat64_find_route6(struct nhop6_basic *,
struct sockaddr_in6 *, struct mbuf *);
static NAT64NOINLINE int
nat64_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
static int
nat64_direct_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct nat64_counters *stats, void *logdata)
{
int error;
@ -100,8 +139,9 @@ nat64_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
return (error);
}
static NAT64NOINLINE int
nat64_output_one(struct mbuf *m, struct nat64_counters *stats, void *logdata)
static int
nat64_direct_output_one(struct mbuf *m, struct nat64_counters *stats,
void *logdata)
{
struct nhop6_basic nh6;
struct nhop4_basic nh4;
@ -153,8 +193,8 @@ nat64_output_one(struct mbuf *m, struct nat64_counters *stats, void *logdata)
NAT64STAT_INC(stats, oerrors);
return (error);
}
#else /* !IPFIREWALL_NAT64_DIRECT_OUTPUT */
static NAT64NOINLINE int
static int
nat64_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
struct nat64_counters *stats, void *logdata)
{
@ -185,13 +225,12 @@ nat64_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *dst,
return (ret);
}
static NAT64NOINLINE int
static int
nat64_output_one(struct mbuf *m, struct nat64_counters *stats, void *logdata)
{
return (nat64_output(NULL, m, NULL, stats, logdata));
}
#endif /* !IPFIREWALL_NAT64_DIRECT_OUTPUT */
/*
* Check the given IPv6 prefix and length according to RFC6052:
@ -424,12 +463,10 @@ nat64_init_ip4hdr(const struct ip6_hdr *ip6, const struct ip6_frag *frag,
ip->ip_hl = sizeof(*ip) >> 2;
ip->ip_tos = (ntohl(ip6->ip6_flow) >> 20) & 0xff;
ip->ip_len = htons(sizeof(*ip) + plen);
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT
ip->ip_ttl = ip6->ip6_hlim - IPV6_HLIMDEC;
#else
/* Forwarding code will decrement TTL. */
ip->ip_ttl = ip6->ip6_hlim;
#endif
/* Forwarding code will decrement TTL for netisr based output. */
if (V_nat64out == &nat64_direct)
ip->ip_ttl -= IPV6_HLIMDEC;
ip->ip_sum = 0;
ip->ip_p = (proto == IPPROTO_ICMPV6) ? IPPROTO_ICMP: proto;
ip_fillid(ip);
@ -647,7 +684,7 @@ nat64_icmp6_reflect(struct mbuf *m, uint8_t type, uint8_t code, uint32_t mtu,
icmp6->icmp6_cksum = in6_cksum(n, IPPROTO_ICMPV6,
sizeof(struct ip6_hdr), plen);
m_freem(m);
nat64_output_one(n, stats, logdata);
V_nat64out->output_one(n, stats, logdata);
return;
freeit:
NAT64STAT_INC(stats, dropped);
@ -750,7 +787,7 @@ nat64_icmp_reflect(struct mbuf *m, uint8_t type,
icmp->icmp_cksum = in_cksum_skip(n, sizeof(struct ip) + plen,
sizeof(struct ip));
m_freem(m);
nat64_output_one(n, stats, logdata);
V_nat64out->output_one(n, stats, logdata);
return;
freeit:
NAT64STAT_INC(stats, dropped);
@ -1167,12 +1204,10 @@ nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr,
ip6.ip6_flow = htonl(ip->ip_tos << 20);
ip6.ip6_vfc |= IPV6_VERSION;
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT
ip6.ip6_hlim = ip->ip_ttl - IPTTLDEC;
#else
/* Forwarding code will decrement HLIM. */
ip6.ip6_hlim = ip->ip_ttl;
#endif
/* Forwarding code will decrement TTL for netisr based output. */
if (V_nat64out == &nat64_direct)
ip6.ip6_hlim -= IPTTLDEC;
ip6.ip6_plen = htons(plen);
ip6.ip6_nxt = (proto == IPPROTO_ICMP) ? IPPROTO_ICMPV6: proto;
/* Convert checksums. */
@ -1205,7 +1240,7 @@ nat64_do_handle_ip4(struct mbuf *m, struct in6_addr *saddr,
mbufq_init(&mq, 255);
nat64_fragment6(&cfg->stats, &ip6, &mq, m, nh.nh_mtu, ip_id, ip_off);
while ((m = mbufq_dequeue(&mq)) != NULL) {
if (nat64_output(nh.nh_ifp, m, (struct sockaddr *)&dst,
if (V_nat64out->output(nh.nh_ifp, m, (struct sockaddr *)&dst,
&cfg->stats, logdata) != 0)
break;
NAT64STAT_INC(&cfg->stats, opcnt46);
@ -1415,9 +1450,8 @@ nat64_handle_icmp6(struct mbuf *m, int hlen, uint32_t aaddr, uint16_t aport,
ip.ip_dst.s_addr = aaddr;
ip.ip_src.s_addr = nat64_extract_ip4(cfg, &ip6i->ip6_src);
/* XXX: Make fake ulp header */
#ifdef IPFIREWALL_NAT64_DIRECT_OUTPUT
ip6i->ip6_hlim += IPV6_HLIMDEC; /* init_ip4hdr will decrement it */
#endif
if (V_nat64out == &nat64_direct) /* init_ip4hdr will decrement it */
ip6i->ip6_hlim += IPV6_HLIMDEC;
nat64_init_ip4hdr(ip6i, ip6f, plen, proto, &ip);
m_adj(m, hlen - sizeof(struct ip));
bcopy(&ip, mtod(m, void *), sizeof(ip));
@ -1587,7 +1621,7 @@ nat64_do_handle_ip6(struct mbuf *m, uint32_t aaddr, uint16_t aport,
m_adj(m, hlen - sizeof(ip));
bcopy(&ip, mtod(m, void *), sizeof(ip));
if (nat64_output(nh.nh_ifp, m, (struct sockaddr *)&dst,
if (V_nat64out->output(nh.nh_ifp, m, (struct sockaddr *)&dst,
&cfg->stats, logdata) == 0)
NAT64STAT_INC(&cfg->stats, opcnt64);
return (NAT64RETURN);

View File

@ -1,6 +1,6 @@
/*-
* Copyright (c) 2015-2016 Yandex LLC
* Copyright (c) 2015-2016 Andrey V. Elsukov <ae@FreeBSD.org>
* Copyright (c) 2015-2018 Yandex LLC
* Copyright (c) 2015-2018 Andrey V. Elsukov <ae@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -142,5 +142,8 @@ void nat64_embed_ip4(const struct nat64_config *cfg, in_addr_t ia,
in_addr_t nat64_extract_ip4(const struct nat64_config *cfg,
const struct in6_addr *ip6);
void nat64_set_output_method(int);
int nat64_get_output_method(void);
#endif