ip_route_output_slow注释

转载自basic coder

根据 http://basiccoder.com/intro-linux-kernel-hash-rt-2.html 和 http://blog.chinaunix.net/uid-24673811-id-1754229.html 改编


static int ip_route_output_slow(struct net *net, struct rtable **rp,
				const struct flowi *oldflp)
{
	u32 tos	= RT_FL_TOS(oldflp); /*获取tos和当前的RTO_ONLINK(?)标志*/
	struct flowi fl = { .fl4_dst = oldflp->fl4_dst,
			    .fl4_src = oldflp->fl4_src,
			    .fl4_tos = tos & IPTOS_RT_MASK,
			    .fl4_scope = ((tos & RTO_ONLINK) ?  /*根据这个标志,得出路由的scope*/
					  RT_SCOPE_LINK : RT_SCOPE_UNIVERSE),
			    .mark = oldflp->mark,
			    .iif = net->loopback_dev->ifindex, /*设备号为回环设备的设备号?*/
			    .oif = oldflp->oif };
	struct fib_result res;
	unsigned int flags = 0;
	struct net_device *dev_out = NULL;
	int err;


	res.fi		= NULL;
#ifdef CONFIG_IP_MULTIPLE_TABLES
	res.r		= NULL;
#endif

	/*先是对源地址, 发包接口号和目的地址进行判断分类处理。*/  
	if (oldflp->fl4_src) {
		/* 若源地址为组播地址,受限广播地址(255.255.255.255)或0地址,
		均不合法,即刻返回 */
		err = -EINVAL;
		if (ipv4_is_multicast(oldflp->fl4_src) ||
		    ipv4_is_lbcast(oldflp->fl4_src) ||
		    ipv4_is_zeronet(oldflp->fl4_src))
			goto out;
			
		/*上面是对报文源地址的合理性检查,源地址是多播,广播或0地址时,返回错误*/

		/* I removed check for oif == dev_out->oif here.
		   It was wrong for two reasons:
		   1. ip_dev_find(net, saddr) can return wrong iface, if saddr
		      is assigned to multiple interfaces.
		   2. Moreover, we are allowed to send packets with saddr
		      of another iface. --ANK
		 */

		 /*当报文初始化的出接口为回环接口,源地址不为空且目的地址是多播或广播地址时,
		   找到源地址所对应的接口,并重新为出接口赋值, 然后创建cache路由项*/
		if (oldflp->oif == 0 &&
		    (ipv4_is_multicast(oldflp->fl4_dst) ||
		     ipv4_is_lbcast(oldflp->fl4_dst))) { /*发包接口为回环接口,目的地址是广播或多播时查找发包设备,ip_dev_find返回与所给定的源地址相等的第一个设备*/
			 /* 等价于inet_addr_type(saddr) == RTN_LOCAL, 
			__ip_dev_find()函数实际是搜索RT_TABLE_LOCAL
			路由表中的路由表项,如果未找到对应设备则返回,因为
			Linux不允许环回接口发组播或受限广播 */
			/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
			dev_out = __ip_dev_find(net, oldflp->fl4_src, false);
			if (dev_out == NULL)
				goto out;

			/* Special hack: user can direct multicasts
			   and limited broadcast via necessary interface
			   without fiddling with IP_MULTICAST_IF or IP_PKTINFO.
			   This hack is not just for fun, it allows
			   vic,vat and friends to work.
			   They bind socket to loopback, set ttl to zero
			   and expect that it will work.
			   From the viewpoint of routing cache they are broken,
			   because we are not allowed to build multicast path
			   with loopback source addr (look, routing cache
			   cannot know, that ttl is zero, so that packet
			   will not leave this host and route is valid).
			   Luckily, this hack is good workaround.
			 */

			 /* 给外面接口赋值后转去创建路由缓存 */
			 
			fl.oif = dev_out->ifindex;
			goto make_route;
		}

		/*本机出发的数据包的源地址如果有的话,必须要在local表中找到一条local路由
		FLOWI_FLAG_ANYSRC 标志可以取消本地地址必须存在于local路由表的限制*/
		if (!(oldflp->flags & FLOWI_FLAG_ANYSRC)) {
			/* It is equivalent to inet_addr_type(saddr) == RTN_LOCAL */
			if (!__ip_dev_find(net, oldflp->fl4_src, false))
				goto out;
		}
	}


	if (oldflp->oif) {/*发包设备不为空*/
		 /*检测出接口是否存在*/
		dev_out = dev_get_by_index_rcu(net, oldflp->oif);
		err = -ENODEV;
		if (dev_out == NULL)
			goto out;

		/* RACE: Check return value of inet_select_addr instead. */
		/* 如果外出接口示启用或外出接口对应的IPv4数据不存在,则返回网络不可达 */
		if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) {
			err = -ENETUNREACH;
			goto out;
		}
		
		/* 若是本地组播地址或受限广播地址则直接转去创建路由缓存 */
		if (ipv4_is_local_multicast(oldflp->fl4_dst) ||
		    ipv4_is_lbcast(oldflp->fl4_dst)) { 
			/*当报文源地址为空时,找出出接口设备上IP地址scope小于RT_SCOPE_LINK的地址,并赋值,然后往cache中添加路由表项*/
			if (!fl.fl4_src)
				fl.fl4_src = inet_select_addr(dev_out, 0,
							      RT_SCOPE_LINK);
			goto make_route;
		}
		/* 若未指定源地址,则根据目地地址类型创建选择一个源地址 */
		if (!fl.fl4_src) {
		/*目的地址是单播地址或空,源地址为空,那就选一个小于特定scope的IP地址*/
			if (ipv4_is_multicast(oldflp->fl4_dst))
				fl.fl4_src = inet_select_addr(dev_out, 0,
							      fl.fl4_scope);
			else if (!oldflp->fl4_dst)
				fl.fl4_src = inet_select_addr(dev_out, 0,
							      RT_SCOPE_HOST);
		}
	}

	/* 如果目的地址不存在,则令目的地址等于源地址,若都不存在,则使用环回接口,
		路由类型为本地路由,转而创建路由缓存 */
	if (!fl.fl4_dst) {
		fl.fl4_dst = fl.fl4_src;
		if (!fl.fl4_dst)
			fl.fl4_dst = fl.fl4_src = htonl(INADDR_LOOPBACK);
		dev_out = net->loopback_dev;
		fl.oif = net->loopback_dev->ifindex;
		res.type = RTN_LOCAL;
		flags |= RTCF_LOCAL;
		goto make_route;
	}

	/*
	OK, 走到这里先总结一下不需要查询路由表即可直接创建路由缓存的情况:
	1. 指定了源地址,未指定外出接口,目的地址为组播地址或受限广播地址
	2. 指定了外出接口,并且目的地址为本地组播地址或受限广播地址
	3. 未指定目的地址。
 
	若以上三种情况均未满足,则需要进行路由表查询。
*/
	if (fib_lookup(net, &fl, &res)) {
		res.fi = NULL;
		if (oldflp->oif) {
			/* 程序走到这里说明查询路由表失败,未找到对应的路由表项,
			但却指定了外出接口,这时候即便没有路由也是可以发送数据包的。
			当然,如果未指定外出接口,则只能返回网络不可达了。 */

			if (fl.fl4_src == 0)
				fl.fl4_src = inet_select_addr(dev_out, 0,
							      RT_SCOPE_LINK);
			res.type = RTN_UNICAST;
			goto make_route;
		}
		err = -ENETUNREACH;
		goto out;
	}

	/* 若为本地路由,则使用环回接口 */
	if (res.type == RTN_LOCAL) {
		if (!fl.fl4_src) {
			if (res.fi->fib_prefsrc)
				fl.fl4_src = res.fi->fib_prefsrc;
			else
				fl.fl4_src = fl.fl4_dst;
		}
		dev_out = net->loopback_dev;
		fl.oif = dev_out->ifindex;
		res.fi = NULL;
		flags |= RTCF_LOCAL;
		goto make_route;
	}

#ifdef CONFIG_IP_ROUTE_MULTIPATH
	if (res.fi->fib_nhs > 1 && fl.oif == 0)
		fib_select_multipath(&fl, &res);
	else
#endif
	/*  使用默认路由需要三个条件:
	1. 若前缀为0,也即掩码长度为0,默认路由匹配所有的目的地址。
	2. 路由类型为RTN_UNICAST,我们知道本地地址,组播地址和广播地址
	3. 未指定出口设备,上面我们提到即便是没有路由的情况下提供了出口设备,数据包也是可以发送的。
	这时候路由是默认路由,因此我们需要选择默认网关 */
	if (!res.prefixlen && res.type == RTN_UNICAST && !fl.oif)
		fib_select_default(net, &fl, &res);

	if (!fl.fl4_src)
		fl.fl4_src = FIB_RES_PREFSRC(res);

	dev_out = FIB_RES_DEV(res);
	fl.oif = dev_out->ifindex;


make_route:
	/* 创建一条路由缓存 */
	err = ip_mkroute_output(rp, &res, &fl, oldflp, dev_out, flags);

out:	return err;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值