Why is getockopt () not returning the expected value for TCP_MAXSEG?

I am trying to programmatically configure the MSS of my TCP connection on a GNU / Linux system, specifically Ubuntu 12.04, kernel 3.2.0-68- generic

According to man 7 tcp

TCP_MAXSEG The maximum segment size for outgoing TCP packets. If this option is set before the connection is established, it also changes the MSS value advertised on the other end of the original packet. more than the (end) MTU interface have no effect. TCP will also impose their minimum and maximum boundaries over the supplied value.

this allows me to think that I can tweak the value before connecting the TCP socket. I wrote a small piece of code to create a socket and I used setsockopt () to set up the MSS. In my test, I am setting mss to 1000B.

The code calls setsockopt () followed by getockopt () to double check that the setting is correct. Both syscalls return 0 (no error). After that, I connect to the remote host to check via tcpdump that mss is using is correct. This is what I see:

  • the MSS return value from getsockopt () is always 536 bytes
  • tcpdump shows configured MSS in syn package

Changed my code to set up MSS after socket connection.

  • the MSS return value from getsockopt () is 1448 Bye

Is there a correct way to explain this behavior?

A few notes:

  • according to wikipedia 536 B = MaxIPDatagramSize - IPHeaderSize - TcpHeaderSize this is necessary to prevent fragmentation of IP packets.
  • creating a socket and connecting it (no setsockopt () call), displays mss of 536 B returned by getsockopt (), but tcpdump shows declared mss 1460 B in a SYN packet, which makes sense to be 1500 - IpHeader - TcpHeader

Below, if you are interested, this is my code:

int setSocketMss( int i_sd, int i_mss )
{
    int res = 0;
    int mss = i_mss;
    socklen_t len = sizeof( mss );

    res = ::setsockopt( i_sd, IPPROTO_TCP, TCP_MAXSEG, &mss, len );
    if ( res < 0 )
    {
        qDebug() << "error: cannot configure mss for socket" << i_sd;
        return -1;
    }
    res = ::getsockopt( i_sd, IPPROTO_TCP, TCP_MAXSEG, &mss, &len );
    if ( mss != i_mss )
    {
        qDebug() << "MSS set to" << i_mss << "but read value is" << mss;
    }
    else
    {
        qDebug() << "MSS for socket" << i_sd << " has been set to" << mss;
    }
    return mss;
}

void configureAddrStruct( const QString & i_ipAddress,
                          quint16 i_port,
                          struct sockaddr_in & o_sockaddr )
{
    o_sockaddr.sin_addr.s_addr = htonl( QHostAddress(i_ipAddress).toIPv4Address() );
    o_sockaddr.sin_port = htons( i_port );
    o_sockaddr.sin_family = PF_INET;
    memset( o_sockaddr.sin_zero, 0, sizeof(o_sockaddr.sin_zero) );
}

int main(int argc, char *argv[])
{
    int sd = ::socket( PF_INET, SOCK_STREAM, getprotobyname("tcp")->p_proto );
    if ( -1 == sd )
    {
        qDebug() << "erro creating socket";
        exit (1);
    }
    else
    {
        qDebug() << "created socket:" << sd;
    }

    setSocketMss( sd, 1000 );

    struct sockaddr_in localAddress;
    struct sockaddr_in remoteAddress;

    configureAddrStruct( "192.168.23.7", 0, localAddress );
    configureAddrStruct( "192.168.23.176", 9999, remoteAddress );

    int res = ::bind( sd,
                      reinterpret_cast<const sockaddr *>( &localAddress ),
                      sizeof(localAddress) );
    if ( -1 == res )
    {
        qDebug() << "error binding socket to local address";
        exit(2);
    }

    //setSocketMss( sd, 1000 );

    res = ::connect( sd,
                     reinterpret_cast<const sockaddr*>( &remoteAddress ),
                     sizeof(remoteAddress) );
    if ( -1 == res )
    {
        qDebug() << "error connecting to remote host";
        ::perror( "connect()" );
        exit(2);
    }

    //setSocketMss( sd, 1000 );

    return 0;
}

      

+3


source to share


1 answer


You are doing the wrong thing. You must send the size of the receive socket buffer to the receiver.



0


source







All Articles