1
0
Fork 0
mirror of https://github.com/Ysurac/openmptcprouter-feeds.git synced 2025-02-12 18:41:51 +00:00

Add MPTCP support to shadowsocks

This commit is contained in:
Ycarus 2018-01-22 11:20:49 +01:00
parent 3b2e9da212
commit f7a9a08a9b
18 changed files with 1588 additions and 0 deletions

View file

@ -0,0 +1,17 @@
#
# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
LUCI_TITLE:=LuCI Support for shadowsocks-libev
LUCI_DEPENDS:=
PKG_LICENSE:=Apache-2.0
include ../luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View file

@ -0,0 +1,33 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
--
module("luci.controller.shadowsocks-libev", package.seeall)
function index()
entry({"admin", "services", "shadowsocks-libev"},
alias("admin", "services", "shadowsocks-libev", "instances"),
_("Shadowsocks-libev"), 59)
entry({"admin", "services", "shadowsocks-libev", "instances"},
arcombine(cbi("shadowsocks-libev/instances"), cbi("shadowsocks-libev/instance-details")),
_("Local Instances"), 10).leaf = true
entry({"admin", "services", "shadowsocks-libev", "servers"},
cbi("shadowsocks-libev/servers"),
_("Remote Servers"), 20).leaf = true
entry({"admin", "services", "shadowsocks-libev", "rules"},
cbi("shadowsocks-libev/rules"),
_("Redir Rules"), 30).leaf = true
entry({"admin", "services", "shadowsocks-libev", "status"}, call("ss_status"), nil).leaf = true
end
function ss_status()
local ut = require "luci.util"
local rv = ut.ubus("service", "list", {name = "shadowsocks-libev"})["shadowsocks-libev"] or {_=0}
luci.http.prepare_content("application/json")
luci.http.write_json(rv)
end

View file

@ -0,0 +1,53 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local ds = require "luci.dispatcher"
local ss = require "luci.model.shadowsocks-libev"
local sname = arg[1]
local redirect_url = ds.build_url("admin/services/shadowsocks-libev/instances")
local s, o
local m = Map("shadowsocks-libev")
local sdata = m:get(sname)
if not sdata then
luci.http.redirect(redirect_url)
return
end
local stype = sdata[".type"]
m.redirect = redirect_url
m.title = "shadowsocks-libev - %s - %s" % {stype, sname}
s = m:section(NamedSection, sname, stype)
s:tab("general", translate("General Settings"))
s:tab("advanced", translate("Advanced Settings"))
s:taboption("general", Flag, "disabled", translate("Disable"))
ss.option_install_package(s, "general")
ss.options_common(s, "advanced")
if stype == "ss_server" then
ss.options_server(s, "general")
o = s:taboption("general", Value, "bind_address",
translate("Bind address"),
translate("The address ss-server will initiate connection from"))
o.datatype = "ipaddr"
o.placeholder = "0.0.0.0"
ss.values_ipaddr(o)
o = s:taboption("general", Value, "manager_address", translate("Manager address"))
o.datatype = "hostport"
else
ss.options_client(s, "general")
if stype == "ss_tunnel" then
o = s:taboption("general", Value, "tunnel_address",
translate("Tunnel address"),
translate("The address ss-tunnel will forward traffic to"))
o.datatype = "hostport"
elseif stype == "ss_redir" then
o = s:taboption("advanced", Flag, "disable_sni",
translate("Disable SNI"),
translate("Disable parsing HTTP/HTTPS payload to find then resolve hostname at remote server"))
end
end
return m

View file

@ -0,0 +1,104 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local ds = require "luci.dispatcher"
local ss = require "luci.model.shadowsocks-libev"
local ut = require "luci.util"
local m, s, o
m = Map("shadowsocks-libev",
translate("Local Instances"),
translate("Instances of shadowsocks-libev components, e.g. ss-local, \
ss-redir, ss-tunnel, ss-server, etc. To enable an instance it \
is required to enable both the instance itself and the remote \
server it refers to."))
local instances = {}
local cfgtypes = { "ss_local", "ss_redir", "ss_server", "ss_tunnel" }
for sname, sdata in pairs(m:get()) do
local key, value = ss.cfgvalue_overview(sdata)
if key ~= nil then
instances[key] = value
end
end
s = m:section(Table, instances)
s.addremove = true
s.template_addremove = "shadowsocks-libev/add_instance"
s.extedit = function(self, section)
local value = instances[section]
if type(value) == "table" then
return ds.build_url(unpack(ds.context.requestpath),
"services/shadowsocks-libev/instances",
value[".name"])
end
end
s.parse = function(self, ...)
Table.parse(self, ...)
local crval = REMOVE_PREFIX .. self.config
local name = self.map:formvaluetable(crval)
for k,v in pairs(name) do
local value = instances[k]
local sname = value[".name"]
if type(value) == "table" then
m:del(sname)
instances[k] = nil
for _, oname in ipairs({"redir_tcp", "redir_udp"}) do
local ovalue = m:get("ss_rules", oname)
if ovalue == sname then
m:del("ss_rules", oname)
end
end
end
end
local stype = m:formvalue("_newinst.type")
local sname = m:formvalue("_newinst.name")
if ut.contains(cfgtypes, stype) then
local created
if sname and #sname > 0 then
created = m:set(sname, nil, stype)
else
created = m:add(stype)
sname = created
end
if created then
m.uci:save("shadowsocks-libev")
luci.http.redirect(ds.build_url(
"admin/services/shadowsocks-libev/instances", sname
))
end
end
end
o = s:option(DummyValue, "name", translate("Name"))
o.rawhtml = true
o = s:option(DummyValue, "overview", translate("Overview"))
o.rawhtml = true
s:option(DummyValue, "running", translate("Running"))
o = s:option(Button, "disabled", translate("Enable/Disable"))
o.render = function(self, section, scope)
if instances[section].disabled then
self.title = translate("Disabled")
self.inputstyle = "reset"
else
self.title = translate("Enabled")
self.inputstyle = "save"
end
Button.render(self, section, scope)
end
o.write = function(self, section)
local sdata = instances[section]
if type(sdata) == "table" then
local sname = sdata[".name"]
local disabled = not sdata["disabled"]
sdata["disabled"] = disabled
m:set(sname, "disabled", tostring(disabled))
end
end
return m

View file

@ -0,0 +1,110 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local ss = require("luci.model.shadowsocks-libev")
local m, s, o
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 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 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', "1")
end
function src_dst_option(s, ...)
local o = s:taboption(...)
--o.datatype = "or(ip4addr,cidr4)"
o.datatype = "ip4addr"
end
s = m:section(NamedSection, "ss_rules", "ss_rules")
s:tab("general", translate("General 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')
o = s:taboption('general', ListValue, "redir_tcp",
translate("ss-redir for TCP"))
ss.values_redir(o, 'tcp')
o = s:taboption('general', ListValue, "redir_udp",
translate("ss-redir for UDP"))
ss.values_redir(o, 'udp')
o = s:taboption('general', ListValue, "local_default",
translate("Local-out default"),
translate("Default action for locally generated TCP packets"))
ss.values_actions(o)
o = s:taboption('general', DynamicList, "ifnames",
translate("Ingress interfaces"),
translate("Only apply rules on packets from these network interfaces"))
ss.values_ifnames(o)
s:taboption('general', Value, "ipt_args",
translate("Extra arguments"),
translate("Passes additional arguments to iptables. Use with care!"))
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 src address do not match any of the src ip/net list"))
ss.values_actions(o)
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('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('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('dst', ListValue, "dst_default",
translate("Dst default"),
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

View file

@ -0,0 +1,31 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
local ds = require "luci.dispatcher"
local ss = require("luci.model.shadowsocks-libev")
local m, s
m = Map("shadowsocks-libev",
translate("Remote Servers"),
translate("Definition of remote shadowsocks servers. \
Disable any of them will also disable instances refering to it."))
local sname = arg[1]
if sname then
if not m:get(sname) then
luci.http.redirect(ds.build_url("admin/services/shadowsocks-libev/servers"))
return
end
s = m:section(NamedSection, sname, "server")
m.title = m.title .. ' - ' .. sname
else
s = m:section(TypedSection, "server")
s.template = 'cbi/tblsection'
s.addremove = true
end
s:option(Flag, "disabled", translate("Disable"))
ss.options_server(s)
return m

View file

@ -0,0 +1,265 @@
-- Copyright 2017 Yousong Zhou <yszhou4tech@gmail.com>
-- Licensed to the public under the Apache License 2.0.
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()
module("luci.model.shadowsocks-libev", function(m)
setmetatable(m, {__index=function (self, k)
local tb = _up
return rawget(self, k) or _up[k]
end})
end)
function values_actions(o)
o:value("bypass")
o:value("forward")
if o.option ~= "dst_default" then
o:value("checkdst")
end
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 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)
o.map.uci.foreach("shadowsocks-libev", "server", function(sdata)
local sname = sdata[".name"]
local server = sdata["server"]
local server_port = sdata["server_port"]
if server and server_port then
local desc = "%s - %s:%s" % {sname, sdata["server"], sdata["server_port"]}
o:value(sname, desc)
end
end)
end
function values_ipaddr(o)
for _, v in ipairs(nw:get_interfaces()) do
for _, a in ipairs(v:ipaddrs()) do
o:value(a:host():string(), '%s (%s)' %{ a:host(), v:shortname() })
end
end
end
function values_ifnames(o)
for _, v in ipairs(sys.net.devices()) do
o:value(v)
end
end
function options_client(s, tab)
local o
o = s:taboption(tab, ListValue, "server", translate("Remote server"))
values_serverlist(o)
o = s:taboption(tab, Value, "local_address", translate("Local address"))
o.datatype = "ipaddr"
o.placeholder = "0.0.0.0"
values_ipaddr(o)
o = s:taboption(tab, Value, "local_port", translate("Local port"))
o.datatype = "port"
end
function options_server(s, tab)
local o
local optfunc
if tab == nil then
optfunc = function(...) return s:option(...) end
else
optfunc = function(...) return s:taboption(tab, ...) end
end
o = optfunc(Value, "server", translate("Server"))
o.datatype = "host"
o.size = 16
o = optfunc(Value, "server_port", translate("Server port"))
o.datatype = "port"
o.size = 5
o = optfunc(ListValue, "method", translate("Method"))
for _, m in ipairs(methods) do
o:value(m)
end
o = optfunc(Value, "key", translate("Key (base64 encoding)"))
o.datatype = "base64"
o.password = true
o.size = 12
o = optfunc(Value, "password", translate("Password"))
o.password = true
o.size = 12
end
function options_common(s, tab)
local o
o = s:taboption(tab, ListValue, "mode", translate("Mode of operation"))
for _, m in ipairs(modes) do
o:value(m)
end
o.default = "tcp_and_udp"
o = s:taboption(tab, Value, "mtu", translate("MTU"))
o.datatype = "uinteger"
o = s:taboption(tab, Value, "timeout", translate("Timeout (sec)"))
o.datatype = "uinteger"
s:taboption(tab, Value, "user", translate("Run as"))
s:taboption(tab, Flag, "verbose", translate("Verbose"))
s:taboption(tab, Flag, "ipv6_first", translate("IPv6 First"), translate("Prefer IPv6 addresses when resolving names"))
s:taboption(tab, Flag, "fast_open", translate("Enable TCP Fast Open"))
s:taboption(tab, Flag, "reuse_port", translate("Enable SO_REUSEPORT"))
s:taboption(tab, Flag, "mptcp", translate("Enable MPTCP"))
end
function ucival_to_bool(val)
return val == "true" or val == "1" or val == "yes" or val == "on"
end
function cfgvalue_overview(sdata)
local stype = sdata[".type"]
local lines = {}
if stype == "ss_server" then
cfgvalue_overview_(sdata, lines, names_options_server)
cfgvalue_overview_(sdata, lines, names_options_common)
cfgvalue_overview_(sdata, lines, {
"bind_address",
"manager_address",
})
elseif stype == "ss_local" or stype == "ss_redir" or stype == "ss_tunnel" then
cfgvalue_overview_(sdata, lines, names_options_client)
if stype == "ss_tunnel" then
cfgvalue_overview_(sdata, lines, {"tunnel_address"})
elseif stype == "ss_redir" then
cfgvalue_overview_(sdata, lines, {"disable_sni"})
end
cfgvalue_overview_(sdata, lines, names_options_common)
else
return nil, nil
end
local sname = sdata[".name"]
local key = "%s.%s" % {stype, sname}
local value = {
[".name"] = sname,
name = '%s.<var>%s</var>' % {stype, sname},
overview = table.concat(lines, "</br>"),
disabled = ucival_to_bool(sdata["disabled"]),
}
return key, value
end
function cfgvalue_overview_(sdata, lines, names)
local line
for _, n in ipairs(names) do
local v = sdata[n]
if v ~= nil then
if n == "key" or n == "password" then
v = translate("<hidden>")
end
local fv = "<var>%s</var>" % ut.pcdata(v)
if sdata[".type"] ~= "ss_server" and n == "server" then
fv = '<a class="label" href="%s">%s</a>' % {
ds.build_url("admin/services/shadowsocks-libev/servers", v), fv}
end
line = n .. ": " .. fv
table.insert(lines, line)
end
end
end
function option_install_package(s, tab)
local bin = s.sectiontype:gsub("_", "-", 1)
local installed = nixio.fs.access("/usr/bin/" .. bin)
if installed then
return
end
local opkg_package = "shadowsocks-libev-" .. bin
local p_install
if tab then
p_install = s:taboption(tab, Button, "_install")
else
p_install = s:option(Button, "_install")
end
p_install.title = translate("Package is not installed")
p_install.inputtitle = translate("Install package %q" % opkg_package)
p_install.inputstyle = "apply"
function p_install.write()
return luci.http.redirect(
luci.dispatcher.build_url("admin/system/packages") ..
"?submit=1&install=%s" % opkg_package
)
end
end
names_options_server = {
"server",
"server_port",
"method",
"key",
"password",
}
names_options_client = {
"server",
"local_address",
"local_port",
}
names_options_common = {
"verbose",
"ipv6_first",
"fast_open",
"reuse_port",
"mode",
"mtu",
"timeout",
"user",
}
modes = {
"tcp_only",
"tcp_and_udp",
"udp_only",
}
methods = {
-- aead
"aes-128-gcm",
"aes-192-gcm",
"aes-256-gcm",
"chacha20-ietf-poly1305",
"xchacha20-ietf-poly1305",
-- stream
"table",
"rc4",
"rc4-md5",
"aes-128-cfb",
"aes-192-cfb",
"aes-256-cfb",
"aes-128-ctr",
"aes-192-ctr",
"aes-256-ctr",
"bf-cfb",
"camellia-128-cfb",
"camellia-192-cfb",
"camellia-256-cfb",
"salsa20",
"chacha20",
"chacha20-ietf",
}

View file

@ -0,0 +1,45 @@
<div class="cbi-section-create cbi-tblsection-create">
<br />
<table class="cbi-section-table">
<tr class="cbi-section-table-row">
<td class="cbi-section-table-cell" style="width:140px">
<select class="cbi-input-select" id="_newinst.type" name="_newinst.type">
<option value="_dummy">-- instance type --</option>
<option value="ss_local">ss-local</option>
<option value="ss_tunnel">ss-tunnel</option>
<option value="ss_redir">ss-redir</option>
<option value="ss_server">ss-server</option>
</select>
</td>
<td class="cbi-section-table-cell" style="width:110px">
<input type="text" class="cbi-input-text" id="_newinst.name" name="_newinst.name" placeholder="<%:Name%>"/>
</td>
<td class="cbi-section-table-cell left">
<input type="submit" class="cbi-button cbi-button-add" name="cbi.cts.<%=self.config%>" value="<%:Add%>" />
</td>
</tr>
</table>
</div>
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url('admin/services/shadowsocks-libev/status')%>', null,
function(x, st)
{
var names = [
<%-
for _, name in ipairs(self:cfgsections()) do
write("%q," % name)
end
-%>
];
var instances = st["instances"] || {};
for (var i = 0, len = names.length; i < len; i++) {
var name = names[i];
var el = document.getElementById('cbi-table-' + name + '-running');
if (el) {
var running = instances.hasOwnProperty(name)? instances[name].running : false;
el.innerText = running ? 'yes' : 'no';
}
}
}
);
//]]></script>

View file

@ -0,0 +1,11 @@
#!/bin/sh
uci -q batch <<-EOF >/dev/null
delete ucitrack.@shadowsocks-libev[-1]
add ucitrack shadowsocks-libev
set ucitrack.@shadowsocks-libev[-1].init=shadowsocks-libev
commit ucitrack
EOF
rm -f /tmp/luci-indexcache
exit 0

128
shadowsocks-libev/Makefile Normal file
View file

@ -0,0 +1,128 @@
#
# Copyright (C) 2017-2018 Yousong Zhou <yszhou4tech@gmail.com>
# Copyright (C) 2018 Ycarus (Yannick Chabanois) <ycarus@zugaina.org>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#
include $(TOPDIR)/rules.mk
# Checklist when bumping versions
#
# - update cipher list by checking src/crypto.c:crypto_init()
# - check if default mode has changed from being tcp_only
#
PKG_NAME:=shadowsocks-libev
PKG_VERSION:=3.1.2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$(PKG_VERSION)
PKG_HASH:=3611f09793af923d988ecbd15ad89fb66839e51a0059685d8f88c0131658e4a7
PKG_MAINTAINER:=Yousong Zhou <yszhou4tech@gmail.com>
PKG_LICENSE:=GPL-3.0+
PKG_LICENSE_FILES:=LICENSE
PKG_FIXUP:=autoreconf
PKG_INSTALL:=1
PKG_USE_MIPS16:=0
PKG_BUILD_PARALLEL:=1
include $(INCLUDE_DIR)/package.mk
define Package/shadowsocks-libev-config
SECTION:=net
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
TITLE:=shadowsocks-libev config scripts
URL:=https://github.com/shadowsocks/shadowsocks-libev
endef
define Package/shadowsocks-libev-config/install
$(INSTALL_DIR) $(1)/etc/config
$(INSTALL_DATA) ./files/shadowsocks-libev.config $(1)/etc/config/shadowsocks-libev
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_BIN) ./files/shadowsocks-libev.init $(1)/etc/init.d/shadowsocks-libev
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/shadowsocks.track $(1)/usr/bin/ss-redir-track
endef
define Package/shadowsocks-libev/Default
define Package/shadowsocks-libev-$(1)
SECTION:=net
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
TITLE:=shadowsocks-libev $(1)
URL:=https://github.com/shadowsocks/shadowsocks-libev
DEPENDS:=+libcares +libev +libmbedtls +libpcre +libpthread +libsodium +shadowsocks-libev-config +zlib
endef
define Package/shadowsocks-libev-$(1)/install
$$(INSTALL_DIR) $$(1)/usr/bin
$$(INSTALL_BIN) $$(PKG_INSTALL_DIR)/usr/bin/$(1) $$(1)/usr/bin
endef
endef
SHADOWSOCKS_COMPONENTS:=ss-local ss-redir ss-tunnel ss-server
define shadowsocks-libev/templates
$(foreach component,$(SHADOWSOCKS_COMPONENTS),
$(call Package/shadowsocks-libev/Default,$(component))
)
endef
$(eval $(call shadowsocks-libev/templates))
define Package/shadowsocks-libev-ss-rules
SECTION:=net
CATEGORY:=Network
SUBMENU:=Web Servers/Proxies
TITLE:=shadowsocks-libev ss-rules
URL:=https://github.com/shadowsocks/shadowsocks-libev
DEPENDS:=+ip +ipset +iptables-mod-tproxy +resolveip +shadowsocks-libev-ss-redir +shadowsocks-libev-config
endef
define Package/shadowsocks-libev-ss-rules/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) ./files/ss-rules $(1)/usr/bin
$(INSTALL_DIR) $(1)/etc/uci-defaults
$(INSTALL_DATA) ./files/firewall.ss-rules $(1)/etc
$(INSTALL_BIN) ./files/ss-rules.defaults $(1)/etc/uci-defaults
endef
define Package/shadowsocks-libev-ss-rules/prerm
#!/bin/sh
s=firewall.ss_rules
uci get "$$s" >/dev/null || exit 0
uci batch <<-EOF
delete $$s
commit firewall
EOF
endef
define Build/Prepare
$(call Build/Prepare/Default)
$(FIND) $(PKG_BUILD_DIR) \
-name '*.o' \
-o -name '*.lo' \
-o -name '.deps' \
-o -name '.libs' \
| $(XARGS) rm -rvf
endef
CONFIGURE_ARGS += \
--disable-documentation \
--disable-silent-rules \
--disable-assert \
--disable-ssp \
$(eval $(call BuildPackage,shadowsocks-libev-config))
$(eval $(call BuildPackage,shadowsocks-libev-ss-rules))
$(foreach component,$(SHADOWSOCKS_COMPONENTS), \
$(eval $(call BuildPackage,shadowsocks-libev-$(component))) \
)

