Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions sys/Makefile.dep
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ ifneq (,$(filter congure_test,$(USEMODULE)))
USEMODULE += fmt
endif

ifneq (,$(filter congure_reno,$(USEMODULE)))
USEMODULE += congure_reno_methods
endif

ifneq (,$(filter congure_reno_methods,$(USEMODULE)))
USEMODULE += seq
endif

ifneq (,$(filter eepreg,$(USEMODULE)))
FEATURES_REQUIRED += periph_eeprom
endif
Expand Down
2 changes: 2 additions & 0 deletions sys/congure/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ menu "CongURE congestion control abstraction"
depends on USEMODULE_CONGURE

rsource "mock/Kconfig"
rsource "reno/Kconfig"
rsource "test/Kconfig"

endmenu # CongURE congestion control abstraction
Expand All @@ -22,6 +23,7 @@ menuconfig MODULE_CONGURE
if MODULE_CONGURE

rsource "mock/Kconfig"
rsource "reno/Kconfig"
rsource "test/Kconfig"

endif # MODULE_CONGURE
Expand Down
6 changes: 6 additions & 0 deletions sys/congure/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
ifneq (,$(filter congure_mock,$(USEMODULE)))
DIRS += mock
endif
ifneq (,$(filter congure_reno,$(USEMODULE)))
DIRS += reno
endif
ifneq (,$(filter congure_reno_methods,$(USEMODULE)))
DIRS += reno/methods
endif
ifneq (,$(filter congure_test,$(USEMODULE)))
DIRS += test
endif
Expand Down
14 changes: 14 additions & 0 deletions sys/congure/reno/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
config MODULE_CONGURE_RENO
bool "CongURE implementation of TCP Reno"
depends on MODULE_CONGURE
select MODULE_CONGURE_RENO_METHODS

config MODULE_CONGURE_RENO_METHODS
bool "Send driver methods for the CongURE implementation of TCP Reno"
depends on MODULE_SEQ
help
Many other congestion control mechanisms are just adaptations of TCP
Reno, so this makes the methods of @ref sys_congure_reno available to
other @ref sys_congure modules. Use module `congure_reno_methods` to
only compile in these modules, but not the driver for
`congure_reno_snd_t` or @ref congure_reno_snd_setup().
3 changes: 3 additions & 0 deletions sys/congure/reno/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE := congure_reno

include $(RIOTBASE)/Makefile.base
36 changes: 36 additions & 0 deletions sys/congure/reno/congure_reno.c
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>
*/

#include "congure/reno.h"

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 = congure_reno_snd_report_ecn_ce,
};

void congure_reno_snd_setup(congure_reno_snd_t *c,
const congure_reno_snd_consts_t *consts)
{
c->super.driver = &_driver;
c->consts = consts;
}

/** @} */
3 changes: 3 additions & 0 deletions sys/congure/reno/methods/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MODULE := congure_reno_methods

include $(RIOTBASE)/Makefile.base
248 changes: 248 additions & 0 deletions sys/congure/reno/methods/congure_reno_methods.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
* 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 <[email protected]>
*/

#include <assert.h>
#include <stdint.h>

#include "clist.h"
#include "seq.h"

#include "congure/reno.h"

static int _snd_in_fast_retransmit(congure_snd_t *cong)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

return (c->dup_acks >= c->consts->frthresh);
}

static inline congure_wnd_size_t _calc_init_wnd(congure_reno_snd_t *c)
{
/* see https://tools.ietf.org/html/rfc5681#section-3.1 */
if (c->mss > c->consts->cwnd_upper) {
return 2 * c->mss;
}
else if (c->mss <= c->consts->cwnd_lower) {
return 4 * c->mss;
}
else {
return 3 * c->mss;
}
}

static void _fr_cwnd_dec(congure_reno_snd_t *c)
{
if (c->consts->fr_cwnd_dec) {
c->consts->fr_cwnd_dec(c);
}
else {
/* max(c->mss * 2, c->super.cwnd / 2) */
c->ssthresh = ((c->mss * 4) > c->super.cwnd)
? (c->mss * 2) : (c->super.cwnd / 2);
c->super.cwnd = c->ssthresh + (3 * c->mss);
}
}

static void _enforce_fast_retransmit(congure_reno_snd_t *c)
{
if (!_snd_in_fast_retransmit(&c->super)) {
c->dup_acks = c->consts->frthresh;
}
_fr_cwnd_dec(c);
c->consts->fr(c);
}

static void _dec_flight_size(congure_reno_snd_t *c, unsigned msg_size)
{
/* check for integer underflow */
if ((c->in_flight_size - msg_size) > c->in_flight_size) {
c->in_flight_size = 0U;
}
else {
c->in_flight_size -= msg_size;
}
}

void congure_reno_set_mss(congure_reno_snd_t *c, congure_wnd_size_t mss)
{
c->mss = mss;
c->super.cwnd = _calc_init_wnd(c);
}

