You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

throttler.go 4.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package connection_limits
  4. import (
  5. "fmt"
  6. "net"
  7. "sync"
  8. "time"
  9. )
  10. // ThrottlerConfig controls the automated connection throttling.
  11. type ThrottlerConfig struct {
  12. Enabled bool
  13. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  14. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  15. ConnectionsPerCidr int `yaml:"max-connections"`
  16. DurationString string `yaml:"duration"`
  17. Duration time.Duration `yaml:"duration-time"`
  18. BanDurationString string `yaml:"ban-duration"`
  19. BanDuration time.Duration
  20. BanMessage string `yaml:"ban-message"`
  21. Exempted []string
  22. }
  23. // ThrottleDetails holds the connection-throttling details for a subnet/IP.
  24. type ThrottleDetails struct {
  25. Start time.Time
  26. ClientCount int
  27. }
  28. // Throttler manages automated client connection throttling.
  29. type Throttler struct {
  30. sync.RWMutex
  31. enabled bool
  32. ipv4Mask net.IPMask
  33. ipv6Mask net.IPMask
  34. subnetLimit int
  35. duration time.Duration
  36. population map[string]ThrottleDetails
  37. // used by the server to ban clients that go over this limit
  38. banDuration time.Duration
  39. banMessage string
  40. // exemptedIPs holds IPs that are exempt from limits
  41. exemptedIPs map[string]bool
  42. // exemptedNets holds networks that are exempt from limits
  43. exemptedNets []net.IPNet
  44. }
  45. // maskAddr masks the given IPv4/6 address with our cidr limit masks.
  46. func (ct *Throttler) maskAddr(addr net.IP) net.IP {
  47. if addr.To4() == nil {
  48. // IPv6 addr
  49. addr = addr.Mask(ct.ipv6Mask)
  50. } else {
  51. // IPv4 addr
  52. addr = addr.Mask(ct.ipv4Mask)
  53. }
  54. return addr
  55. }
  56. // ResetFor removes any existing count for the given address.
  57. func (ct *Throttler) ResetFor(addr net.IP) {
  58. ct.Lock()
  59. defer ct.Unlock()
  60. if !ct.enabled {
  61. return
  62. }
  63. // remove
  64. ct.maskAddr(addr)
  65. addrString := addr.String()
  66. delete(ct.population, addrString)
  67. }
  68. // AddClient introduces a new client connection if possible. If we can't, throws an error instead.
  69. func (ct *Throttler) AddClient(addr net.IP) error {
  70. ct.Lock()
  71. defer ct.Unlock()
  72. if !ct.enabled {
  73. return nil
  74. }
  75. // check exempted lists
  76. if ct.exemptedIPs[addr.String()] {
  77. return nil
  78. }
  79. for _, ex := range ct.exemptedNets {
  80. if ex.Contains(addr) {
  81. return nil
  82. }
  83. }
  84. // check throttle
  85. ct.maskAddr(addr)
  86. addrString := addr.String()
  87. details, exists := ct.population[addrString]
  88. if !exists || details.Start.Add(ct.duration).Before(time.Now()) {
  89. details = ThrottleDetails{
  90. Start: time.Now(),
  91. }
  92. }
  93. if details.ClientCount+1 > ct.subnetLimit {
  94. return errTooManyClients
  95. }
  96. details.ClientCount++
  97. ct.population[addrString] = details
  98. return nil
  99. }
  100. func (ct *Throttler) BanDuration() time.Duration {
  101. ct.RLock()
  102. defer ct.RUnlock()
  103. return ct.banDuration
  104. }
  105. func (ct *Throttler) BanMessage() string {
  106. ct.RLock()
  107. defer ct.RUnlock()
  108. return ct.banMessage
  109. }
  110. // NewThrottler returns a new client connection throttler.
  111. // The throttler is functional, but disabled; it can be enabled via `ApplyConfig`.
  112. func NewThrottler() *Throttler {
  113. var ct Throttler
  114. // initialize empty population; all other state is configurable
  115. ct.population = make(map[string]ThrottleDetails)
  116. return &ct
  117. }
  118. // ApplyConfig atomically applies a config update to a throttler
  119. func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
  120. // assemble exempted nets
  121. exemptedIPs := make(map[string]bool)
  122. var exemptedNets []net.IPNet
  123. for _, cidr := range config.Exempted {
  124. ipaddr := net.ParseIP(cidr)
  125. _, netaddr, err := net.ParseCIDR(cidr)
  126. if ipaddr == nil && err != nil {
  127. return fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
  128. }
  129. if ipaddr != nil {
  130. exemptedIPs[ipaddr.String()] = true
  131. } else {
  132. exemptedNets = append(exemptedNets, *netaddr)
  133. }
  134. }
  135. ct.Lock()
  136. defer ct.Unlock()
  137. ct.enabled = config.Enabled
  138. ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
  139. ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
  140. ct.subnetLimit = config.ConnectionsPerCidr
  141. ct.duration = config.Duration
  142. ct.banDuration = config.BanDuration
  143. ct.banMessage = config.BanMessage
  144. ct.exemptedIPs = exemptedIPs
  145. ct.exemptedNets = exemptedNets
  146. return nil
  147. }