mirror of https://github.com/ARMmbed/mbed-os.git
542 lines
20 KiB
C
542 lines
20 KiB
C
/*
|
|
* Copyright (c) 2013-2017, Arm Limited and affiliates.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed 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.
|
|
*/
|
|
/**
|
|
*
|
|
* \file cipv6_fragmenter.c
|
|
* \brief Packet Fragmentation and Reassembly.
|
|
*
|
|
*/
|
|
|
|
#include "nsconfig.h"
|
|
#include "ns_types.h"
|
|
#include "string.h"
|
|
#include "ns_trace.h"
|
|
#include "randLIB.h"
|
|
#include "Core/include/socket.h"
|
|
#include "6LoWPAN/IPHC_Decode/cipv6.h"
|
|
#include "6LoWPAN/Fragmentation/cipv6_fragmenter.h"
|
|
#include "NWK_INTERFACE/Include/protocol.h"
|
|
#include "nsdynmemLIB.h"
|
|
#include "6LoWPAN/Mesh/mesh.h"
|
|
#include "6LoWPAN/IPHC_Decode/iphc_decompress.h"
|
|
#include "nwk_stats_api.h"
|
|
#include "NWK_INTERFACE/Include/protocol_stats.h"
|
|
#include "common_functions.h"
|
|
#include "6LoWPAN/MAC/mac_helper.h"
|
|
|
|
#define TRACE_GROUP "6frg"
|
|
|
|
typedef struct {
|
|
uint16_t ttl; /*!< Reassembly timer (seconds) */
|
|
uint16_t tag; /*!< Fragmentation datagram TAG ID */
|
|
uint16_t size; /*!< Datagram Total Size (uncompressed) */
|
|
uint16_t orig_size; /*!< Datagram Original Size (compressed) */
|
|
uint16_t frag_max; /*!< Maximum fragment size (MAC payload) */
|
|
uint16_t offset; /*!< Data offset from datagram start */
|
|
int16_t pattern; /*!< Size of compressed LoWPAN headers */
|
|
buffer_t *buf;
|
|
ns_list_link_t link; /*!< List link entry */
|
|
} reassembly_entry_t;
|
|
|
|
typedef NS_LIST_HEAD(reassembly_entry_t, link) reassembly_list_t;
|
|
|
|
typedef struct {
|
|
int8_t interface_id;
|
|
uint16_t timeout;
|
|
reassembly_list_t rx_list;
|
|
reassembly_list_t free_list;
|
|
reassembly_entry_t *entry_pointer_buffer;
|
|
ns_list_link_t link; /*!< List link entry */
|
|
} reassembly_interface_t;
|
|
|
|
static NS_LIST_DEFINE(reassembly_interface_list, reassembly_interface_t, link);
|
|
|
|
|
|
/* Reassembly structures and helpers - basically the same as in
|
|
* ipv6_fragmentation.c, as we are also using a variation of RFC 815, but there
|
|
* are enough minor differences that it doesn't seem worth trying to share code.
|
|
*/
|
|
|
|
/* We reassemble into the datagram buffer in basically the style of RFC 815 */
|
|
/* An 6-byte hole descriptor is placed directly in buffer holes */
|
|
/* We link them them by buffer offset (relative to start of fragmentable section) */
|
|
/* Note the possible need to align means we can't use more than 7 bytes */
|
|
typedef struct hole {
|
|
uint16_t first;
|
|
uint16_t last;
|
|
uint16_t next;
|
|
} hole_t;
|
|
|
|
/* Given the offset of a hole in the datagram buffer, return an aligned pointer
|
|
* to put a hole_t in it. We assume a "normal" platform requiring 2-byte
|
|
* alignment for hole_t, and letting us manipulate uintptr_t in the conventional
|
|
* fashion.
|
|
*/
|
|
static hole_t *hole_pointer(const buffer_t *buf, uint16_t offset)
|
|
{
|
|
uintptr_t ptr = (uintptr_t)(buffer_data_pointer(buf) + offset);
|
|
|
|
return (hole_t *)((ptr + 1) & ~(uintptr_t) 1);
|
|
}
|
|
|
|
static void delete_hole(buffer_t *buf, uint16_t hole, uint16_t *prev_ptr)
|
|
{
|
|
hole_t *hole_ptr = hole_pointer(buf, hole);
|
|
|
|
*prev_ptr = hole_ptr->next;
|
|
}
|
|
|
|
static hole_t *create_hole(buffer_t *buf, uint16_t first, uint16_t last, uint16_t *prev_ptr)
|
|
{
|
|
hole_t *hole_ptr = hole_pointer(buf, first);
|
|
hole_ptr->first = first;
|
|
hole_ptr->last = last;
|
|
hole_ptr->next = *prev_ptr;
|
|
|
|
*prev_ptr = first;
|
|
return hole_ptr;
|
|
}
|
|
|
|
/*
|
|
* RFC 4944 is oddly designed - it has blurred the header compression
|
|
* and fragmentation layers. The datagram_size and datagram_offset field are
|
|
* specified in terms of the uncompressed IPv6 datagram, not the actual 6LoWPAN
|
|
* payload.
|
|
*
|
|
* This complicates reassembly if you don't decompress first; we don't because
|
|
* the original upper layer works on IPHC headers directly, rather than native
|
|
* IPv6.
|
|
*
|
|
* To handle the general case, including arbitrary fragment order so we don't
|
|
* always know the 6LoWPAN size of the first fragment, the reassembly buffer
|
|
* always leaves space for the uncompressed IPv6 header, and 1 byte more for a
|
|
* 6LoWPAN "uncompressed IPv6" dispatch byte. This means non-first fragments
|
|
* are always placed at buffer_data_pointer() + datagram_offset.
|
|
*
|
|
* This routine doesn't explicitly distinguish the compressed and uncompressed
|
|
* cases - the difference arises purely from the output of "iphc_header_scan",
|
|
* which sets "pattern" to the difference between IPv6 and 6LoWPAN size -
|
|
* but to aid understanding, here's what it ends up doing in the two cases:
|
|
*
|
|
* IPHC compressed case
|
|
* --------------------
|
|
*
|
|
* -4 0 0x50
|
|
* +---------+--------+--------+ 0x50 bytes of 6LoWPAN data
|
|
* | FRAG1 | IPHC | data1 | 0x70 bytes of uncompressed IPv6 data
|
|
* +---------+--------+--------+ "pattern" = 0x70 - 0x50 = 0x20
|
|
*
|
|
* -5 0 0x60 (datagram_size = 0xD0)
|
|
* +----------+-------------+
|
|
* |FRAGN 0x70| data2 |
|
|
* +----------+-------------+
|
|
*
|
|
* During assembly, the data pointer points at the "0" position representing the
|
|
* virtual start of the IPv6 packet:
|
|
*
|
|
* -1 0 0x20 0x70 0xD0
|
|
* +-+---------+--------+--------+-------------+
|
|
* | | padding | IPHC | data1 | data2 |
|
|
* +-+---------+--------+--------+-------------+
|
|
*
|
|
* On completion of assembly, the start pointer moves forward to point at the
|
|
* IPHC header. (This means buffer size is slightly inefficient for an IPHC
|
|
* upper layer, but it does reserve headroom for decompression to native IPv6.)
|
|
*
|
|
* -0x21 0 0x50 0xB0
|
|
* +-----------+--------+--------+-------------+
|
|
* | headroom | IPHC | data1 | data2 |
|
|
* +-----------+--------+--------+-------------+
|
|
|
|
* Uncompressed case
|
|
* -----------------
|
|
|
|
* -4 0 1 0x71 D = "Uncompressed IPv6" dispatch type 0x41
|
|
* +---------+-+--------------+ 0x71 bytes of 6LoWPAN data
|
|
* | FRAG1 |D| data1 | 0x70 bytes of uncompressed IPv6 data
|
|
* +---------+-+--------------+ "pattern" = 0x70 - 0x71 = -1
|
|
*
|
|
* -5 0 0x60 (datagram_size = 0xD0)
|
|
* +----------+---------------+
|
|
* |FRAGN 0x70| data2 |
|
|
* +----------+---------------+
|
|
*
|
|
* During assembly, the data pointer points at the "0" position representing the
|
|
* start of the IPv6 packet:
|
|
*
|
|
* -1 0 0x70 0xD0
|
|
* +-+--------------+---------------+
|
|
* |D| data1 | data2 |
|
|
* +-+--------------+---------------+
|
|
*
|
|
* On completion of assembly, the start pointer moves back to point to the
|
|
* 6LoWPAN dispatch byte:
|
|
*
|
|
* 0 1 0x71 0xD1
|
|
* +-+--------------+---------------+
|
|
* |D| data1 | data2 |
|
|
* +-+--------------+---------------+
|
|
|
|
* (And if it's neither of these cases, a "native" 6LoWPAN reassembly happens
|
|
* with "pattern" set to 0 - what probably should have happened in the first
|
|
* place; offsets are treated as 6LoWPAN offsets from the start of the
|
|
* fragmented 6LoWPAN data).
|
|
*/
|
|
|
|
//Discover
|
|
static reassembly_interface_t *reassembly_interface_discover(int8_t interfaceId)
|
|
{
|
|
|
|
ns_list_foreach(reassembly_interface_t, interface_ptr, &reassembly_interface_list) {
|
|
if (interfaceId == interface_ptr->interface_id) {
|
|
return interface_ptr;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void reassembly_entry_free(reassembly_interface_t *interface_ptr, reassembly_entry_t *entry)
|
|
{
|
|
ns_list_remove(&interface_ptr->rx_list, entry);
|
|
ns_list_add_to_start(&interface_ptr->free_list, entry);
|
|
if (entry->buf) {
|
|
entry->buf = buffer_free(entry->buf);
|
|
}
|
|
}
|
|
|
|
static void reassembly_list_free(reassembly_interface_t *interface_ptr)
|
|
{
|
|
ns_list_foreach_safe(reassembly_entry_t, reassembly_entry, &interface_ptr->rx_list) {
|
|
reassembly_entry_free(interface_ptr, reassembly_entry);
|
|
}
|
|
}
|
|
|
|
|
|
static reassembly_entry_t *reassembly_already_action(reassembly_list_t *reassembly_list, buffer_t *buf, uint16_t tag, uint16_t size)
|
|
{
|
|
ns_list_foreach(reassembly_entry_t, reassembly_entry, reassembly_list) {
|
|
if ((reassembly_entry->tag == tag) && (reassembly_entry->size == size) &&
|
|
reassembly_entry->buf->src_sa.addr_type == buf->src_sa.addr_type &&
|
|
reassembly_entry->buf->dst_sa.addr_type == buf->dst_sa.addr_type) {
|
|
/* Type will be either long or short 802.15.4 - we skip the PAN ID */
|
|
if (memcmp(reassembly_entry->buf->src_sa.address + 2, buf->src_sa.address + 2, addr_len_from_type(buf->src_sa.addr_type) - 2) == 0 &&
|
|
memcmp(reassembly_entry->buf->dst_sa.address + 2, buf->dst_sa.address + 2, addr_len_from_type(buf->dst_sa.addr_type) - 2) == 0) {
|
|
return reassembly_entry;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
static reassembly_entry_t *lowpan_adaptation_reassembly_get(reassembly_interface_t *interface_ptr)
|
|
{
|
|
reassembly_entry_t *entry = ns_list_get_first(&interface_ptr->free_list);
|
|
if (!entry) {
|
|
return NULL;
|
|
}
|
|
|
|
ns_list_remove(&interface_ptr->free_list, entry);
|
|
memset(entry, 0, sizeof(reassembly_entry_t));
|
|
//Add to first
|
|
ns_list_add_to_start(&interface_ptr->rx_list, entry);
|
|
|
|
return entry;
|
|
}
|
|
|
|
|
|
buffer_t *cipv6_frag_reassembly(int8_t interface_id, buffer_t *buf)
|
|
{
|
|
reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
|
|
if (!interface_ptr) {
|
|
return buffer_free(buf);
|
|
}
|
|
|
|
uint16_t datagram_size, datagram_tag;
|
|
uint16_t fragment_first;
|
|
uint8_t frag_header;
|
|
|
|
uint8_t *ptr = buffer_data_pointer(buf);
|
|
|
|
frag_header = ptr[0];
|
|
datagram_size = common_read_16_bit(ptr) & 0x07FF;
|
|
|
|
if (datagram_size == 0) {
|
|
goto resassembly_error;
|
|
}
|
|
|
|
ptr += 2;
|
|
datagram_tag = common_read_16_bit(ptr);
|
|
ptr += 2;
|
|
if (frag_header & LOWPAN_FRAGN_BIT) {
|
|
fragment_first = *ptr++ << 3;
|
|
} else {
|
|
fragment_first = 0;
|
|
}
|
|
|
|
/* Consume the fragment header. We don't distinguish FRAG1/FRAGN after this
|
|
* point (we treat FRAGN with offset 0 the same as FRAG1)
|
|
*/
|
|
buffer_data_pointer_set(buf, ptr);
|
|
reassembly_entry_t *frag_ptr = reassembly_already_action(&interface_ptr->rx_list, buf, datagram_tag, datagram_size);
|
|
|
|
if (!frag_ptr) {
|
|
|
|
frag_ptr = lowpan_adaptation_reassembly_get(interface_ptr);
|
|
if (!frag_ptr) {
|
|
goto resassembly_error;
|
|
}
|
|
|
|
buffer_t *reassembly_buffer = buffer_get(1 + ((datagram_size + 7) & ~7));
|
|
if (!reassembly_buffer) {
|
|
//Put allocated back to free
|
|
reassembly_entry_free(interface_ptr, frag_ptr);
|
|
goto resassembly_error;
|
|
}
|
|
|
|
// Allocate the reassembly buffer.
|
|
// Allow 1 byte extra for an "Uncompressed IPv6" dispatch byte - the
|
|
// 6LoWPAN data can be 1 byte longer than the IPv6 data.
|
|
// Also, round datagram size up to a multiple of 8 to ensure we have
|
|
// room for a final hole descriptor (it can spill past the indicated
|
|
// datagram size if the last fragment is smaller than 8 bytes).
|
|
|
|
reassembly_buffer->src_sa = buf->src_sa;
|
|
reassembly_buffer->dst_sa = buf->dst_sa;
|
|
frag_ptr->ttl = interface_ptr->timeout;
|
|
frag_ptr->tag = datagram_tag;
|
|
frag_ptr->size = datagram_size;
|
|
// Set buffer length and adjust start pointer, so it represents the
|
|
// uncompressed IPv6 packet. (See comment block before this function).
|
|
buffer_data_length_set(reassembly_buffer, 1 + datagram_size);
|
|
buffer_data_strip_header(reassembly_buffer, 1);
|
|
// Write initial hole descriptor into buffer
|
|
frag_ptr->offset = 0xffff;
|
|
create_hole(reassembly_buffer, 0, datagram_size - 1, &frag_ptr->offset);
|
|
frag_ptr->buf = reassembly_buffer;
|
|
}
|
|
|
|
/* For the first link fragment, work out and remember the "pattern"
|
|
* (difference between6LoWPAN and IPv6 size), and also copy the buffer
|
|
* header metadata.
|
|
*/
|
|
uint16_t lowpan_size, ipv6_size;
|
|
if (fragment_first == 0) {
|
|
uint16_t uncompressed_header_size;
|
|
uint8_t compressed_header_size;
|
|
compressed_header_size = iphc_header_scan(buf, &uncompressed_header_size);
|
|
lowpan_size = buffer_data_length(buf);
|
|
ipv6_size = lowpan_size - compressed_header_size + uncompressed_header_size;
|
|
frag_ptr->pattern = ipv6_size - lowpan_size;
|
|
|
|
/* Clone the buffer header from this first fragment, preserving only size + pointers */
|
|
/* Also the security flag - this fragment's flag is merged in later */
|
|
bool buf_security = frag_ptr->buf->options.ll_security_bypass_rx;
|
|
buffer_copy_metadata(frag_ptr->buf, buf, true);
|
|
frag_ptr->buf->options.ll_security_bypass_rx = buf_security;
|
|
} else {
|
|
ipv6_size = lowpan_size = buffer_data_length(buf);
|
|
}
|
|
|
|
uint16_t fragment_last = fragment_first + ipv6_size - 1;
|
|
if (fragment_last >= datagram_size) {
|
|
tr_err("Frag out-of-range: last=%u, size=%u", fragment_last, datagram_size);
|
|
//Free Current entry
|
|
reassembly_entry_free(interface_ptr, frag_ptr);
|
|
goto resassembly_error;
|
|
}
|
|
|
|
/* Hole-filling algorithm, basically as per RFC 815, but with added
|
|
* checks for overlap. The hole list is kept sorted, as per
|
|
* ipv6_fragmentation.c, but that's not relevant in this version.
|
|
*/
|
|
uint16_t hole_off = frag_ptr->offset;
|
|
uint16_t *prev_ptr = &frag_ptr->offset;
|
|
do {
|
|
hole_t *hole = hole_pointer(frag_ptr->buf, hole_off);
|
|
uint_fast16_t hole_first = hole->first;
|
|
uint_fast16_t hole_last = hole->last;
|
|
|
|
/* Fragment is beyond this hole - move to next (RFC 815 step 2) */
|
|
/* Fragment is before this hole - move to next (RFC 815 step 3) */
|
|
if (fragment_first > hole_last || fragment_last < hole_first) {
|
|
prev_ptr = &hole->next;
|
|
hole_off = hole->next;
|
|
continue;
|
|
}
|
|
|
|
/* If any of the fragment lies outside the hole, it indicates a problem;
|
|
* we only expect repeat data from retransmission, so fragments should
|
|
* always lie entirely within a hole or existing data, not straddle
|
|
* them. If we see this happen then junk existing data, making this the
|
|
* first fragment of a new reassembly (RFC 4944).
|
|
*/
|
|
if (fragment_first < hole_first || fragment_last > hole_last) {
|
|
tr_err("Frag overlap: hole %"PRIuFAST16"-%"PRIuFAST16", frag %"PRIu16"-%"PRIu16, hole_first, hole_last, fragment_first, fragment_last);
|
|
protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
|
|
/* Forget previous data by marking as "all hole" */
|
|
frag_ptr->offset = 0xffff;
|
|
create_hole(frag_ptr->buf, hole_off = hole_first = 0, hole_last = datagram_size - 1, prev_ptr = &frag_ptr->offset);
|
|
}
|
|
|
|
/* Unhook this hole from the hole list (RFC 815 step 4) */
|
|
delete_hole(frag_ptr->buf, hole_off, prev_ptr);
|
|
|
|
/* Create a new hole in front if necessary (RFC 815 step 5) */
|
|
if (fragment_first > hole_first) {
|
|
prev_ptr = &create_hole(frag_ptr->buf, hole_first, fragment_first - 1, prev_ptr)->next;
|
|
}
|
|
|
|
/* Create a following hole if necessary (RFC 815 step 6) */
|
|
if (fragment_last < hole_last) {
|
|
create_hole(frag_ptr->buf, fragment_last + 1, hole_last, prev_ptr);
|
|
}
|
|
|
|
/* Unlike RFC 815, we're now done. We don't allow overlaps, so we finish
|
|
* as soon as we identify one hole that it entirely or partially fills */
|
|
break;
|
|
} while (hole_off != 0xffff);
|
|
|
|
/* Hole list updated, can now copy in the fragment data - to make sure the
|
|
* initial fragment goes in the right place we use the end offset, rather
|
|
* than the start offset. */
|
|
memcpy(buffer_data_pointer(frag_ptr->buf) + fragment_last + 1 - lowpan_size, buffer_data_pointer(buf), lowpan_size);
|
|
|
|
/* Combine the "improper security" flags, so reassembled buffer's flag is set if any fragment wasn't secure */
|
|
/* XXX should have some sort of overall "merge buffer metadata" routine handling this and whatever else */
|
|
frag_ptr->buf->options.ll_security_bypass_rx |= buf->options.ll_security_bypass_rx;
|
|
|
|
/* We've finished with the original fragment buffer */
|
|
buf = buffer_free(buf);
|
|
|
|
/* Completion check - any holes left? */
|
|
if (frag_ptr->offset != 0xffff) {
|
|
/* Not yet complete - processing finished on this fragment */
|
|
return NULL;
|
|
}
|
|
|
|
/* No more holes, so our reassembly is complete */
|
|
buf = frag_ptr->buf;
|
|
frag_ptr->buf = NULL;
|
|
reassembly_entry_free(interface_ptr, frag_ptr);
|
|
|
|
/* Buffer start pointer is currently at the "start of uncompressed IPv6
|
|
* packet" position. Move it either forwards or backwards to match
|
|
* the IPHC data (could be compressed, or uncompressed with added dispatch
|
|
* byte).
|
|
*/
|
|
buf->buf_ptr += frag_ptr->pattern;
|
|
buf->info = (buffer_info_t)(B_DIR_UP | B_FROM_FRAGMENTATION | B_TO_IPV6_TXRX);
|
|
return buf;
|
|
|
|
resassembly_error:
|
|
protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
|
|
return buffer_free(buf);
|
|
}
|
|
|
|
static void reassembly_entry_timer_update(reassembly_interface_t *interface_ptr, uint16_t seconds)
|
|
{
|
|
ns_list_foreach_safe(reassembly_entry_t, reassembly_entry, &interface_ptr->rx_list) {
|
|
if (reassembly_entry->ttl > seconds) {
|
|
reassembly_entry->ttl -= seconds;
|
|
} else {
|
|
protocol_stats_update(STATS_FRAG_RX_ERROR, 1);
|
|
tr_debug("Reassembly TO: src %s size %u",
|
|
trace_sockaddr(&reassembly_entry->buf->src_sa, true),
|
|
reassembly_entry->size);
|
|
reassembly_entry_free(interface_ptr, reassembly_entry);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cipv6_frag_timer(uint16_t seconds)
|
|
{
|
|
ns_list_foreach(reassembly_interface_t, interface_ptr, &reassembly_interface_list) {
|
|
reassembly_entry_timer_update(interface_ptr, seconds);
|
|
}
|
|
}
|
|
|
|
int8_t reassembly_interface_free(int8_t interface_id)
|
|
{
|
|
//Discover
|
|
reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
|
|
if (!interface_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
ns_list_remove(&reassembly_interface_list, interface_ptr);
|
|
|
|
//Free Dynamic allocated entry buffer
|
|
ns_dyn_mem_free(interface_ptr->entry_pointer_buffer);
|
|
ns_dyn_mem_free(interface_ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int8_t reassembly_interface_init(int8_t interface_id, uint8_t reassembly_session_limit, uint16_t reassembly_timeout)
|
|
{
|
|
|
|
if (!reassembly_session_limit || !reassembly_timeout) {
|
|
return -2;
|
|
}
|
|
|
|
//Remove old interface
|
|
reassembly_interface_free(interface_id);
|
|
|
|
//Allocate new
|
|
reassembly_interface_t *interface_ptr = ns_dyn_mem_alloc(sizeof(reassembly_interface_t));
|
|
reassembly_entry_t *reassemply_ptr = ns_dyn_mem_alloc(sizeof(reassembly_entry_t) * reassembly_session_limit);
|
|
if (!interface_ptr || !reassemply_ptr) {
|
|
ns_dyn_mem_free(interface_ptr);
|
|
ns_dyn_mem_free(reassemply_ptr);
|
|
return -1;
|
|
}
|
|
|
|
memset(interface_ptr, 0, sizeof(reassembly_interface_t));
|
|
interface_ptr->interface_id = interface_id;
|
|
interface_ptr->timeout = reassembly_timeout;
|
|
interface_ptr->entry_pointer_buffer = reassemply_ptr;
|
|
ns_list_init(&interface_ptr->free_list);
|
|
ns_list_init(&interface_ptr->rx_list);
|
|
|
|
for (uint8_t i = 0; i < reassembly_session_limit ; i++) {
|
|
ns_list_add_to_end(&interface_ptr->free_list, reassemply_ptr);
|
|
reassemply_ptr++;
|
|
}
|
|
|
|
ns_list_add_to_end(&reassembly_interface_list, interface_ptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int8_t reassembly_interface_reset(int8_t interface_id)
|
|
{
|
|
//Discover
|
|
reassembly_interface_t *interface_ptr = reassembly_interface_discover(interface_id);
|
|
if (!interface_ptr) {
|
|
return -1;
|
|
}
|
|
|
|
//Free Reaasembled queue
|
|
reassembly_list_free(interface_ptr);
|
|
return 0;
|
|
}
|
|
|