免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 906 | 回复: 0
打印 上一主题 下一主题

转发表的检索过程(fib_lookup) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-05-19 16:43 |只看该作者 |倒序浏览

              
转发表的检索过程(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 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 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 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 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 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 fib_nhs) {
                                res->nh_sel =         nhsel; 输出转发地址编号
                                atomic_inc(&fi->fib_clntref);
                                return 0; 成功返回
                        }
#else
                        if (nhsel 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;
}
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/39518/showart_694517.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP