From 469c06c30e3177ac5a0c62e4aa0db463ff49cfdd Mon Sep 17 00:00:00 2001 From: Christopher Faulet Date: Fri, 25 Jun 2021 15:11:35 +0200 Subject: [PATCH] MINOR: http-act/tcp-act: Add "set-mark" and "set-tos" for tcp content rules It is now possible to set the Netfilter MARK and the TOS field value in all packets sent to the client from any tcp-request rulesets or the "tcp-response content" one. To do so, the parsing of "set-mark" and "set-tos" actions are moved in tcp_act.c and the actions evaluation is handled in dedicated functions. This patch may be backported as far as 2.2 if necessary. --- doc/configuration.txt | 41 ++++++++++++++++ include/haproxy/action-t.h | 2 - src/http_act.c | 69 --------------------------- src/http_ana.c | 16 ------- src/tcp_act.c | 97 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 87 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 7843e04401..9ffcc7581c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -11897,6 +11897,16 @@ tcp-request connection [{if | unless} ] expected result is a boolean. If an error occurs, this action silently fails and the actions evaluation continues. + - set-mark : + Is used to set the Netfilter MARK in all packets sent to the client to + the value passed in on platforms which support it. This value is + an unsigned 32 bit value which can be matched by netfilter and by the + routing table. It can be expressed both in decimal or hexadecimal format + (prefixed by "0x"). This can be useful to force certain packets to take a + different route (for example a cheaper network path for bulk + downloads). This works on Linux kernels 2.6.32 and above and requires + admin privileges. + - set-src : Is used to set the source IP address to the value of specified expression. Useful if you want to mask source IP for privacy. @@ -11963,6 +11973,17 @@ tcp-request connection [{if | unless} ] long as the address family supports a port, otherwise it forces the destination address to IPv4 "0.0.0.0" before rewriting the port. + - set-tos : + Is used to set the TOS or DSCP field value of packets sent to the client + to the value passed in on platforms which support this. This value + represents the whole 8 bits of the IP TOS field, and can be expressed + both in decimal or hexadecimal format (prefixed by "0x"). Note that only + the 6 higher bits are used in DSCP or TOS, and the two lower bits are + always 0. This can be used to adjust some routing behavior on border + routers based on some information from the request. + + See RFC 2474, 2597, 3260 and 4594 for more information. + - "silent-drop" : This stops the evaluation of the rules and makes the client-facing connection suddenly disappear using a system-dependent way that tries @@ -12057,9 +12078,11 @@ tcp-request content [{if | unless} ] - set-dst - set-dst-port - set-log-level + - set-mark - set-nice - set-src - set-src-port + - set-tos - set-var() - switch-mode http [ proto ] - unset-var() @@ -12113,12 +12136,18 @@ tcp-request content [{if | unless} ] The "set-log-level" is used to set the log level of the current session. More information on how to use it at "http-request set-log-level". + The "set-mark" is used to set the Netfilter MARK in all packets sent to the + client. More information on how to use it at "http-request set-mark". + The "set-nice" is used to set the "nice" factor of the current session. More information on how to use it at "http-request set-nice". The "set-src" and "set-src-port" are used to set respectively the source IP and port. More information on how to use it at "http-request set-src". + The "set-tos" is used to set the TOS or DSCP field value of packets sent to + the client. More information on how to use it at "http-request set-tos". + The "set-var" is used to set the content of a variable. The variable is declared inline. For "tcp-request session" rules, only session-level variables can be used, without any layer7 contents. @@ -12363,11 +12392,21 @@ tcp-response content [{if | unless} ] session. More information on how to use it at "http-response set-log-level". + - set-mark + The "set-mark" is used to set the Netfilter MARK in all packets sent to + the client. More information on how to use it at "http-response + set-mark". + - set-nice The "set-nice" is used to set the "nice" factor of the current session. More information on how to use it at "http-response set-nice". + - set-tos + The "set-tos" is used to set the TOS or DSCP field value of packets + sent to the client. More information on how to use it at "http-response + set-tos". + - set-var() Sets a variable. @@ -12510,10 +12549,12 @@ tcp-request session [{if | unless} ] - sc-inc-gpc0() - sc-inc-gpc1() - sc-set-gpt0() { | } + - set-mark - set-dst - set-dst-port - set-src - set-src-port + - set-tos - set-var() - unset-var() - silent-drop diff --git a/include/haproxy/action-t.h b/include/haproxy/action-t.h index 4f0216372d..773ccd1a2a 100644 --- a/include/haproxy/action-t.h +++ b/include/haproxy/action-t.h @@ -81,8 +81,6 @@ enum act_name { /* common http actions .*/ ACT_HTTP_REDIR, - ACT_HTTP_SET_TOS, - ACT_HTTP_SET_MARK, /* http request actions. */ ACT_HTTP_REQ_TARPIT, diff --git a/src/http_act.c b/src/http_act.c index 9e49d33739..b77fd9361d 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -1315,71 +1315,6 @@ static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, stru return ACT_RET_PRS_OK; } -/* Parse a "set-tos" action. It takes the TOS value as argument. It returns - * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error. - */ -static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px, - struct act_rule *rule, char **err) -{ -#ifdef IP_TOS - char *endp; - int cur_arg; - - rule->action = ACT_HTTP_SET_TOS; - - cur_arg = *orig_arg; - if (!*args[cur_arg]) { - memprintf(err, "expects exactly 1 argument (integer/hex value)"); - return ACT_RET_PRS_ERR; - } - rule->arg.http.i = strtol(args[cur_arg], &endp, 0); - if (endp && *endp != '\0') { - memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp); - return ACT_RET_PRS_ERR; - } - - LIST_INIT(&rule->arg.http.fmt); - *orig_arg = cur_arg + 1; - return ACT_RET_PRS_OK; -#else - memprintf(err, "not supported on this platform (IP_TOS undefined)"); - return ACT_RET_PRS_ERR; -#endif -} - -/* Parse a "set-mark" action. It takes the MARK value as argument. It returns - * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error. - */ -static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px, - struct act_rule *rule, char **err) -{ -#ifdef SO_MARK - char *endp; - int cur_arg; - - rule->action = ACT_HTTP_SET_MARK; - - cur_arg = *orig_arg; - if (!*args[cur_arg]) { - memprintf(err, "expects exactly 1 argument (integer/hex value)"); - return ACT_RET_PRS_ERR; - } - rule->arg.http.i = strtoul(args[cur_arg], &endp, 0); - if (endp && *endp != '\0') { - memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp); - return ACT_RET_PRS_ERR; - } - - LIST_INIT(&rule->arg.http.fmt); - *orig_arg = cur_arg + 1; - global.last_checks |= LSTCHK_NETADM; - return ACT_RET_PRS_OK; -#else - memprintf(err, "not supported on this platform (SO_MARK undefined)"); - return ACT_RET_PRS_ERR; -#endif -} - /* This function executes a early-hint action. It adds an HTTP Early Hint HTTP * 103 response header with <.arg.http.str> name and with a value built * according to <.arg.http.fmt> log line format. If it is the first early-hint @@ -2458,11 +2393,9 @@ static struct action_kw_list http_req_actions = { { "set-header", parse_http_set_header, 0 }, { "set-map", parse_http_set_map, KWF_MATCH_PREFIX }, { "set-method", parse_set_req_line, 0 }, - { "set-mark", parse_http_set_mark, 0 }, { "set-path", parse_set_req_line, 0 }, { "set-pathq", parse_set_req_line, 0 }, { "set-query", parse_set_req_line, 0 }, - { "set-tos", parse_http_set_tos, 0 }, { "set-uri", parse_set_req_line, 0 }, { "strict-mode", parse_http_strict_mode, 0 }, { "tarpit", parse_http_deny, 0 }, @@ -2491,9 +2424,7 @@ static struct action_kw_list http_res_actions = { { "return", parse_http_return, 0 }, { "set-header", parse_http_set_header, 0 }, { "set-map", parse_http_set_map, KWF_MATCH_PREFIX }, - { "set-mark", parse_http_set_mark, 0 }, { "set-status", parse_http_set_status, 0 }, - { "set-tos", parse_http_set_tos, 0 }, { "strict-mode", parse_http_strict_mode, 0 }, { "track-sc", parse_http_track_sc, KWF_MATCH_PREFIX }, { "wait-for-body", parse_http_wait_for_body, 0 }, diff --git a/src/http_ana.c b/src/http_ana.c index fc0b5bccc2..63ee760349 100644 --- a/src/http_ana.c +++ b/src/http_ana.c @@ -2831,14 +2831,6 @@ static enum rule_result http_req_get_intercept_rule(struct proxy *px, struct lis rule_ret = HTTP_RULE_RES_ERROR; goto end; - case ACT_HTTP_SET_TOS: - conn_set_tos(objt_conn(sess->origin), rule->arg.http.i); - break; - - case ACT_HTTP_SET_MARK: - conn_set_mark(objt_conn(sess->origin), rule->arg.http.i); - break; - /* other flags exists, but normally, they never be matched. */ default: break; @@ -2958,14 +2950,6 @@ resume_execution: rule_ret = HTTP_RULE_RES_DENY; goto end; - case ACT_HTTP_SET_TOS: - conn_set_tos(objt_conn(sess->origin), rule->arg.http.i); - break; - - case ACT_HTTP_SET_MARK: - conn_set_mark(objt_conn(sess->origin), rule->arg.http.i); - break; - case ACT_HTTP_REDIR: rule_ret = HTTP_RULE_RES_ABRT; if (!http_apply_redirect_rule(rule->arg.redir, s, txn)) diff --git a/src/tcp_act.c b/src/tcp_act.c index a6f0596740..cb6c75f403 100644 --- a/src/tcp_act.c +++ b/src/tcp_act.c @@ -236,6 +236,22 @@ static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct return ACT_RET_ABRT; } + +static enum act_return tcp_action_set_mark(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + conn_set_mark(objt_conn(sess->origin), (uintptr_t)rule->arg.act.p[0]); + return ACT_RET_CONT; +} + +static enum act_return tcp_action_set_tos(struct act_rule *rule, struct proxy *px, + struct session *sess, struct stream *s, int flags) +{ + conn_set_tos(objt_conn(sess->origin), (uintptr_t)rule->arg.act.p[0]); + return ACT_RET_CONT; +} + + /* parse "set-{src,dst}[-port]" action */ static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg, struct proxy *px, struct act_rule *rule, char **err) @@ -283,6 +299,75 @@ static enum act_parse_ret tcp_parse_set_src_dst(const char **args, int *orig_arg } +/* Parse a "set-mark" action. It takes the MARK value as argument. It returns + * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error. + */ +static enum act_parse_ret tcp_parse_set_mark(const char **args, int *cur_arg, struct proxy *px, + struct act_rule *rule, char **err) +{ +#ifdef SO_MARK + char *endp; + unsigned int mark; + + if (!*args[*cur_arg]) { + memprintf(err, "expects exactly 1 argument (integer/hex value)"); + return ACT_RET_PRS_ERR; + } + mark = strtoul(args[*cur_arg], &endp, 0); + if (endp && *endp != '\0') { + memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp); + return ACT_RET_PRS_ERR; + } + + (*cur_arg)++; + + /* Register processing function. */ + rule->action_ptr = tcp_action_set_mark; + rule->action = ACT_CUSTOM; + rule->arg.act.p[0] = (void *)(uintptr_t)mark; + global.last_checks |= LSTCHK_NETADM; + return ACT_RET_PRS_OK; +#else + memprintf(err, "not supported on this platform (SO_MARK undefined)"); + return ACT_RET_PRS_ERR; +#endif +} + + +/* Parse a "set-tos" action. It takes the TOS value as argument. It returns + * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error. + */ +static enum act_parse_ret tcp_parse_set_tos(const char **args, int *cur_arg, struct proxy *px, + struct act_rule *rule, char **err) +{ +#ifdef IP_TOS + char *endp; + int tos; + + if (!*args[*cur_arg]) { + memprintf(err, "expects exactly 1 argument (integer/hex value)"); + return ACT_RET_PRS_ERR; + } + tos = strtol(args[*cur_arg], &endp, 0); + if (endp && *endp != '\0') { + memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp); + return ACT_RET_PRS_ERR; + } + + (*cur_arg)++; + + /* Register processing function. */ + rule->action_ptr = tcp_action_set_tos; + rule->action = ACT_CUSTOM; + rule->arg.act.p[0] = (void *)(uintptr_t)tos; + return ACT_RET_PRS_OK; +#else + memprintf(err, "not supported on this platform (IP_TOS undefined)"); + return ACT_RET_PRS_ERR; +#endif +} + + /* Parse a "silent-drop" action. It takes no argument. It returns ACT_RET_PRS_OK on * success, ACT_RET_PRS_ERR on error. */ @@ -296,10 +381,12 @@ static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *orig_arg static struct action_kw_list tcp_req_conn_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, { "set-src", tcp_parse_set_src_dst }, { "set-src-port", tcp_parse_set_src_dst }, { "set-dst" , tcp_parse_set_src_dst }, { "set-dst-port", tcp_parse_set_src_dst }, + { "set-tos", tcp_parse_set_tos }, { "silent-drop", tcp_parse_silent_drop }, { /* END */ } }}; @@ -307,10 +394,12 @@ static struct action_kw_list tcp_req_conn_actions = {ILH, { INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions); static struct action_kw_list tcp_req_sess_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, { "set-src", tcp_parse_set_src_dst }, { "set-src-port", tcp_parse_set_src_dst }, { "set-dst" , tcp_parse_set_src_dst }, { "set-dst-port", tcp_parse_set_src_dst }, + { "set-tos", tcp_parse_set_tos }, { "silent-drop", tcp_parse_silent_drop }, { /* END */ } }}; @@ -318,10 +407,12 @@ static struct action_kw_list tcp_req_sess_actions = {ILH, { INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions); static struct action_kw_list tcp_req_cont_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, { "set-src", tcp_parse_set_src_dst }, { "set-src-port", tcp_parse_set_src_dst }, { "set-dst" , tcp_parse_set_src_dst }, { "set-dst-port", tcp_parse_set_src_dst }, + { "set-tos", tcp_parse_set_tos }, { "silent-drop", tcp_parse_silent_drop }, { /* END */ } }}; @@ -329,6 +420,8 @@ static struct action_kw_list tcp_req_cont_actions = {ILH, { INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_actions); static struct action_kw_list tcp_res_cont_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, + { "set-tos", tcp_parse_set_tos }, { "silent-drop", tcp_parse_silent_drop }, { /* END */ } }}; @@ -336,17 +429,21 @@ static struct action_kw_list tcp_res_cont_actions = {ILH, { INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions); static struct action_kw_list http_req_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, { "silent-drop", tcp_parse_silent_drop }, { "set-src", tcp_parse_set_src_dst }, { "set-src-port", tcp_parse_set_src_dst }, { "set-dst", tcp_parse_set_src_dst }, { "set-dst-port", tcp_parse_set_src_dst }, + { "set-tos", tcp_parse_set_tos }, { /* END */ } }}; INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions); static struct action_kw_list http_res_actions = {ILH, { + { "set-mark", tcp_parse_set_mark }, + { "set-tos", tcp_parse_set_tos }, { "silent-drop", tcp_parse_silent_drop }, { /* END */ } }}; -- 2.39.5