路由表的创建

inet_init() -> ip_init() -> ip_fib_init() -> fib_net_init() -> ip_fib_net_init()[net\ipv4\fib_frontend.c]

首先为路由表分配空间,这里的每个表项hlist_head实际都会链接一个单独的路由表,FIB_TABLE_HASHSZ表示了分配多少个路由表,一般情况下至少有两个–LOCAL和MAIN。注意这里仅仅是表头的空间分配,还没有真正分配路由表空间。

net->ipv4.fib_table_hash = kzalloc(

sizeof(struct hlist_head)*FIB_TABLE_HASHSZ, GFP_KERNEL);

ip_fib_net_init() -> fib4_rules_init(),这里真正分配了路由表空间

local_table = fib_hash_table(RT_TABLE_LOCAL);

main_table= fib_hash_table(RT_TABLE_MAIN);

然后将local和main表链入之前的fib_table_hash中

hlist_add_head_rcu(&local_table->tb_hlist,

&net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);

hlist_add_head_rcu(&main_table->tb_hlist,

&net->ipv4.fib_table_hash[TABLE_MAIN_INDEX]);

最终生成结构如图,LOCAL表位于fib_table_hash[0],MAIN表位于fib_table_hash[1];两张表通过结构tb_hlist链入链表,而tb_id则标识了功能,255是LOCAL表,254是MAIN表。

关于这里的struct fn_hash,它表示了不同子网掩码长度的hash表[即fn_zone],对于ipv4,从0~32共33个。而fn_hash的实现则是fib_table的最后一个参数unsigned char tb_data[0]。

注意到这里fn_zone还只是空指针,我们还只完成了路由表初始化的一部分。在启动阶段还会调用inet_rtm_newroute() -> fib_table_insert() -> fn_new_zone() [fib_hash.c]来创建fn_zone结构,前面已经讲过,fn_zone一共有33个,其中掩码长度为0[/0]表示为默认路由,fn_zone可以理解为相同掩码的地址集合。

首先为fn_zone分配空间

struct fn_zone *fz = kzalloc(sizeof(struct fn_zone), GFP_KERNEL);

传入参数z代表掩码长度,z = 0的掩码用于默认路由,一般只有一个,所以fz_divisor只需设为1;其它设为16;这里要提到fz_divisor的作用,fz->fz_hash并不是个单链表,而是一个哈希表,而哈希表的大小就是fz_divisor。

if (z) {

fz->fz_divisor = 16;

} else {

fz->fz_divisor = 1;

}

fz_hashmask实际是用于求余数的,当算出hash值,再hash & fz_hashmask就得出了在哈希表的位置;而fz_hash就是下一层的哈希表了,前面已经提过路由表被多组分层了,这里fz_hash就是根据fz_divisor大小来创建的;fz_order就是子网掩码长度;fz_mask就是子网掩码。

fz->fz_hashmask = (fz->fz_divisor - 1);

fz->fz_hash = fz_hash_alloc(fz->fz_divisor);

fz->fz_order = z;

fz->fz_mask = inet_make_mask(z);

从子网长度大于新添加fz的fn_zone中挑选一个不为空的fn_zones[i],将新创建的fz设成fn_zones[i].next;然后将fz根据掩码长度添加到fn_zones[]中相应位置;fn_zone_list始终指向掩码长度最长的fn_zone。

for (i=z+1; i<=32; i++)

if (table->fn_zones[i])

break;

if (i>32) {

fz->fz_next = table->fn_zone_list;

table->fn_zone_list = fz;

} else {

fz->fz_next = table->fn_zones[i]->fz_next;

table->fn_zones[i]->fz_next = fz;

}

table->fn_zones[z] = fz;

这里的fn_hash是数组与链表的结合体,看下fn_hash定义

struct fn_hash {

struct fn_zone*fn_zones[33];

struct fn_zone*fn_zone_list;

};

fn_hash包含33数组元素,每个元素存放一定掩码长度的fn_zone,其中fn_zone[i]存储掩码长度为i。而fn_zone通过内部属性fz_next又彼此串连起来,形成单向链表,其中fn_zone_list可以看作链表头,而这里链表的组织顺序是倒序的,即从掩码长到短。

