blob: 86b4df53818e45279566e99f14a4e5e4f599acf9 [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++;
609 rule->from = ACT_F_TCP_RES_CNT;
610 rule->kw = kw;
611 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
612 return -1;
613 } else {
614 action_build_list(&tcp_res_cont_keywords, &trash);
615 memprintf(err,
616 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200617 args[0], args[1], trash.area,
618 proxy_type_str(curpx), curpx->id, args[arg]);
Willy Tarreau39713102016-11-25 15:49:32 +0100619 return -1;
620 }
621 }
622
623 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
Christopher Faulet1b421ea2017-09-22 14:38:56 +0200624 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
Willy Tarreau39713102016-11-25 15:49:32 +0100625 memprintf(err,
626 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
627 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
628 return -1;
629 }
630 }
631 else if (*args[arg]) {
632 memprintf(err,
633 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
634 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
635 return -1;
636 }
637 return 0;
638}
639
640
641
642/* Parse a tcp-request rule. Return a negative value in case of failure */
643static int tcp_parse_request_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, const char *file, int line)
647{
648 if (curpx == defpx) {
649 memprintf(err, "%s %s is not allowed in 'defaults' sections",
650 args[0], args[1]);
651 return -1;
652 }
653
654 if (!strcmp(args[arg], "accept")) {
655 arg++;
656 rule->action = ACT_ACTION_ALLOW;
657 }
658 else if (!strcmp(args[arg], "reject")) {
659 arg++;
660 rule->action = ACT_ACTION_DENY;
661 }
662 else if (strcmp(args[arg], "capture") == 0) {
663 struct sample_expr *expr;
664 struct cap_hdr *hdr;
665 int kw = arg;
666 int len = 0;
667
668 if (!(curpx->cap & PR_CAP_FE)) {
669 memprintf(err,
670 "'%s %s %s' : proxy '%s' has no frontend capability",
671 args[0], args[1], args[kw], curpx->id);
672 return -1;
673 }
674
675 if (!(where & SMP_VAL_FE_REQ_CNT)) {
676 memprintf(err,
677 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
678 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
679 return -1;
680 }
681
682 arg++;
683
684 curpx->conf.args.ctx = ARGC_CAP;
685 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
686 if (!expr) {
687 memprintf(err,
688 "'%s %s %s' : %s",
689 args[0], args[1], args[kw], *err);
690 return -1;
691 }
692
693 if (!(expr->fetch->val & where)) {
694 memprintf(err,
695 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
696 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
697 free(expr);
698 return -1;
699 }
700
701 if (strcmp(args[arg], "len") == 0) {
702 arg++;
703 if (!args[arg]) {
704 memprintf(err,
705 "'%s %s %s' : missing length value",
706 args[0], args[1], args[kw]);
707 free(expr);
708 return -1;
709 }
710 /* we copy the table name for now, it will be resolved later */
711 len = atoi(args[arg]);
712 if (len <= 0) {
713 memprintf(err,
714 "'%s %s %s' : length must be > 0",
715 args[0], args[1], args[kw]);
716 free(expr);
717 return -1;
718 }
719 arg++;
720 }
721
722 if (!len) {
723 memprintf(err,
724 "'%s %s %s' : a positive 'len' argument is mandatory",
725 args[0], args[1], args[kw]);
726 free(expr);
727 return -1;
728 }
729
730 hdr = calloc(1, sizeof(*hdr));
731 hdr->next = curpx->req_cap;
732 hdr->name = NULL; /* not a header capture */
733 hdr->namelen = 0;
734 hdr->len = len;
735 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
736 hdr->index = curpx->nb_req_cap++;
737
738 curpx->req_cap = hdr;
739 curpx->to_log |= LW_REQHDR;
740
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200741 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100742 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
743
744 rule->arg.cap.expr = expr;
745 rule->arg.cap.hdr = hdr;
746 rule->action = ACT_TCP_CAPTURE;
747 }
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100748 else if (strncmp(args[arg], "track-sc", 8) == 0) {
Willy Tarreau39713102016-11-25 15:49:32 +0100749 struct sample_expr *expr;
750 int kw = arg;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100751 unsigned int tsc_num;
752 const char *tsc_num_str;
Willy Tarreau39713102016-11-25 15:49:32 +0100753
754 arg++;
755
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100756 tsc_num_str = &args[kw][8];
757 if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1) {
758 memprintf(err, "'%s %s %s' : %s", args[0], args[1], args[kw], *err);
759 return -1;
760 }
761
Willy Tarreau39713102016-11-25 15:49:32 +0100762 curpx->conf.args.ctx = ARGC_TRK;
763 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
764 if (!expr) {
765 memprintf(err,
766 "'%s %s %s' : %s",
767 args[0], args[1], args[kw], *err);
768 return -1;
769 }
770
771 if (!(expr->fetch->val & where)) {
772 memprintf(err,
773 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
774 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
775 free(expr);
776 return -1;
777 }
778
Christopher Faulet711ed6a2019-07-16 14:16:10 +0200779 /* check if we need to allocate an http_txn struct for HTTP parsing */
Willy Tarreau39713102016-11-25 15:49:32 +0100780 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
781
782 if (strcmp(args[arg], "table") == 0) {
783 arg++;
784 if (!args[arg]) {
785 memprintf(err,
786 "'%s %s %s' : missing table name",
787 args[0], args[1], args[kw]);
788 free(expr);
789 return -1;
790 }
791 /* we copy the table name for now, it will be resolved later */
792 rule->arg.trk_ctr.table.n = strdup(args[arg]);
793 arg++;
794 }
795 rule->arg.trk_ctr.expr = expr;
Frédéric Lécaillea41d5312018-01-29 12:05:07 +0100796 rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
Christopher Faulet78880fb2017-09-18 14:43:55 +0200797 rule->check_ptr = check_trk_action;
Willy Tarreau39713102016-11-25 15:49:32 +0100798 }
799 else if (strcmp(args[arg], "expect-proxy") == 0) {
800 if (strcmp(args[arg+1], "layer4") != 0) {
801 memprintf(err,
802 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
803 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
804 return -1;
805 }
806
807 if (!(where & SMP_VAL_FE_CON_ACC)) {
808 memprintf(err,
809 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
810 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
811 return -1;
812 }
813
814 arg += 2;
815 rule->action = ACT_TCP_EXPECT_PX;
816 }
817 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
818 if (strcmp(args[arg+1], "layer4") != 0) {
819 memprintf(err,
820 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
821 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
822 return -1;
823 }
824
825 if (!(where & SMP_VAL_FE_CON_ACC)) {
826 memprintf(err,
827 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
828 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
829 return -1;
830 }
831
832 arg += 2;
833 rule->action = ACT_TCP_EXPECT_CIP;
834 }
835 else {
836 struct action_kw *kw;
837 if (where & SMP_VAL_FE_CON_ACC) {
838 /* L4 */
839 kw = tcp_req_conn_action(args[arg]);
840 rule->kw = kw;
841 rule->from = ACT_F_TCP_REQ_CON;
842 } else if (where & SMP_VAL_FE_SES_ACC) {
843 /* L5 */
844 kw = tcp_req_sess_action(args[arg]);
845 rule->kw = kw;
846 rule->from = ACT_F_TCP_REQ_SES;
847 } else {
848 /* L6 */
849 kw = tcp_req_cont_action(args[arg]);
850 rule->kw = kw;
851 rule->from = ACT_F_TCP_REQ_CNT;
852 }
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;
955
956 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;
1069
1070 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;
1114
1115 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;
1159
1160 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 */