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 }