#!/usr/bin/env lua local posix = require "Posix" local json = require "json" local ubus = require "ubus" local now_time local next_time local status local conn local cursor local cfg = { ['stat_file'] = "/tmp/vpn.stat.msg", ['stat_file_last'] = "/tmp/vpn.stat.msg.last", ['time_step'] = 1, ['pptp_mode'] = { "refuse-eap\nrefuse-pap\nrefuse-chap\nrefuse-mschap\nmppe required,no40,no56,stateless", "", "refuse-pap\nrefuse-chap\nrefuse-mschap\nrefuse-mschap-v2", "refuse-eap\nrefuse-chap\nrefuse-mschap\nrefuse-mschap-v2", "refuse-eap\nrefuse-pap\nrefuse-mschap\nrefuse-mschap-v2", "refuse-eap\nrefuse-pap\nrefuse-chap\nrefuse-mschap-v2\nmppe required,no40,no56,stateless" }, ['options_pptp_filename'] = "/etc/ppp/options.pptp", ['debug'] = 0, ['daemon'] = 1, ['max_redail'] = 2 } local g = {} function dlog(fmt, ...) if (cfg.debug == 1) then posix.syslog(posix.LOG_DEBUG, string.format(fmt, unpack(arg))) elseif (cfg.debug == 2) then print(string.format(fmt, unpack(arg))) end end function ilog(fmt, ...) if (cfg.debug == 2) then print(string.format(fmt, unpack(arg))) else posix.syslog(posix.LOG_INFO, string.format(fmt, unpack(arg))) end end function elog(fmt, ...) if (cfg.debug == 2) then print(string.format(fmt, unpack(arg))) else posix.syslog(posix.LOG_ERR, string.format(fmt, unpack(arg))) end end function check_vpn() g.status = conn:call("network.interface", "status", {['interface']="vpn"}) if not g.status.autostart then ilog("autostart=false exit") os.exit(1) end if g.action == "up" and g.status.up then ilog("action=up up=true exit") os.exit(1) end end function get_last_msg() local code = 0 local msg = '' local f = io.open(cfg.stat_file_last) if f == nil then return nil end local line = f:read("*line") while line do _, _, code, msg = string.find(line, "^(%d+) (.*)$") line = f:read("*line") end f:close() if code then dlog("get_last_msg code[%d] msg[%s]", tonumber(code), msg) return {code = tonumber(code) or nil, msg = msg} else return nil end end function init() g.proc = arg[0] g.action = arg[1] if not g.action then print(string.format("usage: %s ", g.proc)) os.exit(1) end if (g.action == "up" or g.action == "down") and cfg.daemon == 1 then posix.daemonize() end posix.openlog(g.proc, "cp", posix.LOG_LOCAL7) conn = ubus.connect() cursor = require("luci.model.uci").cursor() g.status = conn:call("network.interface", "status", {['interface']="vpn"}) if g.status == nil then conn:call("network", "reload", {}) dlog("ubus call network reload") posix.sleep(1) g.status = conn:call("network.interface", "status", {['interface']="vpn"}) end if g.status == nil then elog("network.interface.vpn does not exist") os.exit(1) end g.status.proto = cursor:get("network","vpn","proto") if g.status.proto ~= "pptp" and g.status.proto ~= "l2tp" then elog(string.format("error vpn proto [%s], pptp|l2tp", g.status.proto)) os.exit(1) end g.status.auth = cursor:get("network","vpn","auth") g.status.auto = cursor:get("network","vpn","auto") g.status.server = cursor:get("network","vpn","server") g.pptp_index = 1 g.pptp_redail = 0 g.l2tp_redail = 0 g.max_redail = cfg.max_redail if g.action == "up" and g.status.proto == "pptp" then if g.status.auth == nil or g.status.auth == "auto" then g.pptp_mode = 0 g.max_redail = cfg.max_redail * 6 elseif g.status.auth == "mschap-v2" then g.pptp_mode = 1 elseif g.status.auth == "all" then g.pptp_mode = 2 elseif g.status.auth == "eap" then g.pptp_mode = 3 elseif g.status.auth == "pap" then g.pptp_mode = 4 elseif g.status.auth == "chap" then g.pptp_mode = 5 elseif g.status.auth == "mschap" then g.pptp_mode = 6 else g.pptp_mode = 0 g.max_redail = cfg.max_redail * 6 end end end function check_redial() while posix.stat(cfg.stat_file) == nil do check_vpn() dlog("wait %s", cfg.stat_file) posix.sleep(cfg.time_step) end dlog("rename %s -> %s", cfg.stat_file, cfg.stat_file_last) os.rename(cfg.stat_file, cfg.stat_file_last) local m = get_last_msg() if m ~= nil and m.code == 0 then ilog("succeeded") return false end check_vpn() if g.status.proto == "pptp" then g.pptp_redail = g.pptp_redail + 1 if g.pptp_redail <= g.max_redail then dlog("vpn pptp redial[%d]", g.pptp_redail) return true else os.execute("ifdown vpn") elog("vpn pptp max redial[%d] ifdown vpn and exit", g.pptp_redail) return false end elseif g.status.proto == "l2tp" then g.l2tp_redail = g.l2tp_redail + 1 if g.l2tp_redail <= g.max_redail then dlog("vpn l2tp redial[%d]", g.l2tp_redail) return true else os.execute("ifdown vpn") elog("vpn l2tp max redial[%d] ifdown vpn and exit", g.l2tp_redail) return false end else elog("error vpn proto") os.execute("ifdown vpn") return false end end function update_options_pptp(index) if cfg.pptp_mode[index] then local f = assert(io.open(cfg.options_pptp_filename,"w")) f:write(string.format("noipdefault\nnoauth\nnobsdcomp\nnodeflate\n".. "idle 0\n%s\nmaxfail 0\n", cfg.pptp_mode[index])) f:close() end end function check_hostname() if posix.gethostbyname(g.status.server) == nil then local file,err = io.open( cfg.stat_file_last, "wb" ) if err then elog(string.format("open %s error"), cfg.stat_file_last) return -1 end elog(string.format("701 Host %s not found\n", g.status.server)) file:write(string.format("701 Host %s not found\n", g.status.server)) file:close() return -1 end return 0 end --------------------------------------------------------------------- local status, err = pcall( function () init() if g.action == "up" then if g.status.autostart then elog("already start, %s down first", g.proc) os.exit(1) end if check_hostname() ~= 0 then os.exit(1) end if g.status.proto == "pptp" then update_options_pptp(g.pptp_mode == 0 and 1 or g.pptp_mode) end dlog("rm %s", cfg.stat_file) os.remove(cfg.stat_file) dlog("ifup vpn") os.execute("ifup vpn") while check_redial() do if g.status.proto == "pptp" and g.pptp_mode == 0 then if g.pptp_redail % cfg.max_redail == 0 then update_options_pptp(g.pptp_redail / cfg.max_redail + 1) os.execute("ifup vpn") dlog("options.pptp -> %d", g.pptp_index) end end end os.exit(0) elseif g.action == "down" then --[[ local wan = conn:call("network.interface", "status", {['interface']="wan"}) for k,v in pairs(wan.route) do if v.target == "0.0.0.0" then local command = string.format("route -n | awk '{print $1\" \"$2}' | grep '0.0.0.0 %s' | wc -l", v.nexthop):q local pp = io.popen(command) local data = tonumber(pp:read("*a")) pp:close() if data == 0 then os.execute(string.format("route add -net 0.0.0.0 netmask 0.0.0.0 gw %s", v.nexthop)) end ilog("ifdown vpn and default gw: %s", v.nexthop) end end ]] os.execute("ifdown vpn") os.exit(0) elseif g.action == "status" then status = conn:call("network.interface", "status", {['interface']="vpn"}) status.stat = get_last_msg() status.auto = cursor:get("network","vpn","auto") print(json.encode(status)) os.exit(0) else print(string.format("usage: %s ", arg[0])) os.exit(1) end end ) if not status then elog(err) end