iptables之nat,tcp_wrapper(第27天)

回顾:

telnet: 23/tcp
samba: 137/udp, 138/udp, 139/tcp, 445/tcp
dns: INPUT: 53/udp, OUTPUT: 53/udp

打开核心转发:两种方式打开核心转发
第1种方式:echo 1 > /proc/sys/net/ipv4/ip_forward
                   sysctl -w net.ipv4.ip_forward=1
第2种方式:编辑/etc/sysctl.conf文件   加入一行 net.ipv4.ip_forward = 1     这种方法永久有效




iptables:
iptables的显式扩展和iptables作为网络防火墙

显式扩展模块有:multiport, iprange, string, time, connlimit, limit, state
其中 对于state扩展模块 :
/proc/net/nf_conntrack   表示 已经追踪到的,并记录下的连接
/proc/sys/net/nf_conntrack_max  表示 能够追踪的连接的最大数量

连接的状态分别有:NEW, ESTABLISHED, RELATED, INVALID


想一个问题:



假设主机A在局域网络里,地址为192.168.20.188,该局域网络里还有一台路由主机B,路由主机B有一个网关,假设为192.168.20.1,还有一个公网IP地址,假设为11.12.13.13,通过该公网IP地址可以与互联网通信。将主机A的网关设置为192.168.1.1。然后主机A发送报文给www.magedu.com ,那么这个报文从主机A流入到路由主机B中,假设该路由主机B有一条默认路由条目:
Destination     Gateway         Genmask         Flags Metric Ref    Use   Iface
0.0.0.0             11.12.13.13          0.0.0.0         UG        100    0        0     eth0
那么这个请求报文会通过11.12.13.13被送出去,并且被www.magedu.com 收到,那么主机A可以收到www.magedu.com的响应报文吗?

答案是不能
因为,主机A发送报文给www.magedu.com ,那么该报文的源地址就是主机A的ip地址192.168.20.188,目标地址为129.3.2.1(假设www.magedu.com解析为129.3.2.1)。这个报文可以流入路由主机B,可以被路由主机B通过默认路由11.12.13.13发送出去,经过网络上的层层路由,到达www.magedu.com上(129.3.2.1),www.magedu.com服务器构建响应报文回应主机A,那么该响应报文的源地址为129.3.2.1,目标地址为192.168.20.188。但是目标地址192.168.20.188是一个私有地址,互联网上的路由器是不会给私有地址路由的。所以因为目标地址私网地址,该响应报文无法被路由出去。


iptables:
nat:Network Address Translation,安全性(隐藏本地网络里的主机),nat通常在网络层+传输层
proxy:代理,应用层


nat的分类
SNAT: 只修改请求报文的源地址;
DNAT:只修改请求报文的目标地址;



NAT的原理:


假设主机A的ip地址为CIP,该主机在内网中。
中间的路由器主机是NAT Server(所谓的NAT Server就是通过iptables规则将来改变数据报文的源地址或者目标地址),该主机打开了路由转发,并有2个网络接口,左边的网络接口GIP用作CIP的网关,右边的网络接口FIP可以连接互联网,并且该主机打开了路由转发。
右边的主机的ip地址为SIP,你可以把它想象成www.google.com或者其他。

当CIP发送报文给SIP之时,报文先到达GIP,此时的报文的源地址是CIP,目标地址是SIP。中间的路由器接收到报文之后,因为开启了路由器主机的路由转发,所以这个报文可以被转发到FIP接口处。此时,NAT Server将报文的源地址改变为FIP,目标地址不改变。NAT Server做完这个动作之后,会在表中记录下CIP------SIP这样的键值对。。所以,从FIP到SIP的过程中,报文的源地址为FIP,目标地址为SIP,然后SIP接收到此报文。SIP收到报文之后,就要构建好响应报文,并发送出去,响应报文的源地址是SIP,目标地址是FIP。路由器主机接收到相应报文之后,由于路由器主机并没有应用进程来接收该报文(netfilter是在内核中实现的),路由器主机会查找上文中提到的表的内容,根据SIP,查找对应的CIP。然后,NAT Server将响应报文的目标地址改变为查找到的CIP地址(也就是那个请求者)。此时的响应报文的源地址就是SIP,目标地址就是CIP。最后,由于路由器主机打开了路由转发,所以该报文可以被路由器主机的GIP接口接收,并发送出去。

