--[[ @object-name: firewall @object-desc: 防火墙 --]] local M = {} local fs = require "oui.fs" local rpc = require "oui.rpc" local ubus = require "oui.ubus" local uci = require "uci" local function firewall_apply(apply) if apply == false then return end ubus.call("service", "event", { type = "config.change", data = { package = "firewall" } }) end local function set_opt(c, sid, params) params.name = "GL-" .. (params.name or "") for k, v in pairs(params) do if k ~= "id" then if type(v) == "boolean" then v = v and "1" or "0" end c:set("firewall", sid, k, v) end end end local function get_opt(c, typ, filter) local res = {} c:foreach("firewall", typ, function(s) local name = s.name if not name then return end if name:sub(1, 3) ~= "GL-" then return end s.name = name:sub(4) if s.name == "" then s.name = nil end if filter and filter(s) == false then return end s.enabled = s.enabled ~= "0" s.src_port = s.src_port and tonumber(s.src_port) s.dest_port = s.dest_port and tonumber(s.dest_port) s.id = s[".name"] s[".name"] = nil s[".anonymous"] = nil s[".index"] = nil s[".type"] = nil res[#res + 1] = s end) return res end --[[ @method-type: call @method-name: get_zone_list @method-desc: 获取所有防火墙区域 @out array internals 内部区域列表 @out array externals 外部区域列表 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","get_zone_list"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"internals": ["lan", "guest"], "externals": ["wan"]}} --]] M.get_zone_list = function() local c = uci.cursor() local internals = {} local externals = {} c:foreach("firewall", "zone", function(s) internals[#internals + 1] = s.name externals[#externals + 1] = s.name end) return { internals = internals, externals = externals } end --[[ @method-type: call @method-name: get_rule_list @method-desc: 获取所有防火墙规则 @out array res 规则列表 @out string ?res.name 名称 @out string ?res.src 源区域(从 get_zone_list 接口获取) @out string ?res.src_ip 源 IP 地址 @out string ?res.src_mac 源 MAC 地址 @out number ?res.src_port 源端口 @out string ?res.proto 协议 @out string ?res.dest 目的区域(从 get_zone_list 接口获取) @out string ?res.dest_ip 目的 IP 地址 @out number ?res.dest_port 目的端口 @out bool res.enabled 是否开启 @out string res.target 动作(可选值: ACCEPT, REJECT, DROP) @out string res.id 配置项 ID @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","get_rule_list"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"res": [{"id": "cfg1392bd", "dest_port": 22, "name": "test", "target": "ACCEPT", "enabled": true, "src": "wan"}]}} --]] M.get_rule_list = function() local c = uci.cursor() local res = get_opt(c, "rule") return { res = res } end --[[ @method-type: call @method-name: add_rule @method-desc: 添加防火墙规则: 打开端口(src="wan", proto="tcp" dest_port=80, target="ACCEPT", enabled=true) @in string ?name 名称 @in string ?src 源区域(从 get_zone_list 接口获取) @in string ?src_ip 源 IP 地址 @in string ?src_mac 源 MAC 地址 @in number ?src_port 源端口(1 ~ 65535) @in string ?proto 协议(可选值: "tcp udp", "tcp", "udp") @in string ?dest 目的区域(从 get_zone_list 接口获取) @in string ?dest_ip 目的 IP 地址 @in number ?dest_port 目的端口(1 ~ 65535) @in bool ?enabled 是否开启 @in string target 动作(可选值: ACCEPT, REJECT, DROP) @out string id 配置项 ID @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","add_rule",{"name":"test","src":"wan","dest_port":22,"target":"ACCEPT"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"id": "cfg1392bd"}} --]] M.add_rule = function(params) local c = uci.cursor() local apply = params.apply params.apply = nil local sid = c:add("firewall", "rule") set_opt(c, sid, params) c:commit("firewall") fs.sync() firewall_apply(apply) return { id = sid } end --[[ @method-type: call @method-name: set_rule @method-desc: 修改防火墙规则 @in string ?name 名称 @in string ?src 源区域(从 get_zone_list 接口获取) @in string ?src_ip 源 IP 地址 @in string ?src_mac 源 MAC 地址 @in number ?src_port 源端口(1 ~ 65535) @in string ?proto 协议(可选值: "tcp udp", "tcp", "udp") @in string ?dest 目的区域(从 get_zones 接口获取) @in string ?dest_ip 目的 IP 地址 @in number ?dest_port 目的端口(1 ~ 65535) @in bool ?enabled 是否开启 @in string ?target 动作(可选值: ACCEPT, REJECT, DROP) @in string id 配置项 ID(从 get_rules 或者 add_rule 接口获取) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","set_rule",{"id":"cfg1392bd","src":"wan","dest_port":80}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.set_rule = function(params) local sid = params.id if type(sid) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "id: string required" end local c = uci.cursor() set_opt(c, sid, params) c:commit("firewall") fs.sync() firewall_apply() ngx.timer.at(4, function() os.execute("echo f > /proc/net/nf_conntrack") end) end --[[ @method-type: call @method-name: remove_rule @method-desc: 删除防火墙规则 @in string ?id UCI 标识(从 get_rule_list 或者 add_rule 接口获取) @in bool ?all 是否全部删除(全部删除时不传 id) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","remove_rule",{"id":"cfg1392bd"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.remove_rule = function(params) local c = uci.cursor() if params.all then c:foreach("firewall", "rule", function(s) if not s.name or s.name:sub(1, 3) ~= "GL-" then return end c:delete("firewall", s[".name"]) end) else c:delete("firewall", params.id) end c:commit("firewall") fs.sync() firewall_apply(params.apply) end --[[ @method-type: call @method-name: get_dmz @method-desc: 获取 DMZ 配置 @out bool enabled 是否开启 @out string dest_ip 内部 IP 地址 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","get_dmz"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"enabled": true, "dest_ip": "192.168.8.100"}} --]] M.get_dmz = function() local c = uci.cursor() local res = { enabled = false } c:foreach("firewall", "redirect", function(s) if s.name ~= "GL-DMZ" then return end res.enabled = s.enabled ~= "0" res.dest_ip = s.dest_ip end) return res end --[[ @method-type: call @method-name: set_dmz @method-desc: 获取 DMZ 配置 @in bool enabled=true 是否开启 @in string ?dest_ip 内部 IP 地址(开启时必填) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","set_dmz",{"dest_ip":"192.168.8.100"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.set_dmz = function(params) local c = uci.cursor() local dest_ip = params.dest_ip if params.enabled then if type(dest_ip) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "dest_ip: string required" end end c:foreach("firewall", "redirect", function(s) if s.name ~= "GL-DMZ" then return end c:delete("firewall", s[".name"]) end) local sid = c:add("firewall", "redirect") set_opt(c, sid, { name = "DMZ", src = "wan", proto = "all", dest_ip = dest_ip, enabled = params.enabled }) -- reorder dmz to first if params.enabled then local index c:foreach("firewall", "rule", function(s) index = s[".index"] return false end) c:foreach("firewall", "redirect", function(s) if s.name ~= "GL-DMZ" then if not index or s[".index"] < index then index = s[".index"] end return false end end) if index then c:reorder("firewall", sid, index) end end c:commit("firewall") fs.sync() firewall_apply(params.apply) end --[[ @method-type: call @method-name: get_port_forward_list @method-desc: 获取所有端口转发配置 @out array res 端口转发配置列表 @out string ?res.name 名称 @out string ?res.proto 协议(默认:"tcp udp",可选值: "tcp udp", "tcp", "udp") @out string res.src 外部区域(从 get_zone_list 接口获取) @out string res.src_dport 外部端口 @out string res.dest 内部区域(从 get_zone_list 接口获取) @out string res.dest_ip 内部 IP 地址 @out number res.dest_port 内部端口 @out bool res.enabled 是否开启 @out string res.id 配置项 ID @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","get_port_forward_list"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"res": [{"enabled": true, "id": "cfg143837", "dest_ip": "192.168.8.100", "src": "wan", "proto": "tcp"}]}} --]] M.get_port_forward_list = function() local c = uci.cursor() local res = get_opt(c, "redirect", function(s) if s.name == "DMZ" then return false end end) return { res = res } end --[[ @method-type: call @method-name: add_port_forward @method-desc: 添加端口转发 @in string ?name 名称 @in string ?proto 协议(默认:"tcp udp",可选值: "tcp udp", "tcp", "udp") @in string src 外部区域(从 get_zone_list 接口获取) @in string src_dport 外部端口 @in string dest 内部区域(从 get_zone_list 接口获取) @in string dest_ip 内部 IP 地址 @in number dest_port 内部端口 @in bool ?enabled=true 是否开启 @out string id 配置项 ID @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","add_port_forward",{"name": "test", "proto":"tcp", "src": "wan", "src_dport": 80, "dest":"lan", "dest_ip":"192.168.8.100", "dest_port": 80}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"id": "cfg153837"}} --]] M.add_port_forward = function(params) local c = uci.cursor() local apply = params.apply params.apply = nil local sid = c:add("firewall", "redirect") set_opt(c, sid, params) c:commit("firewall") fs.sync() firewall_apply(apply) return { id = sid } end --[[ @method-type: call @method-name: set_port_forward @method-desc: 修改端口转发 @in string ?name 名称 @in string ?proto 协议(默认:"tcp udp",可选值: "tcp udp", "tcp", "udp") @in string src 外部区域(从 get_zone_list 接口获取) @in string src_dport 外部端口 @in string dest 内部区域(从 get_zone_list 接口获取) @in string dest_ip 内部 IP 地址 @in number dest_port 内部端口 @in bool enabled 是否开启 @in string id 配置项 ID(从 get_port_forward_list 或者 add_port_forward 接口获取) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","set_port_forward",{"id":"cfg153837", "name": "test", "proto":"tcp", "src": "wan", "src_dport": 80, "dest":"lan", "dest_ip":"192.168.8.100", "dest_port": 80}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.set_port_forward = function(params) local sid = params.id if type(sid) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "id: string required" end local c = uci.cursor() set_opt(c, sid, params) c:commit("firewall") fs.sync() firewall_apply() end --[[ @method-type: call @method-name: remove_port_forward @method-desc: 删除防端口转发 @in string id 配置项 ID(从 get_port_forward_list 或者 add_port_forward 接口获取) @in bool ?all 是否全部删除(全部删除时不传 id) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","firewall","remove_port_forward",{"id":"cfg153837"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.remove_port_forward = function(params) local c = uci.cursor() if params.all then c:foreach("firewall", "redirect", function(s) if not s.name or s.name:sub(1, 3) ~= "GL-" then return end c:delete("firewall", s[".name"]) end) else c:delete("firewall", params.id) end c:commit("firewall") fs.sync() firewall_apply(params.apply) end return M