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.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. "github.com/oragono/oragono/irc/utils"
  10. )
  11. // ThrottlerConfig controls the automated connection throttling.
  12. type ThrottlerConfig struct {
  13. Enabled bool
  14. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  15. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  16. ConnectionsPerCidr int `yaml:"max-connections"`
  17. DurationString string `yaml:"duration"`
  18. Duration time.Duration `yaml:"duration-time"`
  19. BanDurationString string `yaml:"ban-duration"`
  20. BanDuration time.Duration
  21. BanMessage string `yaml:"ban-message"`
  22. Exempted []string
  23. }
  24. // ThrottleDetails holds the connection-throttling details for a subnet/IP.
  25. type ThrottleDetails struct {
  26. Start time.Time
  27. Count int
  28. }
  29. // GenericThrottle allows enforcing limits of the form
  30. // "at most X events per time window of duration Y"
  31. type GenericThrottle struct {
  32. ThrottleDetails // variable state: what events have been seen
  33. // these are constant after creation:
  34. Duration time.Duration // window length to consider
  35. Limit int // number of events allowed per window
  36. }
  37. // Touch checks whether an additional event is allowed:
  38. // it either denies it (by returning false) or allows it (by returning true)
  39. // and records it
  40. func (g *GenericThrottle) Touch() (throttled bool, remainingTime time.Duration) {
  41. return g.touch(time.Now())
  42. }
  43. func (g *GenericThrottle) touch(now time.Time) (throttled bool, remainingTime time.Duration) {
  44. if g.Limit == 0 {
  45. return // limit of 0 disables throttling
  46. }
  47. elapsed := now.Sub(g.Start)
  48. if elapsed > g.Duration {
  49. // reset window, record the operation
  50. g.Start = now
  51. g.Count = 1
  52. return false, 0
  53. } else if g.Count >= g.Limit {
  54. // we are throttled
  55. return true, g.Start.Add(g.Duration).Sub(now)
  56. } else {
  57. // we are not throttled, record the operation
  58. g.Count += 1
  59. return false, 0
  60. }
  61. }
  62. // Throttler manages automated client connection throttling.
  63. type Throttler struct {
  64. sync.RWMutex
  65. enabled bool
  66. ipv4Mask net.IPMask
  67. ipv6Mask net.IPMask
  68. subnetLimit int
  69. duration time.Duration
  70. population map[string]ThrottleDetails
  71. // used by the server to ban clients that go over this limit
  72. banDuration time.Duration
  73. banMessage string
  74. // exemptedNets holds networks that are exempt from limits
  75. exemptedNets []net.IPNet
  76. }
  77. // ResetFor removes any existing count for the given address.
  78. func (ct *Throttler) ResetFor(addr net.IP) {
  79. ct.Lock()
  80. defer ct.Unlock()
  81. if !ct.enabled {
  82. return
  83. }
  84. // remove
  85. addrString := addrToKey(addr, ct.ipv4Mask, ct.ipv6Mask)
  86. delete(ct.population, addrString)
  87. }
  88. // AddClient introduces a new client connection if possible. If we can't, throws an error instead.
  89. func (ct *Throttler) AddClient(addr net.IP) error {
  90. ct.Lock()
  91. defer ct.Unlock()
  92. if !ct.enabled {
  93. return nil
  94. }
  95. // check exempted lists
  96. if utils.IPInNets(addr, ct.exemptedNets) {
  97. return nil
  98. }
  99. // check throttle
  100. addrString := addrToKey(addr, ct.ipv4Mask, ct.ipv6Mask)
  101. details := ct.population[addrString] // retrieve mutable throttle state from the map
  102. // add in constant state to process the limiting operation
  103. g := GenericThrottle{
  104. ThrottleDetails: details,
  105. Duration: ct.duration,
  106. Limit: ct.subnetLimit,
  107. }
  108. throttled, _ := g.Touch() // actually check the limit
  109. ct.population[addrString] = g.ThrottleDetails // store modified mutable state
  110. if throttled {
  111. return errTooManyClients
  112. } else {
  113. return nil
  114. }
  115. }
  116. func (ct *Throttler) BanDuration() time.Duration {
  117. ct.RLock()
  118. defer ct.RUnlock()
  119. return ct.banDuration
  120. }
  121. func (ct *Throttler) BanMessage() string {
  122. ct.RLock()
  123. defer ct.RUnlock()
  124. return ct.banMessage
  125. }
  126. // NewThrottler returns a new client connection throttler.
  127. // The throttler is functional, but disabled; it can be enabled via `ApplyConfig`.
  128. func NewThrottler() *Throttler {
  129. var ct Throttler
  130. // initialize empty population; all other state is configurable
  131. ct.population = make(map[string]ThrottleDetails)
  132. return &ct
  133. }
  134. // ApplyConfig atomically applies a config update to a throttler
  135. func (ct *Throttler) ApplyConfig(config ThrottlerConfig) error {
  136. // assemble exempted nets
  137. exemptedNets, err := utils.ParseNetList(config.Exempted)
  138. if err != nil {
  139. return fmt.Errorf("Could not parse throttle exemption list: %v", err.Error())
  140. }
  141. ct.Lock()
  142. defer ct.Unlock()
  143. ct.enabled = config.Enabled
  144. ct.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
  145. ct.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
  146. ct.subnetLimit = config.ConnectionsPerCidr
  147. ct.duration = config.Duration
  148. ct.banDuration = config.BanDuration
  149. ct.banMessage = config.BanMessage
  150. ct.exemptedNets = exemptedNets
  151. return nil
  152. }