diff --git a/configs/b-l475e-iot01a/README.txt b/configs/b-l475e-iot01a/README.txt index 63c9744736..84310d6b6f 100644 --- a/configs/b-l475e-iot01a/README.txt +++ b/configs/b-l475e-iot01a/README.txt @@ -610,7 +610,17 @@ Configuration sub-directories unnecessarily busy. There is some prototype code to do just this in the driver, but does not seem to work. - 2017-08-24: There is only a single buffer for reassemblying larger - packets. This could be an important issue for the hub configuration + 2017-08-26: There was only a single buffer for reassemblying larger + packets. This could be a problem issue for the hub configuration which really needs the capability concurrently reassemble multiple - incoming streams. + incoming streams. The design was extended to support multiple + reassembly buffers. + + Initial testing shows the same basic behavior as noted before: + The UDP test works and TCP test (usually) works. There are, + however, are errors in reported by the hub in the TCP test. + Occassionally the test will hang when ther server echoes the data + back to the client. These errors are presumably the result of ACKs + from the receiver colliding with frames from the sender. + + Needs more investigation. diff --git a/configs/clicker2-stm32/README.txt b/configs/clicker2-stm32/README.txt index b44c5f9767..0f430595df 100644 --- a/configs/clicker2-stm32/README.txt +++ b/configs/clicker2-stm32/README.txt @@ -671,6 +671,7 @@ Configurations two star endpoints via the hub, the frames are correctly directed to the hub. However, they are not being forwarded to the other endpoint. + 2017-06-30: The failure to forward is understood: When the star endpoint sent the IPv6 destination address, the HC06 compression logic elided the address -- meaning that it could be reconstructed @@ -686,13 +687,16 @@ Configurations some additional fixes for byte ordering in 16-bit and 64-bit compressed IPv6 addresses, then all tests are working as expected: TCP, UDP, Telnet. + 2017-08-05: It looks like I have lost one of my Clicker2-STM32 boards. This means that I will not be able to do any regression testing as changes are made to the radio interfaces and 6LoWPAN :( - 2017-08-24: There is only a single buffer for reassemblying larger - packets. This could be an important issue for the hub configuration + + 2017-08-26: There was only a single buffer for reassemblying larger + packets. This could be a problem issue for the hub configuration which really needs the capability concurrently reassemble multiple - incoming streams. + incoming streams. The design was extended to support multiple + reassembly buffers but have not yet been verified on this platform. nsh: diff --git a/configs/nucleo-l432kc/include/nucleo-l432kc.h b/configs/nucleo-l432kc/include/nucleo-l432kc.h index d615c0cdce..36d22c39ec 100644 --- a/configs/nucleo-l432kc/include/nucleo-l432kc.h +++ b/configs/nucleo-l432kc/include/nucleo-l432kc.h @@ -203,7 +203,8 @@ */ /* prescaler common to all PLL inputs; will be 1 (XXX source is implicitly - as per comment above HSI) */ + * as per comment above HSI) + */ #define STM32L4_PLLCFG_PLLM RCC_PLLCFG_PLLM(1) diff --git a/configs/same70-xplained/README.txt b/configs/same70-xplained/README.txt index 4841cab264..2252fc7b94 100644 --- a/configs/same70-xplained/README.txt +++ b/configs/same70-xplained/README.txt @@ -997,7 +997,7 @@ Tickless OS will have an error of 0.6% and will have inaccuracies that will effect the time due to long term error build-up. - Using the slow clock clock input, the Tickless support is functional, + Using the slow clock input, the Tickless support is functional, however, there are inaccuracies in delays. For example, nsh> sleep 10 @@ -1318,7 +1318,7 @@ Configuration sub-directories mrf24j40-starhub - This configuration implement a hub node in a 6LoWPAN start network. + This configuration implements a hub node in a 6LoWPAN start network. It is intended for the us the mrf24j40-starpoint configuration with the clicker2-stm32 configurations. Essentially, the SAME70 Xplained plays the roll of the hub in the configuration and the clicker2-stm32 @@ -1383,10 +1383,11 @@ Configuration sub-directories No significant functional testing has yet been performed. - 2017-08-24: There is only a single buffer for reassemblying larger - packets. This could be an important issue for the hub configuration + 2017-08-26: There was only a single buffer for reassemblying larger + packets. This could be a problem issue for the hub configuration which really needs the capability concurrently reassemble multiple - incoming streams. + incoming streams. The design was extended to support multiple + reassembly buffers but have not yet been verified on this platform. netnsh: diff --git a/configs/samv71-xult/README.txt b/configs/samv71-xult/README.txt index c58270a001..204271892d 100644 --- a/configs/samv71-xult/README.txt +++ b/configs/samv71-xult/README.txt @@ -1956,10 +1956,11 @@ Configuration sub-directories The SPI signals look clean on the board and the MRF24J40 seems fully functional. - 2017-08-24: There is only a single buffer for reassemblying larger - packets. This could be an important issue for the hub configuration + 2017-08-26: There was only a single buffer for reassemblying larger + packets. This could be a problem issue for the hub configuration which really needs the capability concurrently reassemble multiple - incoming streams. + incoming streams. The design was extended to support multiple + reassembly buffers but additional testing is needed. mxtxplnd: diff --git a/drivers/wireless/spirit/drivers/spirit_netdev.c b/drivers/wireless/spirit/drivers/spirit_netdev.c index d5760b58c9..bd2abf9f1a 100644 --- a/drivers/wireless/spirit/drivers/spirit_netdev.c +++ b/drivers/wireless/spirit/drivers/spirit_netdev.c @@ -127,6 +127,7 @@ #include #include #include +#include #include #include @@ -363,6 +364,12 @@ int spirit_hw_initialize(FAR struct spirit_driver_s *dev, * Private Data ****************************************************************************/ +#ifdef CONFIG_NET_6LOWPAN +/* One single packet buffer */ + +static struct sixlowpan_reassbuf_s g_iobuffer; +#endif + /* Spirit radio initialization */ static const struct radio_init_s g_radio_init = @@ -1043,6 +1050,11 @@ static void spirit_receive_work(FAR void *arg) iob = pktmeta->pm_iob; pktmeta->pm_iob = NULL; + /* Make sure the our single packet buffer is attached */ + + priv->radio.r_dev.d_buf = g_iobuffer.rb_buf; + priv->radio.r_dev.d_len = 0; + /* Send the next frame to the network */ wlinfo("Send frame %p to the network: Offset=%u Length=%u\n", @@ -1137,7 +1149,7 @@ static void spirit_interrupt_work(FAR void *arg) #ifdef CONFIG_SPIRIT_FIFOS irqstatus.IRQ_RX_FIFO_ALMOST_FULL = 0; - /* Discard any RX buffer that might have been allocated */ + /* Discard any packet buffer that might have been allocated */ if (priv->rxbuffer != NULL) { @@ -1281,7 +1293,12 @@ static void spirit_interrupt_work(FAR void *arg) if (priv->state != DRIVER_STATE_RECEIVING) { - DEBUGASSERT(priv->state == DRIVER_STATE_IDLE); + /* As a race condition, the TX state, but overriden by concurrent + * RX activity? This assertion here *does* fire: + * + * DEBUGASSERT(priv->state == DRIVER_STATE_IDLE); + */ + priv->state = DRIVER_STATE_RECEIVING; } @@ -1320,7 +1337,7 @@ static void spirit_interrupt_work(FAR void *arg) irqstatus.IRQ_RX_FIFO_ALMOST_FULL = 0; - /* There should be a RX buffer that was allocated when the data sync + /* There should be a packet buffer that was allocated when the data sync * interrupt was processed. */ @@ -1473,7 +1490,7 @@ static void spirit_interrupt_work(FAR void *arg) wlinfo("RX FIFO almost full\n"); - /* There should be a RX buffer that was allocated when the data sync + /* There should be a packet buffer that was allocated when the data sync * interrupt was processed. */ @@ -1571,7 +1588,7 @@ static void spirit_interrupt_work(FAR void *arg) */ #ifdef CONFIG_SPIRIT_FIFOS - /* Discard any RX buffer that might have been allocated */ + /* Discard any packet buffer that might have been allocated */ if (priv->rxbuffer != NULL) { @@ -1752,6 +1769,12 @@ static void spirit_txpoll_work(FAR void *arg) net_lock(); +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->radio.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + /* Do nothing if the network is not yet UP */ if (!priv->ifup) @@ -2752,7 +2775,6 @@ int spirit_netdev_initialize(FAR struct spi_dev_s *spi, FAR struct spirit_driver_s *priv; FAR struct radio_driver_s *radio; FAR struct net_driver_s *dev; - FAR uint8_t *pktbuf; int ret; /* Allocate a driver state structure instance */ @@ -2764,16 +2786,6 @@ int spirit_netdev_initialize(FAR struct spi_dev_s *spi, return -ENOMEM; } - /* Allocate a packet buffer */ - - pktbuf = (uint8_t *)kmm_zalloc(CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE); - if (priv == NULL) - { - wlerr("ERROR: Failed to allocate a packet buffer\n"); - ret = -ENOMEM; - goto errout_with_alloc; - } - /* Attach the interface, lower driver, and devops */ priv->lower = lower; @@ -2798,7 +2810,6 @@ int spirit_netdev_initialize(FAR struct spi_dev_s *spi, /* Initialize the common network device fields */ dev = &radio->r_dev; - dev->d_buf = pktbuf; /* Single packet buffer */ dev->d_ifup = spirit_ifup; /* I/F up (new IP address) callback */ dev->d_ifdown = spirit_ifdown; /* I/F down callback */ dev->d_txavail = spirit_txavail; /* New TX data callback */ @@ -2839,7 +2850,7 @@ int spirit_netdev_initialize(FAR struct spi_dev_s *spi, if (ret < 0) { wlerr("ERROR: Failed to attach interrupt: %d\n", ret); - goto errout_with_pktbuf; + goto errout_with_alloc; } /* Enable Radio IRQ */ @@ -2850,11 +2861,6 @@ int spirit_netdev_initialize(FAR struct spi_dev_s *spi, errout_with_attach: (void)lower->attach(lower, NULL, NULL); -errout_with_pktbuf: -#if 0 - kmm_free(pktbuf); -#endif - errout_with_alloc: kmm_free(priv); return ret; diff --git a/include/nuttx/net/radiodev.h b/include/nuttx/net/radiodev.h index c796cb2d5e..71c43c1adf 100644 --- a/include/nuttx/net/radiodev.h +++ b/include/nuttx/net/radiodev.h @@ -42,8 +42,6 @@ #include -#include -#include #include #if defined(CONFIG_NET_6LOWPAN) || defined(CONFIG_NET_IEEE802154) @@ -76,22 +74,37 @@ struct radiodev_properties_s * The radio network driver does not use the d_buf packet buffer directly. * Rather, it uses a list smaller frame buffers. * - * - The packet fragment data is provided in an IOB in the via the + * - Outgoing frame data is provided in an IOB in the via the * r_req_data() interface method each time that the radio needs to * send more data. The length of the frame is provided in the io_len * field of the IOB. * - * In this case, the d_buf is not used at all and, if fact, may be - * NULL. + * Outgoing frames are generated when the radio network driver calls + * the devif_poll(), devif_timer(), sixlowpan_input(), or + * ieee802154_input() interfaces. In each case, the radio driver must + * provide a working buffer in the d_buf pointer. A special form of + * the packet buffer must be used, struct sixlowpan_reassbuf_s. This + * special for includes appended data for managing reassembly of packets. * * - Received frames are provided by radio network driver to the network * via an IOB parameter in the sixlowpan_input() pr ieee802154_input() * interface. The length of the frame is io_len. * - * - 6LoWPAN frames and will be uncompressed and possibly reassembled in - * the d_buf; d_len will hold the size of the reassembled packet. + * Again, the radio network driver must provide an instance of struct + * sixlowpan_reassbuf_s as the packet buffer in the d_buf field. This + * driver-provided data will only be used if the the receive frames are + * not fragmented. * - * In this case, a d_buf of size CONFIG_NET_6LOWPAN_MTU must be provided. + * - Received 6LoWPAN frames and will be uncompressed and possibly + * reassembled in resassembled the d_buf; d_len will hold the size of + * the reassembled packet. + * + * For fagemented frames, d_buf provided by radio driver will not be + * used. 6LoWPAN must handle mutliple reassemblies from different + * sources simultaneously. To support this, 6LoWPAN will allocate a + * unique reassembly buffer for each active reassembly, based on the + * reassembly tag and source radio address. These reassembly buffers + * are managed entirely by the 6LoWPAN layer. * * This is accomplished by "inheriting" the standard 'struct net_driver_s' * and appending the frame buffer as well as other metadata needed to @@ -103,8 +116,9 @@ struct radiodev_properties_s * structure. In general, all fields must be set to NULL. In addition: * * 1. On a TX poll, the radio network driver should provide its driver - * structure. During the course of the poll, the networking layer may - * generate outgoing frames. These frames will by provided to the MAC + * structure along is (single) reassemby buffer provided at d_buf. + * During the course of the poll, the networking layer may generate + * outgoing frames. These frames will by provided to the radio network * driver via the req_data() method. * * After sending each frame through the radio, the MAC driver must @@ -117,16 +131,17 @@ struct radiodev_properties_s * payload area of an IOB frame structure. That IOB structure may be * obtained using the iob_alloc() function. * - * For 6LoWPAN, the larger dev.d_buf must have a size of at least the - * advertised MTU of the protocol, CONFIG_NET_6LOWPAN_MTU, plus - * CONFIG_NET_GUARDSIZE. If fragmentation is enabled, then the logical - * packet size may be significantly larger than the size of the frame - * buffer. The dev.d_buf is used for de-compressing each frame and - * reassembling any fragmented packets to create the full input packet - * that is provided to the application. + * For 6LoWPAN, fragmented packets will be reassembled using allocated + * reassembly buffers that are managed by the 6LoWPAN layer. The radio + * driver must still provide its (single) reassembly buffer in d_buf; + * that buffer is still used for the case where the packet is not + * fragmented into many frames. In either case, the packet buffer will + * have a size of advertised MTU of the protocol, CONFIG_NET_6LOWPAN_MTU, + * plus CONFIG_NET_GUARDSIZE and some additional overhead for reassembly + * state data. * - * The MAC driver should then inform the network of the reciptor of a - * frame by calling sixlowpan_input() or ieee802154_input(). That + * The radio network driver should then inform the network of the recipt + * of a frame by calling sixlowpan_input() or ieee802154_input(). That * single frame (or, perhaps, list of frames) should be provided as * second argument of that call. * @@ -167,65 +182,6 @@ struct radio_driver_s uint8_t r_msdu_handle; #endif -#if CONFIG_NET_6LOWPAN_FRAG - /* Fragmentation Support *************************************************/ - /* Fragmentation is handled frame by frame and requires that certain - * state information be retained from frame to frame. - */ - - /* r_dgramtag. Datagram tag to be put in the header of the set of - * fragments. It is used by the recipient to match fragments of the - * same payload. - * - * This is the sender's copy of the tag. It is incremented after each - * fragmented packet is sent so that it will be unique to that - * sequence fragmentation. Its value is then persistent, the values of - * other fragmentation variables are valid on during a single - * fragmentation sequence (while r_accumlen > 0) - */ - - uint16_t r_dgramtag; - - /* r_reasstag. Each frame in the reassembly has a tag. That tag must - * match the reassembly tag in the fragments being merged. - * - * This is the same tag as r_dgramtag but is saved on the receiving - * side to match all of the fragments of the packet. - */ - - uint16_t r_reasstag; - - /* r_pktlen. The total length of the IPv6 packet to be re-assembled in - * d_buf. Used to determine when the re-assembly is complete. - */ - - uint16_t r_pktlen; - - /* The current accumulated length of the packet being received in d_buf. - * Included IPv6 and protocol headers. Currently used only to determine - * there is a fragmentation sequence in progress. - */ - - uint16_t r_accumlen; - - /* r_boffset. Offset to the beginning of data in d_buf. As each fragment - * is received, data is placed at an appriate offset added to this. - */ - - uint16_t r_boffset; - - /* The source MAC address of the fragments being merged */ - - struct netdev_varaddr_s r_fragsrc; - - /* That time at which reassembly was started. If the elapsed time - * exceeds CONFIG_NET_6LOWPAN_MAXAGE, then the reassembly will - * be cancelled. - */ - - systime_t r_time; -#endif /* CONFIG_NET_6LOWPAN_FRAG */ - /* MAC network driver callback functions **********************************/ /************************************************************************** * Name: r_get_mhrlen diff --git a/include/nuttx/net/sixlowpan.h b/include/nuttx/net/sixlowpan.h index 4eab7cc48a..839e19b1fb 100644 --- a/include/nuttx/net/sixlowpan.h +++ b/include/nuttx/net/sixlowpan.h @@ -53,6 +53,10 @@ #include #include +#include + +#include +#include #ifdef CONFIG_NET_6LOWPAN @@ -337,6 +341,99 @@ (a)[4] == 0 && (a)[5] == 0 && (a)[6] == 0 && \ (((a)[7] & HTONS(0xff00)) == 0x0000)) +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* Fragmentation Support + * + * This structure defines the reassembly buffer. NOTE: The packet buffer + * is needed even in the case where reassembly is disabled. + */ + +struct sixlowpan_reassbuf_s +{ + /* This is the externally visible packet buffer. This is assigned + * to the driver's d_buf field when the reassembly is complete and + * provides the full reassembly packet to the network. + */ + + uint8_t rb_buf[CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE]; + + /* Memory pool used to allocate this reassembly buffer */ + + uint8_t rb_pool; + + /* True if the reassemby buffer is active (set to false when reassembly is + * complete). + */ + + bool rb_active; + + /* Supports a singly linked list */ + + FAR struct sixlowpan_reassbuf_s *rb_flink; + +#if CONFIG_NET_6LOWPAN_FRAG + /* Fragmentation is handled frame by frame and requires that certain + * state information be retained from frame to frame. That additional + * information follows the externally visible packet buffer. + */ + + /* rb_dgramtag. Datagram tag to be put in the header of the set of + * fragments. It is used by the recipient to match fragments of the + * same payload. + * + * This is the sender's copy of the tag. It is incremented after each + * fragmented packet is sent so that it will be unique to that + * sequence fragmentation. Its value is then persistent, the values of + * other fragmentation variables are valid on during a single + * fragmentation sequence (while rb_accumlen > 0) + */ + + uint16_t rb_dgramtag; + + /* rb_reasstag. Each frame in the reassembly has a tag. That tag must + * match the reassembly tag in the fragments being merged. + * + * This is the same tag as rb_dgramtag but is saved on the receiving + * side to match all of the fragments of the packet. + */ + + uint16_t rb_reasstag; + + /* rb_pktlen. The total length of the IPv6 packet to be re-assembled in + * d_buf. Used to determine when the re-assembly is complete. + */ + + uint16_t rb_pktlen; + + /* The current accumulated length of the packet being received in d_buf. + * Included IPv6 and protocol headers. Currently used only to determine + * there is a fragmentation sequence in progress. + */ + + uint16_t rb_accumlen; + + /* rb_boffset. Offset to the beginning of data in d_buf. As each fragment + * is received, data is placed at an appriate offset added to this. + */ + + uint16_t rb_boffset; + + /* The source MAC address of the fragments being merged */ + + struct netdev_varaddr_s rb_fragsrc; + + /* That time at which reassembly was started. If the elapsed time + * exceeds CONFIG_NET_6LOWPAN_MAXAGE, then the reassembly will + * be cancelled. + */ + + systime_t rb_time; +#endif /* CONFIG_NET_6LOWPAN_FRAG */ +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ diff --git a/net/devif/devif_callback.c b/net/devif/devif_callback.c index c88dda744c..419d836869 100644 --- a/net/devif/devif_callback.c +++ b/net/devif/devif_callback.c @@ -447,7 +447,6 @@ uint16_t devif_conn_event(FAR struct net_driver_s *dev, void *pvconn, * beginning of the list (which will be ignored on this pass) */ - ninfo("Call event=%p with flags=%04x\n", list->event, flags); flags = list->event(dev, pvconn, list->priv, flags); } @@ -511,7 +510,6 @@ uint16_t devif_dev_event(FAR struct net_driver_s *dev, void *pvconn, * beginning of the list (which will be ignored on this pass) */ - ninfo("Call event=%p with flags=%04x\n", cb->event, flags); flags = cb->event(dev, pvconn, cb->priv, flags); } diff --git a/net/devif/devif_send.c b/net/devif/devif_send.c index 43c913a97b..11b32e289d 100644 --- a/net/devif/devif_send.c +++ b/net/devif/devif_send.c @@ -67,7 +67,7 @@ void devif_send(struct net_driver_s *dev, const void *buf, int len) { - DEBUGASSERT(dev && len > 0 && len < NET_DEV_MTU(dev)); + DEBUGASSERT(dev != NULL && len > 0 && len < NET_DEV_MTU(dev)); memcpy(dev->d_appdata, buf, len); dev->d_sndlen = len; diff --git a/net/ieee802154/ieee802154_recvfrom.c b/net/ieee802154/ieee802154_recvfrom.c index 1801781634..fe1e557acb 100644 --- a/net/ieee802154/ieee802154_recvfrom.c +++ b/net/ieee802154/ieee802154_recvfrom.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include diff --git a/net/ipforward/ipfwd_alloc.c b/net/ipforward/ipfwd_alloc.c index 517321a1b8..0db8f337f3 100644 --- a/net/ipforward/ipfwd_alloc.c +++ b/net/ipforward/ipfwd_alloc.c @@ -64,7 +64,7 @@ #endif #if defined(CONFIG_NET_TCP) -# define L3_MAXHDRLEN TCP_MAX_HDRLEN +# define L3_MAXHDRLEN TCP_HDRLEN /* Could be up to TCP_MAX_HDRLEN */ #elif defined(CONFIG_NET_UDP) # define L3_MAXHDRLEN UDP_HDRLEN #elif defined(CONFIG_NET_ICMPv6) diff --git a/net/sixlowpan/Kconfig b/net/sixlowpan/Kconfig index 0a363597bf..a8b76568d1 100644 --- a/net/sixlowpan/Kconfig +++ b/net/sixlowpan/Kconfig @@ -12,6 +12,40 @@ config NET_6LOWPAN_FRAG CONFIG_NET_6LOWPAN_FRAG specifies if 6lowpan fragmentation should be used or not. Fragmentation is on by default. +config NET_6LOWPAN_NREASSBUF + int "Number of preallocated reassembly buffers" + default 2 + depends on NET_6LOWPAN_FRAG + ---help--- + Large IPv6 packets will be fragmented by 6LoWPAN into multiple + frames and reconstitued into a reassembly buffer on the receiving + side. Each concurrent reassembly requires one buffer. Reassembly + buffers are large: The size of the 6LoWPAN MTU plus some overhead + for the reassembly state. + + Some reassembly buffers may be preallocated; some may be allocated + dynamically from the stack. The former require more static memory + usage; the later require additional CPU cycles to perform the + allocation and may effect deterministic behavior. So this is a + trade-off between resources and performance. If the number of pre- + allocated reassembly buffers are exhausted, the reassembly will + continue with dynamically allocated reassembly buffers. + + This behavior can be changed with CONFIG_NET_6LOWPAN_REASS_STATIC + +config NET_6LOWPAN_REASS_STATIC + bool "Static reassembly buffers" + default n + depends on NET_6LOWPAN_FRAG + ---help--- + By default, reassembly buffers may be allocated dynamically from the + stack when all of the statically allocation reassembly buffers are + in use. This will equire additional CPU cycles to perform the + allocation and may effect deterministic behavior. This option may + be selected to suppress all dynamica allocation of reassembly + buffers. In that case, only static reassembly buffers are available; + when those are exhausted, frames that require reassembly will be lost. + config NET_6LOWPAN_FRAMELEN int "Max Radio Frame Size" default 127 diff --git a/net/sixlowpan/Make.defs b/net/sixlowpan/Make.defs index 25cdc8bdd8..654a4e4bcc 100644 --- a/net/sixlowpan/Make.defs +++ b/net/sixlowpan/Make.defs @@ -68,6 +68,10 @@ ifeq ($(CONFIG_NET_6LOWPAN_COMPRESSION_HC06),y) NET_CSRCS += sixlowpan_hc06.c endif +ifeq ($(CONFIG_NET_6LOWPAN_FRAG),y) +NET_CSRCS += sixlowpan_reassbuf.c +endif + # Include the sixlowpan directory in the build DEPPATH += --dep-path sixlowpan diff --git a/net/sixlowpan/sixlowpan_framelist.c b/net/sixlowpan/sixlowpan_framelist.c index 5df2103d4b..8f79bbd791 100644 --- a/net/sixlowpan/sixlowpan_framelist.c +++ b/net/sixlowpan/sixlowpan_framelist.c @@ -54,6 +54,7 @@ #include #include +#include #include #include #include @@ -384,9 +385,9 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, FAR uint8_t *fptr; int framer_hdrlen; struct netdev_varaddr_s bcastmac; +#ifdef CONFIG_NET_6LOWPAN_FRAG uint16_t pktlen; uint16_t paysize; -#ifdef CONFIG_NET_6LOWPAN_FRAG uint16_t outlen = 0; #endif uint8_t protosize; @@ -505,12 +506,18 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, * added at qtail. */ + FAR struct sixlowpan_reassbuf_s *reass; FAR struct iob_s *qhead; FAR struct iob_s *qtail; FAR uint8_t *frame1; FAR uint8_t *fragptr; uint16_t frag1_hdrlen; + /* Recover the reassembly buffer from the driver d_buf. */ + + reass = (FAR struct sixlowpan_reassbuf_s *)radio->r_dev.d_buf; + DEBUGASSERT(reass != NULL); + /* The outbound IPv6 packet is too large to fit into a single 15.4 * packet, so we fragment it into multiple packets and send them. * The first fragment contains frag1 dispatch, then @@ -548,7 +555,7 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, pktlen = buflen + g_uncomp_hdrlen + protosize; PUTHOST16(fragptr, SIXLOWPAN_FRAG_DISPATCH_SIZE, ((SIXLOWPAN_DISPATCH_FRAG1 << 8) | pktlen)); - PUTHOST16(fragptr, SIXLOWPAN_FRAG_TAG, radio->r_dgramtag); + PUTHOST16(fragptr, SIXLOWPAN_FRAG_TAG, reass->rb_dgramtag); g_frame_hdrlen += SIXLOWPAN_FRAG1_HDR_LEN; @@ -575,7 +582,7 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, outlen = paysize; ninfo("First fragment: length %d, tag %d\n", - paysize, radio->r_dgramtag); + paysize, reass->rb_dgramtag); sixlowpan_dumpbuffer("Outgoing frame", (FAR const uint8_t *)iob->io_data, iob->io_len); @@ -630,7 +637,7 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, PUTHOST16(fragptr, SIXLOWPAN_FRAG_DISPATCH_SIZE, ((SIXLOWPAN_DISPATCH_FRAGN << 8) | pktlen)); - PUTHOST16(fragptr, SIXLOWPAN_FRAG_TAG, radio->r_dgramtag); + PUTHOST16(fragptr, SIXLOWPAN_FRAG_TAG, reass->rb_dgramtag); fragptr[SIXLOWPAN_FRAG_OFFSET] = outlen >> 3; fragn_hdrlen += SIXLOWPAN_FRAGN_HDR_LEN; @@ -654,8 +661,8 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, iob->io_len = paysize + fragn_hdrlen; outlen += paysize; - ninfo("Fragment offset=%d, paysize=%d, r_dgramtag=%d\n", - outlen >> 3, paysize, radio->r_dgramtag); + ninfo("Fragment offset=%d, paysize=%d, rb_dgramtag=%d\n", + outlen >> 3, paysize, reass->rb_dgramtag); sixlowpan_dumpbuffer("Outgoing frame", (FAR const uint8_t *)iob->io_data, iob->io_len); @@ -697,7 +704,7 @@ int sixlowpan_queue_frames(FAR struct radio_driver_s *radio, /* Update the datagram TAG value */ - radio->r_dgramtag++; + reass->rb_dgramtag++; #else nerr("ERROR: Packet too large: %d\n", buflen); nerr(" Cannot to be sent without fragmentation support\n"); diff --git a/net/sixlowpan/sixlowpan_hc06.c b/net/sixlowpan/sixlowpan_hc06.c index 85b41a0ff6..deca07733a 100644 --- a/net/sixlowpan/sixlowpan_hc06.c +++ b/net/sixlowpan/sixlowpan_hc06.c @@ -61,6 +61,7 @@ #include #include +#include #include #include #include diff --git a/net/sixlowpan/sixlowpan_hc1.c b/net/sixlowpan/sixlowpan_hc1.c index 4daa4cabb0..77a13a8712 100644 --- a/net/sixlowpan/sixlowpan_hc1.c +++ b/net/sixlowpan/sixlowpan_hc1.c @@ -53,6 +53,7 @@ #include #include +#include #include #include #include "sixlowpan/sixlowpan_internal.h" diff --git a/net/sixlowpan/sixlowpan_initialize.c b/net/sixlowpan/sixlowpan_initialize.c index 99222fc487..8ad5bb9cd4 100644 --- a/net/sixlowpan/sixlowpan_initialize.c +++ b/net/sixlowpan/sixlowpan_initialize.c @@ -69,6 +69,12 @@ void sixlowpan_initialize(void) { +#ifdef CONFIG_NET_6LOWPAN_FRAG + /* Initialize the reassembly buffer allocator */ + + sixlowpan_reass_initialize(); +#endif + #ifdef CONFIG_NET_6LOWPAN_COMPRESSION_HC06 /* Initialize HC06 data data structures */ diff --git a/net/sixlowpan/sixlowpan_input.c b/net/sixlowpan/sixlowpan_input.c index a5d14ed964..3af9ea86e7 100644 --- a/net/sixlowpan/sixlowpan_input.c +++ b/net/sixlowpan/sixlowpan_input.c @@ -54,10 +54,7 @@ #include #include -#ifdef CONFIG_NET_6LOWPAN_FRAG -# include "nuttx/clock.h" -#endif - +#include "nuttx/mm/iob.h" #include "nuttx/net/netdev.h" #include "nuttx/net/radiodev.h" #include "nuttx/net/ip.h" @@ -82,10 +79,6 @@ #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. */ @@ -132,50 +125,6 @@ static uint8_t g_bitbucket[UNCOMP_MAXHDR]; * 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 * @@ -311,14 +260,15 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, 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 */ + int ret; #ifdef CONFIG_NET_6LOWPAN_FRAG + FAR struct sixlowpan_reassbuf_s *reass; + struct netdev_varaddr_s fragsrc; 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). @@ -359,166 +309,119 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, 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_MTU) + { + nwarn("WARNING: Reassembled packet size exeeds CONFIG_NET_6LOWPAN_MTU\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 */ - isfirstfrag = true; - isfrag = true; + bptr = reass->rb_buf; + isfirstfrag = true; + isfrag = true; } break; case SIXLOWPAN_DISPATCH_FRAGN: { - /* Set offset, tag, size. Offset is in units of 8 bytes. */ + /* 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: r_accumlen=%d paysize=%u fragsize=%u\n", - radio->r_accumlen, iob->io_len - g_frame_hdrlen, fragsize); + 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 */ - isfrag = true; + 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; + bptr = reass->rb_buf; 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; - } - } +#else + bptr = radio->r_dev.d_buf; #endif /* CONFIG_NET_6LOWPAN_FRAG */ /* Process next dispatch and headers */ @@ -553,7 +456,8 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, /* Unknown or unsupported header */ nwarn("WARNING: Unknown dispatch: %u\n", hc1[SIXLOWPAN_HC1_DISPATCH]); - return -ENOSYS; + ret = -ENOSYS; + goto errout_with_reass; } #ifdef CONFIG_NET_6LOWPAN_FRAG @@ -565,7 +469,7 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, * begin placing the data payload. */ - radio->r_boffset = g_uncomp_hdrlen; + reass->rb_boffset = g_uncomp_hdrlen; } /* No.. is this a subsequent fragment in the same sequence? */ @@ -576,7 +480,7 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, * we began placing payload data. */ - g_uncomp_hdrlen = radio->r_boffset; + g_uncomp_hdrlen = reass->rb_boffset; } #endif /* CONFIG_NET_6LOWPAN_FRAG */ @@ -591,7 +495,8 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, { nwarn("WARNING: Packet dropped due to payload (%u) > packet buffer (%u)\n", paysize, CONFIG_NET_6LOWPAN_MTU); - return -ENOSPC; + ret = -ENOSPC; + goto errout_with_reass; } /* Sanity-check size of incoming packet to avoid buffer overflow */ @@ -602,14 +507,15 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, 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; + ret = -ENOMEM; + goto errout_with_reass; } 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 + /* Update reass->rb_accumlen if the frame is a fragment, reass->rb_pktlen * otherwise. */ @@ -621,36 +527,49 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, * bytes at the end. We must be liberal in what we accept. */ - radio->r_accumlen = g_uncomp_hdrlen + (fragoffset << 3) + paysize; + reass->rb_accumlen = g_uncomp_hdrlen + (fragoffset << 3) + paysize; } else { - radio->r_pktlen = paysize + g_uncomp_hdrlen; + reass->rb_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); + ninfo("rb_accumlen=%d rb_pktlen=%d paysize=%d\n", + reass->rb_accumlen, reass->rb_pktlen, paysize); - if (radio->r_accumlen == 0 || radio->r_accumlen >= radio->r_pktlen) + if (reass->rb_accumlen == 0 || reass->rb_accumlen >= reass->rb_pktlen) { - ninfo("IP packet ready (length %d)\n", radio->r_pktlen); + ninfo("IP packet ready (length %d)\n", reass->rb_pktlen); - radio->r_dev.d_len = radio->r_pktlen; - radio->r_pktlen = 0; - radio->r_accumlen = 0; + 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; + #else /* Deliver the packet to the IP stack */ radio->r_dev.d_len = paysize + g_uncomp_hdrlen; return INPUT_COMPLETE; + +errout_with_reass: + return ret; + #endif /* CONFIG_NET_6LOWPAN_FRAG */ } @@ -670,6 +589,11 @@ static int sixlowpan_frame_process(FAR struct radio_driver_s *radio, static int sixlowpan_dispatch(FAR struct radio_driver_s *radio) { +#ifdef CONFIG_NET_6LOWPAN_FRAG + FAR struct sixlowpan_reassbuf_s *reass; +#endif + int ret; + sixlowpan_dumpbuffer("Incoming packet", (FAR const uint8_t *)IPv6BUF(&radio->r_dev), radio->r_dev.d_len); @@ -691,7 +615,17 @@ static int sixlowpan_dispatch(FAR struct radio_driver_s *radio) * be set to zero. Oddly, ipv6_input() will return OK in this case. */ - return ipv6_input(&radio->r_dev); + ret = ipv6_input(&radio->r_dev); + +#ifdef CONFIG_NET_6LOWPAN_FRAG + /* Free the reassemby buffer */ + + reass = (FAR struct sixlowpan_reassbuf_s *)radio->r_dev.d_buf; + DEBUGASSERT(reass != NULL); + sixlowpan_reass_free(reass); +#endif + + return ret; } /**************************************************************************** diff --git a/net/sixlowpan/sixlowpan_internal.h b/net/sixlowpan/sixlowpan_internal.h index a65666880f..af2442a8f3 100644 --- a/net/sixlowpan/sixlowpan_internal.h +++ b/net/sixlowpan/sixlowpan_internal.h @@ -115,8 +115,13 @@ /* Sucessful return values from header compression logic */ -#define COMPRESS_HDR_INLINE 0 /* L2 header not compressed */ -#define COMPRESS_HDR_ELIDED 1 /* L2 header compressed */ +#define COMPRESS_HDR_INLINE 0 /* L2 header not compressed */ +#define COMPRESS_HDR_ELIDED 1 /* L2 header compressed */ + +/* Memory Pools *************************************************************/ + +#define REASS_POOL_PREALLOCATED 0 +#define REASS_POOL_DYNAMIC 1 /* Debug ********************************************************************/ @@ -657,5 +662,107 @@ int sixlowpan_extract_destaddr(FAR struct radio_driver_s *radio, FAR const void *metadata, FAR struct netdev_varaddr_s *destaddr); +/**************************************************************************** + * Name: sixlowpan_reass_initialize + * + * Description: + * This function initializes the reassembly buffer allocator. This + * function must be called early in the initialization sequence before + * any radios begin operation. + * + * Called only once during network initialization. + * + * Inputs: + * None + * + * Return Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN_FRAG +void sixlowpan_reass_initialize(void); +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + +/**************************************************************************** + * Name: sixlowpan_reass_allocate + * + * Description: + * The sixlowpan_reass_allocate function will get a free reassembly buffer + * structure for use by 6LoWPAN. + * + * This function will first attempt to allocate from the g_free_reass + * list. If that the list is empty, then the reassembly buffer structure + * will be allocated from the dynamic memory pool. + * + * Inputs: + * reasstag - The reassembly tag for subsequent lookup. + * fragsrc - The source address of the fragment. + * + * Return Value: + * A reference to the allocated reass structure. All fields used by the + * reasembly logic have been zeroed. On a failure to allocate, NULL is + * returned. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN_FRAG +FAR struct sixlowpan_reassbuf_s * + sixlowpan_reass_allocate(uint16_t reasstag, + FAR const struct netdev_varaddr_s *fragsrc); +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + +/**************************************************************************** + * Name: sixlowpan_reass_find + * + * Description: + * Find a previously allocated, active reassembly buffer with the specified + * reassembly tag. + * + * Inputs: + * reasstag - The reassembly tag to match. + * fragsrc - The source address of the fragment. + * + * Return Value: + * A reference to the matching reass structure. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN_FRAG +FAR struct sixlowpan_reassbuf_s * + sixlowpan_reass_find(uint16_t reasstag, + FAR const struct netdev_varaddr_s *fragsrc); +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + +/**************************************************************************** + * Name: sixlowpan_reass_free + * + * Description: + * The sixlowpan_reass_free function will return a reass structure + * to the free list of messages if it was a pre-allocated reass + * structure. If the reass structure was allocated dynamically it will + * be deallocated. + * + * Inputs: + * reass - reass structure to free + * + * Return Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN_FRAG +void sixlowpan_reass_free(FAR struct sixlowpan_reassbuf_s *reass); +#endif /* CONFIG_NET_6LOWPAN_FRAG */ + #endif /* CONFIG_NET_6LOWPAN */ #endif /* _NET_SIXLOWPAN_SIXLOWPAN_INTERNAL_H */ diff --git a/net/sixlowpan/sixlowpan_reassbuf.c b/net/sixlowpan/sixlowpan_reassbuf.c new file mode 100644 index 0000000000..fcce43dd8c --- /dev/null +++ b/net/sixlowpan/sixlowpan_reassbuf.c @@ -0,0 +1,458 @@ +/**************************************************************************** + * net/sixlowpan/sixlowpan_reassbuf.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 NuttX 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sixlowpan_internal.h" + +#ifdef CONFIG_NET_6LOWPAN_FRAG + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Re-assembly timeout in clock ticks */ + +#define NET_6LOWPAN_TIMEOUT SEC2TICK(CONFIG_NET_6LOWPAN_MAXAGE) + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* The g_free_reass is a list of reassembly buffer structures that are available for + * general use. The number of messages in this list is a system configuration + * item. Protected only by the network lock. + */ + +static FAR struct sixlowpan_reassbuf_s *g_free_reass; + +/* This is a list of active, allocated reassemby buffers */ + +static FAR struct sixlowpan_reassbuf_s *g_active_reass; + +/* Pool of pre-allocated reassembly buffer stuctures */ + +static struct sixlowpan_reassbuf_s g_metadata_pool[CONFIG_NET_6LOWPAN_NREASSBUF]; + +/**************************************************************************** + * Public 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 + * fragsrc - The source address of the fragment. + * + * Returned Value: + * true if the sources are the same. + * + ****************************************************************************/ + +static bool sixlowpan_compare_fragsrc(FAR struct sixlowpan_reassbuf_s *reass, + FAR const struct netdev_varaddr_s *fragsrc) +{ + /* The addresses cannot match if they are not the same size */ + + if (fragsrc->nv_addrlen == reass->rb_fragsrc.nv_addrlen) + { + /* The are the same sizer, return the address comparisson */ + + return (memcmp(fragsrc->nv_addr, reass->rb_fragsrc.nv_addr, + fragsrc->nv_addrlen) == 0); + } + + return false; +} + +/**************************************************************************** + * Name: sixlowpan_reass_expire + * + * Description: + * Free all expired or inactive reassembly buffers. + * + * Inputs: + * None + * + * Return Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void sixlowpan_reass_expire(void) +{ + FAR struct sixlowpan_reassbuf_s *reass; + FAR struct sixlowpan_reassbuf_s *next; + systime_t elapsed; + + /* If reassembly timed out, cancel it */ + + for (reass = g_active_reass; reass != NULL; reass = next) + { + /* Needed if 'reass' is freed */ + + next = reass->rb_flink; + + /* Free any inactive reassembly buffers. This is done because the life + * the reassembly buffer is not cerain. + */ + + if (!reass->rb_active) + { + sixlowpan_reass_free(reass); + } + else + { + /* Get the elpased time of the reassembly */ + + elapsed = clock_systimer() - reass->rb_time; + + /* If the reassembly has expired, then free the reassembly buffer */ + + if (elapsed > NET_6LOWPAN_TIMEOUT) + { + nwarn("WARNING: Reassembly timed out\n"); + sixlowpan_reass_free(reass); + } + } + } +} + +/**************************************************************************** + * Name: sixlowpan_remove_active + * + * Description: + * Remove a reassembly buffer from the active reassembly buffer list. + * + * Inputs: + * reass - The reassembly buffer to be removed. + * + * Return Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +static void sixlowpan_remove_active(FAR struct sixlowpan_reassbuf_s *reass) +{ + FAR struct sixlowpan_reassbuf_s *curr; + FAR struct sixlowpan_reassbuf_s *prev; + + /* Find the reassembly buffer in the list of active reassembly buffers */ + + for (prev = NULL, curr = g_active_reass; + curr != NULL && curr != reass; + prev = curr, curr = curr->rb_flink) + { + } + + /* Did we find it? */ + + if (curr != NULL) + { + /* Yes.. remove it from the active reassembly buffer list */ + + if (prev == NULL) + { + g_active_reass = reass->rb_flink; + } + else + { + prev->rb_flink = reass->rb_flink; + } + } + + reass->rb_flink = NULL; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sixlowpan_reass_initialize + * + * Description: + * This function initializes the reassembly buffer allocator. This + * function must be called early in the initialization sequence before + * any radios begin operation. + * + * Called only once during network initialization. + * + * Inputs: + * None + * + * Return Value: + * None + * + ****************************************************************************/ + +void sixlowpan_reass_initialize(void) +{ + FAR struct sixlowpan_reassbuf_s *reass; + int i; + + /* Initialize g_free_reass, the list of reassembly buffer structures that are + * available for allocation. + */ + + g_free_reass = NULL; + for (i = 0, reass = g_metadata_pool; + i < CONFIG_NET_6LOWPAN_NREASSBUF; + i++, reass++) + { + /* Add the next meta data structure from the pool to the list of + * general structures. + */ + + reass->rb_flink = g_free_reass; + g_free_reass = reass; + } +} + +/**************************************************************************** + * Name: sixlowpan_reass_allocate + * + * Description: + * The sixlowpan_reass_allocate function will get a free reassembly buffer + * structure for use by 6LoWPAN. + * + * This function will first attempt to allocate from the g_free_reass + * list. If that the list is empty, then the reassembly buffer structure + * will be allocated from the dynamic memory pool. + * + * Inputs: + * reasstag - The reassembly tag for subsequent lookup. + * fragsrc - The source address of the fragment. + * + * Return Value: + * A reference to the allocated reass structure. All fields used by the + * reasembly logic have been zeroed. On a failure to allocate, NULL is + * returned. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +FAR struct sixlowpan_reassbuf_s * + sixlowpan_reass_allocate(uint16_t reasstag, + FAR const struct netdev_varaddr_s *fragsrc) +{ + FAR struct sixlowpan_reassbuf_s *reass; + uint8_t pool; + + /* First, removed any expired or inactive reassembly buffers. This might + * free up a pre-allocated buffer for this allocation. + */ + + sixlowpan_reass_expire(); + + /* Now, try the free list first */ + + if (g_free_reass != NULL) + { + reass = g_free_reass; + g_free_reass = reass->rb_flink; + pool = REASS_POOL_PREALLOCATED; + } + else + { +#ifdef CONFIG_NET_6LOWPAN_REASS_STATIC + reass = NULL; +#else + /* If we cannot get a reassembly buffer instance from the free list, then we + * will have to allocate one from the kernal memory pool. + */ + + reass = (FAR struct sixlowpan_reassbuf_s *) + kmm_malloc((sizeof (struct sixlowpan_reassbuf_s))); + pool = REASS_POOL_DYNAMIC; +#endif + } + + /* We have successfully allocated memory from some source? */ + + if (reass != NULL) + { + /* Zero and tag the allocated reassembly buffer structure. */ + + memset(reass, 0, sizeof(struct sixlowpan_reassbuf_s)); + memcpy(&reass->rb_fragsrc, fragsrc, sizeof(struct netdev_varaddr_s)); + reass->rb_pool = pool; + reass->rb_active = true; + reass->rb_reasstag = reasstag; + reass->rb_time = clock_systimer(); + + /* Add the reassembly buffer to the list of active reassembly buffers */ + + reass->rb_flink = g_active_reass; + g_active_reass = reass; + } + + return reass; +} + +/**************************************************************************** + * Name: sixlowpan_reass_find + * + * Description: + * Find a previously allocated, active reassembly buffer with the specified + * reassembly tag. + * + * Inputs: + * reasstag - The reassembly tag to match. + * fragsrc - The source address of the fragment. + * + * Return Value: + * A reference to the matching reass structure. + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +FAR struct sixlowpan_reassbuf_s * + sixlowpan_reass_find(uint16_t reasstag, + FAR const struct netdev_varaddr_s *fragsrc) +{ + FAR struct sixlowpan_reassbuf_s *reass; + + /* First, removed any expired or inactive reassembly buffers (we don't want + * to return old reassembly buffer with the same tag) + */ + + sixlowpan_reass_expire(); + + /* Now search for the matching reassembly buffer in the remainng, active + * reassembly buffers. + */ + + for (reass = g_active_reass; reass != NULL; reass = reass->rb_flink) + { + /* In order to be a match, it must have the same reassembly tag as + * well as source address (different sources might use the same + * reassembly tag). + */ + + if (reass->rb_reasstag == reasstag && + sixlowpan_compare_fragsrc(reass, fragsrc)) + { + return reass; + } + } + + /* Not found */ + + return NULL; +} + +/**************************************************************************** + * Name: sixlowpan_reass_free + * + * Description: + * The sixlowpan_reass_free function will return a reass structure + * to the free list of messages if it was a pre-allocated reass + * structure. If the reass structure was allocated dynamically it will + * be deallocated. + * + * Inputs: + * reass - reass structure to free + * + * Return Value: + * None + * + * Assumptions: + * The network is locked. + * + ****************************************************************************/ + +void sixlowpan_reass_free(FAR struct sixlowpan_reassbuf_s *reass) +{ + /* First, remove the reassembly buffer from the list of active reassembly + * buffers. + */ + + sixlowpan_remove_active(reass); + + /* If this is a pre-allocated reassembly buffer structure, then just put it back + * in the free list. + */ + + if (reass->rb_pool == REASS_POOL_PREALLOCATED) + { + reass->rb_flink = g_free_reass; + g_free_reass = reass; + } + else + { +#ifdef CONFIG_NET_6LOWPAN_REASS_STATIC + DEBUGPANIC(); +#else + DEBUGASSERT(reass->rb_pool == REASS_POOL_DYNAMIC); + + /* Otherwise, deallocate it. */ + + sched_kfree(reass); +#endif + } +} + +#endif /* CONFIG_NET_6LOWPAN_FRAG */ diff --git a/net/tcp/tcp_input.c b/net/tcp/tcp_input.c index aefde83fd2..76fecdfd7e 100644 --- a/net/tcp/tcp_input.c +++ b/net/tcp/tcp_input.c @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -342,9 +343,12 @@ found: #endif /* We must free this TCP connection structure; this connection - * will never be established. + * will never be established. There should only be one reference + * on this connection when we allocated for the connection. */ + DEBUGASSERT(conn->crefs == 1); + conn->crefs = 0; tcp_free(conn); } else diff --git a/wireless/ieee802154/mac802154_loopback.c b/wireless/ieee802154/mac802154_loopback.c index 0c32c4c683..92223cd44d 100644 --- a/wireless/ieee802154/mac802154_loopback.c +++ b/wireless/ieee802154/mac802154_loopback.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -137,7 +138,7 @@ struct lo_driver_s static struct lo_driver_s g_loopback; #ifdef CONFIG_NET_6LOWPAN -static uint8_t g_iobuffer[CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE]; +static struct sixlowpan_reassbuf_s g_iobuffer; #endif static uint8_t g_eaddr[IEEE802154_EADDRSIZE] = @@ -388,6 +389,12 @@ static int lo_loopback(FAR struct net_driver_s *dev) #endif #ifdef CONFIG_NET_6LOWPAN { + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; + + /* Then give the frame to 6LoWPAN */ + ret = sixlowpan_input(&priv->lo_radio, iob, (FAR void *)&ind); } #endif @@ -459,6 +466,15 @@ static void lo_poll_work(FAR void *arg) /* Perform the poll */ net_lock(); + +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + + /* Then perform the poll */ + (void)devif_timer(&priv->lo_radio.r_dev, lo_loopback); /* Setup the watchdog poll timer again */ @@ -634,6 +650,12 @@ static void lo_txavail_work(FAR void *arg) { /* If so, then poll the network for new XMIT data */ +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + (void)devif_poll(&priv->lo_radio.r_dev, lo_loopback); } @@ -1063,9 +1085,6 @@ int ieee8021514_loopback(void) #endif #ifdef CONFIG_NETDEV_IOCTL dev->d_ioctl = lo_ioctl; /* Handle network IOCTL commands */ -#endif -#ifdef CONFIG_NET_6LOWPAN - dev->d_buf = g_iobuffer; /* Attach the IO buffer */ #endif dev->d_private = (FAR void *)priv; /* Used to recover private state from dev */ diff --git a/wireless/ieee802154/mac802154_netdev.c b/wireless/ieee802154/mac802154_netdev.c index a47b50e75b..5f112ee05e 100644 --- a/wireless/ieee802154/mac802154_netdev.c +++ b/wireless/ieee802154/mac802154_netdev.c @@ -224,6 +224,14 @@ static int macnet_req_data(FAR struct radio_driver_s *netdev, static int macnet_properties(FAR struct radio_driver_s *netdev, FAR struct radiodev_properties_s *properties); +/**************************************************************************** + * Private Data + ****************************************************************************/ + +#ifdef CONFIG_NET_6LOWPAN +static struct sixlowpan_reassbuf_s g_iobuffer; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -470,6 +478,12 @@ static int macnet_rxframe(FAR struct mac802154_maccb_s *maccb, } else { + /* Make sure the our single packet buffer is attached */ + + priv->md_dev.r_dev.d_buf = g_iobuffer.rb_buf; + + /* And give the packet to 6LoWPAN */ + ret = sixlowpan_input(&priv->md_dev, iob, (FAR void *)ind); } } @@ -568,7 +582,13 @@ static void macnet_txpoll_work(FAR void *arg) net_lock(); - /* Perform the poll */ +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->md_dev.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + + /* Then perform the poll */ (void)devif_timer(&priv->md_dev.r_dev, macnet_txpoll_callback); @@ -836,9 +856,13 @@ static void macnet_txavail_work(FAR void *arg) if (priv->md_bifup) { - /* Check if there is room in the hardware to hold another outgoing packet. */ +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ - /* If so, then poll the network for new XMIT data */ + priv->md_dev.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + + /* Then poll the network for new XMIT data */ (void)devif_poll(&priv->md_dev.r_dev, macnet_txpoll_callback); } @@ -1206,9 +1230,6 @@ int mac802154netdev_register(MACHANDLE mac) FAR struct radio_driver_s *radio; FAR struct net_driver_s *dev; FAR struct mac802154_maccb_s *maccb; -#ifdef CONFIG_NET_6LOWPAN - FAR uint8_t *pktbuf; -#endif int ret; DEBUGASSERT(mac != NULL); @@ -1224,27 +1245,10 @@ int mac802154netdev_register(MACHANDLE mac) return -ENOMEM; } -#ifdef CONFIG_NET_6LOWPAN - /* Allocate a packet buffer (not used by this driver, but needed by the - * upper networking layer) - */ - - pktbuf = (FAR uint8_t *)kmm_malloc(CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE); - if (pktbuf == NULL) - { - nerr("ERROR: Failed to allocate the packet buffer\n"); - kmm_free(priv); - return -ENOMEM; - } -#endif - /* Initialize the driver structure */ radio = &priv->md_dev; dev = &radio->r_dev; -#ifdef CONFIG_NET_6LOWPAN - dev->d_buf = pktbuf; /* Single packet buffer */ -#endif dev->d_ifup = macnet_ifup; /* I/F up (new IP address) callback */ dev->d_ifdown = macnet_ifdown; /* I/F down callback */ dev->d_txavail = macnet_txavail; /* New TX data callback */ @@ -1297,9 +1301,6 @@ int mac802154netdev_register(MACHANDLE mac) /* Free memory and return the error */ -#ifdef CONFIG_NET_6LOWPAN - kmm_free(pktbuf); -#endif kmm_free(priv); return ret; } diff --git a/wireless/pktradio/pktradio_loopback.c b/wireless/pktradio/pktradio_loopback.c index f426f7a86b..8b62a2afb1 100644 --- a/wireless/pktradio/pktradio_loopback.c +++ b/wireless/pktradio/pktradio_loopback.c @@ -51,6 +51,7 @@ #include #include +#include #include #include #include @@ -130,7 +131,9 @@ struct lo_driver_s ****************************************************************************/ static struct lo_driver_s g_loopback; -static uint8_t g_iobuffer[CONFIG_NET_6LOWPAN_MTU + CONFIG_NET_GUARDSIZE]; +#ifdef CONFIG_NET_6LOWPAN +static struct sixlowpan_reassbuf_s g_iobuffer; +#endif static uint8_t g_mac_addr[CONFIG_PKTRADIO_ADDRLEN] = { @@ -346,6 +349,10 @@ static int lo_loopback(FAR struct net_driver_s *dev) priv->lo_tail = NULL; } + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; + /* Return the next frame to the network */ ninfo("Send frame %p to the network: Offset=%u Length=%u\n", @@ -420,6 +427,15 @@ static void lo_poll_work(FAR void *arg) /* Perform the poll */ net_lock(); + +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + + /* And perform the poll */ + (void)devif_timer(&priv->lo_radio.r_dev, lo_loopback); /* Setup the watchdog poll timer again */ @@ -576,6 +592,13 @@ static void lo_txavail_work(FAR void *arg) if (priv->lo_bifup) { /* If so, then poll the network for new XMIT data */ +#ifdef CONFIG_NET_6LOWPAN + /* Make sure the our single packet buffer is attached */ + + priv->lo_radio.r_dev.d_buf = g_iobuffer.rb_buf; +#endif + + /* Then perform the poll */ (void)devif_poll(&priv->lo_radio.r_dev, lo_loopback); } @@ -1009,7 +1032,6 @@ int pktradio_loopback(void) #ifdef CONFIG_NETDEV_IOCTL dev->d_ioctl = lo_ioctl; /* Handle network IOCTL commands */ #endif - dev->d_buf = g_iobuffer; /* Attach the IO buffer */ dev->d_private = (FAR void *)priv; /* Used to recover private state from dev */ /* Set the network mask and advertise our MAC-based IP address */