返回首页

Go TUN UDP 中继:YAML 配置和代码

本文描述了在 Go 上实现 UDP 中继,支持通过 YAML 配置的 TUN 接口。详细介绍了带 MD5 哈希和时间戳的协议,提供了 iperf3 基准测试 925 Mbit/s。适用于中高级网络软件开发者。

使用 TUN 接口在 Go 上创建 UDP 中继
Advertisement 728x90

Go与TUN/TAP:基于YAML配置的UDP中继构建

Go开发者可以轻松构建支持TUN接口和UDP隧道的网络中继。该架构通过YAML配置定义入站/出站链路,每个中继节点均能处理双向流量——在TUN与UDP之间来回转发,实现复杂路由而无需硬编码逻辑。

核心实现采用 io.ReadWriteCloser 接口统一所有节点。逻辑清晰明了:从入站读取数据包,转发至出站,并处理反向流量。

YAML配置结构

配置文件以数组形式定义多个入站/出站对。参数涵盖接口类型、IP地址分配、NAT规则及认证机制。

Google AdInline article slot

示例:TUN到TUN中继

relays:
  - ingress:
      type: tun
      name: tun10
      cidr: "10.0.0.2/24"
      peer: "10.0.0.1"
    egress:
      type: tun
      name: tun11
      cidr: "10.0.1.2/24"
      peer: "10.0.1.1"
      nat:
        forward:
          src: "10.0.1.1"
        backward:
          dst: "10.0.0.2"

若需构建UDP中继,只需添加客户端与服务端端点:

relays:
  - ingress:
      type: tun
      name: tun10
      cidr: "10.0.0.2/24"
      peer: "10.0.0.1"
    egress:
      type: udp
      dial: "localhost:4000"
      password: "pass"
  - ingress:
      type: udp
      listen: "localhost:4000"
      password: "pass"
    egress:
      type: tun
      name: tun11
      cidr: "10.0.1.2/24"
      peer: "10.0.1.1"
      nat:
        forward:
          src: "10.0.1.1"
        backward:
          dst: "10.0.0.2"

此配置形成完整链路:tun10 → UDP客户端 → UDP服务器 → tun11,所有通信均在本地完成。

Google AdInline article slot

UDP隧道协议

每个IP数据包附加一个头部:4字节时间戳(uint32大端序)+ 16字节MD5哈希值。哈希由密码 + 时间戳 + 数据包前64字节计算得出。

接收端进行三项校验:

  • 数据包长度 ≥ 20字节(头部大小)
  • 时间戳与当前时间相差不超过10秒
  • 哈希值匹配

解包代码如下:

Google AdInline article slot
func (i *Ingress) Read(b []byte) (int, error) {
	 n, raddr, err := i.conn.ReadFrom(b)
	 if err != nil {
		 return 0, err
	 }

	 data, err := unpack(b[:n:n], i.pass)
	 if err != nil {
		 return 0, err
	 }

	 i.raddr = raddr
	 copy(b, data)
	 return len(data), nil
}

func unpack(packet []byte, pass string) ([]byte, error) {
	 if len(packet) < HeaderSize {
		 return nil, ErrSmallPacket
	 }

	 rtimestamp := binary.BigEndian.Uint32(packet[0:4])
	 rhash := [HashSize]byte(packet[4 : 4+HashSize])
	 payload := packet[HeaderSize:]

	 timestamp := uint32(time.Now().Unix())
	 if timestamp-rtimestamp > MaxTimeDiff && rtimestamp-timestamp > MaxTimeDiff {
		 return nil, ErrStalePacket
	 }

	 hash, err := calcHash(pass, payload, rtimestamp)
	 if err != nil {
		 return nil, fmt.Errorf("calc hash: %w", err)
	 }
	 if rhash != hash {
		 return nil, ErrWrongPass
	 }

	 return payload, nil
}

打包过程对称:计算哈希值后,按大端序写入并发送。

性能测试

使用 iperf3 在 tun10 与 tun11 间进行本地测试:

服务端:

iperf3 -s -B 10.0.1.2

客户端:

iperf3 -c 10.0.1.2 -B 10.0.0.2

测试结果(持续10秒):

| 区间 | 传输量 | 吞吐率 |

|----------|----------|---------|

| 0-1s | 113 MB | 941 Mbit/s |

| 1-2s | 109 MB | 921 Mbit/s |

| ... | ... | ... |

| 总计 | 1.08 GB | 925 Mbit/s(发送端) |

接收端实测约 922 Mbit/s。CPU占用情况:中继进程约250%(M1芯片),iperf3为10–30%。可通过批量处理、零拷贝或SIMD哈希进一步优化性能。

关键指标分析:

  • 吞吐量:平均达925 Mbit/s
  • CPU负载:单线程占用高,存在瓶颈
  • 延迟:10秒时间窗口,适用于本地测试场景
  • 可靠性:结合时间戳与哈希,有效防止重放攻击与数据注入

核心要点

  • YAML配置:无需重新编译即可灵活混合TUN与UDP接口
  • 协议极简:仅增加20字节开销,依赖MD5与时间戳保障安全
  • 性能表现:本地可达900+ Mbit/s,但受限于CPU性能
  • 扩展性强:基于 io.ReadWriteCloser 接口,可轻松集成WireGuard、QUIC等协议
  • NAT规则内嵌:配置中直接定义,简化点对点连接部署流程

— Editorial Team

Advertisement 728x90

继续阅读