所以,SNAT地址转换早期是用来隐藏本地网络里的主机的,是为了安全性而生的,但是后来由于ipv4 ip地址的紧缺,这使得SNAT源地址转换也被用来让私有网络里的主机上网。





如上图所示:SIP是内网里面的主机,SIP是一个内部私有地址,提供了web服务。
NAT Server是路由器主机,开启了路由转发功能,有2个网络接口,分别是GIP和FIP。SIP的网关指向了GIP,FIP是可以连接互联网的ip地址。
CIP是某个客户主机,用来向SIP请求web服务的。
路由器主机和CIP之间的2个表示互联网中的路由器。

现在CIP客户主机想要访问SIP提供的web服务,但是SIP是私网地址,如果构建源地址为CIP,目标地址为SIP的报文,是不可能将此报文送达到SIP的。因此,NAT Server主机对外宣称自己在80端口提供了web服务。如此一来,CIP向FIP发送报文,报文的源地址是CIP,目标地址是FIP。此报文通过互联网上的层层路由可被FIP接收。NAT Server一旦接收到此报文之后,立即通过iptables规则,将目标地址改变为SIP(提供web服务的主机),并且将FIP----SIP键值对保存在表中。此时的报文的源地址依然是CIP,目标地址改变为SIP。由于NAT Server开启了路由转发,故GIP接口可以接收到此请求报文。GIP通过查找路由条目可以将报文发送给SIP。SIP接收到请求报文之后,构建好响应报文,报文的源地址SIP,目标地址CIP。CIP是公网地址,所以报文被发送给GIP接口,报文的目标地址CIP,所以查找路由条目,发现可以经由FIP接口出去,但是发出去之前,查找上文中的表,根据SIP查找到对应的FIP。报文的源地址SIP被改变为查找到的FIP,目标地址依然是CIP。所以,当报文从FIP发出之时,报文的源地址为FIP,目标地址为CIP,此报文经过互联网上的层层路由最后被CIP收到。综上:一个目标地址转换必然对应一个相应的源地址转换。
目标地址转换之时,也可以转换端口。比如,在FIP上宣称提供的是80端口的web服务,但是实际上内部网络里的主机SIP提供的web服务其实是8080端口,那么在CIP发送请求报文的这条线中,在目标地址转换的时候,也做端口映射,将80端口映射到SIP主机的8080端口。
所以,DNAT可以用来隐藏提供服务的主机。




nat表:
要实现NAT的功能,只要在nat表上添加iptables规则即可
   nat表在PREROUTING链:DNAT,目标地址转换
nat表在OUTPUT链
nat表在POSTROUTING链:SNAT,源地址转换




代理(proxy)的原理:
代理一般工作在应用层。
举个例子:
我们要通过代理服务器访问google。首先我们将报文(报文中包含我们要访问的网站的网址,比如这个例子中就是google.com)发送给代理服务器,代理服务器接收到报文之后,由代理服务器向google.com发起请求,google.com接收到代理服务器的请求之后,发送响应报文给代理服务器,代理服务器接收报文之后,将googel响应的报文发送给我们。
所以,代理服务器即是客户端又是服务器端。





SNAT的实现方式: DNAT的实现方式
使用kali linux当作本地内网的主机,ip地址为192.168.20.2,ifconfig eth0 192.168.20.2/24,并且添加默认网关 route add default gw 192.168.20.1
使用CentOS 7作为网络防火墙,打开路由转发,有2个网络接口,网络接口1的ip地址为192.168.20.1,该接口做为kali linux的网关。网络接口2的ip地址为192.168.1.106
使用Ubuntu作为外部主机,IP地址为192.168.1.104。并且该主机提供web服务。

我们要访问kali linux来访问Ubunt提供的web服务,并且是通过SNAT的方式。



