tcp/ip 协议栈Linux内核源码分析七 路由子系统分析二 策略路由

内核版本:3.4.39

策略路由就是根据配置策略查找路由表,早期的Linux版本是不支持策略路由的,默认的查找策略就是先查找local路由表,找不到再继续查找main表,当支持策略路由功能时,内核最多可以配置255个路由表,这时候根据先匹配策略,匹配后再去查找该策略指定的路由表,内核最多支持32768张策略表,初始化的时候创建了local表,main表和default表。策略表按照优先级从高到低的顺序挂在一张链表上,优先级范围0~32767,数值越低优先级越高。

内核策略路由部分包括初始化、添加、删除和查找。

初始化部分包括初始化默认的策略链表。

添加部分就是在收到用户层配置命令的时候进行添加、删除、修改、查询等操作,即ip rule add、del等。

应用层和内核层通信是使用netlink通信。

策略查找操作是在查找路由的时候进行,当路由缓存查找失败的时候就会去查找路由策略,然后根据策略去查询路由表。

先看下策略路由部分启动的流程:

策略子系统启动的时候分为两部分,上图左边部分放在路由子系统启动的过程中,重点看下策略路由启动:

int __net_init fib4_rules_init(struct net *net)
{
	int err;
	struct fib_rules_ops *ops;

	//注册策略虚函数集,关于策略的很多操作都在这里
	ops = fib_rules_register(&fib4_rules_ops_template, net);
	if (IS_ERR(ops))
		return PTR_ERR(ops);

	//初始化默认策略,0,32766,32767
	err = fib_default_rules_init(ops);
	if (err < 0)的
		goto fail;
	net->ipv4.rules_ops = ops;
	return 0;

fail:
	/* also cleans all rules already added */
	fib_rules_unregister(ops);
	return err;
}

 fib4_rules_ops_template是个函数集合,很多策略操作函数都在这里,策略路由查找的时候就会用到:

static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = {
	.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	= fib_default_rule_pref,
	.nlmsg_payload	= fib4_rule_nlmsg_payload,
	.flush_cache	= fib4_rule_flush_cache,
	.nlgroup	= RTNLGRP_IPV4_RULE,
	.policy		= fib4_rule_policy,
	.owner		= THIS_MODULE,
};

初始化三张默认策略表:

//初始化三张策略表,0标识优先级最高
static int fib_default_rules_init(struct fib_rules_ops *ops)
{
	int err;

    //优先级最高,匹配则查找local路由表
	err = fib_default_rule_add(ops, 0, RT_TABLE_LOCAL, 0);
	if (err < 0)
		return err;

    //优先级为32766,匹配则查找main表
	err = fib_default_rule_add(ops, 0x7FFE, RT_TABLE_MAIN, 0);
	if (err < 0)
		return err;

    //优先级为32737,匹配则查找default表
	err = fib_default_rule_add(ops, 0x7FFF, RT_TABLE_DEFAULT, 0);
	if (err < 0)
		return err;
	return 0;
}

上图右边也是策略路由子系统启动流程:

这一部分主要注册了netlink消息模块,用来监听应用层配置信息,其次就是注册通知链,监听系统其它模块信息,比如说网络设备异常等等。

static int __init fib_rules_init(void)
{
	int err;
	//注册应用层配置命令,添加、删除和dump
	rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL);
	rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL);
	rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL);

	err = register_pernet_subsys(&fib_rules_net_ops);
	if (err < 0)
		goto fail;

	//注册事件通知链,监听其它子系统异常信息
	err = register_netdevice_notifier(&fib_rules_notifier);
	if (err < 0)
		goto fail_unregister;

	return 0;

fail_unregister:
	unregister_pernet_subsys(&fib_rules_net_ops);
fail:
	rtnl_unregister(PF_UNSPEC, RTM_NEWRULE);
	rtnl_unregister(PF_UNSPEC, RTM_DELRULE);
	rtnl_unregister(PF_UNSPEC, RTM_GETRULE);
	return err;
}

当使用ip rule add或者ip rule del等应用层命令时,最终会调用策略子系统注册的netlink处理函数,处理函数做的内容比较简单,先检查参数的完整性,合法后则对策略表net->ipv4->rules_ops->rules_list执行添加或删除等操作。

上述就是策略路由的初始化流程、添加和删除等。

下面看下策略路由查找,查找操作发生在路由缓存没命中的情况下,内核在网络层收到报文的时候需要进行路由以判断是送到本地还是转发,同样,发送报文的时候也需要查找路由,判断走那个出口或者下一跳等。

通过上图可以看到,无论是接收报文还是发送报文,当路由缓存没有命中的时候,查找策略的接口都是fib_rules_look,这个函数就是查找策略表,然后根据匹配的策略执行相应的动作:

