svn commit: r269195 - in projects/ipfw: sbin/ipfw sys/conf sys/modules/ipfw sys/netinet sys/netpfil/ipfw

Alexander V. Chernikov melifaro at FreeBSD.org
Mon Jul 28 19:01:28 UTC 2014


Author: melifaro
Date: Mon Jul 28 19:01:25 2014
New Revision: 269195
URL: http://svnweb.freebsd.org/changeset/base/269195

Log:
  * Add generic ipfw interface tracking API
  * Rewrite interface tables to use interface indexes
  
  Kernel changes:
  * Add generic interface tracking API:
   - ipfw_iface_ref (must call unlocked, performs lazy init if needed, allocates
    state & bumps ref)
   - ipfw_iface_add_ntfy(UH_WLOCK+WLOCK, links comsumer & runs its callback to
    update ifindex)
   - ipfw_iface_del_ntfy(UH_WLOCK+WLOCK, unlinks consumer)
   - ipfw_iface_unref(unlocked, drops reference)
  Additionally, consumer callbacks are called in interface withdrawal/departure.
  
  * Rewrite interface tables to use iface tracking API. Currently tables are
    implemented the following way:
    runtime data is stored as sorted array of {ifidx, val} for existing interfaces
    full data is stored inside namedobj instance (chained hashed table).
  
  * Add IP_FW_XIFLIST opcode to dump status of tracked interfaces
  
  * Pass @chain ptr to most non-locked algorithm callbacks:
    (prepare_add, prepare_del, flush_entry ..). This may be needed for better
    interaction of given algorithm an other ipfw subsystems
  
  * Add optional "change_ti" algorithm handler to permit updating of
    cached table_info pointer (happens in case of table_max resize)
  
  * Fix small bug in ipfw_list_tables()
  * Add badd (insert into sorted array) and bdel (remove from sorted array) funcs
  
  Userland changes:
  * Add "iflist" cmd to print status of currently tracked interface
  * Add stringnum_cmp for better interface/table names sorting

Added:
  projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c
Modified:
  projects/ipfw/sbin/ipfw/ipfw2.c
  projects/ipfw/sbin/ipfw/ipfw2.h
  projects/ipfw/sbin/ipfw/main.c
  projects/ipfw/sbin/ipfw/tables.c
  projects/ipfw/sys/conf/files
  projects/ipfw/sys/modules/ipfw/Makefile
  projects/ipfw/sys/netinet/ip_fw.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table.h
  projects/ipfw/sys/netpfil/ipfw/ip_fw_table_algo.c

Modified: projects/ipfw/sbin/ipfw/ipfw2.c
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sbin/ipfw/ipfw2.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -520,6 +520,26 @@ safe_realloc(void *ptr, size_t size)
 }
 
 /*
+ * Compare things like interface or table names.
+ */
+int
+stringnum_cmp(const char *a, const char *b)
+{
+	int la, lb;
+
+	la = strlen(a);
+	lb = strlen(b);
+
+	if (la > lb)
+		return (1);
+	else if (la < lb)
+		return (-01);
+
+	return (strcmp(a, b));
+}
+
+
+/*
  * conditionally runs the command.
  * Selected options or negative -> getsockopt
  */
@@ -4682,3 +4702,78 @@ ipfw_flush(int force)
 		printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
 }
 
+int
+ipfw_get_tracked_ifaces(ipfw_obj_lheader **polh)
+{
+	ipfw_obj_lheader req, *olh;
+	size_t sz;
+	int error;
+
+	memset(&req, 0, sizeof(req));
+	sz = sizeof(req);
+
+	error = do_get3(IP_FW_XIFLIST, &req.opheader, &sz);
+	if (error != 0 && error != ENOMEM)
+		return (error);
+
+	sz = req.size;
+	if ((olh = calloc(1, sz)) == NULL)
+		return (ENOMEM);
+
+	olh->size = sz;
+	if ((error = do_get3(IP_FW_XIFLIST, &olh->opheader, &sz)) != 0) {
+		free(olh);
+		return (error);
+	}
+
+	*polh = olh;
+	return (0);
+}
+
+static int
+ifinfo_cmp(const void *a, const void *b)
+{
+	ipfw_iface_info *ia, *ib;
+
+	ia = (ipfw_iface_info *)a;
+	ib = (ipfw_iface_info *)b;
+
+	return (stringnum_cmp(ia->ifname, ib->ifname));
+}
+
+/*
+ * Retrieves table list from kernel,
+ * optionally sorts it and calls requested function for each table.
+ * Returns 0 on success.
+ */
+void
+ipfw_list_tifaces()
+{
+	ipfw_obj_lheader *olh;
+	ipfw_iface_info *info;
+	int i, error;
+
+	if ((error = ipfw_get_tracked_ifaces(&olh)) != 0)
+		err(EX_OSERR, "Unable to request ipfw tracked interface list");
+
+
+	qsort(olh + 1, olh->count, olh->objsize, ifinfo_cmp);
+
+	info = (ipfw_iface_info *)(olh + 1);
+	for (i = 0; i < olh->count; i++) {
+		if (info->flags & IPFW_IFFLAG_RESOLVED)
+			printf("%s ifindex: %d refcount: %u changes: %u\n",
+			    info->ifname, info->ifindex, info->refcnt,
+			    info->gencnt);
+		else
+			printf("%s ifindex: unresolved refcount: %u changes: %u\n",
+			    info->ifname, info->refcnt, info->gencnt);
+		info = (ipfw_iface_info *)((caddr_t)info + olh->objsize);
+	}
+
+	free(olh);
+}
+
+
+
+

Modified: projects/ipfw/sbin/ipfw/ipfw2.h
==============================================================================
--- projects/ipfw/sbin/ipfw/ipfw2.h	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sbin/ipfw/ipfw2.h	Mon Jul 28 19:01:25 2014	(r269195)
@@ -246,6 +246,7 @@ void *safe_realloc(void *ptr, size_t siz
 /* string comparison functions used for historical compatibility */
 int _substrcmp(const char *str1, const char* str2);
 int _substrcmp2(const char *str1, const char* str2, const char* str3);
+int stringnum_cmp(const char *a, const char *b);
 
 /* utility functions */
 int match_token(struct _s_x *table, char *string);
@@ -295,6 +296,7 @@ void ipfw_delete(char *av[]);
 void ipfw_flush(int force);
 void ipfw_zero(int ac, char *av[], int optname);
 void ipfw_list(int ac, char *av[], int show_counters);
+void ipfw_list_tifaces(void);
 
 #ifdef PF
 /* altq.c */

Modified: projects/ipfw/sbin/ipfw/main.c
==============================================================================
--- projects/ipfw/sbin/ipfw/main.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sbin/ipfw/main.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -438,6 +438,8 @@ ipfw_main(int oldac, char **oldav)
 			ipfw_list(ac, av, 1 /* show counters */);
 		else if (_substrcmp(*av, "table") == 0)
 			ipfw_table_handler(ac, av);
+		else if (_substrcmp(*av, "iflist") == 0)
+			ipfw_list_tifaces();
 		else
 			errx(EX_USAGE, "bad command `%s'", *av);
 	}

Modified: projects/ipfw/sbin/ipfw/tables.c
==============================================================================
--- projects/ipfw/sbin/ipfw/tables.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sbin/ipfw/tables.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -767,19 +767,11 @@ static int
 tablename_cmp(const void *a, const void *b)
 {
 	ipfw_xtable_info *ia, *ib;
-	int la, lb;
 
 	ia = (ipfw_xtable_info *)a;
 	ib = (ipfw_xtable_info *)b;
-	la = strlen(ia->tablename);
-	lb = strlen(ib->tablename);
 
-	if (la > lb)
-		return (1);
-	else if (la < lb)
-		return (-01);
-
-	return (strcmp(ia->tablename, ib->tablename));
+	return (stringnum_cmp(ia->tablename, ib->tablename));
 }
 
 /*

Modified: projects/ipfw/sys/conf/files
==============================================================================
--- projects/ipfw/sys/conf/files	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/conf/files	Mon Jul 28 19:01:25 2014	(r269195)
@@ -3450,6 +3450,7 @@ netpfil/ipfw/ip_fw_pfil.c	optional inet 
 netpfil/ipfw/ip_fw_sockopt.c	optional inet ipfirewall
 netpfil/ipfw/ip_fw_table.c	optional inet ipfirewall
 netpfil/ipfw/ip_fw_table_algo.c	optional inet ipfirewall
+netpfil/ipfw/ip_fw_iface.c	optional inet ipfirewall
 netpfil/ipfw/ip_fw_nat.c	optional inet ipfirewall_nat
 netpfil/pf/if_pflog.c		optional pflog pf inet
 netpfil/pf/if_pfsync.c		optional pfsync pf inet

Modified: projects/ipfw/sys/modules/ipfw/Makefile
==============================================================================
--- projects/ipfw/sys/modules/ipfw/Makefile	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/modules/ipfw/Makefile	Mon Jul 28 19:01:25 2014	(r269195)
@@ -7,7 +7,7 @@
 KMOD=	ipfw
 SRCS=	ip_fw2.c ip_fw_pfil.c
 SRCS+=	ip_fw_dynamic.c ip_fw_log.c
-SRCS+=	ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.c
+SRCS+=	ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.c ip_fw_iface.c
 SRCS+=	opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h opt_ipsec.h
 
 CFLAGS+= -DIPFIREWALL

Modified: projects/ipfw/sys/netinet/ip_fw.h
==============================================================================
--- projects/ipfw/sys/netinet/ip_fw.h	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/netinet/ip_fw.h	Mon Jul 28 19:01:25 2014	(r269195)
@@ -88,6 +88,7 @@ typedef struct _ip_fw3_opheader {
 #define	IP_FW_XGET		97	/* Retrieve configuration */
 #define	IP_FW_XADD		98	/* add entry */
 #define	IP_FW_TABLE_XFIND	99	/* finds an entry */
