status_t FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer* buffer, bool lastFragment) { // restart the timer gStackModule->set_timer(&fTimer, FRAGMENT_TIMEOUT); if (start >= end) { // invalid fragment return B_BAD_DATA; } // Search for a position in the list to insert the fragment FragmentList::ReverseIterator iterator = fFragments.GetReverseIterator(); net_buffer* previous = NULL; net_buffer* next = NULL; while ((previous = iterator.Next()) != NULL) { if (previous->fragment.start <= start) { // The new fragment can be inserted after this one break; } next = previous; } // See if we already have the fragment's data if (previous != NULL && previous->fragment.start <= start && previous->fragment.end >= end) { // we do, so we can just drop this fragment gBufferModule->free(buffer); return B_OK; } fIndex = buffer->index; // adopt the buffer's device index TRACE(" previous: %p, next: %p", previous, next); // If we have parts of the data already, truncate as needed if (previous != NULL && previous->fragment.end > start) { TRACE(" remove header %d bytes", previous->fragment.end - start); gBufferModule->remove_header(buffer, previous->fragment.end - start); start = previous->fragment.end; } if (next != NULL && next->fragment.start < end) { TRACE(" remove trailer %d bytes", next->fragment.start - end); gBufferModule->remove_trailer(buffer, next->fragment.start - end); end = next->fragment.start; } // Now try if we can already merge the fragments together // We will always keep the last buffer received, so that we can still // report an error (in which case we're not responsible for freeing it) if (previous != NULL && previous->fragment.end == start) { fFragments.Remove(previous); buffer->fragment.start = previous->fragment.start; buffer->fragment.end = end; status_t status = gBufferModule->merge(buffer, previous, false); TRACE(" merge previous: %s", strerror(status)); if (status != B_OK) { fFragments.Insert(next, previous); return status; } fFragments.Insert(next, buffer); // cut down existing hole fBytesLeft -= end - start; if (lastFragment && !fReceivedLastFragment) { fReceivedLastFragment = true; fBytesLeft -= IPV6_MAXPACKET - end; } TRACE(" hole length: %d", (int)fBytesLeft); return B_OK; } else if (next != NULL && next->fragment.start == end) { net_buffer* afterNext = (net_buffer*)next->link.next; fFragments.Remove(next); buffer->fragment.start = start; buffer->fragment.end = next->fragment.end; status_t status = gBufferModule->merge(buffer, next, true); TRACE(" merge next: %s", strerror(status)); if (status != B_OK) { // Insert "next" at its previous position fFragments.Insert(afterNext, next); return status; } fFragments.Insert(afterNext, buffer); // cut down existing hole fBytesLeft -= end - start; if (lastFragment && !fReceivedLastFragment) { fReceivedLastFragment = true; fBytesLeft -= IPV6_MAXPACKET - end; } TRACE(" hole length: %d", (int)fBytesLeft); return B_OK; } // We couldn't merge the fragments, so we need to add it as is TRACE(" new fragment: %p, bytes %d-%d", buffer, start, end); buffer->fragment.start = start; buffer->fragment.end = end; fFragments.Insert(next, buffer); // update length of the hole, if any fBytesLeft -= end - start; if (lastFragment && !fReceivedLastFragment) { fReceivedLastFragment = true; fBytesLeft -= IPV6_MAXPACKET - end; } TRACE(" hole length: %d", (int)fBytesLeft); return B_OK; }