diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/control b/ipk-source/atinout_aarch64_cortex-a53/CONTROL/control similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/control rename to ipk-source/atinout_aarch64_cortex-a53/CONTROL/control diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/postinst b/ipk-source/atinout_aarch64_cortex-a53/CONTROL/postinst similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/postinst rename to ipk-source/atinout_aarch64_cortex-a53/CONTROL/postinst diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/prerm b/ipk-source/atinout_aarch64_cortex-a53/CONTROL/prerm similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/CONTROL/prerm rename to ipk-source/atinout_aarch64_cortex-a53/CONTROL/prerm diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/build-ipk b/ipk-source/atinout_aarch64_cortex-a53/build-ipk similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/build-ipk rename to ipk-source/atinout_aarch64_cortex-a53/build-ipk diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/root/lib/upgrade/keep.d/atinout b/ipk-source/atinout_aarch64_cortex-a53/root/lib/upgrade/keep.d/atinout similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/root/lib/upgrade/keep.d/atinout rename to ipk-source/atinout_aarch64_cortex-a53/root/lib/upgrade/keep.d/atinout diff --git a/ipk-source/atinout_0.9.1_aarch64_cortex-a53/root/usr/bin/atinout b/ipk-source/atinout_aarch64_cortex-a53/root/usr/bin/atinout similarity index 100% rename from ipk-source/atinout_0.9.1_aarch64_cortex-a53/root/usr/bin/atinout rename to ipk-source/atinout_aarch64_cortex-a53/root/usr/bin/atinout diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/control b/ipk-source/luci-app-atinout-mod/CONTROL/control similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/control rename to ipk-source/luci-app-atinout-mod/CONTROL/control diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/postinst b/ipk-source/luci-app-atinout-mod/CONTROL/postinst similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/postinst rename to ipk-source/luci-app-atinout-mod/CONTROL/postinst diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/postinst-pkg b/ipk-source/luci-app-atinout-mod/CONTROL/postinst-pkg similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/postinst-pkg rename to ipk-source/luci-app-atinout-mod/CONTROL/postinst-pkg diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/prerm b/ipk-source/luci-app-atinout-mod/CONTROL/prerm similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/CONTROL/prerm rename to ipk-source/luci-app-atinout-mod/CONTROL/prerm diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/build-ipk b/ipk-source/luci-app-atinout-mod/build-ipk similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/build-ipk rename to ipk-source/luci-app-atinout-mod/build-ipk diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/config/atcommands.user b/ipk-source/luci-app-atinout-mod/root/etc/config/atcommands.user similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/config/atcommands.user rename to ipk-source/luci-app-atinout-mod/root/etc/config/atcommands.user diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/config/atinout b/ipk-source/luci-app-atinout-mod/root/etc/config/atinout similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/config/atinout rename to ipk-source/luci-app-atinout-mod/root/etc/config/atinout diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/uci-defaults/set_at_port.sh b/ipk-source/luci-app-atinout-mod/root/etc/uci-defaults/set_at_port.sh similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/etc/uci-defaults/set_at_port.sh rename to ipk-source/luci-app-atinout-mod/root/etc/uci-defaults/set_at_port.sh diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/sbin/set_at_port.sh b/ipk-source/luci-app-atinout-mod/root/sbin/set_at_port.sh similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/sbin/set_at_port.sh rename to ipk-source/luci-app-atinout-mod/root/sbin/set_at_port.sh diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/bin/luci-app-atinout b/ipk-source/luci-app-atinout-mod/root/usr/bin/luci-app-atinout similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/bin/luci-app-atinout rename to ipk-source/luci-app-atinout-mod/root/usr/bin/luci-app-atinout diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/controller/modem/atc.lua b/ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/controller/modem/atc.lua similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/controller/modem/atc.lua rename to ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/controller/modem/atc.lua diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/model/cbi/modem/atconfig.lua b/ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/model/cbi/modem/atconfig.lua similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/model/cbi/modem/atconfig.lua rename to ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/model/cbi/modem/atconfig.lua diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/view/modem/atcommand.htm b/ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/view/modem/atcommand.htm similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/lib/lua/luci/view/modem/atcommand.htm rename to ipk-source/luci-app-atinout-mod/root/usr/lib/lua/luci/view/modem/atcommand.htm diff --git a/ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/share/rpcd/acl.d/luci-app-atinout-mod.json b/ipk-source/luci-app-atinout-mod/root/usr/share/rpcd/acl.d/luci-app-atinout-mod.json similarity index 100% rename from ipk-source/luci-app-atinout-mod_1.3.4-20241006_all/root/usr/share/rpcd/acl.d/luci-app-atinout-mod.json rename to ipk-source/luci-app-atinout-mod/root/usr/share/rpcd/acl.d/luci-app-atinout-mod.json diff --git a/ipk-source/luci-app-tailscale/control/control b/ipk-source/luci-app-tailscale/control/control new file mode 100644 index 0000000..c982362 --- /dev/null +++ b/ipk-source/luci-app-tailscale/control/control @@ -0,0 +1,11 @@ +Package: luci-app-tailscale +Version: 1.2.3 +Depends: libc, tailscale +Source: package/luci-app-tailscale +SourceName: luci-app-tailscale +Section: luci +SourceDateEpoch: 1699969091 +Maintainer: OpenWrt LuCI community +Architecture: all +Installed-Size: 7745 +Description: LuCI for Tailscale diff --git a/ipk-source/sms-tool/CONTROL/postinst b/ipk-source/luci-app-tailscale/control/postinst old mode 100644 new mode 100755 similarity index 100% rename from ipk-source/sms-tool/CONTROL/postinst rename to ipk-source/luci-app-tailscale/control/postinst diff --git a/ipk-source/luci-app-tailscale/control/postinst-pkg b/ipk-source/luci-app-tailscale/control/postinst-pkg new file mode 100755 index 0000000..2e52d45 --- /dev/null +++ b/ipk-source/luci-app-tailscale/control/postinst-pkg @@ -0,0 +1,5 @@ +[ -n "${IPKG_INSTROOT}" ] || { rm -f /tmp/luci-indexcache.* + rm -rf /tmp/luci-modulecache/ + killall -HUP rpcd 2>/dev/null + exit 0 +} diff --git a/ipk-source/sms-tool/CONTROL/prerm b/ipk-source/luci-app-tailscale/control/prerm old mode 100644 new mode 100755 similarity index 100% rename from ipk-source/sms-tool/CONTROL/prerm rename to ipk-source/luci-app-tailscale/control/prerm diff --git a/ipk-source/luci-app-tailscale/data/etc/config/tailscale b/ipk-source/luci-app-tailscale/data/etc/config/tailscale new file mode 100644 index 0000000..e7a56a3 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/etc/config/tailscale @@ -0,0 +1,2 @@ +config tailscale 'settings' + option enabled '0' \ No newline at end of file diff --git a/ipk-source/luci-app-tailscale/data/etc/hotplug.d/iface/40-tailscale b/ipk-source/luci-app-tailscale/data/etc/hotplug.d/iface/40-tailscale new file mode 100755 index 0000000..971ed55 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/etc/hotplug.d/iface/40-tailscale @@ -0,0 +1,8 @@ +#!/bin/sh + +tailscale_enable="$(uci get tailscale.settings.enabled)" + +[ "$ACTION" = ifup -o "$ACTION" = ifupdate ] || exit 0 +[ "$ACTION" = ifupdate -a -z "$IFUPDATE_ADDRESSES" -a -z "$IFUPDATE_DATA" ] && exit 0 +[ "$tailscale_enable" -eq "1" ] || exit 0 +/etc/init.d/tailscale start > /tmp/tailscale.log 2>&1 & diff --git a/ipk-source/luci-app-tailscale/data/etc/init.d/tailscale b/ipk-source/luci-app-tailscale/data/etc/init.d/tailscale new file mode 100755 index 0000000..a405514 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/etc/init.d/tailscale @@ -0,0 +1,254 @@ +#!/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 + ts_ip=$(tailscale ip -4) + for route in $subnetRoutes; do + uci set network.ts_subnet$i='route' + uci set network.ts_subnet$i.interface='tailscale' + uci set network.ts_subnet$i.target=$route + uci set network.ts_subnet$i.gateway=$ts_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 +} diff --git a/ipk-source/luci-app-tailscale/data/etc/uci-defaults/40_luci-tailscale b/ipk-source/luci-app-tailscale/data/etc/uci-defaults/40_luci-tailscale new file mode 100755 index 0000000..90cd2d6 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/etc/uci-defaults/40_luci-tailscale @@ -0,0 +1,9 @@ +#!/bin/sh + +uci -q batch <<-EOF >/dev/null + delete ucitrack.@tailscale[-1] + commit ucitrack +EOF + +rm -f /tmp/luci-indexcache +exit 0 diff --git a/ipk-source/luci-app-tailscale/data/usr/share/luci/menu.d/luci-app-tailscale.json b/ipk-source/luci-app-tailscale/data/usr/share/luci/menu.d/luci-app-tailscale.json new file mode 100644 index 0000000..fc2a780 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/usr/share/luci/menu.d/luci-app-tailscale.json @@ -0,0 +1,37 @@ +{ + "admin/services/tailscale": { + "title": "Tailscale", + "order": 90, + "action": { + "type": "firstchild" + }, + "depends": { + "acl": [ "luci-app-tailscale" ], + "uci": { "tailscale": true } + } + }, + "admin/services/tailscale/setting": { + "title": "Global Settings", + "order": 10, + "action": { + "type": "view", + "path": "tailscale/setting" + } + }, + "admin/services/tailscale/interface": { + "title": "Interface Info", + "order": 20, + "action": { + "type": "view", + "path": "tailscale/interface" + } + }, + "admin/services/tailscale/log": { + "title": "Logs", + "order": 30, + "action": { + "type": "view", + "path": "tailscale/log" + } + } +} diff --git a/ipk-source/luci-app-tailscale/data/usr/share/rpcd/acl.d/luci-app-tailscale.json b/ipk-source/luci-app-tailscale/data/usr/share/rpcd/acl.d/luci-app-tailscale.json new file mode 100644 index 0000000..6fc1860 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/usr/share/rpcd/acl.d/luci-app-tailscale.json @@ -0,0 +1,21 @@ +{ + "luci-app-tailscale": { + "description": "Grant access to Tailscale configuration", + "read": { + "file": { + "/sbin/ifconfig": [ "exec" ], + "/sbin/logread": [ "exec" ], + "/usr/sbin/tailscale": [ "exec" ] + }, + "ubus": { + "service": [ "list" ], + "network.interface.lan": [ "status" ], + "network.interface.wan": [ "status" ] + }, + "uci": [ "tailscale" ] + }, + "write": { + "uci": [ "tailscale" ] + } + } +} diff --git a/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/interface.js b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/interface.js new file mode 100644 index 0000000..91d225d --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/interface.js @@ -0,0 +1,6 @@ +'use strict';'require dom';'require fs';'require poll';'require ui';'require view';return view.extend({load:function(){return fs.exec('/sbin/ifconfig').then(function(res){if(res.code!==0||!res.stdout||res.stdout.trim()===''){ui.addNotification(null,E('p',{},_('Unable to get interface info: %s.').format(res.message)));return'';} +var interfaces=res.stdout.match(/tailscale[0-9]+/g);if(!interfaces||interfaces.length===0) +return'No interface online.';var promises=interfaces.map(function(name){return fs.exec('/sbin/ifconfig',[name]);});return Promise.all(promises).then(function(results){var data=results.map(function(res,index){if(res.code!==0||!res.stdout||res.stdout.trim()===''){ui.addNotification(null,E('p',{},_('Unable to get interface %s info: %s.').format(interfaces[index],res.message)));return null;} +return{name:interfaces[index],stdout:res.stdout.trim()};}).filter(Boolean);return data.map(function(info){var lines=info.stdout.split('\n');var parsedInfo={name:info.name};lines.forEach(function(line){if(line.includes('inet addr:')){parsedInfo.ipv4=line.split('inet addr:')[1].trim().split(' ')[0];}else if(line.includes('inet6 addr:')){parsedInfo.ipv6=line.split('inet6 addr:')[1].trim().split('/')[0];}else if(line.includes('MTU:')){parsedInfo.mtu=line.split('MTU:')[1].trim().split(' ')[0];}else if(line.includes('RX bytes:')){var rxMatch=line.match(/RX bytes:\d+ \(([\d.]+\s*[a-zA-Z]+)\)/);if(rxMatch&&rxMatch[1]){parsedInfo.rxBytes=rxMatch[1];} +var txMatch=line.match(/TX bytes:\d+ \(([\d.]+\s*[a-zA-Z]+)\)/);if(txMatch&&txMatch[1]){parsedInfo.txBytes=txMatch[1];}}});return parsedInfo;});});});},pollData:function(container){poll.add(L.bind(function(){return this.load().then(L.bind(function(data){dom.content(container,this.renderContent(data));},this));},this));},renderContent:function(data){if(!Array.isArray(data)){return E('div',{},_('No interface online.'));} +var rows=data.flatMap(function(interfaceData){return[E('th',{class:'th',colspan:'2'},_('Network Interface Information')),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Interface Name')),E('td',{class:'td left',width:'25%'},interfaceData.name)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('IPv4 Address')),E('td',{class:'td left',width:'25%'},interfaceData.ipv4)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('IPv6 Address')),E('td',{class:'td left',width:'25%'},interfaceData.ipv6)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('MTU')),E('td',{class:'td left',width:'25%'},interfaceData.mtu)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Total Download')),E('td',{class:'td left',width:'25%'},interfaceData.rxBytes)]),E('tr',{class:'tr'},[E('td',{class:'td left',width:'25%'},_('Total Upload')),E('td',{class:'td left',width:'25%'},interfaceData.txBytes)])];});return E('table',{'class':'table'},rows);},render:function(data){var content=E([],[E('h2',{class:'content'},_('Tailscale')),E('div',{class:'cbi-map-descr'},_('Tailscale is a cross-platform and easy to use virtual LAN.')),E('div')]);var container=content.lastElementChild;dom.content(container,this.renderContent(data));this.pollData(container);return content;},handleSaveApply:null,handleSave:null,handleReset:null}); \ No newline at end of file diff --git a/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/log.js b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/log.js new file mode 100644 index 0000000..c125a21 --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/log.js @@ -0,0 +1 @@ +'use strict';'require fs';'require poll';'require ui';'require view';return view.extend({retrieveLog:async function(){return Promise.all([L.resolveDefault(fs.stat('/sbin/logread'),null),L.resolveDefault(fs.stat('/usr/sbin/logread'),null)]).then(function(stat){var logger=stat[0]?stat[0].path:stat[1]?stat[1].path:null;return fs.exec_direct(logger,['-e','tailscale']).then(logdata=>{var statusMappings={'daemon.err':{status:'StdErr',startIndex:9},'daemon.notice':{status:'Info',startIndex:10}};const loglines=logdata.trim().split(/\n/).map(function(log){var logParts=log.split(' ').filter(Boolean);if(logParts.length>=6){var formattedTime=logParts[1]+' '+logParts[2]+' - '+logParts[3];var status=logParts[5];var mapping=statusMappings[status]||{status:status,startIndex:9};status=mapping.status;var startIndex=mapping.startIndex;var message=logParts.slice(startIndex).join(' ');return formattedTime+' [ '+status+' ] - '+message;}else{return'Log is empty.';}}).filter(Boolean);return{value:loglines.join('\n'),rows:loglines.length+1};}).catch(function(err){ui.addNotification(null,E('p',{},_('Unable to load log data: '+err.message)));return'';});});},pollLog:async function(){const element=document.getElementById('syslog');if(element){const log=await this.retrieveLog();element.value=log.value;element.rows=log.rows;}},load:async function(){poll.add(this.pollLog.bind(this));return await this.retrieveLog();},render:function(loglines){var scrollDownButton=E('button',{'id':'scrollDownButton','class':'cbi-button cbi-button-neutral'},_('Scroll to tail','scroll to bottom (the tail) of the log file'));scrollDownButton.addEventListener('click',function(){scrollUpButton.scrollIntoView();});var scrollUpButton=E('button',{'id':'scrollUpButton','class':'cbi-button cbi-button-neutral'},_('Scroll to head','scroll to top (the head) of the log file'));scrollUpButton.addEventListener('click',function(){scrollDownButton.scrollIntoView();});return E([],[E('div',{'id':'content_syslog'},[E('div',{'style':'padding-bottom: 20px'},[scrollDownButton]),E('textarea',{'id':'syslog','style':'font-size:12px','readonly':'readonly','wrap':'off','rows':loglines.rows,},[loglines.value]),E('div',{'style':'padding-bottom: 20px'},[scrollUpButton])])]);},handleSaveApply:null,handleSave:null,handleReset:null}); \ No newline at end of file diff --git a/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/setting.js b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/setting.js new file mode 100644 index 0000000..5cea6be --- /dev/null +++ b/ipk-source/luci-app-tailscale/data/www/luci-static/resources/view/tailscale/setting.js @@ -0,0 +1,14 @@ +'use strict';'require form';'require fs';'require poll';'require rpc';'require uci';'require view';var callServiceList=rpc.declare({object:'service',method:'list',params:['name'],expect:{'':{}}});function callInterfaceStatus(interfaceName){return rpc.declare({object:`network.interface.${interfaceName}`,method:'status',params:['name'],expect:{'':{}}});} +function getInterfaceSubnets(interfaces=['lan','wan']){const calculateSubnetAndCIDR=(ip,cidr)=>{const cidrInt=parseInt(cidr,10);const maskBinary='1'.repeat(cidrInt).padEnd(32,'0');const ipBinary=(ip)=>ip.split('.').map(octet=>parseInt(octet,10).toString(2).padStart(8,'0')).join('');const subnetBinary=ipBinary(ip).split('').map((bit,index)=>(bit==='1'&&maskBinary[index]==='1')?'1':'0').join('');const subnet=[parseInt(subnetBinary.slice(0,8),2),parseInt(subnetBinary.slice(8,16),2),parseInt(subnetBinary.slice(16,24),2),parseInt(subnetBinary.slice(24,32),2)].join('.');return`${subnet}/${cidrInt}`;};const rpcCalls=interfaces.map(interfaceName=>{const callStatus=callInterfaceStatus(interfaceName);return callStatus('ipv4-address').catch(()=>({'ipv4-address':[]}));});return Promise.all(rpcCalls).then(res=>{const interfaceSubnets=res.flatMap(status=>(status['ipv4-address']||[]).map(addr=>{return calculateSubnetAndCIDR(addr.address,addr.mask)}));return[...new Set(interfaceSubnets)];}).catch(()=>[]);} +function getStatus(){var status={isRunning:false,backendState:undefined,authURL:undefined,displayName:undefined,onlineExitNodes:[],subnetRoutes:[]};return Promise.resolve(callServiceList('tailscale')).then(res=>{try{status.isRunning=res['tailscale']['instances']['instance1']['running'];}catch(e){return status;} +return fs.exec("/usr/sbin/tailscale",["status","--json"]).then(res=>{const tailscaleStatus=JSON.parse(res.stdout.replace(/("\w+"):\s*(\d+)/g,'$1:"$2"'));if(!tailscaleStatus.AuthURL&&tailscaleStatus.BackendState==="NeedsLogin"){fs.exec("/usr/sbin/tailscale",["login"]);} +status.backendState=tailscaleStatus.BackendState;status.authURL=tailscaleStatus.AuthURL;status.displayName=(status.backendState==="Running")?tailscaleStatus.User[tailscaleStatus.Self.UserID].DisplayName:undefined;status.onlineExitNodes=Object.values(tailscaleStatus.Peer).flatMap(peer=>(peer.ExitNodeOption&&peer.Online)?[peer.HostName]:[]);status.subnetRoutes=Object.values(tailscaleStatus.Peer).flatMap(peer=>peer.PrimaryRoutes||[]);return status;});}).catch(()=>status);} +function renderStatus(isRunning){var spanTemp='%s %s';var renderHTML;if(isRunning){renderHTML=String.format(spanTemp,'green',_('Tailscale'),_('RUNNING'));}else{renderHTML=String.format(spanTemp,'red',_('Tailscale'),_('NOT RUNNING'));} +return renderHTML;} +function renderLogin(loginStatus,authURL,displayName){var spanTemp='%s';var renderHTML;if(loginStatus=="NeedsLogin"){renderHTML=String.format('%s',authURL,_('Needs Login'));}else if(loginStatus=="Running"){renderHTML=String.format('%s','https://login.tailscale.com/admin/machines',displayName);renderHTML+=String.format('
%s',_('Logout and Unbind'));}else{renderHTML=String.format(spanTemp,'orange',_('NOT RUNNING'));} +return renderHTML;} +return view.extend({load:function(){return Promise.all([uci.load('tailscale'),getStatus(),getInterfaceSubnets()]);},render:function(data){var m,s,o;var statusData=data[1];var interfaceSubnets=data[2];var onlineExitNodes=statusData.onlineExitNodes;var subnetRoutes=statusData.subnetRoutes;m=new form.Map('tailscale',_('Tailscale'),_('Tailscale is a cross-platform and easy to use virtual LAN.'));s=m.section(form.TypedSection);s.anonymous=true;s.render=function(){poll.add(function(){return Promise.resolve(getStatus()).then(function(res){var service_view=document.getElementById("service_status");var login_view=document.getElementById("login_status_div");service_view.innerHTML=renderStatus(res.isRunning);login_view.innerHTML=renderLogin(res.backendState,res.authURL,res.displayName);var logoutButton=document.getElementById('logout_button');if(logoutButton){logoutButton.onclick=function(){if(confirm(_('Are you sure you want to logout and unbind the current device?'))){fs.exec("/usr/sbin/tailscale",["logout"]);}}}});});return E('div',{class:'cbi-section',id:'status_bar'},[E('p',{id:'service_status'},_('Collecting data ...'))]);} +s=m.section(form.NamedSection,'settings','config');s.tab('basic',_('Basic Settings'));o=s.taboption('basic',form.Flag,'enabled',_('Enable'));o.default=o.disabled;o.rmempty=false;o=s.taboption('basic',form.DummyValue,'login_status',_('Login Status'));o.depends('enabled','1');o.renderWidget=function(section_id,option_id){return E('div',{'id':'login_status_div'},_('Collecting data ...'));};o=s.taboption('basic',form.Value,'port',_('Port'),_('Set the Tailscale port number.'));o.datatype='port';o.default='41641';o.rmempty=false;o=s.taboption('basic',form.Value,'config_path',_('Workdir'),_('The working directory contains config files, audit logs, and runtime info.'));o.default='/etc/tailscale';o.rmempty=false;o=s.taboption('basic',form.ListValue,'fw_mode',_('Firewall Mode'));o.value('nftables','nftables');o.value('iptables','iptables');o.default='nftables';o.rmempty=false;o=s.taboption('basic',form.Flag,'log_stdout',_('StdOut Log'),_('Logging program activities.'));o.default=o.enabled;o.rmempty=false;o=s.taboption('basic',form.Flag,'log_stderr',_('StdErr Log'),_('Logging program errors and exceptions.'));o.default=o.enabled;o.rmempty=false;s.tab('advance',_('Advanced Settings'));o=s.taboption('advance',form.Flag,'acceptRoutes',_('Accept Routes'),_('Accept subnet routes that other nodes advertise.'));o.default=o.disabled;o.rmempty=false;o=s.taboption('advance',form.Value,'hostname',_('Device Name'),_("Leave blank to use the device's hostname."));o.default='';o.rmempty=true;o=s.taboption('advance',form.Flag,'acceptDNS',_('Accept DNS'),_('Accept DNS configuration from the Tailscale admin console.'));o.default=o.enabled;o.rmempty=false;o=s.taboption('advance',form.Flag,'advertiseExitNode',_('Exit Node'),_('Offer to be an exit node for outbound internet traffic from the Tailscale network.'));o.default=o.disabled;o.rmempty=false;o=s.taboption('advance',form.ListValue,'exitNode',_('Online Exit Nodes'),_('Select an online machine name to use as an exit node.'));if(onlineExitNodes.length>0){o.value('',_('-- Please choose --'));onlineExitNodes.forEach(function(node){o.value(node,node);});}else{o.value('',_('No Available Exit Nodes'));o.readonly=true;} +o.default='';o.depends('advertiseExitNode','0');o.rmempty=true;o=s.taboption('advance',form.DynamicList,'advertiseRoutes',_('Expose Subnets'),_('Expose physical network routes into Tailscale, e.g. 10.0.0.0/24.'));if(interfaceSubnets.length>0){interfaceSubnets.forEach(function(subnet){o.value(subnet,subnet);});} +o.default='';o.rmempty=true;o=s.taboption('advance',form.Flag,'s2s',_('Site To Site'),_('Use site-to-site layer 3 networking to connect subnets on the Tailscale network.'));o.default=o.disabled;o.depends('acceptRoutes','1');o.rmempty=false;o=s.taboption('advance',form.DynamicList,'subnetRoutes',_('Subnet Routes'),_('Select subnet routes advertised by other nodes in Tailscale network.'));if(subnetRoutes.length>0){subnetRoutes.forEach(function(route){o.value(route,route);});}else{o.value('',_('No Available Subnet Routes'));o.readonly=true;} +o.default='';o.depends('s2s','1');o.rmempty=true;o=s.taboption('advance',form.MultiValue,'access',_('Access Control'));o.value('tsfwlan',_('Tailscale access LAN'));o.value('tsfwwan',_('Tailscale access WAN'));o.value('lanfwts',_('LAN access Tailscale'));o.value('wanfwts',_('WAN access Tailscale'));o.default="tsfwlan tsfwwan lanfwts";o.rmempty=true;s.tab('extra',_('Extra Settings'));o=s.taboption('extra',form.DynamicList,'flags',_('Additional Flags'),String.format(_('List of extra flags. Format: --flags=value, e.g. --exit-node=10.0.0.1.
%s for enabling settings upon the initiation of Tailscale.'),''+_('Available flags')+''));o.default='';o.rmempty=true;s=m.section(form.NamedSection,'settings','config');s.title=_('Custom Server Settings');s.description=String.format(_('Use %s to deploy a private server.'),'headscale');o=s.option(form.Value,'loginServer',_('Server Address'));o.default='';o.rmempty=true;o=s.option(form.Value,'authKey',_('Auth Key'));o.default='';o.rmempty=true;return m.render();}}); \ No newline at end of file diff --git a/ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/CONTROL/control b/ipk-source/ookla-speedtest_aarch64_cortex-a53/CONTROL/control similarity index 100% rename from ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/CONTROL/control rename to ipk-source/ookla-speedtest_aarch64_cortex-a53/CONTROL/control diff --git a/ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/build-ipk b/ipk-source/ookla-speedtest_aarch64_cortex-a53/build-ipk similarity index 100% rename from ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/build-ipk rename to ipk-source/ookla-speedtest_aarch64_cortex-a53/build-ipk diff --git a/ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/root/usr/bin/speedtest b/ipk-source/ookla-speedtest_aarch64_cortex-a53/root/usr/bin/speedtest similarity index 100% rename from ipk-source/ookla-speedtest_1.2.0_aarch64_cortex-a53/root/usr/bin/speedtest rename to ipk-source/ookla-speedtest_aarch64_cortex-a53/root/usr/bin/speedtest diff --git a/ipk-source/sms-tool/CONTROL/control b/ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/control similarity index 100% rename from ipk-source/sms-tool/CONTROL/control rename to ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/control diff --git a/ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/postinst b/ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/postinst old mode 100755 new mode 100644 similarity index 100% rename from ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/postinst rename to ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/postinst diff --git a/ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/prerm b/ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/prerm old mode 100755 new mode 100644 similarity index 100% rename from ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/prerm rename to ipk-source/sms-toolAP_aarch64_cortex-a53/CONTROL/prerm diff --git a/ipk-source/sms-tool/build-ipk b/ipk-source/sms-toolAP_aarch64_cortex-a53/build-ipk similarity index 100% rename from ipk-source/sms-tool/build-ipk rename to ipk-source/sms-toolAP_aarch64_cortex-a53/build-ipk diff --git a/ipk-source/sms-tool/root/lib/upgrade/keep.d/sms-tool b/ipk-source/sms-toolAP_aarch64_cortex-a53/root/lib/upgrade/keep.d/sms-tool similarity index 100% rename from ipk-source/sms-tool/root/lib/upgrade/keep.d/sms-tool rename to ipk-source/sms-toolAP_aarch64_cortex-a53/root/lib/upgrade/keep.d/sms-tool diff --git a/ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/control b/ipk-source/tailscale_aarch64_cortex-a53/CONTROL/control similarity index 100% rename from ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/CONTROL/control rename to ipk-source/tailscale_aarch64_cortex-a53/CONTROL/control diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/postinst b/ipk-source/tailscale_aarch64_cortex-a53/CONTROL/postinst similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/postinst rename to ipk-source/tailscale_aarch64_cortex-a53/CONTROL/postinst diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/prerm b/ipk-source/tailscale_aarch64_cortex-a53/CONTROL/prerm similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/prerm rename to ipk-source/tailscale_aarch64_cortex-a53/CONTROL/prerm diff --git a/ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/build-ipk b/ipk-source/tailscale_aarch64_cortex-a53/build-ipk similarity index 100% rename from ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/build-ipk rename to ipk-source/tailscale_aarch64_cortex-a53/build-ipk diff --git a/ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/root/usr/sbin/tailscale b/ipk-source/tailscale_aarch64_cortex-a53/root/usr/sbin/tailscale similarity index 100% rename from ipk-source/tailscale_1.78.1-1_aarch64_cortex-a53/root/usr/sbin/tailscale rename to ipk-source/tailscale_aarch64_cortex-a53/root/usr/sbin/tailscale diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/conffiles b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/conffiles similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/conffiles rename to ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/conffiles diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/control b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/control similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/CONTROL/control rename to ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/control diff --git a/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/postinst b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/postinst new file mode 100755 index 0000000..3bba77c --- /dev/null +++ b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/postinst @@ -0,0 +1,5 @@ +#!/bin/sh +[ "${IPKG_NO_SCRIPT}" = "1" ] && exit 0 +[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0 +. ${IPKG_INSTROOT}/lib/functions.sh +default_postinst $0 $@ diff --git a/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/prerm b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/prerm new file mode 100755 index 0000000..12d06ec --- /dev/null +++ b/ipk-source/tailscaled_aarch64_cortex-a53/CONTROL/prerm @@ -0,0 +1,4 @@ +#!/bin/sh +[ -s ${IPKG_INSTROOT}/lib/functions.sh ] || exit 0 +. ${IPKG_INSTROOT}/lib/functions.sh +default_prerm $0 $@ diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/build-ipk b/ipk-source/tailscaled_aarch64_cortex-a53/build-ipk similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/build-ipk rename to ipk-source/tailscaled_aarch64_cortex-a53/build-ipk diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/etc/config/tailscale b/ipk-source/tailscaled_aarch64_cortex-a53/root/etc/config/tailscale similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/etc/config/tailscale rename to ipk-source/tailscaled_aarch64_cortex-a53/root/etc/config/tailscale diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/etc/init.d/tailscale b/ipk-source/tailscaled_aarch64_cortex-a53/root/etc/init.d/tailscale similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/etc/init.d/tailscale rename to ipk-source/tailscaled_aarch64_cortex-a53/root/etc/init.d/tailscale diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/lib/upgrade/keep.d/tailscaled b/ipk-source/tailscaled_aarch64_cortex-a53/root/lib/upgrade/keep.d/tailscaled similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/lib/upgrade/keep.d/tailscaled rename to ipk-source/tailscaled_aarch64_cortex-a53/root/lib/upgrade/keep.d/tailscaled diff --git a/ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/usr/sbin/tailscaled b/ipk-source/tailscaled_aarch64_cortex-a53/root/usr/sbin/tailscaled similarity index 100% rename from ipk-source/tailscaled_1.78.1-1_aarch64_cortex-a53/root/usr/sbin/tailscaled rename to ipk-source/tailscaled_aarch64_cortex-a53/root/usr/sbin/tailscaled