Rework ipfw dynamic states implementation to be lockless on fast path.
o added struct ipfw_dyn_info that keeps all needed for ipfw_chk and for dynamic states implementation information; o added DYN_LOOKUP_NEEDED() macro that can be used to determine the need of new lookup of dynamic states; o ipfw_dyn_rule now becomes obsolete. Currently it used to pass information from kernel to userland only. o IPv4 and IPv6 states now described by different structures dyn_ipv4_state and dyn_ipv6_state; o IPv6 scope zones support is added; o ipfw(4) now depends from Concurrency Kit; o states are linked with "entry" field using CK_SLIST. This allows lockless lookup and protected by mutex modifications. o the "expired" SLIST field is used for states expiring. o struct dyn_data is used to keep generic information for both IPv4 and IPv6; o struct dyn_parent is used to keep O_LIMIT_PARENT information; o IPv4 and IPv6 states are stored in different hash tables; o O_LIMIT_PARENT states now are kept separately from O_LIMIT and O_KEEP_STATE states; o per-cpu dyn_hp pointers are used to implement hazard pointers and they prevent freeing states that are locklessly used by lookup threads; o mutexes to protect modification of lists in hash tables now kept in separate arrays. 65535 limit to maximum number of hash buckets now removed. o Separate lookup and install functions added for IPv4 and IPv6 states and for parent states. o By default now is used Jenkinks hash function. Obtained from: Yandex LLC MFC after: 42 days Sponsored by: Yandex LLC Differential Revision: https://reviews.freebsd.org/D12685
This commit is contained in:
parent
df0cd8ad05
commit
545ce709f1
@ -4374,7 +4374,8 @@ netpfil/ipfw/ip_dn_io.c optional inet dummynet
|
||||
netpfil/ipfw/ip_dn_glue.c optional inet dummynet
|
||||
netpfil/ipfw/ip_fw2.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_bpf.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_dynamic.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_dynamic.c optional inet ipfirewall \
|
||||
compile-with "${NORMAL_C} -I$S/contrib/ck/include"
|
||||
netpfil/ipfw/ip_fw_eaction.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_log.c optional inet ipfirewall
|
||||
netpfil/ipfw/ip_fw_pfil.c optional inet ipfirewall
|
||||
|
@ -9,7 +9,7 @@ SRCS+= ip_fw_sockopt.c ip_fw_table.c ip_fw_table_algo.c ip_fw_iface.c
|
||||
SRCS+= ip_fw_table_value.c
|
||||
SRCS+= opt_inet.h opt_inet6.h opt_ipdivert.h opt_ipfw.h
|
||||
|
||||
CFLAGS+= -DIPFIREWALL
|
||||
CFLAGS+= -DIPFIREWALL -I${SRCTOP}/sys/contrib/ck/include
|
||||
#
|
||||
#If you want it verbose
|
||||
#CFLAGS+= -DIPFIREWALL_VERBOSE
|
||||
|
@ -671,7 +671,7 @@ struct ipfw_flow_id {
|
||||
uint32_t src_ip;
|
||||
uint16_t dst_port;
|
||||
uint16_t src_port;
|
||||
uint8_t fib;
|
||||
uint8_t fib; /* XXX: must be uint16_t */
|
||||
uint8_t proto;
|
||||
uint8_t _flags; /* protocol-specific flags */
|
||||
uint8_t addr_type; /* 4=ip4, 6=ip6, 1=ether ? */
|
||||
@ -682,6 +682,7 @@ struct ipfw_flow_id {
|
||||
};
|
||||
#endif
|
||||
|
||||
#define IS_IP4_FLOW_ID(id) ((id)->addr_type == 4)
|
||||
#define IS_IP6_FLOW_ID(id) ((id)->addr_type == 6)
|
||||
|
||||
/*
|
||||
|
@ -1387,8 +1387,7 @@ ipfw_chk(struct ip_fw_args *args)
|
||||
* MATCH_NONE when checked and not matched (q = NULL),
|
||||
* MATCH_FORWARD or MATCH_REVERSE otherwise (q != NULL)
|
||||
*/
|
||||
int dyn_dir = MATCH_UNKNOWN;
|
||||
uint16_t dyn_name = 0;
|
||||
struct ipfw_dyn_info dyn_info;
|
||||
struct ip_fw *q = NULL;
|
||||
struct ip_fw_chain *chain = &V_layer3_chain;
|
||||
|
||||
@ -1420,6 +1419,7 @@ ipfw_chk(struct ip_fw_args *args)
|
||||
proto = args->f_id.proto = 0; /* mark f_id invalid */
|
||||
/* XXX 0 is a valid proto: IP/IPv6 Hop-by-Hop Option */
|
||||
|
||||
DYN_INFO_INIT(&dyn_info);
|
||||
/*
|
||||
* PULLUP_TO(len, p, T) makes sure that len + sizeof(T) is contiguous,
|
||||
* then it sets p to point at the offset "len" in the mbuf. WARNING: the
|
||||
@ -2605,7 +2605,8 @@ do { \
|
||||
case O_LIMIT:
|
||||
case O_KEEP_STATE:
|
||||
if (ipfw_dyn_install_state(chain, f,
|
||||
(ipfw_insn_limit *)cmd, args, tablearg)) {
|
||||
(ipfw_insn_limit *)cmd, args, ulp,
|
||||
pktlen, &dyn_info, tablearg)) {
|
||||
/* error or limit violation */
|
||||
retval = IP_FW_DENY;
|
||||
l = 0; /* exit inner loop */
|
||||
@ -2619,34 +2620,15 @@ do { \
|
||||
/*
|
||||
* dynamic rules are checked at the first
|
||||
* keep-state or check-state occurrence,
|
||||
* with the result being stored in dyn_dir
|
||||
* and dyn_name.
|
||||
* with the result being stored in dyn_info.
|
||||
* The compiler introduces a PROBE_STATE
|
||||
* instruction for us when we have a
|
||||
* KEEP_STATE (because PROBE_STATE needs
|
||||
* to be run first).
|
||||
*
|
||||
* (dyn_dir == MATCH_UNKNOWN) means this is
|
||||
* first lookup for such f_id. Do lookup.
|
||||
*
|
||||
* (dyn_dir != MATCH_UNKNOWN &&
|
||||
* dyn_name != 0 && dyn_name != cmd->arg1)
|
||||
* means previous lookup didn't find dynamic
|
||||
* rule for specific state name and current
|
||||
* lookup will search rule with another state
|
||||
* name. Redo lookup.
|
||||
*
|
||||
* (dyn_dir != MATCH_UNKNOWN && dyn_name == 0)
|
||||
* means previous lookup was for `any' name
|
||||
* and it didn't find rule. No need to do
|
||||
* lookup again.
|
||||
*/
|
||||
if ((dyn_dir == MATCH_UNKNOWN ||
|
||||
(dyn_name != 0 &&
|
||||
dyn_name != cmd->arg1)) &&
|
||||
(q = ipfw_dyn_lookup_state(&args->f_id,
|
||||
ulp, pktlen, &dyn_dir,
|
||||
(dyn_name = cmd->arg1))) != NULL) {
|
||||
if (DYN_LOOKUP_NEEDED(&dyn_info, cmd) &&
|
||||
(q = ipfw_dyn_lookup_state(args, ulp,
|
||||
pktlen, cmd, &dyn_info)) != NULL) {
|
||||
/*
|
||||
* Found dynamic entry, jump to the
|
||||
* 'action' part of the parent rule
|
||||
@ -2654,13 +2636,7 @@ do { \
|
||||
* cmdlen.
|
||||
*/
|
||||
f = q;
|
||||
/* XXX we would like to have f_pos
|
||||
* readily accessible in the dynamic
|
||||
* rule, instead of having to
|
||||
* lookup q->rule.
|
||||
*/
|
||||
f_pos = ipfw_find_rule(chain,
|
||||
f->rulenum, f->id);
|
||||
f_pos = dyn_info.f_pos;
|
||||
cmd = ACTION_PTR(f);
|
||||
l = f->cmd_len - f->act_ofs;
|
||||
cmdlen = 0;
|
||||
@ -2877,7 +2853,8 @@ do { \
|
||||
case O_FORWARD_IP:
|
||||
if (args->eh) /* not valid on layer2 pkts */
|
||||
break;
|
||||
if (q != f || dyn_dir == MATCH_FORWARD) {
|
||||
if (q != f ||
|
||||
dyn_info.direction == MATCH_FORWARD) {
|
||||
struct sockaddr_in *sa;
|
||||
|
||||
sa = &(((ipfw_insn_sa *)cmd)->sa);
|
||||
@ -2937,7 +2914,8 @@ do { \
|
||||
case O_FORWARD_IP6:
|
||||
if (args->eh) /* not valid on layer2 pkts */
|
||||
break;
|
||||
if (q != f || dyn_dir == MATCH_FORWARD) {
|
||||
if (q != f ||
|
||||
dyn_info.direction == MATCH_FORWARD) {
|
||||
struct sockaddr_in6 *sin6;
|
||||
|
||||
sin6 = &(((ipfw_insn_sa6 *)cmd)->sa);
|
||||
@ -3089,7 +3067,7 @@ do { \
|
||||
* @args content, and it may be
|
||||
* used for new state lookup later.
|
||||
*/
|
||||
dyn_dir = MATCH_UNKNOWN;
|
||||
DYN_INFO_INIT(&dyn_info);
|
||||
}
|
||||
break;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -184,23 +184,48 @@ enum { /* result for matching dynamic rules */
|
||||
struct ip_fw_chain;
|
||||
struct sockopt_data;
|
||||
int ipfw_is_dyn_rule(struct ip_fw *rule);
|
||||
void ipfw_expire_dyn_rules(struct ip_fw_chain *, ipfw_range_tlv *);
|
||||
void ipfw_expire_dyn_states(struct ip_fw_chain *, ipfw_range_tlv *);
|
||||
|
||||
struct tcphdr;
|
||||
struct mbuf *ipfw_send_pkt(struct mbuf *, struct ipfw_flow_id *,
|
||||
u_int32_t, u_int32_t, int);
|
||||
/*
|
||||
* Macro to determine that we need to do or redo dynamic state lookup.
|
||||
* direction == MATCH_UNKNOWN means that this is first lookup, then we need
|
||||
* to do lookup.
|
||||
* Otherwise check the state name, if previous lookup was for "any" name,
|
||||
* this means there is no state with specific name. Thus no need to do
|
||||
* lookup. If previous name was not "any", redo lookup for specific name.
|
||||
*/
|
||||
#define DYN_LOOKUP_NEEDED(p, cmd) \
|
||||
((p)->direction == MATCH_UNKNOWN || \
|
||||
((p)->kidx != 0 && (p)->kidx != (cmd)->arg1))
|
||||
#define DYN_INFO_INIT(p) do { \
|
||||
(p)->direction = MATCH_UNKNOWN; \
|
||||
(p)->kidx = 0; \
|
||||
} while (0)
|
||||
struct ipfw_dyn_info {
|
||||
uint16_t direction; /* match direction */
|
||||
uint16_t kidx; /* state name kidx */
|
||||
uint32_t hashval; /* hash value */
|
||||
uint32_t version; /* bucket version */
|
||||
uint32_t f_pos;
|
||||
};
|
||||
int 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);
|
||||
struct ip_fw *ipfw_dyn_lookup_state(const struct ipfw_flow_id *pkt,
|
||||
const void *ulp, int pktlen, int *match_direction, uint16_t kidx);
|
||||
void ipfw_remove_dyn_children(struct ip_fw *rule);
|
||||
const ipfw_insn_limit *cmd, const struct ip_fw_args *args,
|
||||
const void *ulp, int pktlen, struct ipfw_dyn_info *info,
|
||||
uint32_t tablearg);
|
||||
struct ip_fw *ipfw_dyn_lookup_state(const struct ip_fw_args *args,
|
||||
const void *ulp, int pktlen, const ipfw_insn *cmd,
|
||||
struct ipfw_dyn_info *info);
|
||||
|
||||
void ipfw_get_dynamic(struct ip_fw_chain *chain, char **bp, const char *ep);
|
||||
int ipfw_dump_states(struct ip_fw_chain *chain, struct sockopt_data *sd);
|
||||
|
||||
void ipfw_dyn_init(struct ip_fw_chain *); /* per-vnet initialization */
|
||||
void ipfw_dyn_uninit(int); /* per-vnet deinitialization */
|
||||
int ipfw_dyn_len(void);
|
||||
int ipfw_dyn_get_count(void);
|
||||
uint32_t ipfw_dyn_get_count(void);
|
||||
|
||||
/* common variables */
|
||||
VNET_DECLARE(int, fw_one_pass);
|
||||
|
@ -1062,7 +1062,7 @@ delete_range(struct ip_fw_chain *chain, ipfw_range_tlv *rt, int *ndel)
|
||||
map = swap_map(chain, map, chain->n_rules - n);
|
||||
/* 6. Remove all dynamic states originated by deleted rules */
|
||||
if (ndyn > 0)
|
||||
ipfw_expire_dyn_rules(chain, rt);
|
||||
ipfw_expire_dyn_states(chain, rt);
|
||||
/* 7. now remove the rules deleted from the old map */
|
||||
for (i = start; i < end; i++) {
|
||||
rule = map[i];
|
||||
|
Loading…
x
Reference in New Issue
Block a user