文章目录
这篇笔记来看看路由查询过程中,到底是如何根据策略路由规则引导最终的路由数据库查询的。
源代码路径 | 说明 |
---|---|
net/core/fib_rules.c | 策略路由非协议相关实现 |
net/ipv4/fib_rules.c | 策略路由的IPv4实现 |
策略路由查询: fib_rules_lookup()
实际的路由查询过程中,该函数是由fib_lookup()调用的,fib_lookup()负责完成整个路由查询过程。
// 在查询之前,已经确定了对应的协议族,flowi为查询条件
int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
int flags, struct fib_lookup_arg *arg)
{
struct fib_rule *rule;
int err;
rcu_read_lock();
// 遍历协议规则的策略路由规则列表,该列表本身就是按照优先级由高到低顺序排好的
list_for_each_entry_rcu(rule, &ops->rules_list, list) {
jumped:
// 查询条件如果和策略路由规则不匹配,直接尝试匹配下一条
if (!fib_rule_match(rule, ops, fl, flags))
continue;
// 规则匹配了,下面根据该路由规则的action执行后续动作
if (rule->action == FR_ACT_GOTO) {
// action是跳转到另外一条策略路由规则继续匹配,那么进行跳转
struct fib_rule *target;
// 获取目标规则target的指针,该指针会是保存在ctarget中的
target = rcu_dereference(rule->ctarget);
if (target == NULL) {
// 由于允许先指定一个不存在的target规则,所以这里需要特殊处理一下
continue;
} else {
// 跳转,然后继续匹配
rule = target;
goto jumped;
}
} else if (rule->action == FR_ACT_NOP)
// 如果规则的action是什么都不做,那么继续遍历下一条
continue;
else
// 其它情况调用协议族的action()回调
err = ops->action(rule, fl, flags, arg);
// 如果不等于-EAGAIN,那么查询结束(是否有路由由err决定),增加该规则引用计数并返回
if (err != -EAGAIN) {
fib_rule_get(rule);
arg->rule = rule;
goto out;
}
}
// 执行到最后都没匹配到,那么是一种ESRCH失败
err = -ESRCH;
out:
rcu_read_unlock();
return err;
}
策略路由规则匹配: fib_rule_match()
该函数识别一条策略路由规则和查询条件是否匹配。可以看出,通用规则中,只会检查输入接口和mark值。
static int fib_rule_match(struct fib_rule *rule, struct fib_rules_ops *ops,
struct flowi *fl, int flags)
{
int ret = 0;
// 规则指定了网络设备对象,那么将其和条件中的iif比较
if (rule->ifindex && (rule->ifindex != fl->iif))
goto out;
// fwmark值比较
if ((rule->mark ^ fl->mark) & rule->mark_mask)
goto out;
// 调用协议族的match()回调,对于IPv4,该函数是fib4_rule_match()
ret = ops->match(rule, fl, flags);
out:
// 如果设置了取反标记,对结果取反即可
return (rule->flags & FIB_RULE_INVERT) ? !ret : ret;
}
IPv4策略路由规则匹配回调: fib4_rule_match()
IPv4协议比较源IP和目的IP,以及tos三个字段。
static int fib4_rule_match(struct fib_rule *rule, struct flowi *fl, int flags)
{
struct fib4_rule *r = (struct fib4_rule *) rule;
__be32 daddr = fl->fl4_dst;
__be32 saddr = fl->fl4_src;
if (((saddr ^ r->src) & r->srcmask) || ((daddr ^ r->dst) & r->dstmask))
return 0;
if (r->tos && (r->tos != fl->fl4_tos))
return 0;
return 1;
}
IPv4策略路由action()回调: fib4_rule_action()
static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp,
int flags, struct fib_lookup_arg *arg)
{
int err = -EAGAIN;
struct fib_table *tbl;
switch (rule->action) {
case FR_ACT_TO_TBL: // 查询指定路由表
break;
case FR_ACT_UNREACHABLE: // 不可达
err = -ENETUNREACH;
goto errout;
case FR_ACT_PROHIBIT: // 抑制查询
err = -EACCES;
goto errout;
case FR_ACT_BLACKHOLE: // blackhole
default:
err = -EINVAL;
goto errout;
}
// 调用路由表的查询接口
if ((tbl = fib_get_table(rule->fr_net, rule->table)) == NULL)
goto errout;
err = tbl->tb_lookup(tbl, flp, (struct fib_result *) arg->result);
if (err > 0)
err = -EAGAIN;
errout:
return err;
}