到这里,fz_hash所分配的哈希表还没有插入内容,这部分为fib_insert_node()完成。

inet_rtm_newroute() -> fib_table_insert() -> fib_insert_node() [net\ipv4\fib_hash.c]

这里f是fib_node,可以理解为具有相同网络地址的路由项集合。根据fn_key(网络地址)和fz(掩码长度)来计算hash值,决定将f插入fz_hash的哪个项。

struct hlist_head *head = &fz->fz_hash[fn_hash(f->fn_key, fz)];

hlist_add_head(&f->fn_hash, head);

}

如何fib_node还不存在,则会创建它,这里的kmem_cache_zalloc()其实就是内存分配

new_f = kmem_cache_zalloc(fn_hash_kmem, GFP_KERNEL);

if (new_f == NULL)

goto out;

INIT_HLIST_NODE(&new_f->fn_hash);

INIT_LIST_HEAD(&new_f->fn_alias);

new_f->fn_key = key;

f = new_f;

路由表最后一层是fib_info,具体的路由信息都存储在此,它由fib_create_info()创建。

首先为fib_info分配空间,由于fib_info的最后一个属性是struct fib_nh fib_nh[0],因此大小是fib_info + nhs * fib_nh,这里的fib_nh代表了下一跳(next hop)的信息,nhs代表了下一跳的数目,一般情况下nhs=1,除非配置了支持多路径。

fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);

设置fi的相关属性

fi->fib_net = hold_net(net);

fi->fib_protocol = cfg->fc_protocol;

fi->fib_flags = cfg->fc_flags;

fi->fib_priority = cfg->fc_priority;

fi->fib_prefsrc = cfg->fc_prefsrc;

fi->fib_nhs = nhs;

使fi后面所有的nh->nh_parent指向fi,设置后如图所示

change_nexthops(fi) {

nexthop_nh->nh_parent = fi;

} endfor_nexthops(fi)

设置fib_nh的属性,这里仅展示了单一路径的情况:

struct fib_nh *nh = fi->fib_nh;

nh->nh_oif = cfg->fc_oif;

nh->nh_gw = cfg->fc_gw;

nh->nh_flags = cfg->fc_flags;

然后,再根据cfg->fc_scope值来设置nh的其余属性。如果scope是RT_SCOPE_HOST,则设置下一跳scope为RT_SCOPE_NOWHERE

if (cfg->fc_scope == RT_SCOPE_HOST) {

struct fib_nh *nh = fi->fib_nh;

nh->nh_scope = RT_SCOPE_NOWHERE;

nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif);

}

如果scope是RT_SCOPE_LINK或RT_SCOPE_UNIVERSE,则设置下跳

change_nexthops(fi) {

if ((err = fib_check_nh(cfg, fi, nexthop_nh)) != 0)

goto failure;

} endfor_nexthops(fi)

最后,将fi链入链表中,这里要注意的是所有的fib_info(只要创建了的)都会加入fib_info_hash中,如果路由项使用了优先地址属性,还会加入fib_info_laddrhash中。

hlist_add_head(&fi->fib_hash,

&fib_info_hash[fib_info_hashfn(fi)]);

if (fi->fib_prefsrc) {

struct hlist_head *head;

head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];

hlist_add_head(&fi->fib_lhash, head);

}

