TCP retransmissions although packet has been recieved

classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

TCP retransmissions although packet has been recieved

inderjit
Hi,

I have the following configuration:

H/W
    ATMEL SAM4E ARM Cortex-M4 with KSZ8081MNXRNB Ethernet phy.
LWIP:
    1.4.1
    Config (extract. See attached file for full config):
        #define NO_SYS                          1
        #define MEMP_NUM_TCP_PCB                2
        #define MEMP_NUM_TCP_PCB_LISTEN         1
        #define MEMP_NUM_TCP_SEG                8

Problem:
I am sending HTTP/1.1 packets to server and receiving replies as expected. But it seems that the server keeps sending re-transmissions as local hardware doesn't seem to send an ACK back (3-way handshake?). After some time the server resets the connection and my next http request get's blocked. Should LWIP layer send an ACK back as the app has received the packet?
pcap capture is attatched.

Here is the code snippet when packets is received:

err_t tcp_tx_resp_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    struct pbuf* temp_p;
    eth_ctx_t* ctx = (eth_ctx_t*)arg;
    uint16_t len;

    if(err < 0)
    {
        DBG_W(EINVAL, "Ignoring response err '%d'. err");
        return err;
    }

    len = 0;
    /*        retrieve data */
    if ((err == ERR_OK) && (p != NULL)) {
        /* get response status value */
        ctx->status = http_get_status(p->payload);

        while (p != NULL) {
            if (ctx->recv_buf != NULL) {
                memcpy(&ctx->recv_buf[len], p->payload, p->len);
            }

            temp_p = p->next;
            len += p->len;
            pbuf_free(p);
            p = temp_p;
        }
        /* Terminate it for parsing it more easily */
        ctx->recv_buf[len] = 0;

        if (ctx->recv_buf != NULL) {
            ctx->recv_size = http_get_data(ctx->recv_buf, ctx->recv_buf);
        }

        DBG_I("TCP RESPONSE:  status:%03d. data_size: %d bytes. err: %d.",
              ctx->status, ctx->recv_size, err);
    }

    return ERR_OK;
}

Tanks in advance,
Inderjit

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users

lwipopts.h (14K) Download Attachment
Timout_filtered.pcapng (8K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

Jens Nielsen
Hi

I'll answer the standard reply to this question :)  You're saying that "the app has received the packet" but are you really sure about that? A typical cause for this problem is that the driver stops receiving and/or forwarding packets to lwip properly, lwip can't ack what lwip doesn't receive.

And 1.4.1 is pretty old, you should upgrade...

Best regards
Jens

On 2018-10-02 11:45, Inderjit Singh wrote:
Hi,

I have the following configuration:

H/W
    ATMEL SAM4E ARM Cortex-M4 with KSZ8081MNXRNB Ethernet phy.
LWIP:
    1.4.1
    Config (extract. See attached file for full config):
        #define NO_SYS                          1
        #define MEMP_NUM_TCP_PCB                2
        #define MEMP_NUM_TCP_PCB_LISTEN         1
        #define MEMP_NUM_TCP_SEG                8

Problem:
I am sending HTTP/1.1 packets to server and receiving replies as expected. But it seems that the server keeps sending re-transmissions as local hardware doesn't seem to send an ACK back (3-way handshake?). After some time the server resets the connection and my next http request get's blocked. Should LWIP layer send an ACK back as the app has received the packet?
pcap capture is attatched.

Here is the code snippet when packets is received:

err_t tcp_tx_resp_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    struct pbuf* temp_p;
    eth_ctx_t* ctx = (eth_ctx_t*)arg;
    uint16_t len;

    if(err < 0)
    {
        DBG_W(EINVAL, "Ignoring response err '%d'. err");
        return err;
    }

    len = 0;
    /*        retrieve data */
    if ((err == ERR_OK) && (p != NULL)) {
        /* get response status value */
        ctx->status = http_get_status(p->payload);

        while (p != NULL) {
            if (ctx->recv_buf != NULL) {
                memcpy(&ctx->recv_buf[len], p->payload, p->len);
            }

            temp_p = p->next;
            len += p->len;
            pbuf_free(p);
            p = temp_p;
        }
        /* Terminate it for parsing it more easily */
        ctx->recv_buf[len] = 0;

        if (ctx->recv_buf != NULL) {
            ctx->recv_size = http_get_data(ctx->recv_buf, ctx->recv_buf);
        }

        DBG_I("TCP RESPONSE:  status:%03d. data_size: %d bytes. err: %d.",
              ctx->status, ctx->recv_size, err);
    }

    return ERR_OK;
}

Tanks in advance,
Inderjit


_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users


_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

goldsimon@gmx.de
In reply to this post by inderjit
In addition to the correct answer from Jens:

On 02.10.2018 11:45, Inderjit Singh wrote:
[..]

        while (p != NULL) {
            if (ctx->recv_buf != NULL) {
                memcpy(&ctx->recv_buf[len], p->payload, p->len);
            }

            temp_p = p->next;
            len += p->len;
            pbuf_free(p);
            p = temp_p;
        }

This loop is wrong. You're double-freeing many pbufs here!


Simon
_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

inderjit
Simon,

Can you please elaborate? I'm following example code from:
https://github.com/kennethnoyens/lwipHttpClient/blob/master/httpclient.c
<https://github.com/kennethnoyens/lwipHttpClient/blob/master/httpclient.c>  

struct pbuf is a linked list and referring to the current element at top at
that list should free only that element. That's how pbuf_free is defined (or
am i totally out of understanding?)
/**
 * Dereference a pbuf chain or queue and deallocate any no-longer-used
 * pbufs at the head of this chain or queue.
 *
 * Decrements the pbuf reference count. If it reaches zero, the pbuf is
 * deallocated.
 *
 * For a pbuf chain, this is repeated for each pbuf in the chain,
 * up to the first pbuf which has a non-zero reference count after
 * decrementing. So, when all reference counts are one, the whole
 * chain is free'd.
 *
 * @param p The pbuf (chain) to be dereferenced.
 *
 * @return the number of pbufs that were de-allocated
 * from the head of the chain.
 *
 * @note MUST NOT be called on a packet queue (Not verified to work yet).
 * @note the reference counter of a pbuf equals the number of pointers
 * that refer to the pbuf (or into the pbuf).
 *
 * @internal examples:
 *
 * Assuming existing chains a->b->c with the following reference
 * counts, calling pbuf_free(a) results in:
 *
 * 1->2->3 becomes ...1->3
 * 3->3->3 becomes 2->3->3
 * 1->1->2 becomes ......1
 * 2->1->1 becomes 1->1->1
 * 1->1->1 becomes .......
 *
 */






--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

inderjit
In reply to this post by Jens Nielsen
Jens,

I'm using 1.4.1 as there is no support for the PHY from ATMEL on 2.x.x. AFA=
IK anyway as I had hard time integrating it.

I followed this code reference:  httpclient.c
<https://github.com/kennethnoyens/lwipHttpCl= ient/blob/master/httpclient.c>  

One thing I missed was this:
tcp_recved(pcb, p->tot_len);

This could explain ACK was not sent.

But looking at the pcap transactions, and in app as I am receiving the data=
, the driver does not seem to be the problem AFAICT. The packet is sent (lo=
oking at pcap), but no response from server is received. And a RST is trigg=
ered by the server just before the last HTTP request is sent (ACK not sent =
previously). all othe rprevious HTTP requests' responses are received.

I am trying to determine this issue with tcp_recved call but haven't resolv=
ed it yet. No change in behaviour yet.



--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

inderjit
In reply to this post by Jens Nielsen
Jens,

I'm using 1.4.1 as there is no support for the PHY from ATMEL on 2.x.x. AFA=
IK anyway as I had hard time integrating it.

I followed this code reference:  httpclient.c
<https://github.com/kennethnoyens/lwipHttpCl= ient/blob/master/httpclient.c>  

One thing I missed was this:
tcp_recved(pcb, p->tot_len);

This could explain ACK was not sent.

But looking at the pcap transactions, and in app as I am receiving the data=
, the driver does not seem to be the problem AFAICT. The packet is sent (lo=
oking at pcap), but no response from server is received. And a RST is trigg=
ered by the server just before the last HTTP request is sent (ACK not sent =
previously). all othe rprevious HTTP requests' responses are received.

I am trying to determine this issue with tcp_recved call but haven't resolv=
ed it yet. No change in behaviour yet.



--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

goldsimon@gmx.de
In reply to this post by inderjit
On 03.10.2018 09:39, inderjit wrote:
> Simon,
>
> Can you please elaborate? I'm following example code from:
> https://github.com/kennethnoyens/lwipHttpClient/blob/master/httpclient.c
> <https://github.com/kennethnoyens/lwipHttpClient/blob/master/httpclient.c>

Well, that code is wrong, too. Why don't you take our code as example
instead of (obviously broken) third party code?

Calling pbuf_free() on 'p' is enough. All chained pbufs are freed with
that call.

