mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-09 16:47:18 +02:00
tune.quic.zero-copy-fwd-send can now be used to enable or disable the zero-copy fast-forwarding for the QUIC mux only, for sends. For now, there is no option to disable it for receives because it is not supported yet. It is enabled ('on') by default.
283 lines
8.0 KiB
C
283 lines
8.0 KiB
C
#include <errno.h>
|
|
#include <string.h>
|
|
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/cfgparse.h>
|
|
#include <haproxy/errors.h>
|
|
#include <haproxy/global.h>
|
|
#include <haproxy/listener.h>
|
|
#include <haproxy/proxy-t.h>
|
|
#include <haproxy/quic_cc-t.h>
|
|
#include <haproxy/tools.h>
|
|
|
|
static int bind_parse_quic_force_retry(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
|
{
|
|
conf->options |= BC_O_QUIC_FORCE_RETRY;
|
|
return 0;
|
|
}
|
|
|
|
/* parse "quic-cc-algo" bind keyword */
|
|
static int bind_parse_quic_cc_algo(char **args, int cur_arg, struct proxy *px,
|
|
struct bind_conf *conf, char **err)
|
|
{
|
|
struct quic_cc_algo *cc_algo;
|
|
const char *newreno = "newrno";
|
|
const char *cubic = "cubic";
|
|
const char *nocc = "nocc";
|
|
const char *algo = NULL;
|
|
char *arg;
|
|
|
|
if (!*args[cur_arg + 1]) {
|
|
memprintf(err, "'%s' : missing control congestion algorithm", args[cur_arg]);
|
|
goto fail;
|
|
}
|
|
|
|
arg = args[cur_arg + 1];
|
|
if (strncmp(arg, newreno, strlen(newreno)) == 0) {
|
|
/* newreno */
|
|
algo = newreno;
|
|
cc_algo = &quic_cc_algo_nr;
|
|
arg += strlen(newreno);
|
|
}
|
|
else if (strncmp(arg, cubic, strlen(cubic)) == 0) {
|
|
/* cubic */
|
|
algo = cubic;
|
|
cc_algo = &quic_cc_algo_cubic;
|
|
arg += strlen(cubic);
|
|
}
|
|
else if (strncmp(arg, nocc, strlen(nocc)) == 0) {
|
|
/* nocc */
|
|
if (!experimental_directives_allowed) {
|
|
ha_alert("'%s' algo is experimental, must be allowed via a global "
|
|
"'expose-experimental-directives'\n", arg);
|
|
goto fail;
|
|
}
|
|
|
|
algo = nocc;
|
|
cc_algo = &quic_cc_algo_nocc;
|
|
arg += strlen(nocc);
|
|
}
|
|
else {
|
|
memprintf(err, "'%s' : unknown control congestion algorithm", args[cur_arg + 1]);
|
|
goto fail;
|
|
}
|
|
|
|
if (*arg++ == '(') {
|
|
unsigned long cwnd;
|
|
char *end_opt;
|
|
|
|
errno = 0;
|
|
cwnd = strtoul(arg, &end_opt, 0);
|
|
if (end_opt == arg || errno != 0) {
|
|
memprintf(err, "'%s' : could not parse congestion window value", args[cur_arg + 1]);
|
|
goto fail;
|
|
}
|
|
|
|
if (*end_opt == 'k') {
|
|
cwnd <<= 10;
|
|
end_opt++;
|
|
}
|
|
else if (*end_opt == 'm') {
|
|
cwnd <<= 20;
|
|
end_opt++;
|
|
}
|
|
else if (*end_opt == 'g') {
|
|
cwnd <<= 30;
|
|
end_opt++;
|
|
}
|
|
|
|
if (*end_opt != ')') {
|
|
memprintf(err, "'%s' : expects %s(<max window>)", args[cur_arg + 1], algo);
|
|
goto fail;
|
|
}
|
|
|
|
if (cwnd < 10240 || cwnd > (4UL << 30)) {
|
|
memprintf(err, "'%s' : should be greater than 10k and smaller than 4g", args[cur_arg + 1]);
|
|
goto fail;
|
|
}
|
|
|
|
conf->max_cwnd = cwnd;
|
|
}
|
|
|
|
conf->quic_cc_algo = cc_algo;
|
|
return 0;
|
|
|
|
fail:
|
|
return ERR_ALERT | ERR_FATAL;
|
|
}
|
|
|
|
static int bind_parse_quic_socket(char **args, int cur_arg, struct proxy *px,
|
|
struct bind_conf *conf, char **err)
|
|
{
|
|
char *arg;
|
|
if (!*args[cur_arg + 1]) {
|
|
memprintf(err, "'%s' : missing argument, use either connection or listener.", args[cur_arg]);
|
|
return ERR_ALERT | ERR_FATAL;
|
|
}
|
|
|
|
arg = args[cur_arg + 1];
|
|
if (strcmp(arg, "connection") == 0) {
|
|
conf->quic_mode = QUIC_SOCK_MODE_CONN;
|
|
}
|
|
else if (strcmp(arg, "listener") == 0) {
|
|
conf->quic_mode = QUIC_SOCK_MODE_LSTNR;
|
|
}
|
|
else {
|
|
memprintf(err, "'%s' : unknown argument, use either connection or listener.", args[cur_arg]);
|
|
return ERR_ALERT | ERR_FATAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct bind_kw_list bind_kws = { "QUIC", { }, {
|
|
{ "quic-force-retry", bind_parse_quic_force_retry, 0 },
|
|
{ "quic-cc-algo", bind_parse_quic_cc_algo, 1 },
|
|
{ "quic-socket", bind_parse_quic_socket, 1 },
|
|
{ NULL, NULL, 0 },
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, bind_register_keywords, &bind_kws);
|
|
|
|
/* parse "tune.quic.socket-owner", accepts "listener" or "connection" */
|
|
static int cfg_parse_quic_tune_socket_owner(char **args, int section_type,
|
|
struct proxy *curpx,
|
|
const struct proxy *defpx,
|
|
const char *file, int line, char **err)
|
|
{
|
|
if (too_many_args(1, args, err, NULL))
|
|
return -1;
|
|
|
|
if (strcmp(args[1], "connection") == 0) {
|
|
global.tune.options |= GTUNE_QUIC_SOCK_PER_CONN;
|
|
}
|
|
else if (strcmp(args[1], "listener") == 0) {
|
|
global.tune.options &= ~GTUNE_QUIC_SOCK_PER_CONN;
|
|
}
|
|
else {
|
|
memprintf(err, "'%s' expects either 'listener' or 'connection' but got '%s'.", args[0], args[1]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Must be used to parse tune.quic.* setting which requires a time
|
|
* as value.
|
|
* Return -1 on alert, or 0 if succeeded.
|
|
*/
|
|
static int cfg_parse_quic_time(char **args, int section_type,
|
|
struct proxy *curpx,
|
|
const struct proxy *defpx,
|
|
const char *file, int line, char **err)
|
|
{
|
|
unsigned int time;
|
|
const char *res, *name, *value;
|
|
int prefix_len = strlen("tune.quic.");
|
|
|
|
if (too_many_args(1, args, err, NULL))
|
|
return -1;
|
|
|
|
name = args[0];
|
|
value = args[1];
|
|
res = parse_time_err(value, &time, TIME_UNIT_MS);
|
|
if (res == PARSE_TIME_OVER) {
|
|
memprintf(err, "timer overflow in argument '%s' to '%s' "
|
|
"(maximum value is 2147483647 ms or ~24.8 days)", value, name);
|
|
return -1;
|
|
}
|
|
else if (res == PARSE_TIME_UNDER) {
|
|
memprintf(err, "timer underflow in argument '%s' to '%s' "
|
|
"(minimum non-null value is 1 ms)", value, name);
|
|
return -1;
|
|
}
|
|
else if (res) {
|
|
memprintf(err, "unexpected character '%c' in '%s'", *res, name);
|
|
return -1;
|
|
}
|
|
|
|
if (strcmp(name + prefix_len, "frontend.max-idle-timeout") == 0)
|
|
global.tune.quic_frontend_max_idle_timeout = time;
|
|
else if (strcmp(name + prefix_len, "backend.max-idle-timeout") == 0)
|
|
global.tune.quic_backend_max_idle_timeout = time;
|
|
else {
|
|
memprintf(err, "'%s' keyword not unhandled (please report this bug).", args[0]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Parse any tune.quic.* setting with strictly positive integer values.
|
|
* Return -1 on alert, or 0 if succeeded.
|
|
*/
|
|
static int cfg_parse_quic_tune_setting(char **args, int section_type,
|
|
struct proxy *curpx,
|
|
const struct proxy *defpx,
|
|
const char *file, int line, char **err)
|
|
{
|
|
unsigned int arg = 0;
|
|
int prefix_len = strlen("tune.quic.");
|
|
const char *suffix;
|
|
|
|
if (too_many_args(1, args, err, NULL))
|
|
return -1;
|
|
|
|
if (*(args[1]) != 0)
|
|
arg = atoi(args[1]);
|
|
|
|
if (arg < 1) {
|
|
memprintf(err, "'%s' expects a positive integer.", args[0]);
|
|
return -1;
|
|
}
|
|
|
|
suffix = args[0] + prefix_len;
|
|
if (strcmp(suffix, "frontend.conn-tx-buffers.limit") == 0)
|
|
global.tune.quic_streams_buf = arg;
|
|
else if (strcmp(suffix, "frontend.max-streams-bidi") == 0)
|
|
global.tune.quic_frontend_max_streams_bidi = arg;
|
|
else if (strcmp(suffix, "max-frame-loss") == 0)
|
|
global.tune.quic_max_frame_loss = arg;
|
|
else if (strcmp(suffix, "retry-threshold") == 0)
|
|
global.tune.quic_retry_threshold = arg;
|
|
else {
|
|
memprintf(err, "'%s' keyword not unhandled (please report this bug).", args[0]);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* config parser for global "tune.quic.zero-copy-fwd-send" */
|
|
static int cfg_parse_quic_zero_copy_fwd_snd(char **args, int section_type, struct proxy *curpx,
|
|
const struct proxy *defpx, const char *file, int line,
|
|
char **err)
|
|
{
|
|
if (too_many_args(1, args, err, NULL))
|
|
return -1;
|
|
|
|
if (strcmp(args[1], "on") == 0)
|
|
global.tune.no_zero_copy_fwd &= ~NO_ZERO_COPY_FWD_QUIC_SND;
|
|
else if (strcmp(args[1], "off") == 0)
|
|
global.tune.no_zero_copy_fwd |= NO_ZERO_COPY_FWD_QUIC_SND;
|
|
else {
|
|
memprintf(err, "'%s' expects 'on' or 'off'.", args[0]);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct cfg_kw_list cfg_kws = {ILH, {
|
|
{ CFG_GLOBAL, "tune.quic.socket-owner", cfg_parse_quic_tune_socket_owner },
|
|
{ CFG_GLOBAL, "tune.quic.backend.max-idle-timeou", cfg_parse_quic_time },
|
|
{ CFG_GLOBAL, "tune.quic.frontend.conn-tx-buffers.limit", cfg_parse_quic_tune_setting },
|
|
{ CFG_GLOBAL, "tune.quic.frontend.max-streams-bidi", cfg_parse_quic_tune_setting },
|
|
{ CFG_GLOBAL, "tune.quic.frontend.max-idle-timeout", cfg_parse_quic_time },
|
|
{ CFG_GLOBAL, "tune.quic.max-frame-loss", cfg_parse_quic_tune_setting },
|
|
{ CFG_GLOBAL, "tune.quic.retry-threshold", cfg_parse_quic_tune_setting },
|
|
{ CFG_GLOBAL, "tune.quic.zero-copy-fwd-send", cfg_parse_quic_zero_copy_fwd_snd },
|
|
{ 0, NULL, NULL }
|
|
}};
|
|
|
|
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
|