网络子系统43_ip选项预处理

简介:
//选项格式:
//	1.type中指示该选项在分片时是否需要被拷贝
//	2.ptr从1算起,1为type的位置
//	3.len不包括type字段,其余都包括(len,ptr,选项内容)

//type字段:

ip选项type字段的常见代码值:

//inet_addr_type(addr)返回l3 addr的路由类型:
//	1.RTN_LOCAL 该ip地址属于一个本地接口
//	2.RTN_UNICAST 根据路由表,该ip地址可以抵达,而且是单播地址
//	3.RTN_MULTICAST 该地址是多播地址
//	4.RTN_BROADCAST 该地址是广播地址

//此函数分析ip报文中的如下选项,并设置到skb->cb中
//	1.IPOPT_END 处理办法,使用IPOPT_END覆盖之后出现的所有选项,并标示ip头被更改过
//	2.IPOPT_NOOP 处理办法,跳过
//	3.IPOPT_SSRR IPOPT_LSRR 处理办法,设置opt->ss为选项相对ip头的偏移量,在之后对ip报文的处理上,填充该选项,该选项只能出现一次
//	4.IPOPT_RR 处理办法,拷贝路由缓存中的首选源地址到选项中,更新选项的ptr字段,使其指向下一个空闲位置
//	5.IPOPT_TIMESTAMP 处理办法,分析子选项
//		5.1 IPOPT_TS_TSONLY 只记录时间戳
//		5.2 IPOPT_TS_TSANDADDR 记录时间戳和ip地址
//		5.3 IPOPT_TS_PRESPEC 本机ip等于ptr当前所指的ip时,填入本机时间,否则更新ptr到下一个ip地址
//	6.IPOPT_SEC IPOPT_SID 处理办法,不处理
//	3,4在处理时,如果选项空间不足够,则通过icmp向发送主机报告错误并丢弃封包
//	5在处理时,如果选项空间不足够,则递增溢出次数,如果溢出次数已达15次,则通过icmp项主机发送报告错误并丢弃封包
//调用路径ip_rcv->ip_rcv_finish->ip_options_compile
//opt = NULL

1.1 int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)
{
	int l;
	unsigned char * iph;
	unsigned char * optptr;
	int optlen;
	unsigned char * pp_ptr = NULL;
	struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL;

	if (!opt) {//在接收路径上opt=null
		opt = &(IPCB(skb)->opt);//skb->cb强转成struct ip_options结构
		memset(opt, 0, sizeof(struct ip_options));
		iph = skb->nh.raw;
		opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);//ip选项的长度 = ihl*4 - 20(ip报头长度)
		optptr = iph + sizeof(struct iphdr);//选项的第一个字节
		opt->is_data = 0;
	} else {
		optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);
		iph = optptr - sizeof(struct iphdr);
	}

	for (l = opt->optlen; l > 0; ) {
		switch (*optptr) {
		      case IPOPT_END://在IPOPT_END之后的所有选项,均会被IPOP_END覆盖
			for (optptr++, l--; l>0; optptr++, l--) {
				if (*optptr != IPOPT_END) {
					*optptr = IPOPT_END;
					opt->is_changed = 1;//记录报头被修改
				}
			}
			goto eol;
		      case IPOPT_NOOP://IPOPT_NOOP用于填补选项之间的空白
			l--;
			optptr++;
			continue;
		}
		//非单字节选项
		//1.通过第二个字节optptr[1]指示选项长度
		//2.通过第三个字节optptr[2]指示选项内容的指针,起始值为1,表示type字段
		optlen = optptr[1];
		if (optlen<2 || optlen>l) {//选项的健康性检查,非单字节选项长度至少为2,该选项长度不能超过选项剩余的总长度
			pp_ptr = optptr;
			goto error;
		}
		switch (*optptr) {
		      case IPOPT_SSRR://严格源路由选项,发送者列出沿途上的每一台路由器ip地址,并且沿途不能修改
		      case IPOPT_LSRR://宽松源路由选项,中间路由器可以使用另一台不在列表中的路由器,作为通向列表中下一个路由器的路径,发送者指定的路由器必须按照指定的次序使用
			if (optlen < 3) {//健康性检查
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 4) {
				pp_ptr = optptr + 2;
				goto error;
			}
			
			if (opt->srr) {//opt->srr记录源路由选项相对于ip头的起始位置;只能有一个该选项
				pp_ptr = optptr;
				goto error;
			}
			//输入路径上,skb!=NULL
			if (!skb) {
				if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {
					pp_ptr = optptr + 1;
					goto error;
				}
				memcpy(&opt->faddr, &optptr[3], 4);
				if (optlen > 7)
					memmove(&optptr[3], &optptr[7], optlen-7);
			}
			opt->is_strictroute = (optptr[0] == IPOPT_SSRR);//is_strictroute指示是否为严源路由选项
			opt->srr = optptr - iph;//源路由选项的起始位置
			break;
		      case IPOPT_RR://record route选项
			if (opt->rr) {//opt->rr记录record route选项相对于ip头的偏移量;只能有一个该选项
				pp_ptr = optptr;
				goto error;
			}
			if (optlen < 3) {
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 4) {
				pp_ptr = optptr + 2;
				goto error;
			}
			if (optptr[2] <= optlen) {//说明有空闲空间
				if (optptr[2]+3 > optlen) {//不足4字节
					pp_ptr = optptr + 2;
					goto error;
				}
				if (skb) {
					memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);//入口路径上skb->dst在处理ip选项之前被初始化
					opt->is_changed = 1;//复制rt中的首选源地址,首选源地址在ip_route_input_slow中,根据被路由封包的目的地址被设置
				}
				optptr[2] += 4;//指向下一个可用的空闲位置
				opt->rr_needaddr = 1;
			}
			opt->rr = optptr - iph;
			break;
		      case IPOPT_TIMESTAMP://时间戳选项
			if (opt->ts) {//opt->ts记录time stamp选项相对于ip头的偏移量;只能有一个该选项
				pp_ptr = optptr;
				goto error;
			}
			if (optlen < 4) {
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] < 5) {//IPOPT_TIMESTAMP选项头格式为:[type len ptr (overflow:4 | flag:4)],因此ptr指示位置至少为5,(type默认为1)
				pp_ptr = optptr + 2;
				goto error;
			}
			if (optptr[2] <= optlen) {
				__u32 * timeptr = NULL;
				if (optptr[2]+3 > optptr[1]) {
					pp_ptr = optptr + 2;
					goto error;
				}
				//处理子选项,flag字段,指示子选项
				switch (optptr[3]&0xF) {
				      case IPOPT_TS_TSONLY://记录时间戳
					opt->ts = optptr - iph;//opt->ts指示time stamp选项相对于ip头的偏移量
					if (skb) 
						timeptr = (__u32*)&optptr[optptr[2]-1];//本机记录time stamp的位置
					opt->ts_needtime = 1;//告诉本机,需要记录time stamp
					optptr[2] += 4;//选项指针移动4个字节,下一个主机记录time stamp的起始位置
					break;
				      case IPOPT_TS_TSANDADDR://记录时间戳和地址
					if (optptr[2]+7 > optptr[1]) {//不足8字节
						pp_ptr = optptr + 2;
						goto error;
					}
					opt->ts = optptr - iph;
					if (skb) {
						memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);//首选源地址
						timeptr = (__u32*)&optptr[optptr[2]+3];//time stamp填充位置,之后填充
					}
					opt->ts_needaddr = 1;//指示time stamp子选项要求位置和时间
					opt->ts_needtime = 1;
					optptr[2] += 8;//长度更新8字节
					break;
				      case IPOPT_TS_PRESPEC://只针对发送者指定的ip地址,记录time stamp选项
					if (optptr[2]+7 > optptr[1]) {
						pp_ptr = optptr + 2;
						goto error;
					}
					opt->ts = optptr - iph;
					{
						u32 addr;
						memcpy(&addr, &optptr[optptr[2]-1], 4);//检查该地址的路由类型
						if (inet_addr_type(addr) == RTN_UNICAST)//非本机ip地址,但是该ip可达,而且是单播地址
							break;								
						if (skb)
							timeptr = (__u32*)&optptr[optptr[2]+3];
					}
					opt->ts_needtime = 1;
					optptr[2] += 8;//如果指定的ip非本机地址,也会掉过该选项位置,后续的主机,从下一个选项位置开始
					break;
				      default:
					if (!skb && !capable(CAP_NET_RAW)) {
						pp_ptr = optptr + 3;
						goto error;
					}
					break;
				}
				if (timeptr) {//填充time stamp的位置
					struct timeval tv;
					__u32  midtime;
					do_gettimeofday(&tv);//获取系统时间
					midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);//在一天内,已经过多少秒
					memcpy(timeptr, &midtime, sizeof(__u32));
					opt->is_changed = 1;//ip报头被修改,因此需要重新计算校验和
				}
			} else {//time stamp选项,空闲空间不够
				unsigned overflow = optptr[3]>>4;//溢出的次数
				if (overflow == 15) {//已经达到最大的溢出次数
					pp_ptr = optptr + 3;
					goto error;
				}
				opt->ts = optptr - iph;
				if (skb) {
					optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);//递增溢出次数
					opt->is_changed = 1;
				}
			}
			break;
		      case IPOPT_RA:
			if (optlen < 4) {
				pp_ptr = optptr + 1;
				goto error;
			}
			if (optptr[2] == 0 && optptr[3] == 0)
				opt->router_alert = optptr - iph;
			break;
		      case IPOPT_SEC://security选项
		      case IPOPT_SID://stream id选项
		      default://不处理这两种选项
			if (!skb && !capable(CAP_NET_RAW)) {
				pp_ptr = optptr;
				goto error;
			}
			break;
		}
		l -= optlen;
		optptr += optlen;
	}

eol:
	if (!pp_ptr)
		return 0;

error:
	if (skb) {//通过icmp,返回错误
		icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));
	}
	return -EINVAL;
}






