blob: fae2e152bc95bc439a7a90cbfe4ed534717532f1 [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;
Christopher Faulet245cf792019-12-18 14:58:12 +0100663 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100664 }
665 else if (strcmp(args[arg], "reject") == 0) {
666 arg++;
667 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100668 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100669 }
670 else if (strcmp(args[arg], "close") == 0) {
671 arg++;
672 rule->action = ACT_TCP_CLOSE;
Christopher Faulet245cf792019-12-18 14:58:12 +0100673 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100674 }
675 else {
676 struct action_kw *kw;
677 kw = tcp_res_cont_action(args[arg]);
678 if (kw) {
679 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100680 rule->kw = kw;
681 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
682 return -1;
683 } else {
684 action_build_list(&tcp_res_cont_keywords, &trash);
685 memprintf(err,
686 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200687 args[0], args[1], trash.area,
688 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100689 return -1;
690 }
691 }
692
693 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200694 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100695 memprintf(err,
696 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
697 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
698 return -1;
699 }
700 }
701 else if (*args[arg]) {
702 memprintf(err,
703 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
704 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
705 return -1;
706 }
707 return 0;
708}
709
710
711
712/* Parse a tcp-request rule. Return a negative value in case of failure */
713static int tcp_parse_request_rule(char **args, int arg, int section_type,
714 struct proxy *curpx, struct proxy *defpx,
715 struct act_rule *rule, char **err,
716 unsigned int where, const char *file, int line)
717{
718 if (curpx == defpx) {
719 memprintf(err, "%s %s is not allowed in 'defaults' sections",
720 args[0], args[1]);
721 return -1;
722 }
723
724 if (!strcmp(args[arg], "accept")) {
725 arg++;
726 rule->action = ACT_ACTION_ALLOW;
Christopher Faulet245cf792019-12-18 14:58:12 +0100727 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100728 }
729 else if (!strcmp(args[arg], "reject")) {
730 arg++;
731 rule->action = ACT_ACTION_DENY;
Christopher Faulet245cf792019-12-18 14:58:12 +0100732 rule->flags |= ACT_FLAG_FINAL;
Willy Tarreau39713102016-11-25 15:49:32 +0100733 }
734 else if (strcmp(args[arg], "capture") == 0) {
735 struct sample_expr *expr;
736 struct cap_hdr *hdr;
737 int kw = arg;
738 int len = 0;
739
740 if (!(curpx->cap & PR_CAP_FE)) {
741 memprintf(err,
742 "'%s %s %s' : proxy '%s' has no frontend capability",
743 args[0], args[1], args[kw], curpx->id);
744 return -1;
745 }
746
747 if (!(where & SMP_VAL_FE_REQ_CNT)) {
748 memprintf(err,
749 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
750 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
751 return -1;
752 }
753
754 arg++;
755
756 curpx->conf.args.ctx = ARGC_CAP;
757 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
758 if (!expr) {
759 memprintf(err,
760 "'%s %s %s' : %s",
761 args[0], args[1], args[kw], *err);
762 return -1;
763 }
764
765 if (!(expr->fetch->val & where)) {
766 memprintf(err,
767 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
768 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
769 free(expr);
770 return -1;
771 }
772
773 if (strcmp(args[arg], "len") == 0) {
774 arg++;
775 if (!args[arg]) {
776 memprintf(err,
777 "'%s %s %s' : missing length value",
778 args[0], args[1], args[kw]);
779 free(expr);
780 return -1;
781 }
782 /* we copy the table name for now, it will be resolved later */
783 len = atoi(args[arg]);
784 if (len <= 0) {
785 memprintf(err,
786 "'%s %s %s' : length must be > 0",
787 args[0], args[1], args[kw]);
788 free(expr);
789 return -1;
790 }
791 arg++;
792 }
793
794 if (!len) {
795 memprintf(err,
796 "'%s %s %s' : a positive 'len' argument is mandatory",
797 args[0], args[1], args[kw]);
798 free(expr);
799 return -1;
800 }
801
802 hdr = calloc(1, sizeof(*hdr));
803 hdr->next = curpx->req_cap;
804 hdr->name = NULL; /* not a header capture */
805 hdr->namelen = 0;
806 hdr->len = len;
807 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
808 hdr->index = curpx->nb_req_cap++;
809
810 curpx->req_cap = hdr;
811 curpx->to_log |= LW_REQHDR;
812
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200813 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100814 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
815
816 rule->arg.cap.expr = expr;
817 rule->arg.cap.hdr = hdr;
818 rule->action = ACT_TCP_CAPTURE;
819 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100820 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100821 struct sample_expr *expr;
822 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100823 unsigned int tsc_num;
824 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100825
826 arg++;
827
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100828 tsc_num_str = &args[kw][8];
829 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
830 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
831 return -1;
832 }
833
Willy Tarreau39713102016-11-25 15:49:32 +0100834 curpx->conf.args.ctx = ARGC_TRK;
835 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
836 if (!expr) {
837 memprintf(err,
838 "'%s %s %s' : %s",
839 args[0], args[1], args[kw], *err);
840 return -1;
841 }
842
843 if (!(expr->fetch->val & where)) {
844 memprintf(err,
845 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
846 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
847 free(expr);
848 return -1;
849 }
850
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200851 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100852 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
853
854 if (strcmp(args[arg], "table") == 0) {
855 arg++;
856 if (!args[arg]) {
857 memprintf(err,
858 "'%s %s %s' : missing table name",
859 args[0], args[1], args[kw]);
860 free(expr);
861 return -1;
862 }
863 /* we copy the table name for now, it will be resolved later */
864 rule->arg.trk_ctr.table.n = strdup(args[arg]);
865 arg++;
866 }
867 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100868 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200869 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100870 }
871 else if (strcmp(args[arg], "expect-proxy") == 0) {
872 if (strcmp(args[arg+1], "layer4") != 0) {
873 memprintf(err,
874 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
875 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
876 return -1;
877 }
878
879 if (!(where & SMP_VAL_FE_CON_ACC)) {
880 memprintf(err,
881 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
882 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
883 return -1;
884 }
885
886 arg += 2;
887 rule->action = ACT_TCP_EXPECT_PX;
888 }
889 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
890 if (strcmp(args[arg+1], "layer4") != 0) {
891 memprintf(err,
892 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
893 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
894 return -1;
895 }
896
897 if (!(where & SMP_VAL_FE_CON_ACC)) {
898 memprintf(err,
899 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
900 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
901 return -1;
902 }
903
904 arg += 2;
905 rule->action = ACT_TCP_EXPECT_CIP;
906 }
907 else {
908 struct action_kw *kw;
909 if (where & SMP_VAL_FE_CON_ACC) {
910 /* L4 */
911 kw = tcp_req_conn_action(args[arg]);
912 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100913 } else if (where & SMP_VAL_FE_SES_ACC) {
914 /* L5 */
915 kw = tcp_req_sess_action(args[arg]);
916 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100917 } else {
918 /* L6 */
919 kw = tcp_req_cont_action(args[arg]);
920 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100921 }
922 if (kw) {
923 arg++;
924 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
925 return -1;
926 } else {
927 if (where & SMP_VAL_FE_CON_ACC)
928 action_build_list(&tcp_req_conn_keywords, &trash);
929 else if (where & SMP_VAL_FE_SES_ACC)
930 action_build_list(&tcp_req_sess_keywords, &trash);
931 else
932 action_build_list(&tcp_req_cont_keywords, &trash);
933 memprintf(err,
934 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
935 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200936 args[0], args[1], MAX_SESS_STKCTR-1,
937 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100938 curpx->id, args[arg]);
939 return -1;
940 }
941 }
942
943 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200944 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100945 memprintf(err,
946 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
947 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
948 return -1;
949 }
950 }
951 else if (*args[arg]) {
952 memprintf(err,
953 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
954 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
955 return -1;
956 }
957 return 0;
958}
959
960/* This function should be called to parse a line starting with the "tcp-response"
961 * keyword.
962 */
963static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
964 struct proxy *defpx, const char *file, int line,
965 char **err)
966{
967 const char *ptr = NULL;
968 unsigned int val;
969 int warn = 0;
970 int arg;
971 struct act_rule *rule;
972 unsigned int where;
973 const struct acl *acl;
974 const char *kw;
975
976 if (!*args[1]) {
977 memprintf(err, "missing argument for '%s' in %s '%s'",
978 args[0], proxy_type_str(curpx), curpx->id);
979 return -1;
980 }
981
982 if (strcmp(args[1], "inspect-delay") == 0) {
983 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
984 memprintf(err, "%s %s is only allowed in 'backend' sections",
985 args[0], args[1]);
986 return -1;
987 }
988
989 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
990 memprintf(err,
991 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
992 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200993
994 if (ptr == PARSE_TIME_OVER)
995 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
996 else if (ptr == PARSE_TIME_UNDER)
997 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
998 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100999 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1000 return -1;
1001 }
1002
1003 if (curpx->tcp_rep.inspect_delay) {
1004 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1005 args[0], args[1], proxy_type_str(curpx), curpx->id);
1006 return 1;
1007 }
1008 curpx->tcp_rep.inspect_delay = val;
1009 return 0;
1010 }
1011
1012 rule = calloc(1, sizeof(*rule));
1013 LIST_INIT(&rule->list);
1014 arg = 1;
1015 where = 0;
1016
1017 if (strcmp(args[1], "content") == 0) {
1018 arg++;
1019
1020 if (curpx->cap & PR_CAP_FE)
1021 where |= SMP_VAL_FE_RES_CNT;
1022 if (curpx->cap & PR_CAP_BE)
1023 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001024 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001025 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1026 goto error;
1027
1028 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1029 if (acl) {
1030 if (acl->name && *acl->name)
1031 memprintf(err,
1032 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1033 acl->name, args[0], args[1], sample_ckp_names(where));
1034 else
1035 memprintf(err,
1036 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1037 args[0], args[1],
1038 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1039 sample_ckp_names(where));
1040
1041 warn++;
1042 }
1043 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1044 if (acl->name && *acl->name)
1045 memprintf(err,
1046 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1047 acl->name, kw, sample_ckp_names(where));
1048 else
1049 memprintf(err,
1050 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1051 kw, sample_ckp_names(where));
1052 warn++;
1053 }
1054
1055 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1056 }
1057 else {
1058 memprintf(err,
1059 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1060 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1061 goto error;
1062 }
1063
1064 return warn;
1065 error:
1066 free(rule);
1067 return -1;
1068}
1069
1070
1071/* This function should be called to parse a line starting with the "tcp-request"
1072 * keyword.
1073 */
1074static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1075 struct proxy *defpx, const char *file, int line,
1076 char **err)
1077{
1078 const char *ptr = NULL;
1079 unsigned int val;
1080 int warn = 0;
1081 int arg;
1082 struct act_rule *rule;
1083 unsigned int where;
1084 const struct acl *acl;
1085 const char *kw;
1086
1087 if (!*args[1]) {
1088 if (curpx == defpx)
1089 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1090 else
1091 memprintf(err, "missing argument for '%s' in %s '%s'",
1092 args[0], proxy_type_str(curpx), curpx->id);
1093 return -1;
1094 }
1095
1096 if (!strcmp(args[1], "inspect-delay")) {
1097 if (curpx == defpx) {
1098 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1099 args[0], args[1]);
1100 return -1;
1101 }
1102
1103 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1104 memprintf(err,
1105 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1106 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001107
1108 if (ptr == PARSE_TIME_OVER)
1109 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1110 else if (ptr == PARSE_TIME_UNDER)
1111 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1112 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001113 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1114 return -1;
1115 }
1116
1117 if (curpx->tcp_req.inspect_delay) {
1118 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1119 args[0], args[1], proxy_type_str(curpx), curpx->id);
1120 return 1;
1121 }
1122 curpx->tcp_req.inspect_delay = val;
1123 return 0;
1124 }
1125
1126 rule = calloc(1, sizeof(*rule));
1127 LIST_INIT(&rule->list);
1128 arg = 1;
1129 where = 0;
1130
1131 if (strcmp(args[1], "content") == 0) {
1132 arg++;
1133
1134 if (curpx->cap & PR_CAP_FE)
1135 where |= SMP_VAL_FE_REQ_CNT;
1136 if (curpx->cap & PR_CAP_BE)
1137 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001138 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001139 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1140 goto error;
1141
1142 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1143 if (acl) {
1144 if (acl->name && *acl->name)
1145 memprintf(err,
1146 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1147 acl->name, args[0], args[1], sample_ckp_names(where));
1148 else
1149 memprintf(err,
1150 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1151 args[0], args[1],
1152 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1153 sample_ckp_names(where));
1154
1155 warn++;
1156 }
1157 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1158 if (acl->name && *acl->name)
1159 memprintf(err,
1160 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1161 acl->name, kw, sample_ckp_names(where));
1162 else
1163 memprintf(err,
1164 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1165 kw, sample_ckp_names(where));
1166 warn++;
1167 }
1168
1169 /* the following function directly emits the warning */
1170 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1171 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1172 }
1173 else if (strcmp(args[1], "connection") == 0) {
1174 arg++;
1175
1176 if (!(curpx->cap & PR_CAP_FE)) {
1177 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1178 args[0], args[1], proxy_type_str(curpx), curpx->id);
1179 goto error;
1180 }
1181
1182 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001183 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001184 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1185 goto error;
1186
1187 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1188 if (acl) {
1189 if (acl->name && *acl->name)
1190 memprintf(err,
1191 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1192 acl->name, args[0], args[1], sample_ckp_names(where));
1193 else
1194 memprintf(err,
1195 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1196 args[0], args[1],
1197 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1198 sample_ckp_names(where));
1199
1200 warn++;
1201 }
1202 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1203 if (acl->name && *acl->name)
1204 memprintf(err,
1205 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1206 acl->name, kw, sample_ckp_names(where));
1207 else
1208 memprintf(err,
1209 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1210 kw, sample_ckp_names(where));
1211 warn++;
1212 }
1213
1214 /* the following function directly emits the warning */
1215 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1216 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1217 }
1218 else if (strcmp(args[1], "session") == 0) {
1219 arg++;
1220
1221 if (!(curpx->cap & PR_CAP_FE)) {
1222 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1223 args[0], args[1], proxy_type_str(curpx), curpx->id);
1224 goto error;
1225 }
1226
1227 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001228 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001229 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1230 goto error;
1231
1232 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1233 if (acl) {
1234 if (acl->name && *acl->name)
1235 memprintf(err,
1236 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1237 acl->name, args[0], args[1], sample_ckp_names(where));
1238 else
1239 memprintf(err,
1240 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1241 args[0], args[1],
1242 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1243 sample_ckp_names(where));
1244 warn++;
1245 }
1246 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1247 if (acl->name && *acl->name)
1248 memprintf(err,
1249 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1250 acl->name, kw, sample_ckp_names(where));
1251 else
1252 memprintf(err,
1253 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1254 kw, sample_ckp_names(where));
1255 warn++;
1256 }
1257
1258 /* the following function directly emits the warning */
1259 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1260 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1261 }
1262 else {
1263 if (curpx == defpx)
1264 memprintf(err,
1265 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1266 args[0], args[1]);
1267 else
1268 memprintf(err,
1269 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1270 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1271 goto error;
1272 }
1273
1274 return warn;
1275 error:
1276 free(rule);
1277 return -1;
1278}
1279
1280static struct cfg_kw_list cfg_kws = {ILH, {
1281 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1282 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1283 { 0, NULL, NULL },
1284}};
1285
Willy Tarreau0108d902018-11-25 19:14:37 +01001286INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001287
1288/*
1289 * Local variables:
1290 * c-indent-level: 8
1291 * c-basic-offset: 8
1292 * End:
1293 */