NPF: trunk
authorrmind <rmind@NetBSD.org>
Fri, 06 Dec 2013 01:33:37 +0000
branchtrunk
changeset 222947 9eb77e8da79d
parent 222946 d73db0f423b7
child 222948 ac1ed9663348
NPF: - Adjust NAT to not assume flow direction in some cases and thus support less usual setups which are possible when using 'map' with a custom filter criteria. - Introduce NPF_SRC/NPF_DST and replace npc_src/npc_dst with npc_ips[2] for more convenient handling. - ICMP ALG: restrict matching only to the outgoing traffic, but be more direction-agnostic elsewhere.
sys/net/npf/npf.h
sys/net/npf/npf_alg.c
sys/net/npf/npf_alg_icmp.c
sys/net/npf/npf_bpf.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_inet.c
sys/net/npf/npf_nat.c
sys/net/npf/npf_session.c
--- a/sys/net/npf/npf.h	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf.h	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf.h,v 1.33 2013/11/12 00:46:34 rmind Exp $	*/
+/*	$NetBSD: npf.h,v 1.34 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -104,18 +104,24 @@
 typedef struct {
 	/* Information flags. */
 	uint32_t		npc_info;
-	/* Pointers to the IP v4/v6 addresses. */
-	npf_addr_t *		npc_srcip;
-	npf_addr_t *		npc_dstip;
-	/* Size (v4 or v6) of IP addresses. */
+
+	/*
+	 * Pointers to the IP source and destination addresses,
+	 * and the address length (4 for IPv4 or 16 for IPv6).
+	 */
+	npf_addr_t *		npc_ips[2];
 	uint8_t			npc_alen;
+
+	/* IP header length and L4 protocol. */
 	uint8_t			npc_hlen;
 	uint16_t		npc_proto;
+
 	/* IPv4, IPv6. */
 	union {
 		struct ip *		v4;
 		struct ip6_hdr *	v6;
 	} npc_ip;
+
 	/* TCP, UDP, ICMP. */
 	union {
 		struct tcphdr *		tcp;
@@ -132,6 +138,9 @@
 	return __predict_true((npc->npc_info & inf) != 0);
 }
 
+#define	NPF_SRC		0
+#define	NPF_DST		1
+
 /*
  * Network buffer interface.
  */
--- a/sys/net/npf/npf_alg.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_alg.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_alg.c,v 1.9 2013/06/02 02:20:04 rmind Exp $	*/
+/*	$NetBSD: npf_alg.c,v 1.10 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.9 2013/06/02 02:20:04 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_alg.c,v 1.10 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -58,8 +58,6 @@
 	u_int		na_slot;
 };
 
-#define	NPF_MAX_ALGS	8
-
 /* List of ALGs and the count. */
 static pserialize_t	alg_psz			__cacheline_aligned;
 static npf_alg_t	alg_list[NPF_MAX_ALGS]	__read_mostly;
@@ -218,7 +216,7 @@
  * npf_alg_exec: execute ALG hooks for translation.
  */
 void
