IPv4路由数据库之hash方式路由项的添加

本文详细介绍了IPv4路由数据库中通过fn_hash_insert添加路由项的过程,涉及fib_find_node查找路由结点,fib_find_alias查找路由项,fn_new_zone新建路由区,fib_create_info新建路由项信息,以及fib_find_info和fib_check_nh的功能。路由添加关键在于逐层深入路由表,创建并实例化相关数据结构,并加入到数据库中。
摘要由CSDN通过智能技术生成


这篇笔记从路由项的添加过程来加深对路由表组织方式的理解,hash方式路由表的路由添加回调为fn_hash_insert()。涉及的文件有:

源代码路径说明
include/net/ip_fib.hIPv4路由数据库头文件
core/ipv4/fib_hash.c哈希方式的路由数据库相关实现
core/ipv4/fib_semantics.c路由项信息相关函数实现

fn_hash_insert()

static int fn_hash_insert(struct fib_table *tb, struct fib_config *cfg)
{
	struct fn_hash *table = (struct fn_hash *) tb->tb_data;
	struct fib_node *new_f = NULL;
	struct fib_node *f;
	struct fib_alias *fa, *new_fa;
	struct fn_zone *fz;
	struct fib_info *fi;
	u8 tos = cfg->fc_tos;
	__be32 key;
	int err;

	if (cfg->fc_dst_len > 32) // IPv4的掩码长度当然最大只能32
		return -EINVAL;

    // 根据路由项目的地址的子网掩码长度找到路由区
	fz = table->fn_zones[cfg->fc_dst_len];
	// 添加的是该路由区的第一条路由,先创建路由区,目的地址子网掩码长度作为第二个入参
	if (!fz && !(fz = fn_new_zone(table, cfg->fc_dst_len)))
		return -ENOBUFS;

    // 计算路由结点在路由区哈希表中的索引key,计算的输入就是路由结点代表的目的网络地址
	key = 0;
	if (cfg->fc_dst) {
	    // 路由项中,所有的目的地址都应该只有网络号,主机号都应该为0(对于主机路由项,
	    // 子网掩码长度为32,没有主机号,所以下面条件也成立)
		if (cfg->fc_dst & ~FZ_MASK(fz))
			return -EINVAL;
		key = fz_key(cfg->fc_dst, fz);
	}

    // 根据路由配置信息查询或者新建fib_info对象
	fi = fib_create_info(cfg);
	if (IS_ERR(fi))
		return PTR_ERR(fi);

    // 如果zone中路由项太多了会导致查找性能降低,此时尝试扩展zone的哈希表
	if (fz->fz_nent > (fz->fz_divisor<<1) && fz->fz_divisor < FZ_MAX_DIVISOR &&
	    (cfg->fc_dst_len == 32 || (1 << cfg->fc_dst_len) > fz->fz_divisor))
		fn_rehash_zone(fz);

    // 根据网络号key寻找对应的node,如果node不存在,那么alias必然也不存在,
    // 否则根据tos和优先级寻找匹配的alias
	f = fib_find_node(fz, key);
	if (!f)
		fa = NULL;
	else
		fa = fib_find_alias(&f->fn_alias, tos, fi->fib_priority);

	/* Now fa, if non-NULL, points to the first fib alias
	 * with the same keys [prefix,tos,priority], if such key already
	 * exists or to the node before which we will insert new one.
	 *
	 * If fa is NULL, we will need to allocate a new one and
	 * insert to the head of f.
	 *
	 * If f is NULL, no fib node matched the destination key
	 * and we need to allocate a new one of those as well.
	 */
	// 如注释所述,路由项的目的地址网络号、tos、优先级三个字段决定了是否是同一个fib_alias
	if (fa && fa->fa_tos == tos && fa->fa_info->fib_priority == fi->fib_priority) {
		struct fib_alias *fa_first, *fa_match;

		err = -EEXIST;
		if (cfg->fc_nlflags & NLM_F_EXCL) // 有相同路由项,但是参数指定了不能替换已有项
			goto out;

		/* We have 2 goals:
		 * 1. Find exact match for type, scope, fib_info to avoid
		 * duplicate routes
		 * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
		 */
		// [prefix,tos,priority]三者只是较为粗略的匹配,下面查找精确的匹配fib_alias对象
		fa_match = NULL;
		// 由于路由结点中fa_alias对象在插入时也是有序的,所以只需要从当前fa的位置往后找即可
		fa_first = fa;
		fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
		list_for_each_entry_continue(fa, &f->fn_alias, fa_list) {
			if (fa->fa_tos != tos)
				break;
			if (fa->fa_info->fib_priority != fi->fib_priority)
				break;
			if (fa->fa_type == cfg->fc_type &&
			    fa->fa_scope == cfg->fc_scope &&
			    fa->fa_info == fi) {
				fa_match = fa; // 路由项完全相同
				break;
			}
		}
        // 插入的路由项和已有路由项完全相同,并且指定了替换标记,那么替换旧的路由项,完成插入过程
		if (cfg->fc_nlflags & NLM_F_REPLACE) {
			struct fib_info *fi_drop;
			u8 state;

			fa = fa_first;
			if (fa_match) {
				if (fa == fa_match)
					err = 0;
				goto out;
			}
			write_lock_bh(&fib_hash_lock);
			fi_drop = fa->fa_info;
			fa->fa_info = fi;
			fa->fa_type = cfg->fc_type;
			fa->fa_scope = cfg->fc_scope;
			state = fa->fa_state;
			fa->fa_state &= ~FA_S_ACCESSED;
			fib_hash_genid++;
			write_unlock_bh(&fib_hash_lock);

			fib_release_info(fi_drop);
			if (state & FA_S_ACCESSED)
				rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
			rtmsg_fib(RTM_NEWROUTE, key, fa, cfg->fc_dst_len, tb->tb_id,
				  &cfg->fc_nlinfo, NLM_F_REPLACE);
			return 0;
		}

		/* Error if we find a perfect match which
		 * uses the same scope, type, and nexthop
		 * information.
		 */
		if (fa_match)
			goto out;

		if (!(cfg->fc_nlflags & NLM_F_APPEND))
			fa = fa_first;
	}
    // 路由项尚不存在,尝试新建它
	err = -ENOENT;
	if (!(cfg->fc_nlflags & NLM_F_CREATE))
		goto out;

	err = -ENOBUFS;
	if (!f) {
	    // 路由节点也不存在,新建路由节点
		new_f = kmem_cache_zalloc(fn_hash_kmem, GFP_KERNEL);
		if (new_f == NULL)
			goto out;

		INIT_HLIST_NODE(&new_f->fn_hash);
		INIT_LIST_HEAD(&new_f->fn_alias);
		new_f->fn_key = key;
		f = new_f;
	}

    // 为路由别名赋值,第一条路由会直接使用路由结点中内嵌的路由别名fn_embedded_alias,
    // 后续其它路由项会分配新的路由别名
	new_fa = &f->fn_embedded_alias;
	if (new_fa->fa_info != NULL) {
		new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
		if (new_fa == NULL)
			goto out;
	}
	new_fa->fa_info = fi;
	new_fa->fa_tos = tos;
	new_fa->fa_type = cfg->fc_type;
	new_fa->fa_scope = cfg->fc_scope;
	new_fa->fa_state = 0;

	/*
	 * Insert new entry to the list.
	 */
	write_lock_bh(&fib_hash_lock);
	if (new_f) 
	    // 将新建的路由结点插入路由区中
		fib_insert_node(fz, new_f);
	// 将新的路由别名插入路由结点的链表中
	list_add_tail(&new_fa->fa_list, (fa ? &fa->fa_list : &f->fn_alias));
	fib_hash_genid++;
	write_unlock_bh(&fib_hash_lock);

	if (new_f)
	    // 累加路由区中的路由项计数器
		fz->fz_nent++;
	// 刷新路由缓存
	rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
    // 向用户态太发送路由添加Netlink消息
	rtmsg_fib(RTM_NEWROUTE, key, new_fa, cfg->fc_dst_len, tb->tb_id,
		  &cfg->fc_nlinfo, 0);
	return 0;

out:
	if (new_f)
		kmem_cache_free(fn_hash_kmem, new_f);
	fib_release_info(fi);
	return err;
}

查找路由结点: fib_find_node()

该函数从路由区的哈希表中查找对应条件的路由结点,入参已经指定了应该要遍历哈希表的哪个冲突链。

/* Return the node in FZ matching KEY. */
static struct fib_node *fib_find_node(struct fn_zone *fz, __be32 key)
{
	struct hlist_head *head = &fz->fz_hash[fn_hash(key, fz)];
	struct hlist_node *node;
	struct fib_node *f;
    // 匹配条件key实际上就是该路由结点对应的目的网络地址
	hlist_for_each_entry(f, node, head, fn_hash) {
		if (f->fn_key == key)
			return f;
	}
	return NULL;
}

查找路由项: fib_find_alias()

该函数的实现让人较为迷惑,使用查找结果时要求tos和priority都相等,为何查找时不直接精确匹配。

从实现看,该函数返回的是fib_alias对象需要满足:1)tos小于等于入参;或者2)优先级更低。

/* Return the first fib alias matching TOS with priority less than or equal to PRIO. */
struct fib_alias *fib_find_alias(struct list_head *fah, u8 tos, u32 prio)
{
	if (fah) {
		struct fib_alias *fa;
		list_for_each_entry(fa, fah, fa_list) {
			if (fa->fa_tos > tos)
				continue;
			if (fa->fa_info->fib_priority >= prio || fa->fa_tos < tos)
				return fa;
		}
	}
	return NULL;
}

新建路由区: fn_new_zone()

static struct fn_zone *fn_new_zone(struct fn_hash *table, int z)
{
	int i;
	// 分配路由区内存
	struct fn_zone *fz = kzalloc(sizeof(struct fn_zone), GFP_KERNEL);
	if (!fz)
		return NULL;

    // 设置路由区初始哈希桶大小,初始为16,特殊的,对于z==0(子网掩码长度为0,即主机路由项)
    // 只需要有一个哈希表即可
	if (z) {
		fz->fz_divisor = 16;
	} else {
		fz->fz_divisor = 1;
	}
	fz->fz_hashmask = (fz->fz_divisor - 1);
	// 分配哈希表用于保存路由结点
	fz->fz_hash = fz_hash_alloc(fz->fz_divisor);
	if (!fz->fz_hash) {
		kfree(fz);
		return NULL;
	}
	fz->fz_order = z;
	fz->fz_mask = inet_make_mask(z);

	// 将新建的路由区插入到路由表的fn_zone_list链表中,是按照子网掩码长度由长到短顺序插入的
	for (i=z+1; i<=32; i++)
		if (table->fn_zones[i])
			break;
	write_lock_bh(&fib_hash_lock);
	if (i>32) {
		/* No more specific masks, we are the first. */
		fz->fz_next = table->fn_zone_list;
		table->fn_zone_list = fz;
	} else {
		fz->fz_next = table->fn_zones[i]->fz_next;
		table->fn_zones[i]->fz_next = fz;
	}
	table->fn_zones[z] = fz;
	fib_hash_genid++;
	write_unlock_bh(&fib_hash_lock);
	return fz;
}

新建路由项信息: fib_create_info()

struct fib_info *fib_create_info(struct fib_config *cfg)
{
	int err;
	struct fib_info *fi = NULL;
	struct fib_info *ofi;
	int nhs = 1; // 不开启多路径路由时,每个路由项默认只有一个下一跳
	struct net *net = cfg->fc_nlinfo.nl_net;

	// 检查路由项的type和scope是否设置合理,比如type为RTN_LOCAL,表示路由项目的地址是一个本地地址,
	// 但是scope却指定为RT_SCOPE_UNIVERSE,显然是矛盾的,注意scope值越大,表示距离本机越远
	if (fib_props[cfg->fc_type].scope > cfg->fc_scope)
		goto err_inval;

#ifdef CONFIG_IP_ROUTE_MULTIPATH
    // 多路径路由的情况下,路由项可以有多个下一跳地址
	if (cfg->fc_mp) {
		nhs = fib_count_nexthops(cfg->fc_mp, cfg->fc_mp_len);
		if (nhs == 0)
			goto err_inval;
	}
#endif

	err = -ENOBUFS;
	if (fib_info_cnt >= fib_hash_size) {
	    // 对哈希表扩容
		unsigned int new_size = fib_hash_size << 1;
		struct hlist_head *new_info_hash;
		struct hlist_head *new_laddrhash;
		unsigned int bytes;

		if (!new_size)
			new_size = 1;
		bytes = new_size * sizeof(struct hlist_head *);
		new_info_hash = fib_hash_alloc(bytes);
		new_laddrhash = fib_hash_alloc(bytes);
		if (!new_info_hash || !new_laddrhash) {
			fib_hash_free(new_info_hash, bytes);
			fib_hash_free(new_laddrhash, bytes);
		} else
			fib_hash_move(new_info_hash, new_laddrhash, new_size);

		if (!fib_hash_size)
			goto failure;
	}

