--[[ @object-name: network @object-desc: 网络相关操作 --]] local net = require "oui.network" local uci = require "uci" local utils = require "oui.utils" local fs = require "oui.fs" local hardware = require "hardware" local M = {} local function dnsmasq_leasefile() local c = uci.cursor() local leasefile c:foreach("dhcp", "dnsmasq", function(s) leasefile = s.leasefile return false end) return leasefile end --[[ @method-type: call @method-name: get_dhcp_leases @method-desc: 获取 DHCP 租用列表 @out array leases DHCP 租用列表 @out string leases.ip IP 地址 @out string leases.mac MAC 地址 @out string leases.hostname 主机名 @out number leases.expires 剩余租约(秒) @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","network","get_dhcp_leases"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": { "entries": [{"ip":"192.168.8.2", "mac":"11:22:33:44:55:66","hostname":"test", "expires": 12}] }} --]] function M.get_dhcp_leases() local leases = {} local leasefile = dnsmasq_leasefile() local r, lines = pcall(io.lines, leasefile) if r then for line in lines do local ts, mac, addr, name = line:match("(%S+) +(%S+) +(%S+) +(%S+)") mac = mac and string.upper(mac) or "" leases[#leases + 1] = { ip = addr, mac = mac, hostname = name ~= "*" and name, expires = ts - os.time() } end end return { leases = leases } end --[[ @method-type: call @method-name: get_arp_list @method-desc: 获取 ARP 表 @out array entries ARP 列表 @out string entries.ip IP 地址 @out string entries.mac MAC 地址 @out string entries.device 网络接口 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","network","get_arp_list"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": { "entries": [{"ip":"192.168.8.2", "mac":"11:22:33:44:55:66","device":"eth0.2"},{"ip":"192.168.8.3", "mac":"11:22:33:44:55:26","device":"eth0.2"}] }} --]] function M.get_arp_list() local entries = {} local r, lines = pcall(io.lines, "/proc/net/arp") if r then local exists = {} for line in lines do local ipaddr, macaddr, device = line:match("(%S+) +%S+ +%S+ +(%S+) +%S+ +(%S+)") macaddr = macaddr and string.upper(macaddr) or "" if ipaddr ~= "IP" and macaddr ~= "00:00:00:00:00:00" and not exists[macaddr] then entries[#entries + 1] = { ip = ipaddr, mac = macaddr, device = device } exists[macaddr] = true end end end return {entries = entries} end --[[ @method-type: call @method-name: routes @method-desc: 获取活动的 IPv4 路由表 @out array routes IPv4 路由表 @out string entries.target 目标 @out string entries.nexthop 下一跳 @out number entries.metric 跃点数 @out string entries.device 网络接口 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","network","routes"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": { "entries": [{"target":"192.168.8.2", "nexthop":"192.168.113.1","metric":0,"device":"eth0.2"}] }} --]] function M.routes() local routes = {} for line in io.lines("/proc/net/route") do local field = {} for e in line:gmatch("%S+") do field[#field + 1] = e end if field[2] ~= "Destination" then routes[#routes + 1] = { target = net.hexaddr(field[2], field[8]), nexthop = net.hexaddr(field[3]), metric = tonumber(field[7]), device = field[1] } end end return {routes = routes} end --[[ @method-type: call @method-name: routes6 @method-desc: 获取活动的 IPv6 路由表 @out array routes IPv4 路由表 @out string entries.target 目标 @out string entries.source 源 @out string entries.nexthop 下一跳 @out number entries.metric 跃点数 @out string entries.device 网络接口 @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","network","routes6"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": { "entries": [{"target":"::/0", "source":"fd94:ba1d:265d:8::/62", "nexthop":"fe80::5054:ff:fe00:8145","metric":512,"device":"eth0.2"}] }} --]] function M.routes6() local routes = {} for line in io.lines("/proc/net/ipv6_route") do local field = {} for e in line:gmatch("%S+") do field[#field + 1] = e end routes[#routes + 1] = { target = net.hex6addr(field[1], field[2]), source = net.hex6addr(field[3], field[4]), nexthop = net.hex6addr(field[5]), metric = tonumber(field[6], 16), device = field[10] } end return {routes = routes} end function M.switch_info(msg) local info = {} local f = io.popen("swconfig dev " .. msg.switch .. " help") if f then local line = f:read("*l") local model, num_ports, cpu_port, num_vlans = line:match("%((%S+)%), ports: (%d+) %(cpu @ (%d+)%), vlans: (%d+)") info.model = model info.num_ports = num_ports info.cpu_port = cpu_port info.num_vlans = num_vlans local attributes for line in f:lines() do local typ = line:match("%-%-(%S+)") if typ then info[typ] = {} attributes = info[typ] else local typ, name, description = line:match("%((%a+)%): (%S+) %((.+)%)$") if typ then attributes[#attributes + 1] = { type = typ, name = name, description = description } end end end f:close() end return {info = info} end function M.switch_status(msg) local ports = {} local f = io.popen("swconfig dev " .. msg.switch .. " show") if f then for line in f:lines() do if line:match("link") then local up = false local speed = 0 local duplex = false local txflow = false local rxflow = false local aneg = false for field in line:gmatch("%S+") do if field:match("link:up") then up = true elseif field:match("speed:") then speed = tonumber(field:match("speed:(%d+)")) elseif field == "txflow" then txflow = true elseif field == "rxflow" then rxflow = true elseif field == "full-duplex" then duplex = true elseif field == "auto" then aneg = true end end ports[#ports + 1] = { link = up, rx_flow_control = rxflow, tx_flow_control = txflow, full_duplex = duplex, auto_negotiation = aneg, speed = speed } end end f:close() end return {ports = ports} end --[[ @method-type: call @method-name: check_wan_cable @method-desc: 获取 cable wan 是否连接 ,是否启用 mac克隆 @out bool cable_enabled 查看wan口是否有网线连接 [true 为 有网线连接 , false 为 没有网线连接]. @out bool macclone_enabled 是否启用mac克隆 [true 为启用 , false为不启用]. @out number ?err_code Error code,-1:获取信息失败 cable not in wan. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","network","check_wan_cable",{}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"cable_enabled": true, "macclone_enabled": false}} --]] function M.check_wan_cable() local s = hardware.platform_get_wan_status() return { cable_enabled = s.cable_enabled , macclone_enabled = s.macclone_enabled } end --[[ @method-type: call @method-name: get_hwnat_config @method-desc: 获取 hwnat 状态 @out bool enable 是否已开启 hwnat @in-example: {"jsonrpc":"2.0","method":"call","params":["","network","get_hwnat_config",{}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"enable": true}} --]] function M.get_hwnat_config(param) local enable local c = uci.cursor() if fs.access("/etc/config/mtkhnat") then enable = c:get("mtkhnat", "global", "enable") == "1" else enable = c:get("firewall", "@defaults[0]", "flow_offloading_hw") == "1" end return { enable = enable } end --[[ @method-type: call @method-name: set_hwnat_config @method-desc: 开启或者关闭 hwnat @out bool enable 是否开启 hwnat @in-example: {"jsonrpc":"2.0","method":"call","params":["","network","set_hwnat_config",{"enable": true}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_hwnat_config(param) local c = uci.cursor() local enable = param.enable local mtk_wifi = false if fs.access("/etc/config/mtkhnat") then c:set("mtkhnat", "global", "enable", enable and 1 or 0) c:commit("mtkhnat") local f=io.open("/lib/firmware/e2p","r") if f~=nil then io.close(f) mtk_wifi = true end ngx.pipe.spawn({"/etc/init.d/mtkhnat", "start"}) if mtk_wifi then utils.writefile("/sys/kernel/debug/hnat/hook_toggle", enable and 1 or 0) end else c:set("firewall", "@defaults[0]", "flow_offloading", enable and 1 or 0) c:set("firewall", "@defaults[0]", "flow_offloading_hw", enable and 1 or 0) c:commit("firewall") ngx.pipe.spawn({"/etc/init.d/firewall", "restart"}) end end --[[ @method-type: call @method-name: get_netnat_config @method-desc: 获取 netnat 状态 @out bool enable 是否已开启 netnat @out bool actype 加速类型 @out bool list 加速支持列表 0:Software and Hardware Acceleration;1:Hardware Acceleration;2:Software Acceleration @in-example: {"jsonrpc":"2.0","method":"call","params":["","network","get_netnat_config",{}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"enable": true}} --]] function M.get_netnat_config(param) local enable local actype local c = uci.cursor() if fs.access("/etc/config/mtkhnat") then enable = c:get("mtkhnat", "global", "enable") == "1" else enable = c:get("firewall", "@defaults[0]", "flow_offloading_hw") == "1" end if fs.access("/etc/init.d/shortcut-fe") then local var = c:get("mtkhnat", "global", "actype") if not var then actype = 0 else actype = tonumber(var) end return { enable = enable, actype = actype, list = {0,1,2}} end return { enable = enable, actype = 1, list = {1}} end --[[ @method-type: call @method-name: set_netnat_config @method-desc: 开启或者关闭 netnat @in bool enable 是否开启 netnat @in bool actype 加速类型 @in-example: {"jsonrpc":"2.0","method":"call","params":["","network","set_netnat_config",{"enable": true}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_netnat_config(param) local c = uci.cursor() local enable = param.enable local actype = param.actype local mtk_wifi = false if fs.access("/etc/config/mtkhnat") then c:set("mtkhnat", "global", "enable", enable and 1 or 0) c:set("mtkhnat", "global", "actype", actype) c:commit("mtkhnat") local f=io.open("/lib/firmware/e2p","r") if f~=nil then io.close(f) mtk_wifi = true end ngx.pipe.spawn({"/etc/init.d/mtkhnat", "start"}) if mtk_wifi then utils.writefile("/sys/kernel/debug/hnat/hook_toggle", enable and 1 or 0) end else c:set("firewall", "@defaults[0]", "flow_offloading", enable and 1 or 0) c:set("firewall", "@defaults[0]", "flow_offloading_hw", enable and 1 or 0) c:commit("firewall") ngx.pipe.spawn({"/etc/init.d/firewall", "restart"}) end end return M