diff --git a/include/types/buffers.h b/include/types/buffers.h index ba7bcb8ae..59e6d2960 100644 --- a/include/types/buffers.h +++ b/include/types/buffers.h @@ -142,7 +142,8 @@ #define AN_REQ_HTTP_INNER 0x00000020 /* inner processing of HTTP request */ #define AN_REQ_HTTP_TARPIT 0x00000040 /* wait for end of HTTP tarpit */ #define AN_REQ_HTTP_BODY 0x00000080 /* inspect HTTP request body */ -/* unused: 0x100, 0x200 */ +#define AN_REQ_STICKING_RULES 0x00000100 /* table persistence matching */ +/* unused: 0x200 */ #define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */ #define AN_REQ_HTTP_XFER_BODY 0x00000800 /* forward request body */ @@ -151,6 +152,7 @@ #define AN_RES_WAIT_HTTP 0x00020000 /* wait for HTTP response */ #define AN_RES_HTTP_PROCESS_BE 0x00040000 /* process backend's HTTP part */ #define AN_RES_HTTP_PROCESS_FE 0x00040000 /* process frontend's HTTP part (same for now) */ +#define AN_RES_STORE_RULES 0x00080000 /* table persistence matching */ #define AN_RES_HTTP_XFER_BODY 0x00100000 /* forward response body */ diff --git a/src/cfgparse.c b/src/cfgparse.c index d4ddc2ea4..39a79f2f7 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -4621,6 +4621,10 @@ int check_config_validity() list_for_each_entry(mrule, &curproxy->sticking_rules, list) { struct proxy *target; + curproxy->be_req_ana |= AN_REQ_STICKING_RULES; + if (mrule->flags & STK_IS_STORE) + curproxy->be_rsp_ana |= AN_RES_STORE_RULES; + if (mrule->table.name) target = findproxy(mrule->table.name, PR_CAP_BE); else @@ -4651,6 +4655,8 @@ int check_config_validity() list_for_each_entry(mrule, &curproxy->storersp_rules, list) { struct proxy *target; + curproxy->be_rsp_ana |= AN_RES_STORE_RULES; + if (mrule->table.name) target = findproxy(mrule->table.name, PR_CAP_BE); else diff --git a/src/session.c b/src/session.c index 65e22f601..20a5886a8 100644 --- a/src/session.c +++ b/src/session.c @@ -27,12 +27,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -610,6 +612,169 @@ int process_switching_rules(struct session *s, struct buffer *req, int an_bit) return 0; } +/* This stream analyser works on a request. It applies all sticking rules on + * it then returns 1. The data must already be present in the buffer otherwise + * they won't match. It always returns 1. + */ +int process_sticking_rules(struct session *s, struct buffer *req, int an_bit) +{ + struct proxy *px = s->be; + struct sticking_rule *rule; + + DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n", + now_ms, __FUNCTION__, + s, + req, + req->rex, req->wex, + req->flags, + req->l, + req->analysers); + + list_for_each_entry(rule, &px->sticking_rules, list) { + int ret = 1 ; + int i; + + for (i = 0; i < s->store_count; i++) { + if (rule->table.t == s->store[i].table) + break; + } + + if (i != s->store_count) + continue; + + if (rule->cond) { + ret = acl_exec_cond(rule->cond, px, s, &s->txn, ACL_DIR_REQ); + ret = acl_pass(ret); + if (rule->cond->pol == ACL_COND_UNLESS) + ret = !ret; + } + + if (ret) { + struct stktable_key *key; + + key = pattern_process_key(px, s, &s->txn, PATTERN_FETCH_REQ, rule->expr, rule->table.t->type); + if (!key) + continue; + + if (rule->flags & STK_IS_MATCH) { + struct stksess *ts; + + if ((ts = stktable_lookup(rule->table.t, key)) != NULL) { + if (!(s->flags & SN_ASSIGNED)) { + struct eb32_node *node; + + /* srv found in table */ + node = eb32_lookup(&px->conf.used_server_id, ts->sid); + if (node) { + struct server *srv; + + srv = container_of(node, struct server, conf.id); + if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) { + s->flags |= SN_DIRECT | SN_ASSIGNED; + s->srv = srv; + } + } + } + ts->expire = tick_add(now_ms, MS_TO_TICKS(rule->table.t->expire)); + } + } + if (rule->flags & STK_IS_STORE) { + if (s->store_count < (sizeof(s->store) / sizeof(s->store[0]))) { + struct stksess *ts; + + ts = stksess_new(rule->table.t, key); + if (ts) { + s->store[s->store_count].table = rule->table.t; + s->store[s->store_count++].ts = ts; + } + } + } + } + } + + req->analysers &= ~an_bit; + req->analyse_exp = TICK_ETERNITY; + return 1; +} + +/* This stream analyser works on a response. It applies all store rules on it + * then returns 1. The data must already be present in the buffer otherwise + * they won't match. It always returns 1. + */ +int process_store_rules(struct session *s, struct buffer *rep, int an_bit) +{ + struct proxy *px = s->be; + struct sticking_rule *rule; + int i; + + DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n", + now_ms, __FUNCTION__, + s, + req, + req->rex, req->wex, + req->flags, + req->l, + req->analysers); + + list_for_each_entry(rule, &px->storersp_rules, list) { + int ret = 1 ; + int storereqidx = -1; + + for (i = 0; i < s->store_count; i++) { + if (rule->table.t == s->store[i].table) { + if (!(s->store[i].flags)) + storereqidx = i; + break; + } + } + + if ((i != s->store_count) && (storereqidx == -1)) + continue; + + if (rule->cond) { + ret = acl_exec_cond(rule->cond, px, s, &s->txn, ACL_DIR_RTR); + ret = acl_pass(ret); + if (rule->cond->pol == ACL_COND_UNLESS) + ret = !ret; + } + + if (ret) { + struct stktable_key *key; + + key = pattern_process_key(px, s, &s->txn, PATTERN_FETCH_RTR, rule->expr, rule->table.t->type); + if (!key) + continue; + + if (storereqidx != -1) { + stksess_key(s->store[storereqidx].table, s->store[storereqidx].ts, key); + s->store[storereqidx].flags = 1; + } + else if (s->store_count < (sizeof(s->store) / sizeof(s->store[0]))) { + struct stksess *ts; + + ts = stksess_new(rule->table.t, key); + if (ts) { + s->store[s->store_count].table = rule->table.t; + s->store[s->store_count].flags = 1; + s->store[s->store_count++].ts = ts; + } + } + } + } + + /* process store request and store response */ + for (i = 0; i < s->store_count; i++) { + if (stktable_store(s->store[i].table, s->store[i].ts, s->srv->puid) > 0) { + stksess_free(s->store[i].table, s->store[i].ts); + s->store[i].ts = NULL; + } + } + + rep->analysers &= ~an_bit; + rep->analyse_exp = TICK_ETERNITY; + return 1; +} + /* This macro is very specific to the function below. See the comments in * process_session() below to understand the logic and the tests. */ @@ -893,6 +1058,12 @@ resync_stream_interface: UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE); } + if (ana_list & AN_REQ_STICKING_RULES) { + if (!process_sticking_rules(s, s->req, AN_REQ_STICKING_RULES)) + break; + UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_STICKING_RULES); + } + if (ana_list & AN_REQ_HTTP_XFER_BODY) { if (!http_request_forward_body(s, s->req, AN_REQ_HTTP_XFER_BODY)) break; @@ -975,6 +1146,12 @@ resync_stream_interface: UPDATE_ANALYSERS(s->rep->analysers, ana_list, ana_back, AN_RES_WAIT_HTTP); } + if (ana_list & AN_RES_STORE_RULES) { + if (!process_store_rules(s, s->rep, AN_RES_STORE_RULES)) + break; + UPDATE_ANALYSERS(s->rep->analysers, ana_list, ana_back, AN_RES_STORE_RULES); + } + if (ana_list & AN_RES_HTTP_PROCESS_BE) { if (!http_process_res_common(s, s->rep, AN_RES_HTTP_PROCESS_BE, s->be)) break;