首先我们在kali linux上运行curl  192.168.1.104,表示向ubuntu主机请求web服务,并且在Ubuntu主机上运行tcpdump -i eth0 host  192.168.1.104,则可以抓取到如下的包:
21:32:50.742067 IP 192.168.20.2.37846 > hp8570p.http: Flags [S], seq 820428756, win 29200, options [mss 1460,sackOK,TS val 3134276191 ecr 0,nop,wscale 7], length 0
这表示192.168.20.2.37846向Ubuntu主机的http服务发起请求了

21:32:50.742299 IP hp8570p.http > 192.168.20.2.37846: Flags [S.], seq 1190048106, ack 820428757, win 28960, options [mss 1460,sackOK,TS val 3957167680 ecr 3134275188,nop,wscale 7], length 0
这表示Ubuntu主机向192.168.20.2.37846回应了
但是这此kali linux的请求是不成功的,因为Ubunutu上的默认路由条目是要到达任何地址,通过192.168.1.1,而响应报文是发给192.168.20.2的,通过192.168.1.1网关,该网关不知道如何发送给192.168.20.2。

所以,在网络防火墙上做SNAT,来改变这个请求报文的源地址。
添加的规则如下:
iptables -t nat -A POSTROUTING -s 192.168.20.0/24  -j SNAT --to-source 192.168.1.106

则继续抓包:
22:13:11.454406 IP 192.168.1.106.38384 > hp8570p.http: Flags [P.], seq 1:78, ack 1, win 229, options [nop,nop,TS val 3136696807 ecr 522169221], length 77: HTTP: GET / HTTP/1.1
如上的请求包,可以看出此时请求地址变成192.168.1.106了。
22:13:11.454514 IP hp8570p.http > 192.168.1.106.38384: Flags [.], ack 78, win 227, options [nop,nop,TS val 522169221 ecr 3136696807], length 0
如上的响应包,Ubuntu响应给192.168.1.106。


现在对上面的过程做一个反转:ubuntu主机访问kali linux主机上提供的web服务。
在网络防火墙主机CentOS上添加规则:
iptables -t nat -A PREROUTING -d 192.168.1.106 -p tcp --dport 80 -j DNAT --to-destination 192.168.20.2
这表示如果访问的是192.168.1.106的80端口,那么就做DNAT,把该数据包的目标地址改变为192.168.20.2







源地址转换和目标地址转换规则总结:
源地址转换:iptables -t nat -A POSTROUTING -s LocalNET ! -d LocalNet -j SNAT --to-source ExtIP
iptables -t nat -A POSTROUTING -s LocalNET ! -d LocalNet -j MASQUERADE

目标地址转换:iptables -t nat -A PREROUTING -d ExtIP -p tcp|udp --dport PORT -j DNAT --to-destination InterSeverIP[:PORT]






增大iptables的链接跟踪表最大容量
iptables的链接跟踪表最大容量为/proc/sys/net/ipv4/netfilter/ip_conntrack_max(CentOS5)或者/proc/sys/net/netfilter/nf_conntrack_max(CentOS 7),链接碰到各种状态的超时后就会从表中删除。

所以解決方法一般有两个:
(1) 加大 ip_conntrack_max 值
vi /etc/sysctl.conf    1-2行对于CentOS 5,3-4行对于CentOS 7
net.ipv4.ip_conntrack_max = 393216
net.ipv4.netfilter.ip_conntrack_max = 393216
                net.nf_conntrack_max = 393216
                net.netfilter.nf_conntrack_max = 393216
(2): 降低 ip_conntrack timeout时间
vi /etc/sysctl.conf    1-4行对于CentOS 5,5-8行对于CentOS 7
net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 300
net.ipv4.netfilter.ip_conntrack_tcp_timeout_time_wait = 120
net.ipv4.netfilter.ip_conntrack_tcp_timeout_close_wait = 60
net.ipv4.netfilter.ip_conntrack_tcp_timeout_fin_wait = 120
                net.netfilter.nf_conntrack_tcp_timeout_established = 300
                net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
                net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
                net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120

iptables -t nat -L -n






练习:INPUT和OUTPUT默认策略为DROP;

1、限制本地主机的web服务器在周一不允许访问;新请求的速率不能超过100个每秒;web服务器包含了admin字符串的页面不允许访问;web服务器仅允许响应报文离开本机;

