Linux kernel 的網路參數 rp_filter

這篇來記錄有關 Linux kernel 的 network 參數。

rp_filter

為逆向路徑過濾 (Reverse Path Filtering),其作用為為過濾反向不通的封包,將其丟棄。 而原理為由 NIC1 (network interface card) 進來的封包,reverse path filtering 模塊會將其封包的源地址(source ip)與目標地址(destination ip)做對調,然後再路由表中查找,如果出去的介面是 NIC1 則通過;反之不是 NIC1 則丟棄該封包。

可以從 ip-sysctl 文件中找到參數的用法。其中該參數為整數型,其值包括了 0:不做來源驗證;1:嚴謹認證模式;2:寬鬆認證模式。

rp_filter - INTEGER
    0 - No source validation.
    1 - Strict mode as defined in RFC3704 Strict Reverse Path
        Each incoming packet is tested against the FIB and if the interface
        is not the best reverse path the packet check will fail.
        By default failed packets are discarded.
    2 - Loose mode as defined in RFC3704 Loose Reverse Path
        Each incoming packet's source address is also tested against the FIB
        and if the source address is not reachable via any interface
        the packet check will fail.

Current recommended practice in RFC3704 is to enable strict mode
    to prevent IP spoofing from DDos attacks. If using asymmetric routing
    or other complicated routing, then loose mode is recommended.

The max value from conf/{all,interface}/rp_filter is used
    when doing source validation on the {interface}.

Default value is 0. Note that some distributions enable it
    in startup scripts.

Source code

可以上網找到原始碼 fib_frontend.c,這邊來看一下實作方法。

 1/* Ignore rp_filter for packets protected by IPsec. */
 2int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
 3            u8 tos, int oif, struct net_device *dev,
 4            struct in_device *idev, u32 *itag)
 5{
 6    /* 是否啟用反向封包過濾功能,這邊注意如果封包受 IPSec 保護則忽略反向封包過濾功能 */
 7    int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev);
 8    struct net *net = dev_net(dev);
 9
10    if (!r && !fib_num_tclassid_users(net) &&
11        (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) {
12        /* 本地源的封包不執行反向封包過濾功能 */
13        if (IN_DEV_ACCEPT_LOCAL(idev))
14            goto ok;
15        /* with custom local routes in place, checking local addresses
16        * only will be too optimistic, with custom rules, checking
17        * local addresses only can be too strict, e.g. due to vrf
18        */
19        if (net->ipv4.fib_has_custom_local_routes ||
20            fib4_has_custom_rules(net))
21            goto full_check;
22        if (inet_lookup_ifaddr_rcu(net, src))
23            return -EINVAL;
24
25ok:
26        *itag = 0;
27        return 0;
28    }
29
30full_check:
31    return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag);
32}
 1static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
 2                u8 tos, int oif, struct net_device *dev,
 3                int rpf, struct in_device *idev, u32 *itag)
 4{
 5    struct net *net = dev_net(dev);
 6    struct flow_keys flkeys;
 7    int ret, no_addr;
 8    struct fib_result res;
 9    struct flowi4 fl4;
10    bool dev_match;
11
12    fl4.flowi4_oif = 0;
13    fl4.flowi4_iif = l3mdev_master_ifindex_rcu(dev);
14    if (!fl4.flowi4_iif)
15        fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX;
16    /* 可以看到這邊做反轉了 */
17    fl4.daddr = src;
18    fl4.saddr = dst;
19    fl4.flowi4_tos = tos;
20    fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
21    fl4.flowi4_tun_key.tun_id = 0;
22    fl4.flowi4_flags = 0;
23    fl4.flowi4_uid = sock_net_uid(net, NULL);
24    fl4.flowi4_multipath_hash = 0;
25
26    no_addr = idev->ifa_list == NULL;
27
28    fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0;
29    if (!fib4_rules_early_flow_dissect(net, skb, &fl4, &flkeys)) {
30        fl4.flowi4_proto = 0;
31        fl4.fl4_sport = 0;
32        fl4.fl4_dport = 0;
33    }
34
35    if (fib_lookup(net, &fl4, &res, 0))
36        goto last_resort;
37    if (res.type != RTN_UNICAST &&
38        (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
39        goto e_inval;
40    fib_combine_itag(itag, &res);
41
42    dev_match = fib_info_nh_uses_dev(res.fi, dev);
43    /* This is not common, loopback packets retain skb_dst so normally they
44    * would not even hit this slow path.
45    */
46    dev_match = dev_match || (res.type == RTN_LOCAL &&
47                dev == net->loopback_dev);
48    if (dev_match) {
49        ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST;
50        return ret;
51    }
52    if (no_addr)
53        goto last_resort;
54    if (rpf == 1)
55        goto e_rpf;
56    fl4.flowi4_oif = dev->ifindex;
57
58    ret = 0;
59    if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) {
60        if (res.type == RTN_UNICAST)
61            ret = FIB_RES_NHC(res)->nhc_scope >= RT_SCOPE_HOST;
62    }
63    return ret;
64
65last_resort:
66    if (rpf)
67        goto e_rpf;
68    *itag = 0;
69    return 0;
70
71e_inval:
72    return -EINVAL;
73e_rpf:
74    return -EXDEV;
75}

參數:

  • struct sk_buff - socket buffer
  • __be32 src - source
  • __be32 dst - destination
  • struct net_device - 網路結構體
  • struct in_device - 網路結構體
  • saddr - start address
  • daddr - destination address

科普:

  • __bet32 - le 與 be 分別表示 little endian 和 big endian
  • u8、u32 - 表示無符號 char 字符類型

作用

當前RFC3704文檔建議使用嚴謹認證模式。如果網路是非對稱網路或複雜的網路 rp_filter 建議配置為 2,做寬鬆認證。 而 rp_filter 配置作用通常為下列:

  1. 減少 DDoS 攻擊,注意是減少不是防止
  2. 防止 IP Spoofing
comments powered by Disqus