将 ICMP 数据包的源地址从一个接口更改为另一个接口

将 ICMP 数据包的源地址从一个接口更改为另一个接口

在我的盒子里,我有两个以太网接口。由于我正在处理的系统的限制,我需要所有 ICMP 响应消息都具有接口 1 的源地址。

一个例子:

TTL 为 0 的数据包从接口 0 进入,它会被丢弃,并生成源地址为 0 的 ICMP 响应,并从 0 路由出去。另一个 TTL 为 0 的数据包从接口 1 进入,它会被丢弃并生成 ICMP 响应。我需要源地址不是接口 1,而是接口 0,并且仍然从接口 1 路由出去。这可能吗?

我不希望它影响来自任一接口的其他协议的源地址。

编辑:

这是特定盒子的当前设置:

ip-br 链接

lo           UNKNOWN   00:00:00:00:00:00 <LOOPBACK, UP, LOWER_UP>
sit0@NONE    DOWN      0.0.0.0 <NOARP>
np0          UNKNOWN   <POINTTOPOINT,MULTICAST,NOARP,UP,LOWER_UP>
np1          UNKNOWN   <POINTTOPOINT,MULTICAST,NOARP,UP,LOWER_UP>
eth0         UP        <POINTTOPOINT,MULTICAST,NOARP,UP,LOWER_UP>

ip -4 -br 地址

lo           UNKNOWN   127.0.0.1/8
np0          UNKNOWN   192.0.0.1/24
np1          UNKNOWN   192.10.1.1/24
eth0         UP        193.10.1.1/24

ip路由

default dev np1 scope linke
192.0.0.0/24 dev np0 proto kernel scope link src 192.0.0.1
192.10.1.0/24 dev np1 proto kernel scope link src 192.10.1.1
193.10.1.0/24 dev eth0 proto kernel scope link src 193.10.1.1

系统未设置为运行 VPN。系统有 1 个物理以太网 (eth0) 和 2 个虚拟以太网接口(np0 和 np1)。

使用上面的命名,让接口 0 为 eth0,接口 1 为 np1

答案1

基于L4协议的路由规则

Linux内核 >= 4.17可以基于IP的第4层协议进行策略路由:

  • 扩展 fib 规则匹配支持,包括 sport、dport 和ip 原型匹配(完成五元组匹配支持)。数据中心中基于策略的路由的常见用例需要 5 元组匹配

[...]

最初,系统生成的任何 ICMP 响应(名为盒子)返回到原始源节点将使用面向该源的接口上的地址,就像从主机到该节点的任何发出的流量一样。

# ip route get to 192.10.1.201
192.10.1.201 dev np1 src 192.10.1.1 uid 0 
    cache 
# ip route get to 193.10.1.200
193.10.1.200 dev eth0 src 193.10.1.1 uid 0 
    cache 

人们可以添加一个路由表,该表将显示相关路由的重复但已修改的条目,并具有与通常使用的不同的暗示源地址,然后使用路由规则选择该路由表,但仅当协议是 ICMP 时。请注意,应在配置地址和(正常)路由后填充此路由表,因此添加时不会因为不可能的路由而被拒绝。与往常一样,如果该路由因任何原因消失(例如:接口先关闭然后再启动),与proto kernel主路由表中的路由相反,它不会被添加回来:每当发生此类事件时,都必须再次添加它。

这里,对使用接口发出的流量的重复路由进行更改的行为np1是暗示不同的源地址(193.10.1.1 而不是 192.10.1.1):

ip route add 192.10.1.0/24 dev np1 src 193.10.1.1 table 1000

这应该应用于覆盖主要的仅适用于 ICMP 的表:

ip rule add pref 100 ipproto icmp lookup 1000

非 ICMP 流量没有任何变化np1

# ip route get to 192.10.1.201
192.10.1.101 dev np1 src 192.10.1.1 uid 0 
    cache 

但对于 ICMP 流量来说np1

# ip route get to 192.10.1.201 ipproto icmp
192.10.1.201 dev np1 table 1000 src 193.10.1.1 uid 0 
    cache 

