summaryrefslogtreecommitdiffstats
path: root/sys/netpfil/ipfw/ip_fw2.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/netpfil/ipfw/ip_fw2.c')
-rw-r--r--sys/netpfil/ipfw/ip_fw2.c280
1 files changed, 215 insertions, 65 deletions
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);
}
/*
OpenPOWER on IntegriCloud