nuttx/net/devif/ipv4_input.c

595 lines
18 KiB
C
Raw Normal View History

/****************************************************************************
* net/devif/ipv4_input.c
* Device driver IPv4 packet receipt interface
*
* Copyright (C) 2007-2009, 2013-2015, 2018-2019 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:
*
* uIP is an implementation of the TCP/IP protocol stack intended for
* small 8-bit and 16-bit microcontrollers.
*
* uIP provides the necessary protocols for Internet communication,
* with a very small code footprint and RAM requirements - the uIP
* code size is on the order of a few kilobytes and RAM usage is on
* the order of a few hundred bytes.
*
* 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.
*
****************************************************************************/
/****************************************************************************
* uIP is a small implementation of the IP, UDP and TCP protocols (as
* well as some basic ICMP stuff). The implementation couples the IP,
* UDP, TCP and the application layers very tightly. To keep the size
* of the compiled code down, this code frequently uses the goto
* statement. While it would be possible to break the ipv4_input()
* function into many smaller functions, this would increase the code
* size because of the overhead of parameter passing and the fact that
2014-06-25 02:12:49 +02:00
* the optimizer would not be as efficient.
*
* The principle is that we have a small buffer, called the d_buf,
* in which the device driver puts an incoming packet. The TCP/IP
* stack parses the headers in the packet, and calls the
* application. If the remote host has sent data to the application,
* this data is present in the d_buf and the application read the
* data from there. It is up to the application to put this data into
* a byte stream if needed. The application will not be fed with data
* that is out of sequence.
*
* If the application wishes to send data to the peer, it should put
* its data into the d_buf. The d_appdata pointer points to the
* first available byte. The TCP/IP stack will calculate the
* checksums, and fill in the necessary header fields and finally send
* the packet back to the peer.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifdef CONFIG_NET_IPv4
#include <sys/ioctl.h>
#include <stdint.h>
#include <debug.h>
#include <string.h>
#include <netinet/in.h>
2015-01-24 14:29:43 +01:00
#include <net/if.h>
#include <nuttx/net/netconfig.h>
#include <nuttx/net/netdev.h>
#include <nuttx/net/netstats.h>
#include <nuttx/net/ip.h>
#include "inet/inet.h"
2014-06-25 02:12:49 +02:00
#include "tcp/tcp.h"
#include "udp/udp.h"
2014-06-25 18:34:52 +02:00
#include "pkt/pkt.h"
2014-06-25 17:12:47 +02:00
#include "icmp/icmp.h"
2014-06-25 17:52:36 +02:00
#include "igmp/igmp.h"
#include "ipforward/ipforward.h"
#include "devif/devif.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Macros */
#define BUF ((FAR struct ipv4_hdr_s *)&dev->d_buf[NET_LL_HDRLEN(dev)])
#define FBUF ((FAR struct ipv4_hdr_s *)&g_reassembly_buffer[0])
/* IP fragment re-assembly.
*
* REVISIT: There are multiple issues with the current implementation:
* 1. IPv4 reassembly is untested.
* 2. Currently can only work with Ethernet due to the definition of
* IPv4_REASS_BUFSIZE.
* 3. Since there is only a single reassembly buffer, IPv4 reassembly cannot
* be used in a context where multiple network devices may be concurrently
* re-assembling packets.
*/
#define IP_MF 0x20 /* See IP_FLAG_MOREFRAGS */
This commit attempts remove some long standard confusion in naming and some actual problems that result from the naming confusion. The basic problem is the standard MTU does not include the size of the Ethernet header. For clarity, I changed the naming of most things called MTU to PKTSIZE. For example, CONFIG_NET_ETH_MTU is now CONFIG_NET_ETH_PKTSIZE. This makes the user interface a little hostile. People thing of an MTU of 1500 bytes, but the corresponding packet is really 1514 bytes (including the 14 byte Ethernet header). A more friendly solution would configure the MTU (as before), but then derive the packet buffer size by adding the MAC header length. Instead, we define the packet buffer size then derive the MTU. The MTU is not common currency in networking. On the wire, the only real issue is the MSS which is derived from MTU by subtracting the IP header and TCP header sizes (for the case of TCP). Now it is derived for the PKTSIZE by subtracting the IP header, the TCP header, and the MAC header sizes. So we should be all good and without the recurring 14 byte error in MTU's and MSS's. Squashed commit of the following: Trivial update to fix some spacing issues. net/: Rename several macros containing _MTU to _PKTSIZE. net/: Rename CONFIG_NET_SLIP_MTU to CONFIG_NET_SLIP_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_6LOWPAN_MTU to CONFIG_NET_6LOWPAN_PKTSIZE and similarly for CONFIG_NET_TUN_MTU. These are not the MTU which does not include the size of the link layer header. These are the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename CONFIG_NET_ETH_MTU to CONFIG_NET_ETH_PKTSIZE. This is not the MTU which does not include the size of the link layer header. This is the full size of the packet buffer memory (minus any GUARD bytes). net/: Rename the file d_mtu in the network driver structure to d_pktsize. That value saved there is not the MTU. The packetsize is the memory large enough to hold the maximum packet PLUS the size of the link layer header. The MTU does not include the link layer header.
2018-07-04 22:10:40 +02:00
#define IPv4_REASS_BUFSIZE (CONFIG_NET_ETH_PKTSIZE - ETH_HDRLEN)
#define IPv4_REASS_LASTFRAG 0x01
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_NET_IPv4_REASSEMBLY
static uint8_t g_reassembly_buffer[IPv4_REASS_BUFSIZE];
static uint8_t g_reassembly_bitmap[IPv4_REASS_BUFSIZE / (8 * 8)];
static const uint8_t g_bitmap_bits[8] =
{
0xff, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x03, 0x01
};
static uint16_t g_reassembly_len;
static uint8_t g_reassembly_flags;
#endif /* CONFIG_NET_IPv4_REASSEMBLY */
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: devif_reassembly
*
* Description:
* IP fragment reassembly: not well-tested.
*
* Assumptions:
*
****************************************************************************/
#ifdef CONFIG_NET_IPv4_REASSEMBLY
static uint8_t devif_reassembly(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = BUF;
FAR struct ipv4_hdr_s *fipv4 = FBUF;
uint16_t offset;
uint16_t len;
uint16_t i;
/* If g_reassembly_timer is zero, no packet is present in the buffer, so
* we write the IP header of the fragment into the reassembly buffer. The
* timer is updated with the maximum age.
*/
if (!g_reassembly_timer)
{
memcpy(g_reassembly_buffer, &ipv4->vhl, IPv4_HDRLEN);
g_reassembly_timer = CONFIG_NET_IPv4_REASS_MAXAGE;
g_reassembly_flags = 0;
/* Clear the bitmap. */
memset(g_reassembly_bitmap, 0, sizeof(g_reassembly_bitmap));
}
/* Check if the incoming fragment matches the one currently present
* in the reassembly buffer. If so, we proceed with copying the
* fragment into the buffer.
*/
if (net_ipv4addr_hdrcmp(ipv4->srcipaddr, fipv4->srcipaddr) &&
net_ipv4addr_hdrcmp(ipv4->destipaddr, fipv4->destipaddr) &&
ipv4->ipid[0] == fipv4->ipid[0] && ipv4->ipid[1] == fipv4->ipid[1])
{
len = ((uint16_t)ipv4->len[0] << 8) + (uint16_t)ipv4->len[1] -
(uint16_t)(ipv4->vhl & 0x0f) * 4;
offset = (((ipv4->ipoffset[0] & 0x3f) << 8) + ipv4->ipoffset[1]) * 8;
/* If the offset or the offset + fragment length overflows the
* reassembly buffer, we discard the entire packet.
*/
if (offset > IPv4_REASS_BUFSIZE || offset + len > IPv4_REASS_BUFSIZE)
{
g_reassembly_timer = 0;
goto nullreturn;
}
/* Copy the fragment into the reassembly buffer, at the right offset. */
memcpy(&g_reassembly_buffer[IPv4_HDRLEN + offset],
(FAR char *)ipv4 + (int)((ipv4->vhl & 0x0f) * 4), len);
/* Update the bitmap. */
if (offset / (8 * 8) == (offset + len) / (8 * 8))
{
/* If the two endpoints are in the same byte, we only update that byte. */
g_reassembly_bitmap[offset / (8 * 8)] |=
g_bitmap_bits[(offset / 8) & 7] &
~g_bitmap_bits[((offset + len) / 8) & 7];
}
else
{
/* If the two endpoints are in different bytes, we update the bytes
* in the endpoints and fill the stuff in between with 0xff.
*/
g_reassembly_bitmap[offset / (8 * 8)] |=
g_bitmap_bits[(offset / 8) & 7];
for (i = 1 + offset / (8 * 8); i < (offset + len) / (8 * 8); ++i)
{
g_reassembly_bitmap[i] = 0xff;
}
g_reassembly_bitmap[(offset + len) / (8 * 8)] |=
~g_bitmap_bits[((offset + len) / 8) & 7];
}
/* If this fragment has the More Fragments flag set to zero, we know that
* this is the last fragment, so we can calculate the size of the entire
* packet. We also set the IP_REASS_FLAG_LASTFRAG flag to indicate that
* we have received the final fragment.
*/
if ((ipv4->ipoffset[0] & IP_MF) == 0)
{
g_reassembly_flags |= IPv4_REASS_LASTFRAG;
g_reassembly_len = offset + len;
}
/* Finally, we check if we have a full packet in the buffer. We do this
* by checking if we have the last fragment and if all bits in the bitmap
* are set.
*/
if (g_reassembly_flags & IPv4_REASS_LASTFRAG)
{
/* Check all bytes up to and including all but the last byte in
* the bitmap.
*/
for (i = 0; i < g_reassembly_len / (8 * 8) - 1; ++i)
{
if (g_reassembly_bitmap[i] != 0xff)
{
goto nullreturn;
}
}
/* Check the last byte in the bitmap. It should contain just the
* right amount of bits.
*/
if (g_reassembly_bitmap[g_reassembly_len / (8 * 8)] !=
(uint8_t)~g_bitmap_bits[g_reassembly_len / 8 & 7])
{
goto nullreturn;
}
/* If we have come this far, we have a full packet in the buffer,
* so we allocate a ipv4 and copy the packet into it. We also reset
* the timer.
*/
g_reassembly_timer = 0;
memcpy(ipv4, fipv4, g_reassembly_len);
/* Pretend to be a "normal" (i.e., not fragmented) IP packet from
* now on.
*/
ipv4->ipoffset[0] = ipv4->ipoffset[1] = 0;
ipv4->len[0] = g_reassembly_len >> 8;
ipv4->len[1] = g_reassembly_len & 0xff;
ipv4->ipchksum = 0;
ipv4->ipchksum = ~(ipv4_chksum(dev));
return g_reassembly_len;
}
}
nullreturn:
return 0;
}
#endif /* CONFIG_NET_IPv4_REASSEMBLY */
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ipv4_input
*
* Description:
*
* Returned Value:
2017-07-17 15:14:12 +02:00
* OK - The packet was processed (or dropped) and can be discarded.
* ERROR - Hold the packet and try again later. There is a listening
* socket but no receive in place to catch the packet yet. The
* device's d_len will be set to zero in this case as there is
* no outgoing data.
*
****************************************************************************/
int ipv4_input(FAR struct net_driver_s *dev)
{
FAR struct ipv4_hdr_s *ipv4 = BUF;
in_addr_t destipaddr;
uint16_t llhdrlen;
uint16_t totlen;
/* This is where the input processing starts. */
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.recv++;
#endif
/* Start of IP input header processing code.
*
* Check validity of the IP header.
* REVISIT: Does not account for varying IP header length due to the
* presences of IPv4 options. The header length is encoded as a number
* 32-bit words in the HL nibble of the VHL.
*/
if ((ipv4->vhl & IP_VERSION_MASK) != 0x40 ||
(ipv4->vhl & IPv4_HLMASK) < 5)
{
/* IP version and header length. */
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
g_netstats.ipv4.vhlerr++;
#endif
nwarn("WARNING: Invalid IP version or header length: %02x\n",
ipv4->vhl);
goto drop;
}
/* Get the size of the packet minus the size of link layer header */
llhdrlen = NET_LL_HDRLEN(dev);
if ((llhdrlen + IPv4_HDRLEN) > dev->d_len)
{
nwarn("WARNING: Packet shorter than IPv4 header\n");
goto drop;
}
dev->d_len -= llhdrlen;
/* Check the size of the packet. If the size reported to us in d_len is
* smaller the size reported in the IP header, we assume that the packet
* has been corrupted in transit. If the size of d_len is larger than the
* size reported in the IP packet header, the packet has been padded and
* we set d_len to the correct value.
*/
totlen = (ipv4->len[0] << 8) + ipv4->len[1];
if (totlen <= dev->d_len)
{
dev->d_len = totlen;
}
else
{
nwarn("WARNING: IP packet shorter than length in IP header\n");
goto drop;
}
/* Check the fragment flag. */
if ((ipv4->ipoffset[0] & 0x3f) != 0 || ipv4->ipoffset[1] != 0)
{
#ifdef CONFIG_NET_IPv4_REASSEMBLY
dev->d_len = devif_reassembly(dev);
if (dev->d_len == 0)
#endif
{
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
g_netstats.ipv4.fragerr++;
#endif
nwarn("WARNING: IP fragment dropped\n");
goto drop;
}
}
/* Get the destination IP address in a friendlier form */
destipaddr = net_ip4addr_conv32(ipv4->destipaddr);
#if defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK)
/* If IP broadcast support is configured, we check for a broadcast
* UDP packet, which may be destined to us (even if there is no IP
* address yet assigned to the device as is the case when we are
* negotiating over DHCP for an address).
*/
if (ipv4->proto == IP_PROTO_UDP &&
net_ipv4addr_cmp(destipaddr, INADDR_BROADCAST))
{
#ifdef CONFIG_NET_IPFORWARD_BROADCAST
/* Forward broadcast packets */
ipv4_forward_broadcast(dev, ipv4);
#endif
return udp_ipv4_input(dev);
}
else
#endif
#ifdef CONFIG_NET_ICMP
/* In other cases, the device must be assigned a non-zero IP address. */
if (net_ipv4addr_cmp(dev->d_ipaddr, INADDR_ANY))
{
nwarn("WARNING: No IP address assigned\n");
goto drop;
}
else
#endif
#if defined(CONFIG_NET_BROADCAST) && defined(NET_UDP_HAVE_STACK)
/* The address is not the broadcast address and we have been assigned a
* address. So there is also the possibility that the destination address
* is a sub-net broadcast address which we will need to handle just as for
* the broadcast address above.
*/
if (ipv4->proto == IP_PROTO_UDP &&
net_ipv4addr_maskcmp(destipaddr, dev->d_ipaddr, dev->d_netmask) &&
net_ipv4addr_broadcast(destipaddr, dev->d_netmask))
{
#ifdef CONFIG_NET_IPFORWARD_BROADCAST
/* Forward broadcast packets */
ipv4_forward_broadcast(dev, ipv4);
#endif
return udp_ipv4_input(dev);
}
else
#endif
/* Check if the packet is destined for our IP address. */
if (!net_ipv4addr_cmp(destipaddr, dev->d_ipaddr))
{
/* No.. This is not our IP address. Check for an IPv4 IGMP group
* address
*/
#ifdef CONFIG_NET_IGMP
in_addr_t destip = net_ip4addr_conv32(ipv4->destipaddr);
if (igmp_grpfind(dev, &destip) != NULL)
{
#ifdef CONFIG_NET_IPFORWARD_BROADCAST
/* Forward multicast packets */
ipv4_forward_broadcast(dev, ipv4);
#endif
}
else
#endif
{
/* No.. The packet is not destined for us. */
#ifdef CONFIG_NET_IPFORWARD
/* Try to forward the packet */
int ret = ipv4_forward(dev, ipv4);
if (ret >= 0)
{
/* The packet was forwarded. Return success; d_len will
* be set appropriately by the forwarding logic: Cleared
* if the packet is forward via anoother device or non-
* zero if it will be forwarded by the same device that
* it was received on.
*/
return OK;
}
else
#endif
{
/* Not destined for us and not forwardable... Drop the
* packet.
*/
nwarn("WARNING: Not destined for us; not forwardable... "
"Dropping!\n");
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
#endif
goto drop;
}
}
}
if (ipv4_chksum(dev) != 0xffff)
{
/* Compute and check the IP header checksum. */
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
g_netstats.ipv4.chkerr++;
#endif
nwarn("WARNING: Bad IP checksum\n");
goto drop;
}
2015-01-24 14:29:43 +01:00
/* Make sure that all packet processing logic knows that there is an IPv4
* packet in the device buffer.
*/
2015-01-24 14:29:43 +01:00
IFF_SET_IPv4(dev->d_flags);
/* Now process the incoming packet according to the protocol. */
switch (ipv4->proto)
{
#ifdef NET_TCP_HAVE_STACK
2014-07-05 00:38:51 +02:00
case IP_PROTO_TCP: /* TCP input */
tcp_ipv4_input(dev);
break;
#endif
#ifdef NET_UDP_HAVE_STACK
2014-07-05 00:38:51 +02:00
case IP_PROTO_UDP: /* UDP input */
udp_ipv4_input(dev);
break;
#endif
#ifdef NET_ICMP_HAVE_STACK
/* Check for ICMP input */
2014-07-05 00:38:51 +02:00
case IP_PROTO_ICMP: /* ICMP input */
2014-06-25 17:12:47 +02:00
icmp_input(dev);
break;
#endif
#ifdef CONFIG_NET_IGMP
/* Check for IGMP input */
2014-07-05 00:38:51 +02:00
case IP_PROTO_IGMP: /* IGMP input */
2014-06-25 17:52:36 +02:00
igmp_input(dev);
break;
#endif
default: /* Unrecognized/unsupported protocol */
#ifdef CONFIG_NET_STATISTICS
g_netstats.ipv4.drop++;
g_netstats.ipv4.protoerr++;
#endif
nwarn("WARNING: Unrecognized IP protocol\n");
goto drop;
}
/* Return and let the caller do any pending transmission. */
return OK;
/* Drop the packet. NOTE that OK is returned meaning that the
* packet has been processed (although processed unsuccessfully).
*/
drop:
dev->d_len = 0;
return OK;
}
#endif /* CONFIG_NET_IPv4 */