    // 分配fib_info对象和下一跳地址结构,nhs是下一跳个数
	fi = kzalloc(sizeof(*fi)+nhs*sizeof(struct fib_nh), GFP_KERNEL);
	if (fi == NULL)
		goto failure;
	fib_info_cnt++; // 累加已分配路由项信息个数
    // 配置字段赋值
	fi->fib_net = hold_net(net);
	fi->fib_protocol = cfg->fc_protocol;
	fi->fib_flags = cfg->fc_flags;
	fi->fib_priority = cfg->fc_priority;
	fi->fib_prefsrc = cfg->fc_prefsrc;

    // 关联下一跳nh对象和fib_info对象
	fi->fib_nhs = nhs;
	change_nexthops(fi) {
		nh->nh_parent = fi;
	} endfor_nexthops(fi)

	if (cfg->fc_mx) {
	    // 提取路由项的metric指标
		struct nlattr *nla;
		int remaining;

		nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) {
			int type = nla_type(nla);
			if (type) {
				if (type > RTAX_MAX)
					goto err_inval;
				fi->fib_metrics[type - 1] = nla_get_u32(nla);
			}
		}
	}

    // 根据是否配置多路径设置下一跳信息
	if (cfg->fc_mp) {
#ifdef CONFIG_IP_ROUTE_MULTIPATH
		err = fib_get_nhs(fi, cfg->fc_mp, cfg->fc_mp_len, cfg);
		if (err != 0)
			goto failure;
		if (cfg->fc_oif && fi->fib_nh->nh_oif != cfg->fc_oif)
			goto err_inval;
		if (cfg->fc_gw && fi->fib_nh->nh_gw != cfg->fc_gw)
			goto err_inval;
#ifdef CONFIG_NET_CLS_ROUTE
		if (cfg->fc_flow && fi->fib_nh->nh_tclassid != cfg->fc_flow)
			goto err_inval;
#endif
#else
		goto err_inval;
#endif
	} else {
	    // 只有一个下一跳的场景
		struct fib_nh *nh = fi->fib_nh;

		nh->nh_oif = cfg->fc_oif;
		nh->nh_gw = cfg->fc_gw;
		nh->nh_flags = cfg->fc_flags;
#ifdef CONFIG_NET_CLS_ROUTE
		nh->nh_tclassid = cfg->fc_flow;
#endif
#ifdef CONFIG_IP_ROUTE_MULTIPATH
		nh->nh_weight = 1;
#endif
	}

	if (fib_props[cfg->fc_type].error) {
		if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp)
			goto err_inval;
		goto link_it;
	}

	if (cfg->fc_scope > RT_SCOPE_HOST) // 配置目的地比本机还近的路由项是没有意义的
		goto err_inval;

    // 检查下一跳配置信息的有效性
	if (cfg->fc_scope == RT_SCOPE_HOST) {
	    // 配置的是到达本机的路由,只能有一个下一跳,并且网络设备要存在
		struct fib_nh *nh = fi->fib_nh;

		/* Local address is added. */
		if (nhs != 1 || nh->nh_gw)
			goto err_inval;
		nh->nh_scope = RT_SCOPE_NOWHERE; // 修改scope
		nh->nh_dev = dev_get_by_index(net, fi->fib_nh->nh_oif); // 检查出口网络设备是否存在
		err = -ENODEV;
		if (nh->nh_dev == NULL)
			goto failure;
	} else {
	    // 使用fib_check_nh()函数检查到达其它地址的下一跳是否有效
		change_nexthops(fi) {
			if ((err = fib_check_nh(cfg, fi, nh)) != 0)
				goto failure;
		} endfor_nexthops(fi)
	}
    // 检查优选源IP地址的有效性
	if (fi->fib_prefsrc) {
	    // 对于目的地址非本机地址的路由,其源IP地址当然也不能只是仅主机范围内能够使用的IP地址
		if (cfg->fc_type != RTN_LOCAL || !cfg->fc_dst || fi->fib_prefsrc != cfg->fc_dst)
			if (inet_addr_type(net, fi->fib_prefsrc) != RTN_LOCAL)
				goto err_inval;
	}

