网络子系统51_ip协议报文分片

简介:
//ip分片
//	快速路径的条件:
//		1.skb
//			1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu
//			2.skb的数据长度对齐到8字节的边界
//			3.skb没有被分片
//			4.skb没有被共享
//		2.skb->frag_list
//			1.长度小于(mtu-ip报头-选项)
//			2.除最后一个分片外,长度都需要对齐到8字节边界
//			3.head-data之间的空间,可以容纳ip报头
//	注:skb->frag_list的skb,没有填充ip头,skb填充有ip头
//
//	慢速路径条件:
//		只要不满足快速路径其中的一条,使用慢速路径

//	快速路径处理过程:
//		1.第一个分片使用完整的ip选项
//		2.其余分片使用部分ip选项
//		3.除最后一个分片外,设置MF标志
//		4.设置offset
//		5.使用相同的路由信息,向下层传递

//	慢速路径处理过程:
//		1.分配新的skb,长度为mtu,或者剩余数据量,对齐到8字节边界
//		2.预留l2帧头空间
//		3.拷贝l3报头,以及数据到新skb中
//		4.除第一个分片使用完整的ip选项,其余分片使用部分ip选项
//		5.设置offset
//		5.使用相同的路由信息,向下层传递


//	对比快速路径与慢速路径:
//		1.慢速路径的慢主要表现在分配新的缓存区,从旧缓存区中拷贝数据

//	注:ip报头的offset字段,只针对有效载荷(ip头,ip选项不包括在内)

//调用路径ip_output/ip_mc_output->ip_fragment
1.1 int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))
{
	struct iphdr *iph;
	int raw = 0;
	int ptr;
	struct net_device *dev;
	struct sk_buff *skb2;
	unsigned int mtu, hlen, left, len, ll_rs;
	int offset;
	int not_last_frag;
	struct rtable *rt = (struct rtable*)skb->dst;
	int err = 0;
	//出口设备
	dev = rt->u.dst.dev;
	//ip头
	iph = skb->nh.iph;
	//ip报头设置有DF标志,禁止分片
	////skb->local_df如果被设置,则在需要分片,但是设置DF标志,不向发送方传送ICMP消息
	if (unlikely((iph->frag_off & htons(IP_DF)) && !skb->local_df)) {
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,//需要分片,但是设置DF标志,通知发送方不可达,原因是需要分片,并告知对方mtu
			  htonl(dst_pmtu(&rt->u.dst)));
		kfree_skb(skb);
		return -EMSGSIZE;
	}

	//可以快速分片的条件:
	//	skb
	//		1.skb的数据长度(主缓存区+frags缓存区)小于输出路径的mtu
	//		2.skb的数据长度对齐到8字节的边界
	//		3.skb没有被分片
	//		4.skb没有被共享
	//	skb->frag_list
	//		1.长度小于(mtu-ip报头)
	//		2.除最后一个分片外,长度都需要对齐到8字节边界
	//		3.head-data之间的空间,可以容纳ip报头
	//	注:skb->frag_list的skb,没有填充ip头,skb填充有ip头
	hlen = iph->ihl * 4;//ip头长度
	mtu = dst_pmtu(&rt->u.dst) - hlen;	//数据空间的大小

	if (skb_shinfo(skb)->frag_list) {//frag_list存在skb
		struct sk_buff *frag;
		int first_len = skb_pagelen(skb);//skb主缓存区,frags片段中的数据长度,不包括frag_list中的skb

		if (first_len - hlen > mtu ||//超过允许的最大数据量
		    ((first_len - hlen) & 7) ||//数据长度没有对齐到8字节
		    (iph->frag_off & htons(IP_MF|IP_OFFSET)) ||//此skb是一个分片
		    skb_cloned(skb))//克隆一份skb,慢速分片
			goto slow_path;
		//检查frag_list中的skb是否可以快速分片
		for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
			//frag_list中的skb没有ip头
			if (frag->len > mtu ||
			    ((frag->len & 7) && frag->next) ||//除最后一个分片外,其他分片的长度必须对其到8字节
			    skb_headroom(frag) < hlen)//头空间不够容纳ip报头(ip头+选项)
			    goto slow_path;

			if (skb_shared(frag))//skb被共享
				goto slow_path;
		}
	//快速路径:
		err = 0;
		offset = 0;
		frag = skb_shinfo(skb)->frag_list;
		skb_shinfo(skb)->frag_list = NULL;
		//更新skb->data_len为skb->frags中数据的大小,原始skb->data_len包括frags,frag_list中所有数据的长度
		skb->data_len = first_len - skb_headlen(skb);
		skb->len = first_len;//更新总长度为主缓存区,frags中数据的大小,原始skb->len包括主缓存区,frags,frag_list中所有数据的长度
		iph->tot_len = htons(first_len);//ip报头的数据包长度
		iph->frag_off |= htons(IP_MF);//表示有更多的分片,第一个分片,offset=0
		ip_send_check(iph);//计算ip校验和

		for (;;) {
			if (frag) {//处理skb->frag_list中的skb,为其准备分片的ip报头
				frag->ip_summed = CHECKSUM_NONE;//表示校验和没有计算
				frag->h.raw = frag->data;
				frag->nh.raw = __skb_push(frag, hlen);//移动skb->data指针,填充ip报头和选项
				memcpy(frag->nh.raw, iph, hlen);
				iph = frag->nh.iph;
				iph->tot_len = htons(frag->len);//总长度
				ip_copy_metadata(frag, skb);//使分片skb与头skb有一样的出口设备,路由信息
				if (offset == 0)//第一个分片具有完整的选项,其他分片将所有非copied的选项,均设置为NOOP
					ip_options_fragment(frag);
				offset += skb->len - hlen;//计算本分片的偏移量
				iph->frag_off = htons(offset>>3);//偏移量对齐在8字节
				if (frag->next != NULL)
					iph->frag_off |= htons(IP_MF);//设置还有更多skb
				ip_send_check(iph);//计算ip校验和
			}

			err = output(skb);//向下传递前一个skb,调用ip_finish_output

			if (err || !frag)
				break;

			skb = frag;//保留指向前一个skb的指针
			frag = skb->next;//frag为下一个待处理的skb
			skb->next = NULL;//
		}

		if (err == 0) {
			IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
			return 0;
		}
		//快速路径分片出现错误,释放所有skb
		while (frag) {
			skb = frag->next;
			kfree_skb(frag);
			frag = skb;
		}
		IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
		return err;
	}

	//慢速路径
