Linux 内核源码分析---IPv4 路由子系统

在这里插入图片描述
若要使 host1 可直接通过 ip 地址访问 host3,则需在 host2 中配置路由转发。对到来数据包(从 host1 发送到 host3 或反向发送)进行转发的过程被称为 路由选择,是根据被称为路由选择表的数据结构进行。

常见的路由选择:默认网关和默认路由。
在路由选择表中指定默认网关条目时,不与其他路由选择条目匹配的数据包都将转发到默认网关,不管其 IP 报头中的目标地址是什么。
使用无类别域间路由选择表示法时,默认路由用 0.0.0.0/0。作为一个简单案例,可按如下方法将IPV4地址 为 192.168.3.2 的机器指定默认网关。

IP ROUTE ADD DEFAULT VIA 192.168.3.2
// or
ROUTE ADD DEFAULT GATEWAY 192.168.3.2

CIDR(Classless Inter-Domain Routing,无类别域间路由) 表示法是一种用于表示IP地址及其子网掩码的方法。在CIDR表示法中,IP 地址后面跟着一个斜杠(/)和一个数字,这个数字表示 IP 地址中最左侧的连续比特位是网络地址部分(也称为前缀长度)。剩余的位则是主机地址部分。

例如,IP地址 192.168.1.0/24 表示该IP地址的前24位是网络地址部分,后8位是主机地址部分。在这个例子中,子网掩码是 255.255.255.0,因为该子网掩码在二进制表示中有24个连续的1(表示网络部分)和8个连续的0(表示主机部分)。

http://t.csdnimg.cn/OXUUQ

路由表

网络栈最重要目标是转发流量,对于Internet骨干中的核心路由器来说尤其如此。Linux IP层被称为路由选择子系统,负责转换数据包和维护转发数据包。 接收或发送每个数据包时,都必须在路由选择子系统中查找。

在路由选择子系统中找到匹配条目, FIB_LOOKUP() 将创建一个包含各种路由选择参数 FIB_RESULT对象,并返回 0RTBALE 结构如下:

在这里插入图片描述

在这里插入图片描述

路由选择子系统的主要数据结构是路由选择表,由结构fib_table表示。

在这里插入图片描述

路由选择条目由结构 fib_info 表示,它包含重要的路由选择条目参数,如出站网络设置、优先级、路由选择协议标识符等。fib_info对象由方法fib_create_info()创建,存储在散列表fib_info_hash中。

在这里插入图片描述

缓存:缓存路由选择查找结果是一种优化技术,可用于改善路由选择子系统的性能。

下一跳:结构fib_nh表示下一跳,包含输出网络设备、外出接口索引等信息。
在这里插入图片描述

ICMP重定向消息

有时路由选择条目可能是次优的,在这种情况下将发送 ICMP 重定向消息。次优条目的主要判断标准是:输入设备和输出设备相同

ICMPv4重定向消息的代码有4种:
ICMP_REDIR_NET:重定向网络;
ICMP_REDIR_HOST:重定向主机;
ICMP_REDIR_NETTOS:针对TOS重定向网络;
ICMP_REDIR_HOSTTOS:针对TOS重定向主机;

1、生成ICMPv4重定向消息
存在次优路由时,将发送 ICMPv4 重定向消息。判断次优路由的最重要标准是:输入设备和输出设备相同。但同时还需要满足其他一些条件。ICMPv4重定向消息的生成过程两个阶段:__mkroute_input()设置标志RTCF_DOREDIRECT------>ip_forward()发送 ICMPv4 定向消息。

