From: Amaury Denoyelle Date: Fri, 19 Jul 2024 14:05:15 +0000 (+0200) Subject: MINOR: quic: support ACL for quic-initial rules X-Git-Tag: v3.1-dev5~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1259700763894a73866702913196c25a70f9706b;p=thirdparty%2Fhaproxy.git 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index a64320a698..0af6bb04fd 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -10981,7 +10981,7 @@ persist rdp-cookie() See also : "balance rdp-cookie", "tcp-request" and the "req.rdp_cookie" ACL. -quic-initial +quic-initial [ { if | unless } ] 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 defines the action to perform if the condition applies. See below. + 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 diff --git a/include/haproxy/quic_rules.h b/include/haproxy/quic_rules.h index a38ff696a8..2cfcf99644 100644 --- a/include/haproxy/quic_rules.h +++ b/include/haproxy/quic_rules.h @@ -1,13 +1,17 @@ #ifndef _HAPROXY_QUIC_RULES_H #define _HAPROXY_QUIC_RULES_H +#include + #include 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); diff --git a/src/cfgparse-quic.c b/src/cfgparse-quic.c index 6b287e2910..929360de19 100644 --- a/src/cfgparse-quic.c +++ b/src/cfgparse-quic.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -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); diff --git a/src/quic_rules.c b/src/quic_rules.c index 909c0a08b8..cd7202b61e 100644 --- a/src/quic_rules.c +++ b/src/quic_rules.c @@ -6,10 +6,14 @@ #include #include #include +#include /* Execute registered quic-initial rules on proxy owning
  • 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: diff --git a/src/quic_rx.c b/src/quic_rx.c index 6bec16dc65..d9c4e3290a 100644 --- a/src/quic_rx.c +++ b/src/quic_rx.c @@ -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; }