linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加
上一節(jié)分析了策略規(guī)則相關(guān)的數(shù)據(jù)結(jié)構(gòu),本節(jié)就分析一下策略規(guī)則的添加。對于策略規(guī)則的功能模塊,由于 v4 、 v6 都有用到,所以該模塊也和鄰居模塊一樣,抽象出了通用規(guī)則的接口函數(shù),然后根據(jù)傳入的參數(shù)來進(jìn)入?yún)f(xié)議相關(guān)的策略規(guī)則的接口函數(shù),即分為通用接口
上一節(jié)分析了策略規(guī)則相關(guān)的數(shù)據(jù)結(jié)構(gòu),本節(jié)就分析一下策略規(guī)則的添加。對于策略規(guī)則的功能模塊,由于v4、v6都有用到,所以該模塊也和鄰居模塊一樣,抽象出了通用規(guī)則的接口函數(shù),然后根據(jù)傳入的參數(shù)來進(jìn)入?yún)f(xié)議相關(guān)的策略規(guī)則的接口函數(shù),即分為通用接口函數(shù)與協(xié)議相關(guān)的接口函數(shù)。
1.通用規(guī)則的添加及處理流程
應(yīng)用層主要是通過netlink模塊實(shí)現(xiàn)與內(nèi)核中策略規(guī)則模塊通信,從而實(shí)現(xiàn)策略規(guī)則的添加、刪除等操作(本文不關(guān)注netlink接口實(shí)現(xiàn),僅介紹策略規(guī)則功能模塊相關(guān)的接口)。
1.1?fib_nl_newrule
不管是v4還是v6,在內(nèi)核中添加一個(gè)規(guī)則,首先就會調(diào)用通用接口函數(shù)fib_nl_newrule,然后由該函數(shù)找到協(xié)議相關(guān)的接口函數(shù),再調(diào)用協(xié)議相關(guān)的接口函數(shù),實(shí)現(xiàn)策略規(guī)則的添加。
下面我們就分析一下這個(gè)函數(shù),這個(gè)函數(shù)主要執(zhí)行一下功能:
1.根據(jù)應(yīng)用層傳遞的協(xié)議類型,查找到相應(yīng)注冊的struct?fib_rules_ops類型的變量
對于ipv4而言,就是fib4_rules_ops
2.??對應(yīng)用層傳遞的值進(jìn)行解析,并對傳入的源或者目的地址值進(jìn)行合理性檢查
3.創(chuàng)建一個(gè)新的fib?rule緩存,并對優(yōu)先級、接口index、接口名稱、fwmark、action、table_id進(jìn)行設(shè)置
4.調(diào)用協(xié)議對應(yīng)的configure函數(shù),對fib?rule的源、目的ip、tos等值進(jìn)行配置
5.增加此fib?rule的引用計(jì)數(shù),并根據(jù)優(yōu)先級將新的fib?rule插入到協(xié)議相關(guān)的rules_list鏈表對應(yīng)的位置
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?
goto?errout;
/*對于ipv4而言,獲取到的值即為fib4_rules_ops*/
ops?=?lookup_rules_ops(frh->family);
if?(ops?==?NULL)?{
err?=?EAFNOSUPPORT;
goto?errout;
}
/*對應(yīng)用層傳遞的參數(shù)進(jìn)行解析,并存放在tb中*/
err?=?nlmsg_parse(nlh,?sizeof(*frh),?tb,?FRA_MAX,?ops->policy);
if?(err?
goto?errout;
/*調(diào)用validate_rulemsg對傳入的源或者目的地址值進(jìn)行合理性檢查*/
err?=?validate_rulemsg(frh,?tb,?ops);
if?(err?
goto?errout;
/*
創(chuàng)建一個(gè)struct?fib_rule?*類型的變量
*/
rule?=?kzalloc(ops->rule_size,?GFP_KERNEL);
if?(rule?==?NULL)?{
err?=?-ENOMEM;
goto?errout;
}
/*
根據(jù)應(yīng)用層傳遞的參數(shù),對優(yōu)先級進(jìn)行設(shè)置
*/
if?(tb[FRA_PRIORITY])
rule->pref?=?nla_get_u32(tb[FRA_PRIORITY]);
/*
根據(jù)應(yīng)用層傳遞的參數(shù),決定是否需要設(shè)置fib?rule的ifindex值
*/
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;
}
/*
設(shè)置fib?rule的fwmark,實(shí)際應(yīng)用中可以根據(jù)這個(gè)值決定路由表的選擇,即實(shí)現(xiàn)
策略路由的功能
*/
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;
}
/*
設(shè)置fwmark的掩碼值
*/
if?(tb[FRA_FWMASK])
rule->mark_mask?=?nla_get_u32(tb[FRA_FWMASK]);
/*設(shè)置規(guī)則的action以及與該規(guī)則關(guān)聯(lián)的路由表id,實(shí)現(xiàn)規(guī)則與路由表的關(guān)聯(lián)*/
rule->action?=?frh->action;
rule->flags?=?frh->flags;
rule->table?=?frh_get_table(frh,?tb);
/*
當(dāng)沒有為策略規(guī)則配置優(yōu)先級也沒有默認(rèn)優(yōu)先級時(shí),則會調(diào)用該協(xié)議對應(yīng)
的default_pref獲取一個(gè)默認(rèn)的優(yōu)先級.
對于ipv4,其default_pref的原理是獲取規(guī)則鏈表中非0優(yōu)先級中的最高優(yōu)先級,
即獲取的默認(rèn)優(yōu)先級是除0優(yōu)先級的規(guī)則外,最高的優(yōu)先級。
*/
if?(!rule->pref?&&?ops->default_pref)
rule->pref?=?ops->default_pref();
/*
調(diào)用該協(xié)議對應(yīng)的configure,該該策略進(jìn)行配置
*/
err?=?ops->configure(rule,?skb,?nlh,?frh,?tb);
if?(err?
goto?errout_free;
/*
遍歷規(guī)則鏈表,找到第一個(gè)pref值比當(dāng)前剛創(chuàng)建的fib?rule的pref值大的fib?rule:
若找到符合要求的規(guī)則,則將新創(chuàng)建的策略規(guī)則添加到符合要求的規(guī)則
之前;
若在搜索完整個(gè)鏈表仍沒有找到符合要求的規(guī)則,則將該規(guī)則添加到
當(dāng)前鏈表已有規(guī)則的租后,即鏈尾。
*/
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
在上面的函數(shù)中,在根據(jù)協(xié)議簇查找相應(yīng)的已注冊的協(xié)議相關(guān)的fib_rules_ops變量時(shí),調(diào)用了函數(shù)lookup_rules_ops來實(shí)現(xiàn)的,下面就分析一下這個(gè)函數(shù)
功能:根據(jù)協(xié)議簇在鏈表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;
}
接著就調(diào)用函數(shù)validate_rulemsg,對于應(yīng)用層傳遞的參數(shù)進(jìn)行了合法性檢查,下面就分析一下這個(gè)函數(shù)
1.3?validate_rulemsg
這個(gè)函數(shù)主要判斷frh與tb中的源、目的地址相關(guān)的參數(shù)是否合法,下面分析下這個(gè)函數(shù)的執(zhí)行流程:
1.若源地址的長度不為0時(shí),若tb[FRA_SRC]中為空,或者傳入的地址
長度與相應(yīng)協(xié)議規(guī)定的地址長度不等,或者實(shí)際傳遞的地址的
實(shí)際長度與相應(yīng)協(xié)議規(guī)定的地址長度不等時(shí),則返回-EINVAL。
1.若目的地址的長度不為0時(shí),若tb[FRA_SRC]中為空,或者傳入的地址
長度與相應(yīng)協(xié)議規(guī)定的地址長度不等,或者實(shí)際傳遞的地址的
實(shí)際長度與相應(yīng)協(xié)議規(guī)定的地址長度不等時(shí),則返回-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;
}
接著就是調(diào)用協(xié)議相關(guān)的函數(shù),進(jìn)行協(xié)議相關(guān)的配置操作了,下面我們就分析之
2.協(xié)議相關(guān)的添加處理流程
本節(jié)以ipv4為例,在函數(shù)fib_nl_newrule中,通過ops->configure函數(shù)調(diào)用了協(xié)議相關(guān)的配置函數(shù),而對于ipv4而言,即是函數(shù)fib4_rule_configure。下面就分析一下這個(gè)函數(shù)。
2.1??fib4_rule_configure
當(dāng)新增加一個(gè)ipv4的fib?rule規(guī)則時(shí),就會調(diào)用該函數(shù),對
新創(chuàng)建的struct?fib4_rule類型的變量進(jìn)行初始化操作,即根據(jù)
應(yīng)用層傳遞的配置參數(shù),設(shè)置struct?fib4_rule類型的變量的
源ip地址、源ip的掩碼值、目的ip地址、目的ip的掩碼值、
路由表id、tos等
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;
/*
若應(yīng)用層沒有設(shè)置路由表的id,則調(diào)用fib_empty_table創(chuàng)建
一個(gè)新的路由表,并將新創(chuàng)建的路由表的id傳遞
給rule->table
(使用如下命令,即會使系統(tǒng)創(chuàng)建一個(gè)新的路由表
#?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;
}
剛才的函數(shù)里,有調(diào)用函數(shù)fib_empty_table,那我們就分析一下這個(gè)函數(shù)。
2.2?fib_empty_table
功能:從0開始到RT_TABLE_MAX為止,找到第一個(gè)沒有創(chuàng)建路由表的id后,
即調(diào)用fib_new_table創(chuàng)建此id對應(yīng)的路由表,并返回路由表的首地址;
若從0開始到RT_TABLE_MAX的id,都有創(chuàng)建相應(yīng)的路由表了,則程序
返回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;
}
以上就是策略規(guī)則添加的整個(gè)分析流程,通過分析這個(gè)流程,發(fā)現(xiàn)在添加策略規(guī)則時(shí),函數(shù)并沒有判斷要添加的規(guī)則是否已存在,這樣會不會導(dǎo)致策略規(guī)則的重復(fù)添加呢?還是在應(yīng)用層進(jìn)行的判斷呢?我感覺應(yīng)該是應(yīng)用層做了判斷,由于目前一直在學(xué)習(xí)kernel里的代碼,應(yīng)用層的代碼目前是沒有時(shí)間分析了。
總結(jié)
以上是生活随笔為你收集整理的linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux c配置文件书写格式,读取配置
- 下一篇: linux libodbc.so.1,C