--[[ @object-name: netmode @object-desc: 网络模式配置 --]] local M = {} local lnet = require "oui.network" local utils = require "oui.utils" local cjson = require "cjson" local rpc = require "oui.rpc" local ubus = require "oui.ubus" local uci = require "uci" local fs = require 'oui.fs' local hardware = require "hardware" local lfactory = require "lfactory" local function use_device_describe_interface() local c = uci.cursor() if c:get("network", "lan", "device") == nil then return false else return true end end local function lan_in_mwan3(c, add) if add then c:set("mwan3", "lan", "interface") for k, v in pairs(c:get_all("mwan3", "wan")) do if not k:match("^%.") then c:set("mwan3", "lan", k, v) end end else c:delete("mwan3", "lan") end end local function set_bridge_wifi(c) c:foreach("wireless", "wifi-iface", function(s) if s.mode == "ap" and s.guest == "1" then c:set("wireless", s[".name"], "disabled", 1) end end) c:set("dhcp", "lan", "ignore", 1) end local function turn_off_vpn() local c = uci.cursor() if c:get("network", "ovpnclient", "disabled") == "0" then ngx.pipe.spawn({"/etc/gl-switch.d/openvpn.sh", "off"}):wait() end if c:get("network", "wgclient", "disabled") == "0" then ngx.pipe.spawn({"/etc/gl-switch.d/wireguard.sh", "off"}):wait() end end local function apply(mode) local c = uci.cursor() if c:get("wireless", "@wifi-device[0]", "type") == "mtk" then local iwpriv = require "iwpriv" iwpriv.set("ra0", "DisConnectAllSta", 1) iwpriv.set("rax0", "DisConnectAllSta", 1) elseif c:get("wireless", "@wifi-device[0]", "type") == "mac80211" then ngx.pipe.spawn({"/sbin/wifi"}) end ngx.pipe.spawn({"/etc/init.d/gl_ipv6", "reload"}):wait() for _, config in ipairs({"glconfig", "mwan3"}) do ubus.call("service", "event", { type = "config.change", data = { package = config } }) end ngx.pipe.spawn({"/etc/init.d/dnsmasq", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/relayd", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/sysctl", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/edgerouter", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/firewall", "restart"}):wait() ngx.pipe.spawn({"/etc/init.d/mwan3", "reload"}):wait() local device = c:get("network", "lan", "device") if device then local ports c:foreach("network", "device", function(s) if s.name == device then ports = s.ports return false end end) for _, port in ipairs(ports or {}) do ngx.pipe.spawn({"/sbin/ip", "link", "set", port, "down"}):wait() end ngx.sleep(1.0) for _, port in ipairs(ports or {}) do ngx.pipe.spawn({"/sbin/ip", "link", "set", port, "up"}):wait() end end if mode == "router" then ubus.call("network", "restart", {}) else ubus.call("network", "reload", {}) end hardware.platform_switch_restart() end --[[ @method-type: call @method-name: get_mode @method-desc: Query the current network mode @out string mode current network mode @out string ?mask When switching to passthrough mode, the returned mask @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","netmode","get_mode"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"mode": "router"}} --]] M.get_mode = function() local c = uci.cursor() local mode = c:get("glconfig", "general", "mode") if mode == "passthrough" then local mask = c:get("passthrough", "passthrough", "mask") return { mode = mode, mask = mask} else return { mode = mode or "router" } end end local function connect_ap(params) ubus.call("repeater", "connect", params) for _ = 1, 30 do local s = ubus.call("repeater", "status", {}) if s.state == 2 then return s.ssid == params.ssid end ngx.sleep(1) end return false end local function save_repeater_config(mode) local c = uci.cursor() if mode == "ap" then c:set("repeater", "@main[0]", "auto", 0) ubus.call("repeater", "disconnect", {}) elseif mode == "wds" or mode == "relay" then ubus.call("repeater", "save_config", {}) c:set("repeater", "@main[0]", "auto", 0) else c:foreach("repeater", "network", function(s) if s.network == "lan" then c:set("repeater", s[".name"], "network", "") end end) ubus.call("repeater", "disconnect") end c:commit("repeater") ubus.call("repeater", "reload") end local function check_force_dns(c, mode) local lan = c:get("firewall", "dns_over_lan") local guest = c:get("firewall", "dns_over_lan") if mode == "router" then if lan then c:delete("firewall", "dns_over_lan", "enabled") end if guest then c:delete("firewall", "dns_over_guest", "enabled") end else if lan then c:set("firewall", "dns_over_lan", "enabled", 0) end if guest then c:set("firewall", "dns_over_guest", "enabled", 0) end end c:commit("firewall") ngx.pipe.spawn({"/etc/init.d/firewall", "restart"}):wait() end --[[ secondwan开启的情况下 router:将lan device restore to secondwan device other: 将secondwan device change to lan device ]]-- local function secondwan_config(c, mode) local device = lfactory.get_secondwan_port() or "" local lan2wan = c:get("glconfig", "general", "lan2wan") or "0" if lan2wan == "1" and device ~= "" then if mode == "router" then c:delete("network", "secondwan", "disabled") c:foreach("network", "device", function(s) if s.name == "br-lan" and s.ports ~= nil then for k ,v in pairs(s.ports) do if v == device then table.remove(s.ports, k) end end if #s.ports == 0 then c:delete("network", s[".name"], "ports") else c:set("network", s[".name"], "ports", s.ports) end end end) else c:set("network", "secondwan", "disabled", 1) c:foreach("network", "device", function(s) if s.name == "br-lan" then if s.ports == nil then s.ports = {} end table.insert(s.ports,device) c:set("network", s[".name"], "ports", s.ports) end end) end end end local function get_modem_name() local c = uci.cursor() local modem_name = {} c:foreach("network", "interface", function(s) local str = string.sub(s['.name'], 1, 6) if str == "modem_" and c:get("network", s['.name'], "device") then modem_name[#modem_name+1] = s['.name'] end end) return modem_name end --[[ @method-type: call @method-name: set_mode @method-desc: set network mode @in string mode Network mode, optional values: ap, router, relay, wds, passthrough @in string ?ssid SSID (required for relay and wds modes) @in string ?identity EAP identity (required if WPA-Enterprise encryption) @in string ?key password (required for relay and wds modes) @in string ?mask Client IP mask bits (required for passthrough mode) @out string ?lanip When switching to routing mode, the returned lan ip @out number ?err_code error code: one: Connection failed, two: get IP failed, three: IP network segment conflict (the LAN interface must be set with an address that is not on the same network segment as the WiFi you want to connect to) @out number ?err_msg error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","netmode","set_mode",{"mode":"router"}]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] M.set_mode = function(params) local c = uci.cursor() local mode = params.mode if type(mode) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "mode required" end local modem = get_modem_name() if #modem ~= 0 and mode ~= "router" and mode ~= "passthrough" then for _, v in pairs(modem) do c:set("network", v, "disabled", "1") end end ngx.pipe.spawn({"/etc/init.d/dnsmasq", "stop"}):wait() if mode == "ap" then set_bridge_wifi(c) turn_off_vpn() c:set("network", "wan", "disabled", 1) c:set("wireless", "guest2g", "disabled", 1) c:set("wireless", "guest5g", "disabled", 1) c:delete("network", "stabridge") if use_device_describe_interface() == true then local wan_ifname = c:get("network", "wan", "device") c:foreach("network", "device", function(s) if s.name == "br-lan" then if s.ports == nil then s.ports = {} end table.insert(s.ports,wan_ifname) c:set("network", s[".name"], "ports", s.ports) end end) else local lan_ifname = c:get("network", "lan", "ifname") local wan_ifname = c:get("network", "wan", "ifname") c:set("network", "lan", "ifname", lan_ifname .. " " .. wan_ifname) end c:set("network", "lan", "proto", "dhcp") c:set("glconfig", "general", "mode", "ap") lan_in_mwan3(c, false) elseif mode == "router" or mode == "relay" then if mode == "relay" then if type(params.ssid) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "ssid required" end if not connect_ap(params) then return { err_code = 1, err_msg = "connect fail" } end local s = ubus.call("network.interface.lan", "status", {}) local lan_ip = s["ipv4-address"][1] local wlan_ip for _ = 1, 10 do s = ubus.call("network.interface.wwan", "status", {}) if s and s.up then local ip4 = s["ipv4-address"] if #ip4 > 0 then wlan_ip = ip4[1] end end ngx.sleep(1) end if not wlan_ip then return { err_code = 2, err_msg = "obtain IP fail" } end lan_ip = lnet.ipcalc(lan_ip.address .. "/" .. lan_ip.mask) wlan_ip = lnet.ipcalc(wlan_ip.address .. "/" .. wlan_ip.mask) if lan_ip.network == wlan_ip.network then return { err_code = 3, err_msg = "The LAN interface must be set in a different subnet than the Wi-Fi network you are connecting to." } end set_bridge_wifi(c) turn_off_vpn() c:set("network", "stabridge", "interface") c:set("network", "stabridge", "proto", "relay") c:set("network", "stabridge", "network", "lan wwan") else local passthrough = c:get("passthrough", "passthrough", "enable") if passthrough == "1" then c:set("passthrough", "passthrough", "enable", "0") c:set("glconfig", "general", "mode", mode) c:commit("passthrough") c:commit("glconfig") ubus.call("passthrough", "disable", {}) return end local board_data = cjson.decode(utils.readfile("/etc/board.json")) if use_device_describe_interface() == true then local wan_ifname = c:get("network", "wan", "device") c:foreach("network", "device", function(s) if s.name == "br-lan" and s.ports ~= nil then for k ,v in pairs(s.ports) do if v == wan_ifname then table.remove(s.ports, k) end end if #s.ports == 0 then c:delete("network", s[".name"], "ports") else c:set("network", s[".name"], "ports", s.ports) end end end) else c:set("network", "lan", "ifname", board_data.network.lan.ifname) end c:set("network", "lan", "proto", board_data.network.lan.protocol) c:set("network", "wan", "disabled", 0) c:delete("network", "stabridge") c:delete("wireless", "sta") c:set("dhcp", "lan", "ignore", 0) end c:set("glconfig", "general", "mode", mode) lan_in_mwan3(c, false) elseif mode == "wds" then if type(params.ssid) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "ssid required" end params.network = "lan" if not connect_ap(params) then return { err_code = 1, err_msg = "connect fail" } end set_bridge_wifi(c) turn_off_vpn() c:set("network", "lan", "proto", "dhcp") c:set("glconfig", "general", "mode", "wds") c:delete("network", "stabridge") lan_in_mwan3(c, true) elseif mode == "passthrough" then if type(params.mask) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS, "mask required" end if not fs.access("/etc/config/passthrough") then utils.writefile('/etc/config/passthrough', '') end c:set("glconfig", "general", "mode", "passthrough") c:set("passthrough", "passthrough", "service") c:set("passthrough", "passthrough", "mask", params.mask) c:set("passthrough", "passthrough", "enable", "1") c:commit("passthrough") c:commit("glconfig") ngx.pipe.spawn({"/etc/init.d/passthrough", "reload"}):wait() ngx.sleep(2.0) ubus.call("passthrough", "set", {}) return else return rpc.ERROR_CODE_INVALID_PARAMS, "invalid mode" end save_repeater_config(mode) check_force_dns(c, mode) secondwan_config(c, mode) if mode == "relay" then c:set("firewall", "forwarding_wan2lan", "forwarding") c:set("firewall", "forwarding_wan2lan", "src", "wan") c:set("firewall", "forwarding_wan2lan", "dest", "lan") else c:delete("firewall", "forwarding_wan2lan") end c:commit("dhcp") c:commit("mwan3") c:commit("firewall") c:commit("network") c:commit("wireless") c:commit("glconfig") fs.sync() ngx.timer.at(2, function() apply(mode) end) if mode == "router" then return { lanip = c:get("network", "lan", "ipaddr") } end end return M