Files
m241Neelima/components/ip_net2-6.9/iplite2/src/iplite_frag.c
T
2026-07-30 11:19:44 +05:30

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
****************************************************************************
*/