Chinaunix首页 | 论坛 | 博客
  • 博客访问: 797093
  • 博文数量: 81
  • 博客积分: 612
  • 博客等级: 中士
  • 技术积分: 1216
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-03 14:38
个人简介

蝴蝶翅膀的力量,也许能够改变这个世界。

文章分类

全部博文(81)

文章存档

2020年(2)

2019年(2)

2018年(6)

2017年(2)

2016年(5)

2015年(7)

2014年(20)

2013年(12)

2012年(25)

分类: IT业界

2018-09-23 17:29:44

(2018-10-12更新:OpenWrt的V18.06版本,mt76芯片组的2.4GWiFi有缺陷,请谨慎使用。5G是OK的。)

(声明:欢迎转载分享文章,原创文章请注明出处,转载文章请注明原文链接。由于我在发文章的时候才登录自己的博客,因此无法及时回复消息。如果有技术问题,可以发送到我的邮箱一起探讨,邮箱地址为chou dot o dot ning at gmail dot com)


缘起

    作为一个程序员,工作无法离开Google,因此一直使用ShadowSocks上网。使用PC时,设置浏览器的socks5代理上网,使用手机时,使用ShadowSocks代理的APP软件。最近Google推出了Android9版本,而我的Google Pixel2手机无法从Android8升级(即使手机开了代理也不行)。因此打算在家改造一台无线路由器,直接支持ShadowSocks,被GFW收录的网站走代理,其他网站不走代理。这样家里的手机、PCpad都不用再设置各种代理了。

    本来以为在网上查找一些文章,按照其步骤指导,会很容易地打造一个基于OpenWRTShadowSocks的无线路由器上网方案。但依次做下来,发现有很多坑!大多数的文章只告诉你怎么做,但并不告诉你为什么这么做,导致出现问题的时候,只能自己查找资料定位问题。因此,在踏过这些坑之后,打算把整个过程记录下来,为后来者提供一些参考。

〇、准备工作

1、如果你是Windows用户,请安装putty或其他类似软件,主要用于ssh客户端登录路由器。如果你有Mac或者Ubuntulinux主机,系统自带ssh客户端。

2、需要一个U盘,并且格式化为Fat32格式

3、最好有一些linux的基础知识

一、硬件的选择

第一步,需要选择一款支持OpenWRT的无线路由器,并不是市面上的每款无线路由器都被OpenWRT支持,因此需要在 中查找相关的硬件。

我这里推荐小米路由器3GMiWiFi 3G),理由如下(主要是其性价比):

1OpenWRT主干版本和最新版本支持该硬件,当前最新版本是V18.06.1,是OpenWRTLEDE合并后的第一个版本;

2、淘宝上购买包邮价格为179元,在同类性能的路由器里面算是超级便宜的;

3、一个Wan口和两个Lan上行口为千兆,可以匹配超过百兆的家庭光宽带;

4cpuMediaTekMT7621ATmips双核880MHz,每个核支持双线程;

5Flash128MB,这个比较重要,因为OpenWRT支持很多模块的安装。很多低端的路由器因为降成本,Flash配置很小,安装几个模块之后,Flash就满了;

6、内存为256MB,足够跑绝大多数的应用;

7、带USB3.0口,可以挂接移动硬盘或摄像头,还可以玩一些家庭NAS、监控等一些好玩的应用;

8Wi-Fi支持2.4G5G双频,MediaTek的无线驱动是开源的,因此开源社区支持力度较好(玩OpenWRT很多人不建议用Broadcom的芯片,主要原因就是其无线部分是闭源的)

9PCB版留有UART口,可供在此方面有需求的开发人员使用(对大多数人来说,串口可以不需要,有ssh远程连接就够用了)。当然,如果变砖,可以通过UART来拯救(Bootloader没有被破坏的话)。

二、路由器烧录OpenWRT firmware

下面是烧录的步骤:

1、拆包装;

