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