| /**************************************************************************** |
| * net/sixlowpan/sixlowpan_input.c |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| * 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> |
| |
| #include "nuttx/mm/iob.h" |
| #include "nuttx/net/netdev.h" |
| #include "nuttx/net/radiodev.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 <nuttx/net/pkt.h> |
| # 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 */ |
| |
| /* 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 TCPBUF(dev) ((FAR struct tcp_hdr_s *)&(dev)->d_buf[IPv6_HDRLEN]) |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* This big buffer could be avoided with a little more effort */ |
| |
| static uint8_t g_bitbucket[UNCOMP_MAXHDR]; |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * 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. |
| * proto - True: Copy the protocol header following the IPv6 header too. |
| * |
| * 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; |
| |
| /* 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; |
| } |
| |
| /**************************************************************************** |
| * Name: sixlowpan_uncompress_ipv6proto |
| * |
| * Description: |
| * Copy the protocol header following the IPv4 header |
| * |
| * 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. |
| * proto - True: Copy the protocol header following the IPv6 header too. |
| * |
| * Returned Value: |
| * The size of the protocol header that was copied. |
| * |
| ****************************************************************************/ |
| |
| static uint16_t sixlowpan_uncompress_ipv6proto(FAR uint8_t *fptr, |
| FAR uint8_t *bptr) |
| { |
| FAR struct ipv6_hdr_s *ipv6 = (FAR struct ipv6_hdr_s *)bptr; |
| uint16_t protosize = 0; |
| |
| /* 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 0; |
| } |
| |
| /* 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; |
| return 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 |
| * |
| * Otherwise 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 struct sixlowpan_reassbuf_s *reass; |
| struct netdev_varaddr_s fragsrc; |
| 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 */ |
| FAR uint8_t *fragptr; /* Pointer to the fragmentation header */ |
| uint16_t fragsize = 0; /* Size of the IP packet (read from fragment) */ |
| uint16_t paysize; /* Size of the data payload */ |
| uint16_t fragtag = 0; /* Tag of the fragment */ |
| uint8_t fragoffset = 0; /* Offset of the fragment in the IP packet */ |
| uint8_t protosize = 0; /* Length of the protocol header (treated like payload) */ |
| bool isfrag = false; /* true: Frame is a fragment */ |
| bool isfrag1 = false; /* true: Frame is the first fragment of the series */ |
| int reqsize; /* Required buffer size */ |
| int hdrsize; /* Size of the IEEE802.15.4 header */ |
| int ret; |
| |
| /* 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; |
| |
| /* 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 fragmentation 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); |
| |
| /* Drop any zero length fragments */ |
| |
| if (fragsize == 0) |
| { |
| nwarn("WARNING: Dropping zero-length 6LoWPAN fragment\n"); |
| return INPUT_PARTIAL; |
| } |
| |
| /* Drop the packet if it cannot fit into the d_buf */ |
| |
| if (fragsize > CONFIG_NET_6LOWPAN_PKTSIZE) |
| { |
| nwarn("WARNING: Reassembled packet size exceeds " |
| "CONFIG_NET_6LOWPAN_PKTSIZE\n"); |
| return -ENOSPC; |
| } |
| |
| /* 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 ret; |
| } |
| |
| /* Allocate a new reassembly buffer */ |
| |
| reass = sixlowpan_reass_allocate(fragtag, &fragsrc); |
| if (reass == NULL) |
| { |
| nerr("ERROR: Failed to allocate a reassembly buffer\n"); |
| return -ENOMEM; |
| } |
| |
| radio->r_dev.d_buf = reass->rb_buf; |
| radio->r_dev.d_len = 0; |
| reass->rb_pktlen = fragsize; |
| |
| /* Indicate the first fragment of the reassembly */ |
| |
| bptr = reass->rb_buf; |
| isfrag1 = true; |
| isfrag = true; |
| } |
| break; |
| |
| case SIXLOWPAN_DISPATCH_FRAGN: |
| { |
| /* Get 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; |
| |
| /* 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 ret; |
| } |
| |
| /* Find the existing reassembly buffer |
| * with the same tag and source address |
| */ |
| |
| reass = sixlowpan_reass_find(fragtag, &fragsrc); |
| if (reass == NULL) |
| { |
| nerr("ERROR: Failed to find a reassembly buffer for tag=%04x\n", |
| fragtag); |
| return -ENOENT; |
| } |
| |
| if (fragsize != reass->rb_pktlen) |
| { |
| /* The packet is a fragment but its size does not match. */ |
| |
| nwarn("WARNING: Dropping 6LoWPAN packet. Bad fragsize: %u vs %u\n", |
| fragsize, reass->rb_pktlen); |
| ret = -EPERM; |
| goto errout_with_reass; |
| } |
| |
| radio->r_dev.d_buf = reass->rb_buf; |
| radio->r_dev.d_len = 0; |
| |
| ninfo("FRAGN: fragsize=%d fragtag=%d fragoffset=%d\n", |
| fragsize, fragtag, fragoffset); |
| ninfo("FRAGN: rb_accumlen=%d paysize=%u fragsize=%u\n", |
| reass->rb_accumlen, iob->io_len - g_frame_hdrlen, fragsize); |
| |
| /* Indicate that this frame is a another fragment for reassembly */ |
| |
| bptr = g_bitbucket; |
| isfrag = true; |
| } |
| break; |
| |
| /* Not a fragment */ |
| |
| default: |
| /* We still need a packet buffer. But in this case, the driver should |
| * have provided one. |
| */ |
| |
| DEBUGASSERT(radio->r_dev.d_buf != NULL); |
| reass = (FAR struct sixlowpan_reassbuf_s *)radio->r_dev.d_buf; |
| reass->rb_pool = REASS_POOL_RADIO; |
| bptr = reass->rb_buf; |
| break; |
| } |
| |
| /* 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"); |
| |
| /* Uncompress the IPv6 header */ |
| |
| sixlowpan_uncompress_ipv6hdr(fptr, bptr); |
| |
| /* A protocol header will follow the IPv6 header only on a non- |
| * fragmented packet or on the first fragment of a fragmented |
| * packet. |
| */ |
| |
| if (!isfrag || isfrag1) |
| { |
| protosize = sixlowpan_uncompress_ipv6proto(fptr, bptr); |
| } |
| } |
| else |
| { |
| /* Unknown or unsupported header */ |
| |
| nwarn("WARNING: Unknown dispatch: %u\n", hc1[SIXLOWPAN_HC1_DISPATCH]); |
| ret = -ENOSYS; |
| goto errout_with_reass; |
| } |
| |
| /* Is this the first fragment is a sequence? */ |
| |
| if (isfrag1) |
| { |
| /* Yes.. Remember the offset from the beginning of d_buf where we |
| * begin placing the data payload. |
| */ |
| |
| reass->rb_boffset = g_uncomp_hdrlen - protosize; |
| } |
| |
| /* 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 = reass->rb_boffset; |
| } |
| |
| /* 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_PKTSIZE) |
| { |
| nwarn("Packet dropped due to payload (%u) > packet buffer (%u)\n", |
| paysize, CONFIG_NET_6LOWPAN_PKTSIZE); |
| ret = -ENOSPC; |
| goto errout_with_reass; |
| } |
| |
| /* Sanity-check size of incoming packet to avoid buffer overflow */ |
| |
| reqsize = g_uncomp_hdrlen + (fragoffset << 3) + paysize; |
| if (reqsize > CONFIG_NET_6LOWPAN_PKTSIZE) |
| { |
| nwarn("WARNING: Required buffer size: %u+%u+%u=%u Available=%u\n", |
| g_uncomp_hdrlen, (fragoffset << 3), paysize, |
| reqsize, CONFIG_NET_6LOWPAN_PKTSIZE); |
| ret = -ENOMEM; |
| goto errout_with_reass; |
| } |
| |
| memcpy(radio->r_dev.d_buf + g_uncomp_hdrlen + (fragoffset << 3), |
| fptr + g_frame_hdrlen, paysize); |
| |
| /* Update reass->rb_accumlen if the frame is a fragment, reass->rb_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. |
| */ |
| |
| reass->rb_accumlen = g_uncomp_hdrlen + (fragoffset << 3) + paysize; |
| } |
| else |
| { |
| reass->rb_pktlen = paysize + g_uncomp_hdrlen; |
| } |
| |
| /* If we have a full IP packet in sixlowpan_buf, deliver it to |
| * the IP stack |
| */ |
| |
| ninfo("rb_accumlen=%d rb_pktlen=%d paysize=%d\n", |
| reass->rb_accumlen, reass->rb_pktlen, paysize); |
| |
| if (reass->rb_accumlen == 0 || reass->rb_accumlen >= reass->rb_pktlen) |
| { |
| ninfo("IP packet ready (length %d)\n", reass->rb_pktlen); |
| |
| radio->r_dev.d_buf = reass->rb_buf; |
| radio->r_dev.d_len = reass->rb_pktlen; |
| reass->rb_active = false; |
| reass->rb_pktlen = 0; |
| reass->rb_accumlen = 0; |
| return INPUT_COMPLETE; |
| } |
| |
| radio->r_dev.d_buf = NULL; |
| radio->r_dev.d_len = 0; |
| return INPUT_PARTIAL; |
| |
| errout_with_reass: |
| sixlowpan_reass_free(reass); |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: sixlowpan_dispatch |
| * |
| * Description: |
| * Inject the packet in d_buf into the network for normal packet |
| * processing. |
| * |
| * Input Parameters: |
| * radio - The IEEE802.15.4 MAC network driver interface. |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static int sixlowpan_dispatch(FAR struct radio_driver_s *radio) |
| { |
| FAR struct sixlowpan_reassbuf_s *reass; |
| int ret; |
| |
| #ifdef CONFIG_NET_6LOWPAN_DUMPBUFFER |
| struct net_driver_s *dev = &radio->r_dev; |
| |
| sixlowpan_dumpbuffer("Incoming packet", |
| (FAR const uint8_t *)IPv6BUF, |
| radio->r_dev.d_len); |
| #endif |
| |
| #ifdef CONFIG_NET_PKT |
| /* When packet sockets are enabled, feed the frame into the 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. |
| */ |
| |
| ret = ipv6_input(&radio->r_dev); |
| |
| /* Free the reassemby buffer */ |
| |
| reass = (FAR struct sixlowpan_reassbuf_s *)radio->r_dev.d_buf; |
| DEBUGASSERT(reass != NULL); |
| sixlowpan_reass_free(reass); |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * 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_PKTSIZE + |
| * 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 driver 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 multiple frames in the list, this metadata |
| * must apply to all of the frames in the list. |
| * |
| * Returned Value: |
| * Zero (OK) is returned if the the frame was consumed; Otherwise a negated |
| * errno value is returned. |
| * |
| ****************************************************************************/ |
| |
| int sixlowpan_input(FAR struct radio_driver_s *radio, |
| FAR struct iob_s *framelist, FAR const void *metadata) |
| { |
| struct net_driver_s *dev = &radio->r_dev; |
| int ret = -EINVAL; |
| uint8_t *d_buf_backup; |
| |
| DEBUGASSERT(radio != NULL && framelist != NULL); |
| |
| /* Sixlowpan modifies the d_buf to process fragments using reassembly |
| * buffers. Save the value of d_buf on entry and set it back before |
| * returning |
| */ |
| |
| d_buf_backup = dev->d_buf; |
| |
| /* 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); |
| |
| /* If the frame was a valid 6LoWPAN frame, free the IOB the held the |
| * consumed frame. Otherwise, the frame must stay allocated since the |
| * MAC layer will try and pass it to another receiver to see if that |
| * receiver wants it. |
| */ |
| |
| if (ret >= 0) |
| { |
| 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 (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; |
| |
| /* 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_nexthopaddr(radio, ipv6hdr->destipaddr, |
| &destmac); |
| if (ret < 0) |
| { |
| nerr("ERROR: Failed to get 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(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 prototype: %u\n", |
| ipv6hdr->proto); |
| goto drop; |
| } |
| } |
| |
| if (hdrlen > dev->d_len) |
| { |
| nwarn("WARNING: Packet too small: Have %u need >%zu\n", |
| dev->d_len, hdrlen); |
| goto drop; |
| } |
| |
| /* Convert the outgoing packet into a frame list. */ |
| |
| buffer = dev->d_buf + hdrlen; |
| buflen = dev->d_len - hdrlen; |
| |
| ret = sixlowpan_queue_frames(radio, ipv6hdr, buffer, |
| buflen, &destmac); |
| drop: |
| dev->d_len = 0; |
| |
| /* We consumed the frame, so we must return 0. */ |
| |
| ret = 0; |
| } |
| } |
| } |
| } |
| |
| /* Restore the d_buf back to it's original state */ |
| |
| dev->d_buf = d_buf_backup; |
| |
| return ret; |
| } |
| |
| #endif /* CONFIG_NET_6LOWPAN */ |