diff --git a/include/proto/proto_tcp.h b/include/proto/proto_tcp.h index 5c0087b81..37d8ea8bd 100644 --- a/include/proto/proto_tcp.h +++ b/include/proto/proto_tcp.h @@ -35,6 +35,7 @@ int tcpv4_connect_server(struct stream_interface *si, struct sockaddr *srv_addr, struct sockaddr *from_addr); int tcp_inspect_request(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); #endif /* _PROTO_PROTO_TCP_H */ diff --git a/include/types/protocols.h b/include/types/protocols.h index 0911a40e7..8f8faefbe 100644 --- a/include/types/protocols.h +++ b/include/types/protocols.h @@ -72,6 +72,7 @@ #define LI_O_FOREIGN 0x0002 /* permit listening on foreing addresses */ #define LI_O_NOQUICKACK 0x0004 /* disable quick ack of immediate data (linux) */ #define LI_O_DEF_ACCEPT 0x0008 /* wait up to 1 second for data before accepting */ +#define LI_O_TCP_RULES 0x0010 /* run TCP rules checks on the incoming connection */ /* The listener will be directly referenced by the fdtab[] which holds its * socket. The listener provides the protocol-specific accept() function to diff --git a/src/cfgparse.c b/src/cfgparse.c index e5b66e8ca..41313783f 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -5356,6 +5356,9 @@ out_uri_auth_compat: listener->handler = process_session; listener->analysers |= curproxy->fe_req_ana; + if (!LIST_ISEMPTY(&curproxy->tcp_req.l4_rules)) + listener->options |= LI_O_TCP_RULES; + /* smart accept mode is automatic in HTTP mode */ if ((curproxy->options2 & PR_O2_SMARTACC) || (curproxy->mode == PR_MODE_HTTP && diff --git a/src/frontend.c b/src/frontend.c index 08569ff4f..5af5582e8 100644 --- a/src/frontend.c +++ b/src/frontend.c @@ -63,7 +63,6 @@ int frontend_accept(struct listener *l, int cfd, struct sockaddr_storage *addr) struct session *s; struct http_txn *txn; struct task *t; - struct tcp_rule *rule; if (unlikely((s = pool_alloc2(pool2_session)) == NULL)) { Alert("out of memory in event_accept().\n"); @@ -134,40 +133,13 @@ int frontend_accept(struct listener *l, int cfd, struct sockaddr_storage *addr) proxy_inc_fe_ctr(l, p); /* note: cum_beconn will be increased once assigned */ /* now evaluate the tcp-request rules */ - list_for_each_entry(rule, &p->tcp_req.l4_rules, list) { - int ret = ACL_PAT_PASS; - - if (rule->cond) { - ret = acl_exec_cond(rule->cond, p, s, &s->txn, ACL_DIR_REQ); - 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) { - p->counters.denied_req++; - if (l->counters) - l->counters->denied_req++; - - if (!(s->flags & SN_ERR_MASK)) - s->flags |= SN_ERR_PRXCOND; - if (!(s->flags & SN_FINST_MASK)) - s->flags |= SN_FINST_R; - - task_free(t); - LIST_DEL(&s->list); - pool_free2(pool2_session, s); - - /* let's do a no-linger now to close with a single RST. */ - setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); - return 0; - } - - /* otherwise it's an accept */ - break; - } + if ((l->options & LI_O_TCP_RULES) && !tcp_exec_req_rules(s)) { + task_free(t); + LIST_DEL(&s->list); + pool_free2(pool2_session, s); + /* let's do a no-linger now to close with a single RST. */ + setsockopt(cfd, SOL_SOCKET, SO_LINGER, (struct linger *) &nolinger, sizeof(struct linger)); + return 0; } /* pre-initialize the other side's stream interface */ diff --git a/src/proto_tcp.c b/src/proto_tcp.c index b753c3bbd..3a1abad9e 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -708,6 +708,46 @@ int tcp_inspect_request(struct session *s, struct buffer *req, int an_bit) return 1; } +/* 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 + * matches or if no more rule matches. It can only use rules which don't need + * any data. + */ +int tcp_exec_req_rules(struct session *s) +{ + struct tcp_rule *rule; + int ret; + + list_for_each_entry(rule, &s->fe->tcp_req.l4_rules, list) { + ret = ACL_PAT_PASS; + + if (rule->cond) { + ret = acl_exec_cond(rule->cond, s->fe, s, NULL, ACL_DIR_REQ); + 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) { + s->fe->counters.denied_req++; + if (s->listener->counters) + s->listener->counters->denied_req++; + + if (!(s->flags & SN_ERR_MASK)) + s->flags |= SN_ERR_PRXCOND; + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + return 0; + } + /* otherwise it's an accept */ + break; + } + } + return 1; +} + /* This function should be called to parse a line starting with the "tcp-request" * keyword. */