📄 ip_fw.c.svn-base
字号:
case TCPOPT_CC:
case TCPOPT_CCNEW:
case TCPOPT_CCECHO:
opts &= ~IP_FW_TCPOPT_CC;
nopts &= ~IP_FW_TCPOPT_CC;
break;
}
if (opts == nopts)
break;
}
if (opts == 0 && nopts == nopts_sve)
return 1;
else
return 0;
}
static int
iface_match(struct ifnet *ifp, union ip_fw_if *ifu, int byname)
{
/* Check by name or by IP address */
if (byname) {
/* Check unit number (-1 is wildcard) */
if (ifu->fu_via_if.unit != -1
&& ifp->if_unit != ifu->fu_via_if.unit)
return(0);
/* Check name */
if (strncmp(ifp->if_name, ifu->fu_via_if.name, FW_IFNLEN))
return(0);
return(1);
} else if (ifu->fu_via_ip.s_addr != 0) { /* Zero == wildcard */
struct ifaddr *ia;
TAILQ_FOREACH(ia, &ifp->if_addrhead, ifa_link) {
if (ia->ifa_addr == NULL)
continue;
if (ia->ifa_addr->sa_family != AF_INET)
continue;
if (ifu->fu_via_ip.s_addr != ((struct sockaddr_in *)
(ia->ifa_addr))->sin_addr.s_addr)
continue;
return(1);
}
return(0);
}
return(1);
}
static void
ipfw_report(struct ip_fw *f, struct ip *ip, int ip_off, int ip_len,
struct ifnet *rif, struct ifnet *oif)
{
struct tcphdr *const tcp = (struct tcphdr *) ((u_int32_t *) ip+ ip->ip_hl);
struct udphdr *const udp = (struct udphdr *) ((u_int32_t *) ip+ ip->ip_hl);
struct icmp *const icmp = (struct icmp *) ((u_int32_t *) ip + ip->ip_hl);
u_int64_t count;
char *action;
char action2[32], proto[47], name[18], fragment[27];
int len;
int offset = ip_off & IP_OFFMASK;
#ifdef _WIN32
DECLARE_INET_NTOA_BUF;
#endif
count = f ? f->fw_pcnt : ++counter;
if ((f == NULL && fw_verbose_limit != 0 && count > fw_verbose_limit) ||
(f && f->fw_logamount != 0 && count > f->fw_loghighest))
return;
/* Print command name */
snprintf(SNPARGS(name, 0), "ipfw: %d", f ? f->fw_number : -1);
action = action2;
if (!f)
action = "Refuse";
else {
switch (f->fw_flg & IP_FW_F_COMMAND) {
case IP_FW_F_DENY:
action = "Deny";
break;
case IP_FW_F_REJECT:
if (f->fw_reject_code == IP_FW_REJECT_RST)
action = "Reset";
else
action = "Unreach";
break;
case IP_FW_F_ACCEPT:
action = "Accept";
break;
case IP_FW_F_COUNT:
action = "Count";
break;
#ifdef IPDIVERT
case IP_FW_F_DIVERT:
snprintf(SNPARGS(action2, 0), "Divert %d",
f->fw_divert_port);
break;
case IP_FW_F_TEE:
snprintf(SNPARGS(action2, 0), "Tee %d",
f->fw_divert_port);
break;
#endif
case IP_FW_F_SKIPTO:
snprintf(SNPARGS(action2, 0), "SkipTo %d",
f->fw_skipto_rule);
break;
case IP_FW_F_PIPE:
snprintf(SNPARGS(action2, 0), "Pipe %d",
f->fw_skipto_rule);
break;
case IP_FW_F_QUEUE:
snprintf(SNPARGS(action2, 0), "Queue %d",
f->fw_skipto_rule);
break;
case IP_FW_F_FWD:
if (f->fw_fwd_ip.sin_port)
snprintf(SNPARGS(action2, 0),
"Forward to %s:%d",
inet_ntoa(f->fw_fwd_ip.sin_addr),
f->fw_fwd_ip.sin_port);
else
snprintf(SNPARGS(action2, 0), "Forward to %s",
inet_ntoa(f->fw_fwd_ip.sin_addr));
break;
default:
action = "UNKNOWN";
break;
}
}
switch (ip->ip_p) {
case IPPROTO_TCP:
len = snprintf(SNPARGS(proto, 0), "TCP %s",
inet_ntoa(ip->ip_src));
if (offset == 0)
len += snprintf(SNPARGS(proto, len), ":%d ",
ntohs(tcp->th_sport));
else
len += snprintf(SNPARGS(proto, len), " ");
len += snprintf(SNPARGS(proto, len), "%s",
inet_ntoa(ip->ip_dst));
if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d",
ntohs(tcp->th_dport));
break;
case IPPROTO_UDP:
len = snprintf(SNPARGS(proto, 0), "UDP %s",
inet_ntoa(ip->ip_src));
if (offset == 0)
len += snprintf(SNPARGS(proto, len), ":%d ",
ntohs(udp->uh_sport));
else
len += snprintf(SNPARGS(proto, len), " ");
len += snprintf(SNPARGS(proto, len), "%s",
inet_ntoa(ip->ip_dst));
if (offset == 0)
snprintf(SNPARGS(proto, len), ":%d",
ntohs(udp->uh_dport));
break;
case IPPROTO_ICMP:
if (offset == 0)
len = snprintf(SNPARGS(proto, 0), "ICMP:%u.%u ",
icmp->icmp_type, icmp->icmp_code);
else
len = snprintf(SNPARGS(proto, 0), "ICMP ");
len += snprintf(SNPARGS(proto, len), "%s",
inet_ntoa(ip->ip_src));
snprintf(SNPARGS(proto, len), " %s", inet_ntoa(ip->ip_dst));
break;
default:
len = snprintf(SNPARGS(proto, 0), "P:%d %s", ip->ip_p,
inet_ntoa(ip->ip_src));
snprintf(SNPARGS(proto, len), " %s", inet_ntoa(ip->ip_dst));
break;
}
if (ip_off & (IP_MF | IP_OFFMASK))
snprintf(SNPARGS(fragment, 0), " (frag %d:%d@%d%s)",
ntohs(ip->ip_id), ip_len - (ip->ip_hl << 2),
offset << 3,
(ip_off & IP_MF) ? "+" : "");
else
fragment[0] = '\0';
if (oif)
log(LOG_SECURITY | LOG_INFO, "%s %s %s out via %s%d%s\n",
name, action, proto, oif->if_name, oif->if_unit, fragment);
else if (rif)
log(LOG_SECURITY | LOG_INFO, "%s %s %s in via %s%d%s\n", name,
action, proto, rif->if_name, rif->if_unit, fragment);
else
log(LOG_SECURITY | LOG_INFO, "%s %s %s%s\n", name, action,
proto, fragment);
if ((f ? f->fw_logamount != 0 : 1) &&
count == (f ? f->fw_loghighest : fw_verbose_limit))
log(LOG_SECURITY | LOG_NOTICE,
"ipfw: limit %d reached on entry %d\n",
f ? f->fw_logamount : fw_verbose_limit,
f ? f->fw_number : -1);
}
static __inline int
hash_packet(struct ipfw_flow_id *id)
{
u_int32_t i ;
i = (id->dst_ip) ^ (id->src_ip) ^ (id->dst_port) ^ (id->src_port);
i &= (curr_dyn_buckets - 1) ;
return i ;
}
/**
* unlink a dynamic rule from a chain. prev is a pointer to
* the previous one, q is a pointer to the rule to delete,
* head is a pointer to the head of the queue.
* Modifies q and potentially also head.
*/
#define UNLINK_DYN_RULE(prev, head, q) { \
struct ipfw_dyn_rule *old_q = q; \
\
/* remove a refcount to the parent */ \
if (q->dyn_type == DYN_LIMIT) \
q->parent->count--; \
DEB(printf("-- unlink entry 0x%08x %d -> 0x%08x %d, %d left\n", \
(q->id.src_ip), (q->id.src_port), \
(q->id.dst_ip), (q->id.dst_port), dyn_count-1 ); ) \
if (prev != NULL) \
prev->next = q = q->next ; \
else \
ipfw_dyn_v[i] = q = q->next ; \
dyn_count-- ; \
free(old_q, M_IPFW); }
#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0)
/**
* Remove all dynamic rules pointing to a given rule, or all
* rules if rule == NULL. Second parameter is 1 if we want to
* delete unconditionally, otherwise only expired rules are removed.
*/
static void
remove_dyn_rule(struct ip_fw *rule, int force)
{
struct ipfw_dyn_rule *prev, *q;
int i, pass, max_pass ;
static u_int32_t last_remove = 0 ;
if (ipfw_dyn_v == NULL || dyn_count == 0)
return ;
/* do not expire more than once per second, it is useless */
if (force == 0 && last_remove == time_second)
return ;
last_remove = time_second ;
/*
* because DYN_LIMIT refer to parent rules, during the first pass only
* remove child and mark any pending LIMIT_PARENT, and remove
* them in a second pass.
*/
for (pass = max_pass = 0; pass <= max_pass ; pass++ ) {
for (i = 0 ; i < curr_dyn_buckets ; i++) {
for (prev=NULL, q = ipfw_dyn_v[i] ; q ; ) {
/*
* logic can become complex here, so we split tests.
* First, test if we match any rule,
* then make sure the rule is expired or we want to kill it,
* and possibly more in the future.
*/
int zap = ( rule == NULL || rule == q->rule);
if (zap)
zap = force || TIME_LEQ( q->expire , time_second );
/* do not zap parent in first pass, record we need a second pass */
if (zap && q->dyn_type == DYN_LIMIT_PARENT) {
max_pass = 1; /* we need a second pass */
if (pass == 0 || q->count != 0) {
zap = 0 ;
if (pass == 1 && force) /* should not happen */
printf("OUCH! cannot remove rule, count %d\n",
q->count);
}
}
if (zap) {
UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
} else {
prev = q ;
q = q->next ;
}
}
}
}
}
#define EXPIRE_DYN_CHAIN(rule) remove_dyn_rule(rule, 0 /* expired ones */)
#define EXPIRE_DYN_CHAINS() remove_dyn_rule(NULL, 0 /* expired ones */)
#define DELETE_DYN_CHAIN(rule) remove_dyn_rule(rule, 1 /* force removal */)
#define DELETE_DYN_CHAINS() remove_dyn_rule(NULL, 1 /* force removal */)
/**
* lookup a dynamic rule.
*/
static struct ipfw_dyn_rule *
lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction)
{
/*
* stateful ipfw extensions.
* Lookup into dynamic session queue
*/
struct ipfw_dyn_rule *prev, *q ;
int i, dir = 0;
#define MATCH_FORWARD 1
if (ipfw_dyn_v == NULL)
return NULL ;
i = hash_packet( pkt );
for (prev=NULL, q = ipfw_dyn_v[i] ; q != NULL ; ) {
if (q->dyn_type == DYN_LIMIT_PARENT)
goto next;
if (TIME_LEQ( q->expire , time_second ) ) { /* expire entry */
UNLINK_DYN_RULE(prev, ipfw_dyn_v[i], q);
continue;
}
if ( pkt->proto == q->id.proto) {
if (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 ) {
dir = MATCH_FORWARD ;
goto found ;
}
if (pkt->src_ip == q->id.dst_ip &&
pkt->dst_ip == q->id.src_ip &&
pkt->src_port == q->id.dst_port &&
pkt->dst_port == q->id.src_port ) {
dir = 0 ; /* reverse match */
goto found ;
}
}
next:
prev = q ;
q = q->next ;
}
return NULL ; /* clearly not found */
found:
if ( prev != NULL) { /* found and not in front */
prev->next = q->next ;
q->next = ipfw_dyn_v[i] ;
ipfw_dyn_v[i] = q ;
}
if (pkt->proto == IPPROTO_TCP) {
/* update state according to flags */
u_char flags = pkt->flags & (TH_FIN|TH_SYN|TH_RST);
q->state |= (dir == MATCH_FORWARD ) ? flags : (flags << 8);
switch (q->state) {
case TH_SYN :
/* opening */
q->expire = time_second + dyn_syn_lifetime ;
break ;
case TH_SYN | (TH_SYN << 8) :
/* move to established */
q->expire = time_second + dyn_ack_lifetime ;
break ;
case TH_SYN | (TH_SYN << 8) | TH_FIN :
case TH_SYN | (TH_SYN << 8) | (TH_FIN << 8) :
/* one side tries to close */
q->expire = time_second + dyn_ack_lifetime ;
break ;
case TH_SYN | (TH_SYN << 8) | TH_FIN | (TH_FIN << 8) :
/* both sides closed */
q->expire = time_second + dyn_fin_lifetime ;
break ;
default:
#if 0
/*
* reset or some invalid combination, but can also
* occur if we use keep-state the wrong way.
*/
if ( (q->state & ((TH_RST << 8)|TH_RST)) == 0)
printf("invalid state: 0x%x\n", q->state);
#endif
q->expire = time_second + dyn_rst_lifetime ;
break ;
}
} else if (pkt->proto == IPPROTO_UDP) {
q->expire = time_second + dyn_udp_lifetime ;
} else {
/* other protocols */
q->expire = time_second + dyn_short_lifetime ;
}
if (match_direction)
*match_direction = dir ;
return q ;
}
/**
* Install state of type 'type' for a dynamic session.
* The hash table contains two type of rules:
* - regular rules (DYN_KEEP_STATE)
* - rules for sessions with limited number of sess per user
* (DYN_LIMIT). When they are created, the parent is
* increased by 1, and decreased on delete. In this case,
* the third parameter is the parent rule and not the chain.
* - "parent" rules for the above (DYN_LIMIT_PARENT).
*/
static struct ipfw_dyn_rule *
add_dyn_rule(struct ipfw_flow_id *id, u_int8_t dyn_type, struct ip_fw *rule)
{
struct ipfw_dyn_rule *r ;
int i ;
if (ipfw_dyn_v == NULL ||
(dyn_count == 0 && dyn_buckets != curr_dyn_buckets)) {
/* try reallocation, make sure we have a power of 2 */
u_int32_t i = dyn_buckets ;
while ( i > 0 && (i & 1) == 0 )
i >>= 1 ;
if (i != 1) /* not a power of 2 */
dyn_buckets = curr_dyn_buckets ; /* reset */
else {
curr_dyn_buckets = dyn_buckets ;
if (ipfw_dyn_v != NULL)
free(ipfw_dyn_v, M_IPFW);
ipfw_dyn_v = malloc(curr_dyn_buckets * sizeof r,
M_IPFW, M_DONTWAIT | M_ZERO);
if (ipfw_dyn_v == NULL)
return NULL; /* failed ! */
}
}
i = hash_packet(id);
r = malloc(sizeof *r, M_IPFW, M_DONTWAIT | M_ZERO);
if (r == NULL) {
printf ("sorry cannot allocate state\n");
return NULL ;
}
/* increase refcount on parent, and set pointer */
if (dyn_type == DYN_LIMIT) {
struct ipfw_dyn_rule *parent = (struct ipfw_dyn_rule *)rule;
if ( parent->dyn_type != DYN_LIMIT_PARENT)
panic("invalid parent");
parent->count++ ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -