Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 421842ed authored by David Ahern's avatar David Ahern Committed by David S. Miller
Browse files

net/ipv6: Add fib6_null_entry



ip6_null_entry will stay a dst based return for lookups that fail to
match an entry.

Add a new fib6_null_entry which constitutes the root node and leafs
for fibs. Replace existing references to ip6_null_entry with the
new fib6_null_entry when dealing with FIBs.

Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 14895687
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ struct netns_ipv6 {
#endif
	struct xt_table		*ip6table_nat;
#endif
	struct rt6_info         *fib6_null_entry;
	struct rt6_info		*ip6_null_entry;
	struct rt6_statistics   *rt6_stats;
	struct timer_list       ip6_fib_timer;
+13 −13
Original line number Diff line number Diff line
@@ -231,7 +231,7 @@ static struct fib6_table *fib6_alloc_table(struct net *net, u32 id)
	if (table) {
		table->tb6_id = id;
		rcu_assign_pointer(table->tb6_root.leaf,
				   net->ipv6.ip6_null_entry);
				   net->ipv6.fib6_null_entry);
		table->tb6_root.fn_flags = RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
		inet_peer_base_init(&table->tb6_peers);
	}
@@ -369,7 +369,7 @@ struct fib6_dump_arg {

static void fib6_rt_dump(struct rt6_info *rt, struct fib6_dump_arg *arg)
{
	if (rt == arg->net->ipv6.ip6_null_entry)
	if (rt == arg->net->ipv6.fib6_null_entry)
		return;
	call_fib6_entry_notifier(arg->nb, arg->net, FIB_EVENT_ENTRY_ADD, rt);
}
@@ -658,7 +658,7 @@ static struct fib6_node *fib6_add_1(struct net *net,
			/* remove null_entry in the root node */
			} else if (fn->fn_flags & RTN_TL_ROOT &&
				   rcu_access_pointer(fn->leaf) ==
				   net->ipv6.ip6_null_entry) {
				   net->ipv6.fib6_null_entry) {
				RCU_INIT_POINTER(fn->leaf, NULL);
			}

@@ -1171,9 +1171,9 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
			if (!sfn)
				goto failure;

			atomic_inc(&info->nl_net->ipv6.ip6_null_entry->rt6i_ref);
			atomic_inc(&info->nl_net->ipv6.fib6_null_entry->rt6i_ref);
			rcu_assign_pointer(sfn->leaf,
					   info->nl_net->ipv6.ip6_null_entry);
					   info->nl_net->ipv6.fib6_null_entry);
			sfn->fn_flags = RTN_ROOT;

			/* Now add the first leaf node to new subtree */
