golang tun设备创建并监听

golang tun设备创建并监听

linux tun设备文件地址为/dev/net/tun.直接打开即可(关闭文件描述符创建的tun虚拟接口自动注销)

fd,err:=syscall.Open("/dev/net/tun",syscall.O_RDWR,0640)

//关闭
syscall.Close(fd)

初始化

  1. 配置ip地址
  2. 启动虚拟网卡
ip addr add xxx.xxx.xxx.xxx/24 dev tuname
ip link set dev tuname up

读取ip包

tun 是位于l3网络层,拿到手就是热乎的ip包,直接读文件描述符就ok

var(
    buff []byte=make([]byte,1024)
    lang int
    err error
)
lang,err=syscall.Read(fd,buff)

开始

查看本机网络接口
请添加图片描述

正常情况下,有2个接口,一个lo 本地,一个enxxxx的物理接口,当然在这里有几个不重要,大概了解下就可以了

创建一个名称为tun01的设备过后(不做初始化)

请添加图片描述

初始化后,这设备接口已经可以正常通了(你甚至可以直接使用这个接口的地址进行tcp/udp通信)

请添加图片描述

这个时候呢,这个go程序这边直接读文件描述符是没数据(最开始有几个大小为48的数据包,那是初始化产生的),也不可能会有数据。经过tun01 接口才能读出数据,但是都这步都没配路由表策略,怎么会有数据走这个接口过.这个时候肯定少不了ip4伴侣ip4 报头(这部分不了解的可以查看我[golang 监听ip包]这边文章,golang ip4头结构体里面都有)

这时候你想接受到数据,你得把你有流量来往的数据的网络设备接口导到你这个网络设备接口。查看路由策略(默认策略在main表中)。这个表看数据流入的看法是先不看default,如果ip4 dst 地址前3个对上了,那么就经过那一行对应的网络设备,一个都没对上,走default via 的那个地址,dev xxxx是用于发送这个ip包的网络设备(也就是说你default那一行乱搞,顶多是没网,你同局域网或者在非default网段的网络还是能上的),这里使用ip route改路由策略不要怕改路由表改错了带来什么影响导致重装系统,直接重启一下,一切都恢复如初

请添加图片描述

更改default 数据走向至tun01

请添加图片描述

请添加图片描述
请添加图片描述

演示代码

package main

/*
#include <net/if.h>
#include <linux/if_tun.h>
#include <sys/socket.h>
#include <sys/types.h>
*/
import "C"
import (
	"encoding/binary"
	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"syscall"
	"unsafe"
)

func Raw2String(src uint32) string {
	raw := make([]byte, 4)
	binary.LittleEndian.PutUint32(raw, src)
	return strconv.FormatUint(uint64(raw[0]), 10) + "." + strconv.FormatUint(uint64(raw[1]), 10) + "." + strconv.FormatUint(uint64(raw[2]), 10) + "." + strconv.FormatUint(uint64(raw[3]), 10)
}

// ip包必选,ip6自行根据wireshark进行编写,此处ip4为例
type IPHeader struct {
	Version_And_Len        uint8 //前4个bit为version(4 ip4,6 ip6),后bit个字节为首部length xxxx xxxx
	DiffernetialtedService uint8
	Tot_Len                uint16
	Id                     uint16
	Flag_And_Seek          uint16 //前3bit 为flag后面13bit为seek
	TTL                    uint8
	Protocol               uint8
	CheckSum               uint16
	Source                 uint32
	Dest                   uint32
}
type Pointer[T any] struct {
	T    *T
	buff []byte
}

func NewPointer[T any]() *Pointer[T] {
	var t T
	var ans = &Pointer[T]{buff: make([]byte, unsafe.Sizeof(t))} //获取类型占用内存字节数
	ans.T = (*T)(unsafe.Pointer(&ans.buff[0]))                  //将指针关联过去
	return ans
}
func (s *Pointer[T]) Bytes() []byte {
	return s.buff
}
func Open_Tun(tuname string, ipadd string) int {
	fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 0640)
	if fd < 0 {
		fmt.Fprintln(os.Stderr, "open fd failed "+err.Error())
		return -1
	}
	var (
		ifr C.struct_ifreq
	)
	//网络设备接口注册
	copy(ifr.ifr_ifrn[:len(tuname)], []byte(tuname))
	flags := NewPointer[uint16]()
	*flags.T = syscall.IFF_TUN | syscall.IFF_UP | syscall.IFF_MULTICAST
	copy(ifr.ifr_ifru[:2], flags.Bytes())
	ans, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), syscall.TUNSETIFF, uintptr(unsafe.Pointer(&ifr)))
	if int32(ans) < 0 {
		fmt.Fprintln(os.Stderr, err.Error())
		syscall.Close(fd)
		return -1
	}
	//设备接口初始化
	cmd := exec.Command("ip", "addr", "add", "192.168.99.99/24", "dev", tuname)
	cmd.Run()
	cmd = exec.Command("ip", "link", "set", "dev", tuname, "up")
	cmd.Run()
	return fd
}

func main() {
	fd := Open_Tun("tun01", "10.0.0.2")
	if fd > 0 {
		ch := make(chan os.Signal, 1)
		signal.Notify(ch, syscall.SIGINT)
		go func() {
			var (
				ipheader *IPHeader
				// size     int
				err  error
				buff []byte = make([]byte, 1024)
			)
			for {
				_, err = syscall.Read(fd, buff)
				if err == nil {
					if buff[0] == 0x45 { //只看ip4
						ipheader = (*IPHeader)(unsafe.Pointer(&buff[0]))
						fmt.Printf("protocol %d src %s dst %s\n", ipheader.Protocol, Raw2String(ipheader.Source), Raw2String(ipheader.Dest))
					}
				}
			}
		}()
		<-ch
		syscall.Close(fd)
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值