//选项格式:
// 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;
}

网络子系统43_ip选项预处理的更多相关文章

  1. 《Linux 性能及调优指南》1.5 网络子系统

    翻译:飞哥 (http://hi.baidu.com/imlidapeng) 版权所有,尊重他人劳动成果,转载时请注明作者和原始出处及本声明. 原文名称:<Linux Performance a ...

  2. Linux内核笔记--网络子系统初探

    内核版本:linux-2.6.11 本文对Linux网络子系统的收发包的流程进行一个大致梳理,以流水账的形式记录从应用层write一个socket开始到这些数据被应用层read出来的这个过程中linu ...

  3. Linux 网络子系统

    今天记录一下Linux网络子系统相关的东西. 因为感觉对这一块还是有一个很大的空白,这件事情太可怕了. 摘抄多份博客进行总结一下Linux网络子系统的相关东西. 一. Linux网络子系统体系结构 L ...

  4. linux网络子系统内核分析

    1.选择路由 若要将数据包发至PC2,则linux系统通过查询路由表可知168.1.1.10(目的地址)的网关地址为192.168.1.1,此时linux系统选择网卡1发送数据包. 2.邻居子系统(通 ...

  5. Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介

    原文:Linux内核分析(四)----进程管理|网络子系统|虚拟文件系统|驱动简介 Linux内核分析(四) 两天没有更新了,上次博文我们分析了linux的内存管理子系统,本来我不想对接下来的进程管理 ...

  6. Linux网络子系统

    再Linux的世界里,万物皆文件,通过虚拟文件系统VFS,程序可以用标准的Linux系统调用对不同的文件系统,甚至不同介质上的文件系统进行读写操作.下面我们揭示Linux网络子系统的秘密 sockfs ...

  7. Linux 网络子系统之网络协议接口层(一)

    Linux 网络设备驱动之网络协议接口层介绍. 网络协议接口层最主要的功能是给上层协议提供透明的数据包发送和接收接口. 当上层ARP或IP需要发送数据包时,它将调用网络协议接口层的dev_queue_ ...

  8. Atitit.软件开发概念(11)--网络子系统--url编码 空格问题URLEncoder java js php

    Atitit.软件开发概念(11)--网络子系统--url编码 空格问题URLEncoder java js php 1. RFC2396标准 including HTML 4.01 section  ...

  9. 网络子系统42_ip协议处理函数_数据帧的接收

    //向协议栈注册l3处理函数 1.1 void dev_add_pack(struct packet_type *pt) { int hash; //ptype_all ptype_base共用一把锁 ...

随机推荐

  1. 【Java多线程与并发库】4.传统线程同步通信技术

    我们先通过一道面试题来了解传统的线程同步通信. 题目:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次,请写出程序. 我没有看答案, ...

  2. bcov进行覆盖率统计

    kcov是在bcov基础上进行的,bcov已经很久没有维护了: 首先需要下载依赖库libdwraft,然后在configure时候进行指定: ./configure --with-libdwarf=/ ...

  3. 《深入剖析Tomcat》阅读(一)

    第一章 一个简单的Web服务器 该应用程序仅接受位于指定目录的静态资源的请求,如HTML文件和图像文件.它也可以将传入的HTTP请求字节流显示到控制台上.但是,它并不发送任何头信息到浏览器,如日期或者 ...

  4. 非常好用的正则表达式"\\s+" - 匹配任意空白字符

    说起来,博主使用过的正则场景虽然不多,但是就是在这当中,我发现"\\s+"真好用! 详解 "\\s+" 正则表达式中\s匹配任何空白字符,包括空格.制表符.换页 ...

  5. Python如何进行cross validation training

    以4-fold validation training为例 (1) 给定数据集data和标签集label 样本个数为 sampNum = len(data) (2) 将给定的所有examples分为1 ...

  6. Coursera《machine learning》--(2)单变量线性回归(Linear Regression with One Variable)

    本笔记为Coursera在线课程<Machine Learning>中的单变量线性回归章节的笔记. 2.1 模型表示 参考视频: 2 - 1 - Model Representation ...

  7. 菜鸟的ubuntu学习笔记

    初识ubuntu感觉这个系统绝对够高大上,简洁的桌面,流畅的操作界面,在加上神秘的终端控制,突然感觉自己的世界真的好渺小,所以我下定决心在接下来的日子里我要告别windows,把ubuntu学好,尝试 ...

  8. JSP环境配置

    为免以后忘记,记下了. Jdk在C盘,tomcat在D盘. 1.JAVA_HOME C:\Program Files\Java\jdk1.7.0_07 2.CATALINA_HOME D:\apach ...

  9. Cocos2d-x 2.0以上版本安装方法

    1,cd 到2dx根目录,MAC平台使用./create-multi-platform-projects.py  然后提示: -bash: ./create-multi-platform-projec ...

  10. SKProductsRequest ios 7不调用delegate

    在iOS7中,内购只能在真机上才会调用 - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProdu ...