Tag: ddwrt


ddwrt OPTW2 (Broadcom SoC such as brcm47xx) 笔记

ddwrt OPTW2 (Broadcom SoC such as brcm47xx) 笔记

OPTW2

安装脚本: (已支持3.X kernel)

# Install script
wget -O /tmp/prep_optware http://dd-ware.googlecode.com/svn/otrw2/prep_optware
sh /tmp/prep_optware

安装后, 发现默认每次路由启动时写入 /tmp/root/.profile 覆盖 PATH, 解决方法是修改 /opt/etc/init.d/S05chngProfile, 在 "echo "fi" >>/tmp/root/.profile" 后面加入:

echo "source /jffs/etc/profile" >>/tmp/root/.profile

git

opkg install git

安装后 git pull 失败, workaround:

在用户 profile 脚本 (每次 bash 加载时执行的脚本, 参考)中写入:
alias git="git --exec-path=/opt/usr/libexec/git-core"

然后在 git-core 目录下创建符号链接
ln -s $(which git) /opt/usr/libexec/git-core/git

然后需要创建 git 配置信息
git config --global user.name "Jacques De SAGAN"
git config --global user.email "jacquesde@sagan.me"
git config --global credential.helper cache

将 git 配置信息保存到 /jffs 里
cp /tmp/root/.gitconfig /jffs/

在启动脚本里增加:
cp /jffs/.gitconfig /tmp/root/

之所以这样蛋疼是因为 ddwrt 是只读文件系统, root 用户目录 /tmp/root 位于内存里, 每次启动都会重置.

这样可以 pull 无需验证的公开 repository 了, 但是还无法用 ssh 访问私有 git 库, 必须额外配置公钥验证. 这里我试过 ddwrt 自带的 ssh (openssh) 还有 OPTW2 里的 ssh 都有问题, git 无法使用其连接. 一个解决方法是用 dropbear 这个 ssh client 替代.

生成 dropbear 专用的 ssh identity key:

dropbearkey -t rsa -f ~/.ssh/id_rsa

生成的 key 是 dropbear 专用的二进制格式, 用下面命令查看它的 openssh 格式公钥

dropbearkey -y -f ~/.ssh/id_rsa | head -n 2 | tail -1

然后将输出的文本添加到 bitbucket 等 git 托管网站的 ssh authoried_keys 里.

创建一个脚本用于 git 的 ssh 访问, 例如命名为 ~/.gitssh.sh

#!/bin/sh
dbclient -y -i ~/.ssh/id_rsa $*

在 profile 脚本里设置 git 使用 .gitssh.sh 作为 ssh:

export GIT_SSH=/jffs/sagan/scripts/.gitssh.sh

OPTW2 包

package name说明备注













ddwrt 笔记

ddwrt 笔记

本笔记里内容基于如下环境:
路由器: Asus RT-N16 (mipsel 32位架构)
固件: ddwrt K3 r24461 big (3.10.44 内核)

网络

获取 wan 网关 ip
nvram get wan_gateway

获取 wan interface ip
nvram get wan_ipaddr

这几个 IP 是 ddwrt 在 ppp 拨号连接 /断开 时维护设置的, 不受 VPN 连接 /路由表更改等操作影响.

指定一个直连 (不走 VPN)的策略路由表 table 1
ip route add 0/0 via $(nvram get wan_gateway) table 1

对路由器本机向外发出的 ping (icmp) 打标记. 注意在 OUTPUT 阶段无法使用 -o (路由还没确定), 必须根据数据包dst地址是外部来判断
iptables -t mangle -A OUTPUT ! -d 192.168.0.0/16 -p icmp -j MARK --set-mark 0xa

对含有 0xa (10) 标记数据包 走 table 1 路由
ip rule add prio 1 fwmark 0xa table 1

关于标记 mark: mark比较特殊,它不是包本身 的一部分,而是在包穿越计算机的过程中由内核分配的和它相关联的一个字段。它可能被用来改变包的传输 路径或过滤。 mark 字段的值是一个无符号的整数, 在32位系统上最大可以是4294967296(就是2的32次方)(默认mark是0, 表示没有)
mark 值格式可以是10进制数字或 0xFF 这种格式的二进制. 推荐16进制, 因为 ip rule list / iptables -t mangle -vxnL 显示时候均输出16进制

