blob: 5f8c74dd5fed564fcbda71de9ee97aae37270838 [file] [log] [blame]
Willy Tarreau39713102016-11-25 15:49:32 +01001/*
2 * "tcp" rules processing
3 *
4 * Copyright 2000-2016 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
Willy Tarreaudcc048a2020-06-04 19:11:43 +020012#include <haproxy/acl.h>
Willy Tarreau122eba92020-06-04 10:15:32 +020013#include <haproxy/action.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020014#include <haproxy/arg-t.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020015#include <haproxy/api.h>
Willy Tarreau278161c2020-06-04 11:18:28 +020016#include <haproxy/capture-t.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020017#include <haproxy/cfgparse.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020018#include <haproxy/channel.h>
Willy Tarreau7ea393d2020-06-04 18:02:10 +020019#include <haproxy/connection.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020020#include <haproxy/global.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020021#include <haproxy/list.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020022#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020023#include <haproxy/proxy.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020024#include <haproxy/sample.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020025#include <haproxy/stick_table.h>
26#include <haproxy/stream-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020027#include <haproxy/stream_interface.h>
Willy Tarreau8b550af2020-06-04 17:42:48 +020028#include <haproxy/tcp_rules.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020029#include <haproxy/tools.h>
Willy Tarreauc2f7c582020-06-02 18:15:32 +020030#include <haproxy/ticks.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020031#include <haproxy/time.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020032#include <haproxy/trace.h>
Willy Tarreau39713102016-11-25 15:49:32 +010033
Willy Tarreau39713102016-11-25 15:49:32 +010034
Christopher Fauleteea8fc72019-11-05 16:18:10 +010035#define TRACE_SOURCE &trace_strm
36
Willy Tarreau39713102016-11-25 15:49:32 +010037/* List head of all known action keywords for "tcp-request connection" */
38struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
39struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
40struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
41struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
42
43/*
44 * Register keywords.
45 */
46void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
47{
48 LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
49}
50
51void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
52{
53 LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
54}
55
56void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
57{
58 LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
59}
60
61void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
62{
63 LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
64}
65
66/*
67 * Return the struct tcp_req_action_kw associated to a keyword.
68 */
69static struct action_kw *tcp_req_conn_action(const char *kw)
70{
71 return action_lookup(&tcp_req_conn_keywords, kw);
72}
73
74static struct action_kw *tcp_req_sess_action(const char *kw)
75{
76 return action_lookup(&tcp_req_sess_keywords, kw);
77}
78
79static struct action_kw *tcp_req_cont_action(const char *kw)
80{
81 return action_lookup(&tcp_req_cont_keywords, kw);
82}
83
84static struct action_kw *tcp_res_cont_action(const char *kw)
85{
86 return action_lookup(&tcp_res_cont_keywords, kw);
87}
88
89/* This function performs the TCP request analysis on the current request. It
90 * returns 1 if the processing can continue on next analysers, or zero if it
91 * needs more data, encounters an error, or wants to immediately abort the
92 * request. It relies on buffers flags, and updates s->req->analysers. The
93 * function may be called for frontend rules and backend rules. It only relies
94 * on the backend pointer so this works for both cases.
95 */
96int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
97{
98 struct session *sess = s->sess;
99 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100100 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100101 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100102
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100103 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100104
105 /* We don't know whether we have enough data, so must proceed
106 * this way :
107 * - iterate through all rules in their declaration order
108 * - if one rule returns MISS, it means the inspect delay is
109 * not over yet, then return immediately, otherwise consider
110 * it as a non-match.
111 * - if one rule returns OK, then return OK
112 * - if one rule returns KO, then return KO
113 */
114
Willy Tarreau23752332018-06-15 14:54:53 +0200115 if ((req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
Willy Tarreau39713102016-11-25 15:49:32 +0100116 !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
117 partial = SMP_OPT_FINAL;
118 else
119 partial = 0;
120
121 /* If "the current_rule_list" match the executed rule list, we are in
122 * resume condition. If a resume is needed it is always in the action
123 * and never in the ACL or converters. In this case, we initialise the
124 * current rule, and go to the action execution point.
125 */
126 if (s->current_rule) {
127 rule = s->current_rule;
128 s->current_rule = NULL;
129 if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
130 goto resume_execution;
131 }
132 s->current_rule_list = &s->be->tcp_req.inspect_rules;
133
134 list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
135 enum acl_test_res ret = ACL_TEST_PASS;
136
137 if (rule->cond) {
138 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
139 if (ret == ACL_TEST_MISS)
140 goto missing_data;
141
142 ret = acl_pass(ret);
143 if (rule->cond->pol == ACL_COND_UNLESS)
144 ret = !ret;
145 }
146
147 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100148 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100149resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100150
151 /* Always call the action function if defined */
152 if (rule->action_ptr) {
153 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100154 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100155
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100156 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100157 case ACT_RET_CONT:
158 break;
159 case ACT_RET_STOP:
160 case ACT_RET_DONE:
161 goto end;
162 case ACT_RET_YIELD:
163 s->current_rule = rule;
164 goto missing_data;
165 case ACT_RET_DENY:
166 goto deny;
167 case ACT_RET_ABRT:
168 goto abort;
169 case ACT_RET_ERR:
170 goto internal;
171 case ACT_RET_INV:
172 goto invalid;
173 }
174 continue; /* eval the next rule */
175 }
176
177 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100178 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100179 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100180 }
181 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100182 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100183 }
Willy Tarreau39713102016-11-25 15:49:32 +0100184 }
185 }
186
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100187 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100188 /* if we get there, it means we have no rule which matches, or
189 * we have an explicit accept, so we apply the default accept.
190 */
191 req->analysers &= ~an_bit;
192 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100193 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100194 return 1;
195
196 missing_data:
197 channel_dont_connect(req);
198 /* just set the request timeout once at the beginning of the request */
199 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
200 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100201 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100202 return 0;
203
Christopher Faulet282992e2019-12-16 12:34:31 +0100204 deny:
205 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
206 if (sess->listener && sess->listener->counters)
207 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
208 goto reject;
209
210 internal:
211 _HA_ATOMIC_ADD(&sess->fe->fe_counters.internal_errors, 1);
212 if (sess->listener && sess->listener->counters)
213 _HA_ATOMIC_ADD(&sess->listener->counters->internal_errors, 1);
214 if (!(s->flags & SF_ERR_MASK))
215 s->flags |= SF_ERR_INTERNAL;
216 goto reject;
217
218 invalid:
219 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
220 if (sess->listener && sess->listener->counters)
221 _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1);
222
223 reject:
224 si_must_kill_conn(chn_prod(req));
225 channel_abort(req);
226 channel_abort(&s->res);
227
228 abort:
229 req->analysers &= AN_REQ_FLT_END;
230
231 if (!(s->flags & SF_ERR_MASK))
232 s->flags |= SF_ERR_PRXCOND;
233 if (!(s->flags & SF_FINST_MASK))
234 s->flags |= SF_FINST_R;
235 DBG_TRACE_DEVEL("leaving on error|deny|abort", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
236 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100237}
238
239/* This function performs the TCP response analysis on the current response. It
240 * returns 1 if the processing can continue on next analysers, or zero if it
241 * needs more data, encounters an error, or wants to immediately abort the
242 * response. It relies on buffers flags, and updates s->rep->analysers. The
243 * function may be called for backend rules.
244 */
245int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
246{
247 struct session *sess = s->sess;
248 struct act_rule *rule;
249 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100250 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100251
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100252 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100253
254 /* We don't know whether we have enough data, so must proceed
255 * this way :
256 * - iterate through all rules in their declaration order
257 * - if one rule returns MISS, it means the inspect delay is
258 * not over yet, then return immediately, otherwise consider
259 * it as a non-match.
260 * - if one rule returns OK, then return OK
261 * - if one rule returns KO, then return KO
262 */
263
264 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
265 partial = SMP_OPT_FINAL;
266 else
267 partial = 0;
268
269 /* If "the current_rule_list" match the executed rule list, we are in
270 * resume condition. If a resume is needed it is always in the action
271 * and never in the ACL or converters. In this case, we initialise the
272 * current rule, and go to the action execution point.
273 */
274 if (s->current_rule) {
275 rule = s->current_rule;
276 s->current_rule = NULL;
277 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
278 goto resume_execution;
279 }
280 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
281
282 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
283 enum acl_test_res ret = ACL_TEST_PASS;
284
285 if (rule->cond) {
286 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
287 if (ret == ACL_TEST_MISS) {
288 /* just set the analyser timeout once at the beginning of the response */
289 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
290 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100291 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100292 return 0;
293 }
294
295 ret = acl_pass(ret);
296 if (rule->cond->pol == ACL_COND_UNLESS)
297 ret = !ret;
298 }
299
300 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100301 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100302resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100303 /* Always call the action function if defined */
304 if (rule->action_ptr) {
305 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100306 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100307
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100308 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100309 case ACT_RET_CONT:
310 break;
311 case ACT_RET_STOP:
312 case ACT_RET_DONE:
313 goto end;
314 case ACT_RET_YIELD:
315 s->current_rule = rule;
316 goto missing_data;
317 case ACT_RET_DENY:
318 goto deny;
319 case ACT_RET_ABRT:
320 goto abort;
321 case ACT_RET_ERR:
322 goto internal;
323 case ACT_RET_INV:
324 goto invalid;
325 }
326 continue; /* eval the next rule */
327 }
328
329 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100330 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100331 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100332 }
333 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100334 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100335 }
336 else if (rule->action == ACT_TCP_CLOSE) {
337 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100338 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100339 si_shutr(chn_prod(rep));
340 si_shutw(chn_prod(rep));
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100341 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100342 }
343 }
344 }
345
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100346 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100347 /* if we get there, it means we have no rule which matches, or
348 * we have an explicit accept, so we apply the default accept.
349 */
350 rep->analysers &= ~an_bit;
351 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100352 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100353 return 1;
Christopher Faulet282992e2019-12-16 12:34:31 +0100354
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100355 missing_data:
356 channel_dont_close(rep);
357 s->current_rule = rule;
358 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
359 return 0;
360
Christopher Faulet282992e2019-12-16 12:34:31 +0100361 deny:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100362 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100363 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100364 if (s->sess->listener->counters)
365 _HA_ATOMIC_ADD(&s->sess->listener->counters->denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100366 if (objt_server(s->target))
367 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
368 goto reject;
369
370 internal:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100371 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100372 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100373 if (s->sess->listener->counters)
374 _HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100375 if (objt_server(s->target))
376 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
377 if (!(s->flags & SF_ERR_MASK))
378 s->flags |= SF_ERR_INTERNAL;
379 goto reject;
380
381 invalid:
382 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
383 if (objt_server(s->target))
384 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
385
386 reject:
387 si_must_kill_conn(chn_prod(rep));
388 channel_abort(rep);
389 channel_abort(&s->req);
390
391 abort:
392 rep->analysers &= AN_REQ_FLT_END;
393
394 if (!(s->flags & SF_ERR_MASK))
395 s->flags |= SF_ERR_PRXCOND;
396 if (!(s->flags & SF_FINST_MASK))
397 s->flags |= SF_FINST_D;
398 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
399 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100400}
401
402
403/* This function performs the TCP layer4 analysis on the current request. It
404 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
405 * matches or if no more rule matches. It can only use rules which don't need
406 * any data. This only works on connection-based client-facing stream interfaces.
407 */
408int tcp_exec_l4_rules(struct session *sess)
409{
410 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100411 struct connection *conn = objt_conn(sess->origin);
412 int result = 1;
413 enum acl_test_res ret;
414
415 if (!conn)
416 return result;
417
418 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
419 ret = ACL_TEST_PASS;
420
421 if (rule->cond) {
422 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
423 ret = acl_pass(ret);
424 if (rule->cond->pol == ACL_COND_UNLESS)
425 ret = !ret;
426 }
427
428 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100429 /* Always call the action function if defined */
430 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100431 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100432 case ACT_RET_YIELD:
433 /* yield is not allowed at this point. If this return code is
434 * used it is a bug, so I prefer to abort the process.
435 */
436 send_log(sess->fe, LOG_WARNING,
437 "Internal error: yield not allowed with tcp-request connection actions.");
438 /* fall through */
439 case ACT_RET_STOP:
440 case ACT_RET_DONE:
441 goto end;
442 case ACT_RET_CONT:
443 break;
444 case ACT_RET_DENY:
445 case ACT_RET_ABRT:
446 case ACT_RET_ERR:
447 case ACT_RET_INV:
448 result = 0;
449 goto end;
450 }
451 continue; /* eval the next rule */
452 }
453
454 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100455 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100456 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100457 }
458 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100459 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100460 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100461 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100462
463 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100464 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100465 }
Willy Tarreau39713102016-11-25 15:49:32 +0100466 else if (rule->action == ACT_TCP_EXPECT_PX) {
Willy Tarreau4450b582020-01-23 15:23:13 +0100467 if (!(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200468 if (xprt_add_hs(conn) < 0) {
469 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100470 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200471 }
472 }
Willy Tarreau39713102016-11-25 15:49:32 +0100473 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100474 }
475 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Willy Tarreau4450b582020-01-23 15:23:13 +0100476 if (!(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200477 if (xprt_add_hs(conn) < 0) {
478 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100479 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200480 }
481 }
Willy Tarreau39713102016-11-25 15:49:32 +0100482 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100483 }
Willy Tarreau39713102016-11-25 15:49:32 +0100484 }
485 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100486 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100487 return result;
488}
489
490/* This function performs the TCP layer5 analysis on the current request. It
491 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
492 * matches or if no more rule matches. It can only use rules which don't need
493 * any data. This only works on session-based client-facing stream interfaces.
494 * An example of valid use case is to track a stick-counter on the source
495 * address extracted from the proxy protocol.
496 */
497int tcp_exec_l5_rules(struct session *sess)
498{
499 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100500 int result = 1;
501 enum acl_test_res ret;
502
503 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
504 ret = ACL_TEST_PASS;
505
506 if (rule->cond) {
507 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
508 ret = acl_pass(ret);
509 if (rule->cond->pol == ACL_COND_UNLESS)
510 ret = !ret;
511 }
512
513 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100514 /* Always call the action function if defined */
515 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100516 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100517 case ACT_RET_YIELD:
518 /* yield is not allowed at this point. If this return code is
519 * used it is a bug, so I prefer to abort the process.
520 */
521 send_log(sess->fe, LOG_WARNING,
522 "Internal error: yield not allowed with tcp-request session actions.");
523 /* fall through */
524 case ACT_RET_STOP:
525 case ACT_RET_DONE:
526 goto end;
527 case ACT_RET_CONT:
528 break;
529 case ACT_RET_DENY:
530 case ACT_RET_ABRT:
531 case ACT_RET_ERR:
532 case ACT_RET_INV:
533 result = 0;
534 goto end;
535 }
536 continue; /* eval the next rule */
537 }
538
539 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100540 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100541 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100542 }
543 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100544 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100545 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100546 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100547
548 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100549 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100550 }
Willy Tarreau39713102016-11-25 15:49:32 +0100551 }
552 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100553 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100554 return result;
555}
556
557/* Parse a tcp-response rule. Return a negative value in case of failure */
558static int tcp_parse_response_rule(char **args, int arg, int section_type,
559 struct proxy *curpx, struct proxy *defpx,
560 struct act_rule *rule, char **err,
561 unsigned int where,
562 const char *file, int line)
563{
564 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
565 memprintf(err, "%s %s is only allowed in 'backend' sections",
566 args[0], args[1]);
567 return -1;
568 }
569
570 if (strcmp(args[arg], "accept") == 0) {
571 arg++;
572 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100573 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100574 }
575 else if (strcmp(args[arg], "reject") == 0) {
576 arg++;
577 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100578 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100579 }
580 else if (strcmp(args[arg], "close") == 0) {
581 arg++;
582 rule->action = ACT_TCP_CLOSE;
Christopher Faulet245cf792019-12-18 14:58:12 +0100583 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100584 }
585 else {
586 struct action_kw *kw;
587 kw = tcp_res_cont_action(args[arg]);
588 if (kw) {
589 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100590 rule->kw = kw;
591 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
592 return -1;
593 } else {
594 action_build_list(&tcp_res_cont_keywords, &trash);
595 memprintf(err,
596 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200597 args[0], args[1], trash.area,
598 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100599 return -1;
600 }
601 }
602
603 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200604 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100605 memprintf(err,
606 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
607 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
608 return -1;
609 }
610 }
611 else if (*args[arg]) {
612 memprintf(err,
613 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
614 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
615 return -1;
616 }
617 return 0;
618}
619
Christopher Fauletac98d812019-12-18 09:20:16 +0100620
621/* This function executes a track-sc* actions. On success, it returns
622 * ACT_RET_CONT. If it must yield, it return ACT_RET_YIELD. Otherwsize
623 * ACT_RET_ERR is returned.
624 */
625static enum act_return tcp_action_track_sc(struct act_rule *rule, struct proxy *px,
626 struct session *sess, struct stream *s, int flags)
627{
628 struct stksess *ts;
629 struct stktable *t;
630 struct stktable_key *key;
631 struct sample smp;
632 int opt;
633
Christopher Faulet67307792020-02-10 09:54:49 +0100634 opt = SMP_OPT_DIR_REQ;
Christopher Fauletac98d812019-12-18 09:20:16 +0100635 if (flags & ACT_FLAG_FINAL)
636 opt |= SMP_OPT_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100637
Christopher Fauletac98d812019-12-18 09:20:16 +0100638 t = rule->arg.trk_ctr.table.t;
Christopher Faulet67307792020-02-10 09:54:49 +0100639 if (rule->from == ACT_F_TCP_REQ_CNT) { /* L7 rules: use the stream */
640 if (stkctr_entry(&s->stkctr[rule->action]))
641 goto end;
Christopher Fauletac98d812019-12-18 09:20:16 +0100642
Christopher Faulet67307792020-02-10 09:54:49 +0100643 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, &smp);
Christopher Fauletac98d812019-12-18 09:20:16 +0100644
Christopher Faulet67307792020-02-10 09:54:49 +0100645 if ((smp.flags & SMP_F_MAY_CHANGE) && !(flags & ACT_FLAG_FINAL))
646 return ACT_RET_YIELD; /* key might appear later */
647
648 if (key && (ts = stktable_get_entry(t, key))) {
649 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
Christopher Fauletac98d812019-12-18 09:20:16 +0100650 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
651 if (sess->fe != s->be)
652 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
653 }
654 }
Christopher Faulet67307792020-02-10 09:54:49 +0100655 else { /* L4/L5 rules: use the session */
656 if (stkctr_entry(&sess->stkctr[rule->action]))
657 goto end;
658
659 key = stktable_fetch_key(t, sess->fe, sess, NULL, opt, rule->arg.trk_ctr.expr, NULL);
660 if (key && (ts = stktable_get_entry(t, key)))
661 stream_track_stkctr(&sess->stkctr[rule->action], t, ts);
662 }
Christopher Fauletac98d812019-12-18 09:20:16 +0100663
664 end:
665 return ACT_RET_CONT;
666}
Willy Tarreau39713102016-11-25 15:49:32 +0100667
Christopher Fauletd73b96d2019-12-19 17:27:03 +0100668/* This function executes a capture actions. It executes a fetch expression,
669 * turns the result into a string and puts it in a capture slot. On success, it
670 * returns ACT_RET_CONT. If it must yield, it return ACT_RET_YIELD. Otherwsize
671 * ACT_RET_ERR is returned.
672 */
673static enum act_return tcp_action_capture(struct act_rule *rule, struct proxy *px,
674 struct session *sess, struct stream *s, int flags)
675{
676 struct sample *key;
677 struct cap_hdr *h = rule->arg.cap.hdr;
678 char **cap = s->req_cap;
679 int len, opt;
680
681 opt = ((rule->from == ACT_F_TCP_REQ_CNT) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
682 if (flags & ACT_FLAG_FINAL)
683 opt |= SMP_OPT_FINAL;
684
685 key = sample_fetch_as_type(s->be, sess, s, opt, rule->arg.cap.expr, SMP_T_STR);
686 if (!key)
687 goto end;
688
689 if ((key->flags & SMP_F_MAY_CHANGE) && !(flags & ACT_FLAG_FINAL))
690 return ACT_RET_YIELD; /* key might appear later */
691
692 if (cap[h->index] == NULL) {
693 cap[h->index] = pool_alloc(h->pool);
694 if (cap[h->index] == NULL) /* no more capture memory, ignore error */
695 goto end;
696 }
697
698 len = key->data.u.str.data;
699 if (len > h->len)
700 len = h->len;
701
702 memcpy(cap[h->index], key->data.u.str.area, len);
703 cap[h->index][len] = 0;
704
705 end:
706 return ACT_RET_CONT;
707}
708
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100709static void release_tcp_capture(struct act_rule * rule)
710{
711 release_sample_expr(rule->arg.cap.expr);
712}
713
714
715static void release_tcp_track_sc(struct act_rule * rule)
716{
717 release_sample_expr(rule->arg.trk_ctr.expr);
718}
719
Willy Tarreau39713102016-11-25 15:49:32 +0100720/* Parse a tcp-request rule. Return a negative value in case of failure */
721static int tcp_parse_request_rule(char **args, int arg, int section_type,
722 struct proxy *curpx, struct proxy *defpx,
723 struct act_rule *rule, char **err,
724 unsigned int where, const char *file, int line)
725{
726 if (curpx == defpx) {
727 memprintf(err, "%s %s is not allowed in 'defaults' sections",
728 args[0], args[1]);
729 return -1;
730 }
731
732 if (!strcmp(args[arg], "accept")) {
733 arg++;
734 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100735 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100736 }
737 else if (!strcmp(args[arg], "reject")) {
738 arg++;
739 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100740 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100741 }
742 else if (strcmp(args[arg], "capture") == 0) {
743 struct sample_expr *expr;
744 struct cap_hdr *hdr;
745 int kw = arg;
746 int len = 0;
747
748 if (!(curpx->cap & PR_CAP_FE)) {
749 memprintf(err,
750 "'%s %s %s' : proxy '%s' has no frontend capability",
751 args[0], args[1], args[kw], curpx->id);
752 return -1;
753 }
754
755 if (!(where & SMP_VAL_FE_REQ_CNT)) {
756 memprintf(err,
757 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
758 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
759 return -1;
760 }
761
762 arg++;
763
764 curpx->conf.args.ctx = ARGC_CAP;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100765 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
Willy Tarreau39713102016-11-25 15:49:32 +0100766 if (!expr) {
767 memprintf(err,
768 "'%s %s %s' : %s",
769 args[0], args[1], args[kw], *err);
770 return -1;
771 }
772
773 if (!(expr->fetch->val & where)) {
774 memprintf(err,
775 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
776 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100777 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100778 return -1;
779 }
780
781 if (strcmp(args[arg], "len") == 0) {
782 arg++;
783 if (!args[arg]) {
784 memprintf(err,
785 "'%s %s %s' : missing length value",
786 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100787 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100788 return -1;
789 }
790 /* we copy the table name for now, it will be resolved later */
791 len = atoi(args[arg]);
792 if (len <= 0) {
793 memprintf(err,
794 "'%s %s %s' : length must be > 0",
795 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100796 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100797 return -1;
798 }
799 arg++;
800 }
801
802 if (!len) {
803 memprintf(err,
804 "'%s %s %s' : a positive 'len' argument is mandatory",
805 args[0], args[1], args[kw]);
806 free(expr);
807 return -1;
808 }
809
810 hdr = calloc(1, sizeof(*hdr));
811 hdr->next = curpx->req_cap;
812 hdr->name = NULL; /* not a header capture */
813 hdr->namelen = 0;
814 hdr->len = len;
815 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
816 hdr->index = curpx->nb_req_cap++;
817
818 curpx->req_cap = hdr;
819 curpx->to_log |= LW_REQHDR;
820
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200821 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100822 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
823
824 rule->arg.cap.expr = expr;
825 rule->arg.cap.hdr = hdr;
Christopher Fauletd73b96d2019-12-19 17:27:03 +0100826 rule->action = ACT_CUSTOM;
827 rule->action_ptr = tcp_action_capture;
828 rule->check_ptr = check_capture;
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100829 rule->release_ptr = release_tcp_capture;
Willy Tarreau39713102016-11-25 15:49:32 +0100830 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100831 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100832 struct sample_expr *expr;
833 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100834 unsigned int tsc_num;
835 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100836
837 arg++;
838
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100839 tsc_num_str = &args[kw][8];
840 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
841 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
842 return -1;
843 }
844
Willy Tarreau39713102016-11-25 15:49:32 +0100845 curpx->conf.args.ctx = ARGC_TRK;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100846 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
Willy Tarreau39713102016-11-25 15:49:32 +0100847 if (!expr) {
848 memprintf(err,
849 "'%s %s %s' : %s",
850 args[0], args[1], args[kw], *err);
851 return -1;
852 }
853
854 if (!(expr->fetch->val & where)) {
855 memprintf(err,
856 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
857 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100858 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100859 return -1;
860 }
861
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200862 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100863 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
864
865 if (strcmp(args[arg], "table") == 0) {
866 arg++;
867 if (!args[arg]) {
868 memprintf(err,
869 "'%s %s %s' : missing table name",
870 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100871 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100872 return -1;
873 }
874 /* we copy the table name for now, it will be resolved later */
875 rule->arg.trk_ctr.table.n = strdup(args[arg]);
876 arg++;
877 }
Christopher Fauletac98d812019-12-18 09:20:16 +0100878 rule->action = tsc_num;
Willy Tarreau39713102016-11-25 15:49:32 +0100879 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +0100880 rule->action_ptr = tcp_action_track_sc;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200881 rule->check_ptr = check_trk_action;
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100882 rule->release_ptr = release_tcp_track_sc;
Willy Tarreau39713102016-11-25 15:49:32 +0100883 }
884 else if (strcmp(args[arg], "expect-proxy") == 0) {
885 if (strcmp(args[arg+1], "layer4") != 0) {
886 memprintf(err,
887 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
888 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
889 return -1;
890 }
891
892 if (!(where & SMP_VAL_FE_CON_ACC)) {
893 memprintf(err,
894 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
895 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
896 return -1;
897 }
898
899 arg += 2;
900 rule->action = ACT_TCP_EXPECT_PX;
901 }
902 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
903 if (strcmp(args[arg+1], "layer4") != 0) {
904 memprintf(err,
905 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
906 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
907 return -1;
908 }
909
910 if (!(where & SMP_VAL_FE_CON_ACC)) {
911 memprintf(err,
912 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
913 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
914 return -1;
915 }
916
917 arg += 2;
918 rule->action = ACT_TCP_EXPECT_CIP;
919 }
920 else {
921 struct action_kw *kw;
922 if (where & SMP_VAL_FE_CON_ACC) {
923 /* L4 */
924 kw = tcp_req_conn_action(args[arg]);
925 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100926 } else if (where & SMP_VAL_FE_SES_ACC) {
927 /* L5 */
928 kw = tcp_req_sess_action(args[arg]);
929 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100930 } else {
931 /* L6 */
932 kw = tcp_req_cont_action(args[arg]);
933 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100934 }
935 if (kw) {
936 arg++;
937 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
938 return -1;
939 } else {
940 if (where & SMP_VAL_FE_CON_ACC)
941 action_build_list(&tcp_req_conn_keywords, &trash);
942 else if (where & SMP_VAL_FE_SES_ACC)
943 action_build_list(&tcp_req_sess_keywords, &trash);
944 else
945 action_build_list(&tcp_req_cont_keywords, &trash);
946 memprintf(err,
947 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
948 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200949 args[0], args[1], MAX_SESS_STKCTR-1,
950 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100951 curpx->id, args[arg]);
952 return -1;
953 }
954 }
955
956 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200957 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100958 memprintf(err,
959 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
960 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
961 return -1;
962 }
963 }
964 else if (*args[arg]) {
965 memprintf(err,
966 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
967 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
968 return -1;
969 }
970 return 0;
971}
972
973/* This function should be called to parse a line starting with the "tcp-response"
974 * keyword.
975 */
976static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
977 struct proxy *defpx, const char *file, int line,
978 char **err)
979{
980 const char *ptr = NULL;
981 unsigned int val;
982 int warn = 0;
983 int arg;
984 struct act_rule *rule;
985 unsigned int where;
986 const struct acl *acl;
987 const char *kw;
988
989 if (!*args[1]) {
990 memprintf(err, "missing argument for '%s' in %s '%s'",
991 args[0], proxy_type_str(curpx), curpx->id);
992 return -1;
993 }
994
995 if (strcmp(args[1], "inspect-delay") == 0) {
996 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
997 memprintf(err, "%s %s is only allowed in 'backend' sections",
998 args[0], args[1]);
999 return -1;
1000 }
1001
1002 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1003 memprintf(err,
1004 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1005 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001006
1007 if (ptr == PARSE_TIME_OVER)
1008 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1009 else if (ptr == PARSE_TIME_UNDER)
1010 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1011 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001012 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1013 return -1;
1014 }
1015
1016 if (curpx->tcp_rep.inspect_delay) {
1017 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1018 args[0], args[1], proxy_type_str(curpx), curpx->id);
1019 return 1;
1020 }
1021 curpx->tcp_rep.inspect_delay = val;
1022 return 0;
1023 }
1024
1025 rule = calloc(1, sizeof(*rule));
1026 LIST_INIT(&rule->list);
1027 arg = 1;
1028 where = 0;
1029
1030 if (strcmp(args[1], "content") == 0) {
1031 arg++;
1032
1033 if (curpx->cap & PR_CAP_FE)
1034 where |= SMP_VAL_FE_RES_CNT;
1035 if (curpx->cap & PR_CAP_BE)
1036 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001037 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001038 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1039 goto error;
1040
1041 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1042 if (acl) {
1043 if (acl->name && *acl->name)
1044 memprintf(err,
1045 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1046 acl->name, args[0], args[1], sample_ckp_names(where));
1047 else
1048 memprintf(err,
1049 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1050 args[0], args[1],
1051 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1052 sample_ckp_names(where));
1053
1054 warn++;
1055 }
1056 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1057 if (acl->name && *acl->name)
1058 memprintf(err,
1059 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1060 acl->name, kw, sample_ckp_names(where));
1061 else
1062 memprintf(err,
1063 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1064 kw, sample_ckp_names(where));
1065 warn++;
1066 }
1067
1068 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1069 }
1070 else {
1071 memprintf(err,
1072 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1073 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1074 goto error;
1075 }
1076
1077 return warn;
1078 error:
1079 free(rule);
1080 return -1;
1081}
1082
1083
1084/* This function should be called to parse a line starting with the "tcp-request"
1085 * keyword.
1086 */
1087static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1088 struct proxy *defpx, const char *file, int line,
1089 char **err)
1090{
1091 const char *ptr = NULL;
1092 unsigned int val;
1093 int warn = 0;
1094 int arg;
1095 struct act_rule *rule;
1096 unsigned int where;
1097 const struct acl *acl;
1098 const char *kw;
1099
1100 if (!*args[1]) {
1101 if (curpx == defpx)
1102 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1103 else
1104 memprintf(err, "missing argument for '%s' in %s '%s'",
1105 args[0], proxy_type_str(curpx), curpx->id);
1106 return -1;
1107 }
1108
1109 if (!strcmp(args[1], "inspect-delay")) {
1110 if (curpx == defpx) {
1111 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1112 args[0], args[1]);
1113 return -1;
1114 }
1115
1116 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1117 memprintf(err,
1118 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1119 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001120
1121 if (ptr == PARSE_TIME_OVER)
1122 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1123 else if (ptr == PARSE_TIME_UNDER)
1124 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1125 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001126 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1127 return -1;
1128 }
1129
1130 if (curpx->tcp_req.inspect_delay) {
1131 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1132 args[0], args[1], proxy_type_str(curpx), curpx->id);
1133 return 1;
1134 }
1135 curpx->tcp_req.inspect_delay = val;
1136 return 0;
1137 }
1138
1139 rule = calloc(1, sizeof(*rule));
1140 LIST_INIT(&rule->list);
1141 arg = 1;
1142 where = 0;
1143
1144 if (strcmp(args[1], "content") == 0) {
1145 arg++;
1146
1147 if (curpx->cap & PR_CAP_FE)
1148 where |= SMP_VAL_FE_REQ_CNT;
1149 if (curpx->cap & PR_CAP_BE)
1150 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001151 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001152 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1153 goto error;
1154
1155 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1156 if (acl) {
1157 if (acl->name && *acl->name)
1158 memprintf(err,
1159 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1160 acl->name, args[0], args[1], sample_ckp_names(where));
1161 else
1162 memprintf(err,
1163 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1164 args[0], args[1],
1165 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1166 sample_ckp_names(where));
1167
1168 warn++;
1169 }
1170 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1171 if (acl->name && *acl->name)
1172 memprintf(err,
1173 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1174 acl->name, kw, sample_ckp_names(where));
1175 else
1176 memprintf(err,
1177 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1178 kw, sample_ckp_names(where));
1179 warn++;
1180 }
1181
1182 /* the following function directly emits the warning */
1183 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1184 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1185 }
1186 else if (strcmp(args[1], "connection") == 0) {
1187 arg++;
1188
1189 if (!(curpx->cap & PR_CAP_FE)) {
1190 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1191 args[0], args[1], proxy_type_str(curpx), curpx->id);
1192 goto error;
1193 }
1194
1195 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001196 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001197 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1198 goto error;
1199
1200 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1201 if (acl) {
1202 if (acl->name && *acl->name)
1203 memprintf(err,
1204 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1205 acl->name, args[0], args[1], sample_ckp_names(where));
1206 else
1207 memprintf(err,
1208 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1209 args[0], args[1],
1210 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1211 sample_ckp_names(where));
1212
1213 warn++;
1214 }
1215 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1216 if (acl->name && *acl->name)
1217 memprintf(err,
1218 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1219 acl->name, kw, sample_ckp_names(where));
1220 else
1221 memprintf(err,
1222 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1223 kw, sample_ckp_names(where));
1224 warn++;
1225 }
1226
1227 /* the following function directly emits the warning */
1228 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1229 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1230 }
1231 else if (strcmp(args[1], "session") == 0) {
1232 arg++;
1233
1234 if (!(curpx->cap & PR_CAP_FE)) {
1235 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1236 args[0], args[1], proxy_type_str(curpx), curpx->id);
1237 goto error;
1238 }
1239
1240 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001241 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001242 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1243 goto error;
1244
1245 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1246 if (acl) {
1247 if (acl->name && *acl->name)
1248 memprintf(err,
1249 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1250 acl->name, args[0], args[1], sample_ckp_names(where));
1251 else
1252 memprintf(err,
1253 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1254 args[0], args[1],
1255 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1256 sample_ckp_names(where));
1257 warn++;
1258 }
1259 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1260 if (acl->name && *acl->name)
1261 memprintf(err,
1262 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1263 acl->name, kw, sample_ckp_names(where));
1264 else
1265 memprintf(err,
1266 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1267 kw, sample_ckp_names(where));
1268 warn++;
1269 }
1270
1271 /* the following function directly emits the warning */
1272 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1273 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1274 }
1275 else {
1276 if (curpx == defpx)
1277 memprintf(err,
1278 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1279 args[0], args[1]);
1280 else
1281 memprintf(err,
1282 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1283 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1284 goto error;
1285 }
1286
1287 return warn;
1288 error:
1289 free(rule);
1290 return -1;
1291}
1292
1293static struct cfg_kw_list cfg_kws = {ILH, {
1294 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1295 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1296 { 0, NULL, NULL },
1297}};
1298
Willy Tarreau0108d902018-11-25 19:14:37 +01001299INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001300
1301/*
1302 * Local variables:
1303 * c-indent-level: 8
1304 * c-basic-offset: 8
1305 * End:
1306 */