为了账号安全,请及时绑定邮箱和手机立即绑定

Golang 学习笔记——tap+udp

标签:
Go
  • 如何连接两台机器上的虚拟网卡?
    图片描述
    要连接两块虚拟网卡,可以在 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

结果

图片描述

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
全栈工程师
手记
粉丝
2
获赞与收藏
2

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消