mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2024-12-28 15:42:30 +00:00
MEDIUM: tcp-act: <expr> support for set-fc-{mark,tos} actions
In this patch we add the possibility to use sample expression as argument for set-fc-{mark,tos} actions. To make it backward compatible with previous behavior, during parsing we first try to parse the value as as integer (decimal or hex notation), and then fallback to expr parsing in case of failure. The documentation was updated accordingly.
This commit is contained in:
parent
03cb782bcb
commit
b4ee7b044e
@ -14744,32 +14744,35 @@ set-dst-port <expr>
|
||||
destination address to IPv4 "0.0.0.0" before rewriting the port.
|
||||
|
||||
|
||||
set-fc-mark <mark>
|
||||
set-fc-mark { <mark> | <expr> }
|
||||
Usable in: TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
X | X | X | X | X | X | -
|
||||
|
||||
This is used to set the Netfilter/IPFW MARK on 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/ipfw and by the
|
||||
routing table or monitoring the packets through DTrace. 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, as well on FreeBSD
|
||||
and OpenBSD.
|
||||
to the value passed in <mark> or <expr> on platforms which support it. This
|
||||
value is an unsigned 32 bit value which can be matched by netfilter/ipfw and
|
||||
by the routing table or monitoring the packets through DTrace. <mark> can be
|
||||
expressed both in decimal or hexadecimal format (prefixed by "0x").
|
||||
Alternatively, <expr> can be used: it is a standard HAProxy expression formed
|
||||
by a sample-fetch followed by some converters which must resolve to integer
|
||||
type. This action 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, as well on
|
||||
FreeBSD and OpenBSD.
|
||||
|
||||
|
||||
set-fc-tos <tos>
|
||||
set-fc-tos { <tos | <expr> }
|
||||
Usable in: TCP RqCon| RqSes| RqCnt| RsCnt| HTTP Req| Res| Aft
|
||||
X | X | X | X | X | X | -
|
||||
|
||||
This 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.
|
||||
to the value passed in <tos> or <expr> on platforms which support this. This
|
||||
value represents the whole 8 bits of the IP TOS field. Note that only the 6
|
||||
higher bits are used in DSCP or TOS, and the two lower bits are always 0.
|
||||
Alternatively, <expr> can be used: it is a standard HAProxy expression formed
|
||||
by a sample-fetch followed by some converters which must resolve to integer
|
||||
type. This action 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.
|
||||
|
||||
|
@ -191,6 +191,10 @@ struct act_rule {
|
||||
struct server *srv; /* target server to attach the connection */
|
||||
struct sample_expr *name; /* used to differentiate idle connections */
|
||||
} attach_srv; /* 'attach-srv' rule */
|
||||
struct {
|
||||
int value;
|
||||
struct sample_expr *expr;
|
||||
} expr_int; /* expr or int value (when expr is NULL)*/
|
||||
struct {
|
||||
void *p[4];
|
||||
} act; /* generic pointers to be used by custom actions */
|
||||
|
137
src/tcp_act.c
137
src/tcp_act.c
@ -71,6 +71,29 @@ static enum act_return tcp_action_attach_srv(struct act_rule *rule, struct proxy
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
|
||||
/* tries to extract integer value from rule's argument:
|
||||
* if expr is set, computes expr and sets the result into <value>
|
||||
* else, it's already a numerical value, use it as-is.
|
||||
*
|
||||
* Returns 1 on success and 0 on failure.
|
||||
*/
|
||||
static int extract_int_from_rule(struct act_rule *rule,
|
||||
struct proxy *px, struct session *sess, struct stream *s,
|
||||
int *value)
|
||||
{
|
||||
struct sample *smp;
|
||||
|
||||
if (!rule->arg.expr_int.expr) {
|
||||
*value = rule->arg.expr_int.value;
|
||||
return 1;
|
||||
}
|
||||
smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr_int.expr, SMP_T_SINT);
|
||||
if (!smp)
|
||||
return 0;
|
||||
*value = smp->data.u.sint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Execute the "set-src" action. May be called from {tcp,http}request.
|
||||
* It only changes the address and tries to preserve the original port. If the
|
||||
@ -392,7 +415,10 @@ static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct
|
||||
static enum act_return tcp_action_set_fc_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]);
|
||||
unsigned int mark;
|
||||
|
||||
if (extract_int_from_rule(rule, px, sess, s, (int *)&mark))
|
||||
conn_set_mark(objt_conn(sess->origin), mark);
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
#endif
|
||||
@ -401,7 +427,10 @@ static enum act_return tcp_action_set_fc_mark(struct act_rule *rule, struct prox
|
||||
static enum act_return tcp_action_set_fc_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]);
|
||||
int tos;
|
||||
|
||||
if (extract_int_from_rule(rule, px, sess, s, &tos))
|
||||
conn_set_tos(objt_conn(sess->origin), tos);
|
||||
return ACT_RET_CONT;
|
||||
}
|
||||
#endif
|
||||
@ -423,6 +452,14 @@ static void release_set_src_dst_action(struct act_rule *rule)
|
||||
release_sample_expr(rule->arg.expr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Release expr_int rule argument when action is no longer used
|
||||
*/
|
||||
static __maybe_unused void release_expr_int_action(struct act_rule *rule)
|
||||
{
|
||||
release_sample_expr(rule->arg.expr_int.expr);
|
||||
}
|
||||
|
||||
static int tcp_check_attach_srv(struct act_rule *rule, struct proxy *px, char **err)
|
||||
{
|
||||
struct proxy *be = NULL;
|
||||
@ -565,29 +602,53 @@ 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)
|
||||
static enum act_parse_ret tcp_parse_set_mark(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
#if defined(SO_MARK) || defined(SO_USER_COOKIE) || defined(SO_RTABLE)
|
||||
struct sample_expr *expr;
|
||||
char *endp;
|
||||
unsigned int mark;
|
||||
unsigned int where;
|
||||
int cur_arg = *orig_arg;
|
||||
|
||||
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);
|
||||
if (!*args[*orig_arg]) {
|
||||
memprintf(err, "expects an argument");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
(*cur_arg)++;
|
||||
/* value may be either an unsigned integer or an expression */
|
||||
rule->arg.expr_int.expr = NULL;
|
||||
rule->arg.expr_int.value = strtoul(args[*orig_arg], &endp, 0);
|
||||
if (*endp == '\0') {
|
||||
/* valid unsigned integer */
|
||||
(*orig_arg)++;
|
||||
}
|
||||
else {
|
||||
/* invalid unsigned integer, fallback to expr */
|
||||
expr = sample_parse_expr((char **)args, orig_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
where = 0;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
where |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
|
||||
if (!(expr->fetch->val & where)) {
|
||||
memprintf(err,
|
||||
"fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
rule->arg.expr_int.expr = expr;
|
||||
}
|
||||
|
||||
/* Register processing function. */
|
||||
rule->action_ptr = tcp_action_set_fc_mark;
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->arg.act.p[0] = (void *)(uintptr_t)mark;
|
||||
rule->release_ptr = release_expr_int_action;
|
||||
global.last_checks |= LSTCHK_NETADM;
|
||||
return ACT_RET_PRS_OK;
|
||||
#else
|
||||
@ -600,29 +661,53 @@ static enum act_parse_ret tcp_parse_set_mark(const char **args, int *cur_arg, st
|
||||
/* 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)
|
||||
static enum act_parse_ret tcp_parse_set_tos(const char **args, int *orig_arg, struct proxy *px,
|
||||
struct act_rule *rule, char **err)
|
||||
{
|
||||
#ifdef IP_TOS
|
||||
struct sample_expr *expr;
|
||||
char *endp;
|
||||
int tos;
|
||||
unsigned int where;
|
||||
int cur_arg = *orig_arg;
|
||||
|
||||
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);
|
||||
if (!*args[*orig_arg]) {
|
||||
memprintf(err, "expects an argument");
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
|
||||
(*cur_arg)++;
|
||||
/* value may be either an integer or an expression */
|
||||
rule->arg.expr_int.expr = NULL;
|
||||
rule->arg.expr_int.value = strtol(args[*orig_arg], &endp, 0);
|
||||
if (*endp == '\0') {
|
||||
/* valid integer */
|
||||
(*orig_arg)++;
|
||||
}
|
||||
else {
|
||||
/* invalid unsigned integer, fallback to expr */
|
||||
expr = sample_parse_expr((char **)args, orig_arg, px->conf.args.file, px->conf.args.line, err, &px->conf.args, NULL);
|
||||
if (!expr)
|
||||
return ACT_RET_PRS_ERR;
|
||||
|
||||
where = 0;
|
||||
if (px->cap & PR_CAP_FE)
|
||||
where |= SMP_VAL_FE_HRQ_HDR;
|
||||
if (px->cap & PR_CAP_BE)
|
||||
where |= SMP_VAL_BE_HRQ_HDR;
|
||||
|
||||
if (!(expr->fetch->val & where)) {
|
||||
memprintf(err,
|
||||
"fetch method '%s' extracts information from '%s', none of which is available here",
|
||||
args[cur_arg-1], sample_src_names(expr->fetch->use));
|
||||
free(expr);
|
||||
return ACT_RET_PRS_ERR;
|
||||
}
|
||||
rule->arg.expr_int.expr = expr;
|
||||
}
|
||||
|
||||
/* Register processing function. */
|
||||
rule->action_ptr = tcp_action_set_fc_tos;
|
||||
rule->action = ACT_CUSTOM;
|
||||
rule->arg.act.p[0] = (void *)(uintptr_t)tos;
|
||||
rule->release_ptr = release_expr_int_action;
|
||||
return ACT_RET_PRS_OK;
|
||||
#else
|
||||
memprintf(err, "not supported on this platform (IP_TOS undefined)");
|
||||
|
Loading…
Reference in New Issue
Block a user