--[[ @object-name: macclone @object-desc: macclone设置 --]] local utils = require "oui.utils" local rpc = require "oui.rpc" local uci = require "uci" local lfactory = require "lfactory" local fs = require "oui.fs" local ubus = require "oui.ubus" local M = {} local ERR_MAC = -1 -- mac地址错误 local MAC_CONFLICT = -2 -- mac地址冲突 --[[ @method-type: call @method-name: get_mac @method-desc: 获取mac的配置信息 @out number mode 0:出厂默认 1:克隆 2:手动设置 @out string mac 路由器WAN端口的MAC地址.[对应 Your Router (WAN)][Random] @out string factory_mac 路由器的出厂默认MAC地址.[对应 Factory Default][default] @out string remote_mac 连接到路由器的客户端的MAC地址.[对应 Your Current Client][clone] @out string repeater_mac 路由器的中继wifi的MAC地址. @out number ?err_code Error code,-1:获取信息失败. @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","id":1,"method":"call","params":["","macclone","get_mac"]} @out-example: {"jsonrpc": "2.0", "id": 1, "result": {"mac": "08:10:7B:B3:E8:92", "factory_mac": "94:83:C4:0C:6D:D6", "remote_mac": "94:83:C4:0C:6D:D6"}} --]] function M.get_mac() local c = uci.cursor() local mode = c:get("gl_macclone", "macclone", "mode") local mac = c:get("network", "wan_dev", "macaddr") if not mac then local device = c:get("network", "wan", "device") c:foreach("network", "device", function(s) if s.name == device then mac = s.macaddr return false end end) end if not mac then local device = lfactory.get_wan_port() or "" if device then mac = utils.readfile("/sys/class/net/" .. device .. "/address", "l") end end local remote_addr = ngx.var.remote_addr local remote_mac = "" for l in io.lines("/proc/net/arp") do if l:match(remote_addr) then remote_mac = l:match('%S+%s+%S+%s+%S+%s+(%S+)') break end end local factory_mac = lfactory.get_mac() or "" local repeater_mac = "" local res = ubus.call("repeater", "status") if res and res.state == 2 then local status = ubus.call("network.interface." .. (res.network or "wwan"), "status") if status or status.up then repeater_mac = utils.readfile("/sys/class/net/" .. status.device .. "/address", "l") end elseif res and res.state == 1 then local device = c:get("network", "wan", "device") c:foreach("network", "device", function(s) if s.name == device then repeater_mac = s.macaddr end end) end return { mode = tonumber(mode), mac = mac:upper(), factory_mac = factory_mac:upper(), remote_mac = remote_mac:upper(), repeater_mac = repeater_mac:upper() } end local function get_os_ver() if fs.access("/etc/os-release") then local osver = tonumber(utils.readfile("/etc/os-release"):match('VERSION_ID="(%d+)')) return osver end return 0 end local function set_wan_mac(c, mac) if get_os_ver() > 20 then local device = c:get("network", "wan", "device") local deviceFlag = 0 c:foreach("network", "device", function(s) if s.name == device then c:set("network", s[".name"], "macaddr", mac) deviceFlag = 1 return false end end) if deviceFlag == 0 then local dev = c:add("network", "device") c:set("network", dev, "name", device) c:set("network", dev, "macaddr", mac) return false end else c:set("network", "wan", "macaddr", mac) end end local function valid_mac(mac) if type(mac) ~= "string" or mac:match("[%x:]+") ~= mac or #mac ~= 17 then return false end local n = mac:sub(1, 2) return tonumber(n, 16) % 2 == 0 end --[[ @method-type: call @method-name: set_mac @method-desc: 获取mac的配置信息 @in string mode 模式设置 @in string mac 待克隆的MAC地址. @out number ?err_code Error code,-1:非法参数.-2:WAN口作为LAN使用时,克隆的mac地址不能和下及客户端的相同 @out string ?err_msg Error message @in-example: {"jsonrpc":"2.0","method":"call","params":["","macclone","set_mac",{"mac":"94:83:C4:0C:6D:D6"}],"id":1} @out-example: {"jsonrpc": "2.0", "id": 1, "result": null} --]] function M.set_mac(params) local mac = params.mac if not valid_mac(mac) then return { err_code = -ERR_MAC, err_msg = "The MAC address format is incorrect" } end local c = uci.cursor() c:set("gl_macclone", "macclone", "mode", params.mode) local wan2lan = c:get("glconfig", "general", "wan2lan") if wan2lan == "1" then local remote_addr = ngx.var.remote_addr local remote_mac = "" for l in io.lines("/proc/net/arp") do if l:match(remote_addr) then remote_mac = l:match('%S+%s+%S+%s+%S+%s+(%S+)'):upper() if remote_mac==mac then return { err_code = MAC_CONFLICT, err_msg = "the WAN port is used as a LAN,Mac address conflict" } end end end end local factory_mac = c:get("glconfig", "general", "factory_mac") or lfactory.get_mac() or "" factory_mac = factory_mac:upper() local mac = mac:upper() set_wan_mac(c, mac) if factory_mac == mac then c:delete("glconfig", "general", "macclone_addr") c:set("glconfig", "general", "macclone", "0") else c:set("glconfig", "general", "macclone_addr", mac) c:set("glconfig", "general", "macclone", "1") end c:commit("glconfig") c:commit("network") c:commit("gl_macclone") ubus.call("repeater", "macclone") ngx.timer.at(0.5, function() ngx.pipe.spawn({"/sbin/ifup","wan"}) end) end return M