nuttx/net/sixlowpan/sixlowpan_input.c
Gregory Nutt cff4ac6845 Squashed commit of the following:
commit 2a3ab1652a2c95bcfc8be8380fc7cbdcb6472938
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Sat Aug 19 08:44:31 2017 -0600

    PF_IEEE802154: Finish some missing bind() logic.  Add configs/sim configuration for testing.

commit 59be4b846a6e3bfe82087a888e3fdac9c3c414e5
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 19:30:04 2017 -0600

    PF_IEEE802154:  More renaming to decouple 6LoPAN from radios in general.

commit 69fabb1aea76e54381bdc13de28a3f1441fb42f4
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 19:21:11 2017 -0600

    PF_IEEE802154: Missed a few renamings.

commit ff0af1bb25567720934cc1c2a721ccd92cc35f89
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 17:46:58 2017 -0600

    PF_IEEE802154:  A few bugfixes

commit 01c7c84afd00cf907d280d30cfaf0fb2cf90e02e
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 17:01:31 2017 -0600

    PF_IEEE802154:  A few bugfixes

commit dcef4056d1c1488c93151135f3b7106977faa903
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 16:31:05 2017 -0600

    PF_IEEE802154: Bring in framework for sendto/recvfrom.  Currently just a crude port of functions from net/pkt and do not provide the implemenation needed.

commit 68c5b7b6dd3ab7eb2d0c19890abb38e6561b140e
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 15:18:31 2017 -0600

    Trivial fix to typo in comment

commit fd0af534c089569ccdbd59f13b85453de0a653ad
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 15:07:20 2017 -0600

    PF_IEEE802154: Add device lookup logic; Rename some things that used to be used only by 6LoWPAN but now must be shared with PF_IEEE802154 and need more generic naming.

commit 4fc80a1659f1c699b050775cefc7f6b631b46114
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 13:49:54 2017 -0600

    PF_IEEE802154: Add driver poll logic.

commit d83f71992df8764faa93f9425f1a7602a758f730
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 13:28:59 2017 -0600

    PF_IEEE802154:  Add frame input function.

commit 77561b8c4d5d7be1f8d8eb62cf1a07229afe2048
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 12:46:29 2017 -0600

    PF_IEEE802154: Socket type should be SOCK_DGRAM.  Hook in socket interface.

commit c0f90350282e9905d7f26a1b30f04cc6d6794648
Merge: 8b518abfd0 169c55e546
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 09:36:32 2017 -0600

    Merge remote-tracking branch 'origin/master' into pf_ieee802154

commit 8b518abfd07d492f5148f2c5fdf65604de9822da
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 09:35:39 2017 -0600

    PF_IEEE802154: Add initialization and connection management logic.

commit 98b62620b3cb420041d8ad14204f9410a8aace8c
Author: Gregory Nutt <gnutt@nuttx.org>
Date:   Fri Aug 18 07:52:51 2017 -0600

    PF_IEEE802154: Add basic build support and socket interface framework.
2017-08-19 08:48:52 -06:00

907 lines
30 KiB
C

