From c71c8763eb443da4f88ef9c4ba57cec9556dc8e3 Mon Sep 17 00:00:00 2001 From: Aleksey Mikhaylov Date: Thu, 10 Feb 2022 12:18:04 +0000 Subject: [PATCH] Add option for set cache policy based on cookie name or pattern Fixes #1544 Signed-off-by: Aleksey Mikhaylov --- .gitignore | 3 + fw/http.h | 2 + fw/http_match.c | 227 ++++++++++++++++++++++++++++++++---- fw/http_match.h | 39 ++++++- fw/http_sess.c | 73 +++--------- fw/http_tbl.c | 102 ++++++++++++---- fw/t/unit/test_http_match.c | 3 +- 7 files changed, 341 insertions(+), 108 deletions(-) diff --git a/.gitignore b/.gitignore index 080330348..ebd17ca44 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,9 @@ Module.symvers .settings .pydevproject +# vscode project settings +.vscode + # Python compiler cache files *.pyc diff --git a/fw/http.h b/fw/http.h index bfe911ac4..d1c1eacbb 100644 --- a/fw/http.h +++ b/fw/http.h @@ -263,6 +263,8 @@ enum { TFW_HTTP_FLAGS_REQ, /* Sticky cookie is found and verified. */ TFW_HTTP_B_HAS_STICKY = TFW_HTTP_FLAGS_REQ, + /* Request calculated as fitting some caching by cookie rule */ + TFW_HTTP_B_CACHE_CALC, /* Request is non-idempotent. */ TFW_HTTP_B_NON_IDEMP, /* Request stated 'Accept: text/html' header */ diff --git a/fw/http_match.c b/fw/http_match.c index b93c8e0a9..c8a3b9aca 100644 --- a/fw/http_match.c +++ b/fw/http_match.c @@ -375,7 +375,7 @@ match_hdr_raw(const TfwHttpReq *req, const TfwHttpMatchRule *rule) static bool match_hdr(const TfwHttpReq *req, const TfwHttpMatchRule *rule) { - tfw_http_hdr_t id = rule->hid; + tfw_http_hdr_t id = rule->val.hid; BUG_ON(id < 0); if (id == TFW_HTTP_HDR_RAW) @@ -408,6 +408,57 @@ match_mark(const TfwHttpReq *req, const TfwHttpMatchRule *rule) return mark == rule->arg.num; } +static bool +match_cookie(const TfwHttpReq *req, const TfwHttpMatchRule *rule) +{ + TfwStr cookie_val; + TfwStr *hdr, *end, *dup; + tfw_http_match_op_t op = rule->val.ptn.op; + const char *cstr = rule->val.ptn.str; + unsigned int clen = rule->val.ptn.len; + + if (unlikely(rule->val.type != TFW_HTTP_MATCH_V_COOKIE)) + return false; + + hdr = &req->h_tbl->tbl[TFW_HTTP_HDR_COOKIE]; + if (TFW_STR_EMPTY(hdr)) + return 0; + TFW_STR_FOR_EACH_DUP(dup, hdr, end) { + TfwStr value = { 0 }; + int r; + + tfw_http_msg_clnthdr_val(req, dup, TFW_HTTP_HDR_COOKIE, &value); + r = tfw_http_search_cookie(cstr, clen, + &value, &cookie_val, + op, false); + if (r) + return r; + } + + return 0; + + // tfw_http_search_cookie(, , , &cookie_val, rule->val.ptn.op, false); + + // flags = map_op_to_str_eq_flags(rule->op); + // /* + // * RFC 7230: + // * 5.4: Host header must be ignored when URI is absolute. + // * 5.4, 2.7.3: the comparison is case-insensitive. + // * + // * TODO: + // * 5.4, 2.7.3: Port 80 is equal to a non-given/empty port (done by + // * normalizing the host). + // */ + // flags |= TFW_STR_EQ_CASEI; + // arg = &rule->arg; + // if (rule->op == TFW_HTTP_MATCH_O_SUFFIX) + // return tfw_str_eq_cstr_off(host, host->len - arg->len, + // arg->str, arg->len, flags); + + // return tfw_str_eq_cstr(host, arg->str, arg->len, flags); + // return true; +} + typedef bool (*match_fn)(const TfwHttpReq *, const TfwHttpMatchRule *); static const match_fn match_fn_tbl[_TFW_HTTP_MATCH_F_COUNT] = { @@ -417,6 +468,7 @@ static const match_fn match_fn_tbl[_TFW_HTTP_MATCH_F_COUNT] = { [TFW_HTTP_MATCH_F_METHOD] = match_method, [TFW_HTTP_MATCH_F_URI] = match_uri, [TFW_HTTP_MATCH_F_MARK] = match_mark, + [TFW_HTTP_MATCH_F_COOKIE] = match_cookie, }; /** @@ -425,7 +477,7 @@ static const match_fn match_fn_tbl[_TFW_HTTP_MATCH_F_COUNT] = { * has appropriate action type. */ static bool -do_eval(const TfwHttpReq *req, const TfwHttpMatchRule *rule) +do_eval(TfwHttpReq *req, const TfwHttpMatchRule *rule) { match_fn match_fn; tfw_http_match_fld_t field; @@ -458,6 +510,16 @@ do_eval(const TfwHttpReq *req, const TfwHttpMatchRule *rule) req->msg.skb_head->mark = rule->act.mark; return false; } + /* + * Evaluate binary flag setting action. + */ + if (rule->act.type ==TFW_HTTP_MATCH_ACT_FLAG) { + if (likely(rule->act.flg.set)) + set_bit(rule->act.flg.fid, req->flags); + else + clear_bit(rule->act.flg.fid, req->flags); + return false; + } return true; } @@ -471,6 +533,7 @@ tfw_http_tbl_arg_type(tfw_http_match_fld_t field) [TFW_HTTP_MATCH_F_METHOD] = TFW_HTTP_MATCH_A_METHOD, [TFW_HTTP_MATCH_F_URI] = TFW_HTTP_MATCH_A_STR, [TFW_HTTP_MATCH_F_MARK] = TFW_HTTP_MATCH_A_NUM, + [TFW_HTTP_MATCH_F_COOKIE] = TFW_HTTP_MATCH_A_STR, }; BUG_ON(field <= 0 || field >= _TFW_HTTP_MATCH_F_COUNT); @@ -483,7 +546,7 @@ tfw_http_tbl_arg_type(tfw_http_match_fld_t field) * Return a first matching rule. */ TfwHttpMatchRule * -tfw_http_match_req(const TfwHttpReq *req, struct list_head *mlst) +tfw_http_match_req(TfwHttpReq *req, struct list_head *mlst) { TfwHttpMatchRule *rule; @@ -599,7 +662,8 @@ tfw_http_rule_arg_init(TfwHttpMatchRule *rule, const char *arg, size_t arg_len) rule->arg.len = arg_len; memcpy(rule->arg.str, arg, arg_len); if (rule->field == TFW_HTTP_MATCH_F_HDR - && rule->hid == TFW_HTTP_HDR_RAW) + && rule->val.type == TFW_HTTP_MATCH_V_HEADER + && rule->val.hid == TFW_HTTP_HDR_RAW) { char *p = rule->arg.str; while ((*p = tolower(*p))) @@ -609,14 +673,37 @@ tfw_http_rule_arg_init(TfwHttpMatchRule *rule, const char *arg, size_t arg_len) return 0; } +static +size_t +tfw_http_escape_pre_post(char *out , const char *str) +{ + int i; + size_t len = 0; + bool escaped = false; + + for (i = 0; str[i]; ++i) { + if (str[i] == '*' && !escaped && (i == 0 || !str[i + 1])) + continue; + if (str[i] != '\\' || escaped) { + escaped = false; + *out = str[i]; + ++len; + ++out; + } + else if (str[i] == '\\') { + escaped = true; + } + } + + return len; +} + const char * tfw_http_arg_adjust(const char *arg, tfw_http_match_fld_t field, const char *raw_hdr_name, size_t *size_out, tfw_http_match_arg_t *type_out, tfw_http_match_op_t *op_out) { - int i; - bool escaped; char *arg_out, *pos; size_t name_len = 0, full_name_len = 0, len = strlen(arg); bool wc_arg = (arg[0] == '*' && len == 1); @@ -631,7 +718,7 @@ tfw_http_arg_adjust(const char *arg, tfw_http_match_fld_t field, if (wc_arg && !raw_hdr_name) return NULL; - if (raw_hdr_name) { + if (raw_hdr_name && field != TFW_HTTP_MATCH_F_COOKIE) { name_len = strlen(raw_hdr_name); full_name_len = name_len + SLEN(S_DLM); } @@ -641,7 +728,7 @@ tfw_http_arg_adjust(const char *arg, tfw_http_match_fld_t field, return ERR_PTR(-ENOMEM); } - if (raw_hdr_name) { + if (raw_hdr_name && field != TFW_HTTP_MATCH_F_COOKIE) { memcpy(arg_out, raw_hdr_name, name_len); memcpy(arg_out + name_len, S_DLM, SLEN(S_DLM)); } @@ -676,27 +763,63 @@ tfw_http_arg_adjust(const char *arg, tfw_http_match_fld_t field, *op_out = TFW_HTTP_MATCH_O_SUFFIX; } - len = full_name_len; - escaped = false; pos = arg_out + full_name_len; - for (i = 0; arg[i]; ++i) { - if (arg[i] == '*' && !escaped && (i == 0 || !arg[i + 1])) - continue; - if (arg[i] != '\\' || escaped) { - escaped = false; - *pos = arg[i]; - ++len; - ++pos; - } - else if (arg[i] == '\\') { - escaped = true; + len = tfw_http_escape_pre_post(pos, arg); + *size_out += full_name_len + len + 1; + + return arg_out; +} + +const char * +tfw_http_val_adjust(const char *val, tfw_http_match_fld_t field, + unsigned int *len_out, + tfw_http_match_val_t *type_out, + tfw_http_match_op_t *op_out) +{ + size_t len; + char *val_out; + bool wc_val; + + if (field != TFW_HTTP_MATCH_F_COOKIE) { + *type_out = TFW_HTTP_MATCH_V_HEADER; + return NULL; + } + *type_out = TFW_HTTP_MATCH_V_COOKIE; + + if (!val) { + T_ERR_NL("http_tbl: cookie pattern is empty, must be filled\n"); + return ERR_PTR(-EINVAL); + } + + len = strlen(val); + wc_val = (val[0] == '*' && len == 1); + + if (!(val_out = kzalloc(len + 1, GFP_KERNEL))) { + T_ERR_NL("http_match: unable to allocate rule field value.\n"); + return ERR_PTR(-ENOMEM); + } + + *op_out = TFW_HTTP_MATCH_O_EQ; + if (wc_val) + *op_out = TFW_HTTP_MATCH_O_WILDCARD; + if (!wc_val || (len > 1 && val[len - 1] == '*' && val[len - 2] != '\\')) + *op_out = TFW_HTTP_MATCH_O_PREFIX; + if (!wc_val && val[0] == '*') { + if (*op_out == TFW_HTTP_MATCH_O_PREFIX) { + T_ERR_NL("http_match: unable to match" + " double-wildcard patterns '%s'\n", val); + return ERR_PTR(-EINVAL); + } else { + *op_out = TFW_HTTP_MATCH_O_SUFFIX; } } - *size_out = len + 1; - return arg_out; + *len_out = tfw_http_escape_pre_post(val_out, val); + + return val_out; } + int tfw_http_verify_hdr_field(tfw_http_match_fld_t field, const char **hdr_name, unsigned int *hid_out) @@ -730,3 +853,61 @@ tfw_http_verify_hdr_field(tfw_http_match_fld_t field, const char **hdr_name, return 0; } + +/* + * Search for cookie in `Set-Cookie`/`Cookie` header value @cookie + * and save the cookie value into @val. Prefix, suffix or wildacar compare + * @op is supported, pass TFW_HTTP_MATCH_O_EQ for default behaviour. + * Flag @is_resp_hdr identifies the header name: true for `Set-Cookie`, + * false for `Cookie`. + */ +int +tfw_http_search_cookie(const char *cstr, unsigned long clen, + const TfwStr *cookie, TfwStr *val, + tfw_http_match_op_t op, bool is_resp_hdr) +{ + TfwStr *chunk, *end; + TfwStr tmp = { .flags = 0, }; + unsigned int n = cookie->nchunks; +(void) op; + /* Search cookie name. */ + end = cookie->chunks + cookie->nchunks; + for (chunk = cookie->chunks; chunk != end; ++chunk, --n) { + if (!(chunk->flags & TFW_STR_NAME)) + continue; + if (unlikely(op == TFW_HTTP_MATCH_O_WILDCARD)) + break; + /* + * Create a temporary compound string, starting with this + * chunk. The total string length is not used here, so it + * is not set. + */ + tmp.chunks = chunk; + tmp.nchunks = n; + if (tfw_str_eq_cstr(&tmp, cstr, clen, TFW_STR_EQ_PREFIX)) + break; + /* + * 'Cookie' header has multiple name-value pairs while the + * 'Set-Cookie' has only one. + */ + if (unlikely(is_resp_hdr)) + return 0; + } + if (chunk == end) + return 0; + + /* Search cookie value, starting with next chunk. */ + for (++chunk; chunk != end; ++chunk) + if (chunk->flags & TFW_STR_VALUE) + break; + /* + * The party can send us zero-value cookie, + * treat this as not found cookie. + */ + if (unlikely(chunk == end)) + return 0; + + tfw_str_collect_cmp(chunk, end, val, ";"); + + return 1; +} diff --git a/fw/http_match.h b/fw/http_match.h index 239928099..b4f9574fd 100644 --- a/fw/http_match.h +++ b/fw/http_match.h @@ -35,9 +35,17 @@ typedef enum { TFW_HTTP_MATCH_F_METHOD, TFW_HTTP_MATCH_F_URI, TFW_HTTP_MATCH_F_MARK, + TFW_HTTP_MATCH_F_COOKIE, _TFW_HTTP_MATCH_F_COUNT } tfw_http_match_fld_t; +typedef enum { + TFW_HTTP_MATCH_V_NA = 0, + TFW_HTTP_MATCH_V_HEADER, + TFW_HTTP_MATCH_V_COOKIE, + _TFW_HTTP_MATCH_V_COUNT +} tfw_http_match_val_t; + typedef enum { TFW_HTTP_MATCH_O_NA = 0, TFW_HTTP_MATCH_O_WILDCARD, @@ -65,6 +73,7 @@ typedef enum { TFW_HTTP_MATCH_ACT_VHOST, TFW_HTTP_MATCH_ACT_MARK, TFW_HTTP_MATCH_ACT_BLOCK, + TFW_HTTP_MATCH_ACT_FLAG, _TFW_HTTP_MATCH_ACT_COUNT } tfw_http_rule_act_t; @@ -79,12 +88,28 @@ typedef struct { }; } TfwHttpMatchArg; +typedef struct { + tfw_http_match_val_t type; + union { + unsigned int hid; + struct { + tfw_http_match_op_t op; + unsigned int len; /* String length for speedup */ + const char *str; /* Allocated sring, free it after */ + } ptn; /* Pattern */ + }; +} TfwHttpMatchVal; + typedef struct { tfw_http_rule_act_t type; union { TfwHttpChain *chain; TfwVhost *vhost; unsigned int mark; + struct { + unsigned int fid; + bool set; + } flg; }; } TfwHttpAction; @@ -93,8 +118,8 @@ typedef struct { tfw_http_match_fld_t field; /* Field of a HTTP message to compare. */ tfw_http_match_op_t op; /* Comparison operator. */ TfwHttpAction act; /* Rule action. */ - unsigned int hid; /* Header ID. */ - unsigned int inv; /* Comparison inversion (inequality) flag.*/ + TfwHttpMatchVal val; /* A field value to compare with arg. */ + unsigned int inv; /* Comparison inversion (!=) flag.*/ TfwHttpMatchArg arg; /* A value to be compared with the field. note: the @arg has variable length. */ } TfwHttpMatchRule; @@ -114,7 +139,7 @@ void tfw_http_table_free(TfwHttpTable *table); * Match a HTTP request against a list of rules in chain. * Return a matching rule. */ -TfwHttpMatchRule *tfw_http_match_req(const TfwHttpReq *req, +TfwHttpMatchRule *tfw_http_match_req(TfwHttpReq *req, struct list_head *mlst); /** @@ -130,9 +155,17 @@ const char *tfw_http_arg_adjust(const char *arg, tfw_http_match_fld_t field, const char *raw_hdr_name, size_t *size_out, tfw_http_match_arg_t *type_out, tfw_http_match_op_t *op_out); +const char *tfw_http_val_adjust(const char *val, tfw_http_match_fld_t field, + unsigned int *len_out, + tfw_http_match_val_t *type_out, + tfw_http_match_op_t *op_out); int tfw_http_verify_hdr_field(tfw_http_match_fld_t field, const char **h_name, unsigned int *hid_out); +int tfw_http_search_cookie(const char *cstr, unsigned long clen, + const TfwStr *cookie, TfwStr *val, + tfw_http_match_op_t op, bool is_resp_hdr); + #define tfw_http_chain_rules_for_each(chain, func) \ ({ \ int r = 0; \ diff --git a/fw/http_sess.c b/fw/http_sess.c index ecd89e9c4..e3a19031f 100644 --- a/fw/http_sess.c +++ b/fw/http_sess.c @@ -46,6 +46,7 @@ #include "client.h" #include "hash.h" #include "http_msg.h" +#include "http_match.h" #include "http_sess.h" #include "http_sess_conf.h" #include "vhost.h" @@ -269,63 +270,6 @@ tfw_http_sticky_build_redirect(TfwHttpReq *req, StickyVal *sv, RedirMarkVal *mv, return TFW_HTTP_SESS_REDIRECT_NEED; } -/* - * Search for cookie defined in @sticky configuration in `Set-Cookie`/`Cookie` - * header value @cookie and save the cookie value into @val. @is_resp_hdr flag - * identifies the header name: true for `Set-Cookie`, false for `Cookie`. - */ -static int -search_cookie(TfwStickyCookie *sticky, const TfwStr *cookie, TfwStr *val, - bool is_resp_hdr) -{ - const char *const cstr = sticky->name_eq.data; - const unsigned long clen = sticky->name_eq.len; - TfwStr *chunk, *end; - TfwStr tmp = { .flags = 0, }; - unsigned int n = cookie->nchunks; - - BUG_ON(!TFW_STR_PLAIN(&sticky->name_eq)); - - /* Search cookie name. */ - end = cookie->chunks + cookie->nchunks; - for (chunk = cookie->chunks; chunk != end; ++chunk, --n) { - if (!(chunk->flags & TFW_STR_NAME)) - continue; - /* - * Create a temporary compound string, starting with this - * chunk. The total string length is not used here, so it - * is not set. - */ - tmp.chunks = chunk; - tmp.nchunks = n; - if (tfw_str_eq_cstr(&tmp, cstr, clen, TFW_STR_EQ_PREFIX)) - break; - /* - * 'Cookie' header has multiple name-value pairs while the - * 'Set-Cookie' has only one. - */ - if (unlikely(is_resp_hdr)) - return 0; - } - if (chunk == end) - return 0; - - /* Search cookie value, starting with next chunk. */ - for (++chunk; chunk != end; ++chunk) - if (chunk->flags & TFW_STR_VALUE) - break; - /* - * The party can send us zero-value cookie, - * treat this as not found cookie. - */ - if (unlikely(chunk == end)) - return 0; - - tfw_str_collect_cmp(chunk, end, val, ";"); - - return 1; -} - /* * Find Tempesta sticky cookie in an HTTP request. * @@ -356,7 +300,11 @@ tfw_http_sticky_get_req(TfwHttpReq *req, TfwStr *cookie_val) int r; tfw_http_msg_clnthdr_val(req, dup, TFW_HTTP_HDR_COOKIE, &value); - r = search_cookie(req->vhost->cookie, &value, cookie_val, false); + BUG_ON(!TFW_STR_PLAIN(&req->vhost->cookie->name_eq)); + r = tfw_http_search_cookie(req->vhost->cookie->name_eq.data, + req->vhost->cookie->name_eq.len, + &value, cookie_val, + TFW_HTTP_MATCH_O_EQ, false); if (r) return r; } @@ -1231,6 +1179,8 @@ tfw_http_sticky_get_resp(TfwHttpResp *resp, TfwStr *cookie_val) { TfwStickyCookie *sticky = resp->req->vhost->cookie; TfwStr *hdr, *dup, *dup_end; + + BUG_ON(!TFW_STR_PLAIN(&sticky->name_eq)); /* * Each cookie in set header is placed in its own `Set-Cookie` header, * need to look through all of them @@ -1242,8 +1192,13 @@ tfw_http_sticky_get_resp(TfwHttpResp *resp, TfwStr *cookie_val) if (TFW_STR_EMPTY(dup)) continue; tfw_http_msg_srvhdr_val(dup, TFW_HTTP_HDR_SET_COOKIE, &value); - if (search_cookie(sticky, &value, cookie_val, true)) + if (tfw_http_search_cookie(sticky->name_eq.data, + sticky->name_eq.len, + &value, cookie_val, + TFW_HTTP_MATCH_O_EQ, true)) + { return 1; + } } return 0; diff --git a/fw/http_tbl.c b/fw/http_tbl.c index e980350b4..000ff3131 100644 --- a/fw/http_tbl.c +++ b/fw/http_tbl.c @@ -194,6 +194,7 @@ static const TfwCfgEnum tfw_http_tbl_cfg_field_enum[] = { { "hdr", TFW_HTTP_MATCH_F_HDR }, { "mark", TFW_HTTP_MATCH_F_MARK }, { "method", TFW_HTTP_MATCH_F_METHOD }, + { "cookie", TFW_HTTP_MATCH_F_COOKIE }, { 0 } }; @@ -337,6 +338,7 @@ tfw_cfgop_http_tbl_chain_finish(TfwCfgSpec *cs) * uri == "*.php" -> static; * mark == 2 -> waf_chain; * referer != "*hacked.com" -> mark = 7; + * hdr "Referer" == "http://badhost.com*" -> block; * -> mark = 3; * } * @@ -345,17 +347,21 @@ tfw_cfgop_http_tbl_chain_finish(TfwCfgSpec *cs) * the list of rules of current chain. * * Syntax: - * +------------------------ First operand of rule's condition part - * | (HTTP request field or 'mark'); - * | +-------------------- Condition type: equal ('==') or not - * | | equal ('!='); - * | | +-------------- Second operand of rule's condition part - * | | | (argument for the rule - any string); - * | | | +---- Action part of the rule (reference to - * | | | | other http_chain or vhost, 'block' or - * | | | | 'mark' action). - * V V V V - * uri == "*.php" -> static + * +--------------------------------- First operand of rule's condition part + * | (HTTP request field or 'mark'); + * | +---------------------------- Header or any other field value to do + * | | comparison with + * | | +---------------------- Condition type: equal ('==') or not + * | | | equal ('!='); + * | | | +--------------- Second operand of rule's condition part + * | | | | (argument for the rule - any string); + * | | | | +------- Action part of the rule (reference to + * | | | | | other http_chain or vhost, 'block' or + * | | | | | 'mark' action). + * | | | | | +- Possible value for specified action + * | | | | | | + * V V V V V V + * hdr "Host" == "bad.ru" -> mark = 7 * */ static int @@ -363,10 +369,14 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) { int r; TfwHttpMatchRule *rule; - const char *in_field, *hdr, *action, *action_val, *in_arg, *arg = NULL; - unsigned int invert, hid = TFW_HTTP_HDR_RAW; - tfw_http_match_op_t op = TFW_HTTP_MATCH_O_WILDCARD; + const char *in_field, *in_field_val, *action, *action_val, + *in_arg, *arg = NULL, *val = NULL; + unsigned int invert, hid = TFW_HTTP_HDR_RAW, + act_val_parsed, val_len; + tfw_http_match_op_t op = TFW_HTTP_MATCH_O_WILDCARD, + op_val = TFW_HTTP_MATCH_O_WILDCARD; tfw_http_match_fld_t field = TFW_HTTP_MATCH_F_WILDCARD; + tfw_http_match_val_t type_val = TFW_HTTP_MATCH_V_NA; tfw_http_match_arg_t type = TFW_HTTP_MATCH_A_WILDCARD; TfwCfgRule *cfg_rule = &e->rule; size_t arg_size = 0; @@ -383,7 +393,7 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) invert = cfg_rule->inv; in_field = cfg_rule->fst; - hdr = cfg_rule->fst_ext; + in_field_val = cfg_rule->fst_ext; in_arg = cfg_rule->snd; action = cfg_rule->act; action_val = cfg_rule->val; @@ -406,11 +416,30 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) in_field); return r; } - if ((r = tfw_http_verify_hdr_field(field, &hdr, &hid))) - return r; + if (field != TFW_HTTP_MATCH_F_COOKIE) { + if ((r = tfw_http_verify_hdr_field(field, + &in_field_val, + &hid))) + { + return r; + } + } else { + if (invert) { + T_ERR_NL("http_tbl: invalid rule field: " + "inequality in cookie field: '%s'\n", + in_field); + return -EINVAL; + } + } + - arg = tfw_http_arg_adjust(in_arg, field, hdr, &arg_size, - &type, &op); + val = tfw_http_val_adjust(in_field_val, field, + &val_len, &type_val, &op_val); + if (IS_ERR(val)) + return PTR_ERR(val); + + arg = tfw_http_arg_adjust(in_arg, field, in_field_val, + &arg_size, &type, &op); if (IS_ERR(arg)) return PTR_ERR(arg); } @@ -425,7 +454,16 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) && type != TFW_HTTP_MATCH_A_NUM && type != TFW_HTTP_MATCH_A_METHOD && type != TFW_HTTP_MATCH_A_WILDCARD); - rule->hid = hid; + BUG_ON(type_val != TFW_HTTP_MATCH_V_HEADER + && type_val != TFW_HTTP_MATCH_V_COOKIE); + rule->val.type = type_val; + if (type_val == TFW_HTTP_MATCH_V_COOKIE) { + rule->val.ptn.op = op_val; + rule->val.ptn.str = val; + rule->val.ptn.len = val_len; + } else { + rule->val.hid = hid; + } rule->inv = invert; rule->field = field; rule->op = op; @@ -446,9 +484,28 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) return -EINVAL; } rule->act.type = TFW_HTTP_MATCH_ACT_MARK; + } else if (strlen(action) && action[0] == '$') { + if (!strcasecmp(action, "$cache")) { + tfw_cfg_parse_uint(action_val, &act_val_parsed); + if (act_val_parsed != 1 && act_val_parsed != 0) { + T_ERR_NL("http_tbl: '$cache' action value " + "must 0 or 1: '%s'\n", + action_val); + return -EINVAL; + } + rule->act.type = TFW_HTTP_MATCH_ACT_FLAG; + rule->act.flg.set = (act_val_parsed == 1); + rule->act.flg.fid = TFW_HTTP_B_CACHE_CALC; + } else { + T_ERR_NL("http_tbl: only '$cache' flag setting action " + "supported for now: '%s'\n", + action); + return -EINVAL; + } } else if (action_val) { - T_ERR_NL("http_tbl: not 'mark' actions must not have any value:" - " '%s'\n", action_val); + T_ERR_NL("http_tbl: only 'mark' or '$..' actions " + "may have any value: '%s'\n", + action_val); return -EINVAL; } else if (!strcasecmp(action, "block")) { rule->act.type = TFW_HTTP_MATCH_ACT_BLOCK; @@ -475,6 +532,7 @@ tfw_cfgop_http_rule(TfwCfgSpec *cs, TfwCfgEntry *e) return 0; err: + kfree(val); kfree(arg); return r; } diff --git a/fw/t/unit/test_http_match.c b/fw/t/unit/test_http_match.c index b0e702c23..bef54fdef 100644 --- a/fw/t/unit/test_http_match.c +++ b/fw/t/unit/test_http_match.c @@ -132,7 +132,8 @@ test_chain_add_rule_str(int test_id, tfw_http_match_fld_t field, EXPECT_NOT_NULL(e); if (!e) goto err; - e->rule.hid = hid; + e->rule.val.type = TFW_HTTP_MATCH_V_HEADER; + e->rule.val.hid = hid; e->rule.field = field; e->rule.op = op; e->rule.arg.type = type;