Linux内核报文收发-L3 - Section 3. IP协议、邻居子系统主要是接收、转发和发送三部分
2017-07-11 08:18
2491 查看
http://zhaozhanxu.com/2016/07/14/Linux/2016-07-14-Linux-Kernel-Pkts_Processing3/
网卡驱动: ixgbe
注册TCP协议
继续注册UDP、RAW、PING
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/4995693884910325da29462b662667a7.jpg)
L3报文协议
如果没有查找到,则调用
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/192862079a0fa11e9737137ac615aff1.jpg)
L3报文处理流程
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/6df94d51bb0d60e19cd07968f511f770.jpg)
邻居子系统位置
Generic neighbouring interface(VFT),为上层协议提供了一个统一的输出接口
Generic neighbouring interface,为下层提供的也是一个统一的接口
ARP是为IPV4设计的地址解析协议,而ND(neighbour detect)则是为IPv6设计的。这些地址解析协议可以说是“嵌入”在邻居子系统里面,但是又可以自由灵活的拆卸,非常方便。
为了加速数据包的发送速度,会将路由表和邻居缓存进行绑定,这个绑定其实就是把邻居缓存中的每个项的结构体嵌入路由表中的一个路由项中,这样报文在查找到路由以后,其实也相当于已经在邻居子系统中查到了缓存,减少了查找的次数。这样在进入邻居子系统后的处理流程就很短。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/fe7289b5c4ce01a139270b069cc65c9d.jpg)
邻居子系统数据结构
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/0df4654c7bb402f2bfe286a8458b1df9.jpg)
邻居子系统状态机
邻居项存在一种状态机,邻居项都有一个对于管理和维护邻居表来说非常重要的成员,nud_state,用来表示该邻居项当前所处的状态。下面依依介绍这几个状态:
NUD_NONE:邻居项刚建立时处于的状态,在该状态下,还没有硬件地址可以用,所以还不能发送请求报文。一旦有报文要输出到该邻居,便会出发对该邻居硬件地址的请求,进入NUD_INCOMPLETE状态,并缓存发送的报文。
NUD_INCOMPLETE:该状态是请求报文已发送,但尚未收到应答的状态。该状态下还没解析到硬件地址,因此尚无可用硬件地址,如果有报文要输出到该邻居,会将其缓存起来。这个状态会启动一个定时器,如果在定时器到期时还没有接收到邻居的回应,则会重复发送请求报文,否则发送请求报文的次数打到上限,便会进入NUD_FAILED。
NUD_REACHABLE:该状态以及得到并缓存了邻居的硬件地址。进入该状态首先设置邻居项相关的output函数(该状态使用neighbors_ops结构的connectd_outpt),然后查看是否存在要发送给该邻居的报文。如果在该状态下闲置时间达到上限,便会进入NUD_STATLE。
NUD_STATLE:该状态一旦有报文要输出到该邻居,则会进入NUD_DELAY并将该报文输出。如果在该状态下闲置时间达到上限,且此时的引用计数为1,则通过垃圾回收机制将其删除,在该状态下,报文的输出不收限制,使用慢速发送过程。
NUD_DELAY:该状态下表示NUD_STATE状态下发送的报文已经发出,需得到邻居的可达性确认的状态。在为接收到邻居的应答或确认时也会定时地重发请求,如果发送请求报文的次数到上限,如果收到邻居的应答,进入NUD_REACHABLE,否则进入NUD_FAILED,在该状态下,报文的输出不收限制,使用慢速发送过程。
传输请求:向一台L2地址未知的主机传输请求,就需要对这个地址进行解析。
收到solicitation请求:收到这个请求的主机会假定两个系统有通信发生,会创建一个缓存项。
手工添加:手工添加一个邻居缓存项。
内核企图向一个不可到达的主机发送报文。
与这个邻居结构相关的L2地址改变了。
邻居结构存在的时间太长,内核需要它占用的内存。
更新状态不是
更新L2地址,给状态不是
设置一个新的链路层地址。
改变NUD状态。
处理
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/d82551d6554886e105f1c932738f1d97.jpg)
arp处理流程
版本说明
Linux版本: 3.10.103网卡驱动: ixgbe
网络协议注册
inet_init主要是注册各种协议
注册TCP协议
proto_register(&tcp_prot, 1)
继续注册UDP、RAW、PING
arp_init, ip_init, tcp_init, udp_init, ping_init, icmp_init
dev_add_pack(&ip_packet_type)主要是注册ip报文处理函数
ip_rcv到pttype_base。
arp_init-->dev_add_pack(&arp_packet_type)主要是注册arp报文处理函数
arp_rcv到pttype_base。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/4995693884910325da29462b662667a7.jpg)
L3报文协议
报文处理
网卡调用__netif_receivve_skb_core后,会调用
deliver_skb(skb, pt_prev, orig_dev)处理对应的3层协议函数。
ip协议
ip_rcv主要获取报文头,报文健康检查,最后进入
NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,...,ip_rcv_finish)。
ip_rcv_finish进行路由查找,
ip_route_input_noref-->ip_route_input_slow进行慢速路由判定。
ip_route_input_slow判定报文是本地的话,就给input装载
ip_local_deliver函数,如果不是本地继续调用
ip_mkroute_input-->__mkroute_input查找路由,并且给input装载
ip_forward函数,output装载
ip_output。
ip_local_deliver函数直接进入
NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN,..., ip_local_deliver_finish)。
ip_local_deliver_finish调用
ipprot->handler进入4层的协议处理函数。
ip_forward进入
NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,..., ip_forward_finish)。
ip_forward_finish调用output装载的
ip_output。
ip_output进入
NF_HOOK(NFPROTO_IPV4, NF_INET_POST_ROUTING,...,ip_finish_output)
ip_finish_output进行分片的判断和操作,此处涉及到GSO的判定,最后调用
ip_finish_output2。
ip_finish_output2调用
__ipv4_neigh_lookup_noref进行邻居子系统的表查找,ipv4主要是arp表,查找到arp表,则调用
dst_neigh_output-->neigh_hh_output-->dev_queue_xmit进行报文发送。
如果没有查找到,则调用
__neigh_create-->arp_constructor进行发送等函数的装载,最后也调用
dst_neigh_output,然后也调用装载的发送函数,将报文修改为request,调用
dev_queue_xmit进行发送。
arp协议
arp_rcv健康检查后,进入
NF_HOOK(NFPROTO_ARP, NF_ARP_IN, ..., arp_process)
arp_process完成了所有的处理操作,包括是reply报文则更新arp表;如果是request本地的话,则更新或者创建arp表,并且调用
arp_send回复arp报文;如果不是本地不支持arp proxy的话,丢弃报文;支持的话转发报文。
arp_send调用
arp_create创建报文,并且调用
arp_xmit发送报文。
arp_xmit进入
NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, ..., dev_queue_xmit_sk),最后调用
dev_queue_xmit发送报文。
转发流程
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/192862079a0fa11e9737137ac615aff1.jpg)
L3报文处理流程
邻居子系统
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/6df94d51bb0d60e19cd07968f511f770.jpg)
邻居子系统位置
Generic neighbouring interface(VFT),为上层协议提供了一个统一的输出接口
neigh->output()。
Generic neighbouring interface,为下层提供的也是一个统一的接口
dev_queue_xmit()。
ARP是为IPV4设计的地址解析协议,而ND(neighbour detect)则是为IPv6设计的。这些地址解析协议可以说是“嵌入”在邻居子系统里面,但是又可以自由灵活的拆卸,非常方便。
为了加速数据包的发送速度,会将路由表和邻居缓存进行绑定,这个绑定其实就是把邻居缓存中的每个项的结构体嵌入路由表中的一个路由项中,这样报文在查找到路由以后,其实也相当于已经在邻居子系统中查到了缓存,减少了查找的次数。这样在进入邻居子系统后的处理流程就很短。
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/fe7289b5c4ce01a139270b069cc65c9d.jpg)
邻居子系统数据结构
struct neigh_table:邻居表,每个地址解析协议就会创建这样的一个表(比如arp)
struct neighbour:邻居项代表一个邻居。邻居项用哈希表+链表链的组织方式
struct hh_cache:这个字段存的就是封装好的二层协议头部,每个报文在进入邻居子系统前都会查找路由,路由项中就会包含这
hh_cache这个结构
状态机
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/0df4654c7bb402f2bfe286a8458b1df9.jpg)
邻居子系统状态机
邻居项存在一种状态机,邻居项都有一个对于管理和维护邻居表来说非常重要的成员,nud_state,用来表示该邻居项当前所处的状态。下面依依介绍这几个状态:
NUD_NONE:邻居项刚建立时处于的状态,在该状态下,还没有硬件地址可以用,所以还不能发送请求报文。一旦有报文要输出到该邻居,便会出发对该邻居硬件地址的请求,进入NUD_INCOMPLETE状态,并缓存发送的报文。
NUD_INCOMPLETE:该状态是请求报文已发送,但尚未收到应答的状态。该状态下还没解析到硬件地址,因此尚无可用硬件地址,如果有报文要输出到该邻居,会将其缓存起来。这个状态会启动一个定时器,如果在定时器到期时还没有接收到邻居的回应,则会重复发送请求报文,否则发送请求报文的次数打到上限,便会进入NUD_FAILED。
NUD_REACHABLE:该状态以及得到并缓存了邻居的硬件地址。进入该状态首先设置邻居项相关的output函数(该状态使用neighbors_ops结构的connectd_outpt),然后查看是否存在要发送给该邻居的报文。如果在该状态下闲置时间达到上限,便会进入NUD_STATLE。
NUD_STATLE:该状态一旦有报文要输出到该邻居,则会进入NUD_DELAY并将该报文输出。如果在该状态下闲置时间达到上限,且此时的引用计数为1,则通过垃圾回收机制将其删除,在该状态下,报文的输出不收限制,使用慢速发送过程。
NUD_DELAY:该状态下表示NUD_STATE状态下发送的报文已经发出,需得到邻居的可达性确认的状态。在为接收到邻居的应答或确认时也会定时地重发请求,如果发送请求报文的次数到上限,如果收到邻居的应答,进入NUD_REACHABLE,否则进入NUD_FAILED,在该状态下,报文的输出不收限制,使用慢速发送过程。
操作
neigh_create:
创建一个neighbour项结构,当以下情况发生:传输请求:向一台L2地址未知的主机传输请求,就需要对这个地址进行解析。
收到solicitation请求:收到这个请求的主机会假定两个系统有通信发生,会创建一个缓存项。
手工添加:手工添加一个邻居缓存项。
neigh_release:
这个函数会减少neighbour的引用计数,当引用计数为0时,才真正删除neighbour结构,这个函数真正调用neigh_destroy。以下是函数调用的原因:内核企图向一个不可到达的主机发送报文。
与这个邻居结构相关的L2地址改变了。
邻居结构存在的时间太长,内核需要它占用的内存。
neigh_update:
更新neighbour状态和链路层地址。流程如下:更新状态不是
NUD_VALID态的邻居发生的变化,主要是状态机和neigh->output。
更新L2地址,给状态不是
NUD_VALID态的邻居使用。
设置一个新的链路层地址。
改变NUD状态。
处理
arp_queue队列。
arp例子
![](https://oscdn.geek-share.com/Uploads/Images/Content/202011/06/d82551d6554886e105f1c932738f1d97.jpg)
arp处理流程
相关文章推荐
- 真的懂了:TCP协议中的三次握手和四次挥手(关闭连接时, 当收到对方的FIN报文时, 仅仅表示对方不在发送数据了, 但是还能接收数据, 己方也未必全部数据都发送对方了。相当于一开始还没接上话不要紧,后来接上话以后得让人把话讲完)
- UDP协议发送数据:UDP协议接收数据:UDP协议收发数据注意事项
- 公司要卖我的EDI报文发送接收程序大家说开多少价格
- linux网络报文接收发送浅析
- fpga hdmi接收和发送部分调试
- Linux内核IP层的报文处理流程--从网卡接收的报文处理流程
- Linux内核分析 - 网络[二]:网卡驱动接收报文
- 使用C#进行点对点通讯和文件传输(发送接收部分)
- 基于UDP可靠传输协议UDT----剖析之发送和接收缓冲区
- JAVA TCP协议的发送接收
- Linux 网络代码导读(接收与发送部分)
- UDP协议接收数据与发送数据
- 802.11协议精读6:802.11b的发送过程与接收过程
- Java-TCP/IP 编程-01 主线程:从控制台录入消息,发送到服务器 子线程:接收服务器转发来的消息
- 深入理解Linux网络技术内幕——IPv4 报文的接收(转发与本地传递)
- mina框架中logger接收与发送报文被省略的解决办法
- HTTP报文内的HTTP信息之发送多种数据的多部分对象集合
- 如何限制部分Notes用户接收发送Internet邮件
- 报文的发送函数 (linux网络子系统学习 第十三节)
- [转]使用C#进行点对点通讯和文件传输(发送接收部分)