下面将介绍一些内核网络协议栈中常常涉及到的概念。sk_buff内核显然需要一个数据结构来表示报文,这个结构就是sk_buff,它等同于在中描述的BSD内核中的mbuf。sk_buff结构自身并不存储报文内容,它通过多个指针指向真正的报文内存空间:sk_buff是一个贯穿整个协议栈层次的结构,在各层间传递时,内核只需要调整sk_buff中的指针位置就行。net_device内核使用net_device表示网卡。网卡可以分为物理网卡和虚拟网卡。物理网卡是指真正能把报文发出本机的网卡,包括真实物理机的网卡以及VM虚拟机的网卡,而像tun/tap,vxlan、vethpair这样的则属于虚拟网卡的范畴。如下所示,每个网卡都有两端,一端是协议栈,另一端则有所区别,对物理网卡来说,这一端是网卡生产厂商提供的设备驱动程序,而对虚拟网卡来说差别就大了,正是由于虚拟网卡的存在,内核才能支持各种隧道封装、容器通信等功能。socket&sock用户空间通过socket、bind、listen、accept等库函数进行网络编程。而这里提到的socket和sock是内核中的两个数据结构,其中socket向上面向用户,而sock向下面向协议栈。注意到,这两个结构上都有一个叫ops的指针,但它们的类型不同。socket的ops是一个指向structproto_ops的指针,sock的ops是一个指向structproto的指针,它们在结构被创建时确定。回忆网络编程中socket函数的原型:#includesockfd=socket;实际上,socket->ops和sock->ops由前两个参数socket_family和socket_type共同确定。如果socket_family是最常用的PF_INET协议簇,则socket->ops和sock->ops的取值就记录在INET协议开关表中:staticstructinet_protoswinetsw_array[]={{.type=SOCK_STREAM,.protocol=IPPROTO_TCP,.prot=&tcp_prot,//对应sock->ops.ops=&inet_stream_ops,//对应socket->ops.flags=INET_PROTOSW_PERMANENT|INET_PROTOSW_ICSK,},{.type=SOCK_DGRAM,.protocol=IPPROTO_UDP,.prot=&udp_prot,//对应sock->ops.ops=&inet_dgram_ops,//对应socket->ops.flags=INET_PROTOSW_PERMANENT,},}.......L3->L4我们知道网络协议栈是分层的,但实际上,具体到实现,内核协议栈的分层只是逻辑上的,本质还是函数调用。发送流程通常是直接调用,但接收过程不一样了,比如报文在IP层时,它上面可能是TCP,也可能是UDP,或者是ICMP等等,所以接收过程使用的是注册-回调机制。还是以INET协议簇为例,注册接口是:intinet_add_protocol;在内核网络子系统初始化时,L4层协议会被注册:staticstructnet_protocoltcp_protocol={.......handler=tcp_v4_rcv,......};staticstructnet_protocoludp_protocol={......handler=udp_rcv,.....};.......而在IP层,查询过路由后,如果该报文是需要上送本机的,则会根据报文的L4协议,送给不同的L4处理:staticintip_local_deliver_finish{......ipprot=rcu_dereference;......ret=ipprot->handler;......}.......L2->L3L2->L3如出一辙。只不过注册接口变成了:voiddev_add_pack谁会注册呢?显然至少IP会:staticstructpacket_typeip_packet_type={.type=cpu_to_be16,.func=ip_rcv,}.......而在报文接收过程中,设备驱动程序会将报文的L3类型设置到skb->protocol,然后在内核netif_receive_skb收包时,会根据这个protocol调用不同的回调函数:__netif_receive_skb{......type=skb->protocol;......ret=pt_prev->func;}.......NetfilterNetfilter是报文在内核协议栈必然会通过的路径,我们从下面这张就可以看到,Netfilter在内核的5个地方设置了HOOK点,用户可以通过配置iptables规则,在HOOK点对报文进行过滤、修改等操作。在内核代码中,我们时常可见NF_HOOK这样的调用。我的建议是,如果你暂时不考虑Netfilter,那么就直接跳过,跟踪okfn就行。staticinlineintNF_HOOK){intret=nf_hook;ifret=okfn;returnret;}.......dst_entry内核需要确定收到的报文是应该本地上送还是转发,对本机发送的报文需要确定是从哪个网卡发送出去,这都是内核通过查询fib确定。fib可以理解为一个数据库,数据来源是用户配置或者内核自动生成的路由。fib查询的输入是报文sk_buff,输出是dst_entry.dst_entry会被设置到skb上:staticinlinevoidskb_dst_set{skb->_skb_refdst=dst;}而dst_entry中最重要的是一个input指针和output指针:structdst_entry{......int;int;......}对于需要本机上送的报文:rth->dst.input=ip_local_deliver;对需要转发的报文:rth->dst.input=ip_forward;对本机发送的报文:rth->dst.output=ip_output;-End-
文章为作者独立观点,不代表股票交易接口观点