合同栈的细节
下边将介绍一些内核网路合同栈中往往涉及到的概念。
sk_buff
内核也许须要一个数据结构来表示报文,这个结构就是sk_buff(socketbuffer的简称),它等同于在中描述的BSD内核中的mbuf。
sk_buff结构自身并不储存报文内容,它通过多个表针指向真正的报文显存空间:
sk_buff是一个贯串整个合同栈层次的结构,在各层间传递时,内核只须要调整sk_buff中的表针位置就行。
net_device
内核使用net_device表示网卡。网卡可以分为化学网卡和虚拟网卡。化学网卡是指真正能把报文发出本机的网卡,包括真实化学机的网卡以及VM虚拟机的网卡,而像tun/tap,vxlan、vethpair这样的则属于虚拟网卡的范畴。
如右图所示,每位网卡都有两端,一端是合同栈(IP、TCP、UDP),另一端则有所区别,对化学网卡来说,这一端是网卡生产厂商提供的设备驱动程序,而对虚拟网卡来说差异就大了,正是因为虚拟网卡的存在,内核能够支持各类隧洞封装、容器通讯等功能。
【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人认为比较好的学习书籍、视频资料共享在群文件上面,有须要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)
socket&sock
用户空间通过socket()、bind()、listen()、accept()等库函数进行网路编程。而这儿提及的socket和sock是内核中的两个数据结构,其中socket向下面向用户,而sock向上面向合同栈。
如右图所示,这两个结构实际上是一一对应的。
注意到,这两个结构上都有一个叫ops的表针,但它们的类型不同。socket的ops是一个指向structproto_ops的表针红旗linux5.0,sock的ops是一个指向structproto的表针,它们在结构被创建时确定。
追忆网路编程中socket()函数的原型:
实际上,socket->ops和sock->ops由前两个参数socket_family和socket_type共同确定。
假如socket_family是最常用的PF_INET合同簇,则socket->ops和sock->ops的取值就记录在INET合同开关表中:
L3->L4
我们晓得网路合同栈是分层的linux内核协议栈,但实际上,具体到实现,内核合同栈的分层只是逻辑上的,本质还是函数调用。发送流程(下层调用上层)一般是直接调用(由于没有不确定性linux命令手册,例如TCP晓得下边一定IP),但接收过程不一样了,例如报文在IP层时,它里面可能是TCP,也可能是UDP,或则是ICMP等等,所以接收过程使用的是注册-反弹机制。
还是以INET合同簇为例,注册插口是:
在内核网路子系统初始化时,L4层合同(如下边的TCP和UDP)会被注册:
而在IP层,查询过路由后,假如该报文是须要上送本机的,则会依照报文的L4合同,献给不同的L4处理:
L2->L3
L2->L3如出一辙。只不过注册插口弄成了:
谁会注册呢?其实起码IP会:
而在报文接收过程中,设备驱动程序会将报文的L3类型设置到skb->protocol,之后在内核netif_receive_skb收包时,会依照这个protocol调用不同的反弹函数:
Netfilter
Netfilter是报文在内核合同栈必然会通过的路径,我们从下边这张图就可以看见,Netfilter在内核的5个地方设置了HOOK点,用户可以通过配置iptables规则,在HOOK点对报文进行过滤、修改等操作。
在内核代码中,我们经常可见NF_HOOK这样的调用。我的建议是,假如你暂时不考虑Netfilter,这么就直接跳过,跟踪okfn就行。
dst_entry
内核须要确定收到的报文是应当本地上送(localdeliver)还是转发(forward),对本机发送(localout)的报文须要确定是从那个网卡发送出去linux内核协议栈,这都是内核通过查询fib(forwardinformationbase,转发信息表)确定。fib可以理解为一个数据库,数据来源是用户配置或则内核手动生成的路由。
fib查询的输入是报文sk_buff,输出是dst_entry.dst_entry会被设置到skb上:
而dst_entry中最重要的是一个input表针和output表针:
对于须要本机上送的报文:
对须要转发的报文:
对本机发送的报文: