Chinaunix首页 | 论坛 | 博客
  • 博客访问: 818998
  • 博文数量: 264
  • 博客积分: 592
  • 博客等级: 中士
  • 技术积分: 1574
  • 用 户 组: 普通用户
  • 注册时间: 2011-10-24 22:02
文章分类

全部博文(264)

文章存档

2019年(2)

2018年(1)

2017年(1)

2016年(4)

2015年(14)

2014年(57)

2013年(88)

2012年(97)

分类: LINUX

2014-02-12 15:37:42

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


  1. //type字段:  



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



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



  1. //此函数分析ip报文中的如下选项,并设置到skb->cb中  
  2. //  1.IPOPT_END 处理办法,使用IPOPT_END覆盖之后出现的所有选项,并标示ip头被更改过  
  3. //  2.IPOPT_NOOP 处理办法,跳过  
  4. //  3.IPOPT_SSRR IPOPT_LSRR 处理办法,设置opt->ss为选项相对ip头的偏移量,在之后对ip报文的处理上,填充该选项,该选项只能出现一次  
  5. //  4.IPOPT_RR 处理办法,拷贝路由缓存中的首选源地址到选项中,更新选项的ptr字段,使其指向下一个空闲位置  
  6. //  5.IPOPT_TIMESTAMP 处理办法,分析子选项  
  7. //      5.1 IPOPT_TS_TSONLY 只记录时间戳  
  8. //      5.2 IPOPT_TS_TSANDADDR 记录时间戳和ip地址  
  9. //      5.3 IPOPT_TS_PRESPEC 本机ip等于ptr当前所指的ip时,填入本机时间,否则更新ptr到下一个ip地址  
  10. //  6.IPOPT_SEC IPOPT_SID 处理办法,不处理  
  11. //  3,4在处理时,如果选项空间不足够,则通过icmp向发送主机报告错误并丢弃封包  
  12. //  5在处理时,如果选项空间不足够,则递增溢出次数,如果溢出次数已达15次,则通过icmp项主机发送报告错误并丢弃封包  
  13. //调用路径ip_rcv->ip_rcv_finish->ip_options_compile  
  14. //opt = NULL  
  15.   
  16. 1.1 int ip_options_compile(struct ip_options * opt, struct sk_buff * skb)  
  17. {  
  18.     int l;  
  19.     unsigned char * iph;  
  20.     unsigned char * optptr;  
  21.     int optlen;  
  22.     unsigned char * pp_ptr = NULL;  
  23.     struct rtable *rt = skb ? (struct rtable*)skb->dst : NULL;  
  24.   
  25.     if (!opt) {//在接收路径上opt=null  
  26.         opt = &(IPCB(skb)->opt);//skb->cb强转成struct ip_options结构  
  27.         memset(opt, 0, sizeof(struct ip_options));  
  28.         iph = skb->nh.raw;  
  29.         opt->optlen = ((struct iphdr *)iph)->ihl*4 - sizeof(struct iphdr);//ip选项的长度 = ihl*4 - 20(ip报头长度)  
  30.         optptr = iph + sizeof(struct iphdr);//选项的第一个字节  
  31.         opt->is_data = 0;  
  32.     } else {  
  33.         optptr = opt->is_data ? opt->__data : (unsigned char*)&(skb->nh.iph[1]);  
  34.         iph = optptr - sizeof(struct iphdr);  
  35.     }  
  36.   
  37.     for (l = opt->optlen; l > 0; ) {  
  38.         switch (*optptr) {  
  39.               case IPOPT_END://在IPOPT_END之后的所有选项,均会被IPOP_END覆盖  
  40.             for (optptr++, l--; l>0; optptr++, l--) {  
  41.                 if (*optptr != IPOPT_END) {  
  42.                     *optptr = IPOPT_END;  
  43.                     opt->is_changed = 1;//记录报头被修改  
  44.                 }  
  45.             }  
  46.             goto eol;  
  47.               case IPOPT_NOOP://IPOPT_NOOP用于填补选项之间的空白  
  48.             l--;  
  49.             optptr++;  
  50.             continue;  
  51.         }  
  52.         //非单字节选项  
  53.         //1.通过第二个字节optptr[1]指示选项长度  
  54.         //2.通过第三个字节optptr[2]指示选项内容的指针,起始值为1,表示type字段  
  55.         optlen = optptr[1];  
  56.         if (optlen<2 || optlen>l) {//选项的健康性检查,非单字节选项长度至少为2,该选项长度不能超过选项剩余的总长度  
  57.             pp_ptr = optptr;  
  58.             goto error;  
  59.         }  
  60.         switch (*optptr) {  
  61.               case IPOPT_SSRR://严格源路由选项,发送者列出沿途上的每一台路由器ip地址,并且沿途不能修改  
  62.               case IPOPT_LSRR://宽松源路由选项,中间路由器可以使用另一台不在列表中的路由器,作为通向列表中下一个路由器的路径,发送者指定的路由器必须按照指定的次序使用  
  63.             if (optlen < 3) {//健康性检查  
  64.                 pp_ptr = optptr + 1;  
  65.                 goto error;  
  66.             }  
  67.             if (optptr[2] < 4) {  
  68.                 pp_ptr = optptr + 2;  
  69.                 goto error;  
  70.             }  
  71.               
  72.             if (opt->srr) {//opt->srr记录源路由选项相对于ip头的起始位置;只能有一个该选项  
  73.                 pp_ptr = optptr;  
  74.                 goto error;  
  75.             }  
  76.             //输入路径上,skb!=NULL  
  77.             if (!skb) {  
  78.                 if (optptr[2] != 4 || optlen < 7 || ((optlen-3) & 3)) {  
  79.                     pp_ptr = optptr + 1;  
  80.                     goto error;  
  81.                 }  
  82.                 memcpy(&opt->faddr, &optptr[3], 4);  
  83.                 if (optlen > 7)  
  84.                     memmove(&optptr[3], &optptr[7], optlen-7);  
  85.             }  
  86.             opt->is_strictroute = (optptr[0] == IPOPT_SSRR);//is_strictroute指示是否为严源路由选项  
  87.             opt->srr = optptr - iph;//源路由选项的起始位置  
  88.             break;  
  89.               case IPOPT_RR://record route选项  
  90.             if (opt->rr) {//opt->rr记录record route选项相对于ip头的偏移量;只能有一个该选项  
  91.                 pp_ptr = optptr;  
  92.                 goto error;  
  93.             }  
  94.             if (optlen < 3) {  
  95.                 pp_ptr = optptr + 1;  
  96.                 goto error;  
  97.             }  
  98.             if (optptr[2] < 4) {  
  99.                 pp_ptr = optptr + 2;  
  100.                 goto error;  
  101.             }  
  102.             if (optptr[2] <= optlen) {//说明有空闲空间  
  103.                 if (optptr[2]+3 > optlen) {//不足4字节  
  104.                     pp_ptr = optptr + 2;  
  105.                     goto error;  
  106.                 }  
  107.                 if (skb) {  
  108.                     memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);//入口路径上skb->dst在处理ip选项之前被初始化  
  109.                     opt->is_changed = 1;//复制rt中的首选源地址,首选源地址在ip_route_input_slow中,根据被路由封包的目的地址被设置  
  110.                 }  
  111.                 optptr[2] += 4;//指向下一个可用的空闲位置  
  112.                 opt->rr_needaddr = 1;  
  113.             }  
  114.             opt->rr = optptr - iph;  
  115.             break;  
  116.               case IPOPT_TIMESTAMP://时间戳选项  
  117.             if (opt->ts) {//opt->ts记录time stamp选项相对于ip头的偏移量;只能有一个该选项  
  118.                 pp_ptr = optptr;  
  119.                 goto error;  
  120.             }  
  121.             if (optlen < 4) {  
  122.                 pp_ptr = optptr + 1;  
  123.                 goto error;  
  124.             }  
  125.             if (optptr[2] < 5) {//IPOPT_TIMESTAMP选项头格式为:[type len ptr (overflow:4 | flag:4)],因此ptr指示位置至少为5,(type默认为1)  
  126.                 pp_ptr = optptr + 2;  
  127.                 goto error;  
  128.             }  
  129.             if (optptr[2] <= optlen) {  
  130.                 __u32 * timeptr = NULL;  
  131.                 if (optptr[2]+3 > optptr[1]) {  
  132.                     pp_ptr = optptr + 2;  
  133.                     goto error;  
  134.                 }  
  135.                 //处理子选项,flag字段,指示子选项  
  136.                 switch (optptr[3]&0xF) {  
  137.                       case IPOPT_TS_TSONLY://记录时间戳  
  138.                     opt->ts = optptr - iph;//opt->ts指示time stamp选项相对于ip头的偏移量  
  139.                     if (skb)   
  140.                         timeptr = (__u32*)&optptr[optptr[2]-1];//本机记录time stamp的位置  
  141.                     opt->ts_needtime = 1;//告诉本机,需要记录time stamp  
  142.                     optptr[2] += 4;//选项指针移动4个字节,下一个主机记录time stamp的起始位置  
  143.                     break;  
  144.                       case IPOPT_TS_TSANDADDR://记录时间戳和地址  
  145.                     if (optptr[2]+7 > optptr[1]) {//不足8字节  
  146.                         pp_ptr = optptr + 2;  
  147.                         goto error;  
  148.                     }  
  149.                     opt->ts = optptr - iph;  
  150.                     if (skb) {  
  151.                         memcpy(&optptr[optptr[2]-1], &rt->rt_spec_dst, 4);//首选源地址  
  152.                         timeptr = (__u32*)&optptr[optptr[2]+3];//time stamp填充位置,之后填充  
  153.                     }  
  154.                     opt->ts_needaddr = 1;//指示time stamp子选项要求位置和时间  
  155.                     opt->ts_needtime = 1;  
  156.                     optptr[2] += 8;//长度更新8字节  
  157.                     break;  
  158.                       case IPOPT_TS_PRESPEC://只针对发送者指定的ip地址,记录time stamp选项  
  159.                     if (optptr[2]+7 > optptr[1]) {  
  160.                         pp_ptr = optptr + 2;  
  161.                         goto error;  
  162.                     }  
  163.                     opt->ts = optptr - iph;  
  164.                     {  
  165.                         u32 addr;  
  166.                         memcpy(&addr, &optptr[optptr[2]-1], 4);//检查该地址的路由类型  
  167.                         if (inet_addr_type(addr) == RTN_UNICAST)//非本机ip地址,但是该ip可达,而且是单播地址  
  168.                             break;                                
  169.                         if (skb)  
  170.                             timeptr = (__u32*)&optptr[optptr[2]+3];  
  171.                     }  
  172.                     opt->ts_needtime = 1;  
  173.                     optptr[2] += 8;//如果指定的ip非本机地址,也会掉过该选项位置,后续的主机,从下一个选项位置开始  
  174.                     break;  
  175.                       default:  
  176.                     if (!skb && !capable(CAP_NET_RAW)) {  
  177.                         pp_ptr = optptr + 3;  
  178.                         goto error;  
  179.                     }  
  180.                     break;  
  181.                 }  
  182.                 if (timeptr) {//填充time stamp的位置  
  183.                     struct timeval tv;  
  184.                     __u32  midtime;  
  185.                     do_gettimeofday(&tv);//获取系统时间  
  186.                     midtime = htonl((tv.tv_sec % 86400) * 1000 + tv.tv_usec / 1000);//在一天内,已经过多少秒  
  187.                     memcpy(timeptr, &midtime, sizeof(__u32));  
  188.                     opt->is_changed = 1;//ip报头被修改,因此需要重新计算校验和  
  189.                 }  
  190.             } else {//time stamp选项,空闲空间不够  
  191.                 unsigned overflow = optptr[3]>>4;//溢出的次数  
  192.                 if (overflow == 15) {//已经达到最大的溢出次数  
  193.                     pp_ptr = optptr + 3;  
  194.                     goto error;  
  195.                 }  
  196.                 opt->ts = optptr - iph;  
  197.                 if (skb) {  
  198.                     optptr[3] = (optptr[3]&0xF)|((overflow+1)<<4);//递增溢出次数  
  199.                     opt->is_changed = 1;  
  200.                 }  
  201.             }  
  202.             break;  
  203.               case IPOPT_RA:  
  204.             if (optlen < 4) {  
  205.                 pp_ptr = optptr + 1;  
  206.                 goto error;  
  207.             }  
  208.             if (optptr[2] == 0 && optptr[3] == 0)  
  209.                 opt->router_alert = optptr - iph;  
  210.             break;  
  211.               case IPOPT_SEC://security选项  
  212.               case IPOPT_SID://stream id选项  
  213.               default://不处理这两种选项  
  214.             if (!skb && !capable(CAP_NET_RAW)) {  
  215.                 pp_ptr = optptr;  
  216.                 goto error;  
  217.             }  
  218.             break;  
  219.         }  
  220.         l -= optlen;  
  221.         optptr += optlen;  
  222.     }  
  223.   
  224. eol:  
  225.     if (!pp_ptr)  
  226.         return 0;  
  227.   
  228. error:  
  229.     if (skb) {//通过icmp,返回错误  
  230.         icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((pp_ptr-iph)<<24));  
  231.     }  
  232.     return -EINVAL;  
  233. }  
阅读(1099) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~