> struct pbuf is a linked list and referring to the current element at top at
> that list should free only that element. That's how pbuf_free is defined (or
> am i totally out of understanding?)
> /**
>   * Dereference a pbuf chain or queue and deallocate any no-longer-used
>   * pbufs at the head of this chain or queue.
>   *
>   * Decrements the pbuf reference count. If it reaches zero, the pbuf is
>   * deallocated.
>   *
>   * For a pbuf chain, this is repeated for each pbuf in the chain,
>   * up to the first pbuf which has a non-zero reference count after
>   * decrementing. So, when all reference counts are one, the whole
>   * chain is free'd.
>   *
>   * @param p The pbuf (chain) to be dereferenced.
>   *
>   * @return the number of pbufs that were de-allocated
>   * from the head of the chain.
>   *
>   * @note MUST NOT be called on a packet queue (Not verified to work yet).
>   * @note the reference counter of a pbuf equals the number of pointers
>   * that refer to the pbuf (or into the pbuf).
>   *
>   * @internal examples:
>   *
>   * Assuming existing chains a->b->c with the following reference
>   * counts, calling pbuf_free(a) results in:
>   *
>   * 1->2->3 becomes ...1->3
>   * 3->3->3 becomes 2->3->3
>   * 1->1->2 becomes ......1
>   * 2->1->1 becomes 1->1->1
>   * 1->1->1 becomes .......
>   *
>   */

What's the purpose of pasting this whole comment block here?

Simon

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

goldsimon@gmx.de
In reply to this post by inderjit
On 03.10.2018 11:25, inderjit wrote:
> [..]
>
> One thing I missed was this:
> tcp_recved(pcb, p->tot_len);
>
> This could explain ACK was not sent.

No, ACK is sent implicitly, tcp_recved() only controls window updates.


Simon

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

inderjit
In reply to this post by goldsimon@gmx.de
<quote Well, that code is wrong, too. Why don't you take our code as example
instead of (obviously broken) third party code? &lt;/quote>



Fair enough. I'll revise the code from example code and get back.

Thanks!




--
Sent from: http://lwip.100.n7.nabble.com/lwip-users-f3.html

_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users
Reply | Threaded
Open this post in threaded view
|

Re: TCP retransmissions although packet has been recieved

inderjit
In reply to this post by inderjit
So,

I have updated reception code (from the contrib examples httpd_raw) and still the behaviour is the same (code at bottom).

I did a quick and ugly test by calling tcp_ack_now and tcp_output at the end of the function and then the behaviour dramatically improved as ACKs are being sent back and there is no RST from server (as expected).
I might also add that each http request I am sending is a blocking call (and awaits a response) with timeout at application level. So practically I am only handling one full transaction at a time. The transactions are deterministic and small in size.

So this for me boils down to TCP_WND setting. The http requests I'm sending is roughly at 200 bytes and reception is in equal side so they are fairly small. Is there a guidance on how to set LWIPOPTS variables regarding TCP for this kind of behaviour? Right now my settings are:
LWIPOPTS.H
#define TCP_MSS                            1460

/**
 * TCP_WND: The size of a TCP window.  This must be at least
 * (2 * TCP_MSS) for things to work well
 */
#define TCP_WND                            (2 * TCP_MSS)

/**
 * TCP_SND_BUF: TCP sender buffer space (bytes).
 * To achieve good performance, this should be at least 2 * TCP_MSS.
 */
#define TCP_SND_BUF                        (2 * TCP_MSS)

Reception Code:
err_t tcp_tx_resp_cb(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
{
    eth_ctx_t* ctx = (eth_ctx_t*)arg;
    uint8_t* data;

    if ((err != ERR_OK) || (p == NULL)) {

        DBG_W(EINVAL, "Ignoring response err '%d'.", err);

        /* error or closed by other side? */
        if (p != NULL) {
            /* Inform TCP that we have taken the data. */
            tcp_recved(tpcb, p->tot_len);
            pbuf_free(p);
        }

        tcp_close(tpcb);
        return ERR_OK;
    }

    if (p->len != p->tot_len) {
        DBG_E(ECOMM, "Received message is incomplete %d<%d.",
p->len, p->tot_len);
    }

    /* Acknowledge that we have received the packet */
    tcp_recved(pcb_tcp, p->tot_len);

    if (ctx->recv_buf == NULL){
        DBG_W(EINVAL, "Cannot transfer received buffer. No buffer given.");

        return ERR_OK;
    }

    /*** Parse data ***/

    if(p != NULL) {
        pbuf_free(p);
    }
   
    return ERR_OK;
}


_______________________________________________
lwip-users mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/lwip-users