關於linux網絡包的收發流程,網上隨便一搜都可以搜一桶,但自己不動手永遠都搞不原理。最近在家比較閒,對網絡這一塊也不太瞭解,老婆在看《三生三世枕上書》,我只能看下代碼打發下時間。
小編習慣熟悉內核子系統原理從低版本內核開始.
Linux(2.6.11.12)網絡收包流程圖:
device driver interrupt handler
netif_rx()
cpu_raise_softirq()
do_softirq()
net_rx_atcion()
dev->poll(dev, &budget)( process_backlog)(注0)
process_backlog()
netif_receive_skb()
skb_bond(skb); 如果網卡綁定,則取netdev 的master設備
pt_prev->func() (注1)
type = skb->protocol(L3層 ipv4 or ipv6 ..)
ip_rcv()
NF_HOOK(PF_INET,NF_IP_PRE_ROUTING,skb, dev, NULL,ip_rcv_finish);
ip_rcv_finish()
dst_input()
skb->dst->input();(注2)
(ip_local_deliver或ip_forward)
ip_local_deliver()
NF_HOOK(PF_INET,NF_IP_LOCAL_IN, skb, skb->dev, NULL,
ip_local_deliver_finish);
ip_local_deliver_finish()
ipprot->handler(skb);
(L4層 udp_rcv/tcp_v4_rcv..)
udp_rcv()
udp_queue_rcv_skb()
sock_queue_rcv_skb
sk->sk_data_ready() (sock_def_readable)
static void sock_def_readable(structsock *sk, int len)
{
read_lock(&sk->sk_callback_lock);
if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
wake_up_interruptible(sk->sk_sleep);
sk_wake_async(sk,1,POLL_IN);
read_unlock(&sk->sk_callback_lock);
}
sys_recvfrom()
sock_recvmsg()
sock->ops->recvmsg()(sock_common_recvmsg)
sock_common_recvmsg()
sk->sk_prot->recvmsg()(udp_recvmsg)
udp_recvmsg()
skb_recv_datagram()
wait_for_packet()
static int wait_for_packet(structsock *sk, int *err, long *timeo_p)
{
…
DEFINE_WAIT(wait);
prepare_to_wait_exclusive(sk->sk_sleep,&wait,TASK_INTERRUPTIBLE);
…
}
注0:
net_dev_init()
{
…
queue->backlog_dev.poll = process_backlog;
…
}
注1:
void __init ip_init(void)
{
dev_add_pack(&ip_packet_type);
}
static struct packet_type ip_packet_type = {
.type = __constant_htons(ETH_P_IP),
.func = ip_rcv,
};
void __init ipv6_packet_init(void)
{
dev_add_pack(&ipv6_packet_type);
}
static struct packet_type ipv6_packet_type = {
.type = __constant_htons(ETH_P_IPV6),
.func = ipv6_rcv,
};
void dev_add_pack(struct packet_type *pt)
{
…
list_add_rcu(&pt->list, &ptype_base[hash]);
…
}
注2:
ip_rcv_finish
ip_route_input
ip_route_input_slow
ip_route_input_slow()
{
…
rth->u.dst.input = ip_forward;
…
rth->u.dst.input= ip_local_deliver;
}
參考:
1.《understanding the linux kernel》
2. 謝寶友Linux kernel 2.6.11.12源碼註釋