- 如何连接两台机器上的虚拟网卡?
要连接两块虚拟网卡,可以在 eth0 之间建立 udp 通信,把 tap 网卡上的以太帧通过 udp 发送到远端,再转发到远端 tap 网卡,最后原路返回,这样实现互通。最终拓扑为
环境准备
两台虚拟机:
- host5 网卡:enp0s3 IP:192.168.1.5
- host7 网卡:enp0s3 IP:192.168.1.7
创建 tap
- 命令行创建
sudo ip tuntap add tap0 mode tap
sudo ifconfig tap0 10.0.1.1/24 up
- 程序创建
- 通过 newTap 方法创建网卡
- 通过 runCommand 调用 shell 命令配置 ip
import (
"fmt"
"github.com/songgao/water"
"os/exec"
"strings"
)
func main() {
iface := newTap()
runCommand("ifconfig", iface.Name(), " 10.0.0.1/24", "up")
select {}
}
func newTap() *water.Interface {
tapconfig := water.Config{
DeviceType: water.TAP,
PlatformSpecificParams: water.PlatformSpecificParams{
Name: "tap0",
},
}
iface, err := water.New(tapconfig)
if err != nil {
fmt.Printf("创建失败:%v \n", err)
}
return iface
}
func runCommand(args ...string) string {
cmdStr := strings.Join(args, " ")
cmd := exec.Command("/bin/sh", "-c", cmdStr)
opBytes, err := cmd.Output()
if err != nil {
fmt.Println(err)
return ""
}
return string(opBytes)
}
创建 udp socket
- raddr 远端地址
- udp 客户端和服务端监听同样的端口
func newUDPConn(port int) *net.UDPConn {
srcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: port}
conn, err := net.ListenUDP("udp", srcAddr)
if err != nil {
fmt.Println("udp启动失败!", err)
os.Exit(1)
}
fmt.Println("udp启动成功")
return conn
}
读取数据
- readTapToUdp 读取 tap,写入 udp
- readUdpToTap 读取 udp,写入 tap
- raddr udp 远端地址
func readTapToUdp(conn *net.UDPConn, iface *water.Interface, raddr *net.UDPAddr) {
buf := make([]byte, 65536)
for {
n, err := iface.Read(buf)
if err != nil {
fmt.Printf("tap read error: %v\n", err)
time.Sleep(time.Millisecond * 10)
continue
}
fmt.Printf("tap read num: %v, raddr: %v\n", n, raddr)
if _, err := conn.WriteToUDP(buf[:n], raddr); err != nil {
fmt.Printf("udp write error: %v\n", err)
time.Sleep(time.Millisecond)
continue
}
}
}
func readUdpToTap(conn *net.UDPConn, iface *water.Interface) {
buf := make([]byte, 65536)
for {
n, raddr, err := conn.ReadFromUDP(buf)
if err != nil {
fmt.Printf("udp read error: %v\n", err)
time.Sleep(time.Millisecond * 10)
continue
}
fmt.Printf("udp read num: %v, raddr: %v\n", n, raddr)
if _, err := iface.Write(buf[:n]); err != nil {
fmt.Printf("tun write error: %v\n", err)
}
}
}
增加启动参数
- remote 为远端 IP
- port 被监听的端口
- tapIp 给 tap 设置的 IP
var (
remote string
port int
tapIp string
)
func main() {
flag.StringVar(&remote, "r", "", "")
flag.IntVar(&port, "p", 9000, "")
flag.StringVar(&tapIp, "ip", "", "")
flag.Parse()
addr := fmt.Sprintf("%s:%d", remote, port)
fmt.Println("remote address:", addr)
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
fmt.Println("地址解析失败!", err)
os.Exit(1)
}
iface := newTap()
runCommand("ifconfig", iface.Name(), tapIp, "up")
defer iface.Close()
conn := newUDPConn(port)
defer conn.Close()
go readTapToUdp(conn, iface, raddr)
readUdpToTap(conn, iface)
}
运行
# 编译
go build tap.go
# host7
sudo ./tap -r 192.168.1.5 -ip 10.0.0.1/24
# host5
sudo ./tap -r 192.168.1.7 -ip 10.0.0.2/24