Typos. A bunch of typos I've been collecting.
[vpp.git] / src / vnet / fib / fib_entry.c
index dac1fce..edbfdf6 100644 (file)
  */
 static const char *fib_source_names[] = FIB_SOURCES;
 static const char *fib_attribute_names[] = FIB_ENTRY_ATTRIBUTES;
+static const char *fib_src_attribute_names[] = FIB_ENTRY_SRC_ATTRIBUTES;
 
 /*
  * Pool for all fib_entries
  */
 static fib_entry_t *fib_entry_pool;
 
+/**
+ * the logger
+ */
+vlib_log_class_t fib_entry_logger;
+
 fib_entry_t *
 fib_entry_get (fib_node_index_t index)
 {
@@ -58,12 +64,18 @@ fib_entry_get_index (const fib_entry_t * fib_entry)
     return (fib_entry - fib_entry_pool);
 }
 
-static fib_protocol_t
+fib_protocol_t
 fib_entry_get_proto (const fib_entry_t * fib_entry)
 {
     return (fib_entry->fe_prefix.fp_proto);
 }
 
+dpo_proto_t
+fib_entry_get_dpo_proto (const fib_entry_t * fib_entry)
+{
+    return (fib_proto_to_dpo(fib_entry->fe_prefix.fp_proto));
+}
+
 fib_forward_chain_type_t
 fib_entry_get_default_chain_type (const fib_entry_t *fib_entry)
 {
@@ -75,13 +87,7 @@ fib_entry_get_default_chain_type (const fib_entry_t *fib_entry)
        return (FIB_FORW_CHAIN_TYPE_UNICAST_IP6);
     case FIB_PROTOCOL_MPLS:
        if (MPLS_EOS == fib_entry->fe_prefix.fp_eos)
-           /*
-            * If the entry being asked is a eos-MPLS label entry,
-            * then use the payload-protocol field, that we stashed there
-            * for just this purpose
-            */
-           return (fib_forw_chain_type_from_dpo_proto(
-                       fib_entry->fe_prefix.fp_payload_proto));
+           return (FIB_FORW_CHAIN_TYPE_MPLS_EOS);
        else
            return (FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS);
     }
@@ -89,12 +95,50 @@ fib_entry_get_default_chain_type (const fib_entry_t *fib_entry)
     return (FIB_FORW_CHAIN_TYPE_UNICAST_IP4);
 }
 
+u8 *
+format_fib_source (u8 * s, va_list * args)
+{
+    fib_source_t source = va_arg (*args, int);
+
+    s = format (s, "src:%s", fib_source_names[source]);
+
+    return (s);
+}
+
+u8 *
+format_fib_entry_flags (u8 *s, va_list *args)
+{
+    fib_entry_attribute_t attr;
+    fib_entry_flag_t flag = va_arg(*args, int);
+
+    FOR_EACH_FIB_ATTRIBUTE(attr) {
+        if ((1<<attr) & flag) {
+            s = format (s, "%s,", fib_attribute_names[attr]);
+        }
+    }
+
+    return (s);
+}
+
+u8 *
+format_fib_entry_src_flags (u8 *s, va_list *args)
+{
+    fib_entry_src_attribute_t sattr;
+    fib_entry_src_flag_t flag = va_arg(*args, int);
+
+    FOR_EACH_FIB_SRC_ATTRIBUTE(sattr) {
+        if ((1<<sattr) & flag) {
+            s = format (s, "%s,", fib_src_attribute_names[sattr]);
+        }
+    }
+
+    return (s);
+}
+
 u8 *
 format_fib_entry (u8 * s, va_list * args)
 {
     fib_forward_chain_type_t fct;
-    fib_entry_attribute_t attr;
-    fib_path_ext_t *path_ext;
     fib_entry_t *fib_entry;
     fib_entry_src_t *src;
     fib_node_index_t fei;
@@ -115,31 +159,23 @@ format_fib_entry (u8 * s, va_list * args)
 
        FOR_EACH_SRC_ADDED(fib_entry, src, source,
         ({
-           s = format (s, "\n  src:%s ",
-                       fib_source_names[source]);
-           s = fib_entry_src_format(fib_entry, source, s);
-           s = format (s, " refs:%d ", src->fes_ref_count);
+           s = format (s, "\n  %U", format_fib_source, source);
+           s = format (s, " refs:%d", src->fes_ref_count);
            if (FIB_ENTRY_FLAG_NONE != src->fes_entry_flags) {
-               s = format(s, "flags:");
-               FOR_EACH_FIB_ATTRIBUTE(attr) {
-                   if ((1<<attr) & src->fes_entry_flags) {
-                       s = format (s, "%s,", fib_attribute_names[attr]);
-                   }
-               }
+               s = format(s, " entry-flags:%U",
+                           format_fib_entry_flags, src->fes_entry_flags);
+           }
+           if (FIB_ENTRY_SRC_FLAG_NONE != src->fes_flags) {
+               s = format(s, " src-flags:%U",
+                           format_fib_entry_src_flags, src->fes_flags);
            }
+            s = fib_entry_src_format(fib_entry, source, s);
            s = format (s, "\n");
            if (FIB_NODE_INDEX_INVALID != src->fes_pl)
            {
                s = fib_path_list_format(src->fes_pl, s);
            }
-           if (NULL != src->fes_path_exts)
-           {
-               s = format(s, "    Extensions:");
-               vec_foreach(path_ext, src->fes_path_exts)
-               {
-                   s = format(s, "\n     %U", format_fib_path_ext, path_ext);
-               }
-           }
+            s = format(s, "%U", format_fib_path_ext_list, &src->fes_path_exts);
        }));
     
        s = format (s, "\n forwarding: ");
@@ -190,9 +226,7 @@ format_fib_entry (u8 * s, va_list * args)
 static fib_entry_t*
 fib_entry_from_fib_node (fib_node_t *node)
 {
-#if CLIB_DEBUG > 0
     ASSERT(FIB_NODE_TYPE_ENTRY == node->fn_type);
-#endif
     return ((fib_entry_t*)node);
 }
 
@@ -205,6 +239,8 @@ fib_entry_last_lock_gone (fib_node_t *node)
 
     fib_entry = fib_entry_from_fib_node(node);
 
+    ASSERT(!dpo_id_is_valid(&fib_entry->fe_lb));
+
     FOR_EACH_DELEGATE_CHAIN(fib_entry, fdt, fed,
     {
        dpo_reset(&fed->fd_dpo);
@@ -214,7 +250,6 @@ fib_entry_last_lock_gone (fib_node_t *node)
     FIB_ENTRY_DBG(fib_entry, "last-lock");
 
     fib_node_deinit(&fib_entry->fe_node);
-    // FIXME -RR Backwalk
 
     ASSERT(0 == vec_len(fib_entry->fe_delegates));
     vec_free(fib_entry->fe_delegates);
@@ -306,6 +341,9 @@ fib_entry_back_walk_notify (fib_node_t *node,
      */
     ctx->fnbw_flags &= ~FIB_NODE_BW_FLAG_FORCE_SYNC;
 
+    FIB_ENTRY_DBG(fib_entry, "bw:%U",
+                  format_fib_node_bw_reason, ctx->fnbw_reason);
+
     /*
      * propagate the backwalk further if we haven't already reached the
      * maximum depth.
@@ -334,7 +372,7 @@ fib_entry_show_memory (void)
        n_srcs += vec_len(entry->fe_srcs);
        vec_foreach(esrc, entry->fe_srcs)
        {
-           n_exts += vec_len(esrc->fes_path_exts);
+           n_exts += fib_path_ext_list_length(&esrc->fes_path_exts);
        }
     }));
 
@@ -370,6 +408,36 @@ fib_entry_contribute_urpf (fib_node_index_t entry_index,
     return (fib_path_list_contribute_urpf(fib_entry->fe_parent, urpf));
 }
 
+/*
+ * If the client is request a chain for multicast forwarding then swap
+ * the chain type to one that can provide such transport.
+ */
+static fib_forward_chain_type_t
+fib_entry_chain_type_mcast_to_ucast (fib_forward_chain_type_t fct)
+{
+    switch (fct)
+    {
+    case FIB_FORW_CHAIN_TYPE_MCAST_IP4:
+    case FIB_FORW_CHAIN_TYPE_MCAST_IP6:
+        /*
+         * we can only transport IP multicast packets if there is an
+         * LSP.
+         */
+        fct = FIB_FORW_CHAIN_TYPE_MPLS_EOS;
+        break;
+    case FIB_FORW_CHAIN_TYPE_MPLS_EOS:
+    case FIB_FORW_CHAIN_TYPE_UNICAST_IP4:
+    case FIB_FORW_CHAIN_TYPE_UNICAST_IP6:
+    case FIB_FORW_CHAIN_TYPE_MPLS_NON_EOS:
+    case FIB_FORW_CHAIN_TYPE_ETHERNET:
+    case FIB_FORW_CHAIN_TYPE_NSH:
+    case FIB_FORW_CHAIN_TYPE_BIER:
+        break;
+    }
+
+    return (fct);
+}
+
 /*
  * fib_entry_contribute_forwarding
  *
@@ -385,6 +453,11 @@ fib_entry_contribute_forwarding (fib_node_index_t fib_entry_index,
 
     fib_entry = fib_entry_get(fib_entry_index);
 
+    /*
+     * mfib children ask for mcast chains. fix these to the appropriate ucast types.
+     */
+    fct = fib_entry_chain_type_mcast_to_ucast(fct);
+
     if (fct == fib_entry_get_default_chain_type(fib_entry))
     {
         dpo_copy(dpo, &fib_entry->fe_lb);
@@ -414,6 +487,19 @@ fib_entry_contribute_forwarding (fib_node_index_t fib_entry_index,
 
         dpo_copy(dpo, &fed->fd_dpo);
     }
+    /*
+     * use the drop DPO is nothing else is present
+     */
+    if (!dpo_id_is_valid(dpo))
+    {
+        dpo_copy(dpo, drop_dpo_get(fib_forw_chain_type_to_dpo_proto(fct)));
+    }
+
+    /*
+     * don't allow the special index indicating replicate.vs.load-balance
+     * to escape to the clients
+     */
+    dpo->dpoi_index &= ~MPLS_IS_REPLICATE;
 }
 
 const dpo_id_t *
@@ -428,7 +514,12 @@ fib_entry_contribute_ip_forwarding (fib_node_index_t fib_entry_index)
     ASSERT((fct == FIB_FORW_CHAIN_TYPE_UNICAST_IP4 ||
             fct == FIB_FORW_CHAIN_TYPE_UNICAST_IP6));
 
-    return (&fib_entry->fe_lb);
+    if (dpo_id_is_valid(&fib_entry->fe_lb))
+    {
+        return (&fib_entry->fe_lb);
+    }
+
+    return (drop_dpo_get(fib_forw_chain_type_to_dpo_proto(fct)));
 }
 
 adj_index_t
@@ -437,11 +528,15 @@ fib_entry_get_adj (fib_node_index_t fib_entry_index)
     const dpo_id_t *dpo;
 
     dpo = fib_entry_contribute_ip_forwarding(fib_entry_index);
-    dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
 
-    if (dpo_is_adj(dpo))
+    if (dpo_id_is_valid(dpo))
     {
-       return (dpo->dpoi_index);
+        dpo = load_balance_get_bucket(dpo->dpoi_index, 0);
+
+        if (dpo_is_adj(dpo))
+        {
+            return (dpo->dpoi_index);
+        }
     }
     return (ADJ_INDEX_INVALID);
 }
@@ -506,7 +601,7 @@ fib_entry_alloc (u32 fib_index,
     fib_prefix_t *fep;
 
     pool_get(fib_entry_pool, fib_entry);
-    memset(fib_entry, 0, sizeof(*fib_entry));
+    clib_memset(fib_entry, 0, sizeof(*fib_entry));
 
     fib_node_init(&fib_entry->fe_node,
                  FIB_NODE_TYPE_ENTRY);
@@ -534,14 +629,11 @@ fib_entry_alloc (u32 fib_index,
 
     *fib_entry_index = fib_entry_get_index(fib_entry);
 
-    FIB_ENTRY_DBG(fib_entry, "alloc");
-
     return (fib_entry);
 }
 
 static fib_entry_t*
 fib_entry_post_flag_update_actions (fib_entry_t *fib_entry,
-                                   fib_source_t source,
                                    fib_entry_flag_t old_flags)
 {
     fib_node_index_t fei;
@@ -615,7 +707,6 @@ fib_entry_post_install_actions (fib_entry_t *fib_entry,
                                fib_entry_flag_t old_flags)
 {
     fib_entry = fib_entry_post_flag_update_actions(fib_entry,
-                                                   source,
                                                    old_flags);
     fib_entry_src_action_installed(fib_entry, source);
 }
@@ -654,6 +745,8 @@ fib_entry_create (u32 fib_index,
 
     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
 
+    FIB_ENTRY_DBG(fib_entry, "create");
+
     return (fib_entry_index);
 }
 
@@ -668,7 +761,7 @@ fib_entry_create_special (u32 fib_index,
     fib_entry_t *fib_entry;
 
     /*
-     * create and initiliase the new enty
+     * create and initialize the new enty
      */
     fib_entry = fib_entry_alloc(fib_index, prefix, &fib_entry_index);
 
@@ -680,6 +773,8 @@ fib_entry_create_special (u32 fib_index,
 
     fib_entry_post_install_actions(fib_entry, source, FIB_ENTRY_FLAG_NONE);
 
+    FIB_ENTRY_DBG(fib_entry, "create-special");
+
     return (fib_entry_index);
 }
 
@@ -705,32 +800,44 @@ fib_entry_post_update_actions (fib_entry_t *fib_entry,
     fib_entry_post_install_actions(fib_entry, source, old_flags);
 }
 
+void
+fib_entry_recalculate_forwarding (fib_node_index_t fib_entry_index)
+{
+    fib_source_t best_source;
+    fib_entry_t *fib_entry;
+    fib_entry_src_t *bsrc;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    bsrc = fib_entry_get_best_src_i(fib_entry);
+    best_source = fib_entry_src_get_source(bsrc);
+
+    fib_entry_src_action_reactivate(fib_entry, best_source);
+}
+
 static void
-fib_entry_source_change (fib_entry_t *fib_entry,
-                        fib_source_t best_source,
-                        fib_source_t new_source,
-                        fib_entry_flag_t old_flags)
+fib_entry_source_change_w_flags (fib_entry_t *fib_entry,
+                                 fib_source_t old_source,
+                                 fib_entry_flag_t old_flags,
+                                 fib_source_t new_source)
 {
-    /*
-     * if the path list for the source passed is invalid,
-     * then we need to create a new one. else we are updating
-     * an existing.
-     */
-    if (new_source < best_source)
+    if (new_source < old_source)
     {
        /*
         * we have a new winning source.
         */
-       fib_entry_src_action_deactivate(fib_entry, best_source);
+       fib_entry_src_action_deactivate(fib_entry, old_source);
        fib_entry_src_action_activate(fib_entry, new_source);
     }
-    else if (new_source > best_source)
+    else if (new_source > old_source)
     {
-       /*
-        * the new source loses. nothing to do here.
-        * the data from the source is saved in the path-list created
-        */
-       return;
+        /*
+         * the new source loses. Re-activate the winning sources
+         * in case it is an interposer and hence relied on the losing
+         * source's path-list.
+         */
+        fib_entry_src_action_reactivate(fib_entry, old_source);
+        return;
     }
     else
     {
@@ -739,13 +846,26 @@ fib_entry_source_change (fib_entry_t *fib_entry,
         * But the path-list was updated, which will contribute new forwarding,
         * so install it.
         */
-       fib_entry_src_action_deactivate(fib_entry, new_source);
-       fib_entry_src_action_activate(fib_entry, new_source);
+        fib_entry_src_action_reactivate(fib_entry, new_source);
     }
 
     fib_entry_post_update_actions(fib_entry, new_source, old_flags);
 }
 
+void
+fib_entry_source_change (fib_entry_t *fib_entry,
+                         fib_source_t old_source,
+                        fib_source_t new_source)
+{
+    fib_entry_flag_t old_flags;
+
+    old_flags = fib_entry_get_flags_for_source(
+        fib_entry_get_index(fib_entry), old_source);
+
+    return (fib_entry_source_change_w_flags(fib_entry, old_source,
+                                            old_flags, new_source));
+}
+
 void
 fib_entry_special_add (fib_node_index_t fib_entry_index,
                       fib_source_t source,
@@ -753,18 +873,14 @@ fib_entry_special_add (fib_node_index_t fib_entry_index,
                       const dpo_id_t *dpo)
 {
     fib_source_t best_source;
-    fib_entry_flag_t bflags;
     fib_entry_t *fib_entry;
-    fib_entry_src_t *bsrc;
 
     fib_entry = fib_entry_get(fib_entry_index);
-
-    bsrc = fib_entry_get_best_src_i(fib_entry);
-    best_source = fib_entry_src_get_source(bsrc);
-    bflags = fib_entry_src_get_flags(bsrc);
+    best_source = fib_entry_get_best_source(fib_entry_index);
 
     fib_entry = fib_entry_src_action_add(fib_entry, source, flags, dpo);
-    fib_entry_source_change(fib_entry, best_source, source, bflags);
+    fib_entry_source_change(fib_entry, best_source, source);
+    FIB_ENTRY_DBG(fib_entry, "special-add:%U", format_fib_source, source);
 }
 
 void
@@ -774,18 +890,15 @@ fib_entry_special_update (fib_node_index_t fib_entry_index,
                          const dpo_id_t *dpo)
 {
     fib_source_t best_source;
-    fib_entry_flag_t bflags;
     fib_entry_t *fib_entry;
-    fib_entry_src_t *bsrc;
 
     fib_entry = fib_entry_get(fib_entry_index);
-
-    bsrc = fib_entry_get_best_src_i(fib_entry);
-    best_source = fib_entry_src_get_source(bsrc);
-    bflags = fib_entry_src_get_flags(bsrc);
+    best_source = fib_entry_get_best_source(fib_entry_index);
 
     fib_entry = fib_entry_src_action_update(fib_entry, source, flags, dpo);
-    fib_entry_source_change(fib_entry, best_source, source, bflags);
+    fib_entry_source_change(fib_entry, best_source, source);
+
+    FIB_ENTRY_DBG(fib_entry, "special-updated:%U", format_fib_source, source);
 }
 
 
@@ -796,7 +909,6 @@ fib_entry_path_add (fib_node_index_t fib_entry_index,
                    const fib_route_path_t *rpath)
 {
     fib_source_t best_source;
-    fib_entry_flag_t bflags;
     fib_entry_t *fib_entry;
     fib_entry_src_t *bsrc;
 
@@ -807,43 +919,79 @@ fib_entry_path_add (fib_node_index_t fib_entry_index,
 
     bsrc = fib_entry_get_best_src_i(fib_entry);
     best_source = fib_entry_src_get_source(bsrc);
-    bflags = fib_entry_src_get_flags(bsrc);
     
     fib_entry = fib_entry_src_action_path_add(fib_entry, source, flags, rpath);
 
-    /*
-     * if the path list for the source passed is invalid,
-     * then we need to create a new one. else we are updating
-     * an existing.
-     */
-    if (source < best_source)
+    fib_entry_source_change(fib_entry, best_source, source);
+
+    FIB_ENTRY_DBG(fib_entry, "path add:%U", format_fib_source, source);
+}
+
+static fib_entry_src_flag_t
+fib_entry_src_burn_only_inherited (fib_entry_t *fib_entry)
+{
+    fib_entry_src_t *src;
+    fib_source_t source;
+    int has_only_inherited_sources = 1;
+
+    FOR_EACH_SRC_ADDED(fib_entry, src, source,
+    ({
+        if (!(src->fes_flags & FIB_ENTRY_SRC_FLAG_INHERITED))
+        {
+            has_only_inherited_sources = 0;
+            break;
+        }
+    }));
+    if (has_only_inherited_sources)
     {
-       /*
-        * we have a new winning source.
-        */
-       fib_entry_src_action_deactivate(fib_entry, best_source);
-       fib_entry_src_action_activate(fib_entry, source);
+        FOR_EACH_SRC_ADDED(fib_entry, src, source,
+        ({
+            fib_entry_src_action_remove(fib_entry, source);
+        }));
+        return (FIB_ENTRY_SRC_FLAG_NONE);
     }
-    else if (source > best_source)
+    else
     {
-       /*
-        * the new source loses. nothing to do here.
-        * the data from the source is saved in the path-list created
-        */
-       return;
+        return (FIB_ENTRY_SRC_FLAG_ADDED);
+    }
+}
+
+static fib_entry_src_flag_t
+fib_entry_source_removed (fib_entry_t *fib_entry,
+                          fib_entry_flag_t old_flags)
+{
+    const fib_entry_src_t *bsrc;
+    fib_source_t best_source;
+
+    /*
+     * if all that is left are inherited sources, then burn them
+     */
+    fib_entry_src_burn_only_inherited(fib_entry);
+
+    bsrc = fib_entry_get_best_src_i(fib_entry);
+    best_source = fib_entry_src_get_source(bsrc);
+
+    if (FIB_SOURCE_MAX == best_source)
+    {
+        /*
+         * no more sources left. this entry is toast.
+         */
+        fib_entry = fib_entry_post_flag_update_actions(fib_entry, old_flags);
+        fib_entry_src_action_uninstall(fib_entry);
+
+        return (FIB_ENTRY_SRC_FLAG_NONE);
     }
     else
     {
-       /*
-        * the new source is one this entry already has.
-        * But the path-list was updated, which will contribute new forwarding,
-        * so install it.
-        */
-       fib_entry_src_action_deactivate(fib_entry, source);
-       fib_entry_src_action_activate(fib_entry, source);
+        fib_entry_src_action_activate(fib_entry, best_source);
     }
 
-    fib_entry_post_update_actions(fib_entry, source, bflags);
+    fib_entry_post_update_actions(fib_entry, best_source, old_flags);
+
+    /*
+     * still have sources
+     */
+    return (FIB_ENTRY_SRC_FLAG_ADDED);
 }
 
 /*
@@ -874,12 +1022,14 @@ fib_entry_path_remove (fib_node_index_t fib_entry_index,
 
     sflag = fib_entry_src_action_path_remove(fib_entry, source, rpath);
 
+    FIB_ENTRY_DBG(fib_entry, "path remove:%U", format_fib_source, source);
+
     /*
      * if the path list for the source passed is invalid,
      * then we need to create a new one. else we are updating
      * an existing.
      */
-    if (source < best_source )
+    if (source < best_source)
     {
        /*
         * Que! removing a path from a source that is better than the
@@ -890,9 +1040,23 @@ fib_entry_path_remove (fib_node_index_t fib_entry_index,
     else if (source > best_source )
     {
        /*
-        * the source is not the best. nothing to do.
+        * the source is not the best. no need to update forwarding
         */
-       return (FIB_ENTRY_SRC_FLAG_ADDED);
+       if (FIB_ENTRY_SRC_FLAG_ADDED & sflag)
+        {
+            /*
+             * the source being removed still has paths
+             */
+            return (FIB_ENTRY_SRC_FLAG_ADDED);
+        }
+        else
+        {
+            /*
+             * that was the last path from this source, check if those
+             * that remain are non-inherited
+             */
+            return (fib_entry_src_burn_only_inherited(fib_entry));
+       }
     }
     else
     {
@@ -905,33 +1069,14 @@ fib_entry_path_remove (fib_node_index_t fib_entry_index,
             * the last path from the source was removed.
             * fallback to lower source
             */
-           bsrc = fib_entry_get_best_src_i(fib_entry);
-           best_source = fib_entry_src_get_source(bsrc);
-
-           if (FIB_SOURCE_MAX == best_source) {
-               /*
-                * no more sources left. this entry is toast.
-                */
-               fib_entry = fib_entry_post_flag_update_actions(fib_entry,
-                                                               source,
-                                                               bflags);
-               fib_entry_src_action_uninstall(fib_entry);
-
-               return (FIB_ENTRY_SRC_FLAG_NONE);
-           }
-           else
-           {
-               fib_entry_src_action_activate(fib_entry, best_source);
-               source = best_source;
-           }
+            return (fib_entry_source_removed(fib_entry, bflags));
        }
        else
        {
            /*
             * re-install the new forwarding information
             */
-           fib_entry_src_action_deactivate(fib_entry, source);
-           fib_entry_src_action_activate(fib_entry, source);
+           fib_entry_src_action_reactivate(fib_entry, source);
        }
     }
 
@@ -966,7 +1111,9 @@ fib_entry_special_remove (fib_node_index_t fib_entry_index,
     best_source = fib_entry_src_get_source(bsrc);
     bflags = fib_entry_src_get_flags(bsrc);
 
-    sflag = fib_entry_src_action_remove(fib_entry, source);
+    FIB_ENTRY_DBG(fib_entry, "special remove:%U", format_fib_source, source);
+
+    sflag = fib_entry_src_action_remove_or_update_inherit(fib_entry, source);
 
     /*
      * if the path list for the source passed is invalid,
@@ -984,9 +1131,38 @@ fib_entry_special_remove (fib_node_index_t fib_entry_index,
     }
     else if (source > best_source ) {
        /*
-        * the source is not the best. nothing to do.
+        * the source is not the best. no need to update forwarding
         */
-       return (FIB_ENTRY_SRC_FLAG_ADDED);
+       if (FIB_ENTRY_SRC_FLAG_ADDED & sflag)
+        {
+            /*
+             * the source being removed still has paths
+             */
+            return (FIB_ENTRY_SRC_FLAG_ADDED);
+        }
+        else
+        {
+            /*
+             * that was the last path from this source, check if those
+             * that remain are non-inherited
+             */
+            if (FIB_ENTRY_SRC_FLAG_NONE == fib_entry_src_burn_only_inherited(fib_entry))
+            {
+                /*
+                 * no more sources left. this entry is toast.
+                 */
+                fib_entry = fib_entry_post_flag_update_actions(fib_entry, bflags);
+                fib_entry_src_action_uninstall(fib_entry);
+                return (FIB_ENTRY_SRC_FLAG_NONE);
+            }
+
+            /*
+             * reactivate the best source so the interposer gets restacked
+             */
+            fib_entry_src_action_reactivate(fib_entry, best_source);
+
+            return (FIB_ENTRY_SRC_FLAG_ADDED);
+        }
     }
     else
     {
@@ -995,25 +1171,7 @@ fib_entry_special_remove (fib_node_index_t fib_entry_index,
            /*
             * the source was removed. use the next best.
             */
-           bsrc = fib_entry_get_best_src_i(fib_entry);
-           best_source = fib_entry_src_get_source(bsrc);
-
-           if (FIB_SOURCE_MAX == best_source) {
-               /*
-                * no more sources left. this entry is toast.
-                */
-               fib_entry = fib_entry_post_flag_update_actions(fib_entry,
-                                                               source,
-                                                               bflags);
-               fib_entry_src_action_uninstall(fib_entry);
-
-               return (FIB_ENTRY_SRC_FLAG_NONE);
-           }
-           else
-           {
-               fib_entry_src_action_activate(fib_entry, best_source);
-               source = best_source;
-           }
+            return (fib_entry_source_removed(fib_entry, bflags));
        }
        else
        {
@@ -1032,6 +1190,20 @@ fib_entry_special_remove (fib_node_index_t fib_entry_index,
     return (FIB_ENTRY_SRC_FLAG_ADDED);
 }
 
+/**
+ * fib_entry_inherit
+ *
+ * If the source on the cover is inheriting then push this source
+ * down to the covered.
+ */
+void
+fib_entry_inherit (fib_node_index_t cover,
+                   fib_node_index_t covered)
+{
+    fib_entry_src_inherit(fib_entry_get(cover),
+                          fib_entry_get(covered));
+}
+
 /**
  * fib_entry_delete
  *
@@ -1065,49 +1237,15 @@ fib_entry_update (fib_node_index_t fib_entry_index,
 
     bsrc = fib_entry_get_best_src_i(fib_entry);
     best_source = fib_entry_src_get_source(bsrc);
-    bflags = fib_entry_src_get_flags(bsrc);
-
-    fib_entry_src_action_path_swap(fib_entry,
-                                  source,
-                                  flags,
-                                  paths);
-    /*
-     * handle possible realloc's by refetching the pointer
-     */
-    fib_entry = fib_entry_get(fib_entry_index);
+    bflags = fib_entry_get_flags_i(fib_entry);
 
-    /*
-     * if the path list for the source passed is invalid,
-     * then we need to create a new one. else we are updating
-     * an existing.
-     */
-    if (source < best_source)
-    {
-       /*
-        * we have a new winning source.
-        */
-       fib_entry_src_action_deactivate(fib_entry, best_source);
-       fib_entry_src_action_activate(fib_entry, source);
-    }
-    else if (source > best_source) {
-       /*
-        * the new source loses. nothing to do here.
-        * the data from the source is saved in the path-list created
-        */
-       return;
-    }
-    else
-    {
-       /*
-        * the new source is one this entry already has.
-        * But the path-list was updated, which will contribute new forwarding,
-        * so install it.
-        */
-       fib_entry_src_action_deactivate(fib_entry, source);
-       fib_entry_src_action_activate(fib_entry, source);
-    }
+    fib_entry = fib_entry_src_action_path_swap(fib_entry,
+                                               source,
+                                               flags,
+                                               paths);
 
-    fib_entry_post_update_actions(fib_entry, source, bflags);
+    fib_entry_source_change_w_flags(fib_entry, best_source, bflags, source);
+    FIB_ENTRY_DBG(fib_entry, "update");
 }
 
 
@@ -1123,7 +1261,8 @@ fib_entry_cover_changed (fib_node_index_t fib_entry_index)
        .install = !0,
        .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
     };
-    fib_source_t source, best_source;
+    CLIB_UNUSED(fib_source_t source);
+    fib_source_t best_source;
     fib_entry_flag_t bflags;
     fib_entry_t *fib_entry;
     fib_entry_src_t *esrc;
@@ -1136,7 +1275,7 @@ fib_entry_cover_changed (fib_node_index_t fib_entry_index)
     fib_attached_export_cover_change(fib_entry);
 
     /*
-     * propagate the notificuation to each of the added sources
+     * propagate the notification to each of the added sources
      */
     index = 0;
     FOR_EACH_SRC_ADDED(fib_entry, esrc, source,
@@ -1146,13 +1285,13 @@ fib_entry_cover_changed (fib_node_index_t fib_entry_index)
            /*
             * only the best source gets to set the back walk flags
             */
-           res = fib_entry_src_action_cover_change(fib_entry, source);
+           res = fib_entry_src_action_cover_change(fib_entry, esrc);
             bflags = fib_entry_src_get_flags(esrc);
             best_source = fib_entry_src_get_source(esrc);
        }
        else
        {
-           fib_entry_src_action_cover_change(fib_entry, source);
+           fib_entry_src_action_cover_change(fib_entry, esrc);
        }
        index++;
     }));
@@ -1180,6 +1319,7 @@ fib_entry_cover_changed (fib_node_index_t fib_entry_index)
 
        fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_index, &bw_ctx);
     }
+    FIB_ENTRY_DBG(fib_entry, "cover-changed");
 }
 
 /*
@@ -1195,7 +1335,8 @@ fib_entry_cover_updated (fib_node_index_t fib_entry_index)
        .install = !0,
        .bw_reason = FIB_NODE_BW_REASON_FLAG_NONE,
     };
-    fib_source_t source, best_source;
+    CLIB_UNUSED(fib_source_t source);
+    fib_source_t best_source;
     fib_entry_flag_t bflags;
     fib_entry_t *fib_entry;
     fib_entry_src_t *esrc;
@@ -1208,7 +1349,7 @@ fib_entry_cover_updated (fib_node_index_t fib_entry_index)
     fib_attached_export_cover_update(fib_entry);
 
     /*
-     * propagate the notificuation to each of the added sources
+     * propagate the notification to each of the added sources
      */
     index = 0;
     FOR_EACH_SRC_ADDED(fib_entry, esrc, source,
@@ -1218,13 +1359,13 @@ fib_entry_cover_updated (fib_node_index_t fib_entry_index)
            /*
             * only the best source gets to set the back walk flags
             */
-           res = fib_entry_src_action_cover_update(fib_entry, source);
+           res = fib_entry_src_action_cover_update(fib_entry, esrc);
             bflags = fib_entry_src_get_flags(esrc);
             best_source = fib_entry_src_get_source(esrc);
        }
        else
        {
-           fib_entry_src_action_cover_update(fib_entry, source);
+           fib_entry_src_action_cover_update(fib_entry, esrc);
        }
        index++;
     }));
@@ -1252,6 +1393,7 @@ fib_entry_cover_updated (fib_node_index_t fib_entry_index)
 
        fib_walk_sync(FIB_NODE_TYPE_ENTRY, fib_entry_index, &bw_ctx);
     }
+    FIB_ENTRY_DBG(fib_entry, "cover-updated");
 }
 
 int
@@ -1328,7 +1470,16 @@ fib_entry_get_best_source (fib_node_index_t entry_index)
 }
 
 /**
- * Return !0 is the entry is reoslved, i.e. will return a valid forwarding
+ * Return !0 is the entry represents a host prefix
+ */
+int
+fib_entry_is_host (fib_node_index_t fib_entry_index)
+{
+    return (fib_prefix_is_host(fib_entry_get_prefix(fib_entry_index)));
+}
+
+/**
+ * Return !0 is the entry is resolved, i.e. will return a valid forwarding
  * chain
  */
 int
@@ -1344,7 +1495,7 @@ fib_entry_is_resolved (fib_node_index_t fib_entry_index)
     if (NULL == fed)
     {
         /*
-         * no BFD tracking - resolved
+         * no BFD tracking - consider it resolved.
          */
         return (!0);
     }
@@ -1357,14 +1508,53 @@ fib_entry_is_resolved (fib_node_index_t fib_entry_index)
     }
 }
 
+void
+fib_entry_set_flow_hash_config (fib_node_index_t fib_entry_index,
+                                flow_hash_config_t hash_config)
+{
+    fib_entry_t *fib_entry;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    /*
+     * pass the hash-config on to the load-balance object where it is cached.
+     * we can ignore LBs in the delegate chains, since they will not be of the
+     * correct protocol type (i.e. they are not IP)
+     * There's no way, nor need, to change the hash config for MPLS.
+     */
+    if (dpo_id_is_valid(&fib_entry->fe_lb))
+    {
+        load_balance_t *lb;
+
+        ASSERT(DPO_LOAD_BALANCE == fib_entry->fe_lb.dpoi_type);
+
+        lb = load_balance_get(fib_entry->fe_lb.dpoi_index);
+
+        /*
+         * atomic update for packets in flight
+         */
+        lb->lb_hash_config = hash_config;
+    }
+}
+
+u32
+fib_entry_get_stats_index (fib_node_index_t fib_entry_index)
+{
+    fib_entry_t *fib_entry;
+
+    fib_entry = fib_entry_get(fib_entry_index);
+
+    return (fib_entry->fe_lb.dpoi_index);
+}
+
 static int
 fib_ip4_address_compare (const ip4_address_t * a1,
                          const ip4_address_t * a2)
 {
     /*
-     * IP addresses are unsiged ints. the return value here needs to be signed
+     * IP addresses are unsigned ints. the return value here needs to be signed
      * a simple subtraction won't cut it.
-     * If the addresses are the same, the sort order is undefiend, so phoey.
+     * If the addresses are the same, the sort order is undefined, so phoey.
      */
     return ((clib_net_to_host_u32(a1->data_u32) >
             clib_net_to_host_u32(a2->data_u32) ) ?
@@ -1454,30 +1644,44 @@ fib_entry_unlock (fib_node_index_t fib_entry_index)
 void
 fib_entry_module_init (void)
 {
-    fib_node_register_type (FIB_NODE_TYPE_ENTRY, &fib_entry_vft);
+    fib_node_register_type(FIB_NODE_TYPE_ENTRY, &fib_entry_vft);
+    fib_entry_logger = vlib_log_register_class("fib", "entry");
 }
 
 void
 fib_entry_encode (fib_node_index_t fib_entry_index,
                  fib_route_path_encode_t **api_rpaths)
 {
+    fib_path_ext_list_t *ext_list;
     fib_entry_t *fib_entry;
+    fib_entry_src_t *bsrc;
 
+    ext_list = NULL;
     fib_entry = fib_entry_get(fib_entry_index);
+    bsrc = fib_entry_get_best_src_i(fib_entry);
+
+    if (bsrc)
+    {
+       ext_list = &bsrc->fes_path_exts;
+    }
+
     if (FIB_NODE_INDEX_INVALID != fib_entry->fe_parent)
     {
-        fib_path_list_walk(fib_entry->fe_parent, fib_path_encode, api_rpaths);
+        fib_path_list_walk_w_ext(fib_entry->fe_parent,
+                                 ext_list,
+                                 fib_path_encode,
+                                 api_rpaths);
     }
 }
 
-void
-fib_entry_get_prefix (fib_node_index_t fib_entry_index,
-                     fib_prefix_t *pfx)
+const fib_prefix_t *
+fib_entry_get_prefix (fib_node_index_t fib_entry_index)
 {
     fib_entry_t *fib_entry;
 
     fib_entry = fib_entry_get(fib_entry_index);
-    *pfx = fib_entry->fe_prefix;
+
+    return (&fib_entry->fe_prefix);
 }
 
 u32