因此,OP问题中的两种情况都得到了满足:ICMP,例如ICMP超时,无论是在 上还是在 上eth0回复,都会从hinted 的地址回复。eth0np1

这也会影响本地启动的 ICMP,例如 ping(请参阅下文)。


注意事项和详细信息

  • 警告:(第 2 层)以太网接口和 ARP

    如果np1是以太网接口,由此类 ICMP 错误直接触发的 ARP 请求也会遵循路由规则,并且会在上面显示为从 193.10.1.1 而不是从 192.10.1.1 请求 192.10.1.201,但前提是该节点没有 ARP 条目现有的盒子如果节点没有自行解析 ARP 请求盒子就在之前(即只有当节点确实有时才会发生盒子在 ARP 缓存中但是盒子ARP 缓存中没有该节点,通常情况并非如此)。其他 Linux 系统默认情况下不会关心(它们遵循弱主机型号并默认接受任何此类 ARP 请求)。其他一些操作系统可能会关心并且不会回复这个“错误”的 ARP 请求。

    如果这真的很重要,那么这种已经罕见的情况可以完全预防arp_announce=1:

    - 1 - Try to avoid local addresses that are not in the target's
      subnet for this interface. [...]
    

    所以对于np1接口来说:

    sysctl -w net.ipv4.conf.np1.arp_announce=1
    
  • ping命令还将使用更改后的源地址

    ping不是 ICMP 响应,因此应该照常工作。

    人们仍然可以限制受未真正记录的选项影响的 ICMP 类型(和代码):根据源中的处理方式,将 ICMP 类型+代码映射为路由规则选择器中的目标端口:

    include/net/flow.h:

    union flowi_uli {
        struct {
            __be16  dport;
            __be16  sport;
        } ports;
    
        struct {
            __u8    type;
            __u8    code;
        } icmpt;
    
    
    union flowi_uli     uli;
    #define fl4_sport       uli.ports.sport
    #define fl4_dport       uli.ports.dport
    #define fl4_icmp_type       uli.icmpt.type
    #define fl4_icmp_code       uli.icmpt.code
    

    net/ipv4/fib_rules.c:

        if (fib_rule_port_range_set(&rule->dport_range) &&
            !fib_rule_port_inrange(&rule->dport_range, fl4->fl4_dport))
            return 0;
    

    由于联合,fl4->fl4_dport<=> fl4_icmp_type * 256 + fl4_icmp_code:ip rule ... ipproto icmp dport ...可用于没有专用语法的类型+代码。

    因此,将其限制为除类型 8(ICMP 请求)之外的任何内容都意味着将其限制为 0-7 + 9-255。一旦乘以 256 并调整足够的结束间隔 (+255),这将给出 1-2047(因为 0 无效,从 1 开始。类型 0 代码 0 的 ICMP 回复将不匹配,但不必匹配,因为实际回复它永远不会使用暗示的源,而是使用远程 ICMP 请求中使用的实际本地目标并将用作回复源)+ 2304-65534(65535 同上)。从原来的路由规则开始:

    ip rule add pref 100 ipproto icmp lookup 1000
    
    • 要么用两条规则替换它:

      ip rule del pref 100
      
      ip rule add pref 100 ipproto icmp dport 1-2047 lookup 1000
      ip rule add pref 101 ipproto icmp dport 2304-65534 lookup 1000
      
    • 或者保留初始规则并在其周围添加规则以跳过它(可以简化为仅 2048,因为 ICMP Echo 请求的代码始终为 0):

      ip rule del pref 100
      ip rule del pref 101
      
      ip rule add pref 100 ipproto icmp lookup 1000
      ip rule add pref 101 lookup 424242
      ip rule add pref 99 ipproto icmp dport 2048-2303 goto 101
      

    无论选择哪一种,ping命令都不会再改变:

    # ip route get to 192.10.1.201 ipproto icmp dport 2048
    192.10.1.201 dev np1 src 192.10.1.1 uid 0 
        cache 
    

    ICMP 重定向(类型 5)可能也应该以类似的方式添加到例外中。

相关内容