NPF: implement dynamic handling of interface addresses (the kernel part). trunk
authorrmind <rmind@NetBSD.org>
Mon, 02 Jan 2017 21:49:51 +0000
branchtrunk
changeset 247292 0763e8644acf
parent 247291 8c44e64974ef
child 247293 55aa8ad6937d
NPF: implement dynamic handling of interface addresses (the kernel part).
sys/modules/npf/Makefile
sys/net/npf/files.npf
sys/net/npf/npf_ctl.c
sys/net/npf/npf_ifaddr.c
sys/net/npf/npf_impl.h
sys/net/npf/npf_os.c
sys/net/npf/npf_tableset.c
sys/net/npf/npf_worker.c
--- a/sys/modules/npf/Makefile	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/modules/npf/Makefile	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.20 2016/12/28 13:50:55 christos Exp $
+# $NetBSD: Makefile,v 1.21 2017/01/02 21:49:51 rmind Exp $
 #
 # Public Domain.
 #
@@ -13,7 +13,7 @@
 SRCS+=		npf_bpf.c npf_if.c npf_inet.c npf_mbuf.c npf_nat.c
 SRCS+=		npf_ruleset.c npf_conn.c npf_conndb.c npf_rproc.c
 SRCS+=		npf_state.c npf_state_tcp.c npf_tableset.c
-SRCS+=		lpm.c npf_sendpkt.c npf_worker.c npf_os.c
+SRCS+=		lpm.c npf_sendpkt.c npf_worker.c npf_ifaddr.c npf_os.c
 
 CPPFLAGS+=	-DINET6
 
--- a/sys/net/npf/files.npf	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/files.npf	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.npf,v 1.19 2016/12/26 23:05:06 christos Exp $
+# $NetBSD: files.npf,v 1.20 2017/01/02 21:49:51 rmind Exp $
 #
 # Public Domain.
 #
@@ -11,7 +11,6 @@
 
 # Core
 file	net/npf/npf.c				npf
-file	net/npf/npf_os.c			npf
 file	net/npf/npf_conf.c			npf
 file	net/npf/npf_ctl.c			npf
 file	net/npf/npf_handler.c			npf
@@ -31,6 +30,9 @@
 file	net/npf/npf_sendpkt.c			npf
 file	net/npf/npf_worker.c			npf
 
+file	net/npf/npf_os.c			npf
+file	net/npf/npf_ifaddr.c			npf
+
 # LPM
 file	net/npf/lpm.c				npf
 
--- a/sys/net/npf/npf_ctl.c	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/npf_ctl.c	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_ctl.c,v 1.45 2016/12/26 23:05:06 christos Exp $	*/
+/*	$NetBSD: npf_ctl.c,v 1.46 2017/01/02 21:49:51 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -38,7 +38,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.45 2016/12/26 23:05:06 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_ctl.c,v 1.46 2017/01/02 21:49:51 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/conf.h>
@@ -108,7 +108,7 @@
 }
 
 static int __noinline
-npf_mk_tables(npf_tableset_t *tblset, prop_array_t tables,
+npf_mk_tables(npf_t *npf, npf_tableset_t *tblset, prop_array_t tables,
     prop_dictionary_t errdict)
 {
 	prop_object_iterator_t it;
@@ -160,9 +160,6 @@
 			error = EINVAL;
 			break;
 		}
-		if (type == NPF_TABLE_HASH) {
-			size = 1024; /* XXX */
-		}
 
 		/* Create and insert the table. */
 		t = npf_table_create(name, (u_int)tid, type, blob, size);
@@ -558,7 +555,7 @@
 		goto fail;
 	}
 	tblset = npf_tableset_create(nitems);
