Changeset View
Changeset View
Standalone View
Standalone View
sys/netpfil/ipfw/ip_fw_dynamic.c
| Show First 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
| #include <net/if.h> | #include <net/if.h> | ||||
| #include <net/if_var.h> | #include <net/if_var.h> | ||||
| #include <net/vnet.h> | #include <net/vnet.h> | ||||
| #include <netinet/in.h> | #include <netinet/in.h> | ||||
| #include <netinet/ip.h> | #include <netinet/ip.h> | ||||
| #include <netinet/ip_var.h> | #include <netinet/ip_var.h> | ||||
| #include <netinet/ip_fw.h> | #include <netinet/ip_fw.h> | ||||
| #include <netinet/udp.h> | |||||
| #include <netinet/tcp.h> | #include <netinet/tcp.h> | ||||
| #include <netinet/udp.h> | |||||
| #include <netinet/ip6.h> /* IN6_ARE_ADDR_EQUAL */ | #include <netinet/ip6.h> /* IN6_ARE_ADDR_EQUAL */ | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| #include <netinet6/in6_var.h> | #include <netinet6/in6_var.h> | ||||
| #include <netinet6/ip6_var.h> | #include <netinet6/ip6_var.h> | ||||
| #include <netinet6/scope6_var.h> | #include <netinet6/scope6_var.h> | ||||
| #endif | #endif | ||||
| ▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
| struct dyn_data { | struct dyn_data { | ||||
| void *parent; /* pointer to parent rule */ | void *parent; /* pointer to parent rule */ | ||||
| uint32_t chain_id; /* cached ruleset id */ | uint32_t chain_id; /* cached ruleset id */ | ||||
| uint32_t f_pos; /* cached rule index */ | uint32_t f_pos; /* cached rule index */ | ||||
| uint32_t hashval; /* hash value used for hash resize */ | uint32_t hashval; /* hash value used for hash resize */ | ||||
| uint16_t fibnum; /* fib used to send keepalives */ | uint16_t fibnum; /* fib used to send keepalives */ | ||||
| uint8_t _pad[3]; | uint8_t _pad; | ||||
| uint8_t flags; /* internal flags */ | uint8_t flags; /* internal flags */ | ||||
| uint16_t rulenum; /* parent rule number */ | uint32_t rulenum; /* parent rule number */ | ||||
| uint32_t ruleid; /* parent rule id */ | uint32_t ruleid; /* parent rule id */ | ||||
| uint32_t state; /* TCP session state and flags */ | uint32_t state; /* TCP session state and flags */ | ||||
| uint32_t ack_fwd; /* most recent ACKs in forward */ | uint32_t ack_fwd; /* most recent ACKs in forward */ | ||||
| uint32_t ack_rev; /* and reverse direction (used */ | uint32_t ack_rev; /* and reverse direction (used */ | ||||
| /* to generate keepalives) */ | /* to generate keepalives) */ | ||||
| uint32_t sync; /* synchronization time */ | uint32_t sync; /* synchronization time */ | ||||
| uint32_t expire; /* expire time */ | uint32_t expire; /* expire time */ | ||||
| uint64_t pcnt_fwd; /* bytes counter in forward */ | uint64_t pcnt_fwd; /* bytes counter in forward */ | ||||
| uint64_t bcnt_fwd; /* packets counter in forward */ | uint64_t bcnt_fwd; /* packets counter in forward */ | ||||
| uint64_t pcnt_rev; /* bytes counter in reverse */ | uint64_t pcnt_rev; /* bytes counter in reverse */ | ||||
| uint64_t bcnt_rev; /* packets counter in reverse */ | uint64_t bcnt_rev; /* packets counter in reverse */ | ||||
| }; | }; | ||||
| #define DPARENT_COUNT_DEC(p) do { \ | #define DPARENT_COUNT_DEC(p) do { \ | ||||
| MPASS(p->count > 0); \ | MPASS(p->count > 0); \ | ||||
| ck_pr_dec_32(&(p)->count); \ | ck_pr_dec_32(&(p)->count); \ | ||||
| } while (0) | } while (0) | ||||
| #define DPARENT_COUNT_INC(p) ck_pr_inc_32(&(p)->count) | #define DPARENT_COUNT_INC(p) ck_pr_inc_32(&(p)->count) | ||||
| #define DPARENT_COUNT(p) ck_pr_load_32(&(p)->count) | #define DPARENT_COUNT(p) ck_pr_load_32(&(p)->count) | ||||
| struct dyn_parent { | struct dyn_parent { | ||||
| void *parent; /* pointer to parent rule */ | void *parent; /* pointer to parent rule */ | ||||
| uint32_t count; /* number of linked states */ | uint32_t count; /* number of linked states */ | ||||
| uint8_t _pad[2]; | uint32_t rulenum; /* parent rule number */ | ||||
| uint16_t rulenum; /* parent rule number */ | |||||
| uint32_t ruleid; /* parent rule id */ | uint32_t ruleid; /* parent rule id */ | ||||
| uint32_t hashval; /* hash value used for hash resize */ | uint32_t hashval; /* hash value used for hash resize */ | ||||
| uint32_t expire; /* expire time */ | uint32_t expire; /* expire time */ | ||||
| }; | }; | ||||
| struct dyn_ipv4_state { | struct dyn_ipv4_state { | ||||
| uint8_t type; /* State type */ | uint8_t type; /* State type */ | ||||
| uint8_t proto; /* UL Protocol */ | uint8_t proto; /* UL Protocol */ | ||||
| uint16_t kidx; /* named object index */ | uint16_t spare; | ||||
| uint32_t kidx; /* named object index */ | |||||
| uint16_t sport, dport; /* ULP source and destination ports */ | uint16_t sport, dport; /* ULP source and destination ports */ | ||||
| in_addr_t src, dst; /* IPv4 source and destination */ | in_addr_t src, dst; /* IPv4 source and destination */ | ||||
| union { | union { | ||||
| struct dyn_data *data; | struct dyn_data *data; | ||||
| struct dyn_parent *limit; | struct dyn_parent *limit; | ||||
| }; | }; | ||||
| CK_SLIST_ENTRY(dyn_ipv4_state) entry; | CK_SLIST_ENTRY(dyn_ipv4_state) entry; | ||||
| ▲ Show 20 Lines • Show All 315 Lines • ▼ Show 20 Lines | |||||
| #endif /* !IPFIREWALL_DYNDEBUG */ | #endif /* !IPFIREWALL_DYNDEBUG */ | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| /* Functions to work with IPv6 states */ | /* Functions to work with IPv6 states */ | ||||
| static struct dyn_ipv6_state *dyn_lookup_ipv6_state( | static struct dyn_ipv6_state *dyn_lookup_ipv6_state( | ||||
| const struct ipfw_flow_id *, uint32_t, const void *, | const struct ipfw_flow_id *, uint32_t, const void *, | ||||
| struct ipfw_dyn_info *, int); | struct ipfw_dyn_info *, int); | ||||
| static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *, | static int dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *, | ||||
| uint32_t, const void *, int, uint32_t, uint16_t); | uint32_t, const void *, int, uint32_t, uint32_t); | ||||
| static struct dyn_ipv6_state *dyn_alloc_ipv6_state( | static struct dyn_ipv6_state *dyn_alloc_ipv6_state( | ||||
| const struct ipfw_flow_id *, uint32_t, uint16_t, uint8_t); | const struct ipfw_flow_id *, uint32_t, uint32_t, uint8_t); | ||||
| static int dyn_add_ipv6_state(void *, uint32_t, uint16_t, | static int dyn_add_ipv6_state(void *, uint32_t, uint32_t, | ||||
| const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t, | const struct ipfw_flow_id *, uint32_t, const void *, int, uint32_t, | ||||
| struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t); | struct ipfw_dyn_info *, uint16_t, uint32_t, uint8_t); | ||||
| static void dyn_export_ipv6_state(const struct dyn_ipv6_state *, | static void dyn_export_ipv6_state(const struct dyn_ipv6_state *, | ||||
| ipfw_dyn_rule *); | ipfw_dyn_rule *); | ||||
| static uint32_t dyn_getscopeid(const struct ip_fw_args *); | static uint32_t dyn_getscopeid(const struct ip_fw_args *); | ||||
| static void dyn_make_keepalive_ipv6(struct mbuf *, const struct in6_addr *, | static void dyn_make_keepalive_ipv6(struct mbuf *, const struct in6_addr *, | ||||
| const struct in6_addr *, uint32_t, uint32_t, uint32_t, uint16_t, | const struct in6_addr *, uint32_t, uint32_t, uint32_t, uint16_t, | ||||
| uint16_t); | uint16_t); | ||||
| static void dyn_enqueue_keepalive_ipv6(struct mbufq *, | static void dyn_enqueue_keepalive_ipv6(struct mbufq *, | ||||
| const struct dyn_ipv6_state *); | const struct dyn_ipv6_state *); | ||||
| static void dyn_send_keepalive_ipv6(struct ip_fw_chain *); | static void dyn_send_keepalive_ipv6(struct ip_fw_chain *); | ||||
| static struct dyn_ipv6_state *dyn_lookup_ipv6_parent( | static struct dyn_ipv6_state *dyn_lookup_ipv6_parent( | ||||
| const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t, | const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint32_t, | ||||
| uint32_t); | uint32_t); | ||||
| static struct dyn_ipv6_state *dyn_lookup_ipv6_parent_locked( | static struct dyn_ipv6_state *dyn_lookup_ipv6_parent_locked( | ||||
| const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint16_t, | const struct ipfw_flow_id *, uint32_t, const void *, uint32_t, uint32_t, | ||||
| uint32_t); | uint32_t); | ||||
| static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint16_t, | static struct dyn_ipv6_state *dyn_add_ipv6_parent(void *, uint32_t, uint32_t, | ||||
| const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t, uint16_t); | const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t, uint32_t); | ||||
| #endif /* INET6 */ | #endif /* INET6 */ | ||||
| /* Functions to work with limit states */ | /* Functions to work with limit states */ | ||||
| static void *dyn_get_parent_state(const struct ipfw_flow_id *, uint32_t, | static void *dyn_get_parent_state(const struct ipfw_flow_id *, uint32_t, | ||||
| struct ip_fw *, uint32_t, uint32_t, uint16_t); | struct ip_fw *, uint32_t, uint32_t, uint32_t); | ||||
| static struct dyn_ipv4_state *dyn_lookup_ipv4_parent( | static struct dyn_ipv4_state *dyn_lookup_ipv4_parent( | ||||
| const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t); | const struct ipfw_flow_id *, const void *, uint32_t, uint32_t, uint32_t); | ||||
| static struct dyn_ipv4_state *dyn_lookup_ipv4_parent_locked( | static struct dyn_ipv4_state *dyn_lookup_ipv4_parent_locked( | ||||
| const struct ipfw_flow_id *, const void *, uint32_t, uint16_t, uint32_t); | const struct ipfw_flow_id *, const void *, uint32_t, uint32_t, uint32_t); | ||||
| static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint16_t, | static struct dyn_parent *dyn_alloc_parent(void *, uint32_t, uint32_t, | ||||
| uint32_t); | uint32_t); | ||||
| static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint16_t, | static struct dyn_ipv4_state *dyn_add_ipv4_parent(void *, uint32_t, uint32_t, | ||||
| const struct ipfw_flow_id *, uint32_t, uint32_t, uint16_t); | const struct ipfw_flow_id *, uint32_t, uint32_t, uint32_t); | ||||
| static void dyn_tick(void *); | static void dyn_tick(void *); | ||||
| static void dyn_expire_states(struct ip_fw_chain *, ipfw_range_tlv *); | static void dyn_expire_states(struct ip_fw_chain *, ipfw_range_tlv *); | ||||
| static void dyn_free_states(struct ip_fw_chain *); | static void dyn_free_states(struct ip_fw_chain *); | ||||
| static void dyn_export_parent(const struct dyn_parent *, uint16_t, uint8_t, | static void dyn_export_parent(const struct dyn_parent *, uint32_t, uint8_t, | ||||
| ipfw_dyn_rule *); | ipfw_dyn_rule *); | ||||
| static void dyn_export_data(const struct dyn_data *, uint16_t, uint8_t, | static void dyn_export_data(const struct dyn_data *, uint32_t, uint8_t, | ||||
| uint8_t, ipfw_dyn_rule *); | uint8_t, ipfw_dyn_rule *); | ||||
| static uint32_t dyn_update_tcp_state(struct dyn_data *, | static uint32_t dyn_update_tcp_state(struct dyn_data *, | ||||
| const struct ipfw_flow_id *, const struct tcphdr *, int); | const struct ipfw_flow_id *, const struct tcphdr *, int); | ||||
| static void dyn_update_proto_state(struct dyn_data *, | static void dyn_update_proto_state(struct dyn_data *, | ||||
| const struct ipfw_flow_id *, const void *, int, int); | const struct ipfw_flow_id *, const void *, int, int); | ||||
| /* Functions to work with IPv4 states */ | /* Functions to work with IPv4 states */ | ||||
| struct dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *, | struct dyn_ipv4_state *dyn_lookup_ipv4_state(const struct ipfw_flow_id *, | ||||
| const void *, struct ipfw_dyn_info *, int); | const void *, struct ipfw_dyn_info *, int); | ||||
| static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *, | static int dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *, | ||||
| const void *, int, uint32_t, uint16_t); | const void *, int, uint32_t, uint32_t); | ||||
| static struct dyn_ipv4_state *dyn_alloc_ipv4_state( | static struct dyn_ipv4_state *dyn_alloc_ipv4_state( | ||||
| const struct ipfw_flow_id *, uint16_t, uint8_t); | const struct ipfw_flow_id *, uint32_t, uint8_t); | ||||
| static int dyn_add_ipv4_state(void *, uint32_t, uint16_t, | static int dyn_add_ipv4_state(void *, uint32_t, uint32_t, | ||||
| const struct ipfw_flow_id *, const void *, int, uint32_t, | const struct ipfw_flow_id *, const void *, int, uint32_t, | ||||
| struct ipfw_dyn_info *, uint16_t, uint16_t, uint8_t); | struct ipfw_dyn_info *, uint16_t, uint32_t, uint8_t); | ||||
| static void dyn_export_ipv4_state(const struct dyn_ipv4_state *, | static void dyn_export_ipv4_state(const struct dyn_ipv4_state *, | ||||
| ipfw_dyn_rule *); | ipfw_dyn_rule *); | ||||
| /* | /* | ||||
| * Named states support. | * Named states support. | ||||
| */ | */ | ||||
| static char *default_state_name = "default"; | static char *default_state_name = "default"; | ||||
| struct dyn_state_obj { | struct dyn_state_obj { | ||||
| struct named_object no; | struct named_object no; | ||||
| char name[64]; | char name[64]; | ||||
| }; | }; | ||||
| #define DYN_STATE_OBJ(ch, cmd) \ | |||||
| ((struct dyn_state_obj *)SRV_OBJECT(ch, (cmd)->arg1)) | |||||
| /* | /* | ||||
| * Classifier callback. | * Classifier callback. | ||||
| * Return 0 if opcode contains object that should be referenced | * Return 0 if opcode contains object that should be referenced | ||||
| * or rewritten. | * or rewritten. | ||||
| */ | */ | ||||
| static int | static int | ||||
| dyn_classify(ipfw_insn *cmd, uint16_t *puidx, uint8_t *ptype) | dyn_classify(ipfw_insn *cmd0, uint32_t *puidx, uint8_t *ptype) | ||||
| { | { | ||||
| ipfw_insn_kidx *cmd; | |||||
| DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); | if (F_LEN(cmd0) < 2) | ||||
| return (EINVAL); | |||||
| /* | |||||
| * NOTE: ipfw_insn_kidx and ipfw_insn_limit has overlapped kidx | |||||
| * field, so we can use one type to get access to kidx field. | |||||
| */ | |||||
| cmd = insntod(cmd0, kidx); | |||||
| DYN_DEBUG("opcode %u, kidx %u", cmd0->opcode, cmd->kidx); | |||||
| /* Don't rewrite "check-state any" */ | /* Don't rewrite "check-state any" */ | ||||
| if (cmd->arg1 == 0 && | if (cmd->kidx == 0 && | ||||
| cmd->opcode == O_CHECK_STATE) | cmd0->opcode == O_CHECK_STATE) | ||||
| return (1); | return (1); | ||||
| *puidx = cmd->arg1; | *puidx = cmd->kidx; | ||||
| *ptype = 0; | *ptype = 0; | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| dyn_update(ipfw_insn *cmd, uint16_t idx) | dyn_update(ipfw_insn *cmd0, uint32_t idx) | ||||
| { | { | ||||
| cmd->arg1 = idx; | insntod(cmd0, kidx)->kidx = idx; | ||||
| DYN_DEBUG("opcode %d, arg1 %d", cmd->opcode, cmd->arg1); | DYN_DEBUG("opcode %u, kidx %u", cmd0->opcode, idx); | ||||
| } | } | ||||
| static int | static int | ||||
| dyn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | dyn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | ||||
| struct named_object **pno) | struct named_object **pno) | ||||
| { | { | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| const char *name; | const char *name; | ||||
| DYN_DEBUG("uidx %d", ti->uidx); | DYN_DEBUG("uidx %u", ti->uidx); | ||||
| if (ti->uidx != 0) { | if (ti->uidx != 0) { | ||||
| if (ti->tlvs == NULL) | if (ti->tlvs == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| /* Search ntlv in the buffer provided by user */ | /* Search ntlv in the buffer provided by user */ | ||||
| ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, | ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, | ||||
| IPFW_TLV_STATE_NAME); | IPFW_TLV_STATE_NAME); | ||||
| if (ntlv == NULL) | if (ntlv == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| Show All 11 Lines | dyn_findbyname(struct ip_fw_chain *ch, struct tid_info *ti, | ||||
| * We always return success here. | * We always return success here. | ||||
| * The caller will check *pno and mark object as unresolved, | * The caller will check *pno and mark object as unresolved, | ||||
| * then it will automatically create "default" object. | * then it will automatically create "default" object. | ||||
| */ | */ | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static struct named_object * | static struct named_object * | ||||
| dyn_findbykidx(struct ip_fw_chain *ch, uint16_t idx) | dyn_findbykidx(struct ip_fw_chain *ch, uint32_t idx) | ||||
| { | { | ||||
| DYN_DEBUG("kidx %d", idx); | DYN_DEBUG("kidx %u", idx); | ||||
| return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); | return (ipfw_objhash_lookup_kidx(CHAIN_TO_SRV(ch), idx)); | ||||
| } | } | ||||
| static int | static int | ||||
| dyn_create(struct ip_fw_chain *ch, struct tid_info *ti, | dyn_create(struct ip_fw_chain *ch, struct tid_info *ti, | ||||
| uint16_t *pkidx) | uint32_t *pkidx) | ||||
| { | { | ||||
| struct namedobj_instance *ni; | struct namedobj_instance *ni; | ||||
| struct dyn_state_obj *obj; | struct dyn_state_obj *obj; | ||||
| struct named_object *no; | struct named_object *no; | ||||
| ipfw_obj_ntlv *ntlv; | ipfw_obj_ntlv *ntlv; | ||||
| char *name; | char *name; | ||||
| DYN_DEBUG("uidx %d", ti->uidx); | DYN_DEBUG("uidx %u", ti->uidx); | ||||
| if (ti->uidx != 0) { | if (ti->uidx != 0) { | ||||
| if (ti->tlvs == NULL) | if (ti->tlvs == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, | ntlv = ipfw_find_name_tlv_type(ti->tlvs, ti->tlen, ti->uidx, | ||||
| IPFW_TLV_STATE_NAME); | IPFW_TLV_STATE_NAME); | ||||
| if (ntlv == NULL) | if (ntlv == NULL) | ||||
| return (EINVAL); | return (EINVAL); | ||||
| name = ntlv->name; | name = ntlv->name; | ||||
| Show All 13 Lines | if (no != NULL) { | ||||
| /* | /* | ||||
| * Object is already created. | * Object is already created. | ||||
| * Just return its kidx and bump refcount. | * Just return its kidx and bump refcount. | ||||
| */ | */ | ||||
| *pkidx = no->kidx; | *pkidx = no->kidx; | ||||
| no->refcnt++; | no->refcnt++; | ||||
| IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
| free(obj, M_IPFW); | free(obj, M_IPFW); | ||||
| DYN_DEBUG("\tfound kidx %d", *pkidx); | DYN_DEBUG("\tfound kidx %u for name '%s'", *pkidx, no->name); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| if (ipfw_objhash_alloc_idx(ni, &obj->no.kidx) != 0) { | if (ipfw_objhash_alloc_idx(ni, &obj->no.kidx) != 0) { | ||||
| DYN_DEBUG("\talloc_idx failed for %s", name); | DYN_DEBUG("\talloc_idx failed for %s", name); | ||||
| IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
| free(obj, M_IPFW); | free(obj, M_IPFW); | ||||
| return (ENOSPC); | return (ENOSPC); | ||||
| } | } | ||||
| ipfw_objhash_add(ni, &obj->no); | ipfw_objhash_add(ni, &obj->no); | ||||
| SRV_OBJECT(ch, obj->no.kidx) = obj; | SRV_OBJECT(ch, obj->no.kidx) = obj; | ||||
| obj->no.refcnt++; | obj->no.refcnt++; | ||||
| *pkidx = obj->no.kidx; | *pkidx = obj->no.kidx; | ||||
| IPFW_UH_WUNLOCK(ch); | IPFW_UH_WUNLOCK(ch); | ||||
| DYN_DEBUG("\tcreated kidx %d", *pkidx); | DYN_DEBUG("\tcreated kidx %u for name '%s'", *pkidx, name); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| dyn_destroy(struct ip_fw_chain *ch, struct named_object *no) | dyn_destroy(struct ip_fw_chain *ch, struct named_object *no) | ||||
| { | { | ||||
| struct dyn_state_obj *obj; | struct dyn_state_obj *obj; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| KASSERT(no->etlv == IPFW_TLV_STATE_NAME, | KASSERT(no->etlv == IPFW_TLV_STATE_NAME, | ||||
| ("%s: wrong object type %u", __func__, no->etlv)); | ("%s: wrong object type %u", __func__, no->etlv)); | ||||
| KASSERT(no->refcnt == 1, | KASSERT(no->refcnt == 1, | ||||
| ("Destroying object '%s' (type %u, idx %u) with refcnt %u", | ("Destroying object '%s' (type %u, idx %u) with refcnt %u", | ||||
| no->name, no->etlv, no->kidx, no->refcnt)); | no->name, no->etlv, no->kidx, no->refcnt)); | ||||
| DYN_DEBUG("kidx %d", no->kidx); | DYN_DEBUG("kidx %u", no->kidx); | ||||
| obj = SRV_OBJECT(ch, no->kidx); | obj = SRV_OBJECT(ch, no->kidx); | ||||
| SRV_OBJECT(ch, no->kidx) = NULL; | SRV_OBJECT(ch, no->kidx) = NULL; | ||||
| ipfw_objhash_del(CHAIN_TO_SRV(ch), no); | ipfw_objhash_del(CHAIN_TO_SRV(ch), no); | ||||
| ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), no->kidx); | ipfw_objhash_free_idx(CHAIN_TO_SRV(ch), no->kidx); | ||||
| free(obj, M_IPFW); | free(obj, M_IPFW); | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 345 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* | /* | ||||
| * Lookup IPv4 state. | * Lookup IPv4 state. | ||||
| * Simplifed version is used to check that matching state doesn't exist. | * Simplifed version is used to check that matching state doesn't exist. | ||||
| */ | */ | ||||
| static int | static int | ||||
| dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *pkt, | dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *pkt, | ||||
| const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx) | const void *ulp, int pktlen, uint32_t bucket, uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| int dir; | int dir; | ||||
| dir = MATCH_NONE; | dir = MATCH_NONE; | ||||
| DYN_BUCKET_ASSERT(bucket); | DYN_BUCKET_ASSERT(bucket); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv4[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv4[bucket], entry) { | ||||
| if (s->proto != pkt->proto || | if (s->proto != pkt->proto || | ||||
| Show All 13 Lines | dyn_lookup_ipv4_state_locked(const struct ipfw_flow_id *pkt, | ||||
| } | } | ||||
| if (s != NULL) | if (s != NULL) | ||||
| dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir); | dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir); | ||||
| return (s != NULL); | return (s != NULL); | ||||
| } | } | ||||
| struct dyn_ipv4_state * | struct dyn_ipv4_state * | ||||
| dyn_lookup_ipv4_parent(const struct ipfw_flow_id *pkt, const void *rule, | dyn_lookup_ipv4_parent(const struct ipfw_flow_id *pkt, const void *rule, | ||||
| uint32_t ruleid, uint16_t rulenum, uint32_t hashval) | uint32_t ruleid, uint32_t rulenum, uint32_t hashval) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| uint32_t version, bucket; | uint32_t version, bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| restart: | restart: | ||||
| version = DYN_BUCKET_VERSION(bucket, ipv4_parent_del); | version = DYN_BUCKET_VERSION(bucket, ipv4_parent_del); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) { | ||||
| Show All 19 Lines | if (s->limit->parent == rule && | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return (s); | return (s); | ||||
| } | } | ||||
| static struct dyn_ipv4_state * | static struct dyn_ipv4_state * | ||||
| dyn_lookup_ipv4_parent_locked(const struct ipfw_flow_id *pkt, | dyn_lookup_ipv4_parent_locked(const struct ipfw_flow_id *pkt, | ||||
| const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket) | const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t bucket) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| DYN_BUCKET_ASSERT(bucket); | DYN_BUCKET_ASSERT(bucket); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv4_parent[bucket], entry) { | ||||
| if (s->limit->parent == rule && | if (s->limit->parent == rule && | ||||
| s->limit->ruleid == ruleid && | s->limit->ruleid == ruleid && | ||||
| s->limit->rulenum == rulenum && | s->limit->rulenum == rulenum && | ||||
| ▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* | /* | ||||
| * Lookup IPv6 state. | * Lookup IPv6 state. | ||||
| * Simplifed version is used to check that matching state doesn't exist. | * Simplifed version is used to check that matching state doesn't exist. | ||||
| */ | */ | ||||
| static int | static int | ||||
| dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| const void *ulp, int pktlen, uint32_t bucket, uint16_t kidx) | const void *ulp, int pktlen, uint32_t bucket, uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| int dir; | int dir; | ||||
| dir = MATCH_NONE; | dir = MATCH_NONE; | ||||
| DYN_BUCKET_ASSERT(bucket); | DYN_BUCKET_ASSERT(bucket); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv6[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv6[bucket], entry) { | ||||
| if (s->proto != pkt->proto || s->kidx != kidx || | if (s->proto != pkt->proto || s->kidx != kidx || | ||||
| Show All 14 Lines | dyn_lookup_ipv6_state_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| } | } | ||||
| if (s != NULL) | if (s != NULL) | ||||
| dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir); | dyn_update_proto_state(s->data, pkt, ulp, pktlen, dir); | ||||
| return (s != NULL); | return (s != NULL); | ||||
| } | } | ||||
| static struct dyn_ipv6_state * | static struct dyn_ipv6_state * | ||||
| dyn_lookup_ipv6_parent(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_lookup_ipv6_parent(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t hashval) | const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t hashval) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| uint32_t version, bucket; | uint32_t version, bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| restart: | restart: | ||||
| version = DYN_BUCKET_VERSION(bucket, ipv6_parent_del); | version = DYN_BUCKET_VERSION(bucket, ipv6_parent_del); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) { | ||||
| Show All 20 Lines | if (s->limit->parent == rule && | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| return (s); | return (s); | ||||
| } | } | ||||
| static struct dyn_ipv6_state * | static struct dyn_ipv6_state * | ||||
| dyn_lookup_ipv6_parent_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_lookup_ipv6_parent_locked(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| const void *rule, uint32_t ruleid, uint16_t rulenum, uint32_t bucket) | const void *rule, uint32_t ruleid, uint32_t rulenum, uint32_t bucket) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| DYN_BUCKET_ASSERT(bucket); | DYN_BUCKET_ASSERT(bucket); | ||||
| CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) { | CK_SLIST_FOREACH(s, &V_dyn_ipv6_parent[bucket], entry) { | ||||
| if (s->limit->parent == rule && | if (s->limit->parent == rule && | ||||
| s->limit->ruleid == ruleid && | s->limit->ruleid == ruleid && | ||||
| s->limit->rulenum == rulenum && | s->limit->rulenum == rulenum && | ||||
| Show All 21 Lines | |||||
| struct ip_fw * | struct ip_fw * | ||||
| ipfw_dyn_lookup_state(const struct ip_fw_args *args, const void *ulp, | ipfw_dyn_lookup_state(const struct ip_fw_args *args, const void *ulp, | ||||
| int pktlen, const ipfw_insn *cmd, struct ipfw_dyn_info *info) | int pktlen, const ipfw_insn *cmd, struct ipfw_dyn_info *info) | ||||
| { | { | ||||
| struct dyn_data *data; | struct dyn_data *data; | ||||
| struct ip_fw *rule; | struct ip_fw *rule; | ||||
| IPFW_RLOCK_ASSERT(&V_layer3_chain); | IPFW_RLOCK_ASSERT(&V_layer3_chain); | ||||
| MPASS(F_LEN(cmd) >= F_INSN_SIZE(ipfw_insn_kidx)); | |||||
| data = NULL; | data = NULL; | ||||
| rule = NULL; | rule = NULL; | ||||
| info->kidx = cmd->arg1; | info->kidx = insntoc(cmd, kidx)->kidx; | ||||
| info->direction = MATCH_NONE; | info->direction = MATCH_NONE; | ||||
| info->hashval = hash_packet(&args->f_id); | info->hashval = hash_packet(&args->f_id); | ||||
| DYNSTATE_CRITICAL_ENTER(); | DYNSTATE_CRITICAL_ENTER(); | ||||
| if (IS_IP4_FLOW_ID(&args->f_id)) { | if (IS_IP4_FLOW_ID(&args->f_id)) { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| s = dyn_lookup_ipv4_state(&args->f_id, ulp, info, pktlen); | s = dyn_lookup_ipv4_state(&args->f_id, ulp, info, pktlen); | ||||
| ▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (data->chain_id != V_layer3_chain.id) { | ||||
| * flag and rule will keep around. So we can return | * flag and rule will keep around. So we can return | ||||
| * it. But since it is not in the rules map, we need | * it. But since it is not in the rules map, we need | ||||
| * return such f_pos value, so after the state | * return such f_pos value, so after the state | ||||
| * handling if the search will continue, the next rule | * handling if the search will continue, the next rule | ||||
| * will be the last one - the default rule. | * will be the last one - the default rule. | ||||
| */ | */ | ||||
| if (V_layer3_chain.map[data->f_pos] == rule) { | if (V_layer3_chain.map[data->f_pos] == rule) { | ||||
| data->chain_id = V_layer3_chain.id; | data->chain_id = V_layer3_chain.id; | ||||
| info->f_pos = data->f_pos; | |||||
| } else if (V_dyn_keep_states != 0) { | } else if (V_dyn_keep_states != 0) { | ||||
| /* | /* | ||||
| * The original rule pointer is still usable. | * The original rule pointer is still usable. | ||||
| * So, we return it, but f_pos need to be | * So, we return it, but f_pos need to be | ||||
| * changed to point to the penultimate rule. | * changed to point to the penultimate rule. | ||||
| */ | */ | ||||
| MPASS(V_layer3_chain.n_rules > 1); | MPASS(V_layer3_chain.n_rules > 1); | ||||
| data->chain_id = V_layer3_chain.id; | data->chain_id = V_layer3_chain.id; | ||||
| data->f_pos = V_layer3_chain.n_rules - 2; | data->f_pos = V_layer3_chain.n_rules - 2; | ||||
| info->f_pos = data->f_pos; | |||||
| } else { | } else { | ||||
| rule = NULL; | rule = NULL; | ||||
| info->direction = MATCH_NONE; | info->direction = MATCH_NONE; | ||||
| DYN_DEBUG("rule %p [%u, %u] is considered " | DYN_DEBUG("rule %p [%u, %u] is considered " | ||||
| "invalid in data %p", rule, data->ruleid, | "invalid in data %p", rule, data->ruleid, | ||||
| data->rulenum, data); | data->rulenum, data); | ||||
| /* info->f_pos doesn't matter here. */ | /* info->f_pos doesn't matter here. */ | ||||
| } | } | ||||
| } else | } | ||||
| info->f_pos = data->f_pos; | info->f_pos = data->f_pos; | ||||
| } | } | ||||
| DYNSTATE_CRITICAL_EXIT(); | DYNSTATE_CRITICAL_EXIT(); | ||||
| #if 0 | #if 0 | ||||
| /* | /* | ||||
| * Return MATCH_NONE if parent rule is in disabled set. | * Return MATCH_NONE if parent rule is in disabled set. | ||||
| * This will lead to creation of new similar state that | * This will lead to creation of new similar state that | ||||
| * will be added into head of this bucket. | * will be added into head of this bucket. | ||||
| * | * | ||||
| * XXXAE: we need to be able update state's set when parent | * XXXAE: we need to be able update state's set when parent | ||||
| * rule set is changed. | * rule set is changed. | ||||
| */ | */ | ||||
| if (rule != NULL && (V_set_disable & (1 << rule->set))) { | if (rule != NULL && (V_set_disable & (1 << rule->set))) { | ||||
| rule = NULL; | rule = NULL; | ||||
| info->direction = MATCH_NONE; | info->direction = MATCH_NONE; | ||||
| } | } | ||||
| #endif | #endif | ||||
| return (rule); | return (rule); | ||||
| } | } | ||||
| static struct dyn_parent * | static struct dyn_parent * | ||||
| dyn_alloc_parent(void *parent, uint32_t ruleid, uint16_t rulenum, | dyn_alloc_parent(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| uint32_t hashval) | uint32_t hashval) | ||||
| { | { | ||||
| struct dyn_parent *limit; | struct dyn_parent *limit; | ||||
| limit = uma_zalloc(V_dyn_parent_zone, M_NOWAIT | M_ZERO); | limit = uma_zalloc(V_dyn_parent_zone, M_NOWAIT | M_ZERO); | ||||
| if (limit == NULL) { | if (limit == NULL) { | ||||
| if (last_log != time_uptime) { | if (last_log != time_uptime) { | ||||
| last_log = time_uptime; | last_log = time_uptime; | ||||
| Show All 9 Lines | dyn_alloc_parent(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| limit->ruleid = ruleid; | limit->ruleid = ruleid; | ||||
| limit->rulenum = rulenum; | limit->rulenum = rulenum; | ||||
| limit->hashval = hashval; | limit->hashval = hashval; | ||||
| limit->expire = time_uptime + V_dyn_short_lifetime; | limit->expire = time_uptime + V_dyn_short_lifetime; | ||||
| return (limit); | return (limit); | ||||
| } | } | ||||
| static struct dyn_data * | static struct dyn_data * | ||||
| dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint16_t rulenum, | dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, | const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, | ||||
| uint32_t hashval, uint16_t fibnum) | uint32_t hashval, uint16_t fibnum) | ||||
| { | { | ||||
| struct dyn_data *data; | struct dyn_data *data; | ||||
| data = uma_zalloc(V_dyn_data_zone, M_NOWAIT | M_ZERO); | data = uma_zalloc(V_dyn_data_zone, M_NOWAIT | M_ZERO); | ||||
| if (data == NULL) { | if (data == NULL) { | ||||
| if (last_log != time_uptime) { | if (last_log != time_uptime) { | ||||
| Show All 11 Lines | dyn_alloc_dyndata(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| data->fibnum = fibnum; | data->fibnum = fibnum; | ||||
| data->hashval = hashval; | data->hashval = hashval; | ||||
| data->expire = time_uptime + V_dyn_syn_lifetime; | data->expire = time_uptime + V_dyn_syn_lifetime; | ||||
| dyn_update_proto_state(data, pkt, ulp, pktlen, MATCH_FORWARD); | dyn_update_proto_state(data, pkt, ulp, pktlen, MATCH_FORWARD); | ||||
| return (data); | return (data); | ||||
| } | } | ||||
| static struct dyn_ipv4_state * | static struct dyn_ipv4_state * | ||||
| dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint16_t kidx, | dyn_alloc_ipv4_state(const struct ipfw_flow_id *pkt, uint32_t kidx, | ||||
| uint8_t type) | uint8_t type) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| s = uma_zalloc(V_dyn_ipv4_zone, M_NOWAIT | M_ZERO); | s = uma_zalloc(V_dyn_ipv4_zone, M_NOWAIT | M_ZERO); | ||||
| if (s == NULL) | if (s == NULL) | ||||
| return (NULL); | return (NULL); | ||||
| Show All 10 Lines | |||||
| /* | /* | ||||
| * Add IPv4 parent state. | * Add IPv4 parent state. | ||||
| * Returns pointer to parent state. When it is not NULL we are in | * Returns pointer to parent state. When it is not NULL we are in | ||||
| * critical section and pointer protected by hazard pointer. | * critical section and pointer protected by hazard pointer. | ||||
| * When some error occurs, it returns NULL and exit from critical section | * When some error occurs, it returns NULL and exit from critical section | ||||
| * is not needed. | * is not needed. | ||||
| */ | */ | ||||
| static struct dyn_ipv4_state * | static struct dyn_ipv4_state * | ||||
| dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint16_t rulenum, | dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint32_t rulenum, | ||||
| const struct ipfw_flow_id *pkt, uint32_t hashval, uint32_t version, | const struct ipfw_flow_id *pkt, uint32_t hashval, uint32_t version, | ||||
| uint16_t kidx) | uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| struct dyn_parent *limit; | struct dyn_parent *limit; | ||||
| uint32_t bucket; | uint32_t bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| DYN_BUCKET_LOCK(bucket); | DYN_BUCKET_LOCK(bucket); | ||||
| if (version != DYN_BUCKET_VERSION(bucket, ipv4_parent_add)) { | if (version != DYN_BUCKET_VERSION(bucket, ipv4_parent_add)) { | ||||
| Show All 34 Lines | dyn_add_ipv4_parent(void *rule, uint32_t ruleid, uint32_t rulenum, | ||||
| DYN_BUCKET_VERSION_BUMP(bucket, ipv4_parent_add); | DYN_BUCKET_VERSION_BUMP(bucket, ipv4_parent_add); | ||||
| DYNSTATE_CRITICAL_ENTER(); | DYNSTATE_CRITICAL_ENTER(); | ||||
| DYNSTATE_PROTECT(s); | DYNSTATE_PROTECT(s); | ||||
| DYN_BUCKET_UNLOCK(bucket); | DYN_BUCKET_UNLOCK(bucket); | ||||
| return (s); | return (s); | ||||
| } | } | ||||
| static int | static int | ||||
| dyn_add_ipv4_state(void *parent, uint32_t ruleid, uint16_t rulenum, | dyn_add_ipv4_state(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, | const struct ipfw_flow_id *pkt, const void *ulp, int pktlen, | ||||
| uint32_t hashval, struct ipfw_dyn_info *info, uint16_t fibnum, | uint32_t hashval, struct ipfw_dyn_info *info, uint16_t fibnum, | ||||
| uint16_t kidx, uint8_t type) | uint32_t kidx, uint8_t type) | ||||
| { | { | ||||
| struct dyn_ipv4_state *s; | struct dyn_ipv4_state *s; | ||||
| void *data; | void *data; | ||||
| uint32_t bucket; | uint32_t bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| DYN_BUCKET_LOCK(bucket); | DYN_BUCKET_LOCK(bucket); | ||||
| if (info->direction == MATCH_UNKNOWN || | if (info->direction == MATCH_UNKNOWN || | ||||
| Show All 31 Lines | dyn_add_ipv4_state(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| DYN_BUCKET_VERSION_BUMP(bucket, ipv4_add); | DYN_BUCKET_VERSION_BUMP(bucket, ipv4_add); | ||||
| DYN_BUCKET_UNLOCK(bucket); | DYN_BUCKET_UNLOCK(bucket); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| static struct dyn_ipv6_state * | static struct dyn_ipv6_state * | ||||
| dyn_alloc_ipv6_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_alloc_ipv6_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| uint16_t kidx, uint8_t type) | uint32_t kidx, uint8_t type) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| s = uma_zalloc(V_dyn_ipv6_zone, M_NOWAIT | M_ZERO); | s = uma_zalloc(V_dyn_ipv6_zone, M_NOWAIT | M_ZERO); | ||||
| if (s == NULL) | if (s == NULL) | ||||
| return (NULL); | return (NULL); | ||||
| s->type = type; | s->type = type; | ||||
| Show All 10 Lines | |||||
| /* | /* | ||||
| * Add IPv6 parent state. | * Add IPv6 parent state. | ||||
| * Returns pointer to parent state. When it is not NULL we are in | * Returns pointer to parent state. When it is not NULL we are in | ||||
| * critical section and pointer protected by hazard pointer. | * critical section and pointer protected by hazard pointer. | ||||
| * When some error occurs, it return NULL and exit from critical section | * When some error occurs, it return NULL and exit from critical section | ||||
| * is not needed. | * is not needed. | ||||
| */ | */ | ||||
| static struct dyn_ipv6_state * | static struct dyn_ipv6_state * | ||||
| dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint16_t rulenum, | dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint32_t rulenum, | ||||
| const struct ipfw_flow_id *pkt, uint32_t zoneid, uint32_t hashval, | const struct ipfw_flow_id *pkt, uint32_t zoneid, uint32_t hashval, | ||||
| uint32_t version, uint16_t kidx) | uint32_t version, uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| struct dyn_parent *limit; | struct dyn_parent *limit; | ||||
| uint32_t bucket; | uint32_t bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| DYN_BUCKET_LOCK(bucket); | DYN_BUCKET_LOCK(bucket); | ||||
| if (version != DYN_BUCKET_VERSION(bucket, ipv6_parent_add)) { | if (version != DYN_BUCKET_VERSION(bucket, ipv6_parent_add)) { | ||||
| Show All 34 Lines | dyn_add_ipv6_parent(void *rule, uint32_t ruleid, uint32_t rulenum, | ||||
| DYN_BUCKET_VERSION_BUMP(bucket, ipv6_parent_add); | DYN_BUCKET_VERSION_BUMP(bucket, ipv6_parent_add); | ||||
| DYNSTATE_CRITICAL_ENTER(); | DYNSTATE_CRITICAL_ENTER(); | ||||
| DYNSTATE_PROTECT(s); | DYNSTATE_PROTECT(s); | ||||
| DYN_BUCKET_UNLOCK(bucket); | DYN_BUCKET_UNLOCK(bucket); | ||||
| return (s); | return (s); | ||||
| } | } | ||||
| static int | static int | ||||
| dyn_add_ipv6_state(void *parent, uint32_t ruleid, uint16_t rulenum, | dyn_add_ipv6_state(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| const struct ipfw_flow_id *pkt, uint32_t zoneid, const void *ulp, | const struct ipfw_flow_id *pkt, uint32_t zoneid, const void *ulp, | ||||
| int pktlen, uint32_t hashval, struct ipfw_dyn_info *info, | int pktlen, uint32_t hashval, struct ipfw_dyn_info *info, | ||||
| uint16_t fibnum, uint16_t kidx, uint8_t type) | uint16_t fibnum, uint32_t kidx, uint8_t type) | ||||
| { | { | ||||
| struct dyn_ipv6_state *s; | struct dyn_ipv6_state *s; | ||||
| struct dyn_data *data; | struct dyn_data *data; | ||||
| uint32_t bucket; | uint32_t bucket; | ||||
| bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | bucket = DYN_BUCKET(hashval, V_curr_dyn_buckets); | ||||
| DYN_BUCKET_LOCK(bucket); | DYN_BUCKET_LOCK(bucket); | ||||
| if (info->direction == MATCH_UNKNOWN || | if (info->direction == MATCH_UNKNOWN || | ||||
| Show All 31 Lines | dyn_add_ipv6_state(void *parent, uint32_t ruleid, uint32_t rulenum, | ||||
| DYN_BUCKET_VERSION_BUMP(bucket, ipv6_add); | DYN_BUCKET_VERSION_BUMP(bucket, ipv6_add); | ||||
| DYN_BUCKET_UNLOCK(bucket); | DYN_BUCKET_UNLOCK(bucket); | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| #endif /* INET6 */ | #endif /* INET6 */ | ||||
| static void * | static void * | ||||
| dyn_get_parent_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_get_parent_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| struct ip_fw *rule, uint32_t hashval, uint32_t limit, uint16_t kidx) | struct ip_fw *rule, uint32_t hashval, uint32_t limit, uint32_t kidx) | ||||
| { | { | ||||
| char sbuf[24]; | char sbuf[24]; | ||||
| struct dyn_parent *p; | struct dyn_parent *p; | ||||
| void *ret; | void *ret; | ||||
| uint32_t bucket, version; | uint32_t bucket, version; | ||||
| p = NULL; | p = NULL; | ||||
| ret = NULL; | ret = NULL; | ||||
| ▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | #endif | ||||
| DYNSTATE_CRITICAL_EXIT(); | DYNSTATE_CRITICAL_EXIT(); | ||||
| return (ret); | return (ret); | ||||
| } | } | ||||
| static int | static int | ||||
| dyn_install_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | dyn_install_state(const struct ipfw_flow_id *pkt, uint32_t zoneid, | ||||
| uint16_t fibnum, const void *ulp, int pktlen, struct ip_fw *rule, | uint16_t fibnum, const void *ulp, int pktlen, struct ip_fw *rule, | ||||
| struct ipfw_dyn_info *info, uint32_t limit, uint16_t limit_mask, | struct ipfw_dyn_info *info, uint32_t limit, uint16_t limit_mask, | ||||
| uint16_t kidx, uint8_t type) | uint32_t kidx, uint8_t type) | ||||
| { | { | ||||
| struct ipfw_flow_id id; | struct ipfw_flow_id id; | ||||
| uint32_t hashval, parent_hashval, ruleid, rulenum; | uint32_t hashval, parent_hashval, ruleid, rulenum; | ||||
| int ret; | int ret; | ||||
| MPASS(type == O_LIMIT || type == O_KEEP_STATE); | MPASS(type == O_LIMIT || type == O_KEEP_STATE); | ||||
| ruleid = rule->id; | ruleid = rule->id; | ||||
| ▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | ipfw_dyn_install_state(struct ip_fw_chain *chain, struct ip_fw *rule, | ||||
| if (cmd->o.opcode == O_LIMIT) { | if (cmd->o.opcode == O_LIMIT) { | ||||
| limit = IP_FW_ARG_TABLEARG(chain, cmd->conn_limit, limit); | limit = IP_FW_ARG_TABLEARG(chain, cmd->conn_limit, limit); | ||||
| limit_mask = cmd->limit_mask; | limit_mask = cmd->limit_mask; | ||||
| } else { | } else { | ||||
| limit = 0; | limit = 0; | ||||
| limit_mask = 0; | limit_mask = 0; | ||||
| } | } | ||||
| /* | |||||
| * NOTE: we assume that kidx field of struct ipfw_insn_kidx | |||||
| * located in the same place as kidx field of ipfw_insn_limit. | |||||
| */ | |||||
| return (dyn_install_state(&args->f_id, | return (dyn_install_state(&args->f_id, | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| IS_IP6_FLOW_ID(&args->f_id) ? dyn_getscopeid(args): | IS_IP6_FLOW_ID(&args->f_id) ? dyn_getscopeid(args): | ||||
| #endif | #endif | ||||
| 0, M_GETFIB(args->m), ulp, pktlen, rule, info, limit, | 0, M_GETFIB(args->m), ulp, pktlen, rule, info, limit, | ||||
| limit_mask, cmd->o.arg1, cmd->o.opcode)); | limit_mask, cmd->kidx, cmd->o.opcode)); | ||||
| } | } | ||||
| /* | /* | ||||
| * Free safe to remove state entries from expired lists. | * Free safe to remove state entries from expired lists. | ||||
| */ | */ | ||||
| static void | static void | ||||
| dyn_free_states(struct ip_fw_chain *chain) | dyn_free_states(struct ip_fw_chain *chain) | ||||
| { | { | ||||
| ▲ Show 20 Lines • Show All 87 Lines • ▼ Show 20 Lines | |||||
| /* | /* | ||||
| * Returns: | * Returns: | ||||
| * 0 when state is not matched by specified range; | * 0 when state is not matched by specified range; | ||||
| * 1 when state is matched by specified range; | * 1 when state is matched by specified range; | ||||
| * 2 when state is matched by specified range and requested deletion of | * 2 when state is matched by specified range and requested deletion of | ||||
| * dynamic states. | * dynamic states. | ||||
| */ | */ | ||||
| static int | static int | ||||
| dyn_match_range(uint16_t rulenum, uint8_t set, const ipfw_range_tlv *rt) | dyn_match_range(uint32_t rulenum, uint8_t set, const ipfw_range_tlv *rt) | ||||
| { | { | ||||
| MPASS(rt != NULL); | MPASS(rt != NULL); | ||||
| /* flush all states */ | /* flush all states */ | ||||
| if (rt->flags & IPFW_RCFLAG_ALL) { | if (rt->flags & IPFW_RCFLAG_ALL) { | ||||
| if (rt->flags & IPFW_RCFLAG_DYNAMIC) | if (rt->flags & IPFW_RCFLAG_DYNAMIC) | ||||
| return (2); /* forced */ | return (2); /* forced */ | ||||
| return (1); | return (1); | ||||
| } | } | ||||
| if ((rt->flags & IPFW_RCFLAG_SET) != 0 && set != rt->set) | if ((rt->flags & IPFW_RCFLAG_SET) != 0 && set != rt->set) | ||||
| return (0); | return (0); | ||||
| if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 && | if ((rt->flags & IPFW_RCFLAG_RANGE) != 0 && | ||||
| (rulenum < rt->start_rule || rulenum > rt->end_rule)) | (rulenum < rt->start_rule || rulenum > rt->end_rule)) | ||||
| return (0); | return (0); | ||||
| if (rt->flags & IPFW_RCFLAG_DYNAMIC) | if (rt->flags & IPFW_RCFLAG_DYNAMIC) | ||||
| return (2); | return (2); | ||||
| return (1); | return (1); | ||||
| } | } | ||||
| static void | static void | ||||
| dyn_acquire_rule(struct ip_fw_chain *ch, struct dyn_data *data, | dyn_acquire_rule(struct ip_fw_chain *ch, struct dyn_data *data, | ||||
| struct ip_fw *rule, uint16_t kidx) | struct ip_fw *rule, uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_state_obj *obj; | struct dyn_state_obj *obj; | ||||
| /* | /* | ||||
| * Do not acquire reference twice. | * Do not acquire reference twice. | ||||
| * This can happen when rule deletion executed for | * This can happen when rule deletion executed for | ||||
| * the same range, but different ruleset id. | * the same range, but different ruleset id. | ||||
| */ | */ | ||||
| Show All 10 Lines | dyn_acquire_rule(struct ip_fw_chain *ch, struct dyn_data *data, | ||||
| MPASS(obj->no.etlv == IPFW_TLV_STATE_NAME); | MPASS(obj->no.etlv == IPFW_TLV_STATE_NAME); | ||||
| /* Reference the parent rule */ | /* Reference the parent rule */ | ||||
| rule->refcnt++; | rule->refcnt++; | ||||
| } | } | ||||
| static void | static void | ||||
| dyn_release_rule(struct ip_fw_chain *ch, struct dyn_data *data, | dyn_release_rule(struct ip_fw_chain *ch, struct dyn_data *data, | ||||
| struct ip_fw *rule, uint16_t kidx) | struct ip_fw *rule, uint32_t kidx) | ||||
| { | { | ||||
| struct dyn_state_obj *obj; | struct dyn_state_obj *obj; | ||||
| IPFW_UH_WLOCK_ASSERT(ch); | IPFW_UH_WLOCK_ASSERT(ch); | ||||
| MPASS(kidx != 0); | MPASS(kidx != 0); | ||||
| obj = SRV_OBJECT(ch, kidx); | obj = SRV_OBJECT(ch, kidx); | ||||
| if (obj->no.refcnt == 1) | if (obj->no.refcnt == 1) | ||||
| ▲ Show 20 Lines • Show All 645 Lines • ▼ Show 20 Lines | ipfw_expire_dyn_states(struct ip_fw_chain *chain, ipfw_range_tlv *rt) | ||||
| IPFW_UH_WLOCK_ASSERT(chain); | IPFW_UH_WLOCK_ASSERT(chain); | ||||
| dyn_expire_states(chain, rt); | dyn_expire_states(chain, rt); | ||||
| } | } | ||||
| /* | /* | ||||
| * Pass through all states and reset eaction for orphaned rules. | * Pass through all states and reset eaction for orphaned rules. | ||||
| */ | */ | ||||
| void | void | ||||
| ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint16_t eaction_id, | ipfw_dyn_reset_eaction(struct ip_fw_chain *ch, uint32_t eaction_id, | ||||
| uint16_t default_id, uint16_t instance_id) | uint32_t default_id, uint32_t instance_id) | ||||
| { | { | ||||
| #ifdef INET6 | #ifdef INET6 | ||||
| struct dyn_ipv6_state *s6; | struct dyn_ipv6_state *s6; | ||||
| #endif | #endif | ||||
| struct dyn_ipv4_state *s4; | struct dyn_ipv4_state *s4; | ||||
| struct ip_fw *rule; | struct ip_fw *rule; | ||||
| uint32_t bucket; | uint32_t bucket; | ||||
| ▲ Show 20 Lines • Show All 94 Lines • ▼ Show 20 Lines | case O_CHECK_STATE: | ||||
| return (1); | return (1); | ||||
| } | } | ||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static void | static void | ||||
| dyn_export_parent(const struct dyn_parent *p, uint16_t kidx, uint8_t set, | dyn_export_parent(const struct dyn_parent *p, uint32_t kidx, uint8_t set, | ||||
| ipfw_dyn_rule *dst) | ipfw_dyn_rule *dst) | ||||
| { | { | ||||
| dst->dyn_type = O_LIMIT_PARENT; | dst->type = O_LIMIT_PARENT; | ||||
| dst->set = set; | |||||
| dst->kidx = kidx; | dst->kidx = kidx; | ||||
| dst->count = (uint16_t)DPARENT_COUNT(p); | dst->rulenum = p->rulenum; | ||||
| dst->count = DPARENT_COUNT(p); | |||||
| dst->expire = TIME_LEQ(p->expire, time_uptime) ? 0: | dst->expire = TIME_LEQ(p->expire, time_uptime) ? 0: | ||||
| p->expire - time_uptime; | p->expire - time_uptime; | ||||
| dst->hashval = p->hashval; | |||||
| /* 'rule' is used to pass up the rule number and set */ | |||||
| memcpy(&dst->rule, &p->rulenum, sizeof(p->rulenum)); | |||||
| /* store set number into high word of dst->rule pointer. */ | |||||
| memcpy((char *)&dst->rule + sizeof(p->rulenum), &set, sizeof(set)); | |||||
| /* unused fields */ | /* unused fields */ | ||||
| dst->pad = 0; | |||||
| dst->pcnt = 0; | dst->pcnt = 0; | ||||
| dst->bcnt = 0; | dst->bcnt = 0; | ||||
| dst->parent = NULL; | |||||
| dst->state = 0; | |||||
| dst->ack_fwd = 0; | dst->ack_fwd = 0; | ||||
| dst->ack_rev = 0; | dst->ack_rev = 0; | ||||
| dst->bucket = p->hashval; | |||||
| /* | |||||
| * The legacy userland code will interpret a NULL here as a marker | |||||
| * for the last dynamic rule. | |||||
| */ | |||||
| dst->next = (ipfw_dyn_rule *)1; | |||||
| } | } | ||||
| static void | static void | ||||
| dyn_export_data(const struct dyn_data *data, uint16_t kidx, uint8_t type, | dyn_export_data(const struct dyn_data *data, uint32_t kidx, uint8_t type, | ||||
| uint8_t set, ipfw_dyn_rule *dst) | uint8_t set, ipfw_dyn_rule *dst) | ||||
| { | { | ||||
| dst->dyn_type = type; | dst->type = type; | ||||
| dst->set = set; | |||||
| dst->kidx = kidx; | dst->kidx = kidx; | ||||
| dst->rulenum = data->rulenum; | |||||
| dst->pcnt = data->pcnt_fwd + data->pcnt_rev; | dst->pcnt = data->pcnt_fwd + data->pcnt_rev; | ||||
| dst->bcnt = data->bcnt_fwd + data->bcnt_rev; | dst->bcnt = data->bcnt_fwd + data->bcnt_rev; | ||||
| dst->expire = TIME_LEQ(data->expire, time_uptime) ? 0: | dst->expire = TIME_LEQ(data->expire, time_uptime) ? 0: | ||||
| data->expire - time_uptime; | data->expire - time_uptime; | ||||
| /* 'rule' is used to pass up the rule number and set */ | |||||
| memcpy(&dst->rule, &data->rulenum, sizeof(data->rulenum)); | |||||
| /* store set number into high word of dst->rule pointer. */ | |||||
| memcpy((char *)&dst->rule + sizeof(data->rulenum), &set, sizeof(set)); | |||||
| dst->state = data->state; | dst->state = data->state; | ||||
| if (data->flags & DYN_REFERENCED) | if (data->flags & DYN_REFERENCED) | ||||
| dst->state |= IPFW_DYN_ORPHANED; | dst->state |= IPFW_DYN_ORPHANED; | ||||
| /* unused fields */ | |||||
| dst->parent = NULL; | |||||
| dst->ack_fwd = data->ack_fwd; | dst->ack_fwd = data->ack_fwd; | ||||
| dst->ack_rev = data->ack_rev; | dst->ack_rev = data->ack_rev; | ||||
| dst->count = 0; | dst->hashval = data->hashval; | ||||
| dst->bucket = data->hashval; | |||||
| /* | |||||
| * The legacy userland code will interpret a NULL here as a marker | |||||
| * for the last dynamic rule. | |||||
| */ | |||||
| dst->next = (ipfw_dyn_rule *)1; | |||||
| } | } | ||||
| static void | static void | ||||
| dyn_export_ipv4_state(const struct dyn_ipv4_state *s, ipfw_dyn_rule *dst) | dyn_export_ipv4_state(const struct dyn_ipv4_state *s, ipfw_dyn_rule *dst) | ||||
| { | { | ||||
| struct ip_fw *rule; | struct ip_fw *rule; | ||||
| switch (s->type) { | switch (s->type) { | ||||
| ▲ Show 20 Lines • Show All 108 Lines • ▼ Show 20 Lines | #ifdef INET6 | ||||
| DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket); | DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket); | ||||
| #endif /* INET6 */ | #endif /* INET6 */ | ||||
| } | } | ||||
| /* mark last dynamic rule */ | /* mark last dynamic rule */ | ||||
| if (last != NULL) | if (last != NULL) | ||||
| last->head.flags = IPFW_DF_LAST; /* XXX: unused */ | last->head.flags = IPFW_DF_LAST; /* XXX: unused */ | ||||
| return (0); | return (0); | ||||
| #undef DYN_EXPORT_STATES | |||||
| } | |||||
| /* | |||||
| * Fill given buffer with dynamic states (legacy format). | |||||
| * IPFW_UH_RLOCK has to be held while calling. | |||||
| */ | |||||
| void | |||||
| ipfw_get_dynamic(struct ip_fw_chain *chain, char **pbp, const char *ep) | |||||
| { | |||||
| #ifdef INET6 | |||||
| struct dyn_ipv6_state *s6; | |||||
| #endif | |||||
| struct dyn_ipv4_state *s4; | |||||
| ipfw_dyn_rule *p, *last = NULL; | |||||
| char *bp; | |||||
| uint32_t bucket; | |||||
| if (V_dyn_count == 0) | |||||
| return; | |||||
| bp = *pbp; | |||||
| IPFW_UH_RLOCK_ASSERT(chain); | |||||
| #define DYN_EXPORT_STATES(s, af, head, b) \ | |||||
| CK_SLIST_FOREACH(s, &V_dyn_ ## head[b], entry) { \ | |||||
| if (bp + sizeof(*p) > ep) \ | |||||
| break; \ | |||||
| p = (ipfw_dyn_rule *)bp; \ | |||||
| dyn_export_ ## af ## _state(s, p); \ | |||||
| last = p; \ | |||||
| bp += sizeof(*p); \ | |||||
| } | |||||
| for (bucket = 0; bucket < V_curr_dyn_buckets; bucket++) { | |||||
| DYN_EXPORT_STATES(s4, ipv4, ipv4_parent, bucket); | |||||
| DYN_EXPORT_STATES(s4, ipv4, ipv4, bucket); | |||||
| #ifdef INET6 | |||||
| DYN_EXPORT_STATES(s6, ipv6, ipv6_parent, bucket); | |||||
| DYN_EXPORT_STATES(s6, ipv6, ipv6, bucket); | |||||
| #endif /* INET6 */ | |||||
| } | |||||
| if (last != NULL) /* mark last dynamic rule */ | |||||
| last->next = NULL; | |||||
| *pbp = bp; | |||||
| #undef DYN_EXPORT_STATES | #undef DYN_EXPORT_STATES | ||||
| } | } | ||||
| void | void | ||||
| ipfw_dyn_init(struct ip_fw_chain *chain) | ipfw_dyn_init(struct ip_fw_chain *chain) | ||||
| { | { | ||||
| #ifdef IPFIREWALL_JENKINSHASH | #ifdef IPFIREWALL_JENKINSHASH | ||||
| ▲ Show 20 Lines • Show All 125 Lines • Show Last 20 Lines | |||||