Hi i'm having some serious trouble developing a modbus server in lwip using
raw api, basically i got the server answering the requests, the problem is
that seemingly at random i get the following

mem_free: illegal memory: non-linked: double freemem_free: illegal
memory: non-linked: double free

The interesting part is that it seems to do so when closing a
connection due to an error if im not mistaken
i put a breakpoint where the message is produced and got the following
call stack

mem_free (void * rmem) Line: 717
pbuf_free (struct pbuf * p) Line: 780
tcp_seg_free (struct tcp_seg * seg) Line: 1631
tcp_segs_free (struct tcp_seg * seg) Line: 1616
tcp_pcb_purge (struct tcp_pcb * pcb, struct tcp_pcb * pcb@entry) Line: 2169
tcp_pcb_purge (struct tcp_pcb * pcb) Line: 2139
tcp_pcb_remove (struct tcp_pcb ** pcblist, struct tcp_pcb * pcb,
struct tcp_pcb * pcb@entry) Line: 2191
tcp_input (struct pbuf * p, struct pbuf * p@entry, struct netif * inp,
struct netif * inp@entry) Line: 448
ip4_input (struct pbuf * p, struct pbuf * p@entry, struct netif * inp,
struct netif * inp@entry) Line: 709
ethernet_input (struct pbuf * p, struct netif * netif) Line: 186
ethernetif_input (struct netif * netif) Line: 788
ethernet_task Line: 247
main Line: 929

so maybe this is related to lwipopts rather than the app?

the lwipopts file is the following


#define LWIP_NETIF_HOSTNAME        1#define LWIP_NETIF_API
0#define LWIP_NETIF_STATUS_CALLBACK 1#define LWIP_NETIF_LINK_CALLBACK
 1
#define LWIP_SOCKET  0#define LWIP_NETCONN 0
#define SO_REUSE     1#define LWIP_IGMP    1
//#include <stmlib/rand.h>//#define LWIP_RAND    rand_value
#define LWIP_STATS         1#define LWIP_STATS_DISPLAY 1
extern int printf(const char *format, ...);#define
LWIP_PLATFORM_DIAG(x) {printf x;}#define LWIP_DEBUG
#define LWIP_NOASSERT
#define LWIP_DBG_TYPES_ON               (LWIP_DBG_STATE |
LWIP_DBG_ON)#define ETHARP_DEBUG
LWIP_DBG_OFF#define NETIF_DEBUG
LWIP_DBG_OFF#define DHCP_DEBUG
LWIP_DBG_OFF#define AUTOIP_DEBUG
LWIP_DBG_OFF#define IGMP_DEBUG
LWIP_DBG_OFF#define IP_DEBUG
LWIP_DBG_OFF#define PBUF_DEBUG
LWIP_DBG_LEVEL_SERIOUS#define MEM_DEBUG
LWIP_DBG_LEVEL_SERIOUS#define SIMPLE_DISCOVERY_DEBUG
LWIP_DBG_LEVEL_SERIOUS
#define TCP_TMR_INTERVAL    10

attached are the application files
if anyone can provide any lead i would be really grateful since i've
been stuck with this for a while and i don't think i know the innards
of lwip good enough to track this

modbusBal.h is the one that handles the queued packets


also i know this code isn't really clean and tidy, this is my first
lwip app and initially i just want to get it working more or less
reliably

best, diego
#ifndef MODBUSTCP_H_
#define MODBUSTCP_H_

#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/tcp.h"
#include "../CONTROL_CONCURRENCIA/queue.h"
#include "../MEM_MGT/mempools.h"

#define MTCP_MAX_RETRIES        10
#define MTCP_QUEUE              modbusTcpQueue
#define MTCP_QUEUE_BUF          *mQueueBuffer[18]
#define MTCP_MEMP               modbusTcpMemp

#ifndef MTCP_MAX_CONNS
#define MTCP_MAX_CONNS  5
#endif

extern  queue_t MTCP_QUEUE;
extern  void    MTCP_QUEUE_BUF;
extern  mempool_config  MTCP_MEMP;

enum conn_status
{
    MB_NONE = 0,
    MB_CONNECTED,
    MB_WAIT_TO_CLOSE,
    MB_CLOSING
};

typedef struct
{
    uint16_t    transactionId;
    uint16_t    protocol;
    uint16_t    len;
    uint8_t     unitId;
} mbTcp_mbapHeader;

#define MBAP_PROTO_MB   0

typedef struct
{
    uint8_t estado;
    uint8_t retries;
    struct tcp_pcb *pcb;
    uint8_t *sendP;
    uint8_t sendLen;
} mbTcp_status;

void    modbusTcpStart(uint16_t port);
void    modbusTcpStop(void);

err_t   modbusTcpAccept(void *arg, struct tcp_pcb *pcb, err_t err);

err_t   modbusTcpRecv(void *arg, struct tcp_pcb *mPcb, struct pbuf *p, err_t err);
void    modbusTcpError(void *arg, err_t err);
err_t   modbusTcpPoll(void *arg, struct tcp_pcb *mPcb);
void    modbusTcpSend(struct tcp_pcb *mPcb, mbTcp_status *mbStatus);
err_t   modbusTcpSent(void *arg, struct tcp_pcb *pcb, u16_t len);
void    modbusTcpClose(struct tcp_pcb *pcb, mbTcp_status *mbStatus);

uint16_t    swapBytes(uint8_t *ptr);

extern  uint8_t mTcpCallGood(mbTcp_status *status, struct pbuf *p, mbTcp_mbapHeader hdr)

#endif
*
 * modbusTcp.c
 *
 * Created: 19/08/2019 14:05:11
 *  Author: diego
 */
#include "modbusTcp.h"

uint8_t actConns = 0;

uint16_t    swapBytes(uint8_t *ptr)
{
    uint16_t    out = ((uint16_t) * (ptr + 1)) | ((uint16_t) * (ptr) << 8);
    return out;
}

void    modbusTcpStart(uint16_t port)
{
    volatile err_t err;

    struct tcp_pcb *pcb;

    pcb = tcp_new();
    //LWIP_ASSERT("falla al crear el pcb de modbus tcp", pcb != NULL);

    if(pcb != NULL)
    {
        err = tcp_bind(pcb, IP_ADDR_ANY, port);
        //tcp_setprio(pcb, MODBUS_TCP_PRIO);
        //LWIP_ASSERT("Falla al iniciar modbus TCP (bind)", err == ERR_OK);

        if(err == ERR_OK)
        {
            pcb = tcp_listen(pcb);
            //LWIP_ASSERT("Falla al iniciar modbus TCP (listen)", err == ERR_OK);
            tcp_accept(pcb, modbusTcpAccept);
            return;
        }
    }
    else
        return;
}

err_t   modbusTcpAccept(void *arg, struct tcp_pcb *mPcb, err_t err)
{
    err_t ret_err;
    mbTcp_status *mbStatus;

    LWIP_UNUSED_ARG(arg);
    LWIP_UNUSED_ARG(err);

    if(actConns >= MTCP_MAX_CONNS)
    {
        return ERR_MEM;
    }
    else
    {
        actConns++;
    }

    tcp_setprio(mPcb, TCP_PRIO_MAX);

    mbStatus = (mbTcp_status *)mem_malloc(sizeof(mbTcp_status));

    if(mbStatus != NULL)
    {
        mbStatus->estado = MB_CONNECTED;
        mbStatus->pcb = mPcb;
        mbStatus->retries = 0;
        mbStatus->sendP = NULL;
        /* pass newly allocated es to our callbacks */
        tcp_arg(mPcb, mbStatus);
        tcp_recv(mPcb, modbusTcpRecv);
        tcp_err(mPcb, modbusTcpError);
        tcp_poll(mPcb, modbusTcpPoll, 0);
        ret_err = ERR_OK;
    }
    else
    {
        ret_err = ERR_MEM;
    }

    return ret_err;
}


