Module Name: src Committed By: riz Date: Sat Nov 24 04:34:44 UTC 2012
Modified Files: src/sys/net/npf [netbsd-6]: npf.c npf.h npf_ctl.c npf_impl.h npf_state_tcp.c npf_tableset.c src/usr.sbin/npf/npfctl [netbsd-6]: npf_disassemble.c npfctl.8 npfctl.c npfctl.h src/usr.sbin/npf/npftest/libnpftest [netbsd-6]: npf_table_test.c Log Message: Pull up following revision(s) (requested by rmind in ticket #702): sys/net/npf/npf_tableset.c: revision 1.15 usr.sbin/npf/npfctl/npfctl.h: revision 1.21 usr.sbin/npf/npftest/libnpftest/npf_table_test.c: revision 1.6 usr.sbin/npf/npfctl/npf_disassemble.c: revision 1.10 sys/net/npf/npf_state_tcp.c: revision 1.11 sys/net/npf/npf_impl.h: revision 1.24 sys/net/npf/npf.h: revision 1.22 sys/net/npf/npf_ctl.c: revision 1.19 sys/net/npf/npf.c: revision 1.14 usr.sbin/npf/npfctl/npfctl.8: revision 1.10 usr.sbin/npf/npfctl/npfctl.c: revision 1.21 npf_tcp_inwindow: inspect the sequence numbers even if the packet contains no data, fixing up only the RST to the initial SYN. This makes off-path attacks more difficult. For the reference, see "Reflection Scan: an Off-Path Attack on TCP" by Jan Wrobel. Implement NPF table listing and preservation of entries on reload. Bump the version. npfctl(8): mention table listing. To generate a diff of this commit: cvs rdiff -u -r1.7.2.5 -r1.7.2.6 src/sys/net/npf/npf.c cvs rdiff -u -r1.14.2.7 -r1.14.2.8 src/sys/net/npf/npf.h cvs rdiff -u -r1.12.2.5 -r1.12.2.6 src/sys/net/npf/npf_ctl.c cvs rdiff -u -r1.10.2.9 -r1.10.2.10 src/sys/net/npf/npf_impl.h cvs rdiff -u -r1.3.2.5 -r1.3.2.6 src/sys/net/npf/npf_state_tcp.c cvs rdiff -u -r1.9.2.5 -r1.9.2.6 src/sys/net/npf/npf_tableset.c cvs rdiff -u -r1.3.2.7 -r1.3.2.8 src/usr.sbin/npf/npfctl/npf_disassemble.c cvs rdiff -u -r1.6.6.2 -r1.6.6.3 src/usr.sbin/npf/npfctl/npfctl.8 cvs rdiff -u -r1.10.2.7 -r1.10.2.8 src/usr.sbin/npf/npfctl/npfctl.c cvs rdiff -u -r1.11.2.7 -r1.11.2.8 src/usr.sbin/npf/npfctl/npfctl.h cvs rdiff -u -r1.2.2.6 -r1.2.2.7 \ src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/net/npf/npf.c diff -u src/sys/net/npf/npf.c:1.7.2.5 src/sys/net/npf/npf.c:1.7.2.6 --- src/sys/net/npf/npf.c:1.7.2.5 Mon Nov 19 09:44:42 2012 +++ src/sys/net/npf/npf.c Sat Nov 24 04:34:42 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf.c,v 1.7.2.5 2012/11/19 09:44:42 msaitoh Exp $ */ +/* $NetBSD: npf.c,v 1.7.2.6 2012/11/24 04:34:42 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.7.2.5 2012/11/19 09:44:42 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf.c,v 1.7.2.6 2012/11/24 04:34:42 riz Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -303,7 +303,9 @@ npf_reload(prop_dictionary_t dict, npf_r rw_enter(&npf_lock, RW_WRITER); onc = atomic_swap_ptr(&npf_core, nc); if (onc) { - /* Reload only necessary NAT policies. */ + /* Reload only the static tables. */ + npf_tableset_reload(tset, onc->n_tables); + /* Reload only the necessary NAT policies. */ npf_ruleset_natreload(nset, onc->n_nat_rules); } /* Unlock. Everything goes "live" now. */ Index: src/sys/net/npf/npf.h diff -u src/sys/net/npf/npf.h:1.14.2.7 src/sys/net/npf/npf.h:1.14.2.8 --- src/sys/net/npf/npf.h:1.14.2.7 Sun Nov 18 22:38:26 2012 +++ src/sys/net/npf/npf.h Sat Nov 24 04:34:42 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf.h,v 1.14.2.7 2012/11/18 22:38:26 riz Exp $ */ +/* $NetBSD: npf.h,v 1.14.2.8 2012/11/24 04:34:42 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -45,7 +45,7 @@ #include <netinet/in_systm.h> #include <netinet/in.h> -#define NPF_VERSION 6 +#define NPF_VERSION 7 /* * Public declarations and definitions. @@ -211,15 +211,29 @@ bool npf_autounload_p(void); * IOCTL structures. */ +#define NPF_IOCTL_TBLENT_LOOKUP 0 #define NPF_IOCTL_TBLENT_ADD 1 #define NPF_IOCTL_TBLENT_REM 2 +#define NPF_IOCTL_TBLENT_LIST 3 + +typedef struct npf_ioctl_ent { + int alen; + npf_addr_t addr; + npf_netmask_t mask; +} npf_ioctl_ent_t; + +typedef struct npf_ioctl_buf { + void * buf; + size_t len; +} npf_ioctl_buf_t; typedef struct npf_ioctl_table { int nct_action; u_int nct_tid; - int nct_alen; - npf_addr_t nct_addr; - npf_netmask_t nct_mask; + union { + npf_ioctl_ent_t ent; + npf_ioctl_buf_t buf; + } nct_data; } npf_ioctl_table_t; typedef enum { Index: src/sys/net/npf/npf_ctl.c diff -u src/sys/net/npf/npf_ctl.c:1.12.2.5 src/sys/net/npf/npf_ctl.c:1.12.2.6 --- src/sys/net/npf/npf_ctl.c:1.12.2.5 Sun Nov 18 22:38:26 2012 +++ src/sys/net/npf/npf_ctl.c Sat Nov 24 04:34:42 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_ctl.c,v 1.12.2.5 2012/11/18 22:38:26 riz Exp $ */ +/* $NetBSD: npf_ctl.c,v 1.12.2.6 2012/11/24 04:34:42 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -37,7 +37,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.12.2.5 2012/11/18 22:38:26 riz Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.12.2.6 2012/11/24 04:34:42 riz Exp $"); #include <sys/param.h> #include <sys/conf.h> @@ -710,24 +710,33 @@ fail: int npfctl_table(void *data) { - npf_ioctl_table_t *nct = data; + const npf_ioctl_table_t *nct = data; npf_tableset_t *tblset; int error; npf_core_enter(); /* XXXSMP */ tblset = npf_core_tableset(); switch (nct->nct_action) { + case NPF_IOCTL_TBLENT_LOOKUP: + error = npf_table_lookup(tblset, nct->nct_tid, + nct->nct_data.ent.alen, &nct->nct_data.ent.addr); case NPF_IOCTL_TBLENT_ADD: error = npf_table_insert(tblset, nct->nct_tid, - nct->nct_alen, &nct->nct_addr, nct->nct_mask); + nct->nct_data.ent.alen, &nct->nct_data.ent.addr, + nct->nct_data.ent.mask); break; case NPF_IOCTL_TBLENT_REM: error = npf_table_remove(tblset, nct->nct_tid, - nct->nct_alen, &nct->nct_addr, nct->nct_mask); + nct->nct_data.ent.alen, &nct->nct_data.ent.addr, + nct->nct_data.ent.mask); + break; + case NPF_IOCTL_TBLENT_LIST: + error = npf_table_list(tblset, nct->nct_tid, + nct->nct_data.buf.buf, nct->nct_data.buf.len); break; default: - error = npf_table_lookup(tblset, nct->nct_tid, - nct->nct_alen, &nct->nct_addr); + error = EINVAL; + break; } npf_core_exit(); /* XXXSMP */ return error; Index: src/sys/net/npf/npf_impl.h diff -u src/sys/net/npf/npf_impl.h:1.10.2.9 src/sys/net/npf/npf_impl.h:1.10.2.10 --- src/sys/net/npf/npf_impl.h:1.10.2.9 Sun Nov 18 22:38:26 2012 +++ src/sys/net/npf/npf_impl.h Sat Nov 24 04:34:42 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_impl.h,v 1.10.2.9 2012/11/18 22:38:26 riz Exp $ */ +/* $NetBSD: npf_impl.h,v 1.10.2.10 2012/11/24 04:34:42 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -81,11 +81,9 @@ typedef struct npf_natpolicy npf_natpoli typedef struct npf_session npf_session_t; struct npf_sehash; -struct npf_tblent; struct npf_table; typedef struct npf_sehash npf_sehash_t; -typedef struct npf_tblent npf_tblent_t; typedef struct npf_table npf_table_t; typedef npf_table_t * npf_tableset_t; @@ -208,15 +206,11 @@ const pt_tree_ops_t npf_table_ptree_ops; npf_tableset_t *npf_tableset_create(void); void npf_tableset_destroy(npf_tableset_t *); int npf_tableset_insert(npf_tableset_t *, npf_table_t *); -npf_tableset_t *npf_tableset_reload(npf_tableset_t *); +void npf_tableset_reload(npf_tableset_t *, npf_tableset_t *); npf_table_t * npf_table_create(u_int, int, size_t); void npf_table_destroy(npf_table_t *); -void npf_table_ref(npf_table_t *); -void npf_table_unref(npf_table_t *); -npf_table_t * npf_table_get(npf_tableset_t *, u_int); -void npf_table_put(npf_table_t *); int npf_table_check(const npf_tableset_t *, u_int, int); int npf_table_insert(npf_tableset_t *, u_int, const int, const npf_addr_t *, const npf_netmask_t); @@ -224,6 +218,7 @@ int npf_table_remove(npf_tableset_t *, const int, const npf_addr_t *, const npf_netmask_t); int npf_table_lookup(npf_tableset_t *, u_int, const int, const npf_addr_t *); +int npf_table_list(npf_tableset_t *, u_int, void *, size_t); /* Ruleset interface. */ npf_ruleset_t * npf_ruleset_create(void); Index: src/sys/net/npf/npf_state_tcp.c diff -u src/sys/net/npf/npf_state_tcp.c:1.3.2.5 src/sys/net/npf/npf_state_tcp.c:1.3.2.6 --- src/sys/net/npf/npf_state_tcp.c:1.3.2.5 Wed Jul 25 20:45:24 2012 +++ src/sys/net/npf/npf_state_tcp.c Sat Nov 24 04:34:42 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_state_tcp.c,v 1.3.2.5 2012/07/25 20:45:24 jdc Exp $ */ +/* $NetBSD: npf_state_tcp.c,v 1.3.2.6 2012/11/24 04:34:42 riz Exp $ */ /*- * Copyright (c) 2010-2012 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.3.2.5 2012/07/25 20:45:24 jdc Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_state_tcp.c,v 1.3.2.6 2012/11/24 04:34:42 riz Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -94,6 +94,8 @@ static u_int npf_tcp_timeouts[] __read_m [NPF_TCPS_TIME_WAIT] = 60 * 2 * 2, }; +static bool npf_strict_order_rst __read_mostly = false; + #define NPF_TCP_MAXACKWIN 66000 /* @@ -391,17 +393,20 @@ npf_tcp_inwindow(const npf_cache_t *npc, /* Workaround for some TCP stacks. */ ack = tstate->nst_end; } - if (seq == end) { - /* If packet contains no data - assume it is valid. */ - end = fstate->nst_end; - seq = end; - } -#if 0 - /* Strict in-order sequence for RST packets. */ - if ((tcpfl & TH_RST) != 0 && (fstate->nst_end - seq) > 1) { - return false; + + if (__predict_false(tcpfl & TH_RST)) { + /* RST to the initial SYN may have zero SEQ - fix it up. */ + if (seq == 0 && nst->nst_state == NPF_TCPS_SYN_SENT) { + end = fstate->nst_end; + seq = end; + } + + /* Strict in-order sequence for RST packets. */ + if (npf_strict_order_rst && (fstate->nst_end - seq) > 1) { + return false; + } } -#endif + /* * Determine whether the data is within previously noted window, * that is, upper boundary for valid data (I). Index: src/sys/net/npf/npf_tableset.c diff -u src/sys/net/npf/npf_tableset.c:1.9.2.5 src/sys/net/npf/npf_tableset.c:1.9.2.6 --- src/sys/net/npf/npf_tableset.c:1.9.2.5 Mon Aug 13 17:49:52 2012 +++ src/sys/net/npf/npf_tableset.c Sat Nov 24 04:34:41 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_tableset.c,v 1.9.2.5 2012/08/13 17:49:52 riz Exp $ */ +/* $NetBSD: npf_tableset.c,v 1.9.2.6 2012/11/24 04:34:41 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -32,13 +32,16 @@ /* * NPF tableset module. * - * TODO: - * - Dynamic hash growing/shrinking (i.e. re-hash functionality), maybe? - * - Dynamic array resize. + * Notes + * + * The tableset is an array of tables. After the creation, the array + * is immutable. The caller is responsible to synchronise the access + * to the tableset. The table can either be a hash or a tree. Its + * entries are protected by a read-write lock. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.9.2.5 2012/08/13 17:49:52 riz Exp $"); +__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.9.2.6 2012/11/24 04:34:41 riz Exp $"); #include <sys/param.h> #include <sys/types.h> @@ -58,14 +61,14 @@ __KERNEL_RCSID(0, "$NetBSD: npf_tableset * Table structures. */ -struct npf_tblent { +typedef struct npf_tblent { union { LIST_ENTRY(npf_tblent) hashq; pt_node_t node; } te_entry; int te_alen; npf_addr_t te_addr; -}; +} npf_tblent_t; LIST_HEAD(npf_hashl, npf_tblent); @@ -74,12 +77,15 @@ struct npf_table { /* Lock and reference count. */ krwlock_t t_lock; u_int t_refcnt; + /* Total number of items. */ + u_int t_nitems; /* Table ID. */ u_int t_id; /* The storage type can be: a) hash b) tree. */ int t_type; struct npf_hashl * t_hashl; u_long t_hashmask; + /* Separate trees for IPv4 and IPv6. */ pt_tree_t t_tree[2]; }; @@ -126,7 +132,7 @@ npf_tableset_destroy(npf_tableset_t *tbl */ for (tid = 0; tid < NPF_TABLE_SLOTS; tid++) { t = tblset[tid]; - if (t != NULL) { + if (t && --t->t_refcnt == 0) { npf_table_destroy(t); } } @@ -148,6 +154,7 @@ npf_tableset_insert(npf_tableset_t *tbls if (tblset[tid] == NULL) { tblset[tid] = t; + t->t_refcnt++; error = 0; } else { error = EEXIST; @@ -156,6 +163,30 @@ npf_tableset_insert(npf_tableset_t *tbls } /* + * npf_tableset_reload: iterate all tables and if the new table is of the + * same type and has no items, then we preserve the old one and its entries. + * + * => The caller is responsible for providing synchronisation. + */ +void +npf_tableset_reload(npf_tableset_t *ntset, npf_tableset_t *otset) +{ + for (int i = 0; i < NPF_TABLE_SLOTS; i++) { + npf_table_t *t = ntset[i], *ot = otset[i]; + + if (t == NULL || ot == NULL) { + continue; + } + if (t->t_nitems || t->t_type != ot->t_type) { + continue; + } + ntset[i] = ot; + ot->t_refcnt++; + npf_table_destroy(t); + } +} + +/* * Few helper routines. */ @@ -228,8 +259,8 @@ npf_table_create(u_int tid, int type, si } rw_init(&t->t_lock); t->t_type = type; - t->t_refcnt = 1; t->t_id = tid; + return t; } @@ -241,7 +272,7 @@ npf_table_destroy(npf_table_t *t) { switch (t->t_type) { - case NPF_TABLE_HASH: { + case NPF_TABLE_HASH: for (unsigned n = 0; n <= t->t_hashmask; n++) { npf_tblent_t *ent; @@ -252,12 +283,10 @@ npf_table_destroy(npf_table_t *t) } hashdone(t->t_hashl, HASH_LIST, t->t_hashmask); break; - } - case NPF_TABLE_TREE: { + case NPF_TABLE_TREE: table_tree_destroy(&t->t_tree[0]); table_tree_destroy(&t->t_tree[1]); break; - } default: KASSERT(false); } @@ -266,63 +295,6 @@ npf_table_destroy(npf_table_t *t) } /* - * npf_table_ref: holds the reference on table. - * - * => Table must be locked. - */ -void -npf_table_ref(npf_table_t *t) -{ - - KASSERT(rw_lock_held(&t->t_lock)); - atomic_inc_uint(&t->t_refcnt); -} - -/* - * npf_table_unref: drop reference from the table and destroy the table if - * it is the last reference. - */ -void -npf_table_unref(npf_table_t *t) -{ - - if (atomic_dec_uint_nv(&t->t_refcnt) != 0) { - return; - } - npf_table_destroy(t); -} - -/* - * npf_table_get: find the table according to ID and "get it" by locking it. - */ -npf_table_t * -npf_table_get(npf_tableset_t *tset, u_int tid) -{ - npf_table_t *t; - - KASSERT(tset != NULL); - - if ((u_int)tid >= NPF_TABLE_SLOTS) { - return NULL; - } - t = tset[tid]; - if (t != NULL) { - rw_enter(&t->t_lock, RW_READER); - } - return t; -} - -/* - * npf_table_put: "put table back" by unlocking it. - */ -void -npf_table_put(npf_table_t *t) -{ - - rw_exit(&t->t_lock); -} - -/* * npf_table_check: validate ID and type. */ int @@ -342,7 +314,7 @@ npf_table_check(const npf_tableset_t *ts } static int -npf_table_validate_cidr(const u_int aidx, const npf_addr_t *addr, +table_cidr_check(const u_int aidx, const npf_addr_t *addr, const npf_netmask_t mask) { @@ -375,7 +347,11 @@ npf_table_insert(npf_tableset_t *tset, u npf_table_t *t; int error; - error = npf_table_validate_cidr(aidx, addr, mask); + if ((u_int)tid >= NPF_TABLE_SLOTS || (t = tset[tid]) == NULL) { + return EINVAL; + } + + error = table_cidr_check(aidx, addr, mask); if (error) { return error; } @@ -383,16 +359,10 @@ npf_table_insert(npf_tableset_t *tset, u memcpy(&ent->te_addr, addr, alen); ent->te_alen = alen; - /* Get the table (acquire the lock). */ - t = npf_table_get(tset, tid); - if (t == NULL) { - pool_cache_put(tblent_cache, ent); - return EINVAL; - } - /* * Insert the entry. Return an error on duplicate. */ + rw_enter(&t->t_lock, RW_WRITER); switch (t->t_type) { case NPF_TABLE_HASH: { struct npf_hashl *htbl; @@ -406,6 +376,7 @@ npf_table_insert(npf_tableset_t *tset, u } if (!table_hash_lookup(t, addr, alen, &htbl)) { LIST_INSERT_HEAD(htbl, ent, te_entry.hashq); + t->t_nitems++; } else { error = EEXIST; } @@ -418,18 +389,21 @@ npf_table_insert(npf_tableset_t *tset, u /* * If no mask specified, use maximum mask. */ - if (mask != NPF_NO_NETMASK) { - ok = ptree_insert_mask_node(tree, ent, mask); + ok = (mask != NPF_NO_NETMASK) ? + ptree_insert_mask_node(tree, ent, mask) : + ptree_insert_node(tree, ent); + if (ok) { + t->t_nitems++; + error = 0; } else { - ok = ptree_insert_node(tree, ent); + error = EEXIST; } - error = ok ? 0 : EEXIST; break; } default: KASSERT(false); } - npf_table_put(t); + rw_exit(&t->t_lock); if (error) { pool_cache_put(tblent_cache, ent); @@ -449,15 +423,16 @@ npf_table_remove(npf_tableset_t *tset, u npf_table_t *t; int error; - error = npf_table_validate_cidr(aidx, addr, mask); + error = table_cidr_check(aidx, addr, mask); if (error) { return error; } - t = npf_table_get(tset, tid); - if (t == NULL) { + + if ((u_int)tid >= NPF_TABLE_SLOTS || (t = tset[tid]) == NULL) { return EINVAL; } + rw_enter(&t->t_lock, RW_WRITER); switch (t->t_type) { case NPF_TABLE_HASH: { struct npf_hashl *htbl; @@ -465,6 +440,7 @@ npf_table_remove(npf_tableset_t *tset, u ent = table_hash_lookup(t, addr, alen, &htbl); if (__predict_true(ent != NULL)) { LIST_REMOVE(ent, te_entry.hashq); + t->t_nitems--; } break; } @@ -474,6 +450,7 @@ npf_table_remove(npf_tableset_t *tset, u ent = ptree_find_node(tree, addr); if (__predict_true(ent != NULL)) { ptree_remove_node(tree, ent); + t->t_nitems--; } break; } @@ -481,7 +458,7 @@ npf_table_remove(npf_tableset_t *tset, u KASSERT(false); ent = NULL; } - npf_table_put(t); + rw_exit(&t->t_lock); if (ent == NULL) { return ENOENT; @@ -506,10 +483,11 @@ npf_table_lookup(npf_tableset_t *tset, u return EINVAL; } - t = npf_table_get(tset, tid); - if (__predict_false(t == NULL)) { + if ((u_int)tid >= NPF_TABLE_SLOTS || (t = tset[tid]) == NULL) { return EINVAL; } + + rw_enter(&t->t_lock, RW_READER); switch (t->t_type) { case NPF_TABLE_HASH: { struct npf_hashl *htbl; @@ -524,7 +502,85 @@ npf_table_lookup(npf_tableset_t *tset, u KASSERT(false); ent = NULL; } - npf_table_put(t); + rw_exit(&t->t_lock); return ent ? 0 : ENOENT; } + +static int +table_ent_copyout(npf_tblent_t *ent, npf_netmask_t mask, + void *ubuf, size_t len, size_t *off) +{ + void *ubufp = (uint8_t *)ubuf + *off; + npf_ioctl_ent_t uent; + + if ((*off += sizeof(npf_ioctl_ent_t)) > len) { + return ENOMEM; + } + uent.alen = ent->te_alen; + memcpy(&uent.addr, &ent->te_addr, sizeof(npf_addr_t)); + uent.mask = mask; + + return copyout(&uent, ubufp, sizeof(npf_ioctl_ent_t)); +} + +static int +table_tree_list(pt_tree_t *tree, npf_netmask_t maxmask, void *ubuf, + size_t len, size_t *off) +{ + npf_tblent_t *ent = NULL; + int error = 0; + + while ((ent = ptree_iterate(tree, ent, PT_ASCENDING)) != NULL) { + pt_bitlen_t blen; + + if (!ptree_mask_node_p(tree, ent, &blen)) { + blen = maxmask; + } + error = table_ent_copyout(ent, blen, ubuf, len, off); + if (error) + break; + } + return error; +} + +/* + * npf_table_list: copy a list of all table entries into a userspace buffer. + */ +int +npf_table_list(npf_tableset_t *tset, u_int tid, void *ubuf, size_t len) +{ + npf_table_t *t; + size_t off = 0; + int error = 0; + + if ((u_int)tid >= NPF_TABLE_SLOTS || (t = tset[tid]) == NULL) { + return EINVAL; + } + + rw_enter(&t->t_lock, RW_READER); + switch (t->t_type) { + case NPF_TABLE_HASH: + for (unsigned n = 0; n <= t->t_hashmask; n++) { + npf_tblent_t *ent; + + LIST_FOREACH(ent, &t->t_hashl[n], te_entry.hashq) + if ((error = table_ent_copyout(ent, 0, ubuf, + len, &off)) != 0) + break; + } + break; + case NPF_TABLE_TREE: + error = table_tree_list(&t->t_tree[0], 32, ubuf, len, &off); + if (error) + break; + error = table_tree_list(&t->t_tree[1], 128, ubuf, len, &off); + if (error) + break; + default: + KASSERT(false); + } + rw_exit(&t->t_lock); + + return error; +} Index: src/usr.sbin/npf/npfctl/npf_disassemble.c diff -u src/usr.sbin/npf/npfctl/npf_disassemble.c:1.3.2.7 src/usr.sbin/npf/npfctl/npf_disassemble.c:1.3.2.8 --- src/usr.sbin/npf/npfctl/npf_disassemble.c:1.3.2.7 Mon Aug 13 19:43:44 2012 +++ src/usr.sbin/npf/npfctl/npf_disassemble.c Sat Nov 24 04:34:43 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_disassemble.c,v 1.3.2.7 2012/08/13 19:43:44 riz Exp $ */ +/* $NetBSD: npf_disassemble.c,v 1.3.2.8 2012/11/24 04:34:43 riz Exp $ */ /*- * Copyright (c) 2012 The NetBSD Foundation, Inc. @@ -35,7 +35,7 @@ * FIXME: config generation should be redesigned.. */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npf_disassemble.c,v 1.3.2.7 2012/08/13 19:43:44 riz Exp $"); +__RCSID("$NetBSD: npf_disassemble.c,v 1.3.2.8 2012/11/24 04:34:43 riz Exp $"); #include <stdio.h> #include <stdlib.h> @@ -664,31 +664,11 @@ npfctl_show_nat(nl_rule_t *nrl, unsigned } _npf_nat_getinfo(nt, &type, &flags, &taddr, &alen, &port); - struct sockaddr_storage ss; - char taddrbuf[64], tportbuf[16]; - - if (alen == 4) { - struct sockaddr_in *sin = (void *)&ss; - sin->sin_len = sizeof(*sin); - sin->sin_family = AF_INET; - sin->sin_port = 0; - memcpy(&sin->sin_addr, &taddr, sizeof(sin->sin_addr)); - sockaddr_snprintf(taddrbuf, sizeof(taddrbuf), - "%a", (struct sockaddr *)sin); - } else { - assert(alen == 16); - struct sockaddr_in6 *sin6 = (void *)&ss; - sin6->sin6_len = sizeof(*sin6); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = 0; - memcpy(&sin6->sin6_addr, &taddr, sizeof(sin6->sin6_addr)); - sockaddr_snprintf(taddrbuf, sizeof(taddrbuf), - "%a", (struct sockaddr *)sin6); - } + char *taddrbuf, tportbuf[16]; + taddrbuf = npfctl_print_addrmask(alen, &taddr, 0); if (port) { - snprintf(tportbuf, sizeof(tportbuf), - " port %d", ntohs(port)); + snprintf(tportbuf, sizeof(tportbuf), " port %d", ntohs(port)); } const char *seg1 = "any", *seg2 = "any", *sp1 = "", *sp2 = "", *mt; @@ -708,6 +688,7 @@ npfctl_show_nat(nl_rule_t *nrl, unsigned } printf("map %s dynamic %s%s %s %s%s pass ", ifname, seg1, sp1, mt, seg2, sp2); + free(taddrbuf); const void *nc; size_t nclen; Index: src/usr.sbin/npf/npfctl/npfctl.8 diff -u src/usr.sbin/npf/npfctl/npfctl.8:1.6.6.2 src/usr.sbin/npf/npfctl/npfctl.8:1.6.6.3 --- src/usr.sbin/npf/npfctl/npfctl.8:1.6.6.2 Mon Aug 13 19:43:44 2012 +++ src/usr.sbin/npf/npfctl/npfctl.8 Sat Nov 24 04:34:43 2012 @@ -1,4 +1,4 @@ -.\" $NetBSD: npfctl.8,v 1.6.6.2 2012/08/13 19:43:44 riz Exp $ +.\" $NetBSD: npfctl.8,v 1.6.6.3 2012/11/24 04:34:43 riz Exp $ .\" .\" Copyright (c) 2009-2012 The NetBSD Foundation, Inc. .\" All rights reserved. @@ -27,7 +27,7 @@ .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd August 12, 2012 +.Dd November 15, 2012 .Dt NPFCTL 8 .Os .Sh NAME @@ -79,13 +79,16 @@ Show the current state and configuration Syntax of printed configuration is for the user and may not match the .Xr npf.conf 5 syntax. -.\".It Ic table Ar tid -.\"List all entries in the currently loaded table specified by -.\".Ar tid . -.It Ic table Ar tid Ic [ add | rem ] Aq Ar addr/mask +.It Ic table Ar tid Ic add Aq Ar addr/mask In table .Ar tid , -add or remove the IP address and optionally netmask, specified by +add the IP address and optionally netmask, specified by +.Aq Ar addr/mask . +Only tree-type tables support masks. +.It Ic table Ar tid Ic rem Aq Ar addr/mask +In table +.Ar tid , +remove the IP address and optionally netmask, specified by .Aq Ar addr/mask . Only tree-type tables support masks. .It Ic table Ar tid Ic test Aq Ar addr @@ -94,6 +97,10 @@ Query the table for a specific IP address, specified by .Ar addr . If no mask is specified, a single host is assumed. +.It Ic table Ar tid Ic list +List all entries in the currently loaded table specified by +.Ar tid . +This operation is expensive and should be used with caution. .It Ic sess-save Save all active sessions. The data will be stored in the Index: src/usr.sbin/npf/npfctl/npfctl.c diff -u src/usr.sbin/npf/npfctl/npfctl.c:1.10.2.7 src/usr.sbin/npf/npfctl/npfctl.c:1.10.2.8 --- src/usr.sbin/npf/npfctl/npfctl.c:1.10.2.7 Sun Nov 18 22:38:28 2012 +++ src/usr.sbin/npf/npfctl/npfctl.c Sat Nov 24 04:34:43 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npfctl.c,v 1.10.2.7 2012/11/18 22:38:28 riz Exp $ */ +/* $NetBSD: npfctl.c,v 1.10.2.8 2012/11/24 04:34:43 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -30,7 +30,7 @@ */ #include <sys/cdefs.h> -__RCSID("$NetBSD: npfctl.c,v 1.10.2.7 2012/11/18 22:38:28 riz Exp $"); +__RCSID("$NetBSD: npfctl.c,v 1.10.2.8 2012/11/24 04:34:43 riz Exp $"); #include <sys/ioctl.h> #include <sys/stat.h> @@ -44,6 +44,8 @@ __RCSID("$NetBSD: npfctl.c,v 1.10.2.7 20 #include <unistd.h> #include <errno.h> +#include <util.h> + #include "npfctl.h" extern int yylineno, yycolumn; @@ -142,10 +144,10 @@ usage(void) "\t%s ( sess-save | sess-load )\n", progname); fprintf(stderr, - "\t%s table <tid> [ flush ]\n", + "\t%s table <tid> { add | rem | test } <address/mask>\n", progname); fprintf(stderr, - "\t%s table <tid> { add | rem | test } <address/mask>\n", + "\t%s table <tid> { list | flush }\n", progname); exit(EXIT_FAILURE); @@ -255,62 +257,136 @@ npfctl_print_error(const nl_error_t *ne) } } +char * +npfctl_print_addrmask(int alen, npf_addr_t *addr, npf_netmask_t mask) +{ + struct sockaddr_storage ss; + char *buf = zalloc(64); + int len; + + switch (alen) { + case 4: { + struct sockaddr_in *sin = (void *)&ss; + sin->sin_len = sizeof(*sin); + sin->sin_family = AF_INET; + sin->sin_port = 0; + memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr)); + break; + } + case 16: { + struct sockaddr_in6 *sin6 = (void *)&ss; + sin6->sin6_len = sizeof(*sin6); + sin6->sin6_family = AF_INET6; + sin6->sin6_port = 0; + memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr)); + break; + } + default: + assert(false); + } + len = sockaddr_snprintf(buf, 64, "%a", (struct sockaddr *)&ss); + if (mask) { + snprintf(&buf[len], 64 - len, "/%u", mask); + } + return buf; +} + __dead static void -npfctl_table(int fd, char **argv) +npfctl_table(int fd, int argc, char **argv) { static const struct tblops_s { const char * cmd; int action; } tblops[] = { - { "add", NPF_IOCTL_TBLENT_ADD }, - { "rem", NPF_IOCTL_TBLENT_REM }, - { "test", 0 }, - { NULL, 0 } + { "add", NPF_IOCTL_TBLENT_ADD }, + { "rem", NPF_IOCTL_TBLENT_REM }, + { "test", NPF_IOCTL_TBLENT_LOOKUP }, + { "list", NPF_IOCTL_TBLENT_LIST }, + { NULL, 0 } }; npf_ioctl_table_t nct; fam_addr_mask_t fam; - char *cmd = argv[3]; - char *arg = argv[3]; + size_t buflen = 512; + char *cmd, *arg; int n, alen; + /* Default action is list. */ memset(&nct, 0, sizeof(npf_ioctl_table_t)); - nct.nct_tid = atoi(argv[2]); + nct.nct_tid = atoi(argv[0]); + cmd = argv[1]; for (n = 0; tblops[n].cmd != NULL; n++) { - if (strcmp(cmd, tblops[n].cmd) == 0) { - nct.nct_action = tblops[n].action; - arg = argv[4]; - break; + if (strcmp(cmd, tblops[n].cmd) != 0) { + continue; + } + nct.nct_action = tblops[n].action; + break; + } + if (tblops[n].cmd == NULL) { + errx(EXIT_FAILURE, "invalid command '%s'", cmd); + } + if (nct.nct_action != NPF_IOCTL_TBLENT_LIST) { + if (argc < 3) { + usage(); } + arg = argv[2]; } - if (!npfctl_parse_cidr(arg, &fam, &alen)) { - errx(EXIT_FAILURE, "invalid CIDR '%s'", arg); +again: + if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) { + nct.nct_data.buf.buf = zalloc(buflen); + nct.nct_data.buf.len = buflen; + } else { + if (!npfctl_parse_cidr(arg, &fam, &alen)) { + errx(EXIT_FAILURE, "invalid CIDR '%s'", arg); + } + nct.nct_data.ent.alen = alen; + memcpy(&nct.nct_data.ent.addr, &fam.fam_addr, sizeof(npf_addr_t)); + nct.nct_data.ent.mask = fam.fam_mask; } - memcpy(&nct.nct_addr, &fam.fam_addr, sizeof(npf_addr_t)); - nct.nct_mask = fam.fam_mask; - nct.nct_alen = alen; if (ioctl(fd, IOC_NPF_TABLE, &nct) != -1) { errno = 0; } switch (errno) { - case EEXIST: - warnx("entry already exists or is conflicting"); + case 0: break; + case EEXIST: + errx(EXIT_FAILURE, "entry already exists or is conflicting"); case ENOENT: - warnx("no matching entry was not found"); - break; + errx(EXIT_FAILURE, "no matching entry was not found"); case EINVAL: - warnx("invalid address, mask or table ID"); - break; - case 0: - printf("%s: %s\n", getprogname(), nct.nct_action == 0 ? - "matching entry found" : "success"); - break; + errx(EXIT_FAILURE, "invalid address, mask or table ID"); + case ENOMEM: + if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) { + /* XXX */ + free(nct.nct_data.buf.buf); + buflen <<= 1; + goto again; + } + /* FALLTHROUGH */ default: - warn("error"); + err(EXIT_FAILURE, "ioctl"); + } + + if (nct.nct_action == NPF_IOCTL_TBLENT_LIST) { + npf_ioctl_ent_t *ent = nct.nct_data.buf.buf; + char *buf; + + while (nct.nct_data.buf.len--) { + if (!ent->alen) + break; + buf = npfctl_print_addrmask(ent->alen, + &ent->addr, ent->mask); + puts(buf); + ent++; + } + free(nct.nct_data.buf.buf); + } else { + printf("%s: %s\n", getprogname(), + nct.nct_action == NPF_IOCTL_TBLENT_LOOKUP ? + "matching entry found" : "success"); } - exit(errno ? EXIT_FAILURE : EXIT_SUCCESS); + exit(EXIT_SUCCESS); } static void @@ -355,10 +431,11 @@ npfctl(int action, int argc, char **argv ret = npf_config_flush(fd); break; case NPFCTL_TABLE: - if (argc < 5) { + if ((argc -= 2) < 2) { usage(); } - npfctl_table(fd, argv); + argv += 2; + npfctl_table(fd, argc, argv); break; case NPFCTL_STATS: ret = npfctl_print_stats(fd); Index: src/usr.sbin/npf/npfctl/npfctl.h diff -u src/usr.sbin/npf/npfctl/npfctl.h:1.11.2.7 src/usr.sbin/npf/npfctl/npfctl.h:1.11.2.8 --- src/usr.sbin/npf/npfctl/npfctl.h:1.11.2.7 Sun Nov 18 22:38:27 2012 +++ src/usr.sbin/npf/npfctl/npfctl.h Sat Nov 24 04:34:43 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npfctl.h,v 1.11.2.7 2012/11/18 22:38:27 riz Exp $ */ +/* $NetBSD: npfctl.h,v 1.11.2.8 2012/11/24 04:34:43 riz Exp $ */ /*- * Copyright (c) 2009-2012 The NetBSD Foundation, Inc. @@ -96,6 +96,7 @@ char * xstrdup(const char *); char * xstrndup(const char *, size_t); void npfctl_print_error(const nl_error_t *); +char * npfctl_print_addrmask(int, npf_addr_t *, npf_netmask_t); bool npfctl_table_exists_p(const char *); int npfctl_protono(const char *); in_port_t npfctl_portno(const char *); Index: src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c diff -u src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c:1.2.2.6 src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c:1.2.2.7 --- src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c:1.2.2.6 Sun Nov 18 21:48:56 2012 +++ src/usr.sbin/npf/npftest/libnpftest/npf_table_test.c Sat Nov 24 04:34:44 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: npf_table_test.c,v 1.2.2.6 2012/11/18 21:48:56 riz Exp $ */ +/* $NetBSD: npf_table_test.c,v 1.2.2.7 2012/11/24 04:34:44 riz Exp $ */ /* * NPF tableset test. @@ -44,6 +44,32 @@ static const uint16_t ip6_list[][8] = { #define HASH_TID 1 #define TREE_TID 2 +static bool +npf_table_test_fill4(npf_tableset_t *tblset, npf_addr_t *addr) +{ + const int alen = sizeof(struct in_addr); + const int nm = NPF_NO_NETMASK; + bool fail = false; + + /* Fill both tables with IP addresses. */ + for (unsigned i = 0; i < __arraycount(ip_list); i++) { + int error; + + addr->s6_addr32[0] = inet_addr(ip_list[i]); + + error = npf_table_insert(tblset, HASH_TID, alen, addr, nm); + fail |= !(error == 0); + error = npf_table_insert(tblset, HASH_TID, alen, addr, nm); + fail |= !(error != 0); + + error = npf_table_insert(tblset, TREE_TID, alen, addr, nm); + fail |= !(error == 0); + error = npf_table_insert(tblset, TREE_TID, alen, addr, nm); + fail |= !(error != 0); + } + return fail; +} + bool npf_table_test(bool verbose) { @@ -55,8 +81,6 @@ npf_table_test(bool verbose) bool fail = false; u_int i; - npf_tableset_sysinit(); - tblset = npf_tableset_create(); fail |= !(tblset != NULL); @@ -87,19 +111,7 @@ npf_table_test(bool verbose) fail |= !(error != 0); /* Fill both tables with IP addresses. */ - for (i = 0; i < __arraycount(ip_list); i++) { - addr->s6_addr32[0] = inet_addr(ip_list[i]); - - error = npf_table_insert(tblset, HASH_TID, alen, addr, nm); - fail |= !(error == 0); - error = npf_table_insert(tblset, HASH_TID, alen, addr, nm); - fail |= !(error != 0); - - error = npf_table_insert(tblset, TREE_TID, alen, addr, nm); - fail |= !(error == 0); - error = npf_table_insert(tblset, TREE_TID, alen, addr, nm); - fail |= !(error != 0); - } + fail |= npf_table_test_fill4(tblset, addr); /* Attempt to add duplicates - should fail. */ addr->s6_addr32[0] = inet_addr(ip_list[0]); @@ -111,15 +123,6 @@ npf_table_test(bool verbose) error = npf_table_insert(tblset, TREE_TID, alen, addr, nm); fail |= !(error != 0); - /* Reference checks. */ - t1 = npf_table_get(tblset, HASH_TID); - fail |= !(t1 != NULL); - npf_table_put(t1); - - t2 = npf_table_get(tblset, TREE_TID); - fail |= !(t2 != NULL); - npf_table_put(t2); - /* Match (validate) each IP entry. */ for (i = 0; i < __arraycount(ip_list); i++) { addr->s6_addr32[0] = inet_addr(ip_list[i]); @@ -206,7 +209,6 @@ npf_table_test(bool verbose) } npf_tableset_destroy(tblset); - npf_tableset_sysfini(); return !fail; }