mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge pull request #15968 from miri64/congure/feat/congure_abe
congure_abe: initial import of TCP Alternative Backoff with ECN for CongURE
This commit is contained in:
commit
07c04bc0e3
@ -37,6 +37,10 @@ ifneq (,$(filter congure_%,$(USEMODULE)))
|
||||
USEMODULE += congure
|
||||
endif
|
||||
|
||||
ifneq (,$(filter congure_abe,$(USEMODULE)))
|
||||
USEMODULE += congure_reno_methods
|
||||
endif
|
||||
|
||||
ifneq (,$(filter congure_quic,$(USEMODULE)))
|
||||
USEMODULE += ztimer_msec
|
||||
endif
|
||||
|
@ -8,6 +8,7 @@ if !TEST_KCONFIG
|
||||
menu "CongURE congestion control abstraction"
|
||||
depends on USEMODULE_CONGURE
|
||||
|
||||
rsource "abe/Kconfig"
|
||||
rsource "mock/Kconfig"
|
||||
rsource "quic/Kconfig"
|
||||
rsource "reno/Kconfig"
|
||||
@ -23,6 +24,7 @@ menuconfig MODULE_CONGURE
|
||||
|
||||
if MODULE_CONGURE
|
||||
|
||||
rsource "abe/Kconfig"
|
||||
rsource "mock/Kconfig"
|
||||
rsource "quic/Kconfig"
|
||||
rsource "reno/Kconfig"
|
||||
|
@ -1,3 +1,6 @@
|
||||
ifneq (,$(filter congure_abe,$(USEMODULE)))
|
||||
DIRS += abe
|
||||
endif
|
||||
ifneq (,$(filter congure_quic,$(USEMODULE)))
|
||||
DIRS += quic
|
||||
endif
|
||||
|
31
sys/congure/abe/Kconfig
Normal file
31
sys/congure/abe/Kconfig
Normal file
@ -0,0 +1,31 @@
|
||||
# Copyright (c) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
if !TEST_KCONFIG
|
||||
|
||||
menuconfig KCONFIG_USEMODULE_CONGURE_ABE
|
||||
bool "Configure TCP-ABE-like congestion control"
|
||||
depends on USEMODULE_CONGURE_ABE
|
||||
help
|
||||
Configure TCP-ABE-like congestion control via Kconfig.
|
||||
|
||||
if KCONFIG_USEMODULE_CONGURE_ABE
|
||||
rsource "Kconfig.config"
|
||||
endif # KCONFIG_USEMODULE_CONGURE_ABE
|
||||
|
||||
endif # !TEST_KCONFIG
|
||||
|
||||
if TEST_KCONFIG
|
||||
menuconfig MODULE_CONGURE_ABE
|
||||
bool "CongURE implementation of TCP ABE"
|
||||
depends on MODULE_CONGURE
|
||||
select MODULE_CONGURE_RENO_METHODS
|
||||
|
||||
if MODULE_CONGURE_ABE
|
||||
rsource "Kconfig.config"
|
||||
endif # MODULE_CONGURE_ABE
|
||||
|
||||
endif # TEST_KCONFIG
|
28
sys/congure/abe/Kconfig.config
Normal file
28
sys/congure/abe/Kconfig.config
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2021 Freie Universität
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
#
|
||||
|
||||
# XXX This file only is required since there is no easy way to use these config
|
||||
# options with both the final MODULE_CONGURE_ABE (used with TEST_KCONFIG=1) and
|
||||
# KCONFIG_USEMODULE_CONGURE_ABE (used with TEST_KCONFIG=0) in a # nicely looking
|
||||
# and easy to migrate way. After migration, the content of this file can be folded
|
||||
# back into `sys/congure/abe/Kconfig`
|
||||
|
||||
config CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT
|
||||
int "Default numerator of the ABE multiplier"
|
||||
default 4
|
||||
help
|
||||
RFC 8511, section 3.1 defines the ABE multiplier to be 0.8 = 4/5, so
|
||||
assuming CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT is 5, this should be
|
||||
4.
|
||||
|
||||
config CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT
|
||||
int "Default denominator of the ABE multiplier"
|
||||
default 5
|
||||
help
|
||||
RFC 8511, section 3.1 defines the ABE multiplier to be 0.8 = 4/5, so
|
||||
assuming CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT is 4, this should be
|
||||
5.
|
3
sys/congure/abe/Makefile
Normal file
3
sys/congure/abe/Makefile
Normal file
@ -0,0 +1,3 @@
|
||||
MODULE := congure_abe
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
57
sys/congure/abe/congure_abe.c
Normal file
57
sys/congure/abe/congure_abe.c
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "seq.h"
|
||||
|
||||
#include "congure/abe.h"
|
||||
#include "congure/reno.h"
|
||||
|
||||
static void _snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time);
|
||||
|
||||
static const congure_snd_driver_t _driver = {
|
||||
.init = congure_reno_snd_init,
|
||||
.inter_msg_interval = congure_reno_snd_inter_msg_interval,
|
||||
.report_msg_sent = congure_reno_snd_report_msg_sent,
|
||||
.report_msg_discarded = congure_reno_snd_report_msg_discarded,
|
||||
.report_msgs_timeout = congure_reno_snd_report_msgs_timeout,
|
||||
.report_msgs_lost = congure_reno_snd_report_msgs_lost,
|
||||
.report_msg_acked = congure_reno_snd_report_msg_acked,
|
||||
.report_ecn_ce = _snd_report_ecn_ce,
|
||||
};
|
||||
|
||||
static void _snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time)
|
||||
{
|
||||
congure_abe_snd_t *c = (congure_abe_snd_t *)cong;
|
||||
const congure_abe_snd_consts_t *consts =
|
||||
container_of(c->consts, congure_abe_snd_consts_t, reno);
|
||||
unsigned abe_product = (c->in_flight_size * consts->abe_multiplier_numerator)
|
||||
/ consts->abe_multiplier_denominator;
|
||||
|
||||
(void)time;
|
||||
c->ssthresh = (abe_product > (c->mss * 2)) ? abe_product : (c->mss * 2);
|
||||
c->super.cwnd = c->ssthresh;
|
||||
}
|
||||
|
||||
void congure_abe_snd_setup(congure_abe_snd_t *c,
|
||||
const congure_abe_snd_consts_t *consts)
|
||||
{
|
||||
c->super.driver = &_driver;
|
||||
c->consts = &consts->reno;
|
||||
}
|
||||
|
||||
/** @} */
|
95
sys/include/congure/abe.h
Normal file
95
sys/include/congure/abe.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup sys_congure_abe CongURE implementation of TCP ABE
|
||||
* @ingroup sys_congure
|
||||
* @brief Implementation of the TCP Alternative Backoff with ECN (ABE)
|
||||
* congestion control mechanism for the CongURE framework.
|
||||
*
|
||||
* @see [RFC 8511](https://tools.ietf.org/html/rfc8511)
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief
|
||||
*
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_ABE_H
|
||||
#define CONGURE_ABE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "congure/config.h"
|
||||
#include "congure/reno.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Alias of CongURE Reno state object for CongURE ABE.
|
||||
*
|
||||
* @extends congure_reno_snd_t
|
||||
*/
|
||||
typedef congure_reno_snd_t congure_abe_snd_t;
|
||||
|
||||
/**
|
||||
* @brief Constants for the congestion control.
|
||||
*
|
||||
* @extends congure_reno_snd_consts_t
|
||||
*/
|
||||
typedef struct {
|
||||
congure_reno_snd_consts_t reno; /**< TCP Reno constants */
|
||||
/**
|
||||
* @brief numerator for the `beta_{ecn} `factor used to set the slow start
|
||||
* threshold on an ECN CE.
|
||||
*
|
||||
* Use @ref CONFIG_CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT for a default
|
||||
* value
|
||||
*/
|
||||
uint8_t abe_multiplier_numerator;
|
||||
/**
|
||||
* @brief numerator for the `beta_{ecn} `factor used to set the slow start
|
||||
* threshold on an ECN CE.
|
||||
*
|
||||
* Use @ref CONFIG_CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT for a default
|
||||
* value
|
||||
*/
|
||||
uint8_t abe_multiplier_denominator;
|
||||
} congure_abe_snd_consts_t;
|
||||
|
||||
/**
|
||||
* @brief Set's up the driver for a CongURE ABE object
|
||||
*
|
||||
* @param[in] c A CongURE ABE object.
|
||||
* @param[in] consts The constants to use for @p c
|
||||
*/
|
||||
void congure_abe_snd_setup(congure_abe_snd_t *c,
|
||||
const congure_abe_snd_consts_t *consts);
|
||||
|
||||
/**
|
||||
* @brief Set sender maximum segment size.
|
||||
*
|
||||
* @attention This resets congure_reno_snd_t::cwnd to the new initial window
|
||||
* size based on @p mss. So use with care.
|
||||
*
|
||||
* @param[in] c A CongURE state object
|
||||
* @param[in] mss Maximum segment size of the sender in caller-defined units
|
||||
*/
|
||||
static inline void congure_abe_set_mss(congure_abe_snd_t *c, unsigned mss)
|
||||
{
|
||||
congure_reno_set_mss((congure_reno_snd_t *)c, mss);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_ABE_H */
|
||||
/** @} */
|
@ -24,6 +24,26 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default numerator of the ABE multiplier (0.8)
|
||||
*
|
||||
* @note Use with @ref sys_congure_abe
|
||||
* @see [RFC 8511, section 3.1](https://tools.ietf.org/html/rfc8511#section-3.1)
|
||||
*/
|
||||
#ifndef CONFIG_CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT
|
||||
#define CONFIG_CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT (4U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default denominator of the ABE multiplier (0.8)
|
||||
*
|
||||
* @note Use with @ref sys_congure_abe
|
||||
* @see [RFC 8511, section 3.1](https://tools.ietf.org/html/rfc8511#section-3.1)
|
||||
*/
|
||||
#ifndef CONFIG_CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT
|
||||
#define CONFIG_CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT (5U)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
18
tests/congure_abe/Makefile
Normal file
18
tests/congure_abe/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
USEMODULE += congure_abe
|
||||
USEMODULE += congure_test
|
||||
USEMODULE += fmt
|
||||
USEMODULE += shell
|
||||
|
||||
INCLUDES += -I$(CURDIR)
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
||||
ifndef CONFIG_SHELL_NO_ECHO
|
||||
CFLAGS += -DCONFIG_SHELL_NO_ECHO=1
|
||||
endif
|
||||
|
||||
ifndef CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE
|
||||
CFLAGS += -DCONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=6
|
||||
endif
|
8
tests/congure_abe/Makefile.ci
Normal file
8
tests/congure_abe/Makefile.ci
Normal file
@ -0,0 +1,8 @@
|
||||
BOARD_INSUFFICIENT_MEMORY := \
|
||||
arduino-duemilanove \
|
||||
arduino-leonardo \
|
||||
arduino-nano \
|
||||
arduino-uno \
|
||||
atmega328p \
|
||||
atmega328p-xplained-mini \
|
||||
#
|
30
tests/congure_abe/README.md
Normal file
30
tests/congure_abe/README.md
Normal file
@ -0,0 +1,30 @@
|
||||
Tests for the CongURE TCP ABE implementation
|
||||
=============================================
|
||||
|
||||
This test tests the `congure_abe` implementation.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
The test requires an up-to-date version of `riotctrl` with `rapidjson` support:
|
||||
|
||||
```console
|
||||
$ pip install --upgrade riotctrl[rapidjson]
|
||||
```
|
||||
|
||||
Then simply run the application using:
|
||||
|
||||
```console
|
||||
$ BOARD="<board>" make flash test
|
||||
```
|
||||
|
||||
It can also executed with pytest:
|
||||
|
||||
```console
|
||||
$ pytest tests/01-run.py
|
||||
```
|
||||
|
||||
Expected result
|
||||
---------------
|
||||
|
||||
The application's test script passes without error code.
|
4
tests/congure_abe/app.config
Normal file
4
tests/congure_abe/app.config
Normal file
@ -0,0 +1,4 @@
|
||||
CONFIG_KCONFIG_USEMODULE_CONGURE_TEST=y
|
||||
CONFIG_KCONFIG_USEMODULE_SHELL=y
|
||||
CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=6
|
||||
CONFIG_SHELL_NO_ECHO=y
|
9
tests/congure_abe/app.config.test
Normal file
9
tests/congure_abe/app.config.test
Normal file
@ -0,0 +1,9 @@
|
||||
CONFIG_MODULE_CONGURE=y
|
||||
CONFIG_MODULE_CONGURE_ABE=y
|
||||
CONFIG_MODULE_CONGURE_TEST=y
|
||||
CONFIG_MODULE_FMT=y
|
||||
CONFIG_MODULE_SEQ=y
|
||||
CONFIG_MODULE_SHELL=y
|
||||
|
||||
CONFIG_CONGURE_TEST_LOST_MSG_POOL_SIZE=6
|
||||
CONFIG_SHELL_NO_ECHO=y
|
76
tests/congure_abe/congure_impl.c
Normal file
76
tests/congure_abe/congure_impl.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "kernel_defines.h"
|
||||
|
||||
#include "congure_impl.h"
|
||||
|
||||
static unsigned _fr_calls;
|
||||
static bool _same_wnd_adv_res;
|
||||
|
||||
static void _fr(congure_abe_snd_t *c);
|
||||
static bool _same_wnd_adv(congure_abe_snd_t *c, congure_snd_ack_t *ack);
|
||||
|
||||
static const congure_abe_snd_consts_t _consts[] = {
|
||||
{
|
||||
.reno = {
|
||||
.fr = _fr,
|
||||
.same_wnd_adv = _same_wnd_adv,
|
||||
.init_mss = 1460,
|
||||
.cwnd_lower = 1095,
|
||||
.cwnd_upper = 2190,
|
||||
.init_ssthresh = CONGURE_WND_SIZE_MAX,
|
||||
.frthresh = 3,
|
||||
},
|
||||
.abe_multiplier_numerator = CONFIG_CONGURE_ABE_MULTIPLIER_NUMERATOR_DEFAULT,
|
||||
.abe_multiplier_denominator = CONFIG_CONGURE_ABE_MULTIPLIER_DENOMINATOR_DEFAULT,
|
||||
}
|
||||
};
|
||||
|
||||
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id)
|
||||
{
|
||||
if (id >= ARRAY_SIZE(_consts)) {
|
||||
return -1;
|
||||
}
|
||||
_fr_calls = 0;
|
||||
congure_abe_snd_setup(c, &_consts[id]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned congure_abe_test_get_fr_calls(void)
|
||||
{
|
||||
return _fr_calls;
|
||||
}
|
||||
|
||||
void congure_abe_test_set_same_wnd_adv_res(bool value)
|
||||
{
|
||||
_same_wnd_adv_res = value;
|
||||
}
|
||||
|
||||
static void _fr(congure_abe_snd_t *c)
|
||||
{
|
||||
(void)c;
|
||||
_fr_calls++;
|
||||
}
|
||||
|
||||
static bool _same_wnd_adv(congure_abe_snd_t *c, congure_snd_ack_t *ack)
|
||||
{
|
||||
(void)c;
|
||||
(void)ack;
|
||||
return _same_wnd_adv_res;
|
||||
}
|
||||
|
||||
/** @} */
|
36
tests/congure_abe/congure_impl.h
Normal file
36
tests/congure_abe/congure_impl.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef CONGURE_IMPL_H
|
||||
#define CONGURE_IMPL_H
|
||||
|
||||
#include "congure/abe.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef congure_abe_snd_t congure_test_snd_t;
|
||||
|
||||
int congure_test_snd_setup(congure_test_snd_t *c, unsigned id);
|
||||
unsigned congure_abe_test_get_fr_calls(void);
|
||||
void congure_abe_test_set_same_wnd_adv_res(bool value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONGURE_IMPL_H */
|
||||
/** @} */
|
151
tests/congure_abe/main.c
Normal file
151
tests/congure_abe/main.c
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine S. Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "clist.h"
|
||||
#include "congure/test.h"
|
||||
#include "fmt.h"
|
||||
#include "shell.h"
|
||||
|
||||
#include "congure_impl.h"
|
||||
|
||||
static int _json_statham(int argc, char **argv);
|
||||
static int _set_cwnd(int argc, char **argv);
|
||||
static int _get_fr_calls(int argc, char **argv);
|
||||
static int _set_same_wnd_adv_res(int argc, char **argv);
|
||||
|
||||
static congure_abe_snd_t _congure_state;
|
||||
static const shell_command_t shell_commands[] = {
|
||||
{ "state", "Prints current CongURE state object as JSON", _json_statham },
|
||||
{ "set_cwnd", "Set cwnd member for CongURE state object", _set_cwnd },
|
||||
{ "get_ff_calls",
|
||||
"Get the number of calls to fast_retransmit callback of CongURE state "
|
||||
"object", _get_fr_calls },
|
||||
{ "set_same_wnd_adv",
|
||||
"Set the result for the same_window_advertised callback of CongURE state "
|
||||
"object", _set_same_wnd_adv_res },
|
||||
{ NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
congure_test_snd_t *congure_test_get_state(void)
|
||||
{
|
||||
return &_congure_state;
|
||||
}
|
||||
|
||||
#define PRINT_FIELD_PTR(obj_ptr, field) \
|
||||
print_str("\"" #field "\":\"0x"); \
|
||||
print_u32_hex((intptr_t)((obj_ptr)->field)); \
|
||||
print_str("\",")
|
||||
|
||||
#define PRINT_FIELD_UINT(obj, field) \
|
||||
print_str("\"" #field "\":"); \
|
||||
print_u32_dec((obj).field); \
|
||||
print_str(",")
|
||||
|
||||
static void _print_congure_abe_consts(const congure_abe_snd_consts_t *consts)
|
||||
{
|
||||
print_str("\"consts\":");
|
||||
|
||||
if (consts) {
|
||||
print_str("{");
|
||||
PRINT_FIELD_PTR(&consts->reno, fr);
|
||||
PRINT_FIELD_PTR(&consts->reno, same_wnd_adv);
|
||||
PRINT_FIELD_PTR(&consts->reno, ss_cwnd_inc);
|
||||
PRINT_FIELD_PTR(&consts->reno, ca_cwnd_inc);
|
||||
PRINT_FIELD_PTR(&consts->reno, fr_cwnd_dec);
|
||||
PRINT_FIELD_UINT(consts->reno, init_mss);
|
||||
PRINT_FIELD_UINT(consts->reno, cwnd_upper);
|
||||
PRINT_FIELD_UINT(consts->reno, cwnd_lower);
|
||||
PRINT_FIELD_UINT(consts->reno, init_ssthresh);
|
||||
PRINT_FIELD_UINT(consts->reno, frthresh);
|
||||
PRINT_FIELD_UINT(*consts, abe_multiplier_numerator);
|
||||
PRINT_FIELD_UINT(*consts, abe_multiplier_denominator);
|
||||
print_str("},");
|
||||
}
|
||||
else {
|
||||
print_str("null,");
|
||||
}
|
||||
}
|
||||
|
||||
static int _json_statham(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
print_str("{");
|
||||
|
||||
PRINT_FIELD_UINT(_congure_state.super, cwnd);
|
||||
_print_congure_abe_consts(
|
||||
(congure_abe_snd_consts_t *)_congure_state.consts
|
||||
);
|
||||
PRINT_FIELD_UINT(_congure_state, mss);
|
||||
PRINT_FIELD_UINT(_congure_state, last_ack);
|
||||
PRINT_FIELD_UINT(_congure_state, ssthresh);
|
||||
PRINT_FIELD_UINT(_congure_state, in_flight_size);
|
||||
PRINT_FIELD_UINT(_congure_state, dup_acks);
|
||||
|
||||
print_str("}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_cwnd(int argc, char **argv)
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
if (argc < 2) {
|
||||
print_str("{\"error\":\"`cwnd` argument expected\"}");
|
||||
return 1;
|
||||
}
|
||||
tmp = scn_u32_dec(argv[1], strlen(argv[1]));
|
||||
if (tmp > CONGURE_WND_SIZE_MAX) {
|
||||
print_str("{\"error\":\"`ssthresh` not 16 bit wide\"}\n");
|
||||
}
|
||||
_congure_state.super.cwnd = (congure_wnd_size_t)tmp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _get_fr_calls(int argc, char **argv)
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
print_str("{\"fr_calls\":");
|
||||
print_u32_dec(congure_abe_test_get_fr_calls());
|
||||
print_str("}\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _set_same_wnd_adv_res(int argc, char **argv)
|
||||
{
|
||||
if (argc < 2) {
|
||||
print_str("{\"error\":\"`value` argument expected\"}");
|
||||
return 1;
|
||||
}
|
||||
congure_abe_test_set_same_wnd_adv_res(
|
||||
(bool)scn_u32_dec(argv[1], strlen(argv[1]))
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** @} */
|
332
tests/congure_abe/tests/01-run.py
Executable file
332
tests/congure_abe/tests/01-run.py
Executable file
@ -0,0 +1,332 @@
|
||||
#! /usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2021 Freie Universität Berlin
|
||||
#
|
||||
# This file is subject to the terms and conditions of the GNU Lesser
|
||||
# General Public License v2.1. See the file LICENSE in the top level
|
||||
# directory for more details.
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from riotctrl.ctrl import RIOTCtrl
|
||||
from riotctrl.shell.json import RapidJSONShellInteractionParser, rapidjson
|
||||
|
||||
from riotctrl_shell.congure_test import CongureTest
|
||||
|
||||
|
||||
class TestCongUREBase(unittest.TestCase):
|
||||
# pylint: disable=too-many-public-methods
|
||||
# it's just one more ...
|
||||
DEBUG = False
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.ctrl = RIOTCtrl()
|
||||
cls.ctrl.start_term()
|
||||
if cls.DEBUG:
|
||||
cls.ctrl.term.logfile = sys.stdout
|
||||
cls.ctrl.reset()
|
||||
cls.shell = CongureTest(cls.ctrl)
|
||||
cls.json_parser = RapidJSONShellInteractionParser()
|
||||
cls.json_parser.set_parser_args(
|
||||
parse_mode=rapidjson.PM_TRAILING_COMMAS
|
||||
)
|
||||
cls.logger = logging.getLogger(cls.__name__)
|
||||
if cls.DEBUG:
|
||||
cls.logger.setLevel(logging.DEBUG)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.ctrl.stop_term()
|
||||
|
||||
def setUp(self):
|
||||
self.shell.clear()
|
||||
|
||||
def tearDown(self):
|
||||
self.shell.msgs_reset()
|
||||
|
||||
def _parse(self, res):
|
||||
self.logger.debug(res)
|
||||
if res.strip():
|
||||
return self.json_parser.parse(res)
|
||||
return None
|
||||
|
||||
def exec_cmd(self, cmd, timeout=-1, async_=False):
|
||||
res = self.shell.cmd(cmd, timeout, async_)
|
||||
return self._parse(res)
|
||||
|
||||
def assertSlowStart(self, state):
|
||||
# pylint: disable=invalid-name
|
||||
# trying to be in line with `unittest`
|
||||
"""
|
||||
> The slow start algorithm is used when cwnd < ssthresh, while the
|
||||
> congestion avoidance algorithm is used when cwnd > ssthresh. When
|
||||
> cwnd and ssthresh are equal, the sender may use either slow start or
|
||||
> congestion avoidance.
|
||||
"""
|
||||
self.assertLess(state['cwnd'], state['ssthresh'])
|
||||
|
||||
def assertInFastRetransmit(self, state):
|
||||
# pylint: disable=invalid-name
|
||||
# trying to be in line with `unittest`
|
||||
"""
|
||||
> The TCP sender SHOULD use the "fast retransmit" algorithm to detect
|
||||
> and repair loss, based on incoming duplicate ACKs. The fast
|
||||
> retransmit algorithm uses the arrival of 3 duplicate ACKs [...] as
|
||||
> an indication that a segment has been lost.
|
||||
"""
|
||||
self.assertGreaterEqual(state['dup_acks'], state['consts']['frthresh'])
|
||||
|
||||
def assertNotInFastRetransmit(self, state):
|
||||
# pylint: disable=invalid-name
|
||||
# trying to be in line with `unittest`
|
||||
"""Reverse of self.assertInFastRetransmit()"""
|
||||
self.assertLess(state['dup_acks'], state['consts']['frthresh'])
|
||||
|
||||
def get_ff_calls(self):
|
||||
res = self.exec_cmd('get_ff_calls')
|
||||
return res['fr_calls']
|
||||
|
||||
def set_same_wnd_adv(self, value):
|
||||
self.exec_cmd(f'set_same_wnd_adv {value:d}')
|
||||
|
||||
def set_cwnd(self, cwnd):
|
||||
self.exec_cmd(f'set_cwnd {cwnd}')
|
||||
|
||||
def cong_state(self):
|
||||
return self.exec_cmd('state')
|
||||
|
||||
def cong_init(self, ctx=0):
|
||||
res = self.shell.init(ctx)
|
||||
return self._parse(res)
|
||||
|
||||
def cong_report_msg_sent(self, msg_size):
|
||||
res = self.shell.report_msg_sent(msg_size)
|
||||
return self._parse(res)
|
||||
|
||||
def cong_report_msg_acked(self, msg, ack):
|
||||
res = self.shell.report_msg_acked(msg, ack)
|
||||
return self._parse(res)
|
||||
|
||||
def cong_report_ecn_ce(self, time):
|
||||
res = self.shell.report_ecn_ce(time)
|
||||
return self._parse(res)
|
||||
|
||||
|
||||
class TestCongUREABEWithoutSetup(TestCongUREBase):
|
||||
def test_no_setup(self):
|
||||
state = self.exec_cmd('state')
|
||||
self.assertEqual(state, {
|
||||
'cwnd': 0,
|
||||
'consts': None,
|
||||
'mss': 0,
|
||||
'last_ack': 0,
|
||||
'ssthresh': 0,
|
||||
'in_flight_size': 0,
|
||||
'dup_acks': 0,
|
||||
})
|
||||
|
||||
|
||||
class TestCongUREABEDefaultInitTests(TestCongUREBase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
res = self.shell.setup(0)
|
||||
self.assertIn('success', res)
|
||||
|
||||
def test_setup(self):
|
||||
state = self.cong_state()
|
||||
self.assertIsNotNone(state['consts'])
|
||||
# fast_retransmit and same_window_advertised need to be set to a
|
||||
# function pointer
|
||||
self.assertNotEqual(int(state['consts']['fr'], base=16), 0)
|
||||
self.assertNotEqual(int(state['consts']['same_wnd_adv'], base=16), 0)
|
||||
# ss_cwnd_inc, ca_cwnd_inc, fr_cwnd_dec are optional and setup 0 need
|
||||
# to be set to a function pointer
|
||||
self.assertEqual(int(state['consts']['ss_cwnd_inc'], base=16), 0)
|
||||
self.assertEqual(int(state['consts']['ca_cwnd_inc'], base=16), 0)
|
||||
self.assertEqual(int(state['consts']['fr_cwnd_dec'], base=16), 0)
|
||||
self.assertEqual(state['consts']['init_mss'], 1460)
|
||||
self.assertEqual(state['consts']['cwnd_lower'], 1095)
|
||||
self.assertEqual(state['consts']['cwnd_upper'], 2190)
|
||||
self.assertEqual(state['consts']['init_ssthresh'], 0xffff)
|
||||
self.assertEqual(state['consts']['frthresh'], 3)
|
||||
# https://tools.ietf.org/html/rfc8511#section-3.1
|
||||
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||
state['consts']['abe_multiplier_denominator'])
|
||||
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||
self.assertEqual(state['consts']['frthresh'], 3)
|
||||
|
||||
def test_init(self):
|
||||
"""
|
||||
This is inherited from `congure_reno`, so it should be the same as
|
||||
in `tests/congure_reno`.
|
||||
|
||||
https://tools.ietf.org/html/rfc5681#section-3.1
|
||||
|
||||
> IW, the initial value of cwnd, MUST be set using the following
|
||||
> guidelines as an upper bound.
|
||||
>
|
||||
> If SMSS > 2190 bytes:
|
||||
> IW = 2 * SMSS bytes and MUST NOT be more than 2 segments
|
||||
> If (SMSS > 1095 bytes) and (SMSS <= 2190 bytes):
|
||||
> IW = 3 * SMSS bytes and MUST NOT be more than 3 segments
|
||||
> if SMSS <= 1095 bytes:
|
||||
> IW = 4 * SMSS bytes and MUST NOT be more than 4 segments
|
||||
"""
|
||||
res = self.cong_init()
|
||||
self.assertIn('success', res)
|
||||
state = self.cong_state()
|
||||
self.assertEqual(state['consts']['init_mss'], state['mss'])
|
||||
# (SMSS > 1095 bytes)
|
||||
self.assertGreater(state['mss'], state['consts']['cwnd_lower'])
|
||||
# (SMSS <= 2190 bytes)
|
||||
self.assertLessEqual(state['mss'], state['consts']['cwnd_upper'])
|
||||
# as such, IW = 3 * SMSS bytes
|
||||
self.assertEqual(state['cwnd'], 3 * state['mss'])
|
||||
# We start with slow start
|
||||
self.assertSlowStart(state)
|
||||
self.assertNotInFastRetransmit(state)
|
||||
|
||||
|
||||
class TestCongUREABE(TestCongUREBase):
|
||||
"""
|
||||
Most functionality should be the same as for `congure_reno`, except
|
||||
for the behavior of `report_ecn_ce`. So only test some basics from
|
||||
`tests/congure_reno` and focus testing on `report_ecn_ce`
|
||||
"""
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
res = self.shell.setup(0)
|
||||
self.assertIn('success', res)
|
||||
res = self.cong_init()
|
||||
self.assertIn('success', res)
|
||||
|
||||
def _send_msg_and_recv_ack(self, msg_size, msg_resends=0,
|
||||
ack_id=15, ack_size=None, ack_clean=True):
|
||||
# pylint: disable=too-many-arguments
|
||||
# already reduced number of arguments, cong_report_msg_acked would
|
||||
# need...
|
||||
if ack_size is None:
|
||||
# set ack_size to arbitrary value
|
||||
ack_size = msg_size
|
||||
res = self.cong_report_msg_sent(msg_size=msg_size)
|
||||
self.assertIn('success', res)
|
||||
state = self.cong_state()
|
||||
self.assertEqual(state['in_flight_size'], msg_size)
|
||||
res = self.cong_report_msg_acked(
|
||||
msg={'send_time': 1000, 'size': msg_size, 'resends': msg_resends},
|
||||
ack={'recv_time': 1100, 'id': ack_id, 'size': ack_size,
|
||||
'clean': ack_clean, 'wnd': 1234, 'delay': 0},
|
||||
)
|
||||
self.assertIn('success', res)
|
||||
|
||||
def test_slow_start_increase(self):
|
||||
# pylint: disable=invalid-name
|
||||
# name chosen to be in line with RFC
|
||||
"""
|
||||
See test_slow_start_increase_large_N() from `tests/congure_reno`
|
||||
"""
|
||||
state = self.cong_state()
|
||||
init_cwnd = state['cwnd']
|
||||
init_mss = state['mss']
|
||||
init_ssthresh = state['ssthresh']
|
||||
self.assertEqual(state['in_flight_size'], 0)
|
||||
# pylint: disable=invalid-name
|
||||
# name chosen to be in line with RFC
|
||||
# set N to larger than SMSS
|
||||
N = state['mss'] + 1337
|
||||
self._send_msg_and_recv_ack(N)
|
||||
state = self.cong_state()
|
||||
# MSS did not change
|
||||
self.assertEqual(state['mss'], init_mss)
|
||||
self.assertEqual(state['cwnd'], init_cwnd + state['mss'])
|
||||
self.assertEqual(state['in_flight_size'], 0)
|
||||
self.assertEqual(state['ssthresh'], init_ssthresh)
|
||||
self.assertNotInFastRetransmit(state)
|
||||
|
||||
def test_enter_fast_retransmit(self):
|
||||
"""
|
||||
See self.test_enter_fast_retransmit_all_check_true() from
|
||||
`tests/congure_reno`
|
||||
"""
|
||||
state = self.cong_state()
|
||||
self.assertEqual(0, self.get_ff_calls())
|
||||
self.assertNotInFastRetransmit(state)
|
||||
self.assertEqual(state['in_flight_size'], 0)
|
||||
self._send_msg_and_recv_ack(42, ack_id=15, ack_size=0, ack_clean=True)
|
||||
# make condition (a) true
|
||||
self.cong_report_msg_sent(52)
|
||||
state = self.cong_state()
|
||||
self.assertEqual(state['in_flight_size'], 52)
|
||||
# make condition (e) not true
|
||||
self.set_same_wnd_adv(True)
|
||||
# condition (b) ack['size'] == 0, (c) ack['clean'] == True,
|
||||
# (d) ack['id'] == 15
|
||||
for _ in range(3):
|
||||
res = self.cong_report_msg_acked(
|
||||
msg={'send_time': 1000, 'size': 42, 'resends': 0},
|
||||
ack={'recv_time': 1100, 'id': 15, 'size': 0,
|
||||
'clean': True, 'wnd': 1234, 'delay': 0},
|
||||
)
|
||||
self.assertIn('success', res)
|
||||
self.assertEqual(1, self.get_ff_calls())
|
||||
self.assertInFastRetransmit(self.cong_state())
|
||||
|
||||
def test_ecn_ce_small_flight_size(self):
|
||||
"""
|
||||
https://tools.ietf.org/html/rfc8511#section-3
|
||||
|
||||
> As permitted by RFC 8311, this document specifies a sender-side
|
||||
> change to TCP where receipt of a packet with the ECN-Echo flag SHOULD
|
||||
> trigger the TCP source to set the slow start threshold (ssthresh) to
|
||||
> 0.8 times the FlightSize, with a lower bound of 2 * SMSS applied to
|
||||
> the result (where SMSS stands for Sender Maximum Segment Size)). As
|
||||
> in [RFC5681], the TCP sender also reduces the cwnd value to no more
|
||||
> than the new ssthresh value. Section 6.1.2 of RFC 3168 provides
|
||||
> guidance on setting a cwnd less than 2 * SMSS.
|
||||
"""
|
||||
state = self.cong_state()
|
||||
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||
state['consts']['abe_multiplier_denominator'])
|
||||
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||
flight_size = 150
|
||||
# put some bytes in the air
|
||||
self.cong_report_msg_sent(flight_size)
|
||||
self.cong_report_ecn_ce(1204)
|
||||
state = self.cong_state()
|
||||
# [...] set the slow start threshold (ssthresh) to 0.8 times the
|
||||
# FlightSize, with a lower bound of 2 * SMSS applied to the result
|
||||
self.assertEqual(state['ssthresh'], 2 * state['mss'])
|
||||
# the TCP sender also reduces the cwnd value to no more
|
||||
# than the new ssthresh value
|
||||
self.assertLessEqual(state['cwnd'], state['ssthresh'])
|
||||
|
||||
def test_ecn_ce_large_flight_size(self):
|
||||
"""
|
||||
Same as test_ecn_ce_small_flight_size, but with flight size
|
||||
larger than (2 * SMSS / 0.8)
|
||||
"""
|
||||
state = self.cong_state()
|
||||
beta_ecn = (state['consts']['abe_multiplier_numerator'] /
|
||||
state['consts']['abe_multiplier_denominator'])
|
||||
self.assertAlmostEqual(beta_ecn, 0.8)
|
||||
flight_size = 3 * state['mss']
|
||||
# increase congestion window large enough to send all those bytes
|
||||
self.set_cwnd(flight_size)
|
||||
# put some bytes in the air
|
||||
for _ in range(3):
|
||||
self.cong_report_msg_sent(state['mss'])
|
||||
self.cong_report_ecn_ce(1204)
|
||||
state = self.cong_state()
|
||||
# [...] set the slow start threshold (ssthresh) to 0.8 times the
|
||||
# FlightSize, with a lower bound of 2 * SMSS applied to the result
|
||||
self.assertEqual(state['ssthresh'], beta_ecn * flight_size)
|
||||
# the TCP sender also reduces the cwnd value to no more
|
||||
# than the new ssthresh value
|
||||
self.assertLessEqual(state['cwnd'], state['ssthresh'])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
Loading…
Reference in New Issue
Block a user