1/*
2 * Copyright (c) 2016 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <vnet/vnet.h>
16#include <stdint.h>
17#include <time.h>
18#include <string.h>
19#include <vppinfra/mem.h>
20#include "math64.h"
21#include "pot_util.h"
22
23pot_main_t pot_main;
24
25static void pot_profile_cleanup(pot_profile *profile);
26
27static void pot_main_profiles_reset (void)
28{
29    pot_main_t *sm = &pot_main;
30    int i = 0;
31
32    for (i = 0; i < MAX_POT_PROFILES; i++)
33    {
34      pot_profile_cleanup(&(sm->profile_list[i]));
35    }
36    sm->active_profile_id = 0;
37    if (sm->profile_list_name)
38	vec_free(sm->profile_list_name);
39    sm->profile_list_name = NULL;
40}
41
42int pot_util_init (void)
43{
44    pot_main_profiles_reset();
45
46    return(0);
47}
48
49static void pot_profile_init(pot_profile * new, u8 id)
50{
51    if (new)
52    {
53        clib_memset(new, 0, sizeof(pot_profile));
54        new->id = id;
55    }
56}
57
58pot_profile *pot_profile_find(u8 id)
59{
60    pot_main_t *sm = &pot_main;
61
62    if (id < MAX_POT_PROFILES)
63    {
64        return (&(sm->profile_list[id]));
65    }
66    return (NULL);
67}
68static int pot_profile_name_equal (u8 *name0, u8 *name1)
69{
70    int len0, len1;
71
72    len0 = vec_len (name0);
73    len1 = vec_len (name1);
74    if (len0 != len1)
75        return(0);
76    return (0==strncmp ((char *) name0, (char *)name1, len0));
77}
78
79int pot_profile_list_is_enabled (u8 *name)
80{
81    pot_main_t *sm = &pot_main;
82    return (pot_profile_name_equal(sm->profile_list_name, name));
83}
84
85void pot_profile_list_init(u8 * profile_list_name)
86{
87    pot_main_t *sm = &pot_main;
88    int i = 0;
89
90    /* If it is the same profile list skip reset */
91    if (pot_profile_name_equal(sm->profile_list_name, profile_list_name))
92    {
93      return;
94    }
95
96    pot_main_profiles_reset();
97    if (vec_len(profile_list_name))
98      sm->profile_list_name = (u8 *)vec_dup(profile_list_name);
99    else
100      sm->profile_list_name = 0;
101    sm->active_profile_id = 0;
102
103    for (i = 0; i < MAX_POT_PROFILES; i++)
104    {
105      pot_profile_init(&(sm->profile_list[i]), i);
106    }
107}
108
109static void pot_profile_cleanup(pot_profile * profile)
110{
111    u16 id = profile->id;
112
113    clib_memset(profile, 0, sizeof(pot_profile));
114    profile->id = id;           /* Restore id alone */
115}
116
117int pot_profile_create(pot_profile * profile, u64 prime,
118    u64 poly2, u64 lpc, u64 secret_share)
119{
120    if (profile && !profile->in_use)
121    {
122        pot_profile_cleanup(profile);
123        profile->prime = prime;
124        profile->primeinv = 1.0 / prime;
125        profile->lpc = lpc;
126        profile->poly_pre_eval = poly2;
127        profile->secret_share = secret_share;
128	profile->total_pkts_using_this_profile = 0;
129        profile->valid = 1;
130	return(0);
131    }
132
133    return(-1);
134}
135
136int pot_set_validator(pot_profile * profile, u64 key)
137{
138    if (profile && !profile->in_use)
139    {
140        profile->validator = 1;
141        profile->secret_key = key;
142	return(0);
143    }
144    return(-1);
145}
146
147always_inline u64 pot_update_cumulative_inline(u64 cumulative, u64 random,
148    u64 secret_share, u64 prime, u64 lpc, u64 pre_split, double prime_inv)
149{
150    u64 share_random = 0;
151    u64 cumulative_new = 0;
152
153    /*
154     * calculate split share for random
155     */
156    share_random = add64_mod(pre_split, random, prime, prime_inv);
157
158    /*
159     * lpc * (share_secret + share_random)
160     */
161    share_random = add64_mod(share_random, secret_share, prime, prime_inv);
162    share_random = mul64_mod(share_random, lpc, prime, prime_inv);
163
164    cumulative_new = add64_mod(cumulative, share_random, prime, prime_inv);
165
166    return (cumulative_new);
167}
168
169u64 pot_update_cumulative(pot_profile * profile, u64 cumulative, u64 random)
170{
171    if (profile && profile->valid != 0)
172    {
173        return (pot_update_cumulative_inline(cumulative, random, profile->secret_share,
174                profile->prime, profile->lpc, profile->poly_pre_eval,
175                profile->primeinv));
176    }
177    return (0);
178}
179
180always_inline u8 pot_validate_inline(u64 secret, u64 prime, double prime_inv,
181    u64 cumulative, u64 random)
182{
183    if (cumulative == (random + secret))
184    {
185        return (1);
186    }
187    else if (cumulative == add64_mod(random, secret, prime, prime_inv))
188    {
189        return (1);
190    }
191    return (0);
192}
193
194/*
195 * return True if the cumulative matches secret from a profile
196 */
197u8 pot_validate(pot_profile * profile, u64 cumulative, u64 random)
198{
199    if (profile && profile->validator)
200    {
201        return (pot_validate_inline(profile->secret_key, profile->prime,
202                profile->primeinv, cumulative, random));
203    }
204    return (0);
205}
206
207/*
208 * Utility function to get random number per pack
209 */
210u64 pot_generate_random(pot_profile * profile)
211{
212    u64 random = 0;
213    int32_t second_half;
214    static u32 seed = 0;
215
216    if (PREDICT_FALSE(!seed))
217        seed = random_default_seed();
218
219    /*
220     * Upper 4 bytes seconds
221     */
222    random = (u64) time(NULL);
223
224    random &= 0xffffffff;
225    random = random << 32;
226    /*
227     * Lower 4 bytes random number
228     */
229    second_half = random_u32(&seed);
230
231    random |= second_half;
232
233    if (PREDICT_TRUE(profile != NULL))
234    {
235        random &= profile->bit_mask;
236    }
237    return (random);
238}
239
240int pot_profile_set_bit_mask(pot_profile * profile, u16 bits)
241{
242    int sizeInBits;
243
244    if (profile && !profile->in_use)
245    {
246        sizeInBits = sizeof(profile->bit_mask) * 8;
247        profile->bit_mask =
248            (bits >=
249            sizeInBits ? (u64) - 1 : (u64) ((u64) 1 << (u64) bits) - 1);
250	return(0);
251    }
252    return(-1);
253}
254
255clib_error_t *clear_pot_profile_command_fn(vlib_main_t * vm,
256    unformat_input_t * input, vlib_cli_command_t * cmd)
257{
258
259    pot_main_profiles_reset();
260
261    return 0;
262}
263
264void clear_pot_profiles()
265{
266    clear_pot_profile_command_fn(0, 0, 0);
267}
268
269VLIB_CLI_COMMAND(clear_pot_profile_command) =
270{
271.path = "clear pot profile",
272.short_help = "clear pot profile [<index>|all]",
273.function = clear_pot_profile_command_fn,
274};
275
276static clib_error_t *set_pot_profile_command_fn(vlib_main_t * vm,
277    unformat_input_t * input, vlib_cli_command_t * cmd)
278{
279    u64 prime;
280    u64 secret_share;
281    u64 secret_key;
282    u8 validator = 0;
283    u32 profile_id = ~0;
284    u32 bits;
285    u64 lpc = 0, poly2 = 0;
286    pot_profile *profile = NULL;
287    u8 *profile_list_name = NULL;
288
289    bits = MAX_BITS;
290
291    while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
292    {
293        if (unformat(input, "name %s",
294		     &profile_list_name));
295        else if (unformat(input, "id %d", &profile_id))
296            ;
297        else if (unformat(input, "validate-key 0x%Lx", &secret_key))
298            validator = 1;
299        else if (unformat(input, "prime-number 0x%Lx", &prime))
300            ;
301        else if (unformat(input, "secret_share 0x%Lx", &secret_share))
302            ;
303        else if (unformat(input, "polynomial2 0x%Lx", &poly2))
304            ;
305        else if (unformat(input, "lpc 0x%Lx", &lpc))
306            ;
307        else if (unformat(input, "bits-in-random %d", &bits))
308        {
309            if (bits > MAX_BITS)
310                bits = MAX_BITS;
311        }
312        else
313	  break;
314    }
315    if (profile_list_name == 0)
316    {
317        return clib_error_return(0, "Name cannot be null");
318    }
319    pot_profile_list_init(profile_list_name);
320    profile = pot_profile_find(profile_id);
321
322    if (profile)
323    {
324        pot_profile_create(profile, prime, poly2, lpc, secret_share);
325        if (validator)
326            pot_set_validator(profile, secret_key);
327        pot_profile_set_bit_mask(profile, bits);
328    }
329    vec_free(profile_list_name);
330    return 0;
331}
332
333VLIB_CLI_COMMAND(set_pot_profile_command) =
334{
335.path = "set pot profile",
336.short_help = "set pot profile name <string> id [0-1] [validator-key 0xu64] \
337                  prime-number 0xu64 secret_share 0xu64 lpc 0xu64 \
338                  polynomial2 0xu64 bits-in-random [0-64] ",
339.function = set_pot_profile_command_fn,
340};
341
342static clib_error_t *set_pot_profile_activate_command_fn(vlib_main_t * vm,
343    unformat_input_t * input, vlib_cli_command_t * cmd)
344{
345    pot_main_t *sm = &pot_main;
346    u8 *profile_list_name = NULL;
347    u32 id = 0;
348    clib_error_t *result = NULL;
349
350    while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT)
351    {
352        if (unformat(input, "name %s",
353		     &profile_list_name));
354        else if (unformat(input, "id %d", &id))
355            ;
356        else
357            return clib_error_return(0, "unknown input `%U'",
358                format_unformat_error, input);
359    }
360    if (profile_list_name == 0)
361    {
362        return clib_error_return(0, "Name cannot be null");
363    }
364
365    if (!pot_profile_list_is_enabled(profile_list_name)) {
366        result = clib_error_return(0, "%s list is not enabled, profile in use %s",
367				 profile_list_name, sm->profile_list_name);
368    } else if (0 != pot_profile_set_active((u8)id)) {
369        result = clib_error_return(0, "Profile %d not defined in %s",
370				 id, sm->profile_list_name);
371    }
372    vec_free(profile_list_name);
373    return result;
374}
375
376VLIB_CLI_COMMAND(set_pot_profile_activate_command) =
377{
378.path = "set pot profile-active",
379.short_help = "set pot profile-active name <string> id [0-1]",
380.function = set_pot_profile_activate_command_fn,
381};
382
383static clib_error_t *show_pot_profile_command_fn(vlib_main_t * vm,
384    unformat_input_t * input, vlib_cli_command_t * cmd)
385{
386    pot_main_t *sm = &pot_main;
387    pot_profile *p = NULL;
388    u16 i;
389    u8 *s = 0;
390
391    if (vec_len(sm->profile_list_name) == 0)
392    {
393        s = format(s, "POT Profiles not configured\n");
394        vlib_cli_output(vm, "%v", s);
395        return 0;
396    }
397    s = format(s, "Profile list in use  : %s\n",sm->profile_list_name);
398    for (i = 0; i < MAX_POT_PROFILES; i++)
399    {
400        p = pot_profile_find(i);
401        if (p->valid == 0)
402            continue;
403        s = format(s, "POT Profile at index: %d\n", i);
404        s = format(s, "                 Id : %d\n", p->id);
405        s = format(s, "          Validator : %s (%d)\n",
406            (p->validator) ? "True" : "False", p->validator);
407        if (p->validator == 1)
408            s = format(s, "         Secret key : 0x%Lx (%Ld)\n",
409                p->secret_key, p->secret_key);
410        s = format(s, "       Secret share : 0x%Lx (%Ld)\n",
411            p->secret_share, p->secret_share);
412        s = format(s, "       Prime number : 0x%Lx (%Ld)\n",
413            p->prime, p->prime);
414        s = format(s, "2nd polynomial(eval) : 0x%Lx (%Ld)\n",
415            p->poly_pre_eval, p->poly_pre_eval);
416        s = format(s, "                 LPC : 0x%Lx (%Ld)\n", p->lpc, p->lpc);
417
418        s = format(s, "           Bit mask : 0x%Lx (%Ld)\n",
419            p->bit_mask, p->bit_mask);
420    }
421
422    p = pot_profile_find(sm->active_profile_id);
423
424    if (p && p->valid && p->in_use) {
425        s = format(s, "\nProfile index in use: %d\n", sm->active_profile_id);
426        s = format(s, "Pkts passed : 0x%Lx (%Ld)\n",
427		   p->total_pkts_using_this_profile,
428		   p->total_pkts_using_this_profile);
429        if (pot_is_decap(p))
430            s = format(s, "  This is Decap node.  \n");
431    } else {
432        s = format(s, "\nProfile index in use: None\n");
433    }
434    vlib_cli_output(vm, "%v", s);
435    vec_free(s);
436
437    return 0;
438}
439
440VLIB_CLI_COMMAND(show_pot_profile_command) =
441{
442.path = "show pot profile",
443.short_help = "show pot profile",
444.function = show_pot_profile_command_fn,
445};
446