Commit d4d5dd62 authored by Kaarle Ritvanen's avatar Kaarle Ritvanen

generic class for rate limits

parent b98fe9cb
......@@ -29,6 +29,9 @@ local maplist = util.maplist
local startswith = require('stringy').startswith
local RECENT_MAX_COUNT = 20
local function join(a, b)
local comps = {}
local function add(s) if s and s > '' then table.insert(comps, s) end end
......@@ -67,6 +70,7 @@ function M.ConfigObject:create(cls, params, label, index)
end
end
if type(params) ~= 'table' then params = {params} end
params.label = join(self.label, label)
local obj = cls.morph(params, self.context, self.location)
......@@ -572,6 +576,45 @@ end
function M.Rule:extraoptfrags() return {} end
M.Limit = M.class(M.ConfigObject)
function M.Limit:init(...)
M.Limit.super(self):init(...)
if not self.count then
if not self[1] then
self:error('Packet count not defined for limit')
end
self.count = self[1]
end
if not self.interval then self.interval = 1 end
end
function M.Limit:rate() return math.ceil(self.count / self.interval) end
function M.Limit:recentopts()
local count = self.count
local interval = self.interval
if count > RECENT_MAX_COUNT then
count = self:rate()
interval = 1
end
if count <= RECENT_MAX_COUNT then
return '--update --hitcount '..count..' --seconds '..interval
end
end
function M.Limit:limitopts(name)
local rate = self:rate()
return '-m hashlimit --hashlimit-upto '..rate..
'/second --hashlimit-burst '..rate..
' --hashlimit-mode srcip --hashlimit-name '..(name or self:uniqueid())
end
M.export = {zone={class=M.Zone}, ipset={class=IPSet, before='%modules'}}
return M
......@@ -19,8 +19,6 @@ local contains = util.contains
local extend = util.extend
local listpairs = util.listpairs
local RECENT_MAX_COUNT = 20
local TranslatingRule = class(Rule)
......@@ -240,48 +238,38 @@ function Filter:extraoptfrags()
local limitchain = self:uniqueid('limit')
local limitlog = self[limit].log
local count = self[limit].count
local interval = self[limit].interval or 1
if count > RECENT_MAX_COUNT then
count = math.ceil(count / interval)
interval = 1
end
local limitobj = self:create(model.Limit, self[limit], 'limit')
local ofrags = {}
local logch, limitofs
local accept = self:position() == 'append'
if count > RECENT_MAX_COUNT then
if accept then
ofrags, logch = self:logchain(self.log, 'accept', 'ACCEPT')
else logch = 'RETURN' end
limitofs = {
{
opts='-m hashlimit --hashlimit-upto '..count..'/second --hashlimit-burst '..count..' --hashlimit-mode srcip --hashlimit-name '..limitchain,
target=logch
},
{target='DROP'}
}
if limitlog then table.insert(limitofs, 2, limitlog:optfrag()) end
local recentopts = limitobj:recentopts()
else
if recentopts then
ofrags, logch = self:logchain(limitlog, 'drop', 'DROP')
limitofs = combinations(
{{opts='-m recent --name '..limitchain}},
{
{
opts='--update --hitcount '..count..' --seconds '..interval,
target=logch
},
{opts=recentopts, target=logch},
{opts='--set', target=accept and 'ACCEPT' or nil}
}
)
if accept and self.log then
table.insert(limitofs, 2, self.log:optfrag())
end
else
if accept then
ofrags, logch = self:logchain(self.log, 'accept', 'ACCEPT')
else logch = 'RETURN' end
limitofs = {
{opts=limitobj:limitopts(limitchain), target=logch},
{target='DROP'}
}
if limitlog then table.insert(limitofs, 2, limitlog:optfrag()) end
end
extend(ofrags, combinations({{chain=limitchain}}, limitofs))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment