diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua index b8e961432..0c33ccd90 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua @@ -303,7 +303,7 @@ end o = s:taboption("DNS", Button, "clear_ipset", translate("Clear IPSET"), translate("Try this feature if the rule modification does not take effect.")) o.inputstyle = "remove" function o.write(e, e) - luci.sys.call("/usr/share/" .. appname .. "/iptables.sh flush_ipset > /dev/null 2>&1 &") + luci.sys.call("[ -n \"$(nft list sets 2>/dev/null | grep \"gfwlist\")\" ] && /usr/share/" .. appname .. "/nftables.sh flush_nftset || /usr/share/" .. appname .. "/iptables.sh flush_ipset > /dev/null 2>&1 &") luci.http.redirect(api.url("log")) end diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua index 3858b016a..50e7c6d57 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua @@ -3,6 +3,8 @@ local appname = api.appname local fs = api.fs local has_v2ray = api.is_finded("v2ray") local has_xray = api.is_finded("xray") +local has_fw3 = api.is_finded("fw3") +local has_fw4 = api.is_finded("fw4") m = Map(appname) @@ -95,6 +97,16 @@ o.default = "1:65535" o:value("1:65535", translate("All")) o:value("53", "DNS") +---- Use nftables +o = s:option(ListValue, "use_nft", translate("Firewall tools")) +o.default = "0" +if has_fw3 then + o:value("0", "IPtables") +end +if has_fw4 then + o:value("1", "NFtables") +end + if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod | grep -i TPROXY >/dev/null") == 0) or (os.execute("lsmod | grep -i nft_redir >/dev/null") == 0 and os.execute("lsmod | grep -i nft_tproxy >/dev/null") == 0) then o = s:option(ListValue, "tcp_proxy_way", translate("TCP Proxy Way")) o.default = "redirect" diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua index ffc7c3bdb..e203a1843 100755 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/api/app.lua @@ -15,6 +15,8 @@ local require_dir = "luci.model.cbi.passwall.server.api." local ipt_bin = sys.exec("echo -n $(/usr/share/passwall/iptables.sh get_ipt_bin)") local ip6t_bin = sys.exec("echo -n $(/usr/share/passwall/iptables.sh get_ip6t_bin)") +local nft_flag = sys.exec("command -v fw4") and "1" or "0" + local function log(...) local f, err = io.open(LOG_APP_FILE, "a") if f and err == nil then @@ -47,6 +49,11 @@ end local function gen_include() cmd(string.format("echo '#!/bin/sh' > /tmp/etc/%s.include", CONFIG)) + if nft_flag == "1" then + cmd("echo \"\" > " .. CONFIG_PATH .. "/" .. CONFIG .. ".nft") + local nft_cmd="for chain in $(nft -a list chains |grep -E \"chain PSW-SERVER\" |awk -F ' ' '{print$2}'); do\n nft list chain inet fw4 ${chain} >> " .. CONFIG_PATH .. "/" .. CONFIG .. ".nft\n done" + cmd(nft_cmd) + end local function extract_rules(n, a) local _ipt = ipt_bin if n == "6" then @@ -59,15 +66,21 @@ local function gen_include() end local f, err = io.open("/tmp/etc/" .. CONFIG .. ".include", "a") if f and err == nil then - f:write(ipt_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n") - f:write(ipt_bin .. '-restore -n <<-EOT' .. "\n") - f:write(extract_rules("4", "filter") .. "\n") - f:write("EOT" .. "\n") - f:write(ip6t_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ip6t_bin .. '-restore -c' .. "\n") - f:write(ip6t_bin .. '-restore -n <<-EOT' .. "\n") - f:write(extract_rules("6", "filter") .. "\n") - f:write("EOT" .. "\n") - f:close() + if nft_flag == "0" then + f:write(ipt_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ipt_bin .. '-restore -c' .. "\n") + f:write(ipt_bin .. '-restore -n <<-EOT' .. "\n") + f:write(extract_rules("4", "filter") .. "\n") + f:write("EOT" .. "\n") + f:write(ip6t_bin .. '-save -c | grep -v "PSW-SERVER" | ' .. ip6t_bin .. '-restore -c' .. "\n") + f:write(ip6t_bin .. '-restore -n <<-EOT' .. "\n") + f:write(extract_rules("6", "filter") .. "\n") + f:write("EOT" .. "\n") + f:close() + else + f:write("nft -f " .. CONFIG_PATH .. "/" .. CONFIG .. ".nft\n") + f:write("nft insert rule inet fw4 input position 0 counter jump PSW-SERVER") + f:close() + end end end @@ -78,10 +91,15 @@ local function start() end cmd(string.format("mkdir -p %s %s", CONFIG_PATH, TMP_BIN_PATH)) cmd(string.format("touch %s", LOG_APP_FILE)) - ipt("-N PSW-SERVER") - ipt("-I INPUT -j PSW-SERVER") - ip6t("-N PSW-SERVER") - ip6t("-I INPUT -j PSW-SERVER") + if nft_flag == "0" then + ipt("-N PSW-SERVER") + ipt("-I INPUT -j PSW-SERVER") + ip6t("-N PSW-SERVER") + ip6t("-I INPUT -j PSW-SERVER") + else + cmd("nft add chain inet fw4 PSW-SERVER\n") + cmd("nft insert rule inet fw4 input position 0 counter jump PSW-SERVER") + end uci:foreach(CONFIG, "user", function(user) local id = user[".name"] local enable = user.enable @@ -168,12 +186,19 @@ local function start() local bind_local = user.bind_local or 0 if bind_local and tonumber(bind_local) ~= 1 then - ipt(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - ip6t(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - if udp_forward == 1 then - ipt(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - ip6t(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) - end + if nft_flag == "0" then + ipt(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) + ip6t(string.format('-A PSW-SERVER -p tcp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) + if udp_forward == 1 then + ipt(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) + ip6t(string.format('-A PSW-SERVER -p udp --dport %s -m comment --comment "%s" -j ACCEPT', port, remarks)) + end + else + cmd(string.format('nft add rule inet fw4 PSW-SERVER meta l4proto tcp tcp dport {%s} accept', port)) + if udp_forward == 1 then + cmd(string.format('nft add rule inet fw4 PSW-SERVER meta l4proto udp udp dport {%s} accept', port)) + end + end end end end) @@ -182,12 +207,19 @@ end local function stop() cmd(string.format("top -bn1 | grep -v 'grep' | grep '%s/' | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1", CONFIG_PATH)) - ipt("-D INPUT -j PSW-SERVER 2>/dev/null") - ipt("-F PSW-SERVER 2>/dev/null") - ipt("-X PSW-SERVER 2>/dev/null") - ip6t("-D INPUT -j PSW-SERVER 2>/dev/null") - ip6t("-F PSW-SERVER 2>/dev/null") - ip6t("-X PSW-SERVER 2>/dev/null") + if nft_flag == "0" then + ipt("-D INPUT -j PSW-SERVER 2>/dev/null") + ipt("-F PSW-SERVER 2>/dev/null") + ipt("-X PSW-SERVER 2>/dev/null") + ip6t("-D INPUT -j PSW-SERVER 2>/dev/null") + ip6t("-F PSW-SERVER 2>/dev/null") + ip6t("-X PSW-SERVER 2>/dev/null") + else + nft_cmd="handles=$(nft -a list chain inet fw4 input | grep -E \"PSW-SERVER\" | awk -F '# handle ' '{print$2}')\n for handle in $handles; do\n nft delete rule inet fw4 input handle ${handle} 2>/dev/null\n done" + cmd(nft_cmd) + cmd("nft flush chain inet fw4 PSW-SERVER 2>/dev/null") + cmd("nft delete chain inet fw4 PSW-SERVER 2>/dev/null") + end cmd(string.format("rm -rf %s %s /tmp/etc/%s.include", CONFIG_PATH, LOG_APP_FILE, CONFIG)) end diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index 3cc91228c..e0a858478 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -1114,6 +1114,9 @@ msgstr "节点数量" msgid "You can only set up a maximum of %s nodes for the time being, Used for access control." msgstr "目前最多只能设置%s个节点,用于给访问控制使用。" +msgid "Firewall tools" +msgstr "防火墙工具" + msgid "IPv6 TProxy" msgstr "IPv6透明代理(TProxy)" diff --git a/luci-app-passwall/root/usr/share/passwall/0_default_config b/luci-app-passwall/root/usr/share/passwall/0_default_config index 1e5dbf8f9..447ed4235 100644 --- a/luci-app-passwall/root/usr/share/passwall/0_default_config +++ b/luci-app-passwall/root/usr/share/passwall/0_default_config @@ -32,6 +32,7 @@ config global_forwarding option tcp_redir_ports '22,25,53,143,465,587,853,993,995,80,443' option udp_redir_ports '1:65535' option accept_icmp '0' + option use_nft '0' option tcp_proxy_way 'redirect' option ipv6_tproxy '0' option sniffing '1' diff --git a/luci-app-passwall/root/usr/share/passwall/app.sh b/luci-app-passwall/root/usr/share/passwall/app.sh index ab611dd7f..28c196ef4 100755 --- a/luci-app-passwall/root/usr/share/passwall/app.sh +++ b/luci-app-passwall/root/usr/share/passwall/app.sh @@ -1349,14 +1349,13 @@ start() { start_haproxy start_socks nftflag=0 + local use_nft=$(config_t_get global_forwarding use_nft 0) [ "$NO_PROXY" == 1 ] || { - if [ -n "$(command -v fw4)" ] && [ -z "$(dnsmasq --version | grep 'nftset')" ]; then - echolog "检测到fw4防火墙,但Dnsmasq软件包不满足nftables透明代理要求,如需使用请确保dnsmasq版本在2.87以上并开启nftset支持。" - fi - - if [ -n "$(command -v fw4)" ] && [ -n "$(dnsmasq --version | grep 'nftset')" ]; then - echolog "检测fw4防火墙,使用nftables进行透明代理,一些不支持nftables的组件如smartdns分流等将不可用。" + if [ "$use_nft" == 1 ] && [ -z "$(dnsmasq --version | grep 'Compile time options:.* nftset')" ]; then + echolog "Dnsmasq软件包不满足nftables透明代理要求,如需使用请确保dnsmasq版本在2.87以上并开启nftset支持。" + elif [ "$use_nft" == 1 ] && [ -n "$(dnsmasq --version | grep 'Compile time options:.* nftset')" ]; then + echolog "使用nftables进行透明代理,一些不支持nftables的组件如smartdns分流等将不可用。" nftflag=1 start_redir TCP start_redir UDP @@ -1379,7 +1378,8 @@ start() { stop() { clean_log - [ -n "$(command -v fw4)" ] && [ -n "$(dnsmasq --version | grep 'nftset')" ] && source $APP_PATH/nftables.sh stop || source $APP_PATH/iptables.sh stop + [ -n "$($(source $APP_PATH/iptables.sh get_ipt_bin) -t mangle -t nat -L -nv 2>/dev/null | grep "PSW")" ] && source $APP_PATH/iptables.sh stop + [ -n "$(nft list chains 2>/dev/null | grep "PSW")" ] && source $APP_PATH/nftables.sh stop delete_ip2route kill_all v2ray-plugin obfs-local pgrep -f "sleep.*(6s|9s|58s)" | xargs kill -9 >/dev/null 2>&1 diff --git a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua index e412fe5f7..62d42041a 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq_add.lua @@ -168,7 +168,7 @@ local dnsmasq_default_dns local cache_text = "" local subscribe_proxy=uci:get(appname, "@global_subscribe[0]", "subscribe_proxy") or "0" local new_rules = luci.sys.exec("echo -n $(find /usr/share/passwall/rules -type f | xargs md5sum)") -local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. REMOTE_FAKEDNS .. CHINADNS_DNS .. PROXY_MODE .. NO_PROXY_IPV6 .. subscribe_proxy .. new_rules +local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. REMOTE_FAKEDNS .. CHINADNS_DNS .. PROXY_MODE .. NO_PROXY_IPV6 .. subscribe_proxy .. new_rules .. NFTFLAG if fs.access(CACHE_TEXT_FILE) then for line in io.lines(CACHE_TEXT_FILE) do cache_text = line diff --git a/luci-app-passwall/root/usr/share/passwall/nftables.sh b/luci-app-passwall/root/usr/share/passwall/nftables.sh index 887842381..4ed637ae1 100755 --- a/luci-app-passwall/root/usr/share/passwall/nftables.sh +++ b/luci-app-passwall/root/usr/share/passwall/nftables.sh @@ -461,7 +461,7 @@ load_acl() { elif [ -n "$(echo ${i} | grep '^ipset:')" ]; then _ipset=$(echo ${i} | sed 's#ipset:##g') _ipt_source="ip daddr @${_ipset}" - msg="备注【$remarks】,IPset【${_ipset}】," + msg="备注【$remarks】,NFTset【${_ipset}】," elif [ -n "$(echo ${i} | grep '^ip:')" ]; then _ip=$(echo ${i} | sed 's#ip:##g') _ipt_source=$(factor ${_ip} "ip saddr") @@ -717,13 +717,13 @@ filter_haproxy() { local ip=$(get_host_ip ipv4 $(echo $item | awk -F ":" '{print $1}') 1) insert_nftset $NFTSET_VPSIPLIST $ip done - echolog "加入负载均衡的节点到ipset[$NFTSET_VPSIPLIST]直连完成" + echolog "加入负载均衡的节点到nftset[$NFTSET_VPSIPLIST]直连完成" } filter_vpsip() { insert_nftset $NFTSET_VPSIPLIST $(uci show $CONFIG | grep ".address=" | cut -d "'" -f 2 | grep -E "([0-9]{1,3}[\.]){3}[0-9]{1,3}" | sed -e "/^$/d" | sed -e 's/$/,/' ) insert_nftset $NFTSET_VPSIPLIST6 $(uci show $CONFIG | grep ".address=" | cut -d "'" -f 2 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | sed -e "/^$/d" | sed -e 's/$/,/' ) - echolog "加入所有节点到ipset[$NFTSET_VPSIPLIST]直连完成" + echolog "加入所有节点到nftset[$NFTSET_VPSIPLIST]直连完成" } filter_node() { @@ -1207,10 +1207,13 @@ del_firewall_rule() { done done - for handle in $(nft -a list chains |grep -E "chain PSW" |awk -F '# handle ' '{print$2}'); do + for handle in $(nft -a list chains | grep -E "chain PSW" | grep -v "PSW_RULE" | awk -F '# handle ' '{print$2}'); do nft delete chain inet fw4 handle ${handle} 2>/dev/null done + # Need to be removed at the end, otherwise it will show "Resource busy" + nft delete chain inet fw4 handle $(nft -a list chains | grep -E "PSW_RULE" | awk -F '# handle ' '{print$2}') 2>/dev/null + ip rule del fwmark 1 lookup 100 2>/dev/null ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null @@ -1295,14 +1298,6 @@ gen_include() { return 0 } -get_ipt_bin() { - echo $ipt -} - -get_ip6t_bin() { - echo $ip6t -} - start() { add_firewall_rule gen_include @@ -1325,7 +1320,7 @@ insert_rule_before) insert_rule_after) insert_rule_after "$@" ;; -flush_ipset) +flush_nftset) flush_nftset ;; get_wan_ip) diff --git a/luci-app-passwall/root/usr/share/passwall/rule_update.lua b/luci-app-passwall/root/usr/share/passwall/rule_update.lua index 2784505c4..23d31850d 100755 --- a/luci-app-passwall/root/usr/share/passwall/rule_update.lua +++ b/luci-app-passwall/root/usr/share/passwall/rule_update.lua @@ -56,11 +56,14 @@ local function trim(text) end -- curl -local function curl(url, file) +local function curl(url, file, valifile) local cmd = "curl -skL -w %{http_code} --retry 3 --connect-timeout 3 '" .. url .. "'" if file then cmd = cmd .. " -o " .. file end + if valifile then + cmd = cmd .. " --dump-header " .. valifile + end local stdout = luci.sys.exec(cmd) if file then @@ -87,9 +90,19 @@ local function line_count(file_path) return num; end -local function non_file_check(file_path) +local function non_file_check(file_path, vali_file) if nixio.fs.readfile(file_path, 1000) then - return nil; + local remote_file_size = luci.sys.exec("cat " .. vali_file .. " | grep -i Content-Length | awk '{print $2}'") + local local_file_size = luci.sys.exec("ls -l " .. file_path .. "| awk '{print $5}'") + if remote_file_size then + if tonumber(remote_file_size) == tonumber(local_file_size) then + return nil; + else + return true; + end + else + return nil; + end else return true; end @@ -101,16 +114,26 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain) local sret_tmp = 0 local domains = {} local file_tmp = "/tmp/" ..rule_name.. "_tmp" + local vali_file = "/tmp/" ..rule_name.. "_vali" local download_file_tmp = "/tmp/" ..rule_name.. "_dl" local unsort_file_tmp = "/tmp/" ..rule_name.. "_unsort" log(rule_name.. " 开始更新...") for k,v in ipairs(url) do - sret_tmp = curl(v, download_file_tmp..k) - if sret_tmp == 200 and non_file_check(download_file_tmp..k) then - sret = 0 - log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件读取出错,请检查网络或下载链接后重试!") - elseif sret_tmp == 200 then + sret_tmp = curl(v, download_file_tmp..k, vali_file..k) + if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then + log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件读取出错,尝试重新下载。") + os.remove(download_file_tmp..k) + os.remove(vali_file..k) + sret_tmp = curl(v, download_file_tmp..k, vali_file..k) + if sret_tmp == 200 and non_file_check(download_file_tmp..k, vali_file..k) then + sret = 0 + sret_tmp = 0 + log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载文件读取出错,请检查网络或下载链接后重试!") + end + end + + if sret_tmp == 200 then if rule_name == "gfwlist" then local domains = {} local gfwlist = io.open(download_file_tmp..k, "r") @@ -168,6 +191,7 @@ local function fetch_rule(rule_name,rule_type,url,exclude_domain) log(rule_name.. " 第" ..k.. "条规则:" ..v.. "下载失败,请检查网络或下载链接后重试!") end os.remove(download_file_tmp..k) + os.remove(vali_file..k) end if sret == 200 then