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

Commit 1af5a8c4 authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller
Browse files

[IPV4]: Increase number of possible routing tables to 2^32



Increase the number of possible routing tables to 2^32 by replacing the
fixed sized array of pointers by a hash table and replacing iterations
over all possible table IDs by hash table walking.

Signed-off-by: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 9e762a4a
Loading
Loading
Loading
Loading
+5 −20
Original line number Diff line number Diff line
@@ -150,6 +150,7 @@ struct fib_result_nl {
#endif /* CONFIG_IP_ROUTE_MULTIPATH_WRANDOM */

struct fib_table {
	struct hlist_node tb_hlist;
	u32		tb_id;
	unsigned	tb_stamp;
	int		(*tb_lookup)(struct fib_table *tb, const struct flowi *flp, struct fib_result *res);
@@ -200,29 +201,13 @@ static inline void fib_select_default(const struct flowi *flp, struct fib_result
}

#else /* CONFIG_IP_MULTIPLE_TABLES */
#define ip_fib_local_table (fib_tables[RT_TABLE_LOCAL])
#define ip_fib_main_table (fib_tables[RT_TABLE_MAIN])
#define ip_fib_local_table fib_get_table(RT_TABLE_LOCAL)
#define ip_fib_main_table fib_get_table(RT_TABLE_MAIN)

extern struct fib_table * fib_tables[RT_TABLE_MAX+1];
extern int fib_lookup(struct flowi *flp, struct fib_result *res);
extern struct fib_table *__fib_new_table(u32 id);

static inline struct fib_table *fib_get_table(u32 id)
{
	if (id == 0)
		id = RT_TABLE_MAIN;

	return fib_tables[id];
}

static inline struct fib_table *fib_new_table(u32 id)
{
	if (id == 0)
		id = RT_TABLE_MAIN;

	return fib_tables[id] ? : __fib_new_table(id);
}

extern struct fib_table *fib_new_table(u32 id);
extern struct fib_table *fib_get_table(u32 id);
extern void fib_select_default(const struct flowi *flp, struct fib_result *res);

#endif /* CONFIG_IP_MULTIPLE_TABLES */
+68 −34
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/init.h>
#include <linux/list.h>

#include <net/ip.h>
#include <net/protocol.h>
@@ -51,48 +52,67 @@

#ifndef CONFIG_IP_MULTIPLE_TABLES

#define RT_TABLE_MIN RT_TABLE_MAIN

struct fib_table *ip_fib_local_table;
struct fib_table *ip_fib_main_table;

#else
#define FIB_TABLE_HASHSZ 1
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];

#define RT_TABLE_MIN 1
#else

struct fib_table *fib_tables[RT_TABLE_MAX+1];
#define FIB_TABLE_HASHSZ 256
static struct hlist_head fib_table_hash[FIB_TABLE_HASHSZ];

struct fib_table *__fib_new_table(u32 id)
struct fib_table *fib_new_table(u32 id)
{
	struct fib_table *tb;
	unsigned int h;

	if (id == 0)
		id = RT_TABLE_MAIN;
	tb = fib_get_table(id);
	if (tb)
		return tb;
	tb = fib_hash_init(id);
	if (!tb)
		return NULL;
	fib_tables[id] = tb;
	h = id & (FIB_TABLE_HASHSZ - 1);
	hlist_add_head_rcu(&tb->tb_hlist, &fib_table_hash[h]);
	return tb;
}

struct fib_table *fib_get_table(u32 id)
{
	struct fib_table *tb;
	struct hlist_node *node;
	unsigned int h;

	if (id == 0)
		id = RT_TABLE_MAIN;
	h = id & (FIB_TABLE_HASHSZ - 1);
	rcu_read_lock();
	hlist_for_each_entry_rcu(tb, node, &fib_table_hash[h], tb_hlist) {
		if (tb->tb_id == id) {
			rcu_read_unlock();
			return tb;
		}
	}
	rcu_read_unlock();
	return NULL;
}
#endif /* CONFIG_IP_MULTIPLE_TABLES */


static void fib_flush(void)
{
	int flushed = 0;
#ifdef CONFIG_IP_MULTIPLE_TABLES
	struct fib_table *tb;
	u32 id;
	struct hlist_node *node;
	unsigned int h;

	for (id = RT_TABLE_MAX; id>0; id--) {
		if ((tb = fib_get_table(id))==NULL)
			continue;
	for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
		hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist)
			flushed += tb->tb_flush(tb);
	}
#else /* CONFIG_IP_MULTIPLE_TABLES */
	flushed += ip_fib_main_table->tb_flush(ip_fib_main_table);
	flushed += ip_fib_local_table->tb_flush(ip_fib_local_table);
#endif /* CONFIG_IP_MULTIPLE_TABLES */

	if (flushed)
		rt_cache_flush(-1);
@@ -334,29 +354,37 @@ int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)

int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
{
	u32 t;
	u32 s_t;
	unsigned int h, s_h;
	unsigned int e = 0, s_e;
	struct fib_table *tb;
	struct hlist_node *node;
	int dumped = 0;

	if (NLMSG_PAYLOAD(cb->nlh, 0) >= sizeof(struct rtmsg) &&
	    ((struct rtmsg*)NLMSG_DATA(cb->nlh))->rtm_flags&RTM_F_CLONED)
		return ip_rt_dump(skb, cb);

	s_t = cb->args[0];
	if (s_t == 0)
		s_t = cb->args[0] = RT_TABLE_MIN;

	for (t=s_t; t<=RT_TABLE_MAX; t++) {
		if (t < s_t) continue;
		if (t > s_t)
			memset(&cb->args[1], 0, sizeof(cb->args)-sizeof(cb->args[0]));
		if ((tb = fib_get_table(t))==NULL)
			continue;
	s_h = cb->args[0];
	s_e = cb->args[1];

	for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
		e = 0;
		hlist_for_each_entry(tb, node, &fib_table_hash[h], tb_hlist) {
			if (e < s_e)
				goto next;
			if (dumped)
				memset(&cb->args[2], 0, sizeof(cb->args) -
				                 2 * sizeof(cb->args[0]));
			if (tb->tb_dump(tb, skb, cb) < 0)
			break;
				goto out;
			dumped = 1;
next:
			e++;
		}

	cb->args[0] = t;
	}
out:
	cb->args[1] = e;
	cb->args[0] = h;

	return skb->len;
}
@@ -654,9 +682,15 @@ static struct notifier_block fib_netdev_notifier = {

void __init ip_fib_init(void)
{
	unsigned int i;

	for (i = 0; i < FIB_TABLE_HASHSZ; i++)
		INIT_HLIST_HEAD(&fib_table_hash[i]);
#ifndef CONFIG_IP_MULTIPLE_TABLES
	ip_fib_local_table = fib_hash_init(RT_TABLE_LOCAL);
	hlist_add_head_rcu(&ip_fib_local_table->tb_hlist, &fib_table_hash[0]);
	ip_fib_main_table  = fib_hash_init(RT_TABLE_MAIN);
	hlist_add_head_rcu(&ip_fib_main_table->tb_hlist, &fib_table_hash[0]);
#else
	fib4_rules_init();
#endif
+13 −13
Original line number Diff line number Diff line
@@ -684,7 +684,7 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb,
	struct fib_node *f;
	int i, s_i;

	s_i = cb->args[3];
	s_i = cb->args[4];
	i = 0;
	hlist_for_each_entry(f, node, head, fn_hash) {
		struct fib_alias *fa;
@@ -704,14 +704,14 @@ fn_hash_dump_bucket(struct sk_buff *skb, struct netlink_callback *cb,
					  fa->fa_tos,
					  fa->fa_info,
					  NLM_F_MULTI) < 0) {
				cb->args[3] = i;
				cb->args[4] = i;
				return -1;
			}
		next:
			i++;
		}
	}
	cb->args[3] = i;
	cb->args[4] = i;
	return skb->len;
}

