Linux 路由 学习笔记 之六 策略规则的添加

上一节分析了策略规则相关的数据结构,本节就分析一下策略规则的添加。对于策略规则的功能模块,由于v4v6都有用到,所以该模块也和邻居模块一样,抽象出了通用规则的接口函数,然后根据传入的参数来进入协议相关的策略规则的接口函数,即分为通用接口函数与协议相关的接口函数。

 

1.通用规则的添加及处理流程

应用层主要是通过netlink模块实现与内核中策略规则模块通信,从而实现策略规则的添加、删除等操作(本文不关注netlink接口实现,仅介绍策略规则功能模块相关的接口)。

1.1 fib_nl_newrule

不管是v4还是v6,在内核中添加一个规则,首先就会调用通用接口函数fib_nl_newrule,然后由该函数找到协议相关的接口函数,再调用协议相关的接口函数,实现策略规则的添加。

 

 

下面我们就分析一下这个函数,这个函数主要执行一下功能:

1.根据应用层传递的协议类型,查找到相应注册的struct fib_rules_ops类型的变量

   对于ipv4而言,就是fib4_rules_ops

2.  对应用层传递的值进行解析,并对传入的源或者目的地址值进行合理性检查

3.创建一个新的fib rule缓存,并对优先级、接口index、接口名称、fwmarkactiontable_id进行设置

4.调用协议对应的configure函数,对fib rule的源、目的iptos等值进行配置

5.增加此fib rule的引用计数,并根据优先级将新的fib rule插入到协议相关的rules_list链表对应的位置

int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)

{

struct fib_rule_hdr *frh = nlmsg_data(nlh);

struct fib_rules_ops *ops = NULL;

struct fib_rule *rule, *r, *last = NULL;

struct nlattr *tb[FRA_MAX+1];

int err = -EINVAL;

 

if (nlh->nlmsg_len < nlmsg_msg_size(sizeof(*frh)))

goto errout;

/*对于ipv4而言,获取到的值即为fib4_rules_ops*/

ops = lookup_rules_ops(frh->family);

if (ops == NULL) {

err = EAFNOSUPPORT;

goto errout;

}

/*对应用层传递的参数进行解析,并存放在tb*/

err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy);

if (err < 0)

goto errout;

/*调用validate_rulemsg对传入的源或者目的地址值进行合理性检查*/

err = validate_rulemsg(frh, tb, ops);

if (err < 0)

goto errout;

/*

创建一个struct fib_rule *类型的变量

*/

rule = kzalloc(ops->rule_size, GFP_KERNEL);

if (rule == NULL) {

err = -ENOMEM;

goto errout;

}

/*

根据应用层传递的参数,对优先级进行设置

*/

if (tb[FRA_PRIORITY])

rule->pref = nla_get_u32(tb[FRA_PRIORITY]);

/*

根据应用层传递的参数,决定是否需要设置fib ruleifindex

*/

if (tb[FRA_IFNAME]) {

struct net_device *dev;

 

rule->ifindex = -1;

nla_strlcpy(rule->ifname, tb[FRA_IFNAME], IFNAMSIZ);

dev = __dev_get_by_name(rule->ifname);

if (dev)

rule->ifindex = dev->ifindex;

}

/*

设置fib rulefwmark,实际应用中可以根据这个值决定路由表的选择,即实现

策略路由的功能

*/

if (tb[FRA_FWMARK]) {

rule->mark = nla_get_u32(tb[FRA_FWMARK]);

if (rule->mark)

/* compatibility: if the mark value is non-zero all bits

 * are compared unless a mask is explicitly specified.

 */

rule->mark_mask = 0xFFFFFFFF;

}

/*

设置fwmark的掩码值

*/

if (tb[FRA_FWMASK])

rule->mark_mask = nla_get_u32(tb[FRA_FWMASK]);

/*设置规则的action以及与该规则关联的路由表id,实现规则与路由表的关联*/

rule->action = frh->action;

rule->flags = frh->flags;

rule->table = frh_get_table(frh, tb);

/*

当没有为策略规则配置优先级也没有默认优先级时,则会调用该协议对应

default_pref获取一个默认的优先级.

对于ipv4,其default_pref的原理是获取规则链表中非0优先级中的最高优先级,

即获取的默认优先级是除0优先级的规则外,最高的优先级。

*/

if (!rule->pref && ops->default_pref)

rule->pref = ops->default_pref();

 

/*

调用该协议对应的configure,该该策略进行配置

*/

err = ops->configure(rule, skb, nlh, frh, tb);

if (err < 0)

goto errout_free;

/*

遍历规则链表,找到第一个pref值比当前刚创建的fib rulepref值大的fib rule:

若找到符合要求的规则,则将新创建的策略规则添加到符合要求的规则

之前;

若在搜索完整个链表仍没有找到符合要求的规则,则将该规则添加到

当前链表已有规则的租后,即链尾。

*/

list_for_each_entry(r, ops->rules_list, list) {

if (r->pref > rule->pref)

break;

last = r;

}

 

fib_rule_get(rule);

 

if (last)

list_add_rcu(&rule->list, &last->list);

else

list_add_rcu(&rule->list, ops->rules_list);

 

notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid);

rules_ops_put(ops);

return 0;

 

errout_free:

kfree(rule);

errout:

rules_ops_put(ops);

return err;

}

 

 

 

1.2 lookup_rules_ops

在上面的函数中,在根据协议簇查找相应的已注册的协议相关的fib_rules_ops变量时,调用了函数lookup_rules_ops来实现的,下面就分析一下这个函数

功能:根据协议簇在链表rules_ops中查找符合要求的struct fib_rules_ops类型的

     变量对于ipv4而言,即为fib4_rules_ops

static struct fib_rules_ops *lookup_rules_ops(int family)

{

struct fib_rules_ops *ops;

 

rcu_read_lock();

list_for_each_entry_rcu(ops, &rules_ops, list) {

if (ops->family == family) {

if (!try_module_get(ops->owner))

ops = NULL;

rcu_read_unlock();

return ops;

}

}

rcu_read_unlock();

 

return NULL;

}

 

接着就调用函数validate_rulemsg,对于应用层传递的参数进行了合法性检查,下面就分析一下这个函数

1.3 validate_rulemsg

这个函数主要判断frhtb中的源、目的地址相关的参数是否合法,下面分析下这个函数的执行流程:

 

1.若源地址的长度不为0时,若tb[FRA_SRC]中为空,或者传入的地址

  长度与相应协议规定的地址长度不等,或者实际传递的地址的

  实际长度与相应协议规定的地址长度不等时,则返回-EINVAL

1.若目的地址的长度不为0时,若tb[FRA_SRC]中为空,或者传入的地址

  长度与相应协议规定的地址长度不等,或者实际传递的地址的

  实际长度与相应协议规定的地址长度不等时,则返回-EINVAL

 

static int validate_rulemsg(struct fib_rule_hdr *frh, struct nlattr **tb,

    struct fib_rules_ops *ops)

{

int err = -EINVAL;

 

if (frh->src_len)

if (tb[FRA_SRC] == NULL ||

    frh->src_len > (ops->addr_size * 8) ||

    nla_len(tb[FRA_SRC]) != ops->addr_size)

goto errout;

 

if (frh->dst_len)

if (tb[FRA_DST] == NULL ||

    frh->dst_len > (ops->addr_size * 8) ||

    nla_len(tb[FRA_DST]) != ops->addr_size)

goto errout;

 

err = 0;

errout:

return err;

}

 

接着就是调用协议相关的函数,进行协议相关的配置操作了,下面我们就分析之

2.协议相关的添加处理流程

本节以ipv4为例,在函数fib_nl_newrule中,通过ops->configure函数调用了协议相关的配置函数,而对于ipv4而言,即是函数 fib4_rule_configure。下面就分析一下这个函数。

2.1  fib4_rule_configure

当新增加一个ipv4 fib rule 规则时,就会调用该函数,对

新创建的struct fib4_rule类型的变量进行初始化操作,即根据

应用层传递的配置参数,设置struct fib4_rule类型的变量的

ip地址、源ip的掩码值、目的ip地址、目的ip的掩码值、

路由表idtos

 

static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,

       struct nlmsghdr *nlh, struct fib_rule_hdr *frh,

       struct nlattr **tb)

{

int err = -EINVAL;

struct fib4_rule *rule4 = (struct fib4_rule *) rule;

 

if (frh->tos & ~IPTOS_TOS_MASK)

goto errout;

 

if (rule->table == RT_TABLE_UNSPEC) {

if (rule->action == FR_ACT_TO_TBL) {

struct fib_table *table;

/*

若应用层没有设置路由表的id,则调用fib_empty_table创建

一个新的路由表,并将新创建的路由表的id传递

rule->table

(使用如下命令,即会使系统创建一个新的路由表

# ip rule add from 192.168.192.1 table 0)

*/

table = fib_empty_table();

if (table == NULL) {

err = -ENOBUFS;

goto errout;

}

 

rule->table = table->tb_id;

}

}

 

if (frh->src_len)

rule4->src = nla_get_be32(tb[FRA_SRC]);

 

if (frh->dst_len)

rule4->dst = nla_get_be32(tb[FRA_DST]);

 

#ifdef CONFIG_NET_CLS_ROUTE

if (tb[FRA_FLOW])

rule4->tclassid = nla_get_u32(tb[FRA_FLOW]);

#endif

 

rule4->src_len = frh->src_len;

rule4->srcmask = inet_make_mask(rule4->src_len);

rule4->dst_len = frh->dst_len;

rule4->dstmask = inet_make_mask(rule4->dst_len);

rule4->tos = frh->tos;

 

err = 0;

errout:

return err;

}

刚才的函数里,有调用函数fib_empty_table,那我们就分析一下这个函数。

2.2 fib_empty_table

功能:0开始到RT_TABLE_MAX为止,找到第一个没有创建路由表的id后,

            即调用fib_new_table创建此id对应的路由表,并返回路由表的首地址;

            若从0开始到RT_TABLE_MAXid,都有创建相应的路由表了,则程序

            返回NULL

static struct fib_table *fib_empty_table(void)

{

u32 id;

 

for (id = 1; id <= RT_TABLE_MAX; id++)

if (fib_get_table(id) == NULL)

return fib_new_table(id);

return NULL;

}

 

 

以上就是策略规则添加的整个分析流程,通过分析这个流程,发现在添加策略规则时,函数并没有判断要添加的规则是否已存在,这样会不会导致策略规则的重复添加呢?还是在应用层进行的判断呢?我感觉应该是应用层做了判断,由于目前一直在学习kernel里的代码,应用层的代码目前是没有时间分析了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值