-	error = npf_mk_tables(tblset, tables, errdict);
+	error = npf_mk_tables(npf, tblset, tables, errdict);
 	if (error) {
 		goto fail;
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/net/npf/npf_ifaddr.c	Mon Jan 02 21:49:51 2017 +0000
@@ -0,0 +1,179 @@
+/*	$NetBSD: npf_ifaddr.c,v 1.1 2017/01/02 21:49:51 rmind Exp $	*/
+
+/*-
+ * Copyright (c) 2014 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Mindaugas Rasiukevicius.
+ *
+ * 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``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 FOUNDATION OR CONTRIBUTORS
+ * 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.
+ */
+
+/*
+ * NPF network interface handling module.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: npf_ifaddr.c,v 1.1 2017/01/02 21:49:51 rmind Exp $");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/kmem.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+
+#include "npf_impl.h"
+
+void
+npf_ifaddr_init(npf_t *npf)
+{
+	ifnet_t *ifp;
+
+	KERNEL_LOCK(1, NULL);
+	IFNET_LOCK();
+	IFNET_WRITER_FOREACH(ifp) {
+		npf_ifaddr_sync(npf, ifp);
+	}
+	IFNET_UNLOCK();
+	KERNEL_UNLOCK_ONE(NULL);
+}
+
+static npf_table_t *
+lookup_ifnet_table(npf_t *npf, ifnet_t *ifp)
+{
+	const npf_ifops_t *ifops = npf->ifops;
+	char tname[NPF_TABLE_MAXNAMELEN];
+	npf_tableset_t *ts;
+	const char *ifname;
+	npf_table_t *t;
+	u_int tid;
+
+	/* Get the interface name and prefix it. */
+	ifname = ifops->getname(ifp);
+	snprintf(tname, sizeof(tname), ".ifnet-%s", ifname);
+
+	KERNEL_LOCK(1, NULL);
+	npf_config_enter(npf);
+	ts = npf_config_tableset(npf);
+
+	/*
+	 * Check whether this interface is of any interest to us.
+	 */
+	t = npf_tableset_getbyname(ts, tname);
+	if (!t) {
+		goto out;
+	}
+	tid = npf_table_getid(t);
+
+	/* Create a new NPF table for the interface. */
+	t = npf_table_create(tname, tid, NPF_TABLE_HASH, NULL, 16);
+	if (!t) {
+		goto out;
+	}
+	return t;
+out:
+	npf_config_exit(npf);
+	KERNEL_UNLOCK_ONE(NULL);
+	return NULL;
+}
+
+static void
+replace_ifnet_table(npf_t *npf, npf_table_t *newt)
+{
+	npf_tableset_t *ts = npf_config_tableset(npf);
+	npf_table_t *oldt;
+
+	KERNEL_UNLOCK_ONE(NULL);
+
+	/*
+	 * Finally, swap the tables and issue a sync barrier.
+	 */
+	oldt = npf_tableset_swap(ts, newt);
+	npf_config_sync(npf);
+	npf_config_exit(npf);
+
+	/* At this point, it is safe to destroy the old table. */
+	npf_table_destroy(oldt);
+}
+
+void
+npf_ifaddr_sync(npf_t *npf, ifnet_t *ifp)
+{
+	npf_table_t *t;
+	struct ifaddr *ifa;
+
+	/*
+	 * First, check whether this interface is of any interest to us.
+	 *
+	 * => Acquires npf-config-lock and kernel-lock on success.
+	 */
+	t = lookup_ifnet_table(npf, ifp);
+	if (!t)
+		return;
+
+	/*
+	 * Populate the table with the interface addresses.
+	 * Note: currently, this list is protected by the kernel-lock.
+	 */
+	IFADDR_FOREACH(ifa, ifp) {
+		struct sockaddr *sa = ifa->ifa_addr;
+		const void *p = NULL;
+		int alen = 0;
+
+		if (sa->sa_family == AF_INET) {
+			const struct sockaddr_in *sin4 = satosin(sa);
+			alen = sizeof(struct in_addr);
+			p = &sin4->sin_addr;
+		}
+		if (sa->sa_family == AF_INET6) {
+			const struct sockaddr_in6 *sin6 = satosin6(sa);
+			alen = sizeof(struct in6_addr);
+			p = &sin6->sin6_addr;
+		}
+		if (alen) {
+			npf_addr_t addr;
+			memcpy(&addr, p, alen);
+			npf_table_insert(t, alen, &addr, NPF_NO_NETMASK);
+		}
+	}
+
+	/* Publish the new table. */
+	replace_ifnet_table(npf, t);
+}
+
+void
+npf_ifaddr_flush(npf_t *npf, ifnet_t *ifp)
+{
+	npf_table_t *t;
+
+	/*
+	 * Flush: just load an empty table.
+	 */
+	t = lookup_ifnet_table(npf, ifp);
+	if (!t) {
+		return;
+	}
+	replace_ifnet_table(npf, t);
+}
--- a/sys/net/npf/npf_impl.h	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/npf_impl.h	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_impl.h,v 1.65 2016/12/28 21:55:04 christos Exp $	*/
+/*	$NetBSD: npf_impl.h,v 1.66 2017/01/02 21:49:51 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2014 The NetBSD Foundation, Inc.
@@ -247,6 +247,10 @@
 u_int		npf_ifmap_getid(npf_t *, const ifnet_t *);
 const char *	npf_ifmap_getname(npf_t *, const u_int);
 
+void		npf_ifaddr_init(npf_t *);
+void		npf_ifaddr_sync(npf_t *, ifnet_t *);
+void		npf_ifaddr_flush(npf_t *, ifnet_t *);
+
 /* Packet filter hooks. */
 int		npf_pfil_register(bool);
 void		npf_pfil_unregister(bool);
