幫實驗室設好了 ADSL proxy

因為清大的網路連國外實在是太慢了,
所以在不知道幾年前有位據說 Java 很神的學長幫實驗室辦了 Hinet ADSL,
不過不知道是什麼原因一直沒有人去把它設起來,
前幾天無意間注意到那台 ATU-R 才知道這件事 (其實好像很久以前就有問過的樣子...)。

FreeBSD 5.x 以後因為有功能強大的 pf,
所以挑了實驗室的主 server 來負責擔任 proxy 的角色 (其它都是 Gentoo Linux),
不過還是花了不少時間在這上面,
這其中的原因有點複雜。

首先那台 server 本身就有一張 fxp0 (140.xxx.xxx.xxx) 的網卡對外,
提供了 www 和 mail 等等有的沒的 services,
然後是用內建網卡 bge0 (192.168.xxx.xxx) 直接連在 ATU-R 上,
對外連線則是系統會自動產生一個 tun0 (220.xxx.xxx.xxx) 負責,
這些應該算是常識,
總之目的就是做成這樣:
HiNet <--tun0--> Proxy Server <-- fxp0 --> TANet clients
其中 fxp0 和 tun0 都有 public IP。

proxy server 的程式當然是選擇眾所皆知的 squid,
不過因為它不能指定 incoming device 和 outgoing device,
只能指定 incoming address 和 outgoing address,
所以帶來了不小的災難,
簡單說就是讓 squid 去接收140.xxx.xxx.xxx 的 request,
然後透過 220.xxx.xxx.xxx 走 HiNet 網路對外抓資料,
最後又透過 140.xxx.xxx.xxx 把抓到的資料傳回 client,
所以 outgoing address 當然是設定成 220.xxx.xxx.xxx;
這時比較麻煩的問題就來了,
squid 只有把封包的 src ip 欄填成 220.xxx.xxx.xxx,
出去抓資料的時候還是走接收 client request 的 device fxp0,
這樣丟出去的封包下場當然就是有去無回了。

一開始用 Google 查雙介面分流的 topics 時還搞不太懂下面這種 pf 的設定意義:

不過在用 tcpdump 觀察過封包的流向之後終於知道原因了,
簡單說就是封包會走錯路,
舉例來說:

意思是如果看到 src ip 是 220.xxx.xxx.xxx 的封包在 fxp0 上往外送,
就把它改成往 tun0 送,
routing 的下一個節點也改成 tun0 的 gateway,
這樣就能引導封包走正確的路線出去了。

至於 boot 時 ppp-user 會比 pf 還晚起來,
造成 pf 啟動時會吐 error 的問題,
機八林餅幹找到了一個解法,
就是在 from 後面的 device name 包上括號:

這樣在載入 pf.conf 時似乎就不會去 check from 後面的那個 name,
目前測試看起來也是 ok,
不過不曉得是不是會有 performance 的問題,
因為用 pf -s rules 看的話沒包括號的會直接用那個 device 的 ip 取代 from 後面的那個欄位,
可是有包括號的話就會保持 (tun0) 和 (fxp0) 這種形式,
不曉得是不是每次都會重新 lookup 一遍;
另一個可能的做法是直接填入 ip,
反正現在撥的也是 @ip.hinet.net 拿固定 ip,
所以其實在撥號前就知道 ip 是什麼了。

目前可能還需要一個 proxy auto configuration 的設定檔,
這是給 browser 用的,
用 Google 搜尋 proxy.pac 應該就能找到很多範例,
裡面是用 JavaScript 寫的一個 function,
用來讓 browser 判斷什麼的狀況下要走 proxy,
以及選擇哪台 proxy 等等的,
這樣開國內網頁的話就能直接連線而不去走 proxy 了。