summaryrefslogtreecommitdiffstats
path: root/sys/netpfil/ipfw
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netpfil/ipfw')
-rw-r--r--sys/netpfil/ipfw/ip_dn_io.c1
-rw-r--r--sys/netpfil/ipfw/ip_dummynet.c3
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c280
-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_pfil.c22
-rw-r--r--sys/netpfil/ipfw/ip_fw_private.h57
-rw-r--r--sys/netpfil/ipfw/ip_fw_sockopt.c330
-rw-r--r--sys/netpfil/ipfw/ip_fw_table.c240
9 files changed, 875 insertions, 166 deletions
diff --git a/sys/netpfil/ipfw/ip_dn_io.c b/sys/netpfil/ipfw/ip_dn_io.c
index 9a4b486..c842ce0 100644
--- a/sys/netpfil/ipfw/ip_dn_io.c
+++ b/sys/netpfil/ipfw/ip_dn_io.c
@@ -651,6 +651,7 @@ dummynet_send(struct mbuf *m)
* to carry reinject info.
*/
dst = pkt->dn_dir;
+ pkt->rule.info |= IPFW_IS_DUMMYNET;
ifp = pkt->ifp;
tag->m_tag_cookie = MTAG_IPFW_RULE;
tag->m_tag_id = 0;
diff --git a/sys/netpfil/ipfw/ip_dummynet.c b/sys/netpfil/ipfw/ip_dummynet.c
index 4de2156..40b8e97 100644
--- a/sys/netpfil/ipfw/ip_dummynet.c
+++ b/sys/netpfil/ipfw/ip_dummynet.c
@@ -124,7 +124,7 @@ ipdn_bound_var(int *v, int dflt, int lo, int hi, const char *msg)
op = "Clamp";
} else
return *v;
- if (op && msg)
+ if (op && msg && bootverbose)
printf("%s %s to %d (was %d)\n", op, msg, *v, oldv);
return *v;
}
@@ -2290,7 +2290,6 @@ static moduledata_t dummynet_mod = {
#define DN_SI_SUB SI_SUB_PROTO_IFATTACHDOMAIN
#define DN_MODEV_ORD (SI_ORDER_ANY - 128) /* after ipfw */
DECLARE_MODULE(dummynet, dummynet_mod, DN_SI_SUB, DN_MODEV_ORD);
-MODULE_DEPEND(dummynet, ipfw, 2, 2, 2);
MODULE_VERSION(dummynet, 3);
/*
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index 712c675..c07546b 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);
@@ -358,8 +354,8 @@ iface_match(struct ifnet *ifp, ipfw_insn_if *cmd, struct ip_fw_chain *chain, uin
/* Check by name or by IP address */
if (cmd->name[0] != '\0') { /* match by name */
if (cmd->name[0] == '\1') /* use tablearg to match */
- return ipfw_lookup_table_extended(chain, cmd->p.glob,
- ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE);
+ return (ipfw_lookup_table_extended(chain, cmd->p.glob,
+ ifp->if_xname, tablearg, IPFW_TABLE_INTERFACE, NULL) != NULL);
/* Check name */
if (cmd->p.glob) {
if (fnmatch(cmd->name, ifp->if_xname, 0) == 0)
@@ -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,7 +953,14 @@ 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.
@@ -1288,6 +1294,8 @@ do { \
continue;
skip_or = 0;
+ tblent = NULL;
+ tblent2 = NULL;
for (l = f->cmd_len, cmd = f->cmd ; l > 0 ;
l -= cmdlen, cmd += cmdlen) {
int match;
@@ -1402,7 +1410,7 @@ do { \
break;
case O_IN: /* "out" is "not in" */
- match = (oif == NULL);
+ match = (args->dir == DIR_IN);
break;
case O_LAYER2:
@@ -1438,11 +1446,18 @@ do { \
case O_IP_SRC_LOOKUP:
case O_IP_DST_LOOKUP:
if (is_ipv4) {
+ struct ether_addr *ea = NULL;
+
uint32_t key =
(cmd->opcode == O_IP_DST_LOOKUP) ?
dst_ip.s_addr : src_ip.s_addr;
uint32_t v = 0;
+ if (args->eh) {
+ ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ?
+ args->eh->ether_dhost :
+ args->eh->ether_shost);
+ }
if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
/* generic lookup. The key must be
* in 32bit big-endian format.
@@ -1484,22 +1499,37 @@ do { \
} else
break;
}
- match = ipfw_lookup_table(chain,
- cmd->arg1, key, &v);
- if (!match)
+ tblent2 = ipfw_lookup_table(chain,
+ cmd->arg1, key, &v, ea);
+ if (tblent2 == NULL) {
+ match = 0;
break;
+ } else
+ match = 1;
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
match =
((ipfw_insn_u32 *)cmd)->d[0] == v;
- else
+ if (match)
tablearg = v;
} else if (is_ipv6) {
+ struct ether_addr *ea = NULL;
uint32_t v = 0;
+
+ if (args->eh) {
+ ea = (struct ether_addr*)((cmd->opcode == O_IP_DST_LOOKUP) ?
+ args->eh->ether_dhost :
+ args->eh->ether_shost);
+ }
void *pkey = (cmd->opcode == O_IP_DST_LOOKUP) ?
&args->f_id.dst_ip6: &args->f_id.src_ip6;
- match = ipfw_lookup_table_extended(chain,
+ tblent = ipfw_lookup_table_extended(chain,
cmd->arg1, pkey, &v,
- IPFW_TABLE_CIDR);
+ IPFW_TABLE_CIDR, ea);
+ if (tblent == NULL) {
+ match = 0;
+ break;
+ } else
+ match = 1;
if (cmdlen == F_INSN_SIZE(ipfw_insn_u32))
match = ((ipfw_insn_u32 *)cmd)->d[0] == v;
if (match)
@@ -2314,8 +2344,7 @@ do { \
break;
case O_FORWARD_IP:
- if (args->eh) /* not valid on layer2 pkts */
- break;
+ if (!args->eh) {/* not valid on layer2 pkts */
if (q == NULL || q->rule != f ||
dyn_dir == MATCH_FORWARD) {
struct sockaddr_in *sa;
@@ -2330,6 +2359,48 @@ do { \
args->next_hop = sa;
}
}
+ } else if (args->eh) {
+ struct m_tag *fwd_tag;
+ struct sockaddr_in *sa;
+ u_short sum;
+
+ /*
+ * Checksum correct? (from ip_fastfwd.c)
+ */
+ if (m->m_pkthdr.csum_flags & CSUM_IP_CHECKED)
+ sum = !(m->m_pkthdr.csum_flags & CSUM_IP_VALID);
+ else {
+ if (hlen == sizeof(struct ip))
+ sum = in_cksum_hdr(ip);
+ else
+ sum = in_cksum(m, hlen);
+ }
+ if (sum) {
+ IPSTAT_INC(ips_badsum);
+ retval = IP_FW_DENY;
+ break;
+ }
+
+ /*
+ * Remember that we have checked the IP header and found it valid.
+ */
+ m->m_pkthdr.csum_flags |= (CSUM_IP_CHECKED | CSUM_IP_VALID);
+
+ sa = &(((ipfw_insn_sa *)cmd)->sa);
+ fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
+ sizeof(struct sockaddr_in), M_NOWAIT);
+ if (fwd_tag == NULL)
+ retval = IP_FW_DENY;
+ else {
+ bcopy(sa, (fwd_tag+1), sizeof(struct sockaddr_in));
+ m_tag_prepend(m, fwd_tag);
+
+ if (in_localip(sa->sin_addr))
+ m->m_flags |= M_FASTFWD_OURS;
+ m->m_flags |= M_IP_NEXTHOP;
+ }
+ }
+
retval = IP_FW_PASS;
l = 0; /* exit inner loop */
done = 1; /* exit outer loop */
@@ -2337,8 +2408,7 @@ do { \
#ifdef INET6
case O_FORWARD_IP6:
- if (args->eh) /* not valid on layer2 pkts */
- break;
+ if (!args->eh) {/* not valid on layer2 pkts */
if (q == NULL || q->rule != f ||
dyn_dir == MATCH_FORWARD) {
struct sockaddr_in6 *sin6;
@@ -2346,6 +2416,24 @@ do { \
sin6 = &(((ipfw_insn_sa6 *)cmd)->sa);
args->next_hop6 = sin6;
}
+ } else if (args->eh) {
+ struct m_tag *fwd_tag;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = &(((ipfw_insn_sa6 *)cmd)->sa);
+ fwd_tag = m_tag_get(PACKET_TAG_IPFORWARD,
+ sizeof(struct sockaddr_in6), M_NOWAIT);
+ if (fwd_tag == NULL)
+ retval = IP_FW_DENY;
+ else {
+ bcopy(sin6, (fwd_tag+1), sizeof(struct sockaddr_in6));
+ m_tag_prepend(m, fwd_tag);
+
+ if (in6_localip(&sin6->sin6_addr))
+ m->m_flags |= M_FASTFWD_OURS;
+ m->m_flags |= M_IP6_NEXTHOP;
+ }
+ }
retval = IP_FW_PASS;
l = 0; /* exit inner loop */
done = 1; /* exit outer loop */
@@ -2417,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;
@@ -2432,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: {
@@ -2502,6 +2590,10 @@ do { \
struct ip_fw *rule = chain->map[f_pos];
/* Update statistics */
IPFW_INC_RULE_COUNTER(rule, pktlen);
+ if (tblent != NULL)
+ ipfw_count_table_xentry_stats(tblent, pktlen);
+ if (tblent2 != NULL)
+ ipfw_count_table_entry_stats(tblent2, pktlen);
} else {
retval = IP_FW_DENY;
printf("ipfw: ouch!, skip past end of rules, denying packet\n");
@@ -2536,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
/*
@@ -2614,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() */
@@ -2629,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);
@@ -2642,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;
@@ -2660,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);
}
/*
@@ -2690,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
@@ -2702,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);
@@ -2725,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 81c1b2c..b6cfa62 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;
@@ -975,7 +975,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;
@@ -983,7 +982,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) &&
@@ -992,7 +990,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);
@@ -1308,7 +1311,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;
@@ -1337,12 +1340,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_pfil.c b/sys/netpfil/ipfw/ip_fw_pfil.c
index 2bcd1dd..bf225b8 100644
--- a/sys/netpfil/ipfw/ip_fw_pfil.c
+++ b/sys/netpfil/ipfw/ip_fw_pfil.c
@@ -143,8 +143,9 @@ again:
}
args.m = *m0;
- args.oif = dir == DIR_OUT ? ifp : NULL;
+ args.oif = ifp;
args.inp = inp;
+ args.dir = dir;
ipfw = ipfw_chk(&args);
*m0 = args.m;
@@ -314,9 +315,8 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
/* XXX can we free it after use ? */
mtag->m_tag_id = PACKET_TAG_NONE;
r = (struct ipfw_rule_ref *)(mtag + 1);
- if (r->info & IPFW_ONEPASS)
- return (0);
- args.rule = *r;
+ m_tag_delete(*m0, mtag);
+ return (0);
}
/* I need some amt of data to be contiguous */
@@ -333,12 +333,15 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
save_eh = *eh; /* save copy for restore below */
m_adj(m, ETHER_HDR_LEN); /* strip ethernet header */
+ dir = dir == PFIL_IN ? DIR_IN : DIR_OUT;
+
args.m = m; /* the packet we are looking at */
- args.oif = dir == PFIL_OUT ? dst: NULL; /* destination, if any */
+ args.oif = dst; /* destination, if any */
args.next_hop = NULL; /* we do not support forward yet */
args.next_hop6 = NULL; /* we do not support forward yet */
args.eh = &save_eh; /* MAC header for bridged/MAC packets */
args.inp = NULL; /* used by ipfw uid/gid/jail rules */
+ args.dir = dir; /* pfSense addition */
i = ipfw_chk(&args);
m = args.m;
if (m != NULL) {
@@ -369,13 +372,12 @@ ipfw_check_frame(void *arg, struct mbuf **m0, struct ifnet *dst, int dir,
case IP_FW_DUMMYNET:
ret = EACCES;
- int dir;
if (ip_dn_io_ptr == NULL)
break; /* i.e. drop */
*m0 = NULL;
- dir = PROTO_LAYER2 | (dst ? DIR_OUT : DIR_IN);
+ dir = PROTO_LAYER2 | dir;
ip_dn_io_ptr(&m, dir, &args);
return 0;
@@ -499,7 +501,11 @@ ipfw_hook(int onoff, int pf)
hook_func = (pf == AF_LINK) ? ipfw_check_frame : ipfw_check_packet;
- (void) (onoff ? pfil_add_hook : pfil_remove_hook)
+ if (onoff)
+ (void) pfil_add_named_hook
+ (hook_func, NULL, "ipfw", PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
+ else
+ (void) pfil_remove_hook
(hook_func, NULL, PFIL_IN | PFIL_OUT | PFIL_WAITOK, pfh);
return 0;
diff --git a/sys/netpfil/ipfw/ip_fw_private.h b/sys/netpfil/ipfw/ip_fw_private.h
index e4a2f31..4f4cf93 100644
--- a/sys/netpfil/ipfw/ip_fw_private.h
+++ b/sys/netpfil/ipfw/ip_fw_private.h
@@ -101,6 +101,7 @@ struct ip_fw_args {
struct ipfw_flow_id f_id; /* grabbed from IP header */
//uint32_t cookie; /* a cookie depending on rule action */
+ uint32_t dir; /* direction */
struct inpcb *inp;
struct _ip6dn_args dummypar; /* dummynet->ip6_output */
@@ -170,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().
@@ -178,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 *,
@@ -189,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);
@@ -200,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)
@@ -236,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 */
@@ -303,16 +332,21 @@ int ipfw_chk(struct ip_fw_args *args);
void ipfw_reap_rules(struct ip_fw *head);
/* In ip_fw_table.c */
+struct ether_addr;
struct radix_node;
-int ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint32_t *val);
-int ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
- uint32_t *val, int type);
+void *ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
+ uint32_t *val, struct ether_addr *);
+void *ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
+ uint32_t *val, int type, struct ether_addr *);
+void ipfw_count_table_entry_stats(void *, int);
+void ipfw_count_table_xentry_stats(void *, int);
+int ipfw_zero_table_xentry_stats(struct ip_fw_chain *, ipfw_table_xentry *);
+int ipfw_lookup_table_xentry(struct ip_fw_chain *, ipfw_table_xentry *);
int ipfw_init_tables(struct ip_fw_chain *ch);
void ipfw_destroy_tables(struct ip_fw_chain *ch);
int ipfw_flush_table(struct ip_fw_chain *ch, uint16_t tbl);
int ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
- uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value);
+ uint8_t plen, uint8_t mlen, uint8_t type, u_int64_t mac_addr, uint32_t value);
int ipfw_del_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
uint8_t plen, uint8_t mlen, uint8_t type);
int ipfw_count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt);
@@ -326,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 3c342f7..2776e4c 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -736,8 +736,8 @@ check_ipfw_struct(struct ip_fw *rule, int size)
if (!IPFW_NAT_LOADED)
return EINVAL;
if (cmdlen != F_INSN_SIZE(ipfw_insn_nat))
- goto bad_size;
- goto check_action;
+ goto bad_size;
+ goto check_action;
case O_FORWARD_MAC: /* XXX not implemented yet */
case O_CHECK_STATE:
case O_COUNT:
@@ -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
@@ -1124,7 +1355,7 @@ ipfw_ctl(struct sockopt *sopt)
break;
error = ipfw_add_table_entry(chain, ent.tbl,
&ent.addr, sizeof(ent.addr), ent.masklen,
- IPFW_TABLE_CIDR, ent.value);
+ IPFW_TABLE_CIDR, ent.mac_addr, ent.value);
}
break;
@@ -1157,12 +1388,12 @@ ipfw_ctl(struct sockopt *sopt)
error = EINVAL;
break;
}
-
+
len = xent->len - offsetof(ipfw_table_xentry, k);
error = (opt == IP_FW_TABLE_XADD) ?
ipfw_add_table_entry(chain, xent->tbl, &xent->k,
- len, xent->masklen, xent->type, xent->value) :
+ len, xent->masklen, xent->type, xent->mac_addr, xent->value) :
ipfw_del_table_entry(chain, xent->tbl, &xent->k,
len, xent->masklen, xent->type);
}
@@ -1245,6 +1476,54 @@ ipfw_ctl(struct sockopt *sopt)
}
break;
+ case IP_FW_TABLE_XZEROENTRY: /* IP_FW3 */
+ {
+ ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1);
+
+ /* Check minimum header size */
+ if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Check if len field is valid */
+ if (xent->len > sizeof(ipfw_table_xentry)) {
+ error = EINVAL;
+ break;
+ }
+
+ error = ipfw_zero_table_xentry_stats(chain, xent);
+ if (!error) {
+ xent->timestamp += boottime.tv_sec;
+ error = sooptcopyout(sopt, xent, sizeof(*xent));
+ }
+ }
+ break;
+
+ case IP_FW_TABLE_XLISTENTRY: /* IP_FW3 */
+ {
+ ipfw_table_xentry *xent = (ipfw_table_xentry *)(op3 + 1);
+
+ /* Check minimum header size */
+ if (IP_FW3_OPLENGTH(sopt) < offsetof(ipfw_table_xentry, k)) {
+ error = EINVAL;
+ break;
+ }
+
+ /* Check if len field is valid */
+ if (xent->len > sizeof(ipfw_table_xentry)) {
+ error = EINVAL;
+ break;
+ }
+
+ error = ipfw_lookup_table_xentry(chain, xent);
+ if (!error) {
+ xent->timestamp += boottime.tv_sec;
+ error = sooptcopyout(sopt, xent, sizeof(*xent));
+ }
+ }
+ break;
+
case IP_FW_TABLE_XLIST: /* IP_FW3 */
{
ipfw_xtable *tbl;
@@ -1284,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");
@@ -1294,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");
@@ -1304,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");
@@ -1314,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");
@@ -1331,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))
diff --git a/sys/netpfil/ipfw/ip_fw_table.c b/sys/netpfil/ipfw/ip_fw_table.c
index 760a10c..689596f 100644
--- a/sys/netpfil/ipfw/ip_fw_table.c
+++ b/sys/netpfil/ipfw/ip_fw_table.c
@@ -59,6 +59,7 @@ __FBSDID("$FreeBSD$");
#include <net/route.h>
#include <net/vnet.h>
+#include <net/ethernet.h>
#include <netinet/in.h>
#include <netinet/ip_var.h> /* struct ipfw_rule_ref */
#include <netinet/ip_fw.h>
@@ -74,7 +75,11 @@ static MALLOC_DEFINE(M_IPFW_TBL, "ipfw_tbl", "IpFw tables");
struct table_entry {
struct radix_node rn[2];
struct sockaddr_in addr, mask;
+ u_int64_t mac_addr;
u_int32_t value;
+ u_int32_t timestamp;
+ u_int64_t bytes;
+ u_int64_t packets;
};
struct xaddr_iface {
@@ -97,7 +102,11 @@ struct table_xentry {
#endif
struct xaddr_iface ifmask;
} m;
+ u_int64_t mac_addr;
u_int32_t value;
+ u_int32_t timestamp;
+ u_int64_t bytes;
+ u_int64_t packets;
};
/*
@@ -137,7 +146,7 @@ ipv6_writemask(struct in6_addr *addr6, uint8_t mask)
int
ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
- uint8_t plen, uint8_t mlen, uint8_t type, uint32_t value)
+ uint8_t plen, uint8_t mlen, uint8_t type, u_int64_t mac_addr, uint32_t value)
{
struct radix_node_head *rnh, **rnh_ptr;
struct table_entry *ent;
@@ -161,6 +170,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
return (EINVAL);
ent = malloc(sizeof(*ent), M_IPFW_TBL, M_WAITOK | M_ZERO);
ent->value = value;
+ ent->mac_addr = mac_addr;
/* Set 'total' structure length */
KEY_LEN(ent->addr) = KEY_LEN_INET;
KEY_LEN(ent->mask) = KEY_LEN_INET;
@@ -182,6 +192,7 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
return (EINVAL);
xent = malloc(sizeof(*xent), M_IPFW_TBL, M_WAITOK | M_ZERO);
xent->value = value;
+ xent->mac_addr = mac_addr;
/* Set 'total' structure length */
KEY_LEN(xent->a.addr6) = KEY_LEN_INET6;
KEY_LEN(xent->m.mask6) = KEY_LEN_INET6;
@@ -281,6 +292,28 @@ ipfw_add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
IPFW_WUNLOCK(ch);
if (rn == NULL) {
+ if (type == IPFW_TABLE_CIDR) {
+ /* Just update if any new value needed */
+ if (plen == sizeof(in_addr_t)) {
+ ent = (struct table_entry *)(rnh->rnh_matchaddr(addr_ptr, rnh));
+ if (ent != NULL) {
+ if (ent->mac_addr) {
+ if (!bcmp(&mac_addr, &ent->mac_addr, ETHER_ADDR_LEN))
+ ent->value = value;
+ } else
+ ent->value = value;
+ }
+ } else {
+ xent = (struct table_xentry *)(rnh->rnh_matchaddr(addr_ptr, rnh));
+ if (xent != NULL) {
+ if (xent->mac_addr) {
+ if (!bcmp(&mac_addr, &xent->mac_addr, ETHER_ADDR_LEN))
+ xent->value = value;
+ } else
+ xent->value = value;
+ }
+ }
+ }
free(ent_ptr, M_IPFW_TBL);
return (EEXIST);
}
@@ -530,31 +563,194 @@ ipfw_resize_tables(struct ip_fw_chain *ch, unsigned int ntables)
return (0);
}
-int
+void *
ipfw_lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint32_t *val)
+ uint32_t *val, struct ether_addr *ea)
{
struct radix_node_head *rnh;
struct table_entry *ent;
struct sockaddr_in sa;
if (tbl >= V_fw_tables_max)
- return (0);
+ return (NULL);
if ((rnh = ch->tables[tbl]) == NULL)
- return (0);
+ return (NULL);
KEY_LEN(sa) = KEY_LEN_INET;
sa.sin_addr.s_addr = addr;
ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
if (ent != NULL) {
- *val = ent->value;
- return (1);
+ if (ea && ent->mac_addr) {
+ if (bcmp((u_char *)&ent->mac_addr, ea->octet, ETHER_ADDR_LEN) != 0)
+ ent = NULL;
+ }
+ if (ent != NULL) {
+ *val = ent->value;
+ return (ent);
+ }
}
- return (0);
+ return (NULL);
+}
+
+void
+ipfw_count_table_entry_stats(void *arg, int pktlen)
+{
+ struct table_entry *xent = arg;
+
+ xent->packets++;
+ xent->bytes += pktlen;
+ xent->timestamp = time_uptime;
+}
+
+void
+ipfw_count_table_xentry_stats(void *arg, int pktlen)
+{
+ ipfw_table_xentry *xent= arg;
+
+ xent->packets++;
+ xent->bytes += pktlen;
+ xent->timestamp = time_uptime;
}
int
+ipfw_zero_table_xentry_stats(struct ip_fw_chain *ch, ipfw_table_xentry *arg)
+{
+ struct radix_node_head *rnh;
+ struct table_xentry *xent;
+ struct sockaddr_in6 sa6;
+ struct xaddr_iface iface;
+
+ if (arg->tbl >= V_fw_tables_max)
+ return (EINVAL);
+ if (ch->tables[arg->tbl] != NULL)
+ rnh = ch->tables[arg->tbl];
+ else if (ch->xtables[arg->tbl] != NULL)
+ rnh = ch->xtables[arg->tbl];
+ else
+ return (EINVAL);
+
+ switch (arg->type) {
+ case IPFW_TABLE_CIDR:
+ if (ch->tables[arg->tbl] != NULL) {
+ /* XXX: Maybe better by FreeBSD 11!! */
+ struct sockaddr_in sa;
+ struct table_entry *ent;
+
+ KEY_LEN(sa) = KEY_LEN_INET;
+ sa.sin_addr.s_addr = *((in_addr_t *)&arg->k.addr6);
+ ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
+ if (ent == NULL)
+ return (EINVAL);
+
+ arg->bytes = 0;
+ arg->packets = 0;
+ arg->value = ent->value;
+ arg->timestamp = time_uptime;
+
+ return (0);
+ } else {
+ KEY_LEN(sa6) = KEY_LEN_INET6;
+ memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr));
+ xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
+ }
+ break;
+
+ case IPFW_TABLE_INTERFACE:
+ KEY_LEN(iface) = KEY_LEN_IFACE +
+ strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1;
+ /* Assume direct match */
+ /* FIXME: Add interface pattern matching */
+ xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh));
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ if (xent != NULL) {
+ xent->bytes = 0;
+ xent->packets = 0;
+ xent->timestamp = time_uptime;
+
+ return (0);
+ }
+ return (EINVAL);
+}
+
+int
+ipfw_lookup_table_xentry(struct ip_fw_chain *ch, ipfw_table_xentry *arg)
+{
+ struct radix_node_head *rnh;
+ struct table_xentry *xent;
+
+ if (arg->tbl >= V_fw_tables_max)
+ return (EINVAL);
+ if (ch->tables[arg->tbl] != NULL)
+ rnh = ch->tables[arg->tbl];
+ else if (ch->xtables[arg->tbl] != NULL)
+ rnh = ch->xtables[arg->tbl];
+ else
+ return (EINVAL);
+
+ switch (arg->type) {
+ case IPFW_TABLE_CIDR:
+ {
+ if (ch->tables[arg->tbl] != NULL) {
+ /* XXX: Maybe better by FreeBSD 11!! */
+ struct sockaddr_in sa;
+ struct table_entry *ent;
+
+ KEY_LEN(sa) = KEY_LEN_INET;
+ sa.sin_addr.s_addr = *((in_addr_t *)&arg->k.addr6);
+ ent = (struct table_entry *)(rnh->rnh_matchaddr(&sa, rnh));
+ if (ent == NULL)
+ return (EINVAL);
+
+ arg->bytes = ent->bytes;
+ arg->packets = ent->packets;
+ arg->value = ent->value;
+ arg->timestamp = ent->timestamp;
+ arg->mac_addr = ent->mac_addr;
+ return (0);
+ } else {
+ struct sockaddr_in6 sa6;
+ KEY_LEN(sa6) = KEY_LEN_INET6;
+ memcpy(&sa6.sin6_addr, &arg->k.addr6, sizeof(struct in6_addr));
+ xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
+ }
+ }
+ break;
+
+ case IPFW_TABLE_INTERFACE:
+ {
+ struct xaddr_iface iface;
+
+ KEY_LEN(iface) = KEY_LEN_IFACE +
+ strlcpy(iface.ifname, arg->k.iface, IF_NAMESIZE) + 1;
+ /* Assume direct match */
+ /* FIXME: Add interface pattern matching */
+ xent = (struct table_xentry *)(rnh->rnh_lookup(&iface, NULL, rnh));
+ }
+ break;
+
+ default:
+ return (0);
+ }
+
+ if (xent != NULL) {
+ arg->bytes = xent->bytes;
+ arg->packets = xent->packets;
+ arg->value = xent->value;
+ arg->timestamp = xent->timestamp;
+ arg->mac_addr = xent->mac_addr;
+
+ return (0);
+ }
+ return (EINVAL);
+}
+
+void *
ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
- uint32_t *val, int type)
+ uint32_t *val, int type, struct ether_addr *ea)
{
struct radix_node_head *rnh;
struct table_xentry *xent;
@@ -562,15 +758,21 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
struct xaddr_iface iface;
if (tbl >= V_fw_tables_max)
- return (0);
+ return (NULL);
if ((rnh = ch->xtables[tbl]) == NULL)
- return (0);
+ return (NULL);
switch (type) {
case IPFW_TABLE_CIDR:
KEY_LEN(sa6) = KEY_LEN_INET6;
memcpy(&sa6.sin6_addr, paddr, sizeof(struct in6_addr));
xent = (struct table_xentry *)(rnh->rnh_matchaddr(&sa6, rnh));
+ if (xent != NULL) {
+ if (ea && xent->mac_addr) {
+ if (bcmp((u_char *)&xent->mac_addr, ea->octet, ETHER_ADDR_LEN) != 0)
+ xent = NULL;
+ }
+ }
break;
case IPFW_TABLE_INTERFACE:
@@ -582,14 +784,14 @@ ipfw_lookup_table_extended(struct ip_fw_chain *ch, uint16_t tbl, void *paddr,
break;
default:
- return (0);
+ return (NULL);
}
if (xent != NULL) {
*val = xent->value;
- return (1);
+ return (xent);
}
- return (0);
+ return (NULL);
}
static int
@@ -696,9 +898,13 @@ dump_table_xentry_base(struct radix_node *rn, void *arg)
else
xent->masklen = 33 - ffs(ntohl(n->mask.sin_addr.s_addr));
/* Save IPv4 address as deprecated IPv6 compatible */
- xent->k.addr6.s6_addr32[3] = n->addr.sin_addr.s_addr;
+ xent->k.addr6.s6_addr32[0] = n->addr.sin_addr.s_addr;
xent->flags = IPFW_TCF_INET;
xent->value = n->value;
+ xent->bytes = n->bytes;
+ xent->packets = n->packets;
+ xent->timestamp = n->timestamp;
+ xent->mac_addr = n->mac_addr;
tbl->cnt++;
return (0);
}
@@ -742,6 +948,10 @@ dump_table_xentry_extended(struct radix_node *rn, void *arg)
}
xent->value = n->value;
+ xent->bytes = n->bytes;
+ xent->packets = n->packets;
+ xent->timestamp = n->timestamp;
+ xent->mac_addr = n->mac_addr;
tbl->cnt++;
return (0);
}
OpenPOWER on IntegriCloud