@@ -297,12 +301,14 @@
 int		npf_tableset_insert(npf_tableset_t *, npf_table_t *);
 npf_table_t *	npf_tableset_getbyname(npf_tableset_t *, const char *);
 npf_table_t *	npf_tableset_getbyid(npf_tableset_t *, u_int);
+npf_table_t *	npf_tableset_swap(npf_tableset_t *, npf_table_t *);
 void		npf_tableset_reload(npf_t *, npf_tableset_t *, npf_tableset_t *);
 int		npf_tableset_export(npf_t *, const npf_tableset_t *, prop_array_t);
 
 npf_table_t *	npf_table_create(const char *, u_int, int, void *, size_t);
 void		npf_table_destroy(npf_table_t *);
 
+u_int		npf_table_getid(npf_table_t *);
 int		npf_table_check(npf_tableset_t *, const char *, u_int, int);
 int		npf_table_insert(npf_table_t *, const int,
 		    const npf_addr_t *, const npf_netmask_t);
--- a/sys/net/npf/npf_os.c	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/npf_os.c	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_os.c,v 1.2 2016/12/26 23:59:47 rmind Exp $	*/
+/*	$NetBSD: npf_os.c,v 1.3 2017/01/02 21:49:51 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2016 The NetBSD Foundation, Inc.
@@ -35,7 +35,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.2 2016/12/26 23:59:47 rmind Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_os.c,v 1.3 2017/01/02 21:49:51 rmind Exp $");
 
 #ifdef _KERNEL_OPT
 #include "pf.h"
@@ -150,6 +150,7 @@
 	npf = npf_create(0, NULL, &kern_ifops);
 	npf_setkernctx(npf);
 	npf_pfil_register(true);
+	npf_ifaddr_init(npf);
 
 #ifdef _MODULE
 	devmajor_t bmajor = NODEVMAJOR, cmajor = NODEVMAJOR;
@@ -196,7 +197,6 @@
 static int
 npf_dev_open(dev_t dev, int flag, int mode, lwp_t *l)
 {
-
 	/* Available only for super-user. */
 	if (kauth_authorize_network(l->l_cred, KAUTH_NETWORK_FIREWALL,
 	    KAUTH_REQ_NETWORK_FIREWALL_FW, NULL, NULL, NULL)) {
@@ -354,13 +354,37 @@
 	switch (cmd) {
 	case PFIL_IFNET_ATTACH:
 		npf_ifmap_attach(npf, ifp);
+		npf_ifaddr_sync(npf, ifp);
 		break;
 	case PFIL_IFNET_DETACH:
 		npf_ifmap_detach(npf, ifp);
+		npf_ifaddr_flush(npf, ifp);
 		break;
 	}
 }
 
+static void
+npf_ifaddrhook(void *arg, u_long cmd, void *arg2)
+{
+	npf_t *npf = npf_getkernctx();
+	struct ifaddr *ifa = arg2;
+
+	switch (cmd) {
+	case SIOCSIFADDR:
+	case SIOCAIFADDR:
+	case SIOCDIFADDR:
+#ifdef INET6
+	case SIOCSIFADDR_IN6:
+	case SIOCAIFADDR_IN6:
+	case SIOCDIFADDR_IN6:
+#endif
+		break;
+	default:
+		return;
+	}
+	npf_ifaddr_sync(npf, ifa->ifa_ifp);
+}
+
 /*
  * npf_pfil_register: register pfil(9) hooks.
  */
@@ -380,7 +404,13 @@
 			error = ENOENT;
 			goto out;
 		}
