blob: 6027e1250c721fc63f45ec1c93427c6e1e4de2a8 [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:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100415 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100416 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100417 if (s->sess->listener->counters)
418 _HA_ATOMIC_ADD(&s->sess->listener->counters->denied_resp, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100419 if (objt_server(s->target))
420 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.denied_resp, 1);
421 goto reject;
422
423 internal:
Christopher Fauletcff0f732019-12-16 16:13:44 +0100424 _HA_ATOMIC_ADD(&s->sess->fe->fe_counters.internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100425 _HA_ATOMIC_ADD(&s->be->be_counters.internal_errors, 1);
Christopher Fauletcff0f732019-12-16 16:13:44 +0100426 if (s->sess->listener->counters)
427 _HA_ATOMIC_ADD(&s->sess->listener->counters->internal_errors, 1);
Christopher Faulet282992e2019-12-16 12:34:31 +0100428 if (objt_server(s->target))
429 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.internal_errors, 1);
430 if (!(s->flags & SF_ERR_MASK))
431 s->flags |= SF_ERR_INTERNAL;
432 goto reject;
433
434 invalid:
435 _HA_ATOMIC_ADD(&s->be->be_counters.failed_resp, 1);
436 if (objt_server(s->target))
437 _HA_ATOMIC_ADD(&__objt_server(s->target)->counters.failed_resp, 1);
438
439 reject:
440 si_must_kill_conn(chn_prod(rep));
441 channel_abort(rep);
442 channel_abort(&s->req);
443
444 abort:
445 rep->analysers &= AN_REQ_FLT_END;
446
447 if (!(s->flags & SF_ERR_MASK))
448 s->flags |= SF_ERR_PRXCOND;
449 if (!(s->flags & SF_FINST_MASK))
450 s->flags |= SF_FINST_D;
451 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
452 return 0;
Willy Tarreau39713102016-11-25 15:49:32 +0100453}
454
455
456/* This function performs the TCP layer4 analysis on the current request. It
457 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
458 * matches or if no more rule matches. It can only use rules which don't need
459 * any data. This only works on connection-based client-facing stream interfaces.
460 */
461int tcp_exec_l4_rules(struct session *sess)
462{
463 struct act_rule *rule;
464 struct stksess *ts;
465 struct stktable *t = NULL;
466 struct connection *conn = objt_conn(sess->origin);
467 int result = 1;
468 enum acl_test_res ret;
469
470 if (!conn)
471 return result;
472
473 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
474 ret = ACL_TEST_PASS;
475
476 if (rule->cond) {
477 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
478 ret = acl_pass(ret);
479 if (rule->cond->pol == ACL_COND_UNLESS)
480 ret = !ret;
481 }
482
483 if (ret) {
484 /* we have a matching rule. */
485 if (rule->action == ACT_ACTION_ALLOW) {
486 break;
487 }
488 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100489 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100490 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100491 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100492
493 result = 0;
494 break;
495 }
496 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
497 /* Note: only the first valid tracking parameter of each
498 * applies.
499 */
500 struct stktable_key *key;
501
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200502 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100503 continue;
504
505 t = rule->arg.trk_ctr.table.t;
506 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
507
508 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200509 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100510 }
511 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200512 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
513 if (xprt_add_hs(conn) < 0) {
514 result = 0;
515 break;
516 }
517 }
Willy Tarreau39713102016-11-25 15:49:32 +0100518 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100519 }
520 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200521 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
522 if (xprt_add_hs(conn) < 0) {
523 result = 0;
524 break;
525 }
526 }
Willy Tarreau39713102016-11-25 15:49:32 +0100527 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100528 }
529 else {
530 /* Custom keywords. */
531 if (!rule->action_ptr)
532 break;
533 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
534 case ACT_RET_YIELD:
535 /* yield is not allowed at this point. If this return code is
536 * used it is a bug, so I prefer to abort the process.
537 */
538 send_log(sess->fe, LOG_WARNING,
539 "Internal error: yield not allowed with tcp-request connection actions.");
540 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200541 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100542 break;
543 case ACT_RET_CONT:
544 continue;
Christopher Faulet282992e2019-12-16 12:34:31 +0100545 case ACT_RET_DENY:
546 case ACT_RET_ABRT:
Willy Tarreau39713102016-11-25 15:49:32 +0100547 case ACT_RET_ERR:
Christopher Faulet282992e2019-12-16 12:34:31 +0100548 case ACT_RET_INV:
Willy Tarreau39713102016-11-25 15:49:32 +0100549 result = 0;
550 break;
551 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200552 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100553 }
554 }
555 }
556 return result;
557}
558
559/* This function performs the TCP layer5 analysis on the current request. It
560 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
561 * matches or if no more rule matches. It can only use rules which don't need
562 * any data. This only works on session-based client-facing stream interfaces.
563 * An example of valid use case is to track a stick-counter on the source
564 * address extracted from the proxy protocol.
565 */
566int tcp_exec_l5_rules(struct session *sess)
567{
568 struct act_rule *rule;
569 struct stksess *ts;
570 struct stktable *t = NULL;
571 int result = 1;
572 enum acl_test_res ret;
573
574 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
575 ret = ACL_TEST_PASS;
576
577 if (rule->cond) {
578 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
579 ret = acl_pass(ret);
580 if (rule->cond->pol == ACL_COND_UNLESS)
581 ret = !ret;
582 }
583
584 if (ret) {
585 /* we have a matching rule. */
586 if (rule->action == ACT_ACTION_ALLOW) {
587 break;
588 }
589 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100590 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100591 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100592 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100593
594 result = 0;
595 break;
596 }
597 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
598 /* Note: only the first valid tracking parameter of each
599 * applies.
600 */
601 struct stktable_key *key;
602
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200603 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100604 continue;
605
606 t = rule->arg.trk_ctr.table.t;
607 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
608
609 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200610 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100611 }
612 else {
613 /* Custom keywords. */
614 if (!rule->action_ptr)
615 break;
616 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
617 case ACT_RET_YIELD:
618 /* yield is not allowed at this point. If this return code is
619 * used it is a bug, so I prefer to abort the process.
620 */
621 send_log(sess->fe, LOG_WARNING,
622 "Internal error: yield not allowed with tcp-request session actions.");
623 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200624 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100625 break;
626 case ACT_RET_CONT:
627 continue;
Christopher Faulet282992e2019-12-16 12:34:31 +0100628 case ACT_RET_DENY:
629 case ACT_RET_ABRT:
Willy Tarreau39713102016-11-25 15:49:32 +0100630 case ACT_RET_ERR:
Christopher Faulet282992e2019-12-16 12:34:31 +0100631 case ACT_RET_INV:
Willy Tarreau39713102016-11-25 15:49:32 +0100632 result = 0;
633 break;
634 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200635 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100636 }
637 }
638 }
639 return result;
640}
641
642/* Parse a tcp-response rule. Return a negative value in case of failure */
643static int tcp_parse_response_rule(char **args, int arg, int section_type,
644 struct proxy *curpx, struct proxy *defpx,
645 struct act_rule *rule, char **err,
646 unsigned int where,
647 const char *file, int line)
648{
649 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
650 memprintf(err, "%s %s is only allowed in 'backend' sections",
651 args[0], args[1]);
652 return -1;
653 }
654
655 if (strcmp(args[arg], "accept") == 0) {
656 arg++;
657 rule->action = ACT_ACTION_ALLOW;
658 }
659 else if (strcmp(args[arg], "reject") == 0) {
660 arg++;
661 rule->action = ACT_ACTION_DENY;
662 }
663 else if (strcmp(args[arg], "close") == 0) {
664 arg++;
665 rule->action = ACT_TCP_CLOSE;
666 }
667 else {
668 struct action_kw *kw;
669 kw = tcp_res_cont_action(args[arg]);
670 if (kw) {
671 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100672 rule->kw = kw;
673 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
674 return -1;
675 } else {
676 action_build_list(&tcp_res_cont_keywords, &trash);
677 memprintf(err,
678 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200679 args[0], args[1], trash.area,
680 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100681 return -1;
682 }
683 }
684
685 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200686 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100687 memprintf(err,
688 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
689 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
690 return -1;
691 }
692 }
693 else if (*args[arg]) {
694 memprintf(err,
695 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
696 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
697 return -1;
698 }
699 return 0;
700}
701
702
703
704/* Parse a tcp-request rule. Return a negative value in case of failure */
705static int tcp_parse_request_rule(char **args, int arg, int section_type,
706 struct proxy *curpx, struct proxy *defpx,
707 struct act_rule *rule, char **err,
708 unsigned int where, const char *file, int line)
709{
710 if (curpx == defpx) {
711 memprintf(err, "%s %s is not allowed in 'defaults' sections",
712 args[0], args[1]);
713 return -1;
714 }
715
716 if (!strcmp(args[arg], "accept")) {
717 arg++;
718 rule->action = ACT_ACTION_ALLOW;
719 }
720 else if (!strcmp(args[arg], "reject")) {
721 arg++;
722 rule->action = ACT_ACTION_DENY;
723 }
724 else if (strcmp(args[arg], "capture") == 0) {
725 struct sample_expr *expr;
726 struct cap_hdr *hdr;
727 int kw = arg;
728 int len = 0;
729
730 if (!(curpx->cap & PR_CAP_FE)) {
731 memprintf(err,
732 "'%s %s %s' : proxy '%s' has no frontend capability",
733 args[0], args[1], args[kw], curpx->id);
734 return -1;
735 }
736
737 if (!(where & SMP_VAL_FE_REQ_CNT)) {
738 memprintf(err,
739 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
740 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
741 return -1;
742 }
743
744 arg++;
745
746 curpx->conf.args.ctx = ARGC_CAP;
747 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
748 if (!expr) {
749 memprintf(err,
750 "'%s %s %s' : %s",
751 args[0], args[1], args[kw], *err);
752 return -1;
753 }
754
755 if (!(expr->fetch->val & where)) {
756 memprintf(err,
757 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
758 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
759 free(expr);
760 return -1;
761 }
762
763 if (strcmp(args[arg], "len") == 0) {
764 arg++;
765 if (!args[arg]) {
766 memprintf(err,
767 "'%s %s %s' : missing length value",
768 args[0], args[1], args[kw]);
769 free(expr);
770 return -1;
771 }
772 /* we copy the table name for now, it will be resolved later */
773 len = atoi(args[arg]);
774 if (len <= 0) {
775 memprintf(err,
776 "'%s %s %s' : length must be > 0",
777 args[0], args[1], args[kw]);
778 free(expr);
779 return -1;
780 }
781 arg++;
782 }
783
784 if (!len) {
785 memprintf(err,
786 "'%s %s %s' : a positive 'len' argument is mandatory",
787 args[0], args[1], args[kw]);
788 free(expr);
789 return -1;
790 }
791
792 hdr = calloc(1, sizeof(*hdr));
793 hdr->next = curpx->req_cap;
794 hdr->name = NULL; /* not a header capture */
795 hdr->namelen = 0;
796 hdr->len = len;
797 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
798 hdr->index = curpx->nb_req_cap++;
799
800 curpx->req_cap = hdr;
801 curpx->to_log |= LW_REQHDR;
802
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200803 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100804 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
805
806 rule->arg.cap.expr = expr;
807 rule->arg.cap.hdr = hdr;
808 rule->action = ACT_TCP_CAPTURE;
809 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100810 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100811 struct sample_expr *expr;
812 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100813 unsigned int tsc_num;
814 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100815
816 arg++;
817
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100818 tsc_num_str = &args[kw][8];
819 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
820 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
821 return -1;
822 }
823
Willy Tarreau39713102016-11-25 15:49:32 +0100824 curpx->conf.args.ctx = ARGC_TRK;
825 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
826 if (!expr) {
827 memprintf(err,
828 "'%s %s %s' : %s",
829 args[0], args[1], args[kw], *err);
830 return -1;
831 }
832
833 if (!(expr->fetch->val & where)) {
834 memprintf(err,
835 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
836 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
837 free(expr);
838 return -1;
839 }
840
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200841 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100842 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
843
844 if (strcmp(args[arg], "table") == 0) {
845 arg++;
846 if (!args[arg]) {
847 memprintf(err,
848 "'%s %s %s' : missing table name",
849 args[0], args[1], args[kw]);
850 free(expr);
851 return -1;
852 }
853 /* we copy the table name for now, it will be resolved later */
854 rule->arg.trk_ctr.table.n = strdup(args[arg]);
855 arg++;
856 }
857 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100858 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200859 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100860 }
861 else if (strcmp(args[arg], "expect-proxy") == 0) {
862 if (strcmp(args[arg+1], "layer4") != 0) {
863 memprintf(err,
864 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
865 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
866 return -1;
867 }
868
869 if (!(where & SMP_VAL_FE_CON_ACC)) {
870 memprintf(err,
871 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
872 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
873 return -1;
874 }
875
876 arg += 2;
877 rule->action = ACT_TCP_EXPECT_PX;
878 }
879 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
880 if (strcmp(args[arg+1], "layer4") != 0) {
881 memprintf(err,
882 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
883 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
884 return -1;
885 }
886
887 if (!(where & SMP_VAL_FE_CON_ACC)) {
888 memprintf(err,
889 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
890 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
891 return -1;
892 }
893
894 arg += 2;
895 rule->action = ACT_TCP_EXPECT_CIP;
896 }
897 else {
898 struct action_kw *kw;
899 if (where & SMP_VAL_FE_CON_ACC) {
900 /* L4 */
901 kw = tcp_req_conn_action(args[arg]);
902 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100903 } else if (where & SMP_VAL_FE_SES_ACC) {
904 /* L5 */
905 kw = tcp_req_sess_action(args[arg]);
906 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100907 } else {
908 /* L6 */
909 kw = tcp_req_cont_action(args[arg]);
910 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100911 }
912 if (kw) {
913 arg++;
914 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
915 return -1;
916 } else {
917 if (where & SMP_VAL_FE_CON_ACC)
918 action_build_list(&tcp_req_conn_keywords, &trash);
919 else if (where & SMP_VAL_FE_SES_ACC)
920 action_build_list(&tcp_req_sess_keywords, &trash);
921 else
922 action_build_list(&tcp_req_cont_keywords, &trash);
923 memprintf(err,
924 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
925 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200926 args[0], args[1], MAX_SESS_STKCTR-1,
927 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100928 curpx->id, args[arg]);
929 return -1;
930 }
931 }
932
933 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200934 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100935 memprintf(err,
936 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
937 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
938 return -1;
939 }
940 }
941 else if (*args[arg]) {
942 memprintf(err,
943 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
944 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
945 return -1;
946 }
947 return 0;
948}
949
950/* This function should be called to parse a line starting with the "tcp-response"
951 * keyword.
952 */
953static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
954 struct proxy *defpx, const char *file, int line,
955 char **err)
956{
957 const char *ptr = NULL;
958 unsigned int val;
959 int warn = 0;
960 int arg;
961 struct act_rule *rule;
962 unsigned int where;
963 const struct acl *acl;
964 const char *kw;
965
966 if (!*args[1]) {
967 memprintf(err, "missing argument for '%s' in %s '%s'",
968 args[0], proxy_type_str(curpx), curpx->id);
969 return -1;
970 }
971
972 if (strcmp(args[1], "inspect-delay") == 0) {
973 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
974 memprintf(err, "%s %s is only allowed in 'backend' sections",
975 args[0], args[1]);
976 return -1;
977 }
978
979 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
980 memprintf(err,
981 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
982 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200983
984 if (ptr == PARSE_TIME_OVER)
985 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
986 else if (ptr == PARSE_TIME_UNDER)
987 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
988 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100989 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
990 return -1;
991 }
992
993 if (curpx->tcp_rep.inspect_delay) {
994 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
995 args[0], args[1], proxy_type_str(curpx), curpx->id);
996 return 1;
997 }
998 curpx->tcp_rep.inspect_delay = val;
999 return 0;
1000 }
1001
1002 rule = calloc(1, sizeof(*rule));
1003 LIST_INIT(&rule->list);
1004 arg = 1;
1005 where = 0;
1006
1007 if (strcmp(args[1], "content") == 0) {
1008 arg++;
1009
1010 if (curpx->cap & PR_CAP_FE)
1011 where |= SMP_VAL_FE_RES_CNT;
1012 if (curpx->cap & PR_CAP_BE)
1013 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001014 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001015 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1016 goto error;
1017
1018 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1019 if (acl) {
1020 if (acl->name && *acl->name)
1021 memprintf(err,
1022 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1023 acl->name, args[0], args[1], sample_ckp_names(where));
1024 else
1025 memprintf(err,
1026 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1027 args[0], args[1],
1028 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1029 sample_ckp_names(where));
1030
1031 warn++;
1032 }
1033 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1034 if (acl->name && *acl->name)
1035 memprintf(err,
1036 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1037 acl->name, kw, sample_ckp_names(where));
1038 else
1039 memprintf(err,
1040 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1041 kw, sample_ckp_names(where));
1042 warn++;
1043 }
1044
1045 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
1046 }
1047 else {
1048 memprintf(err,
1049 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
1050 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1051 goto error;
1052 }
1053
1054 return warn;
1055 error:
1056 free(rule);
1057 return -1;
1058}
1059
1060
1061/* This function should be called to parse a line starting with the "tcp-request"
1062 * keyword.
1063 */
1064static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1065 struct proxy *defpx, const char *file, int line,
1066 char **err)
1067{
1068 const char *ptr = NULL;
1069 unsigned int val;
1070 int warn = 0;
1071 int arg;
1072 struct act_rule *rule;
1073 unsigned int where;
1074 const struct acl *acl;
1075 const char *kw;
1076
1077 if (!*args[1]) {
1078 if (curpx == defpx)
1079 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1080 else
1081 memprintf(err, "missing argument for '%s' in %s '%s'",
1082 args[0], proxy_type_str(curpx), curpx->id);
1083 return -1;
1084 }
1085
1086 if (!strcmp(args[1], "inspect-delay")) {
1087 if (curpx == defpx) {
1088 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1089 args[0], args[1]);
1090 return -1;
1091 }
1092
1093 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1094 memprintf(err,
1095 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1096 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001097
1098 if (ptr == PARSE_TIME_OVER)
1099 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1100 else if (ptr == PARSE_TIME_UNDER)
1101 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1102 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001103 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1104 return -1;
1105 }
1106
1107 if (curpx->tcp_req.inspect_delay) {
1108 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1109 args[0], args[1], proxy_type_str(curpx), curpx->id);
1110 return 1;
1111 }
1112 curpx->tcp_req.inspect_delay = val;
1113 return 0;
1114 }
1115
1116 rule = calloc(1, sizeof(*rule));
1117 LIST_INIT(&rule->list);
1118 arg = 1;
1119 where = 0;
1120
1121 if (strcmp(args[1], "content") == 0) {
1122 arg++;
1123
1124 if (curpx->cap & PR_CAP_FE)
1125 where |= SMP_VAL_FE_REQ_CNT;
1126 if (curpx->cap & PR_CAP_BE)
1127 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001128 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001129 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1130 goto error;
1131
1132 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1133 if (acl) {
1134 if (acl->name && *acl->name)
1135 memprintf(err,
1136 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1137 acl->name, args[0], args[1], sample_ckp_names(where));
1138 else
1139 memprintf(err,
1140 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1141 args[0], args[1],
1142 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1143 sample_ckp_names(where));
1144
1145 warn++;
1146 }
1147 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1148 if (acl->name && *acl->name)
1149 memprintf(err,
1150 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1151 acl->name, kw, sample_ckp_names(where));
1152 else
1153 memprintf(err,
1154 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1155 kw, sample_ckp_names(where));
1156 warn++;
1157 }
1158
1159 /* the following function directly emits the warning */
1160 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1161 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1162 }
1163 else if (strcmp(args[1], "connection") == 0) {
1164 arg++;
1165
1166 if (!(curpx->cap & PR_CAP_FE)) {
1167 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1168 args[0], args[1], proxy_type_str(curpx), curpx->id);
1169 goto error;
1170 }
1171
1172 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001173 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001174 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1175 goto error;
1176
1177 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1178 if (acl) {
1179 if (acl->name && *acl->name)
1180 memprintf(err,
1181 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1182 acl->name, args[0], args[1], sample_ckp_names(where));
1183 else
1184 memprintf(err,
1185 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1186 args[0], args[1],
1187 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1188 sample_ckp_names(where));
1189
1190 warn++;
1191 }
1192 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1193 if (acl->name && *acl->name)
1194 memprintf(err,
1195 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1196 acl->name, kw, sample_ckp_names(where));
1197 else
1198 memprintf(err,
1199 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1200 kw, sample_ckp_names(where));
1201 warn++;
1202 }
1203
1204 /* the following function directly emits the warning */
1205 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1206 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1207 }
1208 else if (strcmp(args[1], "session") == 0) {
1209 arg++;
1210
1211 if (!(curpx->cap & PR_CAP_FE)) {
1212 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1213 args[0], args[1], proxy_type_str(curpx), curpx->id);
1214 goto error;
1215 }
1216
1217 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001218 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001219 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1220 goto error;
1221
1222 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1223 if (acl) {
1224 if (acl->name && *acl->name)
1225 memprintf(err,
1226 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1227 acl->name, args[0], args[1], sample_ckp_names(where));
1228 else
1229 memprintf(err,
1230 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1231 args[0], args[1],
1232 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1233 sample_ckp_names(where));
1234 warn++;
1235 }
1236 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1237 if (acl->name && *acl->name)
1238 memprintf(err,
1239 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1240 acl->name, kw, sample_ckp_names(where));
1241 else
1242 memprintf(err,
1243 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1244 kw, sample_ckp_names(where));
1245 warn++;
1246 }
1247
1248 /* the following function directly emits the warning */
1249 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1250 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1251 }
1252 else {
1253 if (curpx == defpx)
1254 memprintf(err,
1255 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1256 args[0], args[1]);
1257 else
1258 memprintf(err,
1259 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1260 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1261 goto error;
1262 }
1263
1264 return warn;
1265 error:
1266 free(rule);
1267 return -1;
1268}
1269
1270static struct cfg_kw_list cfg_kws = {ILH, {
1271 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1272 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1273 { 0, NULL, NULL },
1274}};
1275
Willy Tarreau0108d902018-11-25 19:14:37 +01001276INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001277
1278/*
1279 * Local variables:
1280 * c-indent-level: 8
1281 * c-basic-offset: 8
1282 * End:
1283 */