From b61af9703e64dff5e3a1deff1ce1886c1470dbe7 Mon Sep 17 00:00:00 2001 From: Yousong Zhou Date: Sat, 24 Jun 2017 08:56:18 +0800 Subject: [PATCH] shadowsocks-libev: rewrite - Selecting only a single or subset of all components of shadowsocks-libev is now possible (this is the main motivation behind the rewrite) - Configuring multiple instances of the same component is now also possible - Same option names as with the json config - Unified configuration generation method for each component - Add support for ss-local, ss-tunnel, ss-server - Most data validation is now done with validate_data - USE_PROCD=1 - Update ss-rules with the one from shadowsocks/luci-app-shadowsocks - Add README.md - Set myself as the maintainer Addresses #4435 Signed-off-by: Yousong Zhou --- net/shadowsocks-libev/Makefile | 115 +++-- net/shadowsocks-libev/README.md | 86 ++++ net/shadowsocks-libev/files/firewall.include | 6 - net/shadowsocks-libev/files/firewall.ss-rules | 2 + .../files/shadowsocks-libev.config | 71 ++- .../files/shadowsocks-libev.init | 436 ++++++++++++------ net/shadowsocks-libev/files/ss-rules | 323 +++++++------ net/shadowsocks-libev/files/ss-rules.defaults | 10 + ...1-decouple-use_syslog-from-pid_flags.patch | 151 ++++++ 9 files changed, 865 insertions(+), 335 deletions(-) create mode 100644 net/shadowsocks-libev/README.md delete mode 100644 net/shadowsocks-libev/files/firewall.include create mode 100644 net/shadowsocks-libev/files/firewall.ss-rules create mode 100755 net/shadowsocks-libev/files/ss-rules.defaults create mode 100644 net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch diff --git a/net/shadowsocks-libev/Makefile b/net/shadowsocks-libev/Makefile index 8b5123245c..31d5d74ffa 100644 --- a/net/shadowsocks-libev/Makefile +++ b/net/shadowsocks-libev/Makefile @@ -1,5 +1,4 @@ # -# Copyright (C) 2015 OpenWrt.org # Copyright (C) 2017 Yousong Zhou # # This is free software, licensed under the GNU General Public License v2. @@ -8,67 +7,109 @@ 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 TCP_ONLY +# - check if ss-rules has been upstreamed +# +# TODO +# +# - add validate type: user +# PKG_NAME:=shadowsocks-libev PKG_VERSION:=3.0.6 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://github.com/shadowsocks/shadowsocks-libev/releases/download/v$(PKG_VERSION) PKG_HASH:=7d9b43b0235a57c115bfe160efd54abef96bffcbfff61c5496e7c2800f0734ca -PKG_MAINTAINER:=Jian Chang -PKG_LICENSE:=GPLv2 +PKG_MAINTAINER:=Yousong Zhou + +PKG_LICENSE:=GPL-3.0+ PKG_LICENSE_FILES:=LICENSE -PKG_INSTALL:=1 PKG_FIXUP:=autoreconf +PKG_INSTALL:=1 PKG_USE_MIPS16:=0 PKG_BUILD_PARALLEL:=1 include $(INCLUDE_DIR)/package.mk -define Package/shadowsocks-libev - SECTION:=net - CATEGORY:=Network - TITLE:=Lightweight Secured Socks5 Proxy - URL:=https://github.com/shadowsocks/shadowsocks-libev - DEPENDS:=+libev +libmbedtls +libpthread +libsodium +libudns \ - +ipset +ip +iptables-mod-tproxy +libpcre +zlib + +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/description -Shadowsocks-libev is a lightweight secured socks5 proxy for embedded devices and low end boxes. +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 +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:=+libev +libmbedtls +libpcre +libpthread +libsodium +libudns +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 -define Package/shadowsocks-libev/conffiles -/etc/config/shadowsocks-libev +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/postinst -#!/bin/sh -uci -q batch <<-EOF >/dev/null - delete firewall.shadowsocks_libev - set firewall.shadowsocks_libev=include - set firewall.shadowsocks_libev.type=script - set firewall.shadowsocks_libev.path=/usr/share/shadowsocks-libev/firewall.include - set firewall.shadowsocks_libev.reload=1 - commit firewall -EOF -exit 0 + +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 +shadowsocks-libev-ss-redir +shadowsocks-libev-config endef -define Package/shadowsocks-libev/install +define Package/shadowsocks-libev-ss-rules/install $(INSTALL_DIR) $(1)/usr/bin - $(INSTALL_BIN) $(PKG_BUILD_DIR)/src/ss-{redir,tunnel} $(1)/usr/bin $(INSTALL_BIN) ./files/ss-rules $(1)/usr/bin - $(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/share/shadowsocks-libev - $(INSTALL_DATA) ./files/firewall.include $(1)/usr/share/shadowsocks-libev/firewall.include + $(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 -CONFIGURE_ARGS += --disable-documentation -$(eval $(call BuildPackage,shadowsocks-libev)) +$(eval $(call BuildPackage,shadowsocks-libev-config)) +$(eval $(call BuildPackage,shadowsocks-libev-ss-rules)) +$(foreach component,$(SHADOWSOCKS_COMPONENTS), \ + $(eval $(call BuildPackage,shadowsocks-libev-$(component))) \ +) diff --git a/net/shadowsocks-libev/README.md b/net/shadowsocks-libev/README.md new file mode 100644 index 0000000000..75790ea69c --- /dev/null +++ b/net/shadowsocks-libev/README.md @@ -0,0 +1,86 @@ +## components + +`ss-local` provides SOCKS5 proxy. + + socks5 ss plain + --------> tcp:udp: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 + + --- 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] + + --- for local out tcp packets, the default action can be specified with + + local_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 + +## 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 diff --git a/net/shadowsocks-libev/files/firewall.include b/net/shadowsocks-libev/files/firewall.include deleted file mode 100644 index 3a00e80258..0000000000 --- a/net/shadowsocks-libev/files/firewall.include +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh - -if pidof ss-redir>/dev/null; then - /etc/init.d/shadowsocks-libev rules - logger -t ShadowSocks-libev "Reloading ShadowSocks-libev due to restart of firewall" -fi diff --git a/net/shadowsocks-libev/files/firewall.ss-rules b/net/shadowsocks-libev/files/firewall.ss-rules new file mode 100644 index 0000000000..3a1d32cdc1 --- /dev/null +++ b/net/shadowsocks-libev/files/firewall.ss-rules @@ -0,0 +1,2 @@ +#!/bin/sh +/etc/init.d/shadowsocks-libev reload diff --git a/net/shadowsocks-libev/files/shadowsocks-libev.config b/net/shadowsocks-libev/files/shadowsocks-libev.config index 95aec7b2ec..9b3fe0852b 100644 --- a/net/shadowsocks-libev/files/shadowsocks-libev.config +++ b/net/shadowsocks-libev/files/shadowsocks-libev.config @@ -1,15 +1,60 @@ - -config shadowsocks-libev - option enable '1' - option server '127.0.0.1' - option server_port '8388' +config ss_local + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' option local_port '1080' - option password 'barfoo!' + option timeout '30' + +config ss_tunnel + option disabled 1 + option server 'sss0' + option local_address '0.0.0.0' + option local_port '1090' + option tunnel_address 'example.com:80' + option mode 'tcp_and_udp' option timeout '60' - option encrypt_method 'rc4-md5' - option ignore_list '/dev/null' - option udp_mode '0' - option tunnel_enable '1' - option tunnel_port '5300' - option tunnel_forward '8.8.4.4:53' - option lan_ac_mode '0' + +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 + +config ss_redir hj + 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 + +config ss_rules 'ss_rules' + option disabled 1 + option redir_tcp 'hi' + option redir_udp 'hi' + option src_default 'bypass' + option local_default 'checkdst' + list src_ips_forward '192.168.1.4' + list dst_ips_forward '8.8.8.8' + +config server 'sss0' + option disabled 1 + option server '192.168.1.3' + option server_port '9001' + option password '********' + option method 'aes-256-cfb' + +config ss_server + option disabled 1 + option server_port '9001' + option password '********' + option method 'aes-256-cfb' + option bind_address '192.168.7.72' diff --git a/net/shadowsocks-libev/files/shadowsocks-libev.init b/net/shadowsocks-libev/files/shadowsocks-libev.init index 9a64038a72..68ec93b035 100644 --- a/net/shadowsocks-libev/files/shadowsocks-libev.init +++ b/net/shadowsocks-libev/files/shadowsocks-libev.init @@ -1,156 +1,316 @@ #!/bin/sh /etc/rc.common +# +# Copyright (C) 2017 Yousong Zhou +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# -START=90 -STOP=15 - -SERVICE_USE_PID=1 -SERVICE_WRITE_PID=1 -SERVICE_DAEMONIZE=1 -EXTRA_COMMANDS="rules" -CONFIG_FILE=/var/etc/shadowsocks-libev.json - -get_config() { - config_get_bool enable $1 enable - config_get server $1 server - config_get server_port $1 server_port - config_get local_port $1 local_port - config_get timeout $1 timeout - config_get password $1 password - config_get encrypt_method $1 encrypt_method - config_get ignore_list $1 ignore_list - config_get udp_mode $1 udp_mode - config_get udp_server $1 udp_server - config_get udp_server_port $1 udp_server_port - config_get udp_local_port $1 udp_local_port - config_get udp_timeout $1 udp_timeout - config_get udp_password $1 udp_password - config_get udp_encrypt_method $1 udp_encrypt_method - config_get_bool tunnel_enable $1 tunnel_enable - config_get tunnel_port $1 tunnel_port - config_get tunnel_forward $1 tunnel_forward - config_get lan_ac_mode $1 lan_ac_mode - config_get lan_ac_ip $1 lan_ac_ip - config_get wan_bp_ip $1 wan_bp_ip - config_get wan_fw_ip $1 wan_fw_ip - config_get ipt_ext $1 ipt_ext - : ${timeout:=60} - : ${udp_timeout:=60} - : ${tunnel_port:=5300} - : ${tunnel_forward:=8.8.4.4:53} -} - -start_rules() { - local ac_args - - if [ -n "$lan_ac_ip" ]; then - case $lan_ac_mode in - 1) ac_args="w$lan_ac_ip" - ;; - 2) ac_args="b$lan_ac_ip" - ;; - esac +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 - /usr/bin/ss-rules \ - -s "$server" \ - -l "$local_port" \ - -S "$udp_server" \ - -L "$udp_local_port" \ - -i "$ignore_list" \ - -a "$ac_args" \ - -b "$wan_bp_ip" \ - -w "$wan_fw_ip" \ - -e "$ipt_ext" \ - -o $udp - return $? -} - -start_redir() { - cat <<-EOF >$CONFIG_FILE - { - "server": "$server", - "server_port": $server_port, - "local_address": "0.0.0.0", - "local_port": $local_port, - "password": "$password", - "timeout": $timeout, - "method": "$encrypt_method" - } -EOF - if [ "$udp_mode" = 2 ]; then - /usr/bin/ss-redir \ - -c $CONFIG_FILE \ - -f /var/run/ss-redir_t.pid - cat <<-EOF >$CONFIG_FILE - { - "server": "$udp_server", - "server_port": $udp_server_port, - "local_address": "0.0.0.0", - "local_port": $udp_local_port, - "password": "$udp_password", - "timeout": $udp_timeout, - "method": "$udp_encrypt_method" - } -EOF +} + +ss_mkjson_() { + local func + + for func in "$@"; do + if ! "$func"; then + return 1 + fi + 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 + 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() { + [ "$fast_open" = 0 ] && fast_open=false || fast_open=true + [ "$reuse_port" = 0 ] && reuse_port=false || reuse_port=true + cat <<-EOF + "use_syslog": true, + "fast_open": $fast_open, + "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 +} + +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" + [ "$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 + [ -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 respawn + procd_close_instance + ss_rules_cb "$cfg" fi - /usr/bin/ss-redir \ - -c $CONFIG_FILE \ - -f /var/run/ss-redir.pid \ - $udp - return $? } -start_tunnel() { - : ${udp:="-u"} - /usr/bin/ss-tunnel \ - -c $CONFIG_FILE \ - -l $tunnel_port \ - -L $tunnel_forward \ - -f /var/run/ss-tunnel.pid \ - $udp - return $? +ss_rules_cb() { + local cfgserver + local server + + [ "$cfgtype" != ss_server ] || return + config_get cfgserver "$cfg" server + config_get server "$cfgserver" server + + ss_rules_servers="$ss_rules_servers $server" + if [ "$cfgtype" = ss_redir ]; then + 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" + eval "ss_rules_redir_server_udp_$cfg=$server" + fi + fi } -rules() { - config_load shadowsocks-libev - config_foreach get_config shadowsocks-libev - [ "$enable" = 1 ] || exit 0 - mkdir -p /var/run /var/etc - - : ${server:?} - : ${server_port:?} - : ${local_port:?} - : ${password:?} - : ${encrypt_method:?} - case $udp_mode in - 1) udp="-u" - ;; - 2) - udp="-U" - : ${udp_server:?} - : ${udp_server_port:?} - : ${udp_local_port:?} - : ${udp_password:?} - : ${udp_encrypt_method:?} - ;; +ss_rules() { + local cfg="ss_rules" + local bin="$ss_bindir/ss-rules" + local cfgtype + local args local_port_tcp local_port_udp server_udp + local i a_args d_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" + [ "$disabled" = 0 ] || return 1 + + eval local_port_tcp="\$ss_rules_redir_tcp_$redir_tcp" + eval local_port_udp="\$ss_rules_redir_udp_$redir_udp" + eval server_udp="\$ss_rules_redir_server_udp_$redir_udp" + [ -z "$local_port_udp" ] || args="$args -U" + case "$local_default" in + forward) args="$args -O" ;; + checkdst) args="$args -o" ;; + esac + case "$src_default" in + bypass) d_args=RETURN ;; + forward) d_args=SS_SPEC_WAN_FW ;; + checkdst) d_args=SS_SPEC_WAN_AC ;; esac + ss_rules_servers="$(echo "$ss_rules_servers" | tr ' ' '\n' | sort -u)" + for i in $src_ips_bypass; do a_args="b,$i $a_args"; done + for i in $src_ips_forward; do a_args="g,$i $a_args"; done + for i in $src_ips_checkdst; do a_args="n,$i $a_args"; done + + "$bin" \ + -s "$ss_rules_servers" \ + -l "$local_port_tcp" \ + -S "$server_udp" \ + -L "$local_port_udp" \ + -B "$dst_ips_bypass_file" \ + -W "$dst_ips_forward_file" \ + -b "$dst_ips_bypass" \ + -w "$dst_ips_forward" \ + -e "$ipt_args" \ + -a "$a_args" \ + -d "$d_args" \ + $args \ + || "$bin" -f +} + +start_service() { + local cfgtype="$1" + + 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" +} - start_rules +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 } -boot() { - until iptables-save -t nat | grep -q "^:zone_lan_prerouting"; do - sleep 1 +ss_validate_mklocal() { + local tuple opts + + shift 2 + for tuple in "$@"; do + opts="${tuple%%:*} $opts" done - start + [ -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"' + + "${func:-ss_validate}" "$cfgtype" "$cfg" "$@" \ + 'disabled:bool:false' \ + '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:false' \ + 'verbose:bool:false' \ + 'fast_open:bool:false' \ + 'reuse_port:bool:false' \ + 'mode:or("tcp_only", "udp_only", "tcp_and_udp")' \ + '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}" +} + +validate_ss_rules_section() { + "${2:-ss_validate}" ss_rules "$1" \ + 'disabled:bool:false' \ + 'redir_tcp:uci("shadowsocks-libev", "@ss_redir")' \ + 'redir_udp:uci("shadowsocks-libev", "@ss_redir")' \ + 'src_ips_bypass:list(ipaddr)' \ + 'src_ips_forward:list(ipaddr)' \ + 'src_ips_checkdst:list(ipaddr)' \ + 'dst_ips_bypass_file:file' \ + 'dst_ips_bypass:list(ipaddr)' \ + 'dst_ips_forward_file:file' \ + 'dst_ips_forward:list(ipaddr)' \ + 'src_default:or("bypass", "forward", "checkdst")' \ + 'local_default:or("bypass", "forward", "checkdst")' \ + 'ipt_args:string' } -start() { - rules && start_redir - [ "$tunnel_enable" = 1 ] && start_tunnel +validate_ss_server_section() { + validate_common_server_options_ ss_server "$1" \ + validate_common_options_ \ + "${2}" \ + 'bind_address:ipaddr' \ + 'manager_address:host' } -stop() { - /usr/bin/ss-rules -f - killall -q -9 ss-redir - killall -q -9 ss-tunnel +validate_ss_tunnel_section() { + validate_common_client_options_ ss_tunnel "$1" \ + "${2}" \ + 'tunnel_address:regex(".+\:[0-9]+")' } diff --git a/net/shadowsocks-libev/files/ss-rules b/net/shadowsocks-libev/files/ss-rules index 8ce1000cbc..8bd7264af1 100644 --- a/net/shadowsocks-libev/files/ss-rules +++ b/net/shadowsocks-libev/files/ss-rules @@ -1,4 +1,10 @@ #!/bin/sh +# +# Copyright (C) 2014-2017 Jian Chang +# +# This is free software, licensed under the GNU General Public License v3. +# See /LICENSE for more information. +# usage() { cat <<-EOF @@ -6,20 +12,28 @@ usage() { Valid options are: - -s hostname or ip of shadowsocks remote server + -s ip address of shadowsocks remote server -l port number of shadowsocks local server - -i a file content is bypassed ip list - -a lan ip of access control, need a prefix to - define access control mode + -S ip address of shadowsocks remote UDP server + -L port number of shadowsocks local UDP server + -B a file whose content is bypassed ip list -b wan ip of will be bypassed + -W a file whose content is forwarded ip list -w wan ip of will be forwarded - -e extra options for iptables + -I proxy only for the given interface + -d the default target of lan access control + -a lan ip of access control, need a prefix to + define proxy type + -e extra arguments for iptables -o apply the rules to the OUTPUT chain + -O apply the global rules to the OUTPUT chain -u enable udprelay mode, TPROXY is required -U enable udprelay mode, using different IP and ports for TCP and UDP -f flush the rules + -h show this help message and exit EOF + exit $1 } loger() { @@ -27,135 +41,192 @@ loger() { logger -st ss-rules[$$] -p$1 $2 } -ipt_n="iptables -t nat" -ipt_m="iptables -t mangle" - -flush_r() { - local IPT - - IPT=$(iptables-save -t nat) - eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \ - sed -e 's/^-A/$ipt_n -D/' -e 's/$/;/') - - for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do - $ipt_n -F ${chain:1} 2>/dev/null && $ipt_n -X ${chain:1} - done - - IPT=$(iptables-save -t mangle) - eval $(echo "$IPT" | grep "_SS_SPEC_RULE_" | \ - sed -e 's/^-A/$ipt_m -D/' -e 's/$/;/') - - for chain in $(echo "$IPT" | awk '/^:SS_SPEC/{print $1}'); do - $ipt_m -F ${chain:1} 2>/dev/null && $ipt_m -X ${chain:1} +flush_rules() { + iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c + if command -v ip >/dev/null 2>&1; then + ip rule del fwmark 1 lookup 100 2>/dev/null + ip route del local default dev lo table 100 2>/dev/null + fi + for setname in $(ipset -n list | grep "ss_spec"); do + ipset destroy $setname 2>/dev/null done - - ip rule del fwmark 0x01/0x01 table 100 2>/dev/null - ip route del local 0.0.0.0/0 dev lo table 100 2>/dev/null - ipset -X ss_spec_lan_ac 2>/dev/null - ipset -X ss_spec_wan_ac 2>/dev/null + FWI=$(uci get firewall.shadowsocks.path 2>/dev/null) + [ -n "$FWI" ] && echo '# firewall include file' >$FWI return 0 } -ipset_r() { - ipset -! -R <<-EOF || return 1 - create ss_spec_wan_ac hash:net - $(echo -e "$IPLIST" | sed -e "s/^/add ss_spec_wan_ac /") - $(for ip in $WAN_FW_IP; do echo "add ss_spec_wan_ac $ip nomatch"; done) +ipset_init() { + ipset -! restore <<-EOF || return 1 + create ss_spec_src_ac hash:ip hashsize 64 + create ss_spec_src_bp hash:ip hashsize 64 + create ss_spec_src_fw hash:ip hashsize 64 + create ss_spec_dst_sp hash:net hashsize 64 + create ss_spec_dst_bp hash:net hashsize 64 + create ss_spec_dst_fw hash:net hashsize 64 + $(gen_lan_host_ipset_entry) + $(gen_special_purpose_ip | sed -e "s/^/add ss_spec_dst_sp /") + $(sed -e "s/^/add ss_spec_dst_bp /" ${WAN_BP_LIST:=/dev/null} 2>/dev/null) + $(for ip in $WAN_BP_IP; do echo "add ss_spec_dst_bp $ip"; done) + $(sed -e "s/^/add ss_spec_dst_fw /" ${WAN_FW_LIST:=/dev/null} 2>/dev/null) + $(for ip in $WAN_FW_IP; do echo "add ss_spec_dst_fw $ip"; done) EOF - $ipt_n -N SS_SPEC_WAN_AC && \ - $ipt_n -A SS_SPEC_WAN_AC -m set --match-set ss_spec_wan_ac dst -j RETURN && \ - $ipt_n -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW - return $? + return 0 } -fw_rule() { - $ipt_n -N SS_SPEC_WAN_FW && \ - $ipt_n -A SS_SPEC_WAN_FW -p tcp \ - -j REDIRECT --to-ports $local_port 2>/dev/null || { - loger 3 "Can't redirect, please check the iptables." - exit 1 - } +ipt_nat() { + include_ac_rules nat + ipt="iptables -t nat" + $ipt -A SS_SPEC_WAN_FW -p tcp \ + -j REDIRECT --to-ports $local_port || return 1 + if [ -n "$OUTPUT" ]; then + $ipt -N SS_SPEC_WAN_DG + $ipt -A SS_SPEC_WAN_DG -m set --match-set ss_spec_dst_sp dst -j RETURN + $ipt -A SS_SPEC_WAN_DG -p tcp $EXT_ARGS -j $OUTPUT + $ipt -I OUTPUT 1 -p tcp -j SS_SPEC_WAN_DG + fi return $? } -ac_rule() { - local TAG ROUTECHAIN - - if [ -n "$LAN_AC_IP" ]; then - if [ "${LAN_AC_IP:0:1}" = "w" ]; then - TAG="nomatch" - else - if [ "${LAN_AC_IP:0:1}" != "b" ]; then - loger 3 "Bad argument \`-a $LAN_AC_IP\`." - return 2 - fi - fi +ipt_mangle() { + [ -n "$TPROXY" ] || return 0 + if !(lsmod | grep -q TPROXY && command -v ip >/dev/null); then + loger 4 "TPROXY or ip not found." + return 0 fi + ip rule add fwmark 1 lookup 100 + ip route add local default dev lo table 100 + include_ac_rules mangle + iptables -t mangle -A SS_SPEC_WAN_FW -p udp \ + -j TPROXY --on-port $LOCAL_PORT --tproxy-mark 0x01/0x01 + return $? +} - ROUTECHAIN=PREROUTING - if iptables-save -t nat | grep -q "^:zone_lan_prerouting"; then - ROUTECHAIN=zone_lan_prerouting - fi +export_ipt_rules() { + [ -n "$FWI" ] || return 0 + cat <<-CAT >>$FWI + iptables-save -c | grep -v "SS_SPEC" | iptables-restore -c + iptables-restore -n <<-EOF + $(iptables-save | grep -E "SS_SPEC|^\*|^COMMIT" |\ + sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/") + EOF +CAT + return $? +} + +gen_lan_host_ipset_entry() { + for host in $LAN_HOSTS; do + case "${host:0:1}" in + n|N) + echo add ss_spec_src_ac ${host:2} + ;; + b|B) + echo add ss_spec_src_bp ${host:2} + ;; + g|G) + echo add ss_spec_src_fw ${host:2} + ;; + esac + done +} - ipset -! -R <<-EOF || return 1 - create ss_spec_lan_ac hash:net - $(for ip in ${LAN_AC_IP:1}; do echo "add ss_spec_lan_ac $ip $TAG"; done) +gen_special_purpose_ip() { + cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}" + 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 + $server + $SERVER EOF - $ipt_n -A $ROUTECHAIN -p tcp $EXT_ARGS \ - -m set ! --match-set ss_spec_lan_ac src \ - -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC +} - if [ "$OUTPUT" = 1 ]; then - $ipt_n -A OUTPUT -p tcp $EXT_ARGS \ - -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_WAN_AC - fi - return $? +include_ac_rules() { + local protocol=$([ "$1" = "mangle" ] && echo udp || echo tcp) + iptables-restore -n <<-EOF + *$1 + :SS_SPEC_LAN_DG - [0:0] + :SS_SPEC_LAN_AC - [0:0] + :SS_SPEC_WAN_AC - [0:0] + :SS_SPEC_WAN_FW - [0:0] + -A SS_SPEC_LAN_DG -m set --match-set ss_spec_dst_sp dst -j RETURN + -A SS_SPEC_LAN_DG -p $protocol $EXT_ARGS -j SS_SPEC_LAN_AC + -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_bp src -j RETURN + -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_fw src -j SS_SPEC_WAN_FW + -A SS_SPEC_LAN_AC -m set --match-set ss_spec_src_ac src -j SS_SPEC_WAN_AC + -A SS_SPEC_LAN_AC -j ${LAN_TARGET:=SS_SPEC_WAN_AC} + -A SS_SPEC_WAN_AC -m set --match-set ss_spec_dst_fw dst -j SS_SPEC_WAN_FW + -A SS_SPEC_WAN_AC -m set --match-set ss_spec_dst_bp dst -j RETURN + -A SS_SPEC_WAN_AC -j SS_SPEC_WAN_FW + $(gen_prerouting_rules $protocol) + COMMIT +EOF } -tp_rule() { - [ -n "$TPROXY" ] || return 0 - ip rule add fwmark 0x01/0x01 table 100 - ip route add local 0.0.0.0/0 dev lo table 100 - $ipt_m -N SS_SPEC_TPROXY - $ipt_m -A SS_SPEC_TPROXY -p udp -m set ! --match-set ss_spec_wan_ac dst \ - -j TPROXY --on-port $LOCAL_PORT --tproxy-mark 0x01/0x01 - $ipt_m -A PREROUTING -p udp $EXT_ARGS \ - -m set ! --match-set ss_spec_lan_ac src \ - -m comment --comment "_SS_SPEC_RULE_" -j SS_SPEC_TPROXY - return $? +gen_prerouting_rules() { + [ -z "$IFNAMES" ] && echo -I PREROUTING 1 -p $1 -j SS_SPEC_LAN_DG + for ifname in $IFNAMES; do + echo -I PREROUTING 1 -i $ifname -p $1 -j SS_SPEC_LAN_DG + done } -while getopts ":s:l:S:L:i:e:a:b:w:ouUf" arg; do - case $arg in +while getopts ":s:l:S:L:B:b:W:w:I:d:a:e:oOuUfh" arg; do + case "$arg" in s) - server=$OPTARG + server=$(for ip in $OPTARG; do echo $ip; done) ;; l) local_port=$OPTARG ;; S) - SERVER=$OPTARG + SERVER=$(for ip in $OPTARG; do echo $ip; done) ;; L) LOCAL_PORT=$OPTARG ;; - i) - IGNORE=$OPTARG - ;; - e) - EXT_ARGS=$OPTARG - ;; - a) - LAN_AC_IP=$OPTARG + B) + WAN_BP_LIST=$OPTARG ;; b) - WAN_BP_IP=$(for ip in $OPTARG; do echo $ip; done) + WAN_BP_IP=$OPTARG + ;; + W) + WAN_FW_LIST=$OPTARG ;; w) WAN_FW_IP=$OPTARG ;; + I) + IFNAMES=$OPTARG + ;; + d) + LAN_TARGET=$OPTARG + ;; + a) + LAN_HOSTS=$OPTARG + ;; + e) + EXT_ARGS=$OPTARG + ;; o) - OUTPUT=1 + OUTPUT=SS_SPEC_WAN_AC + ;; + O) + OUTPUT=SS_SPEC_WAN_FW ;; u) TPROXY=1 @@ -164,56 +235,26 @@ while getopts ":s:l:S:L:i:e:a:b:w:ouUf" arg; do TPROXY=2 ;; f) - flush_r + flush_rules exit 0 ;; + h) + usage 0 + ;; esac done -if [ -z "$server" -o -z "$local_port" ]; then - usage - exit 2 -fi +[ -z "$server" -o -z "$local_port" ] && usage 2 if [ "$TPROXY" = 1 ]; then - SERVER=$server + unset SERVER LOCAL_PORT=$local_port +elif [ "$TPROXY" = 2 ]; then + : ${SERVER:?"You must assign an ip for the udp relay server."} + : ${LOCAL_PORT:?"You must assign a port for the udp relay server."} fi -if [ "$TPROXY" = 2 ]; then - if [ -z "$SERVER" -o -z "$LOCAL_PORT" ]; then - loger 3 "Please use -S and -L specifies IP and port for UDP." - fi -fi - -if [ -f "$IGNORE" ]; then - IGNORE_IP=$(cat $IGNORE 2>/dev/null) -fi - -IPLIST=$(cat <<-EOF | grep -E "^([0-9]{1,3}\.){3}[0-9]{1,3}" - $server - $SERVER - 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.88.99.0/24 - 192.168.0.0/16 - 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 - $WAN_BP_IP - $IGNORE_IP -EOF -) - -flush_r && fw_rule && ipset_r && ac_rule && tp_rule - -exit $? +flush_rules && ipset_init && ipt_nat && ipt_mangle && export_ipt_rules +RET=$? +[ "$RET" = 0 ] || loger 3 "Start failed!" +exit $RET diff --git a/net/shadowsocks-libev/files/ss-rules.defaults b/net/shadowsocks-libev/files/ss-rules.defaults new file mode 100755 index 0000000000..c89e2d0b82 --- /dev/null +++ b/net/shadowsocks-libev/files/ss-rules.defaults @@ -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 diff --git a/net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch b/net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch new file mode 100644 index 0000000000..875f105db3 --- /dev/null +++ b/net/shadowsocks-libev/patches/0001-decouple-use_syslog-from-pid_flags.patch @@ -0,0 +1,151 @@ +From ea18a4ffcd9a8de4c5b888d9dc58a2b173c5ff8e Mon Sep 17 00:00:00 2001 +From: Yousong Zhou +Date: Mon, 26 Jun 2017 14:49:36 +0800 +Subject: [PATCH] decouple use_syslog from pid_flags + +Sometimes we need processes to run in the foreground to be supervised +and at the same time use syslog facility instead of logging its stdout, +stderr output +--- + src/jconf.c | 6 ++++++ + src/local.c | 2 +- + src/manager.c | 2 +- + src/redir.c | 2 +- + src/server.c | 2 +- + src/tunnel.c | 2 +- + src/utils.h | 18 +++++++++++------- + 7 files changed, 22 insertions(+), 12 deletions(-) + +diff --git a/src/jconf.c b/src/jconf.c +index 3c58148..05445c3 100644 +--- a/src/jconf.c ++++ b/src/jconf.c +@@ -313,6 +313,12 @@ read_jconf(const char *file) + check_json_value_type(value, json_boolean, + "invalid config file: option 'ipv6_first' must be a boolean"); + conf.ipv6_first = value->u.boolean; ++#ifdef HAS_SYSLOG ++ } else if (strcmp(name, "use_syslog") == 0) { ++ check_json_value_type(value, json_boolean, ++ "invalid config file: option 'use_syslog' must be a boolean"); ++ use_syslog = value->u.boolean; ++#endif + } + } + } else { +diff --git a/src/local.c b/src/local.c +index aa69205..d123516 100644 +--- a/src/local.c ++++ b/src/local.c +@@ -1519,8 +1519,8 @@ main(int argc, char **argv) + local_addr = "127.0.0.1"; + } + ++ USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { +- USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + +diff --git a/src/manager.c b/src/manager.c +index 6e7197c..338ab85 100644 +--- a/src/manager.c ++++ b/src/manager.c +@@ -1149,8 +1149,8 @@ main(int argc, char **argv) + timeout = "60"; + } + ++ USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { +- USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + +diff --git a/src/redir.c b/src/redir.c +index 4856007..88660f8 100644 +--- a/src/redir.c ++++ b/src/redir.c +@@ -1137,8 +1137,8 @@ main(int argc, char **argv) + #endif + } + ++ USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { +- USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + +diff --git a/src/server.c b/src/server.c +index 747f0e5..7e3df9e 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -1726,8 +1726,8 @@ main(int argc, char **argv) + } + #endif + ++ USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { +- USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + +diff --git a/src/tunnel.c b/src/tunnel.c +index 77c7380..2419fa0 100644 +--- a/src/tunnel.c ++++ b/src/tunnel.c +@@ -1022,8 +1022,8 @@ main(int argc, char **argv) + local_addr = "127.0.0.1"; + } + ++ USE_SYSLOG(argv[0], pid_flags); + if (pid_flags) { +- USE_SYSLOG(argv[0]); + daemonize(pid_path); + } + +diff --git a/src/utils.h b/src/utils.h +index 2603e85..53f3983 100644 +--- a/src/utils.h ++++ b/src/utils.h +@@ -35,7 +35,7 @@ + + #include + #define USE_TTY() +-#define USE_SYSLOG(ident) ++#define USE_SYSLOG(ident, _cond) + #define LOGI(...) \ + ((void)__android_log_print(ANDROID_LOG_DEBUG, "shadowsocks", \ + __VA_ARGS__)) +@@ -53,7 +53,7 @@ + extern FILE *logfile; + #define TIME_FORMAT "%Y-%m-%d %H:%M:%S" + #define USE_TTY() +-#define USE_SYSLOG(ident) ++#define USE_SYSLOG(ident, _cond) + #define USE_LOGFILE(ident) \ + do { \ + if (ident != NULL) { logfile = fopen(ident, "w+"); } } \ +@@ -99,11 +99,15 @@ extern int use_syslog; + use_tty = isatty(STDERR_FILENO); \ + } while (0) + +-#define USE_SYSLOG(ident) \ +- do { \ +- use_syslog = 1; \ +- openlog((ident), LOG_CONS | LOG_PID, 0); } \ +- while (0) ++#define USE_SYSLOG(_ident, _cond) \ ++ do { \ ++ if (!use_syslog && (_cond)) { \ ++ use_syslog = 1; \ ++ } \ ++ if (use_syslog) { \ ++ openlog((_ident), LOG_CONS | LOG_PID, 0); \ ++ } \ ++ } while (0) + + #define LOGI(format, ...) \ + do { \ +-- +2.12.2 + -- 2.30.2