ip选项处理(一)中讨论了syn包的发送过程对ip选项的处理,接下来分析接收syn并转发的过程

(2)路由节点接收syn到转发syn

我们知道ip层接收数据的函数调用过程是ip_rcv ---> ip_rcv_finish,在ip_rcv_finish中将会对ip选项进行处理

static inline int ip_rcv_finish(struct sk_buff *skb)

{

......

if (iph->ihl > 5 && ip_rcv_options(skb))//ip选项的处理

goto drop;

......

return dst_input(skb);

......

}static inline int dst_input(struct sk_buff *skb)

{

int err;

for (;;) {

err = skb->dst->input(skb);//skb->dst->input注册为ip_forward函数

......

}

}分析ip_rcv_options函数

static inline int ip_rcv_options(struct sk_buff *skb)

{

......

struct net_device *dev = skb->dev;

.......

if (ip_options_compile(NULL, skb)) {//解析ip选项各字段值,并保存在skb的cb中

IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);

goto drop;

}

opt = &(IPCB(skb)->opt);

if (unlikely(opt->srr)) {

struct in_device *in_dev = in_dev_get(dev);

if (in_dev) {

if (!IN_DEV_SOURCE_ROUTE(in_dev)) {//

#define IN_DEV_SOURCE_ROUTE(in_dev) (ipv4_devconf.accept_source_route && (in_dev)->cnf.accept_source_route)

可以看出如果accept_source_route为0,就丢弃该报;所以如果我们要支持源路径路由,则必须设置

echo 1 > /proc/sys/net/ipv4/conf/eth0/accept_source_route //网卡支持

echo 1 > /proc/sys/net/ipv4/conf/all/accept_source_route

内核中

accept_source_route - BOOLEAN

Accept packets with SRR option.

conf/all/accept_source_route must also be set to TRUE to accept packets

with SRR option on the interface

default TRUE (router)

FALSE (host)

if (IN_DEV_LOG_MARTIANS(in_dev) &&

net_ratelimit())

printk(KERN_INFO "source route option "

"%u.%u.%u.%u -> %u.%u.%u.%u\n",

NIPQUAD(iph->saddr),

NIPQUAD(iph->daddr));

in_dev_put(in_dev);

goto drop;

}

in_dev_put(in_dev);

}

if (ip_options_rcv_srr(skb))//检查输入数据报中的宽松源路由及严格源路由选项,并根据源路由

选项更新IP数据包的下一跳地址

goto drop;

}

return 0;

drop:

return -1;

}

int ip_options_rcv_srr(struct sk_buff *skb)

{

......

if (!opt->srr)//待处理的数据报中没有源路径路由选项,则返回

return 0;

if (skb->pkt_type != PACKET_HOST)

return -EINVAL;

if (rt->rt_type == RTN_UNICAST) {//在路由类型为RTN_UNICAST,即网关或直接连接的路由情况下执行严格路由是会有问题的。此时会发送一个参数错误ICMP差错报文给发送方,并返回参数无效错误

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;

for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {//

srrptr等于offset,srrspace为选项的长度,因为此时为路由节点,offset不是指向选项尾部,所以

optptr[1]-optptr[2]>0

if (srrptr + 3 > srrspace) {

icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));

return -EINVAL;

}

memcpy(&nexthop, &optptr[srrptr-1], 4);//设定下一跳地址,即为偏移量指定的ip地址

rt = (struct rtable*)skb->dst;

skb->dst = NULL;

err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);//根据下一跳来

查找路由,此时dst->input指向了ip_forward函数

rt2 = (struct rtable*)skb->dst;

if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {//skb->dst的下一跳被设置为ip选项中选择的下一跳地址;这个用于在转发时,设置数据包的目的地址时使用

ip_rt_put(rt2);

skb->dst = &rt->u.dst;

return -EINVAL;

}

ip_rt_put(rt);

if (rt2->rt_type != RTN_LOCAL)//rt2->rt_type==RTN_UNICAST,所以break

break;

/* Superfast 8) loopback forward */

memcpy(&iph->daddr, &optptr[srrptr-1], 4);//设定ip头的目的地址为下一跳地址

opt->is_changed = 1;//标识该数据报目的地址做了修改

}

if (srrptr <= srrspace) {

opt->srr_is_hit = 1;/如果源路由选项的路径列表没有遍历完,则说明该IP数据报的目的地址是从源路由选项选出的,因此需设置srr_is_hit标志,待转发时需要进一步处理。同时还需要设置is_changed,标识需重新计算IP数据报的首部校验和

opt->is_changed = 1;

}

return 0;

}ip_options_rcv_srr函数返回后,数据包的目的地址暂时未改为了下一跳的地址,dst->input设置为ip_forward函数,则会进入转发流程

int ip_forward(struct sk_buff *skb)

{

struct iphdr *iph;    /* Our header */

struct rtable *rt;    /* Route we use */

struct ip_options * opt    = &(IPCB(skb)->opt);//指向skb的cb中保存的ip options

......

rt = (struct rtable*)skb->dst;

if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)//如果是严格路由,并且目的地址和网关地址不一致,就会发送ICMP目的不可达报文,报告源路由失败;我们将在试验中提及

goto sr_failed;

......

return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,

ip_forward_finish);//调用ip_forward_finish函数

sr_failed:

/*

*    Strict routing permits no gatewaying

*/

icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);//发送ICMP目的不可达报文

goto drop;

......

}

static inline int ip_forward_finish(struct sk_buff *skb)

{

struct ip_options * opt    = &(IPCB(skb)->opt);//获得ip选项

IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);

if (unlikely(opt->optlen))

ip_forward_options(skb);//想转发的数据包添加所需的关于本地IP的信息,包括记录路由选项和时

间戳选项

return dst_output(skb);//在ip层发送出去

}

void ip_forward_options(struct sk_buff *skb)

{

struct ip_options * opt    = &(IPCB(skb)->opt);

unsigned char * optptr;

struct rtable *rt = (struct rtable*)skb->dst;

unsigned char *raw = skb->nh.raw;

if (opt->rr_needaddr) {

optptr = (unsigned char *)raw + opt->rr;

ip_rt_get_source(&optptr[optptr[2]-5], rt);//如果需要记录IP地址,则获取本地地址并设置到

IP记录路由选项中

opt->is_changed = 1;

}

if (opt->srr_is_hit) {//如果目的地址是从源路由选项指定的,则还需要判断输出路由缓存的目的地址是否存在于路由选项中。

int srrptr, srrspace;

optptr = raw + opt->srr;

for ( srrptr=optptr[2], srrspace = optptr[1];

srrptr <= srrspace;

srrptr += 4

) {

if (srrptr + 3 > srrspace)

break;

if (memcmp(&rt->rt_dst, &optptr[srrptr-1], 4) == 0)

break;

}

if (srrptr + 3 <= srrspace) {

opt->is_changed = 1;

ip_rt_get_source(&optptr[srrptr-1], rt);//将本地地址加入到ip选项中第一个ip地址之前

skb->nh.iph->daddr = rt->rt_dst;//数据包的目的地址被设置为下一跳地址;rt->rt_dst在函数ip_options_rcv_srr被设置为下一跳地址

optptr[2] = srrptr+4;//偏移量指向下一跳地址

} else if (net_ratelimit())

printk(KERN_CRIT "ip_forward(): Argh! Destination lost!\n");

if (opt->ts_needaddr) {

optptr = raw + opt->ts;

ip_rt_get_source(&optptr[optptr[2]-9], rt);

opt->is_changed = 1;

}

}

if (opt->is_changed) {//ip头的目的地址被修改后,就必须重新计算校验和

opt->is_changed = 0;

ip_send_check(skb->nh.iph);

}

}

下面通过两个实验来说明:

实验一(LSRR):

客户端:192.168.18.73 路由节点1:192.168.18.71 路由节点2:192.168.18.72 服务器端:192.168.18.76 18.71上18.0网段有个网关192.168.18.79

1.在18.71上抓包(接收并转发syn)

上图为接收的syn包

上图为转发的syn包

2.在18.72上抓包(接收和转发syn)

上图为接收的syn包

上图为转发的syn包

同样在18.71设置18.79网关时,不影响宽松路由功能

实验二(SSRR)

偏移量的改变以及ip选项中路径的改变和LSRR类似

严格路由时,如果路由节点的网关和ip选项中下一跳不一致时,就会发送icmp目的不可达报文

我在18.71上设置18.79网关,18.71不会转发syn而是发送ICMP目的不可达报文

linux ip rcv处理,linux ip选项处理(二)相关推荐

  1. 数据连接linux网络编程之TCP/IP基础(四):TCP连接的建立和断开、滑动窗口

    在写这篇文章之前,xxx已经写过了几篇关于改数据连接主题的文章,想要了解的朋友可以去翻一下之前的文章 一.TCP段格式: TCP的段格式如下图所示 源端口号与目标端口号 源端口号和目标端口号,加上IP ...

  2. linux ip别名和辅助ip地址

    转:https://blog.csdn.net/xiewen99/article/details/54729112?utm_source=itdadao&utm_medium=referral ...

  3. linux之路由知识之ip route 命令中的疑惑

    1.基础知识 1.1 路由 (Routing) 1.1.1 路由策略 (使用 ip rule 命令操作路由策略数据库) 基于策略的路由比传统路由在功能上更强大,使用更灵活,它使网络管理员不仅能够根据目 ...

  4. linux包之iproute之ip命令

    [root@localhost ~]# rpm -qf /sbin/ip iproute-2.6.32-31.el6.x86_64 ip 是个命令, ip 命令的功能很多!基本上它整合了 ifconf ...

  5. linux下IPROTO_TCP,TCP/IP协议栈在Linux内核中的运行时序分析

    可选题目三:TCP/IP协议栈在Linux内核中的运行时序分析 在深入理解Linux内核任务调度(中断处理.softirg.tasklet.wq.内核线程等)机制的基础上,分析梳理send和recv过 ...

  6. Linux TCP/IP网络协议栈:IP协议源码分析

    目录 IP协议简介 IP头部 IP数据包的发送 IP数据包的接收 https://mp.weixin.qq.com/s/8WNcTxtD4DBcNtcrR8nz4Q IP协议 是网络的最重要部分,毫不 ...

  7. 5 个用于在 Linux 终端中查找域名 IP 地址的命令

    5 个用于在 Linux 终端中查找域名 IP 地址的命令 本教程介绍了如何在 Linux 终端验证域名或计算机名的 IP 地址.本教程将允许你一次检查多个域.你可能已经使用过这些命令来验证信息.但是 ...

  8. linux用什么命令查看ip,Linux中ip命令的使用实例

    导读 在以前的Linux系统版本中,我们一直使用' ifconfig '命令查看ip地址等信息.但是"ifconfig"已经不再被维护,并在近几年的Linux版本中已经被弃用. i ...

  9. linux下dhcp服务器分配出去的IP地址及剩余IP地址

    我认为有2种办法 1.你可以查看防火墙信息,如图,可以清晰的看到,192.1681.200的IP给了哪个MAC地址 2. /var/lib/dhcpd/dhcpd.leases  这个文件专门记录了D ...

最新文章

  1. C++ 中的卷积神经网络 (CNN)
  2. C#使用ExecuteReader返回DataReader既有查询结果集又有输出参数或返回值的使用注意事项...
  3. MySQL查询连接数
  4. python实现手机号归属地相关信息查询
  5. 软件项目管理第一课—IT项目管理实战案例介绍
  6. 六、3D-3D ICP问题线性SVD解法与非线性BA解法
  7. Python自省 type(),dir(),getattr(),hasattr(),isinstance().
  8. Clojure 的 Enlive 库尝试
  9. 磁珠 符号_关于PCB原理图中的FB-FB是磁珠的符号-电子元器件-电路图
  10. 平安夜关于苹果的题目——1705. 吃苹果的最大数目
  11. 华为路由器DHCP服务设置(一)
  12. BoomBeach海水效果实现
  13. shader篇-处理复杂光照
  14. 15.6. footnote 脚注
  15. android怎样传图片到mysql中
  16. 【宝塔邮局管理器】使用教程、Email配置
  17. jQuery File Upload
  18. Awake框架简介---分页
  19. web自动化测试笔记
  20. AlertDialog基本使用

热门文章

  1. 一分钟详解VS中快速生成dll和lib方法
  2. js调整数组某些元素到指定位置顺序_Js数组里剔除指定的元素(不是指定的位置)...
  3. OpenCV java 图像基本处理-模糊 (8)
  4. maven项目首页显示的问题
  5. IOS中Cell自定义
  6. CLASSPATH的作用
  7. 作物驯化与人类的生活
  8. 解决pandas:ValueError: Cannot convert non-finite values (NA or inf) to integer
  9. R语言编写自定义描述统计计算函数、使用doBy包的summaryBy函数计算不同分组(group)的描述性统计值(Descriptive statistics by group、样本个数、均值、标准)
  10. R语言head函数和tail函数获取dataframe、列表list、向量vector的头部和尾部数据:tail提取数据对象的尾部数据、head提取数据对象的头部数据、默认6条数据、自定义设置返回条数