//策略查找
int fib_rules_lookup(struct fib_rules_ops *ops, struct flowi *fl,
		     int flags, struct fib_lookup_arg *arg)
{
	struct fib_rule *rule;
	int err;

	rcu_read_lock();

    //遍历策略表
	list_for_each_entry_rcu(rule, &ops->rules_list, list) {
jumped:
		
        //根据报文信息去查找策略,并将查询到的策略保存到rule里
		if (!fib_rule_match(rule, ops, fl, flags))
			continue;

		//跳过当前策略表,继续查找
		if (rule->action == FR_ACT_GOTO) {
			struct fib_rule *target;

			target = rcu_dereference(rule->ctarget);
			if (target == NULL) {
				continue;
			} else {
				rule = target;
				goto jumped;
			}
		} else if (rule->action == FR_ACT_NOP){
				//效果同上,继续查找
				continue;
		}
		else{
			//查询到了,根据策略执行相应的动作
			//ops->action是虚函数,在路由子系统初始化的时候设定,
			//ipv4里该函数时fib4_rule_action
			err = ops->action(rule, fl, flags, arg);
		}

		if (err != -EAGAIN) {
			if ((arg->flags & FIB_LOOKUP_NOREF) ||
			    likely(atomic_inc_not_zero(&rule->refcnt))) {
				arg->rule = rule;
				goto out;
			}
			break;
		}
	}

	err = -ESRCH;
out:
	rcu_read_unlock();

	return err;
}
EXPORT_SYMBOL_GPL(fib_rules_lookup);

当策略匹配后会调用函数执行ops->action执行相应的动作,这个虚函数在策略子系统初始化的时候被设置,上面已经讲过。ipv4里该函数时fib4_rule_action:

static int fib4_rule_action(struct fib_rule *rule, struct flowi *flp,
			    int flags, struct fib_lookup_arg *arg)
{
	int err = -EAGAIN;
	struct fib_table *tbl;

	switch (rule->action) {
	//查找路由表
	case FR_ACT_TO_TBL:
		break;

	//不可达
	case FR_ACT_UNREACHABLE:
		err = -ENETUNREACH;
		goto errout;

	//禁止此类报文
	case FR_ACT_PROHIBIT:
		err = -EACCES;
		goto errout;

	//丢弃此类报文,
	case FR_ACT_BLACKHOLE:
	default:
		err = -EINVAL;
		goto errout;
	}

	//根据路由表ID获取指定路由表指针
	tbl = fib_get_table(rule->fr_net, rule->table);
	if (!tbl)
		goto errout;

	//查询路由表
	err = fib_table_lookup(tbl, &flp->u.ip4, (struct fib_result *) arg->result, arg->flags);
	if (err > 0)
		err = -EAGAIN;
errout:
	return err;
}

当策略路由的动作是查找路由表时则调用fib_table_lookup查找路由表,如果是其它动作,比如blackhole等则丢弃报文。

以上就是策略路由中策略的主要部分。

 

参考目录:

1. 《Linux Kernel Networking -  Implementation and Theory》

2. 《深入理解Linux网络技术内幕》

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嵌入式Linux网络体系结构与TCP/IP协议栈的关系密切,是嵌入式设备网络通信功能的核心组成部分。 嵌入式Linux网络体系结构是指整个嵌入式设备上的网络通信架构,包括网络接口、网络协议栈、网络驱动等。其中,TCP/IP协议栈是网络协议的核心部分。TCP/IP协议栈是一组网络协议的集合,包括IP协议、TCP协议、UDP协议等。它是实现网络通信的基础,负责数据包的传输和路由。 在嵌入式Linux网络体系结构中,网络接口是设备与网络连接的接口,负责将数据包传输到网络中。网络驱动是设备与操作系统之间的桥梁,负责控制网络设备的硬件和软件。TCP/IP协议栈则是在操作系统内核中实现的软件模块,负责处理网络数据包的封装、解析、传输和路由。 嵌入式Linux网络体系结构与TCP/IP协议栈的紧密结合,使得嵌入式设备能够进行网络通信。通过网络接口和网络驱动,嵌入式设备可以与网络进行连接,并通过TCP/IP协议栈实现数据的传输和交换。TCP/IP协议栈提供了可靠的数据传输机制,保证了数据的完整性和准确性。同时,它还提供了灵活的路由算法,使得数据能够在复杂的网络中传输。 总之,嵌入式Linux网络体系结构与TCP/IP协议栈是嵌入式设备网络通信的核心组成部分。它们共同实现了设备与网络的连接和数据的传输,为嵌入式设备提供了强大的网络通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值