#endif
#include "opt_inet6.h"
#include "opt_ipsec.h"
-#include "opt_mac.h"
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/condvar.h>
+#include <sys/eventhandler.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/priv.h>
#include <sys/proc.h>
+#include <sys/rwlock.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <netinet/icmp6.h>
#ifdef INET6
#include <netinet6/scope6_var.h>
+#include <netinet6/ip6_var.h>
#endif
#include <machine/in_cksum.h> /* XXX for in_cksum */
#include <security/mac/mac_framework.h>
#endif
-#ifdef linux
-//#include <linux/netdevice.h> /* XXX dev_net() is used in linux 2.6.30.5 */
-#define INP_LOCK_ASSERT /* define before missing.h otherwise ? */
-#include "missing.h"
-#define _IPV6_H /* prevent ipv6 inclusion from hashtables and udp.h */
-#include <net/sock.h> /* linux - struct sock and sock_put() */
-#endif
-
static VNET_DEFINE(int, ipfw_vnet_ready) = 0;
#define V_ipfw_vnet_ready VNET(ipfw_vnet_ready)
/*
#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");
+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.");
+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.");
SYSCTL_INT(_net_inet_ip_fw, OID_AUTO, default_to_accept, CTLFLAG_RDTUN,
&default_to_accept, 0,
}
}
}
- if (rule == NULL) /* failure or not a skipto */
+ if (rule == NULL) /* failure or not a skipto */
rule = me->next;
me->next_rule = rule;
return rule;
}
-#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)
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);
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);
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);
-#endif
static void
flush_tables(struct ip_fw_chain *ch)
static int
init_tables(struct ip_fw_chain *ch)
{
-#ifdef radix
int i;
uint16_t j;
return (ENOMEM);
}
}
-#endif
return (0);
}
lookup_table(struct ip_fw_chain *ch, uint16_t tbl, in_addr_t addr,
uint32_t *val)
{
-#ifdef radix
struct radix_node_head *rnh;
struct table_entry *ent;
struct sockaddr_in sa;
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) {
*val = ent->value;
return (1);
}
-#endif
return (0);
}
-#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);
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:
dst_ip.s_addr : src_ip.s_addr;
uint32_t v = 0;
+ if (cmdlen > F_INSN_SIZE(ipfw_insn_u32)) {
+ v = ((ipfw_insn_u32 *)cmd)->d[1];
+ if (v == 0)
+ a = dst_ip.s_addr;
+ else if (v == 1)
+ a = src_ip.s_addr;
+ else if (offset != 0)
+ break;
+ else if (proto != IPPROTO_TCP &&
+ proto != IPPROTO_UDP)
+ break;
+ else if (v == 2)
+ a = dst_port;
+ else if (v == 3)
+ a = src_port;
+ else if (v == 4 || v == 5) {
+ check_uidgid(
+ (ipfw_insn_u32 *)cmd,
+ proto, oif,
+ dst_ip, dst_port,
+ 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_JAIL */)
+ a = ucred_cache.xid;
+#else
+ if (v ==4 /* O_UID */)
+ a = (*uc)->cr_uid;
+ else if (v == 5 /* O_JAIL */)
+ a = (*uc)->cr_prison->pr_id;
+#endif
+ } else
+ break;
+ }
match = lookup_table(chain, cmd->arg1, a,
&v);
if (!match)
/* handle skipto */
if (cmd->arg1 == IP_FW_TABLEARG) {
f = lookup_next_rule(f, tablearg);
- } else {
+ } else { // XXX ?
if (f->next_rule == NULL)
lookup_next_rule(f, 0);
f = f->next_rule;
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:
}
/*
- * 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;
}
case O_IP_DST_LOOKUP:
if (cmd->arg1 >= IPFW_TABLES_MAX) {
printf("ipfw: invalid table number %d\n",
- cmd->arg1);
+ cmd->arg1);
return (EINVAL);
}
if (cmdlen != F_INSN_SIZE(ipfw_insn) &&
+ cmdlen != F_INSN_SIZE(ipfw_insn_u32) + 1 &&
cmdlen != F_INSN_SIZE(ipfw_insn_u32))
goto bad_size;
break;
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();
}
IPFW_WUNLOCK(&V_layer3_chain);
if (reap != NULL)
reap_rules(reap);
- IPFW_DYN_LOCK_DESTROY();
uma_zdestroy(ipfw_dyn_rule_zone);
+ IPFW_DYN_LOCK_DESTROY();
if (V_ipfw_dyn_v != NULL)
free(V_ipfw_dyn_v, M_IPFW);
IPFW_LOCK_DESTROY(&V_layer3_chain);
-#ifdef INET6
- /* Free IPv6 fw sysctl tree. */
- sysctl_ctx_free(&ip6_fw_sysctl_ctx);
-#endif
-
printf("IP firewall unloaded\n");
}
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);