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
<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
#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);
#include <string.h>
#include <netinet/udp.h>
+#include <haproxy/acl.h>
#include <haproxy/action.h>
#include <haproxy/api.h>
#include <haproxy/cfgparse.h>
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]);
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);
#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;
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;
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:
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;
}