ip层本机接受数据包处理

当数据包的目的地址是本机是,Ip_rcv_finish函数就会将skb->dst->input函数指针初始化为ip_local_deliver,ip层本地发送数据包也分为两个阶段分配分别有两个处理函数:ip_local_deliver和ip_local_deliver_finish。本地转发数据包的首要任务是重组数据包,前送的数据包可以不要重组,前送可以转发每个分片数据包。

一、本地重组数据包

ip_local_deliver函数主要的任务是重组ip数据包,重组数据包调用ip_defrag函数完成,ip_defrag重组数据包完毕后返回完整数据包指针,如果还没有收到数据包的所有分片,数据包还不完整就返回NULL。重组数据包成功后调用网络过滤子系统的INPUT链上的钩子函数对数据包做过滤处理,过滤处理完成就调用ip_local_deliver_finish函数继续处理。

int ip_local_deliver(struct sk_buff *skb)
{
	/*
	 *	Reassemble IP fragments.
	 */

	if (ip_hdr(skb)->frag_off & htons(IP_MF | IP_OFFSET)) {
		//重组数据包,如果数据包还不完整返回NULL
		if (ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER))
			return 0;
	}
	//数据包重组完成,调用防火墙上INPUT链上的钩子处理函数
	//网络过滤子系统处理完成就调用ip_local_deliver_finish继续处理
	return NF_HOOK(NFPROTO_IPV4, NF_INET_LOCAL_IN, skb, skb->dev, NULL,
		       ip_local_deliver_finish);
}

二、数据包从IP上传到传输层

将数据包从IP层上传给传输层的处理函数的ip_local_deliver_finish函数,它的主要任务有:

a、将数据包传给正确的上层协议处理函数

b、将数据包传给裸IP

c、对数据包进行安全策略检查

(1)、传输层协议

在IP协议头中protocol数据域占了8位,所以传输层最多支持256种协议,linux支持的传输层协议定义成枚举变量在include/linux/in.h文件中。

/* Standard well-defined IP protocols.  */
enum {
  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
  IPPROTO_PUP = 12,		/* PUP protocol				*/
  IPPROTO_UDP = 17,		/* User Datagram Protocol		*/
  IPPROTO_IDP = 22,		/* XNS IDP protocol			*/
  IPPROTO_DCCP = 33,		/* Datagram Congestion Control Protocol */
  IPPROTO_RSVP = 46,		/* RSVP protocol			*/
  IPPROTO_GRE = 47,		/* Cisco GRE tunnels (rfc 1701,1702)	*/

  IPPROTO_IPV6	 = 41,		/* IPv6-in-IPv4 tunnelling		*/

  IPPROTO_ESP = 50,            /* Encapsulation Security Payload protocol */
  IPPROTO_AH = 51,             /* Authentication Header protocol       */
  IPPROTO_BEETPH = 94,	       /* IP option pseudo header for BEET */
  IPPROTO_PIM    = 103,		/* Protocol Independent Multicast	*/
...

  IPPROTO_RAW	 = 255,		/* Raw IP packets			*/
  IPPROTO_MAX
};

(2)、传输层协议处理函数结构

传输层的每个协议都定义接受网络数据包的处理函数,完成对输入数据包的处理,如分段、错误处理。传输层协议处理结构体struct net_protocol定义在include/net/protocol.h文件中,结构体如下:

/* This is used to register protocols. */
struct net_protocol {
	//协议注册到内核的处理输入数据包的函数
	int			(*handler)(struct sk_buff *skb);
	//内核收到icmp 错误消息处理函数
	void			(*err_handler)(struct sk_buff *skb, u32 info);
	//发送检查gso
	int			(*gso_send_check)(struct sk_buff *skb);
	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
					       int features);
	struct sk_buff	      **(*gro_receive)(struct sk_buff **head,
					       struct sk_buff *skb);
	int			(*gro_complete)(struct sk_buff *skb);
	//是否需要ipsce策略检查,1不需要
	unsigned int		no_policy:1,
				netns_ok:1;
};

传输层的数据包处理结构体在IP层初始化函数inet_init中调用inet_add_protocol注册到内核全局数组inet_protos中。

//内核全局数组,保存了所有传输层的处理结构体
extern const struct net_protocol *inet_protos[MAX_INET_PROTOS];

inet_add_protocol函数:

int inet_add_protocol(const struct net_protocol *prot, unsigned char protocol)
{
	int hash, ret;
	//根据协议编码获取哈希值
	hash = protocol & (MAX_INET_PROTOS - 1);

	spin_lock_bh(&inet_proto_lock);
	//将传输层协议处理结构体添加到全局数组inet_protos中
	if (inet_protos[hash]) {
		ret = -1;
	} else {
		inet_protos[hash] = prot;
		ret = 0;
	}
	spin_unlock_bh(&inet_proto_lock);

	return ret;
}

tcp协议net_protocol协议结构体实例:

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,
};

三、ip_local_deliver_finish函数

数据包通过网络过滤系统INPUT链上的钩子处理函数后调用ip_local_deliver_finish函数继续处理。ip_local_deliver函数主要任务:

a、初始化skb->data,设置传输层协议头指针skb->transplant_header,使指针指向传输层协议头的起始地址,这时IP层已经处理完成,但还是可以通过skb->network_header访问Ip协议头。

b、获取传输层协议编码iphdr-protocol,然后根据协议编码在inet_protos全局数组中找到传输层结构体,调用传输层处理函数,数据包进如传输层。

c、检查数据包是否需要ipsce检查处理

c、错误处理,返回一个imcp包表示地址不可达。

ip_local_deliver_finish函数代码:

static int ip_local_deliver_finish(struct sk_buff *skb)
{
	struct net *net = dev_net(skb->dev);

	//数据包偏移ip头部长度
	__skb_pull(skb, ip_hdrlen(skb));

	/* Point into the IP datagram, just past the header. */
	//设置传输层协议头指针skb->transplant_header
	skb_reset_transport_header(skb);
	//加读锁
	rcu_read_lock();
	{
		int protocol = ip_hdr(skb)->protocol;
		int hash, raw;
		const struct net_protocol *ipprot;

	resubmit:
		raw = raw_local_deliver(skb, protocol);
		//求得传输层的哈希值
		hash = protocol & (MAX_INET_PROTOS - 1);
		//根据传输层协议号获取传输层处理函数的结构体net_protocol
		ipprot = rcu_dereference(inet_protos[hash]);
		if (ipprot != NULL) {
			int ret;

			if (!net_eq(net, &init_net) && !ipprot->netns_ok) {
				if (net_ratelimit())
					printk("%s: proto %d isn't netns-ready\n",
						__func__, protocol);
				kfree_skb(skb);
				goto out;
			}

			//配置了ipsec安全处理
			if (!ipprot->no_policy) {
				//ipsec策略安全检查函数
				if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
					kfree_skb(skb);
					goto out;
				}
				nf_reset(skb);
			}
			//调用传输层的接口处理函数
			//数据包进入传输层
			ret = ipprot->handler(skb);
			if (ret < 0) {
				protocol = -ret;
				goto resubmit;
			}
			IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
		} else {
			//没有找到传输层的处理结构体
			if (!raw) {
				if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
					IP_INC_STATS_BH(net, IPSTATS_MIB_INUNKNOWNPROTOS);
					//错误处理目的地址不可达,回复一个icmp包
					icmp_send(skb, ICMP_DEST_UNREACH,
						  ICMP_PROT_UNREACH, 0);
				}
			} else
				IP_INC_STATS_BH(net, IPSTATS_MIB_INDELIVERS);
			kfree_skb(skb);
		}
	}
 out:
	rcu_read_unlock();

	return 0;
}

 

你可能感兴趣的:(网络,协议栈,个人笔记,IP层)