linux内核路由模块fib_valid_source函数(反向路由检测)

来源:互联网 发布:北京生祥义网络 编辑:程序博客网 时间:2024/06/05 05:39

    表示对linu内核查路由的的效率表示堪忧。
一个经过linux的报文,无论是上送本机,还是转发,都会进行大于等于2次的查路由动作。
即执行大于等于2次的fib_lookup。


    外部包过来,ip_rcv->ip_rcv_finish->ip_route_input->ip_route_input_slow
ip_route_input_slow判断报文是否转发,还是上送本机即ip_forward还是ip_local_deliver
但是在执行到ip_forward/ip_local_deliver前,还是会再次查询一次路由,无论是否开启某些安全防护功能。即无条件再会查询一次路由。
    如果查询结果有问题,那再判断是否开启了某些安全防护,若开启,则丢包。。。。。。。
如果是我写,那肯定先判断安全防护是否开启,如果开启我再去执行查路由操作。。。。。


废话不多说了,ip_route_input_slow判断报文上送本机,还是转发时,执行了一次fib_lookup


ip_route_input_slow中我们可以看到这话 /*  *    Now we are ready to route packet.  */查不到路由,就goto了 if ((err = fib_lookup(net, &fl, &res)) != 0) {  if (!IN_DEV_FORWARD(in_dev))   goto e_hostunreach;  goto no_route; }如果查到路由,则判断包是上送本机,还是转发。 if (res.type == RTN_LOCAL) {//这是发给我自己的,即上送本机的  int result;//罪魁祸首出现了。就是这家伙,我先不分析这个函数,最后再分析,先看看转发流程在哪里也调用了fib_validate_source。  result = fib_validate_source(saddr, daddr, tos,          net->loopback_dev->ifindex,          dev, &spec_dst, &itag, skb->mark);  if (result < 0)   goto martian_source;  if (result)   flags |= RTCF_DIRECTSRC;  spec_dst = daddr;//执行local_input  goto local_input; } if (!IN_DEV_FORWARD(in_dev))  goto e_hostunreach; if (res.type != RTN_UNICAST)  goto martian_destination;//如果不是上送本机,而是需要转发的,执行 ip_mkroute_input err = ip_mkroute_input(skb, &res, &fl, in_dev, daddr, saddr, tos);ip_mkroute_input调用__mkroute_inputstatic int __mkroute_input(struct sk_buff *skb,      struct fib_result *res,      struct in_device *in_dev,      __be32 daddr, __be32 saddr, u32 tos,      struct rtable **result){.................................//再次见到它了,我们可以看到,无论是需要转发还是需要上送本机,几乎都是无条件执行fib_validate_source err = fib_validate_source(saddr, daddr, tos, FIB_RES_OIF(*res),      in_dev->dev, &spec_dst, &itag, skb->mark); if (err < 0) {  ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,      saddr);  err = -EINVAL;  goto cleanup; } }是时候来看看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. */int fib_validate_source(__be32 src, __be32 dst, u8 tos, int oif,   struct net_device *dev, __be32 *spec_dst,   u32 *itag, u32 mark){ struct in_device *in_dev;//组装查找key//注意源地址和目的地址互换,然后查路由之所以何种方式查路由,是有一定意图的。//首先valid_source顾名思义,就是看源地址是否有效,说明才是有效,看我有没有到你的路由//如果我有到你的路由,好吧,暂且认为你有效。如果没有到你的路由,而你却偏偏给我发了一个报文//那么我认为你是伪造原地址了。(该逻辑的前提是“对称路由”,即连在相同网络中的设备,都有相同的路由) struct flowi fl = { .nl_u = { .ip4_u =          { .daddr = src,     .saddr = dst,     .tos = tos } },       .mark = mark,       .iif = oif }; struct fib_result res; int no_addr, rpf; int ret; struct net *net; no_addr = rpf = 0; rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) {//入接口没有ip地址 no_addr =1  no_addr = in_dev->ifa_list == NULL;//该接口开启防护,rpf = 1//纯复制而已.....  rpf = IN_DEV_RPFILTER(in_dev);  if (mark && !IN_DEV_SRC_VMARK(in_dev))   fl.mark = 0; } rcu_read_unlock(); if (in_dev == NULL)  goto e_inval; net = dev_net(dev);//........又查了一次路由//如果没有查到,即我没有到你的路由goto last_resort;在 last_resort中判断if (rpf),即判断是否开启了防护 if (fib_lookup(net, &fl, &res))  goto last_resort;//如果查询的结果不是RTN_UNICAST,不行。你这个报文到这是RTN_UNICAST的,而我到你却不是RTN_UNICAST的,扔掉。 if (res.type != RTN_UNICAST)  goto e_inval_res; *spec_dst = FIB_RES_PREFSRC(res); fib_combine_itag(itag, &res);//如果我查询的出接口,和你报文的入接口时一个,那么ok,我认为你是合法的。#ifdef CONFIG_IP_ROUTE_MULTIPATH if (FIB_RES_DEV(res) == dev || res.fi->fib_nhs > 1)#else if (FIB_RES_DEV(res) == dev)#endif {  ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;  fib_res_put(&res);  return ret; }//到这步了,表示查询到了我到你的路由,但是出接口和你入接口不匹配 fib_res_put(&res);//我的入接口压根没有ip地址,你怎么可能发给我的?扔了 if (no_addr)  goto last_resort;//我的入接口有ip,但是我开启了攻击防护,我认为“我发给你的出接口和你发给我的入接口不匹配”这个行为,是攻击行为,扔掉 if (rpf == 1)  goto e_inval;//到这表示,我没有开启安全防护,入接口有ip,而你给我的报文不是从这我查询的出接口出去的//勉强通过,不过我得看看是否可以从我查询的出接口,有到你的路由,即指定出接口查询路由。如果查询成功,就表示,//从你这个入口出去,其实也可以到达你,所以算了,放过你吧,不扔掉你了。 fl.oif = dev->ifindex; ret = 0; if (fib_lookup(net, &fl, &res) == 0) {  if (res.type == RTN_UNICAST) {   *spec_dst = FIB_RES_PREFSRC(res);   ret = FIB_RES_NH(res).nh_scope >= RT_SCOPE_HOST;  }  fib_res_put(&res); } return ret;//扔掉last_resort: if (rpf)  goto e_inval; *spec_dst = inet_select_addr(dev, 0, RT_SCOPE_UNIVERSE); *itag = 0; return 0;e_inval_res: fib_res_put(&res);e_inval: return -EINVAL;}


原创粉丝点击