linux内核路由模块fib_valid_source函数(反向路由检测)
表示对linu内核查路由的的效率表示堪忧。一个经过linux的报文,无论是上送本机,还是转发,都会进行大于等于2次的查路由动作。即执行大于等于2次的fib_lookup。外部包过来,ip_rcv->ip_rcv_finish->ip_route_input->ip_route_input_slowip_route_input_slow判断报文是否转发,还是上送本机即ip_forward
·
表示对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_input
static 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;
}
更多推荐
已为社区贡献3条内容
所有评论(0)