OpenVPN莫名其妙断线的问题及其解决分析.pdf

上传人:l*** 文档编号:73152346 上传时间:2023-02-15 格式:PDF 页数:14 大小:604.63KB
返回 下载 相关 举报
OpenVPN莫名其妙断线的问题及其解决分析.pdf_第1页
第1页 / 共14页
OpenVPN莫名其妙断线的问题及其解决分析.pdf_第2页
第2页 / 共14页
点击查看更多>>
资源描述

《OpenVPN莫名其妙断线的问题及其解决分析.pdf》由会员分享,可在线阅读,更多相关《OpenVPN莫名其妙断线的问题及其解决分析.pdf(14页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。

1、 OpenVPN 莫名其妙断线的问题及其解决 1.问题 不得不说,这是一个 OpenVPN 的问题,该问题几乎每个使用 OpenVPN 的人都碰到过,也有很多人在网上发问,然而一直都没有人能给出解决办法,甚至很多帖子上表示因为这个问题而放弃了使用 OpenVPN。说实话,我面临这个问题已经两年有余,自从第一次接触 OpenVPN,这个问题就一直困扰着我,去过国内外各大论坛也没有找到满意的结果。这几天终于有点闲暇,我决定自己去摸索一下,要感谢公司能给我提供一个环境!最终,我取得了突破性的进展,还是那句话,我把这个结果贴了出来,就是为了以后人们再面临这个问题时可以多一个可选的答案。顺便说一下,并不

2、能说明网上就没人解决过这个问题,因为我所能看到并理解的,只有中文或者英文的帖子或者文章,虽然日文的也在我老婆的帮忙翻译下看过一些,但是还有大量的德文,意大利文,韩文等作为母语的人写出的东西我无法找到并且理解它,因此为了通用性,我本应该用英文来写这篇文章,然而英文水平太垃圾,怕那样连中国人都不能理解了.问题是这样的,OpenVPN 在跨越公网上连接时,会莫名其妙的时不时断开,但不经常,也不绝对!由于大部分人使用 Windows 版本的作为 OpenVPN 客户端,因此起初一直一为是 Windows 本身的问题,然而当我用 Linux 客户端连接时,还是一样,这就是说,很大程度上冤枉了 Windo

3、ws(也并不是完全冤枉,起码 Linux 就没有 DHCP 租约的问题),于是既然有了环境,那就折腾一番,因此又是一个惊魂 48 小时。以下是在客户端断开时服务端的日志(频繁的断开就会有频繁的日志,现在仅仅截取一段):2013-07-24 16:53:15 MULTI:REAP range 208-224.2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:5 1 2 3 4 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014

4、succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 1 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=5 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK

5、 output sequence broken:6 5 2 3 4 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 2 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:1801

6、4:P_CONTROL_V1 kid=0 pid=6 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:7 5 6 3 4 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=

7、0 3 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=7 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:8 5 6 7 4 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24

8、16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 4 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=8 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence brok

9、en:9 5 6 7 8 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 5 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0

10、pid=9 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:10 9 6 7 8 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 6 2013-07-24 16:53

11、:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=10 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:11 9 10 7 8 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/

12、218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 7 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=11 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 9 10 11 8

13、2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 9 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 10 11 8 2013-07-24 16:53:17 MULTI:REAP rang

14、e 240-256 2013-07-24 16:53:17 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:17 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 10 2013-07-24 16:53:17 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 11 8 2013-07-24 16:53:17 GET INS

15、T BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:17 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 11 2013-07-24 16:53:17 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 8 2013-07-24 16:53:18 MULTI:REAP range 0-16 2013-07-24 16:53:18 Test

16、证书/218.242.253.131:18014 TLS:tls_pre_encrypt:key_id=0 2013-07-24 16:53:18 Test 证书/218.242.253.131:18014 SENT PING 2013-07-24 16:53:18 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 8 2013-07-24 16:53:18 Test证 书/218.242.253.131:18014 UDPv4 WRITE 53 to 218.242.253.131:18014:P_DATA_V1 kid=

17、0 DATA len=52 2013-07-24 16:53:18 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 8.持续了 60 秒没有收到 ID 为 8 的 ACK,因此一直都是 ACK output sequence broken:12 8 2013-07-24 16:54:15 Test 证书/218.242.253.131:18014 TLS Error:TLS key negotiation failed to occur within 60 seconds(check your network connec

18、tivity)2013-07-24 16:54:15 Test 证书/218.242.253.131:18014 TLS Error:TLS handshake failed with peer 0.0.0.0 没隔一段时间就会断一次,并且重连还不一定总能重连成功!因此这里的问题有两点:a.连接正常时断开(ping-restart 的情况,上述日志没有展示)b.重连时不成功(上述日志展示的)2.分析 使用 UDP 的 OpenVPN 就是事多,为了避免重传叠加,在恶劣环境下还真得用 UDP。然而OpenVPN 实现的 UDP reliable 层是一个高度简化的“按序确认连接”层,它仅仅确保了

19、数据安序到达,并且有确认机制,和 TCP 那是没法比。不过如果看一下 TCP 最初的方案,你会发现,TCP 的精髓其实就是 OpenVPN 的 reliable 层,后来的复杂性都是针对特定情况的优化!和 TCP 的实现一样,不对 ACK 进行 ACK 对发送端提出了重传滑动窗口未确认包的要求,因为纯 ACK 可能会丢失,这里先不讨论捎带 ACK。ACK 一旦丢失,发送端肯定就要重传没有被 ACK 的包,关键是“什么时候去重传它?”,协议本身一般都有一个或者多个 Timer,Timer 到期就重传,然而我个人认为这个 Timer 不能依赖上层,而要在协议本身实现,毕竟重传这种事对上层是不可见的

20、!然而,OpenVPN 的 reliable 层在 ACK 丢失的应对方面却什么都没有实现,通过以上的日志可以看出,连续的:Test 证书/218.242.253.131:18014 ACK output sequence broken:12 8 说明 ID 为 8 的包一直都得不到重传,并且从:2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 6 2013-07-24 16:53:16 Test证 书/218.242.253.131

21、:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=10 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:11 9 10 7 8 2013-07-24 16:53:16 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4

22、 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 7 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 WRITE 114 to 218.242.253.131:18014:P_CONTROL_V1 kid=0 pid=11 DATA len=100 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 9 10 11 8 2013-07-24 16:53:16 GET INS

23、T BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:16 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 9 2013-07-24 16:53:16 Test 证书/218.242.253.131:18014 ACK output sequence broken:12 10 11 8 2013-07-24 16:53:17 MULTI:REAP range 240-256 2013-07-24 16:53:

24、17 GET INST BY REAL:218.242.253.131:18014 succeeded 2013-07-24 16:53:17 Test证 书/218.242.253.131:18014 UDPv4 READ 22 from 218.242.253.131:18014:P_ACK_V1 kid=0 10 这几行日志可以看出,确实是没有收到 ID 为 8 的包地 ACK,说明它丢失了,接下来发送的数据包将持续填充发送窗口,直到填满,ID 为 8 的包还未重传并且收到对端对其的 ACK,因此就导致了 ACK output sequence broken,通过查代码,12-8=4,而

25、 4 正是发送窗口的长度。持续了很久 ACK output sequence broken 之后,还是没有重传,直到:a.隧道建立之后的 ping-restart 过期 b.隧道建立阶段的 TLS handshake failed 实际上,正确的方式应该是,检测到窗口爆满就应该马上重传。TCP 通过三次重复 ACK 知晓丢包,而 OpenVPN 的 reliable 则通过 ACK output sequence broken 知晓 ACK 丢失,这是一个信号,应该在获取这个信号后做点什么了!3.原始方案 方案很简单,那就是在打印 ACK output sequence broken 的逻辑块

26、内重传所有未确认的包,然而作为一种优化,仅仅重传 ID 最小的包即可。这是因为,之所以到达 ACK output sequence broken,是因为窗口满了,之所以满是因为 ID 最小的包未确认,占据了很大的一块空间以及其后面的实际上可能已经确认了的空间,因此只要 ID 最小的包被确认,窗口就放开了,故而仅重传 ID 最小的包,以期待对端能再次给出确认。方案虽简单,但是不落实到代码还是 0,以下是一些尝试 4.第一次尝试-出错 7 月 25 日下班后,又睡不着了,自己躲在女儿的小屋,开始了 coding。首先确认一下对于乱序或者重放的包,对端也能 ACK,如果不能,那就要大改了,找到了 s

27、sl.c 的代码,在tls_pre_decrypt 中:plain view plain copy if(op!=P_ACK_V1&reliable_can_get(ks-rec_reliable)packet_id_type id;/*Extract the packet ID from the packet*/if(reliable_ack_read_packet_id(buf,&id)/*Avoid deadlock by rejecting packet that would de-sequentialize receive buffer*/if(reliable_wont_break

28、_tiality(ks-rec_reliable,id)if(reliable_not_replay(ks-rec_reliable,id)/*Save incoming ciphertext packet to reliable buffer*/struct buffer*in=reliable_get_buf(ks-rec_reliable);ASSERT(in);ASSERT(buf_copy(in,buf);reliable_mark_active_incoming(ks-rec_reliable,in,id,op);/注意这个注释,即使是重放包也 ACK!而我解决 ACK 丢失的思路

29、正是重放那个迟迟收不到 /ACK 的包,期待对端发送 ACK,按照随机丢包概率,针对该包的 ACK 总不能一直丢失吧!/*Process outgoing acknowledgment for packet just received,even if its a replay*/reliable_ack_acknowledge_packet_id(ks-rec_ack,id);有了以上的基础,起码我知道,针对 OpenVPN 的 reliable 层修改的代码不多!接下来就是找到修改哪里了,当然是哪里出问题修改哪里!之所以僵持在那里,就是因为“ACK output sequence broke

30、n”,所以说我找到了打印这个的地方,在 reliable_get_buf_output_sequenced函数中:plain view plain copy struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel)struct gc_arena gc=gc_new();int i;packet_id_type min_id=0;bool min_id_defined=false;struct buffer*ret=NULL;/*find minimum active packet_id*/for(i=0;i siz

31、e;+i)const struct reliable_entry*e=&rel-arrayi;if(e-active)if(!min_id_defined|e-packet_id packet_id;/以下判断没有通过的原因,在上面的日志中已经找到了:/.ACK output sequence broken:12 8 /12-8=4,而#define TLS_RELIABLE_N_SEND_BUFFERS 4 if(!min_id_defined|(int)(rel-packet_id-min_id)size)ret=reliable_get_buf(rel);else dmsg(D_REL_

32、LOW,ACK output sequence broken:%s,reliable_print_ids(rel,&gc);gc_free(&gc);return ret;因此仅仅需要在打印 broken 的地方重传 packet_id 为 min_id 的那个 buf 即可!plain view plain copy#ifdef RETRY struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel,int*flag)#else struct buffer*reliable_get_buf_output_sequen

33、ced(struct reliable*rel)#endif struct gc_arena gc=gc_new();int i;packet_id_type min_id=0;bool min_id_defined=false;struct buffer*ret=NULL;#ifdef RETRY struct buffer*retry_buff=NULL;/not named replay_buffer!*flag=0;#endif /*find minimum active packet_id*/for(i=0;i size;+i)const struct reliable_entry*

34、e=&rel-arrayi;if(e-active)if(!min_id_defined|e-packet_id packet_id;#ifdef RETRY /retry_buff=e-buf;ret=&e-buf;#endif /以下判断没有通过的原因,在上面的日志中已经找到了:/.ACK output sequence broken:12 8 /12-8=4,而#define TLS_RELIABLE_N_SEND_BUFFERS 4 if(!min_id_defined|(int)(rel-packet_id-min_id)size)ret=reliable_get_buf(rel);

35、else#ifdef RETRY *flag=1;#endif dmsg(D_REL_LOW,ACK output sequence broken:%s,reliable_print_ids(rel,&gc);gc_free(&gc);return ret;相应地,需要修改该函数的调用逻辑,即 ssl.c 的 tls_process,这里不再给出 ks-state=S_INITIAL 的初始情况:plain view plain copy if(ks-state=S_START)#ifdef RETRY int retry=0;int status=-1;buf=reliable_get_bu

36、f_output_sequenced(ks-send_reliable,&retry);#else buf=reliable_get_buf_output_sequenced(ks-send_reliable);#endif if(buf)#ifdef RETRY if(!retry)#endif status=key_state_read_ciphertext(multi,ks,buf,PAYLOAD_SIZE_DYNAMIC(&multi-opt.frame);if(status=-1)msg(D_TLS_ERRORS,TLS Error:Ciphertext-reliable TCP/U

37、DP transport read error);goto error;#ifdef RETRY else status=1;#endif if(status=1)reliable_mark_active_outgoing(ks-send_reliable,buf,P_CONTROL_V1);INCR_GENERATED;state_change=true;dmsg(D_TLS_DEBUG,Outgoing Ciphertext-Reliable);洋洋洒洒的不合我风格的规整代码,COOL!可是运行之后,ASSERT 失败,明明我重发了 ID 最小的包,却在 write_control_aut

38、h 的:ASSERT(session_id_write_prepend(&session-session_id,buf);这一句华丽丽得退出!发现 buf 竟然不是我要重传的那个 buffer!作为单线程单进程的OpenVPN,不可能有另外什么地方触动这个 buf 啊!5.第二次尝试-成功 失败!夜以沉默,心思向谁说?然而这个问题没有那么复杂,案件的侦破很简单,那就是看代码,终于找到了reliable_schedule_now 函数,关键是它的注释:/*schedule all pending packets for immediate retransmit*/重传!对的,是重传!既然 Ope

39、nVPN 本身有了重传,那么我的那个重传就是多此一举了!因此还是按照步骤来吧,直接调用这个接口即可,话说一定要用既有的接口,千万不要重复实现既有逻辑!于是patch变得更加简单了,仅仅修改一个reliable_get_buf_output_sequenced函数即可:plain view plain copy struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel)struct gc_arena gc=gc_new();int i;packet_id_type min_id=0;bool min_id_define

40、d=false;struct buffer*ret=NULL;/*find minimum active packet_id*/for(i=0;i size;+i)const struct reliable_entry*e=&rel-arrayi;if(e-active)if(!min_id_defined|e-packet_id packet_id;if(!min_id_defined|(int)(rel-packet_id-min_id)size)ret=reliable_get_buf(rel);else#ifdef RETRY reliable_schedule_now(rel);/顺

41、便把日志也改了 dmsg(D_REL_LOW,ACK output sequence broken:%s,retransmit immediately,reliable_print_ids(rel,&gc);#else dmsg(D_REL_LOW,ACK output sequence broken:%s,reliable_print_ids(rel,&gc);#endif gc_free(&gc);return ret;struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel)struct gc_arena gc

42、=gc_new();int i;packet_id_type min_id=0;bool min_id_defined=false;struct buffer*ret=NULL;/*find minimum active packet_id*/for(i=0;i size;+i)const struct reliable_entry*e=&rel-arrayi;if(e-active)if(!min_id_defined|e-packet_id packet_id;if(!min_id_defined|(int)(rel-packet_id-min_id)size)ret=reliable_g

43、et_buf(rel);else#ifdef RETRY reliable_schedule_now(rel);/顺便把日志也改了 dmsg(D_REL_LOW,ACK output sequence broken:%s,retransmit immediately,reliable_print_ids(rel,&gc);#else dmsg(D_REL_LOW,ACK output sequence broken:%s,reliable_print_ids(rel,&gc);#endif gc_free(&gc);return ret;别的什么都不用改!6.优化(去掉副作用)上面的修改虽然简

44、单,但是带来了一个副作用,那就是一次 broken 会带来 reliable 窗口里面 所有的包都会重传,其实我们只需要重传 ID 最小的那个就可以了,毕竟它是罪魁祸首!如果因为网络环境导致的 ACK 丢失,继而重传了所有的包,可能会带来更多的丢包,这个在TCP 上体现最深刻了,因此只重传最小 ID 的那个包,既然它的 ACK 丢失导致了 broken,那么就再发它一次,保证网络管道上的包数量守恒!另外,如果毫无判断地重传,可能会误判很多 ACK 丢包,其实有些 ID 稍微大些的 ACK 并没有丢,它只是乱序到达了而已!没有免费的午餐,因此不得不作的就是修改 reliable_schedule

45、_now 逻辑:plain view plain copy struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel).#ifdef RETRY reliable_schedule_id(rel,0,min_id);/顺便把日志也改了 dmsg(D_REL_LOW,ACK output sequence broken:%s,retransmit immediately,reliable_print_ids(rel,&gc);#else dmsg(D_REL_LOW,ACK output sequence broken

46、:%s,reliable_print_ids(rel,&gc);#endif .struct buffer*reliable_get_buf_output_sequenced(struct reliable*rel).#ifdef RETRY reliable_schedule_id(rel,0,min_id);/顺便把日志也改了 dmsg(D_REL_LOW,ACK output sequence broken:%s,retransmit immediately,reliable_print_ids(rel,&gc);#else dmsg(D_REL_LOW,ACK output seque

47、nce broken:%s,reliable_print_ids(rel,&gc);#endif .真正的修改是 schedule 逻辑:plain view plain copy#ifdef RETRY void reliable_schedule_id(struct reliable*rel,time_t timeout,packet_id_type id)int i;dmsg(D_REL_DEBUG,ACK reliable_schedule_now);rel-hold=false;for(i=0;i size;+i)struct reliable_entry*e=&rel-arrayi

48、;if(e-active)if(id!=0&e-packet_id=id)e-next_try=now+timeout;e-timeout=rel-initial_timeout;break;else e-next_try=now+timeout;e-timeout=rel-initial_timeout;#ifdef RETRY void reliable_schedule_id(struct reliable*rel,time_t timeout,packet_id_type id)int i;dmsg(D_REL_DEBUG,ACK reliable_schedule_now);rel-

49、hold=false;for(i=0;i size;+i)struct reliable_entry*e=&rel-arrayi;if(e-active)if(id!=0&e-packet_id=id)e-next_try=now+timeout;e-timeout=rel-initial_timeout;break;else e-next_try=now+timeout;e-timeout=rel-initial_timeout;#endif /*schedule all pending packets for immediate retransmit*/void reliable_sche

50、dule_now(struct reliable*rel)#ifdef RETRY#调用新接口函数 return reliable_schedule_id(rel,0,0);#else int i;dmsg(D_REL_DEBUG,ACK reliable_schedule_now);rel-hold=false;for(i=0;i size;+i)struct reliable_entry*e=&rel-arrayi;if(e-active)e-next_try=now;e-timeout=rel-initial_timeout;#endif 新的 reliable_schedule_id

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 应用文书 > 工作报告

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号© 2020-2023 www.taowenge.com 淘文阁