2、在工作时间,即周一到周五的8:30-18:00,开放本机的ftp服务给172.16.0.0网络中的主机访问;数据下载请求的次数每分钟不得超过5个;

3、开放本机的ssh服务给172.16.x.1-172.16.x.100中的主机,x为你的座位号,新请求建立的速率一分钟不得超过2个;仅允许响应报文通过其服务端口离开本机;

4、拒绝TCP标志位全部为1及全部为0的报文访问本机;

5、允许本机ping别的主机;但不开放别的主机ping本机;




练习:判断下述规则的意义:
# iptables -N clean_in    添加一条自定义的链
# iptables -A clean_in -d 255.255.255.255 -p icmp -j DROP  目标是广播ping的丢弃
# iptables -A clean_in -d 172.16.255.255 -p icmp -j DROP

# iptables -A clean_in -p tcp ! --syn -m state --state NEW -j DROP  非SYN包,但却是NEW状态的包,可能是恶意伪造的包
# iptables -A clean_in -p tcp --tcp-flags ALL ALL -j DROP 
# iptables -A clean_in -p tcp --tcp-flags ALL NONE -j DROP
# iptables -A clean_in -d 172.16.100.7 -j RETURN 


# iptables -A INPUT -d 172.16.100.7 -j clean_in  目标是172.16.100.7的报文,就继续匹配自定义链clean_in上的的规则。

# iptables -A INPUT      -i  lo -j ACCEPT     通过lo接口进来的报文ACCEPT
# iptables -A OUTPUT  -o lo -j ACCEPT     通过lo接口出去的报文ACCEPT


# iptables -A INPUT  -i eth0 -m multiport -p tcp --dports 53,113,135,137,139,445 -j DROP
# iptables -A INPUT  -i eth0 -m multiport -p udp --dports 53,113,135,137,139,445 -j DROP
# iptables -A INPUT  -i eth0 -p udp --dport 1026 -j DROP
# iptables -A INPUT  -i eth0 -m multiport -p tcp --dports 1433,4899 -j DROP

# iptables -A INPUT  -p icmp -m limit --limit 10/second -j ACCEPT



补充:利用iptables的recent模块来抵御DOS攻击: 22,建立一个列表,保存有所有访问过指定的服务的客户端IP

ssh: 远程连接,

iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP


iptables -I INPUT  -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -I INPUT  -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name SSH -j LOG --log-prefix "SSH Attach: "
iptables -I INPUT  -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 3 --name SSH -j DROP

1.利用connlimit模块将单IP的并发设置为3;会误杀使用NAT上网的用户,可以根据实际情况增大该值;

2.利用recent和state模块限制单IP在300s内只能与本机建立2个新连接。被限制五分钟后即可恢复访问。

下面对最后两句做一个说明:

1.第二句是记录访问tcp 22端口的新连接,记录名称为SSH
--set 记录数据包的来源IP,如果IP已经存在将更新已经存在的条目

2.第三句是指SSH记录中的IP,300s内发起超过3次连接则拒绝此IP的连接。
--update 是指每次建立连接都更新列表;
--seconds必须与--rcheck或者--update同时使用
--hitcount必须与--rcheck或者--update同时使用

3.iptables的记录:/proc/net/xt_recent/SSH


也可以使用下面的这句记录日志:
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --name SSH --second 300 --hitcount 3 -j LOG --log-prefix "SSH Attack"



iptables实现七层访问过滤:

模块:layer7
该模块主要用于识别应用层协议

iptables/netfilter
iptables -m state,
netfilter state

对内核中的netfilter,打补丁layer7,重新编译内核
对iptables打补丁,补上layer7模块,重新编译iptables


diff/patch:文本操作工具


diff是Unix系统的一个很重要的工具程序。它用来比较两个文本文件的差异,是代码版本管理的核心工具之一。其用法非常简单:
  # diff <变动前的文件> <变动后的文件>

由于历史原因,diff有三种格式:
  * 正常格式(normal diff)
  * 上下文格式(context diff)
  * 合并格式(unified diff)

1、正常格式的diff
例如,对file1(变动前的文件)和file2(变动后的文件)进行比较可使用如下命令:
   # diff file1 file2
