Introduction, entry and configuration of Linux NAT soft routing

In the article NAT Network Address Translation Technology Introduction to Detailed Explanation , we introduced the principle of NAT, the classification of NAT, and the function of each type of NAT.
netfilter/iptables is the kernel software and user configuration tool software used to implement NAT. In this article we will describe in detail how to configure and implement NAT using iptables.

1. Confirm the version of the Linux kernel

The usual Linux distributions already come with the two software netfilter and iptables and other kernel modules needed for NAT (such as connection tracking, IP forwarding and other kernel function modules), so usually you only need to use them directly , we do not need to compile and modify the Linux kernel. If you need to compile the Linux kernel yourself, you can refer to the book HowTo on compiling Linux 2.4 and 2.6 kernels (you can find this book on this page http://www.digitalhermit.com/linux/Kernel-Build-HOWTO.html ).

2. Introduction to nat table of netfilter

We already know that the netfilter framework contains the so-called 4 tables and 5 chains . Here we will need to use the nat table in the 4 tables to implement the NAT function. The nat table contains 4 chains - PREROUTING, INPUT, POSTROUTING and OUTPUT. Each chain contains multiple rules, and a packet will be matched in order until it hits a certain rule. We can use the command sudo iptables -t nat -L to find these chains in the nat table.

xxx@raspberrypi:~ $ sudo iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

OUTPUT chain and INPUT chain are not used in this article, the names of PREROUTING and POSTROUTING chains indicate their purpose. After the message enters Linux, the Linux kernel checks the rules in the PREROUTING chain before making a routing decision based on the routing table, so we can use the PREROUTING chain to change the destination IP address of the message, and then let the next routing table To decide how to process the packet ( DNAT ) based on the new destination IP address . After the Linux kernel makes a forwarding decision for a message based on the routing table, the next step will be processed according to the rules in the POSTROUTING chain , which means that here, we can change the source IP address ( SNAT ) of the message to be sent out .
insert image description here

注意:这里和报文从哪个网口进,哪个网口出没有关系,所有进入Linux的的非上送给Linux本机而是需要被转发的报文都会经过这条路径进行处理。

The diagram above shows how DNAT and SNAT work.
DNAT means modifying the target IP address of the message, so the Linux kernel must complete the modification of the target IP address before looking up the routing table according to the target IP address of the message and deciding how to forward the IP message (so it is done by the PREROUTING chain). For example: if we want to do DNAT for a private network server 192.168.1.3, then when the Linux router receives an IP packet from the public network, it needs the destination IP address of the received IP packet to be 192.168.1.3, and then Hand over to the Linux kernel for routing lookup and forwarding decisions:

router:~# route -n
Kernel IP routing table
Destination 	Gateway	Genmask				Flags 	Metric 		Ref		Use Iface
192.168.1.0 	0.0.0.0	255.255.255.0		U		0			0		0 	eth1

The Linux kernel finds that the 192.168.1.0/24 network is directly connected to the Eth1 network card, so it will directly send the message from Eth1 to 192.168.1.3.

SNAT means that the source IP address of the IP packet is modified. Let's consider how IP Masquerade Masquerade works, it will help our understanding. A packet sent from 192.168.1.3 wants to access the external network.

  • The Linux kernel will search the routing table. If the message needs to be sent out by an interface, it will modify the source IP address of the message to the IP address of this interface.
  • If no matching routing table entry is found for the message, it will be routed to the default route, and then the source IP address of the message will be modified to the interface pointed to by the default route.

The syntax for adding, inserting, or deleting rules at the end of a NAT table is very similar:

  • Append adds at the end: iptables –t nat –A
  • Insert to the front: iptables –t nat –I
  • Delete Delete: iptables –t nat –D
  • Delete all Delete all: iptables –t nat –F

Here should be PREROUTING, POSTROUTING, INPUT or OUTPUT, replaced by the real rules.

注意:iptables –F 命令是删除了默认table, filter table里的所有chain的所有规则,如果你要删除nat table里的规则,需要用-t来指定table。

3. Implement SNAT with iptables

SNAT is the most commonly used type of several NAT technologies. Take the following example as an example:
192.168.1.0/24 is the network segment of our office private network. We can access the external network through the IP of 1.2.3.1/30, and the default gateway is 1.2.3.2. The
default gateway of all internal network hosts is 192.168.1.1.
insert image description hereWe can see that the Linux router has two Ethernet ports:

  • Eth0, the IP address is 192.168.1.1 (mask 255.255.255.0), this network port is connected to the private network 192.168.1.0/24 through the switch.
  • Eth1, IP address is 1.2.3.1 (mask 255.255.255.252), connected to the external network,
    we can use the following rule to establish SNAT for all devices in the private network 192.168.1.0/24, and make all devices in the private network All devices can access the external network:
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.3.1

The function of this command is the same as that of the following command, but using the following IP masquerading command, we can support the dynamic IP address of Eth1 without specifying the IP of the external network like the above command Address (note that IP masquerade Masquerade replaces the source address with the IP address of the egress NIC of the specified route, so it can support dynamically obtaining the IP address of the egress NIC instead):

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE

If our network provider says that only ports below 1024 can be used, then we need to modify the source port in addition to modifying the source IP address network. This SNAT rule can also be used to modify the port, as shown below. Change the port to an unused port number between 1-1024:

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.3.1:1-1024

Note that if the laptop users in the network want to access IRC or FTP, then it is necessary to add ip_conntrack_irc
and ip_conntrack_ftp modules for ip_conntrack. The layer also has IP address information, so additional functional modules are required to handle the replacement of IP information in the user layer.

modprobe ip_conntrack_irc
modprobe ip_conntrack_ftp
#或者
insmod ip_conntrack_irc
insmod ip_conntrack_ftp

Below we use a few examples to learn some advanced settings of SNAT to solve some common problems in SNAT:

3.1. Many-to-many (N:N) SNAT

The server side of the IRC network limits the number of client connections from the same IP, which means that when multiple hosts in the private network use SNAT to access the IRC server, the IRC server will detect that they are from the same IP (1.2. 3.1) If the number of client connections exceeds the upper limit, the IRC server will reject the connection after the limit is exceeded. At this point, we can reduce the number of IRC connections using each public IP address as the source IP address by increasing the number of public IP addresses and SNATing private IP addresses to different public IP addresses. After we get all the public IP addresses in the additional 1.2.4.0/27 subnet, we modify the SNAT rule as follows:

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32

This solves the problem, but with such a command, the public IP address of 1.2.3.1 is not added to the SNAT IP pool, and this public IP address is not used, so modify the above command as follows:

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1

3.2. Remove a certain public IP in a network segment from the public IP address pool available to SNAT

For example, we need to remove 1.2.4.15 from the available public IP address pool, then we can use the following command

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.14 --to 1.2.4.16-1.2.4.32 --to 1.2.3.1

3.3. Set the destination address to a specific IP address or network segment without NAT

This is usually used for two purposes:

  • Prevent the private network users from modifying the network configuration, forwarding the internal network access packets to the Linux router, and then being mistakenly SNATed by the Linux router, which will cause the hosts in the private network to be unable to connect.

For example, the 192.168.1.19 host in the private network changes its network mask from 192.168.1.0/24 to 192.168.1.0/27, which means that the 192.168.1.19 host thinks that it only has the IP addresses of 192.168.1.1-192.168.1.31 It is under the same subnet as it, so when it visits the IP address 192.168.1.32, it will first send the message to its default gateway 192.168.1.1, which is Linux Router, and then Linux Router when SNAT is not configured The message will be forwarded to the host 192.168.1.32 according to the routing table, but the SNAT function is set at this time, so Linux Router will first modify the source address of all messages from 192.168.1.19 to 1.2.3.1 or After other public network IPs send the message to 192.168.1.32, when 192.168.1.32 sends back a response message, the response message is sent to 1.2.3.1, and this message cannot be returned to 192.168.1.19. Therefore, the network between 192.168.1.19 and 192.168.1.32 is disconnected. We use the following command to solve this problem, and do not perform SNAT function on all packets whose destination address is in the network segment of 192.168.1.0/24.

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d ! 192.168.1.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1

Or use -o eth1 to specify to only do SNAT on packets sent from Eth1:

iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
  • Prohibit users in the private network from accessing certain public network servers (for example, do not perform SNAT on packets whose destination address is the host IP address under the 1.3.3.0 network segment)
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d ! 1.3.3.0/24 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
  • SNAT is not performed for packets between servers on two different network segments in the LAN.
    Suppose there are two network segments 192.168.1.0/24 and 192.168.2.0/24 in our private network, and both network segments are connected to the Linux Router. , then the following rules need to be added to avoid SNAT for packets that access each other between the two network segments
iptables -t nat -I POSTROUTING -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
iptables -t nat -I POSTROUTING -s 192.168.2.0/24 -d 192.168.1.0/24 -j ACCEPT

These two commands will insert these two rules before the NAT rules (actually -I will insert the current rule before all existing rules), when 192.168.2.0/24 and 192.168.1.0/24 communicate with each other , the packet will first analyze and hit these two rules, and will not analyze the subsequent rules after being hit, so SNAT will not occur.

3.4. Through the port number, set to allow or prohibit SNAT-specific protocols

  • The blacklist system
    assumes that the leader does not want specific employees to access IRC. We know that the IRC server works on port 6666-6669, so we only need to send the IP address of the employee’s computer to access the port 6666-6669 of the target host. If all the packets can be discarded, the following rules can be inserted to implement:
iptables -t nat -I POSTROUTING -s 192.168.1.31 -p tcp --dport 6666:6669 -j DROP
  • Semi-whitelist system
    Or only allow access to specific ports, for example, an employee is only allowed to access web pages, and other protocol services are not available, then we can only allow him to access TCP port 80, and all other data will be discarded:
iptables -t nat -I POSTROUTING -s 192.168.1.31 -p tcp --dport ! 80 -j DROP  

Note that this is not the same as the whitelist system. What is discarded here is the TCP protocol other than port 80, but the communication of the UDP protocol is still possible.

  • Whitelist system
    If you want to implement a whitelist system, you need to add a rule that discards all packets after one or more whitelist rules (the configuration example is to be updated ).

4. Implement DNAT with iptables

The function and principle of DNAT have been introduced in the introduction to the detailed explanation of NAT network address translation technology . We assume that the readers of this article have already understood the function and principle of DNAT, so here is just a simple how to configure and use it.

4.1. If you need full access to a host in the private network from the external network (such as 192.168.1.50).

Bind a public IP address (such as 1.2.4.1)) to the host 192.168.1.50, then we can implement this function through DNAT:

iptables -t nat -A PREROUTING -d 1.2.4.1 -j DNAT --to 192.168.1.50

After configuration, all our access to 192.168.1.50 can be done through 1.2.4.1.

4.2. Access a certain service of a server in the private network from a specific external network IP

This is based on the principle of minimum authorization for security considerations, strictly restricting visitors (through the visitor's IP address) and strictly restricting the services that can be accessed. If you need to access the intranet web service of 192.168.1.100 from 1.2.5.17, then after we set the following rules, he can access the intranet web service of 192.168.1.100 through 1.2.4.2.

iptables -t nat -A PREROUTING -s 1.2.5.17 -d 1.2.4.2 -p tcp --dport 80 -j DNAT --to 192.168.1.100

Also for security reasons, our DNAT can help us hide information and increase security. For example, we want to access the intranet web server through SSH from any host on the external network, because the intranet web server often contains very important information, so directly map a public network IP address to the private network IP of the intranet web server through DNAT The address is not a very secure solution. This will completely expose the SSH service of our intranet web server and increase the risk of being attacked by the network. A safer way is to map the port, and the attacker will not Knowing which port we mapped the SSH port to, this will hide the SSH port from the outside world, thereby increasing security (this is already PAT or NAPT in essence). Use port 65521 to map port 22 as follows.

iptables -t nat -A PREROUTING -d 1.2.4.2 -p tcp --dport 65521 -j DNAT --to 192.168.1.100:22

Under this configuration, if we want to ssh from the external network to the internal network web server, we need ssh 1.2.4.2:65521.

We deploy the company's extranet web server on 192.168.1.200, and map the domain name www.mycompany.com to 1.2.4.5 through the DNS service. In order to make the extranet web server accessible from the extranet, we need to set the following rules:

iptables -t nat -A PREROUTING -d 1.2.4.5 -p tcp --dport 80 -j DNAT --to 192.168.1.200

5. Transparent proxy

Transparent proxying is a method of forcing users to use a proxy server, using a transparent proxy does not require users to configure a proxy on their terminal or their browser. There are many advantages to using a proxy server, such as saving traffic through the web page cache on the proxy server, and access control (such as prohibiting the download of dangerous files). Another advantage of using a transparent
proxy is that it can prevent users from bypassing the proxy server by themselves. This can prevent users from accessing harmful websites such as pornography, gambling, and drugs. If your proxy server works on port 3128 of Linux Router, then we can use the following command to implement transparent proxy:

iptables -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j REDIRECT --to-port 3128

Forward the IP packets from the 192.168.1.0/24 network segment to access the webpage to port 3128.

If you want some hosts in the private network to directly access the external network without going through a proxy, you can add an exception to it with the following command:

iptables -t nat -I PREROUTING -s 192.168.1.50 -p tcp --dport 80 –j ACCEPT

This rule will be hit in PRE-ROUTING, and then access the external network through the SNAT function in POSTROUTING.

6. Configuration scripts for the above scenarios

#!/bin/bash
IP=/sbin/iptables
#... some packet filtering rules
### NAT SECTION
#first of all, we want to flush the NAT table
$IP -t nat -F
############ SNAT PART
#特定电脑只能访问web和DNS.
#Don't SNAT any TCP connections from her computer except www and all
#udp connections except DNS
$IP -t nat -A POSTROUTING -s 192.168.1.31 -p tcp --dport ! 80 -j DROP
$IP -t nat -A POSTROUTING -s 192.168.1.31 -p udp --dport ! 53 -j DROP
#公司内部的二个子网间的通信不要做NAT
#Don't SNAT anything from 192.168.1.0/24 to 192.168.2.0/24
$IP -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.2.0/24 -j ACCEPT
#映射192.168.1.50成1.2.4.1使得192.168.1.50的数据访问固定从1.2.4.1发出
$IP -t nat -A POSTROUTING -s 192.168.1.50 -j SNAT --to 1.2.4.1
#从eth1出去的报文都要做SNAT
$IP -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth1 -j SNAT --to 1.2.4.0-1.2.4.32 --to 1.2.3.1
############ DNAT PART
#Dnat映射192.168.1.50成1.2.4.1使得192.168.1.50可以通过1.2.4.1自由访问
$IP -t nat -A PREROUTING -d 1.2.4.1 -j DNAT --to 192.168.1.50
#DNAT 特定的外部主机1.2.5.17可以访问内网web服务器
$IP -t nat -A PREROUTING -s 1.2.5.17 -d 1.2.4.2 -p tcp --dport 80 -j DNAT --to 192.168.1.100
#DNAT转换SSH登录的端口
$IP -t nat -A PREROUTING -d 1.2.4.2 -p tcp -dport 65521 -j DNAT --to 192.168.1.100:22
#DNAT 将外网web服务器部署到内部主机上
$IP -t nat -A PREROUTING -d 1.2.4.5 -p tcp --dport 80 -j DNAT --to 192.168.1.200
############ 透明代理
#允许192.168.1.50绕过代理
$IP -t nat -A PREROUTING -s 192.168.1.50 -p tcp --dport 80 -j ACCEPT
#给所有其它人设置透明代理
$IP -t nat -A PREROUTING -s 192.168.1.0/24 -p tcp --dport 80 -j REDIRECT --to-port 3128
### End of NAT section

7. Verify the configuration script

We can look at several chains in the nat table to verify that the configuration is correctly configured.

root@router:~# iptables -t nat -L -n
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DNAT all -- 0.0.0.0/0 1.2.4.1 to:192.168.1.50
DNAT tcp -- 1.2.5.17 1.2.4.2 tcp dpt:80
to:192.168.1.100
DNAT tcp -- 0.0.0.0/0 1.2.4.2 tcp dpt:65521
to:192.168.1.100:22
DNAT tcp -- 0.0.0.0/0 1.2.4.5 tcp dpt:80
to:192.168.1.200
ACCEPT tcp -- 192.168.1.50 0.0.0.0/0 tcp dpt:80
REDIRECT tcp -- 192.168.1.0/24 0.0.0.0/0 tcp dpt:80 redir
ports 3128
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
DROP tcp -- 192.168.1.31 0.0.0.0/0 tcp dpt:!80
DROP udp -- 192.168.1.31 0.0.0.0/0 tcp dpt:!53
ACCEPT all -- 192.168.1.0/24 192.168.2.0/24
SNAT all -- 192.168.1.50 0.0.0.0/0 to:1.2.4.1
SNAT all -- 192.168.1.0/24 0.0.0.0/0 to:1.2.4.0-1.2.
4.32 1.2.3.1
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
root@router:~#

We can see that the configuration items in the script have all correctly appeared in the nat table. If we want to verify whether the function is correct, then we need to build the corresponding server, send the corresponding message, and then use packet capture tools such as TCPdump and ethereal to verify.
For example, we capture packets on the Linux router, and then initiate a connection from the external network to a random target port of 1.2.4.1, the TCPdump will display as follows:

  1. Received a packet from 2.2.2.2 to 1.2.4.1 on Eth0
  2. Send a packet from 1.2.3.1 on Eth0, the destination is unreachable
    No packet on Eth1, because no DNAT rule is hit. If a rule is hit, we can use the -v option to view it (iptables –L –n –v).

8. Abnormal scenario: Double NAT

Double NAT is a method used to solve the problem of connecting to a network with overlapping IP addresses. In the network shown in the figure below, there are two LANs connected together through VPN, but the IP addresses of the two LANs overlap. There are two solutions: 1) Change the network segment of one of the LANs and redeploy, 2) Use Double NAT to resolve this conflict.
insert image description here
Under normal circumstances, the two LANs work on different network segments. We configure the routing table on the two Linux Routers, and through the VPN interface, the two LANs can communicate normally without NAT. But we all know that an exception has occurred here. The two LANs use exactly the same network segment, so there is no way for the two networks to communicate with each other only by the routing function. We have to "pretend" that the two LANs use different network segments. Only when the network segment is specified, can the two LANs communicate with each other. So, the solution is: we tell the LAN on the left to work on the network segment 192.168. side can communicate normally.
Below we will take two 192.168.1.60 as an example to illustrate how to configure them so that they can communicate normally. Taking this as an example, it can be extended to all other hosts in 192.168.1.0/24. You can communicate normally.

