Commit 8ed51d37 authored by Kaarle Ritvanen's avatar Kaarle Ritvanen
Browse files

Filter: update limits without dropping packets

parent ff97d234
...@@ -436,6 +436,18 @@ limit object may have an attribute named **log**. It defines how the ...@@ -436,6 +436,18 @@ limit object may have an attribute named **log**. It defines how the
dropped packets should be logged and is semantically similar to the dropped packets should be logged and is semantically similar to the
**log** attribute of rule objects. **log** attribute of rule objects.
Filter objects may have an attribute named **update-limit**. This
causes the packet flow or new connection attempts matching the filter
to be included in the total rate of a named limit without any packets
being dropped. When defined as a string, it is interpreted as the name
of the limit. It can also be defined as an object with a **name**
attribute and additional attributes. The **measure** attribute is used
to select whether to measure the packet flow (**flow**) or connection
attempts (**conn**, default). The **addr** attribute is used to select
whether to consider the source (**src**, default) or destination
(**dest**) address. When **update-limit** is defined, **action**
defaults to **pass** and cannot be set to any other value.
Filter objects may have an attribute named **dnat**, the value of Filter objects may have an attribute named **dnat**, the value of
which is an IPv4 address. If defined, this enables destination NAT for which is an IPv4 address. If defined, this enables destination NAT for
all IPv4 packets matching the rule, such that the specified address all IPv4 packets matching the rule, such that the specified address
......
...@@ -18,6 +18,19 @@ local util = require('awall.util') ...@@ -18,6 +18,19 @@ local util = require('awall.util')
local contains = util.contains local contains = util.contains
local extend = util.extend local extend = util.extend
local listpairs = util.listpairs local listpairs = util.listpairs
local setdefault = util.setdefault
local function initmask(obj)
for _, attr in ipairs{'src-mask', 'dest-mask'} do
if obj[attr] then
obj:error('Attribute not allowed with a named limit: '..attr)
end
end
local limits = obj.root.limit
obj[(obj.addr or 'src')..'-mask'] = limits and limits[obj.name] or true
end
local RECENT_MAX_COUNT = 20 local RECENT_MAX_COUNT = 20
...@@ -25,16 +38,7 @@ local RECENT_MAX_COUNT = 20 ...@@ -25,16 +38,7 @@ local RECENT_MAX_COUNT = 20
local FilterLimit = class(model.Limit) local FilterLimit = class(model.Limit)
function FilterLimit:initmask() function FilterLimit:initmask()
if self.name then if self.name then initmask(self)
for _, attr in ipairs{'src-mask', 'dest-mask'} do
if self[attr] then
self:error('Attribute not allowed with a named limit: '..attr)
end
end
local limits = self.root.limit
self[(self.addr or 'src')..'-mask'] = limits and limits[self.name] or true
elseif self.update ~= nil then elseif self.update ~= nil then
self:error('Attribute allowed only with named limits: update') self:error('Attribute allowed only with named limits: update')
end end
...@@ -72,6 +76,24 @@ function FilterLimit:recentofrags(name) ...@@ -72,6 +76,24 @@ function FilterLimit:recentofrags(name)
end end
local LimitReference = class(model.Maskable)
function LimitReference:initmask()
if not self.name then
if not self[1] then self:error('Limit name not defined') end
self.name = self[1]
end
initmask(self)
LimitReference.super(self):initmask()
end
function LimitReference:recentofrags()
local ofs = self:recentmask()
return ofs and combinations(ofs, {{match='--set'}}) or self:error(MASK_ERROR)
end
local TranslatingRule = class(Rule) local TranslatingRule = class(Rule)
function TranslatingRule:init(...) function TranslatingRule:init(...)
...@@ -129,7 +151,7 @@ local LoggingRule = class(TranslatingRule) ...@@ -129,7 +151,7 @@ local LoggingRule = class(TranslatingRule)
function LoggingRule:init(...) function LoggingRule:init(...)
LoggingRule.super(self):init(...) LoggingRule.super(self):init(...)
util.setdefault(self, 'action', 'accept') setdefault(self, 'action', 'accept')
local custom = self:customtarget() local custom = self:customtarget()
if type(self.log) ~= 'table' then if type(self.log) ~= 'table' then
...@@ -188,6 +210,9 @@ function RelatedRule:target() return 'ACCEPT' end ...@@ -188,6 +210,9 @@ function RelatedRule:target() return 'ACCEPT' end
local Filter = class(LoggingRule) local Filter = class(LoggingRule)
function Filter:init(...) function Filter:init(...)
local ul = self['update-limit']
if ul then setdefault(self, 'action', 'pass') end
Filter.super(self):init(...) Filter.super(self):init(...)
-- alpine v2.4 compatibility -- alpine v2.4 compatibility
...@@ -206,6 +231,21 @@ function Filter:init(...) ...@@ -206,6 +231,21 @@ function Filter:init(...)
end end
self[limit].log = loadclass('log').get(self, self[limit].log, true) self[limit].log = loadclass('log').get(self, self[limit].log, true)
end end
if ul then
if self.action ~= 'pass' then
self:error('Cannot specify action with update-limit')
end
if not contains({'conn', 'flow'}, setdefault(ul, 'measure', 'conn')) then
self:error('Invalid value for measure: '..ul.measure)
end
if self['no-track'] and ul.measure == 'conn' then
self:error('Tracking required when measuring connection rate')
end
self:create(LimitReference, ul, 'update-limit')
end
end end
function Filter:extratrules() function Filter:extratrules()
...@@ -309,8 +349,11 @@ function Filter:limit() ...@@ -309,8 +349,11 @@ function Filter:limit()
end end
function Filter:position() function Filter:position()
return not self['no-track'] and self:limit() == 'flow-limit' return not self['no-track'] and (
and 'prepend' or 'append' self:limit() == 'flow-limit' or (
self['update-limit'] and self['update-limit'].measure == 'flow'
)
) and 'prepend' or 'append'
end end
function Filter:logdefault() function Filter:logdefault()
...@@ -327,12 +370,19 @@ end ...@@ -327,12 +370,19 @@ end
function Filter:mangleoptfrags(ofrags) function Filter:mangleoptfrags(ofrags)
local limit = self:limit() local limit = self:limit()
if not limit then return Filter.super(self):mangleoptfrags(ofrags) end if not limit then
if self['update-limit'] then
ofrags = self:combine(ofrags, self['update-limit']:recentofrags())
end
return Filter.super(self):mangleoptfrags(ofrags)
end
local function incompatible(item) local function incompatible(item)
self:error('Limit incompatible with '..item) self:error('Limit incompatible with '..item)
end end
if self['update-limit'] then incompatible('update-limit') end
if self:customtarget() or self:logdefault() then if self:customtarget() or self:logdefault() then
incompatible('action: '..self.action) incompatible('action: '..self.action)
end end
......
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