summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/ip_fw.h9
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c167
-rw-r--r--sys/netpfil/ipfw/ip_fw_dynamic.c23
-rw-r--r--sys/netpfil/ipfw/ip_fw_nat.c85
-rw-r--r--sys/netpfil/ipfw/ip_fw_private.h41
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c272
6 files changed, 478 insertions, 119 deletions
diff --git a/sys/netinet/ip_fw.h b/sys/netinet/ip_fw.h
index 9b48430..3c60274 100644
--- a/sys/netinet/ip_fw.h
+++ b/sys/netinet/ip_fw.h
@@ -65,7 +65,8 @@
/* IP_FW3 header/opcodes */
typedef struct _ip_fw3_opheader {
uint16_t opcode; /* Operation opcode */
- uint16_t reserved[3]; /* Align to 64-bit boundary */
+ uint16_t ctxid;
+ uint16_t reserved[2]; /* Align to 64-bit boundary */
} ip_fw3_opheader;
@@ -76,6 +77,12 @@ typedef struct _ip_fw3_opheader {
#define IP_FW_TABLE_XLIST 89 /* list table contents */
#define IP_FW_TABLE_XLISTENTRY 90 /* list one table entry contents */
#define IP_FW_TABLE_XZEROENTRY 91 /* zero one table entry stats */
+#define IP_FW_CTX_GET 92
+#define IP_FW_CTX_ADD 93
+#define IP_FW_CTX_DEL 94
+#define IP_FW_CTX_SET 95
+#define IP_FW_CTX_ADDMEMBER 96
+#define IP_FW_CTX_DELMEMBER 97
/*
* The kernel representation of ipfw rules is made of a list of
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index 5ec629b..68d83f8 100644
--- a/sys/netpfil/ipfw/ip_fw2.c
+++ b/sys/netpfil/ipfw/ip_fw2.c
@@ -140,8 +140,7 @@ VNET_DEFINE(int, fw_verbose);
VNET_DEFINE(u_int64_t, norule_counter);
VNET_DEFINE(int, verbose_limit);
-/* layer3_chain contains the list of rules for layer 3 */
-VNET_DEFINE(struct ip_fw_chain, layer3_chain);
+VNET_DEFINE(struct ip_fw_contextes, ip_fw_contexts);
VNET_DEFINE(int, ipfw_nat_ready) = 0;
@@ -182,9 +181,6 @@ SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
"Make the default rule accept all packets.");
TUNABLE_INT("net.inet.ip.fw.default_to_accept", &default_to_accept);
TUNABLE_INT("net.inet.ip.fw.tables_max", (int *)&default_fw_tables);
-SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, static_count,
- CTLFLAG_RD, &VNET_NAME(layer3_chain.n_rules), 0,
- "Number of static rules");
#ifdef INET6
SYSCTL_DECL(_net_inet6_ip6);
@@ -904,6 +900,9 @@ ipfw_chk(struct ip_fw_args *args)
*/
struct ifnet *oif = args->oif;
+ if (V_ip_fw_contexts.chain[oif->if_ispare[0]] == NULL)
+ return (IP_FW_PASS);
+
int f_pos = 0; /* index of current rule in the array */
int retval = 0;
@@ -954,9 +953,15 @@ ipfw_chk(struct ip_fw_args *args)
*/
int dyn_dir = MATCH_UNKNOWN;
ipfw_dyn_rule *q = NULL;
- struct ip_fw_chain *chain = &V_layer3_chain;
void *tblent = NULL, *tblent2 = NULL;
+ /* XXX: WARNING - The chain is accessed unlocked here.
+ * There is a potential race here with context handling.
+ * The chain pointer will get destroyed and a NULL
+ * pointer dereference can happen!
+ */
+ struct ip_fw_chain *chain = V_ip_fw_contexts.chain[oif->if_ispare[0]];
+
/*
* We store in ulp a pointer to the upper layer protocol header.
* In the ipv4 case this is easy to determine from the header,
@@ -2500,7 +2505,7 @@ do { \
set_match(args, f_pos, chain);
/* Check if this is 'global' nat rule */
if (cmd->arg1 == 0) {
- retval = ipfw_nat_ptr(args, NULL, m);
+ retval = ipfw_nat_ptr(args, NULL, m, chain);
break;
}
t = ((ipfw_insn_nat *)cmd)->nat;
@@ -2515,7 +2520,7 @@ do { \
if (cmd->arg1 != IP_FW_TABLEARG)
((ipfw_insn_nat *)cmd)->nat = t;
}
- retval = ipfw_nat_ptr(args, t, m);
+ retval = ipfw_nat_ptr(args, t, m, chain);
break;
case O_REASS: {
@@ -2623,7 +2628,9 @@ sysctl_ipfw_table_num(SYSCTL_HANDLER_ARGS)
if ((error != 0) || (req->newptr == NULL))
return (error);
- return (ipfw_resize_tables(&V_layer3_chain, ntables));
+ for (int i = 1; i < IP_FW_MAXCTX; i++)
+ error += ipfw_resize_tables(V_ip_fw_contexts.chain[i], ntables);
+ return (error);
}
#endif
/*
@@ -2701,11 +2708,6 @@ ipfw_destroy(void)
static int
vnet_ipfw_init(const void *unused)
{
- int error;
- struct ip_fw *rule = NULL;
- struct ip_fw_chain *chain;
-
- chain = &V_layer3_chain;
/* First set up some values that are compile time options */
V_autoinc_step = 100; /* bounded to 1..1000 in add_rule() */
@@ -2716,10 +2718,55 @@ vnet_ipfw_init(const void *unused)
#ifdef IPFIREWALL_VERBOSE_LIMIT
V_verbose_limit = IPFIREWALL_VERBOSE_LIMIT;
#endif
+
+ for (int i = 0; i < IP_FW_MAXCTX; i++)
+ V_ip_fw_contexts.chain[i] = NULL;
+
+ IPFW_CTX_LOCK_INIT();
+
+ V_ip_fw_contexts.ifnet_arrival = EVENTHANDLER_REGISTER(ifnet_arrival_event,
+ ipfw_attach_ifnet_event, NULL, EVENTHANDLER_PRI_ANY);
+
+ ipfw_dyn_init();
+
+ /* First set up some values that are compile time options */
+ V_ipfw_vnet_ready = 1; /* Open for business */
+
+ /*
+ * Hook the sockopt handler and pfil hooks for ipv4 and ipv6.
+ * Even if the latter two fail we still keep the module alive
+ * because the sockopt and layer2 paths are still useful.
+ * ipfw[6]_hook return 0 on success, ENOENT on failure,
+ * so we can ignore the exact return value and just set a flag.
+ *
+ * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so
+ * changes in the underlying (per-vnet) variables trigger
+ * immediate hook()/unhook() calls.
+ * In layer2 we have the same behaviour, except that V_ether_ipfw
+ * is checked on each packet because there are no pfil hooks.
+ */
+ V_ip_fw_ctl_ptr = ipfw_ctl;
+ return ipfw_attach_hooks(1);
+}
+
+int
+ipfw_context_init(int index)
+{
+ struct ip_fw_chain *chain;
+ struct ip_fw *rule = NULL;
+
+ if (index >= IP_FW_MAXCTX)
+ return (-1);
+
+ TAILQ_INIT(&V_ip_fw_contexts.iflist[index]);
+
+ chain = V_ip_fw_contexts.chain[index];
+
+ IPFW_LOCK_INIT(chain);
+
#ifdef IPFIREWALL_NAT
LIST_INIT(&chain->nat);
#endif
-
/* insert the default rule and create the initial map */
chain->n_rules = 1;
chain->static_len = sizeof(struct ip_fw);
@@ -2729,13 +2776,7 @@ vnet_ipfw_init(const void *unused)
/* Set initial number of tables */
V_fw_tables_max = default_fw_tables;
- error = ipfw_init_tables(chain);
- if (error) {
- printf("ipfw2: setting up tables failed\n");
- free(chain->map, M_IPFW);
- free(rule, M_IPFW);
- return (ENOSPC);
- }
+ ipfw_init_tables(chain);
/* fill and insert the default rule */
rule->act_ofs = 0;
@@ -2747,28 +2788,13 @@ vnet_ipfw_init(const void *unused)
chain->default_rule = chain->map[0] = rule;
chain->id = rule->id = 1;
- IPFW_LOCK_INIT(chain);
- ipfw_dyn_init(chain);
-
- /* First set up some values that are compile time options */
- V_ipfw_vnet_ready = 1; /* Open for business */
+ /*
+ * This can potentially be done on first dynamic rule
+ * being added to chain.
+ */
+ resize_dynamic_table(chain, V_curr_dyn_buckets);
- /*
- * Hook the sockopt handler and pfil hooks for ipv4 and ipv6.
- * Even if the latter two fail we still keep the module alive
- * because the sockopt and layer2 paths are still useful.
- * ipfw[6]_hook return 0 on success, ENOENT on failure,
- * so we can ignore the exact return value and just set a flag.
- *
- * Note that V_fw[6]_enable are manipulated by a SYSCTL_PROC so
- * changes in the underlying (per-vnet) variables trigger
- * immediate hook()/unhook() calls.
- * In layer2 we have the same behaviour, except that V_ether_ipfw
- * is checked on each packet because there are no pfil hooks.
- */
- V_ip_fw_ctl_ptr = ipfw_ctl;
- error = ipfw_attach_hooks(1);
- return (error);
+ return (0);
}
/*
@@ -2777,11 +2803,9 @@ vnet_ipfw_init(const void *unused)
static int
vnet_ipfw_uninit(const void *unused)
{
- struct ip_fw *reap, *rule;
- struct ip_fw_chain *chain = &V_layer3_chain;
- int i;
V_ipfw_vnet_ready = 0; /* tell new callers to go away */
+
/*
* disconnect from ipv4, ipv6, layer2 and sockopt.
* Then grab, release and grab again the WLOCK so we make
@@ -2789,14 +2813,51 @@ vnet_ipfw_uninit(const void *unused)
*/
(void)ipfw_attach_hooks(0 /* detach */);
V_ip_fw_ctl_ptr = NULL;
+
+ ipfw_dyn_uninit(0); /* run the callout_drain */
+
+ IPFW_CTX_WLOCK();
+ EVENTHANDLER_DEREGISTER(ifnet_arrival_event, V_ip_fw_contexts.ifnet_arrival);
+ for (int i = 0; i < IP_FW_MAXCTX; i++) {
+ ipfw_context_uninit(i);
+ }
+ IPFW_CTX_WUNLOCK();
+ IPFW_CTX_LOCK_DESTROY();
+
+ ipfw_dyn_uninit(1); /* free the remaining parts */
+
+ return (0);
+}
+
+int
+ipfw_context_uninit(int index)
+{
+ struct ip_fw_chain *chain;
+ struct ip_fw_ctx_iflist *ifl;
+ struct ip_fw *reap, *rule;
+ struct ifnet *ifp;
+ int i;
+
+ if (index >= IP_FW_MAXCTX)
+ return (-1);
+
+ chain = V_ip_fw_contexts.chain[index];
+ if (chain == NULL)
+ return (0);
+
+ while (!TAILQ_EMPTY(&V_ip_fw_contexts.iflist[index])) {
+ ifl = TAILQ_FIRST(&V_ip_fw_contexts.iflist[index]);
+ TAILQ_REMOVE(&V_ip_fw_contexts.iflist[index], ifl, entry);
+ ifp = ifunit(ifl->ifname);
+ if (ifp != NULL)
+ ifp->if_ispare[0] = 0;
+ free(ifl, M_IPFW);
+ }
+
IPFW_UH_WLOCK(chain);
IPFW_UH_WUNLOCK(chain);
IPFW_UH_WLOCK(chain);
- IPFW_WLOCK(chain);
- ipfw_dyn_uninit(0); /* run the callout_drain */
- IPFW_WUNLOCK(chain);
-
ipfw_destroy_tables(chain);
reap = NULL;
IPFW_WLOCK(chain);
@@ -2812,8 +2873,10 @@ vnet_ipfw_uninit(const void *unused)
if (reap != NULL)
ipfw_reap_rules(reap);
IPFW_LOCK_DESTROY(chain);
- ipfw_dyn_uninit(1); /* free the remaining parts */
- return 0;
+
+ free(chain, M_IPFW);
+
+ return (0);
}
/*
diff --git a/sys/netpfil/ipfw/ip_fw_dynamic.c b/sys/netpfil/ipfw/ip_fw_dynamic.c
index a166d12..694362a 100644
--- a/sys/netpfil/ipfw/ip_fw_dynamic.c
+++ b/sys/netpfil/ipfw/ip_fw_dynamic.c
@@ -121,11 +121,9 @@ struct ipfw_dyn_bucket {
*/
static VNET_DEFINE(struct ipfw_dyn_bucket *, ipfw_dyn_v);
static VNET_DEFINE(u_int32_t, dyn_buckets_max);
-static VNET_DEFINE(u_int32_t, curr_dyn_buckets);
static VNET_DEFINE(struct callout, ipfw_timeout);
#define V_ipfw_dyn_v VNET(ipfw_dyn_v)
#define V_dyn_buckets_max VNET(dyn_buckets_max)
-#define V_curr_dyn_buckets VNET(curr_dyn_buckets)
#define V_ipfw_timeout VNET(ipfw_timeout)
static VNET_DEFINE(uma_zone_t, ipfw_dyn_rule_zone);
@@ -181,6 +179,8 @@ static VNET_DEFINE(u_int32_t, dyn_max); /* max # of dynamic rules */
static int last_log; /* Log ratelimiting */
+VNET_DEFINE(u_int32_t, curr_dyn_buckets);
+
static void ipfw_dyn_tick(void *vnetx);
static void check_dyn_rules(struct ip_fw_chain *, struct ip_fw *,
int, int, int);
@@ -472,7 +472,7 @@ ipfw_dyn_unlock(ipfw_dyn_rule *q)
IPFW_BUCK_UNLOCK(q->bucket);
}
-static int
+int
resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets)
{
int i, k, nbuckets_old;
@@ -972,7 +972,6 @@ ipfw_dyn_send_ka(struct mbuf **mtailp, ipfw_dyn_rule *q)
static void
ipfw_dyn_tick(void * vnetx)
{
- struct ip_fw_chain *chain;
int check_ka = 0;
#ifdef VIMAGE
struct vnet *vp = vnetx;
@@ -980,7 +979,6 @@ ipfw_dyn_tick(void * vnetx)
CURVNET_SET(vp);
- chain = &V_layer3_chain;
/* Run keepalive checks every keepalive_period iff ka is enabled */
if ((V_dyn_keepalive_last + V_dyn_keepalive_period <= time_uptime) &&
@@ -989,7 +987,12 @@ ipfw_dyn_tick(void * vnetx)
check_ka = 1;
}
- check_dyn_rules(chain, NULL, RESVD_SET, check_ka, 1);
+ IPFW_CTX_RLOCK();
+ for (int i = 1; i < IP_FW_MAXCTX; i++) {
+ if (V_ip_fw_contexts.chain[i] != NULL)
+ check_dyn_rules(V_ip_fw_contexts.chain[i], NULL, RESVD_SET, check_ka, 1);
+ }
+ IPFW_CTX_RUNLOCK();
callout_reset_on(&V_ipfw_timeout, hz, ipfw_dyn_tick, vnetx, 0);
@@ -1305,7 +1308,7 @@ ipfw_expire_dyn_rules(struct ip_fw_chain *chain, struct ip_fw *rule, int set)
}
void
-ipfw_dyn_init(struct ip_fw_chain *chain)
+ipfw_dyn_init()
{
V_ipfw_dyn_v = NULL;
@@ -1334,12 +1337,6 @@ ipfw_dyn_init(struct ip_fw_chain *chain)
uma_zone_set_max(V_ipfw_dyn_rule_zone, V_dyn_max);
callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE);
-
- /*
- * This can potentially be done on first dynamic rule
- * being added to chain.
- */
- resize_dynamic_table(chain, V_curr_dyn_buckets);
}
void
diff --git a/sys/netpfil/ipfw/ip_fw_nat.c b/sys/netpfil/ipfw/ip_fw_nat.c
index 0fb4534..627603d 100644
--- a/sys/netpfil/ipfw/ip_fw_nat.c
+++ b/sys/netpfil/ipfw/ip_fw_nat.c
@@ -64,26 +64,33 @@ ifaddr_change(void *arg __unused, struct ifnet *ifp)
KASSERT(curvnet == ifp->if_vnet,
("curvnet(%p) differs from iface vnet(%p)", curvnet, ifp->if_vnet));
- chain = &V_layer3_chain;
- IPFW_WLOCK(chain);
- /* Check every nat entry... */
- LIST_FOREACH(ptr, &chain->nat, _next) {
- /* ...using nic 'ifp->if_xname' as dynamic alias address. */
- if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
+
+ IPFW_CTX_RLOCK();
+ for (int i = 1; i < IP_FW_MAXCTX; i++) {
+ chain = V_ip_fw_contexts.chain[i];
+ if (chain == NULL)
continue;
- if_addr_rlock(ifp);
- TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
- if (ifa->ifa_addr == NULL)
- continue;
- if (ifa->ifa_addr->sa_family != AF_INET)
+ IPFW_WLOCK(chain);
+ /* Check every nat entry... */
+ LIST_FOREACH(ptr, &chain->nat, _next) {
+ /* ...using nic 'ifp->if_xname' as dynamic alias address. */
+ if (strncmp(ptr->if_name, ifp->if_xname, IF_NAMESIZE) != 0)
continue;
- ptr->ip = ((struct sockaddr_in *)
- (ifa->ifa_addr))->sin_addr;
- LibAliasSetAddress(ptr->lib, ptr->ip);
+ if_addr_rlock(ifp);
+ TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ ptr->ip = ((struct sockaddr_in *)
+ (ifa->ifa_addr))->sin_addr;
+ LibAliasSetAddress(ptr->lib, ptr->ip);
+ }
+ if_addr_runlock(ifp);
}
- if_addr_runlock(ifp);
+ IPFW_WUNLOCK(chain);
}
- IPFW_WUNLOCK(chain);
+ IPFW_CTX_RUNLOCK();
}
/*
@@ -206,18 +213,18 @@ add_redir_spool_cfg(char *buf, struct cfg_nat *ptr)
/*
* ipfw_nat - perform mbuf header translation.
*
- * Note V_layer3_chain has to be locked while calling ipfw_nat() in
+ * Note *chain has to be locked while calling ipfw_nat() in
* 'global' operation mode (t == NULL).
*
*/
static int
-ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
+ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m,
+ struct ip_fw_chain *chain)
{
struct mbuf *mcl;
struct ip *ip;
/* XXX - libalias duct tape */
int ldt, retval, found;
- struct ip_fw_chain *chain;
char *c;
ldt = 0;
@@ -276,7 +283,6 @@ ipfw_nat(struct ip_fw_args *args, struct cfg_nat *t, struct mbuf *m)
}
found = 0;
- chain = &V_layer3_chain;
IPFW_RLOCK_ASSERT(chain);
/* Check every nat entry... */
LIST_FOREACH(t, &chain->nat, _next) {
@@ -391,11 +397,10 @@ lookup_nat(struct nat_list *l, int nat_id)
}
static int
-ipfw_nat_cfg(struct sockopt *sopt)
+ipfw_nat_cfg(struct sockopt *sopt, struct ip_fw_chain *chain)
{
struct cfg_nat *cfg, *ptr;
char *buf;
- struct ip_fw_chain *chain = &V_layer3_chain;
size_t len;
int gencnt, error = 0;
@@ -468,10 +473,9 @@ out:
}
static int
-ipfw_nat_del(struct sockopt *sopt)
+ipfw_nat_del(struct sockopt *sopt, struct ip_fw_chain *chain)
{
struct cfg_nat *ptr;
- struct ip_fw_chain *chain = &V_layer3_chain;
int i;
sooptcopyin(sopt, &i, sizeof i, sizeof i);
@@ -492,9 +496,8 @@ ipfw_nat_del(struct sockopt *sopt)
}
static int
-ipfw_nat_get_cfg(struct sockopt *sopt)
+ipfw_nat_get_cfg(struct sockopt *sopt, struct ip_fw_chain *chain)
{
- struct ip_fw_chain *chain = &V_layer3_chain;
struct cfg_nat *n;
struct cfg_redir *r;
struct cfg_spool *s;
@@ -552,14 +555,11 @@ retry:
}
static int
-ipfw_nat_get_log(struct sockopt *sopt)
+ipfw_nat_get_log(struct sockopt *sopt, struct ip_fw_chain *chain)
{
uint8_t *data;
struct cfg_nat *ptr;
int i, size;
- struct ip_fw_chain *chain;
-
- chain = &V_layer3_chain;
IPFW_RLOCK(chain);
/* one pass to count, one to copy the data */
@@ -604,17 +604,22 @@ vnet_ipfw_nat_uninit(const void *arg __unused)
struct cfg_nat *ptr, *ptr_temp;
struct ip_fw_chain *chain;
- chain = &V_layer3_chain;
- IPFW_WLOCK(chain);
- LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
- LIST_REMOVE(ptr, _next);
- del_redir_spool_cfg(ptr, &ptr->redir_chain);
- LibAliasUninit(ptr->lib);
- free(ptr, M_IPFW);
+ IPFW_CTX_RLOCK();
+ for (int i = 1; i < IP_FW_MAXCTX; i++) {
+ chain = V_ip_fw_contexts.chain[i];
+ IPFW_WLOCK(chain);
+ LIST_FOREACH_SAFE(ptr, &chain->nat, _next, ptr_temp) {
+ LIST_REMOVE(ptr, _next);
+ del_redir_spool_cfg(ptr, &ptr->redir_chain);
+ LibAliasUninit(ptr->lib);
+ free(ptr, M_IPFW);
+ }
+ flush_nat_ptrs(chain, -1 /* flush all */);
+ V_ipfw_nat_ready = 0;
+ IPFW_WUNLOCK(chain);
}
- flush_nat_ptrs(chain, -1 /* flush all */);
- V_ipfw_nat_ready = 0;
- IPFW_WUNLOCK(chain);
+ IPFW_CTX_RUNLOCK();
+
return (0);
}
diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h
index 4977dae..4f4cf93 100644
--- a/sys/netpfil/ipfw/ip_fw_private.h
+++ b/sys/netpfil/ipfw/ip_fw_private.h
@@ -171,6 +171,9 @@ enum { /* result for matching dynamic rules */
MATCH_UNKNOWN,
};
+VNET_DECLARE(u_int32_t, curr_dyn_buckets);
+#define V_curr_dyn_buckets VNET(curr_dyn_buckets)
+
/*
* The lock for dynamic rules is only used once outside the file,
* and only to release the result of lookup_dyn_rule().
@@ -179,6 +182,7 @@ enum { /* result for matching dynamic rules */
struct ip_fw_chain;
void ipfw_expire_dyn_rules(struct ip_fw_chain *, struct ip_fw *, int);
void ipfw_dyn_unlock(ipfw_dyn_rule *q);
+int resize_dynamic_table(struct ip_fw_chain *, int);
struct tcphdr;
struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *,
@@ -190,7 +194,7 @@ ipfw_dyn_rule *ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt,
void ipfw_remove_dyn_children(struct ip_fw *rule);
void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep);
-void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */
+void ipfw_dyn_init(void); /* per-vnet initialization */
void ipfw_dyn_uninit(int); /* per-vnet deinitialization */
int ipfw_dyn_len(void);
@@ -201,9 +205,6 @@ VNET_DECLARE(int, fw_one_pass);
VNET_DECLARE(int, fw_verbose);
#define V_fw_verbose VNET(fw_verbose)
-VNET_DECLARE(struct ip_fw_chain, layer3_chain);
-#define V_layer3_chain VNET(layer3_chain)
-
VNET_DECLARE(u_int32_t, set_disable);
#define V_set_disable VNET(set_disable)
@@ -237,6 +238,33 @@ struct ip_fw_chain {
#endif
};
+struct ip_fw_ctx_iflist {
+ TAILQ_ENTRY(ip_fw_ctx_iflist) entry;
+ char ifname[IFNAMSIZ];
+};
+
+#define IP_FW_MAXCTX 4096
+struct ip_fw_contextes {
+ struct ip_fw_chain *chain[IP_FW_MAXCTX]; /* Arrays of contextes */
+ TAILQ_HEAD(, ip_fw_ctx_iflist) iflist[IP_FW_MAXCTX];
+ struct rwlock rwctx;
+ eventhandler_tag ifnet_arrival;
+};
+
+VNET_DECLARE(struct ip_fw_contextes, ip_fw_contexts);
+#define V_ip_fw_contexts VNET(ip_fw_contexts)
+
+#define IPFW_CTX_LOCK_INIT() rw_init(&V_ip_fw_contexts.rwctx, "IPFW context")
+#define IPFW_CTX_LOCK_DESTROY() rw_destroy(&V_ip_fw_contexts.rwctx)
+#define IPFW_CTX_WLOCK() rw_wlock(&V_ip_fw_contexts.rwctx)
+#define IPFW_CTX_WUNLOCK() rw_wunlock(&V_ip_fw_contexts.rwctx)
+#define IPFW_CTX_RLOCK() rw_rlock(&V_ip_fw_contexts.rwctx)
+#define IPFW_CTX_RUNLOCK() rw_runlock(&V_ip_fw_contexts.rwctx)
+
+void ipfw_attach_ifnet_event(void *, struct ifnet *);
+int ipfw_context_init(int);
+int ipfw_context_uninit(int);
+
struct sockopt; /* used by tcp_var.h */
/* Macro for working with various counters */
@@ -332,8 +360,9 @@ int ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables);
extern struct cfg_nat *(*lookup_nat_ptr)(struct nat_list *, int);
-typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *);
-typedef int ipfw_nat_cfg_t(struct sockopt *);
+typedef int ipfw_nat_t(struct ip_fw_args *, struct cfg_nat *, struct mbuf *,
+ struct ip_fw_chain *);
+typedef int ipfw_nat_cfg_t(struct sockopt *, struct ip_fw_chain *);
VNET_DECLARE(int, ipfw_nat_ready);
#define V_ipfw_nat_ready VNET(ipfw_nat_ready)
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index eea2725..2776e4c 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -943,12 +943,15 @@ ipfw_ctl(struct sockopt *sopt)
#define RULE_MAXSIZE (256*sizeof(u_int32_t))
int error;
size_t size, len, valsize;
+ struct ifnet *ifp;
struct ip_fw *buf, *rule;
- struct ip_fw_chain *chain;
+ static struct ip_fw_chain *chain;
+ struct ip_fw_ctx_iflist *tmpifl, *tmpifl2 = NULL;
+ ip_fw3_opheader *op3 = NULL;
u_int32_t rulenum[2];
uint32_t opt;
char xbuf[128];
- ip_fw3_opheader *op3 = NULL;
+ char *ifname;
error = priv_check(sopt->sopt_td, PRIV_NETINET_IPFW);
if (error)
@@ -965,7 +968,6 @@ ipfw_ctl(struct sockopt *sopt)
return (error);
}
- chain = &V_layer3_chain;
error = 0;
/* Save original valsize before it is altered via sooptcopyin() */
@@ -980,9 +982,238 @@ ipfw_ctl(struct sockopt *sopt)
return (error);
op3 = (ip_fw3_opheader *)xbuf;
opt = op3->opcode;
+
+ if (op3->ctxid >= IP_FW_MAXCTX)
+ return (EINVAL);
+
+ if (opt != IP_FW_CTX_GET && opt != IP_FW_CTX_ADD) {
+ if (op3->ctxid == 0)
+ return (ENOENT);
+
+ IPFW_CTX_RLOCK();
+ chain = V_ip_fw_contexts.chain[op3->ctxid];
+ IPFW_CTX_RUNLOCK();
+ if (chain == NULL)
+ return (ENOENT);
+ }
+ }
+
+ /* Verification needed to avoid problems */
+ switch (opt) {
+ case IP_FW_CTX_GET:
+ case IP_FW_CTX_ADD:
+ case IP_FW_CTX_DEL:
+ break;
+ default:
+ if (chain == NULL)
+ return (EINVAL);
+ /* NOTREACHED */
}
switch (opt) {
+ case IP_FW_CTX_ADD:
+ IPFW_CTX_WLOCK();
+ if (V_ip_fw_contexts.chain[op3->ctxid] != NULL) {
+ IPFW_CTX_WUNLOCK();
+ return (EEXIST);
+ }
+
+ chain = malloc(sizeof(struct ip_fw_chain), M_IPFW, M_WAITOK | M_ZERO);
+ TAILQ_INIT(&V_ip_fw_contexts.iflist[op3->ctxid]);
+ V_ip_fw_contexts.chain[op3->ctxid] = chain;
+ ipfw_context_init(op3->ctxid); /* XXX: error checking */
+ IPFW_CTX_WUNLOCK();
+ break;
+
+ case IP_FW_CTX_DEL:
+ IPFW_CTX_WLOCK();
+ if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) {
+ IPFW_CTX_WUNLOCK();
+ return (ENOENT);
+ }
+
+ ipfw_context_uninit(op3->ctxid);
+ V_ip_fw_contexts.chain[op3->ctxid] = NULL;
+ IPFW_CTX_WUNLOCK();
+ break;
+
+ case IP_FW_CTX_GET:
+ {
+ int i, n, len = 0, want;
+ char *bufout, *tmpbuf;
+
+ sopt->sopt_valsize = valsize;
+
+ IPFW_CTX_RLOCK();
+ for (i = 1; i < IP_FW_MAXCTX; i++) {
+ if (op3->ctxid > 0 && op3->ctxid != i)
+ continue;
+ if (op3->ctxid > 0 && op3->ctxid < i)
+ break;
+
+ if (V_ip_fw_contexts.chain[i] == NULL)
+ continue;
+
+ /* Calculate number of bytes for the integer */
+ n = i;
+ while (n > 0) {
+ n /= 10;
+ len++;
+ }
+ TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) {
+ len += strlen(tmpifl->ifname) + 1;
+ }
+ len += 3; // newline, :, space
+ }
+ IPFW_CTX_RUNLOCK();
+
+ if (len > sopt->sopt_valsize) {
+ sopt->sopt_valsize = len;
+ break;
+ }
+
+ bufout = malloc(len, M_TEMP, M_WAITOK | M_ZERO);
+ if (bufout == NULL)
+ break;
+
+ /* Record our size for later checks */
+ want = len;
+ len = 0;
+ IPFW_CTX_RLOCK();
+ /* Recalculate length to detect if smth changed */
+ for (i = 1; i < IP_FW_MAXCTX; i++) {
+ if (op3->ctxid > 0 && op3->ctxid != i)
+ continue;
+ if (op3->ctxid > 0 && op3->ctxid < i)
+ break;
+
+ if (V_ip_fw_contexts.chain[i] == NULL)
+ continue;
+
+ /* Calculate number of bytes for the integer */
+ n = i;
+ while (n > 0) {
+ n /= 10;
+ len++;
+ }
+ TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) {
+ len += strlen(tmpifl->ifname) + 1;
+ }
+ len += 3; // newline, :, space
+ }
+
+ if (want >= len) {
+ tmpbuf = bufout;
+ for (i = 1; i < IP_FW_MAXCTX; i++) {
+ if (op3->ctxid > 0 && op3->ctxid != i)
+ continue;
+ if (op3->ctxid > 0 && op3->ctxid < i)
+ break;
+
+ if (V_ip_fw_contexts.chain[i] == NULL)
+ continue;
+
+ sprintf(tmpbuf, "%d: ", i);
+ tmpbuf += strlen(tmpbuf);
+ TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) {
+ sprintf(tmpbuf, "%s,", tmpifl->ifname);
+ tmpbuf += strlen(tmpifl->ifname) + 1;
+ }
+ sprintf(tmpbuf, "\n");
+ tmpbuf++;
+ }
+ }
+ IPFW_CTX_RUNLOCK();
+
+ if (want >= len)
+ error = sooptcopyout(sopt, bufout, len);
+ else
+ len = 0;
+ free(bufout, M_TEMP);
+ }
+ break;
+
+ case IP_FW_CTX_SET:
+ /* XXX: Maybe not use this option at all? */
+ IPFW_CTX_RLOCK();
+ if (V_ip_fw_contexts.chain[op3->ctxid] == NULL)
+ error = ENOENT;
+ else
+ chain = V_ip_fw_contexts.chain[op3->ctxid];
+ IPFW_CTX_RUNLOCK();
+ break;
+
+ case IP_FW_CTX_ADDMEMBER:
+ {
+ int i;
+
+ ifname = (char *)(op3 + 1);
+ ifp = ifunit(ifname);
+ if (ifp == NULL)
+ return (ENOENT);
+
+ tmpifl = malloc(sizeof(*tmpifl), M_IPFW, M_WAITOK | M_ZERO);
+
+ IPFW_CTX_WLOCK();
+ if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) {
+ IPFW_CTX_WUNLOCK();
+ free(tmpifl, M_IPFW);
+ return (ENOENT);
+ }
+
+ for (i = 1; i < IP_FW_MAXCTX; i++) {
+ if (V_ip_fw_contexts.chain[i] == NULL)
+ continue;
+
+ TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[i], entry) {
+ if (strlen(tmpifl2->ifname) != strlen(ifname))
+ continue;
+ if (!strcmp(tmpifl2->ifname, ifname))
+ goto ctxifacefound;
+ }
+ }
+ctxifacefound:
+ if (tmpifl2 != NULL) {
+ IPFW_CTX_WUNLOCK();
+ free(tmpifl, M_IPFW);
+ return (EEXIST);
+ }
+
+ strlcpy(tmpifl->ifname, ifname, IFNAMSIZ);
+ TAILQ_INSERT_HEAD(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl, entry);
+ ifp->if_ispare[0] = op3->ctxid;
+ IPFW_CTX_WUNLOCK();
+ }
+ break;
+
+ case IP_FW_CTX_DELMEMBER:
+ IPFW_CTX_WLOCK();
+ if (V_ip_fw_contexts.chain[op3->ctxid] == NULL) {
+ IPFW_CTX_WUNLOCK();
+ return (ENOENT);
+ }
+
+ ifname = (char *)(op3 + 1);
+ TAILQ_FOREACH(tmpifl2, &V_ip_fw_contexts.iflist[op3->ctxid], entry) {
+ if (strlen(tmpifl2->ifname) != strlen(ifname))
+ continue;
+ if (!strcmp(tmpifl2->ifname, ifname))
+ break;
+ }
+ if (tmpifl2 == NULL) {
+ IPFW_CTX_WUNLOCK();
+ return (ENOENT);
+ }
+
+ TAILQ_REMOVE(&V_ip_fw_contexts.iflist[op3->ctxid], tmpifl2, entry);
+ IPFW_CTX_WUNLOCK();
+ free(tmpifl2, M_IPFW);
+
+ ifp = ifunit(ifname);
+ if (ifp != NULL)
+ ifp->if_ispare[0] = 0;
+ break;
+
case IP_FW_GET:
/*
* pass up a copy of the current rules. Static rules
@@ -1332,7 +1563,7 @@ ipfw_ctl(struct sockopt *sopt)
/*--- NAT operations are protected by the IPFW_LOCK ---*/
case IP_FW_NAT_CFG:
if (IPFW_NAT_LOADED)
- error = ipfw_nat_cfg_ptr(sopt);
+ error = ipfw_nat_cfg_ptr(sopt, chain);
else {
printf("IP_FW_NAT_CFG: %s\n",
"ipfw_nat not present, please load it");
@@ -1342,7 +1573,7 @@ ipfw_ctl(struct sockopt *sopt)
case IP_FW_NAT_DEL:
if (IPFW_NAT_LOADED)
- error = ipfw_nat_del_ptr(sopt);
+ error = ipfw_nat_del_ptr(sopt, chain);
else {
printf("IP_FW_NAT_DEL: %s\n",
"ipfw_nat not present, please load it");
@@ -1352,7 +1583,7 @@ ipfw_ctl(struct sockopt *sopt)
case IP_FW_NAT_GET_CONFIG:
if (IPFW_NAT_LOADED)
- error = ipfw_nat_get_cfg_ptr(sopt);
+ error = ipfw_nat_get_cfg_ptr(sopt, chain);
else {
printf("IP_FW_NAT_GET_CFG: %s\n",
"ipfw_nat not present, please load it");
@@ -1362,7 +1593,7 @@ ipfw_ctl(struct sockopt *sopt)
case IP_FW_NAT_GET_LOG:
if (IPFW_NAT_LOADED)
- error = ipfw_nat_get_log_ptr(sopt);
+ error = ipfw_nat_get_log_ptr(sopt, chain);
else {
printf("IP_FW_NAT_GET_LOG: %s\n",
"ipfw_nat not present, please load it");
@@ -1379,6 +1610,33 @@ ipfw_ctl(struct sockopt *sopt)
#undef RULE_MAXSIZE
}
+void
+ipfw_attach_ifnet_event(void *arg __unused, struct ifnet *ifp)
+{
+ struct ip_fw_ctx_iflist *tmpifl;
+
+ CURVNET_SET(ifp->if_vnet);
+
+ IPFW_CTX_RLOCK();
+ for (int i = 1; i < IP_FW_MAXCTX; i++) {
+ if (V_ip_fw_contexts.chain[i] == NULL)
+ continue;
+ TAILQ_FOREACH(tmpifl, &V_ip_fw_contexts.iflist[i], entry) {
+ if (strlen(tmpifl->ifname) != strlen(ifp->if_xname))
+ continue;
+ if (!strcmp(tmpifl->ifname, ifp->if_xname)) {
+ printf("Restoring context for interface %s to %d\n", ifp->if_xname, i);
+ ifp->if_ispare[0] = i;
+ goto ifctxdone;
+ break;
+ }
+ }
+ }
+ifctxdone:
+ IPFW_CTX_RUNLOCK();
+
+ CURVNET_RESTORE();
+}
#define RULE_MAXSIZE (256*sizeof(u_int32_t))
OpenPOWER on IntegriCloud