为了复现客户环境,我在 VM 虚拟机中安装了 Windows Server 2012 镜像,并部署了 DHCP 服务器。将DHCP 服务器与VPP其中一个接口设置在同一个网段互联,这样可以使用dhcp relay功能了。关于如何在 Windows Server 2012 上部署 DHCP 服务器,网上有很多教程,也可以参考这些教程进行部署。如果需要,也可以使用 Windows Server 2016 进行搭建,步骤类似。
在 VPP 命令行中添加 DHCP 中继服务器地址时,同一个 VRF 下只能指定一个 src-address,但可以配置多个 DHCP 服务器地址。当 DHCP Relay 服务器收到 DHCP DISCOVER 报文后,会将其转换为单播报文,并在 Option 选项中追加 Option 82 的 Sub-option 1 和 Sub-option 5。然后,DHCP Relay 服务器会为每个配置的 DHCP 服务器地址复制一份报文并发送出去。
set dhcp proxy [del] server <ip-addr> src-address <ip-addr> [server-fib-id <n>] [rx-fib-id <n>]
在DHCP 中继代理逻辑中新增了两个node节点,一是dhcp-proxy-to-client 主要处理dhcp 服务器回应给DHCP中继agent的dhcp报文,经过处理之后转发给DHCP 请求终端。另外一个是dhcp-proxy-to-server节点主要是接收到DHCP请求终端发送给DHCP中继agent的dhcp报文,转换成单播报文发送给DHCP server服务器。
这里有疑问报文如何被送两个节点的?通过注册UDP 端口号 67和68,当中继代理服务器收到报文目的端口是68时,表示报文是dhcp 服务器回应的DHCP报文需送到节点dhcp-proxy-to-client 去处理。当中继服务器收到报文的目的端口是67时,表示终端发送给DHCP服务器的报文需送到dhcp-proxy-to-server节点去处理。默认情况下DHCP DISCOVE和DHCP request都是广播报文,目的IP是255.255.255.255,这样就需要设置255.255.255.255路由为local路由,这样就可以送到ip4-udp-loopup节点去解析端口号,将报文送到端口号对应的node节点。在配置前dhcp 中继代理配置命令行前,我们查询了查询路由255.255.255.255 forwarding状态是drop。如下:
dpdk-vpp源码分析: show ip fib 255.255.255.255
ipv4-VRF:0, fib_index:0, flow hash:[src dst sport dport proto flowlabel ] epoch:0 flags:none locks:[adjacency:1, default-route:1, ]
255.255.255.255/32 fib:0 index:4 locks:2
default-route refs:1 entry-flags:drop, src-flags:added,contributing,active,
path-list:[4] locks:2 flags:drop, uPRF-list:4 len:0 itfs:[]
path:[4] pl-index:4 ip4 weight=1 pref=0 special: cfg-flags:drop,
[@0]: dpo-drop ip4
forwarding: unicast-ip4-chain
[@0]: dpo-load-balance: [proto:ip4 index:5 buckets:1 uRPF:4 to:[0:0]]
[0] [@0]: dpo-drop ip4
当我们配置完DHCP 中继代理配置之后,再此查询255.255.255.255路由指向了本地local0路由如下:
dpdk-vpp源码分析: set dhcp proxy server 192.168.1.1 src-address 192.168.10.1
dpdk-vpp源码分析: show ip fib 255.255.255.255
ipv4-VRF:0, fib_index:0, flow hash:[src dst sport dport proto flowlabel ] epoch:0 flags:none locks:[DHCP:2, adjacency:1, default-route:1, ]
255.255.255.255/32 fib:0 index:4 locks:3
DHCP refs:1 entry-flags:local, src-flags:added,contributing,active,
path-list:[19] locks:2 flags:local, uPRF-list:14 len:0 itfs:[]
path:[21] pl-index:19 ip4 weight=1 pref=0 receive: oper-flags:resolved, cfg-flags:local,
[@0]: dpo-receive: 0.0.0.0 on local0
default-route refs:1 entry-flags:drop, src-flags:added,
path-list:[4] locks:1 flags:drop, uPRF-list:4 len:0 itfs:[]
path:[4] pl-index:4 ip4 weight=1 pref=0 special: cfg-flags:drop,
[@0]: dpo-drop ip4
forwarding: unicast-ip4-chain
[@0]: dpo-load-balance: [proto:ip4 index:5 buckets:1 uRPF:14 to:[0:0]]
[0] [@12]: dpo-receive: 0.0.0.0 on local0
接下来,搭建环境学习一下基本转发流程,组网图如下:我们创建2个命名空间tap1和tap2,tap1模拟PC客户端,tap2模拟远程dhcp 服务器。vpp作为DHCP中继代理。
相关配置如下:首先在linux系统上创建2个命名空间:
ip netns add tap1
ip netns add tap2
启动vpp程序,然后在vpp命令行视图中通过exec 命令加载上述组网配置:
creat tap id 1 host-ns tap1 host-if-name tap1
creat tap id 2 host-ns tap2 host-ip4-addr 192.168.1.2/24 host-if-name tap2 host-ip4-gw 192.168.1.1
set interface state tap1 up
set interface ip addr tap1 192.168.2.1/24
set interface state tap2 up
set interface ip addr tap2 192.168.1.1/24
set dhcp proxy server 192.168.3.2 src-address 192.168.10.1
set dhcp proxy server 192.168.1.2 src-address 192.168.10.1
在netns tap2中配置dnsmaq相关配置/etc/dnsmasq.conf,如下:
log-queries
log-facility=/var/log/dnsmasq.log
log-dhcp
dhcp-range=set:tap2,192.168.2.10,192.168.2.200,255.255.255.0,5m
#设置网关地址
dhcp-option=tag:tap2,option:router,192.168.2.1
#设置DNS server地址
dhcp-option=tag:tap2,option:dns-server,114.114.114.114
启动dnsmasq进程:
ip netns exec tap2 dnsmasq -C /etc/dnsmasq.conf
我们可以查询dnsmasq日志,确定dhcp 服务器运行正常
Nov 16 00:16:58 dnsmasq[242831]: started, version 2.90 DNS disabled
Nov 16 00:16:58 dnsmasq[242831]: compile time options: IPv6 GNU-getopt DBus no-UBus i18n IDN2 DHCP DHCPv6 no-Lua TFTP conntrack ipset no-nftset auth cryptohash DNSSEC loop-detect inotify dumpfile
Nov 16 00:16:58 dnsmasq-dhcp[242831]: DHCP, IP range 192.168.2.10 -- 192.168.2.200, lease time 5m
Nov 16 00:17:30 dnsmasq-dhcp[242831]: 1788928788 available DHCP range: 192.168.2.10 -- 192.168.2.200
接下来我们可以启动dhcp 客户端程序。并在vpp中设置trace抓取流程。
ip netns exec tap1 dhclient tap1 &
vpp代码中,设置trace抓包,抓取dhcp交互流程的处理逻辑如下:
#dhcp discover
00:03:29:414728: virtio-input
virtio: hw_if_index 2 next-index 4 vring 0 len 42
hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1
00:03:29:414731: ethernet-input
frame: flags 0x1, hw-if-index 2, sw-if-index 2
ARP: 02:fe:ef:b8:d3:3a -> 02:fe:cd:a0:69:8e
00:03:29:414736: arp-input
reply, type ethernet/IP4, address size 6/4
00:03:29:415393: error-drop
frame: flags 0x1, hw-if-index 1, sw-if-index 1
IP4: 02:fe:57:94:02:cc -> ff:ff:ff:ff:ff:ff
00:03:32:086295: ip4-input
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0x9485
00:03:32:086303: ip4-lookup
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
fib:0 adj:6 flow:0x00000000
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0x9485
00:03:32:086318: ip4-udp-lookup
UDP: src-port 68 dst-port 67
00:03:32:086321: dhcp-proxy-to-server
DHCP proxy: sent to server 192.168.1.2
original_sw_if_index: 1, sw_if_index: 1
DHCP proxy: sent to server 192.168.3.2
original_sw_if_index: 1, sw_if_index: 1
00:03:32:086338: ip4-lookup
fib 0 dpo-idx 4 flow hash: 0x00000000
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 128, length 342, checksum 0xb533 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
fib 0 dpo-idx 0 flow hash: 0x00000000
UDP: 192.168.2.1 -> 192.168.3.2
tos 0x10, ttl 128, length 342, checksum 0xb333 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:32:086343: ip4-rewrite
tx_sw_if_index 2 dpo-idx 4 : ipv4 via 192.168.1.2 tap2: mtu:9000 next:3 flags:[] 02feefb8d33a02fecda0698e0800 flow hash: 0x00000000
00000000: 02feefb8d33a02fecda0698e080045100156000000007f11b633c0a80201c0a8
00000020: 01020044004301420000010106006aa0e31400030000000000000000
00:03:32:086354: ip4-drop
fib:0 adj:0 flow:0x00000000
UDP: 192.168.2.1 -> 192.168.3.2
tos 0x10, ttl 128, length 342, checksum 0xb333 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:32:086357: tap2-output
tap2 flags 0xc0180005
IP4: 02:fe:cd:a0:69:8e -> 02:fe:ef:b8:d3:3a
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 127, length 342, checksum 0xb633 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:32:086362: error-drop
rx:tap1
00:03:32:086365: tap2-tx
buffer 0x9d0ac: current data 0, length 356, buffer-pool 0, ref-count 1, trace handle 0x6
l4-cksum-computed l4-cksum-correct l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:cd:a0:69:8e -> 02:fe:ef:b8:d3:3a
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 127, length 342, checksum 0xb633 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:32:086384: drop
ip4-udp-lookup: No error
#dhcp offer
00:03:35:090272: virtio-input
virtio: hw_if_index 2 next-index 4 vring 0 len 354
hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1
00:03:35:090283: ethernet-input
frame: flags 0x1, hw-if-index 2, sw-if-index 2
IP4: 02:fe:ef:b8:d3:3a -> 02:fe:cd:a0:69:8e
00:03:35:090295: ip4-input
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 340, checksum 0xefc6 dscp CS6 ecn NON_ECN
fragment id 0x04bf
UDP: 67 -> 67
length 320, checksum 0x5678
00:03:35:090303: ip4-lookup
fib 0 dpo-idx 7 flow hash: 0x00000000
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 340, checksum 0xefc6 dscp CS6 ecn NON_ECN
fragment id 0x04bf
UDP: 67 -> 67
length 320, checksum 0x5678
00:03:35:090309: ip4-receive
fib:0 adj:7 flow:0x00000000
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 340, checksum 0xefc6 dscp CS6 ecn NON_ECN
fragment id 0x04bf
UDP: 67 -> 67
length 320, checksum 0x5678
00:03:35:090317: ip4-udp-lookup
UDP: src-port 67 dst-port 67
00:03:35:090326: dhcp-proxy-to-client
DHCP proxy: broadcast to client from 192.168.2.1
original_sw_if_index: 1, sw_if_index: 1
opcode:reply hw[type:1 addr-len:6 addr:02fe579402cc] hops0 transaction-ID:0x14e3a06a seconds:768 flags:0x0 client:0.0.0.0 your:192.168.2.155 server:192.168.1.2 gateway:192.168.2.1 cookie:99.130.83.99, option-53: type:offer, option-54: server:192.168.1.2 option-51: skipped option-118: skipped, option-58: renewal:150 option-59: skipped, option-1: subnet-mask:-256 option-28: skipped, option-6: domian-server:72727272, option-3: router:192.168.2.1 option-82: skipped
00:03:35:090353: tap1-output
tap1 flags 0xc0180005
IP4: 02:fe:7d:93:bb:6d -> ff:ff:ff:ff:ff:ff
UDP: 192.168.2.1 -> 255.255.255.255
tos 0xc0, ttl 64, length 340, checksum 0xb171 dscp CS6 ecn NON_ECN
fragment id 0x04bf
UDP: 67 -> 68
length 320, checksum 0x0000
00:03:35:090356: tap1-tx
buffer 0x9d0fa: current data 0, length 354, buffer-pool 0, ref-count 1, trace handle 0x8
l4-cksum-computed l4-cksum-correct l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:7d:93:bb:6d -> ff:ff:ff:ff:ff:ff
UDP: 192.168.2.1 -> 255.255.255.255
tos 0xc0, ttl 64, length 340, checksum 0xb171 dscp CS6 ecn NON_ECN
fragment id 0x04bf
UDP: 67 -> 68
length 320, checksum 0x0000
#dhcp request
00:03:35:090597: virtio-input
00:03:35:090600: ethernet-input
frame: flags 0x1, hw-if-index 1, sw-if-index 1
IP4: 02:fe:57:94:02:cc -> ff:ff:ff:ff:ff:ff
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090606: ip4-lookup
fib 0 dpo-idx 6 flow hash: 0x00000000
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090608: ip4-receive
fib:0 adj:6 flow:0x00000000
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090611: ip4-udp-lookup
UDP: src-port 68 dst-port 67
00:03:35:090612: dhcp-proxy-to-server
DHCP proxy: sent to server 192.168.3.2
original_sw_if_index: 1, sw_if_index: 1
opcode:request hw[type:1 addr-len:6 addr:02fe579402cc] hops0 transaction-ID:0x14e3a06a seconds:768 flags:0x0 client:0.0.0.0 your:0.0.0.0 server:0.0.0.0 gateway:192.168.2.1 cookie:99.130.83.99, option-53: type:request, option-54: server:192.168.1.2 option-50: skipped, option-12: hostname:6c6561726e696e672d767070 option-55: skipped option-82: skipped
00:03:35:090616: ip4-lookup
fib 0 dpo-idx 4 flow hash: 0x00000000
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 128, length 342, checksum 0xb533 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:35:090618: ip4-rewrite
tx_sw_if_index 2 dpo-idx 4 : ipv4 via 192.168.1.2 tap2: mtu:9000 next:3 flags:[] 02feefb8d33a02fecda0698e0800 flow hash: 0x00000000
00000000: 02feefb8d33a02fecda0698e080045100156000000007f11b633c0a80201c0a8
00000020: 01020044004301420000010106006aa0e31400030000000000000000
00:03:35:090621: tap2-output
tap2 flags 0xc0180005
IP4: 02:fe:cd:a0:69:8e -> 02:fe:ef:b8:d3:3a
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 127, length 342, checksum 0xb633 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:35:090624: tap2-tx
buffer 0x9f7fa: current data 0, length 356, buffer-pool 0, ref-count 1, trace handle 0x9
l4-cksum-computed l4-cksum-correct l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:cd:a0:69:8e -> 02:fe:ef:b8:d3:3a
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 127, length 342, checksum 0xb633 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
#dhcp ack
00:03:35:090597: virtio-input
00:03:35:090600: ethernet-input
frame: flags 0x1, hw-if-index 1, sw-if-index 1
IP4: 02:fe:57:94:02:cc -> ff:ff:ff:ff:ff:ff
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090606: ip4-lookup
fib 0 dpo-idx 6 flow hash: 0x00000000
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090608: ip4-receive
fib:0 adj:6 flow:0x00000000
UDP: 0.0.0.0 -> 255.255.255.255
tos 0x10, ttl 128, length 328, checksum 0x3996 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 308, checksum 0xe38d
00:03:35:090611: ip4-udp-lookup
UDP: src-port 68 dst-port 67
00:03:35:090612: dhcp-proxy-to-server
DHCP proxy: sent to server 192.168.3.2
original_sw_if_index: 1, sw_if_index: 1
00:03:35:090616: ip4-lookup
fib 0 dpo-idx 4 flow hash: 0x00000000
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 128, length 342, checksum 0xb533 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:35:090618: ip4-rewrite
00000000: 02feefb8d33a02fecda0698e080045100156000000007f11b633c0a80201c0a8
00000020: 01020044004301420000010106006aa0e31400030000000000000000
00:03:35:090621: tap2-output
tap2 flags 0xc0180005
IP4: 02:fe:cd:a0:69:8e -> 02:fe:ef:b8:d3:3a
UDP: 192.168.2.1 -> 192.168.1.2
tos 0x10, ttl 127, length 342, checksum 0xb633 dscp unknown ecn NON_ECN
fragment id 0x0000
UDP: 68 -> 67
length 322, checksum 0x0000
00:03:35:090624: tap2-tx
buffer 0x9f7fa: current data 0, length 356, buffer-pool 0, ref-count 1, trace handle 0x9
00:03:35:097735: virtio-input
virtio: hw_if_index 2 next-index 4 vring 0 len 368
hdr: flags 0x00 gso_type 0x00 hdr_len 0 gso_size 0 csum_start 0 csum_offset 0 num_buffers 1
00:03:35:097739: ethernet-input
frame: flags 0x1, hw-if-index 2, sw-if-index 2
IP4: 02:fe:ef:b8:d3:3a -> 02:fe:cd:a0:69:8e
00:03:35:097742: ip4-input
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 354, checksum 0xefb6 dscp CS6 ecn NON_ECN
fragment id 0x04c1
UDP: 67 -> 67
length 334, checksum 0xb807
00:03:35:097745: ip4-lookup
fib 0 dpo-idx 7 flow hash: 0x00000000
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 354, checksum 0xefb6 dscp CS6 ecn NON_ECN
fragment id 0x04c1
UDP: 67 -> 67
length 334, checksum 0xb807
00:03:35:097747: ip4-receive
fib:0 adj:7 flow:0x00000000
UDP: 192.168.1.2 -> 192.168.2.1
tos 0xc0, ttl 64, length 354, checksum 0xefb6 dscp CS6 ecn NON_ECN
fragment id 0x04c1
UDP: 67 -> 67
length 334, checksum 0xb807
00:03:35:097751: ip4-udp-lookup
UDP: src-port 67 dst-port 67
00:03:35:097753: dhcp-proxy-to-client
DHCP proxy: broadcast to client from 192.168.2.1
original_sw_if_index: 1, sw_if_index: 1
opcode:reply hw[type:1 addr-len:6 addr:02fe579402cc] hops0 transaction-ID:0x14e3a06a seconds:768 flags:0x0 client:0.0.0.0 your:192.168.2.155 server:192.168.1.2 gateway:192.168.2.1 cookie:99.130.83.99, option-53: type:ack, option-54: server:192.168.1.2 option-51: skipped option-118: skipped, option-58: renewal:150 option-59: skipped, option-1: subnet-mask:-256 option-28: skipped, option-12: hostname:6c6561726e696e672d767070, option-6: domian-server:72727272, option-3: router:192.168.2.1 option-82: skipped
00:03:35:097759: tap1-output
tap1 flags 0xc0180005
IP4: 02:fe:7d:93:bb:6d -> ff:ff:ff:ff:ff:ff
UDP: 192.168.2.1 -> 255.255.255.255
tos 0xc0, ttl 64, length 354, checksum 0xb161 dscp CS6 ecn NON_ECN
fragment id 0x04c1
UDP: 67 -> 68
length 334, checksum 0x0000
00:03:35:097761: tap1-tx
buffer 0x9d121: current data 0, length 368, buffer-pool 0, ref-count 1, trace handle 0xa
l4-cksum-computed l4-cksum-correct l2-hdr-offset 0 l3-hdr-offset 14
hdr-sz 0 l2-hdr-offset 0 l3-hdr-offset 14 l4-hdr-offset 0 l4-hdr-sz 0
IP4: 02:fe:7d:93:bb:6d -> ff:ff:ff:ff:ff:ff
UDP: 192.168.2.1 -> 255.255.255.255
tos 0xc0, ttl 64, length 354, checksum 0xb161 dscp CS6 ecn NON_ECN
fragment id 0x04c1
UDP: 67 -> 68
length 334, checksum 0x0000
至此,我们通过搭建环境,了解vpp dhcp中继代理的基本配置及业务转发流程。在搭建环境测试中,还遇到了一些BUG,后续整理之后将提交PR。目前看dhcp 中继代理模块距离商业化还很远。继续打补丁。
本文分享自 DPDK VPP源码分析 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!