linux 网络协议栈变化,ZZ Linux网络协议栈学习

最近学习linux内核网络协议栈,把数据包接收流程大致理了一下,

前面也看了瀚海书香兄的总结,感觉总结的比我精炼,抓住了主干,是一目了然的那种

我的这篇本来是自己看得,因此把我自己学习中一些遇到的问题写了出来,可能其他人会觉得废话比较多,呵呵

另外,因为我看的书Understanding Linux Network Internal只讲了ip层及以下,

因此L4层的流程是我自己在代码中找的,不保证100%正确,

如果有错误,还希望大虾及时指出,防止误人子弟

em15.gif

NAPI驱动流程:

中断发生

-->确定中断原因是数据接收完毕(中断原因也可能是发送完毕,DMA完毕,甚至是中断通道上的其他设备中断)

-->通过netif_rx_schedule将驱动自己的napi结构加入softnet_data的poll_list链表,禁用网卡中断,并发出软中断

-->中断返回时触发软中断net_rx_action,从softnet_data的poll_list上取下刚挂入的napi结构,并且调用其

poll函数,这个poll函数也是驱动自己提供的,比如rtl8139网卡驱动中的rtl8139_poll等。

-->在poll函数中进行轮询,直到接受完所有的数据或者预算(budget)耗尽。每接收一个报文要分配skb,用eth_type_trans处理并交给netif_receive_skb。

-->如果数据全部接收完(预算没有用完),则重新使能中断并将napi从链表中取下。如果数据没接收完,则什么也不作,等待下一次poll函数被调度。

非NAPI流程:

中断发生

-->确定中断发生的原因是接收完毕。分配skb,读入数据,用eth_type_trans处理并且将skb交给netif_rx

-->在netif_rx中,将packet加入到softnet_data的input_pkt_queue末尾(NAPI驱动不使用这个

input_pkt_queue),再通过napi_schedule将softnet_data中的backlog(这也是个napi结构)加入

softnet_data的poll_list,最后发出软中断

-->软中断net_rx_action从poll_list上取下softnet_data的backlog,调用其poll函数,这个poll函数是内核提供的process_backlog

-->函数process_backlog从softnet_data的input_pkt_queue末尾取下skb,并且直接交给netif_receive_skb处理。

-->如果input_pkt_queue中所有skb都处理完则将backlog从队列中除去(注意input_pkt_queue中可能有多个网卡加入的报文,因为它是每cpu公用的)并退出循环;如果预算用完后也跳出循环。最后返回接受到的包数

总结:

NAPI和非NAPI的区别

1.NAPI使用中断+轮询的方式,中断产生之后暂时关闭中断然后轮询接收完所有的数据包,接着再开中断。而非NAPI采用纯粹中断的方式,一个中断接收一个数据包

2.NAPI都有自己的struct napi结构,非NAPI没有

3.NAPI有自己的poll函数,而且接收数据都是在软中断调用poll函数时做的,而非NAPI使用公共的process_backlog函数作为其poll函数,接收数据是在硬件中断中做的

4.NAPI在poll函数中接收完数据之后直接把skb发给netif_receive_skb,而非NAPI在硬件中断中接收了数据通过

netif_rx把skb挂到公共的input_pkt_queue上,最后由软中断调用的process_backlog函数来将其发送给

netif_receive_skb

驱动以及软中断这块对skb仅仅做了以下简单处理:

1.调用skb_reserve预留出2个字节的空间,这是为了让ip首部对齐,因为以太网首部是14字节

2.调用skb_put将tail指向数据末尾

3.调用eth_type_trans进行如下处理:

(1)将skb->dev指向接收设备

(2)将skb->mac_header指向data(此时data就是指向mac起始地址)

(3)调用skb_pull(skb, ETH_HLEN)将skb->data后移14字节指向ip首部

(4)通过比较目的mac地址判断包的类型,并将skb->pkt_type赋值PACKET_BROADCAST或PACKET_MULTICAST或者PACKET_OTHERHOST,因为PACKET_HOST为0,所以是默认值

(5)最后判断协议类型,并返回(大部分情况下直接返回eth首部的protocol字段的值),这个返回值被存在skb->protocol字段中

总结,结束后,skb->data指向ip首部,skb->mac_header指向

mac首部,skb->protocol储存L3的协议代码,skb->pkt_type已被设置,skb->len等于接收到的报文

长度减去eth首部长度,也就是整个ip报文的总长。其余字段基本上还是默认值。

netif_receive_skb

1.将skb->iif赋值为skb->dev->ifindex,将skb->network_header和

skb->transport_header都指向skb->data,也就是ip首部,然后skb->mac_len=skb-&

gt;network_header-skb->mac_header,正常情况下应该等于ETH_HLEN吧

2.向ptype_all中注册(通过dev_add_pack)的每一个packet_type调用一次deliver_skb,这里没有拷贝skb,只是先增加了一下skb->users

3.调用handle_bridge处理桥报文,如果该dev不是一个桥端口则直接返回

4.调用handle_macvlan处理vlan

5.对于每一个在ptype_base中注册的packet_type(也是用dev_add_pack),调用deliver_skb

6.如果没有任何一个注册的packet_type接受skb则直接kfree_skb并且返回NET_RX_DROP。否则返回最后一个pkt_type->func返回的值

总结,需要说一下dev_add_pack,这个函数根据传入的packet_type的type字

段决定加入哪个队列,如果是ETH_P_ALL就加入ptype_all,否则计算哈希值并加入ptype_base,通过这个函数注册的都是L3层的协

议,比如ip,arp,rarp,bootp等,其实还有packet协议族套接字的监听函数(除了ETH_P_ALL之外都加入ptype_base,

它们对应的接收函数是packet_rcv),这里对于ip来说,接受函数就是ip_rcv。

经过这个函数,又有几个字段发生变化:

network_header和transport_header都指向ip首部,mac_len为mac首部长度

ip_rcv:

1.丢弃所有pkt_type为OTHER_HOST的包,注意对于将网卡设为混杂模式的监听进程来说,这个包已经在netif_receive_skb中给它们发送了一份拷贝

2.检查skb是否被共享,如果被共享需要用skb_clone拷贝一份,因为后面要对skb的内容进行变更

3.常规检测:如果报文的长度小于ip首部最小长度,丢弃;如果ip协议字段不等于4丢弃;若ip首部长度字段小于5,丢弃;若ip首部长度小于ip首部

长度字段*4,丢弃;如果ip首部校验和出错,丢弃;如果skb->len(此时len为整个ip报文长度)小于ip首部总长字段,丢弃;如果ip

首部总长字段小于ip首部长度字段,丢弃;

4.注意第三步中skb->len是可以小于ip首部的总长字段的,因为根据代码注释,传输介质有可能在末尾添加了padding,在这种情况下,

会调用pskb_trim_rcsum将多余的结尾部分砍掉(通过把skb->tail往前移),并且还要将检查和无效化

5.此处调用NF_INET_PRE_ROUTING钩子函数

总结,ip_rcv主要进行的常规检查,唯一对skb进行操作的就是将结尾的填充字段砍掉。

ip_rcv_finish:

1.首先,如果skb->dst为空,说明还不确定这个ip报文的目的地是本机还是别的机器,这时通过ip_route_input来找到rtable并且赋给skb->rtable

2.如果ip首部长度字段大于5则调用ip_rcv_options处理ip选项。该函数调用ip_options_compile将选项全部处理放在

skb的cb字段中,作为一个struct

ip_options(还要详细看ip_options_compile)。如果有源站路由选项则检查设备是否支持源站路由(软件支持,可配置),则调用

ip_options_rcv_srr(此函数也还需认真看)填写源站路由。

3.添加统计信息并调用dst_input,dst_input只是调用skb->dst->input函数,这个skb->dst就

是前面用ip_route_input确定的,而根据dst类型的不同,这个input函数可能是ip_local_deliver或者

ip_forward,这里我们看ip_local_deliver。

总结,ip_rcv_finish改变了skb->dst字段(如果本来

skb->dst字段已经有值则不改变)和skb->cb字段(在ip_rcv_options中将ip首部选项编译之后放入cb)。

ip_options_compile可以改变报文内容,比如填写路由记录选项,填写时间戳选项等

ip_local_deliver

