//调用路径ip_rcv->ip_rcv_finish->dst_input->(skb->dst->input)
//ip_forward以回调函数的形式,保存在skb->dst->input,input在通过ip_route_input路由封包时被设置
//函数的主要任务:
// 1.更新ttl
// 2.如果路由被重定向,则向发送方发送icmp重定向报文
// 2.如果有选项,通过ip_forward_options处理在转发时需要更新的选项
// 3.通过ip_output,将报文传递到ip发送路径上
//skb的下一跳信息,在ip_rcv->ip_rcv_finish中,已经通过ip_route_input设置,保存在skb->dst中
1.1 int ip_forward(struct sk_buff *skb)
{
struct iphdr *iph;
struct rtable *rt;
struct ip_options * opt = &(IPCB(skb)->opt);//ip协议控制块,由ip_options_compile初始化 //XFRM是 Linux 2.6 内核为安全处理引入的一个可扩展功能框架
if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
goto drop;
//
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))//对router alert选项的处理
return NET_RX_SUCCESS; if (skb->pkt_type != PACKET_HOST)//重复检查,l2地址非本机,直接丢弃
goto drop; skb->ip_summed = CHECKSUM_NONE;//需要软件重新计算校验和 iph = skb->nh.iph; if (iph->ttl <= 1)//数据帧到期
goto too_many_hops;
//XFRM路由
if (!xfrm4_route_forward(skb))
goto drop; iph = skb->nh.iph;
rt = (struct rtable*)skb->dst;
//处理严源路由选项
//rt->rt_dst 目的ip地址
//rt->rt_gateway 当目的主机直连时,rt_gateway匹配目的地址,当需要通过网关到达目的地址时,rt_gateway设置为下一条网关
//skb->dst在ip_options_rcv_srr中,使用选项中指定的ip地址作为目标地址,通过路由查找,设置为第一个非本机ip的单播路由项
if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)//由于是严源路由选项,因此下一跳地址,必须等于源路由选项中列出的地址
goto sr_failed; if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))//检查skb是否被共享,如果被共享的话,拷贝一份,并在头部预留l3+l2
goto drop;
iph = skb->nh.iph;
//递减跳数
ip_decrease_ttl(iph);
//RTCF_DOREDIRECT在ip_route_input_slow中被设置,表示ICMP_REDIRECT必须被送回源地址
if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr)
ip_rt_send_redirect(skb); skb->priority = rt_tos2priority(iph->tos);//通过tos字段计算skb在规则队列中的优先级 return NF_HOOK(PF_INET, NF_IP_FORWARD, skb, skb->dev, rt->u.dst.dev,
ip_forward_finish); sr_failed: icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);
goto drop; too_many_hops:
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
kfree_skb(skb);
return NET_RX_DROP;
}
//调用路径ip_forward->netfilter hooks->ip_forward_finish
1.2 static inline int ip_forward_finish(struct sk_buff *skb)
{
struct ip_options * opt = &(IPCB(skb)->opt); IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS); if (unlikely(opt->optlen))
ip_forward_options(skb);//处理宽松路由选项和time stamp选项 return dst_output(skb);//调用ip_output
}
//处理在转发时需要更新的选项
// 1.record route选项
// 2.源路由选项
// 3.time stamp选项
1.3 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;
//record route选项
if (opt->rr_needaddr) {//说明为出口skb,因为入口skb在ip_options_compile已经被使用首选源地址填写
optptr = (unsigned char *)raw + opt->rr;
ip_rt_get_source(&optptr[optptr[2]-5], rt);
opt->is_changed = 1;
}
if (opt->srr_is_hit) {//表示在源路由选项列表中,有可用的下一跳ip地址
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);
skb->nh.iph->daddr = rt->rt_dst;//更新目的地址为下一跳的地址
optptr[2] = srrptr+4;//更新选项的ptr[2]指针到下一跳
} 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) {
opt->is_changed = 0;
ip_send_check(skb->nh.iph);//计算ip校验和
}
}

