blob: ebf4e05c72366956b49017e6f1dbb24d2dfc1cd9 [file] [log] [blame]
Willy Tarreau39713102016-11-25 15:49:32 +01001/*
2 * "tcp" rules processing
3 *
4 * Copyright 2000-2016 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12#include <common/cfgparse.h>
13#include <common/compat.h>
14#include <common/config.h>
15#include <common/debug.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010016#include <common/initcall.h>
Willy Tarreau39713102016-11-25 15:49:32 +010017#include <common/mini-clist.h>
18#include <common/standard.h>
19#include <common/ticks.h>
20#include <common/time.h>
21
22#include <types/arg.h>
23#include <types/capture.h>
24#include <types/connection.h>
25#include <types/global.h>
26
27#include <proto/acl.h>
28#include <proto/action.h>
29#include <proto/channel.h>
30#include <proto/connection.h>
31#include <proto/log.h>
32#include <proto/proxy.h>
33#include <proto/sample.h>
34#include <proto/stick_table.h>
35#include <proto/stream.h>
36#include <proto/stream_interface.h>
37#include <proto/tcp_rules.h>
38
Christopher Fauleteea8fc72019-11-05 16:18:10 +010039#define TRACE_SOURCE &trace_strm
40
Willy Tarreau39713102016-11-25 15:49:32 +010041/* List head of all known action keywords for "tcp-request connection" */
42struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
43struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
44struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
45struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
46
47/*
48 * Register keywords.
49 */
50void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
51{
52 LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
53}
54
55void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
56{
57 LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
58}
59
60void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
61{
62 LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
63}
64
65void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
66{
67 LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
68}
69
70/*
71 * Return the struct tcp_req_action_kw associated to a keyword.
72 */
73static struct action_kw *tcp_req_conn_action(const char *kw)
74{
75 return action_lookup(&tcp_req_conn_keywords, kw);
76}
77
78static struct action_kw *tcp_req_sess_action(const char *kw)
79{
80 return action_lookup(&tcp_req_sess_keywords, kw);
81}
82
83static struct action_kw *tcp_req_cont_action(const char *kw)
84{
85 return action_lookup(&tcp_req_cont_keywords, kw);
86}
87
88static struct action_kw *tcp_res_cont_action(const char *kw)
89{
90 return action_lookup(&tcp_res_cont_keywords, kw);
91}
92
93/* This function performs the TCP request analysis on the current request. It
94 * returns 1 if the processing can continue on next analysers, or zero if it
95 * needs more data, encounters an error, or wants to immediately abort the
96 * request. It relies on buffers flags, and updates s->req->analysers. The
97 * function may be called for frontend rules and backend rules. It only relies
98 * on the backend pointer so this works for both cases.
99 */
100int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
101{
102 struct session *sess = s->sess;
103 struct act_rule *rule;
104 struct stksess *ts;
105 struct stktable *t;
106 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100107 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100108
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100109 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100110
111 /* We don't know whether we have enough data, so must proceed
112 * this way :
113 * - iterate through all rules in their declaration order
114 * - if one rule returns MISS, it means the inspect delay is
115 * not over yet, then return immediately, otherwise consider
116 * it as a non-match.
117 * - if one rule returns OK, then return OK
118 * - if one rule returns KO, then return KO
119 */
120
Willy Tarreau23752332018-06-15 14:54:53 +0200121 if ((req->flags & CF_SHUTR) || channel_full(req, global.tune.maxrewrite) ||
Willy Tarreau39713102016-11-25 15:49:32 +0100122 !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
123 partial = SMP_OPT_FINAL;
124 else
125 partial = 0;
126
127 /* If "the current_rule_list" match the executed rule list, we are in
128 * resume condition. If a resume is needed it is always in the action
129 * and never in the ACL or converters. In this case, we initialise the
130 * current rule, and go to the action execution point.
131 */
132 if (s->current_rule) {
133 rule = s->current_rule;
134 s->current_rule = NULL;
135 if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
136 goto resume_execution;
137 }
138 s->current_rule_list = &s->be->tcp_req.inspect_rules;
139
140 list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
141 enum acl_test_res ret = ACL_TEST_PASS;
142
143 if (rule->cond) {
144 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
145 if (ret == ACL_TEST_MISS)
146 goto missing_data;
147
148 ret = acl_pass(ret);
149 if (rule->cond->pol == ACL_COND_UNLESS)
150 ret = !ret;
151 }
152
153 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100154 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100155resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100156
157 /* Always call the action function if defined */
158 if (rule->action_ptr) {
159 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100160 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100161
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100162 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100163 case ACT_RET_CONT:
164 break;
165 case ACT_RET_STOP:
166 case ACT_RET_DONE:
167 goto end;
168 case ACT_RET_YIELD:
169 s->current_rule = rule;
170 goto missing_data;
171 case ACT_RET_DENY:
172 goto deny;
173 case ACT_RET_ABRT:
174 goto abort;
175 case ACT_RET_ERR:
176 goto internal;
177 case ACT_RET_INV:
178 goto invalid;
179 }
180 continue; /* eval the next rule */
181 }
182
183 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100184 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100185 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100186 }
187 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100188 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100189 }
190 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
191 /* Note: only the first valid tracking parameter of each
192 * applies.
193 */
194 struct stktable_key *key;
195 struct sample smp;
196
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200197 if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100198 continue;
199
200 t = rule->arg.trk_ctr.table.t;
201 key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
202
203 if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
204 goto missing_data; /* key might appear later */
205
206 if (key && (ts = stktable_get_entry(t, key))) {
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200207 stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
208 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
Willy Tarreau39713102016-11-25 15:49:32 +0100209 if (sess->fe != s->be)
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200210 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
Willy Tarreau39713102016-11-25 15:49:32 +0100211 }
212 }
213 else if (rule->action == ACT_TCP_CAPTURE) {
214 struct sample *key;
215 struct cap_hdr *h = rule->arg.cap.hdr;
216 char **cap = s->req_cap;
217 int len;
218
219 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
220 if (!key)
221 continue;
222
223 if (key->flags & SMP_F_MAY_CHANGE)
224 goto missing_data;
225
226 if (cap[h->index] == NULL)
Willy Tarreaubafbe012017-11-24 17:34:44 +0100227 cap[h->index] = pool_alloc(h->pool);
Willy Tarreau39713102016-11-25 15:49:32 +0100228
229 if (cap[h->index] == NULL) /* no more capture memory */
230 continue;
231
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200232 len = key->data.u.str.data;
Willy Tarreau39713102016-11-25 15:49:32 +0100233 if (len > h->len)
234 len = h->len;
235
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200236 memcpy(cap[h->index], key->data.u.str.area,
237 len);
Willy Tarreau39713102016-11-25 15:49:32 +0100238 cap[h->index][len] = 0;
239 }
Willy Tarreau39713102016-11-25 15:49:32 +0100240 }
241 }
242
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100243 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100244 /* if we get there, it means we have no rule which matches, or
245 * we have an explicit accept, so we apply the default accept.
246 */
247 req->analysers &= ~an_bit;
248 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100249 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100250 return 1;
251
252 missing_data:
253 channel_dont_connect(req);
254 /* just set the request timeout once at the beginning of the request */
255 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
256 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100257 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100258 return 0;
259
Christopher Faulet282992e2019-12-16 12:34:31 +0100260 deny:
261 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
262 if (sess->listener && sess->listener->counters)
263 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
264 goto reject;
265
266 internal:
267 _HA_ATOMIC_ADD(&sess->fe->fe_counters.internal_errors, 1);
268 if (sess->listener && sess->listener->counters)
269 _HA_ATOMIC_ADD(&sess->listener->counters->internal_errors, 1);
270 if (!(s->flags & SF_ERR_MASK))
271 s->flags |= SF_ERR_INTERNAL;
272 goto reject;
273
274 invalid:
275 _HA_ATOMIC_ADD(&sess->fe->fe_counters.failed_req, 1);
276 if (sess->listener && sess->listener->counters)
277 _HA_ATOMIC_ADD(&sess->listener->counters->failed_req, 1);
278
279 reject:
280 si_must_kill_conn(chn_prod(req));
281 channel_abort(req);
282 channel_abort(&s->res);
283
284 abort:
285 req->analysers &= AN_REQ_FLT_END;
286
287 if (!(s->flags & SF_ERR_MASK))
288 s->flags |= SF_ERR_PRXCOND;
289 if (!(s->flags & SF_FINST_MASK))
290 s->flags |= SF_FINST_R;
291 DBG_TRACE_DEVEL("leaving on error|deny|abort", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
292 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100293}
294
295/* This function performs the TCP response analysis on the current response. It
296 * returns 1 if the processing can continue on next analysers, or zero if it
297 * needs more data, encounters an error, or wants to immediately abort the
298 * response. It relies on buffers flags, and updates s->rep->analysers. The
299 * function may be called for backend rules.
300 */
301int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
302{
303 struct session *sess = s->sess;
304 struct act_rule *rule;
305 int partial;
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100306 int act_opts = 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100307
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100308 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100309
310 /* We don't know whether we have enough data, so must proceed
311 * this way :
312 * - iterate through all rules in their declaration order
313 * - if one rule returns MISS, it means the inspect delay is
314 * not over yet, then return immediately, otherwise consider
315 * it as a non-match.
316 * - if one rule returns OK, then return OK
317 * - if one rule returns KO, then return KO
318 */
319
320 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
321 partial = SMP_OPT_FINAL;
322 else
323 partial = 0;
324
325 /* If "the current_rule_list" match the executed rule list, we are in
326 * resume condition. If a resume is needed it is always in the action
327 * and never in the ACL or converters. In this case, we initialise the
328 * current rule, and go to the action execution point.
329 */
330 if (s->current_rule) {
331 rule = s->current_rule;
332 s->current_rule = NULL;
333 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
334 goto resume_execution;
335 }
336 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
337
338 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
339 enum acl_test_res ret = ACL_TEST_PASS;
340
341 if (rule->cond) {
342 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
343 if (ret == ACL_TEST_MISS) {
344 /* just set the analyser timeout once at the beginning of the response */
345 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
346 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100347 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100348 return 0;
349 }
350
351 ret = acl_pass(ret);
352 if (rule->cond->pol == ACL_COND_UNLESS)
353 ret = !ret;
354 }
355
356 if (ret) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100357 act_opts |= ACT_OPT_FIRST;
Willy Tarreau39713102016-11-25 15:49:32 +0100358resume_execution:
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100359 /* Always call the action function if defined */
360 if (rule->action_ptr) {
361 if (partial & SMP_OPT_FINAL)
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100362 act_opts |= ACT_OPT_FINAL;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100363
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100364 switch (rule->action_ptr(rule, s->be, s->sess, s, act_opts)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100365 case ACT_RET_CONT:
366 break;
367 case ACT_RET_STOP:
368 case ACT_RET_DONE:
369 goto end;
370 case ACT_RET_YIELD:
371 s->current_rule = rule;
372 goto missing_data;
373 case ACT_RET_DENY:
374 goto deny;
375 case ACT_RET_ABRT:
376 goto abort;
377 case ACT_RET_ERR:
378 goto internal;
379 case ACT_RET_INV:
380 goto invalid;
381 }
382 continue; /* eval the next rule */
383 }
384
385 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100386 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100387 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100388 }
389 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100390 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100391 }
392 else if (rule->action == ACT_TCP_CLOSE) {
393 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100394 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100395 si_shutr(chn_prod(rep));
396 si_shutw(chn_prod(rep));
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100397 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100398 }
399 }
400 }
401
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100402 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100403 /* if we get there, it means we have no rule which matches, or
404 * we have an explicit accept, so we apply the default accept.
405 */
406 rep->analysers &= ~an_bit;
407 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100408 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100409 return 1;
Christopher Faulet282992e2019-12-16 12:34:31 +0100410
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100411 missing_data:
412 channel_dont_close(rep);
413 s->current_rule = rule;
414 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
415 return 0;
416
Christopher Faulet282992e2019-12-16 12:34:31 +0100417 deny:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100418 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100419 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100420 if (s->sess->listener->counters)
421 _HA_ATOMIC_ADD(&s->sess->listener->counters->denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100422 if (objt_server(s->target))
423 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
424 goto reject;
425
426 internal:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100427 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100428 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100429 if (s->sess->listener->counters)
430 _HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100431 if (objt_server(s->target))
432 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
433 if (!(s->flags & SF_ERR_MASK))
434 s->flags |= SF_ERR_INTERNAL;
435 goto reject;
436
437 invalid:
438 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
439 if (objt_server(s->target))
440 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
441
442 reject:
443 si_must_kill_conn(chn_prod(rep));
444 channel_abort(rep);
445 channel_abort(&s->req);
446
447 abort:
448 rep->analysers &= AN_REQ_FLT_END;
449
450 if (!(s->flags & SF_ERR_MASK))
451 s->flags |= SF_ERR_PRXCOND;
452 if (!(s->flags & SF_FINST_MASK))
453 s->flags |= SF_FINST_D;
454 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
455 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100456}
457
458
459/* This function performs the TCP layer4 analysis on the current request. It
460 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
461 * matches or if no more rule matches. It can only use rules which don't need
462 * any data. This only works on connection-based client-facing stream interfaces.
463 */
464int tcp_exec_l4_rules(struct session *sess)
465{
466 struct act_rule *rule;
467 struct stksess *ts;
468 struct stktable *t = NULL;
469 struct connection *conn = objt_conn(sess->origin);
470 int result = 1;
471 enum acl_test_res ret;
472
473 if (!conn)
474 return result;
475
476 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
477 ret = ACL_TEST_PASS;
478
479 if (rule->cond) {
480 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
481 ret = acl_pass(ret);
482 if (rule->cond->pol == ACL_COND_UNLESS)
483 ret = !ret;
484 }
485
486 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100487 /* Always call the action function if defined */
488 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100489 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100490 case ACT_RET_YIELD:
491 /* yield is not allowed at this point. If this return code is
492 * used it is a bug, so I prefer to abort the process.
493 */
494 send_log(sess->fe, LOG_WARNING,
495 "Internal error: yield not allowed with tcp-request connection actions.");
496 /* fall through */
497 case ACT_RET_STOP:
498 case ACT_RET_DONE:
499 goto end;
500 case ACT_RET_CONT:
501 break;
502 case ACT_RET_DENY:
503 case ACT_RET_ABRT:
504 case ACT_RET_ERR:
505 case ACT_RET_INV:
506 result = 0;
507 goto end;
508 }
509 continue; /* eval the next rule */
510 }
511
512 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100513 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100514 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100515 }
516 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100517 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100518 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100519 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100520
521 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100522 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100523 }
524 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
525 /* Note: only the first valid tracking parameter of each
526 * applies.
527 */
528 struct stktable_key *key;
529
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200530 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100531 continue;
532
533 t = rule->arg.trk_ctr.table.t;
534 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
535
536 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200537 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100538 }
539 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200540 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
541 if (xprt_add_hs(conn) < 0) {
542 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100543 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200544 }
545 }
Willy Tarreau39713102016-11-25 15:49:32 +0100546 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100547 }
548 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200549 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
550 if (xprt_add_hs(conn) < 0) {
551 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100552 goto end;
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200553 }
554 }
Willy Tarreau39713102016-11-25 15:49:32 +0100555 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100556 }
Willy Tarreau39713102016-11-25 15:49:32 +0100557 }
558 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100559 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100560 return result;
561}
562
563/* This function performs the TCP layer5 analysis on the current request. It
564 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
565 * matches or if no more rule matches. It can only use rules which don't need
566 * any data. This only works on session-based client-facing stream interfaces.
567 * An example of valid use case is to track a stick-counter on the source
568 * address extracted from the proxy protocol.
569 */
570int tcp_exec_l5_rules(struct session *sess)
571{
572 struct act_rule *rule;
573 struct stksess *ts;
574 struct stktable *t = NULL;
575 int result = 1;
576 enum acl_test_res ret;
577
578 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
579 ret = ACL_TEST_PASS;
580
581 if (rule->cond) {
582 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
583 ret = acl_pass(ret);
584 if (rule->cond->pol == ACL_COND_UNLESS)
585 ret = !ret;
586 }
587
588 if (ret) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100589 /* Always call the action function if defined */
590 if (rule->action_ptr) {
Christopher Faulet105ba6c2019-12-18 14:41:51 +0100591 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_OPT_FINAL | ACT_OPT_FIRST)) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100592 case ACT_RET_YIELD:
593 /* yield is not allowed at this point. If this return code is
594 * used it is a bug, so I prefer to abort the process.
595 */
596 send_log(sess->fe, LOG_WARNING,
597 "Internal error: yield not allowed with tcp-request session actions.");
598 /* fall through */
599 case ACT_RET_STOP:
600 case ACT_RET_DONE:
601 goto end;
602 case ACT_RET_CONT:
603 break;
604 case ACT_RET_DENY:
605 case ACT_RET_ABRT:
606 case ACT_RET_ERR:
607 case ACT_RET_INV:
608 result = 0;
609 goto end;
610 }
611 continue; /* eval the next rule */
612 }
613
614 /* If not action function defined, check for known actions */
Willy Tarreau39713102016-11-25 15:49:32 +0100615 if (rule->action == ACT_ACTION_ALLOW) {
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100616 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100617 }
618 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100619 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100620 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100621 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100622
623 result = 0;
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100624 goto end;
Willy Tarreau39713102016-11-25 15:49:32 +0100625 }
626 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
627 /* Note: only the first valid tracking parameter of each
628 * applies.
629 */
630 struct stktable_key *key;
631
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200632 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100633 continue;
634
635 t = rule->arg.trk_ctr.table.t;
636 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
637
638 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200639 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100640 }
Willy Tarreau39713102016-11-25 15:49:32 +0100641 }
642 }
Christopher Fauletcd26e8a2019-12-18 11:13:39 +0100643 end:
Willy Tarreau39713102016-11-25 15:49:32 +0100644 return result;
645}
646
647/* Parse a tcp-response rule. Return a negative value in case of failure */
648static int tcp_parse_response_rule(char **args, int arg, int section_type,
649 struct proxy *curpx, struct proxy *defpx,
650 struct act_rule *rule, char **err,
651 unsigned int where,
652 const char *file, int line)
653{
654 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
655 memprintf(err, "%s %s is only allowed in 'backend' sections",
656 args[0], args[1]);
657 return -1;
658 }
659
660 if (strcmp(args[arg], "accept") == 0) {
661 arg++;
662 rule->action = ACT_ACTION_ALLOW;
663 }
664 else if (strcmp(args[arg], "reject") == 0) {
665 arg++;
666 rule->action = ACT_ACTION_DENY;
667 }
668 else if (strcmp(args[arg], "close") == 0) {
669 arg++;
670 rule->action = ACT_TCP_CLOSE;
671 }
672 else {
673 struct action_kw *kw;
674 kw = tcp_res_cont_action(args[arg]);
675 if (kw) {
676 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100677 rule->kw = kw;
678 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
679 return -1;
680 } else {
681 action_build_list(&tcp_res_cont_keywords, &trash);
682 memprintf(err,
683 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200684 args[0], args[1], trash.area,
685 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100686 return -1;
687 }
688 }
689
690 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200691 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100692 memprintf(err,
693 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
694 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
695 return -1;
696 }
697 }
698 else if (*args[arg]) {
699 memprintf(err,
700 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
701 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
702 return -1;
703 }
704 return 0;
705}
706
707
708
709/* Parse a tcp-request rule. Return a negative value in case of failure */
710static int tcp_parse_request_rule(char **args, int arg, int section_type,
711 struct proxy *curpx, struct proxy *defpx,
712 struct act_rule *rule, char **err,
713 unsigned int where, const char *file, int line)
714{
715 if (curpx == defpx) {
716 memprintf(err, "%s %s is not allowed in 'defaults' sections",
717 args[0], args[1]);
718 return -1;
719 }
720
721 if (!strcmp(args[arg], "accept")) {
722 arg++;
723 rule->action = ACT_ACTION_ALLOW;
724 }
725 else if (!strcmp(args[arg], "reject")) {
726 arg++;
727 rule->action = ACT_ACTION_DENY;
728 }
729 else if (strcmp(args[arg], "capture") == 0) {
730 struct sample_expr *expr;
731 struct cap_hdr *hdr;
732 int kw = arg;
733 int len = 0;
734
735 if (!(curpx->cap & PR_CAP_FE)) {
736 memprintf(err,
737 "'%s %s %s' : proxy '%s' has no frontend capability",
738 args[0], args[1], args[kw], curpx->id);
739 return -1;
740 }
741
742 if (!(where & SMP_VAL_FE_REQ_CNT)) {
743 memprintf(err,
744 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
745 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
746 return -1;
747 }
748
749 arg++;
750
751 curpx->conf.args.ctx = ARGC_CAP;
752 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
753 if (!expr) {
754 memprintf(err,
755 "'%s %s %s' : %s",
756 args[0], args[1], args[kw], *err);
757 return -1;
758 }
759
760 if (!(expr->fetch->val & where)) {
761 memprintf(err,
762 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
763 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
764 free(expr);
765 return -1;
766 }
767
768 if (strcmp(args[arg], "len") == 0) {
769 arg++;
770 if (!args[arg]) {
771 memprintf(err,
772 "'%s %s %s' : missing length value",
773 args[0], args[1], args[kw]);
774 free(expr);
775 return -1;
776 }
777 /* we copy the table name for now, it will be resolved later */
778 len = atoi(args[arg]);
779 if (len <= 0) {
780 memprintf(err,
781 "'%s %s %s' : length must be > 0",
782 args[0], args[1], args[kw]);
783 free(expr);
784 return -1;
785 }
786 arg++;
787 }
788
789 if (!len) {
790 memprintf(err,
791 "'%s %s %s' : a positive 'len' argument is mandatory",
792 args[0], args[1], args[kw]);
793 free(expr);
794 return -1;
795 }
796
797 hdr = calloc(1, sizeof(*hdr));
798 hdr->next = curpx->req_cap;
799 hdr->name = NULL; /* not a header capture */
800 hdr->namelen = 0;
801 hdr->len = len;
802 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
803 hdr->index = curpx->nb_req_cap++;
804
805 curpx->req_cap = hdr;
806 curpx->to_log |= LW_REQHDR;
807
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200808 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100809 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
810
811 rule->arg.cap.expr = expr;
812 rule->arg.cap.hdr = hdr;
813 rule->action = ACT_TCP_CAPTURE;
814 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100815 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100816 struct sample_expr *expr;
817 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100818 unsigned int tsc_num;
819 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100820
821 arg++;
822
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100823 tsc_num_str = &args[kw][8];
824 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
825 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
826 return -1;
827 }
828
Willy Tarreau39713102016-11-25 15:49:32 +0100829 curpx->conf.args.ctx = ARGC_TRK;
830 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
831 if (!expr) {
832 memprintf(err,
833 "'%s %s %s' : %s",
834 args[0], args[1], args[kw], *err);
835 return -1;
836 }
837
838 if (!(expr->fetch->val & where)) {
839 memprintf(err,
840 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
841 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
842 free(expr);
843 return -1;
844 }
845
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200846 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100847 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
848
849 if (strcmp(args[arg], "table") == 0) {
850 arg++;
851 if (!args[arg]) {
852 memprintf(err,
853 "'%s %s %s' : missing table name",
854 args[0], args[1], args[kw]);
855 free(expr);
856 return -1;
857 }
858 /* we copy the table name for now, it will be resolved later */
859 rule->arg.trk_ctr.table.n = strdup(args[arg]);
860 arg++;
861 }
862 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100863 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200864 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100865 }
866 else if (strcmp(args[arg], "expect-proxy") == 0) {
867 if (strcmp(args[arg+1], "layer4") != 0) {
868 memprintf(err,
869 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
870 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
871 return -1;
872 }
873
874 if (!(where & SMP_VAL_FE_CON_ACC)) {
875 memprintf(err,
876 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
877 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
878 return -1;
879 }
880
881 arg += 2;
882 rule->action = ACT_TCP_EXPECT_PX;
883 }
884 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
885 if (strcmp(args[arg+1], "layer4") != 0) {
886 memprintf(err,
887 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
888 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
889 return -1;
890 }
891
892 if (!(where & SMP_VAL_FE_CON_ACC)) {
893 memprintf(err,
894 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
895 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
896 return -1;
897 }
898
899 arg += 2;
900 rule->action = ACT_TCP_EXPECT_CIP;
901 }
902 else {
903 struct action_kw *kw;
904 if (where & SMP_VAL_FE_CON_ACC) {
905 /* L4 */
906 kw = tcp_req_conn_action(args[arg]);
907 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100908 } else if (where & SMP_VAL_FE_SES_ACC) {
909 /* L5 */
910 kw = tcp_req_sess_action(args[arg]);
911 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100912 } else {
913 /* L6 */
914 kw = tcp_req_cont_action(args[arg]);
915 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100916 }
917 if (kw) {
918 arg++;
919 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
920 return -1;
921 } else {
922 if (where & SMP_VAL_FE_CON_ACC)
923 action_build_list(&tcp_req_conn_keywords, &trash);
924 else if (where & SMP_VAL_FE_SES_ACC)
925 action_build_list(&tcp_req_sess_keywords, &trash);
926 else
927 action_build_list(&tcp_req_cont_keywords, &trash);
928 memprintf(err,
929 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
930 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200931 args[0], args[1], MAX_SESS_STKCTR-1,
932 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100933 curpx->id, args[arg]);
934 return -1;
935 }
936 }
937
938 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200939 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100940 memprintf(err,
941 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
942 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
943 return -1;
944 }
945 }
946 else if (*args[arg]) {
947 memprintf(err,
948 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
949 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
950 return -1;
951 }
952 return 0;
953}
954
955/* This function should be called to parse a line starting with the "tcp-response"
956 * keyword.
957 */
958static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
959 struct proxy *defpx, const char *file, int line,
960 char **err)
961{
962 const char *ptr = NULL;
963 unsigned int val;
964 int warn = 0;
965 int arg;
966 struct act_rule *rule;
967 unsigned int where;
968 const struct acl *acl;
969 const char *kw;
970
971 if (!*args[1]) {
972 memprintf(err, "missing argument for '%s' in %s '%s'",
973 args[0], proxy_type_str(curpx), curpx->id);
974 return -1;
975 }
976
977 if (strcmp(args[1], "inspect-delay") == 0) {
978 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
979 memprintf(err, "%s %s is only allowed in 'backend' sections",
980 args[0], args[1]);
981 return -1;
982 }
983
984 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
985 memprintf(err,
986 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
987 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200988
989 if (ptr == PARSE_TIME_OVER)
990 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
991 else if (ptr == PARSE_TIME_UNDER)
992 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
993 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100994 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
995 return -1;
996 }
997
998 if (curpx->tcp_rep.inspect_delay) {
999 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1000 args[0], args[1], proxy_type_str(curpx), curpx->id);
1001 return 1;
1002 }
1003 curpx->tcp_rep.inspect_delay = val;
1004 return 0;
1005 }
1006
1007 rule = calloc(1, sizeof(*rule));
1008 LIST_INIT(&rule->list);
1009 arg = 1;
1010 where = 0;
1011
1012 if (strcmp(args[1], "content") == 0) {
1013 arg++;
1014
1015 if (curpx->cap & PR_CAP_FE)
1016 where |= SMP_VAL_FE_RES_CNT;
1017 if (curpx->cap & PR_CAP_BE)
1018 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001019 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001020 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1021 goto error;
1022
1023 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1024 if (acl) {
1025 if (acl->name && *acl->name)
1026 memprintf(err,
1027 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1028 acl->name, args[0], args[1], sample_ckp_names(where));
1029 else
1030 memprintf(err,
1031 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1032 args[0], args[1],
1033 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1034 sample_ckp_names(where));
1035
1036 warn++;
1037 }
1038 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1039 if (acl->name && *acl->name)
1040 memprintf(err,
1041 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1042 acl->name, kw, sample_ckp_names(where));
1043 else
1044 memprintf(err,
1045 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1046 kw, sample_ckp_names(where));
1047 warn++;
1048 }
1049
1050 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1051 }
1052 else {
1053 memprintf(err,
1054 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1055 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1056 goto error;
1057 }
1058
1059 return warn;
1060 error:
1061 free(rule);
1062 return -1;
1063}
1064
1065
1066/* This function should be called to parse a line starting with the "tcp-request"
1067 * keyword.
1068 */
1069static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1070 struct proxy *defpx, const char *file, int line,
1071 char **err)
1072{
1073 const char *ptr = NULL;
1074 unsigned int val;
1075 int warn = 0;
1076 int arg;
1077 struct act_rule *rule;
1078 unsigned int where;
1079 const struct acl *acl;
1080 const char *kw;
1081
1082 if (!*args[1]) {
1083 if (curpx == defpx)
1084 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1085 else
1086 memprintf(err, "missing argument for '%s' in %s '%s'",
1087 args[0], proxy_type_str(curpx), curpx->id);
1088 return -1;
1089 }
1090
1091 if (!strcmp(args[1], "inspect-delay")) {
1092 if (curpx == defpx) {
1093 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1094 args[0], args[1]);
1095 return -1;
1096 }
1097
1098 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1099 memprintf(err,
1100 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1101 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001102
1103 if (ptr == PARSE_TIME_OVER)
1104 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1105 else if (ptr == PARSE_TIME_UNDER)
1106 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1107 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001108 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1109 return -1;
1110 }
1111
1112 if (curpx->tcp_req.inspect_delay) {
1113 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1114 args[0], args[1], proxy_type_str(curpx), curpx->id);
1115 return 1;
1116 }
1117 curpx->tcp_req.inspect_delay = val;
1118 return 0;
1119 }
1120
1121 rule = calloc(1, sizeof(*rule));
1122 LIST_INIT(&rule->list);
1123 arg = 1;
1124 where = 0;
1125
1126 if (strcmp(args[1], "content") == 0) {
1127 arg++;
1128
1129 if (curpx->cap & PR_CAP_FE)
1130 where |= SMP_VAL_FE_REQ_CNT;
1131 if (curpx->cap & PR_CAP_BE)
1132 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001133 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001134 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1135 goto error;
1136
1137 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1138 if (acl) {
1139 if (acl->name && *acl->name)
1140 memprintf(err,
1141 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1142 acl->name, args[0], args[1], sample_ckp_names(where));
1143 else
1144 memprintf(err,
1145 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1146 args[0], args[1],
1147 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1148 sample_ckp_names(where));
1149
1150 warn++;
1151 }
1152 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1153 if (acl->name && *acl->name)
1154 memprintf(err,
1155 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1156 acl->name, kw, sample_ckp_names(where));
1157 else
1158 memprintf(err,
1159 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1160 kw, sample_ckp_names(where));
1161 warn++;
1162 }
1163
1164 /* the following function directly emits the warning */
1165 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1166 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1167 }
1168 else if (strcmp(args[1], "connection") == 0) {
1169 arg++;
1170
1171 if (!(curpx->cap & PR_CAP_FE)) {
1172 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1173 args[0], args[1], proxy_type_str(curpx), curpx->id);
1174 goto error;
1175 }
1176
1177 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001178 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001179 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1180 goto error;
1181
1182 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1183 if (acl) {
1184 if (acl->name && *acl->name)
1185 memprintf(err,
1186 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1187 acl->name, args[0], args[1], sample_ckp_names(where));
1188 else
1189 memprintf(err,
1190 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1191 args[0], args[1],
1192 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1193 sample_ckp_names(where));
1194
1195 warn++;
1196 }
1197 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1198 if (acl->name && *acl->name)
1199 memprintf(err,
1200 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1201 acl->name, kw, sample_ckp_names(where));
1202 else
1203 memprintf(err,
1204 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1205 kw, sample_ckp_names(where));
1206 warn++;
1207 }
1208
1209 /* the following function directly emits the warning */
1210 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1211 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1212 }
1213 else if (strcmp(args[1], "session") == 0) {
1214 arg++;
1215
1216 if (!(curpx->cap & PR_CAP_FE)) {
1217 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1218 args[0], args[1], proxy_type_str(curpx), curpx->id);
1219 goto error;
1220 }
1221
1222 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001223 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001224 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1225 goto error;
1226
1227 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1228 if (acl) {
1229 if (acl->name && *acl->name)
1230 memprintf(err,
1231 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1232 acl->name, args[0], args[1], sample_ckp_names(where));
1233 else
1234 memprintf(err,
1235 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1236 args[0], args[1],
1237 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1238 sample_ckp_names(where));
1239 warn++;
1240 }
1241 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1242 if (acl->name && *acl->name)
1243 memprintf(err,
1244 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1245 acl->name, kw, sample_ckp_names(where));
1246 else
1247 memprintf(err,
1248 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1249 kw, sample_ckp_names(where));
1250 warn++;
1251 }
1252
1253 /* the following function directly emits the warning */
1254 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1255 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1256 }
1257 else {
1258 if (curpx == defpx)
1259 memprintf(err,
1260 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1261 args[0], args[1]);
1262 else
1263 memprintf(err,
1264 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1265 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1266 goto error;
1267 }
1268
1269 return warn;
1270 error:
1271 free(rule);
1272 return -1;
1273}
1274
1275static struct cfg_kw_list cfg_kws = {ILH, {
1276 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1277 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1278 { 0, NULL, NULL },
1279}};
1280
Willy Tarreau0108d902018-11-25 19:14:37 +01001281INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001282
1283/*
1284 * Local variables:
1285 * c-indent-level: 8
1286 * c-basic-offset: 8
1287 * End:
1288 */