1.如果ip首部offset或者MF不为0,则调用ip_defrag进行ip分片的重组,ip_defrag只在成功完全重组了一个报文之后才会返回

0,其他情况都是返回非0,如果返回非0就会从ip_local_deliver返回。ip_defrag也比较复杂,需要细看,总体来说就是将分片放在

一个哈希表中,开启定时器,来一个分片就与前面属于同一ip报文的分片合并(两个分片是否属于同一个ip报文是通过ip的id字段,源目的地址,L4协议

等多个参数确定的,可参考ip4_frag_match)

2.钩子NF_INET_LOCAL_IN,并调用ip_local_deliver_finish

总结,从上两个函数可以看出NF_INET_PRE_ROUTING和

NF_INET_LOCAL_IN之间的区别,前者还没有经过路由处理,即skb->dst一般还没有确定,而后者是已经确定了

skb->dst且dst为本地地址,假如skb->dst不是本地地址则会调用ip_forward,这就不会触发

NF_INET_LOCAL_IN了。另外NF_INET_PRE_ROUTING尚未对ip分片进行合并处理,而NF_INET_LOCAL_IN抓到

的数据包是已经合并成的ip报文了

ip_local_deliver_finish

1.将skb->data继续移动指向传输层首部,并且将skb->transport_header也指向传输层首部,接下来开始处理

2.首先从ip首部取得传输层协议号,然后用这个协议号调用raw_local_deliver将skb传给raw_v4_hashinfo哈希表中的原始套接字协议

3.再利用protocol值作为下标取得inet_protos全局数组中的注册协议(对于tcp,udp,icmp分别是

tcp_protocol,udp_protocol,icmp_protocol)。如果找到了对应的协议处理结构,就把skb交给该结构的

handler函数处理(对于tcp,udp,icmp分别是tcp_v4_rcv,udp_rcv,icmp_rcv)。如果没找到对应的处理结构,则

回发一个icmp协议不可达的目的不可达报文,并释放skb。

总结:这里又一次移动了skb->data指针,将其指向传输层首部,同时设置了

transport_header也指向传输层首部。raw_v4_hashinfo和inet_protos都是一个256项的全局数组,以协议号为下

标保存了各个协议的处理结构。这两个数组就像是L4层的ptype_base,根据本层的协议号来决定处理函数

注意区别raw_v4_hashinfo和上面的ptype_all,前者是AF_INET的SOCK_RAW套接字注册的接收结构,而后者是

AF_PACKET套接字注册的接收结构,可见raw套接字是经过了ip层处理的而packet是在netif_receive_skb中接收的,尚未经

过任何处理,其中一个显著区别就是raw经过了ip_defrag而packet没有

对于udp来说,inet_protos中的结构是全局变量udp_protocol,它的handler函数是udp_rcv

udp_rcv所做的就是直接调用__udp4_lib_rcv(skb, udp_hash, IPPROTO_UDP);

__udp4_lib_rcv

此函数中会调用__udp4_lib_lookup_skb-->()__udp4_lib_lookup()来查找此udp包对应的socket,主要是查找源目的地址和端口号都符合的socket。

如果查找到了对应的socket,则调用udp_queue_rcv_skb将skb放入udp的接收队列,然后返回0

如果没有查找到对应的socket,要向源地址发送一个ICMP端口不可达消息

udp_queue_rcv_skb

它经过__udp_queue_rcv_skb(sk,

skb)-->__udp_queue_rcv_skb-->skb_queue_tail一系列调用过程将skb加入socket的接收队

列sk->sk_receive_queuek末尾。其中还要检测接收缓冲区是否已经满。

接着调用sk->sk_data_ready(sk, skb_len)通知socket有数据就绪,可以读了。一般情况下这个函数对应sock_def_readable,这个函数的功能就是唤醒在sk->sk_sleep上睡眠的进程

那么是谁在这里睡眠呢?在调用recvfrom系统调用接收报文的时候,会经过这样一个流程

sys_socketcall

-->sys_recvfrom

-->sock_recvmsg

-->__sock_recvmsg

-->sock->ops->recvmsg,这个sock->ops对应全局变量inet_dgram_ops,里面的recvmsg对应sock_common_recvmsg

