#!/bin/sh /etc/rc.common START=90 USE_PROCD=1 PROG=/usr/sbin/tailscale PROGD=/usr/sbin/tailscaled CONFIG_PATH=/var/lib/tailscale service_triggers() { procd_add_reload_trigger "tailscale" procd_add_interface_trigger "interface.*.up" wan /etc/init.d/tailscale reload } section_enabled() { config_get_bool enabled "$1" 'enabled' 0 [ $enabled -gt 0 ] } custom_instance() { local cfg="$1" local acceptRoutes hostname acceptDNS advertiseExitNode exitNode advertiseRoutes s2s subnetRoutes flags loginServer authkey std_out std_err local ARGS=" up --reset" if ! section_enabled "$cfg"; then echo "disabled in config" return 1 fi config_get_bool acceptRoutes $cfg 'acceptRoutes' config_get hostname $cfg 'hostname' config_get_bool acceptDNS $cfg 'acceptDNS' config_get_bool advertiseExitNode $cfg 'advertiseExitNode' config_get exitNode $cfg 'exitNode' config_get advertiseRoutes $cfg 'advertiseRoutes' config_get_bool s2s $cfg 's2s' config_get flags $cfg 'flags' config_get loginServer $cfg 'loginServer' config_get authkey $cfg 'authkey' config_get_bool std_out $cfg 'log_stdout' config_get_bool std_err $cfg 'log_stderr' [ "$acceptRoutes" = "1" ] && ARGS="$ARGS --accept-routes=true" [ -n "$hostname" ] && ARGS="$ARGS --hostname=$hostname" [ "$acceptDNS" = "0" ] && ARGS="$ARGS --accept-dns=false" [ "$advertiseExitNode" = "1" ] && ARGS="$ARGS --advertise-exit-node" [ -n "$exitNode" ] && ARGS="$ARGS --exit-node=$exitNode --exit-node-allow-lan-access=true" [ -n "$advertiseRoutes" ] && ARGS="$ARGS --advertise-routes=$(echo $advertiseRoutes | tr ' ' ',')" [ "$s2s" = "1" ] && ARGS="$ARGS --snat-subnet-routes=false" [ -n "$flags" ] && ARGS="$ARGS $flags" [ -n "$loginServer" ] && ARGS="$ARGS --login-server=$loginServer" [ -n "$authkey" ] && ARGS="$ARGS --authkey=$authkey" procd_open_instance procd_set_param command $PROG $ARGS procd_set_param stdout "$std_out" procd_set_param stderr "$std_err" procd_close_instance ( [ -f "/var/run/tailscale.wait.pid" ] && return touch /var/run/tailscale.wait.pid count=0 while [ -z "$(ifconfig | grep 'tailscale' | awk '{print $1}')" ] || [ -z "$(tailscale ip -4)" ] do sleep 2 let count++ [ "${count}" -ge 5 ] && { rm /var/run/tailscale.wait.pid; exit 19; } done if [ "$acceptDNS" = "1" ]; then MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}') sed -i '/100.100.100.100/d' /etc/dnsmasq.conf echo "server=/$MagicDNSSuffix/100.100.100.100" >> /etc/dnsmasq.conf /etc/init.d/dnsmasq reload fi ts0=$(ifconfig | grep 'tailscale' | awk '{print $1}') if [ -z "$(uci -q get network.tailscale)" ]; then uci set network.tailscale='interface' if [ "$ts0" = *$'\n'* ]; then uci set network.ts_lan='device' uci set network.ts_lan.type='bridge' uci set network.ts_lan.name='ts-lan' for port in "${ts0}"; do uci add_list network.ts_lan.ports=$port done uci set network.tailscale.proto='none' uci set network.tailscale.device='ts-lan' else ts_ip=$(tailscale ip -4) uci set network.tailscale.proto='static' uci set network.tailscale.ipaddr=$ts_ip uci set network.tailscale.netmask='255.0.0.0' uci set network.tailscale.device=$ts0 fi fi lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') if [ -n "$exitNode" ]; then uci set firewall.@defaults[0].forward='REJECT' [ -n $lan2wan ] && uci set firewall.@forwarding[$lan2wan].enabled='0' else uci -q delete firewall.@forwarding[$lan2wan].enabled fi config_get subnetRoutes $cfg 'subnetRoutes' if [ -n "$subnetRoutes" ]; then i=1 lan_ip=$(uci get network.lan.ipaddr) for route in $subnetRoutes; do uci set network.ts_subnet$i='route' uci set network.ts_subnet$i.interface='lan' uci set network.ts_subnet$i.target=$route uci set network.ts_subnet$i.gateway=$lan_ip let i++ done else for route in $(uci show network | grep 'network.ts_subnet[0-9]\+=route' | grep -o 'network.ts_subnet[0-9]\+'); do uci -q delete $route done fi config_get access $cfg 'access' if [ -n "$access" ]; then if [ -z "$(uci -q get firewall.tszone)" ]; then uci set firewall.tszone='zone' uci set firewall.tszone.input='ACCEPT' uci set firewall.tszone.output='ACCEPT' uci set firewall.tszone.forward='ACCEPT' uci set firewall.tszone.masq='1' uci set firewall.tszone.mtu_fix='1' uci set firewall.tszone.name='tailscale' uci set firewall.tszone.network='tailscale' fi else uci -q delete firewall.tszone fi if [ "${access//tsfwlan/}" != "$access" ]; then uci set firewall.tsfwlan=forwarding uci set firewall.tsfwlan.dest='lan' uci set firewall.tsfwlan.src='tailscale' else uci -q delete firewall.tsfwlan fi if [ "${access//tsfwwan/}" != "$access" ]; then uci set firewall.tsfwwan=forwarding uci set firewall.tsfwwan.dest='wan' uci set firewall.tsfwwan.src='tailscale' else uci -q delete firewall.tsfwwan fi if [ "${access//lanfwts/}" != "$access" ]; then uci set firewall.lanfwts=forwarding uci set firewall.lanfwts.dest='tailscale' uci set firewall.lanfwts.src='lan' else uci -q delete firewall.lanfwts fi if [ "${access//wanfwts/}" != "$access" ]; then uci set firewall.wanfwts=forwarding uci set firewall.wanfwts.dest='tailscale' uci set firewall.wanfwts.src='wan' else uci -q delete firewall.wanfwts fi [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload rm /var/run/tailscale.wait.pid ) & } start_instance() { local cfg="$1" local port config_path fw_mode std_out std_err state_file local ARGS="" if ! section_enabled "$cfg"; then echo "disabled in config" return 1 fi config_get port $cfg 'port' config_get config_path $cfg 'config_path' config_get fw_mode $cfg 'fw_mode' config_get_bool std_out $cfg 'log_stdout' config_get_bool std_err $cfg 'log_stderr' [ -d $config_path ] || mkdir -p $config_path [ -d $CONFIG_PATH ] || mkdir -p $CONFIG_PATH state_file=$config_path/tailscaled.state /usr/sbin/tailscaled --cleanup [ -n "$port" ] && ARGS="$ARGS --port $port" [ -n "$state_file" ] && ARGS="$ARGS --state $state_file" procd_open_instance procd_set_param command $PROGD $ARGS procd_set_param env TS_DEBUG_FIREWALL_MODE="$fw_mode" procd_set_param respawn procd_set_param stdout "$std_out" procd_set_param stderr "$std_err" procd_close_instance } start_service() { config_load 'tailscale' config_foreach start_instance 'tailscale' config_foreach custom_instance 'tailscale' } stop_instance() { local cfg="$1" /usr/sbin/tailscaled --cleanup # Remove dnsmasq settings sed -i '/100.100.100.100/d' /etc/dnsmasq.conf /etc/init.d/dnsmasq reload # Remove network settings uci -q delete network.tailscale uci -q delete network.ts_lan for route in $(uci show network | grep 'network.ts_subnet[0-9]\+=route' | grep -o 'network.ts_subnet[0-9]\+'); do uci -q delete $route done # Remove firewall settings lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') uci -q delete firewall.@forwarding[$lan2wan].enabled uci -q delete firewall.tszone uci -q delete firewall.tsfwlan uci -q delete firewall.tsfwwan uci -q delete firewall.lanfwts uci -q delete firewall.wanfwts [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload # Remove existing link or folder rm -rf $CONFIG_PATH } stop_service() { config_load 'tailscale' config_foreach stop_instance 'tailscale' } reload_service() { stop start }