Name: Fragmentation and Re-assembly Description: Given a payload that exceeds a certain length MAX_FRAGMENT_LENGTH, the send side of this component splits the payload into more than one fragment such that the length of none of the fragments exceeds MAX_FRAGMENT_LENGTH. The receive side of this component reassembles all fragments of a particular payload into the original payload. Delivery of reassembled payload is not guaranteed. On arrival of the first fragment of a payload, a timer is started. If all fragments of this payload do not arrive before the expiration of the timer, all received fragments of this payload are discarded. Constraints: 1. Values for “payload identifier” do not wrap around. 2. Transmitted fragments of a particular payload are numbered in a monotonically increasing manner, starting at 1 and leaving no gaps. 3. The transmit state machine handles one payload fragmentation at a time. 4. Hop-by-hop re-assembly of fragments is supported (required??). Properties: 1. Length of none of the transmitted fragments exceeds MAX_FRAGMENT_LENGTH. 2. Delivered payload is identical to transmitted payload. 3. No more than one copy of the transmitted payload is delivered. 4. Delivery of the transmitted payload is not guaranteed. 5. Receive side of component never consumes more than MAX_BUFFER_LENGTH of memory for buffering fragments. Assumptions: 1. Received fragments have no bit errors. Parameters: Name: Type: Description: Required? Default Value: (SEND) MAX_FRAGMENT_LENGTH int The maximum size of any transmitted fragment. Yes None Name: Type: Description: (SEND) PayloadIdentifierKey int Key to make generated “payload identifier” globally unique. (unique per stack per node) Required? Default Value: Yes None Note: All values of the “payload identifier” tagged by all sender components (on same or different hosts) are unique. (In conventional protocols, the source address of the fragment is used to distinguish fragments from different hosts that carry the same “payload identifier”. The same technique (requires access to the “source address” header field) or some other mechanism to ensure uniqueness of “payload identifier” shall be used.) Name: Type: Description: Required? Default Value: Name: Type: Description: Required? Default Value: (RECEIVE) TimeoutLimit int Timeout value to start garbage collection of buffered fragments, which could not be reassembled. Yes None (RECEIVE) MAX_BUFFER_LENGTH int Maximum memory that the receive state machine shall use to buffer fragments received out of order. Yes None Local Memory: type RcvdFragments = record of MaxFragmentNum : int BufferedFragments : indexed collection of byte arrays end record This record is meant to hold all received fragments that have the same ‘Payload Identifier’. The member ‘MaxFragmentNum’ will be set to pktComponentMemory.FragmentNum of the fragment whose pktComponentMemory.LastFragment is TRUE and is used to retrieve all buffered fragments from the member ‘BufferedFragments’. The member ‘BufferedFragments’ is indexed by pktComponentMemory.FragmentNum. 1. 2. 3. 4. (SEND) var locmem_NextPayloadIdentifer : int (SEND) var locmem_NumberOfFrag : int (SEND) var locmem_LastSentFragment : int (RECEIVE) var locmem_BufferedFragments : indexed collection of RcvdFragments Note: This collection holds fragments received from all sources and is indexed by pktComponentMemory.PayloadIdentifier 5. (RECEIVE) var locmem_MemUsed : int 6. (RECEIVE) const locmem_MaxFragmentLength : int 7. (RECEIVE) const locmem_GCTimeout : int 8. (RECEIVE) const locmem_MaxBufferLength : int Global Memory: None Protocol Memory: None Packet Memory: type FragHeader = record of PayloadIdentifier : int /* corresponds to UID field of IP */ FragmentNum : int /* analogous to ‘Fragmentation Offset’ field of IP) */ LastFragment : boolean /* corresponds to one of the bits in the ‘Flags’ field of IP */ FragmentDataLen : int FragmentData : byte[] end record /*length of fragmented data , 0 for unfragmented packets */ /*Data of this fragment, empty for unfragmented packets */ Transmit State Machine: 1 2 3 Wait Send Fragments 4 Transition No. 1 Event: Guard: Comment: Action: Packet Arrival pktPayload.length <= locmem_MaxFragmentLength Payload need not be fragmented. Pass the payload along using PktSend(). fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { pktComponentMemory.PayloadIdentifier <- locmem_NextPayloadIdentifier; pktComponentMemory.FragmentNum <- 1; pktComponentMemory.LastFragment <- TRUE; PktSend( pktComponentMemory ); Inc(locmem_NextPayloadIdentifier); } Transition No. 2 Event: Guard: Comment: Action: Packet Arrival pktPayload.length > locmem_MaxFragmentLength Payload should be fragmented. Calculate the number of fragments fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { locmem_NumberOfFrag = pktPayload.length DIV locmem_MaxFragmentLength; if ( pktPayload.length MOD locmem_MaxFragmentLength != 0 ) Inc ( locmem_NumberOfFrag ); fi locmem_LastSentFragment <- 0; } Transition No. 3 Event: Guard: Comment: Action: None locmem_LastSentFragment < locmem_NumberOfFrag Send the ith fragment fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { pktComponentMemory.PayloadIdentifier <-locmem_NextPayloadIdentifier; pktComponentMemory.FragmentNum <- locmem_LastSentFragment + 1; if ( locmem_LastSentFragment + 1 == locmem_NumberOfFrag ) pktComponentMemory.LastFragment <- TRUE else pktComponentMemory.LastFragment <- FALSE fi /* Copy bytes numbered from (locmem_LastSentFragment * locmem_MaxFragmentLength) to ((locmem_LastSentFragment + 1) * locmem_MaxFragmentLength – 1) of pktPayload into pktComponentMemory.FragmentData */ /* First byte of pktPayload is numbered zero */ /* Set pktComponentMemory.FragmentDataLen to the size of the byte array pktComponentMemory.FragmentData */ NewPktSend(pktComponentMemory); Inc(locmem_LastSentFragment); } Transition No. 4 Event: Guard: Comment: Action: None locmem_LastSentFragment >= locmem_NumberOfFrag All fragments sent. Return to wait state fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { Inc(locmem_NextPayloadIdentifier); } Receive State Machine – Version 1: (Fewer states and complex guard conditions) 1, 2, 3, 4, 6 8, 9 WAIT ??? 5, 7 Transition No. 1 Event: Guard: Comment: Action: Timeout None Garbage collect fragments of payload whose timer expired fn (timerID : timerHandle) { /* Free fragments in locmem_BufferedFragments whose ‘PayloadIdentifier’ is ‘timerID’ */ } Transition No. 2 Event: Guard: Comment: Action: Packet Arrival locmem_MemUsed >= locmem_MaxBufferLength No more memory to buffer fragments. Drop the packet fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { DropPacket(); } Transition No. 3 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND pktComponentMemory.LastFragment == TRUE AND pktComponentMemory.FragmentNum == 1 Result of PayloadIdentifierNotSeenYet(pktComponentMemory) does not matter. Unfragmented payload, deliver to upper component. Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { PktDeliver(pktPayload); } Transition No. 4 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND pktComponentMemory.LastFragment == TRUE AND pktComponentMemory.FragmentNum != 1 AND PayloadIdentifierNotSeenYet(pktComponentMemory) == TRUE Length of fragmented payload now known and this is the first received fragment with this ‘PayloadIdentifier’ Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { AddToCollection(pktComponentMemory); locmem_BufferedFragments[pktComponentMemory.PayloadIdentifier]. MaxFragmentNum <- pktComponentMemory.FragmentNum; SetTimeout(pktComponentMemory.PayloadIdentifier, locmem_GCTimeout) } Transition No. 5 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND pktComponentMemory.LastFragment == TRUE AND pktComponentMemory.FragmentNum != 1 AND PayloadIdentifierNotSeenYet(pktComponentMemory) == FALSE Length of fragmented payload now known and this is not the first received fragment with this ‘PayloadIdenitfier’ Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { AddToCollection(pktComponentMemory); locmem_BufferedFragments[pktComponentMemory.PayloadIdentifier]. MaxFragmentNum <- pktComponentMemory.FragmentNum; } Transition No. 6 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND pktComponentMemory.LastFragment == FALSE AND PayloadIdentifierNotSeenYet(pktComponentMemory) == TRUE Value of pktComponentMemory.FragmentNum does not matter. This is the first received fragment with this ‘PayloadIdentifier’ Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { AddToCollection(pktComponentMemory); SetTimeout(pktComponentMemory.PayloadIdentifier, locmem_GCTimeout) } Transition No. 7 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND pktComponentMemory.LastFragment == FALSE AND PayloadIdentifierNotSeenYet(pktComponentMemory) == FALSE Value of pktComponentMemory.FragmentNum does not matter. This is not the first received fragment with this ‘PayloadIdentifier’ Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { AddToCollection(pktComponentMemory); } Transition No. 8 Event: Guard: Comment: Action: None IsPayloadComplete(pktComponentMemory) == TRUE Reassemble and deliver the payload to upper component. fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { var ReassembledPayload : byte array; /* Concatenate all elements from the collection locmem_BufferedFragments[pktComponentMemory.PayloadIdentifier].BufferedFragme nts into ReassembledPayload */ NewPktDeliver(ReassembledPayload); CancelTimeout(pktComponentMemory.PayloadIdentifier); } Transition No. 9 Event: Guard: Comment: Action: None IsPayloadComplete(pktComponentMemory) == FALSE More fragments are needed to reassemble this payload fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { } Helper functions: function PayloadIdentifierNotSeenYet(var header : FragHeader) : boolean /* Return FALSE if locmem_BufferedFragments[header.PayloadIdentifier].BufferedFragments has atleast one of its byte arrays filled, else return TRUE. */ end PayloadIdentifierNotSeenYet Note: PayloadIdentifierNotSeenYet() == TRUE does not imply that a fragment carrying this ‘PayloadIdentifier’ was NEVER received by this state machine. If all fragments of a payload were duplicated at an intermediate router and the first copy of all fragments reached this state machine en masse and some time later, the second copy of all fragments also arrived, then two copies of the reassembled payload will be delivered to the upper component. If the two copies of the fragments reached this state machine intermingled, then only one copy of the reassembled payload will be delivered. This is because the receive state machine does not remember the values for ‘PayloadIdentifier’ of fragments that have been received so far. procedure AddToCollection(var header : FragHeader) /* Insert header.FragmentData into locmem_BufferedFragments[header.PayloadIdentifier].BufferedFragments[heade r.FragmentNum] */ end AddToCollection function IsPayloadComplete(var header : FragHeader) /* Return TRUE if the collection locmem_BufferedFragments[header.PayloadIdentifier].BufferedFragments has all its byte arrays filled from 1 to locmem_BufferedFragments[header.PayloadIdentifier].MaxFragmentNum, else return FALSE */ end IsPayloadComplete Receive State Machine – Version 2: (More states and simpler guard conditions) 5 1, 2, 3 4 Collect Wait 6 7, 8 ??? Transition No. 1 Event: Guard: Comment: Action: Timeout None Garbage collect fragments of payload whose timer expired fn (timerID : timerHandle) { /* Free fragments in locmem_BufferedFragments whose ‘PayloadIdentifier’ is ‘timerID’ */ } Transition No. 2 Event: Guard: Comment: Action: Packet Arrival locmem_MemUsed >= locmem_MaxBufferLength No more memory to buffer fragments. Drop the packet fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { DropPacket(); } Transition No. 3 Event: Guard: Comment: Action: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND (pktComponentMemory.FragmentNum == 1 AND pktComponentMemory.LastFragment == TRUE) Unfragmented payload, deliver to upper component fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { PktDeliver(pktPayload); } Transition No. 4 Event: Guard: Comment: Packet Arrival locmem_MemUsed < locmem_MaxBufferLength AND NOT (pktComponentMemory.FragmentNum == 1 AND pktComponentMemory.LastFragment == TRUE) Add this fragment to the local buffer Action: fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { AddToCollection(pktComponentMemory); } Transition No. 5 Event: Guard: Comment: timer Action: None PayloadIdentifierNotSeenYet(pktComponentMemory) == TRUE This is the first received fragment with this ‘PayloadIdentifier’, start a fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { SetTimeout(pktComponentMemory.PayloadIdentifier, locmem_GCTimeout); } Transition No. 6 Event: Guard: Comment: Action: None PayloadIdentifierNotSeenYet(pktComponentMemory) == FALSE This is not the first received fragment with this ‘PayloadIdentifier’ fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { } Transition No. 7 Event: Guard: Comment: Action: None IsPayloadComplete(pktComponentMemory) == TRUE All fragments of this payload have been received. Deliver to upper comp. fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { var ReassembledPayload : byte array; /* Concatenate all elements from the collection locmem_BufferedFragments[pktComponentMemory.PayloadIdentifier].BufferedFragme nts into ReassembledPayload */ NewPktDeliver(ReassembledPayload); CancelTimeout(pktComponentMemory.PayloadIdentifier); } Transition No. 8 Event: Guard: Comment: Action: None IsPayloadComplete(pktComponentMemory) == FALSE More fragments are needed to reassemble this payload. fn (pktComponentMemory: FragHeader, pktPayload : byte[], pktLowerMemory : byte[]) { } Initialization Function: procedure init ( PayloadIdentifierKey: int, MaxFragmentLength: int, MaxBufferLength: int, Timeout: int ) // Initial value of locmem_NextPayloadIdentifier is derived from PayloadIdentifierKey; locmem_MaxFragmentLength <- MaxFragmentLength; locmem_MaxBufferLength <- MaxBufferLength; locmem_GCTimeout <- Timeout; end init; Pseudo Code: procedure transmit( ) var NumberOfFragments : int; var CurrentSentFragmentNum : int; var FragmentData : byte array; NumberOfFragments <- // No. of fragments depending on UpperPayload.length and locmem_MaxFragmentLength CurrentSentFragmentNum <- 1; while (CurrentSentFragmentNum <= NumberOfFragments) do TxHeader.PayloadIdentifier <- locmem_NextPayloadIdentifier; TxHeader.LastFragment <( CurrentSentFragmentNum == NumberOfFragments ) TxHeader.FragmentNum <- CurrentSentFragmentNum; // FragmentData holds currently sent fragment PktSend(TxHeader, FragmentData, NULL); Inc( CurrentSentFragmentNum ); done Inc( locmem_NextPayloadIdentifier ); end transmit procedure receive() var ReassembledPayload : byte array; if ( Memory is still available to buffer the incoming fragment ) if ( This is the first received fragment with this ‘PayloadIdentifier’ ) SetTimeout( RcvdHeader.PayloadIdentifier, locmem_GCTimeout) fi // Insert UpperPayload into locmem_BufferedFragments[RcvdHeader.PayloadIdentifier]. BufferedFragments[RcvdHeader.FragmentNum] if ( RcvdHeader.LastFragment == TRUE ) locmem_BufferedFragments[RcvdHeader.PayloadIdentifier]. MaxFragmentNum <- RcvdHeader.FragmentNum fi if ( All fragments of this ‘PayloadIdentifier’ are received ) // Reassemble all fragments into ReassembledPayload NewPktDeliver(ReassembledPayload); CancelTimeout( RcvdHeader.PayloadIdentifier ); // Free memory used by fragments with this ‘PayloadIdentifier’ fi else // Drop incoming fragment fi end receive procedure timeout( var TimerID: int ) // TimerID passed to this procedure is the ‘PayloadIdentifier’, whose fragments are to be garbage collected. // Free all memory used by all buffered fragments with this ‘PayloadIdentifier’ end timeout Implications of hop-by-hop re-assembly: The individual fragments do not make any sense even to the component that is immediately “above” the FR component (whose data was fragmented). So when a fragment is received by the FR component, it does not help to deliver the individual fragment to the “upper” layer. (without reassembly) If only end-to-end reassembly is supported, then the above restriction implies that a component like ‘Routing’ (that turns packets around) cannot be placed above the FR component in a stack in a network node. If the routing component is placed below the FR component, then the FR component will never be reached by most packets. So hop-by-hop reassembly is supported.