diff options
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | awall/modules/filter.lua | 78 |
2 files changed, 76 insertions, 14 deletions
@@ -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 **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 which is an IPv4 address. If defined, this enables destination NAT for all IPv4 packets matching the rule, such that the specified address diff --git a/awall/modules/filter.lua b/awall/modules/filter.lua index 963e93e..436fbfa 100644 --- a/awall/modules/filter.lua +++ b/awall/modules/filter.lua @@ -18,6 +18,19 @@ local util = require('awall.util') local contains = util.contains local extend = util.extend 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 @@ -25,16 +38,7 @@ local RECENT_MAX_COUNT = 20 local FilterLimit = class(model.Limit) function FilterLimit:initmask() - if self.name then - 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 - + if self.name then initmask(self) elseif self.update ~= nil then self:error('Attribute allowed only with named limits: update') end @@ -72,6 +76,24 @@ function FilterLimit:recentofrags(name) 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) function TranslatingRule:init(...) @@ -129,7 +151,7 @@ local LoggingRule = class(TranslatingRule) function LoggingRule:init(...) LoggingRule.super(self):init(...) - util.setdefault(self, 'action', 'accept') + setdefault(self, 'action', 'accept') local custom = self:customtarget() if type(self.log) ~= 'table' then @@ -188,6 +210,9 @@ function RelatedRule:target() return 'ACCEPT' end local Filter = class(LoggingRule) function Filter:init(...) + local ul = self['update-limit'] + if ul then setdefault(self, 'action', 'pass') end + Filter.super(self):init(...) -- alpine v2.4 compatibility @@ -206,6 +231,21 @@ function Filter:init(...) end self[limit].log = loadclass('log').get(self, self[limit].log, true) 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 function Filter:extratrules() @@ -309,8 +349,11 @@ function Filter:limit() end function Filter:position() - return not self['no-track'] and self:limit() == 'flow-limit' - and 'prepend' or 'append' + return not self['no-track'] and ( + self:limit() == 'flow-limit' or ( + self['update-limit'] and self['update-limit'].measure == 'flow' + ) + ) and 'prepend' or 'append' end function Filter:logdefault() @@ -327,12 +370,19 @@ end function Filter:mangleoptfrags(ofrags) 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) self:error('Limit incompatible with '..item) end + if self['update-limit'] then incompatible('update-limit') end + if self:customtarget() or self:logdefault() then incompatible('action: '..self.action) end |