+#define	IP_FW_XIFLIST		100	/* list tracked interfaces */
 
 /*
  * Usage guidelines:
@@ -729,6 +730,7 @@ typedef struct  _ipfw_obj_tlv {
 #define	IPFW_TLV_TBL_ENT	5
 #define	IPFW_TLV_DYN_ENT	6
 #define	IPFW_TLV_RULE_ENT	7
+#define	IPFW_TLV_TBLENT_LIST	8
 
 /* Object name TLV */
 typedef struct _ipfw_obj_ntlv {
@@ -787,6 +789,16 @@ typedef struct _ipfw_xtable_info {
 	char		algoname[32];	/* algorithm name		*/
 } ipfw_xtable_info;
 
+typedef struct _ipfw_iface_info {
+	char		ifname[64];	/* interface name		*/
+	uint32_t	ifindex;	/* interface index		*/
+	uint32_t	flags;		/* flags			*/
+	uint32_t	refcnt;		/* number of references		*/
+	uint32_t	gencnt;		/* number of changes		*/
+	uint64_t	spare;
+} ipfw_iface_info;
+#define	IPFW_IFFLAG_RESOLVED	0x01	/* Interface exists		*/
+
 #define	IPFW_OBJTYPE_TABLE	1
 typedef struct _ipfw_obj_header {
 	ip_fw3_opheader	opheader;	/* IP_FW3 opcode		*/
@@ -801,7 +813,7 @@ typedef struct _ipfw_obj_lheader {
 	ip_fw3_opheader	opheader;	/* IP_FW3 opcode		*/
 	uint32_t	set_mask;	/* disabled set mask		*/
 	uint32_t	count;		/* Total objects count		*/
-	uint32_t	size;		/* Total objects size		*/
+	uint32_t	size;		/* Total size (incl. header)	*/
 	uint32_t	objsize;	/* Size of one object		*/
 } ipfw_obj_lheader;
 

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw2.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw2.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -357,15 +357,18 @@ tcpopts_match(struct tcphdr *tcp, ipfw_i
 }
 
 static int
-iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uint32_t *tablearg)
+iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain,
+    uint32_t *tablearg)
 {
+
 	if (ifp == NULL)	/* no iface with this packet, match fails */
-		return 0;
+		return (0);
+
 	/* Check by name or by IP address */
 	if (cmd->name[0] != '\0') { /* match by name */
 		if (cmd->name[0] == '\1') /* use tablearg to match */
 			return ipfw_lookup_table_extended(chain, cmd->p.glob, 0,
-				ifp->if_xname, tablearg);
+				&ifp->if_index, tablearg);
 		/* Check name */
 		if (cmd->p.glob) {
 			if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
@@ -2608,6 +2611,7 @@ ipfw_init(void)
 	  default_fw_tables = IPFW_TABLES_MAX;
 
 	ipfw_log_bpf(1); /* init */
+	ipfw_iface_init();
 	return (error);
 }
 
@@ -2618,6 +2622,7 @@ static void
 ipfw_destroy(void)
 {
 
+	ipfw_iface_destroy();
 	ipfw_log_bpf(0); /* uninit */
 	printf("IP firewall unloaded\n");
 }
@@ -2740,6 +2745,7 @@ vnet_ipfw_uninit(const void *unused)
 	ipfw_destroy_tables(chain);
 	if (reap != NULL)
 		ipfw_reap_rules(reap);
+	vnet_ipfw_iface_destroy(chain);
 	IPFW_LOCK_DESTROY(chain);
 	ipfw_dyn_uninit(1);	/* free the remaining parts */
 	ipfw_destroy_counters();

Added: projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -0,0 +1,529 @@
+/*-
+ * Copyright (c) 2014 Yandex LLC.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_iface.c 267384 2014-06-12 09:59:11Z melifaro $");
+
+/*
+ * Kernel interface tracking API.
+ *
+ */
+
+#include "opt_ipfw.h"
+#include "opt_inet.h"
+#ifndef INET
+#error IPFIREWALL requires INET.
+#endif /* INET */
+#include "opt_inet6.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/eventhandler.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/vnet.h>
+
+#include <netinet/in.h>
+#include <netinet/ip_var.h>	/* struct ipfw_rule_ref */
+#include <netinet/ip_fw.h>
+
+#include <netpfil/ipfw/ip_fw_private.h>
+
+#define	CHAIN_TO_II(ch)		((struct namedobj_instance *)ch->ifcfg)
+
+#define	DEFAULT_IFACES	128
+
+static void handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
+    uint16_t ifindex);
+static void handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
+    uint16_t ifindex);
+
+/*
+ * FreeBSD Kernel interface.
+ */
+
+static void ipfw_kifhandler(void *arg, struct ifnet *ifp);
+static int ipfw_kiflookup(char *name);
+static void iface_khandler_register(void);
+static void iface_khandler_deregister(void);
+
+static eventhandler_tag ipfw_ifdetach_event, ipfw_ifattach_event;
+static int num_vnets = 0;
+struct mtx vnet_mtx;
+
+/*
+ * Checks if kernel interface is contained in our tracked
+ * interface list and calls attach/detach handler.
+ */
+static void
+ipfw_kifhandler(void *arg, struct ifnet *ifp)
+{
+	struct ip_fw_chain *ch;
+	struct ipfw_iface *iif;
+	struct namedobj_instance *ii;
+	uintptr_t htype;
+
+	ch = &V_layer3_chain;
+	htype = (uintptr_t)arg;
+
+	if (ch == NULL)
+		return;
+
+	IPFW_UH_WLOCK(ch);
+	ii = CHAIN_TO_II(ch);
+	if (ii == NULL) {
+		IPFW_UH_WUNLOCK(ch);
+		return;
+	}
+	iif = (struct ipfw_iface*)ipfw_objhash_lookup_name(ii, 0,ifp->if_xname);
+	if (iif != NULL) {
+		if (htype == 1)
+			handle_ifattach(ch, iif, ifp->if_index);
+		else
+			handle_ifdetach(ch, iif, ifp->if_index);
+	}
+	IPFW_UH_WUNLOCK(ch);
+}
+
+/*
+ * Reference current VNET as iface tracking API user.
+ * Registers interface tracking handlers for first VNET.
+ */
+static void
+iface_khandler_register()
+{
+	int create;
+
+	create = 0;
+
+	mtx_lock(&vnet_mtx);
+	if (num_vnets == 0)
+		create = 1;
+	num_vnets++;
+	mtx_unlock(&vnet_mtx);
+
+	if (create == 0)
+		return;
+
+	printf("IPFW: starting up interface tracker\n");
+
+	ipfw_ifdetach_event = EVENTHANDLER_REGISTER(
+	    ifnet_departure_event, ipfw_kifhandler, NULL,
+	    EVENTHANDLER_PRI_ANY);
+	ipfw_ifattach_event = EVENTHANDLER_REGISTER(
+	    ifnet_arrival_event, ipfw_kifhandler, (void*)((uintptr_t)1),
+	    EVENTHANDLER_PRI_ANY);
+}
+
+/*
+ *
+ * Detach interface event handlers on last VNET instance
+ * detach.
+ */
+static void
+iface_khandler_deregister()
+{
+	int destroy;
+
+	destroy = 0;
+	mtx_lock(&vnet_mtx);
+	if (--num_vnets == 0)
+		destroy = 1;
+	mtx_unlock(&vnet_mtx);
+
+	if (destroy == 0)
+		return;
+
+	EVENTHANDLER_DEREGISTER(ifnet_arrival_event,
+	    ipfw_ifattach_event);
+	EVENTHANDLER_DEREGISTER(ifnet_departure_event,
+	    ipfw_ifdetach_event);
+}
+
+/*
+ * Retrieves ifindex for given @name.
+ *
+ * Returns ifindex or 0.
+ */
+static int
+ipfw_kiflookup(char *name)
+{
+	struct ifnet *ifp;
+	int ifindex;
+
+	ifindex = 0;
+
+	if ((ifp = ifunit_ref(name)) != NULL) {
+		ifindex = ifp->if_index;
+		if_rele(ifp);
+	}
+
+	return (ifindex);
+}
+
+
+
+/*
+ * Global ipfw startup hook.
+ * Since we perform lazy initialization, do nothing except
+ * mutex init.
+ */
+int
+ipfw_iface_init()
+{
+
+	mtx_init(&vnet_mtx, "IPFW ifhandler mtx", NULL, MTX_DEF);
+	return (0);
+}
+
+/*
+ * Global ipfw destroy hook.
+ * Unregister khandlers iff init has been done.
+ */
+void
+ipfw_iface_destroy()
+{
+
+	mtx_destroy(&vnet_mtx);
+}
+
+/*
+ * Perform actual init on internal request.
+ * Inits both namehash and global khandler.
+ */
+static void
+vnet_ipfw_iface_init(struct ip_fw_chain *ch)
+{
+	struct namedobj_instance *ii;
+
+	ii = ipfw_objhash_create(DEFAULT_IFACES);
+	IPFW_UH_WLOCK(ch);
+	if (ch->ifcfg == NULL) {
+		ch->ifcfg = ii;
+		ii = NULL;
+	}
+	IPFW_UH_WUNLOCK(ch);
+
+	if (ii != NULL) {
+		/* Already initialized. Free namehash. */
+		ipfw_objhash_destroy(ii);
+	} else {
+		/* We're the first ones. Init kernel hooks. */
+		iface_khandler_register();
+	}
+}
+
+static void
+destroy_iface(struct namedobj_instance *ii, struct named_object *no,
+    void *arg)
+{
+	struct ipfw_iface *iif;
+	struct ip_fw_chain *ch;
+
+	ch = (struct ip_fw_chain *)arg;
+	iif = (struct ipfw_iface *)no;
+
+	/* Assume all consumers have been already detached */
+	free(iif, M_IPFW);
+}
+
+/*
+ * Per-VNET ipfw detach hook.
+ *
+ */
+void
+vnet_ipfw_iface_destroy(struct ip_fw_chain *ch)
+{
+	struct namedobj_instance *ii;
+
+	IPFW_UH_WLOCK(ch);
+	ii = CHAIN_TO_II(ch);
+	ch->ifcfg = NULL;
+	IPFW_UH_WUNLOCK(ch);
+
+	if (ii != NULL) {
+		ipfw_objhash_foreach(ii, destroy_iface, ch);
+		ipfw_objhash_destroy(ii);
+		iface_khandler_deregister();
+	}
+}
+
+/*
+ * Notify the subsystem that we are interested in tracking
+ * interface @name. This function has to be called without
+ * holding any locks to permit allocating the necessary states
+ * for proper interface tracking.
+ *
+ * Returns 0 on success.
+ */
+int
+ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
+    struct ipfw_ifc *ic)
+{
+	struct namedobj_instance *ii;
+	struct ipfw_iface *iif, *tmp;
+
+	if (strlen(name) >= sizeof(iif->ifname))
+		return (EINVAL);
+
+	IPFW_UH_WLOCK(ch);
+
+	ii = CHAIN_TO_II(ch);
+	if (ii == NULL) {
+
+		/*
+		 * First request to subsystem.
+		 * Let's perform init.
+		 */
+		IPFW_UH_WUNLOCK(ch);
+		vnet_ipfw_iface_init(ch);
+		IPFW_UH_WLOCK(ch);
+		ii = CHAIN_TO_II(ch);
+	}
+
+	iif = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
+
+	if (iif != NULL) {
+		iif->no.refcnt++;
+		ic->iface = iif;
+		IPFW_UH_WUNLOCK(ch);
+		return (0);
+	}
+
+	IPFW_UH_WUNLOCK(ch);
+
+	/* Not found. Let's create one */
+	iif = malloc(sizeof(struct ipfw_iface), M_IPFW, M_WAITOK | M_ZERO);
+	TAILQ_INIT(&iif->consumers);
+	iif->no.name = iif->ifname;
+	strlcpy(iif->ifname, name, sizeof(iif->ifname));
+
+	/*
+	 * Ref & link to the list.
+	 *
+	 * We assume  ifnet_arrival_event / ifnet_departure_event
+	 * are not holding any locks.
+	 */
+	iif->no.refcnt = 1;
+	IPFW_UH_WLOCK(ch);
+
+	tmp = (struct ipfw_iface *)ipfw_objhash_lookup_name(ii, 0, name);
+	if (tmp != NULL) {
+		/* Interface has been created since unlock. Ref and return */
+		tmp->no.refcnt++;
+		ic->iface = tmp;
+		IPFW_UH_WUNLOCK(ch);
+		free(iif, M_IPFW);
+		return (0);
+	}
+
+	iif->ifindex = ipfw_kiflookup(name);
+	if (iif->ifindex != 0)
+		iif->resolved = 1;
+
+	ipfw_objhash_add(ii, &iif->no);
+	ic->iface = iif;
+
+	IPFW_UH_WUNLOCK(ch);
+
+	return (0);
+}
+
+/*
+ * Adds @ic to the list of iif interface consumers.
+ * Must be called with holding both UH+WLOCK.
+ * Callback may be immediately called (if interface exists).
+ */
+void
+ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
+{
+	struct ipfw_iface *iif;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+	IPFW_WLOCK_ASSERT(ch);
+
+	iif = ic->iface;
+	
+	TAILQ_INSERT_TAIL(&iif->consumers, ic, next);
+	if (iif->resolved != 0)
+		ic->cb(ch, ic->cbdata, iif->ifindex);
+}
+
+/*
+ * Unlinks interface tracker object @ic from interface.
+ * Must be called whi holding UH lock.
+ */
+void
+ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
+{
+	struct ipfw_iface *iif;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	iif = ic->iface;
+	if (ic->linked != 0)
+		TAILQ_REMOVE(&iif->consumers, ic, next);
+}
+
+/*
+ * Unreference interface specified by @ic.
+ * Must be called without holding any locks.
+ */
+void
+ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic)
+{
+	struct ipfw_iface *iif;
+
+	iif = ic->iface;
+	ic->iface = NULL;
+
+	IPFW_UH_WLOCK(ch);
+	iif->no.refcnt--;
+	/* TODO: check for references & delete */
+	IPFW_UH_WUNLOCK(ch);
+}
+
+/*
+ * Interface arrival handler.
+ */
+static void
+handle_ifattach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
+    uint16_t ifindex)
+{
+	struct ipfw_ifc *ic;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	iif->gencnt++;
+	iif->resolved = 1;
+	iif->ifindex = ifindex;
+
+	IPFW_WLOCK(ch);
+	TAILQ_FOREACH(ic, &iif->consumers, next)
+		ic->cb(ch, ic->cbdata, iif->ifindex);
+	IPFW_WUNLOCK(ch);
+}
+
+/*
+ * Interface departure handler.
+ */
+static void
+handle_ifdetach(struct ip_fw_chain *ch, struct ipfw_iface *iif,
+    uint16_t ifindex)
+{
+	struct ipfw_ifc *ic;
+
+	IPFW_UH_WLOCK_ASSERT(ch);
+
+	IPFW_WLOCK(ch);
+	TAILQ_FOREACH(ic, &iif->consumers, next)
+		ic->cb(ch, ic->cbdata, 0);
+	IPFW_WUNLOCK(ch);
+
+	iif->gencnt++;
+	iif->resolved = 0;
+	iif->ifindex = 0;
+}
+
+struct dump_iface_args {
+	struct ip_fw_chain *ch;
+	struct sockopt_data *sd;
+};
+
+static void
+export_iface_internal(struct namedobj_instance *ii, struct named_object *no,
+    void *arg)
+{
+	ipfw_iface_info *i;
+	struct dump_iface_args *da;
+	struct ipfw_iface *iif;
+
+	da = (struct dump_iface_args *)arg;
+
+	i = (ipfw_iface_info *)ipfw_get_sopt_space(da->sd, sizeof(*i));
+	KASSERT(i != 0, ("previously checked buffer is not enough"));
+
+	iif = (struct ipfw_iface *)no;
+
+	strlcpy(i->ifname, iif->ifname, sizeof(i->ifname));
+	if (iif->resolved)
+		i->flags |= IPFW_IFFLAG_RESOLVED;
+	i->ifindex = iif->ifindex;
+	i->refcnt = iif->no.refcnt;
+	i->gencnt = iif->gencnt;
+}
+
+/*
+ * Lists all interface currently tracked by ipfw.
+ * Data layout (v0)(current):
+ * Request: [ ipfw_obj_lheader ], size = ipfw_obj_lheader.size
+ * Reply: [ ipfw_obj_lheader ipfw_iface_info x N ]
+ *
+ * Returns 0 on success
+ */
+int
+ipfw_list_ifaces(struct ip_fw_chain *ch, struct sockopt_data *sd)
+{
+	struct _ipfw_obj_lheader *olh;
+	struct dump_iface_args da;
+	uint32_t count, size;
+
+	olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
+	if (olh == NULL)
+		return (EINVAL);
+	if (sd->valsize < olh->size)
+		return (EINVAL);
+
+	IPFW_UH_RLOCK(ch);
+	count = ipfw_objhash_count(CHAIN_TO_II(ch));
+	size = count * sizeof(ipfw_iface_info) + sizeof(ipfw_obj_lheader);
+
+	/* Fill in header regadless of buffer size */
+	olh->count = count;
+	olh->objsize = sizeof(ipfw_iface_info);
+
+	if (size > olh->size) {
+		olh->size = size;
+		IPFW_UH_RUNLOCK(ch);
+		return (ENOMEM);
+	}
+	olh->size = size;
+
+	da.ch = ch;
+	da.sd = sd;
+
+	ipfw_objhash_foreach(CHAIN_TO_II(ch), export_iface_internal, &da);
+	IPFW_UH_RUNLOCK(ch);
+
+	return (0);
+}
+
+

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_private.h	Mon Jul 28 19:01:25 2014	(r269195)
@@ -274,6 +274,7 @@ struct ip_fw_chain {
 	struct ip_fw	*reap;		/* list of rules to reap */
 	struct ip_fw	*default_rule;
 	struct tables_config *tblcfg;	/* tables module data */
+	void		*ifcfg;		/* interface module data */
 #if defined( __linux__ ) || defined( _WIN32 )
 	spinlock_t uh_lock;
 #else
@@ -281,6 +282,21 @@ struct ip_fw_chain {
 #endif
 };
 
+struct namedobj_instance;
+
+struct named_object {
+	TAILQ_ENTRY(named_object)	nn_next;	/* namehash */
+	TAILQ_ENTRY(named_object)	nv_next;	/* valuehash */
+	char			*name;	/* object name */
+	uint8_t			type;	/* object type */
+	uint8_t			compat;	/* Object name is number */
+	uint16_t		kidx;	/* object kernel index */
+	uint16_t		uidx;	/* userland idx for compat records */
+	uint32_t		set;	/* set object belongs to */
+	uint32_t		refcnt;	/* number of references */
+};
+TAILQ_HEAD(namedobjects_head, named_object);
+
 struct sockopt;	/* used by tcp_var.h */
 struct sockopt_data {
 	caddr_t		kbuf;		/* allocated buffer */
@@ -292,6 +308,30 @@ struct sockopt_data {
 	size_t		valsize;	/* original data size */
 };
 
+struct ipfw_ifc;
+
+typedef void (ipfw_ifc_cb)(struct ip_fw_chain *ch, void *cbdata,
+    uint16_t ifindex);
+
+struct ipfw_iface {
+	struct named_object	no;
+	char ifname[64];
+	int resolved;
+	uint16_t ifindex;
+	uint16_t spare;
+	uint64_t gencnt;
+	TAILQ_HEAD(, ipfw_ifc)	consumers;
+};
+
+struct ipfw_ifc {
+	TAILQ_ENTRY(ipfw_ifc)	next;
+	struct ipfw_iface	*iface;
+	ipfw_ifc_cb		*cb;
+	void			*cbdata;
+	int			linked;
+	int			spare;
+};
+
 /* Macro for working with various counters */
 #ifdef USERSPACE
 #define	IPFW_INC_RULE_COUNTER(_cntr, _bytes)	do {	\
@@ -442,6 +482,17 @@ struct ip_fw_bcounter0 {
 #define	RULEKSIZE1(r)	roundup2((sizeof(struct ip_fw) + (r)->cmd_len*4 - 4), 8)
 
 
+/* In ip_fw_iface.c */
+int ipfw_iface_init(void);
+void ipfw_iface_destroy(void);
+void vnet_ipfw_iface_destroy(struct ip_fw_chain *ch);
+int ipfw_iface_ref(struct ip_fw_chain *ch, char *name,
+    struct ipfw_ifc *ic);
+void ipfw_iface_unref(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
+void ipfw_iface_add_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
+void ipfw_iface_del_notify(struct ip_fw_chain *ch, struct ipfw_ifc *ic);
+int ipfw_list_ifaces(struct ip_fw_chain *ch, struct sockopt_data *sd);
+
 /* In ip_fw_sockopt.c */
 int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id);
 int ipfw_ctl(struct sockopt *sopt);
@@ -454,21 +505,6 @@ struct ip_fw *ipfw_alloc_rule(struct ip_
 caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
 caddr_t ipfw_get_sopt_header(struct sockopt_data *sd, size_t needed);
 
-struct namedobj_instance;
-
-struct named_object {
-	TAILQ_ENTRY(named_object)	nn_next;	/* namehash */
-	TAILQ_ENTRY(named_object)	nv_next;	/* valuehash */
-	char			*name;	/* object name */
-	uint8_t			type;	/* object type */
-	uint8_t			compat;	/* Object name is number */
-	uint16_t		kidx;	/* object kernel index */
-	uint16_t		uidx;	/* userland idx for compat records */
-	uint32_t		set;	/* set object belongs to */
-	uint32_t		refcnt;	/* number of references */
-};
-TAILQ_HEAD(namedobjects_head, named_object);
-
 typedef void (objhash_cb_t)(struct namedobj_instance *ni, struct named_object *,
     void *arg);
 struct namedobj_instance *ipfw_objhash_create(uint32_t items);

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_sockopt.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -1859,6 +1859,10 @@ ipfw_ctl(struct sockopt *sopt)
 		error = dump_config(chain, &sdata);
 		break;
 
+	case IP_FW_XIFLIST: /* IP_FW3 */
+		error = ipfw_list_ifaces(chain, &sdata);
+		break;
+
 	case IP_FW_XADD: /* IP_FW3 */
 		error = add_entry(chain, &sdata);
 		break;

Modified: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c
==============================================================================
--- projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Mon Jul 28 14:41:22 2014	(r269194)
+++ projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c	Mon Jul 28 19:01:25 2014	(r269195)
@@ -94,7 +94,7 @@ struct tables_config {
 
 static struct table_config *find_table(struct namedobj_instance *ni,
     struct tid_info *ti);
-static struct table_config *alloc_table_config(struct namedobj_instance *ni,
+static struct table_config *alloc_table_config(struct ip_fw_chain *ch,
     struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t vtype);
 static void free_table_config(struct namedobj_instance *ni,
     struct table_config *tc);
@@ -206,7 +206,7 @@ add_table_entry(struct ip_fw_chain *ch, 
 
 	/* Prepare record (allocate memory) */
 	memset(&ta_buf, 0, sizeof(ta_buf));
-	error = ta->prepare_add(tei, &ta_buf);
+	error = ta->prepare_add(ch, tei, &ta_buf);
 	if (error != 0)
 		return (error);
 
@@ -238,7 +238,7 @@ add_table_entry(struct ip_fw_chain *ch, 
 	IPFW_UH_WUNLOCK(ch);
 
 	/* Run cleaning callback anyway */
-	ta->flush_entry(tei, &ta_buf);
+	ta->flush_entry(ch, tei, &ta_buf);
 
 	return (error);
 }
@@ -296,7 +296,7 @@ del_table_entry(struct ip_fw_chain *ch, 
 	 * prepare_del() key, so we're running under UH_LOCK here.
 	 */
 	memset(&ta_buf, 0, sizeof(ta_buf));
-	if ((error = ta->prepare_del(tei, &ta_buf)) != 0) {
+	if ((error = ta->prepare_del(ch, tei, &ta_buf)) != 0) {
 		IPFW_UH_WUNLOCK(ch);
 		return (error);
 	}
@@ -313,7 +313,7 @@ del_table_entry(struct ip_fw_chain *ch, 
 
 	IPFW_UH_WUNLOCK(ch);
 
-	ta->flush_entry(tei, &ta_buf);
+	ta->flush_entry(ch, tei, &ta_buf);
 
 	return (error);
 }
@@ -341,7 +341,7 @@ modify_table(struct ip_fw_chain *ch, str
 	IPFW_UH_WLOCK(ch);
 	ti = KIDX_TO_TI(ch, tc->no.kidx);
 
-	error = ta->fill_mod(tc->astate, ti, &ta_buf, &pflags);
+	error = ta->fill_mod(tc->astate, ti, ta_buf, &pflags);
 
 	/*
 	 * prepare_mofify may return zero in @pflags to
@@ -351,7 +351,7 @@ modify_table(struct ip_fw_chain *ch, str
 	if (error == 0 && pflags != 0) {
 		/* Do actual modification */
 		IPFW_WLOCK(ch);
-		ta->modify(tc->astate, ti, &ta_buf, pflags);
+		ta->modify(tc->astate, ti, ta_buf, pflags);
 		IPFW_WUNLOCK(ch);
 	}
 
@@ -666,7 +666,7 @@ flush_table(struct ip_fw_chain *ch, stru
 	 * TODO: pass startup parametes somehow.
 	 */
 	memset(&ti_new, 0, sizeof(struct table_info));
-	if ((error = ta->init(&astate_new, &ti_new, NULL)) != 0) {
+	if ((error = ta->init(ch, &astate_new, &ti_new, NULL)) != 0) {
 		IPFW_UH_WLOCK(ch);
 		tc->no.refcnt--;
 		IPFW_UH_WUNLOCK(ch);
@@ -803,7 +803,9 @@ ipfw_resize_tables(struct ip_fw_chain *c
 	unsigned int ntables_old, tbl;
 	struct namedobj_instance *ni;
 	void *new_idx, *old_tablestate, *tablestate;
-	int new_blocks;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-projects mailing list