linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加
上一节分析了策略规则相关的数据结构,本节就分析一下策略规则的添加。对于策略规则的功能模块,由于 v4 、 v6 都有用到,所以该模块也和邻居模块一样,抽象出了通用规则的接口函数,然后根据传入的参数来进入协议相关的策略规则的接口函数,即分为通用接口
上一节分析了策略规则相关的数据结构,本节就分析一下策略规则的添加。对于策略规则的功能模块,由于v4、v6都有用到,所以该模块也和邻居模块一样,抽象出了通用规则的接口函数,然后根据传入的参数来进入协议相关的策略规则的接口函数,即分为通用接口函数与协议相关的接口函数。
1.通用规则的添加及处理流程
应用层主要是通过netlink模块实现与内核中策略规则模块通信,从而实现策略规则的添加、删除等操作(本文不关注netlink接口实现,仅介绍策略规则功能模块相关的接口)。
1.1 fib_nl_newrule
不管是v4还是v6,在内核中添加一个规则,首先就会调用通用接口函数fib_nl_newrule,然后由该函数找到协议相关的接口函数,再调用协议相关的接口函数,实现策略规则的添加。
下面我们就分析一下这个函数,这个函数主要执行一下功能:
1.根据应用层传递的协议类型,查找到相应注册的struct fib_rules_ops类型的变量
对于ipv4而言,就是fib4_rules_ops
2. 对应用层传递的值进行解析,并对传入的源或者目的地址值进行合理性检查
3.创建一个新的fib rule缓存,并对优先级、接口index、接口名称、fwmark、action、table_id进行设置
4.调用协议对应的configure函数,对fib rule的源、目的ip、tos等值进行配置
5.增加此fib rule的引用计数,并根据优先级将新的fib rule插入到协议相关的rules_list链表对应的位置
int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
{
struct fib_rule_hdr *frh = nlmsg_data(nlh);
struct fib_rules_ops *ops = NULL;
struct fib_rule *rule, *r, *last = NULL;
struct nlattr *tb[FRA_MAX+1];
int err = -EINVAL;
if (nlh->nlmsg_len
goto errout;
/*对于ipv4而言,获取到的值即为fib4_rules_ops*/
ops = lookup_rules_ops(frh->family);
if (ops == NULL) {
err = EAFNOSUPPORT;
goto errout;
}
/*对应用层传递的参数进行解析,并存放在tb中*/
err = nlmsg_parse(nlh, sizeof(*frh), tb, FRA_MAX, ops->policy);
if (err
goto errout;
/*调用validate_rulemsg对传入的源或者目的地址值进行合理性检查*/
err = validate_rulemsg(frh, tb, ops);
if (err
goto errout;
/*
创建一个struct fib_rule *类型的变量
*/
rule = kzalloc(ops->rule_size, GFP_KERNEL);
if (rule == NULL) {
err = -ENOMEM;
goto errout;
}
/*
根据应用层传递的参数,对优先级进行设置
*/
if (tb[FRA_PRIORITY])
rule->pref = nla_get_u32(tb[FRA_PRIORITY]);
/*
根据应用层传递的参数,决定是否需要设置fib rule的ifindex值
*/
if (tb[FRA_IFNAME]) {
struct net_device *dev;
rule->ifindex = -1;
nla_strlcpy(rule->ifname, tb[FRA_IFNAME], IFNAMSIZ);
dev = __dev_get_by_name(rule->ifname);
if (dev)
rule->ifindex = dev->ifindex;
}
/*
设置fib rule的fwmark,实际应用中可以根据这个值决定路由表的选择,即实现
策略路由的功能
*/
if (tb[FRA_FWMARK]) {
rule->mark = nla_get_u32(tb[FRA_FWMARK]);
if (rule->mark)
/* compatibility: if the mark value is non-zero all bits
* are compared unless a mask is explicitly specified.
*/
rule->mark_mask = 0xFFFFFFFF;
}
/*
设置fwmark的掩码值
*/
if (tb[FRA_FWMASK])
rule->mark_mask = nla_get_u32(tb[FRA_FWMASK]);
/*设置规则的action以及与该规则关联的路由表id,实现规则与路由表的关联*/
rule->action = frh->action;
rule->flags = frh->flags;
rule->table = frh_get_table(frh, tb);
/*
当没有为策略规则配置优先级也没有默认优先级时,则会调用该协议对应
的default_pref获取一个默认的优先级.
对于ipv4,其default_pref的原理是获取规则链表中非0优先级中的最高优先级,
即获取的默认优先级是除0优先级的规则外,最高的优先级。
*/
if (!rule->pref && ops->default_pref)
rule->pref = ops->default_pref();
/*
调用该协议对应的configure,该该策略进行配置
*/
err = ops->configure(rule, skb, nlh, frh, tb);
if (err
goto errout_free;
/*
遍历规则链表,找到第一个pref值比当前刚创建的fib rule的pref值大的fib rule:
若找到符合要求的规则,则将新创建的策略规则添加到符合要求的规则
之前;
若在搜索完整个链表仍没有找到符合要求的规则,则将该规则添加到
当前链表已有规则的租后,即链尾。
*/
list_for_each_entry(r, ops->rules_list, list) {
if (r->pref > rule->pref)
break;
last = r;
}
fib_rule_get(rule);
if (last)
list_add_rcu(&rule->list, &last->list);
else
list_add_rcu(&rule->list, ops->rules_list);
notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).pid);
rules_ops_put(ops);
return 0;
errout_free:
kfree(rule);
errout:
rules_ops_put(ops);
return err;
}
1.2 lookup_rules_ops
在上面的函数中,在根据协议簇查找相应的已注册的协议相关的fib_rules_ops变量时,调用了函数lookup_rules_ops来实现的,下面就分析一下这个函数
功能:根据协议簇在链表rules_ops中查找符合要求的struct fib_rules_ops类型的
变量对于ipv4而言,即为fib4_rules_ops
static struct fib_rules_ops *lookup_rules_ops(int family)
{
struct fib_rules_ops *ops;
rcu_read_lock();
list_for_each_entry_rcu(ops, &rules_ops, list) {
if (ops->family == family) {
if (!try_module_get(ops->owner))
ops = NULL;
rcu_read_unlock();
return ops;
}
}
rcu_read_unlock();
return NULL;
}
接着就调用函数validate_rulemsg,对于应用层传递的参数进行了合法性检查,下面就分析一下这个函数
1.3 validate_rulemsg
这个函数主要判断frh与tb中的源、目的地址相关的参数是否合法,下面分析下这个函数的执行流程:
1.若源地址的长度不为0时,若tb[FRA_SRC]中为空,或者传入的地址
长度与相应协议规定的地址长度不等,或者实际传递的地址的
实际长度与相应协议规定的地址长度不等时,则返回-EINVAL。
1.若目的地址的长度不为0时,若tb[FRA_SRC]中为空,或者传入的地址
长度与相应协议规定的地址长度不等,或者实际传递的地址的
实际长度与相应协议规定的地址长度不等时,则返回-EINVAL。
static int validate_rulemsg(struct fib_rule_hdr *frh, struct nlattr **tb,
struct fib_rules_ops *ops)
{
int err = -EINVAL;
if (frh->src_len)
if (tb[FRA_SRC] == NULL ||
frh->src_len > (ops->addr_size * 8) ||
nla_len(tb[FRA_SRC]) != ops->addr_size)
goto errout;
if (frh->dst_len)
if (tb[FRA_DST] == NULL ||
frh->dst_len > (ops->addr_size * 8) ||
nla_len(tb[FRA_DST]) != ops->addr_size)
goto errout;
err = 0;
errout:
return err;
}
接着就是调用协议相关的函数,进行协议相关的配置操作了,下面我们就分析之
2.协议相关的添加处理流程
本节以ipv4为例,在函数fib_nl_newrule中,通过ops->configure函数调用了协议相关的配置函数,而对于ipv4而言,即是函数fib4_rule_configure。下面就分析一下这个函数。
2.1 fib4_rule_configure
当新增加一个ipv4的fib rule规则时,就会调用该函数,对
新创建的struct fib4_rule类型的变量进行初始化操作,即根据
应用层传递的配置参数,设置struct fib4_rule类型的变量的
源ip地址、源ip的掩码值、目的ip地址、目的ip的掩码值、
路由表id、tos等
static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb,
struct nlmsghdr *nlh, struct fib_rule_hdr *frh,
struct nlattr **tb)
{
int err = -EINVAL;
struct fib4_rule *rule4 = (struct fib4_rule *) rule;
if (frh->tos & ~IPTOS_TOS_MASK)
goto errout;
if (rule->table == RT_TABLE_UNSPEC) {
if (rule->action == FR_ACT_TO_TBL) {
struct fib_table *table;
/*
若应用层没有设置路由表的id,则调用fib_empty_table创建
一个新的路由表,并将新创建的路由表的id传递
给rule->table
(使用如下命令,即会使系统创建一个新的路由表
# ip rule add from 192.168.192.1 table 0)
*/
table = fib_empty_table();
if (table == NULL) {
err = -ENOBUFS;
goto errout;
}
rule->table = table->tb_id;
}
}
if (frh->src_len)
rule4->src = nla_get_be32(tb[FRA_SRC]);
if (frh->dst_len)
rule4->dst = nla_get_be32(tb[FRA_DST]);
#ifdef CONFIG_NET_CLS_ROUTE
if (tb[FRA_FLOW])
rule4->tclassid = nla_get_u32(tb[FRA_FLOW]);
#endif
rule4->src_len = frh->src_len;
rule4->srcmask = inet_make_mask(rule4->src_len);
rule4->dst_len = frh->dst_len;
rule4->dstmask = inet_make_mask(rule4->dst_len);
rule4->tos = frh->tos;
err = 0;
errout:
return err;
}
刚才的函数里,有调用函数fib_empty_table,那我们就分析一下这个函数。
2.2 fib_empty_table
功能:从0开始到RT_TABLE_MAX为止,找到第一个没有创建路由表的id后,
即调用fib_new_table创建此id对应的路由表,并返回路由表的首地址;
若从0开始到RT_TABLE_MAX的id,都有创建相应的路由表了,则程序
返回NULL
static struct fib_table *fib_empty_table(void)
{
u32 id;
for (id = 1; id <= RT_TABLE_MAX; id++)
if (fib_get_table(id) == NULL)
return fib_new_table(id);
return NULL;
}
以上就是策略规则添加的整个分析流程,通过分析这个流程,发现在添加策略规则时,函数并没有判断要添加的规则是否已存在,这样会不会导致策略规则的重复添加呢?还是在应用层进行的判断呢?我感觉应该是应用层做了判断,由于目前一直在学习kernel里的代码,应用层的代码目前是没有时间分析了。
linux 路由指向策略,Linux 路由 学习笔记 之六 策略规则的添加相关推荐
- Linux与C++11多线程编程(学习笔记)
多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...
- Linux内存从0到1学习笔记(4,TLB)
一.TLB简介 Kernel初始化的时候,会在初始化内存中创建页表:而处理器读取指令和数据的时候需要首先通过MMU查表得到物理地址,然后在访问物理地址读取指令或数据.MMU查表过程汇中需要4次访问内存 ...
- 推荐系统学习笔记召回策略之基于协同过滤召回
基于协同过滤的召回 1. 概述 2. 基于近邻的协同过滤算法 3. 相似度计算方法 4. 协同过滤算法的进化-矩阵分解 图1. 推荐系统整体架构 推荐系统学习笔记系列链接: 推荐系统学习笔记--特征工 ...
- 强化学习笔记:策略评估--贝尔曼方程求解示例
目录 1. 前言 2. MDP模型 3. 求解贝尔曼方程 1. 前言 策略评估(Policy Evaluation),简单来说,就是针对某个既定的策略求其状态值函数和动作值函数.求得了状态值函数和动作 ...
- C++设计模式学习笔记:策略模式
C++设计模式学习笔记:策略模式 策略模式设计商场促销 1.策略模式介绍 2.商场收银系统策略模式实现 3.策略模式与工厂模式结合 3.策略模式与工厂模式对比 策略模式设计商场促销 1.策略模式介绍 ...
- 学习笔记:VB.net动态添加控件数组并传递事件
学习笔记:VB.net动态添加控件数组并传递事件 控件数组和事件 "中间人" 动态添加控件 控件数组和事件 新建一个用户窗体,在定义控件数组时,不能用Withevnets来定义数组 ...
- 分享:Django学习笔记(4)---ManyToMany 添加、删除关联、查询
Django学习笔记(4)---ManyToMany 添加.删除关联.查询 http://my.oschina.net/u/572994/blog/105280
- linux oracle流复制文件,【学习笔记】Oracle ASM linux dd命令复制asm中文件 操作磁盘或者分区...
天萃荷净 使用dd复制asm中文件,随着数据库新版本的推广ASM肯定会越来越被重视,最近准备系统的学习下ASM,以备突发情况需要,这是asm深入学习笔记 1.查询ASM某个数据文件AU信息 SQL&g ...
- linux查看ogg客户端版本,OGG学习笔记05-OGG的版本
刚接触OGG的时候,很容易被众多的版本搞晕,虽然官方有提供各版本对应认证OS和DB的表格. 个人认为一个比较简单的方式,是直接去edelivery.oracle.com下载OGG,选定一个大版本后,这 ...
最新文章
- 科略教育——执行力的3W管理法
- 列注释_机器学习 Pandas 03:基础 前16题 ( 带答案、注释 )
- ASP.NET常用代码汇总
- 自定义NodeJS-C++ Addons使用说明
- 解密Twitch:一家游戏直播网站缘何价值10亿刀?
- 【网络编程】Socket网络编程基础
- vim文本编辑器的操作和命令(可作手册查询)
- MPS的DCDC国产代换件
- APP测试---adb命令使用(monkey、input、11大事件等),附按键表(keycode)
- php格式文件怎么改成mp4,dat视频文件如何打开 dat格式文件怎样转换成MP4或其它视频格式...
- 三相并联功率因数校正matlab,基于并联技术的三相功率因数校正方法研究
- 【法律】如何保障未来夫妻合法权益:婚前房屋财产约定协议书
- LSTM之父最新长文:现代AI和深度学习发展史
- Java中占位符的实战运用
- 怎么查计算机上c盘的历史记录,如何查看电脑历史操作记录
- mc杀人Linux指令,杀人雪球指令详解 教你怎么做杀人雪球
- 修改系统文件更改权限
- MySQL 使用SQL语句实现 增删改查
- 将url地址中的编码转汉字
- 《戏妻族语不正》胡曾
热门文章
- 快搜浏览器_opera、Google、firefox三个浏览器的选择
- 数据库降级_阿里 双11 同款流控降级组件 Sentinel Go 正式GA,云原生服务稳稳稳...
- Java黑皮书课后题第5章:*5.24(数列求和)编写程序,计算下面数列的和:1/3+3/5+5/7+7/9+……95/97+97/99
- C语言学习之将一个数组中的值按逆序重新存放。例如,原来顺序为8,6,5,4,1. 要求改为1,4,5,6,8。
- JAVA之获取JavaSwing单选框JRadioButton选中的值(内容)
- 安装Ubuntu 18.04后的一些操作
- Poj2480欧拉函数
- 最长子段和 11061008 谢子鸣
- JBoss5.x6.x 反序列化漏洞
- 【学习排序】 Learning to Rank 中Listwise关于ListNet算法讲解及实现