/****************************************************************************
* net/sixlowpan/sixlowpan_input.c
* 6LoWPAN implementation (RFC4944 and draft-ietf-6LoWPAN-hc-06)
*
* Copyright (C) 2017, Gregory Nutt, all rights reserved
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Derives in large part from Contiki:
*
* Copyright (c) 2008, Swedish Institute of Computer Science.
* All rights reserved.
* Authors: Adam Dunkels <adam@sics.se>
* Nicolas Tsiftes <nvt@sics.se>
* Niclas Finne <nfi@sics.se>
* Mathilde Durvy <mdurvy@cisco.com>
* Julien Abeille <jabeille@cisco.com>
* Joakim Eriksson <joakime@sics.se>
* Joel Hoglund <joel@sics.se>
*
* 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. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``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 INSTITUTE OR CONTRIBUTORS 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>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#ifdef CONFIG_NET_6LOWPAN_FRAG
# include "nuttx/clock.h"
#endif
#include "nuttx/net/netdev.h"
#include "nuttx/net/ip.h"
#include "nuttx/net/icmpv6.h"
#include "nuttx/net/sixlowpan.h"
#include "nuttx/wireless/ieee802154/ieee802154_mac.h"
#ifdef CONFIG_NET_PKT
# include "pkt/pkt.h"
#endif
#include "sixlowpan/sixlowpan_internal.h"
#ifdef CONFIG_NET_6LOWPAN
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Success return values from sixlowpan_frame_process */
#define INPUT_PARTIAL 0 /* Frame processed successful, packet incomplete */
#define INPUT_COMPLETE 1 /* Frame processed successful, packet complete */
/* Re-assembly timeout in clock ticks */
#define NET_6LOWPAN_TIMEOUT SEC2TICK(CONFIG_NET_6LOWPAN_MAXAGE)
/* This is the size of a buffer large enough to hold the largest uncompressed
* HC06 or HC1 headers.
*/
#ifdef CONFIG_NET_TCP
/* The basic TCP header length is TCP_HDRLEN but could include up to 16
* additional 32-bit words of optional data.
*/
# define UNCOMP_MAXHDR (IPv6_HDRLEN + TCP_HDRLEN + 16*sizeof(uint32_t))
#elif defined(CONFIG_NET_UDP)
/* The UDP header length is always 8 bytes */
# define UNCOMP_MAXHDR (IPv6_HDRLEN + UDP_HDRLEN)
#elif defined(CONFIG_NET_ICMPv6)
/* The ICMPv6 header length is a mere 4 bytes */
# define UNCOMP_MAXHDR (IPv6_HDRLEN + ICMPv6_HDRLEN)
#else
/* No other header type is handled. */
# define UNCOMP_MAXHDR IPv6_HDRLEN
#endif
/* Buffer access helpers */
#define IPv6BUF(dev) ((FAR struct ipv6_hdr_s *)((dev)->d_buf))
#define TCPBUF(dev) ((FAR struct tcp_hdr_s *)&(dev)->d_buf[IPv6_HDRLEN])
/****************************************************************************
* Private Data
****************************************************************************/
#ifdef CONFIG_NET_6LOWPAN_FRAG
/* This big buffer could be avoided with a little more effort */
static uint8_t g_bitbucket[UNCOMP_MAXHDR];
#endif
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: sixlowpan_compare_fragsrc
*
* Description:
* Check if the fragment that we just received is from the same source as
* the previosly received fragements.
*
* Input Parameters:
* radio - Radio network device driver state instance
* metadata - Characteristics of the newly received frame
*
* Returned Value:
* true if the sources are the same.
*
****************************************************************************/
static bool sixlowpan_compare_fragsrc(FAR struct radio_driver_s *radio,
FAR const void *metadata)
{
struct netdev_varaddr_s fragsrc;
int ret;
/* Extract the source address from the 'metadata' */
ret = sixlowpan_extract_srcaddr(radio, metadata, &fragsrc);
if (ret < 0)
{
nerr("ERROR: sixlowpan_extract_srcaddr failed: %d\n", ret);
return false;
}
/* The addresses cannot match if they are not the same size */
if (fragsrc.nv_addrlen == radio->r_fragsrc.nv_addrlen)
{
/* The are the same sizer, return the address comparisson */
return (memcmp(fragsrc.nv_addr, radio->r_fragsrc.nv_addr,
fragsrc.nv_addrlen) == 0);
}
return false;
}
/****************************************************************************
* Name: sixlowpan_compress_ipv6hdr
*
* Description:
* IPv6 dispatch "compression" function. Packets "Compression" when only
* IPv6 dispatch is used
*
* There is no compression in this case, all fields are sent
* inline. We just add the IPv6 dispatch byte before the packet.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | IPv6 Dsp | IPv6 header and payload ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Input Parameters:
* fptr - Pointer to the beginning of the frame under construction
* bptr - Output goes here. Normally this is a known offset into d_buf,
* may be redirected to g_bitbucket on the case of FRAGN frames.
*
* Returned Value:
* None
*
****************************************************************************/
static void sixlowpan_uncompress_ipv6hdr(FAR uint8_t *fptr, FAR uint8_t *bptr)
{
FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)bptr;
uint16_t protosize;
/* Put uncompressed IPv6 header in d_buf. */
g_frame_hdrlen += SIXLOWPAN_IPV6_HDR_LEN;
memcpy(ipv6, fptr + g_frame_hdrlen, IPv6_HDRLEN);
/* Update g_uncomp_hdrlen and g_frame_hdrlen. */
g_frame_hdrlen += IPv6_HDRLEN;
g_uncomp_hdrlen += IPv6_HDRLEN;
/* Copy the following protocol header, */
switch (ipv6->proto)
{
#ifdef CONFIG_NET_TCP
case IP_PROTO_TCP:
{
FAR struct tcp_hdr_s *tcp =
(FAR struct tcp_hdr_s *)(fptr + g_frame_hdrlen);
/* The TCP header length is encoded in the top 4 bits of the
* tcpoffset field (in units of 32-bit words).
*/
protosize = ((uint16_t)tcp->tcpoffset >> 4) << 2;
}
break;
#endif
#ifdef CONFIG_NET_UDP
case IP_PROTO_UDP:
protosize = sizeof(struct udp_hdr_s);
break;
#endif
#ifdef CONFIG_NET_ICMPv6
case IP_PROTO_ICMP6:
protosize = sizeof(struct icmpv6_hdr_s);
break;
#endif
default:
nwarn("WARNING: Unrecognized proto: %u\n", ipv6->proto);
return;
}
/* Copy the protocol header. */
memcpy((FAR uint8_t *)ipv6 + g_uncomp_hdrlen, fptr + g_frame_hdrlen,
protosize);
g_frame_hdrlen += protosize;
g_uncomp_hdrlen += protosize;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sixlowpan_frame_process
*
* Description:
* Process an incoming 6LoWPAN frame in 'iob'.
*
* If its a FRAG1 or a non-fragmented frame we first uncompress the IP
* header. The 6LoWPAN payload and possibly the uncompressed IP header
* are then copied into d_buf. An indication is returned if the packet
* in d_buf is complete (i.e., non-fragmented frame or and the last
* FRAGN frame).
*
* NOTE: We do not check for overlapping sixlowpan fragments (that is a
* SHALL in the RFC 4944 and should never happen)
*
* Input Parameters:
* radio - The radio network device driver interface.
* metadata - Metadata characterizing the received frame.
* iob - The IOB containing the frame.
*
* Returned Value:
* On success, a value greater than equal to zero is returned, either:
*
* INPUT_PARTIAL Frame processed successful, packet incomplete
* INPUT_COMPLETE Frame processed successful, packet complete
*
* Othewise a negated errno value is returned to indicate the nature of the
* failure.
*
* Assumptions:
* Network is locked
*
****************************************************************************/
static int sixlowpan_frame_process(FAR struct radio_driver_s *radio,
FAR const void *metadata, FAR struct iob_s *iob)
{
FAR uint8_t *fptr; /* Convenience pointer to beginning of the frame */
FAR uint8_t *bptr; /* Used to redirect uncompressed header to the bitbucket */
FAR uint8_t *hc1; /* Convenience pointer to HC1 data */
uint16_t fragsize = 0; /* Size of the IP packet (read from fragment) */
uint16_t paysize; /* Size of the data payload */
uint8_t fragoffset = 0; /* Offset of the fragment in the IP packet */
int reqsize; /* Required buffer size */
int hdrsize; /* Size of the IEEE802.15.4 header */
#ifdef CONFIG_NET_6LOWPAN_FRAG
FAR uint8_t *fragptr; /* Pointer to the fragmentation header */
bool isfrag = false;
bool isfirstfrag = false;
uint16_t fragtag = 0; /* Tag of the fragment */
systime_t elapsed; /* Elapsed time */
int ret;
#endif /* CONFIG_NET_6LOWPAN_FRAG */
/* Get a pointer to the payload following the IEEE802.15.4 frame header(s).
* This size includes both fragmentation and FCF headers.
*/
fptr = iob->io_data; /* Frame data is in I/O buffer */
hdrsize = iob->io_offset; /* Offset past the MAC header */
DEBUGASSERT((unsigned)hdrsize < iob->io_len);
/* Initialize global data. Locking the network guarantees that we have
* exclusive use of the global values for intermediate calculations.
*/
g_uncomp_hdrlen = 0;
g_frame_hdrlen = hdrsize;
#ifdef CONFIG_NET_6LOWPAN_FRAG
/* Since we don't support the mesh and broadcast header, the first header
* we look for is the fragmentation header. NOTE that g_frame_hdrlen
* already includes the fragementation header, if presetn.
*/
fragptr = fptr + hdrsize;
switch ((GETUINT16(fragptr, SIXLOWPAN_FRAG_DISPATCH_SIZE) & 0xf800) >> 8)
{
/* First fragment of new reassembly */
case SIXLOWPAN_DISPATCH_FRAG1:
{
/* Set up for the reassembly */
fragsize = GETUINT16(fragptr, SIXLOWPAN_FRAG_DISPATCH_SIZE) & 0x07ff;
fragtag = GETUINT16(fragptr, SIXLOWPAN_FRAG_TAG);
g_frame_hdrlen += SIXLOWPAN_FRAG1_HDR_LEN;
ninfo("FRAG1: fragsize=%d fragtag=%d fragoffset=%d\n",
fragsize, fragtag, fragoffset);
/* Indicate the first fragment of the reassembly */
isfirstfrag = true;
isfrag = true;
}
break;
case SIXLOWPAN_DISPATCH_FRAGN:
{
/* Set offset, tag, size. Offset is in units of 8 bytes. */
fragoffset = fragptr[SIXLOWPAN_FRAG_OFFSET];
fragtag = GETUINT16(fragptr, SIXLOWPAN_FRAG_TAG);
fragsize = GETUINT16(fragptr, SIXLOWPAN_FRAG_DISPATCH_SIZE) & 0x07ff;
g_frame_hdrlen += SIXLOWPAN_FRAGN_HDR_LEN;
ninfo("FRAGN: fragsize=%d fragtag=%d fragoffset=%d\n",
fragsize, fragtag, fragoffset);
ninfo("FRAGN: r_accumlen=%d paysize=%u fragsize=%u\n",
radio->r_accumlen, iob->io_len - g_frame_hdrlen, fragsize);
/* Indicate that this frame is a another fragment for reassembly */
isfrag = true;
}
break;
/* Not a fragment */
default:
break;
}
/* Check if we are currently reassembling a packet */
bptr = radio->r_dev.d_buf;
if (radio->r_accumlen > 0)
{
/* If reassembly timed out, cancel it */
elapsed = clock_systimer() - radio->r_time;
if (elapsed > NET_6LOWPAN_TIMEOUT)
{
nwarn("WARNING: Reassembly timed out\n");
radio->r_pktlen = 0;
radio->r_accumlen = 0;
}
/* In this case what we expect is that the next frame will hold the
* next FRAGN of the sequence. We have to handle a few exeptional
* cases that we need to handle:
*
* 1. If we are currently reassembling a packet, but have just received
* the first fragment of another packet. We can either ignore it and
* hope to receive the rest of the under-reassembly packet fragments,
* or we can discard the previous packet altogether, and start
* reassembling the new packet. Here we discard the previous packet,
* and start reassembling the new packet.
* 2. The new frame is not a fragment. We should be able to handle this
* case, but we cannot because that would require two packet buffers.
* It could be handled with a more extensive design.
* 3. The fragment came from a different sender. What would this mean?
*
*/
else if (!isfrag)
{
/* Discard the partially assembled packet */
nwarn("WARNING: Non-fragment frame received during reassembly\n");
radio->r_pktlen = 0;
radio->r_accumlen = 0;
}
/* It is a fragment of some kind. Drop any zero length fragments */
else if (fragsize == 0)
{
nwarn("WARNING: Dropping zero-length 6LoWPAN fragment\n");
return INPUT_PARTIAL;
}
/* A non-zero, first fragement received while we are in the middle of
* rassembly. Discard the partially assembled packet and start over.
*/
else if (isfirstfrag)
{
nwarn("WARNING: First fragment frame received during reassembly\n");
radio->r_pktlen = 0;
radio->r_accumlen = 0;
}
/* Verify that this fragment is part of that reassembly sequence */
else if (fragsize != radio->r_pktlen || radio->r_reasstag != fragtag ||
!sixlowpan_compare_fragsrc(radio, metadata))
{
/* The packet is a fragment that does not belong to the packet
* being reassembled or the packet is not a fragment.
*/
nwarn("WARNING: Dropping 6LoWPAN packet that is not a fragment of "
"the packet currently being reassembled\n");
return -EPERM;
}
else
{
/* Looks good. We are currently processing a reassembling sequence
* and we recieved a valid FRAGN fragment. Redirect the header
* uncompression to our bitbucket.
*/
bptr = g_bitbucket;
}
}
/* There is no reassembly in progress. Check if we received a fragment */
else if (isfrag)
{
/* Another case that we have to handle is if a FRAGN fragment of a
* reassembly is received, but we are not currently reassembling a
* packet. I think we have no choice but to drop the packet in this
* case.
*/
if (!isfirstfrag)
{
nwarn("WARNING: FRAGN 6LoWPAN fragment while not reassembling\n");
return -EPERM;
}
/* Drop the packet if it cannot fit into the d_buf */
if (fragsize > CONFIG_NET_6LOWPAN_MTU)
{
nwarn("WARNING: Reassembled packet size exeeds CONFIG_NET_6LOWPAN_MTU\n");
return -ENOSPC;
}
radio->r_pktlen = fragsize;
radio->r_reasstag = fragtag;
radio->r_time = clock_systimer();
ninfo("Starting reassembly: r_pktlen %u, r_reasstag %d\n",
radio->r_pktlen, radio->r_reasstag);
/* Extract the source address from the 'metadata'. NOTE that the size
* of the source address may be different than our local, destination
* address.
*/
ret = sixlowpan_extract_srcaddr(radio, metadata, &radio->r_fragsrc);
if (ret < 0)
{
nerr("ERROR: sixlowpan_extract_srcaddr failed: %d\n", ret);
return ret;
}
}
#endif /* CONFIG_NET_6LOWPAN_FRAG */
/* Process next dispatch and headers */
hc1 = fptr + g_frame_hdrlen;
#ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06
if ((hc1[SIXLOWPAN_HC1_DISPATCH] & SIXLOWPAN_DISPATCH_IPHC_MASK) == SIXLOWPAN_DISPATCH_IPHC)
{
ninfo("IPHC Dispatch\n");
sixlowpan_uncompresshdr_hc06(radio, metadata, fragsize, iob, fptr, bptr);
}
else
#endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC06 */
#ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC1
if (hc1[SIXLOWPAN_HC1_DISPATCH] == SIXLOWPAN_DISPATCH_HC1)
{
ninfo("HC1 Dispatch\n");
sixlowpan_uncompresshdr_hc1(radio, metadata, fragsize, iob, fptr, bptr);
}
else
#endif /* CONFIG_NET_6LOWPAN_COMPRESSION_HC1 */
if (hc1[SIXLOWPAN_HC1_DISPATCH] == SIXLOWPAN_DISPATCH_IPV6)
{
ninfo("IPv6 Dispatch\n");
sixlowpan_uncompress_ipv6hdr(fptr, bptr);
}
else
{
/* Unknown or unsupported header */
nwarn("WARNING: Unknown dispatch: %u\n", hc1[SIXLOWPAN_HC1_DISPATCH]);
return -ENOSYS;
}
#ifdef CONFIG_NET_6LOWPAN_FRAG
/* Is this the first fragment is a sequence? */
if (isfirstfrag)
{
/* Yes.. Remember the offset from the beginning of d_buf where we
* begin placing the data payload.
*/
radio->r_boffset = g_uncomp_hdrlen;
}
/* No.. is this a subsequent fragment in the same sequence? */
else if (isfrag)
{
/* Yes, recover the offset from the beginning of the d_buf where
* we began placing payload data.
*/
g_uncomp_hdrlen = radio->r_boffset;
}
#endif /* CONFIG_NET_6LOWPAN_FRAG */
/* Copy "payload" from the frame buffer to the IEEE802.15.4 MAC driver's
* packet buffer, d_buf. If this frame is a first fragment or not part of
* a fragmented packet, we have already copied the compressed headers,
* g_uncomp_hdrlen and g_frame_hdrlen are non-zerio, fragoffset is.
*/
paysize = iob->io_len - g_frame_hdrlen;
if (paysize > CONFIG_NET_6LOWPAN_MTU)
{
nwarn("WARNING: Packet dropped due to payload (%u) > packet buffer (%u)\n",
paysize, CONFIG_NET_6LOWPAN_MTU);
return -ENOSPC;
}
/* Sanity-check size of incoming packet to avoid buffer overflow */
reqsize = g_uncomp_hdrlen + (fragoffset << 3) + paysize;
if (reqsize > CONFIG_NET_6LOWPAN_MTU)
{
nwarn("WARNING: Required buffer size: %u+%u+%u=%u Available=%u\n",
g_uncomp_hdrlen, (fragoffset << 3), paysize,
reqsize, CONFIG_NET_6LOWPAN_MTU);
return -ENOMEM;
}
memcpy(radio->r_dev.d_buf + g_uncomp_hdrlen + (fragoffset << 3),
fptr + g_frame_hdrlen, paysize);
#ifdef CONFIG_NET_6LOWPAN_FRAG
/* Update radio->r_accumlen if the frame is a fragment, radio->r_pktlen
* otherwise.
*/
if (isfrag)
{
/* Check if it is the last fragment to be processed.
*
* If this is the last fragment, we may shave off any extrenous
* bytes at the end. We must be liberal in what we accept.
*/
radio->r_accumlen = g_uncomp_hdrlen + (fragoffset << 3) + paysize;
}
else
{
radio->r_pktlen = paysize + g_uncomp_hdrlen;
}
/* If we have a full IP packet in sixlowpan_buf, deliver it to
* the IP stack
*/
ninfo("r_accumlen=%d r_pktlen=%d paysize=%d\n",
radio->r_accumlen, radio->r_pktlen, paysize);
if (radio->r_accumlen == 0 || radio->r_accumlen >= radio->r_pktlen)
{
ninfo("IP packet ready (length %d)\n", radio->r_pktlen);
radio->r_dev.d_len = radio->r_pktlen;
radio->r_pktlen = 0;
radio->r_accumlen = 0;
return INPUT_COMPLETE;
}
return INPUT_PARTIAL;
#else
/* Deliver the packet to the IP stack */
radio->r_dev.d_len = paysize + g_uncomp_hdrlen;
return INPUT_COMPLETE;
#endif /* CONFIG_NET_6LOWPAN_FRAG */
}
/****************************************************************************
* Name: sixlowpan_dispatch
*
* Description:
* Inject the packet in d_buf into the network for normal packet processing.
*
* Input Parmeters
* radio - The IEEE802.15.4 MAC network driver interface.
*
* Returned Value:
* None
*
****************************************************************************/
static int sixlowpan_dispatch(FAR struct radio_driver_s *radio)
{
sixlowpan_dumpbuffer("Incoming packet",
(FAR const uint8_t *)IPv6BUF(&radio->r_dev),
radio->r_dev.d_len);
#ifdef CONFIG_NET_PKT
/* When packet sockets are enabled, feed the frame into the packet tap */
ninfo("Packet tap\n");
pkt_input(&radio->r_dev);
#endif
/* We only accept IPv6 packets. */
ninfo("IPv6 packet dispatch\n");
NETDEV_RXIPV6(&radio->r_dev);
/* Give the IPv6 packet to the network layer. NOTE: If there is a
* problem with IPv6 header, it will be silently dropped and d_len will
* be set to zero. Oddly, ipv6_input() will return OK in this case.
*/
return ipv6_input(&radio->r_dev);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: sixlowpan_input
*
* Description:
* Process an incoming 6LoWPAN frame.
*
* This function is called when the radio device driver has received an
* frame from the network. The frame from the device driver must be
* provided in by the IOB frame argument of the function call:
*
* - The frame data is in the IOB io_data[] buffer,
* - The length of the frame is in the IOB io_len field, and
* - The offset past and radio MAC header is provided in the io_offset
* field.
*
* The frame argument may refer to a single frame (a list of length one)
* or may it be the head of a list of multiple frames.
*
* - The io_flink field points to the next frame in the list (if enable)
* - The last frame in the list will have io_flink == NULL.
*
* An non-NULL d_buf of size CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE
* must also be provided. The frame will be decompressed and placed in
* the d_buf. Fragmented packets will also be reassembled in the d_buf as
* they are received (meaning for the driver, that two packet buffers are
* required: One for reassembly of RX packets and one used for TX polling).
*
* After each frame is processed into d_buf, the IOB is deallocated. If
* reassembly is incomplete, the partially reassembled packet must be
* preserved by the radio network drvier and provided again when the next
* frame is received.
*
* When the packet in the d_buf is fully reassembled, it will be provided
* to the network as with any other received packet. d_len will be set
* the length of the uncompressed, reassembled packet.
*
* After the network processes the packet, d_len will be set to zero.
* Network logic may also decide to send a response to the packet. In
* that case, the outgoing network packet will be placed in d_buf and
* d_len will be set to a non-zero value. That case is handled by this
* function.
*
* If that case occurs, the packet will be converted to a list of
* compressed and possibly fragmented frames and provided to the MAC
* network driver via the req_data() method as with other TX operations.
*
* Input Parameters:
* radio The radio network driver interface.
* framelist - The head of an incoming list of frames. Normally this
* would be a single frame. A list may be provided if
* appropriate, however.
* metadata - Meta data characterizing the received packet. The specific
* type of this metadata is obfuscated and depends on the
* type of the radio driver. This could be be either
* (1) struct ieee802154_data_ind_s for an IEEE 802.15.4
* radio, or (2) struct pktradio_metadata_s for a non-standard
* packet radio.
*
* If there are multilple frames in the list, this metadata
* must apply to all of the frames in the list.
*
* Returned Value:
* Ok is returned on success; Othewise a negated errno value is returned.
*
****************************************************************************/
int sixlowpan_input(FAR struct radio_driver_s *radio,
FAR struct iob_s *framelist, FAR const void *metadata)
{
int ret = -EINVAL;
DEBUGASSERT(radio != NULL && framelist != NULL);
/* Verify that an frame has been provided. */
while (framelist != NULL)
{
FAR struct iob_s *iob;
/* Remove the IOB containing the frame from the device structure */
iob = framelist;
framelist = iob->io_flink;
sixlowpan_dumpbuffer("Incoming frame", iob->io_data, iob->io_len);
/* Process the frame, decompressing it into the packet buffer */
ret = sixlowpan_frame_process(radio, metadata, iob);
/* Free the IOB the held the consumed frame */
iob_free(iob);
/* Was the frame successfully processed? Is the packet in d_buf fully
* reassembled?
*/
if (ret == INPUT_COMPLETE)
{
/* Inject the uncompressed, reassembled packet into the network */
ret = sixlowpan_dispatch(radio);
if (ret >= 0)
{
/* Check if this resulted in a request to send an outgoing
* packet.
*/
if (radio->r_dev.d_len > 0)
{
FAR struct ipv6_hdr_s *ipv6hdr;
FAR uint8_t *buffer;
struct netdev_varaddr_s destmac;
size_t hdrlen;
size_t buflen;
/* The IPv6 header followed by TCP or UDP headers should
* lie at the beginning of d_buf since there is no link
* layer protocol header.
*/
ipv6hdr = IPv6BUF(&radio->r_dev);
/* Get the IEEE 802.15.4 MAC address of the destination.
* This assumes an encoding of the MAC address in the IPv6
* address.
*/
ret = sixlowpan_destaddrfromip(radio, ipv6hdr->destipaddr,
&destmac);
if (ret < 0)
{
nerr("ERROR: Failed to dest MAC address: %d\n", ret);
goto drop;
}
/* The data payload should follow the IPv6 header plus
* the protocol header.
*/
switch (ipv6hdr->proto)
{
#ifdef CONFIG_NET_TCP
case IP_PROTO_TCP:
{
FAR struct tcp_hdr_s *tcp = TCPBUF(&radio->r_dev);
uint16_t tcplen;
/* The TCP header length is encoded in the top 4 bits
* of the tcpoffset field (in units of 32-bit words).
*/
tcplen = ((uint16_t)tcp->tcpoffset >> 4) << 2;
hdrlen = IPv6_HDRLEN + tcplen;
}
break;
#endif
#ifdef CONFIG_NET_UDP
case IP_PROTO_UDP:
{
hdrlen = IPv6_HDRLEN + UDP_HDRLEN;
}
break;
#endif
#ifdef CONFIG_NET_ICMPv6
case IP_PROTO_ICMP6:
{
hdrlen = IPv6_HDRLEN + ICMPv6_HDRLEN;
}
break;
#endif
default:
{
nwarn("WARNING: Unsupported protoype: %u\n",
ipv6hdr->proto);
ret = -EPROTO;
goto drop;
}
}
if (hdrlen < radio->r_dev.d_len)
{
nwarn("WARNING: Packet to small: Have %u need >%u\n",
radio->r_dev.d_len, hdrlen);
ret = -ENOBUFS;
goto drop;
}
/* Convert the outgoing packet into a frame list. */
buffer = radio->r_dev.d_buf + hdrlen;
buflen = radio->r_dev.d_len - hdrlen;
ret = sixlowpan_queue_frames(radio, ipv6hdr, buffer, buflen,
&destmac);
drop:
radio->r_dev.d_len = 0;
}
}
}
}
return ret;
}
#endif /* CONFIG_NET_6LOWPAN */