新增支持隧道和转发选择出口ip

This commit is contained in:
qaq
2025-08-07 13:50:17 +08:00
parent e158e6a0bc
commit 791cde3eb2
19 changed files with 203 additions and 50 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
# ========================================== # ==========================================
# 操作系统生成的文件 # 操作系统生成的文件
# ========================================== # ==========================================
flutter_app/
# macOS # macOS
.DS_Store .DS_Store
@@ -51,7 +52,7 @@ $RECYCLE.BIN/
*.iml *.iml
*.ipr *.ipr
out/ out/
go-gost/ss/
# VS Code # VS Code
.vscode/ .vscode/
*.code-workspace *.code-workspace
BIN
View File
Binary file not shown.
+2
View File
@@ -37,6 +37,7 @@ CREATE TABLE `forward` (
`out_port` int(10) DEFAULT NULL, `out_port` int(10) DEFAULT NULL,
`remote_addr` longtext NOT NULL, `remote_addr` longtext NOT NULL,
`strategy` varchar(100) NOT NULL DEFAULT 'fifo', `strategy` varchar(100) NOT NULL DEFAULT 'fifo',
`interface_name` varchar(200) DEFAULT NULL,
`in_flow` bigint(20) NOT NULL DEFAULT '0', `in_flow` bigint(20) NOT NULL DEFAULT '0',
`out_flow` bigint(20) NOT NULL DEFAULT '0', `out_flow` bigint(20) NOT NULL DEFAULT '0',
`created_time` bigint(20) NOT NULL, `created_time` bigint(20) NOT NULL,
@@ -98,6 +99,7 @@ CREATE TABLE `tunnel` (
`out_ip` varchar(100) NOT NULL, `out_ip` varchar(100) NOT NULL,
`type` int(10) NOT NULL, `type` int(10) NOT NULL,
`protocol` varchar(10) NOT NULL DEFAULT 'tls', `protocol` varchar(10) NOT NULL DEFAULT 'tls',
`interface_name` varchar(200) DEFAULT NULL,
`flow` int(10) NOT NULL, `flow` int(10) NOT NULL,
`tcp_listen_addr` varchar(100) NOT NULL DEFAULT '0.0.0.0', `tcp_listen_addr` varchar(100) NOT NULL DEFAULT '0.0.0.0',
`udp_listen_addr` varchar(100) NOT NULL DEFAULT '0.0.0.0', `udp_listen_addr` varchar(100) NOT NULL DEFAULT '0.0.0.0',
+36
View File
@@ -779,6 +779,42 @@ UPDATE \`forward\`
SET \`inx\` = 0 SET \`inx\` = 0
WHERE \`inx\` IS NULL; WHERE \`inx\` IS NULL;
-- tunnel 表:添加 interface_name 字段(如果不存在)
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'tunnel'
AND column_name = 'interface_name'
),
'ALTER TABLE \`tunnel\` ADD COLUMN \`interface_name\` VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL;',
'SELECT "Column \`interface_name\` already exists in \`tunnel\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- forward 表:添加 interface_name 字段(如果不存在)
SET @sql = (
SELECT IF(
NOT EXISTS (
SELECT 1
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()
AND table_name = 'forward'
AND column_name = 'interface_name'
),
'ALTER TABLE \`forward\` ADD COLUMN \`interface_name\` VARCHAR(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL;',
'SELECT "Column \`interface_name\` already exists in \`forward\`";'
)
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- 创建 vite_config 表(如果不存在) -- 创建 vite_config 表(如果不存在)
CREATE TABLE IF NOT EXISTS \`vite_config\` ( CREATE TABLE IF NOT EXISTS \`vite_config\` (
\`id\` int(10) NOT NULL AUTO_INCREMENT, \`id\` int(10) NOT NULL AUTO_INCREMENT,
@@ -1,5 +1,7 @@
package com.admin.common.dto; package com.admin.common.dto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -27,4 +29,6 @@ public class ForwardDto {
@Max(value = 65535, message = "端口号不能大于65535") @Max(value = 65535, message = "端口号不能大于65535")
private Integer inPort; private Integer inPort;
private String interfaceName;
} }
@@ -1,5 +1,7 @@
package com.admin.common.dto; package com.admin.common.dto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -32,4 +34,7 @@ public class ForwardUpdateDto {
@Min(value = 1, message = "端口号不能小于1") @Min(value = 1, message = "端口号不能小于1")
@Max(value = 65535, message = "端口号不能大于65535") @Max(value = 65535, message = "端口号不能大于65535")
private Integer inPort; private Integer inPort;
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String interfaceName;
} }
@@ -87,4 +87,6 @@ public class ForwardWithTunnelDto {
private String strategy; private String strategy;
private Integer inx; private Integer inx;
private String interfaceName;
} }
@@ -31,6 +31,8 @@ public class TunnelDto {
@DecimalMin(value = "0.0", message = "流量倍率不能小于0.0") @DecimalMin(value = "0.0", message = "流量倍率不能小于0.0")
@DecimalMax(value = "100.0", message = "流量倍率不能大于100.0") @DecimalMax(value = "100.0", message = "流量倍率不能大于100.0")
private BigDecimal trafficRatio = new BigDecimal("1.0"); private BigDecimal trafficRatio = new BigDecimal("1.0");
private String interfaceName;
// 协议类型,默认为tls // 协议类型,默认为tls
private String protocol; private String protocol;
@@ -1,5 +1,7 @@
package com.admin.common.dto; package com.admin.common.dto;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -36,4 +38,7 @@ public class TunnelUpdateDto {
// UDP监听地址 // UDP监听地址
@NotBlank @NotBlank
private String udpListenAddr; private String udpListenAddr;
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String interfaceName;
} }
@@ -5,6 +5,7 @@ import com.admin.common.dto.GostDto;
import com.admin.entity.Tunnel; import com.admin.entity.Tunnel;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.apache.bcel.generic.RET; import org.aspectj.apache.bcel.generic.RET;
import java.util.Objects; import java.util.Objects;
@@ -31,21 +32,21 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, req, "DeleteLimiters"); return WebSocketServer.send_msg(node_id, req, "DeleteLimiters");
} }
public static GostDto AddService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy) { public static GostDto AddService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy, String interfaceName) {
JSONArray services = new JSONArray(); JSONArray services = new JSONArray();
String[] protocols = {"tcp", "udp"}; String[] protocols = {"tcp", "udp"};
for (String protocol : protocols) { for (String protocol : protocols) {
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy); JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy, interfaceName);
services.add(service); services.add(service);
} }
return WebSocketServer.send_msg(node_id, services, "AddService"); return WebSocketServer.send_msg(node_id, services, "AddService");
} }
public static GostDto UpdateService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy) { public static GostDto UpdateService(Long node_id, String name, Integer in_port, Integer limiter, String remoteAddr, Integer fow_type, Tunnel tunnel, String strategy, String interfaceName) {
JSONArray services = new JSONArray(); JSONArray services = new JSONArray();
String[] protocols = {"tcp", "udp"}; String[] protocols = {"tcp", "udp"};
for (String protocol : protocols) { for (String protocol : protocols) {
JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy); JSONObject service = createServiceConfig(name, in_port, limiter, remoteAddr, protocol, fow_type, tunnel, strategy, interfaceName);
services.add(service); services.add(service);
} }
return WebSocketServer.send_msg(node_id, services, "UpdateService"); return WebSocketServer.send_msg(node_id, services, "UpdateService");
@@ -60,10 +61,18 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, data, "DeleteService"); return WebSocketServer.send_msg(node_id, data, "DeleteService");
} }
public static GostDto AddRemoteService(Long node_id, String name, Integer out_port, String remoteAddr, String protocol, String strategy) { public static GostDto AddRemoteService(Long node_id, String name, Integer out_port, String remoteAddr, String protocol, String strategy, String interfaceName) {
JSONObject data = new JSONObject(); JSONObject data = new JSONObject();
data.put("name", name + "_tls"); data.put("name", name + "_tls");
data.put("addr", ":" + out_port); data.put("addr", ":" + out_port);
if (StringUtils.isNotBlank(interfaceName)) {
JSONObject metadata = new JSONObject();
metadata.put("interface", interfaceName);
data.put("metadata", metadata);
}
JSONObject handler = new JSONObject(); JSONObject handler = new JSONObject();
handler.put("type", "relay"); handler.put("type", "relay");
data.put("handler", handler); data.put("handler", handler);
@@ -89,7 +98,7 @@ public class GostUtil {
JSONObject selector = new JSONObject(); JSONObject selector = new JSONObject();
selector.put("strategy", strategy); selector.put("strategy", strategy);
selector.put("maxFails", 1); selector.put("maxFails", 1);
selector.put("failTimeout", "10s"); selector.put("failTimeout", "600s");
forwarder.put("selector", selector); forwarder.put("selector", selector);
data.put("forwarder", forwarder); data.put("forwarder", forwarder);
@@ -98,10 +107,18 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, services, "AddService"); return WebSocketServer.send_msg(node_id, services, "AddService");
} }
public static GostDto UpdateRemoteService(Long node_id, String name, Integer out_port, String remoteAddr,String protocol, String strategy) { public static GostDto UpdateRemoteService(Long node_id, String name, Integer out_port, String remoteAddr,String protocol, String strategy, String interfaceName) {
JSONObject data = new JSONObject(); JSONObject data = new JSONObject();
data.put("name", name + "_tls"); data.put("name", name + "_tls");
data.put("addr", ":" + out_port); data.put("addr", ":" + out_port);
if (StringUtils.isNotBlank(interfaceName)) {
JSONObject metadata = new JSONObject();
metadata.put("interface", interfaceName);
data.put("metadata", metadata);
}
JSONObject handler = new JSONObject(); JSONObject handler = new JSONObject();
handler.put("type", "relay"); handler.put("type", "relay");
data.put("handler", handler); data.put("handler", handler);
@@ -127,7 +144,7 @@ public class GostUtil {
JSONObject selector = new JSONObject(); JSONObject selector = new JSONObject();
selector.put("strategy", strategy); selector.put("strategy", strategy);
selector.put("maxFails", 1); selector.put("maxFails", 1);
selector.put("failTimeout", "10s"); selector.put("failTimeout", "600s");
forwarder.put("selector", selector); forwarder.put("selector", selector);
data.put("forwarder", forwarder); data.put("forwarder", forwarder);
@@ -178,7 +195,7 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, data, "ResumeService"); return WebSocketServer.send_msg(node_id, data, "ResumeService");
} }
public static GostDto AddChains(Long node_id, String name, String remoteAddr, String protocol) { public static GostDto AddChains(Long node_id, String name, String remoteAddr, String protocol, String interfaceName) {
JSONObject dialer = new JSONObject(); JSONObject dialer = new JSONObject();
dialer.put("type", protocol); dialer.put("type", protocol);
if (Objects.equals(protocol, "quic")){ if (Objects.equals(protocol, "quic")){
@@ -200,6 +217,11 @@ public class GostUtil {
node.put("connector", connector); node.put("connector", connector);
node.put("dialer", dialer); node.put("dialer", dialer);
if (StringUtils.isNotBlank(interfaceName)) {
node.put("interface", interfaceName);
}
JSONArray nodes = new JSONArray(); JSONArray nodes = new JSONArray();
nodes.add(node); nodes.add(node);
@@ -217,7 +239,7 @@ public class GostUtil {
return WebSocketServer.send_msg(node_id, data, "AddChains"); return WebSocketServer.send_msg(node_id, data, "AddChains");
} }
public static GostDto UpdateChains(Long node_id, String name, String remoteAddr, String protocol) { public static GostDto UpdateChains(Long node_id, String name, String remoteAddr, String protocol, String interfaceName) {
JSONObject dialer = new JSONObject(); JSONObject dialer = new JSONObject();
dialer.put("type", protocol); dialer.put("type", protocol);
@@ -238,6 +260,10 @@ public class GostUtil {
node.put("connector", connector); node.put("connector", connector);
node.put("dialer", dialer); node.put("dialer", dialer);
if (StringUtils.isNotBlank(interfaceName)) {
node.put("interface", interfaceName);
}
JSONArray nodes = new JSONArray(); JSONArray nodes = new JSONArray();
nodes.add(node); nodes.add(node);
@@ -272,7 +298,7 @@ public class GostUtil {
return data; return data;
} }
private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type, Tunnel tunnel, String strategy) { private static JSONObject createServiceConfig(String name, Integer in_port, Integer limiter, String remoteAddr, String protocol, Integer fow_type, Tunnel tunnel, String strategy, String interfaceName) {
JSONObject service = new JSONObject(); JSONObject service = new JSONObject();
service.put("name", name + "_" + protocol); service.put("name", name + "_" + protocol);
if (Objects.equals(protocol, "tcp")){ if (Objects.equals(protocol, "tcp")){
@@ -281,6 +307,13 @@ public class GostUtil {
service.put("addr", tunnel.getUdpListenAddr() + ":" + in_port); service.put("addr", tunnel.getUdpListenAddr() + ":" + in_port);
} }
if (StringUtils.isNotBlank(interfaceName)) {
JSONObject metadata = new JSONObject();
metadata.put("interface", interfaceName);
service.put("metadata", metadata);
}
// 添加限流器配置 // 添加限流器配置
if (limiter != null) { if (limiter != null) {
service.put("limiter", limiter.toString()); service.put("limiter", limiter.toString());
@@ -348,7 +381,7 @@ public class GostUtil {
JSONObject selector = new JSONObject(); JSONObject selector = new JSONObject();
selector.put("strategy", strategy); selector.put("strategy", strategy);
selector.put("maxFails", 1); selector.put("maxFails", 1);
selector.put("failTimeout", "10s"); selector.put("failTimeout", "600s");
forwarder.put("selector", selector); forwarder.put("selector", selector);
return forwarder; return forwarder;
} }
@@ -32,6 +32,8 @@ public class Forward extends BaseEntity{
private String remoteAddr; private String remoteAddr;
private String interfaceName;
private String strategy; private String strategy;
private Long inFlow; private Long inFlow;
@@ -68,4 +68,6 @@ public class Tunnel extends BaseEntity {
private String tcpListenAddr; private String tcpListenAddr;
private String udpListenAddr; private String udpListenAddr;
private String interfaceName;
} }
@@ -103,8 +103,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
} }
// 7. 调用Gost服务创建转发 // 7. 调用Gost服务创建转发
R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter(), R gostResult = createGostServices(forward, tunnel, permissionResult.getLimiter(), nodeInfo, permissionResult.getUserTunnel());
nodeInfo, permissionResult.getUserTunnel());
if (gostResult.getCode() != 0) { if (gostResult.getCode() != 0) {
this.removeById(forward.getId()); this.removeById(forward.getId());
@@ -949,19 +948,18 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/** /**
* 创建Gost服务 * 创建Gost服务
*/ */
private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter, private R createGostServices(Forward forward, Tunnel tunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
NodeInfo nodeInfo, UserTunnel userTunnel) {
String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel); String serviceName = buildServiceName(forward.getId(), forward.getUserId(), userTunnel);
// 隧道转发需要创建链和远程服务 // 隧道转发需要创建链和远程服务
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) { if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
R chainResult = createChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol()); R chainResult = createChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol(), tunnel.getInterfaceName());
if (chainResult.getCode() != 0) { if (chainResult.getCode() != 0) {
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName); GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
return chainResult; return chainResult;
} }
R remoteResult = createRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol()); R remoteResult = createRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol(), forward.getInterfaceName());
if (remoteResult.getCode() != 0) { if (remoteResult.getCode() != 0) {
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName); GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
GostUtil.DeleteRemoteService(nodeInfo.getOutNode().getId(), serviceName); GostUtil.DeleteRemoteService(nodeInfo.getOutNode().getId(), serviceName);
@@ -969,9 +967,14 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
} }
} }
String interfaceName = null;
// 创建主服务 // 创建主服务
R serviceResult = createMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy()); if (tunnel.getType() != TUNNEL_TYPE_TUNNEL_FORWARD) { // 不是隧道转发服务才会存在网络接口
interfaceName = forward.getInterfaceName();
}
R serviceResult = createMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy(), interfaceName);
if (serviceResult.getCode() != 0) { if (serviceResult.getCode() != 0) {
GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName); GostUtil.DeleteChains(nodeInfo.getInNode().getId(), serviceName);
if (nodeInfo.getOutNode() != null) { if (nodeInfo.getOutNode() != null) {
@@ -991,21 +994,25 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
// 隧道转发需要更新链和远程服务 // 隧道转发需要更新链和远程服务
if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) { if (tunnel.getType() == TUNNEL_TYPE_TUNNEL_FORWARD) {
R chainResult = updateChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol()); R chainResult = updateChainService(nodeInfo.getInNode(), serviceName, tunnel.getOutIp(), forward.getOutPort(), tunnel.getProtocol(), tunnel.getInterfaceName());
if (chainResult.getCode() != 0) { if (chainResult.getCode() != 0) {
updateForwardStatusToError(forward); updateForwardStatusToError(forward);
return chainResult; return chainResult;
} }
R remoteResult = updateRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol()); R remoteResult = updateRemoteService(nodeInfo.getOutNode(), serviceName, forward, tunnel.getProtocol(), forward.getInterfaceName());
if (remoteResult.getCode() != 0) { if (remoteResult.getCode() != 0) {
updateForwardStatusToError(forward); updateForwardStatusToError(forward);
return remoteResult; return remoteResult;
} }
} }
String interfaceName = null;
// 创建主服务
if (tunnel.getType() != TUNNEL_TYPE_TUNNEL_FORWARD) { // 不是隧道转发服务才会存在网络接口
interfaceName = forward.getInterfaceName();
}
// 更新主服务 // 更新主服务
R serviceResult = updateMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy()); R serviceResult = updateMainService(nodeInfo.getInNode(), serviceName, forward, limiter, tunnel.getType(), tunnel, forward.getStrategy(), interfaceName);
if (serviceResult.getCode() != 0) { if (serviceResult.getCode() != 0) {
updateForwardStatusToError(forward); updateForwardStatusToError(forward);
return serviceResult; return serviceResult;
@@ -1017,8 +1024,7 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/** /**
* 隧道变化时更新Gost服务:先删除原配置,再创建新配置 * 隧道变化时更新Gost服务:先删除原配置,再创建新配置
*/ */
private R updateGostServicesWithTunnelChange(Forward existForward, Forward updatedForward, Tunnel newTunnel, private R updateGostServicesWithTunnelChange(Forward existForward, Forward updatedForward, Tunnel newTunnel, Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
Integer limiter, NodeInfo nodeInfo, UserTunnel userTunnel) {
// 1. 获取原隧道信息 // 1. 获取原隧道信息
Tunnel oldTunnel = tunnelService.getById(existForward.getTunnelId()); Tunnel oldTunnel = tunnelService.getById(existForward.getTunnelId());
if (oldTunnel == null) { if (oldTunnel == null) {
@@ -1124,43 +1130,43 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/** /**
* 创建链服务 * 创建链服务
*/ */
private R createChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol) { private R createChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol, String interfaceName) {
String remoteAddr = outIp + ":" + outPort; String remoteAddr = outIp + ":" + outPort;
if (outIp.contains(":")) { if (outIp.contains(":")) {
remoteAddr = "[" + outIp + "]:" + outPort; remoteAddr = "[" + outIp + "]:" + outPort;
} }
GostDto result = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol); GostDto result = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol, interfaceName);
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg()); return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
} }
/** /**
* 创建远程服务 * 创建远程服务
*/ */
private R createRemoteService(Node outNode, String serviceName, Forward forward, String protocol) { private R createRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
GostDto result = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy()); GostDto result = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg()); return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
} }
/** /**
* 创建主服务 * 创建主服务
*/ */
private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy) { private R createMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy, String interfaceName) {
GostDto result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy); GostDto result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg()); return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
} }
/** /**
* 更新链服务 * 更新链服务
*/ */
private R updateChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol) { private R updateChainService(Node inNode, String serviceName, String outIp, Integer outPort, String protocol, String interfaceName) {
// 创建新链 // 创建新链
String remoteAddr = outIp + ":" + outPort; String remoteAddr = outIp + ":" + outPort;
if (outIp.contains(":")) { if (outIp.contains(":")) {
remoteAddr = "[" + outIp + "]:" + outPort; remoteAddr = "[" + outIp + "]:" + outPort;
} }
GostDto createResult = GostUtil.UpdateChains(inNode.getId(), serviceName, remoteAddr, protocol); GostDto createResult = GostUtil.UpdateChains(inNode.getId(), serviceName, remoteAddr, protocol, interfaceName);
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) { if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
createResult = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol); createResult = GostUtil.AddChains(inNode.getId(), serviceName, remoteAddr, protocol, interfaceName);
} }
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg()); return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
} }
@@ -1168,15 +1174,11 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/** /**
* 更新远程服务 * 更新远程服务
*/ */
private R updateRemoteService(Node outNode, String serviceName, Forward forward, String protocol) { private R updateRemoteService(Node outNode, String serviceName, Forward forward, String protocol, String interfaceName) {
// 创建新远程服务 // 创建新远程服务
GostDto createResult = GostUtil.UpdateRemoteService(outNode.getId(), GostDto createResult = GostUtil.UpdateRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(), protocol, forward.getStrategy(), interfaceName);
serviceName, forward.getOutPort(),
forward.getRemoteAddr(), protocol, forward.getStrategy());
if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) { if (createResult.getMsg().contains(GOST_NOT_FOUND_MSG)) {
createResult = GostUtil.AddRemoteService(outNode.getId(), createResult = GostUtil.AddRemoteService(outNode.getId(), serviceName, forward.getOutPort(), forward.getRemoteAddr(),protocol, forward.getStrategy(), interfaceName);
serviceName, forward.getOutPort(),
forward.getRemoteAddr(),protocol, forward.getStrategy());
} }
return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg()); return isGostOperationSuccess(createResult) ? R.ok() : R.err(createResult.getMsg());
} }
@@ -1184,11 +1186,11 @@ public class ForwardServiceImpl extends ServiceImpl<ForwardMapper, Forward> impl
/** /**
* 更新主服务 * 更新主服务
*/ */
private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy) { private R updateMainService(Node inNode, String serviceName, Forward forward, Integer limiter, Integer tunnelType, Tunnel tunnel, String strategy, String interfaceName) {
GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy); GostDto result = GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) { if (result.getMsg().contains(GOST_NOT_FOUND_MSG)) {
result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy); result = GostUtil.AddService(inNode.getId(), serviceName, forward.getInPort(), limiter, forward.getRemoteAddr(), tunnelType, tunnel, strategy, interfaceName);
} }
return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg()); return isGostOperationSuccess(result) ? R.ok() : R.err(result.getMsg());
@@ -22,6 +22,7 @@ import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -176,10 +177,12 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
int up = 0; int up = 0;
if (!Objects.equals(existingTunnel.getTcpListenAddr(), tunnelUpdateDto.getTcpListenAddr()) || if (!Objects.equals(existingTunnel.getTcpListenAddr(), tunnelUpdateDto.getTcpListenAddr()) ||
!Objects.equals(existingTunnel.getUdpListenAddr(), tunnelUpdateDto.getUdpListenAddr()) || !Objects.equals(existingTunnel.getUdpListenAddr(), tunnelUpdateDto.getUdpListenAddr()) ||
!Objects.equals(existingTunnel.getProtocol(), tunnelUpdateDto.getProtocol())) { !Objects.equals(existingTunnel.getProtocol(), tunnelUpdateDto.getProtocol()) ||
!Objects.equals(existingTunnel.getInterfaceName(), tunnelUpdateDto.getInterfaceName())) {
up++; up++;
} }
// 5. 更新允许修改的字段 // 5. 更新允许修改的字段
existingTunnel.setName(tunnelUpdateDto.getName()); existingTunnel.setName(tunnelUpdateDto.getName());
existingTunnel.setFlow(tunnelUpdateDto.getFlow()); existingTunnel.setFlow(tunnelUpdateDto.getFlow());
@@ -187,9 +190,11 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
existingTunnel.setUdpListenAddr(tunnelUpdateDto.getUdpListenAddr()); existingTunnel.setUdpListenAddr(tunnelUpdateDto.getUdpListenAddr());
existingTunnel.setTrafficRatio(tunnelUpdateDto.getTrafficRatio()); existingTunnel.setTrafficRatio(tunnelUpdateDto.getTrafficRatio());
existingTunnel.setProtocol(tunnelUpdateDto.getProtocol()); existingTunnel.setProtocol(tunnelUpdateDto.getProtocol());
existingTunnel.setInterfaceName(tunnelUpdateDto.getInterfaceName());
this.updateById(existingTunnel); this.updateById(existingTunnel);
int err = 0; int err = 0;
if (up == 1){ if (up != 0){
System.out.println("123123");
List<Forward> tunnel = forwardService.list(new QueryWrapper<Forward>().eq("tunnel_id", tunnelUpdateDto.getId())); List<Forward> tunnel = forwardService.list(new QueryWrapper<Forward>().eq("tunnel_id", tunnelUpdateDto.getId()));
if (!tunnel.isEmpty()) { if (!tunnel.isEmpty()) {
for (Forward forward : tunnel) { for (Forward forward : tunnel) {
@@ -201,6 +206,7 @@ public class TunnelServiceImpl extends ServiceImpl<TunnelMapper, Tunnel> impleme
forwardUpdateDto.setRemoteAddr(forward.getRemoteAddr()); forwardUpdateDto.setRemoteAddr(forward.getRemoteAddr());
forwardUpdateDto.setStrategy(forward.getStrategy()); forwardUpdateDto.setStrategy(forward.getStrategy());
forwardUpdateDto.setInPort(forward.getInPort()); forwardUpdateDto.setInPort(forward.getInPort());
forwardUpdateDto.setInterfaceName(forward.getInterfaceName());
R r = forwardService.updateForward(forwardUpdateDto); R r = forwardService.updateForward(forwardUpdateDto);
if (r.getCode() != 0){ if (r.getCode() != 0){
err++; err++;
@@ -498,8 +498,14 @@ public class UserTunnelServiceImpl extends ServiceImpl<UserTunnelMapper, UserTun
for (Forward forward : userTunnelForwards) { for (Forward forward : userTunnelForwards) {
String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnel.getId()); String serviceName = buildServiceName(forward.getId(), Long.valueOf(userId), userTunnel.getId());
String interfaceName = null;
// 创建主服务
if (tunnel.getType() != 2) { // 不是隧道转发服务才会存在网络接口
interfaceName = forward.getInterfaceName();
}
// 6. 更新入口节点的主服务限速配置(使用批量UpdateService接口) // 6. 更新入口节点的主服务限速配置(使用批量UpdateService接口)
GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), speedId, forward.getRemoteAddr(), tunnel.getType(), tunnel, forward.getStrategy()); GostUtil.UpdateService(inNode.getId(), serviceName, forward.getInPort(), speedId, forward.getRemoteAddr(), tunnel.getType(), tunnel, forward.getStrategy(), interfaceName);
} }
} }
} }
@@ -20,6 +20,7 @@
f.strategy AS strategy, f.strategy AS strategy,
f.out_flow AS outFlow, f.out_flow AS outFlow,
f.inx AS inx, f.inx AS inx,
f.interface_name AS interfaceName,
t.name AS tunnelName, t.name AS tunnelName,
t.in_ip AS inIp, t.in_ip AS inIp,
t.out_ip AS outIp, t.out_ip AS outIp,
@@ -51,6 +52,7 @@
f.out_flow AS outFlow, f.out_flow AS outFlow,
f.strategy AS strategy, f.strategy AS strategy,
f.inx AS inx, f.inx AS inx,
f.interface_name AS interfaceName,
t.name AS tunnelName, t.name AS tunnelName,
t.in_ip AS inIp, t.in_ip AS inIp,
t.out_ip AS outIp, t.out_ip AS outIp,
+1 -1
View File
@@ -4,7 +4,7 @@ export type SiteConfig = typeof siteConfig;
// 缓存相关常量 // 缓存相关常量
const CACHE_PREFIX = 'vite_config_'; const CACHE_PREFIX = 'vite_config_';
const VERSION = "1.2.3"; const VERSION = "1.2.4";
// 缓存策略: // 缓存策略:
// - 长期有效,不设置过期时间 // - 长期有效,不设置过期时间
+18
View File
@@ -54,6 +54,7 @@ interface Forward {
inIp: string; inIp: string;
inPort: number; inPort: number;
remoteAddr: string; remoteAddr: string;
interfaceName?: string;
strategy: string; strategy: string;
status: number; status: number;
inFlow: number; inFlow: number;
@@ -79,6 +80,7 @@ interface ForwardForm {
tunnelId: number | null; tunnelId: number | null;
inPort: number | null; inPort: number | null;
remoteAddr: string; remoteAddr: string;
interfaceName?: string;
strategy: string; strategy: string;
} }
@@ -188,6 +190,7 @@ export default function ForwardPage() {
tunnelId: null, tunnelId: null,
inPort: null, inPort: null,
remoteAddr: '', remoteAddr: '',
interfaceName: '',
strategy: 'fifo' strategy: 'fifo'
}); });
@@ -440,6 +443,7 @@ export default function ForwardPage() {
tunnelId: null, tunnelId: null,
inPort: null, inPort: null,
remoteAddr: '', remoteAddr: '',
interfaceName: '',
strategy: 'fifo' strategy: 'fifo'
}); });
setSelectedTunnel(null); setSelectedTunnel(null);
@@ -457,6 +461,7 @@ export default function ForwardPage() {
tunnelId: forward.tunnelId, tunnelId: forward.tunnelId,
inPort: forward.inPort, inPort: forward.inPort,
remoteAddr: forward.remoteAddr.split(',').join('\n'), remoteAddr: forward.remoteAddr.split(',').join('\n'),
interfaceName: forward.interfaceName || '',
strategy: forward.strategy || 'fifo' strategy: forward.strategy || 'fifo'
}); });
const tunnel = tunnels.find(t => t.id === forward.tunnelId); const tunnel = tunnels.find(t => t.id === forward.tunnelId);
@@ -535,6 +540,7 @@ export default function ForwardPage() {
tunnelId: form.tunnelId, tunnelId: form.tunnelId,
inPort: form.inPort, inPort: form.inPort,
remoteAddr: processedRemoteAddr, remoteAddr: processedRemoteAddr,
interfaceName: form.interfaceName,
strategy: addressCount > 1 ? form.strategy : 'fifo' strategy: addressCount > 1 ? form.strategy : 'fifo'
}; };
res = await updateForward(updateData); res = await updateForward(updateData);
@@ -545,6 +551,7 @@ export default function ForwardPage() {
tunnelId: form.tunnelId, tunnelId: form.tunnelId,
inPort: form.inPort, inPort: form.inPort,
remoteAddr: processedRemoteAddr, remoteAddr: processedRemoteAddr,
interfaceName: form.interfaceName,
strategy: addressCount > 1 ? form.strategy : 'fifo' strategy: addressCount > 1 ? form.strategy : 'fifo'
}; };
res = await createForward(createData); res = await createForward(createData);
@@ -1620,6 +1627,17 @@ export default function ForwardPage() {
maxRows={6} maxRows={6}
/> />
<Input
label="出口网卡名或IP"
placeholder="请输入出口网卡名或IP"
value={form.interfaceName}
onChange={(e) => setForm(prev => ({ ...prev, interfaceName: e.target.value }))}
isInvalid={!!errors.interfaceName}
errorMessage={errors.interfaceName}
variant="bordered"
description="用于多IP服务器指定使用那个IP请求远程地址,不懂的默认为空就行"
/>
{getAddressCount(form.remoteAddr) > 1 && ( {getAddressCount(form.remoteAddr) > 1 && (
<Select <Select
label="负载策略" label="负载策略"
+25
View File
@@ -31,6 +31,7 @@ interface Tunnel {
protocol?: string; protocol?: string;
tcpListenAddr: string; tcpListenAddr: string;
udpListenAddr: string; udpListenAddr: string;
interfaceName?: string;
flow: number; // 1: 单向, 2: 双向 flow: number; // 1: 单向, 2: 双向
trafficRatio: number; trafficRatio: number;
status: number; status: number;
@@ -52,6 +53,7 @@ interface TunnelForm {
protocol: string; protocol: string;
tcpListenAddr: string; tcpListenAddr: string;
udpListenAddr: string; udpListenAddr: string;
interfaceName?: string;
flow: number; flow: number;
trafficRatio: number; trafficRatio: number;
status: number; status: number;
@@ -100,6 +102,7 @@ export default function TunnelPage() {
protocol: 'tls', protocol: 'tls',
tcpListenAddr: '0.0.0.0', tcpListenAddr: '0.0.0.0',
udpListenAddr: '0.0.0.0', udpListenAddr: '0.0.0.0',
interfaceName: '',
flow: 1, flow: 1,
trafficRatio: 1.0, trafficRatio: 1.0,
status: 1 status: 1
@@ -194,6 +197,7 @@ export default function TunnelPage() {
protocol: 'tls', protocol: 'tls',
tcpListenAddr: '0.0.0.0', tcpListenAddr: '0.0.0.0',
udpListenAddr: '0.0.0.0', udpListenAddr: '0.0.0.0',
interfaceName: '',
flow: 1, flow: 1,
trafficRatio: 1.0, trafficRatio: 1.0,
status: 1 status: 1
@@ -214,6 +218,7 @@ export default function TunnelPage() {
protocol: tunnel.protocol || 'tls', protocol: tunnel.protocol || 'tls',
tcpListenAddr: tunnel.tcpListenAddr || '0.0.0.0', tcpListenAddr: tunnel.tcpListenAddr || '0.0.0.0',
udpListenAddr: tunnel.udpListenAddr || '0.0.0.0', udpListenAddr: tunnel.udpListenAddr || '0.0.0.0',
interfaceName: tunnel.interfaceName || '',
flow: tunnel.flow, flow: tunnel.flow,
trafficRatio: tunnel.trafficRatio, trafficRatio: tunnel.trafficRatio,
status: tunnel.status status: tunnel.status
@@ -751,6 +756,19 @@ export default function TunnelPage() {
/> />
</div> </div>
{/* 隧道转发时显示出口网卡配置 */}
{form.type === 2 && (
<Input
label="出口网卡名或IP"
placeholder="请输入出口网卡名或IP"
value={form.interfaceName}
onChange={(e) => setForm(prev => ({ ...prev, interfaceName: e.target.value }))}
isInvalid={!!errors.interfaceName}
errorMessage={errors.interfaceName}
variant="bordered"
/>
)}
{/* 隧道转发时显示出口配置 */} {/* 隧道转发时显示出口配置 */}
{form.type === 2 && ( {form.type === 2 && (
<> <>
@@ -830,6 +848,13 @@ export default function TunnelPage() {
description="V6或者双栈填写[::],V4填写0.0.0.0。不懂的就去看文档网站内的说明" description="V6或者双栈填写[::],V4填写0.0.0.0。不懂的就去看文档网站内的说明"
className="mt-4" className="mt-4"
/> />
<Alert
color="primary"
variant="flat"
title="出口网卡名或IP"
description="用于多IP服务器指定使用那个IP和出口服务器通讯,不懂的默认为空就行"
className="mt-4"
/>
</div> </div>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>