閱讀555 返回首頁    go 京東網上商城


網絡子係統48_ip協議數據幀的發送

//ip協議與l4協議接口,l4通過此接口向下l3傳遞數據幀
//函數主要任務:
//	1.通過路由子係統路由封包
//	2.填充l3報頭
//	3.ip分片
//	4.計算校驗和
//	5.銜接鄰居子係統,向下層傳送封包。
1.1 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;//選項與sock相關
	struct rtable *rt;
	struct iphdr *iph;

	//檢查skb是否已經被路由
	rt = (struct rtable *) skb->dst;
	if (rt != NULL)
		goto packet_routed;
	//檢測ip的可用性
	//dst->obsolete 
	//		0 	默認值,該結構有效可以被使用
	//		2 	表示該結構將被刪除因而不能被使用
	//	   -1   表示該結構被IPsec和IPv6使用但不被IPv4使用 	
	rt = (struct rtable *)__sk_dst_check(sk, 0);
	if (rt == NULL) {//沒有可用的路由信息,重新路由封包
		u32 daddr;

		daddr = inet->daddr;//sock中指定的目標ip地址
		if(opt && opt->srr)//源路由選項
			daddr = opt->faddr;//將第一跳地址作為目標地址

		{
			struct flowi fl = { .oif = sk->sk_bound_dev_if,//出口設備
					    .nl_u = { .ip4_u =
						      { .daddr = daddr,//對目標地址進行路由,為sock中指定的目的地址,或者在源路由選項時,使用的第一跳地址
							.saddr = inet->saddr,//源地址
							.tos = RT_CONN_FLAGS(sk) } },//sock中指定的tos
					    .proto = sk->sk_protocol,//l4協議
					    .uli_u = { .ports =
						       { .sport = inet->sport,
							 .dport = inet->dport } } };//源端口,目的端口

			if (ip_route_output_flow(&rt, &fl, sk, 0))//對出口skb進行路由
				goto no_route;
		}
		__sk_dst_set(sk, &rt->u.dst);
		tcp_v4_setup_caps(sk, &rt->u.dst);//根據網卡對tso的支持,更新sk的tso
	}
	skb->dst = dst_clone(&rt->u.dst);//增加dst的引用計數

packet_routed:
	if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)//處理嚴路由選項,下一跳地址必須為網管地址
		goto no_route;

	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));//移動skb->data向上,準備填寫ip報頭
	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));//設置報頭長度為20字節,選項的長度,在ip_options_build中增加
	iph->tot_len = htons(skb->len);//總長度,skb中所有數據的長度,包括報頭,選項,有效載荷
	if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)//檢查禁止了分片
		iph->frag_off = htons(IP_DF);//設置報頭指示禁止分片
	else
		iph->frag_off = 0;//分片中的偏移量
	iph->ttl      = ip_select_ttl(inet, &rt->u.dst);//生存周期
	iph->protocol = sk->sk_protocol;//指示ip上層的l4協議類型
	iph->saddr    = rt->rt_src;//源地址,目的地址
	iph->daddr    = rt->rt_dst;
	skb->nh.iph   = iph;

	if (opt && opt->optlen) {//sock中設置了選項
		iph->ihl += opt->optlen >> 2;
		ip_options_build(skb, opt, inet->daddr, rt, 0);//處理選項
	}

	ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);//為ip選擇id

	ip_send_check(iph);//ip報頭校驗和

	skb->priority = sk->sk_priority;//skb的優先級為sock的優先級,在dev_queue_xmit中用於在規則隊列中排隊

	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
		       dst_output);//調用dst->output,由ip_route_output_flow設置

no_route:
	IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
	kfree_skb(skb);
	return -EHOSTUNREACH;
}

//調用路徑ip_queue_xmit->ip_output或ip_forward_finish->ip_output
1.2 int ip_output(struct sk_buff *skb)
{
	IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);

	if (skb->len > dst_pmtu(skb->dst) && !skb_shinfo(skb)->tso_size)
		return ip_fragment(skb, ip_finish_output);//ip分片
	else
		return ip_finish_output(skb);
}

//調用路徑ip_output->ip_finish_output
1.3 int ip_finish_output(struct sk_buff *skb)
{
	struct net_device *dev = skb->dst->dev;

	skb->dev = dev;//出口設備
	skb->protocol = htons(ETH_P_IP);//skb準備向l2層傳送,在skb->protocol中告訴l2層,上層l3的協議類型

	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
		       ip_finish_output2);//skb被路由之後的netfilter攔截點
}
//調用路徑ip_output->ip_finish_output->ip_finish_output2
//函數主要任務:	
//	1.保證skb頭部足夠空間容納l2幀頭
//	2.通過l2幀頭緩存,拷貝l2幀頭,完成向下層的傳輸
//	3.沒有可供使用的l2幀頭緩存,則銜接鄰居子係統,交由鄰居子係統處理skb
1.4 static inline int ip_finish_output2(struct sk_buff *skb)
{
	struct dst_entry *dst = skb->dst;
	struct hh_cache *hh = dst->hh;
	struct net_device *dev = dst->dev;
	int hh_len = LL_RESERVED_SPACE(dev);

	if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {//skb頭空間不足l2頭,設備驅動提供了在skb中填充l2頭的功能
		struct sk_buff *skb2;

		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));//重新分配skb頭來容納l2頭
		if (skb2 == NULL) {
			kfree_skb(skb);
			return -ENOMEM;
		}
		if (skb->sk)
			skb_set_owner_w(skb2, skb->sk);
		kfree_skb(skb);
		skb = skb2;
	}

	if (hh) {//l2幀頭緩存
		int hh_alen;

		read_lock_bh(&hh->hh_lock);
		hh_alen = HH_DATA_ALIGN(hh->hh_len);
  		memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);//拷貝幀頭緩存中的l2幀頭到skb中
		read_unlock_bh(&hh->hh_lock);
	    skb_push(skb, hh->hh_len);
		return hh->hh_output(skb);//通過l2幀頭緩存的output函數,完成skb向下的傳遞
	} else if (dst->neighbour)//沒有l2幀頭緩存,則需要鄰居子係統,獲取數據幀的l2地址
		return dst->neighbour->output(skb);

	if (net_ratelimit())
		printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour!\n");
	kfree_skb(skb);
	return -EINVAL;
}

最後更新:2017-04-03 14:53:40

  上一篇:go poj 1656 Counting Black
  下一篇:go Ubuntu 12.04 64-bit下安裝android 2.3.x編譯環境指南2