int                     tb_default;
        int                     tb_num_default;
        struct rcu_head         rcu;
-       unsigned long           tb_data[0];
+       unsigned long           *tb_data;
+       unsigned long           __data[0];
 };
 
 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
 int fib_table_dump(struct fib_table *table, struct sk_buff *skb,
                   struct netlink_callback *cb);
 int fib_table_flush(struct fib_table *table);
+struct fib_table *fib_trie_unmerge(struct fib_table *main_tb);
 void fib_table_flush_external(struct fib_table *table);
 void fib_free_table(struct fib_table *tb);
 
-
-
 #ifndef CONFIG_IP_MULTIPLE_TABLES
 
 #define TABLE_LOCAL_INDEX      (RT_TABLE_LOCAL & (FIB_TABLE_HASHSZ - 1))
                             struct fib_result *res)
 {
        struct fib_table *tb;
-       int err;
+       int err = -ENETUNREACH;
 
        rcu_read_lock();
 
-       for (err = 0; !err; err = -ENETUNREACH) {
-               tb = fib_get_table(net, RT_TABLE_LOCAL);
-               if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF))
-                       break;
-               tb = fib_get_table(net, RT_TABLE_MAIN);
-               if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF))
-                       break;
-       }
+       tb = fib_get_table(net, RT_TABLE_MAIN);
+       if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF))
+               err = 0;
 
        rcu_read_unlock();
 
        res->tclassid = 0;
 
        for (err = 0; !err; err = -ENETUNREACH) {
-               tb = rcu_dereference_rtnl(net->ipv4.fib_local);
-               if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF))
-                       break;
-
                tb = rcu_dereference_rtnl(net->ipv4.fib_main);
                if (tb && !fib_table_lookup(tb, flp, res, FIB_LOOKUP_NOREF))
                        break;
        return 0;
 }
 #endif
+int fib_unmerge(struct net *net);
 void fib_flush_external(struct net *net);
 
 /* Exported by fib_semantics.c */
 
 /* Exported by fib_trie.c */
 void fib_trie_init(void);
-struct fib_table *fib_trie_table(u32 id);
+struct fib_table *fib_trie_table(u32 id, struct fib_table *alias);
 
 static inline void fib_combine_itag(u32 *itag, const struct fib_result *res)
 {
 
 {
        struct fib_table *local_table, *main_table;
 
-       local_table = fib_trie_table(RT_TABLE_LOCAL);
-       if (local_table == NULL)
-               return -ENOMEM;
-
-       main_table  = fib_trie_table(RT_TABLE_MAIN);
+       main_table  = fib_trie_table(RT_TABLE_MAIN, NULL);
        if (main_table == NULL)
                goto fail;
 
+       local_table = fib_trie_table(RT_TABLE_LOCAL, main_table);
+       if (local_table == NULL)
+               return -ENOMEM;
+
        hlist_add_head_rcu(&local_table->tb_hlist,
                                &net->ipv4.fib_table_hash[TABLE_LOCAL_INDEX]);
        hlist_add_head_rcu(&main_table->tb_hlist,
 
 struct fib_table *fib_new_table(struct net *net, u32 id)
 {
-       struct fib_table *tb;
+       struct fib_table *tb, *alias = NULL;
        unsigned int h;
 
        if (id == 0)
        if (tb)
                return tb;
 
-       tb = fib_trie_table(id);
+       if (id == RT_TABLE_LOCAL)
+               alias = fib_new_table(net, RT_TABLE_MAIN);
+
+       tb = fib_trie_table(id, alias);
        if (!tb)
                return NULL;
 
 }
 #endif /* CONFIG_IP_MULTIPLE_TABLES */
 
+static void fib_replace_table(struct net *net, struct fib_table *old,
+                             struct fib_table *new)
+{
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+       switch (new->tb_id) {
+       case RT_TABLE_LOCAL:
+               rcu_assign_pointer(net->ipv4.fib_local, new);
+               break;
+       case RT_TABLE_MAIN:
+               rcu_assign_pointer(net->ipv4.fib_main, new);
+               break;
+       case RT_TABLE_DEFAULT:
+               rcu_assign_pointer(net->ipv4.fib_default, new);
+               break;
+       default:
+               break;
+       }
+
+#endif
+       /* replace the old table in the hlist */
+       hlist_replace_rcu(&old->tb_hlist, &new->tb_hlist);
+}
+
+int fib_unmerge(struct net *net)
+{
+       struct fib_table *old, *new;
+
+       old = fib_get_table(net, RT_TABLE_LOCAL);
+       new = fib_trie_unmerge(old);
+
+       if (!new)
+               return -ENOMEM;
+
+       /* replace merged table with clean table */
+       if (new != old) {
+               fib_replace_table(net, old, new);
+               fib_free_table(old);
+       }
+
+       return 0;
+}
+
 static void fib_flush(struct net *net)
 {
        int flushed = 0;
 
                                break;
                        if (fa->fa_info->fib_priority != fi->fib_priority)
                                break;
+                       /* duplicate entry from another table */
+                       if (WARN_ON(fa->tb_id != tb->tb_id))
+                               continue;
                        if (fa->fa_type == cfg->fc_type &&
                            fa->fa_info == fi) {
                                fa_match = fa;
        new_fa->fa_type = cfg->fc_type;
        new_fa->fa_state = 0;
        new_fa->fa_slen = slen;
+       new_fa->tb_id = tb->tb_id;
 
        /* (Optionally) offload fib entry to switch hardware. */
        err = netdev_switch_fib_ipv4_add(key, plen, fi, tos,
                tb->tb_num_default++;
 
        rt_cache_flush(cfg->fc_nlinfo.nl_net);
-       rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
+       rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, new_fa->tb_id,
                  &cfg->fc_nlinfo, 0);
 succeeded:
        return 0;
 int fib_table_lookup(struct fib_table *tb, const struct flowi4 *flp,
                     struct fib_result *res, int fib_flags)
 {
-       struct trie *t = (struct trie *)tb->tb_data;
+       struct trie *t = (struct trie *) tb->tb_data;
 #ifdef CONFIG_IP_FIB_TRIE_STATS
        struct trie_use_stats __percpu *stats = t->stats;
 #endif
                if ((fa->fa_slen != slen) || (fa->fa_tos != tos))
                        break;
 
+               if (fa->tb_id != tb->tb_id)
+                       continue;
+
                if ((!cfg->fc_type || fa->fa_type == cfg->fc_type) &&
                    (cfg->fc_scope == RT_SCOPE_NOWHERE ||
                     fa->fa_info->fib_scope == cfg->fc_scope) &&
        return n;
 }
 
+static void fib_trie_free(struct fib_table *tb)
+{
+       struct trie *t = (struct trie *)tb->tb_data;
+       struct key_vector *pn = t->kv;
+       unsigned long cindex = 1;
+       struct hlist_node *tmp;
+       struct fib_alias *fa;
+
+       /* walk trie in reverse order and free everything */
+       for (;;) {
+               struct key_vector *n;
+
+               if (!(cindex--)) {
+                       t_key pkey = pn->key;
+
+                       if (IS_TRIE(pn))
+                               break;
+
+                       n = pn;
+                       pn = node_parent(pn);
+
+                       /* drop emptied tnode */
+                       put_child_root(pn, n->key, NULL);
+                       node_free(n);
+
+                       cindex = get_index(pkey, pn);
+
+                       continue;
+               }
+
+               /* grab the next available node */
+               n = get_child(pn, cindex);
+               if (!n)
+                       continue;
+
+               if (IS_TNODE(n)) {
+                       /* record pn and cindex for leaf walking */
+                       pn = n;
+                       cindex = 1ul << n->bits;
+
+                       continue;
+               }
+
+               hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
+                       hlist_del_rcu(&fa->fa_list);
+                       alias_free_mem_rcu(fa);
+               }
+
+               put_child_root(pn, n->key, NULL);
+               node_free(n);
+       }
+
+#ifdef CONFIG_IP_FIB_TRIE_STATS
+       free_percpu(t->stats);
+#endif
+       kfree(tb);
+}
+
+struct fib_table *fib_trie_unmerge(struct fib_table *oldtb)
+{
+       struct trie *ot = (struct trie *)oldtb->tb_data;
+       struct key_vector *l, *tp = ot->kv;
+       struct fib_table *local_tb;
+       struct fib_alias *fa;
+       struct trie *lt;
+       t_key key = 0;
+
+       if (oldtb->tb_data == oldtb->__data)
+               return oldtb;
+
+       local_tb = fib_trie_table(RT_TABLE_LOCAL, NULL);
+       if (!local_tb)
+               return NULL;
+
+       lt = (struct trie *)local_tb->tb_data;
+
+       while ((l = leaf_walk_rcu(&tp, key)) != NULL) {
+               struct key_vector *local_l = NULL, *local_tp;
+
+               hlist_for_each_entry_rcu(fa, &l->leaf, fa_list) {
+                       struct fib_alias *new_fa;
+
+                       if (local_tb->tb_id != fa->tb_id)
+                               continue;
+
+                       /* clone fa for new local table */
+                       new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
+                       if (!new_fa)
+                               goto out;
+
+                       memcpy(new_fa, fa, sizeof(*fa));
+
+                       /* insert clone into table */
+                       if (!local_l)
+                               local_l = fib_find_node(lt, &local_tp, l->key);
+
+                       if (fib_insert_alias(lt, local_tp, local_l, new_fa,
+                                            NULL, l->key))
+                               goto out;
+               }
+
+               /* stop loop if key wrapped back to 0 */
+               key = l->key + 1;
+               if (key < l->key)
+                       break;
+       }
+
+       return local_tb;
+out:
+       fib_trie_free(local_tb);
+
+       return NULL;
+}
+
 /* Caller must hold RTNL */
 void fib_table_flush_external(struct fib_table *tb)
 {
 
        /* walk trie in reverse order */
        for (;;) {
+               unsigned char slen = 0;
                struct key_vector *n;
 
                if (!(cindex--)) {
                        if (IS_TRIE(pn))
                                break;
 
-                       /* no need to resize like in flush below */
-                       pn = node_parent(pn);
+                       /* resize completed node */
+                       pn = resize(t, pn);
                        cindex = get_index(pkey, pn);
 
                        continue;
                hlist_for_each_entry_safe(fa, tmp, &n->leaf, fa_list) {
                        struct fib_info *fi = fa->fa_info;
 
+                       /* if alias was cloned to local then we just
+                        * need to remove the local copy from main
+                        */
+                       if (tb->tb_id != fa->tb_id) {
+                               hlist_del_rcu(&fa->fa_list);
+                               alias_free_mem_rcu(fa);
+                               continue;
+                       }
+
+                       /* record local slen */
+                       slen = fa->fa_slen;
+
                        if (!fi || !(fi->fib_flags & RTNH_F_EXTERNAL))
                                continue;
 
                                                   fi, fa->fa_tos,
                                                   fa->fa_type, tb->tb_id);
                }
+
+               /* update leaf slen */
+               n->slen = slen;
+
+               if (hlist_empty(&n->leaf)) {
+                       put_child_root(pn, n->key, NULL);
+                       node_free(n);
+               } else {
+                       leaf_pull_suffix(pn, n);
+               }
        }
 }
 
 #ifdef CONFIG_IP_FIB_TRIE_STATS
        struct trie *t = (struct trie *)tb->tb_data;
 
-       free_percpu(t->stats);
+       if (tb->tb_data == tb->__data)
+               free_percpu(t->stats);
 #endif /* CONFIG_IP_FIB_TRIE_STATS */
        kfree(tb);
 }
                        continue;
                }
 
+               if (tb->tb_id != fa->tb_id) {
+                       i++;
+                       continue;
+               }
+
                if (fib_dump_info(skb, NETLINK_CB(cb->skb).portid,
                                  cb->nlh->nlmsg_seq,
                                  RTM_NEWROUTE,
                                           0, SLAB_PANIC, NULL);
 }
 
-struct fib_table *fib_trie_table(u32 id)
+struct fib_table *fib_trie_table(u32 id, struct fib_table *alias)
 {
        struct fib_table *tb;
        struct trie *t;
+       size_t sz = sizeof(*tb);
+
+       if (!alias)
+               sz += sizeof(struct trie);
 
-       tb = kzalloc(sizeof(*tb) + sizeof(struct trie), GFP_KERNEL);
+       tb = kzalloc(sz, GFP_KERNEL);
        if (tb == NULL)
                return NULL;
 
        tb->tb_id = id;
        tb->tb_default = -1;
        tb->tb_num_default = 0;
+       tb->tb_data = (alias ? alias->__data : tb->__data);
+
+       if (alias)
+               return tb;
 
        t = (struct trie *) tb->tb_data;
        t->kv[0].pos = KEYLENGTH;