slow_path:
	left = skb->len - hlen;	//(主缓存区,frags,frag_list)数据的总大小(不包括ip头,选项)	
	ptr = raw + hlen;		//新分片的数据在原skb中的起始位置

	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;//当前分片的偏移量
	not_last_frag = iph->frag_off & htons(IP_MF);//判断是否为最后一个分片

	//开始进行分片
	while(left > 0)	{
		len = left;
		if (len > mtu)//使用mtu(此处的mtu去掉ip报头和选项长度)
			len = mtu;
		
		if (len < left)	{
			len &= ~7;//长度对齐到8字节边界
		}
		//分配新的skb,长度包括l2帧头,l3报头,l3有效载荷
		if ((skb2 = alloc_skb(len+hlen+ll_rs, GFP_ATOMIC)) == NULL) {
			NETDEBUG(printk(KERN_INFO "IP: frag: no memory for new fragment!\n"));
			err = -ENOMEM;
			goto fail;
		}
		//使所有分片都使用相同的路由信息,出口设备
		ip_copy_metadata(skb2, skb);
		skb_reserve(skb2, ll_rs);//预留l2帧头
		skb_put(skb2, len + hlen);//data-tail之间空间大小为(ip报头+ip选项+ip有效载荷)
		skb2->nh.raw = skb2->data;//设置l3报头起始地址
		skb2->h.raw = skb2->data + hlen;//l3有效载荷
		//设置新创建的skb所属的sock
		if (skb->sk)
			skb_set_owner_w(skb2, skb->sk);
		//拷贝ip头,选项
		memcpy(skb2->nh.raw, skb->data, hlen);
		//将skb起始地址为ptr的len个字节拷贝到skb2中
		//由skb_copy_bits处理frag,frag_list
		if (skb_copy_bits(skb, ptr, skb2->h.raw, len))
			BUG();
		left -= len;//更新剩余待分片的数据量

		iph = skb2->nh.iph;
		iph->frag_off = htons((offset >> 3));//偏移量

		//1.只有第一个分片需要完整的选项,其他分片将非copied的选项设置为NOOP
		//2.由于第一个分片已经拷贝完整的ip报头以及选项到其分片中
		//3.优化效率,在第一个分片拷贝完整的选项后,更新选项,非第一个分片都使用相同的ip选项
		if (offset == 0)
			ip_options_fragment(skb);

		if (left > 0 || not_last_frag)
			iph->frag_off |= htons(IP_MF);//还有跟多的分片
		ptr += len;//下一个分片的数据在原skb中的起始位置
		offset += len;//偏移量

		IP_INC_STATS(IPSTATS_MIB_FRAGCREATES);

		iph->tot_len = htons(len + hlen);

		ip_send_check(iph);

		err = output(skb2);
		if (err)
			goto fail;
	}
	kfree_skb(skb);
	IP_INC_STATS(IPSTATS_MIB_FRAGOKS);
	return err;

fail:
	kfree_skb(skb); 
	IP_INC_STATS(IPSTATS_MIB_FRAGFAILS);
	return err;
}