-npf_alg_exec(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
+npf_alg_exec(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw)
 {
 	int s;
 
@@ -227,7 +225,7 @@
 		npf_alg_func_t func;
 
 		if ((func = alg_tfunc[i]) != NULL) {
-			func(npc, nbuf, nt, di);
+			func(npc, nbuf, nt, (int)forw);
 		}
 	}
 	pserialize_read_exit(s);
--- a/sys/net/npf/npf_alg_icmp.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_alg_icmp.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_alg_icmp.c,v 1.17 2013/06/02 02:20:04 rmind Exp $	*/
+/*	$NetBSD: npf_alg_icmp.c,v 1.18 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.17 2013/06/02 02:20:04 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_alg_icmp.c,v 1.18 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/module.h>
@@ -106,8 +106,8 @@
 }
 
 /*
- * npfa_icmp_match: ALG matching inspector - determines ALG case and
- * associates ALG with NAT entry.
+ * npfa_icmp_match: match inspector - determines ALG case and associates
+ * our ALG with the NAT entry.
  */
 static bool
 npfa_icmp_match(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
@@ -119,8 +119,8 @@
 	KASSERT(npf_iscached(npc, NPC_IP46));
 	KASSERT(npf_iscached(npc, NPC_LAYER4));
 
-	/* Check for low TTL. */
-	if (ip->ip_ttl > TR_MAX_TTL) {
+	/* Check for low TTL.  Also, we support outbound NAT only. */
+	if (ip->ip_ttl > TR_MAX_TTL || di != PFIL_OUT) {
 		return false;
 	}
 
@@ -303,7 +303,7 @@
 	bool ret, forw;
 
 	#define	SWAP(type, x, y) { type tmp = x; x = y; y = tmp; }
-	SWAP(npf_addr_t *, enpc.npc_srcip, enpc.npc_dstip);
+	SWAP(npf_addr_t *, enpc.npc_ips[NPF_SRC], enpc.npc_ips[NPF_DST]);
 
 	switch (enpc.npc_proto) {
 	case IPPROTO_TCP:
@@ -339,15 +339,15 @@
 }
 
 /*
- * npfa_icmp_nat: ALG inbound translation inspector, rewrite IP address
- * in the IP header, which is embedded in ICMP packet.
+ * npfa_icmp_nat: ALG translator - rewrites IP address in the IP header
+ * which is embedded in ICMP packet.  Note: backwards stream only.
  */
 static bool
-npfa_icmp_nat(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int di)
+npfa_icmp_nat(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, int forw)
 {
 	npf_cache_t enpc;
 
-	if (di != PFIL_IN || !npf_iscached(npc, NPC_ICMP))
+	if (forw || !npf_iscached(npc, NPC_ICMP))
 		return false;
 	if (!npfa_icmp_inspect(npc, nbuf, &enpc))
 		return false;
@@ -365,6 +365,9 @@
 	 * Retrieve the original address and port, then calculate ICMP
 	 * checksum for these changes in the embedded packet.  While data
 	 * is not rewritten in the cache, save IP and TCP/UDP checksums.
+	 *
+	 * XXX: Assumes NPF_NATOUT (source address/port).  Currently,
+	 * npfa_icmp_match() matches only for the PFIL_OUT traffic.
 	 */
 	const int proto = enpc.npc_proto;
 	uint16_t ipcksum = 0, l4cksum = 0;
@@ -377,7 +380,7 @@
 		const struct ip *eip = enpc.npc_ip.v4;
 		ipcksum = eip->ip_sum;
 	}
-	cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_srcip, addr);
+	cksum = npf_addr_cksum(cksum, enpc.npc_alen, enpc.npc_ips[NPF_SRC], addr);
 
 	switch (proto) {
 	case IPPROTO_TCP: {
@@ -401,10 +404,10 @@
 
 	/*
 	 * Rewrite the source IP address and port of the embedded IP header,
-	 * which represents the original packet, therefore passing PFIL_OUT.
-	 * This updates the checksums in the embedded packet.
+	 * which represents the original packet.  This updates the checksums
+	 * in the embedded packet.
 	 */
-	if (npf_nat_translate(&enpc, nbuf, nt, false, PFIL_OUT)) {
+	if (npf_nat_translate(&enpc, nbuf, nt, forw)) {
 		return false;
 	}
 
--- a/sys/net/npf/npf_bpf.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_bpf.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_bpf.c,v 1.5 2013/11/23 19:32:20 rmind Exp $	*/
+/*	$NetBSD: npf_bpf.c,v 1.6 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_bpf.c,v 1.5 2013/11/23 19:32:20 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_bpf.c,v 1.6 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/types.h>
 #include <sys/param.h>
@@ -157,6 +157,6 @@
 	if ((t = npf_tableset_getbyid(tblset, tid)) == NULL) {
 		return 0;
 	}
-	addr = (A & SRC_FLAG_BIT) ? npc->npc_srcip : npc->npc_dstip;
+	addr = npc->npc_ips[(A & SRC_FLAG_BIT) ? NPF_SRC : NPF_DST];
 	return npf_table_lookup(t, npc->npc_alen, addr) == 0;
 }
--- a/sys/net/npf/npf_impl.h	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_impl.h	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.44 2013/12/04 01:38:49 rmind Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.45 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2013 The NetBSD Foundation, Inc.
@@ -100,8 +100,12 @@
 typedef npf_session_t *(*npf_alg_sfunc_t)(npf_cache_t *, nbuf_t *, int);
 typedef void (*npf_workfunc_t)(void);
 
-/* Some artificial limits. */
+/*
+ * Some artificial limits.
+ * Note: very unlikely to have many ALGs.
+ */
 #define	NPF_MAX_RULES		(1024 * 1024)
+#define	NPF_MAX_ALGS		4
 #define	NPF_MAX_TABLES		128
 #define	NPF_MAX_RPROCS		128
 #define	NPF_MAX_IFMAP		64
@@ -184,9 +188,9 @@
 int		npf_cache_all(npf_cache_t *, nbuf_t *);
 void		npf_recache(npf_cache_t *, nbuf_t *);
 
-bool		npf_rwrip(const npf_cache_t *, int, const npf_addr_t *);
-bool		npf_rwrport(const npf_cache_t *, int, const in_port_t);
-bool		npf_rwrcksum(const npf_cache_t *, const int,
+bool		npf_rwrip(const npf_cache_t *, u_int, const npf_addr_t *);
+bool		npf_rwrport(const npf_cache_t *, u_int, const in_port_t);
+bool		npf_rwrcksum(const npf_cache_t *, u_int,
 		    const npf_addr_t *, const in_port_t);
 
 uint16_t	npf_fixup16_cksum(uint16_t, uint16_t, uint16_t);
@@ -327,8 +331,7 @@
 void		npf_nat_freealg(npf_natpolicy_t *, npf_alg_t *);
 
 int		npf_do_nat(npf_cache_t *, npf_session_t *, nbuf_t *, const int);
-int		npf_nat_translate(npf_cache_t *, nbuf_t *, npf_nat_t *,
-		    const bool, const int);
+int		npf_nat_translate(npf_cache_t *, nbuf_t *, npf_nat_t *, bool);
 void		npf_nat_destroy(npf_nat_t *);
 void		npf_nat_getorig(npf_nat_t *, npf_addr_t **, in_port_t *);
 void		npf_nat_gettrans(npf_nat_t *, npf_addr_t **, in_port_t *);
@@ -345,7 +348,7 @@
 int		npf_alg_unregister(npf_alg_t *);
 npf_alg_t *	npf_alg_construct(const char *);
 bool		npf_alg_match(npf_cache_t *, nbuf_t *, npf_nat_t *, int);
-void		npf_alg_exec(npf_cache_t *, nbuf_t *, npf_nat_t *, int);
+void		npf_alg_exec(npf_cache_t *, nbuf_t *, npf_nat_t *, bool);
 npf_session_t *	npf_alg_session(npf_cache_t *, nbuf_t *, int);
 
 /* Debugging routines. */
--- a/sys/net/npf/npf_inet.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_inet.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_inet.c,v 1.27 2013/11/22 01:48:36 rmind Exp $	*/
+/*	$NetBSD: npf_inet.c,v 1.28 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
@@ -39,7 +39,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.27 2013/11/22 01:48:36 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_inet.c,v 1.28 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -338,8 +338,8 @@
 
 		/* Cache: layer 3 - IPv4. */
 		npc->npc_alen = sizeof(struct in_addr);
-		npc->npc_srcip = (npf_addr_t *)&ip->ip_src;
-		npc->npc_dstip = (npf_addr_t *)&ip->ip_dst;
+		npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip->ip_src;
+		npc->npc_ips[NPF_DST] = (npf_addr_t *)&ip->ip_dst;
 		npc->npc_hlen = ip->ip_hl << 2;
 		npc->npc_proto = ip->ip_p;
 
@@ -413,8 +413,8 @@
 
 		/* Cache: layer 3 - IPv6. */
 		npc->npc_alen = sizeof(struct in6_addr);
-		npc->npc_srcip = (npf_addr_t *)&ip6->ip6_src;
-		npc->npc_dstip = (npf_addr_t *)&ip6->ip6_dst;
+		npc->npc_ips[NPF_SRC] = (npf_addr_t *)&ip6->ip6_src;
+		npc->npc_ips[NPF_DST]= (npf_addr_t *)&ip6->ip6_dst;
 
 		npc->npc_ip.v6 = ip6;
 		flags |= NPC_IP6;
@@ -516,17 +516,12 @@
  * npf_rwrip: rewrite required IP address.
  */
 bool
-npf_rwrip(const npf_cache_t *npc, int di, const npf_addr_t *addr)
+npf_rwrip(const npf_cache_t *npc, u_int which, const npf_addr_t *addr)
 {
-	npf_addr_t *oaddr;
-
 	KASSERT(npf_iscached(npc, NPC_IP46));
+	KASSERT(which == NPF_SRC || which == NPF_DST);
 
-	/*
-	 * Rewrite source address if outgoing and destination if incoming.
-	 */
-	oaddr = (di == PFIL_OUT) ? npc->npc_srcip : npc->npc_dstip;
-	memcpy(oaddr, addr, npc->npc_alen);
+	memcpy(npc->npc_ips[which], addr, npc->npc_alen);
 	return true;
 }
 
@@ -534,21 +529,22 @@
  * npf_rwrport: rewrite required TCP/UDP port.
  */
 bool
-npf_rwrport(const npf_cache_t *npc, int di, const in_port_t port)
+npf_rwrport(const npf_cache_t *npc, u_int which, const in_port_t port)
 {
 	const int proto = npc->npc_proto;
 	in_port_t *oport;
 
 	KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
 	KASSERT(proto == IPPROTO_TCP || proto == IPPROTO_UDP);
+	KASSERT(which == NPF_SRC || which == NPF_DST);
 
 	/* Get the offset and store the port in it. */
 	if (proto == IPPROTO_TCP) {
 		struct tcphdr *th = npc->npc_l4.tcp;
-		oport = (di == PFIL_OUT) ? &th->th_sport : &th->th_dport;
+		oport = (which == NPF_SRC) ? &th->th_sport : &th->th_dport;
 	} else {
 		struct udphdr *uh = npc->npc_l4.udp;
-		oport = (di == PFIL_OUT) ? &uh->uh_sport : &uh->uh_dport;
+		oport = (which == NPF_SRC) ? &uh->uh_sport : &uh->uh_dport;
 	}
 	memcpy(oport, &port, sizeof(in_port_t));
 	return true;
@@ -558,17 +554,17 @@
  * npf_rwrcksum: rewrite IPv4 and/or TCP/UDP checksum.
  */
 bool
-npf_rwrcksum(const npf_cache_t *npc, const int di,
+npf_rwrcksum(const npf_cache_t *npc, u_int which,
     const npf_addr_t *addr, const in_port_t port)
 {
+	const npf_addr_t *oaddr = npc->npc_ips[which];
 	const int proto = npc->npc_proto;
 	const int alen = npc->npc_alen;
-	npf_addr_t *oaddr;
 	uint16_t *ocksum;
 	in_port_t oport;
 
 	KASSERT(npf_iscached(npc, NPC_LAYER4));
-	oaddr = (di == PFIL_OUT) ? npc->npc_srcip : npc->npc_dstip;
+	KASSERT(which == NPF_SRC || which == NPF_DST);
 
 	if (npf_iscached(npc, NPC_IP4)) {
 		struct ip *ip = npc->npc_ip.v4;
@@ -597,7 +593,7 @@
 		struct tcphdr *th = npc->npc_l4.tcp;
 
 		ocksum = &th->th_sum;
-		oport = (di == PFIL_OUT) ? th->th_sport : th->th_dport;
+		oport = (which == NPF_SRC) ? th->th_sport : th->th_dport;
 	} else {
 		struct udphdr *uh = npc->npc_l4.udp;
 
@@ -607,7 +603,7 @@
 			/* No need to update. */
 			return true;
 		}
-		oport = (di == PFIL_OUT) ? uh->uh_sport : uh->uh_dport;
+		oport = (which == NPF_SRC) ? uh->uh_sport : uh->uh_dport;
 	}
 
 	uint16_t cksum = npf_addr_cksum(*ocksum, alen, oaddr, addr);
--- a/sys/net/npf/npf_nat.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_nat.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_nat.c,v 1.22 2013/12/04 01:38:49 rmind Exp $	*/
+/*	$NetBSD: npf_nat.c,v 1.23 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
@@ -43,14 +43,8 @@
  *
  *	There are two types of translation: outbound (NPF_NATOUT) and
  *	inbound (NPF_NATIN).  It should not be confused with connection
- *	direction.
- *
- *	Outbound NAT rewrites:
- *	- Source on "forwards" stream.
- *	- Destination on "backwards" stream.
- *	Inbound NAT rewrites:
- *	- Destination on "forwards" stream.
- *	- Source on "backwards" stream.
+ *	direction.  See npf_nat_which() for the description of how the
+ *	addresses are rewritten.
  *
  *	It should be noted that bi-directional NAT is a combined outbound
  *	and inbound translation, therefore constructed as two policies.
@@ -76,7 +70,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.22 2013/12/04 01:38:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.23 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -160,7 +154,6 @@
 void
 npf_nat_sysinit(void)
 {
-
 	nat_cache = pool_cache_init(sizeof(npf_nat_t), coherency_unit,
 	    0, 0, "npfnatpl", NULL, IPL_NET, NULL, NULL, NULL);
 	KASSERT(nat_cache != NULL);
@@ -169,8 +162,7 @@
 void
 npf_nat_sysfini(void)
 {
-
-	/* NAT policies should already be destroyed. */
+	/* All NAT policies should already be destroyed. */
 	pool_cache_destroy(nat_cache);
 }
 
@@ -178,7 +170,6 @@
  * npf_nat_newpolicy: create a new NAT policy.
  *
  * => Shares portmap if policy is on existing translation address.
- * => XXX: serialise at upper layer.
  */
 npf_natpolicy_t *
 npf_nat_newpolicy(prop_dictionary_t natdict, npf_ruleset_t *nrlset)
@@ -424,6 +415,29 @@
 }
 
 /*
+ * npf_nat_which: tell which address (source or destination) should be
+ * rewritten given the combination of the NAT type and flow direction.
+ */
+static inline u_int
+npf_nat_which(const int type, bool forw)
+{
+	/*
+	 * Outbound NAT rewrites:
+	 * - Source on "forwards" stream.
+	 * - Destination on "backwards" stream.
+	 * Inbound NAT is other way round.
+	 */
+	if (type == NPF_NATOUT) {
+		forw = !forw;
+	} else {
+		KASSERT(type == NPF_NATIN);
+	}
+	CTASSERT(NPF_SRC == 0 && NPF_DST == 1);
+	KASSERT(forw == 0 || forw == 1);
+	return (u_int)forw;
+}
+
+/*
  * npf_nat_inspect: inspect packet against NAT ruleset and return a policy.
  *
  * => Acquire a reference on the policy, if found.
@@ -471,12 +485,12 @@
 
 	/* Save the original address which may be rewritten. */
 	if (np->n_type == NPF_NATOUT) {
-		/* Source (local) for Outbound NAT. */
-		memcpy(&nt->nt_oaddr, npc->npc_srcip, npc->npc_alen);
+		/* Outbound NAT: source (think internal) address. */
+		memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_SRC], npc->npc_alen);
 	} else {
-		/* Destination (external) for Inbound NAT. */
+		/* Inbound NAT: destination (think external) address. */
 		KASSERT(np->n_type == NPF_NATIN);
-		memcpy(&nt->nt_oaddr, npc->npc_dstip, npc->npc_alen);
+		memcpy(&nt->nt_oaddr, npc->npc_ips[NPF_DST], npc->npc_alen);
 	}
 
 	/*
@@ -517,16 +531,17 @@
  * npf_nat_translate: perform address and/or port translation.
  */
 int
-npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt,
-    const bool forw, const int di)
+npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw)
 {
 	const int proto = npc->npc_proto;
 	const npf_natpolicy_t *np = nt->nt_natpolicy;
+	const u_int which = npf_nat_which(np->n_type, forw);
 	const npf_addr_t *addr;
 	in_port_t port;
 
 	KASSERT(npf_iscached(npc, NPC_IP46));
 	KASSERT(npf_iscached(npc, NPC_LAYER4));
+	KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
 
 	if (forw) {
 		/* "Forwards" stream: use translation address/port. */
@@ -539,33 +554,24 @@
 	}
 	KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0);
 
-	/* Process delayed checksums (XXX: NetBSD). */
-	if (nbuf_cksum_barrier(nbuf, di)) {
-		npf_recache(npc, nbuf);
-	}
-	KASSERT(!nbuf_flag_p(nbuf, NBUF_DATAREF_RESET));
-
 	/* Execute ALG hook first. */
 	if ((npc->npc_info & NPC_ALG_EXEC) == 0) {
 		npc->npc_info |= NPC_ALG_EXEC;
-		npf_alg_exec(npc, nbuf, nt, di);
+		npf_alg_exec(npc, nbuf, nt, forw);
 	}
 
 	/*
-	 * Rewrite IP and/or TCP/UDP checksums first, since it will use
-	 * the cache containing original values for checksum calculation.
+	 * Rewrite IP and/or TCP/UDP checksums first, since we need the
+	 * current (old) address/port for the calculations.  Then perform
+	 * the address translation i.e. rewrite source or destination.
 	 */
-	if (!npf_rwrcksum(npc, di, addr, port)) {
+	if (!npf_rwrcksum(npc, which, addr, port)) {
+		return EINVAL;
+	}
+	if (!npf_rwrip(npc, which, addr)) {
 		return EINVAL;
 	}
 
-	/*
-	 * Address translation: rewrite source/destination address, depending
-	 * on direction (PFIL_OUT - for source, PFIL_IN - for destination).
-	 */
-	if (!npf_rwrip(npc, di, addr)) {
-		return EINVAL;
-	}
 	if ((np->n_flags & NPF_NAT_PORTS) == 0) {
 		/* Done. */
 		return 0;
@@ -574,9 +580,8 @@
 	switch (proto) {
 	case IPPROTO_TCP:
 	case IPPROTO_UDP:
-		KASSERT(npf_iscached(npc, NPC_TCP) || npf_iscached(npc, NPC_UDP));
 		/* Rewrite source/destination port. */
-		if (!npf_rwrport(npc, di, port)) {
+		if (!npf_rwrport(npc, which, port)) {
 			return EINVAL;
 		}
 		break;
@@ -675,8 +680,13 @@
 	}
 
 translate:
+	/* May need to process the delayed checksums first (XXX: NetBSD). */
+	if (nbuf_cksum_barrier(nbuf, di)) {
+		npf_recache(npc, nbuf);
+	}
+
 	/* Perform the translation. */
-	error = npf_nat_translate(npc, nbuf, nt, forw, di);
+	error = npf_nat_translate(npc, nbuf, nt, forw);
 out:
 	if (error && nse) {
 		/* It created for NAT - just expire. */
--- a/sys/net/npf/npf_session.c	Thu Dec 05 22:51:08 2013 +0000
+++ b/sys/net/npf/npf_session.c	Fri Dec 06 01:33:37 2013 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_session.c,v 1.29 2013/12/04 01:38:49 rmind Exp $	*/
+/*	$NetBSD: npf_session.c,v 1.30 2013/12/06 01:33:37 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010-2013 The NetBSD Foundation, Inc.
@@ -92,7 +92,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.29 2013/12/04 01:38:49 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_session.c,v 1.30 2013/12/06 01:33:37 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -494,9 +494,11 @@
 	if (!npf_session_fillent(npc, &senkey)) {
 		return NULL;
 	}
-	KASSERT(npc->npc_srcip && npc->npc_dstip && npc->npc_alen > 0);
-	memcpy(&senkey.se_src_addr, npc->npc_srcip, npc->npc_alen);
-	memcpy(&senkey.se_dst_addr, npc->npc_dstip, npc->npc_alen);
+	KASSERT(npc->npc_ips[NPF_SRC] && npc->npc_ips[NPF_DST]);
+	KASSERT(npc->npc_alen > 0);
+
+	memcpy(&senkey.se_src_addr, npc->npc_ips[NPF_SRC], npc->npc_alen);
+	memcpy(&senkey.se_dst_addr, npc->npc_ips[NPF_DST], npc->npc_alen);
 	senkey.se_alen = npc->npc_alen;
 
 	/*
@@ -638,8 +640,8 @@
 	KASSERT(npf_iscached(npc, NPC_IP46));
 	alen = npc->npc_alen;
 	fw = &se->s_forw_entry;
-	memcpy(&fw->se_src_addr, npc->npc_srcip, alen);
-	memcpy(&fw->se_dst_addr, npc->npc_dstip, alen);
+	memcpy(&fw->se_src_addr, npc->npc_ips[NPF_SRC], alen);
+	memcpy(&fw->se_dst_addr, npc->npc_ips[NPF_DST], alen);
 
 	/* Protocol and interface. */
 	memset(&se->s_common_id, 0, sizeof(npf_secomid_t));
@@ -978,6 +980,7 @@
 		bool removed = (se->s_flags & SE_REMOVED) == SE_REMOVED;
 
 		nse = LIST_NEXT(se, s_list);
+		KASSERT((se->s_flags & SE_EXPIRE) != 0);
 		if (removed && se->s_refcnt == 0) {
 			/* Destroy only if removed and no references. */
 			LIST_REMOVE(se, s_list);