ping 命令可以用 -m mark 设置icmp包标记值 (不是所有 build 都支持)

用户

增加一个用户, 需要修改启动脚本, 开机时写入 /etc/passwd (实际上映射了 /tmp/etc/passwd )

grep -q direct /tmp/etc/passwd || echo "direct:*:1024:1024:direct connecting users:/var:/bin/false" >> /tmp/etc/passwd

iptables 对属于这个用户进程的外出流量打标记 (需要先自己编译安装 iptables 和 owner extension 扩展)

iptables -t mangle -A OUTPUT -m owner --uid-owner 1024 -j MARK --set-mark 0xa

-----------------------------------------------------------------------------------------------------------
This module attempts to match various characteristics of the packet creator, for locally generated packets. This match is only valid in the OUTPUT and POSTROUTING chains. Forwarded packets do not have any socket associated with them. Packets from kernel threads do have a socket, but usually no owner.

[!] --uid-owner username
[!] --uid-owner userid[-userid]
Matches if the packet socket's file structure (if it has one) is owned by the given user. You may also specify a numerical UID, or an UID range.
[!] --gid-owner groupname
[!] --gid-owner groupid[-groupid]
Matches if the packet socket's file structure is owned by the given group. You may also specify a numerical GID, or a GID range.
[!] --socket-exists
Matches if the packet is associated with a socket.
-----------------------------------------------------------------------------------------------------------

--uid-owner 无法应用于 ping 之类的 RAW_SOCKET 发出的数据包 ?

貌似我现在 (2014.10)的 ddwrt 固件 (svn24461, 3.10.44 kernel) 存在 bug, iptables owner match module (kernel xt_owner) 工作不正常, 从测试上看, iptables -m owner 的 --uid-owner 总是返回 False, 即使是对应用户所属进程发出的数据包.

ipkg repository setup

/etc/ipkg.conf 不可编辑 (根分区只读), 所以要修改 ipkg repository 设置只能用 mount 形式

创建 /jffs/etc/ipkg.conf, 使用 OpenWrt 的 repository 源, 建议使用对应内核的 OpenWrt build 的源, 3.10.44 内核的 OpenWrt 版本是 barrier_breaker 14.07-rc1, 或者使用最新版的 OpenWrt snapshot repository (但里面部分 kmod-* 内核模块 package 无法安装)

src base http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/base
src main http://downloads.openwrt.org/snapshots/trunk/brcm47xx/packages/packages
#src attitude http://downloads.openwrt.org/attitude_adjustment/12.09/brcm47xx/generic/packages
dest root /jffs
dest mmc /mmc
dest opt /opt
dest smbfs /tmp/mnt/smbshare
dest ram /tmp

startup 脚本里增加:

mount --bind /jffs/etc/ipkg.conf /etc/ipkg.conf

OpenWrt 的源里很多软件 ddwrt 安装都会报兼容性问题, 但实际上可以正常运行. 建议关闭 ipkg 的 dependency 错误报告, 在 profile 脚本里增加:

alias ipkg="ipkg -force-depends"

Dnsmqsq

ddwrt 里面自带的 dnsmasq 不知道是什么破版本的, 有旧又烂, 什么高级特性都不支持. 建议换掉.

安装 OpenWrt 里的 dnsmasq-full: (推荐安装到默认位置 /jffs, /opt usb 设备可能启动时并未立即挂载完全 )

ipkg -force-depends install dnsmasq-full

在 ddwrt startup 脚本里设置用安装的 dnsmasq-full 里的 dnsmasq 代替系统内置的 dnsmasq: (我不确定改 PATH 优先级有没有作用, 因为 ddwrt一些系统服务的 PATH 貌似是固定的, 所以还是 mount 可靠)

mount --bind /jffs/usr/sbin/dnsmasq /usr/sbin/dnsmasq


ipset

