#include "opt_ipfw.h"
#include "opt_inet.h"
#ifndef INET
#error IPFIREWALL3 requires INET.
#endif
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/socketvar.h>
#include <sys/sysctl.h>
#include <sys/systimer.h>
#include <sys/in_cksum.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ucred.h>
#include <sys/lock.h>
#include <net/if.h>
#include <net/radix.h>
#include <net/route.h>
#include <net/pfil.h>
#include <net/netmsg2.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/in_pcb.h>
#include <netinet/ip.h>
#include <netinet/ip_var.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#include <netinet/udp.h>
#include <netinet/udp_var.h>
#include <netinet/ip_divert.h>
#include <netinet/if_ether.h>
#include <net/ipfw3/ip_fw.h>
#include <net/ipfw3/ip_fw3_set.h>
#include <net/ipfw3_basic/ip_fw3_log.h>
#include <net/ipfw3_basic/ip_fw3_table.h>
#include <net/ipfw3_basic/ip_fw3_sync.h>
#include <net/ipfw3_basic/ip_fw3_basic.h>
#include <net/ipfw3_basic/ip_fw3_state.h>
#include <net/ipfw3_nat/ip_fw3_nat.h>
#include <net/dummynet3/ip_dummynet3.h>
MALLOC_DEFINE(M_IPFW3, "IPFW3", "ipfw3 module");
#define MAX_MODULE 10
#define MAX_OPCODE_PER_MODULE 100
#define IPFW_AUTOINC_STEP_MIN 1
#define IPFW_AUTOINC_STEP_MAX 1000
#define IPFW_AUTOINC_STEP_DEF 100
struct netmsg_ipfw {
struct netmsg_base base;
const struct ipfw_ioc_rule *ioc_rule;
struct ip_fw *rule;
struct ip_fw *next_rule;
struct ip_fw *prev_rule;
struct ip_fw *sibling;
};
struct netmsg_del {
struct netmsg_base base;
struct ip_fw *rule;
struct ip_fw *start_rule;
struct ip_fw *prev_rule;
struct ipfw_ioc_state *ioc_state;
uint16_t rulenum;
uint8_t set_from;
uint8_t set_to;
int kill_default;
};
struct netmsg_zent {
struct netmsg_base base;
struct ip_fw *start_rule;
uint16_t rulenum;
uint16_t log_only;
};
ip_fw_ctl_t *ip_fw3_ctl_nat_ptr = NULL;
ip_fw_ctl_t *ip_fw3_ctl_state_ptr = NULL;
ip_fw_ctl_t *ip_fw3_ctl_table_ptr = NULL;
ip_fw_ctl_t *ip_fw3_ctl_sync_ptr = NULL;
ip_fw_log_t *ip_fw3_log_ptr = NULL;
extern int ip_fw_loaded;
extern struct ipfw3_state_context *fw3_state_ctx[MAXCPU];
int sysctl_var_fw3_enable = 1;
int sysctl_var_fw3_one_pass = 1;
int sysctl_var_fw3_verbose = 0;
static int sysctl_var_fw3_flushing;
static int sysctl_var_fw3_debug;
static int sysctl_var_autoinc_step = IPFW_AUTOINC_STEP_DEF;
int ip_fw3_sysctl_enable(SYSCTL_HANDLER_ARGS);
int ip_fw3_sysctl_autoinc_step(SYSCTL_HANDLER_ARGS);
SYSCTL_NODE(_net_inet_ip, OID_AUTO, fw3, CTLFLAG_RW, 0, "Firewall");
SYSCTL_PROC(_net_inet_ip_fw3, OID_AUTO, enable, CTLTYPE_INT | CTLFLAG_RW,
&sysctl_var_fw3_enable, 0, ip_fw3_sysctl_enable, "I", "Enable ipfw");
SYSCTL_PROC(_net_inet_ip_fw3, OID_AUTO, sysctl_var_autoinc_step,
CTLTYPE_INT | CTLFLAG_RW, &sysctl_var_autoinc_step, 0,
ip_fw3_sysctl_autoinc_step, "I", "Rule number autincrement step");
SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO,one_pass,CTLFLAG_RW,
&sysctl_var_fw3_one_pass, 0,
"Only do a single pass through ipfw3 when using dummynet(4)");
SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, debug, CTLFLAG_RW,
&sysctl_var_fw3_debug, 0, "Enable printing of debug ip_fw statements");
SYSCTL_INT(_net_inet_ip_fw3, OID_AUTO, verbose, CTLFLAG_RW,
&sysctl_var_fw3_verbose, 0, "Log matches to ipfw3 rules");
filter_func filter_funcs[MAX_MODULE][MAX_OPCODE_PER_MODULE];
struct ipfw3_module fw3_modules[MAX_MODULE];
struct ipfw3_context *fw3_ctx[MAXCPU];
struct ipfw3_sync_context fw3_sync_ctx;
void
ip_fw3_register_module(int module_id,char *module_name)
{
struct ipfw3_module *tmp;
int i;
tmp = fw3_modules;
for (i=0; i < MAX_MODULE; i++) {
if (tmp->type == 0) {
tmp->type = 1;
tmp->id = module_id;
strncpy(tmp->name, module_name, strlen(module_name));
break;
}
tmp++;
}
kprintf("ipfw3 module %s loaded\n", module_name);
}
int
ip_fw3_unregister_module(int module_id)
{
struct ipfw3_module *tmp;
struct ip_fw *fw;
ipfw_insn *cmd;
int i, len, cmdlen, found;
found = 0;
tmp = fw3_modules;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
fw = ctx->rules;
for (; fw; fw = fw->next) {
for (len = fw->cmd_len, cmd = fw->cmd; len > 0;
len -= cmdlen,
cmd = (ipfw_insn *)((uint32_t *)cmd + cmdlen)) {
cmdlen = F_LEN(cmd);
if (cmd->module == 0 &&
(cmd->opcode == 0 || cmd->opcode == 1)) {
} else if (cmd->module == module_id) {
found = 1;
goto decide;
}
}
}
decide:
if (found) {
return 1;
} else {
for (i = 0; i < MAX_MODULE; i++) {
if (tmp->type == 1 && tmp->id == module_id) {
tmp->type = 0;
kprintf("ipfw3 module %s unloaded\n",
tmp->name);
break;
}
tmp++;
}
for (i = 0; i < MAX_OPCODE_PER_MODULE; i++) {
if (module_id == 0) {
if (i ==0 || i == 1) {
continue;
}
}
filter_funcs[module_id][i] = NULL;
}
return 0;
}
}
void
ip_fw3_register_filter_funcs(int module, int opcode, filter_func func)
{
filter_funcs[module][opcode] = func;
}
void
check_accept(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
{
*cmd_val = IP_FW_PASS;
*cmd_ctl = IP_FW_CTL_DONE;
if (cmd->arg3 && ip_fw3_log_ptr != NULL) {
ip_fw3_log_ptr((*args)->m, (*args)->eh, cmd->arg1);
}
}
void
check_deny(int *cmd_ctl, int *cmd_val, struct ip_fw_args **args,
struct ip_fw **f, ipfw_insn *cmd, uint16_t ip_len)
{
*cmd_val = IP_FW_DENY;
*cmd_ctl = IP_FW_CTL_DONE;
if (cmd->arg3 && ip_fw3_log_ptr != NULL) {
ip_fw3_log_ptr((*args)->m, (*args)->eh, cmd->arg1);
}
}
void
init_module(void)
{
memset(fw3_modules, 0, sizeof(struct ipfw3_module) * MAX_MODULE);
memset(filter_funcs, 0, sizeof(filter_func) *
MAX_OPCODE_PER_MODULE * MAX_MODULE);
ip_fw3_register_filter_funcs(0, O_BASIC_ACCEPT,
(filter_func)check_accept);
ip_fw3_register_filter_funcs(0, O_BASIC_DENY, (filter_func)check_deny);
}
int
ip_fw3_free_rule(struct ip_fw *rule)
{
kfree(rule, M_IPFW3);
rule = NULL;
return 1;
}
static struct ip_fw *
lookup_next_rule(struct ip_fw *me)
{
struct ip_fw *rule = NULL;
ipfw_insn *cmd;
cmd = ACTION_PTR(me);
if ((int)cmd->module == MODULE_BASIC_ID &&
(int)cmd->opcode == O_BASIC_SKIPTO) {
for (rule = me->next; rule; rule = rule->next) {
if (rule->rulenum >= cmd->arg1)
break;
}
}
if (rule == NULL) {
rule = me->next;
}
me->next_rule = rule;
return rule;
}
int
ip_fw3_chk(struct ip_fw_args *args)
{
struct tcphdr *tcp;
struct udphdr *udp;
struct icmp *icmp;
struct mbuf *m = args->m;
struct ip *ip = mtod(m, struct ip *);
struct ip_fw *f = NULL;
int cmd_val = IP_FW_PASS;
struct m_tag *mtag;
struct divert_info *divinfo;
u_int hlen = 0;
u_short offset = 0;
uint8_t proto;
uint16_t src_port = 0, dst_port = 0;
struct in_addr src_ip, dst_ip;
uint16_t ip_len = 0;
uint8_t prev_module = -1, prev_opcode = -1;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
if (m->m_pkthdr.fw_flags & IPFW_MBUF_GENERATED)
return IP_FW_PASS;
if (args->eh == NULL ||
(m->m_pkthdr.len >= sizeof(struct ip) &&
ntohs(args->eh->ether_type) == ETHERTYPE_IP))
hlen = ip->ip_hl << 2;
if (hlen == 0) {
proto = args->f_id.proto = 0;
goto after_ip_checks;
}
proto = args->f_id.proto = ip->ip_p;
src_ip = ip->ip_src;
dst_ip = ip->ip_dst;
offset = ntohs(ip->ip_off) & IP_OFFMASK;
ip_len = ntohs(ip->ip_len);
#define PULLUP_TO(len) \
do { \
if (m->m_len < (len)) { \
args->m = m = m_pullup(m, (len)); \
if (m == NULL) \
goto pullup_failed; \
ip = mtod(m, struct ip *); \
} \
} while (0)
if (offset == 0) {
switch (proto) {
case IPPROTO_TCP:
PULLUP_TO(hlen + sizeof(struct tcphdr));
tcp = L3HDR(struct tcphdr, ip);
dst_port = tcp->th_dport;
src_port = tcp->th_sport;
args->f_id.flags = tcp->th_flags;
break;
case IPPROTO_UDP:
PULLUP_TO(hlen + sizeof(struct udphdr));
udp = L3HDR(struct udphdr, ip);
dst_port = udp->uh_dport;
src_port = udp->uh_sport;
break;
case IPPROTO_ICMP:
PULLUP_TO(hlen + 4);
icmp = L3HDR(struct icmp, ip);
args->f_id.flags = icmp->icmp_type;
dst_port = icmp->icmp_id;
src_port = dst_port;
break;
default:
break;
}
}
#undef PULLUP_TO
args->f_id.src_ip = ntohl(src_ip.s_addr);
args->f_id.dst_ip = ntohl(dst_ip.s_addr);
args->f_id.src_port = src_port = ntohs(src_port);
args->f_id.dst_port = dst_port = ntohs(dst_port);
after_ip_checks:
if (args->rule) {
if (sysctl_var_fw3_one_pass)
return IP_FW_PASS;
if (sysctl_var_fw3_flushing)
return IP_FW_DENY;
f = args->rule->next_rule;
if (f == NULL)
f = lookup_next_rule(args->rule);
} else {
int skipto;
mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT, NULL);
if (mtag != NULL) {
divinfo = m_tag_data(mtag);
skipto = divinfo->skipto;
} else {
skipto = 0;
}
f = ctx->rules;
if (args->eh == NULL && skipto != 0) {
if (sysctl_var_fw3_flushing) {
return IP_FW_DENY;
}
if (skipto >= IPFW_DEFAULT_RULE) {
return IP_FW_DENY;
}
while (f && f->rulenum <= skipto) {
f = f->next;
}
if (f == NULL) {
return IP_FW_DENY;
}
} else if (sysctl_var_fw3_flushing) {
f = ctx->default_rule;
}
}
if ((mtag = m_tag_find(m, PACKET_TAG_IPFW_DIVERT, NULL)) != NULL) {
m_tag_delete(m, mtag);
}
int prev_val;
int l, cmdlen;
ipfw_insn *cmd;
int cmd_ctl;
for (; f; f = f->next) {
again:
if (ctx->sets & (1 << f->set)) {
continue;
}
prev_val = -1;
for (l = f->cmd_len, cmd = f->cmd; l > 0; l -= cmdlen,
cmd = (ipfw_insn *)((uint32_t *)cmd+ cmdlen)) {
cmdlen = F_LEN(cmd);
if (cmd->len & F_OR &&
cmd->module == prev_module &&
cmd->opcode == prev_opcode &&
prev_val == 1) {
goto next_cmd;
}
check_body:
(filter_funcs[cmd->module][cmd->opcode])
(&cmd_ctl, &cmd_val, &args, &f, cmd, ip_len);
switch(cmd_ctl) {
case IP_FW_CTL_DONE:
if (prev_val == 0)
goto next_rule;
goto done;
case IP_FW_CTL_AGAIN:
goto again;
case IP_FW_CTL_NEXT:
goto next_rule;
case IP_FW_CTL_NAT:
args->rule=f;
goto done;
case IP_FW_CTL_CHK_STATE:
cmd = ACTION_PTR(f);
l = f->cmd_len - f->act_ofs;
goto check_body;
}
if (cmd->len & F_NOT)
cmd_val= !cmd_val;
if (cmd->len & F_OR) {
if (!cmd_val) {
if(prev_val == -1){
prev_val = 0;
prev_module = cmd->module;
prev_opcode = cmd->opcode;
} else if (prev_module == cmd->module &&
prev_opcode == cmd->opcode) {
} else if (prev_module != cmd->module ||
prev_opcode != cmd->opcode) {
if(prev_val == 0){
goto next_rule;
} else {
prev_val = 0;
prev_module = cmd->module;
prev_opcode = cmd->opcode;
}
}
} else {
prev_val = 1;
prev_module = cmd->module;
prev_opcode = cmd->opcode;
}
} else {
if (!cmd_val) {
goto next_rule;
} else {
if (prev_val == 0) {
goto next_rule;
} else {
prev_val = -1;
}
}
}
next_cmd:;
}
next_rule:;
}
kprintf("+++ ipfw: ouch!, skip past end of rules, denying packet\n");
return IP_FW_DENY;
done:
f->pcnt++;
f->bcnt += ip_len;
f->timestamp = time_second;
return cmd_val;
pullup_failed:
if (sysctl_var_fw3_verbose)
kprintf("pullup failed\n");
return IP_FW_DENY;
}
struct mbuf *
ip_fw3_dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa)
{
struct m_tag *mtag;
struct dn_pkt *pkt;
ipfw_insn *cmd;
const struct ipfw_flow_id *id;
struct dn_flow_id *fid;
M_ASSERTPKTHDR(m);
mtag = m_tag_get(PACKET_TAG_DUMMYNET, sizeof(*pkt),
M_INTWAIT | M_NULLOK);
if (mtag == NULL) {
m_freem(m);
return (NULL);
}
m_tag_prepend(m, mtag);
pkt = m_tag_data(mtag);
bzero(pkt, sizeof(*pkt));
cmd = (ipfw_insn *)((uint32_t *)fwa->rule->cmd + fwa->rule->act_ofs);
KASSERT(cmd->opcode == O_DUMMYNET_PIPE ||
cmd->opcode == O_DUMMYNET_QUEUE,
("Rule is not PIPE or QUEUE, opcode %d", cmd->opcode));
pkt->dn_m = m;
pkt->dn_flags = (dir & DN_FLAGS_DIR_MASK);
pkt->ifp = fwa->oif;
pkt->pipe_nr = pipe_nr;
pkt->cpuid = mycpuid;
pkt->msgport = netisr_curport();
id = &fwa->f_id;
fid = &pkt->id;
fid->fid_dst_ip = id->dst_ip;
fid->fid_src_ip = id->src_ip;
fid->fid_dst_port = id->dst_port;
fid->fid_src_port = id->src_port;
fid->fid_proto = id->proto;
fid->fid_flags = id->flags;
pkt->dn_priv = fwa->rule;
if ((int)cmd->opcode == O_DUMMYNET_PIPE)
pkt->dn_flags |= DN_FLAGS_IS_PIPE;
m->m_pkthdr.fw_flags |= DUMMYNET_MBUF_TAGGED;
return (m);
}
void
add_rule_dispatch(netmsg_t nmsg)
{
struct netmsg_ipfw *fwmsg = (struct netmsg_ipfw *)nmsg;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct ip_fw *rule, *prev,*next;
const struct ipfw_ioc_rule *ioc_rule;
ioc_rule = fwmsg->ioc_rule;
rule = kmalloc(RULESIZE(ioc_rule), M_IPFW3, M_WAITOK | M_ZERO);
rule->act_ofs = ioc_rule->act_ofs;
rule->cmd_len = ioc_rule->cmd_len;
rule->rulenum = ioc_rule->rulenum;
rule->set = ioc_rule->set;
bcopy(ioc_rule->cmd, rule->cmd, rule->cmd_len * 4);
for (prev = NULL, next = ctx->rules;
next; prev = next, next = next->next) {
if (ioc_rule->insert) {
if (next->rulenum >= ioc_rule->rulenum) {
break;
}
} else {
if (next->rulenum > ioc_rule->rulenum) {
break;
}
}
}
KASSERT(next != NULL, ("no default rule?!"));
if (prev != NULL) {
rule->next = next;
prev->next = rule;
} else {
rule->next = ctx->rules;
ctx->rules = rule;
}
if (fwmsg->sibling != NULL) {
fwmsg->sibling->sibling = rule;
}
fwmsg->sibling = rule;
netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
}
void
ip_fw3_add_rule(struct ipfw_ioc_rule *ioc_rule)
{
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct netmsg_ipfw fwmsg;
struct netmsg_base *nmsg;
struct ip_fw *f;
IPFW_ASSERT_CFGPORT(&curthread->td_msgport);
if (ioc_rule->rulenum == 0) {
int step = sysctl_var_autoinc_step;
KKASSERT(step >= IPFW_AUTOINC_STEP_MIN &&
step <= IPFW_AUTOINC_STEP_MAX);
for (f = ctx->rules; f; f = f->next) {
if (f->rulenum == IPFW_DEFAULT_RULE)
break;
ioc_rule->rulenum = f->rulenum;
}
if (ioc_rule->rulenum < IPFW_DEFAULT_RULE - step)
ioc_rule->rulenum += step;
}
KASSERT(ioc_rule->rulenum != IPFW_DEFAULT_RULE &&
ioc_rule->rulenum != 0,
("invalid rule num %d", ioc_rule->rulenum));
bzero(&fwmsg, sizeof(fwmsg));
nmsg = &fwmsg.base;
netmsg_init(nmsg, NULL, &curthread->td_msgport,
0, add_rule_dispatch);
fwmsg.ioc_rule = ioc_rule;
netisr_domsg(nmsg, 0);
IPFW3_DEBUG("++ installed rule %d, static count now %d\n",
ioc_rule->rulenum, static_count);
}
struct ip_fw *
ip_fw3_delete_rule(struct ipfw3_context *ctx,
struct ip_fw *prev, struct ip_fw *rule)
{
if (prev == NULL)
ctx->rules = rule->next;
else
prev->next = rule->next;
kfree(rule, M_IPFW3);
rule = NULL;
return NULL;
}
void
flush_rule_dispatch(netmsg_t nmsg)
{
struct netmsg_del *dmsg = (struct netmsg_del *)nmsg;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct ip_fw *rule, *the_rule;
int kill_default = dmsg->kill_default;
rule = ctx->rules;
while (rule != NULL) {
if (rule->rulenum == IPFW_DEFAULT_RULE && kill_default == 0) {
ctx->rules = rule;
break;
}
the_rule = rule;
rule = rule->next;
kfree(the_rule, M_IPFW3);
}
netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
}
void
ip_fw3_ctl_flush_rule(int kill_default)
{
struct netmsg_del dmsg;
IPFW_ASSERT_CFGPORT(&curthread->td_msgport);
if (!kill_default) {
sysctl_var_fw3_flushing = 1;
netmsg_service_sync();
}
bzero(&dmsg, sizeof(dmsg));
netmsg_init(&dmsg.base, NULL, &curthread->td_msgport,
0, flush_rule_dispatch);
dmsg.kill_default = kill_default;
netisr_domsg(&dmsg.base, 0);
sysctl_var_fw3_flushing = 0;
}
void
delete_rule_dispatch(netmsg_t nmsg)
{
struct netmsg_del *dmsg = (struct netmsg_del *)nmsg;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct ip_fw *rule, *prev = NULL;
rule = ctx->rules;
while (rule!=NULL) {
if (rule->rulenum == dmsg->rulenum) {
ip_fw3_delete_rule(ctx, prev, rule);
break;
}
prev = rule;
rule = rule->next;
}
netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
}
int
ip_fw3_ctl_delete_rule(struct sockopt *sopt)
{
struct netmsg_del dmsg;
struct netmsg_base *nmsg;
int *rulenum;
rulenum = (int *) sopt->sopt_val;
bzero(&dmsg, sizeof(dmsg));
nmsg = &dmsg.base;
netmsg_init(nmsg, NULL, &curthread->td_msgport,
0, delete_rule_dispatch);
dmsg.rulenum = *rulenum;
netisr_domsg(nmsg, 0);
return 0;
}
void
ip_fw3_clear_counters(struct ip_fw *rule)
{
rule->bcnt = rule->pcnt = 0;
rule->timestamp = 0;
}
void
ip_fw3_zero_entry_dispatch(netmsg_t nmsg)
{
struct netmsg_zent *zmsg = (struct netmsg_zent *)nmsg;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct ip_fw *rule;
if (zmsg->rulenum == 0) {
for (rule = ctx->rules; rule; rule = rule->next) {
ip_fw3_clear_counters(rule);
}
} else {
for (rule = ctx->rules; rule; rule = rule->next) {
if (rule->rulenum == zmsg->rulenum) {
ip_fw3_clear_counters(rule);
}
}
}
ip_fw3_clear_counters(ctx->default_rule);
netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
}
int
ip_fw3_ctl_zero_entry(int rulenum, int log_only)
{
struct netmsg_zent zmsg;
struct netmsg_base *nmsg;
const char *msg;
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
bzero(&zmsg, sizeof(zmsg));
nmsg = &zmsg.base;
netmsg_init(nmsg, NULL, &curthread->td_msgport,
0, ip_fw3_zero_entry_dispatch);
zmsg.log_only = log_only;
if (rulenum == 0) {
msg = log_only ? "ipfw: All logging counts reset.\n"
: "ipfw: Accounting cleared.\n";
} else {
struct ip_fw *rule;
for (rule = ctx->rules; rule; rule = rule->next) {
if (rule->rulenum == rulenum)
break;
}
if (rule == NULL)
return (EINVAL);
zmsg.start_rule = rule;
zmsg.rulenum = rulenum;
msg = log_only ? "ipfw: Entry %d logging count reset.\n"
: "ipfw: Entry %d cleared.\n";
}
netisr_domsg(nmsg, 0);
KKASSERT(zmsg.start_rule == NULL);
if (sysctl_var_fw3_verbose)
log(LOG_SECURITY | LOG_NOTICE, msg, rulenum);
return (0);
}
int
ip_fw3_ctl_add_rule(struct sockopt *sopt)
{
struct ipfw_ioc_rule *ioc_rule;
size_t size;
size = sopt->sopt_valsize;
if (size > (sizeof(uint32_t) * IPFW_RULE_SIZE_MAX) ||
size < sizeof(*ioc_rule) - sizeof(ipfw_insn)) {
return EINVAL;
}
if (size != (sizeof(uint32_t) * IPFW_RULE_SIZE_MAX)) {
sopt->sopt_val = krealloc(sopt->sopt_val, sizeof(uint32_t) *
IPFW_RULE_SIZE_MAX, M_TEMP, M_WAITOK);
}
ioc_rule = sopt->sopt_val;
ip_fw3_add_rule(ioc_rule);
return 0;
}
int
ip_fw3_ctl_get_modules(struct sockopt *sopt)
{
int i;
struct ipfw3_module *mod;
char module_str[1024];
memset(module_str,0,1024);
for (i = 0, mod = fw3_modules; i < MAX_MODULE; i++, mod++) {
if (mod->type != 0) {
if (i > 0)
strcat(module_str,",");
strcat(module_str,mod->name);
}
}
bzero(sopt->sopt_val, sopt->sopt_valsize);
bcopy(module_str, sopt->sopt_val, strlen(module_str));
sopt->sopt_valsize = strlen(module_str);
return 0;
}
int
ip_fw3_ctl_get_rules(struct sockopt *sopt)
{
struct ipfw3_context *ctx = fw3_ctx[mycpuid];
struct ip_fw *rule;
struct ipfw_ioc_rule *ioc;
const struct ip_fw *sibling;
int total_len = 0;
ioc = (struct ipfw_ioc_rule *)sopt->sopt_val;
for (rule = ctx->rules; rule; rule = rule->next) {
total_len += IOC_RULESIZE(rule);
if (total_len > sopt->sopt_valsize) {
bzero(sopt->sopt_val, sopt->sopt_valsize);
return 0;
}
ioc->act_ofs = rule->act_ofs;
ioc->cmd_len = rule->cmd_len;
ioc->rulenum = rule->rulenum;
ioc->set = rule->set;
ioc->sets = fw3_ctx[mycpuid]->sets;
ioc->pcnt = 0;
ioc->bcnt = 0;
ioc->timestamp = 0;
for (sibling = rule; sibling != NULL; sibling = sibling->sibling) {
ioc->pcnt += sibling->pcnt;
ioc->bcnt += sibling->bcnt;
if (sibling->timestamp > ioc->timestamp)
ioc->timestamp = sibling->timestamp;
}
bcopy(rule->cmd, ioc->cmd, ioc->cmd_len * 4);
ioc = (struct ipfw_ioc_rule *)((uint8_t *)ioc + IOC_RULESIZE(ioc));
}
sopt->sopt_valsize = total_len;
return 0;
}
int
ip_fw3_ctl_x(struct sockopt *sopt)
{
ip_fw_x_header *x_header;
x_header = (ip_fw_x_header *)(sopt->sopt_val);
sopt->sopt_name = x_header->opcode;
sopt->sopt_valsize -= sizeof(ip_fw_x_header);
bcopy(++x_header, sopt->sopt_val, sopt->sopt_valsize);
return ip_fw3_ctl(sopt);
}
int
ip_fw3_ctl(struct sockopt *sopt)
{
int error = 0;
switch (sopt->sopt_name) {
case IP_FW_X:
ip_fw3_ctl_x(sopt);
break;
case IP_FW_GET:
case IP_FW_MODULE:
case IP_FW_FLUSH:
case IP_FW_ADD:
case IP_FW_DEL:
case IP_FW_ZERO:
case IP_FW_RESETLOG:
error = ip_fw3_ctl_sockopt(sopt);
break;
case IP_FW_SET_GET:
case IP_FW_SET_MOVE_RULE:
case IP_FW_SET_MOVE_SET:
case IP_FW_SET_SWAP:
case IP_FW_SET_TOGGLE:
error = ip_fw3_ctl_set_sockopt(sopt);
break;
case IP_FW_NAT_ADD:
case IP_FW_NAT_DEL:
case IP_FW_NAT_FLUSH:
case IP_FW_NAT_GET:
case IP_FW_NAT_GET_RECORD:
if (ip_fw3_ctl_nat_ptr != NULL) {
error = ip_fw3_ctl_nat_ptr(sopt);
}
break;
case IP_DUMMYNET_GET:
case IP_DUMMYNET_CONFIGURE:
case IP_DUMMYNET_DEL:
case IP_DUMMYNET_FLUSH:
error = ip_dn_sockopt(sopt);
break;
case IP_FW_STATE_ADD:
case IP_FW_STATE_DEL:
case IP_FW_STATE_FLUSH:
case IP_FW_STATE_GET:
if (ip_fw3_ctl_state_ptr != NULL) {
error = ip_fw3_ctl_state_ptr(sopt);
}
break;
case IP_FW_TABLE_CREATE:
case IP_FW_TABLE_DELETE:
case IP_FW_TABLE_APPEND:
case IP_FW_TABLE_REMOVE:
case IP_FW_TABLE_LIST:
case IP_FW_TABLE_FLUSH:
case IP_FW_TABLE_SHOW:
case IP_FW_TABLE_TEST:
case IP_FW_TABLE_RENAME:
if (ip_fw3_ctl_table_ptr != NULL) {
error = ip_fw3_ctl_table_ptr(sopt);
}
break;
case IP_FW_SYNC_SHOW_CONF:
case IP_FW_SYNC_SHOW_STATUS:
case IP_FW_SYNC_EDGE_CONF:
case IP_FW_SYNC_EDGE_START:
case IP_FW_SYNC_EDGE_STOP:
case IP_FW_SYNC_EDGE_TEST:
case IP_FW_SYNC_EDGE_CLEAR:
case IP_FW_SYNC_CENTRE_CONF:
case IP_FW_SYNC_CENTRE_START:
case IP_FW_SYNC_CENTRE_STOP:
case IP_FW_SYNC_CENTRE_TEST:
case IP_FW_SYNC_CENTRE_CLEAR:
if (ip_fw3_ctl_sync_ptr != NULL) {
error = ip_fw3_ctl_sync_ptr(sopt);
}
break;
default:
kprintf("ip_fw3_ctl invalid option %d\n",
sopt->sopt_name);
error = EINVAL;
}
return error;
}
int
ip_fw3_ctl_sockopt(struct sockopt *sopt)
{
int error = 0, rulenum;
switch (sopt->sopt_name) {
case IP_FW_GET:
error = ip_fw3_ctl_get_rules(sopt);
break;
case IP_FW_MODULE:
error = ip_fw3_ctl_get_modules(sopt);
break;
case IP_FW_FLUSH:
ip_fw3_ctl_flush_rule(0);
break;
case IP_FW_ADD:
error = ip_fw3_ctl_add_rule(sopt);
break;
case IP_FW_DEL:
error = ip_fw3_ctl_delete_rule(sopt);
break;
case IP_FW_ZERO:
case IP_FW_RESETLOG:
rulenum = 0;
if (sopt->sopt_valsize != 0) {
error = soopt_to_kbuf(sopt, &rulenum,
sizeof(int), sizeof(int));
if (error) {
break;
}
}
error = ip_fw3_ctl_zero_entry(rulenum,
sopt->sopt_name == IP_FW_RESETLOG);
break;
default:
kprintf("ip_fw3_ctl invalid option %d\n",
sopt->sopt_name);
error = EINVAL;
}
return error;
}
int
ip_fw3_check_in(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir)
{
struct ip_fw_args args;
struct mbuf *m = *m0;
struct m_tag *mtag;
int tee = 0, error = 0, ret;
if (m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED) {
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
args.rule = ((struct dn_pkt *)m_tag_data(mtag))->dn_priv;
KKASSERT(args.rule != NULL);
m_tag_delete(m, mtag);
m->m_pkthdr.fw_flags &= ~DUMMYNET_MBUF_TAGGED;
} else {
args.rule = NULL;
}
args.eh = NULL;
args.oif = NULL;
args.m = m;
ret = ip_fw3_chk(&args);
m = args.m;
if (m == NULL) {
error = EACCES;
goto back;
}
switch (ret) {
case IP_FW_PASS:
break;
case IP_FW_DENY:
m_freem(m);
m = NULL;
error = EACCES;
break;
case IP_FW_DUMMYNET:
m = ip_fw3_dummynet_io(m, args.cookie, DN_TO_IP_IN,
&args);
break;
case IP_FW_TEE:
tee = 1;
case IP_FW_DIVERT:
m->m_pkthdr.fw_flags &= ~BRIDGE_MBUF_TAGGED;
if (ip_divert_p != NULL) {
m = ip_divert_p(m, tee, 1);
} else {
m_freem(m);
m = NULL;
error = EACCES;
}
break;
case IP_FW_NAT:
break;
case IP_FW_ROUTE:
break;
default:
panic("unknown ipfw3 return value: %d", ret);
}
back:
*m0 = m;
return error;
}
int
ip_fw3_check_out(void *arg, struct mbuf **m0, struct ifnet *ifp, int dir)
{
struct ip_fw_args args;
struct mbuf *m = *m0;
struct m_tag *mtag;
int tee = 0, error = 0, ret;
if (m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED) {
mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
KKASSERT(mtag != NULL);
args.rule = ((struct dn_pkt *)m_tag_data(mtag))->dn_priv;
KKASSERT(args.rule != NULL);
m_tag_delete(m, mtag);
m->m_pkthdr.fw_flags &= ~DUMMYNET_MBUF_TAGGED;
} else {
args.rule = NULL;
}
args.eh = NULL;
args.m = m;
args.oif = ifp;
ret = ip_fw3_chk(&args);
m = args.m;
if (m == NULL) {
error = EACCES;
goto back;
}
switch (ret) {
case IP_FW_PASS:
break;
case IP_FW_DENY:
m_freem(m);
m = NULL;
error = EACCES;
break;
case IP_FW_DUMMYNET:
m = ip_fw3_dummynet_io(m, args.cookie, DN_TO_IP_OUT,
&args);
break;
case IP_FW_TEE:
tee = 1;
case IP_FW_DIVERT:
if (ip_divert_p != NULL) {
m = ip_divert_p(m, tee, 0);
} else {
m_freem(m);
m = NULL;
error = EACCES;
}
break;
case IP_FW_NAT:
break;
case IP_FW_ROUTE:
break;
default:
panic("unknown ipfw3 return value: %d", ret);
}
back:
*m0 = m;
return error;
}
void
ip_fw3_hook(void)
{
struct pfil_head *pfh;
IPFW_ASSERT_CFGPORT(&curthread->td_msgport);
pfh = pfil_head_get(PFIL_TYPE_AF, AF_INET);
if (pfh == NULL)
return;
pfil_add_hook(ip_fw3_check_in, NULL, PFIL_IN, pfh);
pfil_add_hook(ip_fw3_check_out, NULL, PFIL_OUT, pfh);
}
void
ip_fw3_dehook(void)
{
struct pfil_head *pfh;
IPFW_ASSERT_CFGPORT(&curthread->td_msgport);
pfh = pfil_head_get(PFIL_TYPE_AF, AF_INET);
if (pfh == NULL)
return;
pfil_remove_hook(ip_fw3_check_in, NULL, PFIL_IN, pfh);
pfil_remove_hook(ip_fw3_check_out, NULL, PFIL_OUT, pfh);
}
void
ip_fw3_sysctl_enable_dispatch(netmsg_t nmsg)
{
struct lwkt_msg *lmsg = &nmsg->lmsg;
int enable = lmsg->u.ms_result;
if (sysctl_var_fw3_enable == enable)
goto reply;
sysctl_var_fw3_enable = enable;
if (sysctl_var_fw3_enable)
ip_fw3_hook();
else
ip_fw3_dehook();
reply:
lwkt_replymsg(lmsg, 0);
}
int
ip_fw3_sysctl_enable(SYSCTL_HANDLER_ARGS)
{
struct netmsg_base nmsg;
struct lwkt_msg *lmsg;
int enable, error;
enable = sysctl_var_fw3_enable;
error = sysctl_handle_int(oidp, &enable, 0, req);
if (error || req->newptr == NULL)
return error;
netmsg_init(&nmsg, NULL, &curthread->td_msgport,
0, ip_fw3_sysctl_enable_dispatch);
lmsg = &nmsg.lmsg;
lmsg->u.ms_result = enable;
return lwkt_domsg(IPFW_CFGPORT, lmsg, 0);
}
int
ip_fw3_sysctl_autoinc_step(SYSCTL_HANDLER_ARGS)
{
return sysctl_int_range(oidp, arg1, arg2, req,
IPFW_AUTOINC_STEP_MIN, IPFW_AUTOINC_STEP_MAX);
}
void
ctx_init_dispatch(netmsg_t nmsg)
{
struct netmsg_ipfw *fwmsg = (struct netmsg_ipfw *)nmsg;
struct ipfw3_context *ctx;
struct ip_fw *def_rule;
ctx = kmalloc(LEN_FW3_CTX, M_IPFW3, M_WAITOK | M_ZERO);
fw3_ctx[mycpuid] = ctx;
ctx->sets = IPFW_ALL_SETS;
def_rule = kmalloc(LEN_FW3, M_IPFW3, M_WAITOK | M_ZERO);
def_rule->act_ofs = 0;
def_rule->rulenum = IPFW_DEFAULT_RULE;
def_rule->cmd_len = 2;
def_rule->set = IPFW_DEFAULT_SET;
def_rule->cmd[0].len = LEN_OF_IPFWINSN;
def_rule->cmd[0].module = MODULE_BASIC_ID;
#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT
def_rule->cmd[0].opcode = O_BASIC_ACCEPT;
#else
if (filters_default_to_accept)
def_rule->cmd[0].opcode = O_BASIC_ACCEPT;
else
def_rule->cmd[0].opcode = O_BASIC_DENY;
#endif
ctx->default_rule = def_rule;
ctx->rules = def_rule;
if (fwmsg->sibling != NULL) {
fwmsg->sibling->sibling = def_rule;
}
fwmsg->sibling = def_rule;
netisr_forwardmsg_all(&nmsg->base, mycpuid + 1);
}
void
init_dispatch(netmsg_t nmsg)
{
struct netmsg_ipfw fwmsg;
int error = 0;
if (IPFW3_LOADED) {
kprintf("ipfw3 already loaded\n");
error = EEXIST;
goto reply;
}
bzero(&fwmsg, sizeof(fwmsg));
netmsg_init(&fwmsg.base, NULL, &curthread->td_msgport,
0, ctx_init_dispatch);
netisr_domsg(&fwmsg.base, 0);
ip_fw_chk_ptr = ip_fw3_chk;
ip_fw_ctl_x_ptr = ip_fw3_ctl_x;
ip_fw_dn_io_ptr = ip_fw3_dummynet_io;
kprintf("ipfw3 initialized, default to %s\n",
filters_default_to_accept ? "accept" : "deny");
ip_fw3_loaded = 1;
if (sysctl_var_fw3_enable)
ip_fw3_hook();
reply:
lwkt_replymsg(&nmsg->lmsg, error);
}
int
ip_fw3_init(void)
{
struct netmsg_base smsg;
int error;
init_module();
netmsg_init(&smsg, NULL, &curthread->td_msgport,
0, init_dispatch);
error = lwkt_domsg(IPFW_CFGPORT, &smsg.lmsg, 0);
return error;
}
#ifdef KLD_MODULE
void
fini_dispatch(netmsg_t nmsg)
{
int error = 0, cpu;
ip_fw3_loaded = 0;
ip_fw3_dehook();
netmsg_service_sync();
ip_fw_chk_ptr = NULL;
ip_fw_ctl_x_ptr = NULL;
ip_fw_dn_io_ptr = NULL;
ip_fw3_ctl_flush_rule(1);
for (cpu = 0; cpu < ncpus; ++cpu) {
if (fw3_ctx[cpu] != NULL) {
kfree(fw3_ctx[cpu], M_IPFW3);
fw3_ctx[cpu] = NULL;
}
}
kprintf("ipfw3 unloaded\n");
lwkt_replymsg(&nmsg->lmsg, error);
}
int
ip_fw3_fini(void)
{
struct netmsg_base smsg;
netmsg_init(&smsg, NULL, &curthread->td_msgport,
0, fini_dispatch);
return lwkt_domsg(IPFW_CFGPORT, &smsg.lmsg, 0);
}
#endif
static int
ip_fw3_modevent(module_t mod, int type, void *unused)
{
int err = 0;
switch (type) {
case MOD_LOAD:
err = ip_fw3_init();
break;
case MOD_UNLOAD:
#ifndef KLD_MODULE
kprintf("ipfw3 statically compiled, cannot unload\n");
err = EBUSY;
#else
err = ip_fw3_fini();
#endif
break;
default:
break;
}
return err;
}
static moduledata_t ipfw3mod = {
"ipfw3",
ip_fw3_modevent,
0
};
DECLARE_MODULE(ipfw3, ipfw3mod, SI_SUB_PROTO_END, SI_ORDER_FIRST);
MODULE_VERSION(ipfw3, 1);