linux内核学习笔记------ip选项处理(二)
来源:互联网 发布:虫虫大作战刷气球软件 编辑:程序博客网 时间:2024/05/17 22:13
在以前的笔记中讲过ip数据报的处理,里面提到过ip_rcv_finish这个函数,这个函数会调用ip_rcv_options来解析并处理iP首部中的ip选项。
if (iph->ihl > 5 && ip_rcv_options(skb))goto drop;在ip_rcv_finish中会判断ip首部长度是否大于5,只有首部长度大于20的情况下才会有ip选项,并调用ip_rcv_options处理;
static inline int ip_rcv_options(struct sk_buff *skb){......if (skb_cow(skb, skb_headroom(skb))) {IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);goto drop;}opt = &(IPCB(skb)->opt);opt->optlen = iph->ihl*4 - sizeof(struct iphdr);if (ip_options_compile(dev_net(dev), opt, skb)) {IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INHDRERRORS);goto drop;}if (unlikely(opt->srr)) {struct in_device *in_dev = in_dev_get(dev);if (in_dev) {if (!IN_DEV_SOURCE_ROUTE(in_dev)) {if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())printk(KERN_INFO "source route option %pI4 -> %pI4\n", &iph->saddr, &iph->daddr);in_dev_put(in_dev);goto drop;}in_dev_put(in_dev);}if (ip_options_rcv_srr(skb))goto drop;}return 0;drop:return -1;}首先会确保数据报足够的头部空间
紧接着会获取skb中ip选项,以及ip选项的长度
接着调用ip_options_compile()解析skb的IP首部中IP选项到skb的cb中,IP层的私有数据cb为一个IP选项信息块。
如果存在源路由选项,并且系统允许接收带源路由选项的数据包就会调用ip_options_rcv_srr处理,否则就会丢弃该报文;
下面来看下ip_options_compile是怎么解析ip选项报文的
ip_options_compile()会被两个函数调用:ip_options_get_finish()和 ip_rcv_options(),分别对应了发送和接收两个方向。注意: 在发送时的调用方式是ip_option_compile(opt,NULL),而在接收 时其调用语句是ip_option_compile(NULL,skb),这是因为发送 和接收时,待解析的IP选项以及解析后的IP选项信息块 * 所存储的位置是不同的--发送时IP选项时存储在参数opt 的__data字段起始的区域中,解析得到的信息会保存在opt中;接收时IP选项存储在参数skb中指向的IP首部中, 解析得到的信息则保存在SKB的cb中。因此,opt和skb两者不能同时为NULL
int ip_options_compile(struct net *net, struct ip_options * opt, struct sk_buff * skb){......if (skb != NULL) {rt = skb_rtable(skb);optptr = (unsigned char *)&(ip_hdr(skb)[1]);} elseoptptr = opt->__data;......for (l = opt->optlen; l > 0; ) {switch (*optptr) { case IPOPT_END:for (optptr++, l--; l>0; optptr++, l--) {if (*optptr != IPOPT_END) {*optptr = IPOPT_END;opt->is_changed = 1;}}goto eol; case IPOPT_NOOP:l--;optptr++;continue;}switch (*optptr) { case IPOPT_SSRR: case IPOPT_LSRR:/* * 校验待处理源路由选项的 * 长度是否有效。 */if (optlen < 3) {pp_ptr = optptr + 1;goto error;}/* * 校验待处理源路由选项的指针值 * 是否有效 */if (optptr[2] < 4) {pp_ptr = optptr + 2;goto error;}/* NB: cf RFC-1812 5.2.4.1 *//* * IP选项信息块opt中源路由选项 * 若已处理过,则无需再处理。 */if (opt->srr) {pp_ptr = optptr;goto error;}/* * 显然这是针对发送的,先再次校验选项中 * 的指针及长度的有效性:对于选项指针值其 * 最小值为4,对于选项长度值,除了选项 * 类型、选项长度以及选项指针的三字节外, * 至少应该可以容纳一个IP地址,且扣除了 * 前面的三字节4字节对齐。作为发送方, * 应取出第一个地址作为下一跳地址,并在 * 路径列表中多于一个地址时,将剩余的所有 * 地址往前移动一个位置。 */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);}/* * 根据选项类型标识是不是严格源路由 * 选项,并记录源路由选项在IP首部中 * 的偏移量。 */opt->is_strictroute = (optptr[0] == IPOPT_SSRR);opt->srr = optptr - iph;break;......}这里只列出了源路由选项的处理。
在判断skb是否为空的时候就指定这是为发送构建ip选项还是接收ip数据报时处理ip选项。如果skb为空说明是发送,这时是构建ip选项到参数opt的_data中。否则就是解析skb的cb;
紧接着就是一个循环处理ip选项,这个for循环是为接收ip报文解析skb报文中的ip选项进行处理;发送会在eol处退出这个函数。
解析ip选项的时候如果如果检测到选项列表结束符,则将后面所剩余的全部空间都设置为结束符,因为修改了选项内容,从而需要计算校验和,因此设置opt->is_changed为1,然后结束解析返回。如果检测到空操作符,则修改循环变量l,并将指针移动到下一个选项处后,直接跳入下一次循环。
而后面处理源路由选项的时候就需要对照源路由选项的格式看。这样容易理解点。源码中的注释也讲的比较清楚,这里就不罗嗦了。
ip_options_rcv_srr函数检查输入数据报中的源路由信息,并根据源路由选项更新ip数据报的下一跳地址
int ip_options_rcv_srr(struct sk_buff *skb){...... struct ip_options *opt = &(IPCB(skb)->opt);unsigned char *optptr = skb_network_header(skb) + opt->srr;struct rtable *rt = skb_rtable(skb);......if (!opt->srr)return 0;......for (srrptr=optptr[2], srrspace = optptr[1]; srrptr <= srrspace; srrptr += 4) {/* * 校验源路由选项的路径列表是否还能至少容纳 * 下一个IP地址值,如果不能,则给发送方发送 * 一个参数问题ICMP差错报文。 */if (srrptr + 3 > srrspace) {icmp_send(skb, ICMP_PARAMETERPROB, 0, htonl((opt->srr+2)<<24));return -EINVAL;}/* * 通过输入路由方式来判断是否抵达源路由选项中 * 的某一站,一旦确定本地为源路由选项中的某一 * 站,则获取下一跳的IP地址作为IP数据包的目的地址, * 并设置is_changed,表示该IP数据包作了修改。 */memcpy(&nexthop, &optptr[srrptr-1], 4);rt = skb_rtable(skb);skb_dst_set(skb, NULL);err = ip_route_input(skb, nexthop, iph->saddr, iph->tos, skb->dev);rt2 = skb_rtable(skb);if (err || (rt2->rt_type != RTN_UNICAST && rt2->rt_type != RTN_LOCAL)) {ip_rt_put(rt2);skb_dst_set(skb, &rt->u.dst);return -EINVAL;}ip_rt_put(rt);if (rt2->rt_type != RTN_LOCAL)break;/* Superfast 8) loopback forward */memcpy(&iph->daddr, &optptr[srrptr-1], 4);opt->is_changed = 1;}/* * 如果源路由选项的路径列表没有遍历完,则 * 说明该IP数据包的目的地址是从源路由选项 * 选出的,因此需设置srr_is_hit标志,待转发时 * 需要进一步处理。同时还需要设置is_changed * 标志,标识需重新计算IP数据包的首部校验和。 */if (srrptr <= srrspace) {opt->srr_is_hit = 1;opt->is_changed = 1;}}首先ip_options_rcv_srr会进行一些有效性的检查,最后会通过一个for循环来查找源路由选项,如果找到了。就根据找到的地址信息去路由表和路由缓存信息中查找,如果查找到就设置下一跳目的地址为源路由地址
udp和raw套接口都是通过控制信息来生成ip选项。通过ip_options_get函数完成这个功能,最后还是调用ip_options_compile来处理。
- linux内核学习笔记------ip选项处理(二)
- linux内核学习笔记------iP选项处理(一)
- linux内核学习笔记------ip数据报的输入处理
- linux内核学习笔记(二)
- linux内核学习笔记------ip报文组装
- Linux内核编译选项学习(二)(***General setup***)(综合整理)
- MFC学习笔记(二)处理命令行选项
- Linux内核分析 - 网络[十四]:IP选项
- Linux内核分析 - 网络[十四]:IP选项
- linux内核代码学习笔记(二)
- [LINUX内核编程]学习笔记(二)
- arm-linux内核启动学习笔记(二)
- linux内核学习笔记------ip报文的分片
- Linux 学习笔记(二) -----文件处理命令
- 《TCP/IP详解卷2:实现》笔记--IP选项处理
- Linux内核配置选项 (经典学习)
- Linux 内核网络协议栈 ------ Linux 内核路由机制(二)之 ip层开始 -> 直到包被处理
- Linux内核分析学习笔记:system_call中断处理过程
- Java Servlet
- 错误案例(QPaintDevice: Cannot destroy paint device that is being painted)
- scrapy 架构详解二
- 手把手教你使用PIC单片机驱动继电器
- GridView RowDataBund
- linux内核学习笔记------ip选项处理(二)
- web table 设置1px的边框?
- ros卸载
- Mysql常用命令行大全
- 黑马程序员《java基础总结(三)》(面向对象特点--继承)
- Tomcat7.0.27下安装Solr4.6.0
- JSP与JavaBeans
- Onvif开发之客户端搜索篇
- roscore出错 rosversion