[DD-WRT] 为 Netgear R6300v2 交叉编译 shadowsocks-libev

在开头强烈推荐一篇介绍交叉编译概念的文章,对交叉编译不熟悉的同学建议先读一遍,可以帮助解决接下来可能会遇到的很多问题。

Building and cross-compile tutorial

家里用的路由器是 Netgear 的 R6300v2,刷的是K大去年停止维护前的最后一个版本

DD-WRT v24-sp2 (10/08/14) kongac – build 25100M

之前在学校的时候用它装了迅雷固件脱机下载,用内置的PPTP VPN建了个 VPN Server,跑了一年感觉性能强劲毫无压力,之后一直就这么在用着。到了北京之后情况有了点小变化,PPTP 在外网情况下很容易被探测出来并且被联通直接封锁。所以需要从 PPTP 换成 OpenVPN,在 DD-WRT 下折腾了两天配置,走了不少弯路,总算搞定了,家里的线路连接海外ip时十分稳定,所以在公司的时候也用 OpenVPN 连到家里将国外的流量转发出去,使用了几天感觉很理想,于是想进一步优化一下方案,将 Shadowsocks 部署在路由器上,这样所有可以通过 VPN 连接进来的设备就可以免客户端实现透明翻墙了,对 iOS 设备来说是一个比 VPN 直连更好的方案。

查了一下,Shadowsocks-libev 可以运行在 OpenWRT 上并且已经有前人成功为 DD-WRT 平台成功交叉编译,其中最为详细的一篇是在腾袭的博客里面提到的,不过文章里面提供的版本比较旧了,是1.4.6版本的,这也是公开资源里面能找到的最新的版本了。看了一下距离现在(2.3.1)之间已经相差了很多个版本,本着我用新不用旧的心理产生了重新编译一份新版本的想法。既然前人已经证实了在同样的硬件+软件平台下可以运行,那么基本新版本的代码也可以拿过来运行。

不过首先交叉编译的概念对我来说就很陌生,顺手搜了一发,根据百度百科的说法,就是在一个平台上生成另一个平台的可执行代码。那这句话很好理解了,比如嵌入式设备的存储空间和计算能力有限 ,可能需要在其他平台上直接使用嵌入式设备的运行环境编译好可执行文件拷贝过去直接运行。那么接下来的工作主要还是围绕编译进行的,基本上是“选择对应的工具链(toochain)—设置编译参数—排除问题—测试”,在第一次编译时,参照前面腾袭博客里的文章,除了作者提到的几个地方之外,又又一些新的问题出现了,所以整个排坑的过程持续了将近一天,关键的工作主要由我的同事完成,他在编译和调试方面的经验让我少走了很多弯路。我们一起编译成功了2.3.1版本的源码,已经在我的路由器成功运行将近3个星期的时间,v2.3.1可以在这里下载

下面我将以编译当前最新版本(2.3.3)为例,记录一下编译的过程,以便让有类似需求的同学进行参考。在阅读下面的内容之前建议首先阅读上面博客链接里的内容以便更好的理解整个过程。

假设当前的工作目录为 /home/shadowsocks-libev,首先下载 OpenSSL 和 shadowsocks-libev 的源代码,分别可以在链接里面找到最新版。下载后解压到工作目录。这里 OpenSSL 我选择了1.0.2d版本。接下来下载编译所需要的 toolchain,原文中的链接已经失效了,DD-WRT 官网的下载页面进行了调整,所有的 toolchain 都被打包到一个大的压缩包(1.8G)中,下载下来后经过漫长的解压得到很多不同的组合。

toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi
toolchain-arm_mpcore+vfp_gcc-4.8-linaro_musl-1.1.4_eabi
toolchain-armeb_xscale_gcc-4.8-linaro_musl-1.1.2
toolchain-i386_gcc-4.7-linaro_uClibc-0.9.33.2
toolchain-mips64_octeon_64_gcc-4.9-linaro_uClibc-0.9.33.2
toolchain-mips_34kc_gcc-5.1.0_musl-1.1.9
toolchain-mips_mips32_gcc-4.8-linaro_musl-1.1.6
toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2
toolchain-mipsel_3.3.6_BRCM24
toolchain-mipsel_4.1.1_BRCM24
toolchain-mipsel_74kc+dsp2_gcc-5.1.0_musl-1.1.9
toolchain-mipsel_gcc4.1.2
toolchain-powerpc_e300c3_gcc-4.8-linaro_uClibc-0.9.33.2
toolchain-powerpc_gcc-4.6-linaro_uClibc-0.9.33.2
toolchain-x86_64_gcc-4.8-linaro_uClibc-0.9.33.2

