429 lines
15 KiB
C
429 lines
15 KiB
C
/*
|
|
******************************************************************************
|
|
* SOURCE FILE
|
|
*
|
|
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: iplite_frag.c,v $ $Revision: 1.26 $
|
|
* $Source: /home/interpeak/CVSRoot/iplite2/src/iplite_frag.c,v $
|
|
* $Author: kenneth $
|
|
* $State: Exp $ $Locker: $
|
|
*
|
|
* INTERPEAK_COPYRIGHT_STRING
|
|
* Design and implementation by Kenneth Jonsson <kenneth@interpeak.se>
|
|
******************************************************************************
|
|
*/
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 1 DESCRIPTION
|
|
****************************************************************************
|
|
* Fragmentation and reassembly functions.
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 2 CONFIGURATION
|
|
****************************************************************************
|
|
*/
|
|
|
|
#ifndef IPCOM_KERNEL
|
|
#define IPCOM_KERNEL
|
|
#endif
|
|
#include "iplite_config.h"
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 3 INCLUDE FILES
|
|
****************************************************************************
|
|
*/
|
|
|
|
#if !defined(IPLITE_ONE_FILE) || defined(IPLITE_C)
|
|
|
|
#define IPCOM_USE_CLIB_PROTO
|
|
#include <ipcom_clib.h>
|
|
#include <ipcom_os.h>
|
|
|
|
#include "iplite_h.h"
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 4 DEFINES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 5 TYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 6 EXTERN PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 7 LOCAL PROTOTYPES
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 8 DATA
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 9 LOCAL FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
****************************************************************************
|
|
* 10 PUBLIC FUNCTIONS
|
|
****************************************************************************
|
|
*/
|
|
|
|
/*
|
|
*===========================================================================
|
|
* iplite_fragment_packet
|
|
*===========================================================================
|
|
* Description: Splits a packet that is to big to fit into the MTU of the
|
|
* network interface.
|
|
* Parameters: pkt - The packet to split. pkt->start is the offset to the
|
|
* extension header (or transport layer protocol) with the
|
|
* id of 'next_hdr'. This packet is released by this function.
|
|
* ip_hdr_size - The size of the IP header.
|
|
* other_hdr_size - The size of other headers that will be
|
|
* added due to fragmentation.
|
|
* mtu - The MTU of the network interface.
|
|
* Returns: A list of fragments or IP_NULL if not enough memory could
|
|
* be allocated to create all fragments.
|
|
*/
|
|
IP_GLOBAL Ipcom_pkt *
|
|
iplite_fragment_packet(Ipcom_pkt *pkt, int ip_hdr_size, int other_hdr_size, int mtu)
|
|
{
|
|
Ipcom_pkt *frag_pkt_head;
|
|
Ipcom_pkt *frag_pkt_tail;
|
|
Ipcom_pkt *frag_pkt;
|
|
int frag_size;
|
|
int total_size;
|
|
int size_of_this_fragment;
|
|
int frag_offset;
|
|
|
|
total_size = pkt->end - pkt->start;
|
|
frag_size = (mtu - ip_hdr_size - other_hdr_size) & 0xfff8;
|
|
frag_pkt_head = IP_NULL;
|
|
frag_pkt_tail = IP_NULL;
|
|
frag_offset = 0;
|
|
|
|
while (frag_offset < total_size)
|
|
{
|
|
if (total_size - frag_offset > frag_size)
|
|
size_of_this_fragment = frag_size;
|
|
else
|
|
size_of_this_fragment = total_size - frag_offset;
|
|
|
|
frag_pkt = ipcom_pkt_malloc(mtu,
|
|
IP_FLAG_FC_STACKCONTEXT
|
|
| (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_NONBLOCKING) ? 0 : IP_FLAG_FC_BLOCKOK));
|
|
if (frag_pkt == IP_NULL)
|
|
{
|
|
IPLITE_STATS(pkt_frag_nomem++);
|
|
ipcom_pkt_free(pkt);
|
|
if (frag_pkt_head != IP_NULL)
|
|
ipcom_pkt_free(frag_pkt_head);
|
|
return IP_NULL;
|
|
}
|
|
|
|
#ifdef IPCOM_USE_MIB2
|
|
if(other_hdr_size == 0)
|
|
{
|
|
#ifdef IPCOM_USE_INET
|
|
IPCOM_MIB2(ipFragCreates++);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifdef IPCOM_USE_INET6
|
|
Iplite_netif *netif = iplite_if_indextonetif(pkt->ifindex);
|
|
|
|
IPCOM_MIB2_NETIF(ipv6IfStatsOutFragCreates++);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if (frag_pkt_head == IP_NULL)
|
|
frag_pkt_head = frag_pkt;
|
|
else
|
|
frag_pkt_tail->next_fragment = frag_pkt;
|
|
frag_pkt_tail = frag_pkt;
|
|
|
|
IPCOM_PKT_TRACE(frag_pkt, IPCOM_GET_PKT_ID(pkt));
|
|
frag_pkt->fd = pkt->fd;
|
|
frag_pkt->ifindex = pkt->ifindex;
|
|
frag_pkt->flags = pkt->flags;
|
|
/* Copy IP packet payload */
|
|
frag_pkt->start = pkt->start + other_hdr_size;
|
|
frag_pkt->end = frag_pkt->start + size_of_this_fragment;
|
|
ip_assert(frag_pkt->start < frag_pkt->maxlen);
|
|
ipcom_memcpy(&frag_pkt->data[frag_pkt->start],
|
|
&pkt->data[pkt->start + frag_offset],
|
|
(Ip_size_t)size_of_this_fragment);
|
|
|
|
frag_pkt->offset = (Ip_u16) (frag_offset + pkt->offset);
|
|
frag_offset += size_of_this_fragment;
|
|
IP_BIT_SET(frag_pkt->flags, IPCOM_PKT_FLAG_MORE_FRAG);
|
|
}
|
|
if (IP_BIT_ISSET(pkt->flags, IPCOM_PKT_FLAG_FRAGMENT) == IP_FALSE)
|
|
IP_BIT_CLR(frag_pkt_tail->flags, IPCOM_PKT_FLAG_MORE_FRAG);
|
|
ip_assert(pkt->start + frag_offset == pkt->end);
|
|
ip_assert(frag_pkt_head != IP_NULL);
|
|
ip_assert(frag_pkt_head->next_fragment != IP_NULL);
|
|
ipcom_pkt_free(pkt);
|
|
return frag_pkt_head;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* iplite_reassembly
|
|
*===========================================================================
|
|
* Description: Reassembles fragmented IP packets.
|
|
*
|
|
* ... ...
|
|
* | |
|
|
* pkt #A pkt #B
|
|
* frag #b frag #y
|
|
* ^ ^
|
|
* | |
|
|
* | |
|
|
* next_fragment next_fragment
|
|
* | |
|
|
* | |
|
|
* ---> pkt #A -- next --> pkt #B -- next --> ...
|
|
* | frag #a frag #x
|
|
* |
|
|
* reassembly_list
|
|
*
|
|
* pkt #A chain will time out before pkt #B if not all fragments
|
|
* are recieved.
|
|
* frag #a and frag #x has lower offset than frag #b and frag #y.
|
|
* Parameters: pkt - A newly received fragment.
|
|
* Returns: Pointer to the reassembled packet or IP_NULL if more fragments
|
|
* is needed.
|
|
*/
|
|
IP_GLOBAL Ipcom_pkt *
|
|
iplite_reassembly(Ipcom_pkt **reassembly_list,
|
|
Ipcom_pkt *pkt,
|
|
Iplite_is_part_of_same_pkt_func is_part_of_same_pkt,
|
|
Iplite_get_offset_func get_offset,
|
|
Iplite_more_fragments_func more_fragments,
|
|
Iplite_timeout_handler tmo_cb,
|
|
int frag_hdr_len)
|
|
{
|
|
Ipcom_pkt **pkt_it; /* The packet this fragment belongs to */
|
|
Ipcom_pkt **frag_it; /* The fragment within the selected packet this fragment
|
|
should inserted in-font of */
|
|
Ipcom_pkt *frag_head = IP_NULL;
|
|
Ipcom_pkt *tmo_pkt;
|
|
Ipcom_pkt *new_tmo_pkt;
|
|
int offset;
|
|
int previous_end;
|
|
|
|
IPLITE_CODE_ISLOCKED_ASSERT();
|
|
|
|
tmo_pkt = *reassembly_list;
|
|
|
|
IPLITE_PKT_SET_TMO(pkt, IP_NULL);
|
|
offset = get_offset(pkt);
|
|
for (pkt_it = reassembly_list; *pkt_it != IP_NULL; pkt_it = &(*pkt_it)->next)
|
|
{
|
|
if (is_part_of_same_pkt(pkt, *pkt_it))
|
|
break;
|
|
}
|
|
|
|
if (*pkt_it == IP_NULL)
|
|
{
|
|
/* First fragment received of this packet, add it last to the list */
|
|
*pkt_it = pkt;
|
|
pkt->next = IP_NULL;
|
|
ip_assert(pkt->next_fragment == IP_NULL);
|
|
IPLITE_PKT_SET_TIMEOUT_ABS(pkt, iplite.seconds + IPLITE_CONF_REASSEMBLY_TIMEOUT);
|
|
}
|
|
else
|
|
{
|
|
previous_end = 0;
|
|
for (frag_it = pkt_it; (*frag_it) != IP_NULL; frag_it = &(*frag_it)->next_fragment)
|
|
{
|
|
if (offset <= get_offset(*frag_it))
|
|
{
|
|
/* The new fragment should be in front of this fragment */
|
|
break;
|
|
}
|
|
previous_end = (*frag_it)->end - (*frag_it)->start
|
|
+ get_offset(*frag_it) - frag_hdr_len;
|
|
}
|
|
|
|
if (previous_end > offset
|
|
|| (*frag_it
|
|
&& (offset == get_offset(*frag_it)
|
|
|| offset + pkt->end - pkt->start - frag_hdr_len > get_offset(*frag_it))))
|
|
{
|
|
/* Overlapping segment or this fragment has already been received */
|
|
ipcom_pkt_free(pkt);
|
|
return IP_NULL;
|
|
}
|
|
|
|
/* The timeout is calculated from the point when the first fragment
|
|
in this packet was received */
|
|
IPLITE_PKT_SET_TIMEOUT_ABS(pkt, IPLITE_PKT_GET_TIMEOUT_ABS(*pkt_it));
|
|
|
|
/* Insert the fragment into the fragment list */
|
|
pkt->next_fragment = *frag_it;
|
|
*frag_it = pkt;
|
|
|
|
if (pkt->next_fragment && pkt->next_fragment->next)
|
|
{
|
|
/* New head in this list, move the 'next' pointer to the new head */
|
|
pkt->next = pkt->next_fragment->next;
|
|
pkt->next_fragment->next = IP_NULL;
|
|
}
|
|
}
|
|
|
|
/* Check if all fragments has been received */
|
|
for (frag_it = pkt_it, offset = 0; (*frag_it) != IP_NULL; frag_it = &(*frag_it)->next_fragment)
|
|
{
|
|
ip_assert((offset & 0x7) == 0);
|
|
if (offset != get_offset(*frag_it))
|
|
break;
|
|
|
|
if (!more_fragments(*frag_it))
|
|
{
|
|
/* All fragments was received */
|
|
frag_head = *pkt_it;
|
|
|
|
/* Remove the fragment list from reassembly list */
|
|
*pkt_it = frag_head->next;
|
|
frag_head->next = IP_NULL;
|
|
break;
|
|
}
|
|
|
|
offset += (*frag_it)->end - (*frag_it)->start - frag_hdr_len;
|
|
}
|
|
|
|
/* The TMO packet will change if
|
|
* - the oldest fragment list is returned
|
|
* - if the new fragment is a new head in the oldest fragment list
|
|
*/
|
|
new_tmo_pkt = *reassembly_list;
|
|
if (new_tmo_pkt != tmo_pkt)
|
|
{
|
|
if (tmo_pkt)
|
|
iplite_timeout_cancel(IPLITE_PKT_GET_TMO(tmo_pkt));
|
|
if (new_tmo_pkt)
|
|
(void)iplite_timeout_schedule(IPLITE_PKT_GET_TIMEOUT(new_tmo_pkt) * 1000,
|
|
tmo_cb,
|
|
new_tmo_pkt,
|
|
IPLITE_PKT_GET_TMO_PTR(new_tmo_pkt));
|
|
|
|
}
|
|
/* All fragments has not been received if frag_head == IP_NULL */
|
|
return frag_head;
|
|
}
|
|
|
|
|
|
/*
|
|
*===========================================================================
|
|
* iplite_create_reassembled_packet
|
|
*===========================================================================
|
|
* Description: Reassembles a list of fragments to the orginal packet and
|
|
* creates a new packet of it.
|
|
* Parameters: fragment_list - The first fragment in the list.
|
|
* ip_hdr_size - The size of the IP header.
|
|
* frag_hdr_size - The size of the header containing the fragmentation id.
|
|
* update_ip_header - A function that can finish the IP header
|
|
* of the reassembled packet.
|
|
* Returns: The reassembled packet or IP_NULL if a packet big enough
|
|
* to hold the whole packet.
|
|
*/
|
|
IP_GLOBAL Ipcom_pkt *
|
|
iplite_create_reassembled_packet(Ipcom_pkt *fragment_list,
|
|
int ip_hdr_size,
|
|
int frag_hdr_size,
|
|
Iplite_update_ip_header_func update_ip_header,
|
|
Iplite_reassemble_err_hnd reassemble_err_hnd)
|
|
{
|
|
Ipcom_pkt *pkt = IP_NULL;
|
|
Ipcom_pkt *pkt_it;
|
|
int total_size = 0;
|
|
|
|
for (pkt_it = fragment_list; pkt_it != IP_NULL; pkt_it = pkt_it->next_fragment)
|
|
{
|
|
pkt_it->start += frag_hdr_size;
|
|
total_size += pkt_it->end - pkt_it->start;
|
|
ip_assert(pkt_it->ipstart < pkt_it->start);
|
|
ip_assert(pkt_it->start < pkt_it->end);
|
|
}
|
|
|
|
if (total_size > 65535)
|
|
{
|
|
if (reassemble_err_hnd)
|
|
reassemble_err_hnd(fragment_list);
|
|
goto done;
|
|
}
|
|
|
|
total_size += ip_hdr_size;
|
|
|
|
pkt = ipcom_pkt_malloc(IP_MIN(total_size, iplite.pkt_pool_max_mtu),
|
|
IP_FLAG_FC_STACKCONTEXT);
|
|
if (pkt == IP_NULL || pkt->maxlen < total_size)
|
|
{
|
|
IPLITE_STATS(pkt_reasm_nomem++);
|
|
goto done;
|
|
}
|
|
|
|
pkt->start = fragment_list->ipstart;
|
|
if (pkt->start + total_size > pkt->maxlen)
|
|
pkt->start = (pkt->maxlen - total_size) & ~0x3;
|
|
pkt->ipstart = pkt->start;
|
|
pkt->end = pkt->start + ip_hdr_size;
|
|
pkt->ifindex = fragment_list->ifindex;
|
|
|
|
/* Copy the IP header */
|
|
ipcom_memcpy(&pkt->data[pkt->start],
|
|
&fragment_list->data[fragment_list->ipstart],
|
|
(Ip_size_t)ip_hdr_size);
|
|
|
|
/* Copy the rest of the packet */
|
|
for (pkt_it = fragment_list; pkt_it != IP_NULL; pkt_it = pkt_it->next_fragment)
|
|
{
|
|
ipcom_memcpy(&pkt->data[pkt->end],
|
|
&pkt_it->data[pkt_it->start],
|
|
(Ip_size_t)(pkt_it->end - pkt_it->start));
|
|
pkt->end += pkt_it->end - pkt_it->start;
|
|
ip_assert(pkt->end <= pkt->maxlen);
|
|
}
|
|
|
|
update_ip_header(pkt, fragment_list);
|
|
|
|
done:
|
|
ipcom_pkt_free(fragment_list);
|
|
return pkt;
|
|
}
|
|
|
|
|
|
#endif /* !defined(IPLITE_ONE_FILE) || defined(IPLITE_C) */
|
|
|
|
|
|
/*
|
|
****************************************************************************
|
|
* END OF FILE
|
|
****************************************************************************
|
|
*/
|
|
|