/* called in rcu_read_lock() section */
static int __mkroute_input(struct sk_buff *skb,
			   const struct fib_result *res,
			   struct in_device *in_dev,
			   __be32 daddr, __be32 saddr, u32 tos)
{
	struct fib_nh_exception *fnhe;
	struct rtable *rth;
	int err;
	struct in_device *out_dev;
	bool do_cache;
	u32 itag = 0;

	/* get a working reference to the output device */
	out_dev = __in_dev_get_rcu(FIB_RES_DEV(*res));
	if (!out_dev) {
		net_crit_ratelimited("Bug in ip_route_input_slow(). Please report.\n");
		return -EINVAL;
	}

	err = fib_validate_source(skb, saddr, daddr, tos, FIB_RES_OIF(*res),
				  in_dev->dev, in_dev, &itag);
	if (err < 0) {
		ip_handle_martian_source(in_dev->dev, in_dev, skb, daddr,
					 saddr);

		goto cleanup;
	}

	do_cache = res->fi && !itag;
	if (out_dev == in_dev && err && IN_DEV_TX_REDIRECTS(out_dev) &&
	    skb->protocol == htons(ETH_P_IP) &&
	    (IN_DEV_SHARED_MEDIA(out_dev) ||
	     inet_addr_onlink(out_dev, saddr, FIB_RES_GW(*res))))
		IPCB(skb)->flags |= IPSKB_DOREDIRECT;

	if (skb->protocol != htons(ETH_P_IP)) {
		/* Not IP (i.e. ARP). Do not create route, if it is
		 * invalid for proxy arp. DNAT routes are always valid.
		 *
		 * Proxy arp feature have been extended to allow, ARP
		 * replies back to the same interface, to support
		 * Private VLAN switch technologies. See arp.c.
		 */
		if (out_dev == in_dev &&
		    IN_DEV_PROXY_ARP_PVLAN(in_dev) == 0) {
			err = -EINVAL;
			goto cleanup;
		}
	}

	fnhe = find_exception(&FIB_RES_NH(*res), daddr);
	if (do_cache) {
		if (fnhe) {
			rth = rcu_dereference(fnhe->fnhe_rth_input);
			if (rth && rth->dst.expires &&
			    time_after(jiffies, rth->dst.expires)) {
				ip_del_fnhe(&FIB_RES_NH(*res), daddr);
				fnhe = NULL;
			} else {
				goto rt_cache;
			}
		}

		rth = rcu_dereference(FIB_RES_NH(*res).nh_rth_input);

rt_cache:
		if (rt_cache_valid(rth)) {
			skb_dst_set_noref(skb, &rth->dst);
			goto out;
		}
	}

	rth = rt_dst_alloc(out_dev->dev, 0, res->type,
			   IN_DEV_CONF_GET(in_dev, NOPOLICY),
			   IN_DEV_CONF_GET(out_dev, NOXFRM), do_cache);
	if (!rth) {
		err = -ENOBUFS;
		goto cleanup;
	}

	rth->rt_is_input = 1;
	if (res->table)
		rth->rt_table_id = res->table->tb_id;
	RT_CACHE_STAT_INC(in_slow_tot);

	rth->dst.input = ip_forward;

	rt_set_nexthop(rth, daddr, res, fnhe, res->fi, res->type, itag);
	set_lwt_redirect(rth);
	skb_dst_set(skb, &rth->dst);
out:
	err = 0;
 cleanup:
	return err;
}

2、接收ICMPv4重定向消息
仅当 ICMPv4 重定向消息通过一些完整性检查后,才会对其进行处理,处理 ICMPv4 重定向消息的工作是由__ip_do_redirect()完成。

在这里插入图片描述

static void __ip_do_redirect(struct rtable *rt, struct sk_buff *skb, struct flowi4 *fl4,
			     bool kill_route)
{
	__be32 new_gw = icmp_hdr(skb)->un.gateway;
	__be32 old_gw = ip_hdr(skb)->saddr;
	struct net_device *dev = skb->dev;
	struct in_device *in_dev;
	struct fib_result res;
	struct neighbour *n;
	struct net *net;

	switch (icmp_hdr(skb)->code & 7) {
	case ICMP_REDIR_NET:
	case ICMP_REDIR_NETTOS:
	case ICMP_REDIR_HOST:
	case ICMP_REDIR_HOSTTOS:
		break;

	default:
		return;
	}

	if (rt->rt_gateway != old_gw)
		return;

	in_dev = __in_dev_get_rcu(dev);
	if (!in_dev)
		return;

	net = dev_net(dev);
	if (new_gw == old_gw || !IN_DEV_RX_REDIRECTS(in_dev) ||
	    ipv4_is_multicast(new_gw) || ipv4_is_lbcast(new_gw) ||
	    ipv4_is_zeronet(new_gw))
		goto reject_redirect;

	if (!IN_DEV_SHARED_MEDIA(in_dev)) {
		if (!inet_addr_onlink(in_dev, new_gw, old_gw))
			goto reject_redirect;
		if (IN_DEV_SEC_REDIRECTS(in_dev) && ip_fib_check_default(new_gw, dev))
			goto reject_redirect;
	} else {
		if (inet_addr_type(net, new_gw) != RTN_UNICAST)
			goto reject_redirect;
	}

	n = __ipv4_neigh_lookup(rt->dst.dev, new_gw);
	if (!n)
		n = neigh_create(&arp_tbl, &new_gw, rt->dst.dev);
	if (!IS_ERR(n)) {
		if (!(n->nud_state & NUD_VALID)) {
			neigh_event_send(n, NULL);
		} else {
			if (fib_lookup(net, fl4, &res, 0) == 0) {
				struct fib_nh *nh = &FIB_RES_NH(res);

				update_or_create_fnhe(nh, fl4->daddr, new_gw,
						0, jiffies + ip_rt_gc_timeout);
			}
			if (kill_route)
				rt->dst.obsolete = DST_OBSOLETE_KILL;
			call_netevent_notifiers(NETEVENT_NEIGH_UPDATE, n);
		}
		neigh_release(n);
	}
	return;

reject_redirect:
#ifdef CONFIG_IP_ROUTE_VERBOSE
	if (IN_DEV_LOG_MARTIANS(in_dev)) {
		const struct iphdr *iph = (const struct iphdr *) skb->data;
		__be32 daddr = iph->daddr;
		__be32 saddr = iph->saddr;

		net_info_ratelimited("Redirect from %pI4 on %s about %pI4 ignored\n"
				     "  Advised path = %pI4 -> %pI4\n",
				     &old_gw, dev->name, &new_gw,
				     &saddr, &daddr);
	}
#endif
	;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞大圣

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值