📄 ip_fw.c.svn-base
字号:
r->parent = parent ;
rule = parent->rule;
}
r->id = *id ;
r->expire = time_second + dyn_syn_lifetime ;
r->rule = rule ;
r->dyn_type = dyn_type ;
r->pcnt = r->bcnt = 0 ;
r->count = 0 ;
r->bucket = i ;
r->next = ipfw_dyn_v[i] ;
ipfw_dyn_v[i] = r ;
dyn_count++ ;
DEB(printf("-- add entry 0x%08x %d -> 0x%08x %d, total %d\n",
(r->id.src_ip), (r->id.src_port),
(r->id.dst_ip), (r->id.dst_port),
dyn_count ); )
return r;
}
/**
* lookup dynamic parent rule using pkt and rule as search keys.
* If the lookup fails, then install one.
*/
static struct ipfw_dyn_rule *
lookup_dyn_parent(struct ipfw_flow_id *pkt, struct ip_fw *rule)
{
struct ipfw_dyn_rule *q;
int i;
if (ipfw_dyn_v) {
i = hash_packet( pkt );
for (q = ipfw_dyn_v[i] ; q != NULL ; q=q->next)
if (q->dyn_type == DYN_LIMIT_PARENT && rule == q->rule &&
pkt->proto == q->id.proto &&
pkt->src_ip == q->id.src_ip &&
pkt->dst_ip == q->id.dst_ip &&
pkt->src_port == q->id.src_port &&
pkt->dst_port == q->id.dst_port) {
q->expire = time_second + dyn_short_lifetime ;
DEB(printf("lookup_dyn_parent found 0x%p\n", q);)
return q;
}
}
return add_dyn_rule(pkt, DYN_LIMIT_PARENT, rule);
}
/*
* Install dynamic state.
* There are different types of dynamic rules which can be installed.
* The type is in rule->dyn_type.
* Type 0 (default) is a bidirectional rule
*
* Returns 1 (failure) if state is not installed because of errors or because
* session limitations are enforced.
*/
static int
install_state(struct ip_fw *rule, struct ip_fw_args *args)
{
struct ipfw_dyn_rule *q ;
static int last_log ;
u_int8_t type = rule->dyn_type ;
DEB(printf("-- install state type %d 0x%08x %u -> 0x%08x %u\n",
type,
(args->f_id.src_ip), (args->f_id.src_port),
(args->f_id.dst_ip), (args->f_id.dst_port) );)
q = lookup_dyn_rule(&args->f_id, NULL) ;
if (q != NULL) { /* should never occur */
if (last_log != time_second) {
last_log = time_second ;
printf(" entry already present, done\n");
}
return 0 ;
}
if (dyn_count >= dyn_max) /* try remove old ones... */
EXPIRE_DYN_CHAINS();
if (dyn_count >= dyn_max) {
if (last_log != time_second) {
last_log = time_second ;
printf(" Too many dynamic rules, sorry\n");
}
return 1; /* cannot install, notify caller */
}
switch (type) {
case DYN_KEEP_STATE: /* bidir rule */
add_dyn_rule(&args->f_id, DYN_KEEP_STATE, rule);
break ;
case DYN_LIMIT: /* limit number of sessions */
{
u_int16_t limit_mask = rule->limit_mask ;
u_int16_t conn_limit = rule->conn_limit ;
struct ipfw_flow_id id;
struct ipfw_dyn_rule *parent;
DEB(printf("installing dyn-limit rule %d\n", conn_limit);)
id.dst_ip = id.src_ip = 0;
id.dst_port = id.src_port = 0 ;
id.proto = args->f_id.proto ;
if (limit_mask & DYN_SRC_ADDR)
id.src_ip = args->f_id.src_ip;
if (limit_mask & DYN_DST_ADDR)
id.dst_ip = args->f_id.dst_ip;
if (limit_mask & DYN_SRC_PORT)
id.src_port = args->f_id.src_port;
if (limit_mask & DYN_DST_PORT)
id.dst_port = args->f_id.dst_port;
parent = lookup_dyn_parent(&id, rule);
if (parent == NULL) {
printf("add parent failed\n");
return 1;
}
if (parent->count >= conn_limit) {
EXPIRE_DYN_CHAIN(rule); /* try to expire some */
/*
* The expiry might have removed the parent too.
* We lookup again, which will re-create if necessary.
*/
parent = lookup_dyn_parent(&id, rule);
if (parent == NULL) {
printf("add parent failed\n");
return 1;
}
if (parent->count >= conn_limit) {
if (fw_verbose && last_log != time_second) {
last_log = time_second;
log(LOG_SECURITY | LOG_DEBUG,
"drop session, too many entries\n");
}
return 1;
}
}
add_dyn_rule(&args->f_id, DYN_LIMIT, (struct ip_fw *)parent);
}
break ;
default:
printf("unknown dynamic rule type %u\n", type);
return 1 ;
}
lookup_dyn_rule(&args->f_id, NULL) ; /* XXX just set the lifetime */
return 0;
}
/*
* given an ip_fw *, lookup_next_rule will return a pointer
* of the same type to the next one. This can be either the jump
* target (for skipto instructions) or the next one in the list (in
* all other cases including a missing jump target).
* Backward jumps are not allowed, so start looking from the next
* rule...
*/
static struct ip_fw * lookup_next_rule(struct ip_fw *me);
static struct ip_fw *
lookup_next_rule(struct ip_fw *me)
{
struct ip_fw *rule ;
int rulenum = me->fw_skipto_rule ; /* guess... */
if ( (me->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_SKIPTO )
for (rule = LIST_NEXT(me,next); rule ; rule = LIST_NEXT(rule,next))
if (rule->fw_number >= rulenum)
return rule ;
return LIST_NEXT(me,next) ; /* failure or not a skipto */
}
/*
* Parameters:
*
* *m The packet; we set to NULL when/if we nuke it.
* oif Outgoing interface, or NULL if packet is incoming
* *cookie Skip up to the first rule past this rule number;
* upon return, non-zero port number for divert or tee.
* Special case: cookie == NULL on input for bridging.
* *flow_id pointer to the last matching rule (in/out)
* *next_hop socket we are forwarding to (in/out).
*
* Return value:
*
* IP_FW_PORT_DENY_FLAG the packet must be dropped.
* 0 The packet is to be accepted and routed normally OR
* the packet was denied/rejected and has been dropped;
* in the latter case, *m is equal to NULL upon return.
* port Divert the packet to port, with these caveats:
*
* - If IP_FW_PORT_TEE_FLAG is set, tee the packet instead
* of diverting it (ie, 'ipfw tee').
*
* - If IP_FW_PORT_DYNT_FLAG is set, interpret the lower
* 16 bits as a dummynet pipe number instead of diverting
*/
static int
ip_fw_chk(struct ip_fw_args *args)
{
/*
* grab things into variables to minimize diffs.
* XXX this has to be cleaned up later.
*/
struct mbuf **m = &(args->m);
struct ifnet *oif = args->oif;
u_int16_t *cookie = &(args->divert_rule);
struct ip_fw **flow_id = &(args->rule);
struct sockaddr_in **next_hop = &(args->next_hop);
struct ip_fw *f = NULL; /* matching rule */
struct ip *ip = mtod(*m, struct ip *);
struct ifnet *const rif = (*m)->m_pkthdr.rcvif;
struct ifnet *tif;
u_int hlen = 0;
u_short ip_off=0, offset = 0;
/* local copy of addresses for faster matching */
u_short src_port = 0, dst_port = 0;
struct in_addr src_ip, dst_ip;
u_int8_t proto= 0, flags = 0;
u_int16_t skipto;
u_int16_t ip_len=0;
int dyn_checked = 0 ; /* set after dyn.rules have been checked. */
int direction = MATCH_FORWARD ; /* dirty trick... */
struct ipfw_dyn_rule *q = NULL ;
/* Special hack for bridging (as usual) */
#define BRIDGED (args->eh != NULL)
if (BRIDGED) { /* this is a bridged packet */
if ( (*m)->m_pkthdr.len >= sizeof(struct ip) &&
ntohs(args->eh->ether_type) == ETHERTYPE_IP)
hlen = ip->ip_hl << 2;
else
return 0; /* XXX ipfw1 always accepts non-ip pkts */
} else
hlen = ip->ip_hl << 2;
/* Grab and reset cookie */
skipto = *cookie;
*cookie = 0;
#define PULLUP_TO(len) do { \
if ((*m)->m_len < (len)) { \
if ((*m = m_pullup(*m, (len))) == 0) \
goto bogusfrag; \
ip = mtod(*m, struct ip *); \
} \
} while (0)
if (hlen > 0) { /* this is an IP packet */
/*
* Collect parameters into local variables for faster matching.
*/
proto = ip->ip_p;
src_ip = ip->ip_src;
dst_ip = ip->ip_dst;
#ifndef _WIN32 /* on Windows all incoming packets are as on the wire */
if (BRIDGED) { /* bridged packets are as on the wire */
#endif
ip_off = ntohs(ip->ip_off);
ip_len = ntohs(ip->ip_len);
#ifndef _WIN32
} else {
ip_off = ip->ip_off;
ip_len = ip->ip_len;
}
#endif
offset = ip_off & IP_OFFMASK;
if (offset == 0) {
switch (proto) {
case IPPROTO_TCP : {
struct tcphdr *tcp;
PULLUP_TO(hlen + sizeof(struct tcphdr));
tcp =(struct tcphdr *)((u_int32_t *)ip + ip->ip_hl);
dst_port = tcp->th_dport ;
src_port = tcp->th_sport ;
flags = tcp->th_flags ;
}
break ;
case IPPROTO_UDP : {
struct udphdr *udp;
PULLUP_TO(hlen + sizeof(struct udphdr));
udp =(struct udphdr *)((u_int32_t *)ip + ip->ip_hl);
dst_port = udp->uh_dport ;
src_port = udp->uh_sport ;
}
break;
case IPPROTO_ICMP:
PULLUP_TO(hlen + 4); /* type, code and checksum. */
flags = ((struct icmp *)
((u_int32_t *)ip + ip->ip_hl))->icmp_type ;
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.proto = proto;
args->f_id.src_port = ntohs(src_port);
args->f_id.dst_port = ntohs(dst_port);
args->f_id.flags = flags;
if (*flow_id) {
/*
* Packet has already been tagged. Look for the next rule
* to restart processing.
*/
if (fw_one_pass) /* just accept if fw_one_pass is set */
return 0;
f = (*flow_id)->next_rule_ptr ;
if (f == NULL)
f = (*flow_id)->next_rule_ptr = lookup_next_rule(*flow_id);
if (f == NULL)
goto dropit;
} else {
/*
* Go down the list, looking for enlightment.
* If we've been asked to start at a given rule, do so.
*/
f = LIST_FIRST(&ip_fw_chain_head);
if (skipto != 0) {
if (skipto >= IPFW_DEFAULT_RULE)
goto dropit;
while (f && f->fw_number <= skipto)
f = LIST_NEXT(f, next);
if (f == NULL)
goto dropit;
}
}
for (; f; f = LIST_NEXT(f, next)) {
again:
if (f->fw_number == IPFW_DEFAULT_RULE)
goto got_match ;
/*
* dynamic rules are checked at the first keep-state or
* check-state occurrence.
*/
if (f->fw_flg & (IP_FW_F_KEEP_S|IP_FW_F_CHECK_S) &&
dyn_checked == 0 ) {
dyn_checked = 1 ;
q = lookup_dyn_rule(&args->f_id, &direction);
if (q != NULL) {
DEB(printf("-- dynamic match 0x%08x %d %s 0x%08x %d\n",
(q->id.src_ip), (q->id.src_port),
(direction == MATCH_FORWARD ? "-->" : "<--"),
(q->id.dst_ip), (q->id.dst_port) ); )
f = q->rule ;
q->pcnt++ ;
q->bcnt += ip_len;
goto got_match ; /* random not allowed here */
}
/* if this was a check-only rule, continue with next */
if (f->fw_flg & IP_FW_F_CHECK_S)
continue ;
}
/* Check if rule only valid for bridged packets */
if ((f->fw_flg & IP_FW_BRIDGED) != 0 && !(BRIDGED))
continue;
if (oif) {
/* Check direction outbound */
if (!(f->fw_flg & IP_FW_F_OUT))
continue;
} else {
/* Check direction inbound */
if (!(f->fw_flg & IP_FW_F_IN))
continue;
}
/* Fragments */
if ((f->fw_flg & IP_FW_F_FRAG) && offset == 0 )
continue;
if (f->fw_flg & IP_FW_F_SME) {
INADDR_TO_IFP(src_ip, tif);
if (tif == NULL)
continue;
}
if (f->fw_flg & IP_FW_F_DME) {
INADDR_TO_IFP(dst_ip, tif);
if (tif == NULL)
continue;
}
/* If src-addr doesn't match, not this rule. */
if (((f->fw_flg & IP_FW_F_INVSRC) != 0) ^ ((src_ip.s_addr
& f->fw_smsk.s_addr) != f->fw_src.s_addr))
continue;
/* If dest-addr doesn't match, not this rule. */
if (((f->fw_flg & IP_FW_F_INVDST) != 0) ^ ((dst_ip.s_addr
& f->fw_dmsk.s_addr) != f->fw_dst.s_addr))
continue;
/* Interface check */
if ((f->fw_flg & IF_FW_F_VIAHACK) == IF_FW_F_VIAHACK) {
struct ifnet *const iface = oif ? oif : rif;
/* Backwards compatibility hack for "via" */
if (!iface || !iface_match(iface,
&f->fw_in_if, f->fw_flg & IP_FW_F_OIFNAME))
continue;
} else {
/* Check receive interface */
if ((f->fw_flg & IP_FW_F_IIFACE)
&& (!rif || !iface_match(rif,
&f->fw_in_if, f->fw_flg & IP_FW_F_IIFNAME)))
continue;
/* Check outgoing interface */
if ((f->fw_flg & IP_FW_F_OIFACE)
&& (!oif || !iface_match(oif,
&f->fw_out_if, f->fw_flg & IP_FW_F_OIFNAME)))
continue;
}
/* Check IP options */
if (f->fw_ipopt != f->fw_ipnopt && !ipopts_match(ip, f))
continue;
/* Check protocol; if wildcard, and no [ug]id, match */
if (f->fw_prot == IPPROTO_IP) {
if (!(f->fw_flg & (IP_FW_F_UID|IP_FW_F_GID)))
goto rnd_then_got_match;
} else
/* If different, don't match */
if (proto != f->fw_prot)
continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -