arp,udp,icmp编码实现理解

一直关注tcp/ip的底层协议栈,终于有了大概的了解,通过ntytcp了解tcp相关的底层实现,发现除了tcp协议栈相关的东西,还有udp,arp,icmp(ping命令相关),以及通过netmap对网卡数据进行接收的相关基础知识点,幸好,听过king老师相关的课程,这里就简单的做一些整理。

1:概念介绍:

1:arp: 地址解析协议(ip层/数据链路层)

​ 维护了计算机ip和mac地址(物理地址)的对应关系。 (因为网络通信时实际传输的是“帧”,帧里面是有目标主机的MAC地址的)

​ ==》在TCP/IP模型中,ARP协议属于IP层;在OSI模型中,ARP协议属于链路层。

    ==》局域网内维护ip和mac地址的对应关系

    ==》如果是外网,路由器网关作为中转,网关直连的网卡上请求对方网关的MAC地址,然后网关通过IP层查询目标主机,获取mac地址发送。

    ==》参考连接:https://blog.csdn.net/jiejiemcu/article/details/88406088

2:icmp:网络控制消息协议(网络层)

​ 为了辅助IP 协议,交换各种各样的控制信息而被制造出来的。 属于网络层协议(IP,icmp)

​ 主要功能:差错通知和信息查询

​ 例如:ping命令,traceroute命令

3:udp:用户数据报协议(传输层)

​ UDP是基于IP的简单协议,不可靠的协议。

​ udp传输速度快,可以用在下载,游戏方向。

​ 用户可以自己实现可靠的数据传输,通过增加确认和重传机制,实现udp的可靠传输。

2:协议数据结构

1:根据协议栈分析数据的结构:

在这里插入图片描述

根据图形理解:

​ 发送数据时,其实就是应用层我们的数据外层以此包上各个协议栈的数据。

​ 而接受数据时,其实就是从链路层开始一层层解析最外层的数据,获得最后特定协议特定数据模块

2:以太帧的结构

以太帧起始部分由前同步码和帧开始定界符组成,后面紧跟着一个以太网报头,以 MAC 地址说明目的地址和源地址。以太帧的中部是该帧负载的包含其他协议报头的数据包,如 IP 协议。

以太帧由一个 32 位冗余校验码结尾,用于检验数据传输是否出现损坏。以太帧结构如图所示。

在这里插入图片描述

注意:其中的数据块长度最小为 46 字节,最大为 1500 字节。

3:arp协议的分析

​ arp属于网络层,就是给arp数据块加上网络层的arp头信息,再加上数据链路层头信息。

在这里插入图片描述

arp报文格式:

在这里插入图片描述

相关代码结构定义如下:

#define ETH_ALEN	6
//以太网协议头的定义
struct ethhdr {
	unsigned char h_dest[ETH_ALEN];
	unsigned char h_source[ETH_ALEN];
	unsigned short h_proto;     //arp 为0x0806   ip数据包为 0x0800
};
//arp协议头的定义
struct arphdr {
	unsigned short h_type;    //目标网卡的硬件类型 1为以太网地址
	unsigned short h_proto;   //协议类型 0x0800表示ip协议
	unsigned char h_addrlen;  //硬件地址长度 如以太网地址长度为6,对应硬件类型
	unsigned char protolen;   //协议地址长度 如arp,ip协议,值是4
	unsigned short oper;      // arp请求:1 arp应答:2 rarp请求:3 rarp应答4
	unsigned char smac[ETH_ALEN];  //源mac地址
	unsigned int sip;			   //源ip地址
	unsigned char dmac[ETH_ALEN];  //目标mac地址
	unsigned int dip;			   //目标ip地址
};

//arp的整体包,
struct arppkt {
	struct ethhdr eh;
	struct arphdr arp;
};

如果用wireshark抓包分析,参考网络上的图:
在这里插入图片描述
在这里插入图片描述

4:ICMP协议:控制报文协议

ICMP 协议用于在 IP 主机和路由器之间传递控制消息,描述网络是否通畅、主机是否可达、路由器是否可用等网络状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E07abpj1-1625752926028)(C:\Users\yun68\AppData\Roaming\Typora\typora-user-images\image-20210708212325165.png)]

icmp协议位于传输层:

struct ethhdr {
	unsigned char h_dest[ETH_ALEN];		//目的mac地址
	unsigned char h_source[ETH_ALEN];	//源mac地址
	unsigned short h_proto;				//上一层协议类型,如0x0800代表上一层是IP协议,0x0806为arp
};

struct iphdr {
	unsigned char version; 	//4 位IP版本号+4位首部长度
	unsigned char tos;		//8位服务类型TOS
	unsigned short tot_len; //16位IP包总长度(字节)
	unsigned short id;			//16位标识, 用于辅助IP包的拆装
	unsigned short flag_off;  	//3位标志位+13位偏移位, 也是用于IP包的拆装
	unsigned char ttl;			//8位IP包生存时间 TTL
	unsigned char protocol;		//8位协议 (TCP, UDP 或其他)
	unsigned short check;		//16位IP首部校验和,最初置零,等所有包头都填写正确后,计算并替换.
	unsigned int saddr;			//32位源IP地址
	unsigned int daddr;			//32位目的IP地址
};
struct icmphdr {
	unsigned char type;		//标识ICMP报文的类型,分为两种查询报文和差错报文
	unsigned char code;		//标识对应ICMP报文的代码,于type共同表示报文类型
	unsigned short check;   //ICMP报文数据部分在内的整个ICMP数据报的校验和 从TYPE开始,直到最后一位用户数据,如果为字节数为奇数则补充一位
	unsigned short identifier;	//识别号(一般用进程号作为识别号), 用于匹配ECHO和ECHO REPLY包
	unsigned short seq;			//报文序列号, 用于标记ECHO报文顺序
	unsigned char data[32];		//时间戳
};
//icmp协议包的定义
struct icmppkt {
	struct ethhdr eh;
	struct iphdr ip;
	struct icmphdr icmp;
};

5:UDP协议:用户数据报协议

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qqFKtafN-1625752926029)(C:\Users\yun68\AppData\Roaming\Typora\typora-user-images\image-20210708213734891.png)]

#define ETH_ALEN	6
struct ethhdr {
	unsigned char h_dest[ETH_ALEN];
	unsigned char h_source[ETH_ALEN];
	unsigned short h_proto;
};

struct iphdr {
	unsigned char version;
	unsigned char tos;
	unsigned short tot_len;
	unsigned short id;
	unsigned short flag_off;
	unsigned char ttl;
	unsigned char protocol;
	unsigned short check;
	unsigned int saddr;
	unsigned int daddr;
};
//共8个字节
struct udphdr {
	unsigned short source;  // 源端口号16bit
	unsigned short dest;	// 目的端口号16bit
	unsigned short len;		// 数据包长度16bit
	unsigned short check;	// 校验和16bit
};
//udp数据包
struct udppkt {
	struct ethhdr eh;
	struct iphdr ip;
	struct udphdr udp;
	unsigned char body[128];
};

3:测试抓包

1:arp抓包测试:

2:icmp抓包测试:

3:udp抓包测试:

4:tcp/ip抓包测试:

4:编码实现

用netmap和相关的自定义协议包实现对arp,udp,icmp报文的接受。

void echo_arp_pkt(struct arppkt *arp, struct arppkt *arp_rt, char *hmac) {
	memcpy(arp_rt, arp, sizeof(struct arppkt));

	memcpy(arp_rt->eh.h_dest, arp->eh.h_source, ETH_ALEN);
	str2mac(arp_rt->eh.h_source, hmac);
	arp_rt->eh.h_proto = arp->eh.h_proto;

	arp_rt->arp.h_addrlen = 6;
	arp_rt->arp.protolen = 4;
	arp_rt->arp.oper = htons(2);
	str2mac(arp_rt->arp.smac, hmac);
	arp_rt->arp.sip = arp->arp.dip;
	memcpy(arp_rt->arp.dmac, arp->arp.smac, ETH_ALEN);
	arp_rt->arp.dip = arp->arp.sip;
}


void echo_udp_pkt(struct udppkt *udp, struct udppkt *udp_rt) {
	memcpy(udp_rt, udp, sizeof(struct udppkt));
	memcpy(udp_rt->eh.h_dest, udp->eh.h_source, ETH_ALEN);
	memcpy(udp_rt->eh.h_source, udp->eh.h_dest, ETH_ALEN);
	udp_rt->ip.saddr = udp->ip.daddr;
	udp_rt->ip.daddr = udp->ip.saddr;
	udp_rt->udp.source = udp->udp.dest;
	udp_rt->udp.dest = udp->udp.source;
}

