(基于linux-2.6.21.7)
(一)网卡接收报文
以Intel PRO/1000网卡为例。
网卡收到报文后,产生中断。
驱动程序的中断处理函数e1000_intr(源码文件:\drivers\net\e1000\e1000_main.c)对报文进行接收。
中断处理程序先做一些基本的接收工作,然后开始如下的执行路径(从e1000_intr内部开始):
(注意,内核与网卡驱动之间,有两套数据交互接口:一套老的,一套新的。新的接口(称为NAPI)有更好的效率。
因此,我们这里只看基于NAPI接口的执行路径。)
__netif_rx_schedule (源码文件:net\core\dev.c)
__raise_softirq_irqoff(NET_RX_SOFTIRQ); /* 启动报文接收软中断 */
net_rx_action(由上一步触发,(源码文件:net\core\dev.c)
dev->poll(即e1000_clean,源码文件:\drivers\net\e1000\e1000_main.c)
adapter->clean_rx(即e1000_clean_rx_irq,源码文件:\drivers\net\e1000\e1000_main.c)
netif_receive_skb(源码文件:net\core\dev.c)
netif_receive_skb根据以太头中的type值(当然,还有别的信息,在下还没弄明白)查找一个匹配的packet_type条目,
然后调用pt_prev->func对报文进行处理。pt_prev为指向查找到的packet_type条目的指针。
大部分packet_type条目都位于ptype_base中(源码文件:net\core\dev.c)。
各协议模块通过dev_add_pack接口向ptype_base中添加条目。
packet_type条目的一个关键成员是type,它是ethernet头中的type字段的某个值。
另一个关键成员是func,他是该协议模块对报文进行接收处理的函数。
例如,ip模块向ptype_base添加的packet_type为ip_packet_type,其定义如下:
(源码文件:net\ipv4\af_inet.c)
static struct packet_type ip_packet_type = {
.type = __constant_htons(ETH_P_IP),
.func = ip_rcv,
.gso_send_check = inet_gso_send_check,
.gso_segment = inet_gso_segment,
};
再如,arp模块向ptype_base添加的packet_type为arp_packet_type,其定义如下:
(源码文件:net\ipv4\Arp.c)
static struct packet_type arp_packet_type = {
.type = __constant_htons(ETH_P_ARP),
.func = arp_rcv,
};
接下来,我们看看IP协议模块对报文的接收处理。
(二)IP协议模块对报文的接收处理
根据ip_packet_type的定义,ip报文的接收处理由ip_rcv完成。
我们这里仅关注目的ip为本机的报文。
几个关键的执行路径如下:
ip_rcv (源码文件:net\ipv4\ip_input.c)
ip_rcv_finish
ip_route_input /* 查找路由,确定报文是上送给host还是转发出去 */
dst_input
skb->dst->input(具体函数ip_route_input决定。由于ip为本机,因此对应ip_local_deliver)
ip_local_deliver_finish
ip_local_deliver_finish中,调用ipprot->handler对报文进行处理。
ipprot是net_protocol指针,他是根据IP报文的protocol(例如udp、tcp啦),引用inet_protos[protocol]得到的。
inet_protos定义如下(源码文件:net\ipv4\protocol.c)。
struct net_protocol *inet_protos[MAX_INET_PROTOS];
inet_protos数组元素指向一个inet_protos结构。
此结构的关键信息是handler函数。他是本协议模块对报文进行接收处理的函数。
例如,tcp、udp、icmp协议的net_protocol结构定义如下。
(源码文件:net\ipv4\af_inet.c)
static struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.no_policy = 1,
};
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
static struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
};
各协议模块的handler函数对收到到报文进行处理,并将相应的数据提交到各个socket中。
用户则通过一组socket接口将数据读走。
socket则相当于一个媒介,他处于用户和协议模块之间。
也就是说,socket层对于用户与协议模块都是可见的。
二者最终都是与socket进行交互。
最后,ip_local_deliver_finish中,实际上还做了raw socket方面的处理。
在将报文传递给inet_protos[protocol]之前,还要先看看有没有相应的raw_socket。
如果有的话,需要将报文复制给raw_socket一份。
(一)网卡接收报文
以Intel PRO/1000网卡为例。
网卡收到报文后,产生中断。
驱动程序的中断处理函数e1000_intr(源码文件:\drivers\net\e1000\e1000_main.c)对报文进行接收。
中断处理程序先做一些基本的接收工作,然后开始如下的执行路径(从e1000_intr内部开始):
(注意,内核与网卡驱动之间,有两套数据交互接口:一套老的,一套新的。新的接口(称为NAPI)有更好的效率。
因此,我们这里只看基于NAPI接口的执行路径。)
__netif_rx_schedule (源码文件:net\core\dev.c)
__raise_softirq_irqoff(NET_RX_SOFTIRQ); /* 启动报文接收软中断 */
net_rx_action(由上一步触发,(源码文件:net\core\dev.c)
dev->poll(即e1000_clean,源码文件:\drivers\net\e1000\e1000_main.c)
adapter->clean_rx(即e1000_clean_rx_irq,源码文件:\drivers\net\e1000\e1000_main.c)
netif_receive_skb(源码文件:net\core\dev.c)
netif_receive_skb根据以太头中的type值(当然,还有别的信息,在下还没弄明白)查找一个匹配的packet_type条目,
然后调用pt_prev->func对报文进行处理。pt_prev为指向查找到的packet_type条目的指针。
大部分packet_type条目都位于ptype_base中(源码文件:net\core\dev.c)。
各协议模块通过dev_add_pack接口向ptype_base中添加条目。
packet_type条目的一个关键成员是type,它是ethernet头中的type字段的某个值。
另一个关键成员是func,他是该协议模块对报文进行接收处理的函数。
例如,ip模块向ptype_base添加的packet_type为ip_packet_type,其定义如下:
(源码文件:net\ipv4\af_inet.c)
static struct packet_type ip_packet_type = {
.type = __constant_htons(ETH_P_IP),
.func = ip_rcv,
.gso_send_check = inet_gso_send_check,
.gso_segment = inet_gso_segment,
};
再如,arp模块向ptype_base添加的packet_type为arp_packet_type,其定义如下:
(源码文件:net\ipv4\Arp.c)
static struct packet_type arp_packet_type = {
.type = __constant_htons(ETH_P_ARP),
.func = arp_rcv,
};
接下来,我们看看IP协议模块对报文的接收处理。
(二)IP协议模块对报文的接收处理
根据ip_packet_type的定义,ip报文的接收处理由ip_rcv完成。
我们这里仅关注目的ip为本机的报文。
几个关键的执行路径如下:
ip_rcv (源码文件:net\ipv4\ip_input.c)
ip_rcv_finish
ip_route_input /* 查找路由,确定报文是上送给host还是转发出去 */
dst_input
skb->dst->input(具体函数ip_route_input决定。由于ip为本机,因此对应ip_local_deliver)
ip_local_deliver_finish
ip_local_deliver_finish中,调用ipprot->handler对报文进行处理。
ipprot是net_protocol指针,他是根据IP报文的protocol(例如udp、tcp啦),引用inet_protos[protocol]得到的。
inet_protos定义如下(源码文件:net\ipv4\protocol.c)。
struct net_protocol *inet_protos[MAX_INET_PROTOS];
inet_protos数组元素指向一个inet_protos结构。
此结构的关键信息是handler函数。他是本协议模块对报文进行接收处理的函数。
例如,tcp、udp、icmp协议的net_protocol结构定义如下。
(源码文件:net\ipv4\af_inet.c)
static struct net_protocol tcp_protocol = {
.handler = tcp_v4_rcv,
.err_handler = tcp_v4_err,
.gso_send_check = tcp_v4_gso_send_check,
.gso_segment = tcp_tso_segment,
.no_policy = 1,
};
static struct net_protocol udp_protocol = {
.handler = udp_rcv,
.err_handler = udp_err,
.no_policy = 1,
};
static struct net_protocol icmp_protocol = {
.handler = icmp_rcv,
};
各协议模块的handler函数对收到到报文进行处理,并将相应的数据提交到各个socket中。
用户则通过一组socket接口将数据读走。
socket则相当于一个媒介,他处于用户和协议模块之间。
也就是说,socket层对于用户与协议模块都是可见的。
二者最终都是与socket进行交互。
最后,ip_local_deliver_finish中,实际上还做了raw socket方面的处理。
在将报文传递给inet_protos[protocol]之前,还要先看看有没有相应的raw_socket。
如果有的话,需要将报文复制给raw_socket一份。