blob: e87b70b8276f53141e57db7c5c85bc0347aca460 [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) {
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100161 si_must_kill_conn(chn_prod(req));
Willy Tarreau39713102016-11-25 15:49:32 +0100162 channel_abort(req);
163 channel_abort(&s->res);
164 req->analysers = 0;
165
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100166 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
167 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100168 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100169 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100170
171 if (!(s->flags & SF_ERR_MASK))
172 s->flags |= SF_ERR_PRXCOND;
173 if (!(s->flags & SF_FINST_MASK))
174 s->flags |= SF_FINST_R;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100175 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100176 return 0;
177 }
178 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
179 /* Note: only the first valid tracking parameter of each
180 * applies.
181 */
182 struct stktable_key *key;
183 struct sample smp;
184
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200185 if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100186 continue;
187
188 t = rule->arg.trk_ctr.table.t;
189 key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
190
191 if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
192 goto missing_data; /* key might appear later */
193
194 if (key && (ts = stktable_get_entry(t, key))) {
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200195 stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
196 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
Willy Tarreau39713102016-11-25 15:49:32 +0100197 if (sess->fe != s->be)
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200198 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
Willy Tarreau39713102016-11-25 15:49:32 +0100199 }
200 }
201 else if (rule->action == ACT_TCP_CAPTURE) {
202 struct sample *key;
203 struct cap_hdr *h = rule->arg.cap.hdr;
204 char **cap = s->req_cap;
205 int len;
206
207 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
208 if (!key)
209 continue;
210
211 if (key->flags & SMP_F_MAY_CHANGE)
212 goto missing_data;
213
214 if (cap[h->index] == NULL)
Willy Tarreaubafbe012017-11-24 17:34:44 +0100215 cap[h->index] = pool_alloc(h->pool);
Willy Tarreau39713102016-11-25 15:49:32 +0100216
217 if (cap[h->index] == NULL) /* no more capture memory */
218 continue;
219
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200220 len = key->data.u.str.data;
Willy Tarreau39713102016-11-25 15:49:32 +0100221 if (len > h->len)
222 len = h->len;
223
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200224 memcpy(cap[h->index], key->data.u.str.area,
225 len);
Willy Tarreau39713102016-11-25 15:49:32 +0100226 cap[h->index][len] = 0;
227 }
228 else {
229 /* Custom keywords. */
230 if (!rule->action_ptr)
231 continue;
232
233 if (partial & SMP_OPT_FINAL)
234 act_flags |= ACT_FLAG_FINAL;
235
236 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
237 case ACT_RET_ERR:
238 case ACT_RET_CONT:
239 continue;
240 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200241 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100242 break;
243 case ACT_RET_YIELD:
244 s->current_rule = rule;
245 goto missing_data;
246 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200247 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100248 }
249 }
250 }
251
252 /* if we get there, it means we have no rule which matches, or
253 * we have an explicit accept, so we apply the default accept.
254 */
255 req->analysers &= ~an_bit;
256 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100257 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100258 return 1;
259
260 missing_data:
261 channel_dont_connect(req);
262 /* just set the request timeout once at the beginning of the request */
263 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
264 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100265 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100266 return 0;
267
268}
269
270/* This function performs the TCP response analysis on the current response. It
271 * returns 1 if the processing can continue on next analysers, or zero if it
272 * needs more data, encounters an error, or wants to immediately abort the
273 * response. It relies on buffers flags, and updates s->rep->analysers. The
274 * function may be called for backend rules.
275 */
276int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
277{
278 struct session *sess = s->sess;
279 struct act_rule *rule;
280 int partial;
281 int act_flags = 0;
282
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100283 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100284
285 /* We don't know whether we have enough data, so must proceed
286 * this way :
287 * - iterate through all rules in their declaration order
288 * - if one rule returns MISS, it means the inspect delay is
289 * not over yet, then return immediately, otherwise consider
290 * it as a non-match.
291 * - if one rule returns OK, then return OK
292 * - if one rule returns KO, then return KO
293 */
294
295 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
296 partial = SMP_OPT_FINAL;
297 else
298 partial = 0;
299
300 /* If "the current_rule_list" match the executed rule list, we are in
301 * resume condition. If a resume is needed it is always in the action
302 * and never in the ACL or converters. In this case, we initialise the
303 * current rule, and go to the action execution point.
304 */
305 if (s->current_rule) {
306 rule = s->current_rule;
307 s->current_rule = NULL;
308 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
309 goto resume_execution;
310 }
311 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
312
313 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
314 enum acl_test_res ret = ACL_TEST_PASS;
315
316 if (rule->cond) {
317 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
318 if (ret == ACL_TEST_MISS) {
319 /* just set the analyser timeout once at the beginning of the response */
320 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
321 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100322 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100323 return 0;
324 }
325
326 ret = acl_pass(ret);
327 if (rule->cond->pol == ACL_COND_UNLESS)
328 ret = !ret;
329 }
330
331 if (ret) {
332 act_flags |= ACT_FLAG_FIRST;
333resume_execution:
334 /* we have a matching rule. */
335 if (rule->action == ACT_ACTION_ALLOW) {
336 break;
337 }
338 else if (rule->action == ACT_ACTION_DENY) {
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100339 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100340 channel_abort(rep);
341 channel_abort(&s->req);
342 rep->analysers = 0;
343
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100344 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
345 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100346 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100347 _HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100348
349 if (!(s->flags & SF_ERR_MASK))
350 s->flags |= SF_ERR_PRXCOND;
351 if (!(s->flags & SF_FINST_MASK))
352 s->flags |= SF_FINST_D;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100353 DBG_TRACE_DEVEL("leaving on error", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA|STRM_EV_TCP_ERR, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100354 return 0;
355 }
356 else if (rule->action == ACT_TCP_CLOSE) {
357 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100358 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100359 si_shutr(chn_prod(rep));
360 si_shutw(chn_prod(rep));
361 break;
362 }
363 else {
364 /* Custom keywords. */
365 if (!rule->action_ptr)
366 continue;
367
368 if (partial & SMP_OPT_FINAL)
369 act_flags |= ACT_FLAG_FINAL;
370
371 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
372 case ACT_RET_ERR:
373 case ACT_RET_CONT:
374 continue;
375 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200376 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100377 break;
378 case ACT_RET_YIELD:
379 channel_dont_close(rep);
380 s->current_rule = rule;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100381 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100382 return 0;
383 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200384 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100385 }
386 }
387 }
388
389 /* if we get there, it means we have no rule which matches, or
390 * we have an explicit accept, so we apply the default accept.
391 */
392 rep->analysers &= ~an_bit;
393 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100394 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100395 return 1;
396}
397
398
399/* This function performs the TCP layer4 analysis on the current request. It
400 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
401 * matches or if no more rule matches. It can only use rules which don't need
402 * any data. This only works on connection-based client-facing stream interfaces.
403 */
404int tcp_exec_l4_rules(struct session *sess)
405{
406 struct act_rule *rule;
407 struct stksess *ts;
408 struct stktable *t = NULL;
409 struct connection *conn = objt_conn(sess->origin);
410 int result = 1;
411 enum acl_test_res ret;
412
413 if (!conn)
414 return result;
415
416 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
417 ret = ACL_TEST_PASS;
418
419 if (rule->cond) {
420 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
421 ret = acl_pass(ret);
422 if (rule->cond->pol == ACL_COND_UNLESS)
423 ret = !ret;
424 }
425
426 if (ret) {
427 /* we have a matching rule. */
428 if (rule->action == ACT_ACTION_ALLOW) {
429 break;
430 }
431 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100432 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100433 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100434 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100435
436 result = 0;
437 break;
438 }
439 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
440 /* Note: only the first valid tracking parameter of each
441 * applies.
442 */
443 struct stktable_key *key;
444
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200445 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100446 continue;
447
448 t = rule->arg.trk_ctr.table.t;
449 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
450
451 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200452 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100453 }
454 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200455 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
456 if (xprt_add_hs(conn) < 0) {
457 result = 0;
458 break;
459 }
460 }
Willy Tarreau39713102016-11-25 15:49:32 +0100461 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100462 }
463 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200464 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
465 if (xprt_add_hs(conn) < 0) {
466 result = 0;
467 break;
468 }
469 }
Willy Tarreau39713102016-11-25 15:49:32 +0100470 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100471 }
472 else {
473 /* Custom keywords. */
474 if (!rule->action_ptr)
475 break;
476 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
477 case ACT_RET_YIELD:
478 /* yield is not allowed at this point. If this return code is
479 * used it is a bug, so I prefer to abort the process.
480 */
481 send_log(sess->fe, LOG_WARNING,
482 "Internal error: yield not allowed with tcp-request connection actions.");
483 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200484 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100485 break;
486 case ACT_RET_CONT:
487 continue;
488 case ACT_RET_ERR:
489 result = 0;
490 break;
491 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200492 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100493 }
494 }
495 }
496 return result;
497}
498
499/* This function performs the TCP layer5 analysis on the current request. It
500 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
501 * matches or if no more rule matches. It can only use rules which don't need
502 * any data. This only works on session-based client-facing stream interfaces.
503 * An example of valid use case is to track a stick-counter on the source
504 * address extracted from the proxy protocol.
505 */
506int tcp_exec_l5_rules(struct session *sess)
507{
508 struct act_rule *rule;
509 struct stksess *ts;
510 struct stktable *t = NULL;
511 int result = 1;
512 enum acl_test_res ret;
513
514 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
515 ret = ACL_TEST_PASS;
516
517 if (rule->cond) {
518 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
519 ret = acl_pass(ret);
520 if (rule->cond->pol == ACL_COND_UNLESS)
521 ret = !ret;
522 }
523
524 if (ret) {
525 /* we have a matching rule. */
526 if (rule->action == ACT_ACTION_ALLOW) {
527 break;
528 }
529 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100530 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100531 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100532 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100533
534 result = 0;
535 break;
536 }
537 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
538 /* Note: only the first valid tracking parameter of each
539 * applies.
540 */
541 struct stktable_key *key;
542
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200543 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100544 continue;
545
546 t = rule->arg.trk_ctr.table.t;
547 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
548
549 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200550 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100551 }
552 else {
553 /* Custom keywords. */
554 if (!rule->action_ptr)
555 break;
556 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
557 case ACT_RET_YIELD:
558 /* yield is not allowed at this point. If this return code is
559 * used it is a bug, so I prefer to abort the process.
560 */
561 send_log(sess->fe, LOG_WARNING,
562 "Internal error: yield not allowed with tcp-request session actions.");
563 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200564 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100565 break;
566 case ACT_RET_CONT:
567 continue;
568 case ACT_RET_ERR:
569 result = 0;
570 break;
571 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200572 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100573 }
574 }
575 }
576 return result;
577}
578
579/* Parse a tcp-response rule. Return a negative value in case of failure */
580static int tcp_parse_response_rule(char **args, int arg, int section_type,
581 struct proxy *curpx, struct proxy *defpx,
582 struct act_rule *rule, char **err,
583 unsigned int where,
584 const char *file, int line)
585{
586 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
587 memprintf(err, "%s %s is only allowed in 'backend' sections",
588 args[0], args[1]);
589 return -1;
590 }
591
592 if (strcmp(args[arg], "accept") == 0) {
593 arg++;
594 rule->action = ACT_ACTION_ALLOW;
595 }
596 else if (strcmp(args[arg], "reject") == 0) {
597 arg++;
598 rule->action = ACT_ACTION_DENY;
599 }
600 else if (strcmp(args[arg], "close") == 0) {
601 arg++;
602 rule->action = ACT_TCP_CLOSE;
603 }
604 else {
605 struct action_kw *kw;
606 kw = tcp_res_cont_action(args[arg]);
607 if (kw) {
608 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100609 rule->kw = kw;
610 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
611 return -1;
612 } else {
613 action_build_list(&tcp_res_cont_keywords, &trash);
614 memprintf(err,
615 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200616 args[0], args[1], trash.area,
617 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100618 return -1;
619 }
620 }
621
622 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200623 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100624 memprintf(err,
625 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
626 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
627 return -1;
628 }
629 }
630 else if (*args[arg]) {
631 memprintf(err,
632 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
633 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
634 return -1;
635 }
636 return 0;
637}
638
639
640
641/* Parse a tcp-request rule. Return a negative value in case of failure */
642static int tcp_parse_request_rule(char **args, int arg, int section_type,
643 struct proxy *curpx, struct proxy *defpx,
644 struct act_rule *rule, char **err,
645 unsigned int where, const char *file, int line)
646{
647 if (curpx == defpx) {
648 memprintf(err, "%s %s is not allowed in 'defaults' sections",
649 args[0], args[1]);
650 return -1;
651 }
652
653 if (!strcmp(args[arg], "accept")) {
654 arg++;
655 rule->action = ACT_ACTION_ALLOW;
656 }
657 else if (!strcmp(args[arg], "reject")) {
658 arg++;
659 rule->action = ACT_ACTION_DENY;
660 }
661 else if (strcmp(args[arg], "capture") == 0) {
662 struct sample_expr *expr;
663 struct cap_hdr *hdr;
664 int kw = arg;
665 int len = 0;
666
667 if (!(curpx->cap & PR_CAP_FE)) {
668 memprintf(err,
669 "'%s %s %s' : proxy '%s' has no frontend capability",
670 args[0], args[1], args[kw], curpx->id);
671 return -1;
672 }
673
674 if (!(where & SMP_VAL_FE_REQ_CNT)) {
675 memprintf(err,
676 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
677 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
678 return -1;
679 }
680
681 arg++;
682
683 curpx->conf.args.ctx = ARGC_CAP;
684 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
685 if (!expr) {
686 memprintf(err,
687 "'%s %s %s' : %s",
688 args[0], args[1], args[kw], *err);
689 return -1;
690 }
691
692 if (!(expr->fetch->val & where)) {
693 memprintf(err,
694 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
695 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
696 free(expr);
697 return -1;
698 }
699
700 if (strcmp(args[arg], "len") == 0) {
701 arg++;
702 if (!args[arg]) {
703 memprintf(err,
704 "'%s %s %s' : missing length value",
705 args[0], args[1], args[kw]);
706 free(expr);
707 return -1;
708 }
709 /* we copy the table name for now, it will be resolved later */
710 len = atoi(args[arg]);
711 if (len <= 0) {
712 memprintf(err,
713 "'%s %s %s' : length must be > 0",
714 args[0], args[1], args[kw]);
715 free(expr);
716 return -1;
717 }
718 arg++;
719 }
720
721 if (!len) {
722 memprintf(err,
723 "'%s %s %s' : a positive 'len' argument is mandatory",
724 args[0], args[1], args[kw]);
725 free(expr);
726 return -1;
727 }
728
729 hdr = calloc(1, sizeof(*hdr));
730 hdr->next = curpx->req_cap;
731 hdr->name = NULL; /* not a header capture */
732 hdr->namelen = 0;
733 hdr->len = len;
734 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
735 hdr->index = curpx->nb_req_cap++;
736
737 curpx->req_cap = hdr;
738 curpx->to_log |= LW_REQHDR;
739
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200740 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100741 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
742
743 rule->arg.cap.expr = expr;
744 rule->arg.cap.hdr = hdr;
745 rule->action = ACT_TCP_CAPTURE;
746 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100747 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100748 struct sample_expr *expr;
749 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100750 unsigned int tsc_num;
751 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100752
753 arg++;
754
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100755 tsc_num_str = &args[kw][8];
756 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
757 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
758 return -1;
759 }
760
Willy Tarreau39713102016-11-25 15:49:32 +0100761 curpx->conf.args.ctx = ARGC_TRK;
762 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
763 if (!expr) {
764 memprintf(err,
765 "'%s %s %s' : %s",
766 args[0], args[1], args[kw], *err);
767 return -1;
768 }
769
770 if (!(expr->fetch->val & where)) {
771 memprintf(err,
772 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
773 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
774 free(expr);
775 return -1;
776 }
777
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200778 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100779 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
780
781 if (strcmp(args[arg], "table") == 0) {
782 arg++;
783 if (!args[arg]) {
784 memprintf(err,
785 "'%s %s %s' : missing table name",
786 args[0], args[1], args[kw]);
787 free(expr);
788 return -1;
789 }
790 /* we copy the table name for now, it will be resolved later */
791 rule->arg.trk_ctr.table.n = strdup(args[arg]);
792 arg++;
793 }
794 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100795 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200796 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100797 }
798 else if (strcmp(args[arg], "expect-proxy") == 0) {
799 if (strcmp(args[arg+1], "layer4") != 0) {
800 memprintf(err,
801 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
802 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
803 return -1;
804 }
805
806 if (!(where & SMP_VAL_FE_CON_ACC)) {
807 memprintf(err,
808 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
809 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
810 return -1;
811 }
812
813 arg += 2;
814 rule->action = ACT_TCP_EXPECT_PX;
815 }
816 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
817 if (strcmp(args[arg+1], "layer4") != 0) {
818 memprintf(err,
819 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
820 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
821 return -1;
822 }
823
824 if (!(where & SMP_VAL_FE_CON_ACC)) {
825 memprintf(err,
826 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
827 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
828 return -1;
829 }
830
831 arg += 2;
832 rule->action = ACT_TCP_EXPECT_CIP;
833 }
834 else {
835 struct action_kw *kw;
836 if (where & SMP_VAL_FE_CON_ACC) {
837 /* L4 */
838 kw = tcp_req_conn_action(args[arg]);
839 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100840 } else if (where & SMP_VAL_FE_SES_ACC) {
841 /* L5 */
842 kw = tcp_req_sess_action(args[arg]);
843 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100844 } else {
845 /* L6 */
846 kw = tcp_req_cont_action(args[arg]);
847 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100848 }
849 if (kw) {
850 arg++;
851 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
852 return -1;
853 } else {
854 if (where & SMP_VAL_FE_CON_ACC)
855 action_build_list(&tcp_req_conn_keywords, &trash);
856 else if (where & SMP_VAL_FE_SES_ACC)
857 action_build_list(&tcp_req_sess_keywords, &trash);
858 else
859 action_build_list(&tcp_req_cont_keywords, &trash);
860 memprintf(err,
861 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
862 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200863 args[0], args[1], MAX_SESS_STKCTR-1,
864 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100865 curpx->id, args[arg]);
866 return -1;
867 }
868 }
869
870 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200871 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100872 memprintf(err,
873 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
874 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
875 return -1;
876 }
877 }
878 else if (*args[arg]) {
879 memprintf(err,
880 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
881 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
882 return -1;
883 }
884 return 0;
885}
886
887/* This function should be called to parse a line starting with the "tcp-response"
888 * keyword.
889 */
890static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
891 struct proxy *defpx, const char *file, int line,
892 char **err)
893{
894 const char *ptr = NULL;
895 unsigned int val;
896 int warn = 0;
897 int arg;
898 struct act_rule *rule;
899 unsigned int where;
900 const struct acl *acl;
901 const char *kw;
902
903 if (!*args[1]) {
904 memprintf(err, "missing argument for '%s' in %s '%s'",
905 args[0], proxy_type_str(curpx), curpx->id);
906 return -1;
907 }
908
909 if (strcmp(args[1], "inspect-delay") == 0) {
910 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
911 memprintf(err, "%s %s is only allowed in 'backend' sections",
912 args[0], args[1]);
913 return -1;
914 }
915
916 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
917 memprintf(err,
918 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
919 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200920
921 if (ptr == PARSE_TIME_OVER)
922 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
923 else if (ptr == PARSE_TIME_UNDER)
924 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
925 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100926 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
927 return -1;
928 }
929
930 if (curpx->tcp_rep.inspect_delay) {
931 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
932 args[0], args[1], proxy_type_str(curpx), curpx->id);
933 return 1;
934 }
935 curpx->tcp_rep.inspect_delay = val;
936 return 0;
937 }
938
939 rule = calloc(1, sizeof(*rule));
940 LIST_INIT(&rule->list);
941 arg = 1;
942 where = 0;
943
944 if (strcmp(args[1], "content") == 0) {
945 arg++;
946
947 if (curpx->cap & PR_CAP_FE)
948 where |= SMP_VAL_FE_RES_CNT;
949 if (curpx->cap & PR_CAP_BE)
950 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +0100951 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +0100952 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
953 goto error;
954
955 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
956 if (acl) {
957 if (acl->name && *acl->name)
958 memprintf(err,
959 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
960 acl->name, args[0], args[1], sample_ckp_names(where));
961 else
962 memprintf(err,
963 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
964 args[0], args[1],
965 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
966 sample_ckp_names(where));
967
968 warn++;
969 }
970 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
971 if (acl->name && *acl->name)
972 memprintf(err,
973 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
974 acl->name, kw, sample_ckp_names(where));
975 else
976 memprintf(err,
977 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
978 kw, sample_ckp_names(where));
979 warn++;
980 }
981
982 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
983 }
984 else {
985 memprintf(err,
986 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
987 args[0], proxy_type_str(curpx), curpx->id, args[1]);
988 goto error;
989 }
990
991 return warn;
992 error:
993 free(rule);
994 return -1;
995}
996
997
998/* This function should be called to parse a line starting with the "tcp-request"
999 * keyword.
1000 */
1001static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1002 struct proxy *defpx, const char *file, int line,
1003 char **err)
1004{
1005 const char *ptr = NULL;
1006 unsigned int val;
1007 int warn = 0;
1008 int arg;
1009 struct act_rule *rule;
1010 unsigned int where;
1011 const struct acl *acl;
1012 const char *kw;
1013
1014 if (!*args[1]) {
1015 if (curpx == defpx)
1016 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1017 else
1018 memprintf(err, "missing argument for '%s' in %s '%s'",
1019 args[0], proxy_type_str(curpx), curpx->id);
1020 return -1;
1021 }
1022
1023 if (!strcmp(args[1], "inspect-delay")) {
1024 if (curpx == defpx) {
1025 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1026 args[0], args[1]);
1027 return -1;
1028 }
1029
1030 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1031 memprintf(err,
1032 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1033 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001034
1035 if (ptr == PARSE_TIME_OVER)
1036 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1037 else if (ptr == PARSE_TIME_UNDER)
1038 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1039 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001040 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1041 return -1;
1042 }
1043
1044 if (curpx->tcp_req.inspect_delay) {
1045 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1046 args[0], args[1], proxy_type_str(curpx), curpx->id);
1047 return 1;
1048 }
1049 curpx->tcp_req.inspect_delay = val;
1050 return 0;
1051 }
1052
1053 rule = calloc(1, sizeof(*rule));
1054 LIST_INIT(&rule->list);
1055 arg = 1;
1056 where = 0;
1057
1058 if (strcmp(args[1], "content") == 0) {
1059 arg++;
1060
1061 if (curpx->cap & PR_CAP_FE)
1062 where |= SMP_VAL_FE_REQ_CNT;
1063 if (curpx->cap & PR_CAP_BE)
1064 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001065 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001066 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1067 goto error;
1068
1069 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1070 if (acl) {
1071 if (acl->name && *acl->name)
1072 memprintf(err,
1073 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1074 acl->name, args[0], args[1], sample_ckp_names(where));
1075 else
1076 memprintf(err,
1077 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1078 args[0], args[1],
1079 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1080 sample_ckp_names(where));
1081
1082 warn++;
1083 }
1084 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1085 if (acl->name && *acl->name)
1086 memprintf(err,
1087 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1088 acl->name, kw, sample_ckp_names(where));
1089 else
1090 memprintf(err,
1091 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1092 kw, sample_ckp_names(where));
1093 warn++;
1094 }
1095
1096 /* the following function directly emits the warning */
1097 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1098 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1099 }
1100 else if (strcmp(args[1], "connection") == 0) {
1101 arg++;
1102
1103 if (!(curpx->cap & PR_CAP_FE)) {
1104 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1105 args[0], args[1], proxy_type_str(curpx), curpx->id);
1106 goto error;
1107 }
1108
1109 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001110 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001111 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1112 goto error;
1113
1114 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1115 if (acl) {
1116 if (acl->name && *acl->name)
1117 memprintf(err,
1118 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1119 acl->name, args[0], args[1], sample_ckp_names(where));
1120 else
1121 memprintf(err,
1122 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1123 args[0], args[1],
1124 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1125 sample_ckp_names(where));
1126
1127 warn++;
1128 }
1129 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1130 if (acl->name && *acl->name)
1131 memprintf(err,
1132 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1133 acl->name, kw, sample_ckp_names(where));
1134 else
1135 memprintf(err,
1136 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1137 kw, sample_ckp_names(where));
1138 warn++;
1139 }
1140
1141 /* the following function directly emits the warning */
1142 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1143 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1144 }
1145 else if (strcmp(args[1], "session") == 0) {
1146 arg++;
1147
1148 if (!(curpx->cap & PR_CAP_FE)) {
1149 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1150 args[0], args[1], proxy_type_str(curpx), curpx->id);
1151 goto error;
1152 }
1153
1154 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001155 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001156 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1157 goto error;
1158
1159 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1160 if (acl) {
1161 if (acl->name && *acl->name)
1162 memprintf(err,
1163 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1164 acl->name, args[0], args[1], sample_ckp_names(where));
1165 else
1166 memprintf(err,
1167 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1168 args[0], args[1],
1169 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1170 sample_ckp_names(where));
1171 warn++;
1172 }
1173 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1174 if (acl->name && *acl->name)
1175 memprintf(err,
1176 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1177 acl->name, kw, sample_ckp_names(where));
1178 else
1179 memprintf(err,
1180 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1181 kw, sample_ckp_names(where));
1182 warn++;
1183 }
1184
1185 /* the following function directly emits the warning */
1186 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1187 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1188 }
1189 else {
1190 if (curpx == defpx)
1191 memprintf(err,
1192 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1193 args[0], args[1]);
1194 else
1195 memprintf(err,
1196 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1197 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1198 goto error;
1199 }
1200
1201 return warn;
1202 error:
1203 free(rule);
1204 return -1;
1205}
1206
1207static struct cfg_kw_list cfg_kws = {ILH, {
1208 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1209 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1210 { 0, NULL, NULL },
1211}};
1212
Willy Tarreau0108d902018-11-25 19:14:37 +01001213INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001214
1215/*
1216 * Local variables:
1217 * c-indent-level: 8
1218 * c-basic-offset: 8
1219 * End:
1220 */