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.

limiter.go 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package connection_limits
  4. import (
  5. "errors"
  6. "fmt"
  7. "net"
  8. "sync"
  9. )
  10. // LimiterConfig controls the automated connection limits.
  11. type LimiterConfig struct {
  12. Enabled bool
  13. CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
  14. CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
  15. IPsPerCidr int `yaml:"ips-per-subnet"`
  16. Exempted []string
  17. }
  18. var (
  19. errTooManyClients = errors.New("Too many clients in subnet")
  20. )
  21. // Limiter manages the automated client connection limits.
  22. type Limiter struct {
  23. sync.Mutex
  24. enabled bool
  25. ipv4Mask net.IPMask
  26. ipv6Mask net.IPMask
  27. // subnetLimit is the maximum number of clients per subnet
  28. subnetLimit int
  29. // population holds IP -> count of clients connected from there
  30. population map[string]int
  31. // exemptedIPs holds IPs that are exempt from limits
  32. exemptedIPs map[string]bool
  33. // exemptedNets holds networks that are exempt from limits
  34. exemptedNets []net.IPNet
  35. }
  36. // maskAddr masks the given IPv4/6 address with our cidr limit masks.
  37. func (cl *Limiter) maskAddr(addr net.IP) net.IP {
  38. if addr.To4() == nil {
  39. // IPv6 addr
  40. addr = addr.Mask(cl.ipv6Mask)
  41. } else {
  42. // IPv4 addr
  43. addr = addr.Mask(cl.ipv4Mask)
  44. }
  45. return addr
  46. }
  47. // AddClient adds a client to our population if possible. If we can't, throws an error instead.
  48. // 'force' is used to add already-existing clients (i.e. ones that are already on the network).
  49. func (cl *Limiter) AddClient(addr net.IP, force bool) error {
  50. cl.Lock()
  51. defer cl.Unlock()
  52. if !cl.enabled {
  53. return nil
  54. }
  55. // check exempted lists
  56. // we don't track populations for exempted addresses or nets - this is by design
  57. if cl.exemptedIPs[addr.String()] {
  58. return nil
  59. }
  60. for _, ex := range cl.exemptedNets {
  61. if ex.Contains(addr) {
  62. return nil
  63. }
  64. }
  65. // check population
  66. cl.maskAddr(addr)
  67. addrString := addr.String()
  68. if cl.population[addrString]+1 > cl.subnetLimit && !force {
  69. return errTooManyClients
  70. }
  71. cl.population[addrString] = cl.population[addrString] + 1
  72. return nil
  73. }
  74. // RemoveClient removes the given address from our population
  75. func (cl *Limiter) RemoveClient(addr net.IP) {
  76. cl.Lock()
  77. defer cl.Unlock()
  78. if !cl.enabled {
  79. return
  80. }
  81. addrString := addr.String()
  82. cl.population[addrString] = cl.population[addrString] - 1
  83. // safety limiter
  84. if cl.population[addrString] < 0 {
  85. cl.population[addrString] = 0
  86. }
  87. }
  88. // NewLimiter returns a new connection limit handler.
  89. // The handler is functional, but disabled; it can be enabled via `ApplyConfig`.
  90. func NewLimiter() *Limiter {
  91. var cl Limiter
  92. // initialize empty population; all other state is configurable
  93. cl.population = make(map[string]int)
  94. return &cl
  95. }
  96. // ApplyConfig atomically applies a config update to a connection limit handler
  97. func (cl *Limiter) ApplyConfig(config LimiterConfig) error {
  98. // assemble exempted nets
  99. exemptedIPs := make(map[string]bool)
  100. var exemptedNets []net.IPNet
  101. for _, cidr := range config.Exempted {
  102. ipaddr := net.ParseIP(cidr)
  103. _, netaddr, err := net.ParseCIDR(cidr)
  104. if ipaddr == nil && err != nil {
  105. return fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
  106. }
  107. if ipaddr != nil {
  108. exemptedIPs[ipaddr.String()] = true
  109. } else {
  110. exemptedNets = append(exemptedNets, *netaddr)
  111. }
  112. }
  113. cl.Lock()
  114. defer cl.Unlock()
  115. cl.enabled = config.Enabled
  116. cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
  117. cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
  118. // subnetLimit is explicitly NOT capped at a minimum of one.
  119. // this is so that CL config can be used to allow ONLY clients from exempted IPs/nets
  120. cl.subnetLimit = config.IPsPerCidr
  121. cl.exemptedIPs = exemptedIPs
  122. cl.exemptedNets = exemptedNets
  123. return nil
  124. }