目录
相关文章
|
2月前
|
域名解析 存储 网络协议
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
本文详细介绍了IP协议报头结构及其各字段的功能,包括版本、首部长度、服务类型、总长度、标识、片偏移、标志、生存时间(TTL)、协议、首部检验和等内容。此外,还探讨了IP地址的网段划分、特殊IP地址的应用场景,以及路由选择的大致流程。最后,文章简要介绍了DNS协议的作用及其发展历史,解释了域名解析系统的工作原理。
106 5
深入解析网络通信关键要素:IP 协议、DNS 及相关技术
|
3天前
|
网络协议 网络安全 数据安全/隐私保护
计算机网络概念:网关,DHCP,IP寻址,ARP欺骗,路由,DDOS等
【10月更文挑战第27天】计算机主机网关的作用类似于小区传达室的李大爷,负责将内部网络的请求转发到外部网络。当小区内的小不点想与外面的小明通话时,必须通过李大爷(网关)进行联系。网关不仅帮助内部设备与外部通信,还负责路由选择,确保数据包高效传输。此外,网关还参与路由表的维护和更新,确保网络路径的准确性。
13 2
|
12天前
|
存储 缓存 Ubuntu
配置网络接口的“IP”命令10个
【10月更文挑战第18天】配置网络接口的“IP”命令10个
36 0
|
21天前
|
运维 安全 网络协议
Python 网络编程:端口检测与IP解析
本文介绍了使用Python进行网络编程的两个重要技能:检查端口状态和根据IP地址解析主机名。通过`socket`库实现端口扫描和主机名解析的功能,并提供了详细的示例代码。文章最后还展示了如何整合这两部分代码,实现一个简单的命令行端口扫描器,适用于网络故障排查和安全审计。
|
2月前
|
缓存 网络协议 网络架构
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式
本文详细介绍了如何使用网络抓包工具Wireshark进行网络抓包分析,包括以太网v2 MAC帧、IP数据报、ICMP报文和ARP报文的格式,以及不同网络通信的过程。文章通过抓包分析展示了IP数据报、ICMP数据报和ARP数据报的具体信息,包括MAC地址、IP地址、ICMP类型和代码、以及ARP的硬件类型、协议类型、操作类型等。通过这些分析,可以更好地理解网络协议的工作机制和数据传输过程。
网络抓包分析【IP,ICMP,ARP】以及 IP数据报,MAC帧,ICMP报和ARP报的数据报格式
|
2月前
|
网络协议 网络虚拟化
接收网络包的过程——从硬件网卡解析到IP
【9月更文挑战第18天】这段内容详细描述了网络包接收过程中机制。当网络包触发中断后,内核处理完这批网络包,会进入主动轮询模式,持续处理后续到来的包,直至处理间隙返回其他任务,从而减少中断次数,提高处理效率。此机制涉及网卡驱动初始化时注册轮询函数,通过软中断触发后续处理,并逐步深入内核网络协议栈,最终到达TCP层。整个接收流程分为多个层次,包括DMA技术存入Ring Buffer、中断通知CPU、软中断处理、以及进入内核网络协议栈等多个步骤。
|
2月前
|
网络协议 算法 网络性能优化
C语言 网络编程(十五)套接字选项设置
`setsockopt()`函数用于设置套接字选项,如重复使用地址(`SO_REUSEADDR`)、端口(`SO_REUSEPORT`)及超时时间(`SO_RCVTIMEO`)。其参数包括套接字描述符、协议级别、选项名称、选项值及其长度。成功返回0,失败返回-1并设置`errno`。示例展示了如何创建TCP服务器并设置相关选项。配套的`getsockopt()`函数用于获取这些选项的值。
|
2月前
|
网络协议 安全 数据安全/隐私保护
动静态IP的网络协议有什么不同
IP地址分为静态和动态两种分配方式。静态IP地址由管理员手动分配,确保设备具有固定且唯一的网络标识,适用于服务器等关键设备。动态IP地址则通过DHCP服务器自动分配,提供更高的灵活性和管理效率,适合个人电脑和移动设备。两者在网络配置、管理和安全性方面各有优劣,需根据具体应用场景进行选择。静态IP地址虽稳定但配置复杂,安全性较低;动态IP地址配置简单,安全性更高,能有效防止针对固定IP的攻击。
|
2月前
|
网络协议
网络协议概览:HTTP、UDP、TCP与IP
理解这些基本的网络协议对于任何网络专业人员都是至关重要的,它们不仅是网络通信的基础,也是构建更复杂网络服务和应用的基石。网络技术的不断发展可能会带来新的协议和标准,但这些基本协议的核心概念和原理将继续是理解和创新网络技术的关键。
113 0
|
2月前
|
网络协议 网络架构
IP网络协议的深度讲解
IP协议作为互联网的基础,其设计的简洁性和强大功能使得全球范围内的网络通信成为可能。通过不断的发展和改进,尤其是IPv6的推广使用,IP协议将继续支持着日益增长的网络通信需求。了解IP协议的工作原理,对于任何网络技术的学习和应用都是基础且必要的。
66 0