2014年2月11日 星期二

[Linux] VPN L2TP client + DHCP server + NAT iptables @ Ubuntu 12.04 desktop

VPN client + DHCP server + NAt

試了一個小型的網路架構,原本打定把 AP 刷成 DD-WRT 後,即可將 WAN 設定成 VPN 走法,可惜 AP 實在不穩,就改拉一台 PC 來用。此例用一台 Ubuntu 12.04 Desktop 當作 VPN client,此外,為了讓其他 clients 不需要處理 VPN 的連線,因此,讓這台 Ubuntu desktop 又充當 DHCP server,藉以再接一台 AP 出來,供眾人使用,眾人只需要連上 AP 後,就可以走指定的 VPN 通道。

為此,先介紹 Ubuntu 12.04 這檯機器,需要兩張網卡,分別為 NIC 1 跟 NIC 2 ,以下則用 eth0 跟 eth1 區分,而 VPN 走的網卡是 ppp0 (可以連通 VPN 後,用 ifconfig 查看)。其中 eth0 的網路是自動取得 IP 的。

設定 eth1 網卡:

$ sudo vim /etc/network/interfaces
auto eth1
iface eth1 inet static
address 192.168.168.1
netmask 255.255.255.0
network 192.168.168.0

broadcast 192.168.168.255


架設 DHCP server:

$ sudo apt-get install isc-dhcp-server
$ sudo vim /etc/default/isc-dhcp-server
INTERFACES="eth1"
$ sudo vim /etc/dhcp/dhcpd.conf
option domain-name "example.org";
option domain-name-servers 8.8.8.8;
subnet 192.168.168.0 netmask 255.255.255.0 {
  range dynamic-bootp 192.168.168.50 192.168.168.100;
  option routers 192.168.168.1;
}
$ sudo service isc-dhcp-server restart


設定 NAT 與 iptables:

$ echo "1" > /proc/sys/net/ipv4/ip_forward
$ sudo vim /etc/rc.local

echo "1" | tee /proc/sys/net/ipv4/ip_forward

OIF="eth0"
VIF="ppp0"
IIF="eth1"
INNET="192.168.168.0/24"

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT

iptables -t nat -A POSTROUTING -o $OIF -s $INNET -j MASQUERADE
iptables -t nat -A POSTROUTING -o $VIF -j MASQUERADE


如此一來,即可收工。

[Linux] VPN L2TP client mode @ Ubuntu 12.04 Desktop

持續惡搞網路中,這次換把一台 Ubuntu desktop 當成 VPN Client 去連線。

$ sudo apt-add-repository ppa:werner-jaeger/ppa-werner-vpn
$ sudo apt-get update
$ sudo apt-get install l2tp-ipsec-vpn

註:若採用 Ubuntu 12.04 內建的 apt-get source list 安裝 l2tp-ipsec-vpn 後,卻連不上 :P

接著到左上角的 Dash Home 搜尋 VPN 來用:

vpn_client_setting_0

接著,就可以依序填寫資料:

vpn_client_setting_1

vpn_client_setting_2

填寫完記得按 "OK" 與 "Close" 後,再去右上角的 L2TP Ipsec VPN Applet 去點選連線囉!

如果,進行的不順利,可以試著跑去 VPN Server 上,查看 logs ,而 logs 預設沒有啟動,要去修改:

$ sudo vim /etc/ipsec.conf
config setup
  # ...
  # Use this to log to a file, or disable logging on embedded systems (like openwrt)
  plutostderrlog=/var/log/ipsec.log

$ sudo touch /var/log/ipsec.log
$ sudo service ipsec restart
ipsec_setup: Stopping Openswan IPsec...
ipsec_setup: Starting Openswan IPsec 2.6.37...
$ sudo tail -f /var/log/ipsec.log
added connection description "L2TP-PSK-noNAT"
listening for IKE messages
adding interface eth0/eth0 x.x.x.x:500
adding interface eth0/eth0 x.x.x.x:4500
adding interface lo/lo 127.0.0.1:500
adding interface lo/lo 127.0.0.1:4500
loading secrets from "/etc/ipsec.secrets"
loading secrets from "/var/lib/openswan/ipsec.secrets.inc"
...


如此一來,有人連線就會噴訊息囉。

2014年2月7日 星期五

[Linux] VPN (L2TP) 與使用 iptable 設定 Port forward @ Ubuntu 12.04

這樣的標題不是很恰當,但我暫時想不到好的方式。

使用情境:

  • 有 A, B 兩檯在不同國家的機器
  • 對 A 機器而言,無法直接連到 B 機器之國家內的服務
  • 解法透過 B 機器架設 VPN 服務,讓 A 透過 VPN 進入到 B 機器所在的國家網路
  • 這兩個國家的網路直連很慢,恰好 C 機器所在的國家,對這兩國連線都不錯

