Merge pull request #1323 from yousong/shadowsocks-libev
authorJo-Philipp Wich <jo@mein.io>
Tue, 22 Aug 2017 19:22:45 +0000 (21:22 +0200)
committerGitHub <noreply@github.com>
Tue, 22 Aug 2017 19:22:45 +0000 (21:22 +0200)
luci-app-shadowsocks-libev: support for option dst_forward_recentrst

applications/luci-app-shadowsocks-libev/luasrc/model/cbi/shadowsocks-libev/rules.lua
applications/luci-app-shadowsocks-libev/luasrc/model/shadowsocks-libev.lua
modules/luci-base/htdocs/luci-static/resources/cbi.js
modules/luci-base/luasrc/cbi/datatypes.lua

index 9985790..4a01bed 100644 (file)
@@ -9,23 +9,28 @@ m = Map("shadowsocks-libev",
        translate("Redir Rules"),
        translate("On this page you can configure how traffics are to be \
                forwarded to ss-redir instances. \
-               If enabled, packets will first have their source ip addresses checked \
-               against <em>Src ip bypass</em>, <em>Src ip forward</em>, \
-               <em>Src ip checkdst</em> and if none matches <em>Src default</em> \
+               If enabled, packets will first have their src ip addresses checked \
+               against <em>Src ip/net bypass</em>, <em>Src ip/net forward</em>, \
+               <em>Src ip/net checkdst</em> and if none matches <em>Src default</em> \
                will give the default action to be taken. \
                If the prior check results in action <em>checkdst</em>, packets will continue \
-               to have their destination addresses checked."))
+               to have their dst addresses checked."))
 
 local sdata = m:get('ss_rules')
 if not sdata then
        m:set('ss_rules', nil, 'ss_rules')
-       m:set('ss_rules', 'disabled', true)
+       m:set('ss_rules', 'disabled', "1")
+end
+
+function src_dst_option(s, ...)
+       local o = s:taboption(...)
+       o.datatype = "or(ip4addr,cidr4)"
 end
 
 s = m:section(NamedSection, "ss_rules", "ss_rules")
 s:tab("general", translate("General Settings"))
-s:tab("srcip", translate("Source Settings"))
-s:tab("dstip", translate("Destination Settings"))
+s:tab("src", translate("Source Settings"))
+s:tab("dst", translate("Destination Settings"))
 
 s:taboption('general', Flag, "disabled", translate("Disable"))
 ss.option_install_package(s, 'general')
@@ -49,38 +54,56 @@ s:taboption('general', Value, "ipt_args",
        translate("Extra arguments"),
        translate("Passes additional arguments to iptables. Use with care!"))
 
-s:taboption('srcip', DynamicList, "src_ips_bypass",
-       translate("Src ip bypass"),
-       translate("Bypass redir action for packets with source addresses in this list"))
-s:taboption('srcip', DynamicList, "src_ips_forward",
-       translate("Src ip forward"),
-       translate("Go through redir action for packets with source addresses in this list"))
-s:taboption('srcip', DynamicList, "src_ips_checkdst",
-       translate("Src ip checkdst"),
-       translate("Continue to have dst address checked for packets with source addresses in this list"))
-o = s:taboption('srcip', ListValue, "src_default",
+src_dst_option(s, 'src', DynamicList, "src_ips_bypass",
+       translate("Src ip/net bypass"),
+       translate("Bypass ss-redir for packets with src address in this list"))
+src_dst_option(s, 'src', DynamicList, "src_ips_forward",
+       translate("Src ip/net forward"),
+       translate("Forward through ss-redir for packets with src address in this list"))
+src_dst_option(s, 'src', DynamicList, "src_ips_checkdst",
+       translate("Src ip/net checkdst"),
+       translate("Continue to have dst address checked for packets with src address in this list"))
+o = s:taboption('src', ListValue, "src_default",
        translate("Src default"),
-       translate("Default action for packets whose source addresses do not match any of the source ip list"))
+       translate("Default action for packets whose src address do not match any of the src ip/net list"))
 ss.values_actions(o)
 
-s:taboption('dstip', DynamicList, "dst_ips_bypass",
-       translate("Dst ip bypass"),
-       translate("Bypass redir action for packets with destination addresses in this list"))
-s:taboption('dstip', DynamicList, "dst_ips_forward",
-       translate("Dst ip forward"),
-       translate("Go through redir action for packets with destination addresses in this list"))
+src_dst_option(s, 'dst', DynamicList, "dst_ips_bypass",
+       translate("Dst ip/net bypass"),
+       translate("Bypass ss-redir for packets with dst address in this list"))
+src_dst_option(s, 'dst', DynamicList, "dst_ips_forward",
+       translate("Dst ip/net forward"),
+       translate("Forward through ss-redir for packets with dst address in this list"))
 
-o = s:taboption('dstip', FileBrowser, "dst_ips_bypass_file",
-       translate("Dst ip bypass file"),
-       translate("File containing ip addresses for the purposes as with <em>Dst ip bypass</em>"))
+o = s:taboption('dst', FileBrowser, "dst_ips_bypass_file",
+       translate("Dst ip/net bypass file"),
+       translate("File containing ip/net for the purposes as with <em>Dst ip/net bypass</em>"))
 o.datatype = "file"
-s:taboption('dstip', FileBrowser, "dst_ips_forward_file",
-       translate("Dst ip forward file"),
-       translate("File containing ip addresses for the purposes as with <em>Dst ip forward</em>"))
+s:taboption('dst', FileBrowser, "dst_ips_forward_file",
+       translate("Dst ip/net forward file"),
+       translate("File containing ip/net for the purposes as with <em>Dst ip/net forward</em>"))
 o.datatype = "file"
-o = s:taboption('dstip', ListValue, "dst_default",
+o = s:taboption('dst', ListValue, "dst_default",
        translate("Dst default"),
-       translate("Default action for packets whose destination addresses do not match any of the destination ip list"))
+       translate("Default action for packets whose dst address do not match any of the dst ip list"))
 ss.values_actions(o)
 
+local installed = os.execute("iptables -m recent -h &>/dev/null") == 0
+if installed then
+       o = s:taboption('dst', Flag, "dst_forward_recentrst")
+else
+       m:set('ss_rules', 'dst_forward_recentrst', "0")
+       o = s:taboption("dst", Button, "_install")
+       o.inputtitle = translate("Install package iptables-mod-conntrack-extra")
+       o.inputstyle = "apply"
+       o.write = function()
+               return luci.http.redirect(
+                       luci.dispatcher.build_url("admin/system/packages") ..
+                       "?submit=1&install=iptables-mod-conntrack-extra"
+               )
+       end
+end
+o.title = translate("Forward recentrst")
+o.description = translate("Forward those packets whose dst have recently sent to us multiple tcp-rst")
+
 return m
index 650ff63..7ba6b40 100644 (file)
@@ -3,6 +3,7 @@
 
 local _up = getfenv(3)
 local ut = require("luci.util")
+local sys = require("luci.sys")
 local ds = require("luci.dispatcher")
 local nw = require("luci.model.network")
 nw.init()
@@ -23,13 +24,16 @@ end
 
 function values_redir(o, xmode)
        o.map.uci.foreach("shadowsocks-libev", "ss_redir", function(sdata)
+               local disabled = ucival_to_bool(sdata["disabled"])
                local sname = sdata[".name"]
                local mode = sdata["mode"] or "tcp_only"
-               if mode and mode:find(xmode) then
+               if not disabled and mode:find(xmode) then
                        local desc = "%s - %s" % {sname, mode}
                        o:value(sname, desc)
                end
        end)
+       o:value("", "<unset>")
+       o.default = ""
 end
 
 function values_serverlist(o)
@@ -53,10 +57,8 @@ function values_ipaddr(o)
 end
 
 function values_ifnames(o)
-       for _, v in ipairs(nw:get_interfaces()) do
-               if v.dev then
-                       o:value(v.dev.name)
-               end
+       for _, v in ipairs(sys.net.devices()) do
+               o:value(v)
        end
 end
 
index b819230..884eb62 100644 (file)
@@ -118,48 +118,88 @@ var cbi_validators = {
                return false;
        },
 
-       'ipmask': function()
+       'ip4prefix': function()
        {
-               return cbi_validators.ipmask4.apply(this) ||
-                       cbi_validators.ipmask6.apply(this);
+               return !isNaN(this) && this >= 0 && this <= 32;
        },
 
-       'ipmask4': function()
+       'ip6prefix': function()
        {
-               var ip = this, mask = 32;
+               return !isNaN(this) && this >= 0 && this <= 128;
+       },
 
-               if (ip.match(/^(\S+)\/(\S+)$/))
+       'cidr': function()
+       {
+               return cbi_validators.cidr4.apply(this) ||
+                       cbi_validators.cidr6.apply(this);
+       },
+
+       'cidr4': function()
+       {
+               if (this.match(/^(\S+)\/(\S+)$/))
                {
                        ip = RegExp.$1;
                        mask = RegExp.$2;
+                       return cbi_validators.ip4addr.apply(ip) &&
+                               cbi_validators.ip4prefix.apply(mask);
                }
+               return false;
+       },
 
-               if (!isNaN(mask) && (mask < 0 || mask > 32))
-                       return false;
-
-               if (isNaN(mask) && !cbi_validators.ip4addr.apply(mask))
-                       return false;
-
-               return cbi_validators.ip4addr.apply(ip);
+       'cidr6': function()
+       {
+               if (this.match(/^(\S+)\/(\S+)$/))
+               {
+                       ip = RegExp.$1;
+                       mask = RegExp.$2;
+                       return cbi_validators.ip6addr.apply(ip) &&
+                               cbi_validators.ip6prefix.apply(mask);
+               }
+               return false;
        },
 
-       'ipmask6': function()
+       'ipnet4': function()
        {
-               var ip = this, mask = 128;
+               if (this.match(/^(\S+)\/(\S+)$/))
+               {
+                       ip = RegExp.$1;
+                       net = RegExp.$2;
+                       return cbi_validators.ip4addr.apply(ip) &&
+                               cbi_validators.ip4addr.apply(net);
+               }
+               return false;
+       },
 
-               if (ip.match(/^(\S+)\/(\S+)$/))
+       'ipnet6': function()
+       {
+               if (this.match(/^(\S+)\/(\S+)$/))
                {
                        ip = RegExp.$1;
-                       mask = RegExp.$2;
+                       net = RegExp.$2;
+                       return cbi_validators.ip6addr.apply(ip) &&
+                               cbi_validators.ip6addr.apply(net);
                }
+               return false;
+       },
 
-               if (!isNaN(mask) && (mask < 0 || mask > 128))
-                       return false;
+       'ipmask': function()
+       {
+               return cbi_validators.ipmask4.apply(this) ||
+                       cbi_validators.ipmask6.apply(this);
+       },
 
-               if (isNaN(mask) && !cbi_validators.ip6addr.apply(mask))
-                       return false;
+       'ipmask4': function()
+       {
+               return cbi_validators.cidr4.apply(this) ||
+                       cbi_validators.ipnet4.apply(this) ||
+                       cbi_validators.ip4addr.apply(this);
+       },
 
-               return cbi_validators.ip6addr.apply(ip);
+       'ipmask6': function()
+       {
+               return cbi_validators.cidr6.apply(this) ||
+                       cbi_validators.ipnet6.apply(this) ||
+                       cbi_validators.ip6addr.apply(this);
        },
 
        'port': function()
index cf56566..df23aaf 100644 (file)
@@ -132,38 +132,40 @@ function ip6prefix(val)
        return ( val and val >= 0 and val <= 128 )
 end
 
-function ipmask(val)
-       return ipmask4(val) or ipmask6(val)
+function cidr4(val)
+       local ip, mask = val:match("^([^/]+)/([^/]+)$")
+
+       return ip4addr(ip) and ip4prefix(mask)
 end
 
-function ipmask4(val)
+function cidr6(val)
        local ip, mask = val:match("^([^/]+)/([^/]+)$")
-       local bits = tonumber(mask)
 
-       if bits and (bits < 0 or bits > 32) then
-               return false
-       end
+       return ip6addr(ip) and ip6prefix(mask)
+end
 
-       if not bits and mask and not ip4addr(mask) then
-               return false
-       end
+function ipnet4(val)
+       local ip, mask = val:match("^([^/]+)/([^/]+)$")
 
-       return ip4addr(ip or val)
+       return ip4addr(ip) and ip4addr(mask)
 end
 
-function ipmask6(val)
+function ipnet6(val)
        local ip, mask = val:match("^([^/]+)/([^/]+)$")
-       local bits = tonumber(mask)
 
-       if bits and (bits < 0 or bits > 128) then
-               return false
-       end
+       return ip6addr(ip) and ip6addr(mask)
+end
 
-       if not bits and mask and not ip6addr(mask) then
-               return false
-       end
+function ipmask(val)
+       return ipmask4(val) or ipmask6(val)
+end
+
+function ipmask4(val)
+       return cidr4(val) or ipnet4(val) or ip4addr(val)
+end
 
-       return ip6addr(ip or val)
+function ipmask6(val)
+       return cidr6(val) or ipnet6(val) or ip6addr(val)
 end
 
 function ip6hostid(val)