mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-08 08:07:10 +02:00
[MEDIUM] Implement tcp inspect response rules
This commit is contained in:
parent
c89a57284a
commit
97679e7901
@ -34,6 +34,7 @@ int tcpv4_connect_server(struct stream_interface *si,
|
|||||||
struct proxy *be, struct server *srv,
|
struct proxy *be, struct server *srv,
|
||||||
struct sockaddr *srv_addr, struct sockaddr *from_addr);
|
struct sockaddr *srv_addr, struct sockaddr *from_addr);
|
||||||
int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
|
int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit);
|
||||||
|
int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit);
|
||||||
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
|
int tcp_persist_rdp_cookie(struct session *s, struct buffer *req, int an_bit);
|
||||||
int tcp_exec_req_rules(struct session *s);
|
int tcp_exec_req_rules(struct session *s);
|
||||||
|
|
||||||
|
@ -194,6 +194,10 @@ struct proxy {
|
|||||||
struct list inspect_rules; /* inspection rules */
|
struct list inspect_rules; /* inspection rules */
|
||||||
struct list l4_rules; /* layer4 rules */
|
struct list l4_rules; /* layer4 rules */
|
||||||
} tcp_req;
|
} tcp_req;
|
||||||
|
struct { /* TCP request processing */
|
||||||
|
unsigned int inspect_delay; /* inspection delay */
|
||||||
|
struct list inspect_rules; /* inspection rules */
|
||||||
|
} tcp_rep;
|
||||||
int acl_requires; /* Elements required to satisfy all ACLs (ACL_USE_*) */
|
int acl_requires; /* Elements required to satisfy all ACLs (ACL_USE_*) */
|
||||||
struct server *srv, defsrv; /* known servers; default server configuration */
|
struct server *srv, defsrv; /* known servers; default server configuration */
|
||||||
int srv_act, srv_bck; /* # of servers eligible for LB (UP|!checked) AND (enabled+weight!=0) */
|
int srv_act, srv_bck; /* # of servers eligible for LB (UP|!checked) AND (enabled+weight!=0) */
|
||||||
|
@ -1034,12 +1034,14 @@ static void init_new_proxy(struct proxy *p)
|
|||||||
LIST_INIT(&p->sticking_rules);
|
LIST_INIT(&p->sticking_rules);
|
||||||
LIST_INIT(&p->storersp_rules);
|
LIST_INIT(&p->storersp_rules);
|
||||||
LIST_INIT(&p->tcp_req.inspect_rules);
|
LIST_INIT(&p->tcp_req.inspect_rules);
|
||||||
|
LIST_INIT(&p->tcp_rep.inspect_rules);
|
||||||
LIST_INIT(&p->tcp_req.l4_rules);
|
LIST_INIT(&p->tcp_req.l4_rules);
|
||||||
LIST_INIT(&p->req_add);
|
LIST_INIT(&p->req_add);
|
||||||
LIST_INIT(&p->rsp_add);
|
LIST_INIT(&p->rsp_add);
|
||||||
|
|
||||||
/* Timeouts are defined as -1 */
|
/* Timeouts are defined as -1 */
|
||||||
proxy_reset_timeouts(p);
|
proxy_reset_timeouts(p);
|
||||||
|
p->tcp_rep.inspect_delay = TICK_ETERNITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_default_instance()
|
void init_default_instance()
|
||||||
@ -2768,7 +2770,9 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
|
|||||||
err_code |= ERR_ALERT | ERR_FATAL;
|
err_code |= ERR_ALERT | ERR_FATAL;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (flags & STK_ON_RSP)
|
||||||
|
err_code |= warnif_cond_requires_req(cond, file, linenum);
|
||||||
|
else
|
||||||
err_code |= warnif_cond_requires_resp(cond, file, linenum);
|
err_code |= warnif_cond_requires_resp(cond, file, linenum);
|
||||||
|
|
||||||
rule = (struct sticking_rule *)calloc(1, sizeof(*rule));
|
rule = (struct sticking_rule *)calloc(1, sizeof(*rule));
|
||||||
@ -5970,6 +5974,9 @@ int check_config_validity()
|
|||||||
!LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
|
!LIST_ISEMPTY(&curproxy->tcp_req.inspect_rules))
|
||||||
curproxy->be_req_ana |= AN_REQ_INSPECT_BE;
|
curproxy->be_req_ana |= AN_REQ_INSPECT_BE;
|
||||||
|
|
||||||
|
if (!LIST_ISEMPTY(&curproxy->tcp_rep.inspect_rules))
|
||||||
|
curproxy->be_rsp_ana |= AN_RES_INSPECT;
|
||||||
|
|
||||||
if (curproxy->mode == PR_MODE_HTTP) {
|
if (curproxy->mode == PR_MODE_HTTP) {
|
||||||
curproxy->be_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE;
|
curproxy->be_req_ana |= AN_REQ_WAIT_HTTP | AN_REQ_HTTP_INNER | AN_REQ_HTTP_PROCESS_BE;
|
||||||
curproxy->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE;
|
curproxy->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE;
|
||||||
|
215
src/proto_tcp.c
215
src/proto_tcp.c
@ -50,6 +50,7 @@
|
|||||||
#include <proto/stick_table.h>
|
#include <proto/stick_table.h>
|
||||||
#include <proto/stream_sock.h>
|
#include <proto/stream_sock.h>
|
||||||
#include <proto/task.h>
|
#include <proto/task.h>
|
||||||
|
#include <proto/buffers.h>
|
||||||
|
|
||||||
#ifdef CONFIG_HAP_CTTPROXY
|
#ifdef CONFIG_HAP_CTTPROXY
|
||||||
#include <import/ip_tproxy.h>
|
#include <import/ip_tproxy.h>
|
||||||
@ -750,6 +751,91 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function performs the TCP response analysis on the current response. It
|
||||||
|
* returns 1 if the processing can continue on next analysers, or zero if it
|
||||||
|
* needs more data, encounters an error, or wants to immediately abort the
|
||||||
|
* response. It relies on buffers flags, and updates s->rep->analysers. The
|
||||||
|
* function may be called for backend rules.
|
||||||
|
*/
|
||||||
|
int tcp_inspect_response(struct session *s, struct buffer *rep, int an_bit)
|
||||||
|
{
|
||||||
|
struct tcp_rule *rule;
|
||||||
|
int partial;
|
||||||
|
|
||||||
|
DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
|
||||||
|
now_ms, __FUNCTION__,
|
||||||
|
s,
|
||||||
|
rep,
|
||||||
|
rep->rex, rep->wex,
|
||||||
|
rep->flags,
|
||||||
|
rep->l,
|
||||||
|
rep->analysers);
|
||||||
|
|
||||||
|
/* We don't know whether we have enough data, so must proceed
|
||||||
|
* this way :
|
||||||
|
* - iterate through all rules in their declaration order
|
||||||
|
* - if one rule returns MISS, it means the inspect delay is
|
||||||
|
* not over yet, then return immediately, otherwise consider
|
||||||
|
* it as a non-match.
|
||||||
|
* - if one rule returns OK, then return OK
|
||||||
|
* - if one rule returns KO, then return KO
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (rep->flags & BF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
|
||||||
|
partial = 0;
|
||||||
|
else
|
||||||
|
partial = ACL_PARTIAL;
|
||||||
|
|
||||||
|
list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
|
||||||
|
int ret = ACL_PAT_PASS;
|
||||||
|
|
||||||
|
if (rule->cond) {
|
||||||
|
ret = acl_exec_cond(rule->cond, s->be, s, &s->txn, ACL_DIR_RTR | partial);
|
||||||
|
if (ret == ACL_PAT_MISS) {
|
||||||
|
/* just set the analyser timeout once at the beginning of the response */
|
||||||
|
if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
|
||||||
|
rep->analyse_exp = tick_add_ifset(now_ms, s->be->tcp_rep.inspect_delay);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = acl_pass(ret);
|
||||||
|
if (rule->cond->pol == ACL_COND_UNLESS)
|
||||||
|
ret = !ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
/* we have a matching rule. */
|
||||||
|
if (rule->action == TCP_ACT_REJECT) {
|
||||||
|
buffer_abort(rep);
|
||||||
|
buffer_abort(s->req);
|
||||||
|
rep->analysers = 0;
|
||||||
|
|
||||||
|
s->be->counters.denied_resp++;
|
||||||
|
if (s->listener->counters)
|
||||||
|
s->listener->counters->denied_resp++;
|
||||||
|
|
||||||
|
if (!(s->flags & SN_ERR_MASK))
|
||||||
|
s->flags |= SN_ERR_PRXCOND;
|
||||||
|
if (!(s->flags & SN_FINST_MASK))
|
||||||
|
s->flags |= SN_FINST_D;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* otherwise accept */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we get there, it means we have no rule which matches, or
|
||||||
|
* we have an explicit accept, so we apply the default accept.
|
||||||
|
*/
|
||||||
|
rep->analysers &= ~an_bit;
|
||||||
|
rep->analyse_exp = TICK_ETERNITY;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This function performs the TCP layer4 analysis on the current request. It
|
/* This function performs the TCP layer4 analysis on the current request. It
|
||||||
* returns 0 if a reject rule matches, otherwise 1 if either an accept rule
|
* returns 0 if a reject rule matches, otherwise 1 if either an accept rule
|
||||||
* matches or if no more rule matches. It can only use rules which don't need
|
* matches or if no more rule matches. It can only use rules which don't need
|
||||||
@ -822,6 +908,51 @@ int tcp_exec_req_rules(struct session *s)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse a tcp-response rule. Return a negative value in case of failure */
|
||||||
|
static int tcp_parse_response_rule(char **args, int arg, int section_type,
|
||||||
|
struct proxy *curpx, struct proxy *defpx,
|
||||||
|
struct tcp_rule *rule, char *err, int errlen)
|
||||||
|
{
|
||||||
|
if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
|
||||||
|
snprintf(err, errlen, "%s %s is only allowed in 'backend' sections",
|
||||||
|
args[0], args[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(args[arg], "accept") == 0) {
|
||||||
|
arg++;
|
||||||
|
rule->action = TCP_ACT_ACCEPT;
|
||||||
|
}
|
||||||
|
else if (strcmp(args[arg], "reject") == 0) {
|
||||||
|
arg++;
|
||||||
|
rule->action = TCP_ACT_REJECT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
snprintf(err, errlen,
|
||||||
|
"'%s %s' expects 'accept' or 'reject' in %s '%s' (was '%s')",
|
||||||
|
args[0], args[1], proxy_type_str(curpx), curpx->id, args[arg]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
|
||||||
|
if ((rule->cond = build_acl_cond(NULL, 0, curpx, (const char **)args+arg)) == NULL) {
|
||||||
|
snprintf(err, errlen,
|
||||||
|
"error detected in %s '%s' while parsing '%s' condition",
|
||||||
|
proxy_type_str(curpx), curpx->id, args[arg]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (*args[arg]) {
|
||||||
|
snprintf(err, errlen,
|
||||||
|
"'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (was '%s')",
|
||||||
|
args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Parse a tcp-request rule. Return a negative value in case of failure */
|
/* Parse a tcp-request rule. Return a negative value in case of failure */
|
||||||
static int tcp_parse_request_rule(char **args, int arg, int section_type,
|
static int tcp_parse_request_rule(char **args, int arg, int section_type,
|
||||||
struct proxy *curpx, struct proxy *defpx,
|
struct proxy *curpx, struct proxy *defpx,
|
||||||
@ -890,6 +1021,89 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This function should be called to parse a line starting with the "tcp-response"
|
||||||
|
* keyword.
|
||||||
|
*/
|
||||||
|
static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
|
||||||
|
struct proxy *defpx, char *err, int errlen)
|
||||||
|
{
|
||||||
|
const char *ptr = NULL;
|
||||||
|
unsigned int val;
|
||||||
|
int retlen;
|
||||||
|
int warn = 0;
|
||||||
|
int arg;
|
||||||
|
struct tcp_rule *rule;
|
||||||
|
|
||||||
|
if (!*args[1]) {
|
||||||
|
snprintf(err, errlen, "missing argument for '%s' in %s '%s'",
|
||||||
|
args[0], proxy_type_str(curpx), curpx->id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(args[1], "inspect-delay") == 0) {
|
||||||
|
if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
|
||||||
|
snprintf(err, errlen, "%s %s is only allowed in 'backend' sections",
|
||||||
|
args[0], args[1]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
|
||||||
|
retlen = snprintf(err, errlen,
|
||||||
|
"'%s %s' expects a positive delay in milliseconds, in %s '%s'",
|
||||||
|
args[0], args[1], proxy_type_str(curpx), curpx->id);
|
||||||
|
if (ptr && retlen < errlen)
|
||||||
|
retlen += snprintf(err + retlen, errlen - retlen,
|
||||||
|
" (unexpected character '%c')", *ptr);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curpx->tcp_rep.inspect_delay) {
|
||||||
|
snprintf(err, errlen, "ignoring %s %s (was already defined) in %s '%s'",
|
||||||
|
args[0], args[1], proxy_type_str(curpx), curpx->id);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
curpx->tcp_rep.inspect_delay = val;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
rule = (struct tcp_rule *)calloc(1, sizeof(*rule));
|
||||||
|
LIST_INIT(&rule->list);
|
||||||
|
arg = 1;
|
||||||
|
|
||||||
|
if (strcmp(args[1], "content") == 0) {
|
||||||
|
arg++;
|
||||||
|
if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, errlen) < 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (rule->cond && (rule->cond->requires & ACL_USE_L6REQ_VOLATILE)) {
|
||||||
|
struct acl *acl;
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
acl = cond_find_require(rule->cond, ACL_USE_L6REQ_VOLATILE);
|
||||||
|
name = acl ? acl->name : "(unknown)";
|
||||||
|
|
||||||
|
retlen = snprintf(err, errlen,
|
||||||
|
"acl '%s' involves some request-only criteria which will be ignored.",
|
||||||
|
name);
|
||||||
|
warn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
retlen = snprintf(err, errlen,
|
||||||
|
"'%s' expects 'inspect-delay' or 'content' in %s '%s' (was '%s')",
|
||||||
|
args[0], proxy_type_str(curpx), curpx->id, args[1]);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return warn;
|
||||||
|
error:
|
||||||
|
free(rule);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* This function should be called to parse a line starting with the "tcp-request"
|
/* This function should be called to parse a line starting with the "tcp-request"
|
||||||
* keyword.
|
* keyword.
|
||||||
*/
|
*/
|
||||||
@ -1129,6 +1343,7 @@ pattern_fetch_dport(struct proxy *px, struct session *l4, void *l7, int dir,
|
|||||||
|
|
||||||
static struct cfg_kw_list cfg_kws = {{ },{
|
static struct cfg_kw_list cfg_kws = {{ },{
|
||||||
{ CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
|
{ CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
|
||||||
|
{ CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
|
||||||
{ 0, NULL, NULL },
|
{ 0, NULL, NULL },
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
@ -1585,6 +1585,12 @@ struct task *process_session(struct task *t)
|
|||||||
while (ana_list && max_loops--) {
|
while (ana_list && max_loops--) {
|
||||||
/* Warning! ensure that analysers are always placed in ascending order! */
|
/* Warning! ensure that analysers are always placed in ascending order! */
|
||||||
|
|
||||||
|
if (ana_list & AN_RES_INSPECT) {
|
||||||
|
if (!tcp_inspect_response(s, s->rep, AN_RES_INSPECT))
|
||||||
|
break;
|
||||||
|
UPDATE_ANALYSERS(s->rep->analysers, ana_list, ana_back, AN_RES_INSPECT);
|
||||||
|
}
|
||||||
|
|
||||||
if (ana_list & AN_RES_WAIT_HTTP) {
|
if (ana_list & AN_RES_WAIT_HTTP) {
|
||||||
if (!http_wait_for_response(s, s->rep, AN_RES_WAIT_HTTP))
|
if (!http_wait_for_response(s, s->rep, AN_RES_WAIT_HTTP))
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user