linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加
上一節分析了策略規則相關的數據結構,本節就分析一下策略規則的添加。對于策略規則的功能模塊,由于 v4 、 v6 都有用到,所以該模塊也和鄰居模塊一樣,抽象出了通用規則的接口函數,然后根據傳入的參數來進入協議相關的策略規則的接口函數,即分為通用接口
上一節分析了策略規則相關的數據結構,本節就分析一下策略規則的添加。對于策略規則的功能模塊,由于v4、v6都有用到,所以該模塊也和鄰居模塊一樣,抽象出了通用規則的接口函數,然后根據傳入的參數來進入協議相關的策略規則的接口函數,即分為通用接口函數與協議相關的接口函數。
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、接口名稱、fwmark、action、table_id進行設置
4.調用協議對應的configure函數,對fib?rule的源、目的ip、tos等值進行配置
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?
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?
goto?errout;
/*調用validate_rulemsg對傳入的源或者目的地址值進行合理性檢查*/
err?=?validate_rulemsg(frh,?tb,?ops);
if?(err?
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?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;
}
/*
設置fib?rule的fwmark,實際應用中可以根據這個值決定路由表的選擇,即實現
策略路由的功能
*/
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?
goto?errout_free;
/*
遍歷規則鏈表,找到第一個pref值比當前剛創建的fib?rule的pref值大的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
這個函數主要判斷frh與tb中的源、目的地址相關的參數是否合法,下面分析下這個函數的執行流程:
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的掩碼值、
路由表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;
/*
若應用層沒有設置路由表的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_MAX的id,都有創建相應的路由表了,則程序
返回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里的代碼,應用層的代碼目前是沒有時間分析了。
總結
以上是生活随笔為你收集整理的linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux c配置文件书写格式,读取配置
- 下一篇: linux 下升级apache,Cent