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.
This commit is contained in:
Christopher Faulet 2021-06-25 15:11:35 +02:00
parent 1da374af2f
commit 469c06c30e
5 changed files with 138 additions and 87 deletions

View File

@ -11897,6 +11897,16 @@ tcp-request connection <action> [{if | unless} <condition>]
expected result is a boolean. If an error occurs, this action silently
fails and the actions evaluation continues.
- set-mark <mark>:
Is used to set the Netfilter MARK in all packets sent to the client to
the value passed in <mark> 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 <expr> :
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 <action> [{if | unless} <condition>]
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 <tos>:
Is used to set the TOS or DSCP field value of packets sent to the client
to the value passed in <tos> 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 <action> [{if | unless} <condition>]
- set-dst <expr>
- set-dst-port <expr>
- set-log-level <level>
- set-mark <mark>
- set-nice <nice>
- set-src <expr>
- set-src-port <expr>
- set-tos <tos>
- set-var(<var-name>) <expr>
- switch-mode http [ proto <name> ]
- unset-var(<var-name>)
@ -12113,12 +12136,18 @@ tcp-request content <action> [{if | unless} <condition>]
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 <action> [{if | unless} <condition>]
session. More information on how to use it at "http-response
set-log-level".
- set-mark <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 <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 <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(<var-name>) <expr>
Sets a variable.
@ -12510,10 +12549,12 @@ tcp-request session <action> [{if | unless} <condition>]
- sc-inc-gpc0(<sc-id>)
- sc-inc-gpc1(<sc-id>)
- sc-set-gpt0(<sc-id>) { <int> | <expr> }
- set-mark <mark>
- set-dst <expr>
- set-dst-port <expr>
- set-src <expr>
- set-src-port <expr>
- set-tos <tos>
- set-var(<var-name>) <expr>
- unset-var(<var-name>)
- silent-drop

View File

@ -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,

View File

@ -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 },

View File

@ -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))

View File

@ -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 */ }
}};