fc0c73f47b1b49a7ecbc3f9b00193791ca5eec54
[vpp.git] / vnet / vnet / lib-scv / scv_util.h
1 /* 
2  * scv_util.h -- Service chain validation/Proof Of Transit Utility Header
3  *
4  * Copyright (c) 2015 Cisco and/or its affiliates.
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at:
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #ifndef include_vnet_scv_util_h
19 #define include_vnet_scv_util_h
20
21 #include <vnet/ip/ip6_hop_by_hop.h>
22 #define MAXDEGREE 1024
23 #define MAXTOKENLEN 128
24 #define debug_ioam debug_ioam_fn
25 #define MAX_SERVICE_NODES 10
26 /* Dont change this size 256. This is there across multiple components */
27 #define PATH_NAME_SIZE  256
28
29 /* Ring size. this should be same as the one in ODL. Do not change this
30    without change in ODL. */
31 #define MAX_SERVICE_PROFILES 16
32
33 /**
34  * Usage:
35  * 
36  * On any [service] node that participates in Service / Path verfication:
37  *
38  * Step 1: Initialize this library by calling scv_init()
39  * Step 2: Setup a Service chain validation profile that contains all the parameters needed to compute cumulative:
40  *         Call these functions:
41  *         scv_profile_find
42  *         scv_profile_create
43  *         scv_profile_set_bit_mask - To setup how large we want the numbers used in the computation and random number <= 64 bits
44  * Step 2a: For validator do this:
45  *          scv_set_validator
46  * Step 3a: At the initial Service node to generate Random number that will be read by all other nodes:
47  *          scv_generate_random
48  * Step 3b: At all service nodes including initial and verifier call this to compute cumulative:
49  *          scv_update_cumulative
50  * Step 4: At the verifier:
51  *         scv_validate
52  * 
53  */
54
55 typedef struct scv_profile_
56 {
57     u16 id;
58     u64 random;
59     u8 validator;
60     u64 secret_key;
61     u64 secret_share;
62     u64 prime;
63     u64 lpc;
64     u64 poly_pre_eval;
65     u64 bit_mask;
66     u64 limit;
67     u64 validity;
68     double primeinv;
69     // struct hlist_node my_hash_list; when this gets added to hashtbale
70 } scv_profile;
71
72 extern scv_profile *pow_profile;
73 extern u16 pow_profile_index;
74 extern u64 total_pkts_using_this_profile;
75 extern u8 chain_path_name[PATH_NAME_SIZE];
76 extern u16 invalid_profile_start_index;
77 extern u8 number_of_invalid_profiles;
78 extern f64 next_time_to_send;
79 extern u32 time_exponent;
80
81 /* 
82  * Initialize Service chain
83  */
84 void scv_init(u8 * path_name, u8 max, u8 indx);
85
86 /* 
87  * Get maximum number of profiles configured for this chain.
88  */
89 u8 scv_get_max_profiles(void);
90
91 /* 
92  * Find a SC profile by ID
93  */
94 scv_profile *scv_profile_find(u16 id);
95
96 static inline u16 scv_profile_get_id(scv_profile * profile)
97 {
98     if (profile)
99     {
100         return (profile->id);
101     }
102     return (0);
103 }
104
105 /* setup and clean up profile */
106 void scv_profile_create(scv_profile * profile, u64 prime,
107     u64 poly2, u64 lpc, u64 secret_share, u64 validity);
108 /* 
109  * Setup profile as a validator
110  */
111 void scv_set_validator(scv_profile * profile, u64 key);
112 void scv_profile_cleanup(scv_profile * profile);
113
114 /* 
115  * Setup max bits to be used for random number generation
116  */
117 #define MAX_BITS 64
118 void scv_profile_set_bit_mask(scv_profile * profile, u16 bits);
119
120 /* 
121  * Given a random and cumulative compute the new cumulative for a given profile
122  */
123 u64 scv_update_cumulative(scv_profile * profile, u64 cumulative, u64 random);
124
125 /* 
126  * return True if the cumulative matches secret from a profile
127  */
128 u8 scv_validate(scv_profile * profile, u64 cumulative, u64 random);
129
130 /* 
131  * Utility function to get random number per pack
132  */
133 u64 scv_generate_random(scv_profile * profile);
134
135 int scv_profile_to_str(scv_profile * profile, char *buf, int n);
136
137 extern void clear_ioam_scv_profiles();
138
139 static inline u8 scv_get_profile_in_use(void)
140 {
141     return pow_profile_index;
142 }
143
144 static inline
145     void scv_notification_reset(u16 start_index_recvd, u8 num_profiles_recvd)
146 {
147     /* Profiles recevied w/o notn. Nothing to do. */
148     if (number_of_invalid_profiles == 0)
149         return;
150
151     /* Most likely case. Got all requested profiles */
152     if (PREDICT_TRUE(num_profiles_recvd == number_of_invalid_profiles &&
153             start_index_recvd == invalid_profile_start_index))
154     {
155         number_of_invalid_profiles = 0;
156         invalid_profile_start_index = 0;
157         return;
158     }
159
160     /* Received partial list */
161     if (num_profiles_recvd < number_of_invalid_profiles)
162     {
163         ASSERT(start_index_recvd == invalid_profile_start_index);
164         invalid_profile_start_index = (start_index_recvd + num_profiles_recvd)
165             % scv_get_max_profiles();
166         number_of_invalid_profiles -= num_profiles_recvd;
167     }
168
169     return;
170 }
171
172 int __attribute__ ((weak)) scv_profile_renew(u8 * path_name,
173     u8 start_index, u8 num_profiles);
174 int __attribute__ ((weak)) scv_profile_refresh(u8 * path_name,
175     u8 start_index, u8 num_profiles);
176
177 static inline u8 scv_is_decap(scv_profile * p)
178 {
179     return (p->validator == 1);
180 }
181
182 static inline u16 scv_get_next_profile_id(vlib_main_t * vm, u16 id)
183 {
184     int next_id, num_profiles = 0;
185     scv_profile *p;
186     u8 max;
187
188     max = scv_get_max_profiles();
189
190     next_id = id;
191
192     /* Check for new profile in the ring buffer until a valid one. Exclude
193        checking for the one already in use. */
194     for (num_profiles = 0; num_profiles < max - 1; num_profiles++)
195     {
196         next_id = (next_id + 1) % max;
197         p = scv_profile_find(next_id);
198         if (p->validity != 0)
199         {
200             vlib_cli_output(vm, "Current id: %d, New id: %d\n", id, next_id);
201             return (next_id);
202         }
203     }
204
205     return (id);
206 }
207
208 static inline void
209 scv_profile_invalidate(vlib_main_t * vm, ip6_hop_by_hop_main_t * hm,
210     u16 id, u8 is_encap)
211 {
212     scv_profile *p = scv_profile_find(id);
213     int rc;
214     u8 max;
215     f64 now = 0;
216
217     p->validity = 0;
218
219     /* If there are alredy profiles waiting. If so, use existing start_index. 
220      */
221     if (!number_of_invalid_profiles)
222         invalid_profile_start_index = id;
223
224     max = scv_get_max_profiles();
225
226     /* Check whether the id is already included in existing list */
227     if (!(id >= invalid_profile_start_index &&
228             id <= (invalid_profile_start_index +
229                 number_of_invalid_profiles - 1) % max))
230     {
231         number_of_invalid_profiles++;
232     }
233
234     if (number_of_invalid_profiles > scv_get_max_profiles())
235         number_of_invalid_profiles = scv_get_max_profiles();
236
237     now = (f64) (((f64) hm->unix_time_0) +
238         (vlib_time_now(hm->vlib_main) - hm->vlib_time_0));
239     if (now <= next_time_to_send)
240         return;
241
242     if (is_encap)
243     {
244         rc = scv_profile_renew(chain_path_name,
245             (u8) invalid_profile_start_index, number_of_invalid_profiles);
246         if (rc != 0)
247             vlib_cli_output(vm,
248                 "Renew notification- id start:%d,  num %d failed. rc: %d\n",
249                 invalid_profile_start_index, number_of_invalid_profiles, rc);
250         else
251             vlib_cli_output(vm,
252                 "Renew notification- id start:%d num %d sent. \n",
253                 invalid_profile_start_index, number_of_invalid_profiles);
254
255     }
256     else
257     {
258         /* Non encap node. Send refresh notification for now. Later set a
259            timer and if there is no profile even after the timeout send
260            refresh notification. */
261         rc = scv_profile_refresh(chain_path_name,
262             (u8) invalid_profile_start_index, number_of_invalid_profiles);
263         if (rc != 0)
264             vlib_cli_output(vm,
265                 "Refresh notification- id start:%d,  num %d failed. rc: %d\n",
266                 invalid_profile_start_index, number_of_invalid_profiles, rc);
267         else
268             vlib_cli_output(vm,
269                 "Refresh notification- id start:%d num %d sent. \n",
270                 invalid_profile_start_index, number_of_invalid_profiles);
271     }
272     next_time_to_send = now + time_exponent;
273     time_exponent <<= 1;        /* backoff time is power of 2 seconds */
274
275     return;
276 }
277
278 #endif