unsigned short in_cksum(unsigned short *addr, int len)
{
	register int nleft = len;
	register unsigned short *w = addr;
	register int sum = 0;
	unsigned short answer = 0;

	while (nleft > 1)  {
		sum += *w++;
		nleft -= 2;
	}

	if (nleft == 1) {
		*(u_char *)(&answer) = *(u_char *)w ;
		sum += answer;
	}
	sum = (sum >> 16) + (sum & 0xffff);	
	sum += (sum >> 16);			
	answer = ~sum;
	return (answer);
}

void echo_icmp_pkt(struct icmppkt *icmp, struct icmppkt *icmp_rt) {

	memcpy(icmp_rt, icmp, sizeof(struct icmppkt));

	icmp_rt->icmp.type = 0x0; //
	icmp_rt->icmp.code = 0x0; //
	icmp_rt->icmp.check = 0x0;

	icmp_rt->ip.saddr = icmp->ip.daddr;
	icmp_rt->ip.daddr = icmp->ip.saddr;

	memcpy(icmp_rt->eh.h_dest, icmp->eh.h_source, ETH_ALEN);
	memcpy(icmp_rt->eh.h_source, icmp->eh.h_dest, ETH_ALEN);

	icmp_rt->icmp.check = in_cksum((unsigned short*)&icmp_rt->icmp, sizeof(struct icmphdr));
}
#define PROTO_IP	0x0800
#define PROTO_ARP	0x0806

int main() {
	struct ethhdr *eh;
	struct pollfd pfd = {0};
	struct nm_pkthdr h;
	unsigned char *stream = NULL;

	struct nm_desc *nmr = nm_open("netmap:eth0", NULL, 0, NULL);
	if (nmr == NULL) {
		return -1;
	}

	pfd.fd = nmr->fd;
	pfd.events = POLLIN;

	while (1) {
		int ret = poll(&pfd, 1, -1);
		if (ret < 0) continue;
		
		if (pfd.revents & POLLIN) {
			stream = nm_nextpkt(nmr, &h);
            //解析以太网头
			eh = (struct ethhdr*)stream;
			//根据以太网头中的类型依次处理
			if (ntohs(eh->h_proto) == PROTO_IP) {

				struct udppkt *udp = (struct udppkt*)stream;
                //ip头中检验udp协议和icmp协议
				if (udp->ip.protocol == PROTO_UDP) {

					struct in_addr addr;
					addr.s_addr = udp->ip.saddr;

					int udp_length = ntohs(udp->udp.len);
					printf("%s:%d:length:%d, ip_len:%d --> ", inet_ntoa(addr), udp->udp.source, 
						udp_length, ntohs(udp->ip.tot_len));

					udp->body[udp_length-8] = '\0';
					printf("udp --> %s\n", udp->body);
#if 1
					struct udppkt udp_rt;
					echo_udp_pkt(udp, &udp_rt);
					nm_inject(nmr, &udp_rt, sizeof(struct udppkt));
#endif
				} else if (udp->ip.protocol == PROTO_ICMP) {
					
					struct icmppkt *icmp = (struct icmppkt*)stream;

					printf("icmp ---------- --> %d, %x\n", icmp->icmp.type, icmp->icmp.check);
					if (icmp->icmp.type == 0x08) {
						struct icmppkt icmp_rt = {0};
						echo_icmp_pkt(icmp, &icmp_rt);

						//printf("icmp check %x\n", icmp_rt.icmp.check);
						nm_inject(nmr, &icmp_rt, sizeof(struct icmppkt));
					}
					
				} else if (udp->ip.protocol == PROTO_IGMP) {

				} else {
					printf("other ip packet");
				}
				
			}  else if (ntohs(eh->h_proto) == PROTO_ARP) {

				struct arppkt *arp = (struct arppkt *)stream;
				struct arppkt arp_rt;

				if (arp->arp.dip == inet_addr("192.168.2.217")) {
					echo_arp_pkt(arp, &arp_rt, "00:50:56:33:1c:ca");
					nm_inject(nmr, &arp_rt, sizeof(struct arppkt));
				}
			}
		} 
	}
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值