linux之网络子系统-路由子系统(3)路由表

news2024/10/24 18:34:08

一、路由表

linux 路由子系统代码量虽说不是很多,但是难度还是有的,最近在分析路由子系统这一块,对它的框架有了基本的了解。

路由子系统可以划分为三个部分:路由缓存、路由策略、路由表。前两个部分已经分析完,这里分析路由表的源码。路由表和其它模块类似,都有初始化、添加、删除、查询等操作,要说区别吧,可以就是数据结构不同,不同的数据结构就需要不同的算法。

看《深入理解linux网络计数内幕》这本书路由子模块,它介绍的路由表是基于hash表来组织的,但是新版本的内核已经修改为 lpc-trie 树来组织, lpc-trie树,网上简称 字典树, lpc表示 path compression(路径压缩),level compression(平面压缩),路由表的添加、删除、查找都是基于该树实现,具体的实现还是蛮复杂的,先看一下它的组织结构:

上面左边部分fib_table_hash 就表示 路由表hash 数组, hash值就是路由表ID,每个路由表都有一个fib_table 结构体表示,这个结构体尾部存放一个占位指针,用来指向路由trie树,树中由很多中间节点和叶子节点,中间节点的结构体为 tnode ,叶子节点为 leaf, 无论是中间节点还是叶子节点,都含有一个key 值,该值就是ipv4地址,同一条路径上的节点拥有相同的前缀,比如1.1.1.1 和1.1.1.2, leaf_info 包含了子网掩码长度,fib_alias 包含了路由项里面的tos等信息, fib_alias 指向 fib_info, 这里面也包含了路由信息,fib_nh用来保存下一跳网关信息,可以看到,一个路由项由多个数据结构组成,之所以用这么多结构体而不是用一个超大的结构体是因为 路由里面很多信息是可路由项分割成多个块,相同的块可以共享,有一点需要注意,每个路由项都有一个唯一的fib_alias 结构体。

路由表初始化流程就是申请缓存、注册netlink消息处理函数:

inet_init()->

               ip_init()->

                        ip_rt_init()->

                                ip_fib_init()->

                                        fib_trie_init->

                                        fib_net_init->

                                                ip_fib_net_init (申请net->ipv4.fib_table_hash  的内存)

                                                        

路由初始化主函数:

//kernel/net/ipv4/route.c

int __init ip_rt_init(void)
{
        void *idents_hash;
        int cpu;

....

        ipv4_dst_ops.gc_thresh = ~0;
        ip_rt_max_size = INT_MAX;

        devinet_init();
        ip_fib_init(); // IP层 路由表相关的初始化
.....

}

接着ip_fib_init():

//kernel/net/ipv4/fib_frontend.c

void __init ip_fib_init(void)
{
        fib_trie_init(); //初始化路由用到的缓存池
        
        //初始化路由表系统的操作函数,即注册路由表和缓存
        register_pernet_subsys(&fib_net_ops);

        //注册通知链处理函数,监听系统其他的模块
        register_netdevice_notifier(&fib_netdev_notifier);
        register_inetaddr_notifier(&fib_inetaddr_notifier);

        //注册netlink 路由添加、删除、和dump 命令处理函数
        rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0);
        rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0);
        rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, 0);
}

当使用ip route add 添加路由时 会通过netlink 将信息下发到内核,然后调用路由系统注册的netlink 处理函数,这里就是inet_rtm_newroute ,该函数先检查参数的合理性,通过后则添加到对应的trie路由树中,没有指定路由表id 的话,默认添加到main 表。

fib_net_ops 是个函数集,在子系统启动过程中会被调用:

static struct pernet_operations fib_net_ops = {
        .init = fib_net_init,
        .exit = fib_net_exit,
};

fib_net_init:

//kernel/net/ipv4/fib_frontend.c

static int __net_init fib_net_init(struct net *net)
{
        int error;

#ifdef CONFIG_IP_ROUTE_CLASSID
        atomic_set(&net->ipv4.fib_num_tclassid_users, 0);
#endif
         //初始化路由缓存和策略
        error = ip_fib_net_init(net);
        if (error < 0)
                goto out;

        //创建netlink
        error = nl_fib_lookup_init(net);
        if (error < 0)
                goto out_nlfl;
        // 创建proc 文件
        error = fib_proc_init(net);
        if (error < 0)
                goto out_proc;
out:
        return error;

out_proc:
        nl_fib_lookup_exit(net);
out_nlfl:
        ip_fib_net_exit(net);
        goto out;
}

