--[[ @object-name: lan @object-desc: This is the API related to wired Internet. --]] local lnet = require "oui.network" local ubus = require "oui.ubus" local uci = require "uci" local rpc = require "oui.rpc" local M = {} local ERR_PARAMETER = -1 -- 参数错误 local ERR_IP_FORMAT = -2 -- ip地址格式错误 local ERR_NO_LAN = -5 -- lan没起来 local WAN_IP_CONFLICT = -7 -- 与 wan ip 冲突 local LAN_IP_CONFLICT = -8 -- 与 lan ip 冲突 local GUEST_IP_CONFLICT = -9 -- 与 guest ip 冲突 --[[ @method-name: get_config_list @method-desc: 获取 lan 或 guest ip 信息 @out array interfaces 接口信息数组 @out string interfaces.interface 接口名[ lan | guest ] , 表明当前信息是 lan ip 信息还是 guest ip 信息 @out string interfaces.ip 接口的 ip. @out string interfaces.gateway 接口的 网关. @out string interfaces.netmask 接口的 子网掩码. @out string interfaces.start 接口的 起始IP地址. @out string interfaces.end 接口的 结束IP地址. @out number ?err_code Error code,-1:没有找到参数,-4:uci 初始化错误,-5:没有局域网接口. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","get_config_list",{}],"id":1} @out-example: {"jsonrpc":"2.0","id":1,"result":{"interfaces":[{"interface":"lan","gateway":"192.168.8.100","ip":"192.168.8.1","protocol":"static","netmask":"255.255.255.0","start":"192.168.8.100","end":"192.168.8.249"},{"interface":"guest","ip":"192.168.9.1","protocol":"static","netmask":"255.255.255.0","start":"192.168.9.100","end":"192.168.9.249"}]}} --]] function M.get_config_list(params) local c = uci.cursor() local s = ubus.call("network.interface.lan", "status") if not s or not s.up then return { err_code = ERR_NO_LAN, err_msg = "no lan interface" } end local function get_info(iface) local ip = c:get("network", iface, "ipaddr") local netmask = c:get("network", iface, "netmask") local start = c:get("dhcp", iface, "start") local limit = c:get("dhcp", iface, "limit") local info = lnet.ipcalc({ip, netmask}, start, limit) if iface == "lan" then local gateway = "" local dhcp_gateway = c:get("dhcp", "lan", "dhcp_option") if dhcp_gateway then for _, v in pairs(dhcp_gateway) do gateway = v:match("(%d+%.%d+%.%d+%.%d+)") end end return { interface = iface, gateway = gateway, ip = ip, netmask = netmask, start = info.start, ["end"] = info["end"] } else return { interface = iface, ip = ip, netmask = netmask, start = info.start, ["end"] = info["end"] } end end local interfaces = { get_info("lan") } if c:get("network", "guest") then interfaces[#interfaces + 1] = get_info("guest") end return { interfaces = interfaces } end --[[ @method-name: set_config @method-desc: 设置 局域网的 ip地址。 @in string interface 接口名[ lan | guest ] , 表明当前是对 lan ip 进行设置 还是 对 guest ip 进行设置. @in string ip 设置 新的 IP. @in string gateway 设置 新的 网关. @in string start 设置 新的 起始IP地址. @in string end 设置 新的 结束IP地址. @in string netmask 设置 新的 子网掩码. @out number ?err_code Error code,-1:没有找到参数,-2:ip 格式错误,-4:uci 初始化错误,-7:ip 冲突. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","set_config",{"interface":"guest","ip":"192.168.9.1","start":"192.168.9.101","end":"192.168.9.245","netmask":"255.255.255.0"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {}} --]] function M.set_config(params) local interface = params.interface local ip = params.ip local netmask = params.netmask local start_addr = params.start local end_addr = params["end"] local gateway = params.gateway if params.interface ~= "lan" and params.interface ~= "guest" then return { err_code = ERR_PARAMETER, err_msg = "invalid interface" } end if type(ip) ~= "string" or type(netmask) ~= "string" or type(start_addr) ~= "string" or type(end_addr) ~= "string" then return { err_code = ERR_IP_FORMAT, err_msg = "invalid interface" } end local c = uci.cursor() local s = ubus.call("network.interface.wan", "status") if s and 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) local lan_info = lnet.ipcalc({ip, netmask}, start_addr, end_addr) if wan_info.network == lan_info.network then return { err_code = WAN_IP_CONFLICT, err_msg = "conflict with wan ip" } end end if params.interface == "lan" then local guestip = c:get("network","guest", "ipaddr") local guestmask = c:get("network", "guest", "netmask") if guestip and guestmask then local guest_info = lnet.ipcalc({guestip, guestmask}, start_addr, end_addr) local lan_info = lnet.ipcalc({ip, netmask}, start_addr, end_addr) if guest_info.network == lan_info.network then return { err_code = GUEST_IP_CONFLICT, err_msg = "conflict with guest ip" } end end else local lanip = c:get("network","lan", "ipaddr") local lanmask = c:get("network", "lan", "netmask") local lan_info = lnet.ipcalc({lanip, lanmask}, start_addr, end_addr) local guest_info = lnet.ipcalc({ip, netmask}, start_addr, end_addr) if guest_info.network == lan_info.network then return { err_code = LAN_IP_CONFLICT, err_msg = "conflict with lan ip" } end end local function set_iface(name) local info = lnet.ipcalc({ip, netmask}, start_addr, end_addr) c:set("network", name, "ipaddr", ip) c:set("dhcp", "localhost", "ip", ip) c:set("network", name, "netmask", netmask) c:set("dhcp", name, "start", info.start) c:set("dhcp", name, "limit", info.limit) c:set("firewall", "dns_over_" .. name, "dest_ip", ip) if name == "lan" then c:set("dhcp", "@domain[0]", "ip", ip) if gateway == "" then c:delete("dhcp", "lan", "dhcp_option") else local gateway_info = "3,"..gateway c:set("dhcp", "lan", "dhcp_option", {gateway_info}) end end end set_iface(interface) c:commit("network"); c:commit("dhcp"); c:commit("firewall"); ngx.timer.at(1, function() ubus.call("network", "reload") ngx.sleep(2.0) ngx.pipe.spawn({"/etc/init.d/firewall", "reload"}):wait() ngx.pipe.spawn({"/etc/init.d/dnsmasq", "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 ngx.pipe.spawn({"/sbin/wifi"}) ngx.pipe.spawn({"/sbin/swconfig", "dev", "switch0", "set", "linkdown", "1"}):wait() ngx.pipe.spawn({"/sbin/swconfig", "dev", "switch0", "set", "linkdown", "0"}):wait() ngx.pipe.spawn({"/sbin/swconfig", "dev", "switch0", "set", "reset", "1"}):wait() ngx.pipe.spawn({"/sbin/swconfig", "dev", "switch0", "set", "apply", "1"}):wait() end) end local function valid_mac(mac) return type(mac) == "string" and mac:match("[%x:]+") == mac and #mac == 17 end local function valid_hostname(name) if name == "" then return true end if type(name) ~= "string" or #name > 253 then return false end if name:match("[%w_]+") == name then return true end return name:match("^[%w_][%w%-%._]*%w+$") and name:match("[^%d%.]") end --[[ @method-name: add_static_bind @method-desc: 设置静态ip绑定 @in string mac 设置的静态ip绑定条目的mac地址. @in string ip 设置的静态ip绑定条目的ip地址. @in string ?name 要绑定静态ip的设备的主机名.[可选参数,没有值时默认是 "" ] @out number ?err_code Error code,-1:没有找到参数,-3:mac地址格式错误,-4:uci 初始化错误 @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","add_static_bind",{"name":"DESKTOP-HO0T5C1","mac":"58:41:20:0b:9b:b1","ip":"192.168.8.162"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {}} --]] function M.add_static_bind(params) local mac = params.mac local ip = params.ip local name = params.name if not valid_mac(mac) or type(ip) ~= "string" then return rpc.ERROR_CODE_INVALID_PARAMS end if name and not valid_hostname(name) then return rpc.ERROR_CODE_INVALID_PARAMS end local c = uci.cursor() if not name or name == "" then name = "" end c:foreach("dhcp", "host", function(s) if s.mac == mac then c:delete("dhcp", s[".name"]) end end) local sid = c:add("dhcp", "host") c:set("dhcp", sid, "mac", mac) c:set("dhcp", sid, "ip", ip) c:set("dhcp", sid, "name", name) c:commit("dhcp") ngx.pipe.spawn({"/etc/init.d/dnsmasq", "restart"}) end --[[ @method-name: set_static_bind @method-desc: 更改静态ip绑定 @in string mac 要更改的静态ip绑定条目的mac地址.[根据mac寻找对应的静态ip绑定条目] @in string ip 要更改的静态ip绑定条目的ip地址.[设置对应mac的静态ip绑定条目的ip] @in string name 要更改的静态ip绑定条目的名称.[设置对应mac的静态ip绑定条目的名称] @out number ?err_code Error code,-4:uci 初始化错误. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","set_static_bind",{"mac":"58:41:20:0b:9b:b1","ip":"192.168.8.80","name":"pc1"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {}} --]] function M.set_static_bind(params) return M.add_static_bind(params) end --[[ @method-name: get_static_bind_list @method-desc: 获取当前所有静态ip绑定条目的信息列表 @out array static_bind_list 获取所有静态ip绑定条目的列表. @out string static_bind_list.mac 静态ip绑定条目的mac地址. @out string static_bind_list.ip 静态ip绑定条目的ip地址. @out string ?static_bind_list.name 要绑定静态ip的设备的主机名.[没有值时默认是 "" ] @out number ?err_code Error code,-4:uci 初始化错误. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","get_static_bind_list",{}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"static_bind_list": [{"name": "DESKTOP-HO0T5C1", "mac": "58:41:20:0b:9b:b1", "ip": "192.168.8.162"}, {"name": "DESKTOP-HO0T5C3", "mac": "58:41:20:0b:9b:b3", "ip": "192.168.8.163"}]}} --]] function M.get_static_bind_list() local res = {} local c = uci.cursor() c:foreach("dhcp", "host", function(s) res[#res + 1] = { mac = s.mac, ip = s.ip, name = s.name } end) return { static_bind_list = res } end --[[ @method-name: remove_static_bind @method-desc: 删除一条静态ip绑定或删除全部静态ip绑定 @in number mode mode设置要删除静态ip绑定条目的模式,mode为0是删除单个静态ip绑定(需要传入mac参数),mode为1是删除所有静态ip绑定(不传入mac参数或mac参数为空). @in string mac 要删除的静态ip绑定条目的mac地址,根据mac地址找到对应条目进行删除.[mode为0时必须传入mac地址,mode为1时mac地址为可选参数(mac的值为空或者不传mac参数)] @out number ?err_code Error code,-4:uci 初始化错误. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","lan","remove_static_bind",{"mode":0,"mac":"58:41:20:0b:9b:b1"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {}} --]] function M.remove_static_bind(params) local c = uci.cursor() local mode = params.mode c:foreach("dhcp", "host", function(s) if mode == 1 or s.mac == params.mac then c:delete("dhcp", s[".name"]) end end) c:commit("dhcp") ngx.pipe.spawn({"/etc/init.d/dnsmasq", "restart"}) end return M