2、按照其用户手册,安装小米WiFi APP,注册账号,并绑定路由器后,可在APP中查看和配置路由器

3、到 中,寻找到ROM版本:ROM for R3G 开发版,下载并将路由器更新到该版本。升级时,会提示是否恢复缺省配置,这里不需要恢复缺省配置,否则APP还需要重新绑定和配置一次;

4、登陆到 ,用之前注册的账号登录,获取其root密码。注意:如果你有多台小米路由器,每台的root密码是不同的。然后下载工具包miwifi_ssh.bin;

5、到 寻找到 mir3g-squashfs-kernel1.bin mir3g-squashfs-rootfs0.bin,下载之,文件名带有前缀openwrt-18.06.1-ramips-mt7621-)。这里需要说明一下,kernellinux的内核文件,rootfs是文件系统,嵌入式LinuxFlash一般会有多个分区,kernelrootfs分别放在两个分区,因此需要分别下载;

6、将下载的三个文件(miwifi_ssh.binopenwrt-18.06.1-ramips-mt7621-mir3g-squashfs-kernel1.binopenwrt-18.06.1-ramips-mt7621-mir3g-squashfs-rootfs0.bin)拷贝到格式化为Fat32U盘;

7、路由器下电,按住reset按钮(用回形针或缝衣针),然后路由器上电。等到路由器黄灯开始闪烁时,可以释放reset按钮。等路由器重启后,使用ssh访问路由器,用root用户登录(路由器的ip地址为192.168.31.1),密码为步骤4的密码(MacUbuntu用户登录命令为ssh )。注意:这个步骤后,路由器就失去了保修。

8、在ssh命令行下

cd /extdisks/sda1 (如果你拔出再重插回U盘,这个路径可能会有变化)

mtd write openwrt-18.06.1-ramips-mt7621-mir3g-squashfs-kernel1.bin kernel1

mtd write openwrt-18.06.1-ramips-mt7621-mir3g-squashfs-rootfs0.bin rootfs0

nvram set flag_last_success=1

nvram commit

reboot

9、重启后,再次使用ssh登录(这时ip地址变更为192.168.1.1,用户名还是root,无密码),这时可以看到OpenWRT的命令行界面了。

这里解释一下上面步骤的原理:

A、小米路由器的版本缺省是没有ssh组件的,小米公司对DIY用户还是非常友好的,提供了带ssh server组件的版本。

B、通过一种特定的操作方式(插入有miwifi_ssh.bin文件的U盘,长按reset重启),将ssh功能启用,这样用户登陆后就可以做任何事情,但同时也失去了保修。

C、登陆后,如果敲入下面的命令

root@XiaoQiang:~# cat /proc/mtd

dev:    size   erasesize  name

mtd0: 07f80000 00020000 "ALL"

mtd1: 00080000 00020000 "Bootloader"

mtd2: 00040000 00020000 "Config"

mtd3: 00040000 00020000 "Bdata"

mtd4: 00040000 00020000 "Factory"

mtd5: 00040000 00020000 "crash"

mtd6: 00040000 00020000 "crash_syslog"

mtd7: 00040000 00020000 "reserved0"

mtd8: 00400000 00020000 "kernel0"

mtd9: 00400000 00020000 "kernel1"

mtd10: 02000000 00020000 "rootfs0"

mtd11: 02000000 00020000 "rootfs1"

mtd12: 03580000 00020000 "overlay"

mtd13: 012a6000 0001f000 "ubi_rootfs"

mtd14: 030ec000 0001f000 "data"

可以看到flash的分区,这里看一下几个重要的分区:

ABootloader是启动分区,上电第一条指令从这里运行,然后启动linux kernel,再挂载rootfs文件系统。如果这个分区被破坏,会导致路由器变砖

Bkernel0/kernel1以及rootfs0/rootfs1linxu内核和文件系统,双份,如果一份出错(比如路由器升级过程中断电),有备份,可以防止路由器变砖。

C、小米路由器也基于OpenWRT开发的,因此无须修改bootloader的配置,只要将kernelrootfs刷新即可。

10、回头看上面的刷文件的命令

mtd write openwrt-18.06.1-ramips-mt7621-mir3g-squashfs-kernel1.bin kernel1 (更新kernel1)

mtd write openwrt-18.06.1-ramips-mt7621-mir3g-squashfs-rootfs0.bin rootfs0。(更新rootfs0

nvram set flag_last_success=1(这个命令我不是很确定,可能是让bootloaderkernel1引导,即引导OpenWRTfirmware而不是小米的firmware

nvram commit (保存配置)

reboot(重启)

三、安装OpenWRTWeb UIshadowsocks相关组件

按照上面步骤烧入新的fireware后,只是一个最小化的系统,大多数的组件都没有安装,包括Web管理器。因此首先第一步安装Web UI进行路由器的管理。

root@OpenWrt:~# opkg update

root@OpenWrt:~# opkg install luci

LuciOpenWRTWeb UI组件,安装完毕后,就可以通过 进行管理路由器了。


四、安装ShadowSocks

登入WebUI,进入System->Software。如果没有显示所有的软件,则需要点击“Update lists”

然后搜索 shadowsocks

Shadowsocks在嵌入式设备中的开源项目是shadowsocks-libev (),是用C语言实现的。(这里一定要提一下,shadowsocks的原始作者是clowwindy,用python实现的,其他的shadowsocks项目,包括这里的shadowsocks-libev项目,都是从其github的项目中fork出去的)


我们需要关心和安装下面的组件:

luci-app-shadowsocks-libev      #shadowsocks-libevweb配置模块

shadowsocks-libev-config        #shadowsocks-libev进行配置后,会自动生成配置文件

shadowsocks-libev-ss-redir      #路由器的透明代理

shadowsocks-libev-ss-tunnel         #用于DNS查询报文的隧道,防止DNS污染(如果不安装,即使配置了ss-redir,谷歌仍然无法访问,因为国内的DNS解析的googleip是错误的)


另外,下面的模块可以不安装,主要理由是它存在一些缺点,配置起来不太方便。这个模块是设置ss-redir的规则的,但是。其中目的地址检查的配置页面(被GFW屏蔽的网站,forwardss-redir,其他网站则bypass),只能配置ip地址段,无法支持域名,因此这里就不考虑使用(比如google这么多的服务器,维护ip段还是比较麻烦的,而域名就很简单一条:*.google.com

shadowsocks-libev-ss-rules              


五、配置shadowsocks-libev

首先配置shadowsocks服务器

然后配置ss-redir

配置ss-tunnel

六、解决DNS域名污染问题

首先看一下正常的报文转发过程:

1、查找域名,发起DNS请求,从电信的DNS服务器中获得解析的ip地址;

2、浏览器的http报文发送到路由器,通过路由器的netfilter模块进行地址转换后(NAT转换),发送给目的服务器;

3、目的服务器回应的http报文,返回到路由器,再通过netfilter模块,回到浏览器。

我们访问某个被GFW屏蔽的网站,和上面的步骤相比,有了一些差别

1、首先对域名做一次判断,如果是被GFW的域名,将DNS的请求转发到ss-tunnel(不是转发给电信的DNS服务器,而是Google8.8.8.8 DNS服务器),获得其ip地址;

2、获取ip地址后,路由器的DNS模块会调用ipset组件,在内存中产生一张gfwlist的表,将该ip地址保存到该表中;

3、浏览器的http报文tcp报文,在通过路由器的转发模块(netfilter)时,在gfwlist表中对比该ip地址,如果该ip在表中,则将tcp报文转发到ss-redir;

4、服务器返回的报文回发到ss-redir,然后再回到浏览器;

这里,很多人可能会有一个疑问,为什么需要ipset组件,需要在内存中维护一个gfwlistip表,而不是直接查找域名表?原因很简单,linux内核的netfilter模块只会对比ip地址,它不认识域名。

最后我们需要一张GFW的域名表,很幸运,世界上好人很多,在 可以下载到。

保存并打开该文件,可以看到

# dnsmasq rules generated by gfwlist

# Last Updated on 2018-09-10 13:19:26

#

server=/030buy.com/127.0.0.1#5353

ipset=/030buy.com/gfwlist

server=/0rz.tw/127.0.0.1#5353

ipset=/0rz.tw/gfwlist

server=/1000giri.net/127.0.0.1#5353

ipset=/1000giri.net/gfwlist

这个文件是提供给dns服务的配置文件

“server=”这个选项表示,如果解析域名*.030buy.com,则将DNS解析报文转发到本地的5353端口(即转发到ss-tunnel),这样就把被GFW屏蔽的网站的DSN请求,转发到ss-tunnel通道进行解析,解决了DNS域名污染问题

“ipset=”这个选项表示,如果解析域名 *.030buy.com,则在ipset的内存中的gfwlist表项中,添加解析完成的ip地址。


现在可以开始干活了:

首先需要安装 dnsmasq-full,因为OpenWRT缺省安装的是dnsmasq,它不支持ipset

可以在web UI操作,也可以在ssh的命令行操作

root@OpenWrt:~# opkg update

root@OpenWrt:~# opkg install dnsmasq-full

root@OpenWrt:~# opkg remve dnsmasq


然后将之前下载的dnsmasq_gfwlist_ipset.conf 上载到路由器的/etc目录下

为了实现从pc到路由器的上载,需要安装openssh-sftp-server

root@OpenWrt:~# opkg install openssh-sftp-server


然后从pc上载该文件到路由器的/etc目录中

ningdeMBP:tmp ning$ sftp root@192.168.1.1

root@192.168.1.1's password:

Connected to 192.168.1.1.

sftp> cd /etc

sftp> put dnsmasq_gfwlist_ipset.conf.txt

Uploading dnsmasq_gfwlist_ipset.conf.txt to /etc/dnsmasq_gfwlist_ipset.conf

dnsmasq_gfwlist_ipset.conf.txt                                                                    100%  355KB   2.7MB/s   00:00   

sftp> bye

不要上载到路由器的/tmp或者/var目录,这些目录是ramfs(基于内存的文件系统),系统重启后会丢失。


然后使用vi打开 /etc/dnsmasq.conf,在最后一行添加 conf-file=/etc/dnsmasq_gfwlist_ipset.conf

然后,重启dns服务

root@OpenWrt:~# /etc/init.d/dnsmasq restart

测试一下,大功告成!


root@OpenWrt:/etc# nslookup

Server:          127.0.0.1

Address: 127.0.0.1#53

Name:     

     canonical name = star-z-mini.c10r.facebook.com

Name:      star-z-mini.c10r.facebook.com

Address 1: 157.240.15.38

     canonical name = star-z-mini.c10r.facebook.com

Address 2: 2a03:2880:f10f:86:face:b00c::50fb

正常情况下对facebookDNS访问是下面这样的:

root@OpenWrt:/etc# nslookup

Server:          127.0.0.1

Address: 127.0.0.1#53

Name:     

Address 1: 74.86.12.172

*** Can't find : No answer

再测试一下ipset功能

root@OpenWrt: ipset -N gfwlist hash:ip

root@OpenWrt:/etc# ipset list

Name: gfwlist

Type: hash:ip

Revision: 4

Header: family inet hashsize 1024 maxelem 65536

Size in memory: 56

References: 0

Number of entries: 0

Members:

root@OpenWrt:/etc# nslookup

Server:          127.0.0.1

Address: 127.0.0.1#53

Name:     

     canonical name = star-z-mini.c10r.facebook.com

Name:      star-z-mini.c10r.facebook.com

Address 1: 31.13.82.38

     canonical name = star-z-mini.c10r.facebook.com

Address 2: 2a03:2880:f10f:86:face:b00c::50fb

root@OpenWrt:/etc# ipset list

Name: gfwlist

Type: hash:ip

Revision: 4

Header: family inet hashsize 1024 maxelem 65536

Size in memory: 136

References: 0

Number of entries: 2

Members:

172.217.25.202

31.13.82.38

ipset gfwlist表增加了两个ip地址,表明ipset表项建立成功。


七、最后一步

在命令行中,做如下的配置,即完成最后一步

ipset -N gfwlist hash:ip

iptables -t nat -I PREROUTING -p tcp -m set --match-set gfwlist dst -j REDIRECT --to-port 1100

上面命令的解析:

第一条:使用ipset命令在内存中建立一个gfwlist的表,这样dns在解释被GFW屏蔽的域名时,将域名解释成功的ip地址写入该表

第二条:最重要的一条规则,下面是每个参数的意义:

iptables: iptables是一个用户态的应用程序,可以对linuxnetfilter模块进行规则配置

-t nat -I PREROUTING: 针对的是PREROUTING链的nat表,即入口处的报文

-p tcp: 针对tcp报文

-m set --match-set gfwlist dst: 对于目的地址为ipsetgfwlist表中的ip地址

-j REDIRECT --to-port 1100: 报文转发到1100端口(ss-redir的本地端口)

合起来,即对于目的地址匹配gfwlist表的tcp报文,转发到1100端口,即ss-redir的端口

对于UDP,做如下的配置(请先确认你的shadowsocks服务器支持udp的代理)

ip rule add fwmark 0x01/0x01 table 100

ip route add local 0.0.0.0/0 dev lo table 100

iptables -t mangle -A PREROUTING -p udp -m set --match-set gfwlist dst -j TPROXY --on-port 1100 --tproxy-mark 0x01/0x01

上面的UDP规则,我不清楚是否有错误,因为不知道该怎么测试。我用YouTube测试,但YouTube走的好像是tcp报文

如果遇到下面的错误,说明没有安装ip-tiny组件

root@OpenWrt:~# ip rule add fwmark 0x01/0x01 table 100

ip: invalid argument '0x01/0x01' to 'fwmark'

如果遇到下面的错误,说明没有安装iptables-mod-tproxy

root@OpenWrt:~# iptables -t mangle -A PREROUTING -p udp -m set --match-set gfwlist dst -j TPROXY --on-port 1100 --tproxy-mark 0x01/0x01

iptables v1.6.2: unknown option "--on-port"

然后在将这些规则保存起来,这样路由器重启后,不会丢失这些配置。

Network->Firewall->Custom Rules 保存至


八、gfwlist的更新

有些网站没有收录在gfwlist,因此需要手动添加。Openwrt在家里电信的网络中,经常打开奇慢,添加到列表中就快很多。

ssh的命令行中

root@OpenWrt:~# vi /etc/dnsmasq_gfwlist_ipset.conf

添加类似于下面的规则:

# add by ning

server=/openwrt.org/127.0.0.1#5353

ipset=/openwrt.org/gfwlist

然后重启一下DNS服务

root@OpenWrt:~# /etc/init.d/dnsmasq restart

九、一些调试手段

1、如果错误地保存了一些配置导致pc无法通过ssh连接路由器(比如我曾经配置了一条错误的iptables命令导致pc无法连接路由器),需要用针按住reset 5秒以上,看到灯出现闪烁时放开,这时路由器会恢复缺省配置;

2、使用logread命令查看日志,有些错误日志对问题定位很有帮助;

3、有些问题可能需要通过源代码才能定位,其实对于程序员来说并不算复杂,设置开发环境、下载源码编译,调试、修改。这里就不展开了。

阅读(7868) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chou_o_ning2018-10-12 15:30:42

OpenWrt V18.06版本,mt76芯片组的的2.4GWiFi有缺陷,请谨慎使用。(5G频段是OK的)