歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Netfilter的rpfilter技術-Linux的uRPF

Netfilter的rpfilter技術-Linux的uRPF

日期:2017/2/28 15:48:35   编辑:Linux教程

作為IP路由的一種補充,uRPF(單播反向路徑轉發)可謂非常有用,它認證了IP數據報的源地址,在一定程度了保護了網絡的安全,比如有效防止了洪泛攻擊。然而直到Linux內核的2.6的高版本版本,Linux只能實現嚴格的uRPF,這是由fib_validate_source函數來完成的,具體配置在/proc/sys/net/ipv4/conf/$dev/rp_filter,對於Cisco上很簡單的松散的uRPF,Linux卻無能為力,kernel 2.6的高版本可以為/proc/sys/net/ipv4/conf/$dev/rp_filter設置3個值,分別為不檢查,嚴格uRPF,松散uRPF。kernel 3.3增加了rpfilter機制,將uRPF從協議棧移到了Netfilter,使得Linux可以在協議棧之外實現嚴格/松散uRPF,然而即便如此,對於VRF(虛擬路由轉發),Linux還是沒有實現,不過在rpfilter的基礎上,這個VRF的實現應該很簡單。

反向路由查找並非那麼簡單,因為需要考慮策略路由的問題,這一切從何道來呢?rpfilter技術作用在Netfilter的PREROUTING這個HOOK點上,這是合理的,因為必須在標准路由之前進行反向路徑查詢,以保證沒有任何數據從被拒絕的反向路徑發出,這裡主要是為了禁止ICMP包的回發,然而在PREROUTING這個點上,目標網卡是不知道的,因此也就不能像標准路由中的fib_validate_source那樣設置flowi4的iif,既然rpfilter的目的只是裁決一下反向路徑的出口,那麼其入口就是無關緊要的了,並且我們知道,本機loopback口的通信是可信的,那麼就將反向路徑查詢中的flowi4的iif設置成loopback即可,然而這還有問題,那就是策略路由的問題了,如果我們不查找策略路由表,就會漏掉在策略路由表中的條目而導致正向包被丟棄,而如果想查找策略路由表,由於我們將iif設置成了loopback,就可能會因為rule的iif不匹配而錯過:

  1. static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
  2. struct flowi *fl, int flags)
  3. {
  4. int ret = 0;
  5. if (rule->iifindex && (rule->iifindex != fl->flowi_iif))
  6. goto out;
  7. if (rule->oifindex && (rule->oifindex != fl->flowi_oif))
  8. goto out;
  9. if ((rule->mark ^ fl->flowi_mark) & rule->mark_mask)
  10. goto out;
  11. ret = ops->match(rule, fl, flags);
  12. out:
  13. return (rule->flags & FIB_RULE_INVERT) ? !ret : ret;
  14. }

策略路由中的FIB_RULE_INVERT標志和rpfilter中的XT_RPFILTER_INVERT標志是相互獨立的兩個取反標志,然而代表的含義基本一致,這可以讓我們配置出各種組合,www.linuxidc.com 也就是說,你可能需要單獨的配置一些針對正方向策略路由的反方向策略路由,是不是有點VRF的意思啊!

rpfilter的核心代碼如下:

1.match函數:

  1. static bool rpfilter_mt(const struct sk_buff *skb, struct xt_action_param *par)
  2. {
  3. const struct xt_rpfilter_info *info;
  4. const struct iphdr *iph;
  5. struct flowi4 flow;
  6. bool invert;
  7. info = par->matchinfo;
  8. invert = info->flags & XT_RPFILTER_INVERT;
  9. if (par->in->flags & IFF_LOOPBACK)
  10. return true ^ invert;
  11. iph = ip_hdr(skb);
  12. if (ipv4_is_multicast(iph->daddr)) {
  13. if (ipv4_is_zeronet(iph->saddr))
  14. return ipv4_is_local_multicast(iph->daddr) ^ invert;
  15. flow.flowi4_iif = 0;
  16. } else {
  17. flow.flowi4_iif = dev_net(par->in)->loopback_dev->ifindex;
  18. }
  19. flow.daddr = iph->saddr;
  20. flow.saddr = rpfilter_get_saddr(iph->daddr);
  21. flow.flowi4_oif = 0;
  22. flow.flowi4_mark = info->flags & XT_RPFILTER_VALID_MARK ? skb->mark : 0;
  23. flow.flowi4_tos = RT_TOS(iph->tos);
  24. flow.flowi4_scope = RT_SCOPE_UNIVERSE;
  25. return rpfilter_lookup_reverse(&flow, par->in, info->flags) ^ invert;
  26. }

2.路由查找以及結果判斷邏輯:

  1. static bool rpfilter_lookup_reverse(struct flowi4 *fl4,
  2. const struct net_device *dev, u8 flags)
  3. {
  4. struct fib_result res;
  5. bool dev_match;
  6. struct net *net = dev_net(dev);
  7. int ret __maybe_unused;
  8. if (fib_lookup(net, fl4, &res))
  9. return false;
  10. if (res.type != RTN_UNICAST) {
  11. if (res.type != RTN_LOCAL || !(flags & XT_RPFILTER_ACCEPT_LOCAL))
  12. return false;
  13. }
  14. dev_match = false;
  15. #ifdef CONFIG_IP_ROUTE_MULTIPATH
  16. for (ret = 0; ret < res.fi->fib_nhs; ret++) {
  17. struct fib_nh *nh = &res.fi->fib_nh[ret];
  18. if (nh->nh_dev == dev) {
  19. dev_match = true;
  20. break;
  21. }
  22. }
  23. #else
  24. if (FIB_RES_DEV(res) == dev)
  25. dev_match = true;
  26. #endif
  27. if (dev_match || flags & XT_RPFILTER_LOOSE)
  28. return FIB_RES_NH(res).nh_scope <= RT_SCOPE_HOST;
  29. return dev_match;
  30. }
雖然基於Netfilter的rpfilter比內置的源地址判斷更合理,但是由於Linux協議棧以及Netfilter本身的機制,還是有一些副作用的。
Copyright © Linux教程網 All Rights Reserved