Linux内核分析 - 网络[十四]:IP选项
首先会作一些检查,选项长度optlen不能比3 小,到少有3字节的头部:code, len, ptr。指针ptr不能比4小,因为头部就有4字节。这里optlen是去除了头部的IPOPT_NOOP后 的长度,而ptr的计算是包括IPOPT_NOOP的,因此一个是3一个是4;另外,选项中只能有一个源路由选项,因此当srr有值时,表 示正在处理的是第二个源路由选项,则有错误。 if (optlen < 3) { pp_ptr = optptr + 1; goto error; } if (optptr[2] < 4) { pp_ptr = optptr + 2; goto error; } /* NB: cf RFC-1812 5.2.4.1 */ if (opt->srr) { pp_ptr = optptr; goto error; } 当skb==NULL,对应于第一种情况(生成报文选项时);取出源路由选项的第一跳,记录到选项opt的faddr中,作为下 一跳地址;源路由选项依次前移。对应于开头给出的例子,这里处理后结果如图所示: 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); } 最后 记录,is_strictroute是否是严格的路由选路,srr表示选项到IP报头的距离,同样,它只对处理收到的报文中选项时有效。 opt->is_strictroute = (optptr[0] == IPOPT_SSRR); opt->srr = optptr - iph; 以上是关于IP选项报文的生成,下面从ip_rcv()来看IP选项报文的接收。 ip_rcv() -> ip_rcv_finish() ip_rcv()中重置IP的控制数据struct inet_skb_param为0,在IP章节已经说过,控制数据是skb中48 字节的一个字段,在各层协议中含义不同,在IP层,它被解释为inet_skb_parm,包含opt和flags,其中前者与IP选项有关。 memset(IPCB(skb), 0, sizeof(struct inet_skb_parm)); struct inet_skb_parm { struct ip_options opt; /* Compiled IP options */ unsigned char flags; }; ip_rcv_finish()中如果头部长度字段ihl大于4,则表示含有IP选项,此时调用ip_rcv_optins()来接收IP选项。 if (iph->ihl > 5 && ip_rcv_options(skb)) goto drop; ip_rcv_options() iph指向IP头;opt指向控制数据的opt,对IP选项处理的结构会存放在此,作为skb 的一部分,在其它地方起作用;设置opt->optlen选项长度,这里的长度包括了开头的IPOPT_NOOP字段,是4的整数倍。 iph = ip_hdr(skb); opt = &(IPCB(skb)->opt); opt->optlen = iph->ihl*4 - sizeof(struct iphdr); 调用ip_options_compile()处理选项,这是该函数被调 用的第二种情况(收到带IP选项报文时),传入参数skb是报文的skb,函数的详细说明见上文(还是以LSRR为例),实际上 ip_options_compile()在这种情况下只相应设置了opt->is_strictroute和opt->srr,而不像在生成选项时对IP选项进行 处理,对接收到IP选项的处理要留带到发送报文时。 if (ip_options_compile(dev_net(dev), opt, skb)) { IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS); goto drop; } 如果是LSRR,opt->srr在上一步中被设置,为选项到报头的距离,对于带SSRR或LSRR选项的报文来说,opt- >srr值不为0,进入调用ip_options_rcv_srr()完成LSRR选项的处理。 if (unlikely(opt->srr)) { …… if (ip_options_rcv_srr(skb)) goto drop; } return 0; ip_options_rcv_srr() 该函数的主要作用是根据源站选项重新设置skb的路由项,从而改变报文的正常流 程。它不会对选项进行其它操作,真正的操作在发送时完成。 首先会进行一些检查,报文的目的MAC必须是本主机,这里检查 skb->pkt_type==PACKET_HOST;如果报文的目的IP不是本机(而是在本机的邻居),则本主只是源路径的一个中转站,此时不 用再次查找路由表,直接返回,这里检查rt->rt_type==RTN_UNICAST,这种情况在LSRR中是允许的,SSRR是不允许的;如果 报文的目的IP对本机来说不是直接可达,则错误返回。 if (skb->pkt_type != PACKET_HOST) return -EINVAL; if (rt->rt_type == RTN_UNICAST) { if (!opt->is_strictroute) return 0; icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl(16<<24)); return -EINVAL; } if (rt->rt_type != RTN_LOCAL) return -EINVAL; (编辑:PHP编程网 - 黄冈站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |