上住房和城乡建设部网站,四川建设厅网站施工员证查询,网站制作工作室哪家比较好,1g内存跑wordpress《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 
本篇文章视频地址(稍后上传) 本篇文章主要是想做一个测试#xff1a; 实现的目的是 
希望在宿主机-1上#xff0c;在用户空间里使用ping命令发起ping请求#xff0c;产生的icmp类型的…《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 
本篇文章视频地址(稍后上传) 本篇文章主要是想做一个测试 实现的目的是 
希望在宿主机-1上在用户空间里使用ping命令发起ping请求产生的icmp类型的数据包经过tun类型的虚拟网络设备转发能够ping通宿主机-2上的对外物理网卡eth0不仅能够发送请求还能接收到反馈信息实现的效果跟普通的ping请求效果一样数据包有去有回。 
1、原理图 
实现原理图如下图所示 该图主要分为两下两部分: 
上面部分是宿主机-1里的流程图下面部署是宿主机-2里的流通图。 
我们要实现的目的是 
在宿主机-1上发起ping命令请求使用虚拟网络设备tun19发起希望能够ping通宿主机-2上的对外的物理网卡eth0; 
分在两条线 
黑色实线是ping请求命令数据包走的路线黑色虚线是ping反馈数据包走的路线 
在宿主机-1里,又分了用户空间和内核空间两部分。 
tun-driver就是咱们要编写的程序 
该程序会创建虚拟网络设备tun19并配置了IP信息。该程序内部存在三个组件吧 icmpConn表明这是传输icmp类型数据包的链接类似于tcp链接udp链接。这是icmp链接。imcpTotun表明消息是从icmp链接里读取将读取的消息发送到tun里即发送到/dev/net/tun文件描述符里tunToimcp表明消息是从文件描述符/dev/net/tun里读取的将读取的消息发送到icmp链接里的  
到此为止我们介绍了我们要实现的目的是什么介绍了原理图数据包的走向以及我们实现的程序都做了哪些事情 
接下来看一下我们的代码 
2、方案说明 
本次测试我打算采用两种试验方式 
方案一先单独创建tun虚拟网络设备(tun19)然后在编写tun-driver程序方案二将创建tun虚拟网络设备的程序(tun19)集成到tun-driver程序里。(其实就是在方案一的基础上代码整合了一下) 
想把我学习tun设备的过程给大家复盘一下 
代码不是一开始就写正确的总得有个调试的过程。 
刚好方案一遇到了一些问题。给大家分享一下。 
当然你可以直接跳过方案一直接看方案二。 
3、方案一 
3.1、创建tun设备的代码 
package mainimport (github.com/vishvananda/netlink
)const tunName  tun19func main() {la : netlink.LinkAttrs{Name:  tunName,Index: 8,MTU:   1500,}tun : netlink.Tuntap{LinkAttrs: la,Mode:      netlink.TUNTAP_MODE_TUN,}l, err : netlink.LinkByName(tunName)if err  nil {// 先将tun虚拟网络设备Down掉netlink.LinkSetDown(l)// 将tun虚拟网络设备删掉netlink.LinkDel(l)}// 每次创建新的tun设备err  netlink.LinkAdd(tun)if err ! nil {panic(err)}l, err  netlink.LinkByName(tunName)ip, err : netlink.ParseIPNet(10.244.2.2/24)addr : netlink.Addr{IPNet: ip, Label: }if err  netlink.AddrAdd(l, addr); err ! nil {panic(err)}err  netlink.LinkSetUp(l)if err ! nil {panic(err)}
} 
这段代码就是前文介绍的。 
在本地编译上传到测试服务器上 
build:CGO_ENABLED0 GOOSlinux GOARCHamd64 go build main.goscp:scp main root10.211.55.122:/rootall:make build  make scp执行 
make all登录到远程服务器上 
ip a s | grep eth0ip link sh tun19./mainip link sh tun19ip a sh tun193.2、tun-driver 代码原理介绍 
3.2.1、golang代码 
package mainimport (bytesencoding/binaryflagfmtgolang.org/x/net/icmpgolang.org/x/net/ipv4netossyscalltimeunsafe
)const (tunDevice   /dev/net/tunifnameSize  16
)type ifreqFlags struct {IfrnName  [ifnameSize]byteIfruFlags uint16
}func ioctl(fd int, request, argp uintptr) error {_, _, errno : syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, argp)if errno ! 0 {fmt.Errorf(ioctl failed with %s\n, errno)return fmt.Errorf(ioctl failed with %s, errno)}return nil
}func fromZeroTerm(s []byte) string {return string(bytes.TrimRight(s, \000))
}func OpenTun(name string) (*os.File, string, error) {tun, err : os.OpenFile(tunDevice, os.O_RDWR, 0)if err ! nil {fmt.Printf(OpenTun Failed! err:%v, err.Error())return nil, , err}var ifr ifreqFlagscopy(ifr.IfrnName[:len(ifr.IfrnName)-1], []byte(name\000))ifr.IfruFlags  syscall.IFF_TUN | syscall.IFF_NO_PIerr  ioctl(int(tun.Fd()), syscall.TUNSETIFF, uintptr(unsafe.Pointer(ifr)))if err ! nil {fmt.Printf(OpenTun Failed! err:%v\n, err.Error())return nil, , err}ifName : fromZeroTerm(ifr.IfrnName[:ifnameSize])return tun, ifName, nil
}var tunName stringfunc checkSum(data []byte) uint16 {var (sum    uint32length int  len(data)index  int)for length  1 {sum  uint32(data[index])8  uint32(data[index1])index  2length - 2}if length  0 {sum  uint32(data[index])}sum  sum  16return uint16(^sum)
}func main() {flag.StringVar(tunName, tunName, tun19, Use -tunName xxx)flag.Parse()var err errortunFile, _, err : OpenTun(tunName)if err ! nil {fmt.Printf(ICMP Listen Packet Failed! err:%v\n, err.Error())return}defer tunFile.Close()icmpconn, _ : icmp.ListenPacket(ip4:icmp, 0.0.0.0)defer icmpconn.Close()var srcCh  make(chan string, 1)go tunToicmp(icmpconn, tunFile, srcCh)go icmpToTun(icmpconn, tunFile, srcCh)time.Sleep(time.Hour)
}func tunToicmp(icmpconn *icmp.PacketConn, tunFile *os.File, srcCh chan string) {var srcIP stringpacket : make([]byte, 1024*64)size : 0var err errorfor {if size, err  tunFile.Read(packet); err ! nil {return}fmt.Printf(Msg Length: %d\n, binary.BigEndian.Uint16(packet[2:4]))fmt.Printf(Msg Protocol: %d (1ICMP, 6TCP, 17UDP)\tsize:%d\n, packet[9], size)b : packet[:size]srcIP  GetSrcIP(b)srcCh - srcIPdstIP : GetDstIP(b)fmt.Printf(Msg srcIP: %s\tdstIP:%v\n, srcIP, dstIP)var raddr  net.IPAddr{IP: net.ParseIP(dstIP)}b  b[20:size]if size, err  icmpconn.WriteTo(b, raddr); err ! nil {fmt.Println(err.Error())return}fmt.Printf(Send ICMP Packet Success OK! size:%d\n, size)}
}func icmpToTun(icmpconn *icmp.PacketConn, tunFile *os.File, srcCh chan string) {var sb  make([]byte, 1024*64)var addr net.Addrvar size intvar err errorfor {if size, addr, err  icmpconn.ReadFrom(sb); err ! nil {continue}srcIP : -srcChipHeader : createIPv4Header(net.ParseIP(addr.String()), net.ParseIP(srcIP), os.Getpid())iphb, err : ipHeader.Marshal()if err ! nil {continue}fmt.Printf(Reply MSG Length: %d\n, binary.BigEndian.Uint16(iphb[2:4]))fmt.Printf(Reply MSG Protocol: %d (1ICMP, 6TCP, 17UDP)\n, iphb[9])dstIP : GetDstIP(iphb)fmt.Printf(Reply src IP: %s\tdstIP:%v\n, addr, dstIP)var tunb  make([]byte, 84)tunb  append(iphb, sb[:size]...)size, err  tunFile.Write(tunb)if err ! nil {continue}fmt.Printf(Reply MSG To Tun OK! size:%d\n, size)}
}func createIPv4Header(src, dst net.IP, id int) *ipv4.Header {iph : ipv4.Header{Version:  ipv4.Version,Len:      ipv4.HeaderLen,TOS:      0x00,TotalLen: ipv4.HeaderLen  64,ID:       id,Flags:    ipv4.DontFragment,FragOff:  0,TTL:      64,Protocol: 1,Checksum: 0,Src:      src,Dst:      dst,}h, _ : iph.Marshal()iph.Checksum  int(checkSum(h))return iph
}func IsIPv4(packet []byte) bool {flag : packet[0]  4return flag  4
}func GetIPv4Src(packet []byte) net.IP {return net.IPv4(packet[12], packet[13], packet[14], packet[15])
}func GetIPv4Dst(packet []byte) net.IP {return net.IPv4(packet[16], packet[17], packet[18], packet[19])
}func GetSrcIP(packet []byte) string {key : if IsIPv4(packet)  len(packet)  20 {key  GetIPv4Src(packet).To4().String()}return key
}func GetDstIP(packet []byte) string {key : if IsIPv4(packet)  len(packet)  20 {key  GetIPv4Dst(packet).To4().String()}return key
}接下来对代码进行分析 
3.2.2、 main流程分析 3.2.3、tunToicmp流程分析 
3.2.3.1、主要流程说明 
第111-133行内部死循环的执行任务。 那么任务的主要过程是  
3.2.3.2、IP报文头结构说明 
看一下112行作用是从/dev/net/tun文件描述符里读取数据到packet切片里 
而packet属于字节切片 
前面文章已经介绍过了从/dev/net/tun文件里读取的数据tun设备默认会给原数据包添加一个IP报文头 
如下形式  
那么 接下来的问题就是如何解析IP报文头 
从IP报文头里获取我们想要的信息如ICMP数据包的目的地址是哪里 
IP报文头的结构形式如下所示 IP报文头一共20个字节不包括可选项这20个字节分为了5行来说明、解释(行与行之间的空白行不算)一个byte字节8bit,每一行都是4个字节32bit看第一行第一个小框版本占用了4bit当值为4时即代表IPv4头部长度占用了4bit,值为4字节的倍数4bit位的全部是1的话也就是15因此头部长度占用的最大字节数是15*460字节 比方说二进制0101占用了4bit十进制是5当前IP报文头占用的字节数是5*420个字节。 版本和头部长度合起来就是第0个字节即packet[0]packet[1]表示服务类型packet[2], packet[3]总长packet[4], packet[5]:表示标识packet[6], packet[7]: 表示标记和分段偏移packet[8]: 表示 生命周期packet[9]: 表示 协议(如ICMPtcp, udp)packet[10], packet[11]表示头部校验和packet[12], packet[13], packet[14], packet[15]表示 源地址源IPpacket[16], packet[17], packet[18], packet[19]表示 目的地址目的IP 
从IP报文头结构中我们可以获取到目的地址即packet[16], packet[17], packet[18], packet[19] 
代码中的115行116行118行非必须代码是为给大家演示一下才写的。 
3.2.3.3、通过icmp连接将数据发送到目的地址 
看117行size表示从/dev/net/tun里读取了多少字节 通过这条语句获取到有效的数据 看119行首先说明这条语句非必须的。 在后续的版本的中已经删除了。(代码非一次性写好的)因为把icmp数据包发送给了另一个宿主机上的eth0eth0会反馈消息到icmpTotun函数里在此函数里需要将从icmp链接里接收到的数据添加上一个自定义的IP报文头写到/dev/net/tun文件里此时需要知道srcIP因此在这里传输过去的。但是一般情况下srcIP就是tun19的IP是固定死的。因此没有必要写。  
到目前为止我们已经将数据从tun19设备里发送到了另一台宿主机的对外网卡eth0上了。 
3.2.4、icmpTotun流程分析 
3.2.4.1、主流程分析 
整体看一下这块代码就是一个for循环在死循环的执行逻辑 
主要逻辑如下图所示 3.2.4.2、从ICMP链接里读取消息 
看143行: 从ICMP链接里读取另一个宿主机eth0反馈的ping的消息 将读取到的消息存储到sb字节切片里这个消息肯定是ICMP的回包就不需要解析。ICMP的回包中type为0表明是ICMP的回包。  
3.2.4.3、自定义构建IP报文头 
什么情况下构建IP报文头和什么情况下不需要构建呢 从两个方面说一个是虚拟网卡tun19一个是/dev/net/tun文件描述符而且跟数据包的走向有关系如下 
如果数据包从tun19网卡进入的话不需要自己添加IP报文头tun19网卡内部会自动添加的如果数据包从tun19网卡出去的话tun19网卡内部会自动删除IP报文头如果数据包从/dev/net/tun文件描述符里读取数据的话读取到的数据包里已经包含IP报文头了如果数据包写入/dev/net/tun文件描述符的话写入之前需要给数据包添加上IP报文头才才能写入 
详细的看代码 
看下147行就是获取源IP本条语句非必须在后续的版本中已经删除了。看下149-153行自定义构建了一个IP报文头初始化了版本号协议源IP目的IP等等看154,155行这两个非必须的只是为了给大家演示添加的。可以删除看156行获取目的IP即获取的是tun设备的IPtun19的IP 
3.2.4.4、重新组合数据包 
将143行的切片跟150行的自定义IP报文头进行组合将报文头放到切片的首部 
组成成新的回复数据包。 
3.2.4.5、将数据包写入到/dev/net/tun里 
将最新组合而成的切片写入到/dev/net/tun文件描述符里。 
即tun19虚拟网卡就收到另一个宿主机eth0回馈的数据包了。 
3.3、本地编译上传到服务器 
接下来在本地环境Mac上编译下上传到测试服务器上 Makefile内容 
build:CGO_ENABLED0 GOOSlinux GOARCHamd64 go build main.goscp:scp main root10.211.55.122:/rootall:make build  make scp 
执行 
make all3.4、服务器上测试 
分别打开两个shell终端如下图所示 
左侧的shell终端执行tun-driver程序右侧的shell终端执行ping 10.211.55.123 -I tun19命令 
看一下运行结果 
左侧打印了出了信息右侧缺没有任何信息然后ctrlc终端测试发现ping一共发送了5个数据包但是全部丢失。 再次查看一下tun19的网卡情况  
3.5、问题分析 
通过测试我们发现执行此命令ping 10.211.55.123 -I tun19的时候 没有反馈结果 那么分析的思路是 
先排查虚拟网卡tun19、对外的物理网卡eth0以及另一个宿主机上的eth0网卡是否收到相应的包排查tun-driver程序是否有问题打印日志是否符合要求排查iptables防火墙规则是否满足条件 是否存在DROP策略转发规则是否打开了?经测试这个参数为0也可以的默认值为1 more /proc/sys/net/ipv4/ip_forward查看结果是否为1   
好接下来先抓包分一下 
3.5.1、抓包 
通过抓包来判断一下网卡是否收到数据如果没有收到数据肯定有问题。 
3.5.1.1、分析tun19网卡是否有问题 
针对tun19网卡有两种方式 
3.5.1.1.1、方式一可以查看tun19网卡的统计信息来判断 
通过如下命令 
ifconfig -v tun19查看TX,RX是否有变化 3.5.1.1.2、方式二可以针对tun19网卡进行抓包来分析 
tcpdump -nn icmp -i tun19其实在测试的初期是只有请求数据包并没有回复数据包只是不知道如何回复到当时的场景了。3.5.1.2、分析10.211.55.122节点上的对外网卡eth0是否收到数据 
tcpdump -nn icmp -i eth03.5.1.3、分析10.211.55.123节点上的对外网卡eth0是否收到数据 
其实通过上面的抓包已经说明了 
123节点上的eth0是可以接收到ping命令的请求的并且此网卡也对ping命令进行回复。 
我们可以抓一下并且通过wireshark简单看一下 
tcpdump -nn icmp -i eth0 -w icmp.pcapicmp.pcap用WireShark打开 
ping命令请求数据包 ping命令回复数据包分析  
整个测试试验中涉及到了三个网卡 122节点上的虚拟网卡tun19,eth0以及123节点上的eth0网卡 
通过抓包我们可以看出来这三个网卡都可以正常接收到数据包应该是没有问题的 
但是执行ping 10.211.55.123 -I tun19的时候确实没有返回值 
其实查看tun19网卡的时候已经说明了tun19网卡可以正常接收到123节点的反馈的数据包 
也就是说反馈的数据包从tun19网卡出来后ping没有收到 
是这一段线路出了问题。 
数据包从网卡到应用程序之间的路径是由主机防火墙来规定的。 
有可能是防火墙规则给限制住了。 
因此接下来只能查看防火墙了。 
3.5.2、iptables防火墙规则分析 
通过对tun19虚拟网卡进行抓包发现 
tun19网卡已经接收到反馈的数据包了 
接下来看一下数据包流向图 问题如下  
涉及到两个链 
PREROUTINGINPUT 
涉及到表 
raw表mangle表nat表filter表 
接下来可以依次查看响应的链。 为了节省篇幅不再一一展示了。 直接添加日志。 添加日志方式 
iptables -nvL -t raw iptables -t raw -A PREROUTING -p icmp -j TRACEiptables -t raw -A OUTPUT -p icmp -j TRACEiptables -nvL -t raw 按照数据包的走向依次根据表查看规则链 
tail -f /var/log/messages | grep raw | grep PREROUTING | grep DST10.244.2.2注意打印日志可能不会马上显示可能需要等待10秒左右tail -f /var/log/messages | grep mangle | grep PREROUTING | grep DST10.244.2.2tail -f /var/log/messages | grep nat | grep PREROUTING | grep DST10.244.2.2tail -f /var/log/messages | grep mangle | grep INPUT | grep DST10.244.2.2tail -f /var/log/messages | grep nat | grep INPUT | grep DST10.244.2.2tail -f /var/log/messages | grep filter | grep INPUT | grep DST10.244.2.2其实通过上面的命令去查询的话也不是很靠谱的。 
可能是这样的一个数据包要不要经过某个表的某个链 
是根据这个数据包自带的包状态来判断的。 
比方说这个数据包没有涉及到nat功能可能不会经过nat表因此查询nat表时就不会有响应的日志。 
可能raw表filter会必须经过的。 
有点遗憾感觉没有通过iptables日志完全查出来是什么原因但至少也进一步定位了问题所在。 
数据包从tun19网卡出来后经过了raw表。 
在后续的表中出了问题。 
3.5.3、反向路由校验 
在即将放弃的时候突然想起了一个内核参数net.ipv4.conf.all.rp_filter
尝试的修改了几次发现OK了3.5.3.1、什么是反向路由校验 
所谓反向路由校验 就是在一个网卡收到数据包后  把源地址和目标地址对调后查找路由出口  从而得到反身后路由出口。  然后根据反向路由出口进行过滤。  当rp_filter的值为1时 要求反向路由的出口必须与数据包的入口网卡是同一块否则就会丢弃数据包。  当rp_filter的值为2时 要求反向路由必须是可达的如果反路由不可达则会丢弃数据包。  
3.5.3.2、rp_filter Reverse Path Filtering参数介绍 
定义了网卡对接收到的数据包进行反向路由验证的规则。存在三个值0、1、2具体含意如下 0关闭反向路由校验1开启严格的反向路由校验。 对每个进来的数据包校验其反向路由是否是最佳路由如果反向路由不是最佳路由则直接丢弃该数据包。 2开启松散的反向路由校验。 对每个进来的数据包校验其源地址是否可达即反向路由是否能通通过任意网口如果反向路径不通则直接丢弃该数据包。   
默认值应该是1严格进行反向路由校验 
具体可以参考下面的网址 Linux内核参数 rp_filter 
3.5.3.3、ping不通的原因可能是 
仅仅是猜测 数据包经过了两个网卡一个是虚拟网卡tun19, 一个是eth0 当对数据包进行反向路由校验时从tun19网卡到eth0网卡失败了它校验的时候可能没有添加IP报文头。 
3.6、设置反向路由校验重新测试 
3.6.1、设置反向路由校验参数 
既然问题原因已经找到了设置参数 
sysctl -w net.ipv4.conf.all.rp_filter2sysctl net.ipv4.conf.all.rp_filter3.6.2、重新测试 
ping 10.211.55.123 -I tun19再次查看iptables日志 
tail -f /var/log/messages | grep DST10.244.2.2从上面的日志中可以看出来 
即使在ping通的情况下数据包也只经过了raw表filter表 RPEROUTING链以及INPUT链。 
可能没有涉及到nat、mangle功能因此没有nat、mangle相关日志。 
3.6.3、为什么会存在大量的DUP!呢 
如果你去查百度的话会查到一堆。 
最后在测试时无意中发现是代码的问题。 重新测试 好到这里我们的试验目标已经完全成功了。 
其实测试用了好长时间。结局还是圆满的。 
4、方案二 
4.1、golang代码 
package mainimport (bytesencoding/binaryfmtgithub.com/vishvananda/netlinkgolang.org/x/net/icmpgolang.org/x/net/ipv4netossyscalltimeunsafe
)const (tunName     tun19tunDevice   /dev/net/tunifnameSize  16eth0        10.211.55.122tunIP       10.244.1.3
)type ifreqFlags struct {IfrnName  [ifnameSize]byteIfruFlags uint16
}func ioctl(fd int, request, argp uintptr) error {_, _, errno : syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), request, argp)if errno ! 0 {fmt.Errorf(ioctl failed with %s\n, errno)return fmt.Errorf(ioctl failed with %s, errno)}return nil
}func fromZeroTerm(s []byte) string {return string(bytes.TrimRight(s, \000))
}func OpenTun(name string) (*os.File, string, error) {tun, err : os.OpenFile(tunDevice, os.O_RDWR|syscall.O_NONBLOCK, 0)if err ! nil {fmt.Printf(OpenTun Failed! err:%v, err.Error())return nil, , err}var ifr ifreqFlagscopy(ifr.IfrnName[:len(ifr.IfrnName)-1], []byte(name\000))ifr.IfruFlags  syscall.IFF_TUN | syscall.IFF_NO_PIerr  ioctl(int(tun.Fd()), syscall.TUNSETIFF, uintptr(unsafe.Pointer(ifr)))if err ! nil {fmt.Printf(OpenTun Failed! err:%v\n, err.Error())return nil, , err}ifName : fromZeroTerm(ifr.IfrnName[:ifnameSize])return tun, ifName, nil
}func checkSum(data []byte) uint16 {var (sum    uint32length int  len(data)index  int)for length  1 {sum  uint32(data[index])8  uint32(data[index1])index  2length - 2}if length  0 {sum  uint32(data[index])}sum  sum  16return uint16(^sum)
}func main() {fmt.Printf(Now----Test----Tun1232\n)tunFile, err : createTun()if err ! nil {fmt.Printf(ICMP Listen Packet Failed! err:%v\n, err.Error())return}defer tunFile.Close()icmpConn, _ : icmp.ListenPacket(ip4:icmp, eth0)defer icmpConn.Close()go tunToIcmp(icmpConn, tunFile)go icmpToTun(icmpConn, tunFile)time.Sleep(time.Hour)
}func tunToIcmp(icmpconn *icmp.PacketConn, tunFile *os.File) {var srcIP stringpacket : make([]byte, 1024*64)size : 0var err errorfor {if size, err  tunFile.Read(packet); err ! nil {return}fmt.Printf(Msg Length: %d\n, binary.BigEndian.Uint16(packet[2:4]))fmt.Printf(Msg Protocol: %d (1ICMP, 6TCP, 17UDP)\tsize:%d\n, packet[9], size)b : packet[:size]srcIP  GetSrcIP(b)dstIP : GetDstIP(b)fmt.Printf(Msg srcIP: %s\tdstIP:%v\n, srcIP, dstIP)var raddr  net.IPAddr{IP: net.ParseIP(dstIP)}b  b[20:size]if size, err  icmpconn.WriteTo(b, raddr); err ! nil {fmt.Println(err.Error())return}fmt.Printf(Write Msg To Icmp Conn OK! size:%d\n, size)}
}func icmpToTun(icmpconn *icmp.PacketConn, tunFile *os.File) {var sb  make([]byte, 1024*64)var addr net.Addrvar size intvar err errorfor {if size, addr, err  icmpconn.ReadFrom(sb); err ! nil {continue}ipHeader : createIPv4Header(net.ParseIP(addr.String()), net.ParseIP(tunIP), os.Getpid())iphb, err : ipHeader.Marshal()if err ! nil {continue}fmt.Printf(Reply MSG Length: %d\n, binary.BigEndian.Uint16(iphb[2:4]))fmt.Printf(Reply MSG Protocol: %d (1ICMP, 6TCP, 17UDP)\n, iphb[9])dstIP : GetDstIP(iphb)fmt.Printf(Reply src IP: %s\tdstIP:%v\n, addr, dstIP)var rep  make([]byte, 84)rep  append(iphb, sb[:size]...)size, err  tunFile.Write(rep)if err ! nil {continue}fmt.Printf(Write Msg To /dev/net/tun OK! size:%d\ttime:%v\n, size, time.Now())}
}func createIPv4Header(src, dst net.IP, id int) *ipv4.Header {iph : ipv4.Header{Version:  ipv4.Version,Len:      ipv4.HeaderLen,TOS:      0x00,TotalLen: ipv4.HeaderLen  64,ID:       id,Flags:    ipv4.DontFragment,FragOff:  0,TTL:      64,Protocol: 1,Checksum: 0,Src:      src,Dst:      dst,}h, _ : iph.Marshal()iph.Checksum  int(checkSum(h))return iph
}func IsIPv4(packet []byte) bool {flag : packet[0]  4return flag  4
}func GetIPv4Src(packet []byte) net.IP {return net.IPv4(packet[12], packet[13], packet[14], packet[15])
}func GetIPv4Dst(packet []byte) net.IP {return net.IPv4(packet[16], packet[17], packet[18], packet[19])
}func GetSrcIP(packet []byte) string {key : if IsIPv4(packet)  len(packet)  20 {key  GetIPv4Src(packet).To4().String()}return key
}func GetDstIP(packet []byte) string {key : if IsIPv4(packet)  len(packet)  20 {key  GetIPv4Dst(packet).To4().String()}return key
}func createTun() (*os.File, error) {err : addTun()if err ! nil {return nil, err}err  configTun()if err ! nil {return nil, err}tunFile, _, err : OpenTun(tunName)if err ! nil {return nil, err}return tunFile, nil
}func addTun() error {la : netlink.LinkAttrs{Name:  tunName,Index: 8,MTU:   1500,}tun : netlink.Tuntap{LinkAttrs: la,Mode:      netlink.TUNTAP_MODE_TUN,}l, err : netlink.LinkByName(tunName)if err  nil {netlink.LinkSetDown(l)netlink.LinkDel(l)}err  netlink.LinkAdd(tun)if err ! nil {return err}return nil
}func configTun() error {l, err : netlink.LinkByName(tunName)if err ! nil {return err}ip, err : netlink.ParseIPNet(fmt.Sprintf(%s/%d, tunIP, 24))if err ! nil {return err}addr : netlink.Addr{IPNet: ip, Label: }if err  netlink.AddrAdd(l, addr); err ! nil {return err}err  netlink.LinkSetUp(l)if err ! nil {return err}return nil
} 
本代码里已经集成了虚拟网卡tun19的创建配置了。 
直接使用即可。 
4.2、本地编译上传到服务器上 
build:CGO_ENABLED0 GOOSlinux GOARCHamd64 go build -o tun-driver main.goscp:scp tun-driver root10.211.55.122:/rootall:make build  make scp 
执行 
make all即可 
4.3、反向路由规则校验 
sysctl -w net.ipv4.conf.all.rp_filter2sysctl net.ipv4.conf.all.rp_filter4.4、测试 
登录到远程服务器10.211.55.122上 root目录下 
进行测试 
./tun-driverping 10.211.55.123 -I tun19 -c 15、总结 
本次试验完成了在用户空间发送请求经过tun类型的设备实现请求的跨主机通信。 
当然在上面的测试用例中你可以将ICMP改成udp来做只不过在10.211.55.123节点上也需要开启一个udp服务来接收122节点上发送过来的数据包然后在将数据包转发给123节点上的eth0 
6、参考 
https://www.itdaan.com/blog/2017/03/09/e9d4766e2982.html 
84字节是如何计算处理的(ctrlf全局搜索一下84, 即可发现) https://blog.csdn.net/Rong_Toa/article/details/86665176 
https://www.freesion.com/search 
云原生虚拟化一文读懂网络虚拟化之 tun/tap 网络设备 
Linux虚拟网络设备——tun/tap 
TUN/TAP 学习总结二 —— Linux TUN demo 
c语言版本的tun读 https://blog.haohtml.com/archives/31687 
如何读取二层三层四层数据包 
图解Ping 命令的工作原理 点击 下面 返回 专栏目录 
零入门kubernetes网络实战技术专栏之文章目录 文章转载自: http://www.morning.qqhfc.cn.gov.cn.qqhfc.cn http://www.morning.wzdjl.cn.gov.cn.wzdjl.cn http://www.morning.bfgbz.cn.gov.cn.bfgbz.cn http://www.morning.kdldx.cn.gov.cn.kdldx.cn http://www.morning.sqlh.cn.gov.cn.sqlh.cn http://www.morning.hwpcm.cn.gov.cn.hwpcm.cn http://www.morning.gthgf.cn.gov.cn.gthgf.cn http://www.morning.tymnr.cn.gov.cn.tymnr.cn http://www.morning.rnrwq.cn.gov.cn.rnrwq.cn http://www.morning.qwdlj.cn.gov.cn.qwdlj.cn http://www.morning.wknbc.cn.gov.cn.wknbc.cn http://www.morning.rlrxh.cn.gov.cn.rlrxh.cn http://www.morning.rkzk.cn.gov.cn.rkzk.cn http://www.morning.dtcsp.cn.gov.cn.dtcsp.cn http://www.morning.bydpr.cn.gov.cn.bydpr.cn http://www.morning.kjlia.com.gov.cn.kjlia.com http://www.morning.ssjry.cn.gov.cn.ssjry.cn http://www.morning.grfhd.cn.gov.cn.grfhd.cn http://www.morning.cfqyx.cn.gov.cn.cfqyx.cn http://www.morning.srky.cn.gov.cn.srky.cn http://www.morning.srbmc.cn.gov.cn.srbmc.cn http://www.morning.rnhh.cn.gov.cn.rnhh.cn http://www.morning.jljwk.cn.gov.cn.jljwk.cn http://www.morning.qgwdc.cn.gov.cn.qgwdc.cn http://www.morning.gsrh.cn.gov.cn.gsrh.cn http://www.morning.ltbwq.cn.gov.cn.ltbwq.cn http://www.morning.zsthg.cn.gov.cn.zsthg.cn http://www.morning.glncb.cn.gov.cn.glncb.cn http://www.morning.yxwcj.cn.gov.cn.yxwcj.cn http://www.morning.fwdln.cn.gov.cn.fwdln.cn http://www.morning.yjmlg.cn.gov.cn.yjmlg.cn http://www.morning.hdlhh.cn.gov.cn.hdlhh.cn http://www.morning.ykwqz.cn.gov.cn.ykwqz.cn http://www.morning.lkgqb.cn.gov.cn.lkgqb.cn http://www.morning.clzly.cn.gov.cn.clzly.cn http://www.morning.mzcrs.cn.gov.cn.mzcrs.cn http://www.morning.kpcjl.cn.gov.cn.kpcjl.cn http://www.morning.kqlrl.cn.gov.cn.kqlrl.cn http://www.morning.mngh.cn.gov.cn.mngh.cn http://www.morning.qqrqb.cn.gov.cn.qqrqb.cn http://www.morning.sqqdy.cn.gov.cn.sqqdy.cn http://www.morning.jpfpc.cn.gov.cn.jpfpc.cn http://www.morning.rynrn.cn.gov.cn.rynrn.cn http://www.morning.dndjx.cn.gov.cn.dndjx.cn http://www.morning.skbkq.cn.gov.cn.skbkq.cn http://www.morning.rxydr.cn.gov.cn.rxydr.cn http://www.morning.gkjnz.cn.gov.cn.gkjnz.cn http://www.morning.txqsm.cn.gov.cn.txqsm.cn http://www.morning.dodoking.cn.gov.cn.dodoking.cn http://www.morning.cbmqq.cn.gov.cn.cbmqq.cn http://www.morning.ywqsk.cn.gov.cn.ywqsk.cn http://www.morning.knzmb.cn.gov.cn.knzmb.cn http://www.morning.cwjxg.cn.gov.cn.cwjxg.cn http://www.morning.srltq.cn.gov.cn.srltq.cn http://www.morning.sjftk.cn.gov.cn.sjftk.cn http://www.morning.ngcw.cn.gov.cn.ngcw.cn http://www.morning.prkdl.cn.gov.cn.prkdl.cn http://www.morning.xzkgp.cn.gov.cn.xzkgp.cn http://www.morning.mxhys.cn.gov.cn.mxhys.cn http://www.morning.fflnw.cn.gov.cn.fflnw.cn http://www.morning.cgntj.cn.gov.cn.cgntj.cn http://www.morning.nqmkr.cn.gov.cn.nqmkr.cn http://www.morning.lcbnb.cn.gov.cn.lcbnb.cn http://www.morning.lkgqb.cn.gov.cn.lkgqb.cn http://www.morning.qxnns.cn.gov.cn.qxnns.cn http://www.morning.hcszr.cn.gov.cn.hcszr.cn http://www.morning.wqngt.cn.gov.cn.wqngt.cn http://www.morning.langlaitech.cn.gov.cn.langlaitech.cn http://www.morning.xhklb.cn.gov.cn.xhklb.cn http://www.morning.qinhuangdjy.cn.gov.cn.qinhuangdjy.cn http://www.morning.gnbtp.cn.gov.cn.gnbtp.cn http://www.morning.frfnb.cn.gov.cn.frfnb.cn http://www.morning.rtspr.cn.gov.cn.rtspr.cn http://www.morning.fyzsq.cn.gov.cn.fyzsq.cn http://www.morning.mnkz.cn.gov.cn.mnkz.cn http://www.morning.qnhpq.cn.gov.cn.qnhpq.cn http://www.morning.qnbzs.cn.gov.cn.qnbzs.cn http://www.morning.nthyjf.com.gov.cn.nthyjf.com http://www.morning.hxcuvg.cn.gov.cn.hxcuvg.cn http://www.morning.kndt.cn.gov.cn.kndt.cn