很繞口,但事實就擺在眼前 XD 這時候在 B 機器已經架好 VPN 服務了,從 A 也能夠連到,但是速度表現不佳,所以想要繞道從 C 機器所在的國家進去。

這時 C 機器就只需做 port forwarding 來處理即可,共有 UDP 500, 1701, 450 port 要處理:

$ iptables -A PREROUTING -t nat -i eth0 -p udp --dport 500 -j DNAT --to B_SERVER_IP:500
$ iptables -A PREROUTING -t nat -i eth0 -p udp --dport 4500 -j DNAT --to B_SERVER_IP:4500
$ iptables -A PREROUTING -t nat -i eth0 -p udp --dport 1701 -j DNAT --to B_SERVER_IP:1701
$ iptables -A FORWARD -p udp -d B_SERVER_IP --dport 500 -j ACCEPT
$ iptables -A FORWARD -p udp -d B_SERVER_IP --dport 4500 -j ACCEPT
$ iptables -A FORWARD -p udp -d B_SERVER_IP --dport 1701 -j ACCEPT

2014年2月6日 星期四

D-LINK DIR-632 從 DD-WRT 刷回原廠 firmware

back_to_firmware

上回一拿到 DIR-632 後,馬上刷成 DD-WRT ,連原廠的都還沒測。現在想測回廠 firmware ,才發現需要找一下祕技才能 XD 就順便筆記一下吧。

關鍵之處,莫過於 D-LINK 機台有提供特別的刷 firmware 的方式,透過這招才刷回去:
  1. 首先,先下載 D-LINK DIR-632 官方韌體 (DIR632A1_FW102B10beta02.bin)
  2. 拔掉 AP 電源線,用筆壓出 Reset 鍵
  3. 持續按著 Reset 鍵,把 AP 電源線接上
  4. 當 AP 電源燈號閃爍時,即可把 Reset 放開
  5. 把電腦接上網路孔 1 ,並且手動設定電腦 IP 為 192.168.0.2/255.255.255.0 後,用 Firefox 瀏覽器瀏覽 http://192.168.0.1 (此為 AP 預設 IP)
  6. 上傳 DIR632A1_FW102B10beta02.bin ,可觀察電源燈是否持續在閃爍,若上傳一陣子後不閃爍時,即代表完成(可以透過 ping 192.168.0.1 觀察 AP 狀態)
其他心得:
  • 不知為何用 Chrome Browser 時,遲遲無法上傳成功 :P 就改用 Firefox 了

2014年2月5日 星期三

[MongoDB] 透過 MapReduce 進行 Collections Join @ Ubuntu 12.04

使用 NOSQL 時,其先天設計無法提供像 MySQL Table Joins 等招數,此時的解法就是把兩個 tables 倒進一張 table 來解決。

以 MongoDB command line 操作,新增資料:

> use mydb
switched to db mydb
> db.t1.insert({"data":1, "data2":2, "data3":3})
> db.t1.insert({"data":4, "data2":5, "data3":6})
> db.t1.insert({"data":7, "data2":8, "data3":9})
> db.t1.find()
{ "_id" : ObjectId("52f1f9c7bf6317bb2615b216"), "data" : 1, "data2" : 2, "data3" : 3 }
{ "_id" : ObjectId("52f1f9cbbf6317bb2615b217"), "data" : 4, "data2" : 5, "data3" : 6 }
{ "_id" : ObjectId("52f1f9cebf6317bb2615b218"), "data" : 7, "data2" : 8, "data3" : 9 }
> db.t2.insert({"data4":3, "data5":2, "data6":1})
> db.t2.insert({"data4":6, "data5":5, "data6":4})
> db.t2.insert({"data4":9, "data5":8, "data6":7})
> db.t2.find()
{ "_id" : ObjectId("52f1f9d4bf6317bb2615b219"), "data4" : 3, "data5" : 2, "data6" : 1 }
{ "_id" : ObjectId("52f1f9d7bf6317bb2615b21a"), "data4" : 6, "data5" : 5, "data6" : 4 }
{ "_id" : ObjectId("52f1f9dbbf6317bb2615b21b"), "data4" : 9, "data5" : 8, "data6" : 7 }


定義 t1 跟 t2 的 mapper function:

> t1_map = function() {
emit(this.data3, { "data": this.data, "data2": this.data2, "data3": this.data3 });
}
> t2_map = function() {
emit(this.data4, { "data4": this.data4, "data5": this.data5, "data6": this.data6 });
}