err_t   modbusTcpRecv(void *arg, struct tcp_pcb *mPcb, struct pbuf *p, err_t err)
{

    mbTcp_status *mbStatus;
    volatile    mbTcp_mbapHeader    headerTmp;

    mbStatus = (mbTcp_status *)arg;
    uint8_t *payloadPtr     =   (uint8_t *) p->payload;

    //tcp_output(mPcb);

    if(p == NULL)
    {
        if(mbStatus->sendP == NULL)
        {
            if(getQueueLength(&MTCP_QUEUE) == 0)
            {
                // no quedan respuestas para mandar
                // ni bloques que procesar
                modbusTcpClose(mPcb, (mbTcp_status *)arg);
            }
            else
            {
                mbStatus->estado = MB_WAIT_TO_CLOSE;
                //pbuf_free(p);
            }

            // esto es que queda algo para procesar en la cola
            // la conexion se cerrara luego de que se procesen los que quedan
            // simplemente se descarta el entrante

        }
        else
        {
            // queda algo para mandar
            // asi que lo mandamos
            tcp_sent(mPcb, modbusTcpSent);
            //modbusTcpSend(mPcb, mbStatus);
        }
    }
    else
        if(err != ERR_OK)
        {
            mbStatus->sendP = NULL;
            tcp_recved(mPcb, p->tot_len);
            pbuf_free(p);
            return  err;
        }
        else
        {
            switch(mbStatus->estado)
            {
                case MB_CONNECTED:
                    if(p->len == 0)
                    {
                        tcp_recved(mPcb, p->tot_len);
                        pbuf_free(p);
                        break;
                    }

                    headerTmp.transactionId =   swapBytes(payloadPtr);
                    headerTmp.protocol      =   swapBytes(payloadPtr + 2);
                    headerTmp.len           =   swapBytes(payloadPtr + 4);
                    headerTmp.unitId        =   *(payloadPtr + 6);


                    if(mTcpCallGood(mbStatus, p, headerTmp) != 0)
                    {
                        if(p != NULL)
                        {
                            pbuf_free(p);
                        }

                        tcp_recved(mPcb, p->tot_len);
                        break;
                    }

                    // asumimos que los chequeos y demas fueron realizados por el stack

                    break;

                case MB_WAIT_TO_CLOSE:
                    // notese que no ponemos mbStatus->p a NULL
                    tcp_recved(mPcb, p->tot_len);
                    pbuf_free(p);
                    return ERR_OK;
                    break;

                case MB_CLOSING:
                    tcp_recved(mPcb, p->tot_len);
                    mbStatus->sendP = NULL;
                    pbuf_free(p);
                    return ERR_OK;
                    break;

                default:
                    tcp_recved(mPcb, p->tot_len);
                    mbStatus->sendP = NULL;
                    pbuf_free(p);
                    return ERR_OK;
                    break;
            }
        }
}

void    modbusTcpError(void *arg, err_t err)
{
    mbTcp_status *mbStatus;

    LWIP_UNUSED_ARG(err);

    mbStatus = (mbTcp_status *)arg;



    tcp_arg(mbStatus->pcb, NULL);
    tcp_sent(mbStatus->pcb, NULL);
    tcp_recv(mbStatus->pcb, NULL);
    tcp_err(mbStatus->pcb, NULL);
    tcp_poll(mbStatus->pcb, NULL, 0);
    tcp_close(mbStatus->pcb);

    /*if(mbStatus != NULL)
    {
        mem_free(mbStatus);
    }*/
}

void    modbusTcpSend(struct tcp_pcb *mPcb, mbTcp_status *mbStatus)
{
    err_t wr_err = ERR_OK;

    while((wr_err == ERR_OK) &&
            (mbStatus->sendP != NULL) &&
            (mbStatus->sendLen <= tcp_sndbuf(mPcb))
         )
    {
        wr_err = tcp_write(mPcb, mbStatus->sendP, mbStatus->sendLen, 1);

        if(wr_err == ERR_OK)
        {
            mbStatus->sendLen = 0;
            break;
        }
        else
            if(wr_err == ERR_MEM)
            {
                // por ahora simplemente no mandamos nada
                // idealmente seria defer to poll
            }
    }

    return;
}

err_t   modbusTcpPoll(void *arg, struct tcp_pcb *mPcb)
{

}

void    modbusTcpClose(struct tcp_pcb *mPcb, mbTcp_status *mbStatus)
{
    tcp_arg(mPcb, NULL);
    tcp_sent(mPcb, NULL);
    tcp_recv(mPcb, NULL);
    tcp_err(mPcb, NULL);
    tcp_poll(mPcb, NULL, 0);

    if(mbStatus != NULL)
    {
        mem_free(mbStatus);
    }

    tcp_close(mPcb);
    actConns--;
}

