梦想博客

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

calendar 2024/11/11
refresh-cw 2024/11/11
3229字,7分钟
tag Openwrt;

前言 🔗

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

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

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

实操 🔗

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

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

 1# 更换国内镜像源
 2sed -e 's,https://downloads.immortalwrt.org,https://mirrors.cernet.edu.cn/immortalwrt,g' \
 3    -e 's,https://mirrors.vsean.net/openwrt,https://mirrors.cernet.edu.cn/immortalwrt,g' \
 4    -i.bak /etc/opkg/distfeeds.conf
 5	
 6# 更新并安装核心插件(如果是虚拟机时请额外安装:kmod-vmxnet3)
 7opkg update && opkg install curl wget wget-ssl kmod-nft-tproxy kmod-nft-socket luci-app-smartdns  xray-core
 8
 9# 安装可选插件
10opkg 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 
11
12# Xray Setting
13uci set xray.enabled.enabled=1
14uci commit
15service xray start
16chown root:115414  /usr/bin/xray
17chmod 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中编辑

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

 1{
 2        "inbounds": [
 3                {
 4                        "tag": "tproxy-in",
 5                        "port": 1919,
 6                        "protocol": "dokodemo-door",
 7                        "settings": {
 8                                "network": "tcp,udp",
 9                                "auth": "noauth",
10                                "allowTransparent": false,
11                                "followRedirect": true
12                        },
13                        "streamSettings": {
14                                "sockopt": {
15                                        "tproxy": "tproxy"
16                                }
17                        },
18                        "sniffing": {
19                                "enabled": true,
20                                "destOverride": [
21                                        "http",
22                                        "tls"
23                                ],
24                                "routeOnly": false
25                        }
26                },
27                {
28                        "tag": "socks-in",
29                        "port": 1080,
30                        "protocol": "socks",
31                        "settings": {
32                                "udp": true
33                        },
34                        "sniffing": {
35                                "enabled": true
36                        }
37                }
38        ],
39        "outbounds": [
40                {
41                        "tag": "direct",
42                        "protocol": "freedom"
43                },
44                {
45                        "tag": "reject",
46                        "protocol": "blackhole"
47                },
48        ],
49        "routing": {
50                "domainMatcher": "mph",
51                "rules": [
52                        {
53                                "outboundTag": "reject",
54                                "protocol": [
55                                        "bittorrent"
56                                ]
57                        },
58                        {
59                                "outboundTag": "proxy",
60                                "inboundTag": [
61                                        "tproxy-in",
62                                        "socks-in"
63                                ]
64                        }
65                ]
66        }
67}

路由表 🔗

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

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

 1#!/usr/bin/env sh
 2
 3# 删除现有的 xray 表
 4nft list table inet xray > /dev/null 2>&1
 5if [ $? -eq 0 ]; then
 6    nft delete table inet xray
 7fi
 8# 创建新的 nft 表 xray
 9nft -f <(cat <<EOF
10table inet xray {
11    set dns4 {
12        type ipv4_addr;
13        flags timeout;
14        gc-interval 15d;
15    }
16	
17    set ips4 {
18        type ipv4_addr;
19        flags interval;
20        auto-merge;
21    }
22	
23    chain prerouting_filter {
24        type filter hook prerouting priority filter; policy accept;
25        meta l4proto tcp socket transparent 1 meta mark set 1919 accept;
26        meta l4proto {tcp, udp} meta mark 1919 tproxy ip to 127.0.0.1:1919 accept;
27        meta l4proto {tcp, udp} ip daddr @dns4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
28        meta l4proto {tcp, udp} ip daddr @ips4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
29    }
30
31    chain prerouting_dstnat {
32        type nat hook prerouting priority dstnat; policy accept;
33        udp dport 53 redirect to :53;
34    }
35
36    chain output_filter {
37        type route hook output priority filter; policy accept;
38        meta skgid 114514 accept;
39        meta l4proto {tcp, udp} ip daddr @dns4 meta mark set 1919 accept;
40        meta l4proto {tcp, udp} ip daddr @ips4 meta mark set 1919 accept;
41    }
42}
43EOF
44)
45
46# 删除旧的规则
47while ip rule list | grep -q 'lookup 810'; do
48    ip rule del lookup 810
49done
50# 添加新的规则
51ip rule add fwmark 1919 lookup 810
52# 再次确认删除旧的ip路由
53ip route flush table 810
54# 将 IPv4 CIDR 添加到 nft 规则中
55nft add element inet xray ips4 { 填写自己需要的ipcidr }
56# 添加或替换 IPv4 路由
57ip route replace local 0.0.0.0/0 dev lo table 810

路由表-Ipv6 🔗

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

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

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

 1#!/usr/bin/env sh
 2
 3# 删除现有的 xray 表
 4nft list table inet xray > /dev/null 2>&1
 5if [ $? -eq 0 ]; then
 6    nft delete table inet xray
 7fi
 8# 创建新的 nft 表 xray
 9nft -f <(cat <<EOF
10table inet xray {
11    set dns4 {
12        type ipv4_addr;
13        flags timeout;
14        gc-interval 15d;
15    }
16
17    set dns6 {
18        type ipv6_addr;
19        flags timeout;
20        gc-interval 15d;
21    }
22
23    set ips4 {
24        type ipv4_addr;
25        flags interval;
26        auto-merge;
27    }
28
29    set ips6 {
30        type ipv6_addr;
31        flags interval;
32        auto-merge;
33    }
34
35    chain prerouting_filter {
36        type filter hook prerouting priority filter; policy accept;
37        meta l4proto tcp socket transparent 1 meta mark set 1919 accept;
38        meta l4proto {tcp, udp} meta mark 1919 tproxy ip to 127.0.0.1:1919 accept;
39        meta l4proto {tcp, udp} meta mark 1919 tproxy ip6 to [::1]:1919 accept;
40        meta l4proto {tcp, udp} ip daddr @dns4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
41        meta l4proto {tcp, udp} ip6 daddr @dns6 tproxy ip6 to [::1]:1919 meta mark set 1919 accept;
42        meta l4proto {tcp, udp} ip daddr @ips4 tproxy ip to 127.0.0.1:1919 meta mark set 1919 accept;
43        meta l4proto {tcp, udp} ip6 daddr @ips6 tproxy ip6 to [::1]:1919 meta mark set 1919 accept;
44    }
45
46    chain prerouting_dstnat {
47        type nat hook prerouting priority dstnat; policy accept;
48        udp dport 53 redirect to :53;
49    }
50
51    chain output_filter {
52        type route hook output priority filter; policy accept;
53        meta skgid 114514 accept;
54        meta l4proto {tcp, udp} ip daddr @dns4 meta mark set 1919 accept;
55        meta l4proto {tcp, udp} ip6 daddr @dns6 meta mark set 1919 accept;
56        meta l4proto {tcp, udp} ip daddr @ips4 meta mark set 1919 accept;
57        meta l4proto {tcp, udp} ip6 daddr @ips6 meta mark set 1919 accept;
58    }
59}
60EOF
61)
62
63# 删除旧的规则
64while ip rule list | grep -q 'lookup 810'; do
65    ip rule del lookup 810
66done
67# 添加新的规则
68ip rule add fwmark 1919 lookup 810
69ip -6 rule add fwmark 1919 lookup 810
70# 再次确认删除旧的ip路由
71ip route flush table 810
72ip -6 route flush table 810
73# 将 IPv4 CIDR 添加到 nft 规则中,过滤空行和以 # 或 ; 开头的行
74nft add element inet xray ips6 { 填写自己需要的ipcidr  }
75nft add element inet xray ips4 { 填写自己需要的ipcidr  }
76# 添加或替换 IPv4 路由
77ip route replace local 0.0.0.0/0 dev lo table 810
78ip -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口使用


分类