SR-MPLS: MPLS table checks (VPP-1422)
[vpp.git] / src / vnet / srmpls / sr_mpls_policy.c
index d75f2d1..f94a8e9 100755 (executable)
@@ -34,9 +34,6 @@
 #include <vnet/srmpls/sr_mpls.h>
 #include <vnet/fib/mpls_fib.h>
 #include <vnet/dpo/dpo.h>
-#include <vnet/dpo/replicate_dpo.h>
-#include <vnet/dpo/mpls_label_dpo.h>
-#include <vnet/dpo/lookup_dpo.h>
 #include <vnet/ip/ip.h>
 
 #include <vppinfra/error.h>
@@ -64,6 +61,7 @@ create_sl (mpls_sr_policy_t * sr_policy, mpls_label_t * sl, u32 weight)
 {
   mpls_sr_main_t *sm = &sr_mpls_main;
   mpls_sr_sl_t *segment_list;
+  u32 ii;
 
   pool_get (sm->sid_lists, segment_list);
   memset (segment_list, 0, sizeof (*segment_list));
@@ -75,33 +73,50 @@ create_sl (mpls_sr_policy_t * sr_policy, mpls_label_t * sl, u32 weight)
     (weight != (u32) ~ 0 ? weight : SR_SEGMENT_LIST_WEIGHT_DEFAULT);
   segment_list->segments = vec_dup (sl);
 
-  fib_route_path_t path = {
-    .frp_proto = DPO_PROTO_MPLS,
-    .frp_sw_if_index = ~0,
-    .frp_fib_index = 0,
-    .frp_weight = segment_list->weight,
-    .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
-    .frp_label_stack = NULL,
-    .frp_local_label = sl[0],
-  };
-
-  vec_add (path.frp_label_stack, sl + 1, vec_len (sl) - 1);
-
-  fib_route_path_t *paths = NULL;
-  vec_add1 (paths, path);
-
   mpls_eos_bit_t eos;
   FOR_EACH_MPLS_EOS_BIT (eos)
   {
-               /* *INDENT-OFF* */
-               fib_prefix_t    pfx = {
-                       .fp_len = 21,
-                       .fp_proto = FIB_PROTOCOL_MPLS,
-                       .fp_label = sr_policy->bsid,
-                       .fp_eos = eos,
-                       .fp_payload_proto = DPO_PROTO_MPLS,
-               };
-               /* *INDENT-ON* */
+    fib_route_path_t path = {
+      .frp_proto = DPO_PROTO_MPLS,
+      .frp_sw_if_index = ~0,
+      .frp_fib_index = 0,
+      .frp_weight = segment_list->weight,
+      .frp_flags = FIB_ROUTE_PATH_FLAG_NONE,
+      .frp_label_stack = NULL,
+      .frp_local_label = sl[0],
+    };
+
+    if (vec_len (sl) > 1)
+      {
+       vec_validate (path.frp_label_stack, vec_len (sl) - 2);
+       for (ii = 1; ii < vec_len (sl); ii++)
+         {
+           path.frp_label_stack[ii - 1].fml_value = sl[ii];
+         }
+      }
+    else
+      {
+       /*
+        * add an impliciet NULL label to allow non-eos recursion
+        */
+       fib_mpls_label_t lbl = {
+         .fml_value = MPLS_IETF_IMPLICIT_NULL_LABEL,
+       };
+       vec_add1 (path.frp_label_stack, lbl);
+      }
+
+    fib_route_path_t *paths = NULL;
+    vec_add1 (paths, path);
+
+    /* *INDENT-OFF* */
+    fib_prefix_t pfx = {
+        .fp_len = 21,
+        .fp_proto = FIB_PROTOCOL_MPLS,
+        .fp_label = sr_policy->bsid,
+        .fp_eos = eos,
+        .fp_payload_proto = DPO_PROTO_MPLS,
+    };
+    /* *INDENT-ON* */
 
     fib_table_entry_path_add2 (0,
                               &pfx,
@@ -109,10 +124,9 @@ create_sl (mpls_sr_policy_t * sr_policy, mpls_label_t * sl, u32 weight)
                               (sr_policy->type == SR_POLICY_TYPE_DEFAULT ?
                                FIB_ENTRY_FLAG_NONE :
                                FIB_ENTRY_FLAG_MULTICAST), paths);
+    vec_free (paths);
   }
 
-  vec_free (paths);
-
   return segment_list;
 }
 
@@ -145,6 +159,10 @@ sr_mpls_policy_add (mpls_label_t bsid, mpls_label_t * segments,
   if (!sm->sr_policies_index_hash)
     sm->sr_policies_index_hash = hash_create (0, sizeof (mpls_label_t));
 
+  /* MPLS SR policies cannot be created unless the MPLS table is present */
+  if (~0 == fib_table_find (FIB_PROTOCOL_MPLS, MPLS_FIB_DEFAULT_TABLE_ID))
+    return (VNET_API_ERROR_NO_SUCH_TABLE);
+
   /* Search for existing keys (BSID) */
   p = hash_get (sm->sr_policies_index_hash, bsid);
   if (p)
@@ -155,6 +173,13 @@ sr_mpls_policy_add (mpls_label_t bsid, mpls_label_t * segments,
   /* Add an SR policy object */
   pool_get (sm->sr_policies, sr_policy);
   memset (sr_policy, 0, sizeof (*sr_policy));
+
+  /* the first policy needs to lock the MPLS table so it doesn't
+   * disappear with policies in it */
+  if (1 == pool_elts (sm->sr_policies))
+    fib_table_find_or_create_and_lock (FIB_PROTOCOL_MPLS,
+                                      MPLS_FIB_DEFAULT_TABLE_ID,
+                                      FIB_SOURCE_SR);
   sr_policy->bsid = bsid;
   sr_policy->type = behavior;
   sr_policy->endpoint_type = 0;
@@ -247,6 +272,10 @@ sr_mpls_policy_del (mpls_label_t bsid)
   hash_unset (sm->sr_policies_index_hash, sr_policy->bsid);
   pool_put (sm->sr_policies, sr_policy);
 
+  if (0 == pool_elts (sm->sr_policies))
+    fib_table_unlock (MPLS_FIB_DEFAULT_TABLE_ID,
+                     FIB_PROTOCOL_MPLS, FIB_SOURCE_SR);
+
   return 0;
 }
 
@@ -488,6 +517,7 @@ sr_mpls_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
       rv = sr_mpls_policy_add (bsid, segments,
                               (is_spray ? SR_POLICY_TYPE_SPRAY :
                                SR_POLICY_TYPE_DEFAULT), weight);
+      vec_free (segments);
     }
   else if (is_del)
     rv = sr_mpls_policy_del (bsid);
@@ -502,6 +532,7 @@ sr_mpls_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
       if (operation == 3 && weight == (u32) ~ 0)
        return clib_error_return (0, "No new weight for the SL specified");
       rv = sr_mpls_policy_mod (bsid, operation, segments, sl_index, weight);
+      vec_free (segments);
     }
   switch (rv)
     {
@@ -529,6 +560,8 @@ sr_mpls_policy_command_fn (vlib_main_t * vm, unformat_input_t * input,
       return clib_error_return (0,
                                "Could not modify the segment list. "
                                "The given SL is not associated with such SR policy.");
+    case VNET_API_ERROR_NO_SUCH_TABLE:
+      return clib_error_return (0, "the Default MPLS table is not present");
     default:
       return clib_error_return (0, "BUG: sr policy returns %d", rv);
     }
@@ -665,6 +698,10 @@ sr_mpls_policy_assign_endpoint_color (mpls_label_t bsid,
       old_value =
        mhash_get ((mhash_t *) endpoint_table, &sr_policy->endpoint);
 
+      /* CID 180995 This should never be NULL unless the two hash tables
+       * get out of sync */
+      ASSERT (old_value != NULL);
+
       fib_prefix_t pfx = { 0 };
       pfx.fp_proto = FIB_PROTOCOL_MPLS;
       pfx.fp_len = 21;
@@ -764,7 +801,11 @@ sr_mpls_policy_assign_endpoint_color (mpls_label_t bsid,
                              FIB_SOURCE_SR,
                              FIB_ENTRY_FLAG_LOOSE_URPF_EXEMPT, paths);
 
-      vec_add1 (path.frp_label_stack, MPLS_IETF_IMPLICIT_NULL_LABEL);
+      fib_mpls_label_t fml = {
+       .fml_value = MPLS_IETF_IMPLICIT_NULL_LABEL,
+      };
+
+      vec_add1 (path.frp_label_stack, fml);
       pfx.fp_eos = MPLS_NON_EOS;
       path.frp_eos = MPLS_NON_EOS;