-->sock_common_recvmsg

-->sk->sk_prot->recvmsg,这个sk->sk_prot对应全局变量udp_prot,里面的recvmsg对应udp_recvmsg

-->udp_recvmsg

-->__skb_recv_datagram

在__skb_recv_datagram中,会首先尝试从sk->sk_receive_queue上取下数据包,如果发现队列中没有数据包,则

开始在sk->sk_skeep上睡眠。而上面sock_def_readable唤醒的就是这里睡眠的进程。

可以看到,在__skb_recv_datagram中被唤醒后,函数又尝试从sk->sk_receive_queue上取下数据包,这时当然会

成功,成功之后返回到udp_recvmsg。udp_recvmsg再进行一些简单的检测之后就调用copy_to_user将数据拷贝到用户空间了

(其实这里并不是简单调用copy_to_user,还要处理很多情况,比如用户使用的msghdr可能包含多个iovec,skb可能有多个frags

等等)

这样,一个udp数据包就从网卡到达了用户的缓冲区

阅读(713) | 评论(0) | 转发(0) |

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/572697.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

delete删除

delete只能删除属性,不能删除变量和原型链上的属性 1,对象属性删除 var obj {name: mary } delete obj.name; console.log(obj.name); // undefined 2、删除数组中的元素,删除值,不能删除索引 var arr [1, 2, 3]; console.log(arr[0], arr…

linux安装各种文件格式,Embeded linux中的各类文件系统

Cramfs:(优点:只读,目的:不可更改文件、防入侵)1.下载cramfs-1.1源码工具http://files.cnblogs.com/files/pokerface/cramfs-1.1.tar.gz2.解压tar xvzf cramfs-1.1.tar.gz -C /home/liuzhengwu/tools3.编译make4.得到COPYING cra…

拯救者linux无法正常关机,Ubuntu无法关机解决办法

说明:如果不成功请参考一下文章最后的内容,也许会有帮助。其实不止在ubuntu里面,fedora里面我也遇到了这个问题,就是电脑可以重启,但是不能直接关机,否则就一直停在关机界面,需手动关机。郁闷很…

ES6 正则的扩展

1. RegExp构造函数 ES5中,RegExp构造函数的参数: 参数是字符串,这时第二个参数表示正则表达式的修饰符(flag)参数是一个正则表示式,这时会返回一个原有正则表达式的拷贝。但是,ES5不允许此时使用…

华为荣耀笔记本linux系统怎么样,荣耀笔记本和华为笔记本有什么区别

以荣耀Magic Book Pro和华为matebook 14为例,其区别如下:1、屏幕:荣耀Magic Book Pro屏幕尺寸为16.1英寸,显示比例为16比9,分辨率为1920乘以1080,华为matebook 14屏幕尺寸为14英寸,显示比例3比2…

初始Angularjs2

一、为什么使用Angularjs2 Angularjs2全面使用了模块化、组件化的思想,它有如下的特性: 1)模块化 在ng2的应用中,所有的系统功能都是模块化的,开发者只需要按需导入模块化的好处在于可以当应用加载时,是按需…

linux中输入ls出现蓝色的点,linux上ls的蓝色太深,怎么处理

linux下ls的蓝色太深,怎么办?在linux下使用ls命令时,会将目录以蓝色显示。在某些显示器上,黑底蓝字,看起来相当费力,以至于我有时候不得不使用dir命令,全部显示成白色。先看一个图:看…

四色着色问题 c语言编程,数据结构-图着色问题

