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  }