定義 reducer function:

> reducer = function(key, values){
var result = {} ;
values.forEach( function(value){
var field;
for(field in value)
if( value.hasOwnProperty(field) )
result[field] = value[field];
});
return result;
}


進行 map-reduce:

> db.tmp.drop()
true
> db.t1.mapReduce(t1_map, reducer , {"out":{"reduce":"tmp"}} )
{
        "result" : "tmp",
        "timeMillis" : 10,
        "counts" : {
                "input" : 3,
                "emit" : 3,
                "reduce" : 0,
                "output" : 3
        },
        "ok" : 1,
}
> db.tmp.find()
{ "_id" : 3, "value" : { "data" : 1, "data2" : 2, "data3" : 3 } }
{ "_id" : 6, "value" : { "data" : 4, "data2" : 5, "data3" : 6 } }
{ "_id" : 9, "value" : { "data" : 7, "data2" : 8, "data3" : 9 } }
> db.t2.mapReduce(t2_map, reducer , {"out":{"reduce":"tmp"}} )
{
        "result" : "tmp",
        "timeMillis" : 6,
        "counts" : {
                "input" : 3,
                "emit" : 3,
                "reduce" : 0,
                "output" : 3
        },
        "ok" : 1,
}
> db.tmp.find()
{ "_id" : 3, "value" : { "data4" : 3, "data5" : 2, "data6" : 1, "data" : 1, "data2" : 2, "data3" : 3 } }
{ "_id" : 6, "value" : { "data4" : 6, "data5" : 5, "data6" : 4, "data" : 4, "data2" : 5, "data3" : 6 } }
{ "_id" : 9, "value" : { "data4" : 9, "data5" : 8, "data6" : 7, "data" : 7, "data2" : 8, "data3" : 9 } }


以上的概念就是 t1 採用 data3 作為 join key,而 t2 採用 data4 ,分別跑 map-reduce 後儲存在 tmp 這張表(collection),如此 tmp 即為常用的 SQL Join 結果。

以上須留意的是跑 map-reduce 時,要指定 out 為 reduce 形態,細節可以在 out Options 查看,可分成 replace, reduce, merge ,若沒指定為 reduce 的話,上述最後結果僅有 t2 的資料。

此外,在 pymongo 上也能達成,有興趣可以參考 map-reduce-join.py,寫的彈性一點,所以閱讀性較差,用法:

$ python map-reduce-join.py --host localhost --database mydb --reset-result --result tmp --show-result --select-out-1 data data2 data3 --join-key-1 data3 --select-out-2 data4 data5 data6 --join-key-2 data4

結果:

Mapper 1 Code:
                function() {
                        emit(this.data3, {"data": this.data, "data2": this.data2, "data3": this.data3});
                }

Reducer 1 Code:
                function(key, values) {
                        var out = {};
                        values.forEach( function(value) {
                                for ( field in value )
                                        if( value.hasOwnProperty(field) )
                                                out[field] = value[field];

                        });
                        return out
                }

{u'counts': {u'input': 3, u'reduce': 0, u'emit': 3, u'output': 3}, u'timeMillis': 34, u'ok': 1.0, u'result': u'tmp'}
{u'_id': 3.0, u'value': {u'data': 1.0, u'data3': 3.0, u'data2': 2.0}}
{u'_id': 6.0, u'value': {u'data': 4.0, u'data3': 6.0, u'data2': 5.0}}
{u'_id': 9.0, u'value': {u'data': 7.0, u'data3': 9.0, u'data2': 8.0}}
Mapper 2 Code:
                function() {
                        emit(this.data4, {"data4": this.data4, "data5": this.data5, "data6": this.data6});
                }

Reducer 2 Code:
                function(key, values) {
                        var out = {};
                        values.forEach( function(value) {
                                for ( field in value )
                                        if( value.hasOwnProperty(field) )
                                                out[field] = value[field];
                        });
                        return out
                }

{u'counts': {u'input': 3, u'reduce': 0, u'emit': 3, u'output': 3}, u'timeMillis': 5, u'ok': 1.0, u'result': u'tmp'}
{u'_id': 3.0, u'value': {u'data5': 2.0, u'data4': 3.0, u'data6': 1.0, u'data': 1.0, u'data3': 3.0, u'data2': 2.0}}
{u'_id': 6.0, u'value': {u'data5': 5.0, u'data4': 6.0, u'data6': 4.0, u'data': 4.0, u'data3': 6.0, u'data2': 5.0}}
{u'_id': 9.0, u'value': {u'data5': 8.0, u'data4': 9.0, u'data6': 7.0, u'data': 7.0, u'data3': 9.0, u'data2': 8.0}}