Monthly Archive for January, 2013

關於MTU (Maximum transmission unit)

* MTU工作在鏈路層,是網卡傳輸(發送/接受)的數據楨最大長度。
* Ethernet(乙太網)最大MTU是1500,通常也是大部分設備默認值
* 網卡收到的數據楨長度超過該網卡的MTU時:如果數據楨里的IP或ICMP數據包header沒有DF標誌(Don’t Fragment flag),將數據分楨(fragmentation),分楨後第二個及以後的楨中只含有一部分IP header,並且沒有tcp和udp header;否則,丟棄這個數據楨,並且給src ip發送一個「ICMP fragmentation required「並附帶自己的MTU值,src可以根據這個MTU值調整自己發送的數據楨長度。(Path MTU Discovery)。
* 向網卡發送數據時,如果IP數據包長度(header + payload)超過了網卡的MTU,也會進行相同處理。
* TCP協議連接時會協商MSS(maximum segment size ), 本機的MSS是MTU減去ip header減去tcp header長度,最大為1460。取連接雙方MSS的較小值。

IPv4 header長度最小為20
IPV6 header長度最小為40
ICMPv4 header長度為20+8
ICMPV6 header長度為40+8
TCP header長度為20
UDP header長度為8
IP(V4 & V6) payload(數據部分)長度最大為65535,因為header中表示長度部分是兩位元組。

測試鏈路MTU

# 1472是ethernet最大值,因為ICMP header長度28
ping -s 1472 -M do 8.8.8.8

#超出長度會從最後通過的路由器(這裏就是本機網卡地址)返回ICMP錯誤(如果該路由器防火牆禁止ICMP則沒有返回,直接超時無響應)
ping -s 1473 -M do 8.8.8.8
From A.B.C.D icmp_seq=1 Frag needed and DF set (mtu = 1500)
...

然後有個在景德鎮非常流行的奇葩:PPPoE,工作在鏈路層,header佔用8位元組。所以它封裝的Ethernet楨最大就不能超過1492。超過的楨在進入pppoe時就要被分楨。因為TCP協議中MSS協商的存在,理論上,如果你使用pppoe上網(即使是用路由器撥號),將所有電腦的網卡MTU設為1492(默認是1500)可以提高性能,不過實際上沒什麼用。

對於有nf_conntrack模塊的iptables,MTU以及fragmentation是透明的,分楨的數據包會在進入INPUT或OUTPUT鏈前defragmentation重新組裝完整。

查看以及修改MTU:

ip link list

ip link set eth0 mtu 1500

VPN翻牆與P2P下載

#假裝懂技術
#GFW促進學習

在路由器上部署了VPN Client和chnroutes後,連接到國外VPS的VPN Server後,整個區域網內所有設備能夠透明翻牆,但是一些P2P軟件會遇到問題。

P2P軟件需要監聽固定埠,並且需要外網的機器能夠主動連接到運行P2P軟件內網機器。方法是用路由器的「埠映射」功能,將公網IP的某些埠映射到內網某個機器上。這是通過iptables的DNAT實現的:

假設在路由器管理界面中將10400-10499埠映射到192.168.0.4,路由器會添加下面的iptables規則(由於WAN IP通常不是固定的,路由器會在ppp撥號或DHCP獲得公網IP後動態添加這些規則):

iptables -t NAT -A PREROUTING -d <WAN_IP> -p tcp --dport 10400:10499 -j DNAT --to 192.168.0.4
iptables -t NAT -A PREROUTING -d <WAN_IP> -p udp --dport 10400:10499 -j DNAT --to 192.168.0.4

但是部署OpenVPN和chnroutes後,國外的IP就不能主動和內網機器建立連接了(即使已經按照上面方法在路由器上開啟了埠映射),因為這時國外IP看到的是VPS的IP,不是路由器的公網IP。

要使路由器部署VPN時仍然支持內網P2P下載和其它需要外網埠的應用,需要在VPS和路由器上同時增加新的iptables DNAT規則。

首先,需要知道路由器所使用的OpenVPN客戶端證書的(X509) common name,這個common name是在生成證書時指定的,OpenVPN easy-rsa創建證書時默認使用文件名作為common name。可以在證書crt文件中查看其common name

[email protected]:/etc/openvpn/easy-rsa/keys# cat client-common.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 10 (0xa)
    Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, ST=CA, L=SanFrancisco, O=SAGAN, OU=SAGAN, CN=SAGAN/name=SAGAN/[email protected]
        Validity
            Not Before: Jan 11 02:36:33 2013 GMT
            Not After : Jan  9 02:36:33 2023 GMT
        Subject: C=US, ST=CA, L=SanFrancisco, O=SAGAN, OU=SAGAN, CN=client-common/name=SAGAN/[email protected]
.....

證書的Subject行里的CN欄位就是common name,如上面例子證書的common name就是”client-common”

路由器的VPN Client必須具有固定IP(不能是Server通過DHCP分配的動態IP)。OpenVPN伺服器可以通過下面方式實現給某個客戶端分配固定IP

假設VPS的openvpn伺服器端配置文件位於/etc/openvpn,在這個目錄下創建一個”clients”目錄.

編輯OpenVPN伺服器端配置文件server.conf,加入下面內容

client-config-dir clients

