1. 程式人生 > >ip層本機接受資料包處理

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