Linux內(nèi)核中IP解析全過程_第1頁
Linux內(nèi)核中IP解析全過程_第2頁
Linux內(nèi)核中IP解析全過程_第3頁
Linux內(nèi)核中IP解析全過程_第4頁
Linux內(nèi)核中IP解析全過程_第5頁
已閱讀5頁,還剩11頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)

文檔簡介

1、網(wǎng)絡(luò)代碼分析第二部分網(wǎng)絡(luò)子系統(tǒng)在IP層的收發(fā)過程剖析R.wen( HYPERLINK mailto:)、IP層數(shù)據(jù)包處理全景hlFPJtAL_OUT0000prolocolSl:TC#.UDP/RAWIP/.)MtfmNFIPIKALINNFIPFORWARDINGNFP_mi_ROUTIN(iPREROUTINGTrjffi(ondiulsubitem圖1.1*|-Hlp_erwdit.inpirt虛i輩didin*kb-dstoutputipfragmpiil*ip_fijihh_outpwt2MLsunjniit)1、接收的全過程在上一節(jié)可以看到,鏈路層將數(shù)據(jù)包上傳到IP層時,由IP層相

2、關(guān)協(xié)議的處理例程處理。對于IP協(xié)議,這個注冊的處理例程是ip_rcv(),它處理完成后交給NETFILTE(PRE-ROUTING)R過濾,再上遞給ip_rcv_finish(),這個函數(shù)根據(jù)skb包中的路由信息,決定這個數(shù)據(jù)包是轉(zhuǎn)發(fā)還是上交給本機(jī),由此產(chǎn)生兩條路徑,一為ip_local_deliver(),它首先檢查這個包是否是一個分片包,如果是,它要調(diào)動ip_defrag()將分片重裝,然后再次將包將給NETFILTER(LOCAL_IN)過濾后,再由ipocal_deliver_finish()將數(shù)據(jù)上傳到L4層,這樣就完成了IP層的處理;它負(fù)責(zé)將數(shù)據(jù)上傳,另一路徑為ip_forward

3、(),它負(fù)責(zé)將數(shù)據(jù)轉(zhuǎn)發(fā),經(jīng)由NETFILTER(FORWARD)過濾后將給ip_forward_finish(),然后調(diào)用dst_output()將數(shù)據(jù)包發(fā)送出去。2、發(fā)送全過程由上圖可以看到,當(dāng)L4層有數(shù)據(jù)包待發(fā)送時,它將調(diào)用ip_append_data/ip_push_pending_frams(udp,icmp,RawIP),或ip_append_page(UDP),ip_queue_xmit(TCP,SCTP),或者raw_send_hdrinc(RawIP,IGMP),它們將這些包交由NETFILTER(LOLACL_OUT)處理后,然后交給dst_output,這會根據(jù)是多播或單播

4、選擇合適的發(fā)送函數(shù)。如果是單播,它會調(diào)用ip_output(),然后是ip_finish_output(),這個函數(shù)主要是檢查待發(fā)送的數(shù)據(jù)包大小是否超過MTU,如果是,則要首先調(diào)用ip_fragment()將其分片,然后再傳給ip_finish_output2(),由它交給鏈路層處理了。二、接收的詳細(xì)過程1、我們已經(jīng)知道,鏈路層首先將數(shù)據(jù)包上傳給IP層的ip_rcv()函數(shù),這個函數(shù)主要做一些檢查工作:首先,這個函數(shù)不會接收不是發(fā)給這個主機(jī)的數(shù)據(jù)包,如果主機(jī)是工作在混雜模式,這個數(shù)據(jù)包已經(jīng)由netif_receive_skb()去完成處理了。注意,這里所說的“不屬于”這個主機(jī),是指在這個包目標(biāo)

