incubator-nuttx/net/udp/udp_send.c

343 lines
9.8 KiB
C

/****************************************************************************
* net/udp/udp_send.c
*
* Copyright (C) 2007-2009, 2011, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Adapted for NuttX from logic in uIP which also has a BSD-like license:
*
* Original author Adam Dunkels <adam@dunkels.com>
* Copyright () 2001-2003, Adam Dunkels.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#if defined(CONFIG_NET) && defined(CONFIG_NET_UDP)
#include <string.h>
#include <debug.h>
#include <assert.h>
#include <arpa/inet.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/ip.h>
#include <nuttx/net/udp.h>
#include "devif/devif.h"
#include "inet/inet.h"
#include "socket/socket.h"
#include "utils/utils.h"
#include "udp/udp.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: udp_send_loopback
*
* Description:
* Send a copy of the UDP packet to ourself.
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
*
* Returned Value:
* None
*
****************************************************************************/
#if defined(CONFIG_NET_SOCKOPTS) && \
(defined(CONFIG_NET_IGMP) || defined(CONFIG_NET_MLD))
static void udp_send_loopback(FAR struct net_driver_s *dev)
{
FAR struct iob_s *iob = netdev_iob_clone(dev, true);
if (iob == NULL)
{
nerr("ERROR: IOB clone failed when looping UDP.\n");
return;
}
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(dev->d_flags))
#endif
{
ninfo("IPv4 frame\n");
NETDEV_RXIPV4(dev);
ipv4_input(dev);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
ninfo("IPv6 frame\n");
NETDEV_RXIPV6(dev);
ipv6_input(dev);
}
#endif /* CONFIG_NET_IPv6 */
/* Restore device IOB with backup IOB */
netdev_iob_replace(dev, iob);
}
#endif
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: udp_send
*
* Description:
* Set-up to send a UDP packet
*
* Input Parameters:
* dev - The device driver structure to use in the send operation
* conn - The UDP "connection" structure holding port information
*
* Returned Value:
* None
*
* Assumptions:
* The network is locked.
*
****************************************************************************/
void udp_send(FAR struct net_driver_s *dev, FAR struct udp_conn_s *conn)
{
FAR struct udp_hdr_s *udp;
#ifdef CONFIG_NET_IPv6
FAR const uint16_t *laddr;
#endif
#ifdef CONFIG_NET_IPv4
in_addr_t raddr;
#endif
ninfo("UDP payload: %d (%d) bytes\n", dev->d_sndlen, dev->d_len);
if (dev->d_sndlen > 0)
{
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET ||
(conn->domain == PF_INET6 &&
ip6_is_ipv4addr((FAR struct in6_addr *)conn->u.ipv6.raddr)))
#endif
{
DEBUGASSERT(IFF_IS_IPv4(dev->d_flags));
udp = UDPIPv4BUF;
#ifdef CONFIG_NET_IPv6
if (conn->domain == PF_INET6 &&
ip6_is_ipv4addr((FAR struct in6_addr *)conn->u.ipv6.raddr))
{
raddr = ip6_get_ipv4addr(conn->u.ipv6.raddr);
}
else
#endif
{
raddr = conn->u.ipv4.raddr;
}
/* The total length to send is the size of the application data
* plus the IPv4 and UDP headers (and, eventually, the link layer
* header)
*/
dev->d_len = dev->d_sndlen + IPv4UDP_HDRLEN;
ipv4_build_header(IPv4BUF, dev->d_len, IP_PROTO_UDP,
&dev->d_ipaddr, &raddr, conn->sconn.ttl,
conn->sconn.s_tos, NULL);
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.sent++;
#endif
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
/* Get pointers to the IPv6 header and the offset UDP header */
DEBUGASSERT(IFF_IS_IPv6(dev->d_flags));
udp = UDPIPv6BUF;
/* The IPv6 length, Includes the UDP header size but not the IPv6
* header size
*/
dev->d_len = dev->d_sndlen + UDP_HDRLEN;
/* We use the laddr if the conn is bounded to an address, otherwise
* find a suitable source address corresponding to the raddr
*/
laddr = !net_ipv6addr_cmp(conn->u.ipv6.laddr, g_ipv6_unspecaddr) ?
conn->u.ipv6.laddr :
netdev_ipv6_srcaddr(dev, conn->u.ipv6.raddr);
ipv6_build_header(IPv6BUF, dev->d_len, IP_PROTO_UDP,
laddr, conn->u.ipv6.raddr,
conn->sconn.ttl, conn->sconn.s_tclass);
/* The total length to send is the size of the application data
* plus the IPv6 and UDP headers (and, eventually, the link layer
* header)
*/
dev->d_len += IPv6_HDRLEN;
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv6.sent++;
#endif
}
#endif /* CONFIG_NET_IPv6 */
/* Initialize the UDP header */
udp->srcport = conn->lport;
udp->destport = conn->rport;
udp->udplen = HTONS(dev->d_sndlen + UDP_HDRLEN);
udp->udpchksum = 0;
/* Update the device buffer length */
iob_update_pktlen(dev->d_iob, dev->d_len, false);
#ifdef CONFIG_NET_UDP_CHECKSUMS
/* Calculate UDP checksum. */
#ifdef CONFIG_NET_IPv4
#ifdef CONFIG_NET_IPv6
if (IFF_IS_IPv4(dev->d_flags))
#endif
{
udp->udpchksum = ~udp_ipv4_chksum(dev);
}
#endif /* CONFIG_NET_IPv4 */
#ifdef CONFIG_NET_IPv6
#ifdef CONFIG_NET_IPv4
else
#endif
{
udp->udpchksum = ~udp_ipv6_chksum(dev);
}
#endif /* CONFIG_NET_IPv6 */
if (udp->udpchksum == 0)
{
udp->udpchksum = 0xffff;
}
#endif /* CONFIG_NET_UDP_CHECKSUMS */
ninfo("Outgoing UDP packet length: %d\n", dev->d_len);
#ifdef CONFIG_NET_STATISTICS
g_netstats.udp.sent++;
#endif
#ifdef CONFIG_NET_SOCKOPTS
/* Try loopback multicast to ourself. */
#ifdef CONFIG_NET_IGMP
if (_SO_GETOPT(conn->sconn.s_options, IP_MULTICAST_LOOP) &&
#ifdef CONFIG_NET_IPv6
IFF_IS_IPv4(dev->d_flags) &&
#endif
IN_MULTICAST(NTOHL(raddr)))
{
udp_send_loopback(dev);
}
#endif /* CONFIG_NET_IGMP */
#ifdef CONFIG_NET_MLD
if (_SO_GETOPT(conn->sconn.s_options, IPV6_MULTICAST_LOOP) &&
#ifdef CONFIG_NET_IPv4
IFF_IS_IPv6(dev->d_flags) &&
#endif
IN6_IS_ADDR_MULTICAST((FAR struct in6_addr *)conn->u.ipv6.raddr))
{
udp_send_loopback(dev);
}
#endif /* CONFIG_NET_MLD */
#endif /* CONFIG_NET_SOCKOPTS */
}
}
/****************************************************************************
* Name: udpip_hdrsize
*
* Description:
* Get the total size of L3 and L4 UDP header
*
* Input Parameters:
* conn The connection structure associated with the socket
*
* Returned Value:
* the total size of L3 and L4 TCP header
*
****************************************************************************/
uint16_t udpip_hdrsize(FAR struct udp_conn_s *conn)
{
uint16_t hdrsize = sizeof(struct udp_hdr_s);
#if defined(CONFIG_NET_IPv4) && defined(CONFIG_NET_IPv6)
if (conn->domain == PF_INET6 &&
ip6_is_ipv4addr((FAR struct in6_addr *)conn->u.ipv6.raddr))
{
/* Select the IPv4 domain for hybrid dual-stack IPv6/IPv4 socket */
return sizeof(struct ipv4_hdr_s) + hdrsize;
}
#endif
UNUSED(conn);
return net_ip_domain_select(conn->domain,
sizeof(struct ipv4_hdr_s) + hdrsize,
sizeof(struct ipv6_hdr_s) + hdrsize);
}
#endif /* CONFIG_NET && CONFIG_NET_UDP */