自己打造智能路由 2013-08-11 # Hodgepodge # No Comments »

最近又折腾了一把路由,通过OpenWRT,OpenVPN和ChnRoutes实现的智能路由可以让你所有设备联网即自由,无需在各个设备上进行各种设置。

所需设备清单:

  • 一台自由的主机
  • 一台已经刷了OpenWRT的路由器
  • 一根网线
  • 一个2G的U盘

在此之前请确保你已经有了一台刷过OpenWRT的路由器,然后给路由器上电,通过网线直连,连接路由器:

telnet 192.168.1.1

设置密码:

passwd

而后退出即可用SSH进行登录了:

exit
ssh root@192.168.1.1

接着修改路由的无线配置,从而可以开启Wifi:

vim /etc/config/wireless

注释掉option disabled 1一行,并设置Wifi的加密方式和密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
config wifi-device  radio0
	option type     mac80211
	option channel  11
	option hwmode	11ng
	option path	'platform/ar933x_wmac'
	option htmode	HT20
	list ht_capab	SHORT-GI-20
	list ht_capab	SHORT-GI-40
	list ht_capab	RX-STBC1
	list ht_capab	DSSS_CCK-40
	# REMOVE THIS LINE TO ENABLE WIFI:
	# option disabled 1
 
config wifi-iface
	option device   radio0
	option network  lan
	option mode     ap
	option ssid     OpenWrt
	option encryption psk2
	option key	'password'

然后修改网络配置:

vim /etc/config/network

注释掉option ifname 'eth0'并增加WAN口设置,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
config interface 'loopback'
	option ifname 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'
 
config globals 'globals'
	option ula_prefix 'fd48:f746:e8a5::/48'
 
config interface 'lan'
	# option ifname 'eth0'
	option type 'bridge'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'
 
config interface 'wan'
	option ifname 'eth0'
	option proto 'dhcp'

而后重启路由,即可通过Wifi连接到路由器,此时将路由通过网线接入网络或者上级路由即可作为一个普通的路由器使用了。

reboot

安全模式

如果期间由于IP地址配置错误等原因导致无法连接到路由器,可以进入路由器的安全模式进行恢复:

1、拔掉路由器电源
2、链接电源后使用牙签不断抽插路由器的reset按钮,当路由器的指示灯开始快速闪烁就说明已经进入了安全模式
3、安全模式不会加载任何配置,因此还需要通过最初提到的方式,直连并通过telnet 192.168.1.1连接路由器
4、挂载root分区:mount_root
5、擦除先前的配置:firstboot
6、设置密码:passwd root
7、重启:reboot -f

而后就可以重新开始进行配置了,如果其中通过firstboot擦除配置的过程中遇到错误,可以通过在OpenWRT中重新刷机的方式解决。

准备U盘

我所用的路由只有2MB存储空间,安装OpenWRT之后,空间已所剩无几,如果要安装后续提到的OpenVPN等软件,很明显空间是不够用的,但是我们可以通过路由器的USB接口使用U盘来做扩展。

我这里给2G的U盘分了3个区,1G(primary, bootable, ext4)/500MB(primary, swap)/500MB(primary, ext4),分区完后对其进行格式化,就完成了U盘的准备工作,我是通过Mac中的Ubuntu虚拟机进行下述操作的:

1
2
3
4
cfdisk /dev/sdb
mkfs.ext4 /dev/sdb1
mkswap /dev/sdb2
mkfs.ext4 /dev/sdb3

U盘准备完毕后就可以在路由上安装所需的软件了:

1
2
3
4
5
opkg update
opkg install kmod-usb2 kmod-fs-ext4
opkg install kmod-usb-storage
opkg install block-mount
reboot

接着重启路由器,而后配置fstab实现U盘分区的自动挂载:

1
2
reboot
vim /etc/config/fstab

修改如下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
config 'mount'
	option	target	/mnt/usb
	option	device	/dev/sda1
	option	fstype	ext4
	option	enabled 1
 
config 'swap'
	option	device	/dev/sda2
	option	enabled 1
 
config 'mount'
	option	target	/mnt/home
	option	device	/dev/sda3
	option	fstype	ext4
	option	enabled	1

而后重启路由器后:

reboot

这时候你就可以通过df -h看到分区已经自动挂载了:

df -h
1
2
3
4
5
6
7
8
9
Filesystem                Size      Used Available Use% Mounted on
rootfs                    1.1M    632.0K    456.0K  58% /
/dev/root                 1.8M      1.8M         0 100% /rom
tmpfs                    14.1M     72.0K     14.1M   0% /tmp
/dev/mtdblock3            1.1M    632.0K    456.0K  58% /overlay
overlayfs:/overlay        1.1M    632.0K    456.0K  58% /
tmpfs                   512.0K         0    512.0K   0% /dev
/dev/sda1               945.2M     11.0M    869.4M   1% /mnt/usb
/dev/sda3               451.5M      2.3M    421.5M   1% /mnt/home

接着执行如下操作:

1
2
3
4
5
mkdir /tmp/root
mount -o bind / /tmp/root
cp /tmp/root/* /mnt/usb -a
umount /tmp/root
rm -r /tmp/root

接着在opkg.conf配置中增加dest usb /mnt/usb,之后就可以将我们需要的OpenVPN安装到USB中了:

1
2
3
4
5
6
7
vim /etc/opkg.conf
opkg update
opkg --dest usb install openvpn-openssl
ln -s /mnt/usb/usr/lib/libssl.so.1.0.0 /usr/lib/
ln -s /mnt/usb/usr/lib/libcrypto.so.1.0.0 /usr/lib/
ln -s /mnt/usb/usr/lib/liblzo2.so.2 /usr/lib/
ln -s /mnt/usb/usr/sbin/openvpn /usr/sbin/

到此已经可以运行OpenVPN了:

openvpn --version

由于OpenVPN安装在USB中,所以后续启动OpenVPN会遇到找不到tun模块的状况,接下来配置tun模块:

1
2
3
4
ln -s /mnt/usb/lib/modules/3.10.4/tun.ko /lib/modules/3.10.4/
ln -s /mnt/usb/etc/modules.d/30-tun /etc/modules
ln -s /mnt/usb/etc/modules.d/30-tun /etc/modules.d/
modinfo tun

接着我们关闭防火墙并看一下默认的一些转发和NAT规则:

1
2
3
4
/etc/init.d/firewall stop
/etc/init.d/firewall disable
iptables -L -n --line-number
iptables -t nat -vnL POSTROUTING --line-number

可以看到此时默认是没有任何转发规则的,因此此时连接到路由器的设备是无法上网的,配置路由每次上电重启时候自动加载tun模块并加入转发规则:

vim /etc/rc.local

修改如下配置:

1
2
3
4
5
6
insmod tun
iptables -I FORWARD -o tun0 -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j MASQUERADE
iptables-save
 
exit 0

到此为止你的路由器已经可以再次正常接入互联网了,下面我们需要配置OpenVPN并进行流量的智能分发,即配置哪些流量走VPN。

首先我们先来配置OpenVPN,这包含两方面的配置,服务端和客户端。

服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
wget http://ipxcore.com/openvpn-debian-install.sh
chmod +x openvpn-debian-install.sh
./openvpn-debian-install.sh
 
iptables -I INPUT -p udp --dport 1194 -j ACCEPT
iptables -I INPUT -p tcp --dport 1194 -j ACCEPT
iptables -I INPUT -p udp --dport 443 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -i tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -j ACCEPT
iptables -A FORWARD -o tun0 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
iptables-save

客户端

客户端需要配置在服务端生成的证书路径,首先从服务器下载打包的证书文件:

1
2
3
scp root@100.100.100.100:/root/keys.tgz ./
tar -zxvf keys.tgz
vim 703n.ovpn

703n.ovpn配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
client
remote 100.100.100.100 1194
dev tun
comp-lzo
ca /root/etc/openvpn/easy-rsa/2.0/keys/ca.crt
cert /root/etc/openvpn/easy-rsa/2.0/keys/client1.crt
key /root/etc/openvpn/easy-rsa/2.0/keys/client1.key
route-delay 2
route-method exe
max-routes 3888
redirect-gateway def1
verb 3

到此为止,通过当前的配置文件已经可以启动OpenVPN并实现所有流量经过VPN转发了:

openvpn --config ./703n.ovpn

为了实现流量的智能分发,同时也是为了节省VPN的流量,可以通过ChnRoutes实现,在Mac上下载ChnRoutes并执行:

python chnroutes.py -p android

会生成两个文件vpnup.shvpndown.sh,将这两个文件头部的alias删除,并下载到路由器中之后,在配置文件底部增加如下设置:

1
2
3
script-security 2
up vpnup.sh
down vpndown.sh

好了,这时候再次启动OpenVPN,在Mac上分别traceroute下Baidu和Twitter就会发现,流量已经自动分发了。

Reference:

  • http://www.imtxc.com/blog/2013/02/24/install-and-configure-openwrt-on-wr703n/
  • http://blog.zhen9ao.me/blog/2012/04/10/how-to-config-openwrt-with-openvpn-on-tp-link-wr703n/
  • http://www.dd-wrt.com/phpBB2/viewtopic.php?t=65650
  • https://blog.ipredator.se/howto/openwrt/configuring-openvpn-on-openwrt.html

    http://ipxkb.info/auto-install-openvpn-on-debian/

EOF

Android 原生浏览器“隐身模式”的诡异问题探究 2013-03-21 # Javascript/Ajax # 1 Comment »

昨天QA同学反馈其三星i9100手机突然同时无法访问旅行H5、彩票H5等H5的部分页面,现象是:页面一直在转菊花,未完全加载。而之前则是好的,于是怀疑是手机问题,便找到这台手机看相关网络的设置,Host的绑定等,均无问题。但是发现所用浏览器左上角一个很奇怪的图标。

才发现浏览器所用的是”隐身窗口/匿名窗口(Incognito-Mode)”。于是换用Chrome浏览器同样使用“隐身窗口模式”发现Chrome浏览器无此问题。于是怀疑是Android内置的原生浏览器在匿名窗口下做了一些更为严格的限制导致的。

何为隐身模式?

当关闭隐身窗口后,您在此窗口中查看的网页将不会出现在您的浏览器历史记录或搜索记录中,也不会在您的设备留下任何踪迹(如Cookie)。但是,您下载的任何文件或您创建的书签会予以保留。

原因定位:

于是尝试用jsConsole在线调试,发现jsConsole在隐身模式下自身就报错(擦,这是要闹哪般),于是只能一段段删代码并辅以try…catch…和alert进行原因定位。最后发现是由于代码中用了本地存储(localStorage),在内置的原生浏览器匿名窗口中,localStorage对象虽然存在,但是只要操作localStorage就会出现如下的报错。

去掉本地存储的代码后,页面正常显示。

相关的解决方案和可行性:

1、 代码中不使用localStorage/Cookie?!

弊端:由于我们的H5应用多为WebApp类型,数据、渲染、呈现、交互均由前端完成,且彩票、旅行都涉及选号、下单、交易等流程,代码中不可避免的会用到localStorage/Cookie,但是Android原生浏览器下这种限制在我们目前的架构方式下不可避免的会报错导致加载不出页面。

2、 本地存储改为前后端数据交互,存储的数据从后端服务器吐给前端

弊端:这种方式需要后端均改动逻辑,代码实现上变动较大

3、 检测用户是否处于隐身模式下(storage是否可用),并给出提示让用户在正常模式下浏览

由于用户使用隐身模式浏览的情况并不常见(后续可以统计隐身模式所占百分比)且仅在内置浏览器中有此现象,因此检测并给出提示是个人认为目前成本较低、较可行的解决办法。

其它H5新项目中localStorage最好也“如无必要,请勿使用”。

抛砖引玉,此前Google不到此问题的相关资料,遂写此文,供大家参考。