From: Shwetha Date: Wed, 15 Jun 2016 15:34:16 +0000 (+0100) Subject: VPP-76:APIs for Proof of transit feature added to iOAM X-Git-Tag: v16.09-rc1~276 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=85b528e093b93e939a63cd76feef4cfa140aac6c;p=vpp.git VPP-76:APIs for Proof of transit feature added to iOAM Moved Proof of Transit utility as a plugin Moved Proof of Transit option as a plugin Change-Id: Idc9897205eb8ec80c5dea47b428e6209ac938c32 Signed-off-by: Shwetha --- diff --git a/plugins/Makefile.am b/plugins/Makefile.am index e72b863501c..fee7493d771 100644 --- a/plugins/Makefile.am +++ b/plugins/Makefile.am @@ -27,9 +27,40 @@ BUILT_SOURCES = lib_LTLIBRARIES = libsixrd_plugin.la +######################################## +# iOAM Proof of Transit +######################################## + +ioam_pot_plugin_la_SOURCES = plugins/ioam/lib-pot/pot_util.c plugins/ioam/encap/ip6_ioam_pot.c \ + plugins/ioam/lib-pot/pot_util.h plugins/ioam/lib-pot/math64.h plugins/ioam/lib-pot/pot_api.c +ioam_pot_plugin_la_LDFLAGS = -module + +BUILT_SOURCES = plugins/ioam/lib-pot/pot.api.h +SUFFIXES = .api.h .api + +%.api.h: %.api + mkdir -p `dirname $@` ; \ + $(CC) $(CPPFLAGS) -E -P -C -x c $^ \ + | vppapigen --input - --output $@ --show-name $@ + +nobase_include_HEADERS = \ + plugins/ioam/lib-pot/pot_all_api_h.h \ + plugins/ioam/lib-pot/pot_msg_enum.h \ + plugins/ioam/lib-pot/pot.api.h \ + plugins/ioam/lib-pot/pot_util.h \ + plugins/ioam/lib-pot/math64.h + +ioam_pot_test_plugin_la_SOURCES = plugins/ioam/lib-pot/pot_test.c plugins/ioam/lib-pot/pot_plugin.api.h +ioam_pot_test_plugin_la_LDFLAGS = -module + +lib_LTLIBRARIES += ioam_pot_plugin.la ioam_pot_test_plugin.la + if WITH_PLUGIN_TOOLKIT install-data-hook: mkdir /usr/lib/vpp_plugins || true mkdir /usr/lib/vpp_api_test_plugins || true cp $(prefix)/lib/sixrd_plugin.so.*.*.* /usr/lib/vpp_plugins + cp $(prefix)/lib/ioam_pot_plugin.so.*.*.* /usr/lib/vpp_plugins + cp $(prefix)/lib/ioam_pot_test_plugin.so.*.*.* \ + /usr/lib/vpp_api_test_plugins endif diff --git a/plugins/plugins/ioam/encap/ip6_ioam_pot.c b/plugins/plugins/ioam/encap/ip6_ioam_pot.c new file mode 100644 index 00000000000..e90a6649909 --- /dev/null +++ b/plugins/plugins/ioam/encap/ip6_ioam_pot.c @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2016 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. + */ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +typedef CLIB_PACKED(struct { + ip6_hop_by_hop_option_t hdr; + u8 pot_type; +#define PROFILE_ID_MASK 0xF + u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ + u64 random; + u64 cumulative; +}) ioam_pot_option_t; + +#define foreach_ip6_hop_by_hop_ioam_pot_stats \ + _(PROCESSED, "Pkts with ip6 hop-by-hop pot options") \ + _(PROFILE_MISS, "Pkts with ip6 hop-by-hop pot options but no profile set") \ + _(PASSED, "Pkts with POT in Policy") \ + _(FAILED, "Pkts with POT out of Policy") + +static char * ip6_hop_by_hop_ioam_pot_stats_strings[] = { +#define _(sym,string) string, + foreach_ip6_hop_by_hop_ioam_pot_stats +#undef _ +}; + +typedef enum { +#define _(sym,str) IP6_IOAM_POT_##sym, + foreach_ip6_hop_by_hop_ioam_pot_stats +#undef _ + IP6_IOAM_POT_N_STATS, +} ip6_ioam_pot_stats_t; + +typedef struct { + /* stats */ + u64 counters[ARRAY_LEN(ip6_hop_by_hop_ioam_pot_stats_strings)]; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; +} ip6_hop_by_hop_ioam_pot_main_t; + +ip6_hop_by_hop_ioam_pot_main_t ip6_hop_by_hop_ioam_pot_main; + +always_inline void +ip6_ioam_stats_increment_counter (u32 counter_index, u64 increment) +{ + ip6_hop_by_hop_ioam_pot_main_t *hm = &ip6_hop_by_hop_ioam_pot_main; + + hm->counters[counter_index] += increment; +} + + +static u8 * format_ioam_pot (u8 * s, va_list * args) +{ + ioam_pot_option_t * pot0 = va_arg (*args, ioam_pot_option_t *); + u64 random, cumulative; + random = cumulative = 0; + if (pot0) + { + random = clib_net_to_host_u64 (pot0->random); + cumulative = clib_net_to_host_u64 (pot0->cumulative); + } + + s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x", + random, cumulative, pot0->reserved_profile_id); + return s; +} + +u8 * +ip6_hbh_ioam_proof_of_transit_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt) +{ + ioam_pot_option_t *pot; + + s = format (s, " POT opt present\n"); + pot = (ioam_pot_option_t *) opt; + s = format (s, " %U\n", format_ioam_pot, pot); + return (s); +} + +int +ip6_hbh_ioam_proof_of_transit_handler (vlib_buffer_t *b, + ip6_header_t *ip, + ip6_hop_by_hop_option_t *opt0) +{ + ioam_pot_option_t * pot0; + u64 random = 0, cumulative = 0; + int rv = 0; + u8 pot_profile_index; + pot_profile *pot_profile = 0, *new_profile = 0; + u8 pot_encap = 0; + + pot0 = (ioam_pot_option_t *) opt0; + pot_encap = (pot0->random == 0); + pot_profile_index = pot_profile_get_active_id(); + pot_profile = pot_profile_get_active(); + if (pot_encap && PREDICT_FALSE(!pot_profile)) + { + ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROFILE_MISS, 1); + return(-1); + } + if (pot_encap) + { + pot0->reserved_profile_id = + pot_profile_index & PROFILE_ID_MASK; + pot_profile_incr_usage_stats(pot_profile); + } + else + { /* Non encap node */ + if (PREDICT_FALSE(pot0->reserved_profile_id != + pot_profile_index || pot_profile == 0)) + { + /* New profile announced by encap node. */ + new_profile = + pot_profile_find(pot0->reserved_profile_id); + if (PREDICT_FALSE(new_profile == 0 || + new_profile->valid == 0)) + { + ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROFILE_MISS, 1); + return(-1); + } + else + { + pot_profile_index = pot0->reserved_profile_id; + pot_profile = new_profile; + pot_profile_set_active(pot_profile_index); + pot_profile_reset_usage_stats(pot_profile); + } + } + pot_profile_incr_usage_stats(pot_profile); + } + + if (pot0->random == 0) + { + pot0->random = clib_host_to_net_u64(pot_generate_random(pot_profile)); + pot0->cumulative = 0; + } + random = clib_net_to_host_u64(pot0->random); + cumulative = clib_net_to_host_u64(pot0->cumulative); + pot0->cumulative = clib_host_to_net_u64( + pot_update_cumulative(pot_profile, + cumulative, + random)); + ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PROCESSED, 1); + + return (rv); +} + +int +ip6_hbh_ioam_proof_of_transit_pop_handler (ip6_header_t *ip, + ip6_hop_by_hop_option_t *opt0) +{ + ioam_pot_option_t * pot0; + u64 random = 0; + u64 cumulative = 0; + int rv = 0; + pot_profile *pot_profile = 0; + u8 result = 0; + + pot0 = (ioam_pot_option_t *) opt0; + random = clib_net_to_host_u64(pot0->random); + cumulative = clib_net_to_host_u64(pot0->cumulative); + pot_profile = pot_profile_get_active(); + result = pot_validate (pot_profile, + cumulative, random); + + if (result == 1) + { + ip6_ioam_stats_increment_counter (IP6_IOAM_POT_PASSED, 1); + } + else + { + ip6_ioam_stats_increment_counter (IP6_IOAM_POT_FAILED, 1); + } + return (rv); +} + +int ip6_hop_by_hop_ioam_pot_rewrite_handler (u8 *rewrite_string, u8 rewrite_size) +{ + ioam_pot_option_t * pot_option; + if (rewrite_string && rewrite_size == sizeof(ioam_pot_option_t)) + { + pot_option = (ioam_pot_option_t *)rewrite_string; + pot_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT + | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE; + pot_option->hdr.length = sizeof (ioam_pot_option_t) - + sizeof (ip6_hop_by_hop_option_t); + return(0); + } + return(-1); +} + +static clib_error_t * +ip6_show_ioam_pot_cmd_fn (vlib_main_t * vm, + unformat_input_t * input, + vlib_cli_command_t * cmd) +{ + ip6_hop_by_hop_ioam_pot_main_t *hm = &ip6_hop_by_hop_ioam_pot_main; + u8 *s = 0; + int i = 0; + + for ( i = 0; i < IP6_IOAM_POT_N_STATS; i++) + { + s = format(s, " %s - %lu\n", ip6_hop_by_hop_ioam_pot_stats_strings[i], + hm->counters[i]); + } + + vlib_cli_output(vm, "%v", s); + vec_free(s); + return 0; +} + + +VLIB_CLI_COMMAND (ip6_show_ioam_pot_cmd, static) = { + .path = "show ioam pot", + .short_help = "iOAM pot statistics", + .function = ip6_show_ioam_pot_cmd_fn, +}; + + +static clib_error_t * +ip6_hop_by_hop_ioam_pot_init (vlib_main_t * vm) +{ + ip6_hop_by_hop_ioam_pot_main_t * hm = &ip6_hop_by_hop_ioam_pot_main; + clib_error_t * error; + + if ((error = vlib_call_init_function (vm, ip_main_init))) + return(error); + + if ((error = vlib_call_init_function (vm, ip6_lookup_init))) + return error; + + if ((error = vlib_call_init_function (vm, ip6_hop_by_hop_ioam_init))) + return(error); + + hm->vlib_main = vm; + hm->vnet_main = vnet_get_main(); + memset(hm->counters, 0, sizeof(hm->counters)); + + if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, ip6_hbh_ioam_proof_of_transit_handler, + ip6_hbh_ioam_proof_of_transit_trace_handler) < 0) + return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT failed")); + + if (ip6_hbh_add_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, + sizeof(ioam_pot_option_t), + ip6_hop_by_hop_ioam_pot_rewrite_handler) < 0) + return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT for rewrite failed")); + + if (ip6_hbh_pop_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT, + ip6_hbh_ioam_proof_of_transit_pop_handler) < 0) + return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT POP failed")); + + return (0); +} + +VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_pot_init); + + diff --git a/vnet/vnet/lib-scv/math64.h b/plugins/plugins/ioam/lib-pot/math64.h similarity index 98% rename from vnet/vnet/lib-scv/math64.h rename to plugins/plugins/ioam/lib-pot/math64.h index 9ee6e438f90..4c608a37de4 100644 --- a/vnet/vnet/lib-scv/math64.h +++ b/plugins/plugins/ioam/lib-pot/math64.h @@ -8,7 +8,7 @@ * Hence this header to combine add/multiply followed by modulo of u64 integrers * always resulting in u64. * - * Copyright (c) 2015 Cisco and/or its affiliates. + * Copyright (c) 2016 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: diff --git a/plugins/plugins/ioam/lib-pot/pot.api b/plugins/plugins/ioam/lib-pot/pot.api new file mode 100644 index 00000000000..7fd06b512f2 --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot.api @@ -0,0 +1,97 @@ +/* Hey Emacs use -*- mode: C -*- */ +/* + * Copyright (c) 2016 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. + */ + + +/** \brief Proof of Transit(POT): Set POT profile + @param id - id of the profile + @param validator - True/False to indicate if this is verifier + @param secret_key - Verification key + @param secret_share - Share of the 1st polynomial + @param prime - Prime number used for modulo operation + @param max_bits - Max bits to be used for Random number generation + @param lpc - Lagrange basis polynomial + @param polynomial_public - pre-evaluated public polynomial + @param list_name_len - length of the name of this profile list + @param list_name - name of this profile list +*/ +define pot_profile_add { + u32 client_index; + u32 context; + u8 id; + u8 validator; + u64 secret_key; + u64 secret_share; + u64 prime; + u8 max_bits; + u64 lpc; + u64 polynomial_public; + u8 list_name_len; + u8 list_name[0]; +}; + +/** \brief Proof of Transit profile add / del response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define pot_profile_add_reply { + u32 context; + i32 retval; +}; + + +/** \brief Proof of Transit(POT): Activate POT profile in the list + @param id - id of the profile + @param list_name_len - length of the name of this profile list + @param list_name - name of this profile list +*/ +define pot_profile_activate { + u32 client_index; + u32 context; + u8 id; + u8 list_name_len; + u8 list_name[0]; +}; + +/** \brief Proof of Transit profile activate response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define pot_profile_activate_reply { + u32 context; + i32 retval; +}; + +/** \brief Delete POT Profile + @param client_index - opaque cookie to identify the sender + @param context - sender context, to match reply w/ request + @param list_name_len - length of the name of the profile list + @param list_name - name of profile list to delete +*/ +define pot_profile_del { + u32 client_index; + u32 context; + u8 list_name_len; + u8 list_name[0]; +}; + +/** \brief Proof of Transit profile add / del response + @param context - sender context, to match reply w/ request + @param retval - return value for request +*/ +define pot_profile_del_reply { + u32 context; + i32 retval; +}; diff --git a/plugins/plugins/ioam/lib-pot/pot_all_api_h.h b/plugins/plugins/ioam/lib-pot/pot_all_api_h.h new file mode 100644 index 00000000000..574b8979bf4 --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_all_api_h.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 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. + */ +/* Include the generated file, see BUILT_SOURCES in Makefile.am */ +#include diff --git a/plugins/plugins/ioam/lib-pot/pot_api.c b/plugins/plugins/ioam/lib-pot/pot_api.c new file mode 100644 index 00000000000..656626339a7 --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_api.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2016 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. + */ +/* + *------------------------------------------------------------------ + * pot_api.c - Proof of Transit related APIs to create + * and maintain profiles + *------------------------------------------------------------------ + */ + +#include +#include +#include + +#include +#include +#include + +/* define message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* define generated endian-swappers */ +#define vl_endianfun +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) vlib_cli_output (handle, __VA_ARGS__) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + +/* + * A handy macro to set up a message reply. + * Assumes that the following variables are available: + * mp - pointer to request message + * rmp - pointer to reply message type + * rv - return value + */ + +#define REPLY_MACRO(t) \ +do { \ + unix_shared_memory_queue_t * q = \ + vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = ntohs((t)+sm->msg_id_base); \ + rmp->context = mp->context; \ + rmp->retval = ntohl(rv); \ + \ + vl_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +#define REPLY_MACRO2(t, body) \ +do { \ + unix_shared_memory_queue_t * q; \ + rv = vl_msg_api_pd_handler (mp, rv); \ + q = vl_api_client_index_to_input_queue (mp->client_index); \ + if (!q) \ + return; \ + \ + rmp = vl_msg_api_alloc (sizeof (*rmp)); \ + rmp->_vl_msg_id = ntohs((t)); \ + rmp->context = mp->context; \ + rmp->retval = ntohl(rv); \ + do {body;} while (0); \ + vl_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +/* List of message types that this plugin understands */ + +#define foreach_pot_plugin_api_msg \ +_(POT_PROFILE_ADD, pot_profile_add) \ +_(POT_PROFILE_ACTIVATE, pot_profile_activate) \ +_(POT_PROFILE_DEL, pot_profile_del) \ + +static void vl_api_pot_profile_add_t_handler +(vl_api_pot_profile_add_t *mp) +{ + pot_main_t * sm = &pot_main; + int rv = 0; + vl_api_pot_profile_add_reply_t * rmp; + u8 id; + pot_profile *profile = NULL; + u8 *name = 0; + + if (mp->list_name_len) + name = format(0, "%s", mp->list_name); + + pot_profile_list_init(name); + id = mp->id; + profile = pot_profile_find(id); + if (profile) { + rv = pot_profile_create(profile, + clib_net_to_host_u64(mp->prime), + clib_net_to_host_u64(mp->polynomial_public), + clib_net_to_host_u64(mp->lpc), + clib_net_to_host_u64(mp->secret_share)); + if (rv != 0) + goto ERROROUT; + if (1 == mp->validator) + (void)pot_set_validator(profile, clib_net_to_host_u64(mp->secret_key)); + (void)pot_profile_set_bit_mask(profile, mp->max_bits); + } else { + rv = -3; + } + ERROROUT: + vec_free(name); + REPLY_MACRO(VL_API_POT_PROFILE_ADD_REPLY); +} + +static void vl_api_pot_profile_activate_t_handler +(vl_api_pot_profile_activate_t *mp) +{ + pot_main_t * sm = &pot_main; + int rv = 0; + vl_api_pot_profile_add_reply_t * rmp; + u8 id; + u8 *name = NULL; + + if (mp->list_name_len) + name = format(0, "%s", mp->list_name); + if (!pot_profile_list_is_enabled(name)) { + rv = -1; + } else { + id = mp->id; + rv = pot_profile_set_active(id); + } + + vec_free(name); + REPLY_MACRO(VL_API_POT_PROFILE_ACTIVATE_REPLY); +} + + +static void vl_api_pot_profile_del_t_handler +(vl_api_pot_profile_del_t *mp) +{ + pot_main_t * sm = &pot_main; + int rv = 0; + vl_api_pot_profile_del_reply_t * rmp; + + clear_pot_profiles(); + + REPLY_MACRO(VL_API_POT_PROFILE_DEL_REPLY); +} + + +/* + * This routine exists to convince the vlib plugin framework that + * we haven't accidentally copied a random .dll into the plugin directory. + * + * Also collects global variable pointers passed from the vpp engine + */ + +clib_error_t * +vlib_plugin_register (vlib_main_t * vm, vnet_plugin_handoff_t * h, + int from_early_init) +{ + pot_main_t * sm = &pot_main; + clib_error_t * error = 0; + + sm->vlib_main = vm; + sm->vnet_main = h->vnet_main; + return error; +} + +/* Set up the API message handling tables */ +static clib_error_t * +pot_plugin_api_hookup (vlib_main_t *vm) +{ + pot_main_t * sm = &pot_main; +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_pot_plugin_api_msg; +#undef _ + + return 0; +} + +static clib_error_t * pot_init (vlib_main_t * vm) +{ + pot_main_t * sm = &pot_main; + clib_error_t * error = 0; + u8 * name; + + bzero(sm, sizeof(pot_main)); + (void)pot_util_init(); + name = format (0, "pot_%08x%c", api_version, 0); + + /* Ask for a correctly-sized block of API message decode slots */ + sm->msg_id_base = vl_msg_api_get_msg_ids + ((char *) name, VL_MSG_FIRST_AVAILABLE); + + error = pot_plugin_api_hookup (vm); + + vec_free(name); + + return error; +} + +VLIB_INIT_FUNCTION (pot_init); diff --git a/plugins/plugins/ioam/lib-pot/pot_msg_enum.h b/plugins/plugins/ioam/lib-pot/pot_msg_enum.h new file mode 100644 index 00000000000..bcd7159d50f --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_msg_enum.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 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 included_pot_msg_enum_h +#define included_pot_msg_enum_h + +#include + +#define vl_msg_id(n,h) n, +typedef enum { +#include + /* We'll want to know how many messages IDs we need... */ + VL_MSG_FIRST_AVAILABLE, +} vl_msg_id_t; +#undef vl_msg_id + +#endif /* included_pot_msg_enum_h */ diff --git a/plugins/plugins/ioam/lib-pot/pot_test.c b/plugins/plugins/ioam/lib-pot/pot_test.c new file mode 100644 index 00000000000..706be44ff65 --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_test.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2016 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. + */ +/* + *------------------------------------------------------------------ + * pot_test.c - test harness for pot plugin + *------------------------------------------------------------------ + */ + +#include +#include +#include +#include +#include + +/* Declare message IDs */ +#include + +/* define message structures */ +#define vl_typedefs +#include +#undef vl_typedefs + +/* declare message handlers for each api */ + +#define vl_endianfun /* define message structures */ +#include +#undef vl_endianfun + +/* instantiate all the print functions we know about */ +#define vl_print(handle, ...) +#define vl_printfun +#include +#undef vl_printfun + +/* Get the API version number. */ +#define vl_api_version(n,v) static u32 api_version=(v); +#include +#undef vl_api_version + + +typedef struct { + /* API message ID base */ + u16 msg_id_base; + vat_main_t *vat_main; +} pot_test_main_t; + +pot_test_main_t pot_test_main; + +#define foreach_standard_reply_retval_handler \ +_(pot_profile_add_reply) \ +_(pot_profile_activate_reply) \ +_(pot_profile_del_reply) + +#define _(n) \ + static void vl_api_##n##_t_handler \ + (vl_api_##n##_t * mp) \ + { \ + vat_main_t * vam = pot_test_main.vat_main; \ + i32 retval = ntohl(mp->retval); \ + if (vam->async_mode) { \ + vam->async_errors += (retval < 0); \ + } else { \ + vam->retval = retval; \ + vam->result_ready = 1; \ + } \ + } +foreach_standard_reply_retval_handler; +#undef _ + +/* + * Table of message reply handlers, must include boilerplate handlers + * we just generated + */ +#define foreach_vpe_api_reply_msg \ +_(POT_PROFILE_ADD_REPLY, pot_profile_add_reply) \ +_(POT_PROFILE_ACTIVATE_REPLY, pot_profile_activate_reply) \ +_(POT_PROFILE_DEL_REPLY, pot_profile_del_reply) \ + + +/* M: construct, but don't yet send a message */ + +#define M(T,t) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc(sizeof(*mp)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +#define M2(T,t,n) \ +do { \ + vam->result_ready = 0; \ + mp = vl_msg_api_alloc(sizeof(*mp)+(n)); \ + memset (mp, 0, sizeof (*mp)); \ + mp->_vl_msg_id = ntohs (VL_API_##T + sm->msg_id_base); \ + mp->client_index = vam->my_client_index; \ +} while(0); + +/* S: send a message */ +#define S (vl_msg_api_send_shmem (vam->vl_input_queue, (u8 *)&mp)) + +/* W: wait for results, with timeout */ +#define W \ +do { \ + timeout = vat_time_now (vam) + 1.0; \ + \ + while (vat_time_now (vam) < timeout) { \ + if (vam->result_ready == 1) { \ + return (vam->retval); \ + } \ + } \ + return -99; \ +} while(0); + + +static int api_pot_profile_add (vat_main_t *vam) +{ +#define MAX_BITS 64 + pot_test_main_t * sm = &pot_test_main; + unformat_input_t *input = vam->input; + vl_api_pot_profile_add_t *mp; + u8 *name = NULL; + u64 prime = 0; + u64 secret_share = 0; + u64 secret_key = 0; + u32 bits = MAX_BITS; + u64 lpc = 0, poly2 = 0; + f64 timeout; + u8 id = 0; + int rv = 0; + + while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) + { + if (unformat(input, "name %s", &name)) + ; + else if(unformat(input, "id %d", &id)) + ; + else if (unformat(input, "validator-key 0x%Lx", &secret_key)) + ; + else if (unformat(input, "prime-number 0x%Lx", &prime)) + ; + else if (unformat(input, "secret-share 0x%Lx", &secret_share)) + ; + else if (unformat(input, "polynomial-public 0x%Lx", &poly2)) + ; + else if (unformat(input, "lpc 0x%Lx", &lpc)) + ; + else if (unformat(input, "bits-in-random %u", &bits)) + { + if (bits > MAX_BITS) + bits = MAX_BITS; + } + else + break; + } + + if (!name) + { + errmsg ("name required\n"); + rv = -99; + goto OUT; + } + + M2(POT_PROFILE_ADD, pot_profile_add, vec_len(name)); + + mp->list_name_len = vec_len(name); + clib_memcpy(mp->list_name, name, mp->list_name_len); + mp->secret_share = clib_host_to_net_u64(secret_share); + mp->polynomial_public = clib_host_to_net_u64(poly2); + mp->lpc = clib_host_to_net_u64(lpc); + mp->prime = clib_host_to_net_u64(prime); + if (secret_key != 0) + { + mp->secret_key = clib_host_to_net_u64(secret_key); + mp->validator = 1; + } + else + { + mp->validator = 0; + } + mp->id = id; + mp->max_bits = bits; + + S; W; + +OUT: + vec_free(name); + return(rv); +} + +static int api_pot_profile_activate (vat_main_t *vam) +{ +#define MAX_BITS 64 + pot_test_main_t * sm = &pot_test_main; + unformat_input_t *input = vam->input; + vl_api_pot_profile_activate_t *mp; + u8 *name = NULL; + u8 id = 0; + int rv = 0; + f64 timeout; + + while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) + { + if (unformat(input, "name %s", &name)) + ; + else if(unformat(input, "id %d", &id)) + ; + else + break; + } + + if (!name) + { + errmsg ("name required\n"); + rv = -99; + goto OUT; + } + + M2(POT_PROFILE_ACTIVATE, pot_profile_activate, vec_len(name)); + + mp->list_name_len = vec_len(name); + clib_memcpy(mp->list_name, name, mp->list_name_len); + mp->id = id; + + S; W; + +OUT: + vec_free(name); + return(rv); +} + + +static int api_pot_profile_del (vat_main_t *vam) +{ + pot_test_main_t * sm = &pot_test_main; + vl_api_pot_profile_del_t *mp; + f64 timeout; + + M(POT_PROFILE_DEL, pot_profile_del); + mp->list_name_len = 0; + S; W; + return 0; +} + +/* + * List of messages that the api test plugin sends, + * and that the data plane plugin processes + */ +#define foreach_vpe_api_msg \ +_(pot_profile_add, "name id [0-1] " \ + "prime-number <0xu64> bits-in-random [0-64] " \ + "secret-share <0xu64> lpc <0xu64> polynomial-public <0xu64> " \ + "[validator-key <0xu64>] [validity <0xu64>]") \ +_(pot_profile_activate, "name id [0-1] ") \ +_(pot_profile_del, "[id ]") \ + + +void vat_api_hookup (vat_main_t *vam) +{ + pot_test_main_t * sm = &pot_test_main; + /* Hook up handlers for replies from the data plane plug-in */ +#define _(N,n) \ + vl_msg_api_set_handlers((VL_API_##N + sm->msg_id_base), \ + #n, \ + vl_api_##n##_t_handler, \ + vl_noop_handler, \ + vl_api_##n##_t_endian, \ + vl_api_##n##_t_print, \ + sizeof(vl_api_##n##_t), 1); + foreach_vpe_api_reply_msg; +#undef _ + + /* API messages we can send */ +#define _(n,h) hash_set_mem (vam->function_by_name, #n, api_##n); + foreach_vpe_api_msg; +#undef _ + + /* Help strings */ +#define _(n,h) hash_set_mem (vam->help_by_name, #n, h); + foreach_vpe_api_msg; +#undef _ +} + +clib_error_t * vat_plugin_register (vat_main_t *vam) +{ + pot_test_main_t * sm = &pot_test_main; + u8 * name; + + sm->vat_main = vam; + + name = format (0, "pot_%08x%c", api_version, 0); + sm->msg_id_base = vl_client_get_first_plugin_msg_id ((char *) name); + + if (sm->msg_id_base != (u16) ~0) + vat_api_hookup (vam); + + vec_free(name); + + return 0; +} diff --git a/plugins/plugins/ioam/lib-pot/pot_util.c b/plugins/plugins/ioam/lib-pot/pot_util.c new file mode 100644 index 00000000000..0309cbecf4b --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_util.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2016 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. + */ +#include +#include +#include +#include +#include +#include "math64.h" +#include "pot_util.h" + +pot_main_t pot_main; + +static void pot_profile_cleanup(pot_profile *profile); + +static void pot_main_profiles_reset (void) +{ + pot_main_t *sm = &pot_main; + int i = 0; + + for (i = 0; i < MAX_POT_PROFILES; i++) + { + pot_profile_cleanup(&(sm->profile_list[i])); + } + sm->active_profile_id = 0; + if (sm->profile_list_name) + vec_free(sm->profile_list_name); + sm->profile_list_name = NULL; +} + +int pot_util_init (void) +{ + pot_main_profiles_reset(); + + return(0); +} + +static void pot_profile_init(pot_profile * new, u8 id) +{ + if (new) + { + memset(new, 0, sizeof(pot_profile)); + new->id = id; + } +} + +pot_profile *pot_profile_find(u8 id) +{ + pot_main_t *sm = &pot_main; + + if (id >= 0 && id < MAX_POT_PROFILES) + { + return (&(sm->profile_list[id])); + } + return (NULL); +} +static int pot_profile_name_equal (u8 *name0, u8 *name1) +{ + int len0, len1; + + len0 = vec_len (name0); + len1 = vec_len (name1); + if (len0 != len1) + return(0); + return (0==strncmp ((char *) name0, (char *)name1, len0)); +} + +int pot_profile_list_is_enabled (u8 *name) +{ + pot_main_t *sm = &pot_main; + return (pot_profile_name_equal(sm->profile_list_name, name)); +} + +void pot_profile_list_init(u8 * profile_list_name) +{ + pot_main_t *sm = &pot_main; + int i = 0; + + /* If it is the same profile list skip reset */ + if (pot_profile_name_equal(sm->profile_list_name, profile_list_name)) + { + return; + } + + pot_main_profiles_reset(); + if (vec_len(profile_list_name)) + sm->profile_list_name = (u8 *)vec_dup(profile_list_name); + else + sm->profile_list_name = 0; + sm->active_profile_id = 0; + + for (i = 0; i < MAX_POT_PROFILES; i++) + { + pot_profile_init(&(sm->profile_list[i]), i); + } +} + +static void pot_profile_cleanup(pot_profile * profile) +{ + u16 id = profile->id; + + memset(profile, 0, sizeof(pot_profile)); + profile->id = id; /* Restore id alone */ +} + +int pot_profile_create(pot_profile * profile, u64 prime, + u64 poly2, u64 lpc, u64 secret_share) +{ + if (profile && !profile->in_use) + { + pot_profile_cleanup(profile); + profile->prime = prime; + profile->primeinv = 1.0 / prime; + profile->lpc = lpc; + profile->poly_pre_eval = poly2; + profile->secret_share = secret_share; + profile->total_pkts_using_this_profile = 0; + profile->valid = 1; + return(0); + } + + return(-1); +} + +int pot_set_validator(pot_profile * profile, u64 key) +{ + if (profile && !profile->in_use) + { + profile->validator = 1; + profile->secret_key = key; + return(0); + } + return(-1); +} + +always_inline u64 pot_update_cumulative_inline(u64 cumulative, u64 random, + u64 secret_share, u64 prime, u64 lpc, u64 pre_split, double prime_inv) +{ + u64 share_random = 0; + u64 cumulative_new = 0; + + /* + * calculate split share for random + */ + share_random = add64_mod(pre_split, random, prime, prime_inv); + + /* + * lpc * (share_secret + share_random) + */ + share_random = add64_mod(share_random, secret_share, prime, prime_inv); + share_random = mul64_mod(share_random, lpc, prime, prime_inv); + + cumulative_new = add64_mod(cumulative, share_random, prime, prime_inv); + + return (cumulative_new); +} + +u64 pot_update_cumulative(pot_profile * profile, u64 cumulative, u64 random) +{ + if (profile && profile->valid != 0) + { + return (pot_update_cumulative_inline(cumulative, random, profile->secret_share, + profile->prime, profile->lpc, profile->poly_pre_eval, + profile->primeinv)); + } + return (0); +} + +always_inline u8 pot_validate_inline(u64 secret, u64 prime, double prime_inv, + u64 cumulative, u64 random) +{ + if (cumulative == (random + secret)) + { + return (1); + } + else if (cumulative == add64_mod(random, secret, prime, prime_inv)) + { + return (1); + } + return (0); +} + +/* + * return True if the cumulative matches secret from a profile + */ +u8 pot_validate(pot_profile * profile, u64 cumulative, u64 random) +{ + if (profile && profile->validator) + { + return (pot_validate_inline(profile->secret_key, profile->prime, + profile->primeinv, cumulative, random)); + } + return (0); +} + +/* + * Utility function to get random number per pack + */ +u64 pot_generate_random(pot_profile * profile) +{ + u64 random = 0; + int32_t second_half; + static u32 seed = 0; + + if (PREDICT_FALSE(!seed)) + seed = random_default_seed(); + + /* + * Upper 4 bytes seconds + */ + random = (u64) time(NULL); + + random &= 0xffffffff; + random = random << 32; + /* + * Lower 4 bytes random number + */ + second_half = random_u32(&seed); + + random |= second_half; + + if (PREDICT_TRUE(profile != NULL)) + { + random &= profile->bit_mask; + } + return (random); +} + +int pot_profile_set_bit_mask(pot_profile * profile, u16 bits) +{ + int sizeInBits; + + if (profile && !profile->in_use) + { + sizeInBits = sizeof(profile->bit_mask) * 8; + profile->bit_mask = + (bits >= + sizeInBits ? (u64) - 1 : (u64) ((u64) 1 << (u64) bits) - 1); + return(0); + } + return(-1); +} + +clib_error_t *clear_pot_profile_command_fn(vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + + pot_main_profiles_reset(); + + return 0; +} + +void clear_pot_profiles() +{ + clear_pot_profile_command_fn(0, 0, 0); +} + +VLIB_CLI_COMMAND(clear_pot_profile_command) = +{ +.path = "clear pot profile", +.short_help = "clear pot profile [|all]", +.function = clear_pot_profile_command_fn, +}; + +static clib_error_t *set_pot_profile_command_fn(vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + u64 prime; + u64 secret_share; + u64 secret_key; + u8 validator = 0; + u32 profile_id; + u32 bits; + u64 lpc = 0, poly2 = 0; + pot_profile *profile = NULL; + u8 *profile_list_name = NULL; + + bits = MAX_BITS; + + while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) + { + if (unformat(input, "name %s", + &profile_list_name)); + else if (unformat(input, "id %d", &profile_id)) + ; + else if (unformat(input, "validate-key 0x%Lx", &secret_key)) + validator = 1; + else if (unformat(input, "prime-number 0x%Lx", &prime)) + ; + else if (unformat(input, "secret_share 0x%Lx", &secret_share)) + ; + else if (unformat(input, "polynomial2 0x%Lx", &poly2)) + ; + else if (unformat(input, "lpc 0x%Lx", &lpc)) + ; + else if (unformat(input, "bits-in-random %d", &bits)) + { + if (bits > MAX_BITS) + bits = MAX_BITS; + } + else + return clib_error_return(0, "unknown input `%U'", + format_unformat_error, input); + } + if (profile_list_name == 0) + { + return clib_error_return(0, "Name cannot be null"); + } + pot_profile_list_init(profile_list_name); + profile = pot_profile_find(profile_id); + + if (profile) + { + pot_profile_create(profile, prime, poly2, lpc, secret_share); + if (validator) + pot_set_validator(profile, secret_key); + pot_profile_set_bit_mask(profile, bits); + } + vec_free(profile_list_name); + return 0; +} + +VLIB_CLI_COMMAND(set_pot_profile_command) = +{ +.path = "set pot profile", +.short_help = "set pot profile name id [0-1] [validator-key 0xu64] \ + prime-number 0xu64 secret_share 0xu64 lpc 0xu64 \ + polynomial2 0xu64 bits-in-random [0-64] ", +.function = set_pot_profile_command_fn, +}; + +static clib_error_t *set_pot_profile_activate_command_fn(vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + pot_main_t *sm = &pot_main; + u8 *profile_list_name = NULL; + u32 id = 0; + clib_error_t *result = NULL; + + while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) + { + if (unformat(input, "name %s", + &profile_list_name)); + else if (unformat(input, "id %d", &id)) + ; + else + return clib_error_return(0, "unknown input `%U'", + format_unformat_error, input); + } + if (profile_list_name == 0) + { + return clib_error_return(0, "Name cannot be null"); + } + + if (!pot_profile_list_is_enabled(profile_list_name)) { + result = clib_error_return(0, "%s list is not enabled, profile in use %s", + profile_list_name, sm->profile_list_name); + } else if (0 != pot_profile_set_active((u8)id)) { + result = clib_error_return(0, "Profile %d not defined in %s", + id, sm->profile_list_name); + } + vec_free(profile_list_name); + return result; +} + +VLIB_CLI_COMMAND(set_pot_profile_activate_command) = +{ +.path = "set pot profile-active", +.short_help = "set pot profile-active name id [0-1]", +.function = set_pot_profile_activate_command_fn, +}; + +static clib_error_t *show_pot_profile_command_fn(vlib_main_t * vm, + unformat_input_t * input, vlib_cli_command_t * cmd) +{ + pot_main_t *sm = &pot_main; + pot_profile *p = NULL; + u16 i; + u8 *s = 0; + + if (vec_len(sm->profile_list_name) == 0) + { + s = format(s, "POT Profiles not configured\n"); + vlib_cli_output(vm, "%v", s); + return 0; + } + s = format(s, "Profile list in use : %s\n",sm->profile_list_name); + for (i = 0; i < MAX_POT_PROFILES; i++) + { + p = pot_profile_find(i); + if (p->valid == 0) + continue; + s = format(s, "POT Profile at index: %d\n", i); + s = format(s, " Id : %d\n", p->id); + s = format(s, " Validator : %s (%d)\n", + (p->validator) ? "True" : "False", p->validator); + if (p->validator == 1) + s = format(s, " Secret key : 0x%Lx (%Ld)\n", + p->secret_key, p->secret_key); + s = format(s, " Secret share : 0x%Lx (%Ld)\n", + p->secret_share, p->secret_share); + s = format(s, " Prime number : 0x%Lx (%Ld)\n", + p->prime, p->prime); + s = format(s, "2nd polynomial(eval) : 0x%Lx (%Ld)\n", + p->poly_pre_eval, p->poly_pre_eval); + s = format(s, " LPC : 0x%Lx (%Ld)\n", p->lpc, p->lpc); + + s = format(s, " Bit mask : 0x%Lx (%Ld)\n", + p->bit_mask, p->bit_mask); + } + + p = pot_profile_find(sm->active_profile_id); + + if (p && p->valid && p->in_use) { + s = format(s, "\nProfile index in use: %d\n", sm->active_profile_id); + s = format(s, "Pkts passed : 0x%Lx (%Ld)\n", + p->total_pkts_using_this_profile, + p->total_pkts_using_this_profile); + if (pot_is_decap(p)) + s = format(s, " This is Decap node. \n"); + } else { + s = format(s, "\nProfile index in use: None\n"); + } + vlib_cli_output(vm, "%v", s); + vec_free(s); + + return 0; +} + +VLIB_CLI_COMMAND(show_pot_profile_command) = +{ +.path = "show pot profile", +.short_help = "show pot profile", +.function = show_pot_profile_command_fn, +}; diff --git a/plugins/plugins/ioam/lib-pot/pot_util.h b/plugins/plugins/ioam/lib-pot/pot_util.h new file mode 100644 index 00000000000..9df31fae0df --- /dev/null +++ b/plugins/plugins/ioam/lib-pot/pot_util.h @@ -0,0 +1,195 @@ +/* + * pot_util.h -- Proof Of Transit Utility Header + * + * Copyright (c) 2016 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_pot_util_h +#define include_vnet_pot_util_h + +#include +#define debug_ioam debug_ioam_fn +/* 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_POT_PROFILES 2 + +/** + * Usage: + * + * On any node that participates in Proof of Transit: + * + * Step 1: Initialize this library by calling pot_init() + * Step 2: Setup a proof of transit profile that contains all the parameters needed to compute cumulative: + * Call these functions: + * pot_profile_find + * pot_profile_create + * pot_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: + * pot_set_validator + * Step 2b: On initial node enable the profile to be used: + * pot_profile_set_active / pot_profile_get_active will return the profile + * Step 3a: At the initial node to generate Random number that will be read by all other nodes: + * pot_generate_random + * Step 3b: At all nodes including initial and verifier call this to compute cumulative: + * pot_update_cumulative + * Step 4: At the verifier: + * pot_validate + * + */ + +typedef struct pot_profile_ +{ + u8 id : 1; + u8 valid : 1; + u8 in_use : 1; + u64 random; + u8 validator; + u64 secret_key; + u64 secret_share; + u64 prime; + u64 lpc; + u64 poly_pre_eval; + u64 bit_mask; + u64 limit; + double primeinv; + u64 total_pkts_using_this_profile; +} pot_profile; + +typedef struct { + /* Name of the default profile list in use*/ + u8 *profile_list_name; + pot_profile profile_list[MAX_POT_PROFILES]; + /* number of profiles in the list */ + u8 active_profile_id : 1; + + /* API message ID base */ + u16 msg_id_base; + + /* convenience */ + vlib_main_t * vlib_main; + vnet_main_t * vnet_main; +} pot_main_t; + +extern pot_main_t pot_main; + +/* + * Initialize proof of transit + */ +int pot_util_init(void); +void pot_profile_list_init(u8 * name); + + +/* + * Find a pot profile by ID + */ +pot_profile *pot_profile_find(u8 id); + +static inline u16 pot_profile_get_id(pot_profile * profile) +{ + if (profile) + { + return (profile->id); + } + return (0); +} + +/* setup and clean up profile */ +int pot_profile_create(pot_profile * profile, u64 prime, + u64 poly2, u64 lpc, u64 secret_share); +/* + * Setup profile as a validator + */ +int pot_set_validator(pot_profile * profile, u64 key); + +/* + * Setup max bits to be used for random number generation + */ +#define MAX_BITS 64 +int pot_profile_set_bit_mask(pot_profile * profile, u16 bits); + +/* + * Given a random and cumulative compute the new cumulative for a given profile + */ +u64 pot_update_cumulative(pot_profile * profile, u64 cumulative, u64 random); + +/* + * return True if the cumulative matches secret from a profile + */ +u8 pot_validate(pot_profile * profile, u64 cumulative, u64 random); + +/* + * Utility function to get random number per pack + */ +u64 pot_generate_random(pot_profile * profile); + + +extern void clear_pot_profiles(); +extern int pot_profile_list_is_enabled(u8 *name); + +static inline u8 pot_is_decap(pot_profile * p) +{ + return (p->validator == 1); +} + +static inline int pot_profile_set_active (u8 id) +{ + pot_main_t *sm = &pot_main; + pot_profile *profile = NULL; + pot_profile *current_active_prof = NULL; + + current_active_prof = pot_profile_find(sm->active_profile_id); + profile = pot_profile_find(id); + if (profile && profile->valid) { + sm->active_profile_id = id; + current_active_prof->in_use = 0; + profile->in_use = 1; + return(0); + } + return(-1); +} +static inline u8 pot_profile_get_active_id (void) +{ + pot_main_t *sm = &pot_main; + return (sm->active_profile_id); +} + +static inline pot_profile * pot_profile_get_active (void) +{ + pot_main_t *sm = &pot_main; + pot_profile *profile = NULL; + profile = pot_profile_find(sm->active_profile_id); + if (profile && profile->in_use) + return(profile); + return (NULL); +} + +static inline void pot_profile_reset_usage_stats (pot_profile *pow) +{ + if (pow) { + pow->total_pkts_using_this_profile = 0; + } +} + +static inline void pot_profile_incr_usage_stats (pot_profile *pow) +{ + if (pow) { + pow->total_pkts_using_this_profile++; + } +} + + +#endif diff --git a/vnet/Makefile.am b/vnet/Makefile.am index b758c7f1cc9..61a1998a6eb 100644 --- a/vnet/Makefile.am +++ b/vnet/Makefile.am @@ -717,16 +717,6 @@ libvnetplugin_la_SOURCES += \ nobase_include_HEADERS += \ vnet/plugin/plugin.h -######################################## -# Service Chain verification util -######################################## -libvnet_la_SOURCES += \ - vnet/lib-scv/scv_util.c - -nobase_include_HEADERS += \ - vnet/lib-scv/scv_util.h \ - vnet/lib-scv/math64.h - lib_LTLIBRARIES = libvnet.la libvnetplugin.la dpdk_libs = diff --git a/vnet/vnet/ip/ip6_hop_by_hop.c b/vnet/vnet/ip/ip6_hop_by_hop.c index df0dae5786a..f6e10f08d97 100644 --- a/vnet/vnet/ip/ip6_hop_by_hop.c +++ b/vnet/vnet/ip/ip6_hop_by_hop.c @@ -25,8 +25,6 @@ #include -#include - /* Timestamp precision multipliers for seconds, milliseconds, microseconds * and nanoseconds respectively. */ @@ -115,22 +113,6 @@ static u8 * format_ioam_data_list_element (u8 * s, va_list * args) return s; } -static u8 * format_ioam_pow (u8 * s, va_list * args) -{ - ioam_pow_option_t * pow0 = va_arg (*args, ioam_pow_option_t *); - u64 random, cumulative; - random = cumulative = 0; - if (pow0) - { - random = clib_net_to_host_u64 (pow0->random); - cumulative = clib_net_to_host_u64 (pow0->cumulative); - } - - s = format (s, "random = 0x%Lx, Cumulative = 0x%Lx, Index = 0x%x", - random, cumulative, pow0->reserved_profile_id); - return s; -} - u8 * ip6_hbh_ioam_trace_data_list_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt) { @@ -158,17 +140,6 @@ ip6_hbh_ioam_trace_data_list_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt) return (s); } -u8 * -ip6_hbh_ioam_proof_of_work_trace_handler (u8 *s, ip6_hop_by_hop_option_t *opt) -{ - ioam_pow_option_t *pow; - - s = format (s, " POW opt present\n"); - pow = (ioam_pow_option_t *) opt; - s = format (s, " %U\n", format_ioam_pow, pow); - return (s); -} - int ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt) { @@ -221,76 +192,42 @@ ip6_hbh_ioam_trace_data_list_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_ho return (rv); } +/* The main h-b-h tracer will be invoked, no need to do much here */ int -ip6_hbh_ioam_proof_of_work_handler (vlib_buffer_t *b, ip6_header_t *ip, ip6_hop_by_hop_option_t *opt) +ip6_hbh_add_register_option (u8 option, + u8 size, + int rewrite_options(u8 *rewrite_string, u8 rewrite_size)) { ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; - ioam_pow_option_t * pow; - u64 random = 0, cumulative = 0; - int rv = 0; - pow_profile = scv_profile_find(pow_profile_index); - if (PREDICT_FALSE(!pow_profile)) { + ASSERT (option < ARRAY_LEN (hm->add_options)); + + /* Already registered */ + if (hm->add_options[option]) return (-1); - } - pow = (ioam_pow_option_t *) opt; - - u8 pow_encap = (pow->random == 0); - if (pow_encap) { - if (PREDICT_FALSE(total_pkts_using_this_profile >= pow_profile->validity)) { - /* Choose a new profile */ - u16 new_profile_index; - new_profile_index = scv_get_next_profile_id(hm->vlib_main, pow_profile_index); - if (new_profile_index != pow_profile_index) { - /* Got a new profile */ - scv_profile_invalidate(hm->vlib_main, hm, - pow_profile_index, - pow_encap); - pow_profile_index = new_profile_index; - pow_profile = scv_profile_find(pow_profile_index); - total_pkts_using_this_profile = 0; - } else { - scv_profile_invalidate(hm->vlib_main, hm, pow_profile_index, pow_encap); - } - } - pow->reserved_profile_id = pow_profile_index & PROFILE_ID_MASK; - total_pkts_using_this_profile++; - } else { /* Non encap node */ - if (PREDICT_FALSE(pow->reserved_profile_id != pow_profile_index)) { - /* New profile announced by encap node. */ - scv_profile *new_profile = 0; - new_profile = scv_profile_find(pow->reserved_profile_id); - if (PREDICT_FALSE(new_profile == 0 || new_profile->validity == 0)) { - /* Profile is invalid. Use old profile*/ - rv = -1; - scv_profile_invalidate(hm->vlib_main, hm, - pow->reserved_profile_id, - pow_encap); - } else { - scv_profile_invalidate(hm->vlib_main, hm, - pow_profile_index, - pow_encap); - pow_profile_index = pow->reserved_profile_id; - pow_profile = new_profile; - total_pkts_using_this_profile = 0; - } - } - total_pkts_using_this_profile++; - } + hm->add_options[option] = rewrite_options; + hm->options_size[option] = size; + + return (0); +} - if (pow->random == 0) { - pow->random = clib_host_to_net_u64(scv_generate_random(pow_profile)); - pow->cumulative = 0; - } - random = clib_net_to_host_u64(pow->random); - cumulative = clib_net_to_host_u64(pow->cumulative); - pow->cumulative = clib_host_to_net_u64(scv_update_cumulative(pow_profile, cumulative, random)); +int +ip6_hbh_add_unregister_option (u8 option) +{ + ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; - return (rv); + ASSERT (option < ARRAY_LEN (hm->add_options)); + + /* Not registered */ + if (!hm->add_options[option]) + return (-1); + + hm->add_options[option] = NULL; + hm->options_size[option] = 0; + return (0); } -/* The main h-b-h tracer will be invoked, no need to do much here */ typedef struct { u32 next_index; } ip6_add_hop_by_hop_trace_t; @@ -530,13 +467,44 @@ static u8 * format_ip6_pop_hop_by_hop_trace (u8 * s, va_list * args) return s; } +int +ip6_hbh_pop_register_option (u8 option, + int options(ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)) +{ + ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; + + ASSERT (option < ARRAY_LEN (hm->pop_options)); + + /* Already registered */ + if (hm->pop_options[option]) + return (-1); + + hm->pop_options[option] = options; + + return (0); +} + +int +ip6_hbh_pop_unregister_option (u8 option) +{ + ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; + + ASSERT (option < ARRAY_LEN (hm->pop_options)); + + /* Not registered */ + if (!hm->pop_options[option]) + return (-1); + + hm->pop_options[option] = NULL; + return (0); +} + vlib_node_registration_t ip6_pop_hop_by_hop_node; #define foreach_ip6_pop_hop_by_hop_error \ _(PROCESSED, "Pkts w/ removed ip6 hop-by-hop options") \ -_(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \ -_(SCV_PASSED, "Pkts with SCV in Policy") \ -_(SCV_FAILED, "Pkts with SCV out of Policy") +_(NO_HOHO, "Pkts w/ no ip6 hop-by-hop options") \ +_(OPTION_FAILED, "ip6 pop hop-by-hop failed to process") typedef enum { #define _(sym,str) IP6_POP_HOP_BY_HOP_ERROR_##sym, @@ -551,16 +519,13 @@ static char * ip6_pop_hop_by_hop_error_strings[] = { #undef _ }; -static inline void ioam_end_of_path_validation (vlib_main_t * vm, +static inline void ioam_pop_hop_by_hop_processing (vlib_main_t * vm, ip6_header_t *ip0, ip6_hop_by_hop_header_t *hbh0) { + ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; ip6_hop_by_hop_option_t *opt0, *limit0; - ioam_pow_option_t * pow0; u8 type0; - u64 final_cumulative = 0; - u64 random = 0; - u8 result = 0; if (!hbh0 || !ip0) return; @@ -573,64 +538,37 @@ static inline void ioam_end_of_path_validation (vlib_main_t * vm, { type0 = opt0->type; switch (type0) - { - case HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE: - case HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST: - opt0 = (ip6_hop_by_hop_option_t *) - (((u8 *)opt0) + opt0->length - + sizeof (ip6_hop_by_hop_option_t)); - break; - case HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK: - pow0 = (ioam_pow_option_t *) opt0; - random = clib_net_to_host_u64(pow0->random); - final_cumulative = clib_net_to_host_u64(pow0->cumulative); - result = scv_validate (pow_profile, - final_cumulative, random); - - if (result == 1) - { - vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, - IP6_POP_HOP_BY_HOP_ERROR_SCV_PASSED, result); - } - else - { - vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, - IP6_POP_HOP_BY_HOP_ERROR_SCV_FAILED, 1); - } - /* TODO: notify the scv failure*/ - opt0 = (ip6_hop_by_hop_option_t *) - (((u8 *)opt0) + sizeof (ioam_pow_option_t)); - break; - - case 0: /* Pad */ - opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1; - break; - - default: - format(0, "Something is wrong\n"); - break; - } + { + case 0: /* Pad1 */ + opt0 = (ip6_hop_by_hop_option_t *) ((u8 *)opt0) + 1; + continue; + case 1: /* PadN */ + break; + default: + if (hm->pop_options[type0]) + { + if ((*hm->pop_options[type0])(ip0, opt0) < 0) + { + vlib_node_increment_counter (vm, ip6_pop_hop_by_hop_node.index, + IP6_POP_HOP_BY_HOP_ERROR_OPTION_FAILED, 1); + } + } + } + opt0 = (ip6_hop_by_hop_option_t *) (((u8 *)opt0) + opt0->length + sizeof (ip6_hop_by_hop_option_t)); } } - static uword ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm, vlib_node_runtime_t * node, vlib_frame_t * frame) { - ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; ip6_main_t * im = &ip6_main; ip_lookup_main_t * lm = &im->lookup_main; u32 n_left_from, * from, * to_next; ip_lookup_next_t next_index; u32 processed = 0; u32 no_header = 0; - u32 (*ioam_end_of_path_cb) (vlib_main_t *, vlib_node_runtime_t *, - vlib_buffer_t *, ip6_header_t *, - ip_adjacency_t *); - - ioam_end_of_path_cb = hm->ioam_end_of_path_cb; from = vlib_frame_vector_args (frame); n_left_from = frame->n_vectors; @@ -748,11 +686,8 @@ ip6_pop_hop_by_hop_node_fn (vlib_main_t * vm, /* Perfectly normal to end up here w/ out h-b-h header */ hbh0 = (ip6_hop_by_hop_header_t *)(ip0+1); - /* Collect data from trace via callback */ - next0 = ioam_end_of_path_cb ? ioam_end_of_path_cb (vm, node, b0, ip0, adj0) : next0; - /* TODO:Temporarily doing it here.. do this validation in end_of_path_cb */ - ioam_end_of_path_validation(vm, ip0, hbh0); + ioam_pop_hop_by_hop_processing(vm, ip0, hbh0); /* Pop the trace data */ vlib_buffer_advance (b0, (hbh0->length+1)<<3); new_l0 = clib_net_to_host_u16 (ip0->payload_length) - @@ -820,6 +755,9 @@ ip6_hop_by_hop_ioam_init (vlib_main_t * vm) hm->vlib_time_0 = vlib_time_now (vm); hm->ioam_flag = IOAM_HBYH_MOD; hm->trace_tsp = TSP_MICROSECONDS; /* Micro seconds */ + memset(hm->add_options, 0, sizeof(hm->add_options)); + memset(hm->pop_options, 0, sizeof(hm->pop_options)); + memset(hm->options_size, 0, sizeof(hm->options_size)); /* * Register the handlers @@ -828,9 +766,6 @@ ip6_hop_by_hop_ioam_init (vlib_main_t * vm) if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST, ip6_hbh_ioam_trace_data_list_handler, ip6_hbh_ioam_trace_data_list_trace_handler) < 0) return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST failed")); - if (ip6_hbh_register_option(HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK, ip6_hbh_ioam_proof_of_work_handler, - ip6_hbh_ioam_proof_of_work_trace_handler) < 0) - return (clib_error_create("registration of HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK failed")); return (0); } @@ -838,19 +773,19 @@ ip6_hop_by_hop_ioam_init (vlib_main_t * vm) VLIB_INIT_FUNCTION (ip6_hop_by_hop_ioam_init); int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, - int has_pow_option, int has_ppc_option) + int has_pot_option, int has_ppc_option) { + ip6_hop_by_hop_ioam_main_t * hm = &ip6_hop_by_hop_ioam_main; u8 *rewrite = 0; u32 size, rnd_size; ip6_hop_by_hop_header_t *hbh; ioam_trace_option_t * trace_option; - ioam_pow_option_t * pow_option; u8 *current; u8 trace_data_size = 0; vec_free (*rwp); - if (trace_option_elts == 0 && has_pow_option == 0) + if (trace_option_elts == 0 && has_pot_option == 0) return -1; /* Work out how much space we need */ @@ -869,10 +804,10 @@ int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, size += trace_option_elts * trace_data_size; } - if (has_pow_option) + if (has_pot_option && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0) { size += sizeof (ip6_hop_by_hop_option_t); - size += sizeof (ioam_pow_option_t); + size += hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]; } /* Round to a multiple of 8 octets */ @@ -899,14 +834,11 @@ int ip6_ioam_set_rewrite (u8 **rwp, u32 trace_type, u32 trace_option_elts, current += sizeof (ioam_trace_option_t) + trace_option_elts * trace_data_size; } - if (has_pow_option) + if (has_pot_option && hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT] != 0) { - pow_option = (ioam_pow_option_t *)current; - pow_option->hdr.type = HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK - | HBH_OPTION_TYPE_DATA_CHANGE_ENROUTE; - pow_option->hdr.length = sizeof (ioam_pow_option_t) - - sizeof (ip6_hop_by_hop_option_t); - current += sizeof (ioam_pow_option_t); + if (0 == hm->add_options[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT](current, + hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT])) + current += sizeof (hm->options_size[HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT]); } *rwp = rewrite; @@ -924,7 +856,7 @@ clear_ioam_rewrite_fn(void) hm->app_data = 0; hm->trace_type = 0; hm->trace_option_elts = 0; - hm->has_pow_option = 0; + hm->has_pot_option = 0; hm->has_ppc_option = 0; hm->trace_tsp = TSP_MICROSECONDS; @@ -946,13 +878,13 @@ VLIB_CLI_COMMAND (ip6_clear_ioam_trace_cmd, static) = { clib_error_t * ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id, - u32 app_data, int has_pow_option, u32 trace_tsp, + u32 app_data, int has_pot_option, u32 trace_tsp, int has_ppc_option) { int rv; ip6_hop_by_hop_ioam_main_t *hm = &ip6_hop_by_hop_ioam_main; rv = ip6_ioam_set_rewrite (&hm->rewrite, trace_type, trace_option_elts, - has_pow_option, has_ppc_option); + has_pot_option, has_ppc_option); switch (rv) { @@ -961,7 +893,7 @@ ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id, hm->app_data = app_data; hm->trace_type = trace_type; hm->trace_option_elts = trace_option_elts; - hm->has_pow_option = has_pow_option; + hm->has_pot_option = has_pot_option; hm->has_ppc_option = has_ppc_option; hm->trace_tsp = trace_tsp; break; @@ -982,7 +914,7 @@ ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm, u32 trace_option_elts = 0; u32 trace_type = 0, node_id = 0; u32 app_data = 0, trace_tsp = TSP_MICROSECONDS; - int has_pow_option = 0; + int has_pot_option = 0; int has_ppc_option = 0; clib_error_t * rv = 0; @@ -993,8 +925,8 @@ ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm, &trace_type, &trace_option_elts, &trace_tsp, &node_id, &app_data)) ; - else if (unformat (input, "pow")) - has_pow_option = 1; + else if (unformat (input, "pot")) + has_pot_option = 1; else if (unformat (input, "ppc encap")) has_ppc_option = PPC_ENCAP; else if (unformat (input, "ppc decap")) @@ -1007,7 +939,7 @@ ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm, rv = ip6_ioam_trace_profile_set(trace_option_elts, trace_type, node_id, - app_data, has_pow_option, trace_tsp, has_ppc_option); + app_data, has_pot_option, trace_tsp, has_ppc_option); return rv; } @@ -1015,7 +947,7 @@ ip6_set_ioam_rewrite_command_fn (vlib_main_t * vm, VLIB_CLI_COMMAND (ip6_set_ioam_rewrite_cmd, static) = { .path = "set ioam rewrite", - .short_help = "set ioam rewrite trace-type <0x1f|0x3|0x9|0x11|0x19> trace-elts trace-tsp <0|1|2|3> node-id app-data [pow] [ppc ]", + .short_help = "set ioam rewrite trace-type <0x1f|0x3|0x9|0x11|0x19> trace-elts trace-tsp <0|1|2|3> node-id app-data [pot] [ppc ]", .function = ip6_set_ioam_rewrite_command_fn, }; @@ -1063,10 +995,10 @@ ip6_show_ioam_summary_cmd_fn (vlib_main_t * vm, s = format(s, " HOP BY HOP OPTIONS - TRACE CONFIG - Not configured\n"); } - s = format(s, " POW OPTION - %d (%s)\n", - hm->has_pow_option, (hm->has_pow_option?"Enabled":"Disabled")); - if (hm->has_pow_option) - s = format(s, "Try 'show ioam sc-profile' for more information\n"); + s = format(s, " POT OPTION - %d (%s)\n", + hm->has_pot_option, (hm->has_pot_option?"Enabled":"Disabled")); + if (hm->has_pot_option) + s = format(s, "Try 'show ioam pot and show pot profile' for more information\n"); s = format(s, " EDGE TO EDGE - PPC OPTION - %d (%s)\n", hm->has_ppc_option, ppc_state[hm->has_ppc_option]); diff --git a/vnet/vnet/ip/ip6_hop_by_hop.h b/vnet/vnet/ip/ip6_hop_by_hop.h index 50a14a9b6a8..0ee24b306a1 100644 --- a/vnet/vnet/ip/ip6_hop_by_hop.h +++ b/vnet/vnet/ip/ip6_hop_by_hop.h @@ -45,8 +45,8 @@ typedef struct { u32 node_id; u32 app_data; - /* PoW option */ - u8 has_pow_option; + /* Pot option */ + u8 has_pot_option; #define PPC_NONE 0 #define PPC_ENCAP 1 @@ -59,7 +59,12 @@ typedef struct { #define TSP_NANOSECONDS 3 /* Time stamp precision. This is enumerated to above four options */ u32 trace_tsp; - + + /* Array of function pointers to ADD and POP HBH option handling routines */ + u8 options_size[256]; + int (*add_options[256])(u8 *rewrite_string, u8 rewrite_size); + int (*pop_options[256])(ip6_header_t *ip, ip6_hop_by_hop_option_t *opt); + /* convenience */ vlib_main_t * vlib_main; vnet_main_t * vnet_main; @@ -70,7 +75,7 @@ extern ip6_hop_by_hop_ioam_main_t ip6_hop_by_hop_ioam_main; extern u8 * format_path_map(u8 * s, va_list * args); extern clib_error_t * ip6_ioam_trace_profile_set(u32 trace_option_elts, u32 trace_type, u32 node_id, - u32 app_data, int has_pow_option, u32 trace_tsp, + u32 app_data, int has_pot_option, u32 trace_tsp, int has_e2e_option); extern int ip6_ioam_set_destination (ip6_address_t *addr, u32 mask_width, u32 vrf_id, int is_add, int is_pop, int is_none); @@ -103,5 +108,14 @@ static inline u8 is_zero_ip6_address (ip6_address_t *a) return ((a->as_u64[0] == 0) && (a->as_u64[1] == 0)); } -extern ip6_hop_by_hop_ioam_main_t * hm; +int ip6_hbh_add_register_option (u8 option, + u8 size, + int rewrite_options(u8 *rewrite_string, u8 size)); +int ip6_hbh_add_unregister_option (u8 option); + +int ip6_hbh_pop_register_option (u8 option, + int options(ip6_header_t *ip, ip6_hop_by_hop_option_t *opt)); +int ip6_hbh_pop_unregister_option (u8 option); + + #endif /* __included_ip6_hop_by_hop_ioam_h__ */ diff --git a/vnet/vnet/ip/ip6_hop_by_hop_packet.h b/vnet/vnet/ip/ip6_hop_by_hop_packet.h index 708275af2d8..0a2c3d02ac1 100644 --- a/vnet/vnet/ip/ip6_hop_by_hop_packet.h +++ b/vnet/vnet/ip/ip6_hop_by_hop_packet.h @@ -40,7 +40,7 @@ typedef struct { /* $$$$ IANA banana constants */ #define HBH_OPTION_TYPE_IOAM_TRACE_DATA_LIST 59 /* Third highest bit set (change en-route) */ -#define HBH_OPTION_TYPE_IOAM_PROOF_OF_WORK 60 /* Third highest bit set (change en-route) */ +#define HBH_OPTION_TYPE_IOAM_PROOF_OF_TRANSIT 60 /* Third highest bit set (change en-route) */ #define HBH_OPTION_TYPE_IOAM_EDGE_TO_EDGE 29 /* @@ -171,14 +171,6 @@ typedef CLIB_PACKED(struct { u32 elts[0]; /* Variable type. So keep it generic */ }) ioam_trace_option_t; -typedef CLIB_PACKED(struct { - ip6_hop_by_hop_option_t hdr; - u8 pow_type; -#define PROFILE_ID_MASK 0xF - u8 reserved_profile_id; /* 4 bits reserved, 4 bits to carry profile id */ - u64 random; - u64 cumulative; -}) ioam_pow_option_t; typedef CLIB_PACKED(struct { ip6_hop_by_hop_option_t hdr; diff --git a/vnet/vnet/lib-scv/scv_util.c b/vnet/vnet/lib-scv/scv_util.c deleted file mode 100644 index e69c8837815..00000000000 --- a/vnet/vnet/lib-scv/scv_util.c +++ /dev/null @@ -1,486 +0,0 @@ -/* - * 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. - */ -#include -#include -#include -#include -#include -#include "math64.h" -#include "scv_util.h" - -scv_profile *pow_profile = NULL; -u16 pow_profile_index = 0; -u64 total_pkts_using_this_profile = 0; -u8 chain_path_name[PATH_NAME_SIZE]; -scv_profile profile_list[MAX_SERVICE_PROFILES]; -u8 max_profiles = 0; -u16 invalid_profile_start_index = 0; -u8 number_of_invalid_profiles = 0; -f64 next_time_to_send = 0; -u32 time_exponent = 1; -vlib_main_t *gvm = 0; - -static void scv_profile_init(scv_profile * new, u16 id) -{ - if (new) - { - memset(new, 0, sizeof(scv_profile)); - new->id = id; - } -} - -/* - * Get maximum number of profiles configured for this chain. - */ -u8 scv_get_max_profiles(void) -{ - return max_profiles; -} - -scv_profile *scv_profile_find(u16 id) -{ - u8 max = scv_get_max_profiles(); - - if (id >= 0 && id < max) - { - return (&profile_list[id]); - } - return (NULL); -} - -u8 sc_init_done = 0; -void scv_init(u8 * path_name, u8 max, u8 indx) -{ - int i = 0; - - if (sc_init_done) - { - return; - } - memcpy(chain_path_name, path_name, strlen((const char *)path_name) + 1); - max_profiles = max; - pow_profile_index = indx; - - for (i = 0; i < max_profiles; i++) - { - scv_profile_init(&profile_list[i], i); - } - - sc_init_done = 1; -} - -void scv_profile_cleanup(scv_profile * profile) -{ - u16 id = profile->id; - - memset(profile, 0, sizeof(scv_profile)); - profile->id = id; /* Restore id alone */ -} - -void scv_profile_create(scv_profile * profile, u64 prime, - u64 poly2, u64 lpc, u64 secret_share, u64 validity) -{ - if (profile) - { - scv_profile_cleanup(profile); - profile->prime = prime; - profile->primeinv = 1.0 / prime; - profile->lpc = lpc; - profile->poly_pre_eval = poly2; - profile->secret_share = secret_share; - profile->validity = validity; - time_exponent = 1; /* Got a new profile. Reset backoff */ - next_time_to_send = 0; /* and send next request with no delay */ - } -} - -void scv_set_validator(scv_profile * profile, u64 key) -{ - if (profile) - { - profile->validator = 1; - profile->secret_key = key; - } -} - -static inline u64 sc_update_cumulative(u64 cumulative, u64 random, - u64 secret_share, u64 prime, u64 lpc, u64 pre_split, double prime_inv) -{ - u64 share_random = 0; - u64 cumulative_new = 0; - - /* - * calculate split share for random - */ - share_random = add64_mod(pre_split, random, prime, prime_inv); - - /* - * lpc * (share_secret + share_random) - */ - share_random = add64_mod(share_random, secret_share, prime, prime_inv); - share_random = mul64_mod(share_random, lpc, prime, prime_inv); - - cumulative_new = add64_mod(cumulative, share_random, prime, prime_inv); - - return (cumulative_new); -} - -u64 scv_update_cumulative(scv_profile * profile, u64 cumulative, u64 random) -{ - if (profile && profile->validity != 0) - { - return (sc_update_cumulative(cumulative, random, profile->secret_share, - profile->prime, profile->lpc, profile->poly_pre_eval, - profile->primeinv)); - } - return (0); -} - -static u8 sc_validate(u64 secret, u64 prime, double prime_inv, - u64 cumulative, u64 random) -{ - if (cumulative == (random + secret)) - { - return (1); - } - else if (cumulative == add64_mod(random, secret, prime, prime_inv)) - { - return (1); - } - return (0); -} - -/* - * return True if the cumulative matches secret from a profile - */ -u8 scv_validate(scv_profile * profile, u64 cumulative, u64 random) -{ - if (profile && profile->validator) - { - return (sc_validate(profile->secret_key, profile->prime, - profile->primeinv, cumulative, random)); - } - return (0); -} - -/* - * Utility function to get random number per pack - */ -u64 scv_generate_random(scv_profile * profile) -{ - u64 random = 0; - int32_t second_half; - static u32 seed = 0; - - if (PREDICT_FALSE(!seed)) - seed = random_default_seed(); - - /* - * Upper 4 bytes seconds - */ - random = (u64) time(NULL); - - random &= 0xffffffff; - random = random << 32; - /* - * Lower 4 bytes random number - */ - second_half = random_u32(&seed); - - random |= second_half; - - if (PREDICT_TRUE(profile != NULL)) - { - random &= profile->bit_mask; - } - return (random); -} - -void scv_profile_set_bit_mask(scv_profile * profile, u16 bits) -{ - int sizeInBits; - - if (profile) - { - sizeInBits = sizeof(profile->bit_mask) * 8; - profile->bit_mask = - (bits >= - sizeInBits ? (u64) - 1 : (u64) ((u64) 1 << (u64) bits) - 1); - } -} - -/* - * TODO: Use vector buffers and hash tables - */ -#define MAX_SERVICES 16 - -clib_error_t *clear_scv_profile_command_fn(vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - int i = 0; - - if (!sc_init_done) - return 0; - - for (i = 0; i < max_profiles; i++) - { - scv_profile_cleanup(&profile_list[i]); - } - pow_profile = NULL; - pow_profile_index = 0; - total_pkts_using_this_profile = 0; - memset(chain_path_name, 0, PATH_NAME_SIZE); - max_profiles = 0; - invalid_profile_start_index = 0; - number_of_invalid_profiles = 0; - next_time_to_send = 0; - time_exponent = 1; - sc_init_done = 0; - - return 0; -} - -void clear_scv_profiles() -{ - clear_scv_profile_command_fn(0, 0, 0); -} - -VLIB_CLI_COMMAND(clear_scv_profile_command) = -{ -.path = "clear scv profile", -.short_help = "clear scv profile [|all]", -.function = clear_scv_profile_command_fn, -}; - -static clib_error_t *set_scv_profile_command_fn(vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - u64 prime; - u64 secret_share, validity; - u64 secret_key; - u8 validator = 0; - u16 profile_id; - u32 bits; - u64 lpc = 0, poly2 = 0; - scv_profile *profile = NULL; - - bits = MAX_BITS; - - while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) - { - if (unformat(input, "id %d", &profile_id)) - ; - else if (unformat(input, "validate-key 0x%Lx", &secret_key)) - validator = 1; - else if (unformat(input, "prime-number 0x%Lx", &prime)) - ; - else if (unformat(input, "secret_share 0x%Lx", &secret_share)) - ; - else if (unformat(input, "polynomial2 0x%Lx", &poly2)) - ; - else if (unformat(input, "lpc 0x%Lx", &lpc)) - ; - else if (unformat(input, "validity 0x%Lx", &validity)) - ; - else if (unformat(input, "bits-in-random %d", &bits)) - { - if (bits > MAX_BITS) - bits = MAX_BITS; - } - else - return clib_error_return(0, "unknown input `%U'", - format_unformat_error, input); - } - - scv_init((u8 *) "TEST", MAX_SERVICE_PROFILES, 0 /* start index */ ); - profile = scv_profile_find(profile_id); - - if (profile) - { - scv_profile_create(profile, prime, poly2, lpc, secret_share, validity); - if (validator) - scv_set_validator(profile, secret_key); - scv_profile_set_bit_mask(profile, bits); - } - - return 0; -} - -VLIB_CLI_COMMAND(set_scv_profile_command) = -{ -.path = "set scv profile", -.short_help = "set scv profile id [0-16] [validator-key 0xu64] \ - prime-number 0xu64 secret_share 0xu64 lpc 0xu64 \ - polynomial2 0xu64 bits-in-random [0-64] ", -.function = set_scv_profile_command_fn, -}; - -static clib_error_t *show_scv_profile_command_fn(vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - scv_profile *p = NULL; - u16 i; - u8 *s = 0; - - if (sc_init_done == 0) - { - s = format(s, "SCV Profiles not configured\n"); - vlib_cli_output(vm, "%v", s); - return 0; - } - - for (i = 0; i < max_profiles; i++) - { - p = scv_profile_find(i); - if (p->validity == 0) - continue; - s = format(s, "SCV Profile at index: %d\n", i); - s = format(s, " Id : %d\n", p->id); - s = format(s, " Validator : %s (%d)\n", - (p->validator) ? "True" : "False", p->validator); - if (p->validator == 1) - s = format(s, " Secret key : 0x%Lx (%Ld)\n", - p->secret_key, p->secret_key); - s = format(s, " Secret share : 0x%Lx (%Ld)\n", - p->secret_share, p->secret_share); - s = format(s, " Prime number : 0x%Lx (%Ld)\n", - p->prime, p->prime); - s = format(s, "2nd polynomial(eval) : 0x%Lx (%Ld)\n", - p->poly_pre_eval, p->poly_pre_eval); - s = format(s, " LPC : 0x%Lx (%Ld)\n", p->lpc, p->lpc); - - s = format(s, " Bit mask : 0x%Lx (%Ld)\n", - p->bit_mask, p->bit_mask); - s = format(s, " Validity : 0x%Lx (%Ld)\n", - p->validity, p->validity); - } - - if (max_profiles) - { - p = scv_profile_find(pow_profile_index); - - s = format(s, "\nInvalid profiles start : %d Number : %d\n", - invalid_profile_start_index, number_of_invalid_profiles); - - if (next_time_to_send) - s = format(s, "\nNext time to send : %U, time_exponent:%ld\n", - format_time_interval, "d:h:m:s:f:u", - next_time_to_send, time_exponent); - else - s = format(s, "\nNext time to send : Immediate\n"); - s = format(s, "\nPath name : %s\n", chain_path_name); - s = format(s, "\nProfile index in use: %d\n", pow_profile_index); - s = format(s, "Pkts passed : 0x%Lx (validity:0x%Lx)\n", - total_pkts_using_this_profile, p->validity); - if (scv_is_decap(p)) - s = format(s, " This is Decap node. \n"); - vlib_cli_output(vm, "%v", s); - } - vec_free(s); - - return 0; -} - -VLIB_CLI_COMMAND(show_scv_profile_command) = -{ -.path = "show scv profile", -.short_help = "show scv profile", -.function = show_scv_profile_command_fn, -}; - -static clib_error_t *test_profile_renew_refresh_fn(vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - u8 renew_or_refresh = 0; - -#define TEST_PROFILE_RENEW 1 -#define TEST_PROFILE_REFRESH 2 - u8 *path_name = 0; - u32 start_index = 0, num_profiles = 0; - int rc = 0; - - while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) - { - if (unformat(input, "path-name %s start-index %d num-profiles %d", - &path_name, &start_index, &num_profiles)) - ; - else if (unformat(input, "renew")) - renew_or_refresh = TEST_PROFILE_RENEW; - else if (unformat(input, "refresh")) - renew_or_refresh = TEST_PROFILE_REFRESH; - else - break; - } - - if (renew_or_refresh == TEST_PROFILE_RENEW) - { - - rc = scv_profile_renew(path_name, (u8) start_index, (u8) num_profiles); - } - else if (renew_or_refresh == TEST_PROFILE_REFRESH) - { - - rc = scv_profile_refresh(path_name, (u8) start_index, - (u8) num_profiles); - } - else - { - vec_free(path_name); - return clib_error_return(0, "Enter renew or refresh"); - } - - vlib_cli_output(vm, "%s notification %s. rc = %d\n", - (renew_or_refresh == TEST_PROFILE_RENEW) ? "Renew" : "Refresh", - (rc != 0) ? "failed" : "sent", (u32) rc); - - vec_free(path_name); - - return 0; -} - -VLIB_CLI_COMMAND(test_ioam_profile_renew_refresh_cmd, static) = -{ -.path = "test ioam profile-notification ", -.short_help = - "test ioam profile-notification path-name start-index num-profiles ", -.function = test_profile_renew_refresh_fn, -}; - -static clib_error_t *set_scv_init_fn(vlib_main_t * vm, - unformat_input_t * input, vlib_cli_command_t * cmd) -{ - u8 *path_name = 0; - u32 start_index = 0, num_profiles = 0; - - while (unformat_check_input(input) != UNFORMAT_END_OF_INPUT) - { - if (unformat(input, "path-name %s start-index %d num-profiles %d", - &path_name, &start_index, &num_profiles)) - scv_init(path_name, num_profiles, start_index); - else - return clib_error_return(0, "unknown input `%U'", - format_unformat_error, input); - } - vec_free(path_name); - return 0; -} - -VLIB_CLI_COMMAND(set_ioam_sc_init_command, static) = -{ -.path = "set scv-init ", -.short_help = - "set scv-init path-name start-index num-profiles ", -.function = set_scv_init_fn, -}; diff --git a/vnet/vnet/lib-scv/scv_util.h b/vnet/vnet/lib-scv/scv_util.h deleted file mode 100644 index 5c304e9edc0..00000000000 --- a/vnet/vnet/lib-scv/scv_util.h +++ /dev/null @@ -1,278 +0,0 @@ -/* - * 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 -#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_ioam_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