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 static inline size_t
add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize) 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; tcp_option *option = (tcp_option *)buffer;
size_t length = 0; 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 if ((segment.options & TCP_HAS_TIMESTAMPS) != 0
&& length + 12 <= bufferSize) { && length + 12 <= bufferSize) {
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 // two NOPs so the timestamps get aligned to a 4 byte boundary
option->kind = TCP_OPTION_NOP; option->kind = TCP_OPTION_NOP;
bump_option(option, length); bump_option(option, length);
option->kind = TCP_OPTION_NOP; option->kind = TCP_OPTION_NOP;
bump_option(option, length); bump_option(option, length);
}
option->kind = TCP_OPTION_TIMESTAMP; option->kind = TCP_OPTION_TIMESTAMP;
option->length = 10; option->length = 10;
option->timestamp.value = htonl(segment.timestamp_value); option->timestamp.value = htonl(segment.timestamp_value);
option->timestamp.reply = htonl(segment.timestamp_reply); option->timestamp.reply = htonl(segment.timestamp_reply);
bump_option(option, length); 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 if ((segment.options & TCP_HAS_WINDOW_SCALE) != 0
&& length + 4 <= bufferSize) { && 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; option->kind = TCP_OPTION_NOP;
bump_option(option, length); bump_option(option, length);
@@ -132,13 +152,6 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize)
bump_option(option, length); 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) { if (segment.sackCount > 0) {
int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack); int sackCount = ((int)(bufferSize - length) - 4) / sizeof(tcp_sack);
if (sackCount > segment.sackCount) if (sackCount > segment.sackCount)
@@ -435,14 +448,13 @@ tcp_options_length(tcp_segment_header& segment)
if (segment.max_segment_size > 0) if (segment.max_segment_size > 0)
length += 4; length += 4;
if (segment.options & TCP_HAS_TIMESTAMPS) if ((segment.options & TCP_HAS_TIMESTAMPS) != 0)
length += 12; length += 12;
else if ((segment.options & TCP_SACK_PERMITTED) != 0)
if (segment.options & TCP_HAS_WINDOW_SCALE)
length += 4; length += 4;
if (segment.options & TCP_SACK_PERMITTED) if ((segment.options & TCP_HAS_WINDOW_SCALE) != 0)
length += 2; length += 4;
if (segment.sackCount > 0) { if (segment.sackCount > 0) {
int sackCount = min_c((int)((kMaxOptionSize - length - 4) int sackCount = min_c((int)((kMaxOptionSize - length - 4)