ip_fib_net_init 函数:路由表缓存的申请

//kernel/net/ipv4/fib_frontend.c


static int __net_init ip_fib_net_init(struct net *net)
{
        int err;
        size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ;
        
        err = fib4_notifier_init(net);
        if (err)
                return err;

        /* Avoid false sharing : Use at least a full cache line */
        size = max_t(size_t, size, L1_CACHE_BYTES);
        // 创建路由表缓存
        net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
        if (!net->ipv4.fib_table_hash) {
                err = -ENOMEM;
                goto err_table_hash_alloc;
        }
        //初始化策略路由和路由表
        err = fib4_rules_init(net);
        if (err < 0)
                goto err_rules_init;
        return 0;

err_rules_init:
        kfree(net->ipv4.fib_table_hash);
err_table_hash_alloc:
        fib4_notifier_exit(net);
        return err;
}

 上述就是路由表初始化的过程。

二、路由表如何添加

一般情况下应用层添加有两种手段,一种是使用ip route 添加,一种是使用route 添加,虽然都是添加路由,但是它俩和路由系统通信机制不一样,前者是使用netlink, 后者使用ioctl 。 看一下ip route 命令添加的时候,该命令将参数通过netlink传递给内核的netlink模块,然后调用相应的事件处理函数,添加的时候调用的函数是 inet_rtm_newroute :

 //kernel/net/ipv4/fib_frontend.c

static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh,
                             struct netlink_ext_ack *extack)
{
        struct net *net = sock_net(skb->sk);
        struct fib_config cfg;
        struct fib_table *tb;
        int err;
      //将用户配置信息转换成fib_config 内核可识别的信息
        err = rtm_to_fib_config(net, skb, nlh, &cfg, extack);
        if (err < 0)
                goto errout;
        //如果指定ID的路由表存在则返回该表,不存在则建立
        tb = fib_new_table(net, cfg.fc_table);
        if (!tb) {
                err = -ENOBUFS;
                goto errout;
        }

        //插入路由
        err = fib_table_insert(net, tb, &cfg, extack);
        if (!err && cfg.fc_type == RTN_LOCAL)
                net->ipv4.fib_has_custom_local_routes = true;
errout:
        return err;
}

通过调用 :route 的ioctl 通信机制‘


//kernel/net/ipv4/fib_frontend.c

/*
 * Handle IP routing ioctl calls.
 * These are used to manipulate the routing tables
 */
int ip_rt_ioctl(struct net *net, unsigned int cmd, struct rtentry *rt)
{
        struct fib_config cfg;
        int err;

        switch (cmd) {
        case SIOCADDRT:         /* Add a route */ //添加路由
        case SIOCDELRT:         /* Delete a route */ //删除路由
                if (!ns_capable(net->user_ns, CAP_NET_ADMIN))
                        return -EPERM;

                rtnl_lock();
                //复制应用层数据,将数据转换成路由子系统可识别的结构体
                err = rtentry_to_fib_config(net, cmd, rt, &cfg);
                if (err == 0) {
                        struct fib_table *tb;

                        if (cmd == SIOCDELRT) { //删除操作
                                tb = fib_get_table(net, cfg.fc_table);
                                if (tb)
                                        err = fib_table_delete(net, tb, &cfg,
                                                               NULL);
                                else
                                        err = -ESRCH;
                        } else {
                                tb = fib_new_table(net, cfg.fc_table);//添加路由
                                if (tb)
                                        err = fib_table_insert(net, tb,
                                                               &cfg, NULL);
                                else
                                        err = -ENOBUFS;
                        }

                        /* allocated by rtentry_to_fib_config() */
                        kfree(cfg.fc_mx);
                }
                rtnl_unlock();
                return err;
        }
        return -EINVAL;
}

在系统中执行的结果对比:

可以看到添加操作都是调用fib_table_insert 操作,该操作就是对trie路由树进行添加和删除。

三、路由表如何查询

系统查询路由表的地方通常是两个地方:一个是接收报文,一个是发送报文的时候。

当然查找路由不一定是非要查找路由表,首先是查找路由缓存,没有命中,再查找路由策略,根据路由策略再查找路由表。

接收报文时:

ip_rcv()->

             ip_rcv_finish()->

                        ip_rcv_finish_core()->

                                ip_route_input_noref->

                                        ip_route_input_rcu->

                                                ip_route_input_slow->

                                                                        fib_lookup->

                                                                                __fib_lookup->

                                                                                                fib_rules_lookup->

                                                                                                        fib4_rule_action->

                                                                                                                        fib_table_lookup

 发送报文时:

tcp_sendmsg/udp_sendmsg->

                        ip_route_output_flow->

                                __ip_route_output_key->

                                        ip_route_output_key_hash_rcu->

                                                                                        fib_lookup->

                                                                                                fib_rules_lookup->                                                                                                       

                                                                                                        fib4_rule_action->

                                                                                                                        fib_table_lookup

从上面流程可以看出,收发报文都是调用fib_table_lookup函数查找路由表,这个函数就是在trie树中查找匹配项。查找流程比较复杂,目前没有实力分析源码。后面再补充吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2222615.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解决Vmware自动调整分辨率的问题

打开菜单&#xff1a;虚拟机→设置→硬件→显示器→显示缩放比例→勾选拉伸模式 Virtual Machine

【Linux】————磁盘与文件系统

作者主页&#xff1a; 作者主页 本篇博客专栏&#xff1a;Linux 创作时间 &#xff1a;2024年10月17日 一、磁盘的物理结构 磁盘的物理结构如图所示&#xff1a; 其中具体的物理存储结构如下&#xff1a; 磁盘中存储的基本单位为扇区&#xff0c;一个扇区的大小一般为512字…

【python爬虫实战】爬取全年天气数据并做数据可视化分析!附源码

由于篇幅限制&#xff0c;无法展示完整代码&#xff0c;需要的朋友可在下方获取&#xff01;100%免费。 一、主题式网络爬虫设计方案 1. 主题式网络爬虫名称&#xff1a;天气预报爬取数据与可视化数据 2. 主题式网络爬虫爬取的内容与数据特征分析&#xff1a; - 爬取内容&am…

【软件工程】软件工程入门

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;软件开发必练内功_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前…

多模态大语言模型(MLLM)-Blip3/xGen-MM

论文链接&#xff1a;https://www.arxiv.org/abs/2408.08872 代码链接&#xff1a;https://github.com/salesforce/LAVIS/tree/xgen-mm 本次解读xGen-MM (BLIP-3): A Family of Open Large Multimodal Models 可以看作是 [1] Blip: Bootstrapping language-image pre-training…

uniapp:uni.createSelectorQuery函数结合vue的watch函数使用实例

提醒 本文实例是使用uniapp进行开发演示的。 一、需求场景 在开发详情页面时&#xff0c;不同产品描述文案不同&#xff0c;有的文案比较长&#xff0c;需求上要求描述文案最多展示4行文案&#xff0c;少于4行文案&#xff0c;全部显示&#xff0c;此UI高度自动适配&#xff0c…

智慧城管综合管理系统源码,微服务架构,基于springboot、vue+element+uniapp技术开发,支持二次开发

智慧城管源码&#xff0c;智慧城管执法办案系统源码 智慧城管综合执法办案平台是智慧城市框架下&#xff0c;依托物联网、云计算、多网融合等现代化技术&#xff0c;运用数字基础资源、多维信息感知、协同工作处置、智能化辅助决策分析等手段&#xff0c;形成具备高度感知、互联…

pikachu靶场-Cross-Site Scripting(XSS)

sqli-labs靶场安装以及刷题记录-dockerpikachu靶场-Cross-Site Scripting pikachu靶场的安装刷题记录反射型xss(get)反射型xss(post)存储型xssDOM型xssDOM型xss-xxss盲打xss之过滤xss之htmlspecialcharsxss之href输出xss之js输出 pikachu靶场的安装 刷题记录 反射型xss(get) …

《什么是大模型、超大模型和 Foundation Model?》

前言 大模型旨在解决人类面临的各种问题,提高人类的生产力和生活质量。是一门涉及计算机科学、数学、哲学、心理学等多个领域的交叉学科,旨在研究如何使计算机能够像人类一样思考、学习、推理和创造。大模型的出现,让很多产业人士认为这项技术会改变信息产业格局,即基于数…

解码专业术语——应用系统开发项目中的专业词汇解读

文章目录 引言站点设置管理具体要求包括&#xff1a; Footer管理基于URL的权限控制利用数据连接池优化数据库操作什么是数据连接池&#xff1f;优化的优势 利用反射改造后端代码&#xff0c;AJAX反射的作用及其在后端代码中的应用AJAX 实现前后端无刷新交互 引言 创新实践项目二…

ThingsBoard规则链节点:Delete Attributes节点详解

引言 删除属性节点简介 用法 含义 应用场景 实际项目运用示例 智能家居安全系统 物流跟踪解决方案 工业自动化生产线 结论 引言 ThingsBoard是一个开源的物联网平台&#xff0c;它提供了设备管理、数据收集与处理以及实时监控等功能。其中&#xff0c;规则引擎是其核心…

Clickhouse 笔记(一) 单机版安装并将clickhouse-server定义成服务

ClickHouse 是一个高性能的列式数据库管理系统&#xff08;DBMS&#xff09;&#xff0c;主要用于在线分析处理&#xff08;OLAP&#xff09;场景。它由俄罗斯搜索引擎公司 Yandex 开发&#xff0c;并在 2016 年开源。ClickHouse 以其卓越的查询性能和灵活的扩展性而闻名&#…

模拟信号采集显示器+GPS同步信号发生器制作全过程(焊接、问题、代码、电路)

1、制作最小系统板 在制作最小系统板的时候&#xff0c;要用USB转TTL给板子供电&#xff0c;留了一个电源输入的四个接口&#xff0c;同时又用排针引出来VCC和GND用于后续其他外设的电源供应&#xff0c;电源配有电源指示灯和保护电容&#xff0c; 当时在焊接的时候把接口处的…

云计算实验1——基于VirtualBox的Ubuntu安装和配置

实验步骤 1、VirtualBox的安装 本实验使用VirtualBox-7.0.10 进行演示。对于安装包&#xff0c;大家可以前往 VirtualBox官网下载页面(https :/ / www. virtualbox.org/wiki/Downloads)下载其7.0版本安装包进行安装&#xff0c;或者直接使用QQ群的安装包VirtualBox-7.0.10-15…

基于开源Jetlinks物联网平台协议包-MQTT自定义主题数据的编解码

目录 前言 1.下载官方协议包 2.解压 3.自定义主题 4.重写解码方法 5.以下是我解析后接收到的数据 前言 最近这段时间&#xff0c;一直在用开源的Jetlinks物联网平台在学习&#xff0c;偶尔有一次机会接触到物联网设备对接&#xff0c;在协议对接的时候&#xff0c;遇到了…

Spring面试题——第五篇

1. Spring的优点 轻量级和非侵入性&#xff1a;不需要引入大量的依赖和配置。面向切面编程&#xff1a;Spring提供了强大的面向切面编程&#xff0c;允许用户定义横切关注点&#xff0c;并将其与核心业务逻辑分离&#xff0c;提高了灵活性。依赖注入&#xff08;DI&#xff09…

java对接钉钉发送消息(纯萌新文档解惑)

java对接钉钉&#xff08;纯萌新文档解惑&#xff09; 注意&#xff1a;不是其他直接给你个写好的钉钉工具类&#xff0c;但不知道它怎么来的。是以钉钉官方文档为准&#xff0c;流程是什么&#xff0c;你想要什么可以自己在文档找&#xff08;所有文档都有只是萌新看着懵&…

Kafka高可用性原理深度解析

在分布式系统中&#xff0c;高可用&#xff08;High Availability, HA&#xff09;是指系统在面对硬件故障、网络分区、软件崩溃等异常情况时&#xff0c;仍能继续提供服务的能力。对于消息队列系统而言&#xff0c;高可用性尤为重要&#xff0c;因为它通常作为数据流通的中枢&…

SSD | (十)PCIe介绍(上)

文章目录 📚从PCIe的速度说起📚PCIe拓扑结构🐇PCI——总线型拓扑结构🐇PCIe——树形拓扑结构📚PCIe分层结构📚PCIe TLP类型📚PCIe TLP结构🐇通用结构🐇具体TLP的Header📚从PCIe的速度说起 PCIe发展至今,速度一代比一代快。 连接速度所示1、2等是指PCIe链接…

Python 打包成 EXE 的方法详解

#1024程序员节&#xff5c;征文# 日常开发中&#xff0c;python由于其便捷性成为了很多人的首选语言&#xff0c;但是python的环境配置也是有点麻烦的&#xff0c;那么我们如何让其变得更加友好呢&#xff1f;没错&#xff0c;就是打包成exe可执行文件。 一、PyInstaller 简介…