After the configuration is complete, our goal is to make 192.168.1.60 on the left think that the IP address of 192.168.1.60 on the opposite side is 192.168.20.60; let 192.168.1.60 on the right think that the IP address of 192.168.1.60 on the opposite side is 192.168.10.60.

8.1. Configure Linux Router 1 (left side)

  • Step 1
ifconfig eth1:0 192.168.10.1 netmask 255.255.255.0

This step is not necessary. The IP packets with the destination IP address 192.168.10.0/24 will arrive at Linux Router 1. We don't want to forward these packets through the default route, because 192.168.10.0/24 is the fake network segment of the left LAN.

  • Step 2
route add –net 192.168.20.0 netmask 255.255.255.0 gw 10.10.10.2

Add this route so that the IP packets with the destination IP address 192.168.20.0/24 are forwarded to 10.10.10.2 through vpn1.

  • Step 3
iptables -t nat -A POSTROUTING -s 192.168.1.60 -d 192.168.20.60 -j SNAT --to 192.168.10.60

Create a SNAT rule and change the source IP address of the packet sent from 192.168.1.60 to 192.168.20.60 to 192.168.10.60, so as to pretend that the network on the left is 192.168.10.0/24.

  • Step 4
iptables -t nat -A PREROUTING -d 192.168.10.60 -j DNAT --to 192.168.1.60

Configure DNAT rules on Linux Router 1, and modify the destination IP address of all IP packets arriving at the router whose destination address is 192.168.10.60 to 192.168.1.60, so that the packets can be sent to 192.168.1.60 correctly.

8.2. Configure Linux Router 2 (left side)

On Linux router 2, it is basically a similar configuration, but the target and source are reversed.

  • Step 1
ifconfig eth1:0 192.168.20.1 netmask 255.255.255.0
  • Step 2
route add -net 192.168.10.0 netmask 255.255.255.0 gw 10.10.10.1

Add this route so that the IP packets with the destination IP address 192.168.10.0/24 are forwarded to 10.10.10.1 through vpn1.

  • Step 3
iptables -t nat -A POSTROUTING -s 192.168.1.60 -d 192.168.10.60 -j SNAT --to 192.168.20.60

Create a SNAT rule and change the source IP address of the packet sent from 192.168.1.60 to 192.168.10.60 to 192.168.20.60, so as to pretend that the network on the right is 192.168.20.0/24.

  • Step 4
iptables -t nat -A PREROUTING -d 192.168.20.60 -j DNAT --to 192.168.1.60

Configure the DNAT rule on Linux Router 2, and modify the destination IP address of all IP packets arriving at the router with the destination address 192.168.20.60 to 192.168.1.60, so that the packets can be sent to 192.168.1.60 correctly.

The above is the configuration required for all Double NAT. After the configuration is completed, the two 192.168.1.60 can communicate.

  • When the 192.168.1.60 host on the left communicates with the 192.168.1.60 host on the right :
    • It thinks the IP address of the host on the right is 192.168.20.60. Because 192.168.20.60 is the IP address of another network segment, this message will be forwarded to the default gateway (ie Linux Router 1).
    • Linux Router 1 will analyze the rules in the PREROUTING chain and find that no rules are hit.
    • Next, it will look up the local routing table and find that the packet sent to 192.168.20.60 should be sent to 10.10.10.2 through vpn1,
    • Then Linux Router analyzes the rules in the POSTROUTING chain and finds that it hits the rule "so the packets sent from 192.168. The rule modifies the source IP address of the IP packet,
    • Then save the connection information in ip_conntrack.
    • Packets with modified source IP addresses are forwarded to 10.10.10.2 through vpn1.
    • After the packet reaches Linux Router 2, Linux Router 2 receives a packet sent from 192.168.10.60 to 192.168.20.60.
    • Linux Router 2 analyzes the rules in its own PREROUTING chain and finds that it hits the rule "if the destination address of a packet is 192.168.20.60, modify the destination address of this packet to 192.168.1.60". Linux Router 2 modifies the destination address of the packet to 192.168.1.60.
    • Then find out in its local routing table that the IP packets sent to 192.168.1.60 should be sent directly through Eth0,
    • Then analyze the rules in the POSTROUTING chain and find that no rules are being hit. Linux Router 2 forwards the message to 192.168.1.60.
  • When the 192.168.1.60 host on the right communicates with the 192.168.1.60 host on the left :
    the process is the same, but the direction is reversed.

At this point, we have completed the successful communication between the two hosts with overlapping IPs through Double NAT.

Guess you like

Origin blog.csdn.net/meihualing/article/details/130275346