//调用路径 ip_fragment->ip_options_fragment
//	修改ip_option(skb->cb),将非copied类型的选项,均设置为NOOP类型
2.1 void ip_options_fragment(struct sk_buff * skb) 
{
	unsigned char * optptr = skb->nh.raw;
	struct ip_options * opt = &(IPCB(skb)->opt);
	int  l = opt->optlen;
	int  optlen;

	while (l > 0) {
		switch (*optptr) {
		case IPOPT_END:
			return;
		case IPOPT_NOOP:
			l--;
			optptr++;
			continue;
		}
		optlen = optptr[1];
		if (optlen<2 || optlen>l)
		  return;
		if (!IPOPT_COPIED(*optptr))//在选项type的第8个比特,指出该选项是否应该复制到ip分片中
			memset(optptr, IPOPT_NOOP, optlen);//对不需要拷贝到除第一个分片外的选项,设置为NOOP
		l -= optlen;
		optptr += optlen;
	}
	opt->ts = 0;//均设置为0,表明不需要time stamp,record route
	opt->rr = 0;
	opt->rr_needaddr = 0;
	opt->ts_needaddr = 0;
	opt->ts_needtime = 0;
	return;
}

目录
相关文章
|
3月前
|
数据采集 算法 数据挖掘
模块化控制协议(MCP)在网络中增强智能体执行效率的研究
随着Web3技术的迅速发展,去中心化应用和智能体在各种领域的应用逐渐增多。MCP(Modularized Control Protocol,模块化控制协议)作为一种增强智能体执行能力的关键技术,为Web3场景中的智能体提供了更强的灵活性和可扩展性。本文将探讨如何利用MCP技术提升智能体在Web3场景中的执行能力,并通过实例代码展示其实现路径。
314 22
|
7天前
|
网络协议 Linux 虚拟化
配置VM网络:如何设定静态IP以访问主机IP和互联网
以上就是设定虚拟机网络和静态IP地址的基本步骤。需要注意的是,这些步骤可能会因为虚拟机软件、操作系统以及网络环境的不同而有所差异。在进行设定时,应根据具体情况进行调整。
92 10
|
28天前
|
监控 负载均衡 安全
WebSocket网络编程深度实践:从协议原理到生产级应用
蒋星熠Jaxonic,技术宇宙中的星际旅人,以代码为舟、算法为帆,探索实时通信的无限可能。本文深入解析WebSocket协议原理、工程实践与架构设计,涵盖握手机制、心跳保活、集群部署、安全防护等核心内容,结合代码示例与架构图,助你构建稳定高效的实时应用,在二进制星河中谱写极客诗篇。
WebSocket网络编程深度实践:从协议原理到生产级应用
|
1月前
|
运维 架构师 安全
二层协议透明传输:让跨域二层协议“无感穿越”多服务商网络
简介:本文详解二层协议透明传输技术,适用于企业网工、运营商及架构师,解决LLDP/LACP/BPDU跨运营商传输难题,实现端到端协议透传,提升网络韧性与运维效率。
|
3月前
|
监控 安全 Go
使用Go语言构建网络IP层安全防护
在Go语言中构建网络IP层安全防护是一项需求明确的任务,考虑到高性能、并发和跨平台的优势,Go是构建此类安全系统的合适选择。通过紧密遵循上述步骤并结合最佳实践,可以构建一个强大的网络防护系统,以保障数字环境的安全完整。
110 12
|
5月前
|
安全 网络协议 Linux
Linux网络应用层协议展示:HTTP与HTTPS
此外,必须注意,从HTTP迁移到HTTPS是一项重要且必要的任务,因为这不仅关乎用户信息的安全,也有利于你的网站评级和粉丝的信心。在网络世界中,信息的安全就是一切,选择HTTPS,让您的网站更加安全,使您的用户满意,也使您感到满意。
169 18
|
4月前
|
监控 安全 网络安全
网络安全新姿势:多IP配置的五大好处
服务器配置多IP地址,既能提升网络速度与安全性,又能实现多站点托管和故障转移。本文详解多IP的五大妙用、配置方法及进阶技巧。从理论到实践,合理规划IP资源,让服务器性能跃升新高度。
155 2
|
10月前
|
SQL 安全 网络安全
网络安全与信息安全:知识分享####
【10月更文挑战第21天】 随着数字化时代的快速发展,网络安全和信息安全已成为个人和企业不可忽视的关键问题。本文将探讨网络安全漏洞、加密技术以及安全意识的重要性,并提供一些实用的建议,帮助读者提高自身的网络安全防护能力。 ####
239 17
|
10月前
|
SQL 安全 网络安全
网络安全与信息安全:关于网络安全漏洞、加密技术、安全意识等方面的知识分享
随着互联网的普及,网络安全问题日益突出。本文将从网络安全漏洞、加密技术和安全意识三个方面进行探讨,旨在提高读者对网络安全的认识和防范能力。通过分析常见的网络安全漏洞,介绍加密技术的基本原理和应用,以及强调安全意识的重要性,帮助读者更好地保护自己的网络信息安全。
196 10