linux内核等价多路径路由,Linux内核分析 - 网络[四]:路由表相关推荐

  1. linux内核等价多路径路由,高级路由 - 我是*李世民*的个人空间 - OSCHINA - 中文开源技术交流社区...

    策略路由背后的概念 Linux内核在默认情况下使用两张路由表:一张表用于本地路由,另一张可以由管理员来配置.如果内核编译支持策略路由,那么可以有多大255张不同的.相互独立的路由表.策略路由背后的主要 ...

  2. PHP与ECMP,ECMP等价多路径路由(与PCC区别)

    ECMP-"Equal-Cost Multi-Path Routing"即等价多路径路由是三层路由标准协议,在华为.H3C和思科等路由器中ECMP路由配置是很常见的静态路由负载均衡 ...

  3. 网络协议学习:等价多路径路由ECMP

    简介 在计算机网络中,很多时候通信的源和目的存在多条已知路径.例如在数据中心网络中,需要有大量的带宽资源,且网络拓扑已知,利用这种特性可以采用ECMP(Equal-cost multi-path, 等 ...

  4. Linux内核分析 - 网络[四]:路由表

    路由表 在内核中存在路由表fib_table_hash和路由缓存表rt_hash_table.路由缓存表主要是为了加速路由的查找,每次路由查询都会先查找路由缓存,再查找路由表.这和cache是一个道理 ...

  5. linux下oracle安装路径查看,Linux Oracle 安装目录说明

    Linux 操作系统主要目录 /bin:存放着一百多个Linux下常用的命令.工具 /dev:存放着Linux下所有的设备文件 /home:用户主目录,每建一个用户,就会在这里新建一个与用户同名的目录 ...

  6. linux蓝牙接收文件路径,在Linux中设置蓝牙,方便文件传输、连接耳麦

    在 Windows 和 macOS 上对蓝牙功能进行配置都非常容易,用户可以很简单地就连接蓝牙鼠标.键盘.耳机或其它任何蓝牙配件.而在 Linux 系统中使用蓝牙连接就不那么方便了,至少我还没遇到过能 ...

  7. linux 驱动 include .h 路径 control,linux内核中的MFD子系统

    分析用的内核版本为5.1.3 1.MFD全称 Multi-function Device,多功能设备 2. 为何会出现MFD子系统 由于出现了一类具有多种功能的外围设备或cpu内部集成的硬件模块 3. ...

  8. Linux内核分析 - 网络[四补]:路由表补充

    内核版本:2.6.34       前篇路由表http://blog.csdn.net/qy532846454/article/details/6423496说明了路由表的结构及路由表的创建.下面是一 ...

  9. linux库怎么安装路径设置,Linux libtins 库安装教程

    因为工作原因需要用到libtins网络库, 所以今天去装一下. 很尴尬,由于本人对linux理解比较浅, 所以在中途遇到了一些问题. 虽然只是简单的安装步骤,但是阻挡不了自己菜啊. 一.  下载lib ...

最新文章

  1. Eigen 矩阵运算库在实际项目中的使用
  2. OpenCV提供的各种阈值选项的实例(附完整代码)
  3. 帕金森定律(Parkinson's Law)
  4. mpeg2,mpeg4,h264编码标准的异同
  5. 【笛卡尔树】【线段树】meetings 会议(P5044)
  6. 拒绝了对对象 'sp_sdidebug'(数据库 'master',所有者 'dbo')的 EXECUTE 权限
  7. centos7 源码安装goaccess
  8. 室内定位算法_001:室内定位算法技术咨询服务工作室简介(更新)
  9. js 小数自动补0_JavaScript 时分秒时间代码(自动补零)
  10. oracle 12.1.0.1.0,oracle_linux安装oracle_12.1.0
  11. mysql汪晓青课后答案_MySQL数据库基础实例教程
  12. Eclipse快捷键、Debug调试
  13. 【论文笔记】Exploring Self-attention for Image Recognition
  14. Python的MRO
  15. 罗振宇2018“时间的朋友”跨年演讲未删减全文
  16. 数据库开源备份工具phpmybackup
  17. LoadrRunner报错-Error: The table 'F:\测试脚本\loadrunner\toutTest3\NewParam.dat' does not exist.
  18. 基于STM32MP1和QT的疫情监控平台
  19. 花呗能不能不还?支付宝说春节集五福中彩蛋可帮还
  20. 单片机与 PC 机串口通信实验

热门文章

  1. LeetCode 1271. 十六进制魔术数字(进制转换)
  2. python画饼图_百度飞桨PaddlePaddle之[Python小白逆袭大神]7天训练营
  3. java的foeachr循环_for循环和Dowhile循环的应用
  4. python简单实践作业答案_python入门实践四:爬取牛客网面试专项练习题及答案
  5. c2c旅游springboot开源_重量级开源的商城和SpringBoot等项目看看有没有正好是你需要的...
  6. 质数——python代码赏析
  7. Educational Codeforces Round 104 (Rated for Div. 2)A~E解题报告
  8. 无连接网络通信程序UDP
  9. oracle磁带库清洁带标签,磁带库、磁带机和介质支持
  10. 【Text_Classification】学习到的语法知识