日期:2014-05-16  浏览次数:20905 次

Linux中处理需要传输的IP报文流程

本文主要讲解了Linux中处理需要传输的IP报文流程,使用的内核的版本是2.6.32.27

为了方便理解,本文采用整体流程图加伪代码的方式对Linux中处理需要传输的IP报文流程进行了讲解,希望可以对大家有所帮助。阅读本文章假设大家对C语言有了一定的了解


首先从IP的更高层传输层看看是如何管理的


//-----------------------------------------------------------------------------------------------

/*四层协议的注册,都注册为net_protocol结构,并hash到inet_protos表中进行统一管理*/
#ifdef CONFIG_IP_MULTICAST
static const struct net_protocol igmp_protocol = {
	.handler =	igmp_rcv,
	.netns_ok =	1,
};
#endif

static const 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,
	.gro_receive =	tcp4_gro_receive,
	.gro_complete =	tcp4_gro_complete,
	.no_policy =	1,
	.netns_ok =	1,
};

static const struct net_protocol udp_protocol = {
	.handler =	udp_rcv,
	.err_handler =	udp_err,
	.gso_send_check = udp4_ufo_send_check,
	.gso_segment = udp4_ufo_fragment,
	.no_policy =	1,
	.netns_ok =	1,
};

static const struct net_protocol icmp_protocol = {
	.handler =	icmp_rcv,
	.no_policy =	1,
	.netns_ok =	1,
};


static int __init inet_init(void)
{
	/*
	 *	Add all the base protocols.
	 */
	if (inet_add_protocol(&icmp_protocol, IPPROTO_ICMP) < 0)
		printk(KERN_CRIT "inet_init: Cannot add ICMP protocol\n");
	if (inet_add_protocol(&udp_protocol, IPPROTO_UDP) < 0)
		printk(KERN_CRIT "inet_init: Cannot add UDP protocol\n");
	if (inet_add_protocol(&tcp_protocol, IPPROTO_TCP) < 0)
		printk(KERN_CRIT "inet_init: Cannot add TCP protocol\n");
#ifdef CONFIG_IP_MULTICAST
	if (inet_add_protocol(&igmp_protocol, IPPROTO_IGMP) < 0)
		printk(KERN_CRIT "inet_init: Cannot add IGMP protocol\n");
#endif	
}

在4层处理完成之后,4层会调用IP层的接口ip_qeueu_xmit进行报文发送

//-----------------------------------------------------------------------------------------------

/*ipv4.c中注册的让上层协议使用的接口*/
static const struct inet_connection_sock_af_ops dccp_ipv4_af_ops = {
	.queue_xmit	   = ip_queue_xmit,
};

/*将dccp_ipv4_af_ops注册到协议中*/
static int dccp_v4_init_sock(struct sock *sk)
{
	inet_csk(sk)->icsk_af_ops = &dccp_ipv4_af_ops;
}


/*TCP数据报文发送函数*/
static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,   gfp_t gfp_mask)
{
	const struct inet_connection_sock *icsk = inet_csk(sk);
	
	/*使用ip_queue_xmit发送数据报文*/
	err = icsk->icsk_af_ops->queue_xmit(skb, 0);

}


//-----------------------------------------------------------------------------------------------


int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
{
	struct sock *sk = skb->sk;
	struct inet_sock *inet = inet_sk(sk);
	struct ip_options *opt = inet->opt;
	struct rtable *rt;
	struct iphdr *iph;

	/*检查套接字结构中sk->dst中是否有一个指针指向路由缓存中的某个入口项
	 *如果有,再检查这个指针是否有效,由于套接字的所有包都去往同一个目标
	 *地址,因此路由就存放在skb->_skb_ds