萨摩公园

METO 的部落格

OpenWrt

OpenWrt/LEDE 智能路由器研究

OpenWrt/LEDE 内网转发 IPv6

最近折腾 PT 发现了其他的转发方案,重新更新了文章

  • 2018.08.31 验证 OpenWrt 18.06.1
  • 2018.04.12 更新 Passthrough 方式
  • 2017.10.19 更新 NAT6 方式

想要让路由器下的设备获得 IPv6 地址通常有三种方式:relay(中继)、NAT(地址转换)、Passthrough(穿透)。下面分三个部分谈谈具体的配置方案。

relay

OpenWrt 早在 15.05 就内置了 relay 协议,具体是通过 odhcpd 来实现的。

这种方案可以让子网设备分到公网 IPv6 的地址,是最为科学的,然而 odhcpd 的实现有很多 bug,在实际使用中会经常掉线,需要手动重启服务才能解决,体验不佳。

具体操作如下

  1. 进入路由器终端:$ ssh root@192.168.1.1

  2. 编辑 dhcp 配置文件:$ vi /etc/config/dhcp,改动如下

    config dhcp 'lan'
        option interface 'lan'
        option start '100'
        option limit '150'
        option leasetime '12h'
        option ra 'relay'
        option ndp 'relay'
        option dhcpv6 'relay'
        option ra_management '1'
    
    config dhcp 'wan'
        option interface 'wan'
        option ignore '1'
    
    config dhcp 'wan6'
        option interface 'wan'
        option ra 'relay'
        option ndp 'relay'
        option dhcpv6 'relay'
        option master '1'
    
  3. 编辑 network 文件:$ vi /etc/config/network,将地址第一个字母 f 修改为 d

    config globals 'globals'
        option ula_prefix 'dd53:abcd:abcd::/48'
    
  4. 重启即可

参考资料

Passthrough

Passthrough 的原理是将内外网进行桥接,然后对 IPv4 流量采用 NAT 方式进行转发,稳定性相当好,唯一的缺点就是路由器自身会失去 IPv6 的访问,因此如果想在路由器上做离线下载等功能就会非常麻烦。

配置如下

  1. 安装 ebtableskmod-ebtables-ipv6 模块

    $ opkg update
    $ opkg install ebtables kmod-ebtables-ipv6
    
  2. 设置 IPv4 转发

    $ interface=`ip -6 route | grep "default from" | awk '{print $7}'`
    $ ebtables -t broute -A BROUTING -p ! ipv6 -j DROP -i $interface
    
  3. 内外网桥接

    $ brctl addif br-lan $interface
    
  4. 杀死 odhcpd

    $ /etc/init.d/odhcpd stop
    $ /etc/init.d/odhcpd disable
    
  5. 点击 LAN 接口的 edit 按钮进入设置,在 IPv6 Settings 选项卡中勾上 Always announce default router. (OpenWrt 18.06.1)

做完这些步骤,稍等一会儿子网设备就会分到相应的公网 IPv6 地址了

IPv6-Passthrough

NAT

IPv6 的设计之初是没有 NAT(网络地址转换)这种东西的, 但 Linux 内核从 3.7 版本开始实现了 IPv6 的 NAT。因此这种方案实际上是「歪门邪道」,更要命的是路由器下的设备将会无法获得公网端口,BT/PT 的上传速度受到了很大影响。

Padavan 的 Linux 内核原生不支持 NAT6,不到万不得已,尽量选用其它两种方案

具体操作如下

  1. 进入软件(soft)配置界面安装 ip6tableskmod-ipt-nat6 模块

  2. 打开网络(Network)菜单下的接口(Interfaces),把最下面的 IPv6 ULA-Prefix 改为 2fff::/64

  3. 点击 LAN 接口的 edit 按钮进入设置,在 IPv6 Settings 选项卡中勾上 Always announce default router.

  4. 进入启动项管理 ,把以下命令添加入启动配置(在 exit 0 前),实现开机启动,最后重启即可。

