1
0
Fork 0
forked from External/ergo

refactor limits and throttling

This commit is contained in:
Shivaram Lingamneni 2017-10-09 01:47:04 -04:00
parent 6b20d60c06
commit a2ac4eeef9
4 changed files with 100 additions and 69 deletions

View file

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"net"
"sync"
)
var (
@ -15,6 +16,8 @@ var (
// ConnectionLimits manages the automated client connection limits.
type ConnectionLimits struct {
sync.Mutex
enabled bool
ipv4Mask net.IPMask
ipv6Mask net.IPMask
@ -45,6 +48,9 @@ func (cl *ConnectionLimits) maskAddr(addr net.IP) net.IP {
// AddClient adds a client to our population if possible. If we can't, throws an error instead.
// 'force' is used to add already-existing clients (i.e. ones that are already on the network).
func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
cl.Lock()
defer cl.Unlock()
if !cl.enabled {
return nil
}
@ -75,6 +81,9 @@ func (cl *ConnectionLimits) AddClient(addr net.IP, force bool) error {
// RemoveClient removes the given address from our population
func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
cl.Lock()
defer cl.Unlock()
if !cl.enabled {
return
}
@ -89,34 +98,47 @@ func (cl *ConnectionLimits) RemoveClient(addr net.IP) {
}
// NewConnectionLimits returns a new connection limit handler.
func NewConnectionLimits(config ConnectionLimitsConfig) (*ConnectionLimits, error) {
// The handler is functional, but disabled; it can be enabled via `ApplyConfig`.
func NewConnectionLimits() *ConnectionLimits {
var cl ConnectionLimits
cl.enabled = config.Enabled
// initialize empty population; all other state is configurable
cl.population = make(map[string]int)
cl.exemptedIPs = make(map[string]bool)
cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
// subnetLimit is explicitly NOT capped at a minimum of one.
// this is so that CL config can be used to allow ONLY clients from exempted IPs/nets
cl.subnetLimit = config.IPsPerCidr
return &cl
}
// ApplyConfig atomically applies a config update to a connection limit handler
func (cl *ConnectionLimits) ApplyConfig(config ConnectionLimitsConfig) error {
// assemble exempted nets
exemptedIPs := make(map[string]bool)
var exemptedNets []net.IPNet
for _, cidr := range config.Exempted {
ipaddr := net.ParseIP(cidr)
_, netaddr, err := net.ParseCIDR(cidr)
if ipaddr == nil && err != nil {
return nil, fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
return fmt.Errorf("Could not parse exempted IP/network [%s]", cidr)
}
if ipaddr != nil {
cl.exemptedIPs[ipaddr.String()] = true
exemptedIPs[ipaddr.String()] = true
} else {
cl.exemptedNets = append(cl.exemptedNets, *netaddr)
exemptedNets = append(exemptedNets, *netaddr)
}
}
return &cl, nil
cl.Lock()
defer cl.Unlock()
cl.enabled = config.Enabled
cl.ipv4Mask = net.CIDRMask(config.CidrLenIPv4, 32)
cl.ipv6Mask = net.CIDRMask(config.CidrLenIPv6, 128)
// subnetLimit is explicitly NOT capped at a minimum of one.
// this is so that CL config can be used to allow ONLY clients from exempted IPs/nets
cl.subnetLimit = config.IPsPerCidr
cl.exemptedIPs = exemptedIPs
cl.exemptedNets = exemptedNets
return nil
}