tcp/ip 协议栈Linux内核源码分析七 路由子系统分析二 策略路由
內(nèi)核版本:3.4.39
策略路由就是根據(jù)配置策略查找路由表,早期的Linux版本是不支持策略路由的,默認(rèn)的查找策略就是先查找local路由表,找不到再繼續(xù)查找main表,當(dāng)支持策略路由功能時,內(nèi)核最多可以配置255個路由表,這時候根據(jù)先匹配策略,匹配后再去查找該策略指定的路由表,內(nèi)核最多支持32768張策略表,初始化的時候創(chuàng)建了local表,main表和default表。策略表按照優(yōu)先級從高到低的順序掛在一張鏈表上,優(yōu)先級范圍0~32767,數(shù)值越低優(yōu)先級越高。
內(nèi)核策略路由部分包括初始化、添加、刪除和查找。
初始化部分包括初始化默認(rèn)的策略鏈表。
添加部分就是在收到用戶層配置命令的時候進(jìn)行添加、刪除、修改、查詢等操作,即ip rule add、del等。
應(yīng)用層和內(nèi)核層通信是使用netlink通信。
策略查找操作是在查找路由的時候進(jìn)行,當(dāng)路由緩存查找失敗的時候就會去查找路由策略,然后根據(jù)策略去查詢路由表。
先看下策略路由部分啟動的流程:
策略子系統(tǒng)啟動的時候分為兩部分,上圖左邊部分放在路由子系統(tǒng)啟動的過程中,重點看下策略路由啟動:
int __net_init fib4_rules_init(struct net *net) {int err;struct fib_rules_ops *ops;//注冊策略虛函數(shù)集,關(guān)于策略的很多操作都在這里ops = fib_rules_register(&fib4_rules_ops_template, net);if (IS_ERR(ops))return PTR_ERR(ops);//初始化默認(rèn)策略,0,32766,32767err = fib_default_rules_init(ops);if (err < 0)的goto fail;net->ipv4.rules_ops = ops;return 0;fail:/* also cleans all rules already added */fib_rules_unregister(ops);return err; }?fib4_rules_ops_template是個函數(shù)集合,很多策略操作函數(shù)都在這里,策略路由查找的時候就會用到:
static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = {.family = AF_INET,.rule_size = sizeof(struct fib4_rule),.addr_size = sizeof(u32),.action = fib4_rule_action, //匹配后的動作.match = fib4_rule_match, //判斷是否匹配.configure = fib4_rule_configure,.compare = fib4_rule_compare,.fill = fib4_rule_fill,.default_pref = fib_default_rule_pref,.nlmsg_payload = fib4_rule_nlmsg_payload,.flush_cache = fib4_rule_flush_cache,.nlgroup = RTNLGRP_IPV4_RULE,.policy = fib4_rule_policy,.owner = THIS_MODULE, };初始化三張默認(rèn)策略表:
//初始化三張策略表,0標(biāo)識優(yōu)先級最高 static int fib_default_rules_init(struct fib_rules_ops *ops) {int err;//優(yōu)先級最高,匹配則查找local路由表err = fib_default_rule_add(ops, 0, RT_TABLE_LOCAL, 0);if (err < 0)return err;//優(yōu)先級為32766,匹配則查找main表err = fib_default_rule_add(ops, 0x7FFE, RT_TABLE_MAIN, 0);if (err < 0)return err;//優(yōu)先級為32737,匹配則查找default表err = fib_default_rule_add(ops, 0x7FFF, RT_TABLE_DEFAULT, 0);if (err < 0)return err;return 0; }上圖右邊也是策略路由子系統(tǒng)啟動流程:
這一部分主要注冊了netlink消息模塊,用來監(jiān)聽?wèi)?yīng)用層配置信息,其次就是注冊通知鏈,監(jiān)聽系統(tǒng)其它模塊信息,比如說網(wǎng)絡(luò)設(shè)備異常等等。
static int __init fib_rules_init(void) {int err;//注冊應(yīng)用層配置命令,添加、刪除和dumprtnl_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);err = register_pernet_subsys(&fib_rules_net_ops);if (err < 0)goto fail;//注冊事件通知鏈,監(jiān)聽其它子系統(tǒng)異常信息err = register_netdevice_notifier(&fib_rules_notifier);if (err < 0)goto fail_unregister;return 0;fail_unregister:unregister_pernet_subsys(&fib_rules_net_ops); fail:rtnl_unregister(PF_UNSPEC, RTM_NEWRULE);rtnl_unregister(PF_UNSPEC, RTM_DELRULE);rtnl_unregister(PF_UNSPEC, RTM_GETRULE);return err; }當(dāng)使用ip rule add或者ip rule del等應(yīng)用層命令時,最終會調(diào)用策略子系統(tǒng)注冊的netlink處理函數(shù),處理函數(shù)做的內(nèi)容比較簡單,先檢查參數(shù)的完整性,合法后則對策略表net->ipv4->rules_ops->rules_list執(zhí)行添加或刪除等操作。
上述就是策略路由的初始化流程、添加和刪除等。
下面看下策略路由查找,查找操作發(fā)生在路由緩存沒命中的情況下,內(nèi)核在網(wǎng)絡(luò)層收到報文的時候需要進(jìn)行路由以判斷是送到本地還是轉(zhuǎn)發(fā),同樣,發(fā)送報文的時候也需要查找路由,判斷走那個出口或者下一跳等。
通過上圖可以看到,無論是接收報文還是發(fā)送報文,當(dāng)路由緩存沒有命中的時候,查找策略的接口都是fib_rules_look,這個函數(shù)就是查找策略表,然后根據(jù)匹配的策略執(zhí)行相應(yīng)的動作:
//策略查找 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://根據(jù)報文信息去查找策略,并將查詢到的策略保存到rule里if (!fib_rule_match(rule, ops, fl, flags))continue;//跳過當(dāng)前策略表,繼續(xù)查找if (rule->action == FR_ACT_GOTO) {struct fib_rule *target;target = rcu_dereference(rule->ctarget);if (target == NULL) {continue;} else {rule = target;goto jumped;}} else if (rule->action == FR_ACT_NOP){//效果同上,繼續(xù)查找continue;}else{//查詢到了,根據(jù)策略執(zhí)行相應(yīng)的動作//ops->action是虛函數(shù),在路由子系統(tǒng)初始化的時候設(shè)定,//ipv4里該函數(shù)時fib4_rule_actionerr = ops->action(rule, fl, flags, arg);}if (err != -EAGAIN) {if ((arg->flags & FIB_LOOKUP_NOREF) ||likely(atomic_inc_not_zero(&rule->refcnt))) {arg->rule = rule;goto out;}break;}}err = -ESRCH; out:rcu_read_unlock();return err; } EXPORT_SYMBOL_GPL(fib_rules_lookup);當(dāng)策略匹配后會調(diào)用函數(shù)執(zhí)行ops->action執(zhí)行相應(yīng)的動作,這個虛函數(shù)在策略子系統(tǒng)初始化的時候被設(shè)置,上面已經(jīng)講過。ipv4里該函數(shù)時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;//不可達(dá)case FR_ACT_UNREACHABLE:err = -ENETUNREACH;goto errout;//禁止此類報文case FR_ACT_PROHIBIT:err = -EACCES;goto errout;//丟棄此類報文,case FR_ACT_BLACKHOLE:default:err = -EINVAL;goto errout;}//根據(jù)路由表ID獲取指定路由表指針tbl = fib_get_table(rule->fr_net, rule->table);if (!tbl)goto errout;//查詢路由表err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags);if (err > 0)err = -EAGAIN; errout:return err; }當(dāng)策略路由的動作是查找路由表時則調(diào)用fib_table_lookup查找路由表,如果是其它動作,比如blackhole等則丟棄報文。
以上就是策略路由中策略的主要部分。
?
參考目錄:
1. 《Linux Kernel Networking - ?Implementation and Theory》
2. 《深入理解Linux網(wǎng)絡(luò)技術(shù)內(nèi)幕》
總結(jié)
以上是生活随笔為你收集整理的tcp/ip 协议栈Linux内核源码分析七 路由子系统分析二 策略路由的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 车子开了二十分钟左右,前边四周都很烫什么
- 下一篇: 爱哭爱假笑爱吃零食的是什么星座?