blob: 58e67ca765759ab150f9fa2f11b2b16301c1e230 [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 Tarreau4c7e4b72020-05-27 12:58:42 +020012#include <haproxy/api.h>
Willy Tarreau39713102016-11-25 15:49:32 +010013#include <common/cfgparse.h>
Willy Tarreau39713102016-11-25 15:49:32 +010014#include <common/debug.h>
15#include <common/mini-clist.h>
16#include <common/standard.h>
17#include <common/ticks.h>
18#include <common/time.h>
19
20#include <types/arg.h>
21#include <types/capture.h>
22#include <types/connection.h>
23#include <types/global.h>
24
25#include <proto/acl.h>
26#include <proto/action.h>
27#include <proto/channel.h>
28#include <proto/connection.h>
29#include <proto/log.h>
30#include <proto/proxy.h>
31#include <proto/sample.h>
32#include <proto/stick_table.h>
33#include <proto/stream.h>
34#include <proto/stream_interface.h>
35#include <proto/tcp_rules.h>
36
Christopher Fauleteea8fc72019-11-05 16:18:10 +010037#define TRACE_SOURCE &trace_strm
38
Willy Tarreau39713102016-11-25 15:49:32 +010039/* List head of all known action keywords for "tcp-request connection" */
40struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
41struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
42struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
43struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
44
45/*
46 * Register keywords.
47 */
48void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
49{
50 LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
51}
52
53void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
54{
55 LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
56}
57
58void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
59{
60 LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
61}
62
63void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
64{
65 LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
66}
67
68/*
69 * Return the struct tcp_req_action_kw associated to a keyword.
70 */
71static struct action_kw *tcp_req_conn_action(const char *kw)
72{
73 return action_lookup(&tcp_req_conn_keywords, kw);
74}
75
76static struct action_kw *tcp_req_sess_action(const char *kw)
77{
78 return action_lookup(&tcp_req_sess_keywords, kw);
79}
80
81static struct action_kw *tcp_req_cont_action(const char *kw)
82{
83 return action_lookup(&tcp_req_cont_keywords, kw);
84}
85
86static struct action_kw *tcp_res_cont_action(const char *kw)
87{
88 return action_lookup(&tcp_res_cont_keywords, kw);
89}
90
91/* This function performs the TCP request analysis on the current request. It
92 * returns 1 if the processing can continue on next analysers, or zero if it
93 * needs more data, encounters an error, or wants to immediately abort the
94 * request. It relies on buffers flags, and updates s->req->analysers. The
95 * function may be called for frontend rules and backend rules. It only relies
96 * on the backend pointer so this works for both cases.
97 */
98int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
99{
100 struct session *sess = s->sess;
101 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100102 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100103 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100104
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100105 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100106
107 /* We don't know whether we have enough data, so must proceed
108 * this way :
109 * - iterate through all rules in their declaration order
110 * - if one rule returns MISS, it means the inspect delay is
111 * not over yet, then return immediately, otherwise consider
112 * it as a non-match.
113 * - if one rule returns OK, then return OK
114 * - if one rule returns KO, then return KO
115 */
116
Willy Tarreau23752332018-06-15 14:54:53 +0200117 if ((req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
Willy Tarreau39713102016-11-25 15:49:32 +0100118 !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
119 partial = SMP_OPT_FINAL;
120 else
121 partial = 0;
122
123 /* If "the current_rule_list" match the executed rule list, we are in
124 * resume condition. If a resume is needed it is always in the action
125 * and never in the ACL or converters. In this case, we initialise the
126 * current rule, and go to the action execution point.
127 */
128 if (s->current_rule) {
129 rule = s->current_rule;
130 s->current_rule = NULL;
131 if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
132 goto resume_execution;
133 }
134 s->current_rule_list = &s->be->tcp_req.inspect_rules;
135
136 list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
137 enum acl_test_res ret = ACL_TEST_PASS;
138
139 if (rule->cond) {
140 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
141 if (ret == ACL_TEST_MISS)
142 goto missing_data;
143
144 ret = acl_pass(ret);
145 if (rule->cond->pol == ACL_COND_UNLESS)
146 ret = !ret;
147 }
148
149 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100150 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100151resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100152
153 /* Always call the action function if defined */
154 if (rule->action_ptr) {
155 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100156 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100157
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100158 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100159 case ACT_RET_CONT:
160 break;
161 case ACT_RET_STOP:
162 case ACT_RET_DONE:
163 goto end;
164 case ACT_RET_YIELD:
165 s->current_rule = rule;
166 goto missing_data;
167 case ACT_RET_DENY:
168 goto deny;
169 case ACT_RET_ABRT:
170 goto abort;
171 case ACT_RET_ERR:
172 goto internal;
173 case ACT_RET_INV:
174 goto invalid;
175 }
176 continue; /* eval the next rule */
177 }
178
179 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100180 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100181 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100182 }
183 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100184 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100185 }
Willy Tarreau39713102016-11-25 15:49:32 +0100186 }
187 }
188
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100189 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100190 /* if we get there, it means we have no rule which matches, or
191 * we have an explicit accept, so we apply the default accept.
192 */
193 req->analysers &= ~an_bit;
194 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100195 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100196 return 1;
197
198 missing_data:
199 channel_dont_connect(req);
200 /* just set the request timeout once at the beginning of the request */
201 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
202 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100203 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100204 return 0;
205
Christopher Faulet282992e2019-12-16 12:34:31 +0100206 deny:
207 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
208 if (sess->listener && sess->listener->counters)
209 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
210 goto reject;
211
212 internal:
213 _HA_ATOMIC_ADD(&sess->fe->fe_counters.internal_errors, 1);
214 if (sess->listener && sess->listener->counters)
215 _HA_ATOMIC_ADD(&sess->listener->counters->internal_errors, 1);
216 if (!(s->flags & SF_ERR_MASK))
217 s->flags |= SF_ERR_INTERNAL;
218 goto reject;
219
220 invalid:
221 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
222 if (sess->listener && sess->listener->counters)
223 _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1);
224
225 reject:
226 si_must_kill_conn(chn_prod(req));
227 channel_abort(req);
228 channel_abort(&s->res);
229
230 abort:
231 req->analysers &= AN_REQ_FLT_END;
232
233 if (!(s->flags & SF_ERR_MASK))
234 s->flags |= SF_ERR_PRXCOND;
235 if (!(s->flags & SF_FINST_MASK))
236 s->flags |= SF_FINST_R;
237 DBG_TRACE_DEVEL("leaving on error|deny|abort", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
238 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100239}
240
241/* This function performs the TCP response analysis on the current response. It
242 * returns 1 if the processing can continue on next analysers, or zero if it
243 * needs more data, encounters an error, or wants to immediately abort the
244 * response. It relies on buffers flags, and updates s->rep->analysers. The
245 * function may be called for backend rules.
246 */
247int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
248{
249 struct session *sess = s->sess;
250 struct act_rule *rule;
251 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100252 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100253
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100254 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100255
256 /* We don't know whether we have enough data, so must proceed
257 * this way :
258 * - iterate through all rules in their declaration order
259 * - if one rule returns MISS, it means the inspect delay is
260 * not over yet, then return immediately, otherwise consider
261 * it as a non-match.
262 * - if one rule returns OK, then return OK
263 * - if one rule returns KO, then return KO
264 */
265
266 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
267 partial = SMP_OPT_FINAL;
268 else
269 partial = 0;
270
271 /* If "the current_rule_list" match the executed rule list, we are in
272 * resume condition. If a resume is needed it is always in the action
273 * and never in the ACL or converters. In this case, we initialise the
274 * current rule, and go to the action execution point.
275 */
276 if (s->current_rule) {
277 rule = s->current_rule;
278 s->current_rule = NULL;
279 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
280 goto resume_execution;
281 }
282 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
283
284 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
285 enum acl_test_res ret = ACL_TEST_PASS;
286
287 if (rule->cond) {
288 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
289 if (ret == ACL_TEST_MISS) {
290 /* just set the analyser timeout once at the beginning of the response */
291 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
292 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100293 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100294 return 0;
295 }
296
297 ret = acl_pass(ret);
298 if (rule->cond->pol == ACL_COND_UNLESS)
299 ret = !ret;
300 }
301
302 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100303 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100304resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100305 /* Always call the action function if defined */
306 if (rule->action_ptr) {
307 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100308 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100309
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100310 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100311 case ACT_RET_CONT:
312 break;
313 case ACT_RET_STOP:
314 case ACT_RET_DONE:
315 goto end;
316 case ACT_RET_YIELD:
317 s->current_rule = rule;
318 goto missing_data;
319 case ACT_RET_DENY:
320 goto deny;
321 case ACT_RET_ABRT:
322 goto abort;
323 case ACT_RET_ERR:
324 goto internal;
325 case ACT_RET_INV:
326 goto invalid;
327 }
328 continue; /* eval the next rule */
329 }
330
331 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100332 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100333 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100334 }
335 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100336 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100337 }
338 else if (rule->action == ACT_TCP_CLOSE) {
339 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100340 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100341 si_shutr(chn_prod(rep));
342 si_shutw(chn_prod(rep));
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100343 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100344 }
345 }
346 }
347
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100348 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100349 /* if we get there, it means we have no rule which matches, or
350 * we have an explicit accept, so we apply the default accept.
351 */
352 rep->analysers &= ~an_bit;
353 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100354 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100355 return 1;
Christopher Faulet282992e2019-12-16 12:34:31 +0100356
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100357 missing_data:
358 channel_dont_close(rep);
359 s->current_rule = rule;
360 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
361 return 0;
362
Christopher Faulet282992e2019-12-16 12:34:31 +0100363 deny:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100364 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100365 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100366 if (s->sess->listener->counters)
367 _HA_ATOMIC_ADD(&s->sess->listener->counters->denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100368 if (objt_server(s->target))
369 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
370 goto reject;
371
372 internal:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100373 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100374 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100375 if (s->sess->listener->counters)
376 _HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100377 if (objt_server(s->target))
378 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
379 if (!(s->flags & SF_ERR_MASK))
380 s->flags |= SF_ERR_INTERNAL;
381 goto reject;
382
383 invalid:
384 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
385 if (objt_server(s->target))
386 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
387
388 reject:
389 si_must_kill_conn(chn_prod(rep));
390 channel_abort(rep);
391 channel_abort(&s->req);
392
393 abort:
394 rep->analysers &= AN_REQ_FLT_END;
395
396 if (!(s->flags & SF_ERR_MASK))
397 s->flags |= SF_ERR_PRXCOND;
398 if (!(s->flags & SF_FINST_MASK))
399 s->flags |= SF_FINST_D;
400 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
401 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100402}
403
404
405/* This function performs the TCP layer4 analysis on the current request. It
406 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
407 * matches or if no more rule matches. It can only use rules which don't need
408 * any data. This only works on connection-based client-facing stream interfaces.
409 */
410int tcp_exec_l4_rules(struct session *sess)
411{
412 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100413 struct connection *conn = objt_conn(sess->origin);
414 int result = 1;
415 enum acl_test_res ret;
416
417 if (!conn)
418 return result;
419
420 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
421 ret = ACL_TEST_PASS;
422
423 if (rule->cond) {
424 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
425 ret = acl_pass(ret);
426 if (rule->cond->pol == ACL_COND_UNLESS)
427 ret = !ret;
428 }
429
430 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100431 /* Always call the action function if defined */
432 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100433 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100434 case ACT_RET_YIELD:
435 /* yield is not allowed at this point. If this return code is
436 * used it is a bug, so I prefer to abort the process.
437 */
438 send_log(sess->fe, LOG_WARNING,
439 "Internal error: yield not allowed with tcp-request connection actions.");
440 /* fall through */
441 case ACT_RET_STOP:
442 case ACT_RET_DONE:
443 goto end;
444 case ACT_RET_CONT:
445 break;
446 case ACT_RET_DENY:
447 case ACT_RET_ABRT:
448 case ACT_RET_ERR:
449 case ACT_RET_INV:
450 result = 0;
451 goto end;
452 }
453 continue; /* eval the next rule */
454 }
455
456 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100457 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100458 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100459 }
460 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100461 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100462 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100463 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100464
465 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100466 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100467 }
Willy Tarreau39713102016-11-25 15:49:32 +0100468 else if (rule->action == ACT_TCP_EXPECT_PX) {
Willy Tarreau4450b582020-01-23 15:23:13 +0100469 if (!(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200470 if (xprt_add_hs(conn) < 0) {
471 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100472 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200473 }
474 }
Willy Tarreau39713102016-11-25 15:49:32 +0100475 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100476 }
477 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Willy Tarreau4450b582020-01-23 15:23:13 +0100478 if (!(conn->flags & CO_FL_HANDSHAKE)) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200479 if (xprt_add_hs(conn) < 0) {
480 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100481 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200482 }
483 }
Willy Tarreau39713102016-11-25 15:49:32 +0100484 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100485 }
Willy Tarreau39713102016-11-25 15:49:32 +0100486 }
487 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100488 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100489 return result;
490}
491
492/* This function performs the TCP layer5 analysis on the current request. It
493 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
494 * matches or if no more rule matches. It can only use rules which don't need
495 * any data. This only works on session-based client-facing stream interfaces.
496 * An example of valid use case is to track a stick-counter on the source
497 * address extracted from the proxy protocol.
498 */
499int tcp_exec_l5_rules(struct session *sess)
500{
501 struct act_rule *rule;
Willy Tarreau39713102016-11-25 15:49:32 +0100502 int result = 1;
503 enum acl_test_res ret;
504
505 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
506 ret = ACL_TEST_PASS;
507
508 if (rule->cond) {
509 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
510 ret = acl_pass(ret);
511 if (rule->cond->pol == ACL_COND_UNLESS)
512 ret = !ret;
513 }
514
515 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100516 /* Always call the action function if defined */
517 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100518 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100519 case ACT_RET_YIELD:
520 /* yield is not allowed at this point. If this return code is
521 * used it is a bug, so I prefer to abort the process.
522 */
523 send_log(sess->fe, LOG_WARNING,
524 "Internal error: yield not allowed with tcp-request session actions.");
525 /* fall through */
526 case ACT_RET_STOP:
527 case ACT_RET_DONE:
528 goto end;
529 case ACT_RET_CONT:
530 break;
531 case ACT_RET_DENY:
532 case ACT_RET_ABRT:
533 case ACT_RET_ERR:
534 case ACT_RET_INV:
535 result = 0;
536 goto end;
537 }
538 continue; /* eval the next rule */
539 }
540
541 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100542 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100543 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100544 }
545 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100546 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100547 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100548 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100549
550 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100551 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100552 }
Willy Tarreau39713102016-11-25 15:49:32 +0100553 }
554 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100555 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100556 return result;
557}
558
559/* Parse a tcp-response rule. Return a negative value in case of failure */
560static int tcp_parse_response_rule(char **args, int arg, int section_type,
561 struct proxy *curpx, struct proxy *defpx,
562 struct act_rule *rule, char **err,
563 unsigned int where,
564 const char *file, int line)
565{
566 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
567 memprintf(err, "%s %s is only allowed in 'backend' sections",
568 args[0], args[1]);
569 return -1;
570 }
571
572 if (strcmp(args[arg], "accept") == 0) {
573 arg++;
574 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100575 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100576 }
577 else if (strcmp(args[arg], "reject") == 0) {
578 arg++;
579 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100580 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100581 }
582 else if (strcmp(args[arg], "close") == 0) {
583 arg++;
584 rule->action = ACT_TCP_CLOSE;
Christopher Faulet245cf792019-12-18 14:58:12 +0100585 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100586 }
587 else {
588 struct action_kw *kw;
589 kw = tcp_res_cont_action(args[arg]);
590 if (kw) {
591 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100592 rule->kw = kw;
593 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
594 return -1;
595 } else {
596 action_build_list(&tcp_res_cont_keywords, &trash);
597 memprintf(err,
598 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200599 args[0], args[1], trash.area,
600 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100601 return -1;
602 }
603 }
604
605 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200606 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100607 memprintf(err,
608 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
609 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
610 return -1;
611 }
612 }
613 else if (*args[arg]) {
614 memprintf(err,
615 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
616 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
617 return -1;
618 }
619 return 0;
620}
621
Christopher Fauletac98d812019-12-18 09:20:16 +0100622
623/* This function executes a track-sc* actions. On success, it returns
624 * ACT_RET_CONT. If it must yield, it return ACT_RET_YIELD. Otherwsize
625 * ACT_RET_ERR is returned.
626 */
627static enum act_return tcp_action_track_sc(struct act_rule *rule, struct proxy *px,
628 struct session *sess, struct stream *s, int flags)
629{
630 struct stksess *ts;
631 struct stktable *t;
632 struct stktable_key *key;
633 struct sample smp;
634 int opt;
635
Christopher Faulet67307792020-02-10 09:54:49 +0100636 opt = SMP_OPT_DIR_REQ;
Christopher Fauletac98d812019-12-18 09:20:16 +0100637 if (flags & ACT_FLAG_FINAL)
638 opt |= SMP_OPT_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100639
Christopher Fauletac98d812019-12-18 09:20:16 +0100640 t = rule->arg.trk_ctr.table.t;
Christopher Faulet67307792020-02-10 09:54:49 +0100641 if (rule->from == ACT_F_TCP_REQ_CNT) { /* L7 rules: use the stream */
642 if (stkctr_entry(&s->stkctr[rule->action]))
643 goto end;
Christopher Fauletac98d812019-12-18 09:20:16 +0100644
Christopher Faulet67307792020-02-10 09:54:49 +0100645 key = stktable_fetch_key(t, s->be, sess, s, opt, rule->arg.trk_ctr.expr, &smp);
Christopher Fauletac98d812019-12-18 09:20:16 +0100646
Christopher Faulet67307792020-02-10 09:54:49 +0100647 if ((smp.flags & SMP_F_MAY_CHANGE) && !(flags & ACT_FLAG_FINAL))
648 return ACT_RET_YIELD; /* key might appear later */
649
650 if (key && (ts = stktable_get_entry(t, key))) {
651 stream_track_stkctr(&s->stkctr[rule->action], t, ts);
Christopher Fauletac98d812019-12-18 09:20:16 +0100652 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_CONTENT);
653 if (sess->fe != s->be)
654 stkctr_set_flags(&s->stkctr[rule->action], STKCTR_TRACK_BACKEND);
655 }
656 }
Christopher Faulet67307792020-02-10 09:54:49 +0100657 else { /* L4/L5 rules: use the session */
658 if (stkctr_entry(&sess->stkctr[rule->action]))
659 goto end;
660
661 key = stktable_fetch_key(t, sess->fe, sess, NULL, opt, rule->arg.trk_ctr.expr, NULL);
662 if (key && (ts = stktable_get_entry(t, key)))
663 stream_track_stkctr(&sess->stkctr[rule->action], t, ts);
664 }
Christopher Fauletac98d812019-12-18 09:20:16 +0100665
666 end:
667 return ACT_RET_CONT;
668}
Willy Tarreau39713102016-11-25 15:49:32 +0100669
Christopher Fauletd73b96d2019-12-19 17:27:03 +0100670/* This function executes a capture actions. It executes a fetch expression,
671 * turns the result into a string and puts it in a capture slot. On success, it
672 * returns ACT_RET_CONT. If it must yield, it return ACT_RET_YIELD. Otherwsize
673 * ACT_RET_ERR is returned.
674 */
675static enum act_return tcp_action_capture(struct act_rule *rule, struct proxy *px,
676 struct session *sess, struct stream *s, int flags)
677{
678 struct sample *key;
679 struct cap_hdr *h = rule->arg.cap.hdr;
680 char **cap = s->req_cap;
681 int len, opt;
682
683 opt = ((rule->from == ACT_F_TCP_REQ_CNT) ? SMP_OPT_DIR_REQ : SMP_OPT_DIR_RES);
684 if (flags & ACT_FLAG_FINAL)
685 opt |= SMP_OPT_FINAL;
686
687 key = sample_fetch_as_type(s->be, sess, s, opt, rule->arg.cap.expr, SMP_T_STR);
688 if (!key)
689 goto end;
690
691 if ((key->flags & SMP_F_MAY_CHANGE) && !(flags & ACT_FLAG_FINAL))
692 return ACT_RET_YIELD; /* key might appear later */
693
694 if (cap[h->index] == NULL) {
695 cap[h->index] = pool_alloc(h->pool);
696 if (cap[h->index] == NULL) /* no more capture memory, ignore error */
697 goto end;
698 }
699
700 len = key->data.u.str.data;
701 if (len > h->len)
702 len = h->len;
703
704 memcpy(cap[h->index], key->data.u.str.area, len);
705 cap[h->index][len] = 0;
706
707 end:
708 return ACT_RET_CONT;
709}
710
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100711static void release_tcp_capture(struct act_rule * rule)
712{
713 release_sample_expr(rule->arg.cap.expr);
714}
715
716
717static void release_tcp_track_sc(struct act_rule * rule)
718{
719 release_sample_expr(rule->arg.trk_ctr.expr);
720}
721
Willy Tarreau39713102016-11-25 15:49:32 +0100722/* Parse a tcp-request rule. Return a negative value in case of failure */
723static int tcp_parse_request_rule(char **args, int arg, int section_type,
724 struct proxy *curpx, struct proxy *defpx,
725 struct act_rule *rule, char **err,
726 unsigned int where, const char *file, int line)
727{
728 if (curpx == defpx) {
729 memprintf(err, "%s %s is not allowed in 'defaults' sections",
730 args[0], args[1]);
731 return -1;
732 }
733
734 if (!strcmp(args[arg], "accept")) {
735 arg++;
736 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100737 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100738 }
739 else if (!strcmp(args[arg], "reject")) {
740 arg++;
741 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100742 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100743 }
744 else if (strcmp(args[arg], "capture") == 0) {
745 struct sample_expr *expr;
746 struct cap_hdr *hdr;
747 int kw = arg;
748 int len = 0;
749
750 if (!(curpx->cap & PR_CAP_FE)) {
751 memprintf(err,
752 "'%s %s %s' : proxy '%s' has no frontend capability",
753 args[0], args[1], args[kw], curpx->id);
754 return -1;
755 }
756
757 if (!(where & SMP_VAL_FE_REQ_CNT)) {
758 memprintf(err,
759 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
760 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
761 return -1;
762 }
763
764 arg++;
765
766 curpx->conf.args.ctx = ARGC_CAP;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100767 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
Willy Tarreau39713102016-11-25 15:49:32 +0100768 if (!expr) {
769 memprintf(err,
770 "'%s %s %s' : %s",
771 args[0], args[1], args[kw], *err);
772 return -1;
773 }
774
775 if (!(expr->fetch->val & where)) {
776 memprintf(err,
777 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
778 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100779 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100780 return -1;
781 }
782
783 if (strcmp(args[arg], "len") == 0) {
784 arg++;
785 if (!args[arg]) {
786 memprintf(err,
787 "'%s %s %s' : missing length value",
788 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100789 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100790 return -1;
791 }
792 /* we copy the table name for now, it will be resolved later */
793 len = atoi(args[arg]);
794 if (len <= 0) {
795 memprintf(err,
796 "'%s %s %s' : length must be > 0",
797 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100798 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100799 return -1;
800 }
801 arg++;
802 }
803
804 if (!len) {
805 memprintf(err,
806 "'%s %s %s' : a positive 'len' argument is mandatory",
807 args[0], args[1], args[kw]);
808 free(expr);
809 return -1;
810 }
811
812 hdr = calloc(1, sizeof(*hdr));
813 hdr->next = curpx->req_cap;
814 hdr->name = NULL; /* not a header capture */
815 hdr->namelen = 0;
816 hdr->len = len;
817 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
818 hdr->index = curpx->nb_req_cap++;
819
820 curpx->req_cap = hdr;
821 curpx->to_log |= LW_REQHDR;
822
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200823 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100824 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
825
826 rule->arg.cap.expr = expr;
827 rule->arg.cap.hdr = hdr;
Christopher Fauletd73b96d2019-12-19 17:27:03 +0100828 rule->action = ACT_CUSTOM;
829 rule->action_ptr = tcp_action_capture;
830 rule->check_ptr = check_capture;
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100831 rule->release_ptr = release_tcp_capture;
Willy Tarreau39713102016-11-25 15:49:32 +0100832 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100833 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100834 struct sample_expr *expr;
835 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100836 unsigned int tsc_num;
837 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100838
839 arg++;
840
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100841 tsc_num_str = &args[kw][8];
842 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
843 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
844 return -1;
845 }
846
Willy Tarreau39713102016-11-25 15:49:32 +0100847 curpx->conf.args.ctx = ARGC_TRK;
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100848 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args, NULL);
Willy Tarreau39713102016-11-25 15:49:32 +0100849 if (!expr) {
850 memprintf(err,
851 "'%s %s %s' : %s",
852 args[0], args[1], args[kw], *err);
853 return -1;
854 }
855
856 if (!(expr->fetch->val & where)) {
857 memprintf(err,
858 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
859 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100860 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100861 return -1;
862 }
863
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200864 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100865 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
866
867 if (strcmp(args[arg], "table") == 0) {
868 arg++;
869 if (!args[arg]) {
870 memprintf(err,
871 "'%s %s %s' : missing table name",
872 args[0], args[1], args[kw]);
Christopher Fauletfdb6fbf2020-01-14 15:05:56 +0100873 release_sample_expr(expr);
Willy Tarreau39713102016-11-25 15:49:32 +0100874 return -1;
875 }
876 /* we copy the table name for now, it will be resolved later */
877 rule->arg.trk_ctr.table.n = strdup(args[arg]);
878 arg++;
879 }
Christopher Fauletac98d812019-12-18 09:20:16 +0100880 rule->action = tsc_num;
Willy Tarreau39713102016-11-25 15:49:32 +0100881 rule->arg.trk_ctr.expr = expr;
Christopher Fauletac98d812019-12-18 09:20:16 +0100882 rule->action_ptr = tcp_action_track_sc;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200883 rule->check_ptr = check_trk_action;
Christopher Fauletadfc6e82020-01-14 15:05:33 +0100884 rule->release_ptr = release_tcp_track_sc;
Willy Tarreau39713102016-11-25 15:49:32 +0100885 }
886 else if (strcmp(args[arg], "expect-proxy") == 0) {
887 if (strcmp(args[arg+1], "layer4") != 0) {
888 memprintf(err,
889 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
890 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
891 return -1;
892 }
893
894 if (!(where & SMP_VAL_FE_CON_ACC)) {
895 memprintf(err,
896 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
897 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
898 return -1;
899 }
900
901 arg += 2;
902 rule->action = ACT_TCP_EXPECT_PX;
903 }
904 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
905 if (strcmp(args[arg+1], "layer4") != 0) {
906 memprintf(err,
907 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
908 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
909 return -1;
910 }
911
912 if (!(where & SMP_VAL_FE_CON_ACC)) {
913 memprintf(err,
914 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
915 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
916 return -1;
917 }
918
919 arg += 2;
920 rule->action = ACT_TCP_EXPECT_CIP;
921 }
922 else {
923 struct action_kw *kw;
924 if (where & SMP_VAL_FE_CON_ACC) {
925 /* L4 */
926 kw = tcp_req_conn_action(args[arg]);
927 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100928 } else if (where & SMP_VAL_FE_SES_ACC) {
929 /* L5 */
930 kw = tcp_req_sess_action(args[arg]);
931 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100932 } else {
933 /* L6 */
934 kw = tcp_req_cont_action(args[arg]);
935 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100936 }
937 if (kw) {
938 arg++;
939 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
940 return -1;
941 } else {
942 if (where & SMP_VAL_FE_CON_ACC)
943 action_build_list(&tcp_req_conn_keywords, &trash);
944 else if (where & SMP_VAL_FE_SES_ACC)
945 action_build_list(&tcp_req_sess_keywords, &trash);
946 else
947 action_build_list(&tcp_req_cont_keywords, &trash);
948 memprintf(err,
949 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
950 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200951 args[0], args[1], MAX_SESS_STKCTR-1,
952 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100953 curpx->id, args[arg]);
954 return -1;
955 }
956 }
957
958 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200959 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100960 memprintf(err,
961 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
962 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
963 return -1;
964 }
965 }
966 else if (*args[arg]) {
967 memprintf(err,
968 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
969 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
970 return -1;
971 }
972 return 0;
973}
974
975/* This function should be called to parse a line starting with the "tcp-response"
976 * keyword.
977 */
978static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
979 struct proxy *defpx, const char *file, int line,
980 char **err)
981{
982 const char *ptr = NULL;
983 unsigned int val;
984 int warn = 0;
985 int arg;
986 struct act_rule *rule;
987 unsigned int where;
988 const struct acl *acl;
989 const char *kw;
990
991 if (!*args[1]) {
992 memprintf(err, "missing argument for '%s' in %s '%s'",
993 args[0], proxy_type_str(curpx), curpx->id);
994 return -1;
995 }
996
997 if (strcmp(args[1], "inspect-delay") == 0) {
998 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
999 memprintf(err, "%s %s is only allowed in 'backend' sections",
1000 args[0], args[1]);
1001 return -1;
1002 }
1003
1004 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1005 memprintf(err,
1006 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1007 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001008
1009 if (ptr == PARSE_TIME_OVER)
1010 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1011 else if (ptr == PARSE_TIME_UNDER)
1012 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1013 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001014 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1015 return -1;
1016 }
1017
1018 if (curpx->tcp_rep.inspect_delay) {
1019 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1020 args[0], args[1], proxy_type_str(curpx), curpx->id);
1021 return 1;
1022 }
1023 curpx->tcp_rep.inspect_delay = val;
1024 return 0;
1025 }
1026
1027 rule = calloc(1, sizeof(*rule));
1028 LIST_INIT(&rule->list);
1029 arg = 1;
1030 where = 0;
1031
1032 if (strcmp(args[1], "content") == 0) {
1033 arg++;
1034
1035 if (curpx->cap & PR_CAP_FE)
1036 where |= SMP_VAL_FE_RES_CNT;
1037 if (curpx->cap & PR_CAP_BE)
1038 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001039 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001040 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1041 goto error;
1042
1043 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1044 if (acl) {
1045 if (acl->name && *acl->name)
1046 memprintf(err,
1047 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1048 acl->name, args[0], args[1], sample_ckp_names(where));
1049 else
1050 memprintf(err,
1051 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1052 args[0], args[1],
1053 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1054 sample_ckp_names(where));
1055
1056 warn++;
1057 }
1058 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1059 if (acl->name && *acl->name)
1060 memprintf(err,
1061 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1062 acl->name, kw, sample_ckp_names(where));
1063 else
1064 memprintf(err,
1065 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1066 kw, sample_ckp_names(where));
1067 warn++;
1068 }
1069
1070 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1071 }
1072 else {
1073 memprintf(err,
1074 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1075 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1076 goto error;
1077 }
1078
1079 return warn;
1080 error:
1081 free(rule);
1082 return -1;
1083}
1084
1085
1086/* This function should be called to parse a line starting with the "tcp-request"
1087 * keyword.
1088 */
1089static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1090 struct proxy *defpx, const char *file, int line,
1091 char **err)
1092{
1093 const char *ptr = NULL;
1094 unsigned int val;
1095 int warn = 0;
1096 int arg;
1097 struct act_rule *rule;
1098 unsigned int where;
1099 const struct acl *acl;
1100 const char *kw;
1101
1102 if (!*args[1]) {
1103 if (curpx == defpx)
1104 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1105 else
1106 memprintf(err, "missing argument for '%s' in %s '%s'",
1107 args[0], proxy_type_str(curpx), curpx->id);
1108 return -1;
1109 }
1110
1111 if (!strcmp(args[1], "inspect-delay")) {
1112 if (curpx == defpx) {
1113 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1114 args[0], args[1]);
1115 return -1;
1116 }
1117
1118 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1119 memprintf(err,
1120 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1121 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001122
1123 if (ptr == PARSE_TIME_OVER)
1124 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1125 else if (ptr == PARSE_TIME_UNDER)
1126 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1127 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001128 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1129 return -1;
1130 }
1131
1132 if (curpx->tcp_req.inspect_delay) {
1133 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1134 args[0], args[1], proxy_type_str(curpx), curpx->id);
1135 return 1;
1136 }
1137 curpx->tcp_req.inspect_delay = val;
1138 return 0;
1139 }
1140
1141 rule = calloc(1, sizeof(*rule));
1142 LIST_INIT(&rule->list);
1143 arg = 1;
1144 where = 0;
1145
1146 if (strcmp(args[1], "content") == 0) {
1147 arg++;
1148
1149 if (curpx->cap & PR_CAP_FE)
1150 where |= SMP_VAL_FE_REQ_CNT;
1151 if (curpx->cap & PR_CAP_BE)
1152 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001153 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001154 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1155 goto error;
1156
1157 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1158 if (acl) {
1159 if (acl->name && *acl->name)
1160 memprintf(err,
1161 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1162 acl->name, args[0], args[1], sample_ckp_names(where));
1163 else
1164 memprintf(err,
1165 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1166 args[0], args[1],
1167 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1168 sample_ckp_names(where));
1169
1170 warn++;
1171 }
1172 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1173 if (acl->name && *acl->name)
1174 memprintf(err,
1175 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1176 acl->name, kw, sample_ckp_names(where));
1177 else
1178 memprintf(err,
1179 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1180 kw, sample_ckp_names(where));
1181 warn++;
1182 }
1183
1184 /* the following function directly emits the warning */
1185 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1186 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1187 }
1188 else if (strcmp(args[1], "connection") == 0) {
1189 arg++;
1190
1191 if (!(curpx->cap & PR_CAP_FE)) {
1192 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1193 args[0], args[1], proxy_type_str(curpx), curpx->id);
1194 goto error;
1195 }
1196
1197 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001198 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001199 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1200 goto error;
1201
1202 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1203 if (acl) {
1204 if (acl->name && *acl->name)
1205 memprintf(err,
1206 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1207 acl->name, args[0], args[1], sample_ckp_names(where));
1208 else
1209 memprintf(err,
1210 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1211 args[0], args[1],
1212 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1213 sample_ckp_names(where));
1214
1215 warn++;
1216 }
1217 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1218 if (acl->name && *acl->name)
1219 memprintf(err,
1220 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1221 acl->name, kw, sample_ckp_names(where));
1222 else
1223 memprintf(err,
1224 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1225 kw, sample_ckp_names(where));
1226 warn++;
1227 }
1228
1229 /* the following function directly emits the warning */
1230 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1231 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1232 }
1233 else if (strcmp(args[1], "session") == 0) {
1234 arg++;
1235
1236 if (!(curpx->cap & PR_CAP_FE)) {
1237 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1238 args[0], args[1], proxy_type_str(curpx), curpx->id);
1239 goto error;
1240 }
1241
1242 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001243 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001244 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1245 goto error;
1246
1247 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1248 if (acl) {
1249 if (acl->name && *acl->name)
1250 memprintf(err,
1251 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1252 acl->name, args[0], args[1], sample_ckp_names(where));
1253 else
1254 memprintf(err,
1255 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1256 args[0], args[1],
1257 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1258 sample_ckp_names(where));
1259 warn++;
1260 }
1261 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1262 if (acl->name && *acl->name)
1263 memprintf(err,
1264 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1265 acl->name, kw, sample_ckp_names(where));
1266 else
1267 memprintf(err,
1268 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1269 kw, sample_ckp_names(where));
1270 warn++;
1271 }
1272
1273 /* the following function directly emits the warning */
1274 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1275 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1276 }
1277 else {
1278 if (curpx == defpx)
1279 memprintf(err,
1280 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1281 args[0], args[1]);
1282 else
1283 memprintf(err,
1284 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1285 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1286 goto error;
1287 }
1288
1289 return warn;
1290 error:
1291 free(rule);
1292 return -1;
1293}
1294
1295static struct cfg_kw_list cfg_kws = {ILH, {
1296 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1297 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1298 { 0, NULL, NULL },
1299}};
1300
Willy Tarreau0108d902018-11-25 19:14:37 +01001301INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001302
1303/*
1304 * Local variables:
1305 * c-indent-level: 8
1306 * c-basic-offset: 8
1307 * End:
1308 */