mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 15:47:01 +02:00
Mark the reverse HTTP feature as experimental. This will allow to adjust if needed the configuration mechanism with future developments without maintaining retro-compatibility. Concretely, each config directives linked to it now requires to specify first global expose-experimental-directives before. This is the case for the following directives : - rhttp@ prefix uses in bind and server lines - nbconn bind keyword - attach-srv tcp rule Each documentation section refering to these keywords are updated to highlight this new requirement. Note that this commit has duplicated on several places the code from the global function check_kw_experimental(). This is because the latter only work with cfg_keyword type. This is not adapted with bind_kw or action_kw types. This should be improve in a future patch.
750 lines
22 KiB
C
750 lines
22 KiB
C
/*
|
|
* AF_INET/AF_INET6 SOCK_STREAM protocol layer (tcp)
|
|
*
|
|
* Copyright 2000-2013 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version
|
|
* 2 of the License, or (at your option) any later version.
|
|
*
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <netinet/tcp.h>
|
|
#include <netinet/in.h>
|
|
|
|
#include <haproxy/action-t.h>
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/arg.h>
|
|
#include <haproxy/channel.h>
|
|
#include <haproxy/connection.h>
|
|
#include <haproxy/global.h>
|
|
#include <haproxy/http_rules.h>
|
|
#include <haproxy/proto_tcp.h>
|
|
#include <haproxy/proxy.h>
|
|
#include <haproxy/sample.h>
|
|
#include <haproxy/sc_strm.h>
|
|
#include <haproxy/server.h>
|
|
#include <haproxy/session.h>
|
|
#include <haproxy/tcp_rules.h>
|
|
#include <haproxy/tools.h>
|
|
|
|
static enum act_return tcp_action_attach_srv(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *s, int flags)
|
|
{
|
|
struct server *srv = rule->arg.attach_srv.srv;
|
|
struct sample *name_smp;
|
|
struct connection *conn = objt_conn(sess->origin);
|
|
if (!conn)
|
|
return ACT_RET_ABRT;
|
|
|
|
conn_set_reverse(conn, &srv->obj_type);
|
|
|
|
if (rule->arg.attach_srv.name) {
|
|
name_smp = sample_fetch_as_type(sess->fe, sess, s,
|
|
SMP_OPT_DIR_REQ | SMP_OPT_FINAL,
|
|
rule->arg.attach_srv.name, SMP_T_STR);
|
|
/* TODO strdup du buffer du sample */
|
|
if (name_smp) {
|
|
struct buffer *buf = &name_smp->data.u.str;
|
|
char *area = malloc(b_data(buf));
|
|
|
|
if (!area)
|
|
return ACT_RET_ERR;
|
|
|
|
conn->reverse.name = b_make(area, b_data(buf), 0, 0);
|
|
b_ncat(&conn->reverse.name, buf, b_data(buf));
|
|
}
|
|
}
|
|
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
* previous family was neither AF_INET nor AF_INET6, the port is set to zero.
|
|
*/
|
|
static enum act_return tcp_action_req_set_src(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *s, int flags)
|
|
{
|
|
struct connection *cli_conn;
|
|
struct sockaddr_storage *src;
|
|
struct sample *smp;
|
|
|
|
switch (rule->from) {
|
|
case ACT_F_TCP_REQ_CON:
|
|
cli_conn = objt_conn(sess->origin);
|
|
if (!cli_conn || !conn_get_src(cli_conn))
|
|
goto end;
|
|
src = cli_conn->src;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_SES:
|
|
if (!sess_get_src(sess))
|
|
goto end;
|
|
src = sess->src;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_CNT:
|
|
case ACT_F_HTTP_REQ:
|
|
if (!sc_get_src(s->scf))
|
|
goto end;
|
|
src = s->scf->src;
|
|
break;
|
|
|
|
default:
|
|
goto end;
|
|
}
|
|
|
|
smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
|
|
if (smp) {
|
|
int port = get_net_port(src);
|
|
|
|
if (smp->data.type == SMP_T_IPV4) {
|
|
((struct sockaddr_in *)src)->sin_family = AF_INET;
|
|
((struct sockaddr_in *)src)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
|
|
((struct sockaddr_in *)src)->sin_port = port;
|
|
} else if (smp->data.type == SMP_T_IPV6) {
|
|
((struct sockaddr_in6 *)src)->sin6_family = AF_INET6;
|
|
memcpy(&((struct sockaddr_in6 *)src)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
|
|
((struct sockaddr_in6 *)src)->sin6_port = port;
|
|
}
|
|
}
|
|
|
|
end:
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
/*
|
|
* Execute the "set-dst" action. May be called from {tcp,http}request.
|
|
* It only changes the address and tries to preserve the original port. If the
|
|
* previous family was neither AF_INET nor AF_INET6, the port is set to zero.
|
|
*/
|
|
static enum act_return tcp_action_req_set_dst(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *s, int flags)
|
|
{
|
|
struct connection *cli_conn;
|
|
struct sockaddr_storage *dst;
|
|
struct sample *smp;
|
|
|
|
switch (rule->from) {
|
|
case ACT_F_TCP_REQ_CON:
|
|
cli_conn = objt_conn(sess->origin);
|
|
if (!cli_conn || !conn_get_dst(cli_conn))
|
|
goto end;
|
|
dst = cli_conn->dst;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_SES:
|
|
if (!sess_get_dst(sess))
|
|
goto end;
|
|
dst = sess->dst;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_CNT:
|
|
case ACT_F_HTTP_REQ:
|
|
if (!sc_get_dst(s->scf))
|
|
goto end;
|
|
dst = s->scf->dst;
|
|
break;
|
|
|
|
default:
|
|
goto end;
|
|
}
|
|
|
|
smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_ADDR);
|
|
if (smp) {
|
|
int port = get_net_port(dst);
|
|
|
|
if (smp->data.type == SMP_T_IPV4) {
|
|
((struct sockaddr_in *)dst)->sin_family = AF_INET;
|
|
((struct sockaddr_in *)dst)->sin_addr.s_addr = smp->data.u.ipv4.s_addr;
|
|
((struct sockaddr_in *)dst)->sin_port = port;
|
|
} else if (smp->data.type == SMP_T_IPV6) {
|
|
((struct sockaddr_in6 *)dst)->sin6_family = AF_INET6;
|
|
memcpy(&((struct sockaddr_in6 *)dst)->sin6_addr, &smp->data.u.ipv6, sizeof(struct in6_addr));
|
|
((struct sockaddr_in6 *)dst)->sin6_port = port;
|
|
}
|
|
}
|
|
|
|
end:
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
/*
|
|
* Execute the "set-src-port" action. May be called from {tcp,http}request.
|
|
* We must test the sin_family before setting the port. If the address family
|
|
* is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
|
|
* and the port is assigned.
|
|
*/
|
|
static enum act_return tcp_action_req_set_src_port(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *s, int flags)
|
|
{
|
|
struct connection *cli_conn;
|
|
struct sockaddr_storage *src;
|
|
struct sample *smp;
|
|
|
|
switch (rule->from) {
|
|
case ACT_F_TCP_REQ_CON:
|
|
cli_conn = objt_conn(sess->origin);
|
|
if (!cli_conn || !conn_get_src(cli_conn))
|
|
goto end;
|
|
src = cli_conn->src;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_SES:
|
|
if (!sess_get_src(sess))
|
|
goto end;
|
|
src = sess->src;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_CNT:
|
|
case ACT_F_HTTP_REQ:
|
|
if (!sc_get_src(s->scf))
|
|
goto end;
|
|
src = s->scf->src;
|
|
break;
|
|
|
|
default:
|
|
goto end;
|
|
}
|
|
|
|
smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
|
|
if (smp) {
|
|
if (src->ss_family == AF_INET6) {
|
|
((struct sockaddr_in6 *)src)->sin6_port = htons(smp->data.u.sint);
|
|
} else {
|
|
if (src->ss_family != AF_INET) {
|
|
src->ss_family = AF_INET;
|
|
((struct sockaddr_in *)src)->sin_addr.s_addr = 0;
|
|
}
|
|
((struct sockaddr_in *)src)->sin_port = htons(smp->data.u.sint);
|
|
}
|
|
}
|
|
|
|
end:
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
/*
|
|
* Execute the "set-dst-port" action. May be called from {tcp,http}request.
|
|
* We must test the sin_family before setting the port. If the address family
|
|
* is neither AF_INET nor AF_INET6, the address is forced to AF_INET "0.0.0.0"
|
|
* and the port is assigned.
|
|
*/
|
|
static enum act_return tcp_action_req_set_dst_port(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *s, int flags)
|
|
{
|
|
struct connection *cli_conn;
|
|
struct sockaddr_storage *dst;
|
|
struct sample *smp;
|
|
|
|
switch (rule->from) {
|
|
case ACT_F_TCP_REQ_CON:
|
|
cli_conn = objt_conn(sess->origin);
|
|
if (!cli_conn || !conn_get_dst(cli_conn))
|
|
goto end;
|
|
dst = cli_conn->dst;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_SES:
|
|
if (!sess_get_dst(sess))
|
|
goto end;
|
|
dst = sess->dst;
|
|
break;
|
|
|
|
case ACT_F_TCP_REQ_CNT:
|
|
case ACT_F_HTTP_REQ:
|
|
if (!sc_get_dst(s->scf))
|
|
goto end;
|
|
dst = s->scf->dst;
|
|
break;
|
|
|
|
default:
|
|
goto end;
|
|
}
|
|
|
|
smp = sample_fetch_as_type(px, sess, s, SMP_OPT_DIR_REQ|SMP_OPT_FINAL, rule->arg.expr, SMP_T_SINT);
|
|
if (smp) {
|
|
if (dst->ss_family == AF_INET6) {
|
|
((struct sockaddr_in6 *)dst)->sin6_port = htons(smp->data.u.sint);
|
|
} else {
|
|
if (dst->ss_family != AF_INET) {
|
|
dst->ss_family = AF_INET;
|
|
((struct sockaddr_in *)dst)->sin_addr.s_addr = 0;
|
|
}
|
|
((struct sockaddr_in *)dst)->sin_port = htons(smp->data.u.sint);
|
|
}
|
|
}
|
|
|
|
end:
|
|
return ACT_RET_CONT;
|
|
}
|
|
|
|
/* Executes the "silent-drop" action. May be called from {tcp,http}{request,response}.
|
|
* If rule->arg.act.p[0] is 0, TCP_REPAIR is tried first, with a fallback to
|
|
* sending a RST with TTL 1 towards the client. If it is [1-255], we will skip
|
|
* TCP_REPAIR and prepare the socket to send a RST with the requested TTL when
|
|
* the connection is killed by channel_abort().
|
|
*/
|
|
static enum act_return tcp_exec_action_silent_drop(struct act_rule *rule, struct proxy *px,
|
|
struct session *sess, struct stream *strm, int flags)
|
|
{
|
|
struct connection *conn = objt_conn(sess->origin);
|
|
unsigned int ttl __maybe_unused = (uintptr_t)rule->arg.act.p[0];
|
|
char tcp_repair_enabled __maybe_unused;
|
|
|
|
if (ttl == 0) {
|
|
tcp_repair_enabled = 1;
|
|
ttl = 1;
|
|
} else {
|
|
tcp_repair_enabled = 0;
|
|
}
|
|
|
|
if (!conn)
|
|
goto out;
|
|
|
|
if (!conn_ctrl_ready(conn))
|
|
goto out;
|
|
|
|
#ifdef TCP_QUICKACK
|
|
/* drain is needed only to send the quick ACK */
|
|
conn_ctrl_drain(conn);
|
|
|
|
/* re-enable quickack if it was disabled to ack all data and avoid
|
|
* retransmits from the client that might trigger a real reset.
|
|
*/
|
|
setsockopt(conn->handle.fd, IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
|
|
#endif
|
|
/* lingering must absolutely be disabled so that we don't send a
|
|
* shutdown(), this is critical to the TCP_REPAIR trick. When no stream
|
|
* is present, returning with ERR will cause lingering to be disabled.
|
|
*/
|
|
if (strm)
|
|
strm->scf->flags |= SC_FL_NOLINGER;
|
|
|
|
if (conn->flags & CO_FL_FDLESS)
|
|
goto out;
|
|
|
|
/* We're on the client-facing side, we must force to disable lingering to
|
|
* ensure we will use an RST exclusively and kill any pending data.
|
|
*/
|
|
HA_ATOMIC_OR(&fdtab[conn->handle.fd].state, FD_LINGER_RISK);
|
|
|
|
#ifdef TCP_REPAIR
|
|
/* try to put socket in repair mode if sending a RST was not requested by
|
|
* config. this often fails due to missing permissions (CAP_NET_ADMIN capability)
|
|
*/
|
|
if (tcp_repair_enabled && (setsockopt(conn->handle.fd, IPPROTO_TCP, TCP_REPAIR, &one, sizeof(one)) == 0)) {
|
|
/* socket will be quiet now */
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
/* Either TCP_REPAIR is not defined, it failed (eg: permissions), or was
|
|
* not executed because a RST with a specific TTL was requested to be sent.
|
|
* Set the TTL of the client connection before the connection is killed
|
|
* by channel_abort and a RST packet will be emitted by the TCP/IP stack.
|
|
*/
|
|
#ifdef IP_TTL
|
|
if (conn->src && conn->src->ss_family == AF_INET)
|
|
setsockopt(conn->handle.fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
|
|
#endif
|
|
#ifdef IPV6_UNICAST_HOPS
|
|
if (conn->src && conn->src->ss_family == AF_INET6)
|
|
setsockopt(conn->handle.fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl));
|
|
#endif
|
|
out:
|
|
/* kill the stream if any */
|
|
if (strm) {
|
|
stream_abort(strm);
|
|
strm->req.analysers &= AN_REQ_FLT_END;
|
|
strm->res.analysers &= AN_RES_FLT_END;
|
|
if (strm->flags & SF_BE_ASSIGNED)
|
|
_HA_ATOMIC_INC(&strm->be->be_counters.denied_req);
|
|
if (!(strm->flags & SF_ERR_MASK))
|
|
strm->flags |= SF_ERR_PRXCOND;
|
|
if (!(strm->flags & SF_FINST_MASK))
|
|
strm->flags |= SF_FINST_R;
|
|
}
|
|
|
|
_HA_ATOMIC_INC(&sess->fe->fe_counters.denied_req);
|
|
if (sess->listener && sess->listener->counters)
|
|
_HA_ATOMIC_INC(&sess->listener->counters->denied_req);
|
|
|
|
return ACT_RET_ABRT;
|
|
}
|
|
|
|
|
|
#if defined(SO_MARK) || defined(SO_USER_COOKIE) || defined(SO_RTABLE)
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#ifdef IP_TOS
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Release the sample expr when releasing attach-srv action
|
|
*/
|
|
static void release_attach_srv_action(struct act_rule *rule)
|
|
{
|
|
ha_free(&rule->arg.attach_srv.srvname);
|
|
release_sample_expr(rule->arg.attach_srv.name);
|
|
}
|
|
|
|
/*
|
|
* Release the sample expr when releasing a set src/dst action
|
|
*/
|
|
static void release_set_src_dst_action(struct act_rule *rule)
|
|
{
|
|
release_sample_expr(rule->arg.expr);
|
|
}
|
|
|
|
static int tcp_check_attach_srv(struct act_rule *rule, struct proxy *px, char **err)
|
|
{
|
|
struct proxy *be = NULL;
|
|
struct server *srv = NULL;
|
|
char *name = rule->arg.attach_srv.srvname;
|
|
struct ist be_name, sv_name;
|
|
|
|
if (px->mode != PR_MODE_HTTP) {
|
|
memprintf(err, "attach-srv rule requires HTTP proxy mode");
|
|
return 0;
|
|
}
|
|
|
|
sv_name = ist(name);
|
|
be_name = istsplit(&sv_name, '/');
|
|
if (!istlen(sv_name)) {
|
|
memprintf(err, "attach-srv rule: invalid server name '%s'", name);
|
|
return 0;
|
|
}
|
|
|
|
if (!(be = proxy_be_by_name(ist0(be_name)))) {
|
|
memprintf(err, "attach-srv rule: no such backend '%s/%s'", ist0(be_name), ist0(sv_name));
|
|
return 0;
|
|
}
|
|
if (!(srv = server_find_by_name(be, ist0(sv_name)))) {
|
|
memprintf(err, "attach-srv rule: no such server '%s/%s'", ist0(be_name), ist0(sv_name));
|
|
return 0;
|
|
}
|
|
|
|
if ((rule->arg.attach_srv.name && (!srv->use_ssl || !srv->sni_expr)) ||
|
|
(!rule->arg.attach_srv.name && srv->use_ssl && srv->sni_expr)) {
|
|
memprintf(err, "attach-srv rule: connection will never be used; either specify name argument in conjunction with defined SSL SNI on targeted server or none of these");
|
|
return 0;
|
|
}
|
|
|
|
rule->arg.attach_srv.srv = srv;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static enum act_parse_ret tcp_parse_attach_srv(const char **args, int *cur_arg, struct proxy *px,
|
|
struct act_rule *rule, char **err)
|
|
{
|
|
char *srvname;
|
|
struct sample_expr *expr;
|
|
|
|
/* TODO duplicated code from check_kw_experimental() */
|
|
if (!experimental_directives_allowed) {
|
|
memprintf(err, "parsing [%s:%d] : '%s' action is experimental, must be allowed via a global 'expose-experimental-directives'",
|
|
px->conf.args.file, px->conf.args.line, args[2]);
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
mark_tainted(TAINTED_CONFIG_EXP_KW_DECLARED);
|
|
|
|
rule->action = ACT_CUSTOM;
|
|
rule->action_ptr = tcp_action_attach_srv;
|
|
rule->release_ptr = release_attach_srv_action;
|
|
rule->check_ptr = tcp_check_attach_srv;
|
|
rule->arg.attach_srv.srvname = NULL;
|
|
rule->arg.attach_srv.name = NULL;
|
|
|
|
srvname = my_strndup(args[*cur_arg], strlen(args[*cur_arg]));
|
|
if (!srvname)
|
|
goto err;
|
|
rule->arg.attach_srv.srvname = srvname;
|
|
|
|
++(*cur_arg);
|
|
|
|
if (strcmp(args[*cur_arg], "name") == 0) {
|
|
if (!*args[*cur_arg + 1]) {
|
|
memprintf(err, "missing name value");
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
++(*cur_arg);
|
|
|
|
expr = sample_parse_expr((char **)args, cur_arg, px->conf.args.file, px->conf.args.line,
|
|
err, &px->conf.args, NULL);
|
|
if (!expr)
|
|
return ACT_RET_PRS_ERR;
|
|
|
|
rule->arg.attach_srv.name = expr;
|
|
rule->release_ptr = release_attach_srv_action;
|
|
}
|
|
|
|
return ACT_RET_PRS_OK;
|
|
|
|
err:
|
|
ha_free(&rule->arg.attach_srv.srvname);
|
|
release_sample_expr(rule->arg.attach_srv.name);
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
int cur_arg;
|
|
struct sample_expr *expr;
|
|
unsigned int where;
|
|
|
|
cur_arg = *orig_arg;
|
|
expr = sample_parse_expr((char **)args, &cur_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 = expr;
|
|
rule->action = ACT_CUSTOM;
|
|
|
|
if (strcmp(args[*orig_arg - 1], "set-src") == 0) {
|
|
rule->action_ptr = tcp_action_req_set_src;
|
|
} else if (strcmp(args[*orig_arg - 1], "set-src-port") == 0) {
|
|
rule->action_ptr = tcp_action_req_set_src_port;
|
|
} else if (strcmp(args[*orig_arg - 1], "set-dst") == 0) {
|
|
rule->action_ptr = tcp_action_req_set_dst;
|
|
} else if (strcmp(args[*orig_arg - 1], "set-dst-port") == 0) {
|
|
rule->action_ptr = tcp_action_req_set_dst_port;
|
|
} else {
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
|
|
rule->release_ptr = release_set_src_dst_action;
|
|
(*orig_arg)++;
|
|
|
|
return ACT_RET_PRS_OK;
|
|
}
|
|
|
|
|
|
/* 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)
|
|
{
|
|
#if defined(SO_MARK) || defined(SO_USER_COOKIE) || defined(SO_RTABLE)
|
|
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|SO_USER_COOKIE|SO_RTABLE 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 may take 2 optional arguments to define a
|
|
* "rst-ttl" parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR
|
|
* on error.
|
|
*/
|
|
static enum act_parse_ret tcp_parse_silent_drop(const char **args, int *cur_arg, struct proxy *px,
|
|
struct act_rule *rule, char **err)
|
|
{
|
|
unsigned int rst_ttl = 0;
|
|
char *endp;
|
|
|
|
rule->action = ACT_CUSTOM;
|
|
rule->action_ptr = tcp_exec_action_silent_drop;
|
|
|
|
if (strcmp(args[*cur_arg], "rst-ttl") == 0) {
|
|
if (!*args[*cur_arg + 1]) {
|
|
memprintf(err, "missing rst-ttl value\n");
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
|
|
rst_ttl = (unsigned int)strtoul(args[*cur_arg + 1], &endp, 0);
|
|
|
|
if (endp && *endp != '\0') {
|
|
memprintf(err, "invalid character starting at '%s' (value 1-255 expected)\n",
|
|
endp);
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
if ((rst_ttl == 0) || (rst_ttl > 255) ) {
|
|
memprintf(err, "valid rst-ttl values are [1-255]\n");
|
|
return ACT_RET_PRS_ERR;
|
|
}
|
|
|
|
*cur_arg += 2;
|
|
}
|
|
|
|
rule->arg.act.p[0] = (void *)(uintptr_t)rst_ttl;
|
|
return ACT_RET_PRS_OK;
|
|
}
|
|
|
|
|
|
static struct action_kw_list tcp_req_conn_actions = {ILH, {
|
|
{ "set-dst" , tcp_parse_set_src_dst },
|
|
{ "set-dst-port", tcp_parse_set_src_dst },
|
|
{ "set-mark", tcp_parse_set_mark },
|
|
{ "set-src", tcp_parse_set_src_dst },
|
|
{ "set-src-port", tcp_parse_set_src_dst },
|
|
{ "set-tos", tcp_parse_set_tos },
|
|
{ "silent-drop", tcp_parse_silent_drop },
|
|
{ /* END */ }
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_actions);
|
|
|
|
static struct action_kw_list tcp_req_sess_actions = {ILH, {
|
|
{ "attach-srv" , tcp_parse_attach_srv },
|
|
{ "set-dst" , tcp_parse_set_src_dst },
|
|
{ "set-dst-port", tcp_parse_set_src_dst },
|
|
{ "set-mark", tcp_parse_set_mark },
|
|
{ "set-src", tcp_parse_set_src_dst },
|
|
{ "set-src-port", tcp_parse_set_src_dst },
|
|
{ "set-tos", tcp_parse_set_tos },
|
|
{ "silent-drop", tcp_parse_silent_drop },
|
|
{ /* END */ }
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_actions);
|
|
|
|
static struct action_kw_list tcp_req_cont_actions = {ILH, {
|
|
{ "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-mark", tcp_parse_set_mark },
|
|
{ "set-tos", tcp_parse_set_tos },
|
|
{ "silent-drop", tcp_parse_silent_drop },
|
|
{ /* END */ }
|
|
}};
|
|
|
|
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 */ }
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_cont_actions);
|
|
|
|
static struct action_kw_list http_req_actions = {ILH, {
|
|
{ "set-dst", tcp_parse_set_src_dst },
|
|
{ "set-dst-port", tcp_parse_set_src_dst },
|
|
{ "set-mark", tcp_parse_set_mark },
|
|
{ "set-src", tcp_parse_set_src_dst },
|
|
{ "set-src-port", tcp_parse_set_src_dst },
|
|
{ "set-tos", tcp_parse_set_tos },
|
|
{ "silent-drop", tcp_parse_silent_drop },
|
|
{ /* 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 */ }
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_actions);
|
|
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-indent-level: 8
|
|
* c-basic-offset: 8
|
|
* End:
|
|
*/
|