err_t   modbusTcpSent(void *arg, struct tcp_pcb *mPcb, u16_t len)
{
    mbTcp_status *mbStatus;

    mbStatus = (mbTcp_status *)arg;

    LWIP_UNUSED_ARG(len);

    mbStatus->retries = 0;

    if(mbStatus->estado == MB_WAIT_TO_CLOSE)
    {
        if(getQueueLength(&MTCP_QUEUE) == 0)
        {
            // no quedan respuestas para mandar
            // ni bloques que procesar
            modbusTcpClose(mPcb, (mbTcp_status *)arg);
        }
    }

    // no quedan respuestas para mandar
    // y ya no habia nada en la cola
    if(mbStatus->estado == MB_CLOSING)
    {
        modbusTcpClose(mPcb, mbStatus);
    }

    return ERR_OK;
}
*
 * modbusTcp.c
 *
 * Created: 19/08/2019 14:05:11
 *  Author: diego
 */
#include "modbusTcp.h"

uint8_t actConns = 0;

uint16_t    swapBytes(uint8_t *ptr)
{
    uint16_t    out = ((uint16_t) * (ptr + 1)) | ((uint16_t) * (ptr) << 8);
    return out;
}

void    modbusTcpStart(uint16_t port)
{
    volatile err_t err;

    struct tcp_pcb *pcb;

    pcb = tcp_new();
    //LWIP_ASSERT("falla al crear el pcb de modbus tcp", pcb != NULL);

    if(pcb != NULL)
    {
        err = tcp_bind(pcb, IP_ADDR_ANY, port);
        //tcp_setprio(pcb, MODBUS_TCP_PRIO);
        //LWIP_ASSERT("Falla al iniciar modbus TCP (bind)", err == ERR_OK);

        if(err == ERR_OK)
        {
            pcb = tcp_listen(pcb);
            //LWIP_ASSERT("Falla al iniciar modbus TCP (listen)", err == ERR_OK);
            tcp_accept(pcb, modbusTcpAccept);
            return;
        }
    }
    else
        return;
}

err_t   modbusTcpAccept(void *arg, struct tcp_pcb *mPcb, err_t err)
{
    err_t ret_err;
    mbTcp_status *mbStatus;

    LWIP_UNUSED_ARG(arg);
    LWIP_UNUSED_ARG(err);

    if(actConns >= MTCP_MAX_CONNS)
    {
        return ERR_MEM;
    }
    else
    {
        actConns++;
    }

    tcp_setprio(mPcb, TCP_PRIO_MAX);

    mbStatus = (mbTcp_status *)mem_malloc(sizeof(mbTcp_status));

    if(mbStatus != NULL)
    {
        mbStatus->estado = MB_CONNECTED;
        mbStatus->pcb = mPcb;
        mbStatus->retries = 0;
        mbStatus->sendP = NULL;
        /* pass newly allocated es to our callbacks */
        tcp_arg(mPcb, mbStatus);
        tcp_recv(mPcb, modbusTcpRecv);
        tcp_err(mPcb, modbusTcpError);
        tcp_poll(mPcb, modbusTcpPoll, 0);
        ret_err = ERR_OK;
    }
    else
    {
        ret_err = ERR_MEM;
    }

    return ret_err;
}


err_t   modbusTcpRecv(void *arg, struct tcp_pcb *mPcb, struct pbuf *p, err_t err)
{

    mbTcp_status *mbStatus;
    volatile    mbTcp_mbapHeader    headerTmp;

    mbStatus = (mbTcp_status *)arg;
    uint8_t *payloadPtr     =   (uint8_t *) p->payload;

    //tcp_output(mPcb);

    if(p == NULL)
    {
        if(mbStatus->sendP == NULL)
        {
            if(getQueueLength(&MTCP_QUEUE) == 0)
            {
                // no quedan respuestas para mandar
                // ni bloques que procesar
                modbusTcpClose(mPcb, (mbTcp_status *)arg);
            }
            else
            {
                mbStatus->estado = MB_WAIT_TO_CLOSE;
                //pbuf_free(p);
            }

            // esto es que queda algo para procesar en la cola
            // la conexion se cerrara luego de que se procesen los que quedan
            // simplemente se descarta el entrante

        }
        else
        {
            // queda algo para mandar
            // asi que lo mandamos
            tcp_sent(mPcb, modbusTcpSent);
            //modbusTcpSend(mPcb, mbStatus);
        }
    }
    else
        if(err != ERR_OK)
        {
            mbStatus->sendP = NULL;
            tcp_recved(mPcb, p->tot_len);
            pbuf_free(p);
            return  err;
        }
        else
        {
            switch(mbStatus->estado)
            {
                case MB_CONNECTED:
                    if(p->len == 0)
                    {
                        tcp_recved(mPcb, p->tot_len);
                        pbuf_free(p);
                        break;
                    }

                    headerTmp.transactionId =   swapBytes(payloadPtr);
                    headerTmp.protocol      =   swapBytes(payloadPtr + 2);
                    headerTmp.len           =   swapBytes(payloadPtr + 4);
                    headerTmp.unitId        =   *(payloadPtr + 6);


                    if(mTcpCallGood(mbStatus, p, headerTmp) != 0)
                    {
                        if(p != NULL)
                        {
                            pbuf_free(p);
                        }

                        tcp_recved(mPcb, p->tot_len);
                        break;
                    }

                    // asumimos que los chequeos y demas fueron realizados por el stack

                    break;

                case MB_WAIT_TO_CLOSE:
                    // notese que no ponemos mbStatus->p a NULL
                    tcp_recved(mPcb, p->tot_len);
                    pbuf_free(p);
                    return ERR_OK;
                    break;

                case MB_CLOSING:
                    tcp_recved(mPcb, p->tot_len);
                    mbStatus->sendP = NULL;
                    pbuf_free(p);
                    return ERR_OK;
                    break;

                default:
                    tcp_recved(mPcb, p->tot_len);
                    mbStatus->sendP = NULL;
                    pbuf_free(p);
                    return ERR_OK;
                    break;
            }
        }
}

void    modbusTcpError(void *arg, err_t err)
{
    mbTcp_status *mbStatus;

    LWIP_UNUSED_ARG(err);

    mbStatus = (mbTcp_status *)arg;



    tcp_arg(mbStatus->pcb, NULL);
    tcp_sent(mbStatus->pcb, NULL);
    tcp_recv(mbStatus->pcb, NULL);
    tcp_err(mbStatus->pcb, NULL);
    tcp_poll(mbStatus->pcb, NULL, 0);
    tcp_close(mbStatus->pcb);

    /*if(mbStatus != NULL)
    {
        mem_free(mbStatus);
    }*/
}

void    modbusTcpSend(struct tcp_pcb *mPcb, mbTcp_status *mbStatus)
{
    err_t wr_err = ERR_OK;

    while((wr_err == ERR_OK) &&
            (mbStatus->sendP != NULL) &&
            (mbStatus->sendLen <= tcp_sndbuf(mPcb))
         )
    {
        wr_err = tcp_write(mPcb, mbStatus->sendP, mbStatus->sendLen, 1);

        if(wr_err == ERR_OK)
        {
            mbStatus->sendLen = 0;
            break;
        }
        else
            if(wr_err == ERR_MEM)
            {
                // por ahora simplemente no mandamos nada
                // idealmente seria defer to poll
            }
    }

    return;
}

err_t   modbusTcpPoll(void *arg, struct tcp_pcb *mPcb)
{

}

void    modbusTcpClose(struct tcp_pcb *mPcb, mbTcp_status *mbStatus)
{
    tcp_arg(mPcb, NULL);
    tcp_sent(mPcb, NULL);
    tcp_recv(mPcb, NULL);
    tcp_err(mPcb, NULL);
    tcp_poll(mPcb, NULL, 0);

    if(mbStatus != NULL)
    {
        mem_free(mbStatus);
    }

    tcp_close(mPcb);
    actConns--;
}

err_t   modbusTcpSent(void *arg, struct tcp_pcb *mPcb, u16_t len)
{
    mbTcp_status *mbStatus;

    mbStatus = (mbTcp_status *)arg;

    LWIP_UNUSED_ARG(len);

    mbStatus->retries = 0;

    if(mbStatus->estado == MB_WAIT_TO_CLOSE)
    {
        if(getQueueLength(&MTCP_QUEUE) == 0)
        {
            // no quedan respuestas para mandar
            // ni bloques que procesar
            modbusTcpClose(mPcb, (mbTcp_status *)arg);
        }
    }

    // no quedan respuestas para mandar
    // y ya no habia nada en la cola
    if(mbStatus->estado == MB_CLOSING)
    {
        modbusTcpClose(mPcb, mbStatus);
    }

    return ERR_OK;
}
_______________________________________________
lwip-users mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/lwip-users

Reply via email to