需要内核模块支持:
* kmod-nfnetlink
** kmod-ipt-ipset
ip_set.ko
ip_set_bitmap_ip.ko
ip_set_bitmap_ipmac.ko
ip_set_bitmap_port.ko
ip_set_hash_ip.ko
ip_set_hash_ipport.ko
ip_set_hash_ipportip.ko
ip_set_hash_ipportnet.ko
ip_set_hash_net.ko
ip_set_hash_netiface.ko
ip_set_hash_netport.ko
ip_set_list_set.ko
**
* xt_set.ko (alias: ipt_set, iptables set 模块)

测试内核模块是否支持:
ipset list
// 如果nfnetlink模块未加载 (并且没有编译在内核里), 会提示 "can't connect to kernel." 错误.

测试内核是否支持 xt_set.ko: 执行一条 iptables -m set 命令:

ipset create direct hash:ip
iptables -t mangle -A OUTPUT -m set --match-set direct dst -j MARK --set-mark 0xa
// 如果 xt_set.ko 未加载, iptables 会报错 (iptables: No chain/target/match by that name.)
// 如果 iptables 没有 -m set 动态库, 会提示 match not found 之类错误
cat /proc/net/ip_tables_matches
// see there is a "set"


这个版本 ddwrt 版本内核里已经编译进去了 nfnetlink, 但不包括 xt_set.ko (并且固件自带的内核模块目录里也没有), 所以需要自己编译 xt_set.ko 内核模块. 然后在路由器上加载:

insmod /path/to/xt_set.ko


安装 ipset 用户空间工具: ipkg install ipset

使用:
---------------------------
Usage: ipset [options] COMMAND

Commands:
create SETNAME TYPENAME [type-specific-options] ( create == -N)
Create a new set
add SETNAME ENTRY
Add entry to the named set
del SETNAME ENTRY
Delete entry from the named set
test SETNAME ENTRY
Test entry in the named set
destroy [SETNAME]
Destroy a named set or all sets
list [SETNAME] (list == -L)
List the entries of a named set or all sets
save [SETNAME]
Save the named set or all sets to stdout
restore
Restore a saved state
flush [SETNAME]
Flush a named set or all sets
rename FROM-SETNAME TO-SETNAME
Rename two sets
swap FROM-SETNAME TO-SETNAME
Swap the contect of two existing sets
-------------------------

常用的 set type: (括号里是别名, 两个均可使用)
* bitmap:ip: can store up to 65535 (B-class network) entries. You can store same size network addresses in this kind of sets as well and an IP address will be in the set if the network address it belongs to can be found in the set.
* hash:ip (iphash): 可以存储任意个 ip 地址. Same size network addresses can be stored in an hash:ip type of set as well.
* hash:net: 存储任意个不同子网长度的网络

创建一个 "direct" ipset:

ipset create direct hash:ip

操作 ipset 里面记录:
ipset add direct 8.8.8.8/32

使用 iptables 的 set match 匹配 ipset 里地址:

iptables -t mangle -A PREROUTING -m set --match-set direct dst -j MARK --set-mark 0xa
iptables -t mangle -A OUTPUT -m set --match-set direct dst -j MARK --set-mark 0xa

对 0xa 标记 设置查询路由表:

ip rule add fwmark 0xa table 1

设置 dnsmasq 将查询到的域名 ip 加入 ipset: (域名同时匹配子域名, 与 address=// 和 server=// 行为相同)

ipset=//[domain/][,]


ddwrt 交叉编译笔记

在 PC (x86, linux) 上编译 ddwrt / openwrt mips 路由器上的 C 程序

根据路由器soc和ddwrt版本选择一个 toolchain (不同的 toolchain 使用不同的 libc, 对应不同 build 的 ddwrt 固件). 所有 toolchain 在这里下载, 解压缩后7G多.

对应不同 soc 的 toolchain 版本选择:
brcm26: gcc linaro 4.5 uclibc 0.9.33.2
brcm3x: gcc linaro 4.8.

brcm47XX:

推荐使用 OpenWrt 的 toolchain, ddwrt 的 toolchain 就是一坨屎, 而且基本没有什么文档和说明.
注意这些 toolchain 里的编译工具上都是64位 (x86-64) )Linux下可执行文件.

# 或者, 下载 debian x86 下的 toolchain (这是一个比较旧的版本, 不过是32位的),使用里面的 4.1.0 mips版gcc编译
$ wget ftp://ftp.dd-wrt.com/sourcecode/toolchains.x86.debian.sp1.tar.bz2
$ tar -jxf toolchains.x86.debian.sp1.tar.bz2
$ export PATH=~/toolchains/4.1.0-uclibc-0.9.28/bin:$PATH
$ mipsel-linux-uclibc-gcc helloworld.c -o helloworld
$ file helloworld
helloworld: ELF 32-bit LSB executable, MIPS, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

使用 Makefile 时, 需要修改 Makefile 里相应路径
CROSS = mipsel-openwrt-linux-uclibc-
CC = $(CROSS)gcc
...


OpenWRT 交叉编译文档:

配置 PATH 和 STAGING_DIR 环境变量
~/.bashrc:
export PATH=$PATH:/path/to/toolchain_root/bin
export STAGING_DIR=$PATH:/path/to/toolchain_root

Pass the host and build to the build system of the package to trigger cross-compile
  • For GNU configure, use --build=architecture-unkown-linux-gny --host=architecture-openwrt-linux-uclibc (for example: ./configure --build=x86_64-unkown-linux-gnu –host=mips-openwrt-linux-uclibc)
    • Run ./config.guess to get the --build= option.
    • Check the output and ensure that 'checking whether we are cross compiling… yes' is yes.
  • For GNU make, override the CC and LD environment variables (usually not needed if GNU configure was used)
    • make CC=architecture-openwrt-linux-uclibc-gcc LD=architecture-openwrt-linux-uclibc-ld
architecture: mipsel, arm, x86_64

GNU configure

对于 brcm47xx mipsel 架构的路由器:
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc

但很多软件包的源码并不支持这种 ./configure 这种配置方式 (虽然也用了 GNU Autoconf).

GNU make

make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld

如果需要链接第三方动态库 / 头文件, 用 CPPFLAGS 和 LDFLAGS 参数:
CPPFLAGS=-I/path/to/header/files LDFLAGS=-L/path/to/dynamic/library/files

貌似最新版的 OpenWrt toolchain (OpenWrt toolchain brcm47xx mipsel (barrier breaker 14.07) ) 里存在一个 Bug, 可能导致编译器链接时出现 "undefined reference to `dlclose'" 错误, 解决方法:

in toolchain/bin/mips-openwrt-linux-uclibc-wrapper.sh,
TOOLCHAIN_SYSROOT="$TOOLCHAIN_BIN_DIR/../.."
This pointed to the parent of the actual toolchain directory, correcting it by changing to
TOOLCHAIN_SYSROOT="$TOOLCHAIN_BIN_DIR/.."
Then whole process works fine again.

已测试交叉编译软件包

路由器: Asus RT-N16 (mipsel 32位架构)
固件: ddwrt K3 r24461 big (3.10.44 内核)
工具链: OpenWrt toolchain brcm47xx mipsel (barrier breaker 14.07) (gcc-4.8-linaro_uClibc-0.9.33.2)
编译宿主环境: Ubuntu 14.04 X86_64

注: 部分包可以通过 ipkg / OPTW2 下载, 建议优先下载已有的package, 自己编译麻烦. 如果 ipkg / OPTW2 没有可以到 OpenWrt 的 Repository 里找找看 ipk 下载.

软件包交叉编译情况备注
netstat-nat-1.4.10.tar.gz (查看当前系统 NAT 连接信息工具)./configure
修改生成的 Makefile 里 CC 编译器命令为 mipsel-openwrt-linux-uclibc-gcc
make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld

traceroute 2.0.20修改 Make.rules, 设置 CROSS 变量:
$(call set, CROSS, mipsel-openwrt-linux-uclibc-)
修改含有 LDFLAGS 的另一行
$(call set, LDFLAGS, -s -L../libsupp)
make 即可

make CROSS=mipsel-openwrt-linux-uclibc- LDFLAGS= -L../libsupp

修改 traceroute.c/traceroute.c, 注释掉 getaddr 函数里下面一行代码 (否则程序在路由器上运行出错):
//hints.ai_flags = AI_IDN;

iputils-s20121221 (提供 ping, arping, traceroute6 等工具)修改 Makefile 里三行:
CC=mipsel-openwrt-linux-uclibc-gcc
ADDLIB= -lm -L$(STAGING_DIR)/lib
USE_CAP=no
make
貌似编译部分成功, 不过我就需要里面的 ping 工具.
iptables 1.4.21 (2013.11.22)
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc --prefix=/opt/sagan --enable-libipq --with-xtlibdir=/opt/sagan/lib/iptables --sbindir=/opt/sagan/bin
make
make install

iproute2-3.16.0 (2014.08) (最新完整版的 iproute2, ddwrt 自带的不支持 -batch 选项)
修改 ip/ipnetns.c, 注释掉 #ifndef HAVE_SETNS 和对应的 #endif 两行, 让程序使用自己静态包装的 setns 函数, 否则编译出错

//#ifndef HAVE_SETNS
static int setns(int fd, int nstype)
{
#ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
#else
errno = ENOSYS;
return -1;
#endif
}
//#endif /* HAVE_SETNS */

make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld
编译了一半出错, 不过需要的 ip 已经编译成功了 (ip/ip), 测试可运行, 所以报的错误就不用管了- -
libmnl-1.0.3.tar.bz2 ./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc
make
编译生成的动态库位于 src/.libs

这个包是单纯动态库, 没有任何依赖, OpenWrt repository 里有, 建议直接安装.
ipkg install libmnl
ipset-6.23.tar.bz2先交叉编译依赖的 libml;
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc --with-kbuild=/storage/ddwrt/kernel/linux-3.10-svn24461

make CPPFLAGS=-I/home/sagan/files/dev/src/libmnl-1.0.3/include LDFLAGS=-L/home/sagan/files/dev/src/libmnl-1.0.3/src/.libs
configure 时需要用 --with-kbuild 链接内核源码地址 (可以从ddwrt svn下载对应版的)

make 时需要制定 libml 的头文件和动态库文件路径

编译好的可执行文件位于 src/ipset.
程序依赖 libmnl.so.0

这个包 OpenWrt 的 repository (14.07 / snapshot) 里有 6.20 版, 建议直接安装 ipkg install ipset






编译内核模块

首先看看自己需要的内核模块是否已经包含在固件里了:

find /lib/modules/$(uname -r) -name "*.ko"

如果有, 在启动脚本里加入命令加载:

insmod /lib/modules/$(uname -r)/path/to/module.ko

如果没有, 可以自己编译. 但网上有一些编译好的内核模块 package 可以下载, 如 OpenWrt 的 kmod-* 包, 不过需要与自己的路由器 soc 类型和内核版本严格对应. 对应 brcm47xx 3.10.44 内核的 OpenWrt 的 repository 是 barrier_breaker 14.07-rc1, 里面的 kmod-系列内核模块包大部分应该都可以在 RT-N16 的 ddwrt K3 r24461 big (3.10.44 内核)固件里加载 (不过 big 版的 ddwrt builld 内核已经编译进去很多功能, 还有很多模块则已经编译好包含在固件里了, 直接可以加载).

下载对应内核版本的 ddwrt linux kernel 源代码, 需要严格匹配内核版本和 ddwrt svn 版本

svn co svn://svn.dd-wrt.com/DD-WRT/src/linux/universal/linux-3.10 -r 24461

编译需要 ddwrt 自己的工具链.下载, 解压缩后有一堆工具链文件夹, 编译内核模块需要的是"toolchain-mipsel_gcc4.1.2" 文件夹里的工具链. 将 "toolchain-mipsel_gcc4.1.2/bin" 目录加入 PATH

