前面分析路由查找时,已经捎带提到了策略规则,本节开始就要全面分析策略规则的内容了。也分析了这么多的内核代码,基本上对一个功能模块的几个主要内容也算是比较熟悉了。对于我们开发而言,一定要重视数据结构,当我们分析新的子功能或者开发新的子功能时,一定要好好的构思数据结构,因为数据结构的好坏,某种程度上就决定了代码的好坏。
因此在开始分析策略规则的子模块时,照例需要先分析策略规则相关的数据结构。
策略规则起到什么作用呢?
添加策略规则,主要是用来和路由表一起作用,实现策略路由的功能。简而言之,就是输入或输出的数据包,通过策略规则,通过指定的路由表的路由项而把数据从相应的接口发送出去,从而实现策略路由的功能。
1.数据结构
1.1 fib_rule
该数据结构即为抽象一个策略规则,下面分析下这个数据结构
/*
策略规则对应的数据结构
*/
struct fib_rule
{
/*将策略规则链接在一起*/
struct list_head list;
/*引用计数*/
atomic_t refcnt;
/*接口的index*/
int ifindex;
/*接口的名称*/
char ifname[IFNAMSIZ];
/*mark值以及mark的掩码值*/
u32 mark;
u32 mark_mask;
/*优先级,值越小优先级越大*/
u32 pref;
u32 flags;
/*路由表的id*/
u32 table;
/*fib rule的action规则,包括FR_ACT_TO_TBL等*/
u8 action;
struct rcu_head rcu;
};
对于一个策略规则来说,肯定需要与其他的策略规则连接在一起,此处则是使用链表来实现的。接着还需要有引用计数以及action,指示匹配该策略规则后的下一步操作是什么;还有接口名称、接口index、mark值、路由表id(这个才是最主要的)等。
1.2 fib_rules_ops
该数据结构是策略规则中协议相关的操作函数的结构体,对于v4和v6,其相应的处理函数会有所不同
/*
策略规则的操作相关的数据结构
*/
struct fib_rules_ops
{
/*对应的协议簇,对于ipv4为AF_INET*/
int family;
/*主要是将注册到系统的fib_rules_ops链接到链表rules_ops中*/
struct list_head list;
/*一个策略规则所占用的内存大小*/
int rule_size;
/*协议相关的地址的长度*/
int addr_size;
/*协议相关的action函数,即是策略规则匹配后,所调用的action函数,执行后续的操作,一般是获取到相应的路由表,查找符合要求的路由项*/
int (*action)(struct fib_rule *,
struct flowi *, int,
struct fib_lookup_arg *);
/*协议相关的规则匹配函数,对于策略规则的匹配,首先是通用匹配,待通用匹配完成后,则会调用该函数,进行协议相关参数(源、目的地址等)的匹配*/
int (*match)(struct fib_rule *,
struct flowi *, int);
/*协议相关的配置函数*/
int (*configure)(struct fib_rule *,
struct sk_buff *,
struct nlmsghdr *,
struct fib_rule_hdr *,
struct nlattr **);
int (*compare)(struct fib_rule *,
struct fib_rule_hdr *,
struct nlattr **);
int (*fill)(struct fib_rule *, struct sk_buff *,
struct nlmsghdr *,
struct fib_rule_hdr *);
u32 (*default_pref)(void);
size_t (*nlmsg_payload)(struct fib_rule *);
/*下面两个是netlink相关的参数*/
int nlgroup;
struct nla_policy *policy;
/*链表用于将该协议簇已添加的所有fib_rule规则链接在一起*/
struct list_head *rules_list;
struct module *owner;
};
1.3 策略规则的action类型
/*fib rule的action类型,FR_ACT_TO_TBL即该fib rule与路由表关联*/
enum
{
FR_ACT_UNSPEC,
FR_ACT_TO_TBL, /* Pass to fixed table */
FR_ACT_RES1,
FR_ACT_RES2,
FR_ACT_RES3,
FR_ACT_RES4,
FR_ACT_BLACKHOLE, /* Drop without notification */
FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
FR_ACT_PROHIBIT, /* Drop with EACCES */
__FR_ACT_MAX,
};
我们一般用到的是FR_ACT_TO_TBL,即匹配后的规则,即会进入到相应的路由表,继续进行路由项的匹配。
1.4 fib4_rule
ipv4协议相关的fib rule结构,该结构包含了
struct fib_rule 类型的成员变量,同时增加了源ip地址、目的
ip地址、tos等ipv4相关的成员判断
struct fib4_rule
{
struct fib_rule common;
u8 dst_len;
u8 src_len;
u8 tos;
__be32 src;
__be32 srcmask;
__be32 dst;
__be32 dstmask;
#ifdef CONFIG_NET_CLS_ROUTE
u32 tclassid;
#endif
};
2.策略规则的初始化
对于策略规则的初始化,主要是包括两个方面
a 通用策略规则的初始化
b 协议相关的策略规则的初始化
2.1 通用策略规则的初始化
对于通用策略规则,主要是就是注册通知链而已。
/*
注册通知链
*/
static int __init fib_rules_init(void)
{
return register_netdevice_notifier(&fib_rules_notifier);
}
static struct notifier_block fib_rules_notifier = {
.notifier_call = fib_rules_event,
};
该通知链仅处理NETDEV_REGISTER、NETDEV_UNREGISTER这两个事件通知,主要是在设备注册时,即是遍历策略规则,对于匹配的规则,则会将该策略规则的ifindex进行赋值;而当设备注销时,则会遍历策略规则,对于匹配的规则,则会将该策略规则的ifindex的值设置为-1。
static int fib_rules_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
struct net_device *dev = ptr;
struct fib_rules_ops *ops;
ASSERT_RTNL();
rcu_read_lock();
switch (event) {
case NETDEV_REGISTER:
list_for_each_entry(ops, &rules_ops, list)
attach_rules(ops->rules_list, dev);
break;
case NETDEV_UNREGISTER:
list_for_each_entry(ops, &rules_ops, list)
detach_rules(ops->rules_list, dev);
break;
}
rcu_read_unlock();
return NOTIFY_DONE;
}
2.2 协议相关的策略规则的初始化
本文以ipv4为主,此处就分析ipv4相关的策略规则的的初始化
2.2.1 fib4_rules_init
这个函数主要也就是实现两个功能,增加local、main、default3个策略规则,并添加到fib4_rules_ops.rules_list中;然后就是将fib4_rules_ops注册到系统中,添加到链表rules_ops中
功能:ipv4协议的fib rule的初始化函数
1.分别将默认的3条fib rule规则添加到全局链表fib4_rules的链尾
2.调用函数fib_rules_register将ipv4对应的fib 规则的操作变量fib4_rules_ops添加
到系统的链表rules_ops中
void __init fib4_rules_init(void)
{
list_add_tail(&local_rule.common.list, &fib4_rules);
list_add_tail(&main_rule.common.list, &fib4_rules);
list_add_tail(&default_rule.common.list, &fib4_rules);
fib_rules_register(&fib4_rules_ops);
}
2.2.2 local、main、default等默认规则的定义
默认创建3个fib_rule规则,而fib rule规则添加到相应协议簇
的fib_rules_ops的list链表中时,是根据pref的优先级来进行添加
到,pref的值越小,而优先级越大。
而在ipv4的fib rule的初始化中,首先建立了default_rule、main_rule、local_rule
并分别添加到fib4_rules_ops.list中,因此以后添加的ipv4 fib rule规则,即使
优先级最大,也是在local_rule规则之后。
因此,在使用策略路由查找时,首先就会匹配local_rule规则,即进入
local路由表中进行路由匹配,其次才会匹配其他的fib rule规则
以下3个默认ipv4 fib rule是没有设置匹配条件的,即只要遍历到
下面3个ipv4 fib rule规则,即会匹配。
static struct fib4_rule default_rule = {
.common = {
.refcnt = ATOMIC_INIT(2),
.pref = 0x7FFF,
.table = RT_TABLE_DEFAULT,
.action = FR_ACT_TO_TBL,
},
};
static struct fib4_rule main_rule = {
.common = {
.refcnt = ATOMIC_INIT(2),
.pref = 0x7FFE,
.table = RT_TABLE_MAIN,
.action = FR_ACT_TO_TBL,
},
};
static struct fib4_rule local_rule = {
.common = {
.refcnt = ATOMIC_INIT(2),
.table = RT_TABLE_LOCAL,
.action = FR_ACT_TO_TBL,
.flags = FIB_RULE_PERMANENT,
},
};
2.2.3 ipv4相关的fib_rule_ops的定义以及注册
ipv4对应的struct fib_rules_ops 变量,其中为ipv4 fib rule的match、action、configure、compare、等函数指针进行赋值;规定了ipv4的fib rule链表为fib4_rules。
static struct fib_rules_ops fib4_rules_ops = {
.family = AF_INET,
.rule_size = sizeof(struct fib4_rule),
.addr_size = sizeof(u32),
.action = fib4_rule_action,
.match = fib4_rule_match,
.configure = fib4_rule_configure,
.compare = fib4_rule_compare,
.fill = fib4_rule_fill,
.default_pref = fib4_rule_default_pref,
.nlmsg_payload = fib4_rule_nlmsg_payload,
.nlgroup = RTNLGRP_IPV4_RULE,
.policy = fib4_rule_policy,
.rules_list = &fib4_rules,
.owner = THIS_MODULE,
};
既然讲到了fib_rule_ops的注册,那就分析下相应的注销与注册函数吧,这两个函数也是通用的函数,因此其定义是放在通用策略规则相关的fib_rule.c。
2.2.3.1 fib_rules_register
功能:将fib_rules_ops注册到全局链表rules_ops中去
1.对传入的struct fib_rules_ops变量的成员进行合理性检查包括rule_size是否符合要求,match、configure、compare等函数指针是否为NULL等
2.只有符合1中的合理性检查后,才会将传入的struct fib_rules_ops变量添加到
全局链表rules_ops
int fib_rules_register(struct fib_rules_ops *ops)
{
int err = -EEXIST;
struct fib_rules_ops *o;
if (ops->rule_size < sizeof(struct fib_rule))
return -EINVAL;
if (ops->match == NULL || ops->configure == NULL ||
ops->compare == NULL || ops->fill == NULL ||
ops->action == NULL)
return -EINVAL;
spin_lock(&rules_mod_lock);
/*判断全局链表rules_ops中是否存在相同协议簇的struct fib_rules_ops变量,
若存在,则不再添加,程序返回*/
list_for_each_entry(o, &rules_ops, list)
if (ops->family == o->family)
goto errout;
list_add_tail_rcu(&ops->list, &rules_ops);
err = 0;
errout:
spin_unlock(&rules_mod_lock);
return err;
}
2.2.3.2 fib_rules_unregister
功能:将fib_rules_ops从全局链表rules_ops中删除,并删除该fib_rules_opsd的rules_list链表中的所有fib rule规则(这主要是通过函数cleanup_ops实现)
int fib_rules_unregister(struct fib_rules_ops *ops)
{
int err = 0;
struct fib_rules_ops *o;
spin_lock(&rules_mod_lock);
list_for_each_entry(o, &rules_ops, list) {
if (o == ops) {
list_del_rcu(&o->list);
cleanup_ops(ops);
goto out;
}
}
err = -ENOENT;
out:
spin_unlock(&rules_mod_lock);
synchronize_rcu();
return err;
}
至此,将策略规则的初始化基本分析完了,后面开始分析策略规则的添加、删除、查找等功能。