显示结果中,第一行是一个提示,用来说明变动位置。它分成三个部分:前面的数字,表示file1的第n行有变化;中间的"c"表示变动的模式是内容改变(change),其他模式还有"增加"(a,代表addition)和"删除"(d,代表deletion);

2、上下文格式的diff
上个世纪80年代初,加州大学伯克利分校推出BSD版本的Unix时,觉得diff的显示结果太简单,最好加入上下文,便于了解发生的变动。因此,推出了上下文格式的diff。它的使用方法是加入-c选项(即context)。
   # diff -c f1 f2
结果分成四个部分。第一部分的两行,显示两个文件的基本情况:文件名和时间信息,"***"表示变动前的文件,"---"表示变动后的文件。第二部分是15个星号,将文件的基本情况与变动内容分割开。第三部分显示变动前的文件,即file1。
另外,文件内容的每一行最前面,还有一个标记位。如果为空,表示该行无变化;如果是感叹号(!),表示该行有改动;如果是减号(-),表示该行被删除;如果是加号(+),表示该行为新增。
第四部分显示变动后的文件,即file2。

3、合并格式的diff
如果两个文件相似度很高,那么上下文格式的diff,将显示大量重复的内容,很浪费空间。1990年,GNU diff率先推出了"合并格式"的diff,将f1和f2的上下文合并在一起显示。
它的使用方法是加入u参数(代表unified)。
  # diff -u f1 f2
其结果的第一部分,也是文件的基本信息。"---"表示变动前的文件,"+++"表示变动后的文件。第二部分,变动的位置用两个@作为起首和结束。第三部分是变动的具体内容。
除了有变动的那些行以外,也是上下文各显示3行。它将两个文件的上下文,合并显示在一起,所以叫做"合并格式"。每一行最前面的标志位,空表示无变动,减号表示第一个文件删除的行,加号表示第二个文件新增的行。

diff
-u

patch

尽管并没有指定patch和diff的关系,但通常patch都使用diff的结果来完成打补丁的工作,这和patch本身支持多种diff输出文件格式有很大关系。patch通过读入patch命令文件(可以从标准输入),对目标文件进行修改。通常先用diff命令比较新老版本,patch命令文件则采用diff的输出文件,从而保持原版本与新版本一致。

patch的标准格式为
patch [options] [originalfile] [patchfile]

如果patchfile为空则从标准输入读取patchfile内容;如果originalfile也为空,则从patchfile(肯定来自标准输入)中读取需要打补丁的文件名。因此,如果需要修改的是目录,一般都必须在patchfile中记录目录下的各个文件名。绝大多数情况下,patch都用以下这种简单的方式使用:


patch命令可以忽略文件中的冗余信息,从中取出diff的格式以及所需要patch的文件名,文件名按照diff参数中的"源文件"、"目标文件"以及冗余信息中的"Index:"行中所指定的文件的顺序来决定。

-p参数决定了是否使用读出的源文件名的前缀目录信息,不提供-p参数,则忽略所有目录信息,-p0(或者-p 0)表示使用全部的路径信息,-p1将忽略第一个"/"以前的目录,依此类推。如/usr/src/linux-2.4.15/Makefile这样的文件名,在提供-p3参数时将使用linux-2.4.15/Makefile作为所要patch的文件。

patch
-p
-R

mockbuild

总结:操作步骤

1、获取并编译内核

# useradd mockbuild
# rpm -ivh kernel-2.6.32-431.5.1.x86_64.el6.src.rpm
# cd rpmbuild/SOURCES
# tar linux-2.6.32-*.tar.gz -C /usr/src
# cd /usr/src
# ln -sv

2、给内核打补丁
# tar xf netfilter-layer7-v2.23.tar.bz2
# cd /usr/src/linux
# patch -p1 < /root/netfilter-layer7-v2.23/kernel-2.6.32-layer7-2.23.patch
# cp /boot/config-*  .config
# make menuconfig

按如下步骤启用layer7模块
Networking support → Networking Options →Network packet filtering framework → Core Netfilter Configuration
<M>  “layer7” match support

3、编译并安装内核
# make
# make modules_install
# make install

4、重启系统,启用新内核

5、编译iptables

# tar xf iptables-1.4.20.tar.gz
# cp /root/netfilter-layer7-v2.23/iptables-1.4.3forward-for-kernel-2.6.20forward/* /root/iptables-1.4.20/extensions/
# cp /etc/rc.d/init.d/iptales /root
# cp /etc/sysconfig/iptables-config /root
# rpm -e iptables iptables-ipv6 --nodeps
# ./configure  --prefix=/usr  --with-ksource=/usr/src/linux
# make && make install

# cp /root/iptables /etc/rc.d/init.d
# cp /root/iptables-config /etc/sysconfig

6、为layer7模块提供其所识别的协议的特征码

# tar zxvf l7-protocols-2009-05-28.tar.gz
# cd l7-protocols-2009-05-28
# make install

7、如何使用layer7模块

ACCT的功能已经可以在内核参数中按需启用或禁用。此参数需要装载nf_conntrack模块后方能生效。
net.netfilter.nf_conntrack_acct = 1



l7-filter uses the standard iptables extension syntax
# iptables [specify table & chain] -m layer7 --l7proto [protocol name] -j [action]


# iptables -A FORWARD -m layer7 --l7proto qq -j REJECT



编译内核:
make menuconfig
make -j #
make modules_install
make install

清理内核源码树:

提示:xt_layer7.ko依赖于nf_conntrack.ko模块

博客:iptables所有应用,包括layer7的实现;


课外扩展:recent模块,layer7模块;




tcp_wrapper:tcp包装器

tcp_wrapper是对基于tcp协议开发并提供服务的应用程序,提供的一层访问控制工具;
基于库调用实现其功能:功能实现在库libwrap中。


判断某个服务是否能够由tcp_wrapper进行访问控制:
(1) 动态编译:ldd命令;  例如  ldd `which sshd` | grep libwrap
(2) 静态编译:strings命令查看应用程序文件,其结果中如果出现
hosts.allow
hosts.deny


在配置文件在为各服务分别定义访问控制规则实现访问控制:
/etc/hosts.allow
/etc/hosts.deny
某个客户端访问服务器端的服务的时候,匹配顺序是这样的,首先如果该客户端出现在host.allow文件中的话,则直接允许该客户端访问,如果该客户端没有出现在host.allow文件中的话,则查找其是否出现在host.deny文件中,如果在host.deny 文件中,则直接拒绝该客户端访问,如果没有出现在host.deny中的话,则查看默认规则是什么,如果是允许的话,就允许客户端访问,如果默认规则是拒绝的话,就拒绝客户端访问


上面的配置文件语法格式如下:
daemon_list: client_list [:options]      这表示daemon_list(守护进程),client_list(客户端)


daemon_list的取值有如下几种:
    应用程序的文件名称,而非服务名;
    应用程序文件名称列表,彼此间使用逗号分隔;
例如:sshd, vsftpd:
ALL表示所有服务;



client_list的取值有如下几种:
IP地址;
主机名;
网络地址:必须使用完整格式的掩码,不使用前缀格式掩码;所以类似于172.16.0.0/16不合法;
简短格式的网络地址:例如172.16. 表示 172.16.0.0/255.255.0.0;
ALL:所有主机;
KNOWN:
UNKNOWN
PARANOID
                例如:vsftpd服务不允许172.16.100.1访问

EXCEPT: 除了
      在hosts.allow文件中定义如下的内容:
         vsftpd: 172.16. EXCEPT 172.16.100.0/255.255.255.0 EXCEPT 172.16.100.1



[:options] 的取值有如下几种
deny: 拒绝,主要用于hosts.allow文件中
allow:允许,用于hosts.deny文件,实现allow的功能
spawn: 启动额外应用程序:
vsftpd: ALL :spawn /bin/echo `date` login attempt from %c to %s, %d >> /var/log/vsftpd.deny.log
%c: client ip
%s: server ip
%d: daemon name


练习:控制telnet服务仅允许172.16.0.0网络中的主机访问,但不包括172.16.100.0/255.255.255.0中的主机;
对所有正常登录的主机都记录于/var/log/telnet.allow.log中;
所有未授权访问尝试都记录于/var/log/telnet.deny.log中;



博客作业:iptables



-------------------------华丽的分割线----------------------------------------------------

HTTP协议






























评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用