1 /*
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>
16 #include <common/mini-clist.h>
17 #include <common/standard.h>
18 #include <common/ticks.h>
19 #include <common/time.h>
20
21 #include <types/arg.h>
22 #include <types/capture.h>
23 #include <types/connection.h>
24 #include <types/global.h>
25
26 #include <proto/acl.h>
27 #include <proto/action.h>
28 #include <proto/channel.h>
29 #include <proto/connection.h>
30 #include <proto/log.h>
31 #include <proto/proxy.h>
32 #include <proto/sample.h>
33 #include <proto/stick_table.h>
34 #include <proto/stream.h>
35 #include <proto/stream_interface.h>
36 #include <proto/tcp_rules.h>
37
38 /* List head of all known action keywords for "tcp-request connection" */
39 struct list tcp_req_conn_keywords = LIST_HEAD_INIT(tcp_req_conn_keywords);
40 struct list tcp_req_sess_keywords = LIST_HEAD_INIT(tcp_req_sess_keywords);
41 struct list tcp_req_cont_keywords = LIST_HEAD_INIT(tcp_req_cont_keywords);
42 struct list tcp_res_cont_keywords = LIST_HEAD_INIT(tcp_res_cont_keywords);
43
44 /*
45 * Register keywords.
46 */
tcp_req_conn_keywords_register(struct action_kw_list * kw_list)47 void tcp_req_conn_keywords_register(struct action_kw_list *kw_list)
48 {
49 LIST_ADDQ(&tcp_req_conn_keywords, &kw_list->list);
50 }
51
tcp_req_sess_keywords_register(struct action_kw_list * kw_list)52 void tcp_req_sess_keywords_register(struct action_kw_list *kw_list)
53 {
54 LIST_ADDQ(&tcp_req_sess_keywords, &kw_list->list);
55 }
56
tcp_req_cont_keywords_register(struct action_kw_list * kw_list)57 void tcp_req_cont_keywords_register(struct action_kw_list *kw_list)
58 {
59 LIST_ADDQ(&tcp_req_cont_keywords, &kw_list->list);
60 }
61
tcp_res_cont_keywords_register(struct action_kw_list * kw_list)62 void tcp_res_cont_keywords_register(struct action_kw_list *kw_list)
63 {
64 LIST_ADDQ(&tcp_res_cont_keywords, &kw_list->list);
65 }
66
67 /*
68 * Return the struct tcp_req_action_kw associated to a keyword.
69 */
tcp_req_conn_action(const char * kw)70 struct action_kw *tcp_req_conn_action(const char *kw)
71 {
72 return action_lookup(&tcp_req_conn_keywords, kw);
73 }
74
tcp_req_sess_action(const char * kw)75 struct action_kw *tcp_req_sess_action(const char *kw)
76 {
77 return action_lookup(&tcp_req_sess_keywords, kw);
78 }
79
tcp_req_cont_action(const char * kw)80 struct action_kw *tcp_req_cont_action(const char *kw)
81 {
82 return action_lookup(&tcp_req_cont_keywords, kw);
83 }
84
tcp_res_cont_action(const char * kw)85 struct action_kw *tcp_res_cont_action(const char *kw)
86 {
87 return action_lookup(&tcp_res_cont_keywords, kw);
88 }
89
90 /* This function performs the TCP request analysis on the current request. It
91 * returns 1 if the processing can continue on next analysers, or zero if it
92 * needs more data, encounters an error, or wants to immediately abort the
93 * request. It relies on buffers flags, and updates s->req->analysers. The
94 * function may be called for frontend rules and backend rules. It only relies
95 * on the backend pointer so this works for both cases.
96 */
tcp_inspect_request(struct stream * s,struct channel * req,int an_bit)97 int tcp_inspect_request(struct stream *s, struct channel *req, int an_bit)
98 {
99 struct session *sess = s->sess;
100 struct act_rule *rule;
101 struct stksess *ts;
102 struct stktable *t;
103 int partial;
104 int act_flags = 0;
105
106 DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
107 now_ms, __FUNCTION__,
108 s,
109 req,
110 req->rex, req->wex,
111 req->flags,
112 req->buf->i,
113 req->analysers);
114
115 /* We don't know whether we have enough data, so must proceed
116 * this way :
117 * - iterate through all rules in their declaration order
118 * - if one rule returns MISS, it means the inspect delay is
119 * not over yet, then return immediately, otherwise consider
120 * it as a non-match.
121 * - if one rule returns OK, then return OK
122 * - if one rule returns KO, then return KO
123 */
124
125 if ((req->flags & CF_SHUTR) || buffer_full(req->buf, global.tune.maxrewrite) ||
126 !s->be->tcp_req.inspect_delay || tick_is_expired(req->analyse_exp, now_ms))
127 partial = SMP_OPT_FINAL;
128 else
129 partial = 0;
130
131 /* If "the current_rule_list" match the executed rule list, we are in
132 * resume condition. If a resume is needed it is always in the action
133 * and never in the ACL or converters. In this case, we initialise the
134 * current rule, and go to the action execution point.
135 */
136 if (s->current_rule) {
137 rule = s->current_rule;
138 s->current_rule = NULL;
139 if (s->current_rule_list == &s->be->tcp_req.inspect_rules)
140 goto resume_execution;
141 }
142 s->current_rule_list = &s->be->tcp_req.inspect_rules;
143
144 list_for_each_entry(rule, &s->be->tcp_req.inspect_rules, list) {
145 enum acl_test_res ret = ACL_TEST_PASS;
146
147 if (rule->cond) {
148 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_REQ | partial);
149 if (ret == ACL_TEST_MISS)
150 goto missing_data;
151
152 ret = acl_pass(ret);
153 if (rule->cond->pol == ACL_COND_UNLESS)
154 ret = !ret;
155 }
156
157 if (ret) {
158 act_flags |= ACT_FLAG_FIRST;
159 resume_execution:
160 /* we have a matching rule. */
161 if (rule->action == ACT_ACTION_ALLOW) {
162 break;
163 }
164 else if (rule->action == ACT_ACTION_DENY) {
165 si_must_kill_conn(chn_prod(req));
166 channel_abort(req);
167 channel_abort(&s->res);
168 req->analysers = 0;
169
170 HA_ATOMIC_ADD(&s->be->be_counters.denied_req, 1);
171 HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_req, 1);
172 if (sess->listener && sess->listener->counters)
173 HA_ATOMIC_ADD(&sess->listener->counters->denied_req, 1);
174
175 if (!(s->flags & SF_ERR_MASK))
176 s->flags |= SF_ERR_PRXCOND;
177 if (!(s->flags & SF_FINST_MASK))
178 s->flags |= SF_FINST_R;
179 return 0;
180 }
181 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
182 /* Note: only the first valid tracking parameter of each
183 * applies.
184 */
185 struct stktable_key *key;
186 struct sample smp;
187
188 if (stkctr_entry(&s->stkctr[trk_idx(rule->action)]))
189 continue;
190
191 t = rule->arg.trk_ctr.table.t;
192 key = stktable_fetch_key(t, s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.trk_ctr.expr, &smp);
193
194 if ((smp.flags & SMP_F_MAY_CHANGE) && !(partial & SMP_OPT_FINAL))
195 goto missing_data; /* key might appear later */
196
197 if (key && (ts = stktable_get_entry(t, key))) {
198 stream_track_stkctr(&s->stkctr[trk_idx(rule->action)], t, ts);
199 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
200 if (sess->fe != s->be)
201 stkctr_set_flags(&s->stkctr[trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
202 }
203 }
204 else if (rule->action == ACT_TCP_CAPTURE) {
205 struct sample *key;
206 struct cap_hdr *h = rule->arg.cap.hdr;
207 char **cap = s->req_cap;
208 int len;
209
210 key = sample_fetch_as_type(s->be, sess, s, SMP_OPT_DIR_REQ | partial, rule->arg.cap.expr, SMP_T_STR);
211 if (!key)
212 continue;
213
214 if (key->flags & SMP_F_MAY_CHANGE)
215 goto missing_data;
216
217 if (cap[h->index] == NULL)
218 cap[h->index] = pool_alloc(h->pool);
219
220 if (cap[h->index] == NULL) /* no more capture memory */
221 continue;
222
223 len = key->data.u.str.len;
224 if (len > h->len)
225 len = h->len;
226
227 memcpy(cap[h->index], key->data.u.str.str, len);
228 cap[h->index][len] = 0;
229 }
230 else {
231 /* Custom keywords. */
232 if (!rule->action_ptr)
233 continue;
234
235 if (partial & SMP_OPT_FINAL)
236 act_flags |= ACT_FLAG_FINAL;
237
238 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
239 case ACT_RET_ERR:
240 case ACT_RET_CONT:
241 continue;
242 case ACT_RET_STOP:
243 break;
244 case ACT_RET_YIELD:
245 s->current_rule = rule;
246 goto missing_data;
247 }
248 break; /* ACT_RET_STOP */
249 }
250 }
251 }
252
253 /* if we get there, it means we have no rule which matches, or
254 * we have an explicit accept, so we apply the default accept.
255 */
256 req->analysers &= ~an_bit;
257 req->analyse_exp = TICK_ETERNITY;
258 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);
265 return 0;
266
267 }
268
269 /* This function performs the TCP response analysis on the current response. It
270 * returns 1 if the processing can continue on next analysers, or zero if it
271 * needs more data, encounters an error, or wants to immediately abort the
272 * response. It relies on buffers flags, and updates s->rep->analysers. The
273 * function may be called for backend rules.
274 */
tcp_inspect_response(struct stream * s,struct channel * rep,int an_bit)275 int tcp_inspect_response(struct stream *s, struct channel *rep, int an_bit)
276 {
277 struct session *sess = s->sess;
278 struct act_rule *rule;
279 int partial;
280 int act_flags = 0;
281
282 DPRINTF(stderr,"[%u] %s: stream=%p b=%p, exp(r,w)=%u,%u bf=%08x bh=%d analysers=%02x\n",
283 now_ms, __FUNCTION__,
284 s,
285 rep,
286 rep->rex, rep->wex,
287 rep->flags,
288 rep->buf->i,
289 rep->analysers);
290
291 /* We don't know whether we have enough data, so must proceed
292 * this way :
293 * - iterate through all rules in their declaration order
294 * - if one rule returns MISS, it means the inspect delay is
295 * not over yet, then return immediately, otherwise consider
296 * it as a non-match.
297 * - if one rule returns OK, then return OK
298 * - if one rule returns KO, then return KO
299 */
300 if ((rep->flags & CF_SHUTR) || buffer_full(rep->buf, global.tune.maxrewrite) ||
301 !s->be->tcp_rep.inspect_delay || tick_is_expired(rep->analyse_exp, now_ms))
302 partial = SMP_OPT_FINAL;
303 else
304 partial = 0;
305
306 /* If "the current_rule_list" match the executed rule list, we are in
307 * resume condition. If a resume is needed it is always in the action
308 * and never in the ACL or converters. In this case, we initialise the
309 * current rule, and go to the action execution point.
310 */
311 if (s->current_rule) {
312 rule = s->current_rule;
313 s->current_rule = NULL;
314 if (s->current_rule_list == &s->be->tcp_rep.inspect_rules)
315 goto resume_execution;
316 }
317 s->current_rule_list = &s->be->tcp_rep.inspect_rules;
318
319 list_for_each_entry(rule, &s->be->tcp_rep.inspect_rules, list) {
320 enum acl_test_res ret = ACL_TEST_PASS;
321
322 if (rule->cond) {
323 ret = acl_exec_cond(rule->cond, s->be, sess, s, SMP_OPT_DIR_RES | partial);
324 if (ret == ACL_TEST_MISS) {
325 /* just set the analyser timeout once at the beginning of the response */
326 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
327 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
328 return 0;
329 }
330
331 ret = acl_pass(ret);
332 if (rule->cond->pol == ACL_COND_UNLESS)
333 ret = !ret;
334 }
335
336 if (ret) {
337 act_flags |= ACT_FLAG_FIRST;
338 resume_execution:
339 /* we have a matching rule. */
340 if (rule->action == ACT_ACTION_ALLOW) {
341 break;
342 }
343 else if (rule->action == ACT_ACTION_DENY) {
344 si_must_kill_conn(chn_prod(rep));
345 channel_abort(rep);
346 channel_abort(&s->req);
347 rep->analysers = 0;
348
349 HA_ATOMIC_ADD(&s->be->be_counters.denied_resp, 1);
350 HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_resp, 1);
351 if (sess->listener && sess->listener->counters)
352 HA_ATOMIC_ADD(&sess->listener->counters->denied_resp, 1);
353
354 if (!(s->flags & SF_ERR_MASK))
355 s->flags |= SF_ERR_PRXCOND;
356 if (!(s->flags & SF_FINST_MASK))
357 s->flags |= SF_FINST_D;
358 return 0;
359 }
360 else if (rule->action == ACT_TCP_CLOSE) {
361 chn_prod(rep)->flags |= SI_FL_NOLINGER | SI_FL_NOHALF;
362 si_must_kill_conn(chn_prod(rep));
363 si_shutr(chn_prod(rep));
364 si_shutw(chn_prod(rep));
365 break;
366 }
367 else {
368 /* Custom keywords. */
369 if (!rule->action_ptr)
370 continue;
371
372 if (partial & SMP_OPT_FINAL)
373 act_flags |= ACT_FLAG_FINAL;
374
375 switch (rule->action_ptr(rule, s->be, s->sess, s, act_flags)) {
376 case ACT_RET_ERR:
377 case ACT_RET_CONT:
378 continue;
379 case ACT_RET_STOP:
380 break;
381 case ACT_RET_YIELD:
382 channel_dont_close(rep);
383 s->current_rule = rule;
384 if (!tick_isset(rep->analyse_exp) && s->be->tcp_rep.inspect_delay)
385 rep->analyse_exp = tick_add(now_ms, s->be->tcp_rep.inspect_delay);
386 return 0;
387 }
388 break; /* ACT_RET_STOP */
389 }
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;
398 return 1;
399 }
400
401
402 /* This function performs the TCP layer4 analysis on the current request. It
403 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
404 * matches or if no more rule matches. It can only use rules which don't need
405 * any data. This only works on connection-based client-facing stream interfaces.
406 */
tcp_exec_l4_rules(struct session * sess)407 int tcp_exec_l4_rules(struct session *sess)
408 {
409 struct act_rule *rule;
410 struct stksess *ts;
411 struct stktable *t = NULL;
412 struct connection *conn = objt_conn(sess->origin);
413 int result = 1;
414 enum acl_test_res ret;
415
416 if (!conn)
417 return result;
418
419 list_for_each_entry(rule, &sess->fe->tcp_req.l4_rules, list) {
420 ret = ACL_TEST_PASS;
421
422 if (rule->cond) {
423 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
424 ret = acl_pass(ret);
425 if (rule->cond->pol == ACL_COND_UNLESS)
426 ret = !ret;
427 }
428
429 if (ret) {
430 /* we have a matching rule. */
431 if (rule->action == ACT_ACTION_ALLOW) {
432 break;
433 }
434 else if (rule->action == ACT_ACTION_DENY) {
435 HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_conn, 1);
436 if (sess->listener && sess->listener->counters)
437 HA_ATOMIC_ADD(&sess->listener->counters->denied_conn, 1);
438
439 result = 0;
440 break;
441 }
442 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
443 /* Note: only the first valid tracking parameter of each
444 * applies.
445 */
446 struct stktable_key *key;
447
448 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
449 continue;
450
451 t = rule->arg.trk_ctr.table.t;
452 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
453
454 if (key && (ts = stktable_get_entry(t, key)))
455 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
456 }
457 else if (rule->action == ACT_TCP_EXPECT_PX) {
458 conn->flags |= CO_FL_ACCEPT_PROXY;
459 conn_sock_want_recv(conn);
460 }
461 else if (rule->action == ACT_TCP_EXPECT_CIP) {
462 conn->flags |= CO_FL_ACCEPT_CIP;
463 conn_sock_want_recv(conn);
464 }
465 else {
466 /* Custom keywords. */
467 if (!rule->action_ptr)
468 break;
469 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
470 case ACT_RET_YIELD:
471 /* yield is not allowed at this point. If this return code is
472 * used it is a bug, so I prefer to abort the process.
473 */
474 send_log(sess->fe, LOG_WARNING,
475 "Internal error: yield not allowed with tcp-request connection actions.");
476 case ACT_RET_STOP:
477 break;
478 case ACT_RET_CONT:
479 continue;
480 case ACT_RET_ERR:
481 result = 0;
482 break;
483 }
484 break; /* ACT_RET_STOP */
485 }
486 }
487 }
488 return result;
489 }
490
491 /* This function performs the TCP layer5 analysis on the current request. It
492 * returns 0 if a reject rule matches, otherwise 1 if either an accept rule
493 * matches or if no more rule matches. It can only use rules which don't need
494 * any data. This only works on session-based client-facing stream interfaces.
495 * An example of valid use case is to track a stick-counter on the source
496 * address extracted from the proxy protocol.
497 */
tcp_exec_l5_rules(struct session * sess)498 int tcp_exec_l5_rules(struct session *sess)
499 {
500 struct act_rule *rule;
501 struct stksess *ts;
502 struct stktable *t = NULL;
503 int result = 1;
504 enum acl_test_res ret;
505
506 list_for_each_entry(rule, &sess->fe->tcp_req.l5_rules, list) {
507 ret = ACL_TEST_PASS;
508
509 if (rule->cond) {
510 ret = acl_exec_cond(rule->cond, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
511 ret = acl_pass(ret);
512 if (rule->cond->pol == ACL_COND_UNLESS)
513 ret = !ret;
514 }
515
516 if (ret) {
517 /* we have a matching rule. */
518 if (rule->action == ACT_ACTION_ALLOW) {
519 break;
520 }
521 else if (rule->action == ACT_ACTION_DENY) {
522 HA_ATOMIC_ADD(&sess->fe->fe_counters.denied_sess, 1);
523 if (sess->listener && sess->listener->counters)
524 HA_ATOMIC_ADD(&sess->listener->counters->denied_sess, 1);
525
526 result = 0;
527 break;
528 }
529 else if (rule->action >= ACT_ACTION_TRK_SC0 && rule->action <= ACT_ACTION_TRK_SCMAX) {
530 /* Note: only the first valid tracking parameter of each
531 * applies.
532 */
533 struct stktable_key *key;
534
535 if (stkctr_entry(&sess->stkctr[trk_idx(rule->action)]))
536 continue;
537
538 t = rule->arg.trk_ctr.table.t;
539 key = stktable_fetch_key(t, sess->fe, sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.trk_ctr.expr, NULL);
540
541 if (key && (ts = stktable_get_entry(t, key)))
542 stream_track_stkctr(&sess->stkctr[trk_idx(rule->action)], t, ts);
543 }
544 else {
545 /* Custom keywords. */
546 if (!rule->action_ptr)
547 break;
548 switch (rule->action_ptr(rule, sess->fe, sess, NULL, ACT_FLAG_FINAL | ACT_FLAG_FIRST)) {
549 case ACT_RET_YIELD:
550 /* yield is not allowed at this point. If this return code is
551 * used it is a bug, so I prefer to abort the process.
552 */
553 send_log(sess->fe, LOG_WARNING,
554 "Internal error: yield not allowed with tcp-request session actions.");
555 case ACT_RET_STOP:
556 break;
557 case ACT_RET_CONT:
558 continue;
559 case ACT_RET_ERR:
560 result = 0;
561 break;
562 }
563 break; /* ACT_RET_STOP */
564 }
565 }
566 }
567 return result;
568 }
569
570 /* Parse a tcp-response rule. Return a negative value in case of failure */
tcp_parse_response_rule(char ** args,int arg,int section_type,struct proxy * curpx,struct proxy * defpx,struct act_rule * rule,char ** err,unsigned int where,const char * file,int line)571 static int tcp_parse_response_rule(char **args, int arg, int section_type,
572 struct proxy *curpx, struct proxy *defpx,
573 struct act_rule *rule, char **err,
574 unsigned int where,
575 const char *file, int line)
576 {
577 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
578 memprintf(err, "%s %s is only allowed in 'backend' sections",
579 args[0], args[1]);
580 return -1;
581 }
582
583 if (strcmp(args[arg], "accept") == 0) {
584 arg++;
585 rule->action = ACT_ACTION_ALLOW;
586 }
587 else if (strcmp(args[arg], "reject") == 0) {
588 arg++;
589 rule->action = ACT_ACTION_DENY;
590 }
591 else if (strcmp(args[arg], "close") == 0) {
592 arg++;
593 rule->action = ACT_TCP_CLOSE;
594 }
595 else {
596 struct action_kw *kw;
597 kw = tcp_res_cont_action(args[arg]);
598 if (kw) {
599 arg++;
600 rule->from = ACT_F_TCP_RES_CNT;
601 rule->kw = kw;
602 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
603 return -1;
604 } else {
605 action_build_list(&tcp_res_cont_keywords, &trash);
606 memprintf(err,
607 "'%s %s' expects 'accept', 'close', 'reject', %s in %s '%s' (got '%s')",
608 args[0], args[1], trash.str, proxy_type_str(curpx), curpx->id, args[arg]);
609 return -1;
610 }
611 }
612
613 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
614 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
615 memprintf(err,
616 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
617 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
618 return -1;
619 }
620 }
621 else if (*args[arg]) {
622 memprintf(err,
623 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
624 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
625 return -1;
626 }
627 return 0;
628 }
629
630
631
632 /* Parse a tcp-request rule. Return a negative value in case of failure */
tcp_parse_request_rule(char ** args,int arg,int section_type,struct proxy * curpx,struct proxy * defpx,struct act_rule * rule,char ** err,unsigned int where,const char * file,int line)633 static int tcp_parse_request_rule(char **args, int arg, int section_type,
634 struct proxy *curpx, struct proxy *defpx,
635 struct act_rule *rule, char **err,
636 unsigned int where, const char *file, int line)
637 {
638 if (curpx == defpx) {
639 memprintf(err, "%s %s is not allowed in 'defaults' sections",
640 args[0], args[1]);
641 return -1;
642 }
643
644 if (!strcmp(args[arg], "accept")) {
645 arg++;
646 rule->action = ACT_ACTION_ALLOW;
647 }
648 else if (!strcmp(args[arg], "reject")) {
649 arg++;
650 rule->action = ACT_ACTION_DENY;
651 }
652 else if (strcmp(args[arg], "capture") == 0) {
653 struct sample_expr *expr;
654 struct cap_hdr *hdr;
655 int kw = arg;
656 int len = 0;
657
658 if (!(curpx->cap & PR_CAP_FE)) {
659 memprintf(err,
660 "'%s %s %s' : proxy '%s' has no frontend capability",
661 args[0], args[1], args[kw], curpx->id);
662 return -1;
663 }
664
665 if (!(where & SMP_VAL_FE_REQ_CNT)) {
666 memprintf(err,
667 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
668 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
669 return -1;
670 }
671
672 arg++;
673
674 curpx->conf.args.ctx = ARGC_CAP;
675 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
676 if (!expr) {
677 memprintf(err,
678 "'%s %s %s' : %s",
679 args[0], args[1], args[kw], *err);
680 return -1;
681 }
682
683 if (!(expr->fetch->val & where)) {
684 memprintf(err,
685 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
686 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
687 release_sample_expr(expr);
688 return -1;
689 }
690
691 if (strcmp(args[arg], "len") == 0) {
692 arg++;
693 if (!args[arg]) {
694 memprintf(err,
695 "'%s %s %s' : missing length value",
696 args[0], args[1], args[kw]);
697 release_sample_expr(expr);
698 return -1;
699 }
700 /* we copy the table name for now, it will be resolved later */
701 len = atoi(args[arg]);
702 if (len <= 0) {
703 memprintf(err,
704 "'%s %s %s' : length must be > 0",
705 args[0], args[1], args[kw]);
706 release_sample_expr(expr);
707 return -1;
708 }
709 arg++;
710 }
711
712 if (!len) {
713 memprintf(err,
714 "'%s %s %s' : a positive 'len' argument is mandatory",
715 args[0], args[1], args[kw]);
716 free(expr);
717 return -1;
718 }
719
720 hdr = calloc(1, sizeof(*hdr));
721 hdr->next = curpx->req_cap;
722 hdr->name = NULL; /* not a header capture */
723 hdr->namelen = 0;
724 hdr->len = len;
725 hdr->pool = create_pool("caphdr", hdr->len + 1, MEM_F_SHARED);
726 hdr->index = curpx->nb_req_cap++;
727
728 curpx->req_cap = hdr;
729 curpx->to_log |= LW_REQHDR;
730
731 /* check if we need to allocate an hdr_idx struct for HTTP parsing */
732 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
733
734 rule->arg.cap.expr = expr;
735 rule->arg.cap.hdr = hdr;
736 rule->action = ACT_TCP_CAPTURE;
737 }
738 else if (strncmp(args[arg], "track-sc", 8) == 0 &&
739 args[arg][9] == '\0' && args[arg][8] >= '0' &&
740 args[arg][8] < '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */
741 struct sample_expr *expr;
742 int kw = arg;
743
744 arg++;
745
746 curpx->conf.args.ctx = ARGC_TRK;
747 expr = sample_parse_expr(args, &arg, file, line, err, &curpx->conf.args);
748 if (!expr) {
749 memprintf(err,
750 "'%s %s %s' : %s",
751 args[0], args[1], args[kw], *err);
752 return -1;
753 }
754
755 if (!(expr->fetch->val & where)) {
756 memprintf(err,
757 "'%s %s %s' : fetch method '%s' extracts information from '%s', none of which is available here",
758 args[0], args[1], args[kw], args[arg-1], sample_src_names(expr->fetch->use));
759 release_sample_expr(expr);
760 return -1;
761 }
762
763 /* check if we need to allocate an hdr_idx struct for HTTP parsing */
764 curpx->http_needed |= !!(expr->fetch->use & SMP_USE_HTTP_ANY);
765
766 if (strcmp(args[arg], "table") == 0) {
767 arg++;
768 if (!args[arg]) {
769 memprintf(err,
770 "'%s %s %s' : missing table name",
771 args[0], args[1], args[kw]);
772 release_sample_expr(expr);
773 return -1;
774 }
775 /* we copy the table name for now, it will be resolved later */
776 rule->arg.trk_ctr.table.n = strdup(args[arg]);
777 arg++;
778 }
779 rule->arg.trk_ctr.expr = expr;
780 rule->action = ACT_ACTION_TRK_SC0 + args[kw][8] - '0';
781 rule->check_ptr = check_trk_action;
782 }
783 else if (strcmp(args[arg], "expect-proxy") == 0) {
784 if (strcmp(args[arg+1], "layer4") != 0) {
785 memprintf(err,
786 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
787 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
788 return -1;
789 }
790
791 if (!(where & SMP_VAL_FE_CON_ACC)) {
792 memprintf(err,
793 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
794 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
795 return -1;
796 }
797
798 arg += 2;
799 rule->action = ACT_TCP_EXPECT_PX;
800 }
801 else if (strcmp(args[arg], "expect-netscaler-cip") == 0) {
802 if (strcmp(args[arg+1], "layer4") != 0) {
803 memprintf(err,
804 "'%s %s %s' only supports 'layer4' in %s '%s' (got '%s')",
805 args[0], args[1], args[arg], proxy_type_str(curpx), curpx->id, args[arg+1]);
806 return -1;
807 }
808
809 if (!(where & SMP_VAL_FE_CON_ACC)) {
810 memprintf(err,
811 "'%s %s' is not allowed in '%s %s' rules in %s '%s'",
812 args[arg], args[arg+1], args[0], args[1], proxy_type_str(curpx), curpx->id);
813 return -1;
814 }
815
816 arg += 2;
817 rule->action = ACT_TCP_EXPECT_CIP;
818 }
819 else {
820 struct action_kw *kw;
821 if (where & SMP_VAL_FE_CON_ACC) {
822 /* L4 */
823 kw = tcp_req_conn_action(args[arg]);
824 rule->kw = kw;
825 rule->from = ACT_F_TCP_REQ_CON;
826 } else if (where & SMP_VAL_FE_SES_ACC) {
827 /* L5 */
828 kw = tcp_req_sess_action(args[arg]);
829 rule->kw = kw;
830 rule->from = ACT_F_TCP_REQ_SES;
831 } else {
832 /* L6 */
833 kw = tcp_req_cont_action(args[arg]);
834 rule->kw = kw;
835 rule->from = ACT_F_TCP_REQ_CNT;
836 }
837 if (kw) {
838 arg++;
839 if (kw->parse((const char **)args, &arg, curpx, rule, err) == ACT_RET_PRS_ERR)
840 return -1;
841 } else {
842 if (where & SMP_VAL_FE_CON_ACC)
843 action_build_list(&tcp_req_conn_keywords, &trash);
844 else if (where & SMP_VAL_FE_SES_ACC)
845 action_build_list(&tcp_req_sess_keywords, &trash);
846 else
847 action_build_list(&tcp_req_cont_keywords, &trash);
848 memprintf(err,
849 "'%s %s' expects 'accept', 'reject', 'capture', 'expect-proxy', 'expect-netscaler-ip', 'track-sc0' ... 'track-sc%d', %s "
850 "in %s '%s' (got '%s').\n",
851 args[0], args[1], MAX_SESS_STKCTR-1, trash.str, proxy_type_str(curpx),
852 curpx->id, args[arg]);
853 return -1;
854 }
855 }
856
857 if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
858 if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
859 memprintf(err,
860 "'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
861 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
862 return -1;
863 }
864 }
865 else if (*args[arg]) {
866 memprintf(err,
867 "'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
868 args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
869 return -1;
870 }
871 return 0;
872 }
873
874 /* This function should be called to parse a line starting with the "tcp-response"
875 * keyword.
876 */
tcp_parse_tcp_rep(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)877 static int tcp_parse_tcp_rep(char **args, int section_type, struct proxy *curpx,
878 struct proxy *defpx, const char *file, int line,
879 char **err)
880 {
881 const char *ptr = NULL;
882 unsigned int val;
883 int warn = 0;
884 int arg;
885 struct act_rule *rule;
886 unsigned int where;
887 const struct acl *acl;
888 const char *kw;
889
890 if (!*args[1]) {
891 memprintf(err, "missing argument for '%s' in %s '%s'",
892 args[0], proxy_type_str(curpx), curpx->id);
893 return -1;
894 }
895
896 if (strcmp(args[1], "inspect-delay") == 0) {
897 if (curpx == defpx || !(curpx->cap & PR_CAP_BE)) {
898 memprintf(err, "%s %s is only allowed in 'backend' sections",
899 args[0], args[1]);
900 return -1;
901 }
902
903 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
904 memprintf(err,
905 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
906 args[0], args[1], proxy_type_str(curpx), curpx->id);
907 if (ptr)
908 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
909 return -1;
910 }
911
912 if (curpx->tcp_rep.inspect_delay) {
913 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
914 args[0], args[1], proxy_type_str(curpx), curpx->id);
915 return 1;
916 }
917 curpx->tcp_rep.inspect_delay = val;
918 return 0;
919 }
920
921 rule = calloc(1, sizeof(*rule));
922 LIST_INIT(&rule->list);
923 arg = 1;
924 where = 0;
925
926 if (strcmp(args[1], "content") == 0) {
927 arg++;
928
929 if (curpx->cap & PR_CAP_FE)
930 where |= SMP_VAL_FE_RES_CNT;
931 if (curpx->cap & PR_CAP_BE)
932 where |= SMP_VAL_BE_RES_CNT;
933
934 if (tcp_parse_response_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
935 goto error;
936
937 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
938 if (acl) {
939 if (acl->name && *acl->name)
940 memprintf(err,
941 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
942 acl->name, args[0], args[1], sample_ckp_names(where));
943 else
944 memprintf(err,
945 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
946 args[0], args[1],
947 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
948 sample_ckp_names(where));
949
950 warn++;
951 }
952 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
953 if (acl->name && *acl->name)
954 memprintf(err,
955 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
956 acl->name, kw, sample_ckp_names(where));
957 else
958 memprintf(err,
959 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
960 kw, sample_ckp_names(where));
961 warn++;
962 }
963
964 LIST_ADDQ(&curpx->tcp_rep.inspect_rules, &rule->list);
965 }
966 else {
967 memprintf(err,
968 "'%s' expects 'inspect-delay' or 'content' in %s '%s' (got '%s')",
969 args[0], proxy_type_str(curpx), curpx->id, args[1]);
970 goto error;
971 }
972
973 return warn;
974 error:
975 free(rule);
976 return -1;
977 }
978
979
980 /* This function should be called to parse a line starting with the "tcp-request"
981 * keyword.
982 */
tcp_parse_tcp_req(char ** args,int section_type,struct proxy * curpx,struct proxy * defpx,const char * file,int line,char ** err)983 static int tcp_parse_tcp_req(char **args, int section_type, struct proxy *curpx,
984 struct proxy *defpx, const char *file, int line,
985 char **err)
986 {
987 const char *ptr = NULL;
988 unsigned int val;
989 int warn = 0;
990 int arg;
991 struct act_rule *rule;
992 unsigned int where;
993 const struct acl *acl;
994 const char *kw;
995
996 if (!*args[1]) {
997 if (curpx == defpx)
998 memprintf(err, "missing argument for '%s' in defaults section", args[0]);
999 else
1000 memprintf(err, "missing argument for '%s' in %s '%s'",
1001 args[0], proxy_type_str(curpx), curpx->id);
1002 return -1;
1003 }
1004
1005 if (!strcmp(args[1], "inspect-delay")) {
1006 if (curpx == defpx) {
1007 memprintf(err, "%s %s is not allowed in 'defaults' sections",
1008 args[0], args[1]);
1009 return -1;
1010 }
1011
1012 if (!*args[2] || (ptr = parse_time_err(args[2], &val, TIME_UNIT_MS))) {
1013 memprintf(err,
1014 "'%s %s' expects a positive delay in milliseconds, in %s '%s'",
1015 args[0], args[1], proxy_type_str(curpx), curpx->id);
1016 if (ptr)
1017 memprintf(err, "%s (unexpected character '%c')", *err, *ptr);
1018 return -1;
1019 }
1020
1021 if (curpx->tcp_req.inspect_delay) {
1022 memprintf(err, "ignoring %s %s (was already defined) in %s '%s'",
1023 args[0], args[1], proxy_type_str(curpx), curpx->id);
1024 return 1;
1025 }
1026 curpx->tcp_req.inspect_delay = val;
1027 return 0;
1028 }
1029
1030 rule = calloc(1, sizeof(*rule));
1031 LIST_INIT(&rule->list);
1032 arg = 1;
1033 where = 0;
1034
1035 if (strcmp(args[1], "content") == 0) {
1036 arg++;
1037
1038 if (curpx->cap & PR_CAP_FE)
1039 where |= SMP_VAL_FE_REQ_CNT;
1040 if (curpx->cap & PR_CAP_BE)
1041 where |= SMP_VAL_BE_REQ_CNT;
1042
1043 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1044 goto error;
1045
1046 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1047 if (acl) {
1048 if (acl->name && *acl->name)
1049 memprintf(err,
1050 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1051 acl->name, args[0], args[1], sample_ckp_names(where));
1052 else
1053 memprintf(err,
1054 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1055 args[0], args[1],
1056 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1057 sample_ckp_names(where));
1058
1059 warn++;
1060 }
1061 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1062 if (acl->name && *acl->name)
1063 memprintf(err,
1064 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1065 acl->name, kw, sample_ckp_names(where));
1066 else
1067 memprintf(err,
1068 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1069 kw, sample_ckp_names(where));
1070 warn++;
1071 }
1072
1073 /* the following function directly emits the warning */
1074 warnif_misplaced_tcp_cont(curpx, file, line, args[0]);
1075 LIST_ADDQ(&curpx->tcp_req.inspect_rules, &rule->list);
1076 }
1077 else if (strcmp(args[1], "connection") == 0) {
1078 arg++;
1079
1080 if (!(curpx->cap & PR_CAP_FE)) {
1081 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1082 args[0], args[1], proxy_type_str(curpx), curpx->id);
1083 goto error;
1084 }
1085
1086 where |= SMP_VAL_FE_CON_ACC;
1087
1088 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1089 goto error;
1090
1091 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1092 if (acl) {
1093 if (acl->name && *acl->name)
1094 memprintf(err,
1095 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1096 acl->name, args[0], args[1], sample_ckp_names(where));
1097 else
1098 memprintf(err,
1099 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1100 args[0], args[1],
1101 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1102 sample_ckp_names(where));
1103
1104 warn++;
1105 }
1106 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1107 if (acl->name && *acl->name)
1108 memprintf(err,
1109 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1110 acl->name, kw, sample_ckp_names(where));
1111 else
1112 memprintf(err,
1113 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1114 kw, sample_ckp_names(where));
1115 warn++;
1116 }
1117
1118 /* the following function directly emits the warning */
1119 warnif_misplaced_tcp_conn(curpx, file, line, args[0]);
1120 LIST_ADDQ(&curpx->tcp_req.l4_rules, &rule->list);
1121 }
1122 else if (strcmp(args[1], "session") == 0) {
1123 arg++;
1124
1125 if (!(curpx->cap & PR_CAP_FE)) {
1126 memprintf(err, "%s %s is not allowed because %s %s is not a frontend",
1127 args[0], args[1], proxy_type_str(curpx), curpx->id);
1128 goto error;
1129 }
1130
1131 where |= SMP_VAL_FE_SES_ACC;
1132
1133 if (tcp_parse_request_rule(args, arg, section_type, curpx, defpx, rule, err, where, file, line) < 0)
1134 goto error;
1135
1136 acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
1137 if (acl) {
1138 if (acl->name && *acl->name)
1139 memprintf(err,
1140 "acl '%s' will never match in '%s %s' because it only involves keywords that are incompatible with '%s'",
1141 acl->name, args[0], args[1], sample_ckp_names(where));
1142 else
1143 memprintf(err,
1144 "anonymous acl will never match in '%s %s' because it uses keyword '%s' which is incompatible with '%s'",
1145 args[0], args[1],
1146 LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
1147 sample_ckp_names(where));
1148 warn++;
1149 }
1150 else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &kw)) {
1151 if (acl->name && *acl->name)
1152 memprintf(err,
1153 "acl '%s' involves keyword '%s' which is incompatible with '%s'",
1154 acl->name, kw, sample_ckp_names(where));
1155 else
1156 memprintf(err,
1157 "anonymous acl involves keyword '%s' which is incompatible with '%s'",
1158 kw, sample_ckp_names(where));
1159 warn++;
1160 }
1161
1162 /* the following function directly emits the warning */
1163 warnif_misplaced_tcp_sess(curpx, file, line, args[0]);
1164 LIST_ADDQ(&curpx->tcp_req.l5_rules, &rule->list);
1165 }
1166 else {
1167 if (curpx == defpx)
1168 memprintf(err,
1169 "'%s' expects 'inspect-delay', 'connection', or 'content' in defaults section (got '%s')",
1170 args[0], args[1]);
1171 else
1172 memprintf(err,
1173 "'%s' expects 'inspect-delay', 'connection', or 'content' in %s '%s' (got '%s')",
1174 args[0], proxy_type_str(curpx), curpx->id, args[1]);
1175 goto error;
1176 }
1177
1178 return warn;
1179 error:
1180 free(rule);
1181 return -1;
1182 }
1183
1184 static struct cfg_kw_list cfg_kws = {ILH, {
1185 { CFG_LISTEN, "tcp-request", tcp_parse_tcp_req },
1186 { CFG_LISTEN, "tcp-response", tcp_parse_tcp_rep },
1187 { 0, NULL, NULL },
1188 }};
1189
1190
1191 __attribute__((constructor))
__tcp_protocol_init(void)1192 static void __tcp_protocol_init(void)
1193 {
1194 cfg_register_keywords(&cfg_kws);
1195 }
1196
1197 /*
1198 * Local variables:
1199 * c-indent-level: 8
1200 * c-basic-offset: 8
1201 * End:
1202 */
1203