查看代码可知,查找路由后校验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) {
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子