AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / computer / 问题 / 1810745
Accepted
user1847927
user1847927
Asked: 2023-10-01 05:45:16 +0800 CST2023-10-01 05:45:16 +0800 CST 2023-10-01 05:45:16 +0800 CST

路由表行为与绑定套接字相结合 - 意外行为

  • 772

问题

我遇到了一些相当奇怪的行为,其中看似无关的默认网关路由产生了意想不到的副作用。我设法用一个最小的例子来复制这个问题。这里的目的主要是教育性的,我在尝试更复杂的场景时偶然发现了这一点。简而言之,当我认为不应该连接到192.168.0.3上的 Web 服务器时,我却设法连接到了。

我的笔记本电脑使用 WiFi( 192.168.0.0/24网络)连接到我的家庭网络。路由表如下:

kevin@kevin-UX305LA:~$ ip route
default via 192.168.0.1 dev wlp2s0 proto dhcp metric 600 
169.254.0.0/16 dev wlp2s0 scope link metric 1000 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 
192.168.0.0/24 dev wlp2s0 proto kernel scope link src 192.168.0.210 metric 600

和目前curl 192.168.0.3都curl --interface wlp2s0 192.168.0.3在工作并给出以下响应:

<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.25.2</center>
</body>
</html>

现在,我继续删除与192.168.0.0/24网络相关的所有路由,以便其余路由为:

kevin@kevin-UX305LA:~$ sudo ip route del default via 192.168.0.1 
kevin@kevin-UX305LA:~$ sudo ip route del 192.168.0.0/24 
kevin@kevin-UX305LA:~$ ip route
169.254.0.0/16 dev wlp2s0 scope link metric 1000 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown

此外,ip route get有和没有绑定到接口的情况如下所示:

kevin@kevin-UX305LA:~$ ip route get 192.168.0.3
RTNETLINK answers: Network is unreachable
kevin@kevin-UX305LA:~$ ip route get oif wlp2s0 192.168.0.3
192.168.0.3 dev wlp2s0 src 192.168.0.210 uid 1000 
    cache 

运行curl 192.168.0.3会给出结果curl: (7) Couldn't connect to server,运行curl --interface wlp2s0 192.168.0.3不会给出任何结果(curl 被阻止)。以下是 strace 显示的片段:

setsockopt(5, SOL_SOCKET, SO_BINDTODEVICE, "wlp2s0\0", 7) = 0
connect(5, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("192.168.0.3")}, 16) = -1 EINPROGRESS (Operation now in progress)

这很好,符合我的预期,即笔记本电脑应该无法到达192.168.0.3。

现在这是奇怪的部分。如果我添加一个虚拟默认网关(例如通过随机地址的docker0接口),则我的路由表如下:

kevin@kevin-UX305LA:~$ sudo ip route add default via 172.17.0.2
kevin@kevin-UX305LA:~$ ip route
default via 172.17.0.2 dev docker0 linkdown 
169.254.0.0/16 dev wlp2s0 scope link metric 1000 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown 

运行curl 192.168.0.3失败,但运行curl --interface wlp2s0 192.168.0.3成功并显示先前的 HTML 回复。

kevin@kevin-UX305LA:~$ curl 192.168.0.3
curl: (7) Failed to connect to 192.168.0.3 port 80 after 3068 ms: No route to host

kevin@kevin-UX305LA:~$ ip route get 192.168.0.3
192.168.0.3 via 172.17.0.2 dev docker0 src 172.17.0.1 uid 1000 
    cache 

kevin@kevin-UX305LA:~$ curl --interface wlp2s0 192.168.0.3
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.25.2</center>
</body>
</html>

kevin@kevin-UX305LA:~$ ip route get oif wlp2s0 192.168.0.3
192.168.0.3 dev wlp2s0 src 192.168.0.210 uid 1000 
    cache 

我研究了一下SO_BINDTODEVICE,发现它仍然应该遵守路由表。为什么通过随机地址通过不同接口添加默认网关会curl --interface wlp2s0 192.168.0.3成功?

详细地址

以下命令列出与接口关联的地址。我检查过,执行上面的每个命令后,该命令的结果总是相同的。

kevin@kevin-UX305LA:~$ ip -br addr
lo               UNKNOWN        127.0.0.1/8 ::1/128 
wlp2s0           UP             192.168.0.210/24 fe80::7a03:3420:b8b0:4db7/64 
docker0          DOWN           172.17.0.1/16 

总之,192.168.0.210是这台笔记本电脑,192.168.0.3是网络上托管 Web 服务器的另一台计算机,192.168.0.1是默认网关(我的路由器)。

环境

我正在运行 Linux Mint。这是一些信息。

kevin@kevin-UX305LA:~$ uname -a
Linux kevin-UX305LA 5.15.0-84-generic #93-Ubuntu SMP Tue Sep 5 17:16:10 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