@@ -1212,7 +1212,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
			if (fn->fn_flags & RTN_TL_ROOT) {
				/* put back null_entry for root node */
				rcu_assign_pointer(fn->leaf,
					    info->nl_net->ipv6.ip6_null_entry);
					    info->nl_net->ipv6.fib6_null_entry);
			} else {
				atomic_inc(&rt->rt6i_ref);
				rcu_assign_pointer(fn->leaf, rt);
@@ -1251,7 +1251,7 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt,
				if (!pn_leaf) {
					WARN_ON(!pn_leaf);
					pn_leaf =
					    info->nl_net->ipv6.ip6_null_entry;
					    info->nl_net->ipv6.fib6_null_entry;
				}
#endif
				atomic_inc(&pn_leaf->rt6i_ref);
@@ -1494,7 +1494,7 @@ static struct rt6_info *fib6_find_prefix(struct net *net,
	struct fib6_node *child_left, *child_right;

	if (fn->fn_flags & RTN_ROOT)
		return net->ipv6.ip6_null_entry;
		return net->ipv6.fib6_null_entry;

	while (fn) {
		child_left = rcu_dereference_protected(fn->left,
@@ -1531,7 +1531,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,

	/* Set fn->leaf to null_entry for root node. */
	if (fn->fn_flags & RTN_TL_ROOT) {
		rcu_assign_pointer(fn->leaf, net->ipv6.ip6_null_entry);
		rcu_assign_pointer(fn->leaf, net->ipv6.fib6_null_entry);
		return fn;
	}

@@ -1576,7 +1576,7 @@ static struct fib6_node *fib6_repair_tree(struct net *net,
#if RT6_DEBUG >= 2
			if (!new_fn_leaf) {
				WARN_ON(!new_fn_leaf);
				new_fn_leaf = net->ipv6.ip6_null_entry;
				new_fn_leaf = net->ipv6.fib6_null_entry;
			}
#endif
			atomic_inc(&new_fn_leaf->rt6i_ref);
@@ -1726,7 +1726,7 @@ int fib6_del(struct rt6_info *rt, struct nl_info *info)
		return -ENOENT;
	}
#endif
	if (!fn || rt == net->ipv6.ip6_null_entry)
	if (!fn || rt == net->ipv6.fib6_null_entry)
		return -ENOENT;

	WARN_ON(!(fn->fn_flags & RTN_RTINFO));
@@ -2087,7 +2087,7 @@ static int __net_init fib6_net_init(struct net *net)

	net->ipv6.fib6_main_tbl->tb6_id = RT6_TABLE_MAIN;
	rcu_assign_pointer(net->ipv6.fib6_main_tbl->tb6_root.leaf,
			   net->ipv6.ip6_null_entry);
			   net->ipv6.fib6_null_entry);
	net->ipv6.fib6_main_tbl->tb6_root.fn_flags =
		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
	inet_peer_base_init(&net->ipv6.fib6_main_tbl->tb6_peers);
@@ -2099,7 +2099,7 @@ static int __net_init fib6_net_init(struct net *net)
		goto out_fib6_main_tbl;
	net->ipv6.fib6_local_tbl->tb6_id = RT6_TABLE_LOCAL;
	rcu_assign_pointer(net->ipv6.fib6_local_tbl->tb6_root.leaf,
			   net->ipv6.ip6_null_entry);
			   net->ipv6.fib6_null_entry);
	net->ipv6.fib6_local_tbl->tb6_root.fn_flags =
		RTN_ROOT | RTN_TL_ROOT | RTN_RTINFO;
	inet_peer_base_init(&net->ipv6.fib6_local_tbl->tb6_peers);
+43 −19
Original line number Diff line number Diff line
@@ -276,6 +276,15 @@ static const u32 ip6_template_metrics[RTAX_MAX] = {
	[RTAX_HOPLIMIT - 1] = 0,
};

static const struct rt6_info fib6_null_entry_template = {
	.rt6i_flags	= (RTF_REJECT | RTF_NONEXTHOP),
	.rt6i_protocol  = RTPROT_KERNEL,
	.rt6i_metric	= ~(u32)0,
	.rt6i_ref	= ATOMIC_INIT(1),
	.fib6_type	= RTN_UNREACHABLE,
	.fib6_metrics	= (struct dst_metrics *)&dst_default_metrics,
};

static const struct rt6_info ip6_null_entry_template = {
	.dst = {
		.__refcnt	= ATOMIC_INIT(1),
@@ -522,10 +531,10 @@ static inline struct rt6_info *rt6_device_match(struct net *net,
			return local;

		if (flags & RT6_LOOKUP_F_IFACE)
			return net->ipv6.ip6_null_entry;
			return net->ipv6.fib6_null_entry;
	}

	return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.ip6_null_entry : rt;
	return rt->fib6_nh.nh_flags & RTNH_F_DEAD ? net->ipv6.fib6_null_entry : rt;
}

#ifdef CONFIG_IPV6_ROUTER_PREF
@@ -758,8 +767,8 @@ static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
	bool do_rr = false;
	int key_plen;

	if (!leaf || leaf == net->ipv6.ip6_null_entry)
		return net->ipv6.ip6_null_entry;
	if (!leaf || leaf == net->ipv6.fib6_null_entry)
		return net->ipv6.fib6_null_entry;

	rt0 = rcu_dereference(fn->rr_ptr);
	if (!rt0)
@@ -776,7 +785,7 @@ static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
		key_plen = rt0->rt6i_src.plen;
#endif
	if (fn->fn_bit != key_plen)
		return net->ipv6.ip6_null_entry;
		return net->ipv6.fib6_null_entry;

	match = find_rr_leaf(fn, leaf, rt0, rt0->rt6i_metric, oif, strict,
			     &do_rr);
@@ -797,7 +806,7 @@ static struct rt6_info *rt6_select(struct net *net, struct fib6_node *fn,
		}
	}

	return match ? match : net->ipv6.ip6_null_entry;
	return match ? match : net->ipv6.fib6_null_entry;
}

static bool rt6_is_gw_or_nonexthop(const struct rt6_info *rt)
@@ -1063,7 +1072,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
restart:
	rt = rcu_dereference(fn->leaf);
	if (!rt) {
		rt = net->ipv6.ip6_null_entry;
		rt = net->ipv6.fib6_null_entry;
	} else {
		rt = rt6_device_match(net, rt, &fl6->saddr,
				      fl6->flowi6_oif, flags);
@@ -1071,7 +1080,7 @@ static struct rt6_info *ip6_pol_route_lookup(struct net *net,
			rt = rt6_multipath_select(net, rt, fl6, fl6->flowi6_oif,
						  skb, flags);
	}
	if (rt == net->ipv6.ip6_null_entry) {
	if (rt == net->ipv6.fib6_null_entry) {
		fn = fib6_backtrack(fn, &fl6->saddr);
		if (fn)
			goto restart;
@@ -1820,7 +1829,7 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
	rt = rt6_select(net, fn, oif, strict);
	if (rt->rt6i_nsiblings)
		rt = rt6_multipath_select(net, rt, fl6, oif, skb, strict);
	if (rt == net->ipv6.ip6_null_entry) {
	if (rt == net->ipv6.fib6_null_entry) {
		fn = fib6_backtrack(fn, &fl6->saddr);
		if (fn)
			goto redo_rt6_select;
@@ -1837,7 +1846,8 @@ struct rt6_info *ip6_pol_route(struct net *net, struct fib6_table *table,
	if (rt_cache)
		rt = rt_cache;

	if (rt == net->ipv6.ip6_null_entry) {
	if (rt == net->ipv6.fib6_null_entry) {
		rt = net->ipv6.ip6_null_entry;
		rcu_read_unlock();
		dst_hold(&rt->dst);
		trace_fib6_table_lookup(net, rt, table, fl6);
@@ -2412,13 +2422,13 @@ static struct rt6_info *__ip6_route_redirect(struct net *net,
	}

	if (!rt)
		rt = net->ipv6.ip6_null_entry;
		rt = net->ipv6.fib6_null_entry;
	else if (rt->rt6i_flags & RTF_REJECT) {
		rt = net->ipv6.ip6_null_entry;
		goto out;
	}

	if (rt == net->ipv6.ip6_null_entry) {
	if (rt == net->ipv6.fib6_null_entry) {
		fn = fib6_backtrack(fn, &fl6->saddr);
		if (fn)
			goto restart;
@@ -3051,7 +3061,7 @@ static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
	struct fib6_table *table;
	int err;

	if (rt == net->ipv6.ip6_null_entry) {
	if (rt == net->ipv6.fib6_null_entry) {
		err = -ENOENT;
		goto out;
	}
@@ -3081,7 +3091,7 @@ static int __ip6_del_rt_siblings(struct rt6_info *rt, struct fib6_config *cfg)
	struct fib6_table *table;
	int err = -ENOENT;

	if (rt == net->ipv6.ip6_null_entry)
	if (rt == net->ipv6.fib6_null_entry)
		goto out_put;
	table = rt->rt6i_table;
	spin_lock_bh(&table->tb6_lock);
@@ -3634,7 +3644,7 @@ static int fib6_remove_prefsrc(struct rt6_info *rt, void *arg)
	struct in6_addr *addr = ((struct arg_dev_net_ip *)arg)->addr;

	if (((void *)rt->fib6_nh.nh_dev == dev || !dev) &&
	    rt != net->ipv6.ip6_null_entry &&
	    rt != net->ipv6.fib6_null_entry &&
	    ipv6_addr_equal(addr, &rt->rt6i_prefsrc.addr)) {
		spin_lock_bh(&rt6_exception_lock);
		/* remove prefsrc entry */
@@ -3789,7 +3799,7 @@ static int fib6_ifup(struct rt6_info *rt, void *p_arg)
	const struct arg_netdev_event *arg = p_arg;
	struct net *net = dev_net(arg->dev);

	if (rt != net->ipv6.ip6_null_entry && rt->fib6_nh.nh_dev == arg->dev) {
	if (rt != net->ipv6.fib6_null_entry && rt->fib6_nh.nh_dev == arg->dev) {
		rt->fib6_nh.nh_flags &= ~arg->nh_flags;
		fib6_update_sernum_upto_root(net, rt);
		rt6_multipath_rebalance(rt);
@@ -3873,7 +3883,7 @@ static int fib6_ifdown(struct rt6_info *rt, void *p_arg)
	const struct net_device *dev = arg->dev;
	struct net *net = dev_net(dev);

	if (rt == net->ipv6.ip6_null_entry)
	if (rt == net->ipv6.fib6_null_entry)
		return 0;

	switch (arg->event) {
@@ -4624,7 +4634,7 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
	struct net *net = arg->net;

	if (rt == net->ipv6.ip6_null_entry)
	if (rt == net->ipv6.fib6_null_entry)
		return 0;

	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
@@ -4813,6 +4823,8 @@ static int ip6_route_dev_notify(struct notifier_block *this,
		return NOTIFY_OK;

	if (event == NETDEV_REGISTER) {
		net->ipv6.fib6_null_entry->fib6_nh.nh_dev = dev;
		net->ipv6.fib6_null_entry->rt6i_idev = in6_dev_get(dev);
		net->ipv6.ip6_null_entry->dst.dev = dev;
		net->ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(dev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
@@ -4826,6 +4838,7 @@ static int ip6_route_dev_notify(struct notifier_block *this,
		/* NETDEV_UNREGISTER could be fired for multiple times by
		 * netdev_wait_allrefs(). Make sure we only call this once.
		 */
		in6_dev_put_clear(&net->ipv6.fib6_null_entry->rt6i_idev);
		in6_dev_put_clear(&net->ipv6.ip6_null_entry->rt6i_idev);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
		in6_dev_put_clear(&net->ipv6.ip6_prohibit_entry->rt6i_idev);
@@ -5009,11 +5022,17 @@ static int __net_init ip6_route_net_init(struct net *net)
	if (dst_entries_init(&net->ipv6.ip6_dst_ops) < 0)
		goto out_ip6_dst_ops;

	net->ipv6.fib6_null_entry = kmemdup(&fib6_null_entry_template,
					    sizeof(*net->ipv6.fib6_null_entry),
					    GFP_KERNEL);
	if (!net->ipv6.fib6_null_entry)
		goto out_ip6_dst_entries;

	net->ipv6.ip6_null_entry = kmemdup(&ip6_null_entry_template,
					   sizeof(*net->ipv6.ip6_null_entry),
					   GFP_KERNEL);
	if (!net->ipv6.ip6_null_entry)
		goto out_ip6_dst_entries;
		goto out_fib6_null_entry;
	net->ipv6.ip6_null_entry->dst.ops = &net->ipv6.ip6_dst_ops;
	dst_init_metrics(&net->ipv6.ip6_null_entry->dst,
			 ip6_template_metrics, true);
@@ -5060,6 +5079,8 @@ static int __net_init ip6_route_net_init(struct net *net)
out_ip6_null_entry:
	kfree(net->ipv6.ip6_null_entry);
#endif
out_fib6_null_entry:
	kfree(net->ipv6.fib6_null_entry);
out_ip6_dst_entries:
	dst_entries_destroy(&net->ipv6.ip6_dst_ops);
out_ip6_dst_ops:
@@ -5068,6 +5089,7 @@ static int __net_init ip6_route_net_init(struct net *net)

static void __net_exit ip6_route_net_exit(struct net *net)
{
	kfree(net->ipv6.fib6_null_entry);
	kfree(net->ipv6.ip6_null_entry);
#ifdef CONFIG_IPV6_MULTIPLE_TABLES
	kfree(net->ipv6.ip6_prohibit_entry);
@@ -5138,6 +5160,8 @@ void __init ip6_route_init_special_entries(void)
	/* Registering of the loopback is done before this portion of code,
	 * the loopback reference in rt6_info will not be taken, do it
	 * manually for init_net */
	init_net.ipv6.fib6_null_entry->fib6_nh.nh_dev = init_net.loopback_dev;
	init_net.ipv6.fib6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
	init_net.ipv6.ip6_null_entry->dst.dev = init_net.loopback_dev;
	init_net.ipv6.ip6_null_entry->rt6i_idev = in6_dev_get(init_net.loopback_dev);
  #ifdef CONFIG_IPV6_MULTIPLE_TABLES