VPP-44: iOAM service and path verification using shamir's secret sharing
[vpp.git] / vnet / vnet / lib-scv / scv_util.h
diff --git a/vnet/vnet/lib-scv/scv_util.h b/vnet/vnet/lib-scv/scv_util.h
new file mode 100644 (file)
index 0000000..fc0c73f
--- /dev/null
@@ -0,0 +1,278 @@
+/* 
+ * scv_util.h -- Service chain validation/Proof Of Transit Utility Header
+ *
+ * Copyright (c) 2015 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef include_vnet_scv_util_h
+#define include_vnet_scv_util_h
+
+#include <vnet/ip/ip6_hop_by_hop.h>
+#define MAXDEGREE 1024
+#define MAXTOKENLEN 128
+#define debug_ioam debug_ioam_fn
+#define MAX_SERVICE_NODES 10
+/* Dont change this size 256. This is there across multiple components */
+#define PATH_NAME_SIZE  256
+
+/* Ring size. this should be same as the one in ODL. Do not change this
+   without change in ODL. */
+#define MAX_SERVICE_PROFILES 16
+
+/**
+ * Usage:
+ * 
+ * On any [service] node that participates in Service / Path verfication:
+ *
+ * Step 1: Initialize this library by calling scv_init()
+ * Step 2: Setup a Service chain validation profile that contains all the parameters needed to compute cumulative:
+ *         Call these functions:
+ *         scv_profile_find
+ *         scv_profile_create
+ *         scv_profile_set_bit_mask - To setup how large we want the numbers used in the computation and random number <= 64 bits
+ * Step 2a: For validator do this:
+ *          scv_set_validator
+ * Step 3a: At the initial Service node to generate Random number that will be read by all other nodes:
+ *          scv_generate_random
+ * Step 3b: At all service nodes including initial and verifier call this to compute cumulative:
+ *          scv_update_cumulative
+ * Step 4: At the verifier:
+ *         scv_validate
+ * 
+ */
+
+typedef struct scv_profile_
+{
+    u16 id;
+    u64 random;
+    u8 validator;
+    u64 secret_key;
+    u64 secret_share;
+    u64 prime;
+    u64 lpc;
+    u64 poly_pre_eval;
+    u64 bit_mask;
+    u64 limit;
+    u64 validity;
+    double primeinv;
+    // struct hlist_node my_hash_list; when this gets added to hashtbale
+} scv_profile;
+
+extern scv_profile *pow_profile;
+extern u16 pow_profile_index;
+extern u64 total_pkts_using_this_profile;
+extern u8 chain_path_name[PATH_NAME_SIZE];
+extern u16 invalid_profile_start_index;
+extern u8 number_of_invalid_profiles;
+extern f64 next_time_to_send;
+extern u32 time_exponent;
+
+/* 
+ * Initialize Service chain
+ */
+void scv_init(u8 * path_name, u8 max, u8 indx);
+
+/* 
+ * Get maximum number of profiles configured for this chain.
+ */
+u8 scv_get_max_profiles(void);
+
+/* 
+ * Find a SC profile by ID
+ */
+scv_profile *scv_profile_find(u16 id);
+
+static inline u16 scv_profile_get_id(scv_profile * profile)
+{
+    if (profile)
+    {
+        return (profile->id);
+    }
+    return (0);
+}
+
+/* setup and clean up profile */
+void scv_profile_create(scv_profile * profile, u64 prime,
+    u64 poly2, u64 lpc, u64 secret_share, u64 validity);
+/* 
+ * Setup profile as a validator
+ */
+void scv_set_validator(scv_profile * profile, u64 key);
+void scv_profile_cleanup(scv_profile * profile);
+
+/* 
+ * Setup max bits to be used for random number generation
+ */
+#define MAX_BITS 64
+void scv_profile_set_bit_mask(scv_profile * profile, u16 bits);
+
+/* 
+ * Given a random and cumulative compute the new cumulative for a given profile
+ */
+u64 scv_update_cumulative(scv_profile * profile, u64 cumulative, u64 random);
+
+/* 
+ * return True if the cumulative matches secret from a profile
+ */
+u8 scv_validate(scv_profile * profile, u64 cumulative, u64 random);
+
+/* 
+ * Utility function to get random number per pack
+ */
+u64 scv_generate_random(scv_profile * profile);
+
+int scv_profile_to_str(scv_profile * profile, char *buf, int n);
+
+extern void clear_ioam_scv_profiles();
+
+static inline u8 scv_get_profile_in_use(void)
+{
+    return pow_profile_index;
+}
+
+static inline
+    void scv_notification_reset(u16 start_index_recvd, u8 num_profiles_recvd)
+{
+    /* Profiles recevied w/o notn. Nothing to do. */
+    if (number_of_invalid_profiles == 0)
+        return;
+
+    /* Most likely case. Got all requested profiles */
+    if (PREDICT_TRUE(num_profiles_recvd == number_of_invalid_profiles &&
+            start_index_recvd == invalid_profile_start_index))
+    {
+        number_of_invalid_profiles = 0;
+        invalid_profile_start_index = 0;
+        return;
+    }
+
+    /* Received partial list */
+    if (num_profiles_recvd < number_of_invalid_profiles)
+    {
+        ASSERT(start_index_recvd == invalid_profile_start_index);
+        invalid_profile_start_index = (start_index_recvd + num_profiles_recvd)
+            % scv_get_max_profiles();
+        number_of_invalid_profiles -= num_profiles_recvd;
+    }
+
+    return;
+}
+
+int __attribute__ ((weak)) scv_profile_renew(u8 * path_name,
+    u8 start_index, u8 num_profiles);
+int __attribute__ ((weak)) scv_profile_refresh(u8 * path_name,
+    u8 start_index, u8 num_profiles);
+
+static inline u8 scv_is_decap(scv_profile * p)
+{
+    return (p->validator == 1);
+}
+
+static inline u16 scv_get_next_profile_id(vlib_main_t * vm, u16 id)
+{
+    int next_id, num_profiles = 0;
+    scv_profile *p;
+    u8 max;
+
+    max = scv_get_max_profiles();
+
+    next_id = id;
+
+    /* Check for new profile in the ring buffer until a valid one. Exclude
+       checking for the one already in use. */
+    for (num_profiles = 0; num_profiles < max - 1; num_profiles++)
+    {
+        next_id = (next_id + 1) % max;
+        p = scv_profile_find(next_id);
+        if (p->validity != 0)
+        {
+            vlib_cli_output(vm, "Current id: %d, New id: %d\n", id, next_id);
+            return (next_id);
+        }
+    }
+
+    return (id);
+}
+
+static inline void
+scv_profile_invalidate(vlib_main_t * vm, ip6_hop_by_hop_main_t * hm,
+    u16 id, u8 is_encap)
+{
+    scv_profile *p = scv_profile_find(id);
+    int rc;
+    u8 max;
+    f64 now = 0;
+
+    p->validity = 0;
+
+    /* If there are alredy profiles waiting. If so, use existing start_index. 
+     */
+    if (!number_of_invalid_profiles)
+        invalid_profile_start_index = id;
+
+    max = scv_get_max_profiles();
+
+    /* Check whether the id is already included in existing list */
+    if (!(id >= invalid_profile_start_index &&
+            id <= (invalid_profile_start_index +
+                number_of_invalid_profiles - 1) % max))
+    {
+        number_of_invalid_profiles++;
+    }
+
+    if (number_of_invalid_profiles > scv_get_max_profiles())
+        number_of_invalid_profiles = scv_get_max_profiles();
+
+    now = (f64) (((f64) hm->unix_time_0) +
+        (vlib_time_now(hm->vlib_main) - hm->vlib_time_0));
+    if (now <= next_time_to_send)
+        return;
+
+    if (is_encap)
+    {
+        rc = scv_profile_renew(chain_path_name,
+            (u8) invalid_profile_start_index, number_of_invalid_profiles);
+        if (rc != 0)
+            vlib_cli_output(vm,
+                "Renew notification- id start:%d,  num %d failed. rc: %d\n",
+                invalid_profile_start_index, number_of_invalid_profiles, rc);
+        else
+            vlib_cli_output(vm,
+                "Renew notification- id start:%d num %d sent. \n",
+                invalid_profile_start_index, number_of_invalid_profiles);
+
+    }
+    else
+    {
+        /* Non encap node. Send refresh notification for now. Later set a
+           timer and if there is no profile even after the timeout send
+           refresh notification. */
+        rc = scv_profile_refresh(chain_path_name,
+            (u8) invalid_profile_start_index, number_of_invalid_profiles);
+        if (rc != 0)
+            vlib_cli_output(vm,
+                "Refresh notification- id start:%d,  num %d failed. rc: %d\n",
+                invalid_profile_start_index, number_of_invalid_profiles, rc);
+        else
+            vlib_cli_output(vm,
+                "Refresh notification- id start:%d num %d sent. \n",
+                invalid_profile_start_index, number_of_invalid_profiles);
+    }
+    next_time_to_send = now + time_exponent;
+    time_exponent <<= 1;        /* backoff time is power of 2 seconds */
+
+    return;
+}
+
+#endif