7-38 图着色问题 (25 分)图着色问题是一个著名的NP完全问题。给定无向图G(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配…

HTML学习笔记4之第五章第六章

2017-03-02 完成第五章第六章的学习 第五章&#xff1a;为界面增加图像 浏览器处理图像的过程&#xff1a;先请求页面&#xff0c;若需要图片&#xff0c;在请求图片 图像的格式&#xff1a;JPEG&#xff0c;PNG&#xff0c;GIF&#xff0c;各自的特点和优势 内联元素<img&g…

c语言printf %llo,c++ - Printf疯狂了 - 堆栈内存溢出

你不能使用%d来打印long long 。 你必须使用%lld 。 (因此请使用"\\n%d %d | %lld %lld | %d %d"作为格式字符串。)特别是&#xff0c;显而易见的是&#xff0c;在“52 0 | 52 0”中&#xff0c;第一个52 0是a.rez &#xff0c;第二个52 0是b.rez (这些中的每一个都是…

js柯里化

function sum(x){console.log(x:x);var y function(x){ console.log(xx:x);console.log(y:y);return sum(xy) }y.toString y.valueOf function(){ return x; } console.log(yy:y);return y; } sum(1)(2)(3)(4);//10 转载于:https://www.cnblogs.com/yuri2016/p/6496486.ht…

用c语言随机获区100个整数,用rand() 函数去100 个随机的整数的有关问题

用rand() 函数去100 个随机的整数的问题int rand100 (((double) rand() / (double) RAND_MAX) * RANGE_MAX RANGE_MIN);你让 RANGE_MAX1&#xff1b;RANGE_MIN 0问题&#xff1a;1 rand() 返回的是 什么类型的数&#xff1f;2 (double) rand() / (double) RAND_MAX)&#x…

BZOJ 3697: 采药人的路径 [点分治] [我想上化学课]

传送门 题意&#xff1a; 路径有$-1,1$两种权值&#xff0c;求有多少路径满足权值和为$0$且有一个点将路径分成权值和为$0$的两段 第四节课本来想去上化学&#xff0c;然后快上课了这道题还没调出来.....可恶我想上化学 昨天两节语文课潸然的李煜讲座也没去听呜呜听说今天的语文…

c#语言规范所在文件夹,C#规范整理·语言要素

如有不理解&#xff0c;请留言&#xff0c;开始!1. 正确操作字符串拼接字符串一定要考虑使用 StringBuilder ,默认长度为16,实际看情况设置。StringBuilder本质&#xff1a; 是以非托管方式分配内存。同时StringFormat方法 内部也是使用StringBuilder进行字符串格式化。2. 使用…

Discuz常见小问题-如何取消登陆发帖验证码

1 正常情况下&#xff0c;用户点击登录之后&#xff0c;需要填写验证码 2 进入后台&#xff0c;点击防灌水&#xff0c;验证设置&#xff0c;然后下面的各个选项可以设置是否启用验证码。 转载于:https://www.cnblogs.com/acetaohai123/p/6504754.html

RC电路的充放过程C语言实现,RC串联电路的暂态过程基本原理介绍

RC串联电路的特点&#xff1a;由于有电容存在不能流过直流电流&#xff0c;电阻和电容都对电流存在阻碍作用&#xff0c;其总阻抗由电阻和容抗确定&#xff0c;总阻抗随频率变化而变化。RC 串联有一个转折频率&#xff1a;f01/2πR1C1当输入信号频率大于f0 时&#xff0c;整个 …

jvm 方法区

方法区在一个jvm实例的内部&#xff0c;类型信息被存储在一个称为方法区的内存逻辑区中。类型信息是由类加载器在类加载时从类文件中提取出来的。类(静态)变量也存储在方法区中。 jvm实现的设计者决定了类型信息的内部表现形式。如&#xff0c;多字节变量在类文件是以big-endia…

C语言1094题目,基于visual Studio2013解决C语言竞赛题之1094纵横图

/************************************************************************//* 二)程序设计⑴奇阶纵横图n2m1请见填写方法分析1)。 该程序在 数组23题已经完成⑵偶阶纵横图n2(2m1)时&#xff0c;算法分析是把方阵划成A、B、C、D四个小子阵&#xff0c;然后进行多次交换数字来…

c语言考试常考试卷,c语言面试最必考的十道试题,求职必看!!!

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼6、free()函数问&#xff1a;下面的程序会在用户输入’freeze’的时候出问题&#xff0c;而’zebra’则不会&#xff0c;为什么?#include int main(int argc, char *argv[]) {char *ptr (char*)malloc(10);if(NULL ptr){printf(…

Java 导出Excel

前台代码&#xff1a; View Code?12345678910111213141516171819202122232425262728293031323334353637383940414243444546<button class"btn btn-sm btn-success" type"submit" id"detailEp" onclick"return exportCheck(true);"…