在剛剛創建的/etc/openvpn/clients目錄下新建一個”client-common”文件(無擴展名),文件名就是路由器使用的證書的common name。這個文件用於給路由器的VPN Client分配固定IP,內容只有一行:

#假設你的OpenVPN虛擬網段是192.168.200.0/24
ifconfig-push 192.168.200.2 255.255.255.0

這樣做以後,路由器的OpenVPN Client連接後會自動獲取到192.168.200.2這個固定IP。

PS. 另一種讓路由器的OpenVPN客戶端獲取固定IP的方法是OpenVPN的static key模式。

配置好OpenVPN後只需要在VPS和路由器上分別添加iptables DNAT規則即可。規則如下。

# 下面代碼中
# A.B.C.D是VPS的公網IP
# 192.168.200.0/24是OpenVPN的網段
# 192.168.200.2是路由器OpenVPN Client獲得的IP
# 192.168.0.0/24是路由器所在的本地內網網段
# 192.168.0.1是路由器內網IP

# VPS上的iptables規則
iptables -t nat -A PREROUTING -p tcp -d A.B.C.D --dport 10000:20000 -j DNAT --to 192.168.200.2
iptables -t nat -A PREROUTING -p udp -d A.B.C.D --dport 10000:20000 -j DNAT --to 192.168.200.2

# 路由器上的iptables規則
iptables -t nat -A PREROUTING -p tcp -d 192.168.200.2 --dport 10400:10499 -j DNAT --to 192.168.0.4
iptables -t nat -A PREROUTING -p udp -d 192.168.200.2 --dport 10400:10499 -j DNAT --to 192.168.0.4

上面的iptables規則把VPS的10000-20000埠全部轉發給路由器,然後路由器再做一次DNAT,把這些埠任意分配給內網機器。如上面的示例把10400-10499映射到192.168.0.4,可以再把10500-10599映射給192.168.0.5,依此類推。

數據流向: 192.168.0.4:10401與某個Internet IP W.X.Y.Z:abc通信:

W.X.Y.Z:abc 向 A.B.C.D:10401發起連接 ->
  src W.X.Y.Z:abc dst A.B.C.D:10401

VPS DNAT ->
  src W.X.Y.Z:abc dst 192.168.200.2:10401

Router DNAT ->
  src W.X.Y.Z:abc dst 192.168.0.4:14001

192.168.0.4:10401 received


反方向:


192.168.0.4:10401 回應 W.X.Y.Z:abc
  src 192.168.0.4:10401 dst W.X.Y.Z;abc

Router Un DNAT
  src 192.168.200.2:10401 dst W.X.Y.Z:abc

VPS Un DNAT
  src A.B.C.D:10401 dst W.X.Y.Z:abc

W.X.Y.Z:abc reveived

Un DNAT(DNAT的Reverse)是自動的

注意,使用這種方法下載國外的P2P資源(如share)有版權風險,可能導致VPS被封 = =

PS. 還有一種路由器部署VPN時內網機器下載國外P2P資源的方法,就是在路由器上做策略路由(policy routing),用iptables給P2P數據包打標記(MARK),然後ip rule創建規則對這些數據包走默認網關。但是因為GFW現在連P2P連接也會reset(比如用emule搜索某個敏感關鍵字文件時),不推薦此方法。

架設區域網翻牆網關方法

任一台linux機器都可以,不需要配置路由器。

假設LAN網段是192.168.1.0/24

首先在192.168.1.A上正常部署OpenVPN Client和chnroutes,這樣本機就可以翻牆了

然後用下面命令(可以放在啟動腳本rc.local里)

echo 1 > /proc/sys/net/ipv4/ip_forward
# or below
#sysctl -w net.ipv4.ip_forward=1
# or edit /etc/sysctl.conf, 
# add (or edit) the following line 
# then run "sysctl -p" (permanant)
#net.ipv4.ip_forward=1

#可以加-o tun0條件,在開啟chnroutes時這使得LAN內機器能夠受益於ICMP Redirect,對國內IP直接路由到原始網關
#但會在某些綁定IP和MAC的網絡環境下遇到問題。
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE

就可以了了。區域網內的其它機器只需要將網關改為192.168.1.A,就可以透明翻牆並且國內外分流

此方法適用於公司內自己機器架設VPN翻牆然後分享給同事。

OpenVPN丟包率嚴重

在國外VPS上部署的OpenVPN。客戶端使用static key模式連接,可能因為沒有使用tls模式,埠一直沒有被封。但是長時間連接VPN後丟包率很高,超過30%,有時甚至達到80%。懷疑GFW干擾。

驗證方法很簡單:我的VPS有兩個同一網段IP,A.B.C.67和A.B.C.68,OpenVPN監聽前者埠

ping A.B.C.67
丟包率為0,正常。

ping A.B.C.68
丟包率非常高,干擾嚴重

路由中A.B.C.67是直連的,A.B.C.68走的VPN。

結論是GFW選擇性丟棄了OpenVPN的數據包。

—-
VPN是我唯一選擇的翻牆手段,配合第三方固件路由器、dnsmasq、chnroutes和策略路由可以達到最佳效果:LAN所有設備透明翻牆、國內外IP分流、支持CDN加速和P2P下載。這些是其它翻牆方式很難實現的。

あけましておめでとう

今年もよろしくお願いします

PS. 沒有妹紙沒有神社沒有初詣的新年不幸福哈 – –