void congure_reno_snd_init(congure_snd_t *cong, void *ctx)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

c->super.ctx = ctx;
c->mss = c->consts->init_mss;
c->last_ack = UINT32_MAX;
c->super.cwnd = _calc_init_wnd(c);
c->ssthresh = c->consts->init_ssthresh;
c->dup_acks = 0;
}

int32_t congure_reno_snd_inter_msg_interval(congure_snd_t *cong,
unsigned msg_size)
{
(void)cong;
(void)msg_size;
return -1;
}

void congure_reno_snd_report_msg_sent(congure_snd_t *cong, unsigned sent_size)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if ((c->in_flight_size + sent_size) < c->super.cwnd) {
c->in_flight_size += sent_size;
}
else {
/* state machine is dependent on flight size being smaller or equal
* to cwnd as such cap cwnd here, in case caller reports a message in
* flight that was marked as lost, but the caller is using a later
* message to send another ACK. */
c->in_flight_size = c->super.cwnd;
}
}

void congure_reno_snd_report_msg_discarded(congure_snd_t *cong,
unsigned msg_size)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

assert(msg_size <= c->in_flight_size);

_dec_flight_size(c, msg_size);
}

int _check_resends(clist_node_t *node, void *ctx)
{
congure_snd_msg_t *msg = (congure_snd_msg_t *)node;

(void)ctx;
if (msg->resends == 0) {
return 1;
}
return 0;
}

int _mark_msg_lost(clist_node_t *node, void *ctx)
{
congure_snd_msg_t *msg = (congure_snd_msg_t *)node;
congure_reno_snd_t *c = (void *)ctx;

_dec_flight_size(c, msg->size);
return 0;
}

void congure_reno_snd_report_msgs_timeout(congure_snd_t *cong,
congure_snd_msg_t *msgs)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if (msgs) {
if (clist_foreach(&msgs->super, _check_resends, NULL)) {
/* see https://tools.ietf.org/html/rfc5681#section-3.1 equation 4 */
c->ssthresh = ((c->in_flight_size / 2) > (c->mss * 2))
? (c->in_flight_size / 2)
: (c->mss * 2);
}
/* do decrementing of flight size _after_ ssthresh reduction,
* since we use the in_flight_size there */
clist_foreach(&msgs->super, _mark_msg_lost, c);
/* > Furthermore, upon a timeout (as specified in [RFC2988]) cwnd
* > MUST be set to no more than the loss window, LW, which equals
* > 1 full-sized segment (regardless of the value of IW). */
c->super.cwnd = c->mss;
}
}

void congure_reno_snd_report_msgs_lost(congure_snd_t *cong,
congure_snd_msg_t *msgs)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

clist_foreach(&msgs->super, _mark_msg_lost, c);
_enforce_fast_retransmit(c);
}

void congure_reno_snd_report_msg_acked(congure_snd_t *cong,
congure_snd_msg_t *msg,
congure_snd_ack_t *ack)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

if (seq32_compare(ack->id, c->last_ack) <= 0) {
/* check for duplicate ACK according to
* https://tools.ietf.org/html/rfc5681#section-2
* An acknowledgment is considered a "duplicate" [...] when
* (a) the receiver of the ACK has outstanding data, */
if ((c->in_flight_size > 0) &&
/* (b) the incoming acknowledgment carries no data, */
(ack->size == 0) &&
/* (c) the SYN and FIN bits are both off */
(ack->clean) &&
/* (d) the acknowledgment number is equal to the greatest
* acknowledgment received on the given connection, and */
(ack->id == c->last_ack) &&
/* (e) the advertised window in the incoming acknowledgment equals
* the advertised window in the last incoming acknowledgment. */
((ack->wnd == 0) || (c->consts->same_wnd_adv(c, ack)))) {
c->dup_acks++;
if (_snd_in_fast_retransmit(cong)) {
_fr_cwnd_dec(c);
c->consts->fr(c);
}
}
}
else {
c->dup_acks = 0;
c->last_ack = ack->id;
if (c->super.cwnd < c->ssthresh) {
/* slow start */
if (c->consts->ss_cwnd_inc) {
c->consts->ss_cwnd_inc(c);
}
else {
c->super.cwnd += (c->in_flight_size < c->mss)
? c->in_flight_size
: c->mss;
}
}
else {
/* congestion avoidance */
if (c->consts->ca_cwnd_inc) {
c->consts->ca_cwnd_inc(c);
}
else {
c->super.cwnd += c->mss;
}
}
assert(msg->size <= c->in_flight_size);
_dec_flight_size(c, msg->size);
}
}

void congure_reno_snd_report_ecn_ce(congure_snd_t *cong, ztimer_now_t time)
{
congure_reno_snd_t *c = (congure_reno_snd_t *)cong;

/* see https://tools.ietf.org/html/rfc8311#section-4.1 */
(void)time;
c->super.cwnd /= 2;
c->ssthresh -= c->mss;
}

/** @} */
Loading