blob: 2d4310ea11668c1f14d467f92228c38981b3f0ba [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;
107 int act_flags = 0;
108
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) {
154 act_flags |= ACT_FLAG_FIRST;
155resume_execution:
156 /* we have a matching rule. */
157 if (rule->action == ACT_ACTION_ALLOW) {
158 break;
159 }
160 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100161 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100162 }
163 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
164 /* Note: only the first valid tracking parameter of each
165 * applies.
166 */
167 struct stktable_key *key;
168 struct sample smp;
169
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200170 if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100171 continue;
172
173 t = rule->arg.trk_ctr.table.t;
174 key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
175
176 if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
177 goto missing_data; /* key might appear later */
178
179 if (key && (ts = stktable_get_entry(t, key))) {
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200180 stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
181 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
Willy Tarreau39713102016-11-25 15:49:32 +0100182 if (sess->fe != s->be)
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200183 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
Willy Tarreau39713102016-11-25 15:49:32 +0100184 }
185 }
186 else if (rule->action == ACT_TCP_CAPTURE) {
187 struct sample *key;
188 struct cap_hdr *h = rule->arg.cap.hdr;
189 char **cap = s->req_cap;
190 int len;
191
192 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
193 if (!key)
194 continue;
195
196 if (key->flags & SMP_F_MAY_CHANGE)
197 goto missing_data;
198
199 if (cap[h->index] == NULL)
Willy Tarreaubafbe012017-11-24 17:34:44 +0100200 cap[h->index] = pool_alloc(h->pool);
Willy Tarreau39713102016-11-25 15:49:32 +0100201
202 if (cap[h->index] == NULL) /* no more capture memory */
203 continue;
204
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200205 len = key->data.u.str.data;
Willy Tarreau39713102016-11-25 15:49:32 +0100206 if (len > h->len)
207 len = h->len;
208
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200209 memcpy(cap[h->index], key->data.u.str.area,
210 len);
Willy Tarreau39713102016-11-25 15:49:32 +0100211 cap[h->index][len] = 0;
212 }
213 else {
214 /* Custom keywords. */
215 if (!rule->action_ptr)
216 continue;
217
218 if (partial & SMP_OPT_FINAL)
219 act_flags |= ACT_FLAG_FINAL;
220
221 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
Willy Tarreau39713102016-11-25 15:49:32 +0100222 case ACT_RET_CONT:
223 continue;
224 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200225 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100226 break;
227 case ACT_RET_YIELD:
228 s->current_rule = rule;
229 goto missing_data;
Christopher Faulet282992e2019-12-16 12:34:31 +0100230 case ACT_RET_DENY:
231 goto deny;
232 case ACT_RET_ABRT:
233 goto abort;
234 case ACT_RET_ERR:
235 goto internal;
236 case ACT_RET_INV:
237 goto invalid;
Willy Tarreau39713102016-11-25 15:49:32 +0100238 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200239 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100240 }
241 }
242 }
243
244 /* 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;
306 int act_flags = 0;
307
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) {
357 act_flags |= ACT_FLAG_FIRST;
358resume_execution:
359 /* we have a matching rule. */
360 if (rule->action == ACT_ACTION_ALLOW) {
361 break;
362 }
363 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet282992e2019-12-16 12:34:31 +0100364 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100365 }
366 else if (rule->action == ACT_TCP_CLOSE) {
367 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100368 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100369 si_shutr(chn_prod(rep));
370 si_shutw(chn_prod(rep));
371 break;
372 }
373 else {
374 /* Custom keywords. */
375 if (!rule->action_ptr)
376 continue;
377
378 if (partial & SMP_OPT_FINAL)
379 act_flags |= ACT_FLAG_FINAL;
380
381 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
Willy Tarreau39713102016-11-25 15:49:32 +0100382 case ACT_RET_CONT:
383 continue;
384 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200385 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100386 break;
387 case ACT_RET_YIELD:
388 channel_dont_close(rep);
389 s->current_rule = rule;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100390 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100391 return 0;
Christopher Faulet282992e2019-12-16 12:34:31 +0100392 case ACT_RET_DENY:
393 goto deny;
394 case ACT_RET_ABRT:
395 goto abort;
396 case ACT_RET_ERR:
397 goto internal;
398 case ACT_RET_INV:
399 goto invalid;
Willy Tarreau39713102016-11-25 15:49:32 +0100400 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200401 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100402 }
403 }
404 }
405
406 /* if we get there, it means we have no rule which matches, or
407 * we have an explicit accept, so we apply the default accept.
408 */
409 rep->analysers &= ~an_bit;
410 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100411 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100412 return 1;
Christopher Faulet282992e2019-12-16 12:34:31 +0100413
414 deny:
415 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
416 if (objt_server(s->target))
417 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
418 goto reject;
419
420 internal:
421 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
422 if (objt_server(s->target))
423 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
424 if (!(s->flags & SF_ERR_MASK))
425 s->flags |= SF_ERR_INTERNAL;
426 goto reject;
427
428 invalid:
429 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
430 if (objt_server(s->target))
431 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
432
433 reject:
434 si_must_kill_conn(chn_prod(rep));
435 channel_abort(rep);
436 channel_abort(&s->req);
437
438 abort:
439 rep->analysers &= AN_REQ_FLT_END;
440
441 if (!(s->flags & SF_ERR_MASK))
442 s->flags |= SF_ERR_PRXCOND;
443 if (!(s->flags & SF_FINST_MASK))
444 s->flags |= SF_FINST_D;
445 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
446 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100447}
448
449
450/* This function performs the TCP layer4 analysis on the current request. It
451 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
452 * matches or if no more rule matches. It can only use rules which don't need
453 * any data. This only works on connection-based client-facing stream interfaces.
454 */
455int tcp_exec_l4_rules(struct session *sess)
456{
457 struct act_rule *rule;
458 struct stksess *ts;
459 struct stktable *t = NULL;
460 struct connection *conn = objt_conn(sess->origin);
461 int result = 1;
462 enum acl_test_res ret;
463
464 if (!conn)
465 return result;
466
467 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
468 ret = ACL_TEST_PASS;
469
470 if (rule->cond) {
471 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
472 ret = acl_pass(ret);
473 if (rule->cond->pol == ACL_COND_UNLESS)
474 ret = !ret;
475 }
476
477 if (ret) {
478 /* we have a matching rule. */
479 if (rule->action == ACT_ACTION_ALLOW) {
480 break;
481 }
482 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100483 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100484 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100485 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100486
487 result = 0;
488 break;
489 }
490 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
491 /* Note: only the first valid tracking parameter of each
492 * applies.
493 */
494 struct stktable_key *key;
495
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200496 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100497 continue;
498
499 t = rule->arg.trk_ctr.table.t;
500 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
501
502 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200503 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100504 }
505 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200506 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
507 if (xprt_add_hs(conn) < 0) {
508 result = 0;
509 break;
510 }
511 }
Willy Tarreau39713102016-11-25 15:49:32 +0100512 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100513 }
514 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200515 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
516 if (xprt_add_hs(conn) < 0) {
517 result = 0;
518 break;
519 }
520 }
Willy Tarreau39713102016-11-25 15:49:32 +0100521 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100522 }
523 else {
524 /* Custom keywords. */
525 if (!rule->action_ptr)
526 break;
527 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
528 case ACT_RET_YIELD:
529 /* yield is not allowed at this point. If this return code is
530 * used it is a bug, so I prefer to abort the process.
531 */
532 send_log(sess->fe, LOG_WARNING,
533 "Internal error: yield not allowed with tcp-request connection actions.");
534 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200535 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100536 break;
537 case ACT_RET_CONT:
538 continue;
Christopher Faulet282992e2019-12-16 12:34:31 +0100539 case ACT_RET_DENY:
540 case ACT_RET_ABRT:
Willy Tarreau39713102016-11-25 15:49:32 +0100541 case ACT_RET_ERR:
Christopher Faulet282992e2019-12-16 12:34:31 +0100542 case ACT_RET_INV:
Willy Tarreau39713102016-11-25 15:49:32 +0100543 result = 0;
544 break;
545 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200546 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100547 }
548 }
549 }
550 return result;
551}
552
553/* This function performs the TCP layer5 analysis on the current request. It
554 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
555 * matches or if no more rule matches. It can only use rules which don't need
556 * any data. This only works on session-based client-facing stream interfaces.
557 * An example of valid use case is to track a stick-counter on the source
558 * address extracted from the proxy protocol.
559 */
560int tcp_exec_l5_rules(struct session *sess)
561{
562 struct act_rule *rule;
563 struct stksess *ts;
564 struct stktable *t = NULL;
565 int result = 1;
566 enum acl_test_res ret;
567
568 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
569 ret = ACL_TEST_PASS;
570
571 if (rule->cond) {
572 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
573 ret = acl_pass(ret);
574 if (rule->cond->pol == ACL_COND_UNLESS)
575 ret = !ret;
576 }
577
578 if (ret) {
579 /* we have a matching rule. */
580 if (rule->action == ACT_ACTION_ALLOW) {
581 break;
582 }
583 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100584 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100585 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100586 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100587
588 result = 0;
589 break;
590 }
591 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
592 /* Note: only the first valid tracking parameter of each
593 * applies.
594 */
595 struct stktable_key *key;
596
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200597 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100598 continue;
599
600 t = rule->arg.trk_ctr.table.t;
601 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
602
603 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200604 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100605 }
606 else {
607 /* Custom keywords. */
608 if (!rule->action_ptr)
609 break;
610 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
611 case ACT_RET_YIELD:
612 /* yield is not allowed at this point. If this return code is
613 * used it is a bug, so I prefer to abort the process.
614 */
615 send_log(sess->fe, LOG_WARNING,
616 "Internal error: yield not allowed with tcp-request session actions.");
617 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200618 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100619 break;
620 case ACT_RET_CONT:
621 continue;
Christopher Faulet282992e2019-12-16 12:34:31 +0100622 case ACT_RET_DENY:
623 case ACT_RET_ABRT:
Willy Tarreau39713102016-11-25 15:49:32 +0100624 case ACT_RET_ERR:
Christopher Faulet282992e2019-12-16 12:34:31 +0100625 case ACT_RET_INV:
Willy Tarreau39713102016-11-25 15:49:32 +0100626 result = 0;
627 break;
628 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200629 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100630 }
631 }
632 }
633 return result;
634}
635
636/* Parse a tcp-response rule. Return a negative value in case of failure */
637static int tcp_parse_response_rule(char **args, int arg, int section_type,
638 struct proxy *curpx, struct proxy *defpx,
639 struct act_rule *rule, char **err,
640 unsigned int where,
641 const char *file, int line)
642{
643 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
644 memprintf(err, "%s %s is only allowed in 'backend' sections",
645 args[0], args[1]);
646 return -1;
647 }
648
649 if (strcmp(args[arg], "accept") == 0) {
650 arg++;
651 rule->action = ACT_ACTION_ALLOW;
652 }
653 else if (strcmp(args[arg], "reject") == 0) {
654 arg++;
655 rule->action = ACT_ACTION_DENY;
656 }
657 else if (strcmp(args[arg], "close") == 0) {
658 arg++;
659 rule->action = ACT_TCP_CLOSE;
660 }
661 else {
662 struct action_kw *kw;
663 kw = tcp_res_cont_action(args[arg]);
664 if (kw) {
665 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100666 rule->kw = kw;
667 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
668 return -1;
669 } else {
670 action_build_list(&tcp_res_cont_keywords, &trash);
671 memprintf(err,
672 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200673 args[0], args[1], trash.area,
674 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100675 return -1;
676 }
677 }
678
679 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200680 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100681 memprintf(err,
682 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
683 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
684 return -1;
685 }
686 }
687 else if (*args[arg]) {
688 memprintf(err,
689 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
690 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
691 return -1;
692 }
693 return 0;
694}
695
696
697
698/* Parse a tcp-request rule. Return a negative value in case of failure */
699static int tcp_parse_request_rule(char **args, int arg, int section_type,
700 struct proxy *curpx, struct proxy *defpx,
701 struct act_rule *rule, char **err,
702 unsigned int where, const char *file, int line)
703{
704 if (curpx == defpx) {
705 memprintf(err, "%s %s is not allowed in 'defaults' sections",
706 args[0], args[1]);
707 return -1;
708 }
709
710 if (!strcmp(args[arg], "accept")) {
711 arg++;
712 rule->action = ACT_ACTION_ALLOW;
713 }
714 else if (!strcmp(args[arg], "reject")) {
715 arg++;
716 rule->action = ACT_ACTION_DENY;
717 }
718 else if (strcmp(args[arg], "capture") == 0) {
719 struct sample_expr *expr;
720 struct cap_hdr *hdr;
721 int kw = arg;
722 int len = 0;
723
724 if (!(curpx->cap & PR_CAP_FE)) {
725 memprintf(err,
726 "'%s %s %s' : proxy '%s' has no frontend capability",
727 args[0], args[1], args[kw], curpx->id);
728 return -1;
729 }
730
731 if (!(where & SMP_VAL_FE_REQ_CNT)) {
732 memprintf(err,
733 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
734 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
735 return -1;
736 }
737
738 arg++;
739
740 curpx->conf.args.ctx = ARGC_CAP;
741 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
742 if (!expr) {
743 memprintf(err,
744 "'%s %s %s' : %s",
745 args[0], args[1], args[kw], *err);
746 return -1;
747 }
748
749 if (!(expr->fetch->val & where)) {
750 memprintf(err,
751 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
752 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
753 free(expr);
754 return -1;
755 }
756
757 if (strcmp(args[arg], "len") == 0) {
758 arg++;
759 if (!args[arg]) {
760 memprintf(err,
761 "'%s %s %s' : missing length value",
762 args[0], args[1], args[kw]);
763 free(expr);
764 return -1;
765 }
766 /* we copy the table name for now, it will be resolved later */
767 len = atoi(args[arg]);
768 if (len <= 0) {
769 memprintf(err,
770 "'%s %s %s' : length must be > 0",
771 args[0], args[1], args[kw]);
772 free(expr);
773 return -1;
774 }
775 arg++;
776 }
777
778 if (!len) {
779 memprintf(err,
780 "'%s %s %s' : a positive 'len' argument is mandatory",
781 args[0], args[1], args[kw]);
782 free(expr);
783 return -1;
784 }
785
786 hdr = calloc(1, sizeof(*hdr));
787 hdr->next = curpx->req_cap;
788 hdr->name = NULL; /* not a header capture */
789 hdr->namelen = 0;
790 hdr->len = len;
791 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
792 hdr->index = curpx->nb_req_cap++;
793
794 curpx->req_cap = hdr;
795 curpx->to_log |= LW_REQHDR;
796
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200797 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100798 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
799
800 rule->arg.cap.expr = expr;
801 rule->arg.cap.hdr = hdr;
802 rule->action = ACT_TCP_CAPTURE;
803 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100804 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100805 struct sample_expr *expr;
806 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100807 unsigned int tsc_num;
808 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100809
810 arg++;
811
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100812 tsc_num_str = &args[kw][8];
813 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
814 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
815 return -1;
816 }
817
Willy Tarreau39713102016-11-25 15:49:32 +0100818 curpx->conf.args.ctx = ARGC_TRK;
819 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
820 if (!expr) {
821 memprintf(err,
822 "'%s %s %s' : %s",
823 args[0], args[1], args[kw], *err);
824 return -1;
825 }
826
827 if (!(expr->fetch->val & where)) {
828 memprintf(err,
829 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
830 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
831 free(expr);
832 return -1;
833 }
834
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200835 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100836 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
837
838 if (strcmp(args[arg], "table") == 0) {
839 arg++;
840 if (!args[arg]) {
841 memprintf(err,
842 "'%s %s %s' : missing table name",
843 args[0], args[1], args[kw]);
844 free(expr);
845 return -1;
846 }
847 /* we copy the table name for now, it will be resolved later */
848 rule->arg.trk_ctr.table.n = strdup(args[arg]);
849 arg++;
850 }
851 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100852 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200853 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100854 }
855 else if (strcmp(args[arg], "expect-proxy") == 0) {
856 if (strcmp(args[arg+1], "layer4") != 0) {
857 memprintf(err,
858 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
859 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
860 return -1;
861 }
862
863 if (!(where & SMP_VAL_FE_CON_ACC)) {
864 memprintf(err,
865 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
866 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
867 return -1;
868 }
869
870 arg += 2;
871 rule->action = ACT_TCP_EXPECT_PX;
872 }
873 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
874 if (strcmp(args[arg+1], "layer4") != 0) {
875 memprintf(err,
876 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
877 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
878 return -1;
879 }
880
881 if (!(where & SMP_VAL_FE_CON_ACC)) {
882 memprintf(err,
883 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
884 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
885 return -1;
886 }
887
888 arg += 2;
889 rule->action = ACT_TCP_EXPECT_CIP;
890 }
891 else {
892 struct action_kw *kw;
893 if (where & SMP_VAL_FE_CON_ACC) {
894 /* L4 */
895 kw = tcp_req_conn_action(args[arg]);
896 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100897 } else if (where & SMP_VAL_FE_SES_ACC) {
898 /* L5 */
899 kw = tcp_req_sess_action(args[arg]);
900 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100901 } else {
902 /* L6 */
903 kw = tcp_req_cont_action(args[arg]);
904 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100905 }
906 if (kw) {
907 arg++;
908 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
909 return -1;
910 } else {
911 if (where & SMP_VAL_FE_CON_ACC)
912 action_build_list(&tcp_req_conn_keywords, &trash);
913 else if (where & SMP_VAL_FE_SES_ACC)
914 action_build_list(&tcp_req_sess_keywords, &trash);
915 else
916 action_build_list(&tcp_req_cont_keywords, &trash);
917 memprintf(err,
918 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
919 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200920 args[0], args[1], MAX_SESS_STKCTR-1,
921 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100922 curpx->id, args[arg]);
923 return -1;
924 }
925 }
926
927 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200928 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100929 memprintf(err,
930 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
931 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
932 return -1;
933 }
934 }
935 else if (*args[arg]) {
936 memprintf(err,
937 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
938 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
939 return -1;
940 }
941 return 0;
942}
943
944/* This function should be called to parse a line starting with the "tcp-response"
945 * keyword.
946 */
947static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
948 struct proxy *defpx, const char *file, int line,
949 char **err)
950{
951 const char *ptr = NULL;
952 unsigned int val;
953 int warn = 0;
954 int arg;
955 struct act_rule *rule;
956 unsigned int where;
957 const struct acl *acl;
958 const char *kw;
959
960 if (!*args[1]) {
961 memprintf(err, "missing argument for '%s' in %s '%s'",
962 args[0], proxy_type_str(curpx), curpx->id);
963 return -1;
964 }
965
966 if (strcmp(args[1], "inspect-delay") == 0) {
967 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
968 memprintf(err, "%s %s is only allowed in 'backend' sections",
969 args[0], args[1]);
970 return -1;
971 }
972
973 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
974 memprintf(err,
975 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
976 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200977
978 if (ptr == PARSE_TIME_OVER)
979 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
980 else if (ptr == PARSE_TIME_UNDER)
981 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
982 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100983 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
984 return -1;
985 }
986
987 if (curpx->tcp_rep.inspect_delay) {
988 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
989 args[0], args[1], proxy_type_str(curpx), curpx->id);
990 return 1;
991 }
992 curpx->tcp_rep.inspect_delay = val;
993 return 0;
994 }
995
996 rule = calloc(1, sizeof(*rule));
997 LIST_INIT(&rule->list);
998 arg = 1;
999 where = 0;
1000
1001 if (strcmp(args[1], "content") == 0) {
1002 arg++;
1003
1004 if (curpx->cap & PR_CAP_FE)
1005 where |= SMP_VAL_FE_RES_CNT;
1006 if (curpx->cap & PR_CAP_BE)
1007 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001008 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001009 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1010 goto error;
1011
1012 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1013 if (acl) {
1014 if (acl->name && *acl->name)
1015 memprintf(err,
1016 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1017 acl->name, args[0], args[1], sample_ckp_names(where));
1018 else
1019 memprintf(err,
1020 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1021 args[0], args[1],
1022 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1023 sample_ckp_names(where));
1024
1025 warn++;
1026 }
1027 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1028 if (acl->name && *acl->name)
1029 memprintf(err,
1030 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1031 acl->name, kw, sample_ckp_names(where));
1032 else
1033 memprintf(err,
1034 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1035 kw, sample_ckp_names(where));
1036 warn++;
1037 }
1038
1039 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1040 }
1041 else {
1042 memprintf(err,
1043 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1044 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1045 goto error;
1046 }
1047
1048 return warn;
1049 error:
1050 free(rule);
1051 return -1;
1052}
1053
1054
1055/* This function should be called to parse a line starting with the "tcp-request"
1056 * keyword.
1057 */
1058static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1059 struct proxy *defpx, const char *file, int line,
1060 char **err)
1061{
1062 const char *ptr = NULL;
1063 unsigned int val;
1064 int warn = 0;
1065 int arg;
1066 struct act_rule *rule;
1067 unsigned int where;
1068 const struct acl *acl;
1069 const char *kw;
1070
1071 if (!*args[1]) {
1072 if (curpx == defpx)
1073 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1074 else
1075 memprintf(err, "missing argument for '%s' in %s '%s'",
1076 args[0], proxy_type_str(curpx), curpx->id);
1077 return -1;
1078 }
1079
1080 if (!strcmp(args[1], "inspect-delay")) {
1081 if (curpx == defpx) {
1082 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1083 args[0], args[1]);
1084 return -1;
1085 }
1086
1087 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1088 memprintf(err,
1089 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1090 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001091
1092 if (ptr == PARSE_TIME_OVER)
1093 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1094 else if (ptr == PARSE_TIME_UNDER)
1095 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1096 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001097 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1098 return -1;
1099 }
1100
1101 if (curpx->tcp_req.inspect_delay) {
1102 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1103 args[0], args[1], proxy_type_str(curpx), curpx->id);
1104 return 1;
1105 }
1106 curpx->tcp_req.inspect_delay = val;
1107 return 0;
1108 }
1109
1110 rule = calloc(1, sizeof(*rule));
1111 LIST_INIT(&rule->list);
1112 arg = 1;
1113 where = 0;
1114
1115 if (strcmp(args[1], "content") == 0) {
1116 arg++;
1117
1118 if (curpx->cap & PR_CAP_FE)
1119 where |= SMP_VAL_FE_REQ_CNT;
1120 if (curpx->cap & PR_CAP_BE)
1121 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001122 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001123 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1124 goto error;
1125
1126 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1127 if (acl) {
1128 if (acl->name && *acl->name)
1129 memprintf(err,
1130 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1131 acl->name, args[0], args[1], sample_ckp_names(where));
1132 else
1133 memprintf(err,
1134 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1135 args[0], args[1],
1136 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1137 sample_ckp_names(where));
1138
1139 warn++;
1140 }
1141 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1142 if (acl->name && *acl->name)
1143 memprintf(err,
1144 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1145 acl->name, kw, sample_ckp_names(where));
1146 else
1147 memprintf(err,
1148 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1149 kw, sample_ckp_names(where));
1150 warn++;
1151 }
1152
1153 /* the following function directly emits the warning */
1154 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1155 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1156 }
1157 else if (strcmp(args[1], "connection") == 0) {
1158 arg++;
1159
1160 if (!(curpx->cap & PR_CAP_FE)) {
1161 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1162 args[0], args[1], proxy_type_str(curpx), curpx->id);
1163 goto error;
1164 }
1165
1166 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001167 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001168 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1169 goto error;
1170
1171 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1172 if (acl) {
1173 if (acl->name && *acl->name)
1174 memprintf(err,
1175 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1176 acl->name, args[0], args[1], sample_ckp_names(where));
1177 else
1178 memprintf(err,
1179 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1180 args[0], args[1],
1181 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1182 sample_ckp_names(where));
1183
1184 warn++;
1185 }
1186 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1187 if (acl->name && *acl->name)
1188 memprintf(err,
1189 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1190 acl->name, kw, sample_ckp_names(where));
1191 else
1192 memprintf(err,
1193 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1194 kw, sample_ckp_names(where));
1195 warn++;
1196 }
1197
1198 /* the following function directly emits the warning */
1199 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1200 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1201 }
1202 else if (strcmp(args[1], "session") == 0) {
1203 arg++;
1204
1205 if (!(curpx->cap & PR_CAP_FE)) {
1206 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1207 args[0], args[1], proxy_type_str(curpx), curpx->id);
1208 goto error;
1209 }
1210
1211 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001212 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001213 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1214 goto error;
1215
1216 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1217 if (acl) {
1218 if (acl->name && *acl->name)
1219 memprintf(err,
1220 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1221 acl->name, args[0], args[1], sample_ckp_names(where));
1222 else
1223 memprintf(err,
1224 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1225 args[0], args[1],
1226 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1227 sample_ckp_names(where));
1228 warn++;
1229 }
1230 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1231 if (acl->name && *acl->name)
1232 memprintf(err,
1233 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1234 acl->name, kw, sample_ckp_names(where));
1235 else
1236 memprintf(err,
1237 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1238 kw, sample_ckp_names(where));
1239 warn++;
1240 }
1241
1242 /* the following function directly emits the warning */
1243 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1244 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1245 }
1246 else {
1247 if (curpx == defpx)
1248 memprintf(err,
1249 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1250 args[0], args[1]);
1251 else
1252 memprintf(err,
1253 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1254 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1255 goto error;
1256 }
1257
1258 return warn;
1259 error:
1260 free(rule);
1261 return -1;
1262}
1263
1264static struct cfg_kw_list cfg_kws = {ILH, {
1265 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1266 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1267 { 0, NULL, NULL },
1268}};
1269
Willy Tarreau0108d902018-11-25 19:14:37 +01001270INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001271
1272/*
1273 * Local variables:
1274 * c-indent-level: 8
1275 * c-basic-offset: 8
1276 * End:
1277 */