这次主要介绍下forward和local delivery。 
上次我们提到当ip_rcv_finish完成后后调用相关的发送函数ip_forward或者ip_local_deliver.这次就主要介绍这两个函数。 
先来看forward。 
forward一般由下面几部组成: 
1 执行ip option 
2 确定这个包能被forward 
3 减小ttl,当ttl为0时,丢掉这个包 
4 如果需要,则将这个包切片 
5 发送包到输出网络设备

这里要注意,如果包由于一些原因,不能被forward,则必须发送ICMP消息到发送主机。

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);//gso相关设置if (skb_warn_if_lro(skb))goto drop;//xfrm(ipsec)的相关检测if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))goto drop;//判断是否有Router_alter option(也就是保存发送端的ip),如果有的话,调用ip_call_ra_chain处理,当空间已满,则返回false,并继续处理。if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))return NET_RX_SUCCESS;//判断这个包是否是由本地主机的2层进行接受的。在2层设置帧的类型,当帧的目的地址就是本机2层地址的时候,skb->pkt_type设置为PCAKET_HOST.if (skb->pkt_type != PACKET_HOST)goto drop;//由于是forward,因此我们不需要在意4层的校验。设置ip_summed为CHECKSUM_NONE。skb_forward_csum(skb);/**   According to the RFC, we must first decrease the TTL field. If* that reaches zero, we must reply an ICMP control message telling*   that the packet's lifetime expired.*///ttl小于1,此时丢掉这个包if (ip_hdr(skb)->ttl <= 1)goto too_many_hops;//ipsec的检测if (!xfrm4_route_forward(skb))goto drop;//得到路由表rt = skb->rtable;//判断是否是Strict源路由option。如果是的话,看源路由option所制定的路由能否和rt_gateway(下一跳)匹配。if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway)goto sr_failed;//检测一些相关域。如果出错,则发送icmp,并丢弃这个包if (unlikely(skb->len > dst_mtu(&rt->u.dst) && !skb_is_gso(skb) &&(ip_hdr(skb)->frag_off & htons(IP_DF))) && !skb->local_df) {IP_INC_STATS(dev_net(rt->u.dst.dev), IPSTATS_MIB_FRAGFAILS);icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,htonl(dst_mtu(&rt->u.dst)));goto drop;}//由于我们将要修改这个skb的一些东西(在下面的ip_forward_finish中),因此我们需要复制一个拷贝(主要是防止skb共享)if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))goto drop;iph = ip_hdr(skb);//减少ttlip_decrease_ttl(iph);//如果我们所找到的下一跳地址比请求的更好的话,源host现在将会收到一个ICMP REDIRESCT消息(只有当源host没有请求 source routing option时)if (rt->rt_flags&RTCF_DOREDIRECT && !opt->srr && !skb->sp)ip_rt_send_redirect(skb);//QOS的优先级设置skb->priority = rt_tos2priority(iph->tos);//最终返回netfilter的hook,这里我们还是暂时忽略netfilter,只关注ip_forward_finish.return NF_HOOK(PF_INET, NF_INET_FORWARD, skb, skb->dev, rt->u.dst.dev,ip_forward_finish);sr_failed:/**   Strict routing permits no gatewaying*/icmp_send(skb, ICMP_DEST_UNREACH, ICMP_SR_FAILED, 0);goto drop;too_many_hops:/* Tell the sender its packet died... */IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_INHDRERRORS);icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:kfree_skb(skb);return NET_RX_DROP;
}

下来看ip_forward_finish方法,它主要是用来处理一些剩下的options,并且ip_forward_options还会重新计算ip checksum,因为它会update一些ip头的域:

static int ip_forward_finish(struct sk_buff *skb)
{struct ip_options * opt    = &(IPCB(skb)->opt);IP_INC_STATS_BH(dev_net(skb->dst->dev), IPSTATS_MIB_OUTFORWDATAGRAMS);if (unlikely(opt->optlen))ip_forward_options(skb);//最终返回dst_output,这个虚函数最终调用skb->dst_output,如果是单播则是ip_output,如果是多播则是ip_mc_output.而且切片(如果有需要)也会在这个函数进行).这里还有一个neighboring subsystem的概念,我们后面会讲到。return dst_output(skb);
}

来看ip_local_deliver函数:

int ip_local_deliver(struct sk_buff *skb)
{/**    Reassemble IP fragments.*///如果有切片,则开始组包。if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))return 0;}//返回netfilter hook,最终会调用ip_local_deliver_finish.它最终会将数据包发送往4层。下一次我们会详细介绍这个函数。return NF_HOOK(PF_INET, NF_INET_LOCAL_IN, skb, skb->dev, NULL,ip_local_deliver_finish);
}

这里我们可以看到一个对比,那就是forward时,不需要组包,这是因为local delivery必须把完整的包发送往4层,而forward,不需要经过4层,就直接把帧发送出去。

最后我们来看下ip_local_deliver_finish函数片段:

static int ip_local_deliver_finish(struct sk_buff *skb)
{struct net *net = dev_net(skb->dev);__skb_pull(skb, ip_hdrlen(skb));/* Point into the IP datagram, just past the header. */skb_reset_transport_header(skb);rcu_read_lock();{int protocol = ip_hdr(skb)->protocol;int hash, raw;struct net_protocol *ipprot;resubmit://对raw socket进行处理。raw = raw_local_deliver(skb, protocol);hash = protocol & (MAX_INET_PROTOS - 1);ipprot = rcu_dereference(inet_protos[hash]);if (ipprot != NULL) {int ret;//...................................//将数据包交给已注册的高层协议的处理函数。ret = ipprot->handler(skb);if (ret < 0) {protocol = -ret;goto resubmit;}IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);} //..........................................}out:rcu_read_unlock();return 0;
}

linux下ip协议(V4)的实现(二)相关推荐

  1. linux下ip协议(V4)的实现(四)

    这次主要介绍的是ip层的切片与组包的实现. 首先来看一下分片好的帧的一些概念: 1 第一个帧的offset位非0并且MF位为1 2 所有的在第一个帧和最后一个帧之间的帧都拥有长度大于0的域 3 最后一 ...

  2. linux下ip协议(V4)的实现(三)

    这次我们来看数据包如何从4层传递到3层. 先看下面的图,这张图表示了4层和3层之间(也就是4层传输给3层)的传输所需要调用的主要的函数: 我们注意到3层最终会把帧用dst_output函数进行输出,而 ...

  3. linux下ip协议(V4)的实现(一)

    首先来看校验相关的一些结构: 1 net_device结构: 包含一个features的域,这个表示设备的一些特性(比如控制校验),下面的几个flag就是用来控制校验: #define NETIF_F ...

  4. Linux下ip route、ip rule、iptables的关系(转

    http://www.cnblogs.com/sammyliu/p/4713562.html(本文内容转自此篇博客) Linux下ip route.ip rule.iptables的关系(转) 1.基 ...

  5. linux下ip冲突检测 arp

    linux下ip冲突检测 ARP协议具体解释之Gratuitous ARP(免费ARP)

  6. Linux下IP地址两种修改方式的总结(IP地址、子网掩码、网关、DNS简介)

    目录 一.IP地址.子网掩码.网关.DNS简介 1.IP地址 2.子网掩码 3.网关 4.DNS 二.Linux下IP地址修改两种方式介绍(Centos7.6) 1.查看IP地址 2.修改配置文件修改 ...

  7. linux系统下的ip分片程序,Linux下IP分片与重组

    Linux下IP――分片与重组 原理介绍 为一个数据包片再次分片 为数据包分片和为数据包片再次分片之间的细微差别就在于网关处理MF比特的不同.但一个网关为原来为分片的数据包分片时,除了末尾的数据包片, ...

  8. Linux下使用WPS做office的二次开发

    Linux下使用WPS做office的二次开发 序 上个版本WPS在Linux上就已经支持二次开发了,可以直接去看官网相关的介绍.https://open.wps.cn/ 我们选择WPS的客户端进行二 ...

  9. Linux下多进程服务端客户端模型二(粘包问题与一种解决方法)

    一.Linux发送网络消息的过程 (1) 应用程序调用write()将消息发送到内核中 ( 2)内核中的缓存达到了固定长度数据后,一般是SO_SNDBUF,将发送到TCP协议层 (3)IP层从TCP层 ...

最新文章

  1. websphere部署项目报Result Maps collection already contains value for XXX
  2. Active Directory操作主机的转移 —图形操作
  3. 《软件设计精要与模式》第二版源代码
  4. Gradle实战:发布aar包到maven仓库
  5. 别以为程序员的工作就是写代码
  6. 火了!堪称神级的 Spring Boot 手册
  7. 从JSP WEB页面往数据库写入出现乱码的一种解决方法
  8. 轻量化卷积神经网络:SqueezeNet、MobileNet、ShuffleNet、Xception
  9. 大学计算机课代表竞选稿,课代表竞选演讲稿
  10. 软件需求说明书-总务办公管理系统
  11. Python官网无法打开解决方案
  12. 概率运算中C(k,n)是怎么算的啊? 比如C(6,3)等于几?怎么来的.
  13. Windows 下 VS 配置 OpenGL 环境
  14. 《刘擎西方现代思想讲义》读书笔记
  15. 【JavaScript】9.基本引用类型-原始值包装类型
  16. Spring Advice 有哪些类型?
  17. 97-ICMP 协议(端口不可达)
  18. LeGO-LOAM:Ubuntu20.04下的编译与运行
  19. 银河麒麟项目经验记录2
  20. 《WEB安全深度学习实战》笔记

热门文章

  1. linux内核arc4算法,linux内核中与进程相关的数据结构(基于linux-mainline-rc4)
  2. python 数据变化——n次多项式
  3. pandas使用dataframe直接绘图时,取消图例(legend)
  4. pandas to_sql保存数据到数据库后,添加自增长的主键ID(PRIMARY KEY)
  5. 解决pandas读取parquet报错ImportError:Unable to find a usable engine;tried using: ‘pyarrow‘, ‘fastparquet‘
  6. cefsharp异步抓取html5,winform插件cefsharp65最新版完美demo,完美flash、html5、和调用摄像头支持,部署就能用...
  7. zookeeper删除节点的权限_zookeeper权限管理
  8. [NOI2002]荒岛野人 数论
  9. jQuery 的属性操作方法
  10. [转载] 使用异步 I/O 大大提高应用程序的性能