Menu

Windows builds - fail to set IP_PMTUDISC_DONT on socket

Digarok
2018-04-23
2018-08-30
  • Digarok

    Digarok - 2018-04-23

    I'm trying to build on Windows, and I was eventually able to make static binaries with OpenSSL linked in. However I'm still having a problem with one bit of code. It fails to set the IP_PMTUDISC_DONT flag on the socket.

    > uftpd.exe -D C:\xfertest\ -p 1044 -d -T c:\tmp
    Error disabling MTU discovery: (10042) An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call.
    Error leaving multicast group: (10038) An operation was attempted on something that is not a socket.
    

    This is the code that is trying to set the flag on the socket in client_init.c:

    #ifdef IP_MTU_DISCOVER
            {
                int mtuflag = IP_PMTUDISC_DONT;
                if (setsockopt(listener, IPPROTO_IP, IP_MTU_DISCOVER, &mtuflag, sizeof(mtuflag)) == SOCKET_ERROR) {
                    sockerror(0, 0, 0, "Error disabling MTU discovery");
                    closesocket(listener);
                    exit(ERR_SOCKET);
                }
            }
    #endif
    

    This is built on Windows Server 2016 with VS2017 community edition.

    I have tried testing builds that don't include this flag, but they seem to have far worse performance (tons of NAKs), which maybe isn't a surprise if this causes fragmentation.

    Can anyone think of why I can't setsockopt() on this? The listener is indeed a socket as created in the same file by listener = socket(family, SOCK_DGRAM, 0). I see other feedback on the internet claiming that some of these APIs may have changed through time. I will see if I can build on earlier VS and OS versions, but I am hoping someone who has hit this before can point out a clear path to a solution faster than I can.

     
  • KSmith

    KSmith - 2018-08-09

    The reason for the problem, is that the newer Windows SDKs has support for IP_MTU_DISCOVER and IPV6_MTU_DISCOVER, but the Windows you actually run the program on, misses the runtime support for the feature.
    See more at https://stackoverflow.com/questions/49982697/why-cant-i-disable-mtu-discovery-on-windows-socket-ip-pmtudisc-dont-fails-to-s

    To fix the problem in the source code, I did these changes to client_init.c, proxy_init.c and server_init.c:

    #ifdef IPV6_MTU_DISCOVER
            {
                int mtuflag = IP_PMTUDISC_DONT;
                if (setsockopt(listener, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
                               (char *)&mtuflag, sizeof(mtuflag)) == SOCKET_ERROR) {
    #ifdef WINDOWS
                  if (WSAGetLastError() != WSAENOPROTOOPT) {
    #endif
                    sockerror(0, 0, 0, "Error disabling MTU discovery");
                    closesocket(listener);
                    exit(ERR_SOCKET);
    #ifdef WINDOWS
                  }
    #endif
                }
            }
    #endif
    
    #ifdef IP_MTU_DISCOVER
            {
                int mtuflag = IP_PMTUDISC_DONT;
                if (setsockopt(listener, IPPROTO_IP, IP_MTU_DISCOVER,
                        (char *)&mtuflag, sizeof(mtuflag)) == SOCKET_ERROR) {
    #ifdef WINDOWS
                  if (WSAGetLastError() != WSAENOPROTOOPT) {
    #endif
                    sockerror(0, 0, 0, "Error disabling MTU discovery");
                    closesocket(listener);
                    exit(ERR_SOCKET);
    #ifdef WINDOWS
                  }
    #endif
                }
            }
    #endif
    

    The #ifdef WINDOWS blocks was added.

    In addition, I fixed a problem related to the programs selecting UID on machines where the first network interface is missing an IP address, so the changed proxy_init.c and client_init.c noe loops through network interfaces, until a non-null UID may be extracted from the IP-address:

    for (i = 0; i < interface_count; i++) {
        if (m_interface[i].su.ss.ss_family == AF_INET6) {
            uid = m_interface[i].su.sin6.sin6_addr.s6_addr[12] << 24;
            uid |= m_interface[i].su.sin6.sin6_addr.s6_addr[13] << 16;
            uid |= m_interface[i].su.sin6.sin6_addr.s6_addr[14] << 8;
            uid |= m_interface[i].su.sin6.sin6_addr.s6_addr[15];
        } else {
            uid = m_interface[i].su.sin.sin_addr.s_addr;
        }
        // Break if found interface with IP address set
        if (uid) break;
    }
    
     
  • Dennis Bush

    Dennis Bush - 2018-08-30

    These fixes have been added to UFTP 4.9.8.

     

Log in to post a comment.