我们最后选择最接近的一个 toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi ,拷贝到工作目录中来。然后将 toolchain 下的 bin 目录加入到环境变量中

export PATH="/home/shadowsocks-libev/toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi/bin:$PATH"

首先编译OpenSSL,腾袭在博客中提到需要首先解决缺少 uClibc 才需要的 termio.h 头文件的问题,我们现在使用的是musl,所以首先在 Configure 文件中将 -DTERMIO 改为 -DTERMIOS

sed -i 's/-DTERMIO/-DTERMIOS/g' Configure

然后修改 OpenSSL 源码中 ./crypto/ui/ui_openssl.c 文件,在下面一行(222行)

#ifdef TERMIOS

前添加以下内容来绕过这个问题。

#define TERMIOS
#undef  TERMIO
#undef  SGTTY

接下来可以进行配置和编译了

CC=arm-linux-gcc 
CXX=arm-linux-g++ 
AR=arm-linux-ar 
RANLIB=arm-linux-ranlib 
./Configure no-asm shared --prefix=/home/shadowsocks-libev/ssl linux-armv4
make
make install

编译成功后会在 /home/shadowsocks-libev/ssl 目录下得到编译好的 OpenSSL,接下来继续编译 Shadowsocks-libev。

编译 Shadowsocks 的时候是一样的思路,只不过中间出现的一点迷惑性的问题把思路带偏了,排查了很久才解决。在 Configure 的过程中,配置脚本会尝试对一些脚本进行尝试编译,这过程中如果出现问题就会终止脚本报错,我在编译2.3.1版本的代码时,出现了如下的错误提示

checking for linux/netfilter_ipv6/ip6_tables.h... no
configure: error: Missing netfilter headers

这里的提示 Missing netfilter headers 实际上是由Configure脚本在 try compile 的时候出错导致的,但是错误提示却让人以为是没有找到这个文件,所以一开始解决问题的方向错了, 耽误了很长的时间,最后找到错误的原因是 linux/netfilter_ipv6/ip6_tables.h 文件中使用了 u_int16_t 和 u_int8_t 类型却没有找到声明的原因,因此在编译的时候会因为 undefined type 而中止。我这里简单地在 /home/shadowsocks-libev/toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi/include/linux/netfilter_ipv6/ip6_tables.h 文件中添加了一行(可以添加在第25行)

#include <sys/types.h>

解决了问题,不知道这样的解决方案会不会带来什么其他的问题,不过在我后面的时候中一切正常。有更彻底地解决方案欢迎留言。

后记:在最近的更新中,可能增加了 pcre、zlib 等依赖,需要同 openssl 交叉编译的方法同样编译好后再使用 –with-xxx= 的方法在编译时传入,此处不再赘述,可以直接使用下面编译好的版本或者自行尝试~

CC=arm-openwrt-linux-muslgnueabi-gcc 
CXX=arm-openwrt-linux-muslgnueabi-g++
AR=arm-openwrt-linux-muslgnueabi-ar 
RANLIB=arm-openwrt-linux-muslgnueabi-ranlib 
./configure --prefix=/home/shadowsocks-libev/shadowsocks-libev-dd-wrt --with-openssl=/home/shadowsocks-libev/ssl --host=arm-linux --disable-ssp
make
make install

最后附上编译好的版本的下载链接吧,没有条件的可以直接用,只打包了可执行文件:

(我会在我的路由器服役周期内不定期进行新版本的编译并更新下载链接,如果有新版本发布后我没及时更新,可以在留言里喊我)

2016-11-16 更新:编译v2.5.6 –disable-ssp

2017-04-06 更新:在 R8000 下测试可以直接使用

  • 点击下载:v2.5.6
  • 点击下载:v3.0.5  3.0后改动较大,正在测试中

支持固件列表(经本人测试正常使用)

  • DD-WRT v24-sp2 (10/08/14) kongac – build 25100M
  • DD-WRT v24-sp2 (02/04/15) std – build 26138(推荐,感觉相比于25100M版本在5G下的表现稳定了许多,同时也是目前推荐使用的最新的 stable 版本)
  • DD-WRT v3.0-r28000M kongac (10/24/15)
  • DD-WRT v3.0-r31800M kongac (03/31/17)

