Module Name: src Committed By: rmind Date: Fri Feb 7 23:45:22 UTC 2014
Modified Files: src/lib/libnpf: npf.c npf.h src/sys/net/npf: npf.h npf_nat.c src/usr.sbin/npf/npfctl: npf_build.c npf_show.c src/usr.sbin/npf/npftest: npftest.conf src/usr.sbin/npf/npftest/libnpftest: npf_nat_test.c npf_test.h Log Message: NPF: add support for static (stateless) NAT. To generate a diff of this commit: cvs rdiff -u -r1.26 -r1.27 src/lib/libnpf/npf.c cvs rdiff -u -r1.23 -r1.24 src/lib/libnpf/npf.h cvs rdiff -u -r1.35 -r1.36 src/sys/net/npf/npf.h cvs rdiff -u -r1.23 -r1.24 src/sys/net/npf/npf_nat.c cvs rdiff -u -r1.34 -r1.35 src/usr.sbin/npf/npfctl/npf_build.c cvs rdiff -u -r1.8 -r1.9 src/usr.sbin/npf/npfctl/npf_show.c cvs rdiff -u -r1.3 -r1.4 src/usr.sbin/npf/npftest/npftest.conf cvs rdiff -u -r1.6 -r1.7 src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c cvs rdiff -u -r1.13 -r1.14 src/usr.sbin/npf/npftest/libnpftest/npf_test.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/lib/libnpf/npf.c diff -u src/lib/libnpf/npf.c:1.26 src/lib/libnpf/npf.c:1.27 --- src/lib/libnpf/npf.c:1.26 Thu Feb 6 02:51:28 2014 +++ src/lib/libnpf/npf.c Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf.c,v 1.26 2014/02/06 02:51:28 rmind Exp $ */ +/* $NetBSD: npf.c,v 1.27 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2010-2014 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.26 2014/02/06 02:51:28 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.27 2014/02/07 23:45:22 rmind Exp $"); #include <sys/types.h> #include <netinet/in_systm.h> @@ -874,6 +874,16 @@ npf_nat_gettype(nl_nat_t *nt) return type; } +u_int +npf_nat_getflags(nl_nat_t *nt) +{ + prop_dictionary_t rldict = nt->nrl_dict; + unsigned flags = 0; + + prop_dictionary_get_uint32(rldict, "flags", &flags); + return flags; +} + void npf_nat_getmap(nl_nat_t *nt, npf_addr_t *addr, size_t *alen, in_port_t *port) { Index: src/lib/libnpf/npf.h diff -u src/lib/libnpf/npf.h:1.23 src/lib/libnpf/npf.h:1.24 --- src/lib/libnpf/npf.h:1.23 Thu Feb 6 02:51:28 2014 +++ src/lib/libnpf/npf.h Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf.h,v 1.23 2014/02/06 02:51:28 rmind Exp $ */ +/* $NetBSD: npf.h,v 1.24 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2011-2013 The NetBSD Foundation, Inc. @@ -136,6 +136,7 @@ int npf_table_gettype(nl_table_t *); nl_nat_t * npf_nat_iterate(nl_config_t *); int npf_nat_gettype(nl_nat_t *); +unsigned npf_nat_getflags(nl_nat_t *); void npf_nat_getmap(nl_nat_t *, npf_addr_t *, size_t *, in_port_t *); nl_rproc_t * npf_rproc_iterate(nl_config_t *); Index: src/sys/net/npf/npf.h diff -u src/sys/net/npf/npf.h:1.35 src/sys/net/npf/npf.h:1.36 --- src/sys/net/npf/npf.h:1.35 Thu Feb 6 02:51:28 2014 +++ src/sys/net/npf/npf.h Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf.h,v 1.35 2014/02/06 02:51:28 rmind Exp $ */ +/* $NetBSD: npf.h,v 1.36 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2009-2013 The NetBSD Foundation, Inc. @@ -87,7 +87,7 @@ typedef uint8_t npf_netmask_t; #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> -#define NPC_IP4 0x01 /* Indicates fetched IPv4 header. */ +#define NPC_IP4 0x01 /* Indicates IPv4 header. */ #define NPC_IP6 0x02 /* Indicates IPv6 header. */ #define NPC_IPFRAG 0x04 /* IPv4/IPv6 fragment. */ #define NPC_LAYER4 0x08 /* Layer 4 has been fetched. */ @@ -235,6 +235,7 @@ bool npf_autounload_p(void); #define NPF_NAT_PORTS 0x01 #define NPF_NAT_PORTMAP 0x02 +#define NPF_NAT_STATIC 0x04 /* Table types. */ #define NPF_TABLE_HASH 1 Index: src/sys/net/npf/npf_nat.c diff -u src/sys/net/npf/npf_nat.c:1.23 src/sys/net/npf/npf_nat.c:1.24 --- src/sys/net/npf/npf_nat.c:1.23 Fri Dec 6 01:33:37 2013 +++ src/sys/net/npf/npf_nat.c Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_nat.c,v 1.23 2013/12/06 01:33:37 rmind Exp $ */ +/* $NetBSD: npf_nat.c,v 1.24 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2010-2013 The NetBSD Foundation, Inc. @@ -70,7 +70,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.23 2013/12/06 01:33:37 rmind Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_nat.c,v 1.24 2014/02/07 23:45:22 rmind Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -423,8 +423,8 @@ npf_nat_which(const int type, bool forw) { /* * Outbound NAT rewrites: - * - Source on "forwards" stream. - * - Destination on "backwards" stream. + * - Source (NPF_SRC) on "forwards" stream. + * - Destination (NPF_DST) on "backwards" stream. * Inbound NAT is other way round. */ if (type == NPF_NATOUT) { @@ -433,7 +433,7 @@ npf_nat_which(const int type, bool forw) KASSERT(type == NPF_NATIN); } CTASSERT(NPF_SRC == 0 && NPF_DST == 1); - KASSERT(forw == 0 || forw == 1); + KASSERT(forw == NPF_SRC || forw == NPF_DST); return (u_int)forw; } @@ -528,37 +528,14 @@ out: } /* - * npf_nat_translate: perform address and/or port translation. + * npf_nat_rwr: perform address and/or port translation. */ -int -npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw) +static int +npf_nat_rwr(npf_cache_t *npc, const npf_natpolicy_t *np, + const npf_addr_t *addr, const in_addr_t port, bool forw) { - const int proto = npc->npc_proto; - const npf_natpolicy_t *np = nt->nt_natpolicy; + const unsigned proto = npc->npc_proto; 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. */ - addr = &np->n_taddr; - port = nt->nt_tport; - } else { - /* "Backwards" stream: use original address/port. */ - addr = &nt->nt_oaddr; - port = nt->nt_oport; - } - KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0); - - /* Execute ALG hook first. */ - if ((npc->npc_info & NPC_ALG_EXEC) == 0) { - npc->npc_info |= NPC_ALG_EXEC; - npf_alg_exec(npc, nbuf, nt, forw); - } /* * Rewrite IP and/or TCP/UDP checksums first, since we need the @@ -571,7 +548,6 @@ npf_nat_translate(npf_cache_t *npc, nbuf if (!npf_rwrip(npc, which, addr)) { return EINVAL; } - if ((np->n_flags & NPF_NAT_PORTS) == 0) { /* Done. */ return 0; @@ -596,6 +572,41 @@ npf_nat_translate(npf_cache_t *npc, nbuf } /* + * npf_nat_translate: perform translation given the state data. + */ +int +npf_nat_translate(npf_cache_t *npc, nbuf_t *nbuf, npf_nat_t *nt, bool forw) +{ + const npf_natpolicy_t *np = nt->nt_natpolicy; + 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. */ + addr = &np->n_taddr; + port = nt->nt_tport; + } else { + /* "Backwards" stream: use original address/port. */ + addr = &nt->nt_oaddr; + port = nt->nt_oport; + } + KASSERT((np->n_flags & NPF_NAT_PORTS) != 0 || port == 0); + + /* Execute ALG hook first. */ + if ((npc->npc_info & NPC_ALG_EXEC) == 0) { + npc->npc_info |= NPC_ALG_EXEC; + npf_alg_exec(npc, nbuf, nt, forw); + } + + /* Finally, perform the translation. */ + return npf_nat_rwr(npc, np, addr, port, forw); +} + +/* * npf_do_nat: * - Inspect packet for a NAT policy, unless a session with a NAT * association already exists. In such case, determine whether it @@ -640,6 +651,16 @@ npf_do_nat(npf_cache_t *npc, npf_session } forw = true; + /* Static NAT - just perform the translation. */ + if (np->n_flags & NPF_NAT_STATIC) { + if (nbuf_cksum_barrier(nbuf, di)) { + npf_recache(npc, nbuf); + } + error = npf_nat_rwr(npc, np, &np->n_taddr, np->n_tport, forw); + atomic_dec_uint(&np->n_refcnt); + return error; + } + /* * If there is no local session (no "stateful" rule - unusual, but * possible configuration), establish one before translation. Note @@ -688,11 +709,11 @@ translate: /* Perform the translation. */ error = npf_nat_translate(npc, nbuf, nt, forw); out: - if (error && nse) { - /* It created for NAT - just expire. */ - npf_session_expire(nse); - } - if (nse) { + if (__predict_false(nse)) { + if (error) { + /* It created for NAT - just expire. */ + npf_session_expire(nse); + } npf_session_release(nse); } return error; Index: src/usr.sbin/npf/npfctl/npf_build.c diff -u src/usr.sbin/npf/npfctl/npf_build.c:1.34 src/usr.sbin/npf/npfctl/npf_build.c:1.35 --- src/usr.sbin/npf/npfctl/npf_build.c:1.34 Thu Feb 6 18:48:09 2014 +++ src/usr.sbin/npf/npfctl/npf_build.c Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_build.c,v 1.34 2014/02/06 18:48:09 christos Exp $ */ +/* $NetBSD: npf_build.c,v 1.35 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2011-2014 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npf_build.c,v 1.34 2014/02/06 18:48:09 christos Exp $"); +__RCSID("$NetBSD: npf_build.c,v 1.35 2014/02/07 23:45:22 rmind Exp $"); #include <sys/types.h> #include <sys/mman.h> @@ -535,7 +535,7 @@ npfctl_build_rule(uint32_t attr, const c */ static void npfctl_build_nat(int type, const char *ifname, sa_family_t family, - const addr_port_t *ap, const filt_opts_t *fopts, bool binat) + const addr_port_t *ap, const filt_opts_t *fopts, u_int flags) { const opt_proto_t op = { .op_proto = -1, .op_opts = NULL }; fam_addr_mask_t *am; @@ -551,36 +551,16 @@ npfctl_build_nat(int type, const char *i yyerror("IPv6 NAT is not supported"); } - switch (type) { - case NPF_NATOUT: - /* - * Outbound NAT (or source NAT) policy, usually used for the - * traditional NAPT. If it is a half for bi-directional NAT, - * then no port translation with mapping. - */ - nat = npf_nat_create(NPF_NATOUT, !binat ? - (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0, - ifname, &am->fam_addr, am->fam_family, 0); - break; - case NPF_NATIN: - /* - * Inbound NAT (or destination NAT). Unless bi-NAT, a port - * must be specified, since it has to be redirection. - */ + if (ap->ap_portrange) { + port = npfctl_get_singleport(ap->ap_portrange); + flags &= ~NPF_NAT_PORTMAP; + flags |= NPF_NAT_PORTS; + } else { port = 0; - if (!binat) { - if (!ap->ap_portrange) { - yyerror("inbound port is not specified"); - } - port = npfctl_get_singleport(ap->ap_portrange); - } - nat = npf_nat_create(NPF_NATIN, !binat ? NPF_NAT_PORTS : 0, - ifname, &am->fam_addr, am->fam_family, port); - break; - default: - assert(false); } + nat = npf_nat_create(type, flags, ifname, + &am->fam_addr, am->fam_family, port); npfctl_build_code(nat, family, &op, fopts); npf_nat_insert(npf_conf, nat, NPF_PRI_LAST); } @@ -595,21 +575,33 @@ npfctl_build_natseg(int sd, int type, co { sa_family_t af = AF_INET; filt_opts_t imfopts; + u_int flags; bool binat; - if (sd == NPFCTL_NAT_STATIC) { - yyerror("static NAT is not yet supported"); - } - assert(sd == NPFCTL_NAT_DYNAMIC); assert(ifname != NULL); /* * Bi-directional NAT is a combination of inbound NAT and outbound - * NAT policies. Note that the translation address is local IP and - * the filter criteria is inverted accordingly. + * NAT policies with the translation segments inverted respectively. */ binat = (NPF_NATIN | NPF_NATOUT) == type; + switch (sd) { + case NPFCTL_NAT_DYNAMIC: + /* + * Dynamic NAT: traditional NAPT is expected. Unless it + * is bi-directional NAT, perform port mapping. + */ + flags = !binat ? (NPF_NAT_PORTS | NPF_NAT_PORTMAP) : 0; + break; + case NPFCTL_NAT_STATIC: + /* Static NAT: mechanic translation. */ + flags = NPF_NAT_STATIC; + break; + default: + abort(); + } + /* * If the filter criteria is not specified explicitly, apply implicit * filtering according to the given network segments. @@ -623,12 +615,12 @@ npfctl_build_natseg(int sd, int type, co if (type & NPF_NATIN) { memset(&imfopts, 0, sizeof(filt_opts_t)); memcpy(&imfopts.fo_to, ap2, sizeof(addr_port_t)); - npfctl_build_nat(NPF_NATIN, ifname, af, ap1, fopts, binat); + npfctl_build_nat(NPF_NATIN, ifname, af, ap1, fopts, flags); } if (type & NPF_NATOUT) { memset(&imfopts, 0, sizeof(filt_opts_t)); memcpy(&imfopts.fo_from, ap1, sizeof(addr_port_t)); - npfctl_build_nat(NPF_NATOUT, ifname, af, ap2, fopts, binat); + npfctl_build_nat(NPF_NATOUT, ifname, af, ap2, fopts, flags); } } Index: src/usr.sbin/npf/npfctl/npf_show.c diff -u src/usr.sbin/npf/npfctl/npf_show.c:1.8 src/usr.sbin/npf/npfctl/npf_show.c:1.9 --- src/usr.sbin/npf/npfctl/npf_show.c:1.8 Fri Nov 22 18:42:02 2013 +++ src/usr.sbin/npf/npfctl/npf_show.c Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_show.c,v 1.8 2013/11/22 18:42:02 rmind Exp $ */ +/* $NetBSD: npf_show.c,v 1.9 2014/02/07 23:45:22 rmind Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npf_show.c,v 1.8 2013/11/22 18:42:02 rmind Exp $"); +__RCSID("$NetBSD: npf_show.c,v 1.9 2014/02/07 23:45:22 rmind Exp $"); #include <sys/socket.h> #include <netinet/in.h> @@ -378,6 +378,7 @@ npfctl_print_nat(npf_conf_info_t *ctx, n npf_addr_t addr; in_port_t port; size_t alen; + u_int flags; char *seg; /* Get the interface. */ @@ -405,12 +406,14 @@ npfctl_print_nat(npf_conf_info_t *ctx, n seg2 = seg; break; default: - assert(false); + abort(); } + flags = npf_nat_getflags(nt); /* Print out the NAT policy with the filter criteria. */ - fprintf(ctx->fp, "map %s dynamic %s %s %s pass ", - ifname, seg1, arrow, seg2); + fprintf(ctx->fp, "map %s %s %s %s %s pass ", + ifname, (flags & NPF_NAT_STATIC) ? "static" : "dynamic", + seg1, arrow, seg2); npfctl_print_filter(ctx, rl); fputs("\n", ctx->fp); free(seg); Index: src/usr.sbin/npf/npftest/npftest.conf diff -u src/usr.sbin/npf/npftest/npftest.conf:1.3 src/usr.sbin/npf/npftest/npftest.conf:1.4 --- src/usr.sbin/npf/npftest/npftest.conf:1.3 Mon Sep 23 15:30:32 2013 +++ src/usr.sbin/npf/npftest/npftest.conf Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -# $NetBSD: npftest.conf,v 1.3 2013/09/23 15:30:32 rmind Exp $ +# $NetBSD: npftest.conf,v 1.4 2014/02/07 23:45:22 rmind Exp $ $ext_if = "npftest0" $int_if = "npftest1" @@ -9,6 +9,7 @@ $int_if = "npftest1" $pub_ip1 = 192.0.2.1 $pub_ip2 = 192.0.2.2 +$pub_ip3 = 192.0.2.3 $local_ip1 = 10.1.1.1 $local_ip2 = 10.1.1.2 @@ -18,11 +19,15 @@ $local_ip4 = 10.1.1.4 $local_net = { 10.1.1.0/24 } $ports = { 8000, 9000 } +map $ext_if static $local_ip3 <-> $pub_ip3 map $ext_if dynamic $local_ip2 <-> $pub_ip2 map $ext_if dynamic $local_net -> $pub_ip1 map $ext_if dynamic $local_ip1 port 6000 <- $pub_ip1 port 8000 group "ext" on $ext_if { + pass out final from $local_ip3 + pass in final to $pub_ip3 + pass stateful out final proto tcp flags S/SA all pass stateful out final from $local_net pass stateful in final to any port $ports Index: src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c diff -u src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c:1.6 src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c:1.7 --- src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c:1.6 Wed Feb 5 03:49:48 2014 +++ src/usr.sbin/npf/npftest/libnpftest/npf_nat_test.c Fri Feb 7 23:45:22 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_nat_test.c,v 1.6 2014/02/05 03:49:48 rmind Exp $ */ +/* $NetBSD: npf_nat_test.c,v 1.7 2014/02/07 23:45:22 rmind Exp $ */ /* * NPF NAT test. @@ -68,7 +68,7 @@ static const struct test_case { /* * NAT redirect (inbound NAT): - * map $ext_if dynamic $local_ip1 port 8000 <- $pub_ip1 port 8000 + * map $ext_if dynamic $local_ip1 port 6000 <- $pub_ip1 port 8000 */ { REMOTE_IP2, 16000, PUB_IP1, 8000, @@ -106,6 +106,21 @@ static const struct test_case { RESULT_PASS, LOCAL_IP2, 18000 }, + /* + * Static NAT: plain translation both ways. + * map $ext_if static $local_ip3 <-> $pub_ip3 + */ + { + LOCAL_IP3, 19000, REMOTE_IP3, 10000, + NPF_BINAT, IFNAME_EXT, PFIL_OUT, + RESULT_PASS, PUB_IP3, 19000 + }, + { + REMOTE_IP3, 10000, PUB_IP3, 19000, + NPF_BINAT, IFNAME_EXT, PFIL_IN, + RESULT_PASS, LOCAL_IP3, 19000 + }, + }; static bool Index: src/usr.sbin/npf/npftest/libnpftest/npf_test.h diff -u src/usr.sbin/npf/npftest/libnpftest/npf_test.h:1.13 src/usr.sbin/npf/npftest/libnpftest/npf_test.h:1.14 --- src/usr.sbin/npf/npftest/libnpftest/npf_test.h:1.13 Thu Feb 6 02:51:28 2014 +++ src/usr.sbin/npf/npftest/libnpftest/npf_test.h Fri Feb 7 23:45:22 2014 @@ -36,8 +36,11 @@ /* Note: RFC 5737 compliant addresses. */ #define PUB_IP1 "192.0.2.1" #define PUB_IP2 "192.0.2.2" -#define REMOTE_IP1 "192.0.2.3" -#define REMOTE_IP2 "192.0.2.4" +#define PUB_IP3 "192.0.2.3" + +#define REMOTE_IP1 "192.0.2.101" +#define REMOTE_IP2 "192.0.2.102" +#define REMOTE_IP3 "192.0.2.103" void npf_test_init(long (*)(void)); int npf_test_load(const void *);