网络子系统46_ip协议数据帧的转发的更多相关文章

  1. 网络子系统48_ip协议数据帧的发送

    //ip协议与l4协议接口,l4通过此接口向下l3传递数据帧 //函数主要任务: // 1.通过路由子系统路由封包 // 2.填充l3报头 // 3.ip分片 // 4.计算校验和 // 5.衔接邻居 ...

  2. 网络的分层协议总结(转发:https://www.cnblogs.com/Zhang-wj/p/5907534.html)

    网络的分层协议总结 OSI七层模型OSI 中的层            功能                                                        TCP/IP ...

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

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

  4. 网络子系统54_ip协议分片重组_定位ipq

    //为分片确定正确的ipq结构 // 定位5元组 // 1.<id, 源ip, 目的ip, l4协议> 可通过ip报文获取 // 2.user 通过ip_defrag给出,指出重组是由谁发 ...

  5. 网络子系统55_ip协议分片重组_加入ipq

    //ip分片加入到正确的ipq结构 //调用路径:ip_defrag->ip_frag_queue // 处理过程: // 1.正在被释放的ipq,不处理新加入的分片(ipq正在被释放由last ...

  6. 网络子系统45_ip协议tos处理

    //ip报头tos字段,一个字节 // 二进制位:[0 1 2] [3] [4] [5] [6] [7] // 1.[0 1 2] 表示优先级: // 000 路由 // 001 优先级 // 010 ...

  7. 网络子系统53_ip协议分片重组_内存阈值

    //调用路径:ip_defrag->ip_evictor // 分片重组时,可使用内存上下限: // 1.sysctl_ipfrag_high_thresh 可用内存上限 // 2.sysctl ...

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

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

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

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

随机推荐

  1. 数据库——IN、ANY、SOME 和 ALL 操作符的使用

    sql中all,any,some用法 简介: --All:对所有数据都满足条件,整个条件才成立,例如:5大于所有返回的id select * from #A where 5>All(select ...

  2. [转]RosBridge小结

    1.rosbridge介绍 rosbridge(rosbridge_suite)是ros官方为开发者提供的一个用于非ros系统和ros系统进行交互通信的功能包.rosbridge主要包含两个部分,Ro ...

  3. Java-jdbc工具类DBUtils

    创建项目: 导入相应jar包: 看上图. JDBCUtil.java获取数据库连接文件: package com.gordon.jdbcutil; import java.io.InputStream ...

  4. spring配置事务 元素 "tx:annotation-driven" 的前缀 "tx" 未绑定

    在进行spring与mybatis整合时,启动项目报错,控制台提示“元素 "tx:annotation-driven" 的前缀 "tx" 未绑定”. 经过查找, ...

  5. Specified VM install not found: type Standard VM, name jdk1.6...

    运行ant的时候出现 Specified VM install not found: type Standard VM, name.... 搞了好久..汗汗 尝试删除这些文件: ... / .meta ...

  6. (转)SDL2.0在mfc窗口中显示yuv的一种方法

    DWORD ThreadFun() {    //用mfc窗口句柄创建一个sdl window    SDL_Window * pWindow = SDL_CreateWindowFrom( (voi ...

  7. 说一下zoom:1的原理,万一被问到呢?

    某一天.前同事低着头从鹅厂面试回来.他说他被一道非经常见的问题难倒了. 对方问他知道zoom:1的作用吗? 前同事:清楚浮动啊,触发haslayout. 再问:那你知道zoom:1的工作原理和来龙去脉 ...

  8. 关于VS2012连接MySql数据库时无法选择数据源

    您的C#开发工具是用VS2012吗?    No! return;    您的数据库用的是MySql吗?     No! return;    您新建ADO.NET数据实体模型的时候选择数据源的时候没 ...

  9. linux常用命令中篇

    1.打印当月的日期

  10. c++ mktime()

    今天联系写一个日历的程序,需要算出月份中的第一天是星期几,用到了mktime()这个函数,感觉这个函数挺有用的,分享给大家. 原型:time_t mktime(struct tm *) 其中的tm结构 ...