* 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
This commit is contained in:
parent
db785d3199
commit
68394ec88e
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/ipfw/; revision=269195
@ -519,6 +519,26 @@ safe_realloc(void *ptr, size_t size)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* conditionally runs the command.
|
||||||
* Selected options or negative -> getsockopt
|
* Selected options or negative -> getsockopt
|
||||||
@ -4682,3 +4702,78 @@ ipfw_flush(int force)
|
|||||||
printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules");
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -246,6 +246,7 @@ void *safe_realloc(void *ptr, size_t size);
|
|||||||
/* string comparison functions used for historical compatibility */
|
/* string comparison functions used for historical compatibility */
|
||||||
int _substrcmp(const char *str1, const char* str2);
|
int _substrcmp(const char *str1, const char* str2);
|
||||||
int _substrcmp2(const char *str1, const char* str2, const char* str3);
|
int _substrcmp2(const char *str1, const char* str2, const char* str3);
|
||||||
|
int stringnum_cmp(const char *a, const char *b);
|
||||||
|
|
||||||
/* utility functions */
|
/* utility functions */
|
||||||
int match_token(struct _s_x *table, char *string);
|
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_flush(int force);
|
||||||
void ipfw_zero(int ac, char *av[], int optname);
|
void ipfw_zero(int ac, char *av[], int optname);
|
||||||
void ipfw_list(int ac, char *av[], int show_counters);
|
void ipfw_list(int ac, char *av[], int show_counters);
|
||||||
|
void ipfw_list_tifaces(void);
|
||||||
|
|
||||||
#ifdef PF
|
#ifdef PF
|
||||||
/* altq.c */
|
/* altq.c */
|
||||||
|
@ -438,6 +438,8 @@ ipfw_main(int oldac, char **oldav)
|
|||||||
ipfw_list(ac, av, 1 /* show counters */);
|
ipfw_list(ac, av, 1 /* show counters */);
|
||||||
else if (_substrcmp(*av, "table") == 0)
|
else if (_substrcmp(*av, "table") == 0)
|
||||||
ipfw_table_handler(ac, av);
|
ipfw_table_handler(ac, av);
|
||||||
|
else if (_substrcmp(*av, "iflist") == 0)
|
||||||
|
ipfw_list_tifaces();
|
||||||
else
|
else
|
||||||
errx(EX_USAGE, "bad command `%s'", *av);
|
errx(EX_USAGE, "bad command `%s'", *av);
|
||||||
}
|
}
|
||||||
|
@ -767,19 +767,11 @@ static int
|
|||||||
tablename_cmp(const void *a, const void *b)
|
tablename_cmp(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
ipfw_xtable_info *ia, *ib;
|
ipfw_xtable_info *ia, *ib;
|
||||||
int la, lb;
|
|
||||||
|
|
||||||
ia = (ipfw_xtable_info *)a;
|
ia = (ipfw_xtable_info *)a;
|
||||||
ib = (ipfw_xtable_info *)b;
|
ib = (ipfw_xtable_info *)b;
|
||||||
la = strlen(ia->tablename);
|
|
||||||
lb = strlen(ib->tablename);
|
|
||||||
|
|
||||||
if (la > lb)
|
return (stringnum_cmp(ia->tablename, ib->tablename));
|
||||||
return (1);
|
|
||||||
else if (la < lb)
|
|
||||||
return (-01);
|
|
||||||
|
|
||||||
return (strcmp(ia->tablename, ib->tablename));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3450,6 +3450,7 @@ netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall
|
|||||||
netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall
|
netpfil/ipfw/ip_fw_sockopt.c optional inet ipfirewall
|
||||||
netpfil/ipfw/ip_fw_table.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_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/ipfw/ip_fw_nat.c optional inet ipfirewall_nat
|
||||||
netpfil/pf/if_pflog.c optional pflog pf inet
|
netpfil/pf/if_pflog.c optional pflog pf inet
|
||||||
netpfil/pf/if_pfsync.c optional pfsync pf inet
|
netpfil/pf/if_pfsync.c optional pfsync pf inet
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
KMOD= ipfw
|
KMOD= ipfw
|
||||||
SRCS= ip_fw2.c ip_fw_pfil.c
|
SRCS= ip_fw2.c ip_fw_pfil.c
|
||||||
SRCS+= ip_fw_dynamic.c ip_fw_log.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
|
SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h opt_ipsec.h
|
||||||
|
|
||||||
CFLAGS+= -DIPFIREWALL
|
CFLAGS+= -DIPFIREWALL
|
||||||
|
@ -88,6 +88,7 @@ typedef struct _ip_fw3_opheader {
|
|||||||
#define IP_FW_XGET 97 /* Retrieve configuration */
|
#define IP_FW_XGET 97 /* Retrieve configuration */
|
||||||
#define IP_FW_XADD 98 /* add entry */
|
#define IP_FW_XADD 98 /* add entry */
|
||||||
#define IP_FW_TABLE_XFIND 99 /* finds an entry */
|
#define IP_FW_TABLE_XFIND 99 /* finds an entry */
|
||||||
|
#define IP_FW_XIFLIST 100 /* list tracked interfaces */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usage guidelines:
|
* Usage guidelines:
|
||||||
@ -729,6 +730,7 @@ typedef struct _ipfw_obj_tlv {
|
|||||||
#define IPFW_TLV_TBL_ENT 5
|
#define IPFW_TLV_TBL_ENT 5
|
||||||
#define IPFW_TLV_DYN_ENT 6
|
#define IPFW_TLV_DYN_ENT 6
|
||||||
#define IPFW_TLV_RULE_ENT 7
|
#define IPFW_TLV_RULE_ENT 7
|
||||||
|
#define IPFW_TLV_TBLENT_LIST 8
|
||||||
|
|
||||||
/* Object name TLV */
|
/* Object name TLV */
|
||||||
typedef struct _ipfw_obj_ntlv {
|
typedef struct _ipfw_obj_ntlv {
|
||||||
@ -787,6 +789,16 @@ typedef struct _ipfw_xtable_info {
|
|||||||
char algoname[32]; /* algorithm name */
|
char algoname[32]; /* algorithm name */
|
||||||
} ipfw_xtable_info;
|
} 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
|
#define IPFW_OBJTYPE_TABLE 1
|
||||||
typedef struct _ipfw_obj_header {
|
typedef struct _ipfw_obj_header {
|
||||||
ip_fw3_opheader opheader; /* IP_FW3 opcode */
|
ip_fw3_opheader opheader; /* IP_FW3 opcode */
|
||||||
@ -801,7 +813,7 @@ typedef struct _ipfw_obj_lheader {
|
|||||||
ip_fw3_opheader opheader; /* IP_FW3 opcode */
|
ip_fw3_opheader opheader; /* IP_FW3 opcode */
|
||||||
uint32_t set_mask; /* disabled set mask */
|
uint32_t set_mask; /* disabled set mask */
|
||||||
uint32_t count; /* Total objects count */
|
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 */
|
uint32_t objsize; /* Size of one object */
|
||||||
} ipfw_obj_lheader;
|
} ipfw_obj_lheader;
|
||||||
|
|
||||||
|
@ -357,15 +357,18 @@ tcpopts_match(struct tcphdr *tcp, ipfw_insn *cmd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
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 */
|
if (ifp == NULL) /* no iface with this packet, match fails */
|
||||||
return 0;
|
return (0);
|
||||||
|
|
||||||
/* Check by name or by IP address */
|
/* Check by name or by IP address */
|
||||||
if (cmd->name[0] != '\0') { /* match by name */
|
if (cmd->name[0] != '\0') { /* match by name */
|
||||||
if (cmd->name[0] == '\1') /* use tablearg to match */
|
if (cmd->name[0] == '\1') /* use tablearg to match */
|
||||||
return ipfw_lookup_table_extended(chain, cmd->p.glob, 0,
|
return ipfw_lookup_table_extended(chain, cmd->p.glob, 0,
|
||||||
ifp->if_xname, tablearg);
|
&ifp->if_index, tablearg);
|
||||||
/* Check name */
|
/* Check name */
|
||||||
if (cmd->p.glob) {
|
if (cmd->p.glob) {
|
||||||
if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
|
if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
|
||||||
@ -2608,6 +2611,7 @@ ipfw_init(void)
|
|||||||
default_fw_tables = IPFW_TABLES_MAX;
|
default_fw_tables = IPFW_TABLES_MAX;
|
||||||
|
|
||||||
ipfw_log_bpf(1); /* init */
|
ipfw_log_bpf(1); /* init */
|
||||||
|
ipfw_iface_init();
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2618,6 +2622,7 @@ static void
|
|||||||
ipfw_destroy(void)
|
ipfw_destroy(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
ipfw_iface_destroy();
|
||||||
ipfw_log_bpf(0); /* uninit */
|
ipfw_log_bpf(0); /* uninit */
|
||||||
printf("IP firewall unloaded\n");
|
printf("IP firewall unloaded\n");
|
||||||
}
|
}
|
||||||
@ -2740,6 +2745,7 @@ vnet_ipfw_uninit(const void *unused)
|
|||||||
ipfw_destroy_tables(chain);
|
ipfw_destroy_tables(chain);
|
||||||
if (reap != NULL)
|
if (reap != NULL)
|
||||||
ipfw_reap_rules(reap);
|
ipfw_reap_rules(reap);
|
||||||
|
vnet_ipfw_iface_destroy(chain);
|
||||||
IPFW_LOCK_DESTROY(chain);
|
IPFW_LOCK_DESTROY(chain);
|
||||||
ipfw_dyn_uninit(1); /* free the remaining parts */
|
ipfw_dyn_uninit(1); /* free the remaining parts */
|
||||||
ipfw_destroy_counters();
|
ipfw_destroy_counters();
|
||||||
|
529
sys/netpfil/ipfw/ip_fw_iface.c
Normal file
529
sys/netpfil/ipfw/ip_fw_iface.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -274,6 +274,7 @@ struct ip_fw_chain {
|
|||||||
struct ip_fw *reap; /* list of rules to reap */
|
struct ip_fw *reap; /* list of rules to reap */
|
||||||
struct ip_fw *default_rule;
|
struct ip_fw *default_rule;
|
||||||
struct tables_config *tblcfg; /* tables module data */
|
struct tables_config *tblcfg; /* tables module data */
|
||||||
|
void *ifcfg; /* interface module data */
|
||||||
#if defined( __linux__ ) || defined( _WIN32 )
|
#if defined( __linux__ ) || defined( _WIN32 )
|
||||||
spinlock_t uh_lock;
|
spinlock_t uh_lock;
|
||||||
#else
|
#else
|
||||||
@ -281,6 +282,21 @@ struct ip_fw_chain {
|
|||||||
#endif
|
#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; /* used by tcp_var.h */
|
||||||
struct sockopt_data {
|
struct sockopt_data {
|
||||||
caddr_t kbuf; /* allocated buffer */
|
caddr_t kbuf; /* allocated buffer */
|
||||||
@ -292,6 +308,30 @@ struct sockopt_data {
|
|||||||
size_t valsize; /* original data size */
|
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 */
|
/* Macro for working with various counters */
|
||||||
#ifdef USERSPACE
|
#ifdef USERSPACE
|
||||||
#define IPFW_INC_RULE_COUNTER(_cntr, _bytes) do { \
|
#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)
|
#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 */
|
/* In ip_fw_sockopt.c */
|
||||||
int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id);
|
int ipfw_find_rule(struct ip_fw_chain *chain, uint32_t key, uint32_t id);
|
||||||
int ipfw_ctl(struct sockopt *sopt);
|
int ipfw_ctl(struct sockopt *sopt);
|
||||||
@ -454,21 +505,6 @@ struct ip_fw *ipfw_alloc_rule(struct ip_fw_chain *chain, size_t rulesize);
|
|||||||
caddr_t ipfw_get_sopt_space(struct sockopt_data *sd, size_t needed);
|
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);
|
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 *,
|
typedef void (objhash_cb_t)(struct namedobj_instance *ni, struct named_object *,
|
||||||
void *arg);
|
void *arg);
|
||||||
struct namedobj_instance *ipfw_objhash_create(uint32_t items);
|
struct namedobj_instance *ipfw_objhash_create(uint32_t items);
|
||||||
|
@ -1859,6 +1859,10 @@ ipfw_ctl(struct sockopt *sopt)
|
|||||||
error = dump_config(chain, &sdata);
|
error = dump_config(chain, &sdata);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case IP_FW_XIFLIST: /* IP_FW3 */
|
||||||
|
error = ipfw_list_ifaces(chain, &sdata);
|
||||||
|
break;
|
||||||
|
|
||||||
case IP_FW_XADD: /* IP_FW3 */
|
case IP_FW_XADD: /* IP_FW3 */
|
||||||
error = add_entry(chain, &sdata);
|
error = add_entry(chain, &sdata);
|
||||||
break;
|
break;
|
||||||
|
@ -94,7 +94,7 @@ struct tables_config {
|
|||||||
|
|
||||||
static struct table_config *find_table(struct namedobj_instance *ni,
|
static struct table_config *find_table(struct namedobj_instance *ni,
|
||||||
struct tid_info *ti);
|
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);
|
struct tid_info *ti, struct table_algo *ta, char *adata, uint8_t vtype);
|
||||||
static void free_table_config(struct namedobj_instance *ni,
|
static void free_table_config(struct namedobj_instance *ni,
|
||||||
struct table_config *tc);
|
struct table_config *tc);
|
||||||
@ -206,7 +206,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
|
|
||||||
/* Prepare record (allocate memory) */
|
/* Prepare record (allocate memory) */
|
||||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
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)
|
if (error != 0)
|
||||||
return (error);
|
return (error);
|
||||||
|
|
||||||
@ -238,7 +238,7 @@ add_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
/* Run cleaning callback anyway */
|
/* Run cleaning callback anyway */
|
||||||
ta->flush_entry(tei, &ta_buf);
|
ta->flush_entry(ch, tei, &ta_buf);
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -296,7 +296,7 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
* prepare_del() key, so we're running under UH_LOCK here.
|
* prepare_del() key, so we're running under UH_LOCK here.
|
||||||
*/
|
*/
|
||||||
memset(&ta_buf, 0, sizeof(ta_buf));
|
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);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -313,7 +313,7 @@ del_table_entry(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
ta->flush_entry(tei, &ta_buf);
|
ta->flush_entry(ch, tei, &ta_buf);
|
||||||
|
|
||||||
return (error);
|
return (error);
|
||||||
}
|
}
|
||||||
@ -341,7 +341,7 @@ modify_table(struct ip_fw_chain *ch, struct table_config *tc,
|
|||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
ti = KIDX_TO_TI(ch, tc->no.kidx);
|
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
|
* prepare_mofify may return zero in @pflags to
|
||||||
@ -351,7 +351,7 @@ modify_table(struct ip_fw_chain *ch, struct table_config *tc,
|
|||||||
if (error == 0 && pflags != 0) {
|
if (error == 0 && pflags != 0) {
|
||||||
/* Do actual modification */
|
/* Do actual modification */
|
||||||
IPFW_WLOCK(ch);
|
IPFW_WLOCK(ch);
|
||||||
ta->modify(tc->astate, ti, &ta_buf, pflags);
|
ta->modify(tc->astate, ti, ta_buf, pflags);
|
||||||
IPFW_WUNLOCK(ch);
|
IPFW_WUNLOCK(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -666,7 +666,7 @@ flush_table(struct ip_fw_chain *ch, struct tid_info *ti)
|
|||||||
* TODO: pass startup parametes somehow.
|
* TODO: pass startup parametes somehow.
|
||||||
*/
|
*/
|
||||||
memset(&ti_new, 0, sizeof(struct table_info));
|
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);
|
IPFW_UH_WLOCK(ch);
|
||||||
tc->no.refcnt--;
|
tc->no.refcnt--;
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
@ -803,7 +803,9 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
|
|||||||
unsigned int ntables_old, tbl;
|
unsigned int ntables_old, tbl;
|
||||||
struct namedobj_instance *ni;
|
struct namedobj_instance *ni;
|
||||||
void *new_idx, *old_tablestate, *tablestate;
|
void *new_idx, *old_tablestate, *tablestate;
|
||||||
int new_blocks;
|
struct table_info *ti;
|
||||||
|
struct table_config *tc;
|
||||||
|
int i, new_blocks;
|
||||||
|
|
||||||
/* Check new value for validity */
|
/* Check new value for validity */
|
||||||
if (ntables > IPFW_TABLES_MAX)
|
if (ntables > IPFW_TABLES_MAX)
|
||||||
@ -845,6 +847,19 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
|
|||||||
V_fw_tables_max = ntables;
|
V_fw_tables_max = ntables;
|
||||||
|
|
||||||
IPFW_WUNLOCK(ch);
|
IPFW_WUNLOCK(ch);
|
||||||
|
|
||||||
|
/* Notify all consumers that their @ti pointer has changed */
|
||||||
|
ti = (struct table_info *)ch->tablestate;
|
||||||
|
for (i = 0; i < tbl; i++, ti++) {
|
||||||
|
if (ti->lookup == NULL)
|
||||||
|
continue;
|
||||||
|
tc = (struct table_config *)ipfw_objhash_lookup_kidx(ni, i);
|
||||||
|
if (tc == NULL || tc->ta->change_ti == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tc->ta->change_ti(tc->astate, ti);
|
||||||
|
}
|
||||||
|
|
||||||
IPFW_UH_WUNLOCK(ch);
|
IPFW_UH_WUNLOCK(ch);
|
||||||
|
|
||||||
/* Free old pointers */
|
/* Free old pointers */
|
||||||
@ -923,21 +938,15 @@ int
|
|||||||
ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
ipfw_list_tables(struct ip_fw_chain *ch, struct sockopt_data *sd)
|
||||||
{
|
{
|
||||||
struct _ipfw_obj_lheader *olh;
|
struct _ipfw_obj_lheader *olh;
|
||||||
uint32_t sz;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
|
olh = (struct _ipfw_obj_lheader *)ipfw_get_sopt_header(sd,sizeof(*olh));
|
||||||
if (olh == NULL)
|
if (olh == NULL)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
if (sd->valsize < olh->size)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
IPFW_UH_RLOCK(ch);
|
IPFW_UH_RLOCK(ch);
|
||||||
sz = ipfw_objhash_count(CHAIN_TO_NI(ch));
|
|
||||||
|
|
||||||
if (sd->valsize < sz) {
|
|
||||||
IPFW_UH_RUNLOCK(ch);
|
|
||||||
return (ENOMEM);
|
|
||||||
}
|
|
||||||
|
|
||||||
error = export_tables(ch, olh, sd);
|
error = export_tables(ch, olh, sd);
|
||||||
IPFW_UH_RUNLOCK(ch);
|
IPFW_UH_RUNLOCK(ch);
|
||||||
|
|
||||||
@ -1214,7 +1223,7 @@ create_table_internal(struct ip_fw_chain *ch, struct tid_info *ti,
|
|||||||
if (ta == NULL)
|
if (ta == NULL)
|
||||||
return (ENOTSUP);
|
return (ENOTSUP);
|
||||||
|
|
||||||
if ((tc = alloc_table_config(ni, ti, ta, aname, vtype)) == NULL)
|
if ((tc = alloc_table_config(ch, ti, ta, aname, vtype)) == NULL)
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
|
|
||||||
IPFW_UH_WLOCK(ch);
|
IPFW_UH_WLOCK(ch);
|
||||||
@ -1320,7 +1329,7 @@ export_table_internal(struct namedobj_instance *ni, struct named_object *no,
|
|||||||
dta = (struct dump_table_args *)arg;
|
dta = (struct dump_table_args *)arg;
|
||||||
|
|
||||||
i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
|
i = (ipfw_xtable_info *)ipfw_get_sopt_space(dta->sd, sizeof(*i));
|
||||||
KASSERT(i == 0, ("previously checked buffer is not enough"));
|
KASSERT(i != 0, ("previously checked buffer is not enough"));
|
||||||
|
|
||||||
export_table_info(dta->ch, (struct table_config *)no, i);
|
export_table_info(dta->ch, (struct table_config *)no, i);
|
||||||
}
|
}
|
||||||
@ -1346,10 +1355,10 @@ export_tables(struct ip_fw_chain *ch, ipfw_obj_lheader *olh,
|
|||||||
olh->objsize = sizeof(ipfw_xtable_info);
|
olh->objsize = sizeof(ipfw_xtable_info);
|
||||||
|
|
||||||
if (size > olh->size) {
|
if (size > olh->size) {
|
||||||
/* Store necessary size */
|
|
||||||
olh->size = size;
|
olh->size = size;
|
||||||
return (ENOMEM);
|
return (ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
olh->size = size;
|
olh->size = size;
|
||||||
|
|
||||||
dta.ch = ch;
|
dta.ch = ch;
|
||||||
@ -1581,7 +1590,8 @@ find_table_algo(struct tables_config *tcfg, struct tid_info *ti, char *name)
|
|||||||
case IPFW_TABLE_CIDR:
|
case IPFW_TABLE_CIDR:
|
||||||
return (&radix_cidr);
|
return (&radix_cidr);
|
||||||
case IPFW_TABLE_INTERFACE:
|
case IPFW_TABLE_INTERFACE:
|
||||||
return (&radix_iface);
|
return (&idx_iface);
|
||||||
|
//return (&radix_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -1799,7 +1809,7 @@ find_table(struct namedobj_instance *ni, struct tid_info *ti)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct table_config *
|
static struct table_config *
|
||||||
alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
|
alloc_table_config(struct ip_fw_chain *ch, struct tid_info *ti,
|
||||||
struct table_algo *ta, char *aname, uint8_t vtype)
|
struct table_algo *ta, char *aname, uint8_t vtype)
|
||||||
{
|
{
|
||||||
char *name, bname[16];
|
char *name, bname[16];
|
||||||
@ -1838,7 +1848,7 @@ alloc_table_config(struct namedobj_instance *ni, struct tid_info *ti,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Preallocate data structures for new tables */
|
/* Preallocate data structures for new tables */
|
||||||
error = ta->init(&tc->astate, &tc->ti, aname);
|
error = ta->init(ch, &tc->astate, &tc->ti, aname);
|
||||||
if (error != 0) {
|
if (error != 0) {
|
||||||
free(tc, M_IPFW);
|
free(tc, M_IPFW);
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -1852,7 +1862,7 @@ free_table_config(struct namedobj_instance *ni, struct table_config *tc)
|
|||||||
{
|
{
|
||||||
|
|
||||||
if (tc->linked == 0)
|
if (tc->linked == 0)
|
||||||
tc->ta->destroy(&tc->astate, &tc->ti);
|
tc->ta->destroy(tc->astate, &tc->ti);
|
||||||
|
|
||||||
free(tc, M_IPFW);
|
free(tc, M_IPFW);
|
||||||
}
|
}
|
||||||
@ -1879,6 +1889,10 @@ link_table(struct ip_fw_chain *ch, struct table_config *tc)
|
|||||||
ti = KIDX_TO_TI(ch, kidx);
|
ti = KIDX_TO_TI(ch, kidx);
|
||||||
*ti = tc->ti;
|
*ti = tc->ti;
|
||||||
|
|
||||||
|
/* Notify algo on real @ti address */
|
||||||
|
if (tc->ta->change_ti != NULL)
|
||||||
|
tc->ta->change_ti(tc->astate, ti);
|
||||||
|
|
||||||
tc->linked = 1;
|
tc->linked = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1904,6 +1918,10 @@ unlink_table(struct ip_fw_chain *ch, struct table_config *tc)
|
|||||||
ti = KIDX_TO_TI(ch, kidx);
|
ti = KIDX_TO_TI(ch, kidx);
|
||||||
memset(ti, 0, sizeof(struct table_info));
|
memset(ti, 0, sizeof(struct table_info));
|
||||||
tc->linked = 0;
|
tc->linked = 0;
|
||||||
|
|
||||||
|
/* Notify algo on real @ti address */
|
||||||
|
if (tc->ta->change_ti != NULL)
|
||||||
|
tc->ta->change_ti(tc->astate, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2169,7 +2187,8 @@ ipfw_rewrite_table_uidx(struct ip_fw_chain *chain,
|
|||||||
error = ENOTSUP;
|
error = ENOTSUP;
|
||||||
goto free;
|
goto free;
|
||||||
}
|
}
|
||||||
tc = alloc_table_config(ni, &ti, ta, NULL, IPFW_VTYPE_U32);
|
tc = alloc_table_config(chain, &ti, ta, NULL,
|
||||||
|
IPFW_VTYPE_U32);
|
||||||
|
|
||||||
if (tc == NULL) {
|
if (tc == NULL) {
|
||||||
error = ENOMEM;
|
error = ENOMEM;
|
||||||
|
@ -62,15 +62,19 @@ struct tentry_info {
|
|||||||
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
|
#define TEI_FLAGS_UPDATED 0x02 /* Entry has been updated */
|
||||||
#define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */
|
#define TEI_FLAGS_COMPAT 0x04 /* Called from old ABI */
|
||||||
|
|
||||||
typedef int (ta_init)(void **ta_state, struct table_info *ti, char *data);
|
typedef int (ta_init)(struct ip_fw_chain *ch, void **ta_state,
|
||||||
|
struct table_info *ti, char *data);
|
||||||
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
|
typedef void (ta_destroy)(void *ta_state, struct table_info *ti);
|
||||||
typedef int (ta_prepare_add)(struct tentry_info *tei, void *ta_buf);
|
typedef int (ta_prepare_add)(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
typedef int (ta_prepare_del)(struct tentry_info *tei, void *ta_buf);
|
void *ta_buf);
|
||||||
|
typedef int (ta_prepare_del)(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf);
|
||||||
typedef int (ta_add)(void *ta_state, struct table_info *ti,
|
typedef int (ta_add)(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
||||||
typedef int (ta_del)(void *ta_state, struct table_info *ti,
|
typedef int (ta_del)(void *ta_state, struct table_info *ti,
|
||||||
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags);
|
||||||
typedef void (ta_flush_entry)(struct tentry_info *tei, void *ta_buf);
|
typedef void (ta_flush_entry)(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf);
|
||||||
|
|
||||||
typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
|
typedef int (ta_prepare_mod)(void *ta_buf, uint64_t *pflags);
|
||||||
typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
|
typedef int (ta_fill_mod)(void *ta_state, struct table_info *ti,
|
||||||
@ -79,6 +83,7 @@ typedef int (ta_modify)(void *ta_state, struct table_info *ti,
|
|||||||
void *ta_buf, uint64_t pflags);
|
void *ta_buf, uint64_t pflags);
|
||||||
typedef void (ta_flush_mod)(void *ta_buf);
|
typedef void (ta_flush_mod)(void *ta_buf);
|
||||||
|
|
||||||
|
typedef void (ta_change_ti)(void *ta_state, struct table_info *ti);
|
||||||
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
|
typedef void (ta_print_config)(void *ta_state, struct table_info *ti, char *buf,
|
||||||
size_t bufsize);
|
size_t bufsize);
|
||||||
|
|
||||||
@ -109,9 +114,11 @@ struct table_algo {
|
|||||||
ta_dump_tentry *dump_tentry;
|
ta_dump_tentry *dump_tentry;
|
||||||
ta_print_config *print_config;
|
ta_print_config *print_config;
|
||||||
ta_find_tentry *find_tentry;
|
ta_find_tentry *find_tentry;
|
||||||
|
ta_change_ti *change_ti;
|
||||||
};
|
};
|
||||||
|
|
||||||
void ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta);
|
void ipfw_add_table_algo(struct ip_fw_chain *ch, struct table_algo *ta);
|
||||||
extern struct table_algo radix_cidr, radix_iface;
|
extern struct table_algo radix_cidr, idx_iface;
|
||||||
|
|
||||||
void ipfw_table_algo_init(struct ip_fw_chain *chain);
|
void ipfw_table_algo_init(struct ip_fw_chain *chain);
|
||||||
void ipfw_table_algo_destroy(struct ip_fw_chain *chain);
|
void ipfw_table_algo_destroy(struct ip_fw_chain *chain);
|
||||||
|
@ -29,14 +29,6 @@ __FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 267384 2014-06-
|
|||||||
/*
|
/*
|
||||||
* Lookup table algorithms.
|
* Lookup table algorithms.
|
||||||
*
|
*
|
||||||
* Lookup tables are implemented (at the moment) using the radix
|
|
||||||
* tree used for routing tables. Tables store key-value entries, where
|
|
||||||
* keys are network prefixes (addr/masklen), and values are integers.
|
|
||||||
* As a degenerate case we can interpret keys as 32-bit integers
|
|
||||||
* (with a /32 mask).
|
|
||||||
*
|
|
||||||
* The table is protected by the IPFW lock even for manipulation coming
|
|
||||||
* from userland, because operations are typically fast.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "opt_ipfw.h"
|
#include "opt_ipfw.h"
|
||||||
@ -68,6 +60,17 @@ __FBSDID("$FreeBSD: projects/ipfw/sys/netpfil/ipfw/ip_fw_table.c 267384 2014-06-
|
|||||||
|
|
||||||
static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
|
static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
|
||||||
|
|
||||||
|
static int badd(const void *key, void *item, void *base, size_t nmemb,
|
||||||
|
size_t size, int (*compar) (const void *, const void *));
|
||||||
|
static int bdel(const void *key, void *base, size_t nmemb, size_t size,
|
||||||
|
int (*compar) (const void *, const void *));
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CIDR implementation using radix
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The radix code expects addr and mask to be array of bytes,
|
* The radix code expects addr and mask to be array of bytes,
|
||||||
* with the first byte being the length of the array. rn_inithead
|
* with the first byte being the length of the array. rn_inithead
|
||||||
@ -83,11 +86,9 @@ static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
|
|||||||
*/
|
*/
|
||||||
#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
|
#define KEY_LEN_INET (offsetof(struct sockaddr_in, sin_addr) + sizeof(in_addr_t))
|
||||||
#define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
|
#define KEY_LEN_INET6 (offsetof(struct sa_in6, sin6_addr) + sizeof(struct in6_addr))
|
||||||
#define KEY_LEN_IFACE (offsetof(struct xaddr_iface, ifname))
|
|
||||||
|
|
||||||
#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr))
|
#define OFF_LEN_INET (8 * offsetof(struct sockaddr_in, sin_addr))
|
||||||
#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr))
|
#define OFF_LEN_INET6 (8 * offsetof(struct sa_in6, sin6_addr))
|
||||||
#define OFF_LEN_IFACE (8 * offsetof(struct xaddr_iface, ifname))
|
|
||||||
|
|
||||||
struct radix_cidr_entry {
|
struct radix_cidr_entry {
|
||||||
struct radix_node rn[2];
|
struct radix_node rn[2];
|
||||||
@ -110,23 +111,6 @@ struct radix_cidr_xentry {
|
|||||||
uint8_t masklen;
|
uint8_t masklen;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct xaddr_iface {
|
|
||||||
uint8_t if_len; /* length of this struct */
|
|
||||||
uint8_t pad[7]; /* Align name */
|
|
||||||
char ifname[IF_NAMESIZE]; /* Interface name */
|
|
||||||
};
|
|
||||||
|
|
||||||
struct radix_iface {
|
|
||||||
struct radix_node rn[2];
|
|
||||||
struct xaddr_iface iface;
|
|
||||||
uint32_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* CIDR implementation using radix
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
|
ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
|
||||||
uint32_t *val)
|
uint32_t *val)
|
||||||
@ -164,7 +148,8 @@ ta_lookup_radix(struct table_info *ti, void *key, uint32_t keylen,
|
|||||||
* New table
|
* New table
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
ta_init_radix(void **ta_state, struct table_info *ti, char *data)
|
ta_init_radix(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
|
||||||
|
char *data)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!rn_inithead(&ti->state, OFF_LEN_INET))
|
if (!rn_inithead(&ti->state, OFF_LEN_INET))
|
||||||
@ -310,7 +295,8 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
|
|||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ta_prepare_add_cidr(struct tentry_info *tei, void *ta_buf)
|
ta_prepare_add_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_cidr *tb;
|
struct ta_buf_cidr *tb;
|
||||||
struct radix_cidr_entry *ent;
|
struct radix_cidr_entry *ent;
|
||||||
@ -432,7 +418,8 @@ ta_add_cidr(void *ta_state, struct table_info *ti,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ta_prepare_del_cidr(struct tentry_info *tei, void *ta_buf)
|
ta_prepare_del_cidr(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_cidr *tb;
|
struct ta_buf_cidr *tb;
|
||||||
struct sockaddr_in sa, mask;
|
struct sockaddr_in sa, mask;
|
||||||
@ -512,7 +499,8 @@ ta_del_cidr(void *ta_state, struct table_info *ti,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ta_flush_cidr_entry(struct tentry_info *tei, void *ta_buf)
|
ta_flush_cidr_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_cidr *tb;
|
struct ta_buf_cidr *tb;
|
||||||
|
|
||||||
@ -539,25 +527,168 @@ struct table_algo radix_cidr = {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iface table cmds
|
* Iface table cmds.
|
||||||
|
*
|
||||||
|
* Implementation:
|
||||||
|
*
|
||||||
|
* Runtime part:
|
||||||
|
* - sorted array of "struct ifidx" pointed by ti->state.
|
||||||
|
* Array is allocated with routing up to IFIDX_CHUNK. Only existing
|
||||||
|
* interfaces are stored in array, however its allocated size is
|
||||||
|
* sufficient to hold all table records if needed.
|
||||||
|
* - current array size is stored in ti->data
|
||||||
|
*
|
||||||
|
* Table data:
|
||||||
|
* - "struct iftable_cfg" is allocated to store table state (ta_state).
|
||||||
|
* - All table records are stored inside namedobj instance.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
struct ifidx {
|
||||||
|
uint16_t kidx;
|
||||||
|
uint16_t spare;
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iftable_cfg;
|
||||||
|
|
||||||
|
struct ifentry {
|
||||||
|
struct named_object no;
|
||||||
|
struct ipfw_ifc ic;
|
||||||
|
struct iftable_cfg *icfg;
|
||||||
|
TAILQ_ENTRY(ifentry) next;
|
||||||
|
uint32_t value;
|
||||||
|
int linked;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iftable_cfg {
|
||||||
|
struct namedobj_instance *ii;
|
||||||
|
struct ip_fw_chain *ch;
|
||||||
|
struct table_info *ti;
|
||||||
|
void *main_ptr;
|
||||||
|
size_t size; /* Number of items allocated in array */
|
||||||
|
size_t count; /* Number of all items */
|
||||||
|
size_t used; /* Number of items _active_ now */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFIDX_CHUNK 16
|
||||||
|
|
||||||
|
int compare_ifidx(const void *k, const void *v);
|
||||||
|
static void if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex);
|
||||||
|
|
||||||
|
int
|
||||||
|
compare_ifidx(const void *k, const void *v)
|
||||||
|
{
|
||||||
|
struct ifidx *ifidx;
|
||||||
|
uint16_t key;
|
||||||
|
|
||||||
|
key = *((uint16_t *)k);
|
||||||
|
ifidx = (struct ifidx *)v;
|
||||||
|
|
||||||
|
if (key < ifidx->kidx)
|
||||||
|
return (-1);
|
||||||
|
else if (key > ifidx->kidx)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds item @item with key @key into ascending-sorted array @base.
|
||||||
|
* Assumes @base has enough additional storage.
|
||||||
|
*
|
||||||
|
* Returns 1 on success, 0 on duplicate key.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
ta_lookup_iface(struct table_info *ti, void *key, uint32_t keylen,
|
badd(const void *key, void *item, void *base, size_t nmemb,
|
||||||
|
size_t size, int (*compar) (const void *, const void *))
|
||||||
|
{
|
||||||
|
int min, max, mid, shift, res;
|
||||||
|
caddr_t paddr;
|
||||||
|
|
||||||
|
if (nmemb == 0) {
|
||||||
|
memcpy(base, item, size);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Binary search */
|
||||||
|
min = 0;
|
||||||
|
max = nmemb - 1;
|
||||||
|
mid = 0;
|
||||||
|
while (min <= max) {
|
||||||
|
mid = (min + max) / 2;
|
||||||
|
res = compar(key, (const void *)((caddr_t)base + mid * size));
|
||||||
|
if (res == 0)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
if (res > 0)
|
||||||
|
min = mid + 1;
|
||||||
|
else
|
||||||
|
max = mid - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Item not found. */
|
||||||
|
res = compar(key, (const void *)((caddr_t)base + mid * size));
|
||||||
|
if (res > 0)
|
||||||
|
shift = mid + 1;
|
||||||
|
else
|
||||||
|
shift = mid;
|
||||||
|
|
||||||
|
paddr = (caddr_t)base + shift * size;
|
||||||
|
if (nmemb > shift)
|
||||||
|
memmove(paddr + size, paddr, (nmemb - shift) * size);
|
||||||
|
|
||||||
|
memcpy(paddr, item, size);
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Deletes item with key @key from ascending-sorted array @base.
|
||||||
|
*
|
||||||
|
* Returns 1 on success, 0 for non-existent key.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
bdel(const void *key, void *base, size_t nmemb, size_t size,
|
||||||
|
int (*compar) (const void *, const void *))
|
||||||
|
{
|
||||||
|
caddr_t item;
|
||||||
|
size_t sz;
|
||||||
|
|
||||||
|
item = (caddr_t)bsearch(key, base, nmemb, size, compar);
|
||||||
|
|
||||||
|
if (item == NULL)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
sz = (caddr_t)base + nmemb * size - item;
|
||||||
|
|
||||||
|
if (sz > 0)
|
||||||
|
memmove(item, item + size, sz);
|
||||||
|
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ifidx *
|
||||||
|
ifidx_find(struct table_info *ti, void *key)
|
||||||
|
{
|
||||||
|
struct ifidx *ifi;
|
||||||
|
|
||||||
|
ifi = bsearch(key, ti->state, ti->data, sizeof(struct ifidx),
|
||||||
|
compare_ifidx);
|
||||||
|
|
||||||
|
return (ifi);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ta_lookup_ifidx(struct table_info *ti, void *key, uint32_t keylen,
|
||||||
uint32_t *val)
|
uint32_t *val)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct ifidx *ifi;
|
||||||
struct xaddr_iface iface;
|
|
||||||
struct radix_iface *xent;
|
|
||||||
|
|
||||||
KEY_LEN(iface) = KEY_LEN_IFACE +
|
ifi = ifidx_find(ti, key);
|
||||||
strlcpy(iface.ifname, (char *)key, IF_NAMESIZE) + 1;
|
|
||||||
|
|
||||||
rnh = (struct radix_node_head *)ti->xstate;
|
if (ifi != NULL) {
|
||||||
xent = (struct radix_iface *)(rnh->rnh_matchaddr(&iface, rnh));
|
*val = ifi->value;
|
||||||
if (xent != NULL) {
|
|
||||||
*val = xent->value;
|
|
||||||
return (1);
|
return (1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,56 +696,182 @@ ta_lookup_iface(struct table_info *ti, void *key, uint32_t keylen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
flush_iface_entry(struct radix_node *rn, void *arg)
|
ta_init_ifidx(struct ip_fw_chain *ch, void **ta_state, struct table_info *ti,
|
||||||
|
char *data)
|
||||||
{
|
{
|
||||||
struct radix_node_head * const rnh = arg;
|
struct iftable_cfg *icfg;
|
||||||
struct radix_iface *xent;
|
|
||||||
|
|
||||||
xent = (struct radix_iface *)
|
icfg = malloc(sizeof(struct iftable_cfg), M_IPFW, M_WAITOK | M_ZERO);
|
||||||
rnh->rnh_deladdr(rn->rn_key, rn->rn_mask, rnh);
|
|
||||||
if (xent != NULL)
|
|
||||||
free(xent, M_IPFW_TBL);
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
icfg->ii = ipfw_objhash_create(16);
|
||||||
ta_init_iface(void **ta_state, struct table_info *ti, char *data)
|
icfg->main_ptr = malloc(sizeof(struct ifidx) * IFIDX_CHUNK, M_IPFW,
|
||||||
{
|
M_WAITOK | M_ZERO);
|
||||||
|
icfg->size = IFIDX_CHUNK;
|
||||||
|
icfg->ch = ch;
|
||||||
|
|
||||||
if (!rn_inithead(&ti->xstate, OFF_LEN_IFACE))
|
*ta_state = icfg;
|
||||||
return (ENOMEM);
|
ti->state = icfg->main_ptr;
|
||||||
|
ti->lookup = ta_lookup_ifidx;
|
||||||
*ta_state = NULL;
|
|
||||||
ti->lookup = ta_lookup_iface;
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle tableinfo @ti pointer change (on table array resize).
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ta_change_ti_ifidx(void *ta_state, struct table_info *ti)
|
||||||
|
{
|
||||||
|
struct iftable_cfg *icfg;
|
||||||
|
|
||||||
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
icfg->ti = ti;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ta_destroy_iface(void *ta_state, struct table_info *ti)
|
destroy_ifidx_locked(struct namedobj_instance *ii, struct named_object *no,
|
||||||
|
void *arg)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct ifentry *ife;
|
||||||
|
struct ip_fw_chain *ch;
|
||||||
|
|
||||||
rnh = (struct radix_node_head *)(ti->xstate);
|
ch = (struct ip_fw_chain *)arg;
|
||||||
rnh->rnh_walktree(rnh, flush_iface_entry, rnh);
|
ife = (struct ifentry *)no;
|
||||||
rn_detachhead(&ti->xstate);
|
|
||||||
|
ipfw_iface_del_notify(ch, &ife->ic);
|
||||||
|
free(ife, M_IPFW_TBL);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ta_buf_iface
|
|
||||||
|
/*
|
||||||
|
* Destroys table @ti
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ta_destroy_ifidx(void *ta_state, struct table_info *ti)
|
||||||
{
|
{
|
||||||
void *addr_ptr;
|
struct iftable_cfg *icfg;
|
||||||
void *mask_ptr;
|
struct ip_fw_chain *ch;
|
||||||
void *ent_ptr;
|
|
||||||
struct xaddr_iface iface;
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
ch = icfg->ch;
|
||||||
|
|
||||||
|
if (icfg->main_ptr != NULL)
|
||||||
|
free(icfg->main_ptr, M_IPFW);
|
||||||
|
|
||||||
|
ipfw_objhash_foreach(icfg->ii, destroy_ifidx_locked, ch);
|
||||||
|
|
||||||
|
ipfw_objhash_destroy(icfg->ii);
|
||||||
|
|
||||||
|
free(icfg, M_IPFW);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ta_buf_ifidx
|
||||||
|
{
|
||||||
|
struct ifentry *ife;
|
||||||
|
uint32_t value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare state to add to the table:
|
||||||
|
* allocate ifentry and reference needed interface.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
ta_prepare_add_iface(struct tentry_info *tei, void *ta_buf)
|
ta_prepare_add_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
|
{
|
||||||
|
struct ta_buf_ifidx *tb;
|
||||||
|
char *ifname;
|
||||||
|
struct ifentry *ife;
|
||||||
|
|
||||||
|
tb = (struct ta_buf_ifidx *)ta_buf;
|
||||||
|
memset(tb, 0, sizeof(struct ta_buf_cidr));
|
||||||
|
|
||||||
|
/* Check if string is terminated */
|
||||||
|
ifname = (char *)tei->paddr;
|
||||||
|
if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
ife = malloc(sizeof(struct ifentry), M_IPFW_TBL, M_WAITOK | M_ZERO);
|
||||||
|
ife->value = tei->value;
|
||||||
|
ife->ic.cb = if_notifier;
|
||||||
|
ife->ic.cbdata = ife;
|
||||||
|
|
||||||
|
if (ipfw_iface_ref(ch, ifname, &ife->ic) != 0)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
/* Use ipfw_iface 'ifname' field as stable storage */
|
||||||
|
ife->no.name = ife->ic.iface->ifname;
|
||||||
|
|
||||||
|
tb->ife = ife;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
ta_add_ifidx(void *ta_state, struct table_info *ti,
|
||||||
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
|
{
|
||||||
|
struct iftable_cfg *icfg;
|
||||||
|
struct ifentry *ife, *tmp;
|
||||||
|
struct ta_buf_ifidx *tb;
|
||||||
|
struct ipfw_iface *iif;
|
||||||
|
struct ifidx *ifi;
|
||||||
|
char *ifname;
|
||||||
|
|
||||||
|
tb = (struct ta_buf_ifidx *)ta_buf;
|
||||||
|
ifname = (char *)tei->paddr;
|
||||||
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
ife = tb->ife;
|
||||||
|
|
||||||
|
ife->icfg = icfg;
|
||||||
|
|
||||||
|
tmp = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
|
||||||
|
|
||||||
|
if (tmp != NULL) {
|
||||||
|
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
|
||||||
|
return (EEXIST);
|
||||||
|
|
||||||
|
/* We need to update value */
|
||||||
|
iif = tmp->ic.iface;
|
||||||
|
tmp->value = ife->value;
|
||||||
|
|
||||||
|
if (iif->resolved != 0) {
|
||||||
|
/* We need to update runtime value, too */
|
||||||
|
ifi = ifidx_find(ti, &iif->ifindex);
|
||||||
|
ifi->value = ife->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indicate that update has happened instead of addition */
|
||||||
|
tei->flags |= TEI_FLAGS_UPDATED;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Link to internal list */
|
||||||
|
ipfw_objhash_add(icfg->ii, &ife->no);
|
||||||
|
|
||||||
|
/* Link notifier (possible running its callback) */
|
||||||
|
ipfw_iface_add_notify(icfg->ch, &ife->ic);
|
||||||
|
icfg->count++;
|
||||||
|
|
||||||
|
if (icfg->count + 1 == icfg->size) {
|
||||||
|
/* Notify core we need to grow */
|
||||||
|
*pflags = icfg->size + IFIDX_CHUNK;
|
||||||
|
}
|
||||||
|
|
||||||
|
tb->ife = NULL;
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare to delete key from table.
|
||||||
|
* Do basic interface name checks.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ta_prepare_del_ifidx(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_iface *tb;
|
struct ta_buf_iface *tb;
|
||||||
struct radix_iface *xent;
|
|
||||||
int mlen;
|
|
||||||
char *ifname;
|
char *ifname;
|
||||||
|
|
||||||
tb = (struct ta_buf_iface *)ta_buf;
|
tb = (struct ta_buf_iface *)ta_buf;
|
||||||
@ -625,189 +882,302 @@ ta_prepare_add_iface(struct tentry_info *tei, void *ta_buf)
|
|||||||
if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
|
if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
|
||||||
return (EINVAL);
|
return (EINVAL);
|
||||||
|
|
||||||
/* Include last \0 into comparison */
|
return (0);
|
||||||
mlen = strlen(ifname) + 1;
|
}
|
||||||
|
|
||||||
xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
|
/*
|
||||||
xent->value = tei->value;
|
* Remove key from both configuration list and
|
||||||
/* Set 'total' structure length */
|
* runtime array. Removed interface notification.
|
||||||
KEY_LEN(xent->iface) = KEY_LEN_IFACE + mlen;
|
*/
|
||||||
memcpy(xent->iface.ifname, tei->paddr, mlen);
|
static int
|
||||||
/* Set pointers */
|
ta_del_ifidx(void *ta_state, struct table_info *ti,
|
||||||
tb->ent_ptr = xent;
|
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
||||||
tb->addr_ptr = (struct sockaddr *)&xent->iface;
|
{
|
||||||
/* Assume direct match */
|
struct iftable_cfg *icfg;
|
||||||
tb->mask_ptr = NULL;
|
struct ifentry *ife;
|
||||||
|
struct ta_buf_ifidx *tb;
|
||||||
|
char *ifname;
|
||||||
|
uint16_t ifindex;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
tb = (struct ta_buf_ifidx *)ta_buf;
|
||||||
|
ifname = (char *)tei->paddr;
|
||||||
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
ife = tb->ife;
|
||||||
|
|
||||||
|
ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
|
||||||
|
|
||||||
|
if (ife == NULL)
|
||||||
|
return (ENOENT);
|
||||||
|
|
||||||
|
if (ife->linked != 0) {
|
||||||
|
/* We have to remove item from runtime */
|
||||||
|
ifindex = ife->ic.iface->ifindex;
|
||||||
|
|
||||||
|
res = bdel(&ifindex, icfg->main_ptr, icfg->used,
|
||||||
|
sizeof(struct ifidx), compare_ifidx);
|
||||||
|
|
||||||
|
KASSERT(res == 1, ("index %d does not exist", ifindex));
|
||||||
|
icfg->used--;
|
||||||
|
ti->data = icfg->used;
|
||||||
|
ife->linked = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unlink from local list */
|
||||||
|
ipfw_objhash_del(icfg->ii, &ife->no);
|
||||||
|
/* Unlink notifier */
|
||||||
|
ipfw_iface_del_notify(icfg->ch, &ife->ic);
|
||||||
|
|
||||||
|
icfg->count--;
|
||||||
|
|
||||||
|
tb->ife = ife;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
/*
|
||||||
ta_add_iface(void *ta_state, struct table_info *ti,
|
* Flush deleted entry.
|
||||||
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
* Drops interface reference and frees entry.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ta_flush_ifidx_entry(struct ip_fw_chain *ch, struct tentry_info *tei,
|
||||||
|
void *ta_buf)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct ta_buf_ifidx *tb;
|
||||||
struct radix_node *rn;
|
|
||||||
struct ta_buf_iface *tb;
|
|
||||||
uint32_t value;
|
|
||||||
|
|
||||||
tb = (struct ta_buf_iface *)ta_buf;
|
tb = (struct ta_buf_ifidx *)ta_buf;
|
||||||
|
|
||||||
rnh = ti->xstate;
|
if (tb->ife != NULL) {
|
||||||
rn = rnh->rnh_addaddr(tb->addr_ptr, tb->mask_ptr, rnh, tb->ent_ptr);
|
/* Unlink first */
|
||||||
|
ipfw_iface_unref(ch, &tb->ife->ic);
|
||||||
if (rn == NULL) {
|
free(tb->ife, M_IPFW_TBL);
|
||||||
if ((tei->flags & TEI_FLAGS_UPDATE) == 0)
|
}
|
||||||
return (EEXIST);
|
}
|
||||||
/* Record already exists. Update value if we're asked to */
|
|
||||||
rn = rnh->rnh_lookup(tb->addr_ptr, tb->mask_ptr, rnh);
|
|
||||||
if (rn == NULL) {
|
|
||||||
/* Radix may have failed addition for other reasons */
|
|
||||||
return (EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
value = ((struct radix_iface *)tb->ent_ptr)->value;
|
|
||||||
((struct radix_iface *)rn)->value = value;
|
|
||||||
|
|
||||||
/* Indicate that update has happened instead of addition */
|
|
||||||
tei->flags |= TEI_FLAGS_UPDATED;
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Handle interface announce/withdrawal for particular table.
|
||||||
|
* Every real runtime array modification happens here.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
if_notifier(struct ip_fw_chain *ch, void *cbdata, uint16_t ifindex)
|
||||||
|
{
|
||||||
|
struct ifentry *ife;
|
||||||
|
struct ifidx ifi;
|
||||||
|
struct iftable_cfg *icfg;
|
||||||
|
struct table_info *ti;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
ife = (struct ifentry *)cbdata;
|
||||||
|
icfg = ife->icfg;
|
||||||
|
ti = icfg->ti;
|
||||||
|
|
||||||
|
KASSERT(ti != NULL, ("ti=NULL, check change_ti handler"));
|
||||||
|
|
||||||
|
if (ife->linked == 0 && ifindex != 0) {
|
||||||
|
/* Interface announce */
|
||||||
|
ifi.kidx = ifindex;
|
||||||
|
ifi.spare = 0;
|
||||||
|
ifi.value = ife->value;
|
||||||
|
res = badd(&ifindex, &ifi, icfg->main_ptr, icfg->used,
|
||||||
|
sizeof(struct ifidx), compare_ifidx);
|
||||||
|
KASSERT(res == 1, ("index %d already exists", ifindex));
|
||||||
|
icfg->used++;
|
||||||
|
ti->data = icfg->used;
|
||||||
|
ife->linked = 1;
|
||||||
|
} else if (ife->linked != 0 && ifindex == 0) {
|
||||||
|
/* Interface withdrawal */
|
||||||
|
ifindex = ife->ic.iface->ifindex;
|
||||||
|
|
||||||
|
res = bdel(&ifindex, icfg->main_ptr, icfg->used,
|
||||||
|
sizeof(struct ifidx), compare_ifidx);
|
||||||
|
|
||||||
|
KASSERT(res == 1, ("index %d does not exist", ifindex));
|
||||||
|
icfg->used--;
|
||||||
|
ti->data = icfg->used;
|
||||||
|
ife->linked = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Table growing callbacks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct mod_ifidx {
|
||||||
|
void *main_ptr;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate ned, larger runtime ifidx array.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ta_prepare_mod_ifidx(void *ta_buf, uint64_t *pflags)
|
||||||
|
{
|
||||||
|
struct mod_ifidx *mi;
|
||||||
|
|
||||||
|
mi = (struct mod_ifidx *)ta_buf;
|
||||||
|
|
||||||
|
memset(mi, 0, sizeof(struct mod_ifidx));
|
||||||
|
mi->size = *pflags;
|
||||||
|
mi->main_ptr = malloc(sizeof(struct ifidx) * mi->size, M_IPFW,
|
||||||
|
M_WAITOK | M_ZERO);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy data from old runtime array to new one.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
ta_fill_mod_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
|
||||||
|
uint64_t *pflags)
|
||||||
|
{
|
||||||
|
struct mod_ifidx *mi;
|
||||||
|
struct iftable_cfg *icfg;
|
||||||
|
|
||||||
|
mi = (struct mod_ifidx *)ta_buf;
|
||||||
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
|
||||||
|
/* Check if we still need to grow array */
|
||||||
|
if (icfg->size >= mi->size) {
|
||||||
|
*pflags = 0;
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
tb->ent_ptr = NULL;
|
memcpy(mi->main_ptr, icfg->main_ptr, icfg->used * sizeof(struct ifidx));
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Switch old & new arrays.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
ta_prepare_del_iface(struct tentry_info *tei, void *ta_buf)
|
ta_modify_ifidx(void *ta_state, struct table_info *ti, void *ta_buf,
|
||||||
|
uint64_t pflags)
|
||||||
{
|
{
|
||||||
struct ta_buf_iface *tb;
|
struct mod_ifidx *mi;
|
||||||
int mlen;
|
struct iftable_cfg *icfg;
|
||||||
char c;
|
void *old_ptr;
|
||||||
|
|
||||||
tb = (struct ta_buf_iface *)ta_buf;
|
mi = (struct mod_ifidx *)ta_buf;
|
||||||
memset(tb, 0, sizeof(struct ta_buf_cidr));
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
|
|
||||||
/* Check if string is terminated */
|
old_ptr = icfg->main_ptr;
|
||||||
c = ((char *)tei->paddr)[IF_NAMESIZE - 1];
|
icfg->main_ptr = mi->main_ptr;
|
||||||
((char *)tei->paddr)[IF_NAMESIZE - 1] = '\0';
|
icfg->size = mi->size;
|
||||||
mlen = strlen((char *)tei->paddr);
|
ti->state = icfg->main_ptr;
|
||||||
if ((mlen == IF_NAMESIZE - 1) && (c != '\0'))
|
|
||||||
return (EINVAL);
|
|
||||||
|
|
||||||
struct xaddr_iface ifname, ifmask;
|
mi->main_ptr = old_ptr;
|
||||||
memset(&ifname, 0, sizeof(ifname));
|
|
||||||
|
|
||||||
/* Include last \0 into comparison */
|
|
||||||
mlen++;
|
|
||||||
|
|
||||||
/* Set 'total' structure length */
|
|
||||||
KEY_LEN(ifname) = KEY_LEN_IFACE + mlen;
|
|
||||||
KEY_LEN(ifmask) = KEY_LEN_IFACE + mlen;
|
|
||||||
/* Assume direct match */
|
|
||||||
memcpy(ifname.ifname, tei->paddr, mlen);
|
|
||||||
/* Set pointers */
|
|
||||||
tb->iface = ifname;
|
|
||||||
tb->addr_ptr = &tb->iface;
|
|
||||||
tb->mask_ptr = NULL;
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
ta_del_iface(void *ta_state, struct table_info *ti,
|
|
||||||
struct tentry_info *tei, void *ta_buf, uint64_t *pflags)
|
|
||||||
{
|
|
||||||
struct radix_node_head *rnh;
|
|
||||||
struct radix_node *rn;
|
|
||||||
struct ta_buf_iface *tb;
|
|
||||||
|
|
||||||
tb = (struct ta_buf_iface *)ta_buf;
|
|
||||||
|
|
||||||
rnh = ti->xstate;
|
|
||||||
rn = rnh->rnh_deladdr(tb->addr_ptr, tb->mask_ptr, rnh);
|
|
||||||
|
|
||||||
tb->ent_ptr = rn;
|
|
||||||
|
|
||||||
if (rn == NULL)
|
|
||||||
return (ENOENT);
|
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free unneded array.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
ta_flush_iface_entry(struct tentry_info *tei, void *ta_buf)
|
ta_flush_mod_ifidx(void *ta_buf)
|
||||||
{
|
{
|
||||||
struct ta_buf_iface *tb;
|
struct mod_ifidx *mi;
|
||||||
|
|
||||||
tb = (struct ta_buf_iface *)ta_buf;
|
mi = (struct mod_ifidx *)ta_buf;
|
||||||
|
if (mi->main_ptr != NULL)
|
||||||
if (tb->ent_ptr != NULL)
|
free(mi->main_ptr, M_IPFW);
|
||||||
free(tb->ent_ptr, M_IPFW_TBL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ta_dump_iface_tentry(void *ta_state, struct table_info *ti, void *e,
|
ta_dump_ifidx_tentry(void *ta_state, struct table_info *ti, void *e,
|
||||||
ipfw_obj_tentry *tent)
|
ipfw_obj_tentry *tent)
|
||||||
{
|
{
|
||||||
struct radix_iface *xn;
|
struct ifentry *ife;
|
||||||
|
|
||||||
|
ife = (struct ifentry *)e;
|
||||||
|
|
||||||
xn = (struct radix_iface *)e;
|
|
||||||
tent->masklen = 8 * IF_NAMESIZE;
|
tent->masklen = 8 * IF_NAMESIZE;
|
||||||
memcpy(&tent->k, &xn->iface.ifname, IF_NAMESIZE);
|
memcpy(&tent->k, ife->no.name, IF_NAMESIZE);
|
||||||
tent->value = xn->value;
|
tent->value = ife->value;
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ta_find_iface_tentry(void *ta_state, struct table_info *ti, void *key,
|
ta_find_ifidx_tentry(void *ta_state, struct table_info *ti, void *key,
|
||||||
uint32_t keylen, ipfw_obj_tentry *tent)
|
uint32_t keylen, ipfw_obj_tentry *tent)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct iftable_cfg *icfg;
|
||||||
struct xaddr_iface iface;
|
struct ifentry *ife;
|
||||||
void *e;
|
char *ifname;
|
||||||
e = NULL;
|
|
||||||
|
|
||||||
KEY_LEN(iface) = KEY_LEN_IFACE +
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
strlcpy(iface.ifname, (char *)key, IF_NAMESIZE) + 1;
|
ifname = (char *)key;
|
||||||
|
|
||||||
rnh = (struct radix_node_head *)ti->xstate;
|
if (strnlen(ifname, IF_NAMESIZE) == IF_NAMESIZE)
|
||||||
e = rnh->rnh_matchaddr(&iface, rnh);
|
return (EINVAL);
|
||||||
|
|
||||||
if (e != NULL) {
|
ife = (struct ifentry *)ipfw_objhash_lookup_name(icfg->ii, 0, ifname);
|
||||||
ta_dump_iface_tentry(ta_state, ti, e, tent);
|
|
||||||
|
if (ife != NULL) {
|
||||||
|
ta_dump_ifidx_tentry(ta_state, ti, ife, tent);
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ENOENT);
|
return (ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct wa_ifidx {
|
||||||
|
ta_foreach_f *f;
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ta_foreach_iface(void *ta_state, struct table_info *ti, ta_foreach_f *f,
|
foreach_ifidx(struct namedobj_instance *ii, struct named_object *no,
|
||||||
void *arg)
|
void *arg)
|
||||||
{
|
{
|
||||||
struct radix_node_head *rnh;
|
struct ifentry *ife;
|
||||||
|
struct wa_ifidx *wa;
|
||||||
|
|
||||||
rnh = (struct radix_node_head *)(ti->xstate);
|
ife = (struct ifentry *)no;
|
||||||
rnh->rnh_walktree(rnh, (walktree_f_t *)f, arg);
|
wa = (struct wa_ifidx *)arg;
|
||||||
|
|
||||||
|
wa->f(ife, wa->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct table_algo radix_iface = {
|
static void
|
||||||
.name = "radix_iface",
|
ta_foreach_ifidx(void *ta_state, struct table_info *ti, ta_foreach_f *f,
|
||||||
.lookup = ta_lookup_iface,
|
void *arg)
|
||||||
.init = ta_init_iface,
|
{
|
||||||
.destroy = ta_destroy_iface,
|
struct iftable_cfg *icfg;
|
||||||
.prepare_add = ta_prepare_add_iface,
|
struct wa_ifidx wa;
|
||||||
.prepare_del = ta_prepare_del_iface,
|
|
||||||
.add = ta_add_iface,
|
icfg = (struct iftable_cfg *)ta_state;
|
||||||
.del = ta_del_iface,
|
|
||||||
.flush_entry = ta_flush_iface_entry,
|
wa.f = f;
|
||||||
.foreach = ta_foreach_iface,
|
wa.arg = arg;
|
||||||
.dump_tentry = ta_dump_iface_tentry,
|
|
||||||
.find_tentry = ta_find_iface_tentry,
|
ipfw_objhash_foreach(icfg->ii, foreach_ifidx, &wa);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct table_algo idx_iface = {
|
||||||
|
.name = "idx_iface",
|
||||||
|
.lookup = ta_lookup_ifidx,
|
||||||
|
.init = ta_init_ifidx,
|
||||||
|
.destroy = ta_destroy_ifidx,
|
||||||
|
.prepare_add = ta_prepare_add_ifidx,
|
||||||
|
.prepare_del = ta_prepare_del_ifidx,
|
||||||
|
.add = ta_add_ifidx,
|
||||||
|
.del = ta_del_ifidx,
|
||||||
|
.flush_entry = ta_flush_ifidx_entry,
|
||||||
|
.foreach = ta_foreach_ifidx,
|
||||||
|
.dump_tentry = ta_dump_ifidx_tentry,
|
||||||
|
.find_tentry = ta_find_ifidx_tentry,
|
||||||
|
.prepare_mod = ta_prepare_mod_ifidx,
|
||||||
|
.fill_mod = ta_fill_mod_ifidx,
|
||||||
|
.modify = ta_modify_ifidx,
|
||||||
|
.flush_mod = ta_flush_mod_ifidx,
|
||||||
|
.change_ti = ta_change_ti_ifidx,
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -817,7 +1187,7 @@ ipfw_table_algo_init(struct ip_fw_chain *chain)
|
|||||||
* Register all algorithms presented here.
|
* Register all algorithms presented here.
|
||||||
*/
|
*/
|
||||||
ipfw_add_table_algo(chain, &radix_cidr);
|
ipfw_add_table_algo(chain, &radix_cidr);
|
||||||
ipfw_add_table_algo(chain, &radix_iface);
|
ipfw_add_table_algo(chain, &idx_iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
Loading…
Reference in New Issue
Block a user