kevin@kevin-UX305LA:~$ lsb_release -a
No LSB modules are available.
Distributor ID: Linuxmint
Description:    Linux Mint 21.2
Release:    21.2
Codename:   victoria
ip
  • 1 1 个回答
  • 58 Views

1 个回答

  • Voted
  1. Best Answer
    A.B
    2023-10-01T19:37:38+08:002023-10-01T19:37:38+08:00

    绑定到接口时,除非在此接口上找到足够的路由并重新使用,否则在解析路由结果时,将在该接口上添加没有网关的隐式默认路由。因此,只有当不存在这样的默认路由时,这才重要。

    注意:缺少网关可能会导致第 2 层接口出现问题(例如:尝试到达 8.8.8.8 将发送 8.8.8.8 的 ARP 请求。它不会导致第 3 层接口出现问题,例如 IP 隧道根本需要网关。由于在这个问题中,目的地无论如何都在 LAN 中,因此不需要网关,因此这也不会导致问题。

    curl可以通过查询内核的路由结果来验证命令的效果,如下所示ip route get:

    $ ip route get 192.168.0.3
    RTNETLINK answers: Network is unreachable
    # ip route get oif wlp2s0 192.168.0.3
    192.168.0.3 dev wlp2s0 src 192.168.0.210 uid 0 
        cache 
    $ ip route get oif wlp2s0 8.8.8.8
    8.8.8.8 dev wlp2s0 src 192.168.0.210 uid 1000 
        cache 
    

    可以很好地解析到 192.168.0.3。

    为了防止这种情况,可以添加一条策略规则,仅在使用选择器绑定到接口时才会选择备用路由oif。通常,这将用于提供具有足够网关的路由,以解决上述缺少网关的问题,例如如下所示:

    ip route add default via 192.168.0.1 dev wlp2s0 onlink table 1000
    ip rule add oif wlp2s0 lookup table 1000
    

    但对于这种精确的情况,目标是防止这种路由存在,这更困难,因为oif wlp2s0只保留dev wlp2s0其中包含的路由:简单地添加黑洞或无法访问的路由也不能包含dev wlp2s0,因此它总是会被忽略。相反,这需要一条虚假的路线,这将使最终结果失败。选择自己的地址作为网关与选择不网关相同:也不起作用。因此,这需要在 LAN 中选择一个保证不存在的任意地址(以防止它执行 ICMP 重定向,如果它也是路由器)。假设 192.168.0.4 不存在,并将保留用于此用途:

    # ip route add default via 192.168.0.4 onlink dev wlp2s0 table 1000
    # ip rule add oif wlp2s0 lookup 1000
    

    现在得到:

    $ ip route get oif wlp2s0 192.168.0.3
    192.168.0.3 via 192.168.0.4 dev wlp2s0 table 1000 src 192.168.0.210 uid 1000 
        cache  
    

    这将触发 192.168.0.4 的 ARP 请求,并在大约 3 秒后失败(此类尝试的标准 ARP 最大延迟),并显示EHOSTUNREACH(没有到主机的路由):

    curl: (7) Failed to connect to 192.168.0.3 port 80 after 3071 ms: Couldn't connect to server
    

    请注意,任意 IP 地址甚至不必位于 192.168.0.0/24 范围内。只要 ARP 请求最终失败,这就会起到同样的作用:

    ip route add default via 192.0.2.2 onlink dev wlp2s0 table 1000
    

    注意:同样的技巧不适用于第 3 层接口(例如:tun 模式下的 WireGuard 或 OpenVPN),因为它没有网关的概念。


    更新

    解决OP的问题

    在OP遇到的问题之前我没有解决:连接失败而不是成功,并且在其他地方添加默认路由使尝试成功。

    原因是,目前只检查了一半的通信:从主机到服务器,可以成功发出数据包,而不是将流量从服务器返回到尚未考虑的主机。

    还发现(通过聊天)OP 正在使用这些rp_filter设置:

    $ sysctl net.ipv4.conf.all.rp_filter
    net.ipv4.conf.all.rp_filter = 2
    $ sysctl net.ipv4.conf.wlp2s0.rp_filter
    net.ipv4.conf.wlp2s0.rp_filter = 2
    

    它将接口设置为RFC 3704wlp2s0中定义的松散反向路径转发模式。

    2.4. 松散反向路径转发

    松散反向路径转发(松散 RPF)在算法上 与严格 RPF类似,但不同之处在于它仅检查路由是否存在(即使是默认路由,如果适用), 而不检查路由指向的位置。

    反向路径转发(RPF)正在检查反向路径:从服务器到主机的返回流量与“正常”路径(即从主机到服务器的路由)兼容。

    因此,当存在默认路由(这是最常见的情况)时,松散 RPF 实际上总是会成功。如果不存在默认路由,也不存在任何到目标的路由,它将失败。对于传出流量,强制接口始终会成功,但这不会更改从服务器接收到主机的反向流量。此类入口流量的接收独立于出口流量被强制到接口的事实,并且不能解释这种情况。ip route get当任何地方都没有默认路由时,可以再次检查这一点。从OP的情况开始,没有添加规则:

    # ip route get from 192.168.0.3 iif wlp2s0 to 192.168.0.210
    RTNETLINK answers: Invalid cross-device link
    

    Invalid cross-device link是网络堆栈选择告诉 RPF 失败的错误:此类路径无效,这意味着从服务器返回的流量应该是什么。绑定到接口时选择隐式默认值不会影响入口流量,因此不会更改此结果:超时(但不是由于 ARP,因此不限于 3 秒)。

    同样的检查rp_filter=0:

    # sysctl -w net.ipv4.conf.all.rp_filter=0
    net.ipv4.conf.all.rp_filter = 0
    # sysctl -w net.ipv4.conf.all.rp_filter=0
    net.ipv4.conf.all.rp_filter = 0
    # ip route get from 192.168.0.3 iif wlp2s0 to 192.168.0.210
    local 192.168.0.210 from 192.168.0.3 dev lo table local 
        cache <local> iif wlp2s0 
    

    工作正常,因为检查不再完成。

    否则,返回到rp_filter=2,执行以下操作:

    # ip route add 192.168.0.3/32 via 172.17.0.2
    # ip route get from 192.168.0.3 iif wlp2s0 to 192.168.0.210
    local 192.168.0.210 from 192.168.0.3 dev lo table local 
        cache <local> iif wlp2s0 
    

    也让它发挥作用。

    只需为 192.168.0.3 添加一条错误的路由即可使其通过 Loose RPF 算法。当然,添加默认路由也有同样的效果:只要有到 192.168.0.3 的路由,Loose RPF 就会通过。

    因此,要么rp_filter像上面那样完全禁用检查,要么拥有 192.168.0.3 的任何路由,无论它在哪里,包括使用其他接口的默认路由。

    • 2

相关问题

  • 如何为不同的设备分配一个静态IP地址

  • 动态 IP 地址:是否有针对黑名单的修复?

  • 在 Linux 上的 PostgreSQL 中,编辑 pg_hba.conf 和 postgresql.conf 并重新启动 Postgres 会使我的集群在重新启动时崩溃(使其“关闭”)

  • 为什么 Ultrasurf 不隐藏我的 IP 地址?

  • 是否可以在客户端中使用没有文件系统的 FTP?

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    如何减少“vmmem”进程的消耗?

    • 11 个回答
  • Marko Smith

    从 Microsoft Stream 下载视频

    • 4 个回答
  • Marko Smith

    Google Chrome DevTools 无法解析 SourceMap:chrome-extension

    • 6 个回答
  • Marko Smith

    Windows 照片查看器因为内存不足而无法运行?

    • 5 个回答
  • Marko Smith

    支持结束后如何激活 WindowsXP?

    • 6 个回答
  • Marko Smith

    远程桌面间歇性冻结

    • 7 个回答
  • Marko Smith

    子网掩码 /32 是什么意思?

    • 6 个回答
  • Marko Smith

    鼠标指针在 Windows 中按下的箭头键上移动?

    • 1 个回答
  • Marko Smith

    VirtualBox 无法以 VERR_NEM_VM_CREATE_FAILED 启动

    • 8 个回答
  • Marko Smith

    应用程序不会出现在 MacBook 的摄像头和麦克风隐私设置中

    • 5 个回答
  • Martin Hope
    Vickel Firefox 不再允许粘贴到 WhatsApp 网页中? 2023-08-18 05:04:35 +0800 CST
  • Martin Hope
    Saaru Lindestøkke 为什么使用 Python 的 tar 库时 tar.xz 文件比 macOS tar 小 15 倍? 2021-03-14 09:37:48 +0800 CST
  • Martin Hope
    CiaranWelsh 如何减少“vmmem”进程的消耗? 2020-06-10 02:06:58 +0800 CST
  • Martin Hope
    Jim Windows 10 搜索未加载,显示空白窗口 2020-02-06 03:28:26 +0800 CST
  • Martin Hope
    andre_ss6 远程桌面间歇性冻结 2019-09-11 12:56:40 +0800 CST
  • Martin Hope
    Riley Carney 为什么在 URL 后面加一个点会删除登录信息? 2019-08-06 10:59:24 +0800 CST
  • Martin Hope
    zdimension 鼠标指针在 Windows 中按下的箭头键上移动? 2019-08-04 06:39:57 +0800 CST
  • Martin Hope
    jonsca 我所有的 Firefox 附加组件突然被禁用了,我该如何重新启用它们? 2019-05-04 17:58:52 +0800 CST
  • Martin Hope
    MCK 是否可以使用文本创建二维码? 2019-04-02 06:32:14 +0800 CST
  • Martin Hope
    SoniEx2 更改 git init 默认分支名称 2019-04-01 06:16:56 +0800 CST

热门标签

windows-10 linux windows microsoft-excel networking ubuntu worksheet-function bash command-line hard-drive

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve