TCP/IP实现(六) IP选项处理

一.概述

       IP选项是IP固定首部之后的选项部分,由于IP首部长度是用4bit来计数,以4个字节为表示的,所以首部长度最多为60个字节,IP选项最多为40个字节。IP选项字段可能包含0~多个单独选项。选项包含两类:单字节与多字节。单字节选项只有1个字节的类型字段,多字节字段包含1个字节的类型字段,1个字节的长度字段,以及之后的数据字段(数据字段的第一个字节一般是数据偏移,如在源路由选项中代表下一个待处理的字段)。其结构如下图所示:

         

       常见的IP选项有以下几种:

  1.      NOP选项              IPOPT_NOP      单字节选项       用于与后续选项凑成4字节,使后续选项落在4字节边界上,比如常与多字节选项中的type,len,offset凑成4个字节。
  2.      EOL选项              IPOPT_EOL       单字节选项       用于补充在IP选项的最后部分,因为IP选项的总长度必须是4字节的倍数。
  3.      LSRR选项           IPOPT_LSRR     多字节选项       宽松路由选项
  4.      SSRR选项           IPOPT_SSRR    多字节选项       严格路由选项
  5.      Record Route      IPOPT_RR         多字节选项       记录路由选项
  6.      Timestamp          IPOPT_RR         多字节选项       时间戳选项

        可以在其上设置IP选项的套接字包括TCP,UDP和原始IP套接字,要清除这些选项可以使用setsockopt(第四个值参数为空或第五个值长度参数为0)设置。TCP套接字自动使用来自SYN所在数据报的原路径选项的逆转,而不必我们使用setsockopt告诉内核使用什么路径来发送。

二.IP选项使用
1.LSRR(宽松源路由选项) & SSRR(严格源路由选项)

      虽然IP选项最多只含有40个字节,但是传递给函数setsockopt的参数却是一个指向大小小于等于44的缓冲区,这是因为缓冲区中指定的源路由路径中的第一个地址会被赋值到IP数据报的目的地址中,然后这个地址就可以被移除了。每到一个指定的路由节点都会将offset所指的地址设置为下一目的地址,在将从当前路由外出的接口地址放至offset处,随后将offset后移。其传输过程中填充步骤的示意图如下:

   

   具体的调用setsockopt的写法在此不再赘述,可以参考UNP p564 ~ p566。

三.IP选项处理实现

        在博文《TCP/IP实现(五) IP协议》中提到,IP选项是由IP层的ipintr函数在验证IP固定首部之后,匹配目的地址之前进行处理的。在这期间会调用ip_dooptions处理IP选项。其总体实现如下:

int ip_dooptions(mbuf* m)
{
    struct ip* = mtod(m, ip*);//指向mbuf中的ip数据报部分
    u_char* *cp = (u_char*)(ip + 1); //指向ip选项
    int cnt = (ip->ip_hl << 2) - sizeof(struct ip); // 计算ip选项长度
    int optlen = 0, icmp_type = ICMP_PARAMROB;// 默认ICMP报文为参数错误 
    for(; cnt > 0; cnt - optlen, cp += optlen) { // 遍历选项
        opt = cp[type];// 选项类型
        if(opt == IPOPT_EOL) // EOL为尾部填充选项,结束遍历
            break;
        if(opt == IPOPT_NOP) // 填充选项,不做处理
            optlen = 1;      // 选项长度
        else {
            optlen = cp[len];
            if(optlen <= 0 || optlen > cnt) { // 检查选项长度
                goto bad; // 返送ICMP报文
            }
            switch(opt){
                处理各选项

                default: // 未识别选项不做处理
                    break;
            } // switch
        } //else
    } //for
    
    判断是否根据源路由进行转发
    return 1;
    bad:
        //回复ICMP报文;
        icmp_error(xx, icmp_type, code,...);
}

1.记录路由选项IPOPT_RR

       首先检查数据偏移offset是否过小(小于4),若是则发送ICMP_PARAMROB参数问题ICMP报文,code为出错字节在分组内的偏移。接着计算该IP选项的可用存储空间,若不够则不记录,否则查找路由,记录外出接口的IP地址,若无路由信息则返回ICMP住居不可达报文。选项处理代码如下:

case IPORT_RR:
    if((off = cp[offset]) < 4) {
        code = &cp[offset] - (u_char*)ip;
        goto bad;
    }

    if(off + sizeof(struct in_addr) > optlen) // 选项的存储空间不足
        break;
    
    //将目的地址拷贝到一个存储地址的全局buf中
    bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr, sizeof(ipaddr.sin_addr)); 

    if(邻接网络和路由搜索失败){
        icmp_type = ICMP_UNREACH;
        code = ICMP_UNREACH_HOST;
        goto bad;
    }

    若目的地址就是本机则将接收接口的地址拷贝到选项中,否则将外出接口的地址拷贝到选项中
    break;

2.源路由选项LSRR & SSRR

       源路由选项分为宽松源路由选项和严格源路由选项。宽松源路由选项只指定某些中间路由器的路由,而严格路由选项却包含了源站到目的栈之间的全部路由。从源站出发时会将IP数据报的目的地址设置为源路由选项中的offset所指的地址,且每到一个源路由选项中所指明的站,都会将IP数据报的目的地址设置为源路由表项中的下一站,即offset偏移处的地址。实现代码如下:

case IPOPT_LSRR:
case IPOPT_SSRR:
    if((off = cp[offset]) < 4){   // 偏移过小
        code = &cp[offset] - (u_char *)ip; //计算错误字节在分组中的偏移
		goto bad;
    }

    // 第一步
    将IP数据报中的目的地址与本地地址进行匹配
    if(若无匹配的本地地址){
        if(是严格路由选项) { //说明源路由失败,因为指定了每一个节点
            icmp_type = ICMP_UNREACH;
            code = ICMP_UNREACH_SRCFALL;// 源路由失败ICMP报文 
            goto bad;
        }
        // 运行到此处说明是宽松路由协议,宽松路由允许经过未指定的节点,对于未指定的节点不做过多处理
        // 若无其它选项则会交还IP处理函数ipintr继续处理,否则处理其它选项
        break;
    }

    // 允许到此处说明本站是源路由站之一
    if(源路由表项中已经没有下一个路由项了) {
        //说明已到达终点
        break;
    }
    
    // 运行到此处说明还未到终点
    // 检查源路由表项中的下一站地址
    if(是严格路由协议){
        下一站只能在邻接网络上,因此调用调用ifa_ifwithdstaddr匹配各接口的目的地址(SLIP接口就指定了目的地址),
        若匹配失败则继续调用ifa_ifwithnet匹配邻接网络
    }else {
        //宽松路由
        进行路由查找
    }
    
    if(下一站地址搜索失败) {
            icmp_type = ICMP_UNREACH;
            code = ICMP_UNREACH_SRCFALL;// 源路由失败ICMP报文 
            goto bad;
    }

    将IP地址设置未下一站地址,即将cp[off]处的地址设置为下一站地址
    将外出接口的地址放至cp[off]处
    break;

    当我们调用getsockopt函数获取源路径时,会发现得到的原路径是反向的,且最后一个地址是收到IP数据报源站地址,在源路由中的最后一个地址(即发送端设置的目的地址,也就是接收者的本机地址)也会被放至到第一个字节(选项类型之前)。示意图如下:

          

    这个过程是通过两个函数和一个结构体实现的,save_rte函数将IP数据报中的源路由及源地址存储到结构体ip_srcrt中,接着再由函数ip_srcroute将其逆转存储到一个mbuf中,存储后的格式如上图所示。对于其具体实现在此不进行赘述,可以参考TCP V2 P205 ~ P207。

3.时间戳选项IPOPT_TS

      待补充

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
TCP/IP详解·卷2:实现》完整而详细地介绍了TCP/IP协议是如何实现的。书中给出了约500个图例,15000行实际操作的C代码,采用举例教学的方法帮助你掌握TCP/IP实现。《TCP/IP详解·卷2:实现》不仅说明了插口API和协议族的关系以及主机实现与路由器实现的差别。还介绍了4.4BSD-Lite版的新的特点。《TCP/IP详解·卷2:实现》适用于希望理解TCP/IP协议如何实现的人,包括编写网络应用程序的程序员以及利用TCP/IP维护计算机网络的系统管理员。 目录 · · · · · · 第一章 概述 1.1 引言 1.2 源代码表示 1.3 历史 1.4 应用编程接口 1.5 程序示例 1.6 系统调用和库函数 1.7 描述符 1.8 网络实现概述 1.9 mbuf与输出处理 1.10 输入处理 1.11 网络实现概述 1.12 中断级别与并发 1.13 源代码组织 1.14 测试网络 1.15 小结 第二章 mduf:存储器缓存 2.1 引言 2.2 代码介绍 2.3 mduf的定义 2.4 mduf结构 2.5 简单的mduf宏和函数 2.6 m_devget和m_pullup函数 2.7 mduf宏和函数的小结 2.8 Net/3联网数据结构小结 2.9 m_copy和簇引用记数 2.10 其他选择 2.11 小结 第三章 接口层 3.1 引言 3.2 代码介绍 3.3 ifnet结构 3.4 ifaddr结构 3.5 sockaddr结构 3.6 ifnet与ifaddr的专用化 3.7 网络初始化概述 3.8 以太网初始化 3.9 SLIP初始化 3.10 环回初始化 3.11 if_attach函数 3.12 ifinit函数 3.13 小结 第四章 接口:以太网 4.1 引言 4.2 代码介绍 4.3 以太网接口 4.4 ioctl系统调用 4.5 小结 第五章 接口:SLIP和环回 5.1 引言 5.2 代码介绍 5.3 SLIP接口 5.4 环回接口 5.5 小结 第IP编址 6.1 引言 6.2 代码介绍 6.3 接口和地址小结 6.4 sockaddr_in结构 6.5 in_ifaddr结构 6.6 地址指派 6.7 接口ioctl处理 6.8 internet实用函数 6.9 ifnet实用函数 6.10 小结 第七章 域和协议 7.1 引言 7.2 代码介绍 7.3 domain结构 7.4 protosw结构 7.5 IP的domain和protosw结构 7.6 pffindproto和pffindtype函数 7.7 pfctlinput函数 7.8 IP初始化 7.9 sysctl系统调用 7.10 小结 第八章 IP:网际协议 8.1 引言 8.2 代码介绍 8.3 IP分组 8.4 输入处理ipintr函数 8.5 转发:ip_forward函数 8.6 输出处理ip_output函数 8.7 Internet检验和:in_cksum函数 8.8 setsockopt和getsockopt系统调用 8.9 ip_sysctl函数 8.10 小结 第九章 IP选项处理 9.1 引言 9.2 代码介绍 9.3 选项格式 9.4 ip_dooptions函数 9.5 记录路由选项 9.6 源站和记录路由选项 9.7 时间戳选项 9.8 ip_insertoptions函数 9.9 ip_pcbopts函数 9.10 一些限制 9.11 小结 第十章 IP的分片与重装 10.1 引言 10.2 代码介绍 10.3 分片 10.4 ip_optcopy函数 10.5 重装 10.6 ip_optcopy函数 10.7 ip_slowtimo函数 10.8 小结 第十一章 ICMP:Internet控制报文协议 第十二章 IP多播 第十三章 IGMP:Internet组管理协议 第十四章 IP多播选路 第十五章 插口层 第十章 插口I/O 第十七章 插口选项 第十八章 Radix树路由表 第十九章 选路请求和选路消息 第二十章 选路接口 第二十一章 ARP:地址解析协议 第二十二章 协议控制块 第二十三章 UDP:用户数据报协议 第二十四章 TCP:传输控制协议 第二十五章 TCP的定时器 第二十TCP输出 第二十七章 TCP的函数 第二十八章 TCP的输入 第二十九章 TCP的输入(续) 第三十章 TCP的用户需求 第三十一章 BPF:BSD分组过滤程序 第三十二章 原始IP 结束语 附录A 部分习题的解答 附录B 源代码的获取 附录C RFC 1122的有关内容 参考文献
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值