res=`ip -6 route | grep "default from"`
gateway=`echo $res | awk '{print $5}'`
interface=`echo $res | awk '{print $7}'`

if [ "$ACTION" = ifup ]; then
    ip -6 r add default via $gateway dev $interface
    if !(ip6tables-save -t nat | grep -q "v6NAT"); then
        ip6tables -t nat -A POSTROUTING -o $interface -m comment --comment "v6NAT" -j MASQUERADE
    fi
else
    ip6tables -t nat -D POSTROUTING -o $interface -m comment --comment "v6NAT" -j MASQUERADE
    ip -6 r del default via $gateway dev $interface
fi

参考资料


端口映射

以上方案除了 NAT 方案外,可以开启 UPnP 进行高位端口映射,优化 BT/PT 体验。

方案如下

  1. 安装 miniupnpdluci-app-upnp 两个软件包
  2. services 下的 upnp 面板中开启即可

uTorrent


方案测试通过机型,不保证其它版本可用

Model Version
NETGEAR WNDR4300 OpenWrt 18.06.1
NETGEAR WNDR4300 LEDE 17.01.4
Hiwifi HC5661A LEDE snapshots
psg1218(K2) LEDE 17.01.3
Newifi mini Y1 LEDE 17.01.3

关于校园网HTTP劫持的分析

接上文 https://i-meto.com/shit-scutweb/

最近发现不少应用的联网异常,尤其是网易云音乐的播放时常请求失败,怀疑是垃圾校园网搞的鬼,于是做了一些分析。

通过抓包不难发现有不少类似 http://202.38.196.91/cache/5/05/music.126.net/**.mp3 的请求。通过查询 IP 发现这台服务器位于校内,开放了 80 端口,初步判断是一台缓存服务器。

使用 wireshark 进行抓包,得到更详细的请求过程

webp

  • 18,19,20: TCP 三次握手
  • 21: GET 请求下载 QQ 安装包
  • 22: (抢答)302 跳转,同时关闭连接 [FIN]
  • 27: 真正的应答包,[Out-Of-Order]

