From owner-svn-src-projects@FreeBSD.ORG Mon Jul 28 19:01:28 2014 Return-Path: Delivered-To: svn-src-projects@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 8A631B1C; Mon, 28 Jul 2014 19:01:28 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 6B1952473; Mon, 28 Jul 2014 19:01:28 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.9/8.14.9) with ESMTP id s6SJ1Sx9031895; Mon, 28 Jul 2014 19:01:28 GMT (envelope-from melifaro@svn.freebsd.org) Received: (from melifaro@localhost) by svn.freebsd.org (8.14.9/8.14.9/Submit) id s6SJ1QI3031877; Mon, 28 Jul 2014 19:01:26 GMT (envelope-from melifaro@svn.freebsd.org) Message-Id: <201407281901.s6SJ1QI3031877@svn.freebsd.org> From: "Alexander V. Chernikov" Date: Mon, 28 Jul 2014 19:01:25 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r269195 - in projects/ipfw: sbin/ipfw sys/conf sys/modules/ipfw sys/netinet sys/netpfil/ipfw X-SVN-Group: projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 28 Jul 2014 19:01:28 -0000 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 +__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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* struct ipfw_rule_ref */ +#include + +#include + +#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 ***