mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 07:37:02 +02:00
This patch allows to specify "tcp-request session attach-srv" without requiring that each associated bind lines mandates HTTP/2 usage. If a non supported protocol is targetted by this rule, conn_install_mux_fe() is responsible to reject it. This change is mandatory to be able to mix attach-srv and standard non-reversable connection on the same bind instances. An ACL can be used to activate attach-srv only on some conditions.
742 lines
21 KiB
C
742 lines
21 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 conjonction with defined SSL SNI on targetted 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;
|
|
|
|
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:
|
|
*/
|