cd /opt
wget ftp://ftp.dd-wrt.com/toolchains/toolchains.tar.xz
tar jxvf current-toolchains.tar.bz2
PATH=$PATH:/opt/toolchain-mipsel_gcc4.1.2/bin
根据路由器架构, 将对应的 .config_XX 文件复制为 .config. 以 brcm mips 的路由为例:

cp .config_bcmmips .config

配置编译选项 (这里 mips == mipsel) (或者手工编辑 .config, 将需要编译的内核模块设为 m, 但不推荐, 需要自己处理依赖关系)
make menuconfig ARCH=mips

编译 ( 先编译一次内核再编译一次内核模块?)

make
make modules ARCH=mips

如果遇到 "drivers/net/wireless/Kconfig:284: can't open file "drivers/net/wireless/rt3352/rt2860v2_ap/Kconfig"" 错误, 修改 drivers/net/wireless/Kconfig, 注释掉相关代码(Ralink 驱动, svn中没有)

#if RALINK_DEVICE
#source "drivers/net/wireless/rt3352/rt2860v2_ap/Kconfig"
#source "drivers/net/wireless/rt3352/rt2860v2_sta/Kconfig"
#endif
# if SOC_MT7620_OPENWRT
# source "drivers/net/wireless/rt7620/rt2860v2_ap/Kconfig"
# source "drivers/net/wireless/rt7620/rt2860v2_sta/Kconfig"
# source "drivers/net/wireless/rt5592/Kconfig"
# source "drivers/net/wireless/rt7612/rlt_wifi/Kconfig"
# endif

如果遇到 " ERROR: "emfc_exit" [drivers/net/wl/wl.ko] undefined!" 错误, 将.config 里 CONFIG_WL 编译选项设为 no

CONFIG_WL=n

如果遇到 "Debug Mismatch" 错误, 关闭CONFIG_DEBUG_SECTION_MISMATCH 编译选项

CONFIG_DEBUG_SECTION_MISMATCH=n

也可以用 make 的参数传入选项

make CONFIG_DEBUG_SECTION_MISMATCH=n modules

已测试编译的内核模块

Asus RT-N16 / ddwrt K3 r24461 big (3.10.44内核) svn 24461

模块选项 (.config)说明
xt_owner.ko (netfilters iptables owner模块)CONFIG_NETFILTER_XT_MATCH_OWNER=m增加 iptables 匹配进程 uid/pid / gid MATCH 选项: ( This module attempts to match various characteristics of the packet creator, for locally generated packets. This match is only valid in the OUTPUT and POSTROUTING chains. Forwarded packets do not have any socket associated with them. Packets from kernel threads do have a socket, but usually no owner. )

-m owner
[!] --uid-owner username
[!] --uid-owner userid[-userid]
Matches if the packet socket's file structure (if it has one) is owned by the given user. You may also specify a numerical UID, or an UID range.
[!] --gid-owner groupname
[!] --gid-owner groupid[-groupid]
Matches if the packet socket's file structure is owned by the given group. You may also specify a numerical GID, or a GID range.
[!] --socket-exists
Matches if the packet is associated with a socket.

除了内核模块, 还需要用户空间 iptables 的动态库 libipt_owner.so (ddwrt 默认 iptables 不包含任何 extension, 需要自己编译 iptables )
IPV6 相关

ip6_tables.ko
nf_defrag_ipv6.ko
ip6table_filter.ko
ip6table_mangle.ko
nf_conntrack_ipv6.ko
CONFIG_IP6_NF_FILTER=m
CONFIG_IP6_NF_MATCH_RPFILTER=m
CONFIG_IP6_NF_MATCH_RT=m
CONFIG_IP6_NF_RAW=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_NF_NAT_IPV6=m
big 版的 ddwrt 已经内置了相关内核模块 (只是没有默认载入):
find /lib/modules/$(uname -r)/kernel/ -name *6*
在启动脚本里增加所需要的模块 ( ipv6, ip_tunnel, sit 三个模块 ddwrt 默认载入, 如果在 web 管理界面开启 IPV6 的话)

insmod ip6_tables
insmod ip6t_REJECT
insmod ip6table_filter
insmod nf_defrag_ipv6
insmod nf_conntrack_ipv6