上一节分析了策略规则相关的数据结构,本节就分析一下策略规则的添加。对于策略规则的功能模块,由于 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 路由 学习笔记 之六 策略规则的添加相关推荐

  1. Linux与C++11多线程编程(学习笔记)

    多线程编程与资源同步 在Windows下,主线程退出后,子线程也会被关闭; 在Linux下,主线程退出后,系统不会关闭子线程,这样就产生了僵尸进程 3.2.1创建线程 Linux 线程的创建 #inc ...

  2. Linux内存从0到1学习笔记(4,TLB)

    一.TLB简介 Kernel初始化的时候,会在初始化内存中创建页表:而处理器读取指令和数据的时候需要首先通过MMU查表得到物理地址,然后在访问物理地址读取指令或数据.MMU查表过程汇中需要4次访问内存 ...

  3. 推荐系统学习笔记召回策略之基于协同过滤召回

    基于协同过滤的召回 1. 概述 2. 基于近邻的协同过滤算法 3. 相似度计算方法 4. 协同过滤算法的进化-矩阵分解 图1. 推荐系统整体架构 推荐系统学习笔记系列链接: 推荐系统学习笔记--特征工 ...

  4. 强化学习笔记:策略评估--贝尔曼方程求解示例

    目录 1. 前言 2. MDP模型 3. 求解贝尔曼方程 1. 前言 策略评估(Policy Evaluation),简单来说,就是针对某个既定的策略求其状态值函数和动作值函数.求得了状态值函数和动作 ...

  5. C++设计模式学习笔记:策略模式

    C++设计模式学习笔记:策略模式 策略模式设计商场促销 1.策略模式介绍 2.商场收银系统策略模式实现 3.策略模式与工厂模式结合 3.策略模式与工厂模式对比 策略模式设计商场促销 1.策略模式介绍 ...

  6. 学习笔记:VB.net动态添加控件数组并传递事件

    学习笔记:VB.net动态添加控件数组并传递事件 控件数组和事件 "中间人" 动态添加控件 控件数组和事件 新建一个用户窗体,在定义控件数组时,不能用Withevnets来定义数组 ...

  7. 分享:Django学习笔记(4)---ManyToMany 添加、删除关联、查询

    Django学习笔记(4)---ManyToMany 添加.删除关联.查询 http://my.oschina.net/u/572994/blog/105280

  8. linux oracle流复制文件,【学习笔记】Oracle ASM linux dd命令复制asm中文件 操作磁盘或者分区...

    天萃荷净 使用dd复制asm中文件,随着数据库新版本的推广ASM肯定会越来越被重视,最近准备系统的学习下ASM,以备突发情况需要,这是asm深入学习笔记 1.查询ASM某个数据文件AU信息 SQL&g ...

  9. linux查看ogg客户端版本,OGG学习笔记05-OGG的版本

    刚接触OGG的时候,很容易被众多的版本搞晕,虽然官方有提供各版本对应认证OS和DB的表格. 个人认为一个比较简单的方式,是直接去edelivery.oracle.com下载OGG,选定一个大版本后,这 ...

最新文章

  1. 科略教育——执行力的3W管理法
  2. 列注释_机器学习 Pandas 03:基础 前16题 ( 带答案、注释 )
  3. ASP.NET常用代码汇总
  4. 自定义NodeJS-C++ Addons使用说明
  5. 解密Twitch:一家游戏直播网站缘何价值10亿刀?
  6. 【网络编程】Socket网络编程基础
  7. vim文本编辑器的操作和命令(可作手册查询)
  8. MPS的DCDC国产代换件
  9. APP测试---adb命令使用(monkey、input、11大事件等),附按键表(keycode)
  10. php格式文件怎么改成mp4,dat视频文件如何打开 dat格式文件怎样转换成MP4或其它视频格式...
  11. 三相并联功率因数校正matlab,基于并联技术的三相功率因数校正方法研究
  12. 【法律】如何保障未来夫妻合法权益:婚前房屋财产约定协议书
  13. LSTM之父最新长文:现代AI和深度学习发展史
  14. Java中占位符的实战运用
  15. 怎么查计算机上c盘的历史记录,如何查看电脑历史操作记录
  16. mc杀人Linux指令,杀人雪球指令详解 教你怎么做杀人雪球
  17. 修改系统文件更改权限
  18. MySQL 使用SQL语句实现 增删改查
  19. 将url地址中的编码转汉字
  20. 《戏妻族语不正》胡曾

热门文章

  1. 快搜浏览器_opera、Google、firefox三个浏览器的选择
  2. 数据库降级_阿里 双11 同款流控降级组件 Sentinel Go 正式GA,云原生服务稳稳稳...
  3. Java黑皮书课后题第5章:*5.24(数列求和)编写程序,计算下面数列的和:1/3+3/5+5/7+7/9+……95/97+97/99
  4. C语言学习之将一个数组中的值按逆序重新存放。例如,原来顺序为8,6,5,4,1. 要求改为1,4,5,6,8。
  5. JAVA之获取JavaSwing单选框JRadioButton选中的值(内容)
  6. 安装Ubuntu 18.04后的一些操作
  7. Poj2480欧拉函数
  8. 最长子段和 11061008 谢子鸣
  9. JBoss5.x6.x 反序列化漏洞
  10. 【学习排序】 Learning to Rank 中Listwise关于ListNet算法讲解及实现