--[[ Copyright (c) 2012 Kaarle Ritvanen See LICENSE file for license details --]] module(..., package.seeall) local M = require('acf.model') local object = require('acf.object') local class = object.class local super = object.super IPv4Addr = class(M.String) function IPv4Addr:validate(txn, path, value) local function test(...) if #arg ~= 4 then return true end for _, octet in ipairs(arg) do if tonumber(octet) > 255 then return true end end end if test(string.match(value, '(%d+)%.(%d+)%.(%d+)%.(%d+)')) then error('Invalid IP address: '..value) end end Port = class(M.Integer) function Port:validate(txn, path, value) super(self, Port):validate(txn, path, value) if value < 0 or value > 65535 then error('Invalid port: '..value) end end Range = class(M.String) function Range:validate(txn, path, value) local comps = stringy.split(value, '-') if #comps > 2 then error('Invalid range: '..value) end for _, v in ipairs(comps) do local num = tonumber(v) if not num then error('Invalid range: ' ..value) end M.to_field(self.type):validate(txn, path, num) end end PortRange = class(Range) function PortRange:init() super(self, PortRange):init{type=Port} end Direction = class(M.String) function Direction:init() super(self, Direction):init{choice={'in', 'out'}} end -- TODO reference types? IPSet = M.new() -- TODO choices IPSet.type = M.String{required=true} IPSet.family = M.String{required=true, choice={'inet', 'inet6'}} Service = M.new() Service.proto = M.String{required=true} Service.port = M.Collection{type=PortRange} Service['icmp-type'] = M.String -- TODO fw zone Zone = M.new() Zone.iface = M.PrimitiveList{type=M.String} Zone.addr = M.PrimitiveList{type=M.String} Zone['route-back'] = M.Boolean{default=false} LogClass = M.new() LogClass.mode = M.String{default='log', choice={'log', 'nflog', 'ulog'}} LogClass.limit = M.Integer LogClass.prefix = M.String IPSetReference = M.new() IPSetReference.name = M.Reference{scope='../../ipset', required=true} IPSetReference.args = M.Collection{type=Direction, required=true} Rule = M.new() Rule['in'] = M.Collection{type=M.Reference{scope='../../zone'}} Rule.out = M.Collection{type=M.Reference{scope='../../zone'}} Rule.src = M.Collection{type=M.String} Rule.dest = M.Collection{type=M.String} Rule.ipset = IPSetReference Rule.ipsec = Direction Rule.service = M.Collection{type=M.Reference{scope='../../service'}} Rule.action = M.String{choice={'accept'}} -- TODO no service field PolicyRule = M.new(Rule) PolicyRule.log = M.Reference{scope='../log'} PolicyRule.action = M.String{required=true, choice={'accept', 'drop', 'reject', 'tarpit'}} Limit = M.new() Limit.count = M.Integer Limit.interval = M.Integer Limit.log = M.Reference{scope='../../log'} FilterRule = M.new(PolicyRule) FilterRule['conn-limit'] = Limit FilterRule['flow-limit'] = Limit FilterRule.dnat = IPv4Addr FilterRule['no-track'] = M.Boolean{default=false} NATRule = M.new(Rule) NATRule['to-addr'] = Range{type=IPv4Addr} NATRule['to-port'] = PortRange MarkRule = M.new(Rule) MarkRule.mark = M.Integer{required=true} ClampMSSRule = M.new(Rule) ClampMSSRule.mss = M.Integer AWall = M.new() -- TODO differentiate lists? AWall.service = M.Collection{type=M.Collection{type=Service}} AWall.zone = M.Collection{type=Zone} AWall.log = M.Collection{type=LogClass} AWall.policy = M.Collection{type=PolicyRule} AWall.filter = M.Collection{type=FilterRule} AWall.dnat = M.Collection{type=NATRule} AWall.snat = M.Collection{type=NATRule} AWall.mark = M.Collection{type=MarkRule} AWall['route-track'] = M.Collection{type=MarkRule} AWall['clamp-mss'] = M.Collection{type=ClampMSSRule} AWall['no-track'] = M.Collection{type=Rule} AWall.ipset = M.Collection{type=IPSet} M.register('awall', '/json'..require('lfs').currentdir()..'/config/awall.json', AWall)