blob: ce3900e6b984f61720153091537003dd442d540c [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 Faulet491ab5e2019-12-13 09:31:00 +0100161 deny:
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100162 si_must_kill_conn(chn_prod(req));
Willy Tarreau39713102016-11-25 15:49:32 +0100163 channel_abort(req);
164 channel_abort(&s->res);
165 req->analysers = 0;
166
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100167 _HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
168 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100169 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100170 _HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100171
172 if (!(s->flags & SF_ERR_MASK))
173 s->flags |= SF_ERR_PRXCOND;
174 if (!(s->flags & SF_FINST_MASK))
175 s->flags |= SF_FINST_R;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100176 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 +0100177 return 0;
178 }
179 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
180 /* Note: only the first valid tracking parameter of each
181 * applies.
182 */
183 struct stktable_key *key;
184 struct sample smp;
185
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200186 if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100187 continue;
188
189 t = rule->arg.trk_ctr.table.t;
190 key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
191
192 if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
193 goto missing_data; /* key might appear later */
194
195 if (key && (ts = stktable_get_entry(t, key))) {
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200196 stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
197 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
Willy Tarreau39713102016-11-25 15:49:32 +0100198 if (sess->fe != s->be)
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200199 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
Willy Tarreau39713102016-11-25 15:49:32 +0100200 }
201 }
202 else if (rule->action == ACT_TCP_CAPTURE) {
203 struct sample *key;
204 struct cap_hdr *h = rule->arg.cap.hdr;
205 char **cap = s->req_cap;
206 int len;
207
208 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
209 if (!key)
210 continue;
211
212 if (key->flags & SMP_F_MAY_CHANGE)
213 goto missing_data;
214
215 if (cap[h->index] == NULL)
Willy Tarreaubafbe012017-11-24 17:34:44 +0100216 cap[h->index] = pool_alloc(h->pool);
Willy Tarreau39713102016-11-25 15:49:32 +0100217
218 if (cap[h->index] == NULL) /* no more capture memory */
219 continue;
220
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200221 len = key->data.u.str.data;
Willy Tarreau39713102016-11-25 15:49:32 +0100222 if (len > h->len)
223 len = h->len;
224
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200225 memcpy(cap[h->index], key->data.u.str.area,
226 len);
Willy Tarreau39713102016-11-25 15:49:32 +0100227 cap[h->index][len] = 0;
228 }
229 else {
230 /* Custom keywords. */
231 if (!rule->action_ptr)
232 continue;
233
234 if (partial & SMP_OPT_FINAL)
235 act_flags |= ACT_FLAG_FINAL;
236
237 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
238 case ACT_RET_ERR:
Christopher Faulet491ab5e2019-12-13 09:31:00 +0100239 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100240 case ACT_RET_CONT:
241 continue;
242 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200243 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100244 break;
245 case ACT_RET_YIELD:
246 s->current_rule = rule;
247 goto missing_data;
248 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200249 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100250 }
251 }
252 }
253
254 /* if we get there, it means we have no rule which matches, or
255 * we have an explicit accept, so we apply the default accept.
256 */
257 req->analysers &= ~an_bit;
258 req->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100259 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100260 return 1;
261
262 missing_data:
263 channel_dont_connect(req);
264 /* just set the request timeout once at the beginning of the request */
265 if (!tick_isset(req->analyse_exp) && s->be->tcp_req.inspect_delay)
266 req->analyse_exp = tick_add(now_ms, s->be->tcp_req.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100267 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100268 return 0;
269
270}
271
272/* This function performs the TCP response analysis on the current response. It
273 * returns 1 if the processing can continue on next analysers, or zero if it
274 * needs more data, encounters an error, or wants to immediately abort the
275 * response. It relies on buffers flags, and updates s->rep->analysers. The
276 * function may be called for backend rules.
277 */
278int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
279{
280 struct session *sess = s->sess;
281 struct act_rule *rule;
282 int partial;
283 int act_flags = 0;
284
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100285 DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100286
287 /* We don't know whether we have enough data, so must proceed
288 * this way :
289 * - iterate through all rules in their declaration order
290 * - if one rule returns MISS, it means the inspect delay is
291 * not over yet, then return immediately, otherwise consider
292 * it as a non-match.
293 * - if one rule returns OK, then return OK
294 * - if one rule returns KO, then return KO
295 */
296
297 if (rep->flags & CF_SHUTR || tick_is_expired(rep->analyse_exp, now_ms))
298 partial = SMP_OPT_FINAL;
299 else
300 partial = 0;
301
302 /* If "the current_rule_list" match the executed rule list, we are in
303 * resume condition. If a resume is needed it is always in the action
304 * and never in the ACL or converters. In this case, we initialise the
305 * current rule, and go to the action execution point.
306 */
307 if (s->current_rule) {
308 rule = s->current_rule;
309 s->current_rule = NULL;
310 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
311 goto resume_execution;
312 }
313 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
314
315 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
316 enum acl_test_res ret = ACL_TEST_PASS;
317
318 if (rule->cond) {
319 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
320 if (ret == ACL_TEST_MISS) {
321 /* just set the analyser timeout once at the beginning of the response */
322 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
323 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100324 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100325 return 0;
326 }
327
328 ret = acl_pass(ret);
329 if (rule->cond->pol == ACL_COND_UNLESS)
330 ret = !ret;
331 }
332
333 if (ret) {
334 act_flags |= ACT_FLAG_FIRST;
335resume_execution:
336 /* we have a matching rule. */
337 if (rule->action == ACT_ACTION_ALLOW) {
338 break;
339 }
340 else if (rule->action == ACT_ACTION_DENY) {
Christopher Faulet491ab5e2019-12-13 09:31:00 +0100341 deny:
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100342 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100343 channel_abort(rep);
344 channel_abort(&s->req);
345 rep->analysers = 0;
346
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100347 _HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
348 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100349 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100350 _HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100351
352 if (!(s->flags & SF_ERR_MASK))
353 s->flags |= SF_ERR_PRXCOND;
354 if (!(s->flags & SF_FINST_MASK))
355 s->flags |= SF_FINST_D;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100356 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 +0100357 return 0;
358 }
359 else if (rule->action == ACT_TCP_CLOSE) {
360 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
Willy Tarreau0f9cd7b2019-01-31 19:02:43 +0100361 si_must_kill_conn(chn_prod(rep));
Willy Tarreau39713102016-11-25 15:49:32 +0100362 si_shutr(chn_prod(rep));
363 si_shutw(chn_prod(rep));
364 break;
365 }
366 else {
367 /* Custom keywords. */
368 if (!rule->action_ptr)
369 continue;
370
371 if (partial & SMP_OPT_FINAL)
372 act_flags |= ACT_FLAG_FINAL;
373
374 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
375 case ACT_RET_ERR:
Christopher Faulet491ab5e2019-12-13 09:31:00 +0100376 goto deny;
Willy Tarreau39713102016-11-25 15:49:32 +0100377 case ACT_RET_CONT:
378 continue;
379 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200380 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100381 break;
382 case ACT_RET_YIELD:
383 channel_dont_close(rep);
384 s->current_rule = rule;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100385 DBG_TRACE_DEVEL("waiting for more data", STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100386 return 0;
387 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200388 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100389 }
390 }
391 }
392
393 /* if we get there, it means we have no rule which matches, or
394 * we have an explicit accept, so we apply the default accept.
395 */
396 rep->analysers &= ~an_bit;
397 rep->analyse_exp = TICK_ETERNITY;
Christopher Fauleteea8fc72019-11-05 16:18:10 +0100398 DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_TCP_ANA, s);
Willy Tarreau39713102016-11-25 15:49:32 +0100399 return 1;
400}
401
402
403/* This function performs the TCP layer4 analysis on the current request. It
404 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
405 * matches or if no more rule matches. It can only use rules which don't need
406 * any data. This only works on connection-based client-facing stream interfaces.
407 */
408int tcp_exec_l4_rules(struct session *sess)
409{
410 struct act_rule *rule;
411 struct stksess *ts;
412 struct stktable *t = NULL;
413 struct connection *conn = objt_conn(sess->origin);
414 int result = 1;
415 enum acl_test_res ret;
416
417 if (!conn)
418 return result;
419
420 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
421 ret = ACL_TEST_PASS;
422
423 if (rule->cond) {
424 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
425 ret = acl_pass(ret);
426 if (rule->cond->pol == ACL_COND_UNLESS)
427 ret = !ret;
428 }
429
430 if (ret) {
431 /* we have a matching rule. */
432 if (rule->action == ACT_ACTION_ALLOW) {
433 break;
434 }
435 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100436 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100437 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100438 _HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100439
440 result = 0;
441 break;
442 }
443 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
444 /* Note: only the first valid tracking parameter of each
445 * applies.
446 */
447 struct stktable_key *key;
448
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200449 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100450 continue;
451
452 t = rule->arg.trk_ctr.table.t;
453 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
454
455 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200456 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100457 }
458 else if (rule->action == ACT_TCP_EXPECT_PX) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200459 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
460 if (xprt_add_hs(conn) < 0) {
461 result = 0;
462 break;
463 }
464 }
Willy Tarreau39713102016-11-25 15:49:32 +0100465 conn->flags |= CO_FL_ACCEPT_PROXY;
Willy Tarreau39713102016-11-25 15:49:32 +0100466 }
467 else if (rule->action == ACT_TCP_EXPECT_CIP) {
Olivier Houchardfe50bfb2019-05-27 12:09:19 +0200468 if (!(conn->flags & (CO_FL_HANDSHAKE_NOSSL))) {
469 if (xprt_add_hs(conn) < 0) {
470 result = 0;
471 break;
472 }
473 }
Willy Tarreau39713102016-11-25 15:49:32 +0100474 conn->flags |= CO_FL_ACCEPT_CIP;
Willy Tarreau39713102016-11-25 15:49:32 +0100475 }
476 else {
477 /* Custom keywords. */
478 if (!rule->action_ptr)
479 break;
480 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
481 case ACT_RET_YIELD:
482 /* yield is not allowed at this point. If this return code is
483 * used it is a bug, so I prefer to abort the process.
484 */
485 send_log(sess->fe, LOG_WARNING,
486 "Internal error: yield not allowed with tcp-request connection actions.");
487 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200488 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100489 break;
490 case ACT_RET_CONT:
491 continue;
492 case ACT_RET_ERR:
493 result = 0;
494 break;
495 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200496 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100497 }
498 }
499 }
500 return result;
501}
502
503/* This function performs the TCP layer5 analysis on the current request. It
504 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
505 * matches or if no more rule matches. It can only use rules which don't need
506 * any data. This only works on session-based client-facing stream interfaces.
507 * An example of valid use case is to track a stick-counter on the source
508 * address extracted from the proxy protocol.
509 */
510int tcp_exec_l5_rules(struct session *sess)
511{
512 struct act_rule *rule;
513 struct stksess *ts;
514 struct stktable *t = NULL;
515 int result = 1;
516 enum acl_test_res ret;
517
518 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
519 ret = ACL_TEST_PASS;
520
521 if (rule->cond) {
522 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
523 ret = acl_pass(ret);
524 if (rule->cond->pol == ACL_COND_UNLESS)
525 ret = !ret;
526 }
527
528 if (ret) {
529 /* we have a matching rule. */
530 if (rule->action == ACT_ACTION_ALLOW) {
531 break;
532 }
533 else if (rule->action == ACT_ACTION_DENY) {
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100534 _HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
Willy Tarreaua12dde02016-12-22 18:14:41 +0100535 if (sess->listener && sess->listener->counters)
Olivier Houchard64dbb2d2019-03-08 18:55:10 +0100536 _HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
Willy Tarreau39713102016-11-25 15:49:32 +0100537
538 result = 0;
539 break;
540 }
541 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
542 /* Note: only the first valid tracking parameter of each
543 * applies.
544 */
545 struct stktable_key *key;
546
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200547 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
Willy Tarreau39713102016-11-25 15:49:32 +0100548 continue;
549
550 t = rule->arg.trk_ctr.table.t;
551 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
552
553 if (key && (ts = stktable_get_entry(t, key)))
Christopher Faulet4fce0d82017-09-18 11:57:31 +0200554 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
Willy Tarreau39713102016-11-25 15:49:32 +0100555 }
556 else {
557 /* Custom keywords. */
558 if (!rule->action_ptr)
559 break;
560 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
561 case ACT_RET_YIELD:
562 /* yield is not allowed at this point. If this return code is
563 * used it is a bug, so I prefer to abort the process.
564 */
565 send_log(sess->fe, LOG_WARNING,
566 "Internal error: yield not allowed with tcp-request session actions.");
567 case ACT_RET_STOP:
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200568 case ACT_RET_DONE:
Willy Tarreau39713102016-11-25 15:49:32 +0100569 break;
570 case ACT_RET_CONT:
571 continue;
572 case ACT_RET_ERR:
573 result = 0;
574 break;
575 }
Christopher Faulet2e4843d2019-07-04 11:08:38 +0200576 break; /* ACT_RET_STOP/DONE */
Willy Tarreau39713102016-11-25 15:49:32 +0100577 }
578 }
579 }
580 return result;
581}
582
583/* Parse a tcp-response rule. Return a negative value in case of failure */
584static int tcp_parse_response_rule(char **args, int arg, int section_type,
585 struct proxy *curpx, struct proxy *defpx,
586 struct act_rule *rule, char **err,
587 unsigned int where,
588 const char *file, int line)
589{
590 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
591 memprintf(err, "%s %s is only allowed in 'backend' sections",
592 args[0], args[1]);
593 return -1;
594 }
595
596 if (strcmp(args[arg], "accept") == 0) {
597 arg++;
598 rule->action = ACT_ACTION_ALLOW;
599 }
600 else if (strcmp(args[arg], "reject") == 0) {
601 arg++;
602 rule->action = ACT_ACTION_DENY;
603 }
604 else if (strcmp(args[arg], "close") == 0) {
605 arg++;
606 rule->action = ACT_TCP_CLOSE;
607 }
608 else {
609 struct action_kw *kw;
610 kw = tcp_res_cont_action(args[arg]);
611 if (kw) {
612 arg++;
Willy Tarreau39713102016-11-25 15:49:32 +0100613 rule->kw = kw;
614 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
615 return -1;
616 } else {
617 action_build_list(&tcp_res_cont_keywords, &trash);
618 memprintf(err,
619 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200620 args[0], args[1], trash.area,
621 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100622 return -1;
623 }
624 }
625
626 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200627 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100628 memprintf(err,
629 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
630 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
631 return -1;
632 }
633 }
634 else if (*args[arg]) {
635 memprintf(err,
636 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
637 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
638 return -1;
639 }
640 return 0;
641}
642
643
644
645/* Parse a tcp-request rule. Return a negative value in case of failure */
646static int tcp_parse_request_rule(char **args, int arg, int section_type,
647 struct proxy *curpx, struct proxy *defpx,
648 struct act_rule *rule, char **err,
649 unsigned int where, const char *file, int line)
650{
651 if (curpx == defpx) {
652 memprintf(err, "%s %s is not allowed in 'defaults' sections",
653 args[0], args[1]);
654 return -1;
655 }
656
657 if (!strcmp(args[arg], "accept")) {
658 arg++;
659 rule->action = ACT_ACTION_ALLOW;
660 }
661 else if (!strcmp(args[arg], "reject")) {
662 arg++;
663 rule->action = ACT_ACTION_DENY;
664 }
665 else if (strcmp(args[arg], "capture") == 0) {
666 struct sample_expr *expr;
667 struct cap_hdr *hdr;
668 int kw = arg;
669 int len = 0;
670
671 if (!(curpx->cap & PR_CAP_FE)) {
672 memprintf(err,
673 "'%s %s %s' : proxy '%s' has no frontend capability",
674 args[0], args[1], args[kw], curpx->id);
675 return -1;
676 }
677
678 if (!(where & SMP_VAL_FE_REQ_CNT)) {
679 memprintf(err,
680 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
681 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
682 return -1;
683 }
684
685 arg++;
686
687 curpx->conf.args.ctx = ARGC_CAP;
688 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
689 if (!expr) {
690 memprintf(err,
691 "'%s %s %s' : %s",
692 args[0], args[1], args[kw], *err);
693 return -1;
694 }
695
696 if (!(expr->fetch->val & where)) {
697 memprintf(err,
698 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
699 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
700 free(expr);
701 return -1;
702 }
703
704 if (strcmp(args[arg], "len") == 0) {
705 arg++;
706 if (!args[arg]) {
707 memprintf(err,
708 "'%s %s %s' : missing length value",
709 args[0], args[1], args[kw]);
710 free(expr);
711 return -1;
712 }
713 /* we copy the table name for now, it will be resolved later */
714 len = atoi(args[arg]);
715 if (len <= 0) {
716 memprintf(err,
717 "'%s %s %s' : length must be > 0",
718 args[0], args[1], args[kw]);
719 free(expr);
720 return -1;
721 }
722 arg++;
723 }
724
725 if (!len) {
726 memprintf(err,
727 "'%s %s %s' : a positive 'len' argument is mandatory",
728 args[0], args[1], args[kw]);
729 free(expr);
730 return -1;
731 }
732
733 hdr = calloc(1, sizeof(*hdr));
734 hdr->next = curpx->req_cap;
735 hdr->name = NULL; /* not a header capture */
736 hdr->namelen = 0;
737 hdr->len = len;
738 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
739 hdr->index = curpx->nb_req_cap++;
740
741 curpx->req_cap = hdr;
742 curpx->to_log |= LW_REQHDR;
743
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200744 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100745 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
746
747 rule->arg.cap.expr = expr;
748 rule->arg.cap.hdr = hdr;
749 rule->action = ACT_TCP_CAPTURE;
750 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100751 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100752 struct sample_expr *expr;
753 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100754 unsigned int tsc_num;
755 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100756
757 arg++;
758
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100759 tsc_num_str = &args[kw][8];
760 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
761 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
762 return -1;
763 }
764
Willy Tarreau39713102016-11-25 15:49:32 +0100765 curpx->conf.args.ctx = ARGC_TRK;
766 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
767 if (!expr) {
768 memprintf(err,
769 "'%s %s %s' : %s",
770 args[0], args[1], args[kw], *err);
771 return -1;
772 }
773
774 if (!(expr->fetch->val & where)) {
775 memprintf(err,
776 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
777 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
778 free(expr);
779 return -1;
780 }
781
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200782 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100783 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
784
785 if (strcmp(args[arg], "table") == 0) {
786 arg++;
787 if (!args[arg]) {
788 memprintf(err,
789 "'%s %s %s' : missing table name",
790 args[0], args[1], args[kw]);
791 free(expr);
792 return -1;
793 }
794 /* we copy the table name for now, it will be resolved later */
795 rule->arg.trk_ctr.table.n = strdup(args[arg]);
796 arg++;
797 }
798 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100799 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200800 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100801 }
802 else if (strcmp(args[arg], "expect-proxy") == 0) {
803 if (strcmp(args[arg+1], "layer4") != 0) {
804 memprintf(err,
805 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
806 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
807 return -1;
808 }
809
810 if (!(where & SMP_VAL_FE_CON_ACC)) {
811 memprintf(err,
812 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
813 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
814 return -1;
815 }
816
817 arg += 2;
818 rule->action = ACT_TCP_EXPECT_PX;
819 }
820 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
821 if (strcmp(args[arg+1], "layer4") != 0) {
822 memprintf(err,
823 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
824 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
825 return -1;
826 }
827
828 if (!(where & SMP_VAL_FE_CON_ACC)) {
829 memprintf(err,
830 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
831 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
832 return -1;
833 }
834
835 arg += 2;
836 rule->action = ACT_TCP_EXPECT_CIP;
837 }
838 else {
839 struct action_kw *kw;
840 if (where & SMP_VAL_FE_CON_ACC) {
841 /* L4 */
842 kw = tcp_req_conn_action(args[arg]);
843 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100844 } else if (where & SMP_VAL_FE_SES_ACC) {
845 /* L5 */
846 kw = tcp_req_sess_action(args[arg]);
847 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100848 } else {
849 /* L6 */
850 kw = tcp_req_cont_action(args[arg]);
851 rule->kw = kw;
Willy Tarreau39713102016-11-25 15:49:32 +0100852 }
853 if (kw) {
854 arg++;
855 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
856 return -1;
857 } else {
858 if (where & SMP_VAL_FE_CON_ACC)
859 action_build_list(&tcp_req_conn_keywords, &trash);
860 else if (where & SMP_VAL_FE_SES_ACC)
861 action_build_list(&tcp_req_sess_keywords, &trash);
862 else
863 action_build_list(&tcp_req_cont_keywords, &trash);
864 memprintf(err,
865 "'%s %s' expects 'accept', 'reject', 'track-sc0' ... 'track-sc%d', %s "
866 "in %s '%s' (got '%s').\n",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200867 args[0], args[1], MAX_SESS_STKCTR-1,
868 trash.area, proxy_type_str(curpx),
Willy Tarreau39713102016-11-25 15:49:32 +0100869 curpx->id, args[arg]);
870 return -1;
871 }
872 }
873
874 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200875 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100876 memprintf(err,
877 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
878 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
879 return -1;
880 }
881 }
882 else if (*args[arg]) {
883 memprintf(err,
884 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
885 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
886 return -1;
887 }
888 return 0;
889}
890
891/* This function should be called to parse a line starting with the "tcp-response"
892 * keyword.
893 */
894static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
895 struct proxy *defpx, const char *file, int line,
896 char **err)
897{
898 const char *ptr = NULL;
899 unsigned int val;
900 int warn = 0;
901 int arg;
902 struct act_rule *rule;
903 unsigned int where;
904 const struct acl *acl;
905 const char *kw;
906
907 if (!*args[1]) {
908 memprintf(err, "missing argument for '%s' in %s '%s'",
909 args[0], proxy_type_str(curpx), curpx->id);
910 return -1;
911 }
912
913 if (strcmp(args[1], "inspect-delay") == 0) {
914 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
915 memprintf(err, "%s %s is only allowed in 'backend' sections",
916 args[0], args[1]);
917 return -1;
918 }
919
920 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
921 memprintf(err,
922 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
923 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +0200924
925 if (ptr == PARSE_TIME_OVER)
926 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
927 else if (ptr == PARSE_TIME_UNDER)
928 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
929 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +0100930 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
931 return -1;
932 }
933
934 if (curpx->tcp_rep.inspect_delay) {
935 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
936 args[0], args[1], proxy_type_str(curpx), curpx->id);
937 return 1;
938 }
939 curpx->tcp_rep.inspect_delay = val;
940 return 0;
941 }
942
943 rule = calloc(1, sizeof(*rule));
944 LIST_INIT(&rule->list);
945 arg = 1;
946 where = 0;
947
948 if (strcmp(args[1], "content") == 0) {
949 arg++;
950
951 if (curpx->cap & PR_CAP_FE)
952 where |= SMP_VAL_FE_RES_CNT;
953 if (curpx->cap & PR_CAP_BE)
954 where |= SMP_VAL_BE_RES_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +0100955 rule->from = ACT_F_TCP_RES_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +0100956 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
957 goto error;
958
959 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
960 if (acl) {
961 if (acl->name && *acl->name)
962 memprintf(err,
963 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
964 acl->name, args[0], args[1], sample_ckp_names(where));
965 else
966 memprintf(err,
967 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
968 args[0], args[1],
969 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
970 sample_ckp_names(where));
971
972 warn++;
973 }
974 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
975 if (acl->name && *acl->name)
976 memprintf(err,
977 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
978 acl->name, kw, sample_ckp_names(where));
979 else
980 memprintf(err,
981 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
982 kw, sample_ckp_names(where));
983 warn++;
984 }
985
986 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
987 }
988 else {
989 memprintf(err,
990 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
991 args[0], proxy_type_str(curpx), curpx->id, args[1]);
992 goto error;
993 }
994
995 return warn;
996 error:
997 free(rule);
998 return -1;
999}
1000
1001
1002/* This function should be called to parse a line starting with the "tcp-request"
1003 * keyword.
1004 */
1005static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
1006 struct proxy *defpx, const char *file, int line,
1007 char **err)
1008{
1009 const char *ptr = NULL;
1010 unsigned int val;
1011 int warn = 0;
1012 int arg;
1013 struct act_rule *rule;
1014 unsigned int where;
1015 const struct acl *acl;
1016 const char *kw;
1017
1018 if (!*args[1]) {
1019 if (curpx == defpx)
1020 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
1021 else
1022 memprintf(err, "missing argument for '%s' in %s '%s'",
1023 args[0], proxy_type_str(curpx), curpx->id);
1024 return -1;
1025 }
1026
1027 if (!strcmp(args[1], "inspect-delay")) {
1028 if (curpx == defpx) {
1029 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1030 args[0], args[1]);
1031 return -1;
1032 }
1033
1034 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1035 memprintf(err,
1036 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1037 args[0], args[1], proxy_type_str(curpx), curpx->id);
Willy Tarreau9faebe32019-06-07 19:00:37 +02001038
1039 if (ptr == PARSE_TIME_OVER)
1040 memprintf(err, "%s (timer overflow in '%s', maximum value is 2147483647 ms or ~24.8 days)", *err, args[2]);
1041 else if (ptr == PARSE_TIME_UNDER)
1042 memprintf(err, "%s (timer underflow in '%s', minimum non-null value is 1 ms)", *err, args[2]);
1043 else if (ptr)
Willy Tarreau39713102016-11-25 15:49:32 +01001044 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1045 return -1;
1046 }
1047
1048 if (curpx->tcp_req.inspect_delay) {
1049 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1050 args[0], args[1], proxy_type_str(curpx), curpx->id);
1051 return 1;
1052 }
1053 curpx->tcp_req.inspect_delay = val;
1054 return 0;
1055 }
1056
1057 rule = calloc(1, sizeof(*rule));
1058 LIST_INIT(&rule->list);
1059 arg = 1;
1060 where = 0;
1061
1062 if (strcmp(args[1], "content") == 0) {
1063 arg++;
1064
1065 if (curpx->cap & PR_CAP_FE)
1066 where |= SMP_VAL_FE_REQ_CNT;
1067 if (curpx->cap & PR_CAP_BE)
1068 where |= SMP_VAL_BE_REQ_CNT;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001069 rule->from = ACT_F_TCP_REQ_CNT;
Willy Tarreau39713102016-11-25 15:49:32 +01001070 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1071 goto error;
1072
1073 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1074 if (acl) {
1075 if (acl->name && *acl->name)
1076 memprintf(err,
1077 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1078 acl->name, args[0], args[1], sample_ckp_names(where));
1079 else
1080 memprintf(err,
1081 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1082 args[0], args[1],
1083 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1084 sample_ckp_names(where));
1085
1086 warn++;
1087 }
1088 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1089 if (acl->name && *acl->name)
1090 memprintf(err,
1091 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1092 acl->name, kw, sample_ckp_names(where));
1093 else
1094 memprintf(err,
1095 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1096 kw, sample_ckp_names(where));
1097 warn++;
1098 }
1099
1100 /* the following function directly emits the warning */
1101 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1102 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1103 }
1104 else if (strcmp(args[1], "connection") == 0) {
1105 arg++;
1106
1107 if (!(curpx->cap & PR_CAP_FE)) {
1108 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1109 args[0], args[1], proxy_type_str(curpx), curpx->id);
1110 goto error;
1111 }
1112
1113 where |= SMP_VAL_FE_CON_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001114 rule->from = ACT_F_TCP_REQ_CON;
Willy Tarreau39713102016-11-25 15:49:32 +01001115 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1116 goto error;
1117
1118 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1119 if (acl) {
1120 if (acl->name && *acl->name)
1121 memprintf(err,
1122 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1123 acl->name, args[0], args[1], sample_ckp_names(where));
1124 else
1125 memprintf(err,
1126 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1127 args[0], args[1],
1128 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1129 sample_ckp_names(where));
1130
1131 warn++;
1132 }
1133 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1134 if (acl->name && *acl->name)
1135 memprintf(err,
1136 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1137 acl->name, kw, sample_ckp_names(where));
1138 else
1139 memprintf(err,
1140 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1141 kw, sample_ckp_names(where));
1142 warn++;
1143 }
1144
1145 /* the following function directly emits the warning */
1146 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1147 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1148 }
1149 else if (strcmp(args[1], "session") == 0) {
1150 arg++;
1151
1152 if (!(curpx->cap & PR_CAP_FE)) {
1153 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1154 args[0], args[1], proxy_type_str(curpx), curpx->id);
1155 goto error;
1156 }
1157
1158 where |= SMP_VAL_FE_SES_ACC;
Christopher Fauletcb9106b2019-12-19 15:23:17 +01001159 rule->from = ACT_F_TCP_REQ_SES;
Willy Tarreau39713102016-11-25 15:49:32 +01001160 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1161 goto error;
1162
1163 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1164 if (acl) {
1165 if (acl->name && *acl->name)
1166 memprintf(err,
1167 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1168 acl->name, args[0], args[1], sample_ckp_names(where));
1169 else
1170 memprintf(err,
1171 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1172 args[0], args[1],
1173 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1174 sample_ckp_names(where));
1175 warn++;
1176 }
1177 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1178 if (acl->name && *acl->name)
1179 memprintf(err,
1180 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1181 acl->name, kw, sample_ckp_names(where));
1182 else
1183 memprintf(err,
1184 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1185 kw, sample_ckp_names(where));
1186 warn++;
1187 }
1188
1189 /* the following function directly emits the warning */
1190 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1191 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1192 }
1193 else {
1194 if (curpx == defpx)
1195 memprintf(err,
1196 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1197 args[0], args[1]);
1198 else
1199 memprintf(err,
1200 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1201 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1202 goto error;
1203 }
1204
1205 return warn;
1206 error:
1207 free(rule);
1208 return -1;
1209}
1210
1211static struct cfg_kw_list cfg_kws = {ILH, {
1212 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1213 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1214 { 0, NULL, NULL },
1215}};
1216
Willy Tarreau0108d902018-11-25 19:14:37 +01001217INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreau39713102016-11-25 15:49:32 +01001218
1219/*
1220 * Local variables:
1221 * c-indent-level: 8
1222 * c-basic-offset: 8
1223 * End:
1224 */