github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/packet_handler_map.go (about) 1 package quic 2 3 import ( 4 "crypto/hmac" 5 "crypto/rand" 6 "crypto/sha256" 7 "hash" 8 "io" 9 "net" 10 "sync" 11 "time" 12 13 "github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol" 14 "github.com/danielpfeifer02/quic-go-prio-packs/internal/utils" 15 ) 16 17 type connCapabilities struct { 18 // This connection has the Don't Fragment (DF) bit set. 19 // This means it makes to run DPLPMTUD. 20 DF bool 21 // GSO (Generic Segmentation Offload) supported 22 GSO bool 23 // ECN (Explicit Congestion Notifications) supported 24 ECN bool 25 } 26 27 // rawConn is a connection that allow reading of a receivedPackeh. 28 type rawConn interface { 29 ReadPacket() (receivedPacket, error) 30 // WritePacket writes a packet on the wire. 31 // gsoSize is the size of a single packet, or 0 to disable GSO. 32 // It is invalid to set gsoSize if capabilities.GSO is not set. 33 WritePacket(b []byte, addr net.Addr, packetInfoOOB []byte, gsoSize uint16, ecn protocol.ECN) (int, error) 34 LocalAddr() net.Addr 35 SetReadDeadline(time.Time) error 36 io.Closer 37 38 capabilities() connCapabilities 39 } 40 41 type closePacket struct { 42 payload []byte 43 addr net.Addr 44 info packetInfo 45 } 46 47 type packetHandlerMap struct { 48 mutex sync.Mutex 49 handlers map[protocol.ConnectionID]packetHandler 50 resetTokens map[protocol.StatelessResetToken] /* stateless reset token */ packetHandler 51 52 closed bool 53 closeChan chan struct{} 54 55 enqueueClosePacket func(closePacket) 56 57 deleteRetiredConnsAfter time.Duration 58 59 statelessResetMutex sync.Mutex 60 statelessResetHasher hash.Hash 61 62 logger utils.Logger 63 } 64 65 var _ packetHandlerManager = &packetHandlerMap{} 66 67 func newPacketHandlerMap(key *StatelessResetKey, enqueueClosePacket func(closePacket), logger utils.Logger) *packetHandlerMap { 68 h := &packetHandlerMap{ 69 closeChan: make(chan struct{}), 70 handlers: make(map[protocol.ConnectionID]packetHandler), 71 resetTokens: make(map[protocol.StatelessResetToken]packetHandler), 72 deleteRetiredConnsAfter: protocol.RetiredConnectionIDDeleteTimeout, 73 enqueueClosePacket: enqueueClosePacket, 74 logger: logger, 75 } 76 if key != nil { 77 h.statelessResetHasher = hmac.New(sha256.New, key[:]) 78 } 79 if h.logger.Debug() { 80 go h.logUsage() 81 } 82 return h 83 } 84 85 func (h *packetHandlerMap) logUsage() { 86 ticker := time.NewTicker(2 * time.Second) 87 var printedZero bool 88 for { 89 select { 90 case <-h.closeChan: 91 return 92 case <-ticker.C: 93 } 94 95 h.mutex.Lock() 96 numHandlers := len(h.handlers) 97 numTokens := len(h.resetTokens) 98 h.mutex.Unlock() 99 // If the number tracked handlers and tokens is zero, only print it a single time. 100 hasZero := numHandlers == 0 && numTokens == 0 101 if !hasZero || (hasZero && !printedZero) { 102 h.logger.Debugf("Tracking %d connection IDs and %d reset tokens.\n", numHandlers, numTokens) 103 printedZero = false 104 if hasZero { 105 printedZero = true 106 } 107 } 108 } 109 } 110 111 func (h *packetHandlerMap) Get(id protocol.ConnectionID) (packetHandler, bool) { 112 h.mutex.Lock() 113 defer h.mutex.Unlock() 114 115 handler, ok := h.handlers[id] 116 return handler, ok 117 } 118 119 func (h *packetHandlerMap) Add(id protocol.ConnectionID, handler packetHandler) bool /* was added */ { 120 h.mutex.Lock() 121 defer h.mutex.Unlock() 122 123 if _, ok := h.handlers[id]; ok { 124 h.logger.Debugf("Not adding connection ID %s, as it already exists.", id) 125 return false 126 } 127 128 h.handlers[id] = handler 129 h.logger.Debugf("Adding connection ID %s.", id) 130 return true 131 } 132 133 func (h *packetHandlerMap) AddWithConnID(clientDestConnID, newConnID protocol.ConnectionID, handler packetHandler) bool { 134 h.mutex.Lock() 135 defer h.mutex.Unlock() 136 137 if _, ok := h.handlers[clientDestConnID]; ok { 138 h.logger.Debugf("Not adding connection ID %s for a new connection, as it already exists.", clientDestConnID) 139 return false 140 } 141 h.handlers[clientDestConnID] = handler 142 h.handlers[newConnID] = handler 143 h.logger.Debugf("Adding connection IDs %s and %s for a new connection.", clientDestConnID, newConnID) 144 return true 145 } 146 147 func (h *packetHandlerMap) Remove(id protocol.ConnectionID) { 148 h.mutex.Lock() 149 delete(h.handlers, id) 150 h.mutex.Unlock() 151 h.logger.Debugf("Removing connection ID %s.", id) 152 } 153 154 func (h *packetHandlerMap) Retire(id protocol.ConnectionID) { 155 h.logger.Debugf("Retiring connection ID %s in %s.", id, h.deleteRetiredConnsAfter) 156 time.AfterFunc(h.deleteRetiredConnsAfter, func() { 157 h.mutex.Lock() 158 delete(h.handlers, id) 159 h.mutex.Unlock() 160 h.logger.Debugf("Removing connection ID %s after it has been retired.", id) 161 }) 162 } 163 164 // ReplaceWithClosed is called when a connection is closed. 165 // Depending on which side closed the connection, we need to: 166 // * remote close: absorb delayed packets 167 // * local close: retransmit the CONNECTION_CLOSE packet, in case it was lost 168 func (h *packetHandlerMap) ReplaceWithClosed(ids []protocol.ConnectionID, connClosePacket []byte) { 169 var handler packetHandler 170 if connClosePacket != nil { 171 handler = newClosedLocalConn( 172 func(addr net.Addr, info packetInfo) { 173 h.enqueueClosePacket(closePacket{payload: connClosePacket, addr: addr, info: info}) 174 }, 175 h.logger, 176 ) 177 } else { 178 handler = newClosedRemoteConn() 179 } 180 181 h.mutex.Lock() 182 for _, id := range ids { 183 h.handlers[id] = handler 184 } 185 h.mutex.Unlock() 186 h.logger.Debugf("Replacing connection for connection IDs %s with a closed connection.", ids) 187 188 time.AfterFunc(h.deleteRetiredConnsAfter, func() { 189 h.mutex.Lock() 190 for _, id := range ids { 191 delete(h.handlers, id) 192 } 193 h.mutex.Unlock() 194 h.logger.Debugf("Removing connection IDs %s for a closed connection after it has been retired.", ids) 195 }) 196 } 197 198 func (h *packetHandlerMap) AddResetToken(token protocol.StatelessResetToken, handler packetHandler) { 199 h.mutex.Lock() 200 h.resetTokens[token] = handler 201 h.mutex.Unlock() 202 } 203 204 func (h *packetHandlerMap) RemoveResetToken(token protocol.StatelessResetToken) { 205 h.mutex.Lock() 206 delete(h.resetTokens, token) 207 h.mutex.Unlock() 208 } 209 210 func (h *packetHandlerMap) GetByResetToken(token protocol.StatelessResetToken) (packetHandler, bool) { 211 h.mutex.Lock() 212 defer h.mutex.Unlock() 213 214 handler, ok := h.resetTokens[token] 215 return handler, ok 216 } 217 218 func (h *packetHandlerMap) Close(e error) { 219 h.mutex.Lock() 220 221 if h.closed { 222 h.mutex.Unlock() 223 return 224 } 225 226 close(h.closeChan) 227 228 var wg sync.WaitGroup 229 for _, handler := range h.handlers { 230 wg.Add(1) 231 go func(handler packetHandler) { 232 handler.destroy(e) 233 wg.Done() 234 }(handler) 235 } 236 h.closed = true 237 h.mutex.Unlock() 238 wg.Wait() 239 } 240 241 func (h *packetHandlerMap) GetStatelessResetToken(connID protocol.ConnectionID) protocol.StatelessResetToken { 242 var token protocol.StatelessResetToken 243 if h.statelessResetHasher == nil { 244 // Return a random stateless reset token. 245 // This token will be sent in the server's transport parameters. 246 // By using a random token, an off-path attacker won't be able to disrupt the connection. 247 rand.Read(token[:]) 248 return token 249 } 250 h.statelessResetMutex.Lock() 251 h.statelessResetHasher.Write(connID.Bytes()) 252 copy(token[:], h.statelessResetHasher.Sum(nil)) 253 h.statelessResetHasher.Reset() 254 h.statelessResetMutex.Unlock() 255 return token 256 }