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