]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - net/ipv4/fib_frontend.c
[IPV4]: Increase number of possible routing tables to 2^32
[net-next-2.6.git] / net / ipv4 / fib_frontend.c
index cdde963909603aae61b7398afb09112dde407e52..ad4c14f968a132b98804687decfba81ed6516cb0 100644 (file)
@@ -15,7 +15,6 @@
  *             2 of the License, or (at your option) any later version.
  */
 
-#include <linux/config.h>
 #include <linux/module.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <linux/inet.h>
 #include <linux/inetdevice.h>
 #include <linux/netdevice.h>
+#include <linux/if_addr.h>
 #include <linux/if_arp.h>
 #include <linux/skbuff.h>
 #include <linux/netlink.h>
 #include <linux/init.h>
+#include <linux/list.h>
 
 #include <net/ip.h>
 #include <net/protocol.h>
 
 #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(int 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;
-       int id;
+       struct hlist_node *node;
+       unsigned int h;
 
-       for (id = RT_TABLE_MAX; id>0; id--) {
-               if ((tb = fib_get_table(id))==NULL)
-                       continue;
-               flushed += tb->tb_flush(tb);
+       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);
@@ -294,7 +314,8 @@ static int inet_check_attr(struct rtmsg *r, struct rtattr **rta)
                if (attr) {
                        if (RTA_PAYLOAD(attr) < 4)
                                return -EINVAL;
-                       if (i != RTA_MULTIPATH && i != RTA_METRICS)
+                       if (i != RTA_MULTIPATH && i != RTA_METRICS &&
+                           i != RTA_TABLE)
                                *rta = (struct rtattr*)RTA_DATA(attr);
                }
        }
@@ -310,7 +331,7 @@ int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (inet_check_attr(r, rta))
                return -EINVAL;
 
-       tb = fib_get_table(r->rtm_table);
+       tb = fib_get_table(rtm_get_table(rta, r->rtm_table));
        if (tb)
                return tb->tb_delete(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
        return -ESRCH;
@@ -325,7 +346,7 @@ int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg)
        if (inet_check_attr(r, rta))
                return -EINVAL;
 
-       tb = fib_new_table(r->rtm_table);
+       tb = fib_new_table(rtm_get_table(rta, r->rtm_table));
        if (tb)
                return tb->tb_insert(tb, r, (struct kern_rta*)rta, nlh, &NETLINK_CB(skb));
        return -ENOBUFS;
@@ -333,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)
 {
-       int t;
-       int 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;
-               if (tb->tb_dump(tb, skb, cb) < 0) 
-                       break;
+       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)
+                               goto out;
+                       dumped = 1;
+next:
+                       e++;
+               }
        }
-
-       cb->args[0] = t;
+out:
+       cb->args[1] = e;
+       cb->args[0] = h;
 
        return skb->len;
 }
@@ -653,11 +682,17 @@ 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
-       fib_rules_init();
+       fib4_rules_init();
 #endif
 
        register_netdevice_notifier(&fib_netdev_notifier);
@@ -666,3 +701,4 @@ void __init ip_fib_init(void)
 }
 
 EXPORT_SYMBOL(inet_addr_type);
+EXPORT_SYMBOL(ip_dev_find);