HAProxy research notes -- rules implementation

  • Author: Godbach 
  • Blog:  http://godbach.blog.chinaunix.net  


  • ----

This paper studies the implementation of rules in haproxy-1.5-dev17.


  • 1. ACL
  • 2. Composition of rules
  • 3. Execution of the rule
  • 4. Types of rules

1. ACL

If you want to implement rules with rich functions, you need to have a matching ACL mechanism.

The format of the ACL is as follows:

acl [flags] [operator] ...

Definition of ACL data structure in haproxy:

/* The acl will be linked to from the proxy where it is declared */
struct acl {
	struct list list;           /* chaining */
	char *name;		    /* acl name */
	struct list expr;	    /* list of acl_exprs */
	int cache_idx;              /* ACL index in cache */
	unsigned int requires;      /* or'ed bit mask of all acl_expr's ACL_USE_* */
};

in:

  • name: the name of the ACL
  • expr: The expression defined by the ACL. It is the expression following the defined ACL name. This is a linked list structure. Therefore, it is possible to define multiple ACLs with different expressions but the same name. In this way, multiple expressions belong to the same ACL.
  • requires: a collection of the scopes corresponding to the keywords in all expressions (where the keyword can be used)

The function parse_acl() is responsible for parsing the defined ACL:

  • Find the name of the ACL, if it does not exist, alloc a new acl structure
  • Parse the expression by calling parse_acl_expr() and return the struct acl_expr structure
    • Expression in ACL should have only one kw
    • To find this keyword, it must be registered. And return the data structure struct acl_expr when the keyword is registered
    • alloc a struct acl_expr structure, record the returned kw data structure, and initialize the members
    • Call the parse method corresponding to kw, save the parsed result in the struct acl_pattern structure, and add the structure to the linked list of expr->patterns
  • Insert the parsed expression into the expr linked list in acl

Summary: An ACL contains one or more expressions. Each expression contains a kw and one or more patterns.

2. Composition of rules

Here is a brief description of the logical relationship between rule and acl:

  • rule should be composed of action + condition
    • Some actions themselves may also need to record some information. Different rules may have different action information, such as reqirep, etc.
    • The action of block rules is relatively simple, and the processing results are the same after the condition is satisfied
  • condition, the corresponding data structure of the judgment condition to complete the rule detection: struct acl_cond
            struct acl_cond {
            	struct list list;           /* Some specific tests may use multiple conditions */
            	struct list suites;         /* list of acl_term_suites */
            	int pol;                    /* polarity: ACL_COND_IF / ACL_COND_UNLESS */
            	unsigned int requires;      /* or'ed bit mask of all acl's ACL_USE_* */
            	const char *file;           /* config file where the condition is declared */
            	int line;                   /* line in the config file where the condition is declared */
            };
            
    
  • condition contains multiple ACL groups. The division logic of the group is logical or (|| or or), that is, a member of struct list suites, the data structure of the group struct acl_term_suite
        struct acl_term_suite {
        	struct list list;           /* chaining of term suites */
        	struct list terms;          /* list of acl_terms */
        };
    
    • The data structure can contain multiple ACLs, as well as a possible negation flag '!' for each ACL
    • Adjacent ACLs in all expressions and their logical relationship is logical AND (&&) form an ACL group
      • For example, if acl1 !acl2 or acl3 acl4, it constitutes two acl_term_suites, namely acl1 !acl2 and acl3 acl4
      • Data structure for each ACL and its possible negation flags: struct acl_term
            struct acl_term {
            	struct list list;           /* chaining */
            	struct acl *acl;            /* acl pointed to by this term */
            	int neg;                    /* 1 if the ACL result must be negated */
            };
        
    • An ACL contains multiple exprs

3. Execution of the rule

To sum it up is very simple, execute the judgment condition. If the conditions are met, the corresponding action is performed.

Here is the sample code for rspadd:

	/* add response headers from the rule sets in the same order */
	list_for_each_entry(wl, &rule_set->rsp_add, list) {
		if (txn->status < 200)
			break;
		if (wl->cond) {
			int ret = acl_exec_cond(wl->cond, px, t, txn, SMP_OPT_DIR_RES|SMP_OPT_FINAL);
			ret = acl_pass (ret);
			if (((struct acl_cond *)wl->cond)->pol == ACL_COND_UNLESS)
				ret =! ret;
			if (! ret)
				continue;
		}
		if (unlikely(http_header_add_tail(&txn->rsp, &txn->hdr_idx, wl->s) < 0))
			goto return_bad_resp;
	}

For the same kind of rules, the execution logic is as follows:

  • Mainly traverse the rule and call acl_exec_cond to execute the detection condition of the rule. The test result only gives a match or not.
    • 逐个遍历 cond 上的 ACL 组,即cond->suites。任一 suite 匹配成功,则认为匹配成功
    • 同一个 ACL 组上,遍历所有 suite->terms (ACL + 取反逻辑)。任意一个 ACL 匹配失败,则跳到下一个 ACL 组继续匹配。同一组全部 ACL 匹配成功,则认为该 ACL 组匹配
      • 同一个 ACL 上的匹配,则是逐一遍历 ACL 的 expr。只要任意一个 expr 匹配成功,则认为该 ACL 匹配成功
  • 结合 condition 中的条件 if/unless,确定最终匹配结果
  • 如果匹配则执行对应的 action,否则检测下一条规则。

4. rule 的种类

从 proxy 结构体可以看出 rule 的种类

struct proxy {
	...
	struct list acl;                        /* ACL declared on this proxy */
	struct list http_req_rules;		/* HTTP request rules: allow/deny/http-auth */
	struct list block_cond;                 /* early blocking conditions (chained) */
	struct list redirect_rules;             /* content redirecting rules (chained) */
	struct list switching_rules;            /* content switching rules (chained) */
	struct list persist_rules;		/* 'force-persist' and 'ignore-persist' rules (chained) */
	struct list sticking_rules;             /* content sticking rules (chained) */
	struct list storersp_rules;             /* content store response rules (chained) */
	struct list server_rules;               /* server switching rules (chained) */
	struct {                                /* TCP request processing */
		unsigned int inspect_delay;     /* inspection delay */
		struct list inspect_rules;      /* inspection rules */
		struct list l4_rules;           /* layer4 rules */
	} tcp_req;
	struct {                                /* TCP request processing */
		unsigned int inspect_delay;     /* inspection delay */
		struct list inspect_rules;      /* inspection rules */
	} tcp_rep;
    ...
}

其中, 函数 http_process_req_common 中处理的规则如下:

http_process_req_common
{
    ... 
    1. process block rules
    ...
    2. process http req rules
    ...
    3. execute regular exp if any
    ...
    4. req add
    ...
    5. process redirect rules
    ...
}


这里没有详细的介绍各种具体用途的 rules。随后具体分析代码的时候总结一下再加上。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325603601&siteId=291194637