“[DD-WRT] 为 Netgear R6300v2 交叉编译 shadowsocks-libev”的22个回复

      1. zlib的问题我解决了,configure有隐含参数 –with-zlib-include= –with-zlib-lib= ,另外,要仅仅靠4个文件拷到jffs中就能用,必须选择–enable-static 楼主漏掉了

        1. 我编译的时候看到这个参数了,但是编译了zlib传进去总是有错误,没能解决,就只好disable了。 –enable-static影响的是什么?这个还真不清楚,我看默认确实是no,但是我这里好像直接拷过来还是可以用。

          1. zlib要先下载编译,
            [ -e zlib-1.2.8.tar.gz ] || wget -O zlib-1.2.8.tar.gz http://zlib.net/zlib-1.2.8.tar.gz
            tar -ixf zlib-1.2.8.tar.gz
            cd zlib-1.2.8/
            export CHOST=arm-linux-gcc
            ./configure –prefix=$DEST/zlib –static
            make clean && make
            ZLIB=pwd
            cp ./zlib.h ./zconf.h /home/wjw/R6300v2/toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi/include
            cp ./libz.so.1.2.8 /home/wjw/R6300v2/toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi/lib/libz.so
            cd ..

            ./configure –prefix=$DEST –enable-static –with-openssl=$DEST/ssl –with-zlib=$ZLIB –with-zlib-include=$ZLIB –with-zlib-lib=$ZLIB –host=arm-linux

          2. 很奇怪,我之前就是这样编译的 zlib 。但是编译出来的zlib在 2.4.0 ~ 2.4.1 版本的 shadowsocks-zlib 代码中都都编译不通过,Configure 的时候会提示
            checking for zlib.h... yes
            checking for compress2 in -lz... no
            configure: error: "zlib libraries not found."

            在 config.log 中,具体的错误是
            configure:12926: checking zlib.h usability
            configure:12926: arm-linux-gcc -c -g -O2 -I/home/shadowsocks-libev/zilb/include -I/home/shadowsocks-libev/zlib/include conftest.c >&5
            configure:12926: $? = 0
            configure:12926: result: yes
            configure:12926: checking zlib.h presence
            configure:12926: arm-linux-gcc -E -I/home/shadowsocks-libev/zilb/include -I/home/shadowsocks-libev/zlib/include conftest.c
            configure:12926: $? = 0
            configure:12926: result: yes
            configure:12926: checking for zlib.h
            configure:12926: result: yes
            configure:12940: checking for compress2 in -lz
            configure:12965: arm-linux-gcc -o conftest -g -O2 -I/home/shadowsocks-libev/zilb/include -I/home/shadowsocks-libev/zlib/include -L/home/shadowsocks-libev/zilb/lib -L/home/shadowsocks-libev/zlib/lib conftest.c -lz >&5
            /home/shadowsocks-libev/zlib/lib/libz.a: error adding symbols: File format not recognized
            collect2: error: ld returned 1 exit status

            zlib 编译的时候没有什么错误,尝试着重新编译了几遍,最后都是这样,最开始我就是遇到这样的问题,所以后面一直都是用的是 –without-zlib,你是已经带着 zlib 编译成功了吗?方便留一个联系方式请教一下吗?

          3. 是的,已经带zlib编译通过,经测试可以工作。看你的出错信息是zlib调用的编译器不对,用gcc了,导致后面ss想把它加进来的时候,出错了。zlib比较老,不能支持cc一类变量,要用chost变量,你看我的脚本。这个博客是你自己搭的吧,那你应该可以看到我的邮箱

  1. 非常感谢楼主分享交叉编译经验。不过我在编译SS中遇到问题说OPENSSL库没找到。但我的OPENSSL编译成功,SS在Configure过程中可以出错,错误信息如下,:

    checking for openssl/err.h… yes
    checking openssl/sha.h usability… yes
    checking openssl/sha.h presence… no
    configure: WARNING: openssl/sha.h: accepted by the compiler, rejected by the preprocessor!
    configure: WARNING: openssl/sha.h: proceeding with the compiler’s result
    checking for openssl/sha.h… yes
    checking openssl/pem.h usability… yes
    checking openssl/pem.h presence… no
    configure: WARNING: openssl/pem.h: accepted by the compiler, rejected by the preprocessor!
    configure: WARNING: openssl/pem.h: proceeding with the compiler’s result
    checking for openssl/pem.h… yes
    checking openssl/engine.h usability… yes
    checking openssl/engine.h presence… no
    configure: WARNING: openssl/engine.h: accepted by the compiler, rejected by the preprocessor!
    configure: WARNING: openssl/engine.h: proceeding with the compiler’s result
    checking for openssl/engine.h… yes
    checking for EVP_EncryptInit_ex in -lcrypto… no
    configure: error: OpenSSL libraries not found.

    不知楼主有何指教?

      1. 非常感谢博主回复啊!真心感谢!
        我用得Ubuntu编译,完全按照你的过程来的,Openssl版本用的一样的1.0.2d版本,Shadowsocks用的最新的2.4.1版,因为有人发现2.4.0有漏洞,而且作者已经修复。
        目标路由器也是R6300v2,编译器也是用的DD-WRT官网的toolchain-arm_cortex-a9_gcc-4.8-linaro_musl-1.1.5_eabi 。
        OPENSSL编译成功,也能看到相应文件,但是编译SS的时候就提示OPENSSL库找不到,真是奇怪。而且出错在Configure阶段,和编译器关系不是太大吧~
        再次感谢博主回复~

        1. 如果你按照我帖子里面的步骤来的话,应该是没有问题的,我刚才试着编译了一份2.4.1版本的源码,编译成功并且在路由器上简单跑了一下,可以正常使用,已经在下面更新下载链接了。针对你遇到的问题,你可以看一下编译的时候源码目录下面 config.status 和 config.log 里面有没有什么有用的信息,定位一下具体的错误问题。因为从 configure 脚本里看好像在这一步会进行尝试编译的动作,我上面的 Missing netfilter headers 问题就是在这一步脚本进行 try compile 动作报错,然后在 log 里面看到具体错误信息的。

          1. 恩,谢谢,暂时没找到原因,先用你编译的用住先,以后再研究为什么。我用这个toolchain编译的最简单的hello world在r6300v2上也没有任何输出显示,不知道是不是编译器有问题。

    1. 简单的教程可以参考这里,http://www.right.com.cn/forum/forum.php?mod=viewthread&tid=158405&highlight=shadowsocks 其实这已经是可以直接用的可执行文件了,剩下的是一些配置上的东西,进阶一点的用法是配合路由表自动为国外的流量走代理。我正在写一篇详细些的教程,可以关注一下。这个帖子里介绍的哪一部分不明白也可以在这里留言。

  2. 楼主您好,我在编译ss-libev的时候遇到“找不到pcre”的错误 (Cannot find pcre library. Configure –with-pcre=DIR).
    请问pcre,是装在本机(debian),还是说也需要用arm的toolchain交叉编译成arm平台的pcre?

    1. 推荐一篇交叉编译的文章,我刚读了一下觉得基础的流程讲的比较详细,可以解决一些概念上的疑惑。http://www.fabriziodini.eu/posts/cross_compile_tutorial/

      1. 谢谢您的回复,中午吃饭回来刚看到。请看我上一条回复,出错像是编译arm平台时缺少一个可执行文件。。。

    2. 想了一下,应该也需要编译成arm平台: ./configure –prefix=$DEST/pcre –host=arm-linux
      可是:
      $ cd ../shadowsocks-libev
      $ ./configure –prefix=$DEST/shadowsocks-libev-ddwrt –enable-static –with-openssl=$DEST/ssl –with-pcre=$DEST/pcre –host=arm-linux

      Output

      checking checking for pcre includes in $DEST/pcre… checking for pcre headers in $DEST/pcre/include… ok
      configure: adding $DEST/pcre/lib to RPATH
      checking for library containing pcre_exec… no
      configure: error: Cannot find pcre library. Configure –with-pcre=DIR

      看样子可以找到pcre的header,但是找不到pcre_exec。搜过了一下编译pcre的output目录,没有这个可执行文件,不知道问题出在哪里。。。

      1. 问题应该出现在你编译 pcre 的过程中。我今天用最新版本的 2.5.6 编译的时候也提示缺少 pcre,下载了 pcre 重新编译测试了一下,根据 Github 上面作者的说法,需要用 pcre 不能用 pcre2。编译的时候我只加了这两个额外的参数 –enable-static –host=arm-linux 请检查你 pcre 的交叉编译是否正确。

欢迎留言讨论