• 反向路由检测 问题记录


       查看代码可知,查找路由后校验src dst ip 不过!

    同时通过ip route查看ip 命中的路由

     

     可知 会发往lo接口

    查看路由也能看到结果:

    ip rule add fwmark 1 lookup 100 
    ip route add local default dev lo table 100 
    //内核中涉及到如下hokk

    ip_fib_init in fib_frontend.c (net\ipv4) :
    rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL);
    rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL);
    rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL);
    fib_rules_init in fib_rules.c (net\core) :
    rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL);
    rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL);
    rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL);

     mark 为0x的命中table 100的路由 也就是local dev lo

      既然命中了路由为啥还要去执行fib_validate_source  做校验呢? 

    因为收到包后就”必须回包“,所以要反向查找路由,免得后续回报找不到出口或者怎样

    /* Given (packet source, input interface) and optional (dst, oif, tos):
     * - (main) check, that source is valid i.e. not broadcast or our local
     *   address.
     * - figure out what "logical" interface this packet arrived
     *   and calculate "specific destination" address.
     * - check, that packet arrived from expected physical interface.
     * called with rcu_read_lock()
     */
    static int __fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
                     u8 tos, int oif, struct net_device *dev,
                     int rpf, struct in_device *idev, u32 *itag)
    {
        int ret, no_addr;
        struct fib_result res;
        struct flowi4 fl4;
        struct net *net;
        bool dev_match;
    //组装查找key
    //注意源地址和目的地址互换,然后查路由之所以何种方式查路由,是有一定意图的。
    //首先valid_source顾名思义,就是看源地址是否有效,怎样说明才是有效,看我有没有到你的路由
    //如果我有到你的路由,好吧,暂且认为你有效。如果没有到你的路由,而你却偏偏给我发了一个报文
    //那么我认为你是伪造原地址了。(该逻辑的前提是“对称路由”,即连在相同网络中的设备,都有相同的路由)
    
        fl4.flowi4_oif = 0;
        fl4.flowi4_iif = l3mdev_master_ifindex_rcu(dev);
        if (!fl4.flowi4_iif)
            fl4.flowi4_iif = oif ? : LOOPBACK_IFINDEX;
        fl4.daddr = src;
        fl4.saddr = dst;
        fl4.flowi4_tos = tos;
        fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
        fl4.flowi4_tun_key.tun_id = 0;
        fl4.flowi4_flags = 0;
    //入接口没有ip地址 no_addr =1
        no_addr = idev->ifa_list == NULL;
    
        fl4.flowi4_mark = IN_DEV_SRC_VMARK(idev) ? skb->mark : 0;
    
        trace_fib_validate_source(dev, &fl4);
    
        net = dev_net(dev);
        //........又查了一次路由
    //如果没有查到,即我没有到你的路由goto last_resort;在 last_resort中判断if (rpf),即判断是否开启了防护
        if (fib_lookup(net, &fl4, &res, 0))// 查询失败是允许的,只要rp_filter未启用
            goto last_resort;
            //如果查询的结果不是RTN_UNICAST,不行。你这个报文到这是RTN_UNICAST的,而我到你却不是RTN_UNICAST的,扔掉
            //排除同时 LOCAL路由 接口开启local 情况  可以参考这个patch http://patchwork.ozlabs.org/project/netdev/patch/20091203112557.15100.85827.sendpatchset@x2.localnet/
            /*Change fib_validate_source() to accept packets with a local source address when
        the "accept_local" sysctl is set for the incoming inet device. Combined with the
        previous patches, this allows to communicate between multiple local interfaces
        over the wire.*/
        if (res.type != RTN_UNICAST &&
            (res.type != RTN_LOCAL || !IN_DEV_ACCEPT_LOCAL(idev)))
            goto e_inval;
        if (!rpf && !fib_num_tclassid_users(dev_net(dev)) &&
            (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev)))
            goto last_resort;
        fib_combine_itag(itag, &res);
        dev_match = false;
    
    #ifdef CONFIG_IP_ROUTE_MULTIPATH
        for (ret = 0; ret < res.fi->fib_nhs; ret++) {
            struct fib_nh *nh = &res.fi->fib_nh[ret];
    
            if (nh->nh_dev == dev) {
                dev_match = true;
                break;
            } else if (l3mdev_master_ifindex_rcu(nh->nh_dev) == dev->ifindex) {
                dev_match = true;
                break;
            }
        }
    #else
        if (FIB_RES_DEV(res) == dev)
            dev_match = true;//如果我查询的出接口,和你报文的入接口时一个,那么ok,我认为你是合法的
    #endif
        if (dev_match) {
            ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
            return ret;
        }
        
        //到这步了,表示查询到了我到你的路由,但是出接口和你入接口不匹配
        if (no_addr)
            goto last_resort;
        if (rpf == 1)
            goto e_rpf;
            
            //到这表示,我没有开启安全防护,入接口有ip,而你给我的报文不是从这我查询的出接口出去的
    //勉强通过,不过我得看看是否可以从我查询的出接口,有到你的路由,即指定出接口查询路由。如果查询成功,就表示,
    //从你这个入口出去,其实也可以到达你,所以算了,放过你吧,不扔掉你了。
    
        fl4.flowi4_oif = dev->ifindex;
    
        ret = 0;
        if (fib_lookup(net, &fl4, &res, FIB_LOOKUP_IGNORE_LINKSTATE) == 0) {
            if (res.type == RTN_UNICAST)
                ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;
        }
        return ret;
    
    last_resort:
        if (rpf)
            goto e_rpf;
        *itag = 0;
        return 0;
    
    e_inval:
        return -EINVAL;
    e_rpf:
        return -EXDEV;
    }
    /* Ignore rp_filter for packets protected by IPsec. */
    int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
                u8 tos, int oif, struct net_device *dev,
                struct in_device *idev, u32 *itag)
    {
        
        //该接口开启防护,rpf = 1
        int r = secpath_exists(skb) ? 0 : IN_DEV_RPFILTER(idev);
    
        if (!r && !fib_num_tclassid_users(dev_net(dev)) &&
            IN_DEV_ACCEPT_LOCAL(idev) &&
            (dev->ifindex != oif || !IN_DEV_TX_REDIRECTS(idev))) {
            *itag = 0;
            return 0;
        }
        return __fib_validate_source(skb, src, dst, tos, oif, dev, r, idev, itag);
    }

    根据fib_validate_source 的log :

     可知只需要关闭rpf 就行

     ip_rcv

     --> ip_rcv_finish

      --> ip_route_input_noref  ##如果skb还没有目的条目(路由相关),初始化虚拟路径cache

        ##目的地址是组播地址 ,这就是我们要分析的一支路径

         -->ip_route_input_mc -->fib_validate_source --> __fib_validate_source -->fib_lookup

        ##else 目的地址 非组播地址

        -->ip_route_input_slow

          -->fib_validate_source ## 通过fib_lookup查找到RTN_LOCAL类型路由,做反向检查,最终走local_input流程

                    ##查找到RTN_BROADCAST类型路由且源地址非全0,也做反向检查

          ##不满足RTN_LOCAL和RTN_BROADCAST类型路由,则调用ip_mkroute_intput, 创建route cache entry

          -->ip_mkroute_input-->__mkroute_input-->fib_validate_source

    /*
     *    NOTE. We drop all the packets that has local source
     *    addresses, because every properly looped back packet
     *    must have correct destination already attached by output routine.
     *
     *    Such approach solves two big problems:
     *    1. Not simplex devices are handled properly.
     *    2. IP spoofing attempts are filtered with 100% of guarantee.
     *    called with rcu_read_lock()
     */
    
    static int ip_route_input_slow(struct sk_buff *skb, __be32 daddr, __be32 saddr,
                       u8 tos, struct net_device *dev)
    {
        struct fib_result res;
        struct in_device *in_dev = __in_dev_get_rcu(dev);
        struct ip_tunnel_info *tun_info;
        struct flowi4    fl4;
        unsigned int    flags = 0;
        u32        itag = 0;
        struct rtable    *rth;
        int        err = -EINVAL;
        struct net    *net = dev_net(dev);
        bool do_cache;
    ------------------------------------
    
        /*
         *    Now we are ready to route packet.
         */
        fl4.flowi4_oif = 0;
        fl4.flowi4_iif = l3mdev_fib_oif_rcu(dev);
        fl4.flowi4_mark = skb->mark;
        fl4.flowi4_tos = tos;
        fl4.flowi4_scope = RT_SCOPE_UNIVERSE;
        fl4.flowi4_flags = 0;
        fl4.daddr = daddr;
        fl4.saddr = saddr;
        err = fib_lookup(net, &fl4, &res, 0);
        if (err != 0) {
            if (!IN_DEV_FORWARD(in_dev))
                err = -EHOSTUNREACH;
            goto no_route;
        }
    
        if (res.type == RTN_BROADCAST)
            goto brd_input;
    
        if (res.type == RTN_LOCAL) {
            err = fib_validate_source(skb, saddr, daddr, tos,
                          0, dev, in_dev, &itag);
            if (err < 0)
                goto martian_source;
            goto local_input;
        }
    
        if (!IN_DEV_FORWARD(in_dev)) {
            err = -EHOSTUNREACH;
            goto no_route;
        }
        if (res.type != RTN_UNICAST)
            goto martian_destination;
    
        err = ip_mkroute_input(skb, &res, &fl4, in_dev, daddr, saddr, tos);
    out:    return err;
    
    brd_input:
        if (skb->protocol != htons(ETH_P_IP))
            goto e_inval;
    
        if (!ipv4_is_zeronet(saddr)) {
            err = fib_validate_source(skb, saddr, 0, tos, 0, dev,
                          in_dev, &itag);
            if (err < 0)
                goto martian_source;
        }
        flags |= RTCF_BROADCAST;
        res.type = RTN_BROADCAST;
        RT_CACHE_STAT_INC(in_brd);
    
    local_input:
        do_cache = false;
        if (res.fi) {
            if (!itag) {
                rth = rcu_dereference(FIB_RES_NH(res).nh_rth_input);
                if (rt_cache_valid(rth)) {
                    skb_dst_set_noref(skb, &rth->dst);
                    err = 0;
                    goto out;
                }
                do_cache = true;
            }
        }
    
        rth = rt_dst_alloc(net->loopback_dev, flags | RTCF_LOCAL, res.type,
                   IN_DEV_CONF_GET(in_dev, NOPOLICY), false, do_cache);
        if (!rth)
            goto e_nobufs;
    
        rth->dst.output= ip_rt_bug;
        rth->rt_is_input = 1;
        if (res.table)
            rth->rt_table_id = res.table->tb_id;
    
        RT_CACHE_STAT_INC(in_slow_tot);
        if (res.type == RTN_UNREACHABLE) {
            rth->dst.input= ip_error;
            rth->dst.error= -err;
            rth->rt_flags     &= ~RTCF_LOCAL;
        }
        if (do_cache) {
            if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) {
                rth->dst.flags |= DST_NOCACHE;
                rt_add_uncached_list(rth);
            }
        }
        skb_dst_set(skb, &rth->dst);
        err = 0;
        goto out;
    
    no_route:
        RT_CACHE_STAT_INC(in_no_route);
        res.type = RTN_UNREACHABLE;
        res.fi = NULL;
        res.table = NULL;
        goto local_input;
    
        /*
         *    Do not cache martian addresses: they should be logged (RFC1812)
         */
    martian_destination:
        RT_CACHE_STAT_INC(in_martian_dst);
    
    e_inval:
        err = -EINVAL;
        goto out;
    
    e_nobufs:
        err = -ENOBUFS;
        goto out;
    
    martian_source:
        ip_handle_martian_source(dev, in_dev, skb, daddr, saddr);
        goto out;
    }

     

    static inline int fib_lookup(struct net *net, struct flowi4 *flp,
                     struct fib_result *res, unsigned int flags)
    {
        struct fib_table *tb;
        int err = -ENETUNREACH;
    
        flags |= FIB_LOOKUP_NOREF;
         /* 系统配置了路由规则,因此走这个路径; 只要添加了rule,此值为1,即使删除添加的rule,仍为1 */
        if (net->ipv4.fib_has_custom_rules)
            return __fib_lookup(net, flp, res, flags);
    
        rcu_read_lock();
        /* 无策略路由规则时,直接查询local/main/default三张路由表 */
        res->tclassid = 0;
    
        tb = rcu_dereference_rtnl(net->ipv4.fib_main);
        if (tb)
            err = fib_table_lookup(tb, flp, res, flags);
    
        if (!err)
            goto out;
    
        tb = rcu_dereference_rtnl(net->ipv4.fib_default);
        if (tb)
            err = fib_table_lookup(tb, flp, res, flags);
    
    out:
        if (err == -EAGAIN)
            err = -ENETUNREACH;
    
        rcu_read_unlock();
    
        return err;
    }
    int __fib_lookup(struct net *net, struct flowi4 *flp,
             struct fib_result *res, unsigned int flags)
    {
        struct fib_lookup_arg arg = {
            .result = res,
            .flags = flags,
        };
        int err;
        /* 通过路由规则查询对应的路由表 
        分析代码我们可以导到,路由匹配函数为fib4_rule_match,一般action函数为fib4_rule_action,在下面会继续分析这两个函数。*/
        err = fib_rules_lookup(net->ipv4.rules_ops, flowi4_to_flowi(flp), 0, &arg);
    #ifdef CONFIG_IP_ROUTE_CLASSID
        if (arg.rule)
            res->tclassid = ((struct fib4_rule *)arg.rule)->tclassid;
        else
            res->tclassid = 0;
    #endif
    
        if (err == -ESRCH)
            err = -ENETUNREACH;
    
        return err;
    }

    当 rp_filter=1  src_valid_mark=0
    022] IPv4: dev_name:eth10  src:192.168.42.27-->dst:192.168.42.11 mark:1 fib_loopup[err0 res.type:2]
    [Tue Mar  1 12:47:32 2022] dev:eth10 idev:eth10  src:192.168.42.27-->dst:192.168.42.11 mark:1 111111__fib_validate_source lookup restype:1  match:0
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth10  src:192.168.42.30-->dst:192.168.42.11 mark:1 fib_loopup[err0 res.type:2]
    [Tue Mar  1 12:47:32 2022] dev:eth10 idev:eth10  src:192.168.42.30-->dst:192.168.42.11 mark:1 111111__fib_validate_source lookup restype:1  match:0
    [Tue Mar  1 12:47:32 2022] dev:eth10 idev:eth10  src:192.168.42.30-->dst:192.168.42.11 mark:1 2222222__fib_validate_source lookup restype:1  match:0
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth10  src:192.168.42.30-->dst:192.168.42.11 mark:1 fib_validate_source[err:-18 ]
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth0  src:10.67.8.226-->dst:10.67.11.138 mark:0 fib_loopup[err0 res.type:1]
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth0  src:10.67.10.179-->dst:10.67.10.178 mark:0 fib_loopup[err0 res.type:1]
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth0  src:10.67.10.3-->dst:10.67.8.214 mark:0 fib_loopup[err0 res.type:1]
    [Tue Mar  1 12:47:32 2022] dev:eth10 idev:eth10  src:192.168.42.27-->dst:192.168.42.11 mark:1 2222222__fib_validate_source lookup restype:1  match:0
    [Tue Mar  1 12:47:32 2022] IPv4: dev_name:eth10  src:192.168.42.27-->dst:192.168.42.11 mark:1 fib_validate_source[err:-18 ]
    [Tue Mar  1 12:47:32 2022] net_ratelimit: 2 callbacks suppressed
    [Tue Mar  1 12:47:32 2022] IPv4: martian source 192.168.42.11 from 192.168.42.27, on dev eth10
    
    
    当 rp_filter=1  src_valid_mark=1
    
    
     IPv4: dev_name:eth10  src:192.168.42.20-->dst:192.168.42.11 mark:1 fib_loopup[err0 res.type:2]
    [Tue Mar  1 12:57:55 2022] dev:eth10 idev:eth10  src:192.168.42.20-->dst:192.168.42.11 mark:1 111111__fib_validate_source lookup restype:2  match:0
    [Tue Mar  1 12:57:55 2022] IPv4: dev_name:eth10  src:192.168.42.20-->dst:192.168.42.11 mark:1 fib_validate_source[err:-22 ]
    [Tue Mar  1 12:57:55 2022] IPv4: martian source 192.168.42.11 from 192.168.42.20, on dev eth10
    [Tue Mar  1 12:57:55 2022] ll header: 00000000: 02 25 57 75 0e d4 02 25 57 75 0e cd 08 00        .%Wu...%Wu....

    ip route get 192168.42.20 执行时, 也是调用fib_lookup 函数去查找 其结果也就是上述的结果

    所以会看到反向查找的时候 入口和出口不一样 如果开启rp_filter 就会导致drop

     类似的可以去看 arp_filter  arp_ignore arp_accept 等选项

    ps: 处理arp 请求报文的时候也会涉及到路由查找以及ip 校验

    if (arp->ar_op == htons(ARPOP_REQUEST) &&
            ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {

     

  • 相关阅读:
    IIS6的http.ini伪静态规则转换为IIS7伪静态规则的方法
    php ecshop 二级域名切换跳转时session不同步,解决session无法共享同步导致无法登陆的问题
    ECSHOP登录自动退出问题解决
    aspcms 分享到微信显示找不到模版的解决办法!
    ASPCMS内容调用去掉html标签
    凡是出现了问题,都是有原因存在的。
    spring下载地址
    比较好的资源
    xp系统下,安装github时,需要下载的补丁
    联合更新语句,根据photo中的数量更新相册表Num列
  • 原文地址:https://www.cnblogs.com/codestack/p/15847483.html
Copyright © 2020-2023  润新知