可以看出这是经典的单向中断访问型劫持,其原理就是通过旁路设备监听数据包,一旦发现特征(如存在相应缓存)就伪造服务器向用户发送 RST 阻止后续 TCP 连接(很像 DNS 污染的原理

network

既然只是抢答包,那么直接在路由器上丢弃就可以了。一种最简单的方式就是通过匹配字符串丢弃,详情见前文。由于效率问题,本文介绍一种基于 TTL/ID 的拦截手段。

劫持包
webp

正常应答包
webp

根据 RFC 的规定,identification 应该是公差为 1 的单调递增数列,TTL 则是每经过一个路由递减。而上图中劫持包的 RST 与握手时不一致,并且 ID 永远都是 0x0080,这是不正常的。

  • 更新:经大牛提醒,这套系统在 ISP 中非常常见,返回的 302 数据包带有 [FIN, PSH, ACK], 而正常的 302 数据包是不会同时带有上面标志位的。

抓到特征后,可以编写规则进行拦截,这里介绍 iptables 的 u32 匹配模式。

u32 允许你从数据包中提取 4 个字节,然后通过指定的掩码,偏移一定数量的单位到某个位置然后比对这个数值是否为某个值或者是否在某个区间中。(man iptables)

u32 的语法其实很简单 iptables -m u32 --u32 "Start&Mask=Range"

  • start: 表示开始取值的位置 / 字节
  • Mask: 表示掩码
  • Range: 表示匹配的范围,可以是一个使用 : 分割开的区间,也可以只是一个数

为了示范,这里贴出劫持包的 IP 报文

0000  45 00 00 e3 00 80 00 00  3a 06 c5 28 70 5a 87 eb
0010  c0 a8 01 7f                                     

TCP 报文

0000  00 50 d7 14 93 f0 17 72  fd 04 3b 71 50 19 39 08
0010  3a 5c 00 00                                     

我们关心的位置是 identification(0x0080), TTL(0x3A), Flag(5019), 也就是 IP 报文中的第 05, 08 位置,TCP 报文的第 10 位置。

对应 u32 规则就是 2&0xFFFF=0x0080, 5&0xFF=0x3A, 0>>22&0x3C@10&25=25.

参考 iptables 文档可以编写出如下规则

iptables -I FORWARD -p tcp --sport 80 -m u32 --u32 "0>>22&0x3C@10&25=25 && 2&0xFFFF=0x0080 && 5&0xFF=0x3A" -j DROP

以上三个规则可以根据需要自行组合,然后配置到路由器上(需要安装 iptables-mod-u32 模块)重启防火墙生效。和之前 string 的方案对比测试,u32 几乎感受不到效率的影响。

参考资料

胡思乱想

如果能把抢答包加个延迟转发就好了,还不用担心误拦截 XD
每次投诉完都要喝点酒,气!

吔屎啦,校园网

前几天测试一个项目时发现虾米音乐始终播不过去

查看了请求,是被劫持到一个缓存服务器上了
查了下 IP,是华南理工大学的服务器

向工信部投诉,他们受理后让我同时向网络中心联系,无法解决可以再向他们投诉。
向网络中心发了工单,没过几分钟真的接到电话,派了三个技术人员来协查。
忙了一下午,总算解决问题。

没过几天,其他网站也开始遇到这个问题了

同时发现开始劫持常用下载软件了

吔屎啦,校园网!

再投诉感觉只是挤牙膏似的把一些域名加到白名单里
干脆自己动手

抓包后发现其劫持的方法是在跟正常服务器的 HTTP 报文头之后插入一个重定向 header,共同特征是 Location: http://202.38.196.89,在 BurpSuite 里删除这个语句,继续接收报文,发现可以正常下载请求的静态文件。

问题就很好解决了,在路由器端 Drop 掉含有劫持请求的数据包,让正常的数据包能够被继续接收。

Openwrt 解决方案

打开路由器后台
在软件包页面安装必要软件 iptables-mod-filter

选择网络->防火墙->自定义规则,插入下面的语句

// 丢弃跳转该 IP 的数据包 
iptables -A FORWARD -p tcp --sport 80 -m string --string "Location: http://202.38.196.89" --algo bm -j DROP

重启问题解决


校园网变烂历程:

  • 半夜断网
  • 客户端认证方式拨号
  • 视频网站触发限速
  • 19 时后限速
  • HTTP 缓存劫持,抢答包

Openwrt 内网转发 IPv6

本文内容可能已经过期,最新:https://i-meto.com/lede-ipv6/

学校校园网是 IPv4+IPv6 双栈接入的,然而分配的 IPv6 地址只能由一台机器使用,接入路由器后下线的设备全部无法使用 IPv6,所以要对路由器进行设置内网 nat 转发。

测试路由:网件 4300 (刷入 Openwrt 15.05.1,测试在 cc 版本以上成功,其他版本自测)

  1. 进入软件(soft)配置界面安装 kmod-ipt-nat6 模块,如果下载失败可以修改软件源重试。
  2. 打开网络(Network)菜单下的接口(Interfaces),把最下面的 IPv6 ULA-Prefix 选项中把地址的第一个字符 f 改成 d.
  3. 点击 LAN 接口的 edit 按钮进入设置,在 IPv6 Settings 选项卡中把 Router Advertisement-Service 改成 server mode ,关掉 DHCPv6-ServiceNDP-Proxy,并勾上 Always announce default router.
  4. 进入启动项管理 ,把以下命令添加入启动配置(在 exit 0 前),实现开机启动,最后重启即可。
#/bin/ash
line=0
while [ $line -eq 0 ]
do
  sleep 10
  line=`route -A inet6 | grep ::/0 | awk 'END{print NR}'`
done
ip6tables -t nat -I POSTROUTING -s `uci get network.globals.ula_prefix` -j MASQUERADE
route -A inet6 add 2000::/3 `route -A inet6 | grep ::/0 | awk 'NR==1{print "gw "$2" dev "$7}'`
exit 0