当前位置: 首页 > Linux

linux下用C语言在tap0--app--tap1路径发送数据包

时间:2023-04-06 07:06:52 Linux

记录自己做的探索tap虚拟网络设备的实验。概述要达到的效果创建两个tap,绑定到同一个程序,如图。数据发送给tap0,tap0转发数据给程序,程序转发数据给tap1。本场景验证两个tap之间的通信,IP地址配置如图。编码思路程序应该实现以下几个部分:创建两个tap,在阻塞状态下使用socket发送数据包,接收数据包,收到数据时转发代码//tap.c#include#include#include#include#include#include#include#include<字符串。h>#include#include#include#include#include#include#include#definePORT80/*使用的端口*/inttun_alloc(intflags){structifreqifr;intfd,错误;char*clonedev="/dev/net/tun";如果((fd=open(clonedev,O_RDWR))<0){returnfd;}memset(&ifr,0,sizeof(ifr));ifr.ifr_flags=标志;如果((err=ioctl(fd,TUNSETIFF,(void*)&ifr))<0){关闭(fd);返回错误;}printf("打开tun/tap设备:%s用于读取...\n",ifr.ifr_name);returnfd;}voidPrintBuffer(char*buffer,intnread){诠释我=0;printf("从tun/tap设备读取%d字节\n读取信息:\ndst地址:",nread);对于(i=0;i<6;i++){printf("%x",buffer[i]);}printf("\nsrc地址:");for(;i<12;i++){printf("%x",buffer[i]);}printf("\n帧类型:");for(;i<14;i++){printf("%x",buffer[i]);}printf("\ndata(至idx99):");for(;i<100;i++){printf("%x",buffer[i]);}printf("\n");}intmain(){structsockaddr_insaddr,caddr;inttun_fd,nread,i,sockfd,ret;字符缓冲区[1500];inttun_fd1;整数计数=0;/*标志:IFF_TUN-TUN设备(无以太网头)*IFF_TAP-TAP设备*IFF_NO_PI-不提供数据包信息*/tun_fd=tun_alloc(IFF_TAP|IFF_NO_PI);if(tun_fd<0){perror("分配接口");退出(1);}tun_fd1=tun_alloc(IFF_TAP|IFF_NO_PI);如果(你n_fd1<0){perror("分配接口1");退出(1);}睡眠(10);//由于第103行硬编码数据包的源IP地址是tap0,所以这里预留了10秒的时间间隔,用来配置tap0的IPsockfd=socket(AF_INET,SOCK_DGRAM,0);如果(sockfd<0){perror(“套接字失败:”);退出(1);}bzero(&caddr,sizeof(caddr));caddr.sin_family=AF_INET;caddr.sin_port=htons(PORT);caddr.sin_addr.s_addr=inet_addr("192.168.3.1");//这是发送者的IPif(bind(sockfd,(structsockaddr*)&caddr,sizeof(caddr))<0){perror("Bindfailed:");退出(1);}while(1){count++;bzero(缓冲区,sizeof(缓冲区));//内存集(缓冲区,0,sizeof(缓冲区));nread=read(tun_fd,buffer,sizeof(buffer));printf("idx:%d------------READ------------\n",count,nread);if(nread<0){perror("从接口读取");关闭(tun_fd);退出(1);}PrintBuffer(缓冲区,nread);//读取数据包后,将缓冲区发送到另一个tap,发送到192.168.4.11(通过这个程序作为中介)//TODO:目的地址应该如何是MAC地址(MAC->index)?;saddr.sin_family=AF_INET;saddr.sin_port=htons(PORT);saddr.sin_addr.s_addr=inet_addr("192.168.4.2");//这是接收端的IPprintf("--------------------SEND--------------\n",nread);ret=sendto(sockfd,buffer,sizeof(buffer),0,(structsockaddr*)&saddr,sizeof(saddr));if(ret<0){printf("发送ret失败:%d\n",ret);继续;//退出(1);}printf("发送ret:%d\n",ret);printf("发送缓冲区:");对于(i=0;i<100;i++){printf("%x",buffer[i]);}printf("\n发送到:%s\n",inet_ntoa(saddr.sin_addr));}return0;}程序首先打开/dev/net/tun文件,调用ioctl并指定TUNSETIFF,传入flags=IFF_TAP|IFF_NO_PI。这一步代表创建的虚拟设备是tap而不是tun,然后预先创建一个socket并绑定sockaddr_in结构。用于后面收到数据包时转发给tap1。这里休眠10秒是因为配置的源IP地址在环境中不存在,10秒是为新创建的tap0和tap1设置IP地址预留的时间间隔。read函数被不断调用,一旦tap0收到数据包,就可以触发read操作,将数据内容读入缓存缓冲区。然后通过sendto函数将buffer发送到tap1。(这里tap1的地址是硬编码的,后面可以优化分析程序运行时带来的参数args。)验证这里创建了4个shell窗口,第一个用来运行程序,后面的第二和第三次使用tcpdump抓取tap0和tap1和lo设备包,第四次用于命令下发。先把程序放到linux设备上,在window1中执行#Window1[root@localhost~]#vitap.c#复制代码[root@localhost~]#gcctap.c-otap[root@localhost~]#./tapOpentun/tapdevice:tap0forreading...打开tun/tapdevice:tap1forreading...然后在窗口5中发出命令来配置两个tap和up设备的IP地址。#window4[root@localhostJerCode]#sudoipaddradd192.168.3.1/24devtun0[root@localhostJerCode]#sudoipaddradd192.168.4.1/24devtun1[root@localhostJerCode]#iplinksettun0up[root@localhostJerCode]#iplinksettun1upup操作后,在window2-4中发出tcpdump命令#Window2[root@localhost~]#tcpdump-nitap0#Window3[root@localhost~]#Aftertcpdump-nitap1配置完成,观察环境中的设备信息。已知如下:tap0的IP地址为192.168.3.1,192.168.3.1-254网段的数据包都会发送到tap0,tap1的IP地址为192.168.4.1、192.168。4.1-254网段的数据包会发给tap1,MAC地址为4a:dd:f7:bc:dd[root@localhostJerCode]#ipaddr1:lo:......2:ens192:......110:tap0:mtu1500qdiscpfifo_faststateUNKNOWNgroupdefaultqlen1000link/etherfe:a3:e4:8c:47:50brdff:ff:ff:ff:ff:ffinet192.168.3.1/24scopeglobaltap0valid_lftforeverpreferred_lftforeverinet6fe80::fca3:e4ff:fe8c:4750/64scopelinkvalid_lftforeverpreferred_lftforever111:tap1:m状态未知组默认qlen1000链接/以太4a:dd:f7:bc:dd:8dbrdff:ff:ff:ff:ff:ffinet192.168.4.1/24范围全局tap1valid_lftforeverpreferred_lftforeverinet6fe80::48dd:f7ff:febc:dd8d/64范围链接valid_lftforeverpreferred_lftforever[root@localhostJerCode]#route-nKernelIProutingtableDestination网关Genmask标志MetricRefUseIface0.0.0.0192.168.1.2540.0.0.0UG10000ens192192.168.1.00.0.0.0255.255.255.0U10000ens192192.168.3.00.0.05.05.255.05.25.02550tap0192.168.4.00.0.0.0255.255.255.0U000tap1开始验证,在窗口4中下发ping192.168.3.11,查看各个窗口的回显#窗口-1id-x-:1---READ--------------从tun/tapdeviceReadinfo:dstaddress:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffsrcaddress:fffffffefffffffa3ffffffe4ffffff8c4750frametype:86data(toidx99):01806401fffffffeffffffa3ffffffe4ffffff8c4750ffffffc0ffffffa831000000ffffffc0ffffffa83b000000000000000000000000000000000000000000000000000000000----------------SEND----------------Sendret:1500Sendbuffer:fffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffa3ffffffe4ffffff8c47508601806401fffffffeffffffa3ffffffe4ffffff8c4750ffffffc0ffffffa831000000ffffffc0ffffffa83b0000000000000000000000000000000000000000000000000000000000发送至:192.168.4.2idx:2------------READ----------------#窗口2[root@localhost~]#tcpdump-nitap0tcpdump:verbose抑制输出,使用-v或-vv在tap0上进行完整协议解码侦听,链路类型EN10MB(以太网),捕获大小262144字节17:45:34.075924ARP,请求who-has192.168.3.11告诉192.168.3.1,长度2817:45:35.078049ARP,Requestwho-has192.168.3.11tell192.168.3.1,length2817:45:36.080049ARP,Requestwho-has192.168.3.11tell192.168.3.1,length28#窗口3[root@localhost~]#-tcpdumpnitap1tcpdump:抑制详细输出,使用-v或-vv在tap1上进行完整协议解码侦听,链路类型EN10MB(以太网),捕获大小262144bytes17:45:34.076107ARP,Requestwho-has192.168.4.2tell192.168.3.1,length2817:45:35.078060ARP,Requestwho-has192.168.4.2tell192.168.3.1,length28360.Rpwhohas192.2.168.4.3.1,length28#Window4[root@localhost~]#ping192.168.3.11PING192.168.3.11(192.168.3.11)56(84)bytesofdata.#敲ctrl+c---192.168.3.11pingstatistics---3packetstransmitted,0received,100%packetloss,time1999ms[root@localhost~]#分析ping192.168.3.11构造一个数据包发送给内核协议栈,内核协议栈查询路由表,认为This数据包应该发送到tap0。tap0收到包后,发送了一个arp请求,查询谁是192.168.3.11,但是没有收到回复。tap0将数据包转发给与其绑定的程序。程序读取数据后,将数据存入缓冲区,调用sendto函数发送到192.168.4.2。内核协议栈收到数据后,认为192.168.4.2要发给tap1。tap1收到数据包后,也发送了一个arp请求,询问目的地址192.168.4.2在源地址为192.168.3.1的数据包中的什么位置,但是没有回复。程序绑定了tap1,但是程序没有处理tap1的操作,报文被丢弃,所以ping不一样。上面实现了数据从tap0到app再到tap1的过程。window1程序收到的包的src地址是tap0的MAC地址,即包的来源是tap0。窗口2和3tap0发送arp请求,询问192.168.3.11的MAC地址在哪里。tap1发出arp请求,询问192.168.4.2的MAC地址在哪里。都没有收到回复。Window4数据包最终被丢弃,ping不一样参考Linux虚拟网络设备tun/tapTun/Tap接口教程C语言使用AF_PACKETrawsocket发送任意以太网帧(一)编程发送以太网帧