mirror of
https://github.com/jeremyd/ergo.git
synced 2026-07-20 06:28:09 -07:00
stop autocreating d-lines for throttle violations
This didn't work correctly for IPv6 or custom nets. /UNDLINE IP can temporarily be used to reset the throttle.
This commit is contained in:
@@ -7,7 +7,6 @@ import (
|
||||
"crypto/md5"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -48,8 +47,7 @@ type rawLimiterConfig struct {
|
||||
|
||||
Throttle bool
|
||||
Window time.Duration
|
||||
MaxPerWindow int `yaml:"max-connections-per-window"`
|
||||
BanDuration time.Duration `yaml:"throttle-ban-duration"`
|
||||
MaxPerWindow int `yaml:"max-connections-per-window"`
|
||||
|
||||
CidrLenIPv4 int `yaml:"cidr-len-ipv4"`
|
||||
CidrLenIPv6 int `yaml:"cidr-len-ipv6"`
|
||||
@@ -126,44 +124,49 @@ type Limiter struct {
|
||||
|
||||
// addrToKey canonicalizes `addr` to a string key, and returns
|
||||
// the relevant connection limit and throttle max-per-window values
|
||||
func (cl *Limiter) addrToKey(flat flatip.IP) (key limiterKey, limit int, throttle int) {
|
||||
func (cl *Limiter) addrToKey(addr flatip.IP) (key limiterKey, limit int, throttle int) {
|
||||
for _, custom := range cl.config.customLimits {
|
||||
for _, net := range custom.nets {
|
||||
if net.Contains(flat) {
|
||||
if net.Contains(addr) {
|
||||
return limiterKey{maskedIP: custom.name, prefixLen: 0}, custom.maxConcurrent, custom.maxPerWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var prefixLen int
|
||||
if flat.IsIPv4() {
|
||||
if addr.IsIPv4() {
|
||||
prefixLen = cl.config.CidrLenIPv4
|
||||
flat = flat.Mask(prefixLen, 32)
|
||||
addr = addr.Mask(prefixLen, 32)
|
||||
prefixLen += 96
|
||||
} else {
|
||||
prefixLen = cl.config.CidrLenIPv6
|
||||
flat = flat.Mask(prefixLen, 128)
|
||||
addr = addr.Mask(prefixLen, 128)
|
||||
}
|
||||
|
||||
return limiterKey{maskedIP: flat, prefixLen: uint8(prefixLen)}, cl.config.MaxConcurrent, cl.config.MaxPerWindow
|
||||
return limiterKey{maskedIP: addr, prefixLen: uint8(prefixLen)}, cl.config.MaxConcurrent, cl.config.MaxPerWindow
|
||||
}
|
||||
|
||||
// AddClient adds a client to our population if possible. If we can't, throws an error instead.
|
||||
func (cl *Limiter) AddClient(addr net.IP) error {
|
||||
flat := flatip.FromNetIP(addr)
|
||||
|
||||
func (cl *Limiter) AddClient(addr flatip.IP) error {
|
||||
cl.Lock()
|
||||
defer cl.Unlock()
|
||||
|
||||
// we don't track populations for exempted addresses or nets - this is by design
|
||||
if flatip.IPInNets(flat, cl.config.exemptedNets) {
|
||||
if flatip.IPInNets(addr, cl.config.exemptedNets) {
|
||||
return nil
|
||||
}
|
||||
|
||||
addrString, maxConcurrent, maxPerWindow := cl.addrToKey(flat)
|
||||
addrString, maxConcurrent, maxPerWindow := cl.addrToKey(addr)
|
||||
|
||||
// check limiter
|
||||
var count int
|
||||
if cl.config.Count {
|
||||
count = cl.limiter[addrString] + 1
|
||||
if count > maxConcurrent {
|
||||
return ErrLimitExceeded
|
||||
}
|
||||
}
|
||||
|
||||
// XXX check throttle first; if we checked limit first and then checked throttle,
|
||||
// we'd have to decrement the limit on an unsuccessful throttle check
|
||||
if cl.config.Throttle {
|
||||
details := cl.throttler[addrString] // retrieve mutable throttle state from the map
|
||||
// add in constant state to process the limiting operation
|
||||
@@ -175,16 +178,13 @@ func (cl *Limiter) AddClient(addr net.IP) error {
|
||||
throttled, _ := g.Touch() // actually check the limit
|
||||
cl.throttler[addrString] = g.ThrottleDetails // store modified mutable state
|
||||
if throttled {
|
||||
// back out the limiter add
|
||||
return ErrThrottleExceeded
|
||||
}
|
||||
}
|
||||
|
||||
// now check limiter
|
||||
// success, record in limiter
|
||||
if cl.config.Count {
|
||||
count := cl.limiter[addrString] + 1
|
||||
if count > maxConcurrent {
|
||||
return ErrLimitExceeded
|
||||
}
|
||||
cl.limiter[addrString] = count
|
||||
}
|
||||
|
||||
@@ -192,17 +192,15 @@ func (cl *Limiter) AddClient(addr net.IP) error {
|
||||
}
|
||||
|
||||
// RemoveClient removes the given address from our population
|
||||
func (cl *Limiter) RemoveClient(addr net.IP) {
|
||||
flat := flatip.FromNetIP(addr)
|
||||
|
||||
func (cl *Limiter) RemoveClient(addr flatip.IP) {
|
||||
cl.Lock()
|
||||
defer cl.Unlock()
|
||||
|
||||
if !cl.config.Count || flatip.IPInNets(flat, cl.config.exemptedNets) {
|
||||
if !cl.config.Count || flatip.IPInNets(addr, cl.config.exemptedNets) {
|
||||
return
|
||||
}
|
||||
|
||||
addrString, _, _ := cl.addrToKey(flat)
|
||||
addrString, _, _ := cl.addrToKey(addr)
|
||||
count := cl.limiter[addrString]
|
||||
count -= 1
|
||||
if count < 0 {
|
||||
@@ -212,17 +210,15 @@ func (cl *Limiter) RemoveClient(addr net.IP) {
|
||||
}
|
||||
|
||||
// ResetThrottle resets the throttle count for an IP
|
||||
func (cl *Limiter) ResetThrottle(addr net.IP) {
|
||||
flat := flatip.FromNetIP(addr)
|
||||
|
||||
func (cl *Limiter) ResetThrottle(addr flatip.IP) {
|
||||
cl.Lock()
|
||||
defer cl.Unlock()
|
||||
|
||||
if !cl.config.Throttle || flatip.IPInNets(flat, cl.config.exemptedNets) {
|
||||
if !cl.config.Throttle || flatip.IPInNets(addr, cl.config.exemptedNets) {
|
||||
return
|
||||
}
|
||||
|
||||
addrString, _, _ := cl.addrToKey(flat)
|
||||
addrString, _, _ := cl.addrToKey(addr)
|
||||
delete(cl.throttler, addrString)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,26 +5,20 @@ package connection_limits
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/oragono/oragono/irc/flatip"
|
||||
)
|
||||
|
||||
func easyParseIP(ipstr string) (result net.IP) {
|
||||
result = net.ParseIP(ipstr)
|
||||
if result == nil {
|
||||
panic(ipstr)
|
||||
func easyParseIP(ipstr string) (result flatip.IP) {
|
||||
result, err := flatip.ParseIP(ipstr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func easyParseFlat(ipstr string) (result flatip.IP) {
|
||||
r1 := easyParseIP(ipstr)
|
||||
return flatip.FromNetIP(r1)
|
||||
}
|
||||
|
||||
var baseConfig = LimiterConfig{
|
||||
rawLimiterConfig: rawLimiterConfig{
|
||||
Count: true,
|
||||
@@ -56,20 +50,20 @@ func TestKeying(t *testing.T) {
|
||||
limiter.ApplyConfig(&config)
|
||||
|
||||
// an ipv4 /32 looks like a /128 to us after applying the 4-in-6 mapping
|
||||
key, maxConc, maxWin := limiter.addrToKey(easyParseFlat("1.1.1.1"))
|
||||
key, maxConc, maxWin := limiter.addrToKey(easyParseIP("1.1.1.1"))
|
||||
assertEqual(key.prefixLen, uint8(128), t)
|
||||
assertEqual(key.maskedIP[12:], []byte{1, 1, 1, 1}, t)
|
||||
assertEqual(maxConc, 4, t)
|
||||
assertEqual(maxWin, 8, t)
|
||||
|
||||
testIPv6 := easyParseFlat("2607:5301:201:3100::7426")
|
||||
testIPv6 := easyParseIP("2607:5301:201:3100::7426")
|
||||
key, maxConc, maxWin = limiter.addrToKey(testIPv6)
|
||||
assertEqual(key.prefixLen, uint8(64), t)
|
||||
assertEqual(key.maskedIP[:], []byte(easyParseIP("2607:5301:201:3100::")), t)
|
||||
assertEqual(flatip.IP(key.maskedIP), easyParseIP("2607:5301:201:3100::"), t)
|
||||
assertEqual(maxConc, 4, t)
|
||||
assertEqual(maxWin, 8, t)
|
||||
|
||||
key, maxConc, maxWin = limiter.addrToKey(easyParseFlat("8.8.4.4"))
|
||||
key, maxConc, maxWin = limiter.addrToKey(easyParseIP("8.8.4.4"))
|
||||
assertEqual(key.prefixLen, uint8(0), t)
|
||||
assertEqual([16]byte(key.maskedIP), md5.Sum([]byte("google")), t)
|
||||
assertEqual(maxConc, 128, t)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
package connection_limits
|
||||
|
||||
import (
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -83,7 +82,7 @@ func makeTestThrottler(v4len, v6len int) *Limiter {
|
||||
|
||||
func TestConnectionThrottle(t *testing.T) {
|
||||
throttler := makeTestThrottler(32, 64)
|
||||
addr := net.ParseIP("8.8.8.8")
|
||||
addr := easyParseIP("8.8.8.8")
|
||||
|
||||
for i := 0; i < 3; i += 1 {
|
||||
err := throttler.AddClient(addr)
|
||||
@@ -97,14 +96,14 @@ func TestConnectionThrottleIPv6(t *testing.T) {
|
||||
throttler := makeTestThrottler(32, 64)
|
||||
|
||||
var err error
|
||||
err = throttler.AddClient(net.ParseIP("2001:0db8::1"))
|
||||
err = throttler.AddClient(easyParseIP("2001:0db8::1"))
|
||||
assertEqual(err, nil, t)
|
||||
err = throttler.AddClient(net.ParseIP("2001:0db8::2"))
|
||||
err = throttler.AddClient(easyParseIP("2001:0db8::2"))
|
||||
assertEqual(err, nil, t)
|
||||
err = throttler.AddClient(net.ParseIP("2001:0db8::3"))
|
||||
err = throttler.AddClient(easyParseIP("2001:0db8::3"))
|
||||
assertEqual(err, nil, t)
|
||||
|
||||
err = throttler.AddClient(net.ParseIP("2001:0db8::4"))
|
||||
err = throttler.AddClient(easyParseIP("2001:0db8::4"))
|
||||
assertEqual(err, ErrThrottleExceeded, t)
|
||||
}
|
||||
|
||||
@@ -112,13 +111,13 @@ func TestConnectionThrottleIPv4(t *testing.T) {
|
||||
throttler := makeTestThrottler(24, 64)
|
||||
|
||||
var err error
|
||||
err = throttler.AddClient(net.ParseIP("192.168.1.101"))
|
||||
err = throttler.AddClient(easyParseIP("192.168.1.101"))
|
||||
assertEqual(err, nil, t)
|
||||
err = throttler.AddClient(net.ParseIP("192.168.1.102"))
|
||||
err = throttler.AddClient(easyParseIP("192.168.1.102"))
|
||||
assertEqual(err, nil, t)
|
||||
err = throttler.AddClient(net.ParseIP("192.168.1.103"))
|
||||
err = throttler.AddClient(easyParseIP("192.168.1.103"))
|
||||
assertEqual(err, nil, t)
|
||||
|
||||
err = throttler.AddClient(net.ParseIP("192.168.1.104"))
|
||||
err = throttler.AddClient(easyParseIP("192.168.1.104"))
|
||||
assertEqual(err, ErrThrottleExceeded, t)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user