This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TMS320C6657: Telnet session gets lost due to network inactivity

Guru* 95265 points
Part Number: TMS320C6657
Other Parts Discussed in Thread: MSP432E401Y, SYSBIOS

Hi,

I have a customer issue on telnet application using TI NDK 3.60. The same can also be reproduce on TI NIMU_emacExampleClient_EVMC6657C66BiosExampleProject by creating a Telnet server using:

hTelnet = DaemonNew (SOCK_STREAM, 0, 23, (int(*)(SOCKET,uint32_t))telnetClientProcess, OS_TASKPRINORM, OS_TASKSTKLOW,
(uint32_t)ConsoleOpen, 2 );

The observation is:

  1. After run the application on C6657, I was able to do telnet connection to it from a host Win 10 PC.
  2. I was able to run some command like “help”
  3. If I keep idle for a few minutes, the connection is lost
  4. Most of the time, I can telnet to it again.
  5. If I continue running the “help” command or ping the EVM (tried >1 hours), the telnet session never lost.

It looks there is a time-out, the network socket is closed if no activity for a while. Customer mentioned that in the old NDK 1.94 there is no such issue.

From TI Network Developer's Kit (NDK) v2.24 User's Guide

2.2.3.2 Telnet Server

The client example application also includes a console application with several tests and status query
functions available. In order to get to the console, simply telnet into the application's IP address. Note that
the console program will timeout and disconnect after a period of inactivity.

So, this is a known issue! How do avoid it?

Regards, Eric

  • Correction to 5:  If I continue running the “help” command or ping the EVM (tried >1 hours), the telnet session never lost. ====>That is, the Telnet still closed even I have continuously ping the EVM.

    Regards, Eric

  • Hello Eric,

    There is indeed a timeout in the telnet console library. 

    In ti/ndk/tools/console/console.c the ConGetCh() function sets the timeout. If no chars are sent from the user in that timeout the console session and the listening telnet socket is closed.

    Unfortunately you cannot configure this timeout at runtime, but you can edit the below code in ConGetCh() to set the timeout to what you want. 

    /* Configure our console timeout to be 5 minutes */
        timeout.tv_sec  = 5 * 60;
        timeout.tv_usec = 0;

    You will need to rebuild the NDK after changing this. Instructions on how to do that can be found here.

    Regards,

    Dalton

  • Dalton,

    Thanks for the quick solution! I updated the customer and will let you know the results!

    Regards, Eric

  • Dalton,

    One thing I thought about yesterday was how to disable the timeout, I looked at the source code: 

        /* Configure our console timeout to be 5 minutes */

        timeout.tv_sec  = 5 * 60;

    timeout.tv_usec = 0;

     

    cnt = fdSelect( 0, &ibits, 0, 0, &timeout );

     

    where:

    int fdSelect( int maxfd, NDK_fd_set *readfds, NDK_fd_set *writefds,

                             NDK_fd_set *exceptfds, struct timeval *timeout )

    {

        FDTABLE     *pfdt;

        int32_t     tval;

        FDPOLLITEM  *ppi;

        int          num_fd;

        unsigned int fd_count;

        unsigned int i;

        unsigned int ppiCnt;

     

        (void)maxfd;

     

        /* Convert timeout value in "timeout" to time in milliseconds used by poll */

        if( !timeout )

            tval = POLLINFTIM;

           

           … fdPoll(0,0,tval)

     

    Where:

    /*-------------------------------------------------------------------- */

    /* fdPoll()     (USER FUNCTION) */

    /* Selects on a file descriptor list. */

    /* items[] - File Descriptor items to test */

    /* itemcnt - Number of items in the list */

    /* timeout - Timeout in MS, or POLLINFTIM for no timeout */

    /*-------------------------------------------------------------------- */

    int fdPoll( FDPOLLITEM items[], uint32_t itemcnt, int32_t timeout )

     

    So, if needed, this can be set to 0 for never timeout:

        timeout.tv_sec  = 0;

    timeout.tv_usec = 0;

    This was my suggestion to customer yesterday. Do you think it make sense?

    Regards, Eric

  • Then, I have the customer feedback today, I posted here (192.168.1.5 is the TI EVM, 192.168.1.6 is the host PC), can you help?

    - Eric

    ==========

    I set the timeout to 0 and rebuilt the NDK. Then I built the example client with this NDK. When I attempted to connect to the evm, it closed the connection immediately. Then I changed the timeout to 1M which is >100 days, and rebuilt. The connection stayed up for 1 hour, then window's telnet just said connection to host lost. I tried to open the connection again with Window's telnet and i don't see TCP packets but I do see ARPs looking for the IP i'm trying to connect to (192.168.1.5). but there is no response.

    I can see that once the connection is lost, and I try to telnet to the evm again, an ARP is sent but there is no response. The ARP and its response should look like this:

    No.                  Time                                                               Source                                   Destination                          Protocol        Length           Info

    20                   2020-04-01 12:41:57.532330               D-LinkIn_d4:7b:4e                                     Broadcast      ARP                 42                   Who has 192.168.1.5? Tell 192.168.1.6

    21                   2020-04-01 12:41:57.535689               TexasIns_0a:2e:20             D-LinkIn_d4:7b:4e             ARP                 60                   192.168.1.5 is at 00:18:30:0a:2e:20

     

    The response is missing. I Disabled/Enabled the USB enet dongle, restarted CCS and reloaded the example client with its timeout still set to 1M and could then see the ARP response and telnet connected again. But after 5 minutes, there’s no response from the evm (192.168.1.5). It seems like the connection can stay up for 5 to 30 minutes until it stops responding. This is the wireshark output from the last failure:

     

     

    No.     Time                        Source        Destination   ProtocolLength       Info

    171     2020-04-01 13:02:03.659121 192.168.1.5   192.168.1.6   TELNET 548    Telnet Data ...

    172     2020-04-01 13:02:03.659178 192.168.1.6   192.168.1.5   TCP    54     63055 → 23 [ACK] Seq=16 Ack=1189 Win=63052 Len=0

    173     2020-04-01 13:02:06.014391 D-LinkIn_d4:7b:4e    TexasIns_0a:2e:20    ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    174     2020-04-01 13:02:06.017856 TexasIns_0a:2e:20    D-LinkIn_d4:7b:4e    ARP    60     192.168.1.5 is at 00:18:30:0a:2e:20

    175     2020-04-01 13:03:20.667338 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    176     2020-04-01 13:03:21.678859 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    177     2020-04-01 13:03:22.679073 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    178     2020-04-01 13:03:23.679202 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    179     2020-04-01 13:04:31.989252 192.168.1.6   192.168.1.255 NBNS   92     Name query NB CRNTDC03<20>

    180     2020-04-01 13:04:31.989902 192.168.1.6   224.0.0.252   LLMNR  68     Standard query 0xaf21 A CRNTDC03

    181     2020-04-01 13:04:31.990525 192.168.1.6   224.0.0.252   LLMNR  68     Standard query 0x9e88 AAAA CRNTDC03

    182     2020-04-01 13:04:32.733304 192.168.1.6   192.168.1.255 NBNS   92     Name query NB CRNTDC03<20>

    183     2020-04-01 13:04:33.499084 192.168.1.6   192.168.1.255 NBNS   92     Name query NB CRNTDC03<20>

    184     2020-04-01 13:05:20.681461 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    185     2020-04-01 13:05:21.693548 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    186     2020-04-01 13:05:22.709543 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    187     2020-04-01 13:05:23.709813 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    188     2020-04-01 13:07:20.693362 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    189     2020-04-01 13:07:21.706084 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    190     2020-04-01 13:07:22.706167 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    191     2020-04-01 13:07:23.706699 192.168.1.6   239.255.255.250      SSDP   216    M-SEARCH * HTTP/1.1

    192     2020-04-01 13:07:45.443404 192.168.1.6   192.168.1.5   TELNET 56     Telnet Data ...

    193     2020-04-01 13:07:46.240157 192.168.1.6   192.168.1.5   TCP    56     [TCP Retransmission] 63055 → 23 [PSH, ACK] Seq=16 Ack=1189 Win=63052 Len=2

    194     2020-04-01 13:07:48.896793 192.168.1.6   192.168.1.5   TCP    58     [TCP Retransmission] 63055 → 23 [PSH, ACK] Seq=16 Ack=1189 Win=63052 Len=4

    195     2020-04-01 13:07:50.021748 D-LinkIn_d4:7b:4e    TexasIns_0a:2e:20    ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    196     2020-04-01 13:07:51.022888 D-LinkIn_d4:7b:4e    TexasIns_0a:2e:20    ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    197     2020-04-01 13:07:52.021971 D-LinkIn_d4:7b:4e    TexasIns_0a:2e:20    ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    198     2020-04-01 13:07:54.131671 D-LinkIn_d4:7b:4e    Broadcast     ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    199     2020-04-01 13:07:55.022293 D-LinkIn_d4:7b:4e    Broadcast     ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

    200     2020-04-01 13:07:56.022424 D-LinkIn_d4:7b:4e    Broadcast     ARP    42     Who has 192.168.1.5? Tell 192.168.1.6

  • Hello Eric,

    To set no timeout you would have to modify the fdSelect() call like below:

    cnt = fdSelect( 0, &ibits, 0, 0, NULL );

    Giving it a timeval with both members set to 0 will just cause the app to timeout immediately as the customer experienced.
    As for the customer's issue with setting the value to 1M. They are hitting an overflow issue. fdSelect() converts the timeval struct into a single uint32_t var that represents time in milliseconds. 1 Million minutes in milliseconds is too big to fit into the int32_t. 
    Regards,
    Dalton
  • Dalton,

    1M is 1000000 which is hex 0xF 4240 so it exceeds 16 bit limits but not 32 bits.

    best regards,

    David Zhou

  • David,

    That is true 1 million alone is sufficient to be held in 32 bits. But the timeval struct is being set in units of seconds. So we really have 1000000 * 60. That is still sufficient to hold the value in the timeval struct, but ultimately the timeval struct is converted to milliseconds. This means the final value used to calculate the timeout is 1000000 * 60 * 1000 = 6e10 = 0xDF8475800. That is the number that is overflowing.

    Regards,

    Dalton

  • Dalton,

    Got it!

    best regards,

    David Zhou

  • Dalton,

    From the code it seems timeout.tv_sec is already in seconds, so 1M is already in seconds. So should it be 1M * 1000 which is still within 32 bits , where does the second 60 come from?

        /* Configure our console timeout to be 5 minutes */

        timeout.tv_sec  = 5 * 60;

    regards,

    David

  • Hi David,

    I'm assuming based on the response from the customer that the 1M figure they were setting was in minutes because they say that "I changed the timeout to 1M which is >100 days". 1 million seconds is ~11 days, but 1 million minutes is ~694 days. 

    The 60 in code you mentioned is just to convert the seconds to minutes. 

    Eric, can you confirm my assumption here is correct? Regardless I think the solution is to set the timeout argument to NULL in fdSelect() to prevent any telnet console timeouts.

    Regards,

    Dalton

  • Dalton,

    >>>>can you confirm my assumption here is correct? Regardless I think the solution is to set the timeout argument to NULL in fdSelect() to prevent any telnet console timeouts.>>>>

    From customer:

    Anyway, I tried:

                cnt = fdSelect( 0, &ibits, 0, 0, NULL );

    in place of:

                cnt = fdSelect( 0, &ibits, 0, 0, &timeout);

    and I still see the port close in 1 to 20 minutes.

    Regards, Eric

  • Hello Eric,

    I tried reproducing this issue after I modified my fdSelect() call to not have a timeout, but I was able to have the telnet console remain active without any input from me for 45+ minutes so far.

    Could I see a wireshark capture log file from the customer? Just as a sanity check, it would be good to confirm the customer rebuilt his whole app after rebuilding the NDK too. Otherwise, the changes likely wouldn't have been picked up from the rebuilt NDK libraries.

    Regards,

    Dalton

  • Dalton,

    I was also able to reproduce customer's issue. Setup is the TI C6657 EVM, and pdk_c665x_2_0_15\packages\MyExampleProjects\NIMU_emacExampleClient_EVMC6657C66BiosExampleProject.

    NDK: ndk_3_60_00_13 (customer version)

    After the code change: cnt = fdSelect( 0, &ibits, 0, 0, NULL );, several console libraries are built. console_min.ae66 is the one linked into the application. I still have the telnet connection lost, typically < 10 minutes.

    Attached is Wireshark. 192.168.1.4 is the EVM, 192.168.1.11 is the PC. This is a direct 1Gbps connection. After loss, I was still able to ping the EVM. If I try the telnet again, it showed "

    Console is busy

    Connection to host lost."

    Regards, Eric

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/Telnet_5F00_loss.pcapng

  • Hey Eric,

    I'm still having trouble reproducing it. I've tried both the windows and linux telnet clients and I'm staying connected for far more than 10 minutes. However there is the possibility a firewall or network setting on your network is causing the connection to drop.

    Can you set a breakpoint on the following label in ConGetCh(), and test it again for me?

    abort_console:
        ConsoleClose();
    
        fdClose( scon );
        TaskExit();
    
        return(0);
    }

    Based on your wireshark log, it doesn't look like you are losing connection here, but this will at least rule out the timeout change not working if you don't ever hit this breakpoint. 

    Also did you have wireshark running when you tried to reconnect with telnet? I don't see that communication in your posted log.

    Regards,

    Dalton

  • Dalton,

    Thanks! Was your test based on K2G EVM or some other SOC board? I tested it today with a breakpoint at ConsoleClose():

    • original NDK with timeout 5 minutes, the telnet closed in a few minutes (< 5) and the breakpoint is not hit, there is no additional packets in wireshark when disconnected. 
    • modified NDK with timeout to NULL (no timeout) 

     cnt = fdSelect( 0, &ibits, 0, 0, &timeout );

    same observation: the telnet closed in a few minutes and the breakpoint is not hit, there is no additional packets in wireshark when disconnected. 

    • modfied NDK with timeout to 1 minute

    /* Configure our console timeout to be 5 minutes */
    timeout.tv_sec = 1 * 60;
    timeout.tv_usec = 0;

    observation: the breakpoint hit, at telnet is not closed at this moment. Run, the telnet is closed, I can telnet again. Wireshark is attached, not sure what you look for. I repeated this several times (hit, run, telnet).

    So, it looks to me that the timeout in the NDK work as expected. "is the possibility a firewall or network setting on your network is causing the connection to drop"? ====>Any suggestions to check this?  

    Attached two wireshark captures. 

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/Telnet_5F00_loss_5F00_NDK_5F00_with_5F00_timeout_5F00_NULL.pcapng

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/Telnet_5F00_loss_5F00_NDK_5F00_with_5F00_timeout_5F00_1min.pcapng

    Regards, Eric

  • Dalton,

    Is there any TCP keepalive number we can configure inside NDK?

    Regards, Eric 

  • Hey Eric,

    Yes there is a keepalive timer in the NDK. The TCP algorithm will manage it for you, but we can turn it on. 

    In console.c go to console() function and modify it like so:

    static void console( SOCKET sCon, struct sockaddr *pClient )
    {
        uint32_t tmp;
        char   tstr[80];
        char   *tok[10];
        int    i,logon=0;
    
        fdOpenSession( TaskSelf() );
    
        /* Get our socket */
        scon = sCon;
    
        int optval = 1;
        int status = 0;
        status = setsockopt(scon, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
        if(status == -1) {
            while(1)
            {
                //error
            }
        }

    If it's working you should see periodic keepalive pings in wire shark. Note not all tcp implementations implement a keepalive timer, so if your pc doesn't this might not have any affect. 

    Also I am running my tests on a MSP432E401Y.

    Regards,

    Dalton

  • Hey Eric,

    I tried to reproduce the TCP Keepalive timer in your scenario and my last post was actually wrong. The socket in the console can't have the keepalive turned on. 

    You have to turn it on in the telnet code itself. In ti/ndk/nettols/telnet/telnetd.c change the telnetClientProcess() function as shown below and rebuild the ndk:

    int telnetClientProcess( SOCKET s, SOCKET (*cbfn)(struct sockaddr *) )
    {
    #ifdef _INCLUDE_IPv6_CODE
        struct sockaddr_in6 sin1;
    #else
        struct sockaddr_in  sin1;
    #endif
        int                tmp1;
        TINSTANCE          *pti;
    
        /* Alloc our instance structure */
        pti = mmBulkAlloc( sizeof(TINSTANCE) );
    
        /* If the alloc failed, abort */
        if( !pti )
            return(1);
    
        int optval = 1;
        int status = 0;
        status = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
        if(status == -1) {
            while(1)
            {
                //error
            }
        }

    This will turn on the keepalive timer for the telnet socket. However there is one more thing to change. We have to change how long it takes to start a keepalive ping. By default it will take 2 hours!

    In your *.cfg file for the project you will need to add the following lines of config

    var Tcp = xdc.useModule('ti.ndk.config.Tcp');
    Tcp.keepIdleTime = 1200; // 2 minutes. This timer is in units of .1 seconds

    I can't test if that fixes your telnet disconnect issue, but I can confirm this causes tcp keepalive probes to be sent after 2 minutes of inactivity. If a NAT or firewall is causing your connection to drop because of inactivity these probes should prevent that though. 

    Regards,

    Dalton

  • Dalton,

    Will TCP Alive packets be shown on the Wireshark capture (I didn't see anything)? Attached is the telnetd.c and I configure keepalive with Tcp.keepIdleTime = 600; I still have telnet drop. 

    /*
     * Copyright (c) 2012-2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     * */
    /*
     * ========  ========
     *
     * Telnet Server Utility
     *
     */
    
    #include <string.h>
    #include <stdint.h>
    
    #include "telnet.h"
    
    /* Some instance and buffering limits */
    #define BUFSIZE     512
    
    /* Application Supplies Echo */
    #define APP_ECHO        1
    
    /* Data specific to a telnet connection task */
    
    typedef struct _ti {
            unsigned char     options[256];
    
            SOCKET      sNet;                  /* Socket to Telnet client */
            SOCKET      sTerm;                 /* Socket to Terminal */
    
            int         InputState;            /* Status of input processing */
    
            unsigned char     tnet[BUFSIZE];         /* Data ready for NET */
            int         tnetcnt;
            unsigned char     fterm[BUFSIZE];        /* Raw data from terminal */
            int         ftermcnt;
            int         ftermridx;             /* Read index */
            unsigned char     fnet[BUFSIZE];         /* Raw data from network */
            int         fnetcnt;
            int         fnetridx;              /* Read index */
            unsigned char     tterm[BUFSIZE];        /* Data ready for terminal */
            int         ttermcnt;
    } TINSTANCE;
    
    
    /* 
     * TelnetOpen
     * Create an instance of the Telnet Server
     */
    void *TelnetOpen( NTARGS *pNTA, NTPARAM_TELNET *pNTP )
    {
        void *hEntry;
        uint32_t IpAddr;
    
        if( !pNTA || !pNTP )
            return(0);
    
        /* Check for called by address */
        if( pNTA->CallMode == NT_MODE_IPADDR )
            IpAddr = pNTA->IPAddr;
        /* Check for called by IfIfx */
        else if( pNTA->CallMode == NT_MODE_IFIDX )
        {
            if( !NtIfIdx2Ip( pNTA->IfIdx, &IpAddr ) )
                return(0);
        }
        else
            return(0);
    
        if ( !pNTP->Callback )
            return(0);
    
        if( !pNTP->MaxCon )
            pNTP->MaxCon = 4;
    
        if( !pNTP->Port )
            pNTP->Port = 23;
    
        hEntry = DaemonNew( SOCK_STREAM, IpAddr, pNTP->Port,
                            (int(*)(SOCKET,uint32_t))telnetClientProcess,
                            OS_TASKPRINORM, OS_TASKSTKLOW,
                            (uintptr_t)(pNTP->Callback), pNTP->MaxCon );
    
        return( hEntry );
    }
    
    /* 
     * TelnetClose
     *
     * Destroy an instance of the Telnet Server
     */
    void TelnetClose( void *h )
    {
        DaemonFree( h );
    }
    
    
    static void telnet_engine( TINSTANCE *pti );
    static int  CmdWriteBytes( TINSTANCE *pti, unsigned char *pData, int len );
    static int  CmdWriteCmd( TINSTANCE *pti, unsigned char cmd, unsigned char opt );
    static void telnet_termdata( TINSTANCE *pti );
    static void telnet_netdata( TINSTANCE *pti );
    
    /* 
     * telnetClientProcess()
     *
     * Connects to terminal via callback and initiates telnet
     */
    int telnetClientProcess( SOCKET s, SOCKET (*cbfn)(struct sockaddr *) )
    {
    #ifdef _INCLUDE_IPv6_CODE
        struct sockaddr_in6 sin1;
    #else
        struct sockaddr_in  sin1;
    #endif
        int                tmp1;
        TINSTANCE          *pti;
    
        /* Alloc our instance structure */
        pti = mmBulkAlloc( sizeof(TINSTANCE) );
    
        /* If the alloc failed, abort */
        if( !pti )
            return(1);
    
            int optval = 1;
        int status = 0;
        status = setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
        if(status == -1) {
            while(1)
            {
                //error
            }
        }	
    		
    	/* Initialize the instance structure */
        memset( pti, 0, sizeof(TINSTANCE) );
        pti->sNet = s;
    
        /* Initialize socket defaults (sNet is already initialized) */
        pti->sTerm = INVALID_SOCKET;
    
        /* Create Local Connection */
    
        /* First, find out who we're connected to */
        tmp1 = sizeof( sin1 );
        getpeername( pti->sNet,(struct sockaddr *)&sin1, &tmp1 );
    
        /* Call the callback function with the peer name */
        /* and get our terminal socket */
        if( (pti->sTerm = cbfn((struct sockaddr *)&sin1 )) == INVALID_SOCKET )
            goto leave;
    
        /* Initialize Connection Instance */
    
        /* Set the default state of the options we do use */
        pti->options[OPT_BINARY] = OS_WONT | OS_DONT;
        pti->options[OPT_ECHO  ] = OS_WILL | OS_DONT;
        pti->options[OPT_SGA   ] = OS_WILL | OS_DONT;
        pti->options[OPT_STATUS] = OS_WONT | OS_DONT;
        pti->options[OPT_TM    ] = OS_WONT | OS_DONT;
        pti->options[OPT_EXOPL ] = OS_WONT | OS_DONT;
    
        /* Tell the other side our configuration preferences */
        CmdWriteCmd( pti, CMD_WILL, OPT_ECHO );
        CmdWriteCmd( pti, CMD_WILL, OPT_SGA );
    
        /* Start Telnet */
        telnet_engine( pti );
    
    leave:
        /* telnet task is dead, and we need to clean up the sockets. */
        if( pti->sTerm != INVALID_SOCKET )
            fdClose(pti->sTerm);
    
        mmBulkFree(pti);
    
        /* Return "1" since the original socket is still open */
        return(1);
    }
    
    static int CmdWriteBytes( TINSTANCE *pti, unsigned char *pData, int len )
    {
        if( (pti->tnetcnt+len) > BUFSIZE )
            return(0);
        while(len--)
            pti->tnet[pti->tnetcnt++] = *pData++;
        return(1);
    }
    
    static int CmdWriteCmd( TINSTANCE *pti, unsigned char cmd, unsigned char opt )
    {
        if( (pti->tnetcnt+3) > BUFSIZE )
            return(0);
        pti->tnet[pti->tnetcnt++] = CMD_IAC;
        pti->tnet[pti->tnetcnt++] = cmd;
        pti->tnet[pti->tnetcnt++] = opt;
        return(1);
    }
    
    static void telnet_engine( TINSTANCE *pti )
    {
        int quit = 0;
    
        for(;;)
        {
            /* If there is data from the telnet peer to process, do so */
            if( pti->fnetcnt )
                telnet_netdata( pti );
    
            /* If there is data to send to terminal, send it */
            if( pti->ttermcnt )
            {
                if( send( pti->sTerm, pti->tterm, pti->ttermcnt, 0 ) < 0 )
                    quit = 1;
                pti->ttermcnt = 0;
            }
    
            /* If there is data from the terminal, process it */
            if( pti->ftermcnt )
                telnet_termdata( pti );
    
            /* If there is data to send to telnet peer, send it */
            if( pti->tnetcnt )
            {
                if( send( pti->sNet, pti->tnet, pti->tnetcnt, 0 ) < 0 )
                    quit = 1;
                pti->tnetcnt = 0;
            }
    
            /* If everyone is idle, drop into a select call. */
            if( !pti->tnetcnt && !pti->ttermcnt &&
                              !pti->fnetcnt && !pti->ftermcnt )
            {
                NDK_fd_set ibits;
                int    c;
    
                if( quit )
                    break;
    
                NDK_FD_ZERO(&ibits);
    
                NDK_FD_SET( pti->sNet, &ibits );
                NDK_FD_SET( pti->sTerm, &ibits );
                /* arg 0 or fdSelect not used. Pass 0 for 64-bit compatibility */
                c = fdSelect( 0, &ibits, 0, 0, (struct timeval *)0 );
                if( c < 0 )
                    break;
    
                /* Read new data from the network */
                if( NDK_FD_ISSET(pti->sNet, &ibits) )
                {
                    c = recv( pti->sNet, pti->fnet, BUFSIZE, 0 );
                    if( c <= 0 )
                        quit = 1;
                    else
                        pti->fnetcnt = c;
                    pti->fnetridx = 0;
                }
    
                /* Read new data from the terminal */
                if( NDK_FD_ISSET(pti->sTerm, &ibits) )
                {
                    c = recv( pti->sTerm, pti->fterm, BUFSIZE, 0 );
                    if( c <= 0 )
                        quit = 1;
                    else
                        pti->ftermcnt = c;
                    pti->ftermridx = 0;
                }
            }
        }
    }
    
    /* Some MACROS to use with our "pti" variable */
    
    #define IS_WONT(x)              (!(pti->options[(x)]&OS_WILL))
    #define IS_WILL(x)              (pti->options[(x)]&OS_WILL)
    #define IS_DONT(x)              (!(pti->options[(x)]&OS_DO))
    #define IS_DO(x)                (pti->options[(x)]&OS_DO)
    
    #define SET_WONT(x)             (pti->options[(x)]&=~OS_WILL)
    #define SET_WILL(x)             (pti->options[(x)]|=OS_WILL)
    #define SET_DONT(x)             (pti->options[(x)]&=~OS_DO)
    #define SET_DO(x)               (pti->options[(x)]|=OS_DO)
    
    /* Status of Telnet Input Processing */
    #define TOK_NORMAL      0   /* Normal operation */
    #define TOK_IAC         1   /* rcvd IAC */
    #define TOK_CR          2   /* rcvd CR */
    #define TOK_SB          3   /* In SB/SE Pairing */
    #define TOK_WILL        4   /* rcvd will */
    #define TOK_WONT        5   /* rcvd wont */
    #define TOK_DO          6   /* rcvd do */
    #define TOK_DONT        7   /* rcvd dont */
    
    static void telnet_netdata( TINSTANCE *pti )
    {
        unsigned char data;
    
        /* Process data from the network */
        while( pti->fnetridx < pti->fnetcnt )
        {
            data = pti->fnet[pti->fnetridx++];
    
            switch( pti->InputState )
            {
            case TOK_NORMAL:
                /* Under normal operation, we channel the charater to */
                /* the terminal. We may also echo it back to the sender */
                if( data == CMD_IAC )
                {
                    pti->InputState = TOK_IAC;
                    break;
                }
    
                /* Copy the data to the output buffer */
    
                /* Must be able to fit in output buffer */
                if( (pti->ttermcnt+2) > BUFSIZE )
                    goto abort;
    
                /* If echoing, the character must also fit in tnetcnt */
                if( IS_WILL(OPT_ECHO) && (pti->tnetcnt+2) > BUFSIZE )
                    goto abort;
    
                /* Write to terminal */
                pti->tterm[pti->ttermcnt++] = data;
    
    #if !APP_ECHO
                /* Echo back to network */
                if( IS_WILL(OPT_ECHO) )
                    pti->tnet[pti->tnetcnt++] = data;
    #endif
    
                if( data == '\r' && IS_DONT(OPT_BINARY) )
                    pti->InputState = TOK_CR;
                break;
    
            case TOK_IAC:
                switch( data )
                {
                case CMD_IAC:
                    /* Copy the data to the output buffer */
    
                    /* Must be able to fit in output buffer */
                    if( (pti->ttermcnt+2) > BUFSIZE )
                        goto abort;
    
                    /* If echoing, the character must also fit in tnetcnt */
                    if( IS_WILL(OPT_ECHO) && (pti->tnetcnt+2) > BUFSIZE )
                        goto abort;
    
                    /* Write to terminal */
                    pti->tterm[pti->ttermcnt++] = data;
    
    #if !APP_ECHO
                    /* Echo double sequence back to network */
                    if( IS_WILL(OPT_ECHO) )
                    {
                        pti->tnet[pti->tnetcnt++] = data;
                        pti->tnet[pti->tnetcnt++] = data;
                    }
    #endif
    
                    pti->InputState = TOK_NORMAL;
                    break;
    
                case CMD_AYT:
                    if( !CmdWriteBytes( pti, (unsigned char *)"YES", 3 ) )
                        goto abort;
                    pti->InputState = TOK_NORMAL;
                    break;
    
                case CMD_SB:
                    pti->InputState = TOK_SB;
                    break;
    
                case CMD_SE:
                    pti->InputState = TOK_NORMAL;
                    break;
    
                case CMD_WILL:
                    pti->InputState = TOK_WILL;
                    break;
    
                case CMD_WONT:
                    pti->InputState = TOK_WONT;
                    break;
    
                case CMD_DO:
                    pti->InputState = TOK_DO;
                    break;
    
                case CMD_DONT:
                    pti->InputState = TOK_DONT;
                    break;
    
                default:
                    pti->InputState = TOK_NORMAL;
                    break;
                }
                break;
    
            case TOK_CR:
                /* If "\r\0", eat the '\0'. Let "\r\n" go through */
                if( data != 0 )
                    pti->fnetridx--;
    #if !APP_ECHO
                else
                {
                    /* Still Echo back to network */
                    if( IS_WILL(OPT_ECHO) )
                        pti->tnet[pti->tnetcnt++] = data;
                }
    #endif
                pti->InputState = TOK_NORMAL;
                break;
    
            case TOK_SB:
                if( data != CMD_IAC )
                    break;
                pti->InputState = TOK_IAC;
                break;
    
            case TOK_WILL:
                /* Other side saying "WILL: something". The only thing */
                /* we agree to turn "on" is SGA & BINARY. We'll ignore */
                /* timing mark since "WILL" is a reply. */
                /* We reply to everything else with DONT */
                if( data == OPT_BINARY || data == OPT_SGA )
                {
                    if( IS_DONT(data) )
                    {
                        if( !CmdWriteCmd( pti, CMD_DO, data ) )
                            goto abort;
                        SET_DO(data);
                    }
                }
                else if( data != OPT_TM )
                {
                    if( !CmdWriteCmd( pti, CMD_DONT, data ) )
                        goto abort;
                }
                pti->InputState = TOK_NORMAL;
                break;
    
            case TOK_WONT:
                /* Other side saying "WONT: something". We don't care */
                /* what it is - we just change the state. */
                if( IS_DO(data) )
                {
                    if( !CmdWriteCmd( pti, CMD_DONT, data ) )
                        goto abort;
                    SET_DONT(data);
                }
                pti->InputState = TOK_NORMAL;
                break;
    
            case TOK_DO:
                /* Other side saying "DO: something". The only thing */
                /* we agree to turn "on" is SGA, BINARY or ECHO. */
                /* We'll always reply to timing mark with "WILL". */
                /* We don't reply to anything else since nothing else */
                /* can possible be in an "on" state */
                if( data == OPT_BINARY || data == OPT_SGA ||
                    data == OPT_ECHO || data == OPT_TM )
                {
                    if( IS_WONT(data) )
                    {
                        if( !CmdWriteCmd( pti, CMD_WILL, data ) )
                            goto abort;
                        if( data != OPT_TM )
                            SET_WILL(data);
                    }
                }
                pti->InputState = TOK_NORMAL;
                break;
    
            case TOK_DONT:
                /* Other side saying "DONT: something". We can turn */
                /* anything "off". */
                if( IS_WILL(data) )
                {
                    if( !CmdWriteCmd( pti, CMD_WONT, data ) )
                        goto abort;
                    SET_WONT(data);
                }
                pti->InputState = TOK_NORMAL;
                break;
    
            default:
                pti->InputState = TOK_NORMAL;
                break;
            }
        }
        pti->fnetcnt = 0;
        return;
    
    abort:
        /* Back up one character */
        pti->fnetridx--;
        return;
    }
    
    static void telnet_termdata( TINSTANCE *pti )
    {
        unsigned char data;
    
        /* Process data from the terminal */
        while( pti->ftermridx < pti->ftermcnt )
        {
            data = pti->fterm[pti->ftermridx++];
    
            /* Must be able to fit in output buffer */
            if( (pti->tnetcnt+2) > BUFSIZE )
                goto abort;
    
            /* Write to network */
    
            /* All '\n' becomes '\r\n' */
            /* All '\r' becomes '\rNUL' */
            /* All [CMD_IAC] becomes [CMD_IAC][CMD_IAC] */
    
            if( data == '\n' && IS_WONT(OPT_BINARY) )
                pti->tnet[pti->tnetcnt++] = '\r';
    
            pti->tnet[pti->tnetcnt++] = data;
    
            if( data == CMD_IAC )
                pti->tnet[pti->tnetcnt++] = CMD_IAC;
    
            if( data == '\r' && IS_WONT(OPT_BINARY) )
                pti->tnet[pti->tnetcnt++] = 0;
        }
    
        pti->ftermcnt = 0;
        return;
    
    abort:
        pti->ftermridx--;
        return;
    }
    

    https://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/keepalive600.pcapng

    Regards, Eric

  • Hey Eric,

    Yes you should be able to see the keep-alive probes in wireshark. It sounds like your keepIdleTime change might not have taken correctly. You can verify the change by inspecting the _ipcfg.TcpKeepIdle value in the ccs expression view. It should match up with your value in the *.cfg file. Additionally you can tell keep-alive probes are happening if you put a breakpoint on TcpTimeoutKeep() (and you hit it).

    If the change is not taking from the *.cfg file you might have some code like the following in the file: 

    var Global    = xdc.useModule('ti.ndk.config.Global');
    Global.enableCodeGeneration = false;

    If so, take it out and try again. If not, you might need to change the value manually in the C code. To do that place this statement somewhere early in your app code. 

    #include <ti/ndk/inc/netmain.h>
    #include <ti/ndk/inc/stkmain.h>
    
    uint32_t tcpKeepAliveInitIdle = 600;
    CfgAddEntry(0, CFGTAG_IP, CFGITEM_IP_TCPKEEPIDLE, CFG_ADDMODE_UNIQUE,
                sizeof(uint32_t), (unsigned char *)&tcpKeepAliveInitIdle, NULL);

    Regards,

    Dalton

  • Dalton,

    Several trials:

    1) this morning build, I looked at _ipcfg.TcpKeepIdle  was 72000, so programmed is not taken.

    2) I changed .cfg with below:

    Then, in the CFG file, I have below:   

    var Tcp = xdc.useModule('ti.ndk.config.Tcp');
    Tcp.keepIdleTime = 600; // 1 minutes. This timer is in units of .1 seconds

    Global.enableCodeGeneration = true;

    I loaded the out into CCS, after go main(), it is still 72000. Then I ran, it changed to 600. But with code generation, it caused other issues to the application:

    [C66xx_0] 

    TCP/IP Stack 'Client!' Application

    ti.sysbios.family.c64p.Hwi: line 194: E_alreadyDefined: Hwi already defined: intr# 10
    00000.000 ExecStart: Already Open
    00000.000 Service Status: DHCPC : Enabled : : 000

    00000.000 Service Status: DHCPC : Enabled : Running : 000

    00009.000 Service Status: DHCPC : Disabled : : 000

    00009.002 mmFree: Double Free
    00009.004 mmFree: Double Free
    00009.005 mmFree: Double Free
    00009.007 mmFree: Double Free
    00009.008 mmFree: Double Free
    00009.009 mmFree: Double Free
    00009.010 mmFree: Double Free
    00009.011

    NC_NetStart: WARNING: Boot thread has not completed!


    00009.017 mmFree: Double Free
    00009.019 mmFree: Double Free
    00009.020 NodeTreeFree: Null endpoints
    00009.022 mmFree: Double Free
    00009.024 mmFree: Double Free

    3) I reverted the change in the .cfg

    //Tcp.keepIdleTime = 600; // 1 minutes. This timer is in units of .1 seconds

    //Global.enableCodeGeneration = true;

    And added below into my application:

    #include <ti/ndk/inc/netmain.h>
    #include <ti/ndk/inc/stkmain.h>
    
    uint32_t tcpKeepAliveInitIdle = 600;
    CfgAddEntry(0, CFGTAG_IP, CFGITEM_IP_TCPKEEPIDLE, CFG_ADDMODE_UNIQUE,
                sizeof(uint32_t), (unsigned char *)&tcpKeepAliveInitIdle, NULL);

    This is executed but the value is still 72000 in expression window.

    Regards, Eric

     

  • Dalton,

    Attached the wireshark where a reset from PC caused the telnet drop.

    Regards, Erichttps://e2e.ti.com/cfs-file/__key/communityserver-discussions-components-files/791/keepalive600_5F00_drop.pcapng 

  • Hey Eric,

    Is the customer still experiencing this issue? Did the tcp keep-alive probes fix it for them?

    Regards,

    Dalton