blob: fbd876857309c06713a70cf2b26ce1c6c0e0b503 [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 */
12#include <common/cfgparse.h>
13#include <common/compat.h>
14#include <common/config.h>
15#include <common/debug.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010016#include <common/initcall.h>
Willy Tarreau39713102016-11-25 15:49:32 +010017#include <common/mini-clist.h>
18#include <common/standard.h>
19#include <common/ticks.h>
20#include <common/time.h>
21
22#include <types/arg.h>
23#include <types/capture.h>
24#include <types/connection.h>
25#include <types/global.h>
26
27#include <proto/acl.h>
28#include <proto/action.h>
29#include <proto/channel.h>
30#include <proto/connection.h>
31#include <proto/log.h>
32#include <proto/proxy.h>
33#include <proto/sample.h>
34#include <proto/stick_table.h>
35#include <proto/stream.h>
36#include <proto/stream_interface.h>
37#include <proto/tcp_rules.h>
38
Christopher Fauleteea8fc72019-11-05 16:18:10 +010039#define TRACE_SOURCE &trace_strm
40
Willy Tarreau39713102016-11-25 15:49:32 +010041/* List head of all known action keywords for "tcp-request connection" */
42struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
43struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
44struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
45struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
46
47/*
48 * Register keywords.
49 */
50void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
51{
52 LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
53}
54
55void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
56{
57 LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
58}
59
60void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
61{
62 LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
63}
64
65void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
66{
67 LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
68}
69
70/*
71 * Return the struct tcp_req_action_kw associated to a keyword.
72 */
73static struct action_kw *tcp_req_conn_action(const char *kw)
74{
75 return action_lookup(&tcp_req_conn_keywords, kw);
76}
77
78static struct action_kw *tcp_req_sess_action(const char *kw)
79{
80 return action_lookup(&tcp_req_sess_keywords, kw);
81}
82
83static struct action_kw *tcp_req_cont_action(const char *kw)
84{
85 return action_lookup(&tcp_req_cont_keywords, kw);
86}
87
88static struct action_kw *tcp_res_cont_action(const char *kw)
89{
90 return action_lookup(&tcp_res_cont_keywords, kw);
91}
92
93/* This function performs the TCP request analysis on the current request. It
94 * returns 1 if the processing can continue on next analysers, or zero if it
95 * needs more data, encounters an error, or wants to immediately abort the
96 * request. It relies on buffers flags, and updates s->req->analysers. The
97 * function may be called for frontend rules and backend rules. It only relies
98 * on the backend pointer so this works for both cases.
99 */
100int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
101{
102 struct session *sess = s->sess;
103 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100104 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100105 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100106
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100107 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100108
109 /* We don't know whether we have enough data, so must proceed
110 * this way :
111 * - iterate through all rules in their declaration order
112 * - if one rule returns MISS, it means the inspect delay is
113 * not over yet, then return immediately, otherwise consider
114 * it as a non-match.
115 * - if one rule returns OK, then return OK
116 * - if one rule returns KO, then return KO
117 */
118
Willy Tarreau23752332018-06-15 14:54:53 +0200119 if ((req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
Willy Tarreau39713102016-11-25 15:49:32 +0100120 !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
121 partial = SMP_OPT_FINAL;
122 else
123 partial = 0;
124
125 /* If "the current_rule_list" match the executed rule list, we are in
126 * resume condition. If a resume is needed it is always in the action
127 * and never in the ACL or converters. In this case, we initialise the
128 * current rule, and go to the action execution point.
129 */
130 if (s->current_rule) {
131 rule = s->current_rule;
132 s->current_rule = NULL;
133 if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
134 goto resume_execution;
135 }
136 s->current_rule_list = &s->be->tcp_req.inspect_rules;
137
138 list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
139 enum acl_test_res ret = ACL_TEST_PASS;
140
141 if (rule->cond) {
142 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
143 if (ret == ACL_TEST_MISS)
144 goto missing_data;
145
146 ret = acl_pass(ret);
147 if (rule->cond->pol == ACL_COND_UNLESS)
148 ret = !ret;
149 }
150
151 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100152 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100153resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100154
155 /* Always call the action function if defined */
156 if (rule->action_ptr) {
157 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100158 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100159
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100160 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100161 case ACT_RET_CONT:
162 break;
163 case ACT_RET_STOP:
164 case ACT_RET_DONE:
165 goto end;
166 case ACT_RET_YIELD:
167 s->current_rule = rule;
168 goto missing_data;
169 case ACT_RET_DENY:
170 goto deny;
171 case ACT_RET_ABRT:
172 goto abort;
173 case ACT_RET_ERR:
174 goto internal;
175 case ACT_RET_INV:
176 goto invalid;
177 }
178 continue; /* eval the next rule */
179 }
180
181 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100182 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100183 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100184 }
185 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100186 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100187 }
Willy Tarreau39713102016-11-25 15:49:32 +0100188 else if (rule->action == ACT_TCP_CAPTURE) {
189 struct sample *key;
190 struct cap_hdr *h = rule->arg.cap.hdr;
191 char **cap = s->req_cap;
192 int len;
193
194 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
195 if (!key)
196 continue;
197
198 if (key->flags & SMP_F_MAY_CHANGE)
199 goto missing_data;
200
201 if (cap[h->index] == NULL)
Willy Tarreaubafbe012017-11-24 17:34:44 +0100202 cap[h->index] = pool_alloc(h->pool);
Willy Tarreau39713102016-11-25 15:49:32 +0100203
204 if (cap[h->index] == NULL) /* no more capture memory */
205 continue;
206
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200207 len = key->data.u.str.data;
Willy Tarreau39713102016-11-25 15:49:32 +0100208 if (len > h->len)
209 len = h->len;
210
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200211 memcpy(cap[h->index], key->data.u.str.area,
212 len);
Willy Tarreau39713102016-11-25 15:49:32 +0100213 cap[h->index][len] = 0;
214 }
Willy Tarreau39713102016-11-25 15:49:32 +0100215 }
216 }
217
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100218 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100219 /* if we get there, it means we have no rule which matches, or
220 * we have an explicit accept, so we apply the default accept.
221 */
222 req->analysers &= ~an_bit;
223 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100224 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100225 return 1;
226
227 missing_data:
228 channel_dont_connect(req);
229 /* just set the request timeout once at the beginning of the request */
230 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
231 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100232 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100233 return 0;
234
Christopher Faulet282992e2019-12-16 12:34:31 +0100235 deny:
236 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
237 if (sess->listener && sess->listener->counters)
238 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
239 goto reject;
240
241 internal:
242 _HA_ATOMIC_ADD(&sess->fe->fe_counters.internal_errors, 1);
243 if (sess->listener && sess->listener->counters)
244 _HA_ATOMIC_ADD(&sess->listener->counters->internal_errors, 1);
245 if (!(s->flags & SF_ERR_MASK))
246 s->flags |= SF_ERR_INTERNAL;
247 goto reject;
248
249 invalid:
250 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
251 if (sess->listener && sess->listener->counters)
252 _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1);
253
254 reject:
255 si_must_kill_conn(chn_prod(req));
256 channel_abort(req);
257 channel_abort(&s->res);
258
259 abort:
260 req->analysers &= AN_REQ_FLT_END;
261
262 if (!(s->flags & SF_ERR_MASK))
263 s->flags |= SF_ERR_PRXCOND;
264 if (!(s->flags & SF_FINST_MASK))
265 s->flags |= SF_FINST_R;
266 DBG_TRACE_DEVEL("leaving on error|deny|abort", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
267 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100268}
269
270/* This function performs the TCP response analysis on the current response. It
271 * returns 1 if the processing can continue on next analysers, or zero if it
272 * needs more data, encounters an error, or wants to immediately abort the
273 * response. It relies on buffers flags, and updates s->rep->analysers. The
274 * function may be called for backend rules.
275 */
276int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
277{
278 struct session *sess = s->sess;
279 struct act_rule *rule;
280 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100281 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100282
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100283 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100284
285 /* We don't know whether we have enough data, so must proceed
286 * this way :
287 * - iterate through all rules in their declaration order
288 * - if one rule returns MISS, it means the inspect delay is
289 * not over yet, then return immediately, otherwise consider
290 * it as a non-match.
291 * - if one rule returns OK, then return OK
292 * - if one rule returns KO, then return KO
293 */
294
295 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
296 partial = SMP_OPT_FINAL;
297 else
298 partial = 0;
299
300 /* If "the current_rule_list" match the executed rule list, we are in
301 * resume condition. If a resume is needed it is always in the action
302 * and never in the ACL or converters. In this case, we initialise the
303 * current rule, and go to the action execution point.
304 */
305 if (s->current_rule) {
306 rule = s->current_rule;
307 s->current_rule = NULL;
308 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
309 goto resume_execution;
310 }
311 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
312
313 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
314 enum acl_test_res ret = ACL_TEST_PASS;
315
316 if (rule->cond) {
317 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
318 if (ret == ACL_TEST_MISS) {
319 /* just set the analyser timeout once at the beginning of the response */
320 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
321 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100322 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100323 return 0;
324 }
325
326 ret = acl_pass(ret);
327 if (rule->cond->pol == ACL_COND_UNLESS)
328 ret = !ret;
329 }
330
331 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100332 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100333resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100334 /* Always call the action function if defined */
335 if (rule->action_ptr) {
336 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100337 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100338
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100339 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100340 case ACT_RET_CONT:
341 break;
342 case ACT_RET_STOP:
343 case ACT_RET_DONE:
344 goto end;
345 case ACT_RET_YIELD:
346 s->current_rule = rule;
347 goto missing_data;
348 case ACT_RET_DENY:
349 goto deny;
350 case ACT_RET_ABRT:
351 goto abort;
352 case ACT_RET_ERR:
353 goto internal;
354 case ACT_RET_INV:
355 goto invalid;
356 }
357 continue; /* eval the next rule */
358 }
359
360 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100361 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100362 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100363 }
364 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100365 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100366 }
367 else if (rule->action == ACT_TCP_CLOSE) {
368 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100369 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100370 si_shutr(chn_prod(rep));
371 si_shutw(chn_prod(rep));
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100372 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100373 }
374 }
375 }
376
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100377 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100378 /* if we get there, it means we have no rule which matches, or
379 * we have an explicit accept, so we apply the default accept.
380 */
381 rep->analysers &= ~an_bit;
382 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100383 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100384 return 1;
Christopher Faulet282992e2019-12-16 12:34:31 +0100385
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100386 missing_data:
387 channel_dont_close(rep);
388 s->current_rule = rule;
389 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
390 return 0;
391
Christopher Faulet282992e2019-12-16 12:34:31 +0100392 deny:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100393 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100394 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100395 if (s->sess->listener->counters)
396 _HA_ATOMIC_ADD(&s->sess->listener->counters->denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100397 if (objt_server(s->target))
398 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
399 goto reject;
400
401 internal:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100402 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100403 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100404 if (s->sess->listener->counters)
405 _HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100406 if (objt_server(s->target))
407 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
408 if (!(s->flags & SF_ERR_MASK))
409 s->flags |= SF_ERR_INTERNAL;
410 goto reject;
411
412 invalid:
413 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
414 if (objt_server(s->target))
415 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
416
417 reject:
418 si_must_kill_conn(chn_prod(rep));
419 channel_abort(rep);
420 channel_abort(&s->req);
421
422 abort:
423 rep->analysers &= AN_REQ_FLT_END;
424
425 if (!(s->flags & SF_ERR_MASK))
426 s->flags |= SF_ERR_PRXCOND;
427 if (!(s->flags & SF_FINST_MASK))
428 s->flags |= SF_FINST_D;
429 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
430 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100431}
432
433
434/* This function performs the TCP layer4 analysis on the current request. It
435 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
436 * matches or if no more rule matches. It can only use rules which don't need
437 * any data. This only works on connection-based client-facing stream interfaces.
438 */
439int tcp_exec_l4_rules(struct session *sess)
440{
441 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100442 struct connection *conn = objt_conn(sess->origin);
443 int result = 1;
444 enum acl_test_res ret;
445
446 if (!conn)
447 return result;
448
449 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
450 ret = ACL_TEST_PASS;
451
452 if (rule->cond) {
453 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
454 ret = acl_pass(ret);
455 if (rule->cond->pol == ACL_COND_UNLESS)
456 ret = !ret;
457 }
458
459 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100460 /* Always call the action function if defined */
461 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100462 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100463 case ACT_RET_YIELD:
464 /* yield is not allowed at this point. If this return code is
465 * used it is a bug, so I prefer to abort the process.
466 */
467 send_log(sess->fe, LOG_WARNING,
468 "Internal error: yield not allowed with tcp-request connection actions.");
469 /* fall through */
470 case ACT_RET_STOP:
471 case ACT_RET_DONE:
472 goto end;
473 case ACT_RET_CONT:
474 break;
475 case ACT_RET_DENY:
476 case ACT_RET_ABRT:
477 case ACT_RET_ERR:
478 case ACT_RET_INV:
479 result = 0;
480 goto end;
481 }
482 continue; /* eval the next rule */
483 }
484
485 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100486 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100487 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100488 }
489 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100490 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100491 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100492 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100493
494 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100495 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100496 }
Willy Tarreau39713102016-11-25 15:49:32 +0100497 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200498 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
499 if (xprt_add_hs(conn) < 0) {
500 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100501 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200502 }
503 }
Willy Tarreau39713102016-11-25 15:49:32 +0100504 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100505 }
506 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200507 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
508 if (xprt_add_hs(conn) < 0) {
509 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100510 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200511 }
512 }
Willy Tarreau39713102016-11-25 15:49:32 +0100513 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100514 }
Willy Tarreau39713102016-11-25 15:49:32 +0100515 }
516 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100517 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100518 return result;
519}
520
521/* This function performs the TCP layer5 analysis on the current request. It
522 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
523 * matches or if no more rule matches. It can only use rules which don't need
524 * any data. This only works on session-based client-facing stream interfaces.
525 * An example of valid use case is to track a stick-counter on the source
526 * address extracted from the proxy protocol.
527 */
528int tcp_exec_l5_rules(struct session *sess)
529{
530 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100531 int result = 1;
532 enum acl_test_res ret;
533
534 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
535 ret = ACL_TEST_PASS;
536
537 if (rule->cond) {
538 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
539 ret = acl_pass(ret);
540 if (rule->cond->pol == ACL_COND_UNLESS)
541 ret = !ret;
542 }
543
544 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100545 /* Always call the action function if defined */
546 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100547 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100548 case ACT_RET_YIELD:
549 /* yield is not allowed at this point. If this return code is
550 * used it is a bug, so I prefer to abort the process.
551 */
552 send_log(sess->fe, LOG_WARNING,
553 "Internal error: yield not allowed with tcp-request session actions.");
554 /* fall through */
555 case ACT_RET_STOP:
556 case ACT_RET_DONE:
557 goto end;
558 case ACT_RET_CONT:
559 break;
560 case ACT_RET_DENY:
561 case ACT_RET_ABRT:
562 case ACT_RET_ERR:
563 case ACT_RET_INV:
564 result = 0;
565 goto end;
566 }
567 continue; /* eval the next rule */
568 }
569
570 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100571 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100572 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100573 }
574 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100575 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100576 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100577 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100578
579 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100580 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100581 }
Willy Tarreau39713102016-11-25 15:49:32 +0100582 }
583 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100584 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100585 return result;
586}
587
588/* Parse a tcp-response rule. Return a negative value in case of failure */
589static int tcp_parse_response_rule(char **args, int arg, int section_type,
590 struct proxy *curpx, struct proxy *defpx,
591 struct act_rule *rule, char **err,
592 unsigned int where,
593 const char *file, int line)
594{
595 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
596 memprintf(err, "%s %s is only allowed in 'backend' sections",
597 args[0], args[1]);
598 return -1;
599 }
600
601 if (strcmp(args[arg], "accept") == 0) {
602 arg++;
603 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100604 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100605 }
606 else if (strcmp(args[arg], "reject") == 0) {
607 arg++;
608 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100609 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100610 }
611 else if (strcmp(args[arg], "close") == 0) {
612 arg++;
613 rule->action = ACT_TCP_CLOSE;
Christopher Faulet245cf792019-12-18 14:58:12 +0100614 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100615 }
616 else {
617 struct action_kw *kw;
618 kw = tcp_res_cont_action(args[arg]);
619 if (kw) {
620 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100621 rule->kw = kw;
622 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
623 return -1;
624 } else {
625 action_build_list(&tcp_res_cont_keywords, &trash);
626 memprintf(err,
627 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200628 args[0], args[1], trash.area,
629 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100630 return -1;
631 }
632 }
633
634 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200635 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100636 memprintf(err,
637 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
638 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
639 return -1;
640 }
641 }
642 else if (*args[arg]) {
643 memprintf(err,
644 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
645 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
646 return -1;
647 }
648 return 0;
649}
650
Christopher Fauletac98d812019-12-18 09:20:16 +0100651
652/* This function executes a track-sc* actions. On success, it returns
653 * ACT_RET_CONT. If it must yield, it return ACT_RET_YIELD. Otherwsize
654 * ACT_RET_ERR is returned.
655 */
656static enum act_return tcp_action_track_sc(struct act_rule *rule, struct proxy *px,
657 struct session *sess, struct stream *s, int flags)
658{
659 struct stksess *ts;
660 struct stktable *t;
661 struct stktable_key *key;
662 struct sample smp;
663 int opt;
664
665 opt = ((rule->from == ACT_F_TCP_REQ_CNT) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
666 if (flags & ACT_FLAG_FINAL)
667 opt |= SMP_OPT_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100668
Christopher Fauletac98d812019-12-18 09:20:16 +0100669 if (stkctr_entry(&s->stkctr[rule->action]))
670 goto end;
671
672 t = rule->arg.trk_ctr.table.t;
673 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, &smp);
674
675 if ((smp.flags & SMP_F_MAY_CHANGE) && !(flags & ACT_FLAG_FINAL))
676 return ACT_RET_YIELD; /* key might appear later */
677
678 if (key && (ts = stktable_get_entry(t, key))) {
679 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
680 if (rule->from == ACT_F_TCP_REQ_CNT) {
681 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
682 if (sess->fe != s->be)
683 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
684 }
685 }
686
687 end:
688 return ACT_RET_CONT;
689}
Willy Tarreau39713102016-11-25 15:49:32 +0100690
691/* Parse a tcp-request rule. Return a negative value in case of failure */
692static int tcp_parse_request_rule(char **args, int arg, int section_type,
693 struct proxy *curpx, struct proxy *defpx,
694 struct act_rule *rule, char **err,
695 unsigned int where, const char *file, int line)
696{
697 if (curpx == defpx) {
698 memprintf(err, "%s %s is not allowed in 'defaults' sections",
699 args[0], args[1]);
700 return -1;
701 }
702
703 if (!strcmp(args[arg], "accept")) {
704 arg++;
705 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100706 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100707 }
708 else if (!strcmp(args[arg], "reject")) {
709 arg++;
710 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100711 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100712 }
713 else if (strcmp(args[arg], "capture") == 0) {
714 struct sample_expr *expr;
715 struct cap_hdr *hdr;
716 int kw = arg;
717 int len = 0;
718
719 if (!(curpx->cap & PR_CAP_FE)) {
720 memprintf(err,
721 "'%s %s %s' : proxy '%s' has no frontend capability",
722 args[0], args[1], args[kw], curpx->id);
723 return -1;
724 }
725
726 if (!(where & SMP_VAL_FE_REQ_CNT)) {
727 memprintf(err,
728 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
729 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
730 return -1;
731 }
732
733 arg++;
734
735 curpx->conf.args.ctx = ARGC_CAP;
736 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
737 if (!expr) {
738 memprintf(err,
739 "'%s %s %s' : %s",
740 args[0], args[1], args[kw], *err);
741 return -1;
742 }
743
744 if (!(expr->fetch->val & where)) {
745 memprintf(err,
746 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
747 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
748 free(expr);
749 return -1;
750 }
751
752 if (strcmp(args[arg], "len") == 0) {
753 arg++;
754 if (!args[arg]) {
755 memprintf(err,
756 "'%s %s %s' : missing length value",
757 args[0], args[1], args[kw]);
758 free(expr);
759 return -1;
760 }
761 /* we copy the table name for now, it will be resolved later */
762 len = atoi(args[arg]);
763 if (len <= 0) {
764 memprintf(err,
765 "'%s %s %s' : length must be > 0",
766 args[0], args[1], args[kw]);
767 free(expr);
768 return -1;
769 }
770 arg++;
771 }
772
773 if (!len) {
774 memprintf(err,
775 "'%s %s %s' : a positive 'len' argument is mandatory",
776 args[0], args[1], args[kw]);
777 free(expr);
778 return -1;
779 }
780
781 hdr = calloc(1, sizeof(*hdr));
782 hdr->next = curpx->req_cap;
783 hdr->name = NULL; /* not a header capture */
784 hdr->namelen = 0;
785 hdr->len = len;
786 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
787 hdr->index = curpx->nb_req_cap++;
788
789 curpx->req_cap = hdr;
790 curpx->to_log |= LW_REQHDR;
791
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200792 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100793 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
794
795 rule->arg.cap.expr = expr;
796 rule->arg.cap.hdr = hdr;
797 rule->action = ACT_TCP_CAPTURE;
798 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100799 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100800 struct sample_expr *expr;
801 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100802 unsigned int tsc_num;
803 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100804
805 arg++;
806
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100807 tsc_num_str = &args[kw][8];
808 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
809 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
810 return -1;
811 }
812
Willy Tarreau39713102016-11-25 15:49:32 +0100813 curpx->conf.args.ctx = ARGC_TRK;
814 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
815 if (!expr) {
816 memprintf(err,
817 "'%s %s %s' : %s",
818 args[0], args[1], args[kw], *err);
819 return -1;
820 }
821
822 if (!(expr->fetch->val & where)) {
823 memprintf(err,
824 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
825 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
826 free(expr);
827 return -1;
828 }
829
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200830 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100831 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
832
833 if (strcmp(args[arg], "table") == 0) {
834 arg++;
835 if (!args[arg]) {
836 memprintf(err,
837 "'%s %s %s' : missing table name",
838 args[0], args[1], args[kw]);
839 free(expr);
840 return -1;
841 }
842 /* we copy the table name for now, it will be resolved later */
843 rule->arg.trk_ctr.table.n = strdup(args[arg]);
844 arg++;
845 }
Christopher Fauletac98d812019-12-18 09:20:16 +0100846 rule->action = tsc_num;
Willy Tarreau39713102016-11-25 15:49:32 +0100847 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +0100848 rule->action_ptr = tcp_action_track_sc;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200849 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100850 }
851 else if (strcmp(args[arg], "expect-proxy") == 0) {
852 if (strcmp(args[arg+1], "layer4") != 0) {
853 memprintf(err,
854 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
855 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
856 return -1;
857 }
858
859 if (!(where & SMP_VAL_FE_CON_ACC)) {
860 memprintf(err,
861 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
862 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
863 return -1;
864 }
865
866 arg += 2;
867 rule->action = ACT_TCP_EXPECT_PX;
868 }
869 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
870 if (strcmp(args[arg+1], "layer4") != 0) {
871 memprintf(err,
872 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
873 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
874 return -1;
875 }
876
877 if (!(where & SMP_VAL_FE_CON_ACC)) {
878 memprintf(err,
879 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
880 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
881 return -1;
882 }
883
884 arg += 2;
885 rule->action = ACT_TCP_EXPECT_CIP;
886 }
887 else {
888 struct action_kw *kw;
889 if (where & SMP_VAL_FE_CON_ACC) {
890 /* L4 */
891 kw = tcp_req_conn_action(args[arg]);
892 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100893 } else if (where & SMP_VAL_FE_SES_ACC) {
894 /* L5 */
895 kw = tcp_req_sess_action(args[arg]);
896 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100897 } else {
898 /* L6 */
899 kw = tcp_req_cont_action(args[arg]);
900 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100901 }
902 if (kw) {
903 arg++;
904 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
905 return -1;
906 } else {
907 if (where & SMP_VAL_FE_CON_ACC)
908 action_build_list(&tcp_req_conn_keywords, &trash);
909 else if (where & SMP_VAL_FE_SES_ACC)
910 action_build_list(&tcp_req_sess_keywords, &trash);
911 else
912 action_build_list(&tcp_req_cont_keywords, &trash);
913 memprintf(err,
914 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
915 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200916 args[0], args[1], MAX_SESS_STKCTR-1,
917 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100918 curpx->id, args[arg]);
919 return -1;
920 }
921 }
922
923 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200924 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100925 memprintf(err,
926 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
927 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
928 return -1;
929 }
930 }
931 else if (*args[arg]) {
932 memprintf(err,
933 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
934 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
935 return -1;
936 }
937 return 0;
938}
939
940/* This function should be called to parse a line starting with the "tcp-response"
941 * keyword.
942 */
943static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
944 struct proxy *defpx, const char *file, int line,
945 char **err)
946{
947 const char *ptr = NULL;
948 unsigned int val;
949 int warn = 0;
950 int arg;
951 struct act_rule *rule;
952 unsigned int where;
953 const struct acl *acl;
954 const char *kw;
955
956 if (!*args[1]) {
957 memprintf(err, "missing argument for '%s' in %s '%s'",
958 args[0], proxy_type_str(curpx), curpx->id);
959 return -1;
960 }
961
962 if (strcmp(args[1], "inspect-delay") == 0) {
963 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
964 memprintf(err, "%s %s is only allowed in 'backend' sections",
965 args[0], args[1]);
966 return -1;
967 }
968
969 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
970 memprintf(err,
971 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
972 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200973
974 if (ptr == PARSE_TIME_OVER)
975 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
976 else if (ptr == PARSE_TIME_UNDER)
977 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
978 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100979 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
980 return -1;
981 }
982
983 if (curpx->tcp_rep.inspect_delay) {
984 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
985 args[0], args[1], proxy_type_str(curpx), curpx->id);
986 return 1;
987 }
988 curpx->tcp_rep.inspect_delay = val;
989 return 0;
990 }
991
992 rule = calloc(1, sizeof(*rule));
993 LIST_INIT(&rule->list);
994 arg = 1;
995 where = 0;
996
997 if (strcmp(args[1], "content") == 0) {
998 arg++;
999
1000 if (curpx->cap & PR_CAP_FE)
1001 where |= SMP_VAL_FE_RES_CNT;
1002 if (curpx->cap & PR_CAP_BE)
1003 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001004 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001005 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1006 goto error;
1007
1008 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1009 if (acl) {
1010 if (acl->name && *acl->name)
1011 memprintf(err,
1012 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1013 acl->name, args[0], args[1], sample_ckp_names(where));
1014 else
1015 memprintf(err,
1016 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1017 args[0], args[1],
1018 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1019 sample_ckp_names(where));
1020
1021 warn++;
1022 }
1023 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1024 if (acl->name && *acl->name)
1025 memprintf(err,
1026 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1027 acl->name, kw, sample_ckp_names(where));
1028 else
1029 memprintf(err,
1030 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1031 kw, sample_ckp_names(where));
1032 warn++;
1033 }
1034
1035 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1036 }
1037 else {
1038 memprintf(err,
1039 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1040 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1041 goto error;
1042 }
1043
1044 return warn;
1045 error:
1046 free(rule);
1047 return -1;
1048}
1049
1050
1051/* This function should be called to parse a line starting with the "tcp-request"
1052 * keyword.
1053 */
1054static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1055 struct proxy *defpx, const char *file, int line,
1056 char **err)
1057{
1058 const char *ptr = NULL;
1059 unsigned int val;
1060 int warn = 0;
1061 int arg;
1062 struct act_rule *rule;
1063 unsigned int where;
1064 const struct acl *acl;
1065 const char *kw;
1066
1067 if (!*args[1]) {
1068 if (curpx == defpx)
1069 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1070 else
1071 memprintf(err, "missing argument for '%s' in %s '%s'",
1072 args[0], proxy_type_str(curpx), curpx->id);
1073 return -1;
1074 }
1075
1076 if (!strcmp(args[1], "inspect-delay")) {
1077 if (curpx == defpx) {
1078 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1079 args[0], args[1]);
1080 return -1;
1081 }
1082
1083 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1084 memprintf(err,
1085 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1086 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001087
1088 if (ptr == PARSE_TIME_OVER)
1089 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1090 else if (ptr == PARSE_TIME_UNDER)
1091 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1092 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001093 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1094 return -1;
1095 }
1096
1097 if (curpx->tcp_req.inspect_delay) {
1098 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1099 args[0], args[1], proxy_type_str(curpx), curpx->id);
1100 return 1;
1101 }
1102 curpx->tcp_req.inspect_delay = val;
1103 return 0;
1104 }
1105
1106 rule = calloc(1, sizeof(*rule));
1107 LIST_INIT(&rule->list);
1108 arg = 1;
1109 where = 0;
1110
1111 if (strcmp(args[1], "content") == 0) {
1112 arg++;
1113
1114 if (curpx->cap & PR_CAP_FE)
1115 where |= SMP_VAL_FE_REQ_CNT;
1116 if (curpx->cap & PR_CAP_BE)
1117 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001118 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001119 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1120 goto error;
1121
1122 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1123 if (acl) {
1124 if (acl->name && *acl->name)
1125 memprintf(err,
1126 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1127 acl->name, args[0], args[1], sample_ckp_names(where));
1128 else
1129 memprintf(err,
1130 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1131 args[0], args[1],
1132 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1133 sample_ckp_names(where));
1134
1135 warn++;
1136 }
1137 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1138 if (acl->name && *acl->name)
1139 memprintf(err,
1140 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1141 acl->name, kw, sample_ckp_names(where));
1142 else
1143 memprintf(err,
1144 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1145 kw, sample_ckp_names(where));
1146 warn++;
1147 }
1148
1149 /* the following function directly emits the warning */
1150 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1151 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1152 }
1153 else if (strcmp(args[1], "connection") == 0) {
1154 arg++;
1155
1156 if (!(curpx->cap & PR_CAP_FE)) {
1157 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1158 args[0], args[1], proxy_type_str(curpx), curpx->id);
1159 goto error;
1160 }
1161
1162 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001163 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001164 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1165 goto error;
1166
1167 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1168 if (acl) {
1169 if (acl->name && *acl->name)
1170 memprintf(err,
1171 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1172 acl->name, args[0], args[1], sample_ckp_names(where));
1173 else
1174 memprintf(err,
1175 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1176 args[0], args[1],
1177 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1178 sample_ckp_names(where));
1179
1180 warn++;
1181 }
1182 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1183 if (acl->name && *acl->name)
1184 memprintf(err,
1185 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1186 acl->name, kw, sample_ckp_names(where));
1187 else
1188 memprintf(err,
1189 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1190 kw, sample_ckp_names(where));
1191 warn++;
1192 }
1193
1194 /* the following function directly emits the warning */
1195 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1196 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1197 }
1198 else if (strcmp(args[1], "session") == 0) {
1199 arg++;
1200
1201 if (!(curpx->cap & PR_CAP_FE)) {
1202 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1203 args[0], args[1], proxy_type_str(curpx), curpx->id);
1204 goto error;
1205 }
1206
1207 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001208 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001209 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1210 goto error;
1211
1212 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1213 if (acl) {
1214 if (acl->name && *acl->name)
1215 memprintf(err,
1216 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1217 acl->name, args[0], args[1], sample_ckp_names(where));
1218 else
1219 memprintf(err,
1220 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1221 args[0], args[1],
1222 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1223 sample_ckp_names(where));
1224 warn++;
1225 }
1226 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1227 if (acl->name && *acl->name)
1228 memprintf(err,
1229 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1230 acl->name, kw, sample_ckp_names(where));
1231 else
1232 memprintf(err,
1233 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1234 kw, sample_ckp_names(where));
1235 warn++;
1236 }
1237
1238 /* the following function directly emits the warning */
1239 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1240 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1241 }
1242 else {
1243 if (curpx == defpx)
1244 memprintf(err,
1245 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1246 args[0], args[1]);
1247 else
1248 memprintf(err,
1249 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1250 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1251 goto error;
1252 }
1253
1254 return warn;
1255 error:
1256 free(rule);
1257 return -1;
1258}
1259
1260static struct cfg_kw_list cfg_kws = {ILH, {
1261 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1262 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1263 { 0, NULL, NULL },
1264}};
1265
Willy Tarreau0108d902018-11-25 19:14:37 +01001266INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001267
1268/*
1269 * Local variables:
1270 * c-indent-level: 8
1271 * c-basic-offset: 8
1272 * End:
1273 */