tcp: combine timestamp and sack_permitted options when possible

any ordering is legal, but some devices don't cope with what we do.
thus we reorder sack_permitted before timestamp, this doesn't cost us anything.
noticed by Sikk: https://dev.haiku-os.org/ticket/13681#comment:11

Change-Id: Ic2e1589945dd74e3034a653427a2ff45626b3a76
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4598
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Adrien Destugues <[email protected]>
This commit is contained in:
Jérôme Duval
2021-10-19 19:27:42 +00:00
committed by waddlesplash
parent eb1d596ff6
commit f0a567c451
@@ -96,6 +96,9 @@ bump_option(tcp_option *&option, size_t &length)
static inline size_t
add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
{
// Some network devices can be very sensitive to the ordering of TCP options
// https://github.com/torvalds/linux/blob/9e9fb7655ed585da8f468e29221f0ba194a5f613/net/ipv4/tcp_output.c#L598
tcp_option *option = (tcp_option *)buffer;
size_t length = 0;
@@ -108,21 +111,38 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
if ((segment.options & TCP_HAS_TIMESTAMPS) != 0
&& length + 12 <= bufferSize) {
// two NOPs so the timestamps get aligned to a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
if ((segment.options & TCP_SACK_PERMITTED) != 0) {
// combine with timestamp
option->kind = TCP_OPTION_SACK_PERMITTED;
option->length = 2;
bump_option(option, length);
} else {
// two NOPs so the timestamps get aligned to a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
}
option->kind = TCP_OPTION_TIMESTAMP;
option->length = 10;
option->timestamp.value = htonl(segment.timestamp_value);
option->timestamp.reply = htonl(segment.timestamp_reply);
bump_option(option, length);
} else if ((segment.options & TCP_SACK_PERMITTED) != 0
&& length + 4 <= bufferSize) {
// two NOPs so that the subsequent data is aligned on a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
option->kind = TCP_OPTION_SACK_PERMITTED;
option->length = 2;
bump_option(option, length);
}
if ((segment.options & TCP_HAS_WINDOW_SCALE) != 0
&& length + 4 <= bufferSize) {
// insert one NOP so that the subsequent data is aligned on a 4 byte boundary
// one NOP so that the subsequent data is aligned on a 4 byte boundary
option->kind = TCP_OPTION_NOP;
bump_option(option, length);
@@ -132,13 +152,6 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
bump_option(option, length);
}
if ((segment.options & TCP_SACK_PERMITTED) != 0
&& length + 2 <= bufferSize) {
option->kind = TCP_OPTION_SACK_PERMITTED;
option->length = 2;
bump_option(option, length);
}
if (segment.sackCount > 0) {
int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack);
if (sackCount > segment.sackCount)
@@ -435,14 +448,13 @@ tcp_options_length(tcp_segment_header& segment)
if (segment.max_segment_size > 0)
length += 4;
if (segment.options & TCP_HAS_TIMESTAMPS)
if ((segment.options & TCP_HAS_TIMESTAMPS) != 0)
length += 12;
if (segment.options & TCP_HAS_WINDOW_SCALE)
else if ((segment.options & TCP_SACK_PERMITTED) != 0)
length += 4;
if (segment.options & TCP_SACK_PERMITTED)
length += 2;
if ((segment.options & TCP_HAS_WINDOW_SCALE) != 0)
length += 4;
if (segment.sackCount > 0) {
int sackCount = min_c((int)((kMaxOptionSize - length - 4)