link_it:
    // 查找是否已经存在符合条件的路由项信息对象可以复用
	if ((ofi = fib_find_info(fi)) != NULL) {
	    // 可以复用,增加已有路由项信息对象引用,释放新建的路由项信息对象,然后返回
		fi->fib_dead = 1;
		free_fib_info(fi);
		ofi->fib_treeref++;
		return ofi;
	}
	
	// 下面的逻辑对应新增一个路由项信息的场景

	fi->fib_treeref++;
	atomic_inc(&fi->fib_clntref);
	// 将新建的路由项信息对象加入全局的fib_info_hash哈希表
	spin_lock_bh(&fib_info_lock);
	hlist_add_head(&fi->fib_hash, &fib_info_hash[fib_info_hashfn(fi)]);
	if (fi->fib_prefsrc) {
	    // 若指定了优选源IP地址,也将新建的路由项信息对象加入全局的fib_laddr_hashfn哈希表
		struct hlist_head *head;
		head = &fib_info_laddrhash[fib_laddr_hashfn(fi->fib_prefsrc)];
		hlist_add_head(&fi->fib_lhash, head);
	}
	// 将该路由项信息中所有的下一跳对象保存到全局哈希表fib_info_devhash中
	change_nexthops(fi) {
		struct hlist_head *head;
		unsigned int hash;

		if (!nh->nh_dev)
			continue;
		hash = fib_devindex_hashfn(nh->nh_dev->ifindex);
		head = &fib_info_devhash[hash];
		hlist_add_head(&nh->nh_hash, head);
	} endfor_nexthops(fi)
	spin_unlock_bh(&fib_info_lock);
	return fi;

err_inval:
	err = -EINVAL;

failure:
	if (fi) {
		fi->fib_dead = 1;
		free_fib_info(fi);
	}
	return ERR_PTR(err);
}

综上,fib_create_info()实际上做了许多事情,总结如下:

  1. 对配置的参数进行一系列的合法性检查;
  2. 参数都ok,那么如果该fib_info实例已经有了,则复用已有的,如果没有则用新的;
  3. 维护fib_info和fib_nh在全局链表中的数据结构;

查找路由项信息: fib_find_info()

该函数通过查找全局的fib_info_hash哈希表,判断其中是否已经有和参数相同的路由项信息对象,如果有那么返回哈希表中fib_info对象指针。

static struct fib_info *fib_find_info(const struct fib_info *nfi)
{
	struct hlist_head *head;
	struct hlist_node *node;
	struct fib_info *fi;
	unsigned int hash;

    // 遍历冲突链
	hash = fib_info_hashfn(nfi);
	head = &fib_info_hash[hash];
	hlist_for_each_entry(fi, node, head, fib_hash) {
		if (fi->fib_net != nfi->fib_net) // 需要属于同一个网络命名空间
			continue;
		if (fi->fib_nhs != nfi->fib_nhs) // 下一跳个数要一致
			continue;
		// 如下字段也要相同,可见已经涵盖了路由项信息对象中的大多数字段
		if (nfi->fib_protocol == fi->fib_protocol &&
		    nfi->fib_prefsrc == fi->fib_prefsrc &&
		    nfi->fib_priority == fi->fib_priority &&
		    memcmp(nfi->fib_metrics, fi->fib_metrics, sizeof(fi->fib_metrics)) == 0 &&
		    ((nfi->fib_flags^fi->fib_flags)&~RTNH_F_DEAD) == 0 &&
		    (nfi->fib_nhs == 0 || nh_comp(fi, nfi) == 0)) // 下一跳对象比较
			return fi;
	}

	return NULL;
}

下一跳地址检查: fib_check_nh()

