zephyr/drivers/wifi/eswifi/eswifi_socket.c
Léo BRIAND cf0c2a5275 drivers: wifi: eswifi: Fix memory buffer allocation in off_read_work
When receiving data over the eswifi module, we currently read the data
first, then allocate a buffer, and finally write the data into the
buffer. The issue is that if we can't allocate the buffer, the data
that was read is lost. To fix this, we should first attempt to allocate
the buffer before reading any data. If we can't allocate the buffer, we
should not proceed with reading the data. By allocating a buffer with
the MTU size, we can read the packet, write it into the allocated buffer
and then resize by removing unused allocated buffer with
net_pkt_trim_buffer().

Signed-off-by: Léo BRIAND <leo.briand@smile.fr>
2024-08-02 18:42:53 -05:00

371 lines
8.3 KiB
C

/*
* Copyright (c) 2019 Linumiz
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "eswifi_log.h"
LOG_MODULE_DECLARE(LOG_MODULE_NAME);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "eswifi.h"
#include <zephyr/net/net_pkt.h>
int eswifi_socket_type_from_zephyr(int proto, enum eswifi_transport_type *type)
{
if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS) &&
proto >= IPPROTO_TLS_1_0 && proto <= IPPROTO_TLS_1_2) {
*type = ESWIFI_TRANSPORT_TCP_SSL;
} else if (proto == IPPROTO_TCP) {
*type = ESWIFI_TRANSPORT_TCP;
} else if (proto == IPPROTO_UDP) {
*type = ESWIFI_TRANSPORT_UDP;
} else {
return -EPFNOSUPPORT;
}
return 0;
}
static int __stop_socket(struct eswifi_dev *eswifi,
struct eswifi_off_socket *socket)
{
char cmd_srv[] = "P5=0\r";
char cmd_cli[] = "P6=0\r";
LOG_DBG("Stopping socket %d", socket->index);
if (socket->state != ESWIFI_SOCKET_STATE_CONNECTED) {
return 0;
}
socket->state = ESWIFI_SOCKET_STATE_NONE;
return eswifi_at_cmd(eswifi, socket->is_server ? cmd_srv : cmd_cli);
}
static int __read_data(struct eswifi_dev *eswifi, size_t len, char **data)
{
char cmd[] = "R0\r";
char size[] = "R1=9999\r";
char timeout[] = "R2=30000\r";
int ret;
/* Set max read size */
snprintk(size, sizeof(size), "R1=%u\r", len);
ret = eswifi_at_cmd(eswifi, size);
if (ret < 0) {
LOG_ERR("Unable to set read size");
return -EIO;
}
/* Set timeout */
snprintk(timeout, sizeof(timeout), "R2=%u\r", 30); /* 30 ms */
ret = eswifi_at_cmd(eswifi, timeout);
if (ret < 0) {
LOG_ERR("Unable to set timeout");
return -EIO;
}
return eswifi_at_cmd_rsp(eswifi, cmd, data);
}
int __eswifi_bind(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket,
const struct sockaddr *addr, socklen_t addrlen)
{
int err;
if (addr->sa_family != AF_INET) {
LOG_ERR("Only AF_INET is supported!");
return -EPFNOSUPPORT;
}
__select_socket(eswifi, socket->index);
socket->port = sys_be16_to_cpu(net_sin(addr)->sin_port);
/* Set Local Port */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P2=%d\r", socket->port);
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to set local port");
return -EIO;
}
if (socket->type == ESWIFI_TRANSPORT_UDP) {
/* No listen or accept, so start UDP server now */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P5=1\r");
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to start UDP server");
return -EIO;
}
}
return 0;
}
static void eswifi_off_read_work(struct k_work *work)
{
struct eswifi_off_socket *socket;
struct eswifi_dev *eswifi;
struct net_pkt *pkt = NULL;
int next_timeout_ms = 100;
int err, len;
char *data;
struct k_work_delayable *dwork = k_work_delayable_from_work(work);
LOG_DBG("");
socket = CONTAINER_OF(dwork, struct eswifi_off_socket, read_work);
eswifi = eswifi_socket_to_dev(socket);
eswifi_lock(eswifi);
if ((socket->type == ESWIFI_TRANSPORT_TCP ||
socket->type == ESWIFI_TRANSPORT_TCP_SSL) &&
socket->state != ESWIFI_SOCKET_STATE_CONNECTED) {
goto done;
}
__select_socket(eswifi, socket->index);
/* Verify if we can allocate a rx packet before reading data to prevent leaks */
pkt = net_pkt_rx_alloc_with_buffer(eswifi->iface, 1460,
AF_UNSPEC, 0, K_NO_WAIT);
if (!pkt) {
LOG_ERR("Cannot allocate rx packet");
goto done;
}
len = __read_data(eswifi, 1460, &data); /* 1460 is max size */
if (len < 0) {
__stop_socket(eswifi, socket);
if (socket->recv_cb) {
/* send EOF (null pkt) */
net_pkt_unref(pkt);
pkt = NULL;
goto do_recv_cb;
}
}
if (!len || !socket->recv_cb) {
net_pkt_unref(pkt);
goto done;
}
LOG_DBG("payload sz = %d", len);
if (net_pkt_write(pkt, data, len) < 0) {
LOG_WRN("Incomplete buffer copy");
}
/* Resize the packet */
net_pkt_trim_buffer(pkt);
net_pkt_cursor_init(pkt);
do_recv_cb:
socket->recv_cb(socket->context, pkt,
NULL, NULL, 0, socket->recv_data);
if (!socket->context) {
/* something destroyed the socket in the recv path */
eswifi_unlock(eswifi);
return;
}
k_sem_give(&socket->read_sem);
next_timeout_ms = 0;
done:
err = k_work_reschedule_for_queue(&eswifi->work_q, &socket->read_work,
K_MSEC(next_timeout_ms));
if (err < 0) {
LOG_ERR("Rescheduling socket read error");
}
eswifi_unlock(eswifi);
}
int __eswifi_off_start_client(struct eswifi_dev *eswifi,
struct eswifi_off_socket *socket)
{
struct sockaddr *addr = &socket->peer_addr;
struct in_addr *sin_addr = &net_sin(addr)->sin_addr;
int err;
LOG_DBG("");
__select_socket(eswifi, socket->index);
/* Stop any running client */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P6=0\r");
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to stop running client");
return -EIO;
}
/* Stop any running server */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P5=0\r");
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to stop running client");
return -EIO;
}
/* Clear local port */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P2=0\r");
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to stop running client");
return -EIO;
}
/* Set Remote IP */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P3=%u.%u.%u.%u\r",
sin_addr->s4_addr[0], sin_addr->s4_addr[1],
sin_addr->s4_addr[2], sin_addr->s4_addr[3]);
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to set remote ip");
return -EIO;
}
/* Set Remote Port */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P4=%d\r",
(uint16_t)sys_be16_to_cpu(net_sin(addr)->sin_port));
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to set remote port");
return -EIO;
}
/* Start TCP/UDP client */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P6=1\r");
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to start TCP/UDP client");
return -EIO;
}
#if !defined(CONFIG_NET_SOCKETS_OFFLOAD)
net_context_set_state(socket->context, NET_CONTEXT_CONNECTED);
#endif
return 0;
}
int __eswifi_listen(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket, int backlog)
{
int err;
__select_socket(eswifi, socket->index);
/* Set backlog */
snprintk(eswifi->buf, sizeof(eswifi->buf), "P8=%d\r", backlog);
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to start set listen backlog");
err = -EIO;
}
socket->is_server = true;
return 0;
}
int __eswifi_accept(struct eswifi_dev *eswifi, struct eswifi_off_socket *socket)
{
char cmd[] = "P5=1\r";
if (socket->state != ESWIFI_SOCKET_STATE_NONE) {
/* we can only handle one connection at a time */
return -EBUSY;
}
__select_socket(eswifi, socket->index);
/* Start TCP Server */
if (eswifi_at_cmd(eswifi, cmd) < 0) {
LOG_ERR("Unable to start TCP server");
return -EIO;
}
LOG_DBG("TCP Server started");
socket->state = ESWIFI_SOCKET_STATE_ACCEPTING;
return 0;
}
int __eswifi_socket_free(struct eswifi_dev *eswifi,
struct eswifi_off_socket *socket)
{
__select_socket(eswifi, socket->index);
k_work_cancel_delayable(&socket->read_work);
__select_socket(eswifi, socket->index);
__stop_socket(eswifi, socket);
return 0;
}
int __eswifi_socket_new(struct eswifi_dev *eswifi, int family, int type,
int proto, void *context)
{
struct eswifi_off_socket *socket = NULL;
int err, i;
LOG_DBG("");
if (family != AF_INET) {
LOG_ERR("Only AF_INET is supported!");
return -EPFNOSUPPORT;
}
/* pickup available socket */
for (i = 0; i < ESWIFI_OFFLOAD_MAX_SOCKETS; i++) {
if (!eswifi->socket[i].context) {
socket = &eswifi->socket[i];
socket->index = i;
socket->context = context;
break;
}
}
if (!socket) {
LOG_ERR("No socket resource available");
return -ENOMEM;
}
err = eswifi_socket_type_from_zephyr(proto, &socket->type);
if (err) {
LOG_ERR("Only TCP & UDP is supported");
return err;
}
err = __select_socket(eswifi, socket->index);
if (err < 0) {
LOG_ERR("Unable to select socket %u", socket->index);
return -EIO;
}
snprintk(eswifi->buf, sizeof(eswifi->buf), "P1=%d\r", socket->type);
err = eswifi_at_cmd(eswifi, eswifi->buf);
if (err < 0) {
LOG_ERR("Unable to set transport protocol");
return -EIO;
}
k_work_init_delayable(&socket->read_work, eswifi_off_read_work);
socket->usage = 1;
LOG_DBG("Socket index %d", socket->index);
return socket->index;
}