//选项格式:
// 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. js学习--DOM操作详解大全二(window对象)

    一.window - 计时器 1、setTimeout()可以用来在指定的时间之后单次调用函数.setTimeount(f,1000);//一秒后调用函数fclearTimeout();取消函数的执行 ...

  2. CANoe 入门 Step by step系列(一)基础应用【转】

    CANoe是Vector公司的针对汽车电子行业的总线分析工具,现在我用CANoe7.6版本进行介绍,其他版本功能基本差不多. 硬件我使用的是CAN case XL. 1,CANoe软件的安装很简单,先 ...

  3. Python 学习之urllib模块---用于发送网络请求,获取数据

    1.urllib urllib是Python标准库的一部分,包含urllib.request,urllib.error,urllib.parse,urlli.robotparser四个子模块. (1) ...

  4. 关于java reflect

    反射的基石 Class类 对比提问: Person类代表人,它的实例对象就是张三,李四这样一个个具体的人, Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class.对比 ...

  5. wordpress 如何从后台数据库修改theme(图文教程)

    我们在wordpress主题theme配置的时候,会从网站上下载比较流行的theme,使自己的blog看着很酷,也有不顺利的时候,你下载的theme有bug或者下载包出问题了,安装过后你的web页面不 ...

  6. unity Character Controller 点滴

    unity Character Controller  点滴 1.今天在做角色的时候,发现人物跳不起来,原来设置这个属性即可,Step Offset, 这个是台阶的高度,这个值设置的越大,人物爬的越高 ...

  7. 提升 DevOps 效率,试试 ChatOps 吧!

    本文翻译自文章 To Boost DevOps, Try ChatOps,文中用简单易懂的方式介绍了 ChatOps 的发展和价值,由 OneAPM 工程师编译整理. 当我们谈论 DevOps 时,总 ...

  8. Hibernate中对象的三种状态以及Session类中saveOrUpdate方法与merge方法的区别

    首先,用一张图说明一个对象,在Hibernate中,在调用了不同方法之后对象所处的不同状态 在Hibernate中,一个对象的状态可以被分为如图所示的三种 Transient:瞬时对象,该对象在数据库 ...

  9. Linux 关机命令 重启命令

    Linux centos重启命令: 1.reboot2.shutdown -r now 立刻重启(root用户使用)3.shutdown -r 10 过10分钟自动重启(root用户使用)4.shut ...

  10. nginx -- nginx平台初探(100%)

    初探nginx架构(100%) 众所周知,nginx性能高,而nginx的高性能与其架构是分不开的.那么nginx究竟是怎么样的呢?这一节我们先来初识一下nginx框架吧. nginx在启动后,在un ...