View file

@ -0,0 +1,97 @@
## components
`ss-local` provides SOCKS5 proxy with UDP associate support.
socks5 ss plain
--------> tcp:local_address:local_port ----> ss server -------> dest
`ss-redir`. The REDIRECT and TPROXY part are to be provided by `ss-rules` script. REDIRECT only works for tcp traffic (see also darkk/redsocks). TPROXY is used to proxy udp messages, but it's only available in the PREROUTING chain and as such cannot proxy local out traffic.
plain plain ss plain
---------> REDIRECT ------> tcp:local_address:local_port ----> ss server -----> original dest
plain plain ss plain
---------> TPROXY -------> udp:local_address:local_port -----> ss server -----> original dest
`ss-tunnel` provides ssh `-L` local-forwarding-like tunnel. Typically it's used to tunnel DNS traffic to the remote.
plain ss plain
---------> tcp|udp:local_address:local_port ------> ss server -------> tunnel_address
`ss-server`, the "ss server" in the above diagram
## uci
Option names are the same as those used in json config files. Check `validate_xxx` func definition of the [service script](files/shadowsocks-libev.init) and shadowsocks-libev's own documentation for supported options and expected value types. A [sample config file](files/shadowsocks-libev.config) is also provided for reference.
Every section have a `disabled` option to temporarily turn off the component instance or component instances referring to it.
Section type `server` is for definition of remote shadowsocks servers. They will be referred to from other component sections and as such should be named (as compared to anonymous section).
Section type `ss_local`, `ss_redir`, `ss_tunnel` are for specification of shadowsocks-libev components. They share mostly a common set of options like `local_port`, `verbose`, `fast_open`, `timeout`, etc.
We can have multiple instances of component and `server` sections. The relationship between them is many-to-one. This will have the following implications
- It's possible to have both `ss_local` and `ss_redir` referring to the same `server` definition
- It's possible to have multiple instances of `ss_redir` listening on the same address:port with `reuse_port` enabled referring to the same or different `server` sections
`ss_rules` section is for configuring the behaviour of `ss-rules` script. There can only exist at most one such section with the name also being `ss_rules`
redir_tcp name of ss_redir section with mode tcp_only or tcp_and_udp
redir_udp name of ss_redir section with mode udp_only or tcp_and_udp
ifnames only apply rules on packets from these ifnames
--- for incoming packets having source address in
src_ips_bypass will bypass the redir chain
src_ips_forward will always go through the redir chain
src_ips_checkdst will continue to have their destination addresses checked
--- otherwise, the default action can be specified with
src_default bypass, forward, [checkdst]
--- if the previous check result is checkdst,
--- then packets having destination address in
dst_ips_bypass_file
dst_ips_bypass will bypass the redir chain
dst_ips_forward_file
dst_ips_forward will go through the redir chain
--- otherwise, the default action can be specified with
dst_default [bypass], forward
--- for local out tcp packets, the default action can be specified with
local_default [bypass], forward, checkdst
Bool option `dst_forward_recentrst` requires iptables/netfilter `recent` match module (`opkg install iptables-mod-conntrack-extra`). When enabled, `ss-rules` will setup iptables rules to forward through `ss-redir` those packets whose destination have recently sent to us multiple tcp-rst.
ss-rules uses kernel ipset mechanism for storing addresses/networks. Those ipsets are also part of the API and can be populated by other programs, e.g. dnsmasq with builtin ipset support. For more details please read output of `ss-rules --help`
Note also that `src_ips_xx` and `dst_ips_xx` actually also accepts cidr network representation. Option names are retained in its current form for backward compatibility coniderations
## notes and faq
Useful paths and commands for debugging
# check current running status
ubus call service list '{"name": "shadowsocks-libev"}'
ubus call service list '{"name": "shadowsocks-libev", "verbose": true}'
# dump validate definition
ubus call service validate '{"package": "shadowsocks-libev"}'
ubus call service validate '{"package": "shadowsocks-libev"}' \
| jsonfilter -e '$["shadowsocks-libev"]["ss_tunnel"]'
# check json config
ls -l /var/etc/shadowsocks-libev/
# set uci config option verbose to 1, restart the service and follow the log
logread -f
ss-redir needs to open a new socket and setsockopt IP_TRANSPARENT when sending udp reply to client. This requires `CAP_NET_ADMIN` and as such the process cannot run as `nobody`
ss-local, ss-redir, etc. supports specifying an array of remote ss server, but supporting this in uci seems to be overkill. The workaround can be defining multiple `server` sections and multiple `ss-redir` instances with `reuse_port` enabled

View file

@ -0,0 +1,2 @@
#!/bin/sh
/etc/init.d/shadowsocks-libev reload

View file

@ -0,0 +1,33 @@
config ss_redir hi
option disabled 1
option server 'sss0'
option local_address '0.0.0.0'
option local_port '1100'
option mode 'tcp_and_udp'
option timeout '60'
option fast_open 1
option verbose 1
option reuse_port 1
option mptcp 1
config ss_rules 'ss_rules'
option disabled 1
option redir_tcp 'hi'
option redir_udp 'hi'
option src_default 'forward'
option dst_default 'forward'
option local_default 'forward'
list dst_ips_forward '8.8.8.8'
config server 'sss0'
option disabled 1
option server '192.168.1.3'
option server_port '65101'
option password '********'
option method 'aes-256-cfb'
config ss_tunnel 'dns'
option mode 'tcp_and_udp'
option server 'sss0'
option local_port '5353'
option tunnel_address '8.8.8.8:53'

View file

@ -0,0 +1,321 @@
#!/bin/sh /etc/rc.common
#
# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
USE_PROCD=1
START=99
ss_confdir=/var/etc/shadowsocks-libev
ss_bindir=/usr/bin
q='"'
ss_mkjson() {
echo "{" >"$confjson"
if ss_mkjson_ "$@" >>$confjson; then
sed -i -e '/^\s*$/d' -e '2,$s/^/\t/' -e '$s/,$//' "$confjson"
echo "}" >>"$confjson"
else
rm -f "$confjson"
return 1
fi
}
ss_mkjson_() {
local func
for func in "$@"; do
"$func" || return 1
done
}
ss_mkjson_server_conf() {
local cfgserver
config_get cfgserver "$cfg" server
[ -n "$cfgserver" ] || return 1
eval "$(validate_server_section "$cfg" ss_validate_mklocal)"
validate_server_section "$cfgserver" || return 1
[ "$disabled" = 0 ] || return 1
ss_mkjson_server_conf_ "$cfgserver"
}
ss_mkjson_server_conf_() {
[ -n "$server_port" ] || return 1
password="${password//\"/\\\"}"
cat <<-EOF
${server:+${q}server${q}: ${q}$server${q},}
"server_port": $server_port,
${method:+${q}method${q}: ${q}$method${q},}
${key:+${q}key${q}: ${q}$key${q},}
${password:+${q}password${q}: ${q}$password${q},}
EOF
}
ss_mkjson_common_conf() {
[ "$ipv6_first" = 0 ] && ipv6_first=false || ipv6_first=true
[ "$fast_open" = 0 ] && fast_open=false || fast_open=true
[ "$reuse_port" = 0 ] && reuse_port=false || reuse_port=true
[ "$mptcp" = 0 ] && mptcp=false || mptcp=true
cat <<-EOF
"use_syslog": true,
"ipv6_first": $ipv6_first,
"fast_open": $fast_open,
"mptcp": $mptcp,
"reuse_port": $reuse_port,
${local_address:+${q}local_address${q}: ${q}$local_address${q},}
${local_port:+${q}local_port${q}: $local_port,}
${mode:+${q}mode${q}: ${q}$mode${q},}
${mtu:+${q}mtu${q}: $mtu,}
${timeout:+${q}timeout${q}: $timeout,}
${user:+${q}user${q}: ${q}$user${q},}
EOF
}
ss_mkjson_ss_local_conf() {
ss_mkjson_server_conf
}
ss_mkjson_ss_redir_conf() {
ss_mkjson_server_conf || return 1
[ "$disable_sni" = 0 ] && disable_sni=false || disable_sni=true
cat <<-EOF
"disable_sni": $disable_sni,
EOF
}
ss_mkjson_ss_server_conf() {
ss_mkjson_server_conf_
}
ss_mkjson_ss_tunnel_conf() {
ss_mkjson_server_conf || return 1
[ -n "$tunnel_address" ] || return 1
cat <<-EOF
${tunnel_address:+${q}tunnel_address${q}: ${q}$tunnel_address${q},}
EOF
}
ss_xxx() {
local cfg="$1"
local cfgtype="$2"
local bin="$ss_bindir/${cfgtype/_/-}"
local confjson="$ss_confdir/$cfgtype.$cfg.json"
[ -x "$bin" ] || return
eval "$("validate_${cfgtype}_section" "$cfg" ss_validate_mklocal)"
"validate_${cfgtype}_section" "$cfg" || return 1
[ "$disabled" = 0 ] || return
if ss_mkjson \
ss_mkjson_common_conf \
ss_mkjson_${cfgtype}_conf \
; then
procd_open_instance "$cfgtype.$cfg"
procd_set_param command "$bin" -c "$confjson"
[ "$verbose" = 0 ] || procd_append_param command -v
[ "$no_delay" = 0 ] || procd_append_param command --no-delay
[ -z "$bind_address" ] || procd_append_param command -b "$bind_address"
[ -z "$manager_address" ] || procd_append_param command --manager-address "$manager_address"
procd_set_param file "$confjson"
procd_set_param limits nofile="51200 51200"
procd_set_param respawn
procd_close_instance
ss_rules_cb
fi
}
ss_rules_cb() {
local cfgserver server
if [ "$cfgtype" = ss_redir ]; then
config_get cfgserver "$cfg" server
config_get server "$cfgserver" server
ss_redir_servers="$ss_redir_servers $server"
if [ "$mode" = tcp_only -o "$mode" = "tcp_and_udp" ]; then
eval "ss_rules_redir_tcp_$cfg=$local_port"
fi
if [ "$mode" = udp_only -o "$mode" = "tcp_and_udp" ]; then
eval "ss_rules_redir_udp_$cfg=$local_port"
fi
fi
}
ss_rules() {
local cfg="ss_rules"
local bin="$ss_bindir/ss-rules"
local cfgtype
local local_port_tcp local_port_udp
local args
[ -x "$bin" ] || return 1
config_get cfgtype "$cfg" TYPE
[ "$cfgtype" = ss_rules ] || return 1
eval "$(validate_ss_rules_section "$cfg" ss_validate_mklocal)"
validate_ss_rules_section "$cfg" || return 1
[ "$disabled" = 0 ] || return 1
eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp"
eval local_port_udp="\$ss_rules_redir_udp_$redir_udp"
[ -n "$local_port_tcp" -o -n "$local_port_udp" ] || return 1
ss_redir_servers="$(echo "$ss_redir_servers" | tr ' ' '\n' | sort -u)"
[ "$dst_forward_recentrst" = 0 ] || args="$args --dst-forward-recentrst"
"$bin" \
-s "$ss_redir_servers" \
-l "$local_port_tcp" \
-L "$local_port_udp" \
--src-default "$src_default" \
--dst-default "$dst_default" \
--local-default "$local_default" \
--dst-bypass-file "$dst_ips_bypass_file" \
--dst-forward-file "$dst_ips_forward_file" \
--dst-bypass "$dst_ips_bypass" \
--dst-forward "$dst_ips_forward" \
--src-bypass "$src_ips_bypass" \
--src-forward "$src_ips_forward" \
--src-checkdst "$src_ips_checkdst" \
--ifnames "$ifnames" \
--ipt-extra "$ipt_args" \
$args \
|| "$bin" -f
}
start_service() {
local cfgtype
mkdir -p "$ss_confdir"
config_load shadowsocks-libev
for cfgtype in ss_local ss_redir ss_server ss_tunnel; do
config_foreach ss_xxx "$cfgtype" "$cfgtype"
done
ss_rules
}
stop_service() {
local bin="$ss_bindir/ss-rules"
[ -x "$bin" ] && "$bin" -f
rm -rf "$ss_confdir"
}
service_triggers() {
procd_add_reload_interface_trigger wan
procd_add_reload_trigger shadowsocks-libev
procd_open_validate
validate_server_section
validate_ss_local_section
validate_ss_redir_section
validate_ss_rules_section
validate_ss_server_section
validate_ss_tunnel_section
procd_close_validate
}
ss_validate_mklocal() {
local tuple opts
shift 2
for tuple in "$@"; do
opts="${tuple%%:*} $opts"
done
[ -z "$opts" ] || echo "local $opts"
}
ss_validate() {
uci_validate_section shadowsocks-libev "$@"
}
validate_common_server_options_() {
local cfgtype="$1"; shift
local cfg="$1"; shift
local func="$1"; shift
local stream_methods='"table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", "chacha20-ietf"'
local aead_methods='"aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305"'
"${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \
'disabled:bool:0' \
'server:host' \
'server_port:port' \
'password:string' \
'key:string' \
"method:or($stream_methods, $aead_methods)"
}
validate_common_client_options_() {
validate_common_options_ "$@" \
'server:uci("shadowsocks-libev", "@server")' \
'local_address:host:0.0.0.0' \
'local_port:port'
}
validate_common_options_() {
local cfgtype="$1"; shift
local cfg="$1"; shift
local func="$1"; shift
"${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \
'disabled:bool:0' \
'fast_open:bool:0' \
'ipv6_first:bool:0' \
'no_delay:bool:0' \
'reuse_port:bool:0' \
'mptcp:bool:0' \
'verbose:bool:0' \
'mode:or("tcp_only", "udp_only", "tcp_and_udp"):tcp_only' \
'mtu:uinteger' \
'timeout:uinteger' \
'user:string'
}
validate_server_section() {
validate_common_server_options_ server "$1" "${2}"
}
validate_ss_local_section() {
validate_common_client_options_ ss_local "$1" "${2}"
}
validate_ss_redir_section() {
validate_common_client_options_ ss_redir "$1" \
"${2}" \
'disable_sni:bool:0'
}
validate_ss_rules_section() {
"${2:-ss_validate}" ss_rules "$1" \
'disabled:bool:0' \
'redir_tcp:uci("shadowsocks-libev", "@ss_redir")' \
'redir_udp:uci("shadowsocks-libev", "@ss_redir")' \
'src_ips_bypass:or(ip4addr,cidr4)' \
'src_ips_forward:or(ip4addr,cidr4)' \
'src_ips_checkdst:or(ip4addr,cidr4)' \
'dst_ips_bypass_file:file' \
'dst_ips_bypass:or(ip4addr,cidr4)' \
'dst_ips_forward_file:file' \
'dst_ips_forward:or(ip4addr,cidr4)' \
'src_default:or("bypass", "forward", "checkdst"):checkdst' \
'dst_default:or("bypass", "forward"):bypass' \
'local_default:or("bypass", "forward", "checkdst"):bypass' \
'dst_forward_recentrst:bool:0' \
'ifnames:maxlength(15)' \
'ipt_args:string'
}
validate_ss_server_section() {
validate_common_server_options_ ss_server "$1" \
validate_common_options_ \
"${2}" \
'bind_address:ipaddr' \
'manager_address:host'
}
validate_ss_tunnel_section() {
validate_common_client_options_ ss_tunnel "$1" \
"${2}" \
'tunnel_address:regex(".+\:[0-9]+")'
}

View file

@ -0,0 +1,50 @@
#!/bin/sh
# vim: set noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 :
name=$0
basename="$(basename $0)"
usage() {
printf "Usage: %s: [-t TIMEOUT] [-v INTERVAL] [ -r RETRY ] HOST\n" "${name}"
exit 2
}
log() {
logger -p daemon.info -t "${basename}" "$@"
}
uri="ping"
timeout=5
interval=1
retry=0
while getopts "t:v:r:" opt; do
case $opt in
t) timeout="${OPTARG}";;
v) interval="${OPTARG}";;
r) retry="${OPTARG}";;
*) usage;;
esac
done
shift $((OPTIND - 1))
[ -z "$1" ] && usage
last=0
log "Starting..."
while true; do
if curl -s --max-time "${timeout}" --retry "${retry}" "$1/${uri}" &>/dev/null ; then
[ ${last} = 0 ] && log "Shadowsocks is up"
/etc/init.d/shadowsocks ifup 2> /dev/null
last=1
else
[ ${last} = 1 ] && log "Shadowsocks is down"
/etc/init.d/shadowsocks ifdown 2> /dev/null
last=0
fi
sleep "${interval}"
done

264
shadowsocks-libev/files/ss-rules Executable file
View file

@ -0,0 +1,264 @@
#!/bin/sh -e
#
# Copyright (C) 2017 Yousong Zhou <yszhou4tech@gmail.com>
#
# The design idea was derived from ss-rules by Jian Chang <aa65535@live.com>
#
# This is free software, licensed under the GNU General Public License v3.
# See /LICENSE for more information.
#
ss_rules_usage() {
cat >&2 <<EOF
Usage: ss-rules [options]
-h, --help Show this help message then exit
-f, --flush Flush rules, ipset then exit
-l <port> Local port number of ss-redir with TCP mode
-L <port> Local port number of ss-redir with UDP mode
-s <ips> List of ip addresses of remote shadowsocks server
--ifnames Only apply rules on packets from these ifnames
--src-bypass <ips|cidr>
--src-forward <ips|cidr>
--src-checkdst <ips|cidr>
--src-default <bypass|forward|checkdst>
Packets will have their src ip checked in order against
bypass, forward, checkdst list and will bypass, forward
through, or continue to have their dst ip checked
respectively on the first match. Otherwise, --src-default
decide the default action
--dst-bypass <ips|cidr>
--dst-forward <ips|cidr>
--dst-bypass-file <file>
--dst-forward-file <file>
--dst-default <bypass|forward>
Same as with their --src-xx equivalent
--dst-forward-recentrst
Forward those packets whose destinations have recently
sent to us multiple tcp-rst packets
--local-default <bypass|forward|checkdst>
Default action for local out TCP traffic
The following ipsets will be created by ss-rules. They are also intended to be
populated by other programs like dnsmasq with ipset support
ss_rules_src_bypass
ss_rules_src_forward
ss_rules_src_checkdst
ss_rules_dst_bypass
ss_rules_dst_forward
EOF
}
o_dst_bypass_="
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/24
192.0.2.0/24
192.31.196.0/24
192.52.193.0/24
192.88.99.0/24
192.168.0.0/16
192.175.48.0/24
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/4
240.0.0.0/4
255.255.255.255
"
o_src_default=bypass
o_dst_default=bypass
o_local_default=bypass
__errmsg() {
echo "ss-rules: $*" >&2
}
ss_rules_parse_args() {
while [ "$#" -gt 0 ]; do
case "$1" in
-h|--help) ss_rules_usage; exit 0;;
-f|--flush) ss_rules_flush; exit 0;;
-l) o_redir_tcp_port="$2"; shift 2;;
-L) o_redir_udp_port="$2"; shift 2;;
-s) o_remote_servers="$2"; shift 2;;
--ifnames) o_ifnames="$2"; shift 2;;
--ipt-extra) o_ipt_extra="$2"; shift 2;;
--src-default) o_src_default="$2"; shift 2;;
--dst-default) o_dst_default="$2"; shift 2;;
--local-default) o_local_default="$2"; shift 2;;
--src-bypass) o_src_bypass="$2"; shift 2;;
--src-forward) o_src_forward="$2"; shift 2;;
--src-checkdst) o_src_checkdst="$2"; shift 2;;
--dst-bypass) o_dst_bypass="$2"; shift 2;;
--dst-forward) o_dst_forward="$2"; shift 2;;
--dst-forward-recentrst) o_dst_forward_recentrst=1; shift 1;;
--dst-bypass-file) o_dst_bypass_file="$2"; shift 2;;
--dst-forward-file) o_dst_forward_file="$2"; shift 2;;
*) __errmsg "unknown option $1"; return 1;;
esac
done
if [ -z "$o_redir_tcp_port" -a -z "$o_redir_udp_port" ]; then
__errmsg "Requires at least -l or -L option"
return 1
fi
if [ -n "$o_dst_forward_recentrst" ] && ! iptables -m recent -h >/dev/null; then
__errmsg "Please install iptables-mod-conntrack-extra with opkg"
return 1
fi
o_remote_servers="$(for s in $o_remote_servers; do resolveip -4 "$s"; done)"
}
ss_rules_flush() {
local setname
iptables-save --counters | grep -v ss_rules_ | iptables-restore --counters
while ip rule del fwmark 1 lookup 100 2>/dev/null; do true; done
ip route flush table 100
for setname in $(ipset -n list | grep "ss_rules_"); do
ipset destroy "$setname" 2>/dev/null || true
done
}
ss_rules_ipset_init() {
ipset --exist restore <<-EOF
create ss_rules_src_bypass hash:net hashsize 64
create ss_rules_src_forward hash:net hashsize 64
create ss_rules_src_checkdst hash:net hashsize 64
create ss_rules_dst_bypass hash:net hashsize 64
create ss_rules_dst_bypass_ hash:net hashsize 64
create ss_rules_dst_forward hash:net hashsize 64
create ss_rules_dst_forward_recentrst_ hash:ip hashsize 64 timeout 3600
$(ss_rules_ipset_mkadd ss_rules_dst_bypass_ "$o_dst_bypass_ $o_remote_servers")
$(ss_rules_ipset_mkadd ss_rules_src_bypass "$o_src_bypass")
$(ss_rules_ipset_mkadd ss_rules_src_forward "$o_src_forward")
$(ss_rules_ipset_mkadd ss_rules_src_checkdst "$o_src_checkdst")
$(ss_rules_ipset_mkadd ss_rules_dst_bypass "$o_dst_bypass $(cat "$o_dst_bypass_file" 2>/dev/null)")
$(ss_rules_ipset_mkadd ss_rules_dst_forward "$o_dst_forward $(cat "$o_dst_forward_file" 2>/dev/null)")
EOF
}
ss_rules_ipset_mkadd() {
local setname="$1"; shift
local i
for i in $*; do
echo "add $setname $i"
done
}
ss_rules_iptchains_init() {
ss_rules_iptchains_init_tcp
ss_rules_iptchains_init_udp
}
ss_rules_iptchains_init_tcp() {
local local_target
[ -n "$o_redir_tcp_port" ] || return 0
ss_rules_iptchains_init_ nat tcp
case "$o_local_default" in
checkdst) local_target=ss_rules_dst ;;
forward) local_target=ss_rules_forward ;;
bypass|*) return 0;;
esac
iptables-restore --noflush <<-EOF
*nat
:ss_rules_local_out -
-I OUTPUT 1 -p tcp -j ss_rules_local_out
-A ss_rules_local_out -m set --match-set ss_rules_dst_bypass_ dst -j RETURN
-A ss_rules_local_out -p tcp $o_ipt_extra -j $local_target -m comment --comment "local_default: $o_local_default"
COMMIT
EOF
}
ss_rules_iptchains_init_udp() {
[ -n "$o_redir_udp_port" ] || return 0
ss_rules_iptchains_init_ mangle udp
}
ss_rules_iptchains_init_() {
local table="$1"
local proto="$2"
local forward_rules
local src_default_target dst_default_target
local recentrst_mangle_rules recentrst_addset_rules
case "$proto" in
tcp)
forward_rules="-A ss_rules_forward -p tcp -j REDIRECT --to-ports $o_redir_tcp_port"
if [ -n "$o_dst_forward_recentrst" ]; then
recentrst_mangle_rules="
*mangle
-I PREROUTING 1 -p tcp -m tcp --tcp-flags RST RST -m recent --name ss_rules_recentrst --set --rsource
COMMIT
"
recentrst_addset_rules="
-A ss_rules_dst -m recent --name ss_rules_recentrst --rcheck --rdest --seconds 3 --hitcount 3 -j SET --add-set ss_rules_dst_forward_recentrst_ dst --exist
-A ss_rules_dst -m set --match-set ss_rules_dst_forward_recentrst_ dst -j ss_rules_forward
"
fi
;;
udp)
ip rule add fwmark 1 lookup 100
ip route add local default dev lo table 100
forward_rules="-A ss_rules_forward -p udp -j TPROXY --on-port "$o_redir_udp_port" --tproxy-mark 0x01/0x01"
;;
esac
case "$o_src_default" in
forward) src_default_target=ss_rules_forward ;;
checkdst) src_default_target=ss_rules_dst ;;
bypass|*) src_default_target=RETURN ;;
esac
case "$o_dst_default" in
forward) dst_default_target=ss_rules_forward ;;
bypass|*) dst_default_target=RETURN ;;
esac
sed -e '/^\s*$/d' -e 's/^\s\+//' <<-EOF | iptables-restore --noflush
*$table
:ss_rules_pre_src -
:ss_rules_src -
:ss_rules_dst -
:ss_rules_forward -
$(ss_rules_iptchains_mkprerules "$proto")
-A ss_rules_pre_src -m set --match-set ss_rules_dst_bypass_ dst -j RETURN
-A ss_rules_pre_src -p $proto $o_ipt_extra -j ss_rules_src
-A ss_rules_src -m set --match-set ss_rules_src_bypass src -j RETURN
-A ss_rules_src -m set --match-set ss_rules_src_forward src -j ss_rules_forward
-A ss_rules_src -m set --match-set ss_rules_src_checkdst src -j ss_rules_dst
-A ss_rules_src -j $src_default_target -m comment --comment "src_default: $o_src_default"
-A ss_rules_dst -m set --match-set ss_rules_dst_bypass dst -j RETURN
-A ss_rules_dst -m set --match-set ss_rules_dst_forward dst -j ss_rules_forward
$recentrst_addset_rules
-A ss_rules_dst -j $dst_default_target -m comment --comment "dst_default: $o_dst_default"
$forward_rules
COMMIT
$recentrst_mangle_rules
EOF
}
ss_rules_iptchains_mkprerules() {
local proto="$1"
if [ -z "$o_ifnames" ]; then
echo "-I PREROUTING 1 -p $proto -j ss_rules_pre_src"
else
echo $o_ifnames \
| tr ' ' '\n' \
| sed "s/.*/-I PREROUTING 1 -i \\0 -p $proto -j ss_rules_pre_src/"
fi
}
ss_rules_parse_args "$@"
ss_rules_flush
ss_rules_ipset_init
ss_rules_iptchains_init

View file

@ -0,0 +1,10 @@
#!/bin/sh
s=firewall.ss_rules
uci get "$s" >/dev/null && exit 0
uci batch <<-EOF
set $s=include
set $s.path=/etc/firewall.ss-rules
set $s.reload=1
commit firewall
EOF

View file

@ -0,0 +1,14 @@
Index: shadowsocks-libev-3.1.2/src/redir.c
===================================================================
--- shadowsocks-libev-3.1.2.orig/src/redir.c
+++ shadowsocks-libev-3.1.2/src/redir.c
@@ -212,7 +212,8 @@ server_recv_cb(EV_P_ ev_io *w, int reven
// continue to wait for recv
return;
} else {
- ERROR("server recv");
+ if (errno != ECONNRESET)
+ ERROR("server recv");
close_and_free_remote(EV_A_ remote);
close_and_free_server(EV_A_ server);
return;