Changeset View
Changeset View
Standalone View
Standalone View
head/sys/netpfil/ipfw/ip_fw_dynamic.c
Show First 20 Lines • Show All 245 Lines • ▼ Show 20 Lines | |||||
SYSEND | SYSEND | ||||
#endif /* SYSCTL_NODE */ | #endif /* SYSCTL_NODE */ | ||||
#ifdef INET6 | #ifdef INET6 | ||||
static __inline int | static __inline int | ||||
hash_packet6(struct ipfw_flow_id *id) | hash_packet6(const struct ipfw_flow_id *id) | ||||
{ | { | ||||
u_int32_t i; | u_int32_t i; | ||||
i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ | i = (id->dst_ip6.__u6_addr.__u6_addr32[2]) ^ | ||||
(id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ | (id->dst_ip6.__u6_addr.__u6_addr32[3]) ^ | ||||
(id->src_ip6.__u6_addr.__u6_addr32[2]) ^ | (id->src_ip6.__u6_addr.__u6_addr32[2]) ^ | ||||
(id->src_ip6.__u6_addr.__u6_addr32[3]); | (id->src_ip6.__u6_addr.__u6_addr32[3]); | ||||
return ntohl(i); | return ntohl(i); | ||||
} | } | ||||
#endif | #endif | ||||
/* | /* | ||||
* IMPORTANT: the hash function for dynamic rules must be commutative | * IMPORTANT: the hash function for dynamic rules must be commutative | ||||
* in source and destination (ip,port), because rules are bidirectional | * in source and destination (ip,port), because rules are bidirectional | ||||
* and we want to find both in the same bucket. | * and we want to find both in the same bucket. | ||||
*/ | */ | ||||
static __inline int | static __inline int | ||||
hash_packet(struct ipfw_flow_id *id, int buckets) | hash_packet(const struct ipfw_flow_id *id, int buckets) | ||||
{ | { | ||||
u_int32_t i; | u_int32_t i; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
if (IS_IP6_FLOW_ID(id)) | if (IS_IP6_FLOW_ID(id)) | ||||
i = hash_packet6(id); | i = hash_packet6(id); | ||||
else | else | ||||
#endif /* INET6 */ | #endif /* INET6 */ | ||||
▲ Show 20 Lines • Show All 191 Lines • ▼ Show 20 Lines | static struct opcode_obj_rewrite dyn_opcodes[] = { | ||||
dyn_findbyname, dyn_findbykidx, | dyn_findbyname, dyn_findbykidx, | ||||
dyn_create, dyn_destroy | dyn_create, dyn_destroy | ||||
}, | }, | ||||
}; | }; | ||||
/** | /** | ||||
* Print customizable flow id description via log(9) facility. | * Print customizable flow id description via log(9) facility. | ||||
*/ | */ | ||||
static void | static void | ||||
print_dyn_rule_flags(struct ipfw_flow_id *id, int dyn_type, int log_flags, | print_dyn_rule_flags(const struct ipfw_flow_id *id, int dyn_type, | ||||
char *prefix, char *postfix) | int log_flags, char *prefix, char *postfix) | ||||
{ | { | ||||
struct in_addr da; | struct in_addr da; | ||||
#ifdef INET6 | #ifdef INET6 | ||||
char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; | char src[INET6_ADDRSTRLEN], dst[INET6_ADDRSTRLEN]; | ||||
#else | #else | ||||
char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; | char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN]; | ||||
#endif | #endif | ||||
Show All 17 Lines | |||||
#define print_dyn_rule(id, dtype, prefix, postfix) \ | #define print_dyn_rule(id, dtype, prefix, postfix) \ | ||||
print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix) | print_dyn_rule_flags(id, dtype, LOG_DEBUG, prefix, postfix) | ||||
#define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) | #define TIME_LEQ(a,b) ((int)((a)-(b)) <= 0) | ||||
#define TIME_LE(a,b) ((int)((a)-(b)) < 0) | #define TIME_LE(a,b) ((int)((a)-(b)) < 0) | ||||
static void | static void | ||||
dyn_update_proto_state(ipfw_dyn_rule *q, const struct ipfw_flow_id *id, | dyn_update_proto_state(ipfw_dyn_rule *q, const struct ipfw_flow_id *id, | ||||
const struct tcphdr *tcp, int dir) | const void *ulp, int dir) | ||||
{ | { | ||||
const struct tcphdr *tcp; | |||||
uint32_t ack; | uint32_t ack; | ||||
u_char flags; | u_char flags; | ||||
if (id->proto == IPPROTO_TCP) { | if (id->proto == IPPROTO_TCP) { | ||||
tcp = (const struct tcphdr *)ulp; | |||||
flags = id->_flags & (TH_FIN | TH_SYN | TH_RST); | flags = id->_flags & (TH_FIN | TH_SYN | TH_RST); | ||||
#define BOTH_SYN (TH_SYN | (TH_SYN << 8)) | #define BOTH_SYN (TH_SYN | (TH_SYN << 8)) | ||||
#define BOTH_FIN (TH_FIN | (TH_FIN << 8)) | #define BOTH_FIN (TH_FIN | (TH_FIN << 8)) | ||||
#define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) | #define TCP_FLAGS (TH_FLAGS | (TH_FLAGS << 8)) | ||||
#define ACK_FWD 0x10000 /* fwd ack seen */ | #define ACK_FWD 0x10000 /* fwd ack seen */ | ||||
#define ACK_REV 0x20000 /* rev ack seen */ | #define ACK_REV 0x20000 /* rev ack seen */ | ||||
q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); | q->state |= (dir == MATCH_FORWARD) ? flags : (flags << 8); | ||||
▲ Show 20 Lines • Show All 59 Lines • ▼ Show 20 Lines | #endif | ||||
q->expire = time_uptime + V_dyn_short_lifetime; | q->expire = time_uptime + V_dyn_short_lifetime; | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* Lookup a dynamic rule, locked version. | * Lookup a dynamic rule, locked version. | ||||
*/ | */ | ||||
static ipfw_dyn_rule * | static ipfw_dyn_rule * | ||||
lookup_dyn_rule_locked(struct ipfw_flow_id *pkt, int i, int *match_direction, | lookup_dyn_rule_locked(const struct ipfw_flow_id *pkt, const void *ulp, | ||||
struct tcphdr *tcp, uint16_t kidx) | int i, int *match_direction, uint16_t kidx) | ||||
{ | { | ||||
/* | /* | ||||
* Stateful ipfw extensions. | * Stateful ipfw extensions. | ||||
* Lookup into dynamic session queue. | * Lookup into dynamic session queue. | ||||
*/ | */ | ||||
ipfw_dyn_rule *prev, *q = NULL; | ipfw_dyn_rule *prev, *q = NULL; | ||||
int dir; | int dir; | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | lookup_dyn_rule_locked(const struct ipfw_flow_id *pkt, const void *ulp, | ||||
if (prev != NULL) { /* found and not in front */ | if (prev != NULL) { /* found and not in front */ | ||||
prev->next = q->next; | prev->next = q->next; | ||||
q->next = V_ipfw_dyn_v[i].head; | q->next = V_ipfw_dyn_v[i].head; | ||||
V_ipfw_dyn_v[i].head = q; | V_ipfw_dyn_v[i].head = q; | ||||
} | } | ||||
/* update state according to flags */ | /* update state according to flags */ | ||||
dyn_update_proto_state(q, pkt, tcp, dir); | dyn_update_proto_state(q, pkt, ulp, dir); | ||||
done: | done: | ||||
if (match_direction != NULL) | if (match_direction != NULL) | ||||
*match_direction = dir; | *match_direction = dir; | ||||
return (q); | return (q); | ||||
} | } | ||||
ipfw_dyn_rule * | struct ip_fw * | ||||
ipfw_lookup_dyn_rule(struct ipfw_flow_id *pkt, int *match_direction, | ipfw_dyn_lookup_state(const struct ipfw_flow_id *pkt, const void *ulp, | ||||
struct tcphdr *tcp, uint16_t kidx) | int pktlen, int *match_direction, uint16_t kidx) | ||||
{ | { | ||||
struct ip_fw *rule; | |||||
ipfw_dyn_rule *q; | ipfw_dyn_rule *q; | ||||
int i; | int i; | ||||
i = hash_packet(pkt, V_curr_dyn_buckets); | i = hash_packet(pkt, V_curr_dyn_buckets); | ||||
IPFW_BUCK_LOCK(i); | IPFW_BUCK_LOCK(i); | ||||
q = lookup_dyn_rule_locked(pkt, i, match_direction, tcp, kidx); | q = lookup_dyn_rule_locked(pkt, ulp, i, match_direction, kidx); | ||||
if (q == NULL) | if (q == NULL) | ||||
rule = NULL; | |||||
else { | |||||
rule = q->rule; | |||||
IPFW_INC_DYN_COUNTER(q, pktlen); | |||||
} | |||||
IPFW_BUCK_UNLOCK(i); | IPFW_BUCK_UNLOCK(i); | ||||
/* NB: return table locked when q is not NULL */ | return (rule); | ||||
return q; | |||||
} | } | ||||
/* | |||||
* Unlock bucket mtx | |||||
* @p - pointer to dynamic rule | |||||
*/ | |||||
void | |||||
ipfw_dyn_unlock(ipfw_dyn_rule *q) | |||||
{ | |||||
IPFW_BUCK_UNLOCK(q->bucket); | |||||
} | |||||
static int | static int | ||||
resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) | resize_dynamic_table(struct ip_fw_chain *chain, int nbuckets) | ||||
{ | { | ||||
int i, k, nbuckets_old; | int i, k, nbuckets_old; | ||||
ipfw_dyn_rule *q; | ipfw_dyn_rule *q; | ||||
struct ipfw_dyn_bucket *dyn_v, *dyn_v_old; | struct ipfw_dyn_bucket *dyn_v, *dyn_v_old; | ||||
/* Check if given number is power of 2 and less than 64k */ | /* Check if given number is power of 2 and less than 64k */ | ||||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Lines | |||||
* - regular rules (O_KEEP_STATE) | * - regular rules (O_KEEP_STATE) | ||||
* - rules for sessions with limited number of sess per user | * - rules for sessions with limited number of sess per user | ||||
* (O_LIMIT). When they are created, the parent is | * (O_LIMIT). When they are created, the parent is | ||||
* increased by 1, and decreased on delete. In this case, | * increased by 1, and decreased on delete. In this case, | ||||
* the third parameter is the parent rule and not the chain. | * the third parameter is the parent rule and not the chain. | ||||
* - "parent" rules for the above (O_LIMIT_PARENT). | * - "parent" rules for the above (O_LIMIT_PARENT). | ||||
*/ | */ | ||||
static ipfw_dyn_rule * | static ipfw_dyn_rule * | ||||
add_dyn_rule(struct ipfw_flow_id *id, int i, uint8_t dyn_type, | add_dyn_rule(const struct ipfw_flow_id *id, int i, uint8_t dyn_type, | ||||
struct ip_fw *rule, uint16_t kidx) | struct ip_fw *rule, uint16_t kidx) | ||||
{ | { | ||||
ipfw_dyn_rule *r; | ipfw_dyn_rule *r; | ||||
IPFW_BUCK_ASSERT(i); | IPFW_BUCK_ASSERT(i); | ||||
r = uma_zalloc(V_ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); | r = uma_zalloc(V_ipfw_dyn_rule_zone, M_NOWAIT | M_ZERO); | ||||
if (r == NULL) { | if (r == NULL) { | ||||
Show All 33 Lines | add_dyn_rule(const struct ipfw_flow_id *id, int i, uint8_t dyn_type, | ||||
return r; | return r; | ||||
} | } | ||||
/** | /** | ||||
* lookup dynamic parent rule using pkt and rule as search keys. | * lookup dynamic parent rule using pkt and rule as search keys. | ||||
* If the lookup fails, then install one. | * If the lookup fails, then install one. | ||||
*/ | */ | ||||
static ipfw_dyn_rule * | static ipfw_dyn_rule * | ||||
lookup_dyn_parent(struct ipfw_flow_id *pkt, int *pindex, struct ip_fw *rule, | lookup_dyn_parent(const struct ipfw_flow_id *pkt, int *pindex, | ||||
uint16_t kidx) | struct ip_fw *rule, uint16_t kidx) | ||||
{ | { | ||||
ipfw_dyn_rule *q; | ipfw_dyn_rule *q; | ||||
int i, is_v6; | int i, is_v6; | ||||
is_v6 = IS_IP6_FLOW_ID(pkt); | is_v6 = IS_IP6_FLOW_ID(pkt); | ||||
i = hash_packet( pkt, V_curr_dyn_buckets ); | i = hash_packet( pkt, V_curr_dyn_buckets ); | ||||
*pindex = i; | *pindex = i; | ||||
IPFW_BUCK_LOCK(i); | IPFW_BUCK_LOCK(i); | ||||
Show All 27 Lines | |||||
/** | /** | ||||
* Install dynamic state for rule type cmd->o.opcode | * Install dynamic state for rule type cmd->o.opcode | ||||
* | * | ||||
* Returns 1 (failure) if state is not installed because of errors or because | * Returns 1 (failure) if state is not installed because of errors or because | ||||
* session limitations are enforced. | * session limitations are enforced. | ||||
*/ | */ | ||||
int | int | ||||
ipfw_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, | ipfw_dyn_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, | ||||
ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg) | ipfw_insn_limit *cmd, struct ip_fw_args *args, uint32_t tablearg) | ||||
{ | { | ||||
ipfw_dyn_rule *q; | ipfw_dyn_rule *q; | ||||
int i; | int i; | ||||
DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", | DEB(print_dyn_rule(&args->f_id, cmd->o.opcode, "install_state", | ||||
(cmd->o.arg1 == 0 ? "": DYN_STATE_OBJ(chain, &cmd->o)->name));) | (cmd->o.arg1 == 0 ? "": DYN_STATE_OBJ(chain, &cmd->o)->name));) | ||||
i = hash_packet(&args->f_id, V_curr_dyn_buckets); | i = hash_packet(&args->f_id, V_curr_dyn_buckets); | ||||
IPFW_BUCK_LOCK(i); | IPFW_BUCK_LOCK(i); | ||||
q = lookup_dyn_rule_locked(&args->f_id, i, NULL, NULL, cmd->o.arg1); | q = lookup_dyn_rule_locked(&args->f_id, NULL, i, NULL, cmd->o.arg1); | ||||
if (q != NULL) { /* should never occur */ | if (q != NULL) { /* should never occur */ | ||||
DEB( | DEB( | ||||
if (last_log != time_uptime) { | if (last_log != time_uptime) { | ||||
last_log = time_uptime; | last_log = time_uptime; | ||||
printf("ipfw: %s: entry already present, done\n", | printf("ipfw: %s: entry already present, done\n", | ||||
__func__); | __func__); | ||||
}) | }) | ||||
IPFW_BUCK_UNLOCK(i); | IPFW_BUCK_UNLOCK(i); | ||||
▲ Show 20 Lines • Show All 765 Lines • Show Last 20 Lines |