github.com/status-im/status-go@v1.1.0/waku/common/rate_limiter.go (about) 1 // Copyright 2019 The Waku Library Authors. 2 // 3 // The Waku library is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Lesser General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // (at your option) any later version. 7 // 8 // The Waku library is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty off 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Lesser General Public License for more details. 12 // 13 // You should have received a copy of the GNU Lesser General Public License 14 // along with the Waku library. If not, see <http://www.gnu.org/licenses/>. 15 // 16 // This software uses the go-ethereum library, which is licensed 17 // under the GNU Lesser General Public Library, version 3 or any later. 18 19 package common 20 21 import ( 22 "bytes" 23 "errors" 24 "fmt" 25 "net" 26 "time" 27 28 "github.com/tsenart/tb" 29 30 "github.com/ethereum/go-ethereum/p2p" 31 "github.com/ethereum/go-ethereum/p2p/enode" 32 ) 33 34 var errRateLimitExceeded = errors.New("rate limit has been exceeded") 35 36 type runLoop func(rw p2p.MsgReadWriter) error 37 38 // RateLimiterPeer interface represents a Peer that is capable of being rate limited 39 type RateLimiterPeer interface { 40 ID() []byte 41 IP() net.IP 42 } 43 44 // RateLimiterHandler interface represents handler functionality for a Rate Limiter in the cases of 45 // exceeding a peer limit and exceeding an IP limit 46 type RateLimiterHandler interface { 47 ExceedPeerLimit() error 48 ExceedIPLimit() error 49 } 50 51 // MetricsRateLimiterHandler implements RateLimiterHandler, represents a handler for reporting rate limit Exceed data 52 // to the metrics collection service (currently prometheus) 53 type MetricsRateLimiterHandler struct{} 54 55 func (MetricsRateLimiterHandler) ExceedPeerLimit() error { 56 RateLimitsExceeded.WithLabelValues("peer_id").Inc() 57 return nil 58 } 59 func (MetricsRateLimiterHandler) ExceedIPLimit() error { 60 RateLimitsExceeded.WithLabelValues("ip").Inc() 61 return nil 62 } 63 64 // RateLimits contains information about rate limit settings. 65 // It's agnostic on what it's being rate limited on (bytes or number of packets currently) 66 // It's exchanged with the status-update packet code 67 type RateLimits struct { 68 IPLimits uint64 // amount per second from a single IP (default 0, no limits) 69 PeerIDLimits uint64 // amount per second from a single peer ID (default 0, no limits) 70 TopicLimits uint64 // amount per second from a single topic (default 0, no limits) 71 } 72 73 func (r RateLimits) IsZero() bool { 74 return r == (RateLimits{}) 75 } 76 77 // DropPeerRateLimiterHandler implements RateLimiterHandler, represents a handler that introduces Tolerance to the 78 // number of Peer connections before Limit Exceeded errors are returned. 79 type DropPeerRateLimiterHandler struct { 80 // Tolerance is a number by which a limit must be exceeded before a peer is dropped. 81 Tolerance int64 82 83 peerLimitExceeds int64 84 ipLimitExceeds int64 85 } 86 87 func (h *DropPeerRateLimiterHandler) ExceedPeerLimit() error { 88 h.peerLimitExceeds++ 89 if h.Tolerance > 0 && h.peerLimitExceeds >= h.Tolerance { 90 return errRateLimitExceeded 91 } 92 return nil 93 } 94 95 func (h *DropPeerRateLimiterHandler) ExceedIPLimit() error { 96 h.ipLimitExceeds++ 97 if h.Tolerance > 0 && h.ipLimitExceeds >= h.Tolerance { 98 return errRateLimitExceeded 99 } 100 return nil 101 } 102 103 // PeerRateLimiterConfig represents configurations for initialising a PeerRateLimiter 104 type PeerRateLimiterConfig struct { 105 PacketLimitPerSecIP int64 106 PacketLimitPerSecPeerID int64 107 BytesLimitPerSecIP int64 108 BytesLimitPerSecPeerID int64 109 WhitelistedIPs []string 110 WhitelistedPeerIDs []enode.ID 111 } 112 113 var defaultPeerRateLimiterConfig = PeerRateLimiterConfig{ 114 PacketLimitPerSecIP: 10, 115 PacketLimitPerSecPeerID: 5, 116 BytesLimitPerSecIP: 1048576, // 1MB 117 BytesLimitPerSecPeerID: 1048576, // 1MB 118 WhitelistedIPs: nil, 119 WhitelistedPeerIDs: nil, 120 } 121 122 // PeerRateLimiter represents a rate limiter that limits communication between Peers 123 type PeerRateLimiter struct { 124 packetThrottler *tb.Throttler 125 bytesThrottler *tb.Throttler 126 127 PacketLimitPerSecIP int64 128 PacketLimitPerSecPeerID int64 129 130 BytesLimitPerSecIP int64 131 BytesLimitPerSecPeerID int64 132 133 whitelistedPeerIDs []enode.ID 134 whitelistedIPs []string 135 136 handlers []RateLimiterHandler 137 } 138 139 func NewPeerRateLimiter(cfg *PeerRateLimiterConfig, handlers ...RateLimiterHandler) *PeerRateLimiter { 140 if cfg == nil { 141 cfgCopy := defaultPeerRateLimiterConfig 142 cfg = &cfgCopy 143 } 144 145 return &PeerRateLimiter{ 146 packetThrottler: tb.NewThrottler(time.Millisecond * 100), 147 bytesThrottler: tb.NewThrottler(time.Millisecond * 100), 148 PacketLimitPerSecIP: cfg.PacketLimitPerSecIP, 149 PacketLimitPerSecPeerID: cfg.PacketLimitPerSecPeerID, 150 BytesLimitPerSecIP: cfg.BytesLimitPerSecIP, 151 BytesLimitPerSecPeerID: cfg.BytesLimitPerSecPeerID, 152 whitelistedPeerIDs: cfg.WhitelistedPeerIDs, 153 whitelistedIPs: cfg.WhitelistedIPs, 154 handlers: handlers, 155 } 156 } 157 158 func (r *PeerRateLimiter) Decorate(p RateLimiterPeer, rw p2p.MsgReadWriter, runLoop runLoop) error { 159 errC := make(chan error, 1) 160 161 in, out := p2p.MsgPipe() 162 defer func() { 163 if err := in.Close(); err != nil { 164 // Don't block as otherwise we might leak go routines 165 select { 166 case errC <- err: 167 default: 168 } 169 } 170 }() 171 defer func() { 172 if err := out.Close(); err != nil { 173 errC <- err 174 } 175 }() 176 177 // Read from the original reader and write to the message pipe. 178 go func() { 179 for { 180 packet, err := rw.ReadMsg() 181 if err != nil { 182 // Don't block as otherwise we might leak go routines 183 select { 184 case errC <- fmt.Errorf("failed to read packet: %v", err): 185 return 186 default: 187 return 188 } 189 } 190 191 RateLimitsProcessed.Inc() 192 193 var ip string 194 if p != nil { 195 // this relies on <nil> being the string representation of nil 196 // as IP() might return a nil value 197 ip = p.IP().String() 198 } 199 if halted := r.throttleIP(ip, packet.Size); halted { 200 for _, h := range r.handlers { 201 if err := h.ExceedIPLimit(); err != nil { 202 // Don't block as otherwise we might leak go routines 203 select { 204 205 case errC <- fmt.Errorf("exceed rate limit by IP: %v", err): 206 return 207 default: 208 return 209 } 210 } 211 } 212 } 213 214 var peerID []byte 215 if p != nil { 216 peerID = p.ID() 217 } 218 if halted := r.throttlePeer(peerID, packet.Size); halted { 219 for _, h := range r.handlers { 220 if err := h.ExceedPeerLimit(); err != nil { 221 // Don't block as otherwise we might leak go routines 222 select { 223 case errC <- fmt.Errorf("exceeded rate limit by peer: %v", err): 224 return 225 default: 226 return 227 } 228 } 229 } 230 } 231 232 if err := in.WriteMsg(packet); err != nil { 233 // Don't block as otherwise we might leak go routines 234 select { 235 case errC <- fmt.Errorf("failed to write packet to pipe: %v", err): 236 return 237 default: 238 return 239 } 240 } 241 } 242 }() 243 244 // Read from the message pipe and write to the original writer. 245 go func() { 246 for { 247 packet, err := in.ReadMsg() 248 if err != nil { 249 // Don't block as otherwise we might leak go routines 250 select { 251 case errC <- fmt.Errorf("failed to read packet from pipe: %v", err): 252 return 253 default: 254 return 255 } 256 } 257 if err := rw.WriteMsg(packet); err != nil { 258 // Don't block as otherwise we might leak go routines 259 select { 260 case errC <- fmt.Errorf("failed to write packet: %v", err): 261 return 262 default: 263 return 264 } 265 } 266 } 267 }() 268 269 go func() { 270 // Don't block as otherwise we might leak go routines 271 select { 272 case errC <- runLoop(out): 273 return 274 default: 275 return 276 } 277 }() 278 279 return <-errC 280 } 281 282 // throttleIP throttles packets incoming from a given IP. 283 func (r *PeerRateLimiter) throttleIP(ip string, size uint32) bool { 284 if stringSliceContains(r.whitelistedIPs, ip) { 285 return false 286 } 287 288 var packetLimiterResponse bool 289 var bytesLimiterResponse bool 290 291 if r.PacketLimitPerSecIP != 0 { 292 packetLimiterResponse = r.packetThrottler.Halt(ip, 1, r.PacketLimitPerSecIP) 293 } 294 if r.BytesLimitPerSecIP != 0 { 295 bytesLimiterResponse = r.bytesThrottler.Halt(ip, int64(size), r.BytesLimitPerSecIP) 296 } 297 298 return packetLimiterResponse || bytesLimiterResponse 299 } 300 301 // throttlePeer throttles packets incoming from a peer. 302 func (r *PeerRateLimiter) throttlePeer(peerID []byte, size uint32) bool { 303 var id enode.ID 304 copy(id[:], peerID) 305 if enodeIDSliceContains(r.whitelistedPeerIDs, id) { 306 return false 307 } 308 309 var packetLimiterResponse bool 310 var bytesLimiterResponse bool 311 312 if r.PacketLimitPerSecPeerID != 0 { 313 packetLimiterResponse = r.packetThrottler.Halt(id.String(), 1, r.PacketLimitPerSecPeerID) 314 } 315 316 if r.BytesLimitPerSecPeerID != 0 { 317 bytesLimiterResponse = r.bytesThrottler.Halt(id.String(), int64(size), r.BytesLimitPerSecPeerID) 318 } 319 320 return packetLimiterResponse || bytesLimiterResponse 321 } 322 323 func stringSliceContains(s []string, searched string) bool { 324 for _, item := range s { 325 if item == searched { 326 return true 327 } 328 } 329 return false 330 } 331 332 func enodeIDSliceContains(s []enode.ID, searched enode.ID) bool { 333 for _, item := range s { 334 if bytes.Equal(item.Bytes(), searched.Bytes()) { 335 return true 336 } 337 } 338 return false 339 }