-		error = pfil_add_ihook(npf_ifhook, NULL, PFIL_IFNET, npf_ph_if);
+
+		error = pfil_add_ihook(npf_ifhook, NULL,
+		    PFIL_IFNET, npf_ph_if);
+		KASSERT(error == 0);
+
+		error = pfil_add_ihook(npf_ifaddrhook, NULL,
+		    PFIL_IFADDR, npf_ph_if);
 		KASSERT(error == 0);
 	}
 	if (init) {
@@ -432,7 +462,10 @@
 	KERNEL_LOCK(1, NULL);
 
 	if (fini && npf_ph_if) {
-		(void)pfil_remove_ihook(npf_ifhook, NULL, PFIL_IFNET, npf_ph_if);
+		(void)pfil_remove_ihook(npf_ifhook, NULL,
+		    PFIL_IFNET, npf_ph_if);
+		(void)pfil_remove_ihook(npf_ifaddrhook, NULL,
+		    PFIL_IFADDR, npf_ph_if);
 	}
 	if (npf_ph_inet) {
 		(void)pfil_remove_hook(npfkern_packet_handler, npf,
--- a/sys/net/npf/npf_tableset.c	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/npf_tableset.c	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_tableset.c,v 1.25 2016/12/26 23:05:06 christos Exp $	*/
+/*	$NetBSD: npf_tableset.c,v 1.26 2017/01/02 21:49:51 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2009-2016 The NetBSD Foundation, Inc.
@@ -42,7 +42,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.25 2016/12/26 23:05:06 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_tableset.c,v 1.26 2017/01/02 21:49:51 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -183,6 +183,21 @@
 	return error;
 }
 
+npf_table_t *
+npf_tableset_swap(npf_tableset_t *ts, npf_table_t *newt)
+{
+	const u_int tid = newt->t_id;
+	npf_table_t *oldt = ts->ts_map[tid];
+
+	KASSERT(tid < ts->ts_nitems);
+	KASSERT(oldt->t_id == newt->t_id);
+
+	newt->t_refcnt = oldt->t_refcnt;
+	oldt->t_refcnt = 0;
+
+	return atomic_swap_ptr(&ts->ts_map[tid], newt);
+}
+
 /*
  * npf_tableset_getbyname: look for a table in the set given the name.
  */
@@ -354,7 +369,8 @@
 		LIST_INIT(&t->t_list);
 		break;
 	case NPF_TABLE_HASH:
-		t->t_hashl = hashinit(1024, HASH_LIST, true, &t->t_hashmask);
+		size = MIN(size, 128);
+		t->t_hashl = hashinit(size, HASH_LIST, true, &t->t_hashmask);
 		if (t->t_hashl == NULL) {
 			goto out;
 		}
@@ -409,6 +425,12 @@
 	kmem_free(t, sizeof(npf_table_t));
 }
 
+u_int
+npf_table_getid(npf_table_t *t)
+{
+	return t->t_id;
+}
+
 /*
  * npf_table_check: validate the name, ID and type.
  */
--- a/sys/net/npf/npf_worker.c	Mon Jan 02 21:46:59 2017 +0000
+++ b/sys/net/npf/npf_worker.c	Mon Jan 02 21:49:51 2017 +0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: npf_worker.c,v 1.2 2016/12/26 23:05:06 christos Exp $	*/
+/*	$NetBSD: npf_worker.c,v 1.3 2017/01/02 21:49:51 rmind Exp $	*/
 
 /*-
  * Copyright (c) 2010-2015 The NetBSD Foundation, Inc.
@@ -31,7 +31,7 @@
 
 #ifdef _KERNEL
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.2 2016/12/26 23:05:06 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.3 2017/01/02 21:49:51 rmind Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -73,9 +73,8 @@
 		mutex_init(&wrk->worker_lock, MUTEX_DEFAULT, IPL_SOFTNET);
 		cv_init(&wrk->worker_cv, "npfgccv");
 
-		if (kthread_create(PRI_NONE, KTHREAD_MPSAFE |
-		    KTHREAD_MUSTJOIN, NULL, npf_worker, wrk, &wrk->worker_lwp,
-			"npfgc-%u", i)) {
+		if (kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN,
+		    NULL, npf_worker, wrk, &wrk->worker_lwp, "npfgc-%u", i)) {
 			npf_worker_sysfini();
 			return ENOMEM;
 		}