MINOR: quic: support ACL for quic-initial rules

Add ACL condition support for quic-initial rules. This requires the
extension of quic_parse_quic_initial() to parse an extra if/unless
block.

Only layer4 client samples are allowed to be used with quic-initial
rules. However, due to the early execution of quic-initial rules prior
to any connection instantiation, some samples are non supported.

To be able to use the 4 described samples, a dummy session is
instantiated before quic-initial rules execution. Its src and dst fields
are set from the received datagram values.
This commit is contained in:
Amaury Denoyelle 2024-07-19 16:05:15 +02:00
parent cafe596608
commit 1259700763
5 changed files with 81 additions and 7 deletions

View File

@ -10981,7 +10981,7 @@ persist rdp-cookie(<name>)
See also : "balance rdp-cookie", "tcp-request" and the "req.rdp_cookie" ACL.
quic-initial <action>
quic-initial <action> [ { if | unless } <condition> ]
Perform an action on an incoming QUIC Initial packet. Contrary to
"tcp-request connection", this is executed prior to any connection element
instantiation and starting and completion of the SSL handshake, which is more
@ -10996,6 +10996,15 @@ quic-initial <action>
<action> defines the action to perform if the condition applies. See
below.
<condition> is a standard layer4-only ACL-based condition (see section 7).
However, QUIC initial rules are executed too early even for
some layer4 sample fetch methods despite no configuration
warning and may result in unspecified runtime behavior,
although they will not crash. Consider that only internal
samples and layer4 "src*" and "dst*" are considered as
supported for now.
This action is executed early during QUIC packet parsing. As such, only a
minimal list of actions is supported :
- accept

View File

@ -1,13 +1,17 @@
#ifndef _HAPROXY_QUIC_RULES_H
#define _HAPROXY_QUIC_RULES_H
#include <sys/socket.h>
#include <haproxy/action-t.h>
struct listener;
extern struct action_kw_list quic_init_actions_list;
int quic_init_exec_rules(struct listener *li);
int quic_init_exec_rules(struct listener *li,
struct sockaddr_storage *saddr,
struct sockaddr_storage *daddr);
struct action_kw *action_quic_init_custom(const char *kw);

View File

@ -2,6 +2,7 @@
#include <string.h>
#include <netinet/udp.h>
#include <haproxy/acl.h>
#include <haproxy/action.h>
#include <haproxy/api.h>
#include <haproxy/cfgparse.h>
@ -345,10 +346,16 @@ static int quic_parse_quic_initial(char **args, int section_type, struct proxy *
const struct proxy *defpx, const char *file, int line,
char **err)
{
const struct acl *acl;
struct act_rule *rule;
struct action_kw *kw;
const char *acl_kw;
unsigned int where;
int warn = 0;
int arg = 1;
where = SMP_VAL_FE_CON_ACC;
if (curpx == defpx && strlen(defpx->id) == 0) {
memprintf(err, "%s is not allowed in anonymous 'defaults' sections",
args[0]);
@ -397,12 +404,54 @@ static int quic_parse_quic_initial(char **args, int section_type, struct proxy *
goto err;
}
if (strcmp(args[arg], "if") == 0 || strcmp(args[arg], "unless") == 0) {
if ((rule->cond = build_acl_cond(file, line, &curpx->acl, curpx, (const char **)args+arg, err)) == NULL) {
memprintf(err,
"'%s %s %s' : error detected in %s '%s' while parsing '%s' condition : %s",
args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg], *err);
goto err;
}
}
else if (*args[arg]) {
memprintf(err,
"'%s %s %s' only accepts 'if' or 'unless', in %s '%s' (got '%s')",
args[0], args[1], args[2], proxy_type_str(curpx), curpx->id, args[arg]);
goto err;
}
acl = rule->cond ? acl_cond_conflicts(rule->cond, where) : NULL;
if (acl) {
if (acl->name && *acl->name)
memprintf(err,
"acl '%s' will never match in '%s' because it only involves keywords that are incompatible with '%s'",
acl->name, args[0], sample_ckp_names(where));
else
memprintf(err,
"anonymous acl will never match in '%s' because it uses keyword '%s' which is incompatible with '%s'",
args[0],
LIST_ELEM(acl->expr.n, struct acl_expr *, list)->kw,
sample_ckp_names(where));
warn++;
}
else if (rule->cond && acl_cond_kw_conflicts(rule->cond, where, &acl, &acl_kw)) {
if (acl->name && *acl->name)
memprintf(err,
"acl '%s' involves keyword '%s' which is incompatible with '%s'",
acl->name, acl_kw, sample_ckp_names(where));
else
memprintf(err,
"anonymous acl involves keyword '%s' which is incompatible with '%s'",
acl_kw, sample_ckp_names(where));
warn++;
}
/* the following function directly emits the warning */
warnif_misplaced_quic_init(curpx, file, line, args[0]);
LIST_APPEND(&curpx->quic_init_rules, &rule->list);
return 0;
return warn;
err:
free_act_rule(rule);

View File

@ -6,10 +6,14 @@
#include <haproxy/listener.h>
#include <haproxy/proxy-t.h>
#include <haproxy/sample-t.h>
#include <haproxy/session-t.h>
/* Execute registered quic-initial rules on proxy owning <li> listener. */
int quic_init_exec_rules(struct listener *li)
int quic_init_exec_rules(struct listener *li,
struct sockaddr_storage *saddr,
struct sockaddr_storage *daddr)
{
static THREAD_LOCAL struct session rule_sess;
struct act_rule *rule;
enum acl_test_res ret;
struct proxy *px;
@ -17,11 +21,19 @@ int quic_init_exec_rules(struct listener *li)
px = li->bind_conf->frontend;
/* Initialize session elements specific to the current datagram. All
* others members are set to 0 thanks to static storage class.
*/
rule_sess.fe = px;
rule_sess.listener = li;
rule_sess.src = saddr;
rule_sess.dst = daddr;
list_for_each_entry(rule, &px->quic_init_rules, list) {
ret = ACL_TEST_PASS;
if (rule->cond) {
ret = acl_exec_cond(rule->cond, px, NULL, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
ret = acl_exec_cond(rule->cond, px, &rule_sess, NULL, SMP_OPT_DIR_REQ|SMP_OPT_FINAL);
ret = acl_pass(ret);
if (rule->cond->pol == ACL_COND_UNLESS)
ret = !ret;
@ -29,7 +41,7 @@ int quic_init_exec_rules(struct listener *li)
if (ret) {
if (rule->action_ptr) {
switch (rule->action_ptr(rule, px, NULL, NULL, 0)) {
switch (rule->action_ptr(rule, px, &rule_sess, NULL, 0)) {
case ACT_RET_CONT:
break;
case ACT_RET_DONE:

View File

@ -1607,7 +1607,7 @@ static struct quic_conn *quic_rx_pkt_retrieve_conn(struct quic_rx_packet *pkt,
goto err;
}
if (!quic_init_exec_rules(l)) {
if (!quic_init_exec_rules(l, &dgram->saddr, &dgram->daddr)) {
TRACE_USER("drop datagram on quic-initial rules", QUIC_EV_CONN_LPKT, NULL, NULL, NULL, pkt->version);
goto err;
}