github.com/lightlus/netstack@v1.2.0/tcpip/network/fragmentation/reassembler.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package fragmentation
    16  
    17  import (
    18  	"container/heap"
    19  	"fmt"
    20  	"math"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/lightlus/netstack/tcpip/buffer"
    25  )
    26  
    27  type hole struct {
    28  	first   uint16
    29  	last    uint16
    30  	deleted bool
    31  }
    32  
    33  type reassembler struct {
    34  	reassemblerEntry
    35  	id           uint32
    36  	size         int
    37  	mu           sync.Mutex
    38  	holes        []hole
    39  	deleted      int
    40  	heap         fragHeap
    41  	done         bool
    42  	creationTime time.Time
    43  }
    44  
    45  func newReassembler(id uint32) *reassembler {
    46  	r := &reassembler{
    47  		id:           id,
    48  		holes:        make([]hole, 0, 16),
    49  		deleted:      0,
    50  		heap:         make(fragHeap, 0, 8),
    51  		creationTime: time.Now(),
    52  	}
    53  	r.holes = append(r.holes, hole{
    54  		first:   0,
    55  		last:    math.MaxUint16,
    56  		deleted: false})
    57  	return r
    58  }
    59  
    60  // updateHoles updates the list of holes for an incoming fragment and
    61  // returns true iff the fragment filled at least part of an existing hole.
    62  func (r *reassembler) updateHoles(first, last uint16, more bool) bool {
    63  	used := false
    64  	for i := range r.holes {
    65  		if r.holes[i].deleted || first > r.holes[i].last || last < r.holes[i].first {
    66  			continue
    67  		}
    68  		used = true
    69  		r.deleted++
    70  		r.holes[i].deleted = true
    71  		if first > r.holes[i].first {
    72  			r.holes = append(r.holes, hole{r.holes[i].first, first - 1, false})
    73  		}
    74  		if last < r.holes[i].last && more {
    75  			r.holes = append(r.holes, hole{last + 1, r.holes[i].last, false})
    76  		}
    77  	}
    78  	return used
    79  }
    80  
    81  func (r *reassembler) process(first, last uint16, more bool, vv buffer.VectorisedView) (buffer.VectorisedView, bool, int, error) {
    82  	r.mu.Lock()
    83  	defer r.mu.Unlock()
    84  	consumed := 0
    85  	if r.done {
    86  		// A concurrent goroutine might have already reassembled
    87  		// the packet and emptied the heap while this goroutine
    88  		// was waiting on the mutex. We don't have to do anything in this case.
    89  		return buffer.VectorisedView{}, false, consumed, nil
    90  	}
    91  	if r.updateHoles(first, last, more) {
    92  		// We store the incoming packet only if it filled some holes.
    93  		heap.Push(&r.heap, fragment{offset: first, vv: vv.Clone(nil)})
    94  		consumed = vv.Size()
    95  		r.size += consumed
    96  	}
    97  	// Check if all the holes have been deleted and we are ready to reassamble.
    98  	if r.deleted < len(r.holes) {
    99  		return buffer.VectorisedView{}, false, consumed, nil
   100  	}
   101  	res, err := r.heap.reassemble()
   102  	if err != nil {
   103  		return buffer.VectorisedView{}, false, consumed, fmt.Errorf("fragment reassembly failed: %v", err)
   104  	}
   105  	return res, true, consumed, nil
   106  }
   107  
   108  func (r *reassembler) tooOld(timeout time.Duration) bool {
   109  	return time.Now().Sub(r.creationTime) > timeout
   110  }
   111  
   112  func (r *reassembler) checkDoneOrMark() bool {
   113  	r.mu.Lock()
   114  	prev := r.done
   115  	r.done = true
   116  	r.mu.Unlock()
   117  	return prev
   118  }