[En-Nut-Discussion] MSS not calculated correctly
Henrik Maier
hmnews at proconx.com
Sun Apr 25 06:06:34 CEST 2010
Hi,
I came across a TCP/IP comms issue when a machine connects to Nut/OS (AVR,
any 4.x version) and advertises a MSS of 1024 instead of the more common
1460 bytes. Suddenly I saw Nut/OS replying a SYNC/ACK with a MSS of 4 and
this was the end of all sensible TCP/IP comms as from then on segment size
was 4 bytes which brakes most applications. (Most Linux and Windows PCs use
a MSS of 1460 but some other embedded TCP/IP stacks or WAN routers may use a
different size.)
After analysing what is going on I found the following code snippet in
tcpsm.c to be the culprit:
/* Read MAXSEG option */
case TCPOPT_MAXSEG:
s = ntohs(((u_short)cp[2] << 8) | cp[3]);
if (s < sock->so_mss)
sock->so_mss = s;
The ntohs() call is not necessary here as byte swapping is already done by
the shift and logic operation. ntohs swaps again and gives the following
results:
(Note: sock->so_mss is initialised to 536)
a) Common case of MSS=1460:
s = ntohs( 0x05 | 0xB4) = 0xb405 = 46085
if (46085 < sock->so_mss)
sock->so_mss = s;
Result: No change to sock->so_mss, it stays at Nut/OS default of 536.
b) Special case of MSS=1024:
s = ntohs( 0x04 | 0x00) = 0x0004 = 4
if (4 < sock->so_mss)
sock->so_mss = 4;
Result: sock->so_mss is now 4 and this is the maximum size of sent TCP
packets. All further TCP/IP comms will most likely not work because of the
small segment size.
I believe the correct implementation should be:
/* Read MAXSEG option */
case TCPOPT_MAXSEG:
s = ((u_short)cp[2] << 8) | cp[3];
if (s < sock->so_mss)
sock->so_mss = s;
Any thoughts/agreements/disagreements?
Regards
Henrik
PS1: This also may have been the explanation of the issue András Szemző has
reported on the 30/9/2009.
PS2: I am also not sure if topics like this should go to the mailing list or
rather be lodged as bug report on SourceForge instead (or both)?
More information about the En-Nut-Discussion
mailing list