mirror of
https://github.com/fscarmen/sing-box.git
synced 2026-08-10 17:55:16 +08:00
v1.3.16 feat: support bind_interface for multi-NIC servers
- Add bind_interface option in sb -d menu to bind outbound traffic to a specific NIC - Change v2rayN Hysteria2 Realm config from Finalmask field to ProtoExtraObj v1.3.16 feat: 支持指定网络出口,多网卡绑定 - sb -d 菜单新增「指定网络出口」选项,可为出站流量绑定特定网卡 - v2rayN 的 Hysteria2 Realm 配置从 Finalmask 字段改为 ProtoExtraObj
This commit is contained in:
@@ -24,6 +24,8 @@
|
|||||||
|
|
||||||
* * *
|
* * *
|
||||||
## 1.更新信息
|
## 1.更新信息
|
||||||
|
2026.07.16 v1.3.16 1. Add bind_interface option in sb -d menu to bind outbound traffic to a specific NIC; 2. Change v2rayN Hysteria2 Realm config from Finalmask field to ProtoExtraObj; 1. sb -d 菜单新增「指定网络出口」选项,可为出站流量绑定特定网卡; 2. v2rayN 的 Hysteria2 Realm 配置从 Finalmask 字段改为 ProtoExtraObj
|
||||||
|
|
||||||
2026.07.01 v1.3.15 Add v2rayN Finalmask field for hysteria2 realm subscription output; v2rayN 订阅输出增加 Hysteria2 Realm 的 Finalmask 字段
|
2026.07.01 v1.3.15 Add v2rayN Finalmask field for hysteria2 realm subscription output; v2rayN 订阅输出增加 Hysteria2 Realm 的 Finalmask 字段
|
||||||
|
|
||||||
2026.05.29 v1.3.14 Added optional custom warp route rule management, available after installation via [sb -d] without affecting the main installation flow; 增加可选的自定义 warp 路由规则管理,不影响主程序安装流程,安装后可通过 [sb -d] 按需管理
|
2026.05.29 v1.3.14 Added optional custom warp route rule management, available after installation via [sb -d] without affecting the main installation flow; 增加可选的自定义 warp 路由规则管理,不影响主程序安装流程,安装后可通过 [sb -d] 按需管理
|
||||||
@@ -417,6 +419,7 @@ bash <(wget -qO- https://raw.githubusercontent.com/fscarmen/sing-box/main/sing-b
|
|||||||
| --HY2_REALM | [true, false],是否启用 Hysteria2 Realm,true 为启用。适用于没有公网入口、住宅 NAT、CGNAT 或需要回国打洞的机器,有公网入口时不建议使用,默认为 false |
|
| --HY2_REALM | [true, false],是否启用 Hysteria2 Realm,true 为启用。适用于没有公网入口、住宅 NAT、CGNAT 或需要回国打洞的机器,有公网入口时不建议使用,默认为 false |
|
||||||
| --HY2_WARP | [true, false],是否启用 Realm 的 WARP 辅助打洞,true 为启用。适用于 NAT 严格环境;设置为 true 时会自动启用 Realm,默认为 false |
|
| --HY2_WARP | [true, false],是否启用 Realm 的 WARP 辅助打洞,true 为启用。适用于 NAT 严格环境;设置为 true 时会自动启用 Realm,默认为 false |
|
||||||
| --REALITY_PRIVATE | reality 密钥 |
|
| --REALITY_PRIVATE | reality 密钥 |
|
||||||
|
| --BIND_INTERFACE | 指定网络出口,绑定到特定网卡,如 eth0<br>值为 default 或不指定则不绑定 |
|
||||||
| --NODE_NAME_CONFIRM | 节点名 |
|
| --NODE_NAME_CONFIRM | 节点名 |
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-4
@@ -71,8 +71,7 @@ ARGO_AUTH=''
|
|||||||
|
|
||||||
# 端口跳跃范围,用于 Hysteria2 协议,格式为 起始端口:结束端口
|
# 端口跳跃范围,用于 Hysteria2 协议,格式为 起始端口:结束端口
|
||||||
# Port hopping range for Hysteria2 protocol, format: start_port:end_port
|
# Port hopping range for Hysteria2 protocol, format: start_port:end_port
|
||||||
HY2_PORT_HOPPING_RANGE=''
|
PORT_HOPPING_RANGE=''
|
||||||
|
|
||||||
|
|
||||||
# 是否启用 Hysteria2 Realm / Enable Hysteria2 Realm: true or false
|
# 是否启用 Hysteria2 Realm / Enable Hysteria2 Realm: true or false
|
||||||
# 适用于回国或者没有公网入口的机器;有公网入口时不建议使用
|
# 适用于回国或者没有公网入口的机器;有公网入口时不建议使用
|
||||||
@@ -84,11 +83,15 @@ HY2_REALM=''
|
|||||||
# Useful for strict NAT environments such as symmetric NAT. Setting this to true automatically enables HY2_REALM
|
# Useful for strict NAT environments such as symmetric NAT. Setting this to true automatically enables HY2_REALM
|
||||||
HY2_WARP=''
|
HY2_WARP=''
|
||||||
|
|
||||||
|
|
||||||
# Reality 的私钥, 留空则随机生成
|
# Reality 的私钥, 留空则随机生成
|
||||||
# Reality private key. Random generation if left empty
|
# Reality private key. Random generation if left empty
|
||||||
REALITY_PRIVATE=''
|
REALITY_PRIVATE=''
|
||||||
|
|
||||||
# 节点名称,支持 emoji 表情
|
# 节点名称,支持 emoji 表情
|
||||||
# Node name, emoji supported
|
# Node name, emoji supported
|
||||||
NODE_NAME_CONFIRM=''
|
NODE_NAME_CONFIRM=''
|
||||||
|
|
||||||
|
# 指定网络出口,绑定到指定网卡 / Bind network interface to a specific network adapter
|
||||||
|
# 例如 / e.g. BIND_INTERFACE=eth0
|
||||||
|
# 留空则不指定 / Leave empty to not specify
|
||||||
|
BIND_INTERFACE=''
|
||||||
+79
-13
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# 当前脚本版本号
|
# 当前脚本版本号
|
||||||
VERSION='v1.3.15 (2026.07.01)'
|
VERSION='v1.3.16 (2026.07.16)'
|
||||||
|
|
||||||
# Github 反代加速代理
|
# Github 反代加速代理
|
||||||
GITHUB_PROXY=('https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/')
|
GITHUB_PROXY=('https://hub.glowp.xyz/' 'https://proxy.vvvv.ee/')
|
||||||
@@ -40,8 +40,8 @@ mkdir -p "$TEMP_DIR"
|
|||||||
|
|
||||||
E[0]="Language:\n 1. English (default) \n 2. 简体中文"
|
E[0]="Language:\n 1. English (default) \n 2. 简体中文"
|
||||||
C[0]="${E[0]}"
|
C[0]="${E[0]}"
|
||||||
E[1]="1. Added Hysteria2 Realm support for machines without public inbound access, with optional WARP-assisted hole punching; 2. add v2rayN Finalmask field for hysteria2 realm subscription output"
|
E[1]="1. Add bind_interface option in sb -d menu to bind outbound traffic to a specific NIC; 2. Change v2rayN Hysteria2 Realm config from Finalmask field to ProtoExtraObj"
|
||||||
C[1]="1. 增加 Hysteria2 Realm 支持,适用于没有公网入口的机器,并可选 WARP 辅助打洞; 2. v2rayN 订阅输出增加 Realm 的 Finalmask 字段"
|
C[1]="1. sb -d 菜单新增「指定网络出口」选项,可为出站流量绑定特定网卡; 2. v2rayN 的 Hysteria2 Realm 配置从 Finalmask 字段改为 ProtoExtraObj"
|
||||||
E[2]="Downloading Sing-box. Please wait a seconds ..."
|
E[2]="Downloading Sing-box. Please wait a seconds ..."
|
||||||
C[2]="下载 Sing-box 中,请稍等 ..."
|
C[2]="下载 Sing-box 中,请稍等 ..."
|
||||||
E[3]="Input errors up to 5 times.The script is aborted."
|
E[3]="Input errors up to 5 times.The script is aborted."
|
||||||
@@ -374,6 +374,14 @@ E[167]="Close Realm"
|
|||||||
C[167]="关闭 Realm"
|
C[167]="关闭 Realm"
|
||||||
E[168]="Open Realm"
|
E[168]="Open Realm"
|
||||||
C[168]="开启 Realm"
|
C[168]="开启 Realm"
|
||||||
|
E[169]="Bind network interface (current: \${_val:-default})"
|
||||||
|
C[169]="指定网络出口 (当前: \${_val:-默认})"
|
||||||
|
E[170]="Please select network interface:"
|
||||||
|
C[170]="请选择网络接口:"
|
||||||
|
E[171]="1. Default (not specified)"
|
||||||
|
C[171]="1. 默认(不指定)"
|
||||||
|
E[172]="Bound interface updated to: "
|
||||||
|
C[172]="绑定接口已更新为: "
|
||||||
|
|
||||||
# 自定义字体彩色,read 函数
|
# 自定义字体彩色,read 函数
|
||||||
warning() { echo -e "\033[31m\033[01m$*\033[0m"; } # 红色
|
warning() { echo -e "\033[31m\033[01m$*\033[0m"; } # 红色
|
||||||
@@ -514,7 +522,7 @@ statistics_of_run_times() {
|
|||||||
{ wget --no-check-certificate -qO- --timeout=3 "https://stat.cloudflare.now.cc/updateStats?script=${SCRIPT}" > $TEMP_DIR/statistics 2>/dev/null || true; }&
|
{ wget --no-check-certificate -qO- --timeout=3 "https://stat.cloudflare.now.cc/updateStats?script=${SCRIPT}" > $TEMP_DIR/statistics 2>/dev/null || true; }&
|
||||||
elif grep -q 'get' <<< "$UPDATE_OR_GET"; then
|
elif grep -q 'get' <<< "$UPDATE_OR_GET"; then
|
||||||
[ -s $TEMP_DIR/statistics ] && [[ $(cat $TEMP_DIR/statistics) =~ \"todayCount\":([0-9]+),\"totalCount\":([0-9]+) ]] && local TODAY="${BASH_REMATCH[1]}" && local TOTAL="${BASH_REMATCH[2]}" && rm -f $TEMP_DIR/statistics
|
[ -s $TEMP_DIR/statistics ] && [[ $(cat $TEMP_DIR/statistics) =~ \"todayCount\":([0-9]+),\"totalCount\":([0-9]+) ]] && local TODAY="${BASH_REMATCH[1]}" && local TOTAL="${BASH_REMATCH[2]}" && rm -f $TEMP_DIR/statistics
|
||||||
hint "\n*******************************************\n\n $(text 55) \n"
|
hint "\n *******************************************\n\n $(text 55) \n"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,6 +676,10 @@ change_config() {
|
|||||||
local FP_NOW=$(awk -F '"' '/"fingerprint"/{print $4; exit}' ${WORK_DIR}/list)
|
local FP_NOW=$(awk -F '"' '/"fingerprint"/{print $4; exit}' ${WORK_DIR}/list)
|
||||||
[ -n "$FP_NOW" ] && MENU_IDX+=(164) && MENU_KEY+=(fingerprint) && MENU_VAL+=("$FP_NOW")
|
[ -n "$FP_NOW" ] && MENU_IDX+=(164) && MENU_KEY+=(fingerprint) && MENU_VAL+=("$FP_NOW")
|
||||||
|
|
||||||
|
# 指定网络出口
|
||||||
|
local BIND_IFACE_NOW=$(awk -F '"' '/"bind_interface"[[:space:]]*:[[:space:]]*"/{print $4}' "${WORK_DIR}/conf/01_outbounds.json" 2>/dev/null)
|
||||||
|
MENU_IDX+=(169) && MENU_KEY+=(bindinterface) && MENU_VAL+=("${BIND_IFACE_NOW:-default}")
|
||||||
|
|
||||||
# Hysteria2 带宽和端口跳跃(仅在 Hysteria2 已安装时显示)
|
# Hysteria2 带宽和端口跳跃(仅在 Hysteria2 已安装时显示)
|
||||||
if ls ${WORK_DIR}/conf/*_${NODE_TAG[1]}_inbounds.json >/dev/null 2>&1; then
|
if ls ${WORK_DIR}/conf/*_${NODE_TAG[1]}_inbounds.json >/dev/null 2>&1; then
|
||||||
local HY2_LINE=$(grep 'type: hysteria2' ${WORK_DIR}/subscribe/proxies)
|
local HY2_LINE=$(grep 'type: hysteria2' ${WORK_DIR}/subscribe/proxies)
|
||||||
@@ -854,6 +866,60 @@ change_config() {
|
|||||||
[[ ! "${NEW_VAL,,}" =~ ^[0-9a-z]+$ ]] && error " $(text 166) " || FINGER_PRINT="$NEW_VAL"
|
[[ ! "${NEW_VAL,,}" =~ ^[0-9a-z]+$ ]] && error " $(text 166) " || FINGER_PRINT="$NEW_VAL"
|
||||||
export_list
|
export_list
|
||||||
return
|
return
|
||||||
|
elif [ "$KEY" = "bindinterface" ]; then
|
||||||
|
# 指定网络出口 — 获取系统接口列表 + 选择 + 更新 JSON
|
||||||
|
local IFACE_LIST=() CHOOSE_BIND IDX=2 TMP_FILE="${WORK_DIR}/conf/01_outbounds.json.tmp"
|
||||||
|
|
||||||
|
if command -v ip >/dev/null 2>&1; then
|
||||||
|
while read -r _ iface; do
|
||||||
|
iface="${iface%%:*}"
|
||||||
|
iface="${iface%%@*}"
|
||||||
|
[ "$iface" != "lo" ] && IFACE_LIST+=("$iface")
|
||||||
|
done < <(ip -o link show up 2>/dev/null)
|
||||||
|
elif command -v ifconfig >/dev/null 2>&1; then
|
||||||
|
while read -r iface _; do
|
||||||
|
iface="${iface%%:}"
|
||||||
|
[ "$iface" != "lo" ] && IFACE_LIST+=("$iface")
|
||||||
|
done < <(ifconfig -a 2>/dev/null | awk '/^[a-zA-Z]/')
|
||||||
|
else
|
||||||
|
for _if in /sys/class/net/*; do
|
||||||
|
_if="${_if##*/}"
|
||||||
|
[ "$_if" != "lo" ] && IFACE_LIST+=("$_if")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
mapfile -t IFACE_LIST < <(printf '%s\n' "${IFACE_LIST[@]}" | sort -u)
|
||||||
|
[ "${#IFACE_LIST[@]}" -eq 0 ] && warning " $(text 172) " && return
|
||||||
|
|
||||||
|
hint "\n $(text 170) \n"
|
||||||
|
hint " $(text 171) "
|
||||||
|
for _if in "${IFACE_LIST[@]}"; do
|
||||||
|
hint " $IDX. $_if"
|
||||||
|
((IDX++))
|
||||||
|
done
|
||||||
|
hint " 0. $(text 35)"
|
||||||
|
hint ""
|
||||||
|
reading " $(text 24) " CHOOSE_BIND
|
||||||
|
|
||||||
|
if [[ "$CHOOSE_BIND" == "1" || "${CHOOSE_BIND,,}" == "default" ]]; then
|
||||||
|
jq_exec '.outbounds |= map(if .tag == "direct" then del(.bind_interface) else . end)' \
|
||||||
|
"${WORK_DIR}/conf/01_outbounds.json" > "$TMP_FILE" && mv "$TMP_FILE" "${WORK_DIR}/conf/01_outbounds.json"
|
||||||
|
info " $(text 172) $(text 171 | sed 's/^1\. //')"
|
||||||
|
elif [[ "$CHOOSE_BIND" =~ ^[0-9]+$ ]] && [ "$CHOOSE_BIND" -ge 2 ] && [ "$CHOOSE_BIND" -le "$((IDX - 1))" ]; then
|
||||||
|
local SELECTED_IF="${IFACE_LIST[$((CHOOSE_BIND - 2))]}"
|
||||||
|
jq_exec --arg iface "$SELECTED_IF" '.outbounds |= map(if .tag == "direct" then .bind_interface = $iface else . end)' \
|
||||||
|
"${WORK_DIR}/conf/01_outbounds.json" > "$TMP_FILE" && mv "$TMP_FILE" "${WORK_DIR}/conf/01_outbounds.json"
|
||||||
|
info " $(text 172) $SELECTED_IF"
|
||||||
|
elif [ "$CHOOSE_BIND" == "0" ]; then
|
||||||
|
return
|
||||||
|
else
|
||||||
|
warning " Invalid selection " && return
|
||||||
|
fi
|
||||||
|
|
||||||
|
cmd_systemctl restart sing-box
|
||||||
|
sleep 2
|
||||||
|
cmd_systemctl status sing-box &>/dev/null && info "\n Sing-box $(text 28) $(text 37) \n" || warning "\n Sing-box $(text 27) $(text 38) \n"
|
||||||
|
export_list
|
||||||
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
hint ""
|
hint ""
|
||||||
@@ -3409,7 +3475,8 @@ EOF
|
|||||||
"outbounds":[
|
"outbounds":[
|
||||||
{
|
{
|
||||||
"type":"direct",
|
"type":"direct",
|
||||||
"tag":"direct"
|
"tag":"direct"${BIND_INTERFACE:+,
|
||||||
|
"bind_interface":"${BIND_INTERFACE}"}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -4723,15 +4790,10 @@ vless://${UUID[11]}@${SERVER_IP_1}:${PORT_XTLS_REALITY}?encryption=none${VISION_
|
|||||||
if [ -n "$PORT_HYSTERIA2" ]; then
|
if [ -n "$PORT_HYSTERIA2" ]; then
|
||||||
[[ -n "$PORT_HOPPING_START" && -n "$PORT_HOPPING_END" ]] && local V2RAYN_PARAMS=",\"Ports\":\"${PORT_HOPPING_START}-${PORT_HOPPING_END}\",\"HopInterval\":\"30s\""
|
[[ -n "$PORT_HOPPING_START" && -n "$PORT_HOPPING_END" ]] && local V2RAYN_PARAMS=",\"Ports\":\"${PORT_HOPPING_START}-${PORT_HOPPING_END}\",\"HopInterval\":\"30s\""
|
||||||
local REALM_PARAMS=""
|
local REALM_PARAMS=""
|
||||||
[ "$IS_HY2_REALM" = 'is_hy2_realm' ] && REALM_PARAMS=",\"Finalmask\":\"{\\n \\\"udp\\\": [\\n {\\n \\\"type\\\": \\\"realm\\\",\\n \\\"settings\\\": {\\n \\\"url\\\": \\\"realm://public@realm.hy2.io:443/${UUID[12]}\\\",\\n \\\"stunServers\\\": [\\n \\\"stun.nextcloud.com:3478\\\",\\n \\\"stun.sip.us:3478\\\",\\n \\\"turn.cloudflare.com:3478\\\",\\n \\\"global.stun.twilio.com:3478\\\"\\n ]\\n }\\n }\\n ],\\n \\\"quicParams\\\": {\\n \\\"congestion\\\": \\\"bbr\\\"\\n }\\n }\""
|
[ "$IS_HY2_REALM" = 'is_hy2_realm' ] && REALM_PARAMS="\"Hy2RealmUrl\":\"realm://public@realm.hy2.io:443/${UUID[12]}?stun=stun.nextcloud.com:3478&stun=stun.sip.us:3478&stun=turn.cloudflare.com:3478&stun=global.stun.twilio.com:3478\","
|
||||||
|
|
||||||
local HY2_JSON_1="{\"ConfigType\":7,\"ConfigVersion\":4,\"Remarks\":\"${NODE_NAME[12]} ${NODE_TAG[1]}\",\"Address\":\"${SERVER_IP}\",\"Port\":${PORT_HYSTERIA2},\"Password\":\"${UUID[12]}\",\"StreamSecurity\":\"tls\",\"AllowInsecure\":\"false\",\"Sni\":\"${TLS_SERVER}\",\"Cert\":\"${CERT_URL_2}\""
|
|
||||||
local HY2_JSON_2=",\"ProtoExtraObj\":{\"UpMbps\":${HY2_UP:-200},\"DownMbps\":${HY2_DOWN:-1000}"
|
|
||||||
local HY2_JSON_3="}}"
|
|
||||||
local CLEAN_V2RAYN_PARAMS="${V2RAYN_PARAMS:-}"
|
|
||||||
local V2RAYN_SUBSCRIBE+="
|
local V2RAYN_SUBSCRIBE+="
|
||||||
----------------------------
|
----------------------------
|
||||||
v2rayn://hysteria2/$(printf "%s%s%s%s%s\n" "$HY2_JSON_1" "$REALM_PARAMS" "${HY2_JSON_2%$'\n'*}" "${CLEAN_V2RAYN_PARAMS%$'\n'*}" "$HY2_JSON_3" | base64 -w0 | tr '+/' '-_' | tr -d '=')"
|
v2rayn://hysteria2/$(echo -n "{\"ConfigType\":7,\"ConfigVersion\":4,\"Remarks\":\"${NODE_NAME[12]} ${NODE_TAG[1]}\",\"Address\":\"${SERVER_IP}\",\"Port\":${PORT_HYSTERIA2},\"Password\":\"${UUID[12]}\",\"StreamSecurity\":\"tls\",\"AllowInsecure\":\"false\",\"Sni\":\"${TLS_SERVER}\",\"Cert\":\"${CERT_URL_2}\",\"ProtoExtraObj\":{"${REALM_PARAMS}"\"UpMbps\":${HY2_UP:-200},\"DownMbps\":${HY2_DOWN:-1000}}}" | base64 -w0 | tr '+/' '-_' | tr -d '=')"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ -n "$PORT_TUIC" ] && local V2RAYN_SUBSCRIBE+="
|
[ -n "$PORT_TUIC" ] && local V2RAYN_SUBSCRIBE+="
|
||||||
@@ -5114,7 +5176,7 @@ $(info "$(echo "{ \"outbounds\":[ ${OUTBOUND_REPLACE%,} ] }" | ${WORK_DIR}/jq)
|
|||||||
|
|
||||||
${PROMPT}
|
${PROMPT}
|
||||||
|
|
||||||
$(text 72)")
|
$(text 72)")
|
||||||
"
|
"
|
||||||
|
|
||||||
[ "$IS_SUB" = 'is_sub' ] && EXPORT_LIST_FILE+="
|
[ "$IS_SUB" = 'is_sub' ] && EXPORT_LIST_FILE+="
|
||||||
@@ -5917,6 +5979,10 @@ for z in ${!ALL_PARAMETER[@]}; do
|
|||||||
--HY2_WARP|--REALM_WARP|--WARP_REALM )
|
--HY2_WARP|--REALM_WARP|--WARP_REALM )
|
||||||
((z++)); [[ "${ALL_PARAMETER[z],,}" =~ ^(true|1|y|yes)$ ]] && IS_HY2_WARP=is_hy2_warp && IS_HY2_REALM=is_hy2_realm
|
((z++)); [[ "${ALL_PARAMETER[z],,}" =~ ^(true|1|y|yes)$ ]] && IS_HY2_WARP=is_hy2_warp && IS_HY2_REALM=is_hy2_realm
|
||||||
;;
|
;;
|
||||||
|
--BIND_INTERFACE )
|
||||||
|
((z++)); BIND_INTERFACE=${ALL_PARAMETER[z]}
|
||||||
|
[[ "${BIND_INTERFACE,,}" = "default" ]] && unset BIND_INTERFACE
|
||||||
|
;;
|
||||||
--REALITY_PRIVATE )
|
--REALITY_PRIVATE )
|
||||||
((z++)); REALITY_PRIVATE=${ALL_PARAMETER[z]}
|
((z++)); REALITY_PRIVATE=${ALL_PARAMETER[z]}
|
||||||
;;
|
;;
|
||||||
|
|||||||
Reference in New Issue
Block a user