linux内核路由转发表的组成,linux路由转发表的检索过程(fib_lookup)
1) 转发表(fib_table)是记录IP转发信息的索引表, 转发表的每一记录(节点)描述了具有某一类目的地址的IP包应该使用哪一输出设备发给哪一目的主机. 转发表记录按网络区进行分类, 每一网络区描述了在特定网络地址位长下具有不同网络号的目的地址的转发信息. 第0区的网络地址位长为0, 与所有的IP地址都匹配, 用来描述缺省网关, 第32区的网络地址位长为32, 用来与完整的IP地址匹配. 在建立网络区时, 它们按网络地址位长从大到小的顺序排列, 在搜索IP地址时, 先从全主机地址的第32区开始匹配, 最后第0区与所有的地址都匹配, 产生缺省网关.
2) 系统至少使用两个转发表, 一个是局部转发表, 描述与所有本地地址匹配的转发信息, 另一个是主转发表, 描述与外部地址匹配的转发信息. 可以通过策略表来选择指定的转发表.
; net/ipv4/fib_rules.c fib_frontend.c fib_hash.c fib_semantics.c
static struct fib_rule *fib_rules = &local_rule; 转发策略链表
static struct fib_rule default_rule = { NULL, ATOMIC_INIT(2), 0x7FFF, RT_TABLE_DEFAULT, RTN_UNICAST, };
static struct fib_rule main_rule = { &default_rule, ATOMIC_INIT(2), 0x7FFE, RT_TABLE_MAIN, RTN_UNICAST, };
static struct fib_rule local_rule = { &main_rule, ATOMIC_INIT(2), 0, RT_TABLE_LOCAL, RTN_UNICAST, };
int fib_lookup(const struct rt_key *key, struct fib_result *res)
{
int err;
struct fib_rule *r, *policy;
struct fib_table *tb;
u32 daddr = key->dst;
u32 saddr = key->src;
FRprintk("Lookup: %u.%u.%u.%u
NIPQUAD(key->dst), NIPQUAD(key->src));
read_lock(&fib_rules_lock);
for (r = fib_rules; r; r=r->r_next) { 扫描策略表
if (((saddr^r->r_src) & r->r_srcmask) || 如果源地址不匹配
((daddr^r->r_dst) & r->r_dstmask) || 或者目的地址不匹配
#ifdef CONFIG_IP_ROUTE_TOS
(r->r_tos && r->r_tos != key->tos) || 或者服务类型不等
#endif
#ifdef CONFIG_IP_ROUTE_FWMARK
(r->r_fwmark && r->r_fwmark != key->fwmark) || 或者转发标记不等
#endif
(r->r_ifindex && r->r_ifindex != key->iif)) 或者输入设备不等
continue; 下一策略
FRprintk("tb %d r %d ", r->r_table, r->r_action);
switch (r->r_action) { 策略所表示操作
case RTN_UNICAST: 单目转发
case RTN_NAT: 地址变换转发
policy = r; 获取匹配的策略
break;
case RTN_UNREACHABLE: 不可达的转发
read_unlock(&fib_rules_lock);
return -ENETUNREACH;
default:
case RTN_BLACKHOLE: 转发到黑洞
read_unlock(&fib_rules_lock);
return -EINVAL;
case RTN_PROHIBIT: 禁止转发
read_unlock(&fib_rules_lock);
return -EACCES;
}
if ((tb = fib_get_table(r->r_table)) == NULL) 取策略对应的转发表
continue;
err = tb->tb_lookup(tb, key, res); 查询转发表
if (err == 0) { 查询成功
res->r = policy; 在转发信息中设置该策略表地址
if (policy)
atomic_inc(&policy->r_clntref); 引用策略表
read_unlock(&fib_rules_lock);
return 0;
}
if (err < 0 && err != -EAGAIN) {
read_unlock(&fib_rules_lock);
return err; 错误
}
}
FRprintk("FAILURE\n");
read_unlock(&fib_rules_lock);
return -ENETUNREACH;
}
static inline struct fib_table *fib_get_table(int id)
{
if (id == 0)
id = RT_TABLE_MAIN;
return fib_tables[id];
}
static inline struct fib_table *fib_new_table(int id)
{
if (id == 0) 0号转发表为主转发表
id = RT_TABLE_MAIN;
return fib_tables[id] ? : __fib_new_table(id);
}
struct fib_table *__fib_new_table(int id)
{
struct fib_table *tb;
tb = fib_hash_init(id);
if (!tb)
return NULL;
fib_tables[id] = tb;
return tb;
}
/* Reserved table identifiers */
enum rt_class_t
{
RT_TABLE_UNSPEC=0,
/* User defined values */
RT_TABLE_DEFAULT=253,
RT_TABLE_MAIN=254,
RT_TABLE_LOCAL=255
};
#define RT_TABLE_MAX RT_TABLE_LOCAL
struct fib_table *fib_tables[RT_TABLE_MAX+1]; 转发表数组
#ifdef CONFIG_IP_MULTIPLE_TABLES
struct fib_table * fib_hash_init(int id)
#else
struct fib_table * __init fib_hash_init(int id)
#endif
{
struct fib_table *tb;
if (fn_hash_kmem == NULL)
fn_hash_kmem = kmem_cache_create("ip_fib_hash",
sizeof(struct fib_node),
0, SLAB_HWCACHE_ALIGN,
NULL, NULL);
tb = kmalloc(sizeof(struct fib_table) + sizeof(struct fn_hash), GFP_KERNEL);
if (tb == NULL) 分配转发表操作结构和索引结构
return NULL;
tb->tb_id = id;
tb->tb_lookup = fn_hash_lookup; 检索转发表
tb->tb_insert = fn_hash_insert; 插入转发表记录
tb->tb_delete = fn_hash_delete; 删除转发表记录
tb->tb_flush = fn_hash_flush; 洗刷转发表无效记录
tb->tb_select_default = fn_hash_select_default; 选取缺省的转发信息
#ifdef CONFIG_RTNETLINK
tb->tb_dump = fn_hash_dump; 向链路套接字倾倒转发表记录
#endif
#ifdef CONFIG_PROC_FS
tb->tb_get_info = fn_hash_get_info; 从PROC文件系统显示转发表信息
#endif
memset(tb->tb_data, 0, sizeof(struct fn_hash)); 清除散列盘
return tb;
}
static int
fn_hash_lookup(struct fib_table *tb, const struct rt_key *key, struct fib_result *res)
{
int err;
struct fn_zone *fz;
struct fn_hash *t = (struct fn_hash*)tb->tb_data;
read_lock(&fib_hash_lock);
for (fz = t->fn_zone_list; fz; fz = fz->fz_next) { 扫描网络区
struct fib_node *f;
fn_key_t k = fz_key(key->dst, fz); 取目标地址在该网络区的网络号
for (f = fz_chain(k, fz); f; f = f->fn_next) { 扫描该网络号的散列链
if (!fn_key_eq(k, f->fn_key)) { 如果与链节的网络号不相等
if (fn_key_leq(k, f->fn_key)) 如果网络号比链节的网络号小(散列链是按网络号从小到大排序的)
break; 下一转发区
else
continue; 下一链节
}
; 搜索到网络号相等的节点
#ifdef CONFIG_IP_ROUTE_TOS
if (f->fn_tos && f->fn_tos != key->tos) 如果它们的服务类型不等
continue; 下一链节
#endif
f->fn_state |= FN_S_ACCESSED; 访问标记
if (f->fn_state&FN_S_ZOMBIE) 如果该节点无效
continue; 下一链节
if (f->fn_scope < key->scope) 如果该节点的转发域值小于请求的转发域值(或者说该节点的转发域大于请求的转发域)
continue; 下一链节
err = fib_semantic_match(f->fn_type, FIB_INFO(f), key, res); 进一步与节点转发信息匹配
if (err == 0) { 匹配成功
res->type = f->fn_type; 输出转发类型
res->scope = f->fn_scope; 输出转发域
res->prefixlen = fz->fz_order; 输出该区网络地址所占位长
goto out; 返回
}
if (err < 0)
goto out;
}
}
err = 1;
out:
read_unlock(&fib_hash_lock);
return err;
}
static struct
{
int error;
u8 scope;
} fib_props[RTA_MAX+1] = {
{ 0, RT_SCOPE_NOWHERE}, /* RTN_UNSPEC */ 无法转发
{ 0, RT_SCOPE_UNIVERSE}, /* RTN_UNICAST */ 全网范围转发
{ 0, RT_SCOPE_HOST}, /* RTN_LOCAL */ 本机范围转发
{ 0, RT_SCOPE_LINK}, /* RTN_BROADCAST */ 设备范围转发
{ 0, RT_SCOPE_LINK}, /* RTN_ANYCAST */
{ 0, RT_SCOPE_UNIVERSE}, /* RTN_MULTICAST */
{ -EINVAL, RT_SCOPE_UNIVERSE}, /* RTN_BLACKHOLE */
{ -EHOSTUNREACH, RT_SCOPE_UNIVERSE},/* RTN_UNREACHABLE */
{ -EACCES, RT_SCOPE_UNIVERSE}, /* RTN_PROHIBIT */
{ -EAGAIN, RT_SCOPE_UNIVERSE}, /* RTN_THROW */
#ifdef CONFIG_IP_ROUTE_NAT
{ 0, RT_SCOPE_HOST}, /* RTN_NAT */
#else
{ -EINVAL, RT_SCOPE_NOWHERE}, /* RTN_NAT */
#endif
{ -EINVAL, RT_SCOPE_NOWHERE} /* RTN_XRESOLVE */
};
#define FIB_RES_NH(res) ((res).fi->fib_nh[(res).nh_sel])
#define FIB_RES_RESET(res) ((res).nh_sel = 0)
#define for_nexthops(fi) { int nhsel; const struct fib_nh * nh; \
for (nhsel=0, nh = (fi)->fib_nh; nhsel < (fi)->fib_nhs; nh++, nhsel++)
int
fib_semantic_match(int type, struct fib_info *fi, const struct rt_key *key, struct fib_result *res)
{
int err = fib_props[type].error; 取转发类型错误码
if (err == 0) { 允许的转发类型
if (fi->fib_flags&RTNH_F_DEAD) 如果该转发节点不通
return 1;
res->fi = fi; 输出转发节点信息
switch (type) {
#ifdef CONFIG_IP_ROUTE_NAT
case RTN_NAT: 地址变换转发
FIB_RES_RESET(*res); 复位转发地址选择编号
atomic_inc(&fi->fib_clntref);
return 0;
#endif
case RTN_UNICAST: 单目转发
case RTN_LOCAL: 本地转发
case RTN_BROADCAST: 广播转发
case RTN_ANYCAST: 任意转发
case RTN_MULTICAST: 多目转发
for_nexthops(fi) { 对于转发信息中的每一个转发地址
if (nh->nh_flags&RTNH_F_DEAD) 如果转发地址不通
continue; 下一转发地址
if (!key->oif || key->oif == nh->nh_oif) 匹配转发地址的输出设备
break; 匹配成功
}
#ifdef CONFIG_IP_ROUTE_MULTIPATH 多径路由
if (nhsel < fi->fib_nhs) {
res->nh_sel = nhsel; 输出转发地址编号
atomic_inc(&fi->fib_clntref);
return 0; 成功返回
}
#else
if (nhsel < 1) { 非多径路由转发地址编号必须小于1
atomic_inc(&fi->fib_clntref);
return 0; 成功返回
}
#endif
endfor_nexthops(fi);
res->fi = NULL;
return 1; 匹配失败
default:
res->fi = NULL;
printk(KERN_DEBUG "impossible 102\n");
return -EINVAL;
}
}
return err;
}
linux内核路由转发表的组成,linux路由转发表的检索过程(fib_lookup)相关推荐
- Linux内核--网络栈实现分析(二)--数据包的传递过程--转
转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的&qu ...
- 【Linux 内核】Linux 操作系统结构 ( Linux 内核在操作系统中的层级 | Linux 内核子系统及关系 | 进程调度 | 内存管理 | 虚拟文件系统 | 网络管理 | 进程间通信 )
文章目录 一.Linux 内核在操作系统中的层级 二.Linux 内核子系统 三.Linux 内核子系统之间的关系 一.Linux 内核在操作系统中的层级 Linux 内核 所在层级 : 整个计算机系 ...
- Linux内核分析(三)----初识linux内存管理子系统
原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...
- linux内核分成如下五个子系统,linux内核主要由5个子系统 Linux内核由哪几个子系统组成?...
1, Linux内核由哪几个子系统组成? Linux内核主要由五个子系统组成:进程调度,内存管理,虚拟文件系统,网络接口,进程间通信.1.进程调度(SCHED):控制进程对CPU的访问.当需要选择下一 ...
- Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理
为什么80%的码农都做不了架构师?>>> Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理 本书使用高分辨率精心绘制的300多张图片,帮助大家理解操作系统特有 ...
- 给linux内核传递数组,数组与指针 - Linux C编程实战之路_Linux编程_Linux公社-Linux系统门户网站...
谈到C语言编程,数组和指针是很多人的心头大石,总觉得它们是重点难点,重点是没错的,但绝不是什么难点,要说C语言的难点,客观地讲应该是带参宏,而数组和指针,概念浅显易懂,操作简洁方便,根本不是很多初学者 ...
- linux内核锁死怎么解决_解决Linux内核中的2038年问题
linux内核锁死怎么解决 由于时间在Linux中的表示方式,带符号的32位数字无法支持20:38(UTC)3:14:07之后的时间. 2038年 (Y2038或Y2K38)问题是关于时间数据类型表示 ...
- iso linux 内核版本号_Linux发行版和Linux内核的区别
Linux内核版本与linux发行版本的区别 LINUX内核版本 是指系统内核的版本号.LINUX的内核具有两种不同的版本号,实验版本和产品化版本.要确定 LINUX版本 的类型,只要查看一下版本号: ...
- Linux内核学习之路_1_编译Linux内核
1.准备工作 1.1 学习环境 1.2 下载Linux内核源码 1.3 解压Linux内核 1.4 目录结构介绍 1.2.2 Linux内核配置 1.1 学习环境 本系列教程使用的环境如下: 操作系统 ...
- 图解linux内核设计艺术,Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理...
Linux内核设计的艺术:图解Linux操作系统架构设计与实现原理 作者:新设计团队 出版日期:2011年05月 文件大小:29.01M 支持设备: ¥6.00在线试读 适用客户端: 言商书局 iPa ...
最新文章
- 10个你必须知道的ios框架
- Tomcat、Websphere和Jboss类加载机制
- Cloudstack介绍(一)
- mysql双向复制(主主模式)
- BZOJ[1713][Usaco2007 China]The Bovine Accordion and Banjo Orchestra 音乐会 二维斜率优化
- uat环境是什么环境_环境污染会对环境造成怎样的损害?污水处理活性炭能起到什么效果呢?...
- 整数划分递归相关问题
- 详解spl_autoload_register()函数
- 【STM32】随机数发生器相关函数和类型
- python时间差怎么转换为数字_pandas进行时间数据的转换和计算时间差并提取年月日...
- 18 MM配置-BP业务伙伴-定义业务伙伴角色分组
- MFC开发IM-自绘按钮控件,给按钮设置背景图片
- WebRTC之SDP篇
- 计算机无法添加本地策略组,电脑打不开本地组策略编辑器,求破~!
- VC获取系统临时文件夹temp
- 访问页面升级访问_BPC页面访问缓慢无报错
- blp和biba属于哪种访问控制_CISP官方信息安全技术章节练习二
- Verilog中repeat的用法
- mysql多对一[自用]
- python值得学习吗?