style="display:inline-block;width:728px;height:90px"
data-ad-client="ca-pub-7505528228218001"
data-ad-slot="1225241371">

Step 4 - reassemble buffers

public static class IpReassemblyBuffer
	    extends JBuffer implements Comparable<IpReassemblyBuffer> {

Our reassembly buffer simply overrides a JBuffer. Therefore we can copy fragment data directly into it. As a matter of fact we are setting up the buffer to be a complete ip-only packet with all the fragments in it.

+------------+--------+--------+
| Ip4 header | frag 1 | frag 2 |
+------------+--------+--------+

We just have to make sure that as fragments arrive we copy their data into the buffer at the right offset. Lucky for us Ip4.offset() field gives us an offset of every fragment we have to process. We know exactly where the fragment needs to be copied to. Its always going to be ip.offset * 8 + 20. We multiply by 8 because offset is in multiples of 8 per Ip4 specification. We add 20 because that is exactly how many bytes our Ip4 header upfront takes up.

Our buffer will also keep track of how many bytes were already copied into it. When the number of bytes copied equals the length of the original ip datagram we know we are done. In order to keep things simplified a bit, we are assuming that all the fragments arriving are not duplicates and they do not overlap. We would have to implement additional logic to also keep track of each individual fragment by the buffer.

We are going to use the first fragment that triggered the creation of this reassembly buffer as a template to write our ip header up front. We do need to reset certain values to make the header applicable to the new reassembled datagram.

All the fragment data will be written into the buffer starting at offset 20, past our ip header.

private final Queue<IpReassemblyBuffer> timeoutQueue = new PriorityQueue<IpReassemblyBuffer>();

private void timeoutBuffers() {
  while (timeoutQueue.isEmpty() == false) {

    if (timeoutQueue.peek().isTimedout()) {
      dispatch(timeoutQueue.poll());
    } else {
      break;
    }
  }
}

The buffer also maintains a timeout timestamp. This is a time when the buffer will been timedout. If that timestamp is still in the future, the buffer is still active, if its in the past, the buffer is timed out and can be cleared from our buffer map and timeout queues. Notice that the buffer implements a JRE Comparable interface. This is because we are using a JRE PriorityQueue class to manage and sort our timeout queue. We prioritise the buffers on the queue according to their timeout timestamp. Oldest up on top, youngest down below. The priority queue sorts that for us automatically. We can check starting at the top of the queue, expiring all the buffers until we reach a buffer that is not expired yet. We stop and break out of that time out loop since there can be no more expired buffer blow due to priority queue sorting.