5、主機(jī)的MAC地址不是本機(jī),而不是L3層的ip地址。所以,它不包括路由的包。if(skb-pkt_type=PACKET_OTHERHOST)gotodrop;接下來是一個共享的檢查,如果是共享的數(shù)據(jù)包,因為它可能需要修改skb中的信息,所以要先復(fù)制一個副本,再作進(jìn)一步的處理。if(skb=skb_share_check(skb,GFP_ATOMIC)=NULL)IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);gotoout;再下來就是檢查首部的長度是否夠長,校檢和等等:if(!pskb_may_pull(skb,sizeof(structiphdr)gotoin

6、hdr_error;去掉padded部分的長度:len=ntohs(iph-tot_len);/*Ourtransportmediummayhavepaddedthebufferout.NowweknowitisIPwecantrimtothetruelengthoftheframe.Notethisnowmeansskb-lenholdsntohs(iph-tot_len).*/if(pskb_trim_rcsum(skb,len)IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);gotodrop;returnNF_HOOK(PF_INET,NF_IP_PRE_

7、ROUTING,skb,dev,NULL,ip_rcv_finish);2、ip_rcv_finish()ip_rcv將數(shù)據(jù)傳給ip_rcv_finish()繼續(xù)處理,這個函數(shù)工作也比較簡單:它首先查找路由信息,在這里先忽略這部分:if(skb-dst=NULL)interr=ip_route_input(skb,iph-daddr,iph-saddr,iph-tos,skb-dev);然后就是對IP頭選項部分的處理了:if(iph-ihl5&ip_rcv_options(skb)gotodrop;最后就是dst_input了:returndst_input(skb);dst_input的工作

8、更為簡單,它只是根據(jù)skb的路由信息調(diào)用相應(yīng)的input函數(shù)了:skb-dst-input(skb);由全景圖可以看到,這個input有可能是ipocal_deliver()或ip_forward()。3、ip_forward()檢查skb是否共享,或是否頭部預(yù)留的空間是否足夠存放L2的頭部,因為在轉(zhuǎn)發(fā)這個數(shù)據(jù)包的時候要將L2的頭部拷貝進(jìn)去。/*Weareabouttomanglepacket.Copyit!*/if(skb_cow(skb,LL_RESERVED_SPACE(rt-u.dst.dev)+rt-u.dst.header_len)gotodrop;接著就是ip_forward_f

9、inish()了。returnNF_HOOK(PF_INET,NF_IP_FORWARD,skb,skb-dev,rt-u.dst.dev,ip_forward_finish);ip_forward_finish:首先處理未處理的頭部if(unlikely(opt-optlen)ip_forward_options(skb);然后就是dst_output:returndst_output(skb);這個已經(jīng)是發(fā)送部分所做的工作了,我們將在下一部分討論它。4、ipocal_deliver()首先,確定接收到的包是不是分片,如果是,則要將分片重裝成一個完整的IP包再上傳給L4層。if(skb-nh

10、.iph-frag_off&htons(IP_MF|IP_OFFSET)skb=ip_defrag(skb,IP_DEFRAG_LOCAL_DELIVER);然后就是ip_local_deliver_finish:returnNF_HOOK(PF_INET,NF_IP_LOCAL_IN,skb,skb-dev,NULL,ip_local_deliver_finish);5、ip_local_deliver_finish至此,已經(jīng)確定將這個數(shù)據(jù)包傳送給L4層,而L3層的頭已沒有作用,所以,先去掉L3的頭部:intihl=skb-nh.iph-ihl*4;_skb_pull(skb,ihl);將s

11、kb-data指向L4的頭部/*PointintotheIPdatagram,justpasttheheader.*/skb-h.raw=skb-data;/重新設(shè)備skb-h.raw值,它在receive_skb()中被置為L3層頭部的位置。接下來就要處理與L4層相關(guān)的協(xié)議了,首先處理的是RawIP,它先查看raw_v4_htable有沒有注冊到這個L4協(xié)議的RawIP:hash=protocol&(MAX_INET_PROTOS-1);raw_sk=sk_head(&raw_v4_htablehash);如果有,則要執(zhí)行raw_v4_input(),為其提交一份副本:if(raw_sk&!

12、raw_v4_input(skb,skb-nh.iph,hash)raw_sk=NULL;如何處理接收所有協(xié)議的RawIP?當(dāng)socket(AF_INET,SOCK_RAW,IPPROTO_RAW)時,它會接收所有協(xié)議的數(shù)據(jù)包,并且IP_HDRINCL是默認(rèn)打開的,即是說應(yīng)用層要提供L3和L4層的頭。再如,如果是IPPROTO_TCP時,它只接收到TCP包。而IP_HDRINCL是默認(rèn)不打開的,即系統(tǒng)會處理L3的頭部。接著就是對特定L4協(xié)議的處理:if(ipprot=rcu_dereference(inet_protoshash)!=NULL)intret;ret=ipprot-handler

13、(skb);它首先查找inet_protos數(shù)組,看有沒有相關(guān)的注冊的協(xié)議,如果有,則執(zhí)行它的處理例程。6、協(xié)議的注冊在inet_init()的時候,系統(tǒng)會注冊幾個常用的L4層協(xié)議:if(inet_add_protocol(&icmp_protocol,IPPROTO_ICMP)0)printk(KERN_CRITinet_init:CannotaddICMPprotocoln);if(inet_add_protocol(&udp_protocol,IPPROTO_UDP)0)printk(KERN_CRITinet_init:CannotaddUDPprotocoln);if(inet_ad

14、d_protocol(&tcp_protocol,IPPROTO_TCP)dst;if(rt!=NULL)gotopacket_routed;如果還沒有,它則要在路由表中查找相關(guān)的rt,但我們這里不關(guān)心路由部分。填充IP頭信息,這里用skb_push為IP頭留出空間/*OK,weknowwheretosendit,allocateandbuildIPheader.*/iph=(structiphdr*)skb_push(skb,sizeof(structiphdr)+(opt?opt-optlen:0);*(_ul6*)iph)=htons(412)I(5tos&0 xff);iph-tot_

15、len=htons(skb-len);if(ip_dont_fragment(sk,&rt-u.dst)&!ipfragok)iph-frag_off=htons(IP_DF);elseiph-frag_off=0;iph-ttl=ip_select_ttl(inet,&rt-u.dst);iph-protocol=sk-sk_protocol;iph-saddr=rt-rt_src;iph-daddr=rt-rt_dst;skb-nh.iph=iph;/*Transportlayersetskb-h.fooitself.*/b.創(chuàng)建選項部分和計算校檢:if(opt&opt-optlen)ip

16、h-ihl+=opt-optlen2;ip_options_build(skb,opt,inet-daddr,rt,0);設(shè)置包頭的ID域ip_select_ident_more(iph,&rt-u.dst,sk,(skb_shinfo(skb)-gso_segs?:1)-1);/*AddanIPchecksum.*/ip_send_check(iph);最后就是調(diào)用dst_output了。1.2、ip_append_data(ip_append_page)/ip_push_pending_frames這是一對由UDP等協(xié)議使用的一對函數(shù),ip_append_data是一個比較復(fù)雜的函數(shù),它主

17、要將收到的大數(shù)據(jù)包分成多個小于MTU(1500)的$*4為L3層要實現(xiàn)的IP分片做準(zhǔn)備。例如如果待發(fā)送的數(shù)據(jù)包大小為4000字節(jié),假設(shè)先前sock中的隊列又非空(sk-sk_write_queue!=NULL),(因為ip_append_data可以被L4層多次調(diào)用,用于添加數(shù)據(jù)。)并且之前一個skb還沒填滿,剩余大小為500字節(jié)。這時,當(dāng)L4層調(diào)用ip_append_data時,它首先將這個剩余的skb填滿,這里還有一個問題就是關(guān)于scatter/gatherIO的,當(dāng)NIC不支持時,它會直接將數(shù)據(jù)寫到這個skb-tail處,但是,如果NIC支持這種IO,它便會將數(shù)據(jù)寫到frags所指向的指

18、針中,如果相關(guān)的page已經(jīng)填滿,它會再分配一個新的page用于這個skb。這一步完成之后,ip_append_data再次進(jìn)入下三次循環(huán),每次循環(huán)都分配一個skb,并將數(shù)據(jù)通過getfrag從L4層復(fù)制下來。在循環(huán)結(jié)束之前,它通過_skb_queue_tail(&sk-sk_write_queue,skb),將這個skb鏈入這個sock的sk_write_queue隊列中去。待到這個循環(huán)結(jié)束時,所有的數(shù)staticinlinevoid_skb_queue_tail(structsk_buff_head*list,structsk_buffstructsk_buff*newsk)*prev,*

19、next;list-qlen+;next=(structsk_buff*)list;prev=next-prev;newsk-next=next;newsk-prev:=prev;next-prev=prev-next=newsk;據(jù)都從L4復(fù)制到各個skb,并鏈入了它的sk_write_queue隊列。換言之,待發(fā)送數(shù)據(jù)已經(jīng)在sk_write_queue隊列中了。它的循環(huán)過程如下,由于代碼較長,這里就不列出來了。圖2.1ip_append_page()跟ip_append_data()是實現(xiàn)同樣功能的函數(shù),它們的主要不同在于,ip_append_data()需要將用戶空間的數(shù)據(jù)復(fù)制到內(nèi)核空間

20、,而ip_append_page()則不需要這個復(fù)制,它直接使用用戶提供的數(shù)據(jù),從而實現(xiàn)了數(shù)據(jù)的“零拷貝”。現(xiàn)在來看一下它們之間的差別:a.分配skb空間:ip_append_data():alloclen=datalen+fragheaderlen;if(atomic_read(&sk-sk_wmem_alloc)sk_sndbuf)skb=sock_wmalloc(sk,alloclen+hh_len+15,1,sk-sk_allocation);ip_append_page():alloclen=fragheaderlen+hh_len+fraggap+15;skb=sock_wmall

21、oc(sk,alloclen,1,sk-sk_allocation);我們可以看到,這兩個函數(shù)分配的空間大小是不一樣的,對于ip_append_page(),它不為新的數(shù)據(jù)分配空間,只是分配一些頭部所需要的空間,和一個fraggap,它個值大小為17個字節(jié),是由于對齊問題從上一個skb移動過來的。b.ip_append_page()只能用于支持S/GIO的NIC,它只是將數(shù)據(jù)連接到frags數(shù)組中,而沒有ip_append_data()的復(fù)制數(shù)據(jù)。i=skb_shinfo(skb)-nr_frags;if(lensize)len=size;if(skb_can_coalesce(skb,i,p

22、age,offset)skb_shinfo(skb)-fragsi-l.size+=len;elseif(inetx,而是skb_shinfo(skb)-frag_list域了。(a)nextUpayload(b)L4pa/loadstructsockL4payloadheaddatatailendhead蟲“titlendstructik.buff$(ruc(5kbuff12headerIFheadertIni珀Ii圧dbyip_push_pefiding_frameiInitiali毘dbyip_push_pefidifig_framescaller5kbshinfo(skb)L2hwde

23、rIPheaderstructsk_twffLlheaderIPheaderL4payloadstructsock$k_wr映_quEue-sirurtsk_buffL2headerIPheaderL4headernextL4payloadwuusk_tuffnextL2headerIPhpadPrL4headpfL2headeriPaderL4pdylo祖sk_wriitqueuenextnext5(fufragist);/*moveskb-datatoipheaderfromextheader*/if(skb-datanh.raw)_skb_pull(skb,skb-nh.raw-skb-

24、data);while(tmp_skb=_skb_dequeue(&sk-sk_write_queue)!=NULL)_skb_pull(tmp_skb,skb-h.raw-skb-nh.raw);*tail_skbtail_skbskb-lenskb-data_lenskb-truesize=tmp_skb;=&(tmp_skb-next);+=tmp_skb-len;+=tmp_skb-len;+=tmp_skb-truesize;_sock_put(tmp_skb-sk);tmp_skb-destructor=NULL;tmp_skb-sk=NULL;接下來就是一些值的設(shè)置了,如建立IP

25、頭,IP選項,計算校檢值等,這些跟ip_queue_xmit()所做的工作差不多。最后跟ip_queue_xmit()樣,也是調(diào)用dst_output()完成發(fā)送工作。我們看到,對于UDP,它需要調(diào)用ip_append_data/ip_append_page處理很多輔助分片的工作,而對于TCP,ip_queue_xmit()沒有做這份工作,它是由L4層去完成的,相對于ip_append_data/ip_append_page,它有tcp_sendmsg/tcp_sendpage。1.3、dst_output()由開始的全景圖可以看到,對于單播IP來說,它執(zhí)行的是ip_output():inti

26、p_output(structsk_buff*skb)structnet_device*dev=skb-dst-dev;IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);skb-dev=dev;skb-protocol=htons(ETH_P_IP);returnNF_HOOK_COND(PF_INET,NF_IP_POST_ROUTING,skb,NULL,dev,ip_finish_output,!(IPCB(skb)-flags&IPSKB_REROUTED);由上,它再執(zhí)行ip_finish_output:staticinlineintip_finish_ou

27、tput(structsk_buff*skb)if(skb-lendst_mtu(skb-dst)&!skb_is_gso(skb)returnip_fragment(skb,ip_finish_output2);elsereturnip_finish_output2(skb);由ip_finish_output可以看到,如果IP數(shù)據(jù)包的長度大于MTU,它便會先將IP分成合適大小的分片,最后調(diào)用ip_finish_output2(),將這個包路由出去。三、IP的分片與重裝(ip_fragment/ip_defrag)先來看看產(chǎn)生IP分片與重裝的地方,這里只考慮主機(jī)的情況,而不關(guān)心路由。由上可以

28、看到,當(dāng)一個IP包在ip_finish_output()中被檢查到它的長度大于MTU,則它要調(diào)用ip_fragment將這個IP包進(jìn)行分片。而當(dāng)IP分片由ipocal_deliver()上傳給L4層時,它要將這些分片暫時保存起來,一旦所有的分片都到達(dá)時,它便將一個完整的IP包上傳給L4層。1、ip_fragment當(dāng)一個包由于太大而需要通過分片傳遞時,它便過調(diào)用這個函數(shù)將這個大的數(shù)據(jù)包分成多個小的IP分片。由圖2.2可以看到,ip_append_data/ip_push_pending_frames為IP分片做了很多輔助工作。當(dāng)一個L4層傳遞過來的數(shù)據(jù)包經(jīng)過ip_append_data和ip_

29、push_pending_frames處理后,它得到的結(jié)果如圖2.2(b)所示,這時,隊列中每個skb的大小都小于MTU減小鏈路頭部的大小(因為發(fā)送數(shù)據(jù)時需要加上鏈路層頭部),因些,ip_fragment只要將這些skb依照第一個skb的IP頭部為每個skb加上IP頭就完成了這個分片工作,這種分片稱為“快速分片”:structintif(skb_shinfo(skb)-frag_list)sk_buff*frag;first_len=skb_pagelen(skb);/第一個skb長度erroffsetfrag/省略了一些檢查工作/*EverythingisOK.Generate!*/處理第一

30、個skb=0;=0;=skb_shinfo(skb)-frag_list;skb_shinfo(skb)-frag_list=NULL;skb-data_len=first_len-skb_headlen(skb);=first_len;=htons(first_len);skb-leniph-tot_leniph-frag_offsend_check(iph);ip_foriffrag-ip_summedfrag-h.rawfrag-nh.raw(;)/*Prepareheaderofthenextframe,*beforepreviousonewentdown.*/(frag)memcpy

31、(frag-nh.raw,iph,hlen);/=CHECKSUM_NONE;=frag-data;=_skb_push(frag,hlen);為每個分片復(fù)制一份IP頭=htons(IP_MF);iph=frag-nh.iph;iph-tot_len=htons(frag-len);ip_copy_metadata(frag,skb);/復(fù)制其它一些關(guān)于skb的設(shè)置if(offset=0)ip_options_fragment(frag);offset+=skb-len-hlen;iph-frag_off=htons(offset3);ifiph-frag_off/*(frag-next!=N

32、ULL)|=htons(IP_MF);Ready,completechecksum*/ip_send_check(iph);err=output(skb);/發(fā)送函數(shù)skbfragskb-next=frag;=skb-next;=NULL;而當(dāng)L4層傳遞過來的包沒有滿足快速分片的要求時,它只能使用一般的分片辦法了,這個辦法原理很簡單,假設(shè)L4傳遞過來的一個包大小為4000字節(jié),而MTU大小為1500,那這個方法就是通過一個循環(huán),每次分配一個skb,大小分別為1500,1500,1000(忽略頭部與對齊、填充等空間),然后再將原來的skb里的IP頭,部分?jǐn)?shù)據(jù)拷貝進(jìn)新分配的skb中去,直至所有的數(shù)

33、據(jù)都拷貝完成。slow_path:left=skb-len-hlen;/*Spaceperframe*/原始長度ptr=raw+hlen;/*Wheretostartfrom*/offset=(ntohs(iph-frag_off)&IP_OFFSET)frag_off&htons(IP_MF);while(left0)len=left;/*IF:itdoesntfit,usemtu-thedataspaceleft*/if(lenmtu)/每次拷貝的長度len=mtu;if(skb2=alloc_skb(len+hlen+ll_rs,GFP_ATOMIC)=NULL)/出錯處理設(shè)置新的skb

34、頭與復(fù)制IP頭ip_copy_metadata(skb2,skb);skb_reserve(skb2,ll_rs);skb_put(skb2,len+hlen);skb2-nh.raw=skb2-data;skb2-h.raw=skb2-data+hlen;memcpy(skb2-nh.raw,skb-data,hlen);復(fù)制數(shù)據(jù)部分,注意不能簡單的memcpy,因為skb中可以存在數(shù)據(jù)分片if(skb_copy_bits(skb,ptr,skb2-h.raw,len)BUG();left-=len;/*Fillinthenewheaderfields.*/iph=skb2-nh.iph;i

35、ph-frag_off=htons(offset3);if(offset=0)ip_options_fragment(skb);if(left0|not_last_frag)iph-frag_off|=htons(IP_MF);ptr+=len;offset+=len;iph-tot_len=htons(len+hlen);ip_send_check(iph);err=output(skb2);2、ip_defrag當(dāng)L3層在將數(shù)據(jù)包上傳給L4層的時候,它如果發(fā)現(xiàn)這個包只是一個IP分片,那么它將調(diào)用ip_defrag這個函數(shù),將這個分片暫時隊列起來,如果所有的分片都到齊,它便將這些分片重裝成一

36、個完整的IP包再上傳給L4層。由下圖看到,系統(tǒng)為分片準(zhǔn)備了一個hash表ipq_hash,大小為64,并且每個ipq結(jié)構(gòu)代表一個被重裝成一個完成IP包的所有IP分片的隊列頭,將被重裝的skb存放在這個ipq的fragments鏈表中。注意,同一個hash鏈中的ipq結(jié)構(gòu)是相互獨(dú)立無關(guān)的,它們只是hash表的沖突鏈表。如下圖所示,ID為1234的IP包收到了2個分片,它們根據(jù)offset的值順次插入到這個ipq的fragments鏈表中。2565127&B卿IK12ST-12K圖3.1SUZSHS至ElsaddrdaddrProtocolIDFugmenwusaddrdaddrProtocolI

37、DFragments毎uttddrdaddrProtocolIDFragmentsipq.hash$Mdrddd曲ProtocolIDFragmentsBczstructipqstructipqsaddrlPldaddr=IP2pr(rtool-IPPROTOTCPid=1234len=996meat-384-fragmenisstructipqoffset25&她t=768structskbuffstructskbuff啟q備lgnh.iph;structipq*qp;structnet_device*dev;/*Startbycleaningupthememory.*/if(atomic_

38、read(&ip_frag_mem)sysctl_ipfrag_high_thresh)ip_evictor();/如果分片的數(shù)量超過了sysctl_ipfrag_high_thresh則調(diào)用這個函數(shù)釋放掉一些LRUipq。dev=skb-dev;/*Lookup(orcreate)queueheader*/if(qp=ip_find(iph,user)!=NULL)structsk_buff*ret=NULL;spin_lock(&qp-lock);ip_frag_queue(qp,skb);/將這個skb插入到qp的fragment隊列中if(qp-last_in=(FIRST_IN|LA

39、ST_IN)&qp-meat=qp-len)/所有的分片已收到ret=ip_frag_reasm(qp,dev);/將這些分片重裝spin_unlock(&qp-lock);ipq_put(qp,NULL);returnret;2.1、p_frag_queue最主要的工作就是將收到的skb插入相應(yīng)的ipq,但它比想像中要復(fù)雜,因為它要處理很多出錯的情況,如收到多個相同的分片,收到重疊的分片等。先來看看ipq結(jié)構(gòu):/*Describeanentryintheincompletedatagramsqueue.*/structipqstructhlist_nodelist;structlist_headlru_list;/*lruli

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論