从tap设备到http

linux tap设备是个二层网络设备,收发的数据包包含mac头的信息,除了做不可描述方面的事情外,也可以用于模拟嵌入式网络设备的收发情况,用于调试网络功能。
源代码在gitee
https://gitee.com/hl1200/tap2http.git

本机安装ubuntu服务器版本,已经带有tun的驱动。
创建一个tap设备:

  fd = open("/dev/net/tun", O_RDWR);
  printf("fd is %d \n", fd);
  if(fd == -1) {
    perror("tapdev: tapdev_init: open");
    exit(1);
  }

设置tap设备

struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TAP|IFF_NO_PI;
    if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
      perror(cmdbuf);
      exit(1);
    }

设置tap0的ip

snprintf(cmdbuf, sizeof(cmdbuf), "ifconfig tap0 inet %d.%d.%d.%d",
	   192, 168, 30, 1);    
	   system(cmdbuf);

这样就创建了一个tap0网卡。在主机上发送到192.168.30.2/255的所有数据包都在fd中接收到。
当客户端访问服务器时,客户端会发数据包,数据包以mac头开始


struct ethernet_hdr {
	u8		et_dest[6];	/* Destination node		*/
	u8		et_src[6];	/* Source node			*/	
	u16		et_protlen;	/* Protocol or length		*/};

在开始时,客户端不知道目标的mac地址,所以要发送arp广播包,向对应ip的服务器获取mac地址

struct arp_hdr {
	u16		ar_hrd;		/* Format of hardware address	*/
#   define ARP_ETHER	    1		/* Ethernet  hardware address	*/
	u16		ar_pro;		/* Format of protocol address	*/
	u8		ar_hln;		/* Length of hardware address	*/
#   define ARP_HLEN	6
	u8		ar_pln;		/* Length of protocol address	*/
#   define ARP_PLEN	4
	u16		ar_op;		/* Operation			*/
#   define ARPOP_REQUEST    1		/* Request  to resolve  address	*/
#   define ARPOP_REPLY	    2		/* Response to previous request	*/

#   define RARPOP_REQUEST   3		/* Request  to resolve  address	*/
#   define RARPOP_REPLY	    4		/* Response to previous request */

	/*
	 * The remaining fields are variable in size, according to
	 * the sizes above, and are defined as appropriate for
	 * specific hardware/protocol combinations.
	 */
	u8		ar_data[0];
#define ar_sha		ar_data[0]
#define ar_spa		ar_data[ARP_HLEN]
#define ar_tha		ar_data[ARP_HLEN + ARP_PLEN]
#define ar_tpa		ar_data[ARP_HLEN + ARP_PLEN + ARP_HLEN]
#if 0
	u8		ar_sha[];	/* Sender hardware address	*/
	u8		ar_spa[];	/* Sender protocol address	*/
	u8		ar_tha[];	/* Target hardware address	*/
	u8		ar_tpa[];	/* Target protocol address	*/
#endif /* 0 */
};

收到arp报文后,把op改为reply,源地址目标地址相应填上,发回去就行了。
取得服务器mac后,客户端就可以发送ip包了。
这是一个接收的ip包
mac头
32 0c 36 7c 00 72 目标mac地址
7a a5 bf 66 20 5d 源mac地址
08 00 协议类型,0x08 00是ip协议

ip头
45 4是ipv4,5是5*32bit,20byte长
00 服务类型,一般全为0,不用理会
00 3c 包长度,包括包头
60 f4 标识符,拆包用
40 00 标记片偏移,拆包用
40 生存时间
06 协议类型,06是tcp协议
1c 73 首部校验和
c0 a8 1e 01 源ip
c0 a8 1e 03 目标ip
20字节ip头到此为止
tcp数据
d9 f2 源端口
00 50 目标端口,80端口是http
e8 e9 3a a6 数据序列号
00 00 00 00 数据确认序列号,这个是第一次握手,没有。
a0 02 前4位1011说明数据离包头偏移40byte,后六位000010,SYN位1,是个同步包。
fa f0 窗口
9e fb 校验和
00 00 紧急指针
以下都是tcp的选项,为了简化,可以直接不要,这样
mac头就6+6+2=14 byte
ip头 20 byte
tcp头 20byte
最短的tcp数据包就 54byte
02 04 05 b4 mss大小
04 02 SACK permitted
08 0a 9e 22 54 c9 00 00 00 00 TSOPT
01 03 03 No_Operation
07 填充
如果用结构体来表示整个tcp包,注意一下位对齐,用了usigned int类型后,mac头14byte,下面可能会有空byte,最后整个结构体发出去,接收就可能错位。
接着就是tcp的三次握手,这里只做服务端,基本就是

  1. C->S,flag = S,client_sqn = a,client_ack =0
  2. S->C, flag = S|A server_sqn = b, server_ack = a+1
  3. C->S, flag = A client_sqn = a+1, client_ack = b+1
    建立起tcp连接以后,可以传输http数据
  4. 客户端先发get报文,
    C->S, flag = P|A client_sqn = a+1, client = b+1, http_len = n
  5. 服务端发ack表明收到,然虎发响应报文。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值