#include <machine/in_cksum.h> /* XXX for in_cksum */
+#ifdef IPFW_HASHTABLES
+#include "hashtable.h"
+#endif
+
#ifdef MAC
#include <security/mac/mac_framework.h>
#endif
#endif
static uma_zone_t ipfw_dyn_rule_zone;
-struct ip_fw *ip_fw_default_rule;
-
-/*
- * Data structure to cache our ucred related
- * information. This structure only gets used if
- * the user specified UID/GID based constraints in
- * a firewall rule.
- */
-struct ip_fw_ugid {
- gid_t fw_groups[NGROUPS];
- int fw_ngroups;
- uid_t fw_uid;
- int fw_prid;
-};
-
/*
* list of rules for layer 3
*/
SYSCTL_VNET_INT(_net_inet_ip_fw, OID_AUTO, verbose_limit,
CTLFLAG_RW, &VNET_NAME(verbose_limit), 0,
"Set upper limit of matches of ipfw rules logged");
+static unsigned int dummy_default_rule = IPFW_DEFAULT_RULE;
SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, default_rule, CTLFLAG_RD,
- NULL, IPFW_DEFAULT_RULE,
+ &dummy_default_rule, IPFW_DEFAULT_RULE,
"The default/max possible rule number.");
+static unsigned int dummy_tables_max = IPFW_TABLES_MAX;
SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, tables_max, CTLFLAG_RD,
- NULL, IPFW_TABLES_MAX,
+ &dummy_tables_max, IPFW_TABLES_MAX,
"The maximum number of tables.");
+static unsigned int skipto_entries = 256;
+SYSCTL_UINT(_net_inet_ip_fw, OID_AUTO, skipto_entries,
+ CTLFLAG_RW, &skipto_entries, 0,
+ "Number of entries in the skipto cache");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
&default_to_accept, 0,
"Make the default rule accept all packets.");
#endif /* SYSCTL_NODE */
-#ifndef IPFW_NEWTABLES_MAX
-#define IPFW_NEWTABLES_MAX 256
-#endif
/*
* Description of dynamic rules.
*
args->m = NULL;
}
+static void
+set_skipto_table(struct ip_fw_chain *ch)
+{
+ int i, n, sh;
+ struct ip_fw *f, **t, **oldt;
+
+ for (sh = 15; sh > 0; sh--)
+ if (skipto_entries > 1<<sh)
+ break;
+ sh++;
+ skipto_entries = 1<< (16 - sh) ;
+ /* XXX unsafe and too long */
+ t = malloc(skipto_entries * sizeof(*t), M_IPFW_TBL, M_WAITOK | M_ZERO);
+ if (t == NULL)
+ return;
+ IPFW_RLOCK(ch);
+ /* Store pointers in the table. In the loop i is the next
+ * free slot, n is the slot where the current rule goes.
+ */
+ for (i = 0, f = ch->rules; f; f = f->next) {
+ n = f->rulenum >> sh ;
+ while (i <= n)
+ t[i++] = f;
+ }
+ V_layer3_chain.skipto_shift = sh;
+ V_layer3_chain.skipto_size = skipto_entries;
+ oldt = V_layer3_chain.skipto_ptrs;
+ V_layer3_chain.skipto_ptrs = t;
+ IPFW_RUNLOCK(ch);
+ if (oldt) {
+ IPFW_WLOCK(ch);
+ IPFW_WUNLOCK(ch);
+ /* now can free oldt */
+ free(oldt, M_IPFW_TBL);
+ }
+}
+#if 0
+/*
+ * Map a rule number to a rule pointer, using the skipto table.
+ * First lookup the slot, then follow the chain until we find a
+ * non-null entry with rulenum >= num. Return default_rule on error.
+ */
+static struct ip_fw *
+rule2ptr(struct ip_fw_chain *ch, int num)
+{
+ struct ip_fw *r = NULL;
+ int ix = (num & 0xffff) >> ch->skipto_shift;
+
+ while (ix < ch->skipto_size && (r = ch->skipto_ptrs[ix]) == NULL)
+ ix++;
+ while (r && num < r->rulenum)
+ r = r->next;
+ return (r ? r : ch->default_rule);
+}
+#endif
/**
*
* Given an ip_fw *, lookup_next_rule will return a pointer
*/
static struct ip_fw *
-lookup_next_rule(struct ip_fw *me, u_int32_t tablearg)
+lookup_next_rule(struct ip_fw_chain *ch, struct ip_fw *me, uint32_t tablearg)
{
struct ip_fw *rule = NULL;
ipfw_insn *cmd;
- u_int16_t rulenum;
-printf("%s called\n", __FUNCTION__);
+
/* look for action, in case it is a skipto */
cmd = ACTION_PTR(me);
if (cmd->opcode == O_LOG)
cmd += F_LEN(cmd);
if (cmd->opcode == O_TAG)
cmd += F_LEN(cmd);
- if (cmd->opcode == O_SKIPTO ) {
- if (tablearg != 0) {
- rulenum = (u_int16_t)tablearg;
- } else {
- rulenum = cmd->arg1;
- }
+ if (cmd->opcode != O_SKIPTO ) {
+ rule = me->next;
+ } else {
+ tablearg = tablearg ? tablearg : cmd->arg1;
for (rule = me->next; rule ; rule = rule->next) {
- if (rule->rulenum >= rulenum) {
+ if (rule->rulenum >= tablearg) {
break;
}
}
- }
- if (rule == NULL) /* failure or not a skipto */
- rule = me->next;
- me->next_rule = rule;
- return rule;
-}
-
-#ifdef IPFW_HAVE_SKIPTO_TABLE
-struct ip_fw *lookup_skipto_table(struct ip_fw_chain *chain, uint16_t num);
-struct ip_fw *
-lookup_skipto_table(struct ip_fw_chain *chain, uint16_t num)
-{
- struct ip_fw *f;
-
- printf("--%s called\n", __FUNCTION__);
- if (1)
- return NULL;
- if (chain->skipto_pointers[num].id == chain->id) {
- printf("-- %s pointer ok, return it\n", __FUNCTION__);
- return chain->skipto_pointers[num].rule;
+// rule = rule2ptr(ch, tablearg ? tablearg : cmd->arg1);
}
- printf("-- %s search pointer\n", __FUNCTION__);
-
- for (f = chain->rules; f ; f = f->next) {
- if (f->rulenum == num) {
- chain->skipto_pointers[num].id = chain->id;
- chain->skipto_pointers[num].rule = f;
- printf("-- %s found, set and return\n", __FUNCTION__);
- return f;
- }
- }
- printf("-- %s NOT found return NULL\n", __FUNCTION__);
-
- return NULL;
+ me->next_rule = rule; /* XXX perhaps unnecessary ? */
+ return rule;
}
-#endif /* IPFW_HAVE_SKIPTO_TABLE */
-#ifdef radix
static int
add_table_entry(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
uint8_t mlen, uint32_t value)
struct table_entry *ent;
struct radix_node *rn;
+#ifdef IPFW_HASHTABLES
+ if (tbl >= 2*IPFW_TABLES_MAX)
+ return EINVAL;
+ return EINVAL; // XXX to be completed
+#endif
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
rnh = ch->tables[tbl];
if (ent == NULL)
return (ENOMEM);
ent->value = value;
+#ifdef linux
+ /* there is no sin_len on linux, and the code assumes the first
+ * byte in the sockaddr to contain the length in bits.
+ * So we just dump the number right there
+ */
+ *((uint8_t *)&(ent->addr)) = 8;
+ *((uint8_t *)&(ent->mask)) = 8;
+#else
ent->addr.sin_len = ent->mask.sin_len = 8;
+#endif
ent->mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
ent->addr.sin_addr.s_addr = addr & ent->mask.sin_addr.s_addr;
IPFW_WLOCK(ch);
struct table_entry *ent;
struct sockaddr_in sa, mask;
+#ifdef IPFW_HASHTABLES
+ if (tbl >= 2*IPFW_TABLES_MAX)
+ return EINVAL;
+ return EINVAL; // XXX to be completed
+#endif
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
rnh = ch->tables[tbl];
+#ifdef linux
+ /* there is no sin_len on linux, see above */
+ *((uint8_t *)&sa) = 8;
+ *((uint8_t *)&mask) = 8;
+#else
sa.sin_len = mask.sin_len = 8;
+#endif
mask.sin_addr.s_addr = htonl(mlen ? ~((1 << (32 - mlen)) - 1) : 0);
sa.sin_addr.s_addr = addr & mask.sin_addr.s_addr;
IPFW_WLOCK(ch);
IPFW_WLOCK_ASSERT(ch);
+#ifdef IPFW_HASHTABLES
+ if (tbl >= 2*IPFW_TABLES_MAX)
+ return EINVAL;
+ return EINVAL; // XXX to be completed
+#endif
if (tbl >= IPFW_TABLES_MAX)
return (EINVAL);
rnh = ch->tables[tbl];
rnh->rnh_walktree(rnh, flush_table_entry, rnh);
return (0);
}
-#else
-extern int add_table_entry(struct ip_fw_chain *ch, uint16_t tbl,
- in_addr_t addr, uint8_t mlen, uint32_t value);
-extern int del_table_entry(struct ip_fw_chain *ch, uint16_t tbl,
- in_addr_t addr, uint8_t mlen);
-extern int flush_table(struct ip_fw_chain *ch, uint16_t tbl);
-extern int count_table(struct ip_fw_chain *ch, uint32_t tbl, uint32_t *cnt);
-extern int dump_table(struct ip_fw_chain *ch, ipfw_table *tbl);
-extern int lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
- uint32_t *val);
-extern int init_tables(struct ip_fw_chain *ch);
-#endif
static void
flush_tables(struct ip_fw_chain *ch)
IPFW_WLOCK_ASSERT(ch);
- for (tbl = IPFW_TABLES_MAX -1; tbl < IPFW_NEWTABLES_MAX; tbl++)
+ for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++)
flush_table(ch, tbl);
+#ifdef IPFW_HASHTABLES
+ for (tbl = 0; tbl < IPFW_TABLES_MAX; tbl++)
+ ch->hashtab[tbl] = ipfw_ht_destroy(ch->hashtab[tbl]);
+#endif
}
-#ifdef radix
static int
init_tables(struct ip_fw_chain *ch)
{
return (ENOMEM);
}
}
+#ifdef IPFW_HASHTABLES
+ for (i = 0; i < IPFW_TABLES_MAX; i++)
+ ch->hashtab[i] = ipfw_ht_destroy(ch->hashtab[i]);
+#endif
return (0);
}
if (tbl >= IPFW_TABLES_MAX)
return (0);
rnh = ch->tables[tbl];
+#ifdef linux
+ /* there is no sin_len on linux, see above */
+ *((uint8_t *)&sa) = 8;
+#else
sa.sin_len = 8;
+#endif
sa.sin_addr.s_addr = addr;
ent = (struct table_entry *)(rnh->rnh_lookup(&sa, NULL, rnh));
if (ent != NULL) {
}
return (0);
}
-#endif
-#ifdef radix
static int
count_table_entry(struct radix_node *rn, void *arg)
{
rnh->rnh_walktree(rnh, dump_table_entry, tbl);
return (0);
}
-#endif
-
-#ifndef linux /* FreeBSD */
-static void
-fill_ugid_cache(struct inpcb *inp, struct ip_fw_ugid *ugp)
-{
- struct ucred *cr;
-
- cr = inp->inp_cred;
- ugp->fw_prid = jailed(cr) ? cr->cr_prison->pr_id : -1;
- ugp->fw_uid = cr->cr_uid;
- ugp->fw_ngroups = cr->cr_ngroups;
- bcopy(cr->cr_groups, ugp->fw_groups, sizeof(ugp->fw_groups));
-}
-#endif
static int
check_uidgid(ipfw_insn_u32 *insn, int proto, struct ifnet *oif,
struct in_addr dst_ip, u_int16_t dst_port, struct in_addr src_ip,
- u_int16_t src_port, struct ip_fw_ugid *ugp, int *ugid_lookupp,
+ u_int16_t src_port, struct ucred **uc, int *ugid_lookup,
struct inpcb *inp)
{
#ifdef linux
int match = 0;
struct sk_buff *skb = ((struct mbuf *)inp)->m_skb;
+ struct bsd_ucred *u = (struct bsd_ucred *)uc;
- if (*ugid_lookupp == 0) { /* actively lookup and copy in cache */
-
+ if (*ugid_lookup == 0) { /* actively lookup and copy in cache */
/* returns null if any element of the chain up to file is null.
* if sk != NULL then we also have a reference
*/
- *ugid_lookupp = linux_lookup(proto,
+ *ugid_lookup = linux_lookup(proto,
src_ip.s_addr, htons(src_port),
dst_ip.s_addr, htons(dst_port),
- skb, oif ? 1 : 0, ugp);
+ skb, oif ? 1 : 0, u);
}
- if (*ugid_lookupp < 0)
+ if (*ugid_lookup < 0)
return 0;
if (insn->o.opcode == O_UID)
- match = (ugp->fw_uid == (uid_t)insn->d[0]);
+ match = (u->uid == (uid_t)insn->d[0]);
else if (insn->o.opcode == O_JAIL)
- match = (ugp->fw_groups[1] == (uid_t)insn->d[0]);
+ match = (u->xid == (uid_t)insn->d[0]);
else if (insn->o.opcode == O_GID)
- match = (ugp->fw_groups[0] == (uid_t)insn->d[0]);
+ match = (u->gid == (uid_t)insn->d[0]);
return match;
int wildcard;
struct inpcb *pcb;
int match;
- gid_t *gp;
/*
* Check to see if the UDP or TCP stack supplied us with
if (inp && *ugid_lookupp == 0) {
INP_LOCK_ASSERT(inp);
if (inp->inp_socket != NULL) {
- fill_ugid_cache(inp, ugp);
+ *uc = crhold(inp->inp_cred);
*ugid_lookupp = 1;
} else
*ugid_lookupp = -1;
dst_ip, htons(dst_port),
wildcard, NULL);
if (pcb != NULL) {
- fill_ugid_cache(pcb, ugp);
+ *uc = crhold(pcb->inp_cred);
*ugid_lookupp = 1;
}
INP_INFO_RUNLOCK(pi);
}
}
if (insn->o.opcode == O_UID)
- match = (ugp->fw_uid == (uid_t)insn->d[0]);
- else if (insn->o.opcode == O_GID) {
- for (gp = ugp->fw_groups;
- gp < &ugp->fw_groups[ugp->fw_ngroups]; gp++)
- if (*gp == (gid_t)insn->d[0]) {
- match = 1;
- break;
- }
- } else if (insn->o.opcode == O_JAIL)
- match = (ugp->fw_prid == (int)insn->d[0]);
+ match = ((*uc)->cr_uid == (uid_t)insn->d[0]);
+ else if (insn->o.opcode == O_GID)
+ match = groupmember((gid_t)insn->d[0], *uc);
+ else if (insn->o.opcode == O_JAIL)
+ match = ((*uc)->cr_prison->pr_id == (int)insn->d[0]);
return match;
#endif
}
* these types of constraints, as well as decrease contention
* on pcb related locks.
*/
- struct ip_fw_ugid fw_ugid_cache;
- int ugid_lookup = 0;
+ struct bsd_ucred ucred_cache;
+ int ucred_lookup = 0;
/*
* divinput_flags If non-zero, set to the IP_FW_DIVERT_*_FLAG
IPFW_RUNLOCK(chain);
return (IP_FW_PASS);
}
+ if (chain->id != args->chain_id) {
+ for (f = chain->rules; f != NULL; f = f->next)
+ if (f == args->rule && f->id == args->rule_id)
+ break;
- f = args->rule->next_rule;
+ if (f != NULL)
+ f = f->next_rule;
+ else
+ f = chain->default_rule;
+ } else
+ f = args->rule->next_rule;
if (f == NULL)
- f = lookup_next_rule(args->rule, 0);
+ f = lookup_next_rule(chain, args->rule, 0);
} else {
/*
* Find the starting rule. It can be either the first
IPFW_RUNLOCK(chain);
return (IP_FW_DENY); /* invalid */
}
+// f = rule2ptr(chain, skipto+1);
while (f && f->rulenum <= skipto)
f = f->next;
- if (f == NULL) { /* drop packet */
- IPFW_RUNLOCK(chain);
- return (IP_FW_DENY);
- }
}
}
/* reset divert rule to avoid confusion later */
(ipfw_insn_u32 *)cmd,
proto, oif,
dst_ip, dst_port,
- src_ip, src_port, &fw_ugid_cache,
- &ugid_lookup, (struct inpcb *)args->m);
+ src_ip, src_port, (struct ucred **)&ucred_cache,
+ &ucred_lookup, (struct inpcb *)args->m);
break;
case O_RECV:
(ipfw_insn_u32 *)cmd,
proto, oif,
dst_ip, dst_port,
- src_ip, src_port, &fw_ugid_cache,
- &ugid_lookup, (struct inpcb *)args->m);
+ src_ip, src_port, (struct ucred **)&ucred_cache,
+ &ucred_lookup, (struct inpcb *)args->m);
+#ifdef linux
+ if (v ==4 /* O_UID */)
+ a = ucred_cache.uid;
+ else if (v == 5 /* O_GID */)
+ a = ucred_cache.gid;
+ else if (v == 6 /* O_JAIL */)
+ a = ucred_cache.xid;
+#else
if (v ==4 /* O_UID */)
- a = fw_ugid_cache.fw_uid;
+ a = (*uc)->cr_uid;
else if (v == 5 /* O_GID */)
- a = fw_ugid_cache.fw_groups[0];
+ ; // a = groupmember((gid_t)insn->d[0], *uc);
else if (v == 6 /* O_JAIL */)
- a = fw_ugid_cache.fw_groups[1];
+ a = (*uc)->cr_prison->pr_id;
+#endif
} else
break;
}
break;
}
/* handle skipto */
-#ifdef IPFW_HAVE_SKIPTO_TABLE
- /* NOTE: lookup_skipto_table can return NULL
- * if the rule isn't found, so the
- * standard lookup function must be
- * called XXX
- */
- if (cmd->arg1 == IP_FW_TABLEARG) {
- f = lookup_skipto_table(chain,
- tablearg);
- if (f == NULL)
- f = lookup_next_rule(f, tablearg);
- }
- else {
- f = lookup_skipto_table(chain,
- cmd->arg1);
- if (f == NULL) {
- if (f->next_rule == NULL)
- lookup_next_rule(f, 0);
- f = f->next_rule;
- }
- }
-
-#else
if (cmd->arg1 == IP_FW_TABLEARG) {
- f = lookup_next_rule(f, tablearg);
+ f = lookup_next_rule(chain, f, tablearg);
} else {
if (f->next_rule == NULL)
- lookup_next_rule(f, 0);
+ lookup_next_rule(chain, f, 0);
f = f->next_rule;
}
-#endif
/*
* Skip disabled rules, and
* re-enter the inner loop
printf("ipfw: ouch!, skip past end of rules, denying packet\n");
}
IPFW_RUNLOCK(chain);
+#ifdef __FreeBSD__
+ if (ucred_cache != NULL)
+ crfree(ucred_cache);
+#endif
return (retval);
pullup_failed:
goto done;
}
- /*
- * If rulenum is 0, find highest numbered rule before the
- * default rule, and add autoinc_step
- */
if (V_autoinc_step < 1)
V_autoinc_step = 1;
else if (V_autoinc_step > 1000)
V_autoinc_step = 1000;
if (rule->rulenum == 0) {
/*
- * locate the highest numbered rule before default
+ * If rulenum is 0, use highest numbered rule before
+ * the default, adding autoinc_step if room.
+ * Also set the number in the caller.
*/
for (f = chain->rules; f; f = f->next) {
if (f->rulenum == IPFW_DEFAULT_RULE)
/*
* Now insert the new rule in the right place in the sorted list.
+ * XXX TODO also put in the skipto table.
*/
for (prev = NULL, f = chain->rules; f; prev = f, f = f->next) {
if (f->rulenum > rule->rulenum) { /* found the location */
prev->next = n;
V_static_count--;
V_static_len -= l;
+ // XXX remove from the skipto table
rule->next = chain->reap;
chain->reap = rule;
}
/*
- * Hook for cleaning up dummynet when an ipfw rule is deleted.
- * Set/cleared when dummynet module is loaded/unloaded.
- */
-void (*ip_dn_ruledel_ptr)(void *) = NULL;
-
-/**
* Reclaim storage associated with a list of rules. This is
* typically the list created using remove_rule.
* A NULL pointer on input is handled correctly.
while ((rule = head) != NULL) {
head = head->next;
- if (ip_dn_ruledel_ptr)
- ip_dn_ruledel_ptr(rule);
free(rule, M_IPFW);
}
}
IPFW_WLOCK_ASSERT(chain);
+ chain->reap = NULL;
flush_rule_ptrs(chain); /* more efficient to do outside the loop */
for (prev = NULL, rule = chain->rules; rule ; )
if (kill_default || rule->set != RESVD_SET)
* avoid a LOR with dummynet.
*/
rule = chain->reap;
- chain->reap = NULL;
IPFW_WUNLOCK(chain);
- if (rule)
- reap_rules(rule);
+ reap_rules(rule);
return 0;
}
int i;
time_t boot_seconds;
- boot_seconds = boottime.tv_sec;
+ boot_seconds = boottime.tv_sec;
/* XXX this can take a long time and locking will block packet flow */
IPFW_RLOCK(chain);
for (rule = chain->rules; rule ; rule = rule->next) {
if (last != NULL) /* mark last dynamic rule */
bzero(&last->next, sizeof(last));
}
-
return (bp - (char *)buf);
}
*/
IPFW_WLOCK(&V_layer3_chain);
- V_layer3_chain.reap = NULL;
free_chain(&V_layer3_chain, 0 /* keep default rule */);
rule = V_layer3_chain.reap;
- V_layer3_chain.reap = NULL;
IPFW_WUNLOCK(&V_layer3_chain);
- if (rule != NULL)
- reap_rules(rule);
+ reap_rules(rule);
break;
case IP_FW_ADD:
#undef RULE_MAXSIZE
}
-/**
- * dummynet needs a reference to the default rule, because rules can be
- * deleted while packets hold a reference to them. When this happens,
- * dummynet changes the reference to the default rule (it could well be a
- * NULL pointer, but this way we do not need to check for the special
- * case, plus here he have info on the default behaviour).
- */
-//struct ip_fw *ip_fw_default_rule;
/*
* This procedure is only used to handle keepalives. It is invoked
#endif
done:
callout_reset(&V_ipfw_timeout, V_dyn_keepalive_period*hz,
- ipfw_tick, NULL);
+ ipfw_tick, vnetx);
CURVNET_RESTORE();
}
if (error) {
panic("init_tables"); /* XXX Marko fix this ! */
}
-
-#ifdef IPFW_HAVE_SKIPTO_TABLE
-// for (error = 0; error < 64*1024; error++)
-// V_layer3_chain.skipto_pointers[error].id = -1;
-#endif /* IPFW_HAVE_SKIPTO_TABLE */
-
#ifdef IPFIREWALL_NAT
LIST_INIT(&V_layer3_chain.nat);
#endif
IPFW_LOCK_INIT(&V_layer3_chain);
callout_init(&V_ipfw_timeout, CALLOUT_MPSAFE);
+ set_skipto_table(&V_layer3_chain);
+
bzero(&default_rule, sizeof default_rule);
default_rule.act_ofs = 0;
default_rule.rulenum = IPFW_DEFAULT_RULE;
return (error);
}
- ip_fw_default_rule = V_layer3_chain.rules;
+ V_layer3_chain.default_rule = V_layer3_chain.rules;
/* curvnet is NULL in the !VIMAGE case */
callout_reset(&V_ipfw_timeout, hz, ipfw_tick, curvnet);