@@ -722,21 +722,21 @@ fn_hash_dump_zone(struct sk_buff *skb, struct netlink_callback *cb,
{
	int h, s_h;

	s_h = cb->args[2];
	s_h = cb->args[3];
	for (h=0; h < fz->fz_divisor; h++) {
		if (h < s_h) continue;
		if (h > s_h)
			memset(&cb->args[3], 0,
			       sizeof(cb->args) - 3*sizeof(cb->args[0]));
			memset(&cb->args[4], 0,
			       sizeof(cb->args) - 4*sizeof(cb->args[0]));
		if (fz->fz_hash == NULL ||
		    hlist_empty(&fz->fz_hash[h]))
			continue;
		if (fn_hash_dump_bucket(skb, cb, tb, fz, &fz->fz_hash[h])<0) {
			cb->args[2] = h;
			cb->args[3] = h;
			return -1;
		}
	}
	cb->args[2] = h;
	cb->args[3] = h;
	return skb->len;
}

@@ -746,21 +746,21 @@ static int fn_hash_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
	struct fn_zone *fz;
	struct fn_hash *table = (struct fn_hash*)tb->tb_data;

	s_m = cb->args[1];
	s_m = cb->args[2];
	read_lock(&fib_hash_lock);
	for (fz = table->fn_zone_list, m=0; fz; fz = fz->fz_next, m++) {
		if (m < s_m) continue;
		if (m > s_m)
			memset(&cb->args[2], 0,
			       sizeof(cb->args) - 2*sizeof(cb->args[0]));
			memset(&cb->args[3], 0,
			       sizeof(cb->args) - 3*sizeof(cb->args[0]));
		if (fn_hash_dump_zone(skb, cb, tb, fz) < 0) {
			cb->args[1] = m;
			cb->args[2] = m;
			read_unlock(&fib_hash_lock);
			return -1;
		}
	}
	read_unlock(&fib_hash_lock);
	cb->args[1] = m;
	cb->args[2] = m;
	return skb->len;
}

+2 −2
Original line number Diff line number Diff line
@@ -172,8 +172,8 @@ static struct fib_table *fib_empty_table(void)
	u32 id;

	for (id = 1; id <= RT_TABLE_MAX; id++)
		if (fib_tables[id] == NULL)
			return __fib_new_table(id);
		if (fib_get_table(id) == NULL)
			return fib_new_table(id);
	return NULL;
}

+13 −13
Original line number Diff line number Diff line
@@ -1848,7 +1848,7 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi

	u32 xkey = htonl(key);

	s_i = cb->args[3];
	s_i = cb->args[4];
	i = 0;

	/* rcu_read_lock is hold by caller */
@@ -1870,12 +1870,12 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah, struct fi
				  plen,
				  fa->fa_tos,
				  fa->fa_info, 0) < 0) {
			cb->args[3] = i;
			cb->args[4] = i;
			return -1;
		}
		i++;
	}
	cb->args[3] = i;
	cb->args[4] = i;
	return skb->len;
}

@@ -1886,14 +1886,14 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str
	struct list_head *fa_head;
	struct leaf *l = NULL;

	s_h = cb->args[2];
	s_h = cb->args[3];

	for (h = 0; (l = nextleaf(t, l)) != NULL; h++) {
		if (h < s_h)
			continue;
		if (h > s_h)
			memset(&cb->args[3], 0,
			       sizeof(cb->args) - 3*sizeof(cb->args[0]));
			memset(&cb->args[4], 0,
			       sizeof(cb->args) - 4*sizeof(cb->args[0]));

		fa_head = get_fa_head(l, plen);

@@ -1904,11 +1904,11 @@ static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb, str
			continue;

		if (fn_trie_dump_fa(l->key, plen, fa_head, tb, skb, cb)<0) {
			cb->args[2] = h;
			cb->args[3] = h;
			return -1;
		}
	}
	cb->args[2] = h;
	cb->args[3] = h;
	return skb->len;
}

@@ -1917,23 +1917,23 @@ static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb, struct netlin
	int m, s_m;
	struct trie *t = (struct trie *) tb->tb_data;

	s_m = cb->args[1];
	s_m = cb->args[2];

	rcu_read_lock();
	for (m = 0; m <= 32; m++) {
		if (m < s_m)
			continue;
		if (m > s_m)
			memset(&cb->args[2], 0,
				sizeof(cb->args) - 2*sizeof(cb->args[0]));
			memset(&cb->args[3], 0,
				sizeof(cb->args) - 3*sizeof(cb->args[0]));

		if (fn_trie_dump_plen(t, 32-m, tb, skb, cb)<0) {
			cb->args[1] = m;
			cb->args[2] = m;
			goto out;
		}
	}
	rcu_read_unlock();
	cb->args[1] = m;
	cb->args[2] = m;
	return skb->len;
out:
	rcu_read_unlock();