/*
   Picture
   -------

   Semantics of nexthop is very messy by historical reasons.
   We have to take into account, that:
   a) gateway can be actually local interface address,
      so that gatewayed route is direct.
   b) gateway must be on-link address, possibly
      described not by an ifaddr, but also by a direct route.
   c) If both gateway and interface are specified, they should not
      contradict.
   d) If we use tunnel routes, gateway could be not on-link.

   Attempt to reconcile all of these (alas, self-contradictory) conditions
   results in pretty ugly and hairy code with obscure logic.

   I chose to generalized it instead, so that the size
   of code does not increase practically, but it becomes
   much more general.
   Every prefix is assigned a "scope" value: "host" is local address,
   "link" is direct route,
   [ ... "site" ... "interior" ... ]
   and "universe" is true gateway route with global meaning.

   Every prefix refers to a set of "nexthop"s (gw, oif),
   where gw must have narrower scope. This recursion stops
   when gw has LOCAL scope or if "nexthop" is declared ONLINK,
   which means that gw is forced to be on link.

   Code is still hairy, but now it is apparently logically
   consistent and very flexible. F.e. as by-product it allows
   to co-exists in peace independent exterior and interior
   routing processes.

   Normally it looks as following.

   {universe prefix}  -> (gw, oif) [scope link]
			  |
			  |-> {link prefix} -> (gw, oif) [scope local]
						|
						|-> {local prefix} (terminal node)
 */
static int fib_check_nh(struct fib_config *cfg, struct fib_info *fi, struct fib_nh *nh)
{
	int err;
	struct net *net;

	net = cfg->fc_nlinfo.nl_net;
	// 按照是否指定网关,分两种情况处理。一般来讲,只有配置非本地网络路由时才需要指定网关,
	// 因为这种情况需要明确告诉路由子系统,下一跳的IP地址是什么
	if (nh->nh_gw) {
		struct fib_result res;

#ifdef CONFIG_IP_ROUTE_PERVASIVE
		if (nh->nh_flags&RTNH_F_PERVASIVE)
			return 0;
#endif
		if (nh->nh_flags&RTNH_F_ONLINK) {
			struct net_device *dev;

			if (cfg->fc_scope >= RT_SCOPE_LINK)
				return -EINVAL;
			if (inet_addr_type(net, nh->nh_gw) != RTN_UNICAST)
				return -EINVAL;
			if ((dev = __dev_get_by_index(net, nh->nh_oif)) == NULL)
				return -ENODEV;
			if (!(dev->flags&IFF_UP))
				return -ENETDOWN;
			nh->nh_dev = dev;
			dev_hold(dev);
			nh->nh_scope = RT_SCOPE_LINK;
			return 0;
		}
		{
			struct flowi fl = {
				.nl_u = {
					.ip4_u = {
						.daddr = nh->nh_gw,
						.scope = cfg->fc_scope + 1,
					},
				},
				.oif = nh->nh_oif,
			};

			/* It is not necessary, but requires a bit of thinking */
			if (fl.fl4_scope < RT_SCOPE_LINK)
				fl.fl4_scope = RT_SCOPE_LINK;
			if ((err = fib_lookup(net, &fl, &res)) != 0)
				return err;
		}
		err = -EINVAL;
		if (res.type != RTN_UNICAST && res.type != RTN_LOCAL)
			goto out;
		nh->nh_scope = res.scope;
		nh->nh_oif = FIB_RES_OIF(res);
		if ((nh->nh_dev = FIB_RES_DEV(res)) == NULL)
			goto out;
		dev_hold(nh->nh_dev);
		err = -ENETDOWN;
		if (!(nh->nh_dev->flags & IFF_UP))
			goto out;
		err = 0;
out:
		fib_res_put(&res);
		return err;
	} else {
	    // 没有指定网关场景的下一跳地址检查
		struct in_device *in_dev;

		if (nh->nh_flags&(RTNH_F_PERVASIVE|RTNH_F_ONLINK))
			return -EINVAL;
        // 出口网络设备必须是UP状态
		in_dev = inetdev_by_index(net, nh->nh_oif);
		if (in_dev == NULL)
			return -ENODEV;
		if (!(in_dev->dev->flags&IFF_UP)) {
			in_dev_put(in_dev);
			return -ENETDOWN;
		}
		nh->nh_dev = in_dev->dev;
		dev_hold(nh->nh_dev);
		nh->nh_scope = RT_SCOPE_HOST;
		in_dev_put(in_dev);
	}
	return 0;
}

总结

综上,理解路由添加过程是非常重要的,结合它可以理解路由数据库相关的核心数据结构之间的组织关系。路由添加的核心思想实际上非常简单,就是找到要操作的路由表,然后根据路由数据库的数据结构,层层递进,最终实例化fib_node、fib_alias、fib_info、fib_nh,然后将这些数据结构加入到数据库中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值