mirror of
https://github.com/apache/nuttx.git
synced 2026-05-30 05:16:47 +08:00
net/udp: Support zero-length UDP datagrams
According to RFC768 page2 and referring to the Linux implementation, the message with udp length of 0 is supported. Signed-off-by: gaohedong <gaohedong@xiaomi.com>
This commit is contained in:
@@ -74,7 +74,8 @@ ssize_t psock_sendmsg(FAR struct socket *psock, FAR struct msghdr *msg,
|
|||||||
{
|
{
|
||||||
/* Verify that non-NULL pointers were passed */
|
/* Verify that non-NULL pointers were passed */
|
||||||
|
|
||||||
if (msg == NULL || msg->msg_iov == NULL || msg->msg_iov->iov_base == NULL)
|
if (msg == NULL || msg->msg_iov == NULL ||
|
||||||
|
(psock->s_type != SOCK_DGRAM && msg->msg_iov->iov_base == NULL))
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ void udp_poll(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
|
|||||||
|
|
||||||
/* If the application has data to send, setup the UDP/IP header */
|
/* If the application has data to send, setup the UDP/IP header */
|
||||||
|
|
||||||
if (dev->d_sndlen > 0)
|
if (dev->d_len > 0)
|
||||||
{
|
{
|
||||||
udp_send(dev, conn);
|
udp_send(dev, conn);
|
||||||
return;
|
return;
|
||||||
|
|||||||
+1
-1
@@ -155,7 +155,7 @@ void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
|
|||||||
|
|
||||||
ninfo("UDP payload: %d (%d) bytes\n", dev->d_sndlen, dev->d_len);
|
ninfo("UDP payload: %d (%d) bytes\n", dev->d_sndlen, dev->d_len);
|
||||||
|
|
||||||
if (dev->d_sndlen > 0)
|
if (dev->d_len > 0)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_NET_IPv4
|
#ifdef CONFIG_NET_IPv4
|
||||||
#ifdef CONFIG_NET_IPv6
|
#ifdef CONFIG_NET_IPv6
|
||||||
|
|||||||
+128
-130
@@ -687,136 +687,134 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf,
|
|||||||
|
|
||||||
BUF_DUMP("psock_udp_sendto", buf, len);
|
BUF_DUMP("psock_udp_sendto", buf, len);
|
||||||
|
|
||||||
if (len > 0)
|
net_lock();
|
||||||
{
|
|
||||||
net_lock();
|
|
||||||
|
|
||||||
#if CONFIG_NET_SEND_BUFSIZE > 0
|
#if CONFIG_NET_SEND_BUFSIZE > 0
|
||||||
/* If the send buffer size exceeds the send limit,
|
/* If the send buffer size exceeds the send limit,
|
||||||
* wait for the write buffer to be released
|
* wait for the write buffer to be released
|
||||||
*/
|
*/
|
||||||
|
|
||||||
while (udp_wrbuffer_inqueue_size(conn) + len > conn->sndbufs)
|
while (udp_wrbuffer_inqueue_size(conn) + len > conn->sndbufs)
|
||||||
{
|
{
|
||||||
if (nonblock)
|
|
||||||
{
|
|
||||||
ret = -EAGAIN;
|
|
||||||
goto errout_with_lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = net_sem_timedwait_uninterruptible(&conn->sndsem,
|
|
||||||
udp_send_gettimeout(start, timeout));
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
if (ret == -ETIMEDOUT)
|
|
||||||
{
|
|
||||||
ret = -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto errout_with_lock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_NET_SEND_BUFSIZE */
|
|
||||||
|
|
||||||
/* Allocate a write buffer. Careful, the network will be momentarily
|
|
||||||
* unlocked here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef CONFIG_NET_JUMBO_FRAME
|
|
||||||
|
|
||||||
/* alloc iob of gso pkt for udp data */
|
|
||||||
|
|
||||||
wrb = udp_wrbuffer_tryalloc(len + udpip_hdrsize(conn) +
|
|
||||||
CONFIG_NET_LL_GUARDSIZE);
|
|
||||||
#else
|
|
||||||
if (nonblock)
|
if (nonblock)
|
||||||
{
|
{
|
||||||
wrb = udp_wrbuffer_tryalloc();
|
ret = -EAGAIN;
|
||||||
|
goto errout_with_lock;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
ret = net_sem_timedwait_uninterruptible(&conn->sndsem,
|
||||||
|
udp_send_gettimeout(start, timeout));
|
||||||
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
wrb = udp_wrbuffer_timedalloc(udp_send_gettimeout(start,
|
if (ret == -ETIMEDOUT)
|
||||||
timeout));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (wrb == NULL)
|
|
||||||
{
|
|
||||||
/* A buffer allocation error occurred */
|
|
||||||
|
|
||||||
nerr("ERROR: Failed to allocate write buffer\n");
|
|
||||||
|
|
||||||
if (nonblock || timeout != UINT_MAX)
|
|
||||||
{
|
{
|
||||||
ret = -EAGAIN;
|
ret = -EAGAIN;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto errout_with_lock;
|
goto errout_with_lock;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_NET_SEND_BUFSIZE */
|
||||||
|
|
||||||
/* Initialize the write buffer
|
/* Allocate a write buffer. Careful, the network will be momentarily
|
||||||
*
|
* unlocked here.
|
||||||
* Check if the socket is connected
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
if (_SS_ISCONNECTED(conn->sconn.s_flags))
|
#ifdef CONFIG_NET_JUMBO_FRAME
|
||||||
|
|
||||||
|
/* alloc iob of gso pkt for udp data */
|
||||||
|
|
||||||
|
wrb = udp_wrbuffer_tryalloc(len + udpip_hdrsize(conn) +
|
||||||
|
CONFIG_NET_LL_GUARDSIZE);
|
||||||
|
#else
|
||||||
|
if (nonblock)
|
||||||
|
{
|
||||||
|
wrb = udp_wrbuffer_tryalloc();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wrb = udp_wrbuffer_timedalloc(udp_send_gettimeout(start, timeout));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (wrb == NULL)
|
||||||
|
{
|
||||||
|
/* A buffer allocation error occurred */
|
||||||
|
|
||||||
|
nerr("ERROR: Failed to allocate write buffer\n");
|
||||||
|
|
||||||
|
if (nonblock || timeout != UINT_MAX)
|
||||||
{
|
{
|
||||||
/* Yes.. get the connection address from the connection structure */
|
ret = -EAGAIN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto errout_with_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the write buffer
|
||||||
|
*
|
||||||
|
* Check if the socket is connected
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (_SS_ISCONNECTED(conn->sconn.s_flags))
|
||||||
|
{
|
||||||
|
/* Yes.. get the connection address from the connection structure */
|
||||||
|
|
||||||
#ifdef CONFIG_NET_IPv4
|
#ifdef CONFIG_NET_IPv4
|
||||||
#ifdef CONFIG_NET_IPv6
|
#ifdef CONFIG_NET_IPv6
|
||||||
if (conn->domain == PF_INET)
|
if (conn->domain == PF_INET)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
FAR struct sockaddr_in *addr4 =
|
FAR struct sockaddr_in *addr4 =
|
||||||
(FAR struct sockaddr_in *)&wrb->wb_dest;
|
(FAR struct sockaddr_in *)&wrb->wb_dest;
|
||||||
|
|
||||||
addr4->sin_family = AF_INET;
|
addr4->sin_family = AF_INET;
|
||||||
addr4->sin_port = conn->rport;
|
addr4->sin_port = conn->rport;
|
||||||
net_ipv4addr_copy(addr4->sin_addr.s_addr, conn->u.ipv4.raddr);
|
net_ipv4addr_copy(addr4->sin_addr.s_addr, conn->u.ipv4.raddr);
|
||||||
memset(addr4->sin_zero, 0, sizeof(addr4->sin_zero));
|
memset(addr4->sin_zero, 0, sizeof(addr4->sin_zero));
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_NET_IPv4 */
|
#endif /* CONFIG_NET_IPv4 */
|
||||||
|
|
||||||
#ifdef CONFIG_NET_IPv6
|
#ifdef CONFIG_NET_IPv6
|
||||||
#ifdef CONFIG_NET_IPv4
|
#ifdef CONFIG_NET_IPv4
|
||||||
else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
FAR struct sockaddr_in6 *addr6 =
|
|
||||||
(FAR struct sockaddr_in6 *)&wrb->wb_dest;
|
|
||||||
|
|
||||||
addr6->sin6_family = AF_INET6;
|
|
||||||
addr6->sin6_port = conn->rport;
|
|
||||||
net_ipv6addr_copy(addr6->sin6_addr.s6_addr,
|
|
||||||
conn->u.ipv6.raddr);
|
|
||||||
}
|
|
||||||
#endif /* CONFIG_NET_IPv6 */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not connected. Use the provided destination address */
|
|
||||||
|
|
||||||
else
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
memcpy(&wrb->wb_dest, to, tolen);
|
FAR struct sockaddr_in6 *addr6 =
|
||||||
udp_connect(conn, to);
|
(FAR struct sockaddr_in6 *)&wrb->wb_dest;
|
||||||
|
|
||||||
|
addr6->sin6_family = AF_INET6;
|
||||||
|
addr6->sin6_port = conn->rport;
|
||||||
|
net_ipv6addr_copy(addr6->sin6_addr.s6_addr, conn->u.ipv6.raddr);
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_NET_IPv6 */
|
||||||
|
}
|
||||||
|
|
||||||
/* Skip l2/l3/l4 offset before copy */
|
/* Not connected. Use the provided destination address */
|
||||||
|
|
||||||
udpiplen = udpip_hdrsize(conn);
|
else
|
||||||
|
{
|
||||||
|
memcpy(&wrb->wb_dest, to, tolen);
|
||||||
|
udp_connect(conn, to);
|
||||||
|
}
|
||||||
|
|
||||||
iob_reserve(wrb->wb_iob, CONFIG_NET_LL_GUARDSIZE);
|
/* Skip l2/l3/l4 offset before copy */
|
||||||
iob_update_pktlen(wrb->wb_iob, udpiplen, false);
|
|
||||||
|
|
||||||
/* Copy the user data into the write buffer. We cannot wait for
|
udpiplen = udpip_hdrsize(conn);
|
||||||
* buffer space if the socket was opened non-blocking.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
iob_reserve(wrb->wb_iob, CONFIG_NET_LL_GUARDSIZE);
|
||||||
|
iob_update_pktlen(wrb->wb_iob, udpiplen, false);
|
||||||
|
|
||||||
|
/* Copy the user data into the write buffer. We cannot wait for
|
||||||
|
* buffer space if the socket was opened non-blocking.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
if (nonblock)
|
if (nonblock)
|
||||||
{
|
{
|
||||||
ret = iob_trycopyin(wrb->wb_iob, (FAR uint8_t *)buf,
|
ret = iob_trycopyin(wrb->wb_iob, (FAR uint8_t *)buf,
|
||||||
@@ -845,48 +843,48 @@ ssize_t psock_udp_sendto(FAR struct socket *psock, FAR const void *buf,
|
|||||||
{
|
{
|
||||||
goto errout_with_wrb;
|
goto errout_with_wrb;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Dump I/O buffer chain */
|
/* Dump I/O buffer chain */
|
||||||
|
|
||||||
UDP_WBDUMP("I/O buffer chain", wrb, wrb->wb_iob->io_pktlen, 0);
|
UDP_WBDUMP("I/O buffer chain", wrb, wrb->wb_iob->io_pktlen, 0);
|
||||||
|
|
||||||
/* sendto_eventhandler() will send data in FIFO order from the
|
/* sendto_eventhandler() will send data in FIFO order from the
|
||||||
* conn->write_q.
|
* conn->write_q.
|
||||||
*
|
*
|
||||||
* REVISIT: Why FIFO order? Because it is easy. In a real world
|
* REVISIT: Why FIFO order? Because it is easy. In a real world
|
||||||
* environment where there are multiple network devices this might
|
* environment where there are multiple network devices this might
|
||||||
* be inefficient because we could be sending data to different
|
* be inefficient because we could be sending data to different
|
||||||
* device out-of-queued-order to optimize performance. Sending
|
* device out-of-queued-order to optimize performance. Sending
|
||||||
* data to different networks from a single UDP socket is probably
|
* data to different networks from a single UDP socket is probably
|
||||||
* not a very common use case, however.
|
* not a very common use case, however.
|
||||||
|
*/
|
||||||
|
|
||||||
|
empty = sq_empty(&conn->write_q);
|
||||||
|
|
||||||
|
sq_addlast(&wrb->wb_node, &conn->write_q);
|
||||||
|
ninfo("Queued WRB=%p pktlen=%u write_q(%p,%p)\n",
|
||||||
|
wrb, wrb->wb_iob->io_pktlen,
|
||||||
|
conn->write_q.head, conn->write_q.tail);
|
||||||
|
|
||||||
|
if (empty)
|
||||||
|
{
|
||||||
|
/* The new write buffer lies at the head of the write queue. Set
|
||||||
|
* up for the next packet transfer by setting the connection
|
||||||
|
* address to the address of the next packet now at the header of
|
||||||
|
* the write buffer queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
empty = sq_empty(&conn->write_q);
|
ret = sendto_next_transfer(conn);
|
||||||
|
if (ret < 0)
|
||||||
sq_addlast(&wrb->wb_node, &conn->write_q);
|
|
||||||
ninfo("Queued WRB=%p pktlen=%u write_q(%p,%p)\n",
|
|
||||||
wrb, wrb->wb_iob->io_pktlen,
|
|
||||||
conn->write_q.head, conn->write_q.tail);
|
|
||||||
|
|
||||||
if (empty)
|
|
||||||
{
|
{
|
||||||
/* The new write buffer lies at the head of the write queue. Set
|
sq_remlast(&conn->write_q);
|
||||||
* up for the next packet transfer by setting the connection
|
goto errout_with_wrb;
|
||||||
* address to the address of the next packet now at the header of
|
|
||||||
* the write buffer queue.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ret = sendto_next_transfer(conn);
|
|
||||||
if (ret < 0)
|
|
||||||
{
|
|
||||||
sq_remlast(&conn->write_q);
|
|
||||||
goto errout_with_wrb;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
net_unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
net_unlock();
|
||||||
|
|
||||||
/* Return the number of bytes that will be sent */
|
/* Return the number of bytes that will be sent */
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
|
|||||||
@@ -197,12 +197,28 @@ static uint16_t sendto_eventhandler(FAR struct net_driver_s *dev,
|
|||||||
{
|
{
|
||||||
/* Copy the user data into d_appdata and send it */
|
/* Copy the user data into d_appdata and send it */
|
||||||
|
|
||||||
int ret = devif_send(dev, pstate->st_buffer, pstate->st_buflen,
|
if (pstate->st_buflen > 0)
|
||||||
udpip_hdrsize(pstate->st_conn));
|
|
||||||
if (ret <= 0)
|
|
||||||
{
|
{
|
||||||
pstate->st_sndlen = ret;
|
int ret = devif_send(dev, pstate->st_buffer, pstate->st_buflen,
|
||||||
goto end_wait;
|
udpip_hdrsize(pstate->st_conn));
|
||||||
|
if (ret <= 0)
|
||||||
|
{
|
||||||
|
pstate->st_sndlen = ret;
|
||||||
|
goto end_wait;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (netdev_iob_prepare(dev, false, 0) != OK)
|
||||||
|
{
|
||||||
|
pstate->st_sndlen = -ENOMEM;
|
||||||
|
goto end_wait;
|
||||||
|
}
|
||||||
|
|
||||||
|
iob_update_pktlen(dev->d_iob, udpip_hdrsize(pstate->st_conn),
|
||||||
|
false);
|
||||||
|
dev->d_sndlen = 0;
|
||||||
|
dev->d_len = dev->d_iob->io_pktlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NEED_IPDOMAIN_SUPPORT
|
#ifdef NEED_IPDOMAIN_SUPPORT
|
||||||
|
|||||||
Reference in New Issue
Block a user