梦想博客

深入Openwrt在家庭网络环境中的妙用

· 3069 words · 7 minutes to read
Tags: Openwrt

通过OpenWrt浅研网络相关姿势后使用了一段时间,恰巧最近买了新电脑,并在fox大佬的指点下实现了基于Openwrt的透明代理

该方案可以规避bt流量走内核的问题,且完整实现了目前我所有的需求,如果后续没有什么意外的话,此方案应该会最终落实在我以后的家庭网络环境中

放弃使用了istoreos,从而使用immortalwrt来获取最接近原生的openwrt体验,短短的周末两天我刷机了不下于5次,鬼知道我到底经历了什么

实操 🔗

刷机环节跳过,请自行按照自己的设备来搜索镜像,并安装指定的插件,安装完成后按照以下内容进行配置

wan口配置是dhcp还是pppoe请按需设置,且在路由中ipv4的静态路由必须指定0.0.0.0/0

# 更换国内镜像源
sed -e 's,https://downloads.immortalwrt.org,https://mirrors.cernet.edu.cn/immortalwrt,g' \
    -e 's,https://mirrors.vsean.net/openwrt,https://mirrors.cernet.edu.cn/immortalwrt,g' \
    -i.bak /etc/opkg/distfeeds.conf
	
# 更新并安装核心插件(如果是虚拟机时请额外安装:kmod-vmxnet3)
opkg update && opkg install curl wget wget-ssl kmod-nft-tproxy kmod-nft-socket luci-app-smartdns  xray-core

# 安装可选插件
opkg install htop luci-app-vnstat2 vnstat2 luci-i18n-vnstat2-zh-cn luci-app-commands luci-i18n-commands-zh-cn luci-app-diskman luci-i18n-diskman-zh-cn 

# Xray Setting
uci set xray.enabled.enabled=1
uci commit
service xray start
chown root:115414  /usr/bin/xray
chmod g+s  /usr/bin/xray

概念 🔗

本质的核心是通过smartdns进行dns查询,并将指定dns group的域名查询存入NFTset Name中,然后通过路由表判断ip是否存在该ipset,存在则按照路由表的规则走下去

仔细想想还可以通过ip-mac映射实现指定的客户端ip无法访问某些网站或者游戏的功能,以后孩子有福了

Smartdns 🔗

这里只介绍几个核心的配置

  • 增加上游dns服务器后,如果是dot或者doh则直接输入ip即可,例如阿里云的https://223.5.5.5/dns-query,然后在Advance Settings中将TLS name和Http Host中输入你自己的域名,否则会导致无法解析你自己的域名造成的dns查询失败

  • 在dns forwarding setting中选择组,在NFTset Name中输入 #4:inet#xray#dns4,#6:inet#xray#dns6

  • Domain List File可以在Download File Setting中自动下载

  • Proxy Server输入 socks5://127.0.0.1:1080

建议跳过双栈检测以及关闭Cache Persist,同时不建议以Automatically Set Dnsmasq来设置

建议首选dns服务器为dot或者doh,但是某些运营商会阻断这种查询,doh似乎暂时未发现阻断现象(国内doh)

AdGuardHome 🔗

引用AdGuardHome后会导致软路由内存占用提升,但是换来的是更方便快捷的dns拦截以及dns查询日志和dns缓存

强烈建议放弃Smartdns的缓存,因为其广告拦截功能做的不尽人意远不如AdGuardHome配置起来得心应手

除了增加默认的ch规则外,我这边额外补充了一些dns的拦截AdguardFilter

Xray配置 🔗

没有此配置可以在 /etc/xray/config.json中编辑

此配置中没有设置出站的相关内容,请按照内容自行配置

{
        "inbounds": [
                {
                        "tag": "tproxy-in",
                        "port": 1919,
                        "protocol": "dokodemo-door",
                        "settings": {
                                "network": "tcp,udp",
                                "auth": "noauth",
                                "allowTransparent": false,
                                "followRedirect": true
                        },
                        "streamSettings": {
                                "sockopt": {
                                        "tproxy": "tproxy"
                                }
                        },
                        "sniffing": {
                                "enabled": true,
                                "destOverride": [
                                        "http",
                                        "tls"
                                ],
                                "routeOnly": false
                        }
                },
                {
                        "tag": "socks-in",
                        "port": 1080,
                        "protocol": "socks",
                        "settings": {
                                "udp": true
                        },
                        "sniffing": {
                                "enabled": true
                        }
                }
        ],
        "outbounds": [
                {
                        "tag": "direct",
                        "protocol": "freedom"
                },
                {
                        "tag": "reject",
                        "protocol": "blackhole"
                },
        ],
        "routing": {
                "domainMatcher": "mph",
                "rules": [
                        {
                                "outboundTag": "reject",
                                "protocol": [
                                        "bittorrent"
                                ]
                        },
                        {
                                "outboundTag": "proxy",
                                "inboundTag": [
                                        "tproxy-in",
                                        "socks-in"
                                ]
                        }
                ]
        }
}

路由表 🔗

在每次系统重启或者加载时运行此脚本即可完成路由表的配置

WAN口为PPPOE的模式下切勿使用curl -s xxx | sh 否则会导致openwrt无法正常开机!!!!

#!/usr/bin/env sh

# 删除现有的 xray 表
nft list table inet xray > /dev/null 2>&1
if [ $? -eq 0 ]; then
    nft delete table inet xray
fi
# 创建新的 nft 表 xray
nft -f <(cat <<EOF
table inet xray {
    set dns4 {
        type ipv4_addr;
        flags timeout;
        gc-interval 15d;
    }
	
    set ips4 {
        type ipv4_addr;
        flags interval;
        auto-merge;
    }
	
    chain prerouting_filter {
        type filter hook prerouting priority filter; policy accept;
        meta l4proto tcp socket transparent 1 meta mark set 1919 accept;
        meta l4proto {tcp, udp} meta mark 1919 tproxy ip to 127.0.0.1:1919 accept;
        meta l4proto {tcp, udp} ip daddr @dns4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip daddr @ips4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
    }

    chain prerouting_dstnat {
        type nat hook prerouting priority dstnat; policy accept;
        udp dport 53 redirect to :53;
    }

    chain output_filter {
        type route hook output priority filter; policy accept;
        meta skgid 114514 accept;
        meta l4proto {tcp, udp} ip daddr @dns4 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip daddr @ips4 meta mark set 1919 accept;
    }
}
EOF
)

# 删除旧的规则
while ip rule list | grep -q 'lookup 810'; do
    ip rule del lookup 810
done
# 添加新的规则
ip rule add fwmark 1919 lookup 810
# 再次确认删除旧的ip路由
ip route flush table 810
# 将 IPv4 CIDR 添加到 nft 规则中
nft add element inet xray ips4 { 填写自己需要的ipcidr }
# 添加或替换 IPv4 路由
ip route replace local 0.0.0.0/0 dev lo table 810

路由表-Ipv6 🔗

一般情况下不建议启动ipv6相关的配置,因为很容易出现各种各样的问题

目前ipv6的大环境尚在布局中,放眼国际来看ipv6在主流网络国家中(中,美,日,欧盟等地)部署速度还行,但是其余国家很慢

由于使用Smartdns可以做到指定的域名Force AAAA SOA(不解析ipv6)来实现大部分网站走双栈,特定的域名走ipv4来规避大部分的问题,但是为了日后的兼容性以下是v6的路由表

#!/usr/bin/env sh

# 删除现有的 xray 表
nft list table inet xray > /dev/null 2>&1
if [ $? -eq 0 ]; then
    nft delete table inet xray
fi
# 创建新的 nft 表 xray
nft -f <(cat <<EOF
table inet xray {
    set dns4 {
        type ipv4_addr;
        flags timeout;
        gc-interval 15d;
    }

    set dns6 {
        type ipv6_addr;
        flags timeout;
        gc-interval 15d;
    }

    set ips4 {
        type ipv4_addr;
        flags interval;
        auto-merge;
    }

    set ips6 {
        type ipv6_addr;
        flags interval;
        auto-merge;
    }

    chain prerouting_filter {
        type filter hook prerouting priority filter; policy accept;
        meta l4proto tcp socket transparent 1 meta mark set 1919 accept;
        meta l4proto {tcp, udp} meta mark 1919 tproxy ip to 127.0.0.1:1919 accept;
        meta l4proto {tcp, udp} meta mark 1919 tproxy ip6 to [::1]:1919 accept;
        meta l4proto {tcp, udp} ip daddr @dns4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip6 daddr @dns6 tproxy ip6 to [::1]:1919 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip daddr @ips4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip6 daddr @ips6 tproxy ip6 to [::1]:1919 meta mark set 1919 accept;
    }

    chain prerouting_dstnat {
        type nat hook prerouting priority dstnat; policy accept;
        udp dport 53 redirect to :53;
    }

    chain output_filter {
        type route hook output priority filter; policy accept;
        meta skgid 114514 accept;
        meta l4proto {tcp, udp} ip daddr @dns4 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip6 daddr @dns6 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip daddr @ips4 meta mark set 1919 accept;
        meta l4proto {tcp, udp} ip6 daddr @ips6 meta mark set 1919 accept;
    }
}
EOF
)

# 删除旧的规则
while ip rule list | grep -q 'lookup 810'; do
    ip rule del lookup 810
done
# 添加新的规则
ip rule add fwmark 1919 lookup 810
ip -6 rule add fwmark 1919 lookup 810
# 再次确认删除旧的ip路由
ip route flush table 810
ip -6 route flush table 810
# 将 IPv4 CIDR 添加到 nft 规则中,过滤空行和以 # 或 ; 开头的行
nft add element inet xray ips6 { 填写自己需要的ipcidr  }
nft add element inet xray ips4 { 填写自己需要的ipcidr  }
# 添加或替换 IPv4 路由
ip route replace local 0.0.0.0/0 dev lo table 810
ip -6 route replace local ::/0 dev lo table 810

路由表解释 🔗

  • prerouting_dstnat

客户端手动指定了dns服务器为网关或者下发的dhcp,客户端又遵循dhcp的话,都不需要这条规则也行。 那么,这条的意义在哪里?就是给那些dns设置已经写死,或者没开放dns设置的设备用的,我有个很廉价的小摄像头就是没dns设置,但它又傻傻地用厂商预置的一个dns。这种现象在平价的IoT设备中还蛮常见的,因为产品本身成本很低,不会专门还给你搞个dns设置的接口,然后又怕客户网络环境配置错误,所以事先就给你hardcode了一个dns服务器设置。

一个联网摄像头,厂家出厂hardcode了一个2xx.xx.xx.xx的dns,你还没法改,这时候就需要这条兜底规则来吧这个摄像头的dns查询流量劫持到我指定的dns客户端监听的端口了,只有有了这条规则才能确保所有经过这个网关的dns查询都过我指定的dns走,否则总有些不那么规范和不听话的设备特立独行

  • prerouting_filter

如果 ip daddr能匹配到dns4/6这个ipset里存在的ip地址,就把打上标记1919,并且关联到本机透明代理

  • dns4/6

这段代码定义了一个名为 dns 的集合,存储目标 IP 地址(daddr),用于透明代理的目标控制。gc-interval 15d 表示垃圾回收间隔为15天,即在15天不活动的 IP 地址会被移出该集合。

局限性 🔗

使用如上方法设置后可以解决bt下载规避的问题,同时避免某些流量走内核带来的软路由性能瓶颈

但是仍然会出现一些问题,例如无法像openclash那样进行订阅或者通过webui来手动选择node(最大的问题)

由于目前已经实现自己的需求就不在折腾了,等到后续有新的情况下可能会选择mihomo或者singbox内核

环境布局 🔗

设备:友善r5s,配置:1个WAN口1G,2个LAN口2.5G

R5SWAN口进行PPPOE拨号,1个LAN口插交换机(2.5G)用于家庭设备的上网,一个LNA口插其他(NAS等)

可以实现家庭内网2.5G的传输速率,外网1G的速率,等家庭网络带宽普及2.5G的时候可以将其中一个LAN口作为WAN口使用


Categories