--[[ @object-name: zerotier @object-desc: zerotier settings --]] local M = {} local uci = require "uci" local lnet = require "oui.network" local ubus = require "oui.ubus" local fs = require "oui.fs" local function zerotier_init() local c = uci.cursor() for ifname in fs.dir("/sys/class/net") do if ifname:match("zt*") then c:set("network", "zerotier", "interface") c:set("network", "zerotier", "device", ifname) c:set("network", "zerotier", "proto", "none") c:set("firewall", "zerotier", "zone") c:set("firewall", "zerotier", "name", "zerotier") c:set("firewall", "zerotier", "network", { "zerotier" }) c:set("firewall", "zerotier", "input", "ACCEPT") c:set("firewall", "zerotier", "forward", "REJECT") c:set("firewall", "zerotier", "masq", "1") c:set("firewall", "zerotier", "mtu_fix", "1") c:set("firewall", "zerotier", "output", "ACCEPT") c:commit("network") c:commit("firewall") end end end local function zerotier_lan_nat_start() local c = uci.cursor() c:set("firewall", "zerotier_lan", "forwarding") c:set("firewall", "zerotier_lan", "dest", "lan") c:set("firewall", "zerotier_lan", "src", "zerotier") c:set("firewall", "lan_zerotier", "forwarding") c:set("firewall", "lan_zerotier", "dest", "zerotier") c:set("firewall", "lan_zerotier", "src", "lan") c:commit("firewall") end local function zerotier_lan_nat_stop() local c = uci.cursor() c:delete("firewall", "zerotier_lan") c:delete("firewall", "lan_zerotier") c:commit("firewall") end local function zerotier_wan_nat_start() local c = uci.cursor() c:set("firewall", "zerotier_wan", "forwarding") c:set("firewall", "zerotier_wan", "dest", "wan") c:set("firewall", "zerotier_wan", "src", "zerotier") c:commit("firewall") end local function zerotier_wan_nat_stop() local c = uci.cursor() c:delete("firewall", "zerotier_wan") c:commit("firewall") end --[[ @method-type: call @method-name: get_config @method-desc: Get zerotier settings @out string id zerotier network id @out bool enabled Whether to enable zerotier @out bool lan_enabled Whether to enable lan side access @out bool wan_enabled Whether to enable wan side access @out number ?err_code Error code,-1:Failed to get settings. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","zerotier","get_config"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"id": "22222222", "enable": true, "lan_enabled": true, "wan_enabled": false}} --]] function M.get_config() local c = uci.cursor() local enabled = c:get("zerotier", "gl", "enabled") == "1" local lan_enabled = c:get("zerotier", "gl", "nat") == "1" local wan_enabled = c:get("zerotier", "gl", "wan_nat") == "1" local join_id = c:get("zerotier","gl", "join") local id if join_id then for _, v in ipairs(join_id) do id = v end end return { enabled = enabled, lan_enabled = lan_enabled, wan_enabled = wan_enabled, id = id } end --[[ @method-type: call @method-name: set_config @method-desc: Set zerotier settings @in bool lan_enabled Whether to allow lan side access: false: Disable, true:Enable @in bool wan_enabled Whether to allow lan side access: false: Disable, true:Enable @in bool enabled Status of zerotier:false :Disable, true:Enable @in string id zerotier network id @out number ?err_code Error code,-1:illegal parameter. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","zerotier","set_config",{"enabled":true,"lan_enabled":true,"wan_enabled":true,"id":"1234679862"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_config(params) if params.enabled == true and ( #params.id ~= 16 or params.id:match("^%x+$") == nil ) then return { err_code = 1, err_msg = "parameter error" } end local c = uci.cursor() local lan_enabled = params.lan_enabled and 1 or 0 local wan_enabled = params.wan_enabled and 1 or 0 if params.enabled then c:set("zerotier", "gl", "enabled", 1) c:set("zerotier", "gl", "nat", lan_enabled) c:set("zerotier", "gl", "wan_nat", wan_enabled) local id_change = false local join_id = c:get("zerotier","gl", "join") if join_id then for _, v in ipairs(join_id) do if v ~= params.id then id_change = true end end end c:set("zerotier", "gl", "join", {params.id}) c:commit("zerotier") ngx.pipe.spawn({"/etc/init.d/zerotier", "reload"}):wait() local proto = c:get("network", "zerotier", "proto") if proto == nil or id_change then ngx.sleep(4) zerotier_init() end if params.lan_enabled then zerotier_lan_nat_start() else zerotier_lan_nat_stop() end if params.wan_enabled then zerotier_wan_nat_start() else zerotier_wan_nat_stop() end ubus.call("network", "reload") ngx.pipe.spawn({"/etc/init.d/firewall", "reload"}):wait() ngx.pipe.spawn({"/etc/init.d/zerotier", "reload"}):wait() ngx.timer.at(5, function() ngx.pipe.spawn({"/usr/bin/flush_zerotier_nf_conntrack"}):wait() end) else c:set("zerotier", "gl", "enabled", 0) c:commit("zerotier") ngx.pipe.spawn({"/etc/init.d/zerotier", "reload"}):wait() end fs.sync() end --[[ @method-type: call @method-name: get_status @method-desc: Get zerotier status @out number status 0:connection succeeded, 1:connecting, 2:unauthorized, 3:The network id was not found @out string zerotier_ip After the connection is successful, return the zerotier server ip @out string lan_ip Enable to allow lan side access and return lan ip @out string wan_ip Enable to allow wan side access and return wan ip @out string secondwan_ip Enable to allow wan side access and return secondwan ip @out string wwan_ip Enable to allow wan side access and return wwan ip @out number ?err_code Error code,-1:Failed to get settings. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","zerotier","get_status"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"status": 0, "zerotier_ip": "192.168.17.144", "lan_ip": "192.168.8.0/24", "wan_ip": "192.168.83.0/24", , "wwan_ip": "192.168.66.0/24"}} --]] function M.get_status() local c = uci.cursor() local status local zerotier_ip, lan_ip, wan_ip, wwan_ip, secondwan_ip local zerotier_list = assert(io.popen("zerotier-cli listnetworks | tail -n 1")) local zerotier_info = zerotier_list:read() zerotier_list:close() if zerotier_info then if zerotier_info:match("OK") then zerotier_ip = zerotier_info:match("(%d+%.%d+%.%d+%.%d+)") local ip = c:get("network", "lan", "ipaddr") local lan_mask = c:get("network", "lan", "netmask") local lan_info = lnet.ipcalc({ip, lan_mask}) lan_ip = lan_info.network .. "/" .. lan_info.prefix local s = ubus.call("network.interface.wan", "status") if s['ipv4-address'] and s['ipv4-address'][1]['address'] then local _wan_info = s['ipv4-address'][1]['address'] .. '/' .. s['ipv4-address'][1]['mask'] local wan_info = lnet.ipcalc(_wan_info) wan_ip = wan_info.network .. "/" .. wan_info.prefix end local f = ubus.call("network.interface.secondwan", "status") if f then if f['ipv4-address'] and f['ipv4-address'][1]['address'] then local _secondwan_info = f['ipv4-address'][1]['address'] .. '/' .. f['ipv4-address'][1]['mask'] local secondwan_info = lnet.ipcalc(_secondwan_info) secondwan_ip = secondwan_info.network .. "/" .. secondwan_info.prefix end end f = ubus.call("network.interface.wwan", "status") if f then if f['ipv4-address'] and f['ipv4-address'][1]['address'] then local _wwan_info = f['ipv4-address'][1]['address'] .. '/' .. f['ipv4-address'][1]['mask'] local wwan_info = lnet.ipcalc(_wwan_info) wwan_ip = wwan_info.network .. "/" .. wwan_info.prefix end end status = 0 elseif zerotier_info:match("REQUESTING_CONFIGURATION") then status = 1 elseif zerotier_info:match("ACCESS_DENIED") then status = 2 else status = 3 end end return { status = status, zerotier_ip = zerotier_ip, lan_ip = lan_ip, wan_ip = wan_ip, secondwan_ip = secondwan_ip, wwan_ip = wwan_ip } end return M