/**************************************************************************** * net/sixlowpan/sixlowpan_reassbuf.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include "sixlowpan_internal.h" /**************************************************************************** * 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 structures */ 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 previously received fragments. * * 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 size, return the address comparison */ 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. * * Input Parameters: * None * * Returned 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; clock_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_systime_ticks() - 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. * * Input Parameters: * reass - The reassembly buffer to be removed. * * Returned 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. * * Input Parameters: * None * * Returned 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. * * Input Parameters: * reasstag - The reassembly tag for subsequent lookup. * fragsrc - The source address of the fragment. * * Returned 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 kernel 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_systime_ticks(); /* 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. * * Input Parameters: * reasstag - The reassembly tag to match. * fragsrc - The source address of the fragment. * * Returned 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. * * Input Parameters: * reass - reass structure to free * * Returned 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 if (reass->rb_pool == REASS_POOL_DYNAMIC) { #ifdef CONFIG_NET_6LOWPAN_REASS_STATIC DEBUGPANIC(); #else DEBUGASSERT(reass->rb_pool == REASS_POOL_DYNAMIC); /* Otherwise, deallocate it. */ kmm_free(reass); #endif } /* If the reassembly buffer structure was provided by the driver, nothing * needs to be freed. */ }