# # This script creates policy routing rules for ZeroTier to coexist with Tailscale. # It prevents ZeroTier's traffic from being intercepted by Tailscale's high-priority rule. # Version 3: Intelligently handles IPv4 and IPv6 addresses. #
START=99 STOP=15
# --- CONFIGURATION --- # The name of your ZeroTier virtual network interface. Find it with `ifconfig | grep zt`. ZT_IFACE="ztypia4gba"
# The priority for the new routing rule. Must be lower (higher priority) than Tailscale's (usually 5270). RULE_PRIO="5260"
# Tag for system log entries. LOG_TAG="zerotier-policy-route" # --- END CONFIGURATION ---
add_rules() { # Get all global/unique IP addresses (v4 and v6), excluding link-local (fe80::) addresses. IP_LIST=$(ip addr show dev "${ZT_IFACE}" | grep -E 'inet|inet6' | grep -v ' fe80::' | awk '{print $2}' | cut -d'/' -f1)
if [ -z "${IP_LIST}" ]; then logger -t "${LOG_TAG}""Warning: No usable IP addresses found on interface ${ZT_IFACE}." return fi
for ip in${IP_LIST}; do # Check if a rule for this IP already exists to avoid duplication. if ! ip rule | grep -q "from ${ip} lookup main"; then logger -t "${LOG_TAG}""Adding rule for ${ip} with priority ${RULE_PRIO}."
# CRITICAL FIX: Use the '-6' flag for IPv6 addresses. ifecho"${ip}" | grep -q ":"; then ip -6 rule add from "${ip}" lookup main prio "${RULE_PRIO}" else ip rule add from "${ip}" lookup main prio "${RULE_PRIO}" fi else logger -t "${LOG_TAG}""Rule for ${ip} already exists. Skipping." fi done }
remove_rules() { IP_LIST=$(ip addr show dev "${ZT_IFACE}" | grep -E 'inet|inet6' | grep -v ' fe80::' | awk '{print $2}' | cut -d'/' -f1) if [ -z "${IP_LIST}" ]; thenreturn; fi
for ip in${IP_LIST}; do if ip rule | grep -q "from ${ip} lookup main"; then logger -t "${LOG_TAG}""Removing rule for ${ip}."
# Use the '-6' flag for deletion of IPv6 rules as well. ifecho"${ip}" | grep -q ":"; then ip -6 rule del from "${ip}" lookup main prio "${RULE_PRIO}" else ip rule del from "${ip}" lookup main prio "${RULE_PRIO}" fi fi done }
执行 ip rule show,你将看到类似下面的输出,我们为 ZeroTier 的每个 IP 添加的 5260 规则已经稳稳地待在了 Tailscale 的规则之上。
1 2 3 4 5 6 7
0: fromall lookup local ... 5260: from172.27.72.251 lookup main 5260: from fddd:ec77:... lookup main 5260: from fce1:9571:... lookup main 5270: fromall lookup 52 ...
# # This script creates policy routing rules for all ZeroTier interfaces # to coexist with Tailscale on a Debian-based system. #
set -e # Exit immediately if a command exits with a non-zero status.
# --- CONFIGURATION --- # The priority for the new routing rule. # This MUST be a lower number (higher priority) than Tailscale's rule (usually 5270). RULE_PRIO="5260" LOG_TAG="zerotier-policy-route" # --- END CONFIGURATION ---
# Find all ZeroTier network interfaces (names starting with 'zt'). # We use `ip -o link show` for a stable, one-line-per-interface output. ZT_INTERFACES=$(ip -o link show | awk -F': ''{print $2}' | grep '^zt')
if [ -z "$ZT_INTERFACES" ]; then logger -t "$LOG_TAG""No ZeroTier interfaces found. Exiting." exit 0 fi
# Function to add rules for all found interfaces add_rules() { logger -t "$LOG_TAG""Applying policy routing rules..." for iface in$ZT_INTERFACES; do # Get all global/unique IP addresses (v4 and v6), excluding link-local (fe80::) IP_LIST=$(ip addr show dev "$iface" | grep -E 'inet|inet6' | grep -v 'fe80::' | awk '{print $2}' | cut -d'/' -f1)
if [ -z "$IP_LIST" ]; then logger -t "$LOG_TAG""No usable IP addresses on interface $iface. Skipping." continue fi
for ip in$IP_LIST; do # Only add the rule if it doesn't already exist. if ! ip rule | grep -q "from $ip lookup main"; then logger -t "$LOG_TAG""Adding rule for $ip (on $iface) with priority $RULE_PRIO." # Use the '-6' flag for IPv6 addresses. if [[ "$ip" == *":"* ]]; then ip -6 rule add from "$ip" lookup main prio "$RULE_PRIO" else ip rule add from "$ip" lookup main prio "$RULE_PRIO" fi fi done done }
# Function to remove rules for all found interfaces remove_rules() { logger -t "$LOG_TAG""Removing policy routing rules..." # We need to find all possible IPs that could have had rules. # It's safer to just try deleting any rule with our priority. # This is simpler and more robust than tracking IPs. while ip rule | grep -q "prio $RULE_PRIO"; do # Find the first rule with our priority and delete it. Loop until none are left. RULE_TO_DELETE=$(ip rule | grep "prio $RULE_PRIO" | head -n 1) logger -t "$LOG_TAG""Deleting rule: $RULE_TO_DELETE" # Re-construct the delete command from the rule string. ip rule del ${RULE_TO_DELETE} done }