From dfd7ce27fea04c1a76844e21286c2b1d6653e153 Mon Sep 17 00:00:00 2001 From: Jim Gibson Date: Mon, 20 Feb 2017 11:53:54 -0500 Subject: Initial Commit: VPP cicn VPP plugin Change-Id: If1b965f0a4b7cfacda8f6caf6925072a9007ffb4 Signed-off-by: Jim Gibson --- cicn-plugin/cicn/cicn_mgmt.c | 2277 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2277 insertions(+) create mode 100644 cicn-plugin/cicn/cicn_mgmt.c (limited to 'cicn-plugin/cicn/cicn_mgmt.c') diff --git a/cicn-plugin/cicn/cicn_mgmt.c b/cicn-plugin/cicn/cicn_mgmt.c new file mode 100644 index 00000000..18f03530 --- /dev/null +++ b/cicn-plugin/cicn/cicn_mgmt.c @@ -0,0 +1,2277 @@ +/* + * Copyright (c) 2017 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. + */ +/* + * Management plane: + * - Handlers for CICN binary API operations (vpp_api_test) + * - Declarations of and handlers for DBG-cli commands + * - Internal management operation handlers called by both of the + * above, to minimize copied code. + */ +#include + +#include +#include +#include +#include +#include +#include + +#include // port registration + +#include + +/* define message IDs */ +#include + +/* 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 + +/* + * Handy macros to initialize/send a message reply. + * Assumes that certain variables, showed in comments, are available + * as local variables in using routing. + */ + +// allocate rmp buffer, verify queue is valie +#define REPLY_SETUP(t, rmp, q, mp) \ +do { \ + 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(sm->msg_id_base + (t)); \ + rmp->context = mp->context; \ +} while(0); + +// set return value and send response +#define REPLY_FINISH(rmp, q, rv) \ +do { \ + rmp->retval = ntohl(rv); \ + vl_msg_api_send_shmem (q, (u8 *)&rmp); \ +} while(0); + +// combined single vector to allocate rmp buffer and send rv response +// can only be used for API calls (e.g. "set" calls) that only return rv +#define REPLY_MACRO(t/*, rmp, mp, rv*/) \ +do { \ + unix_shared_memory_queue_t * q; \ + REPLY_SETUP(t, rmp, q, mp); \ + REPLY_FINISH(rmp, q, rv); \ +} while(0); + + +/* + * Convert a unix return code to a vnet_api return code. + * Currently stubby: should have more cases. + */ +static inline vnet_api_error_t +cicn_api_rv_from_unix_rc (int ux_rc) +{ + vnet_api_error_t vae; + + switch (ux_rc) + { + case AOK: + vae = CICN_VNET_API_ERROR_NONE; + break; + default: + vae = VNET_API_ERROR_SYSCALL_ERROR_9; // should not happen, add cases + break; + } + return (vae); +} + +/* + * Convert a unix return code to a vnet_api return code. + * Currently stubby: should use cl_error unix_rc if available + */ +static inline vnet_api_error_t +cicn_api_rv_from_clib_error (clib_error_t * cl_err) +{ + vnet_api_error_t vae; + + if (cl_err == NULL) + { + vae = CICN_VNET_API_ERROR_NONE; + } + else + { + vae = VNET_API_ERROR_SYSCALL_ERROR_9; // should not happen, add cases + } + return (vae); +} + + +/* + * Hide the details of cli output from the cicn-aware modules + */ +int +cicn_cli_output (const char *fmt, ...) +{ + cicn_main_t *sm = &cicn_main; + va_list args; + char buf[200]; + + va_start (args, fmt); + vsnprintf (buf, sizeof (buf), fmt, args); + va_end (args); + + /* Belt and suspenders */ + buf[sizeof (buf) - 1] = 0; + + vlib_cli_output (sm->vlib_main, buf); + + return (0); +} + +/* API message handler */ +static void +vl_api_cicn_api_node_params_set_t_handler (vl_api_cicn_api_node_params_set_t * + mp) +{ + vl_api_cicn_api_node_params_set_reply_t *rmp; + vnet_api_error_t rv; + + cicn_main_t *sm = &cicn_main; + int ux_rc; + + //TODO: implement clib_net_to_host_f64 in VPP ? + int fib_max_size = clib_net_to_host_i32 (mp->fib_max_size); + int pit_max_size = clib_net_to_host_i32 (mp->pit_max_size); + f64 pit_dflt_lifetime_sec = mp->pit_dflt_lifetime_sec; + f64 pit_min_lifetime_sec = mp->pit_min_lifetime_sec; + f64 pit_max_lifetime_sec = mp->pit_max_lifetime_sec; + int cs_max_size = clib_net_to_host_i32 (mp->cs_max_size); + + ux_rc = cicn_infra_plugin_enable_disable ((int) (mp->enable_disable), + fib_max_size, pit_max_size, + pit_dflt_lifetime_sec, + pit_min_lifetime_sec, + pit_max_lifetime_sec, + cs_max_size); + + rv = cicn_api_rv_from_unix_rc (ux_rc); + REPLY_MACRO (VL_API_CICN_API_NODE_PARAMS_SET_REPLY /*, rmp, mp, rv */ ); +} + +/* API message handler */ +static void +vl_api_cicn_api_node_params_get_t_handler (vl_api_cicn_api_node_params_get_t * + mp) +{ + vl_api_cicn_api_node_params_get_reply_t *rmp; + vnet_api_error_t rv; + + cicn_main_t *sm = &cicn_main; + int ux_rc = AOK; + + 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 (sm->msg_id_base + VL_API_CICN_API_NODE_PARAMS_GET_REPLY); + rmp->context = mp->context; + rmp->is_enabled = sm->is_enabled; + + rmp->feature_multithread = CICN_FEATURE_MULTITHREAD; + rmp->feature_cs = CICN_FEATURE_CS; + rmp->feature_dpdk_rtembuf_cloning = CICN_FEATURE_DPDK_RTEMBUF_CLONING; + rmp->feature_vpp_vlib_cloning = CICN_FEATURE_VPP_VLIB_CLONING; + + rmp->worker_count = clib_host_to_net_u32 (sm->worker_count); + + rmp->fib_max_size = clib_host_to_net_u32 (sm->fib.fib_capacity); + + rmp->pit_max_size = + clib_host_to_net_u32 (cicn_infra_shard_pit_size * sm->shard_count); + //TODO: add clib_host_to_net_f64 to VPP ? + rmp->pit_dflt_lifetime_sec = ((f64) sm->pit_lifetime_dflt_ms) / SEC_MS; + rmp->pit_min_lifetime_sec = ((f64) sm->pit_lifetime_min_ms) / SEC_MS; + rmp->pit_max_lifetime_sec = ((f64) sm->pit_lifetime_max_ms) / SEC_MS; + rmp->cs_max_size = + clib_host_to_net_u32 (cicn_infra_shard_cs_size * sm->shard_count); + + rv = cicn_api_rv_from_unix_rc (ux_rc); + rmp->retval = clib_host_to_net_i32 (rv); + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static vl_api_cicn_api_node_params_set_t node_ctl_params = { + .fib_max_size = -1, + .pit_max_size = -1, + .pit_dflt_lifetime_sec = -1.0f, + .pit_min_lifetime_sec = -1.0f, + .pit_max_lifetime_sec = -1.0f, + .cs_max_size = -1, +}; + +/* + * cli handler for 'control start' + */ +static clib_error_t * +cicn_cli_node_ctl_start_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int ux_rc; + + /* Catch unexpected extra arguments on this line. + * Get a line of input but only in the unexpected case that line + * line not already consumed by matching VLIB_CLI_COMMAND.path) + * [i.e. on "cicn control start\n", don't consume the following line (cmd) + * while catching unexpected extra arguments on "cicn control start XXX"] + */ + if (main_input->index > 0 && + main_input->buffer[main_input->index - 1] != '\n') + { + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + return clib_error_return (0, "Unknown argument '%U'", + format_unformat_error, line_input); + } + } + + ux_rc = cicn_infra_plugin_enable_disable (1 /*enable */ , + node_ctl_params.fib_max_size, + node_ctl_params.pit_max_size, + node_ctl_params.pit_dflt_lifetime_sec, + node_ctl_params.pit_min_lifetime_sec, + node_ctl_params.pit_max_lifetime_sec, + node_ctl_params.cs_max_size); + + switch (ux_rc) + { + case AOK: + break; + default: + return clib_error_return (0, "cmd returned %d", ux_rc); + } + + return (0); +} + +/* + * cli handler for 'control stop' + */ +static clib_error_t * +cicn_cli_node_ctl_stop_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int ux_rc; + + /* Catch unexpected extra arguments on this line. + * See comment on cicn_cli_node_ctrl_start_set_command_fn + */ + if (main_input->index > 0 && + main_input->buffer[main_input->index - 1] != '\n') + { + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + return clib_error_return (0, "Unknown argument '%U'", + format_unformat_error, line_input); + } + } + + ux_rc = cicn_infra_plugin_enable_disable (0 /*!enable */ , + node_ctl_params.fib_max_size, + node_ctl_params.pit_max_size, + node_ctl_params.pit_dflt_lifetime_sec, + node_ctl_params.pit_min_lifetime_sec, + node_ctl_params.pit_max_lifetime_sec, + node_ctl_params.cs_max_size); + + switch (ux_rc) + { + case AOK: + break; + default: + return clib_error_return (0, "cmd returned %d", ux_rc); + } + + return (0); +} + +#define DFLTD_RANGE_OK(val, min, max) \ +({ \ + __typeof__ (val) _val = (val); \ + __typeof__ (min) _min = (min); \ + __typeof__ (max) _max = (max); \ + (_val == -1) || \ + (_val >= _min && _val <= _max); \ +}) + +/* + * cli handler for 'control param' + */ +static clib_error_t * +cicn_cli_node_ctl_param_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int rv = 0; + + int table_size; + f64 lifetime; + + if (cicn_main.is_enabled) + { + return (clib_error_return + (0, "params cannot be altered once cicn started")); + } + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "fib")) + { + if (unformat (line_input, "size %d", &table_size)) + { + if (!DFLTD_RANGE_OK (table_size, CICN_PARAM_FIB_ENTRIES_MIN, + CICN_PARAM_FIB_ENTRIES_MAX)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.fib_max_size = table_size; + } + else + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + break; + } + } + else if (unformat (line_input, "pit")) + { + if (unformat (line_input, "size %d", &table_size)) + { + if (!DFLTD_RANGE_OK (table_size, CICN_PARAM_PIT_ENTRIES_MIN, + CICN_PARAM_PIT_ENTRIES_MAX)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.pit_max_size = table_size; + } + else if (unformat (line_input, "dfltlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, CICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + CICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.pit_dflt_lifetime_sec = lifetime; + } + else if (unformat (line_input, "minlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, CICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + CICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.pit_min_lifetime_sec = lifetime; + } + else if (unformat (line_input, "maxlife %f", &lifetime)) + { + if (!DFLTD_RANGE_OK + (lifetime, CICN_PARAM_PIT_LIFETIME_BOUND_MIN_SEC, + CICN_PARAM_PIT_LIFETIME_BOUND_MAX_SEC)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.pit_max_lifetime_sec = lifetime; + } + else + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + break; + } + } + else if (unformat (line_input, "cs")) + { + if (unformat (line_input, "size %d", &table_size)) + { + if (!DFLTD_RANGE_OK (table_size, CICN_PARAM_CS_ENTRIES_MIN, + CICN_PARAM_CS_ENTRIES_MAX)) + { + rv = VNET_API_ERROR_INVALID_VALUE; + break; + } + node_ctl_params.cs_max_size = table_size; + } + else + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + break; + } + } + else + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + break; + } + } + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Unknown argument '%U'", + format_unformat_error, line_input); + default: + return clib_error_return (0, "cmd returned %d", rv); + } + + return (0); +} + +/* + * cli handler for 'enable' + */ +static clib_error_t * +cicn_cli_node_enable_disable_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int enable_disable = 1; + int ux_rc; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "disable")) + { + enable_disable = 0; + } + else + { + return clib_error_return (0, "Unknown argument '%U'", + format_unformat_error, line_input); + } + } + + ux_rc = cicn_infra_plugin_enable_disable (enable_disable, + node_ctl_params.fib_max_size, + node_ctl_params.pit_max_size, + node_ctl_params.pit_dflt_lifetime_sec, + node_ctl_params.pit_min_lifetime_sec, + node_ctl_params.pit_max_lifetime_sec, + node_ctl_params.cs_max_size); + + switch (ux_rc) + { + case AOK: + break; + + default: + return clib_error_return (0, "cicn enable_disable returned %d", ux_rc); + } + return 0; +} + +/* + * cli handler for 'cfg name': router's own ICN name + */ +static clib_error_t * +cicn_cli_node_name_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + cicn_infra_fwdr_name_t *gfname = &cicn_infra_fwdr_name; + int delete = 0; + uint8_t buf[200]; + int len, ret; + const char *fwdr_name = NULL; + uint8_t *ptr; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "delete")) + { + delete = 1; + } + else if (unformat (line_input, "%s", &fwdr_name)) + { + ; + } + else + { + return clib_error_return (0, "Unknown argument '%U'", + format_unformat_error, line_input); + } + } + + /* Verify that the given name is not empty */ + if (fwdr_name == NULL) + { + return clib_error_return (0, "Please specify an non-empty name..."); + } + + /* Handle delete case */ + if (delete) + { + if (gfname->fn_reply_payload_flen == 0) + { + return clib_error_return (0, + "Forwarder does not have a name yet..."); + } + else if (strcmp (gfname->fn_str, fwdr_name) == 0) + { + cicn_sstrncpy (gfname->fn_str, "no-name", sizeof (gfname->fn_str)); + gfname->fn_reply_payload_flen = 0; + vlib_cli_output (vm, "name:%s: deleted successfully", fwdr_name); + } + else + { + return clib_error_return (0, "Name for deletion not found..."); + } + } + else + { + /* TODO: Potentially do more validation for the parsed name */ + if (strlen (fwdr_name) > sizeof (buf)) + { + return clib_error_return (0, "The given name is too long..."); + } + /* Convert prefix to wire-format */ + cicn_rd_t cicn_rd; + len = + cicn_parse_name_comps_from_str (buf, sizeof (buf), fwdr_name, + &cicn_rd); + if (len < 0) + { + return clib_error_return (0, + "Could not parse name comps from the name: %s...", + cicn_rd_str (&cicn_rd)); + } + /* Hash the prefix */ + ret = cicn_hashtb_hash_prefixes (buf, len, 0 /*full_name */ , + &gfname->fn_hashinf, 0 /*limit */ ); + if (ret != AOK) + { + return clib_error_return (0, "Could not hash the given name..."); + } + gfname->fn_match_pfx_hash = + gfname->fn_hashinf.pfx_hashes[gfname->fn_hashinf.pfx_count - 1]; + cicn_sstrncpy (gfname->fn_str, fwdr_name, sizeof (gfname->fn_str)); + + gfname->fn_reply_payload_flen = CICN_TLV_HDR_LEN + len; + /* Check for overflow */ + if (gfname->fn_reply_payload_flen > CICN_FWDR_NAME_BUFSIZE) + { + vlib_cli_output (vm, "traceroute payload TLV: overflow"); + } + + /* Create the traceroute payload (name TLV) */ + memset (gfname->fn_reply_payload, 0, sizeof (gfname->fn_reply_payload)); + ptr = gfname->fn_reply_payload; + C_PUTINT16 (&ptr[0], CICN_TLV_PAYLOAD); + C_PUTINT16 (&ptr[CICN_TLV_TYPE_LEN], len); + + memcpy (&ptr[CICN_TLV_HDR_LEN], buf, len); + + vlib_cli_output (vm, "name %s: added successfully", gfname->fn_str); + } + return (0); +} + +/* shared routine betweeen API and CLI, leveraging API message structure */ +static int +cicn_mgmt_node_stats_get (vl_api_cicn_api_node_stats_get_reply_t * rmp) +{ + rmp->pkts_processed = 0; + rmp->pkts_interest_count = 0; + rmp->pkts_data_count = 0; + rmp->pkts_nak_count = 0; + rmp->pkts_from_cache_count = 0; + rmp->pkts_nacked_interests_count = 0; + rmp->pkts_nak_hoplimit_count = 0; + rmp->pkts_nak_no_route_count = 0; + rmp->pkts_no_pit_count = 0; + rmp->pit_expired_count = 0; + rmp->cs_expired_count = 0; + rmp->cs_lru_count = 0; + rmp->pkts_drop_no_buf = 0; + rmp->interests_aggregated = 0; + rmp->interests_retx = 0; + rmp->pit_entries_count = 0; + rmp->cs_entries_count = 0; + + vlib_error_main_t *em; + vlib_node_t *n; + foreach_vlib_main (( + { + em = &this_vlib_main->error_main; + n = vlib_get_node (this_vlib_main, icnfwd_node.index); + u32 node_cntr_base_idx = n->error_heap_index; + rmp->pkts_processed += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_PROCESSED]); + rmp->pkts_interest_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_INTERESTS]); + rmp->pkts_data_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_DATAS]); + rmp->pkts_nak_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_NAKS]); + rmp->pkts_from_cache_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_CACHED]); + rmp->pkts_nacked_interests_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_NACKED_INTERESTS]); + rmp->pkts_nak_hoplimit_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_HOPLIMIT_EXCEEDED]); + rmp->pkts_nak_no_route_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_NO_ROUTE]); + rmp->pkts_no_pit_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_NO_PIT]); + rmp->pit_expired_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_PIT_EXPIRED]); + rmp->cs_expired_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_CS_EXPIRED]); + rmp->cs_lru_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_CS_LRU]); + rmp->pkts_drop_no_buf += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_NO_BUFS]); + rmp->interests_aggregated += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_INTEREST_AGG]); + rmp->interests_retx += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_INT_RETRANS]); + rmp->pit_entries_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_INT_COUNT]); + rmp->cs_entries_count += + clib_host_to_net_u64 (em->counters[node_cntr_base_idx + + ICNFWD_ERROR_CS_COUNT]); + })); + return (AOK); +} + +/* API message handler */ +static void +vl_api_cicn_api_node_stats_get_t_handler (vl_api_cicn_api_node_stats_get_t * + mp) +{ + vl_api_cicn_api_node_stats_get_reply_t *rmp; + cicn_main_t *sm = &cicn_main; + vnet_api_error_t vaec = CICN_VNET_API_ERROR_NONE; + + 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 (sm->msg_id_base + VL_API_CICN_API_NODE_STATS_GET_REPLY); + rmp->context = mp->context; + + int ux_rc = cicn_mgmt_node_stats_get (rmp); + if (ux_rc != AOK) + { + vaec = cicn_api_rv_from_unix_rc (ux_rc); + } + + rmp->retval = clib_host_to_net_i32 (vaec); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +/* + * cli handler for 'cfg salt': per-router hash salt/nonce + */ +static clib_error_t * +cicn_cli_salt_set_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + return (clib_error_return (0, "Not yet implemented...")); +} + +typedef enum +{ + CICN_MGMT_FACE_OP_NONE = 0, + CICN_MGMT_FACE_OP_CREATE, + CICN_MGMT_FACE_OP_DELETE, + CICN_MGMT_FACE_OP_ADMIN, + CICN_MGMT_FACE_OP_HELLO, +} cicn_mgmt_face_op_e; + +/* + * Push Face notifications to all subscribers + */ +static void +cicn_api_face_event_send (int faceid, int faceflags) +{ + cicn_main_t *sm = &cicn_main; + vl_api_cicn_api_face_event_t *event = vl_msg_api_alloc (sizeof (*event)); + + int i; + for (i = 0; i < sm->n_face_event_subscribers; i++) + { + unix_shared_memory_queue_t *mq = + vl_api_client_index_to_input_queue (sm-> + face_event_subscribers + [i].client_index); + if (!mq) + continue; + + memset (event, 0, sizeof (*event)); + event->_vl_msg_id = + ntohs (sm->msg_id_base + VL_API_CICN_API_FACE_EVENT); + event->context = sm->face_event_subscribers[i].context; + event->client_index = sm->face_event_subscribers[i].client_index; + event->faceid = clib_host_to_net_i32 (faceid); + event->flags = clib_host_to_net_i32 (faceflags); + + vl_msg_api_send_shmem (mq, (u8 *) & event); + } +} + +/* + * Face add routine common to binary api and cli. + * + * Adds UDPv4 face and returns new Face ID if successful, -1 otherwise + * + * TODO -- how to un-register? doesn't seem to be an api for that. + */ +static vnet_api_error_t +cicn_mgmt_face_add (ip4_address_t local_addr4, int local_port, + ip4_address_t remote_addr4, int remote_port, + int app_face, int *faceid) +{ + vnet_api_error_t rv; + + cicn_main_t *sm = &cicn_main; + vnet_main_t *vnm = vnet_get_main (); + vnet_interface_main_t *im = &vnm->interface_main; + vnet_sw_interface_t *swif_list = 0, *si; + ip4_main_t *im4 = &ip4_main; + ip_lookup_main_t *lm4 = &im4->lookup_main; + ip4_address_t *addr4; + ip_interface_address_t *ia = 0; + int found_p; + + /* Look for a matching swif for the local address */ + found_p = 0; + swif_list = vec_new (vnet_sw_interface_t, pool_elts (im->sw_interfaces)); + _vec_len (swif_list) = 0; + + pool_foreach (si, im->sw_interfaces, ( + { + vec_add1 (swif_list, si[0]); + })); + + vec_foreach (si, swif_list) + { + foreach_ip_interface_address (lm4, ia, si->sw_if_index, 1, ( + { + addr4 = + ip_interface_address_get_address + (lm4, ia); + if + (addr4->as_u32 + == + local_addr4.as_u32) + { + found_p = 1; + break;} + } + )); + + if (found_p) + { + break; + } + }; + + if (!found_p) + { + rv = VNET_API_ERROR_NO_SUCH_ENTRY; + goto done; + } + + // vlib_cli_output(sm->vlib_main, "cicn: face swif %d", si->sw_if_index); + + /* TODO -- Check that the swif is 'up'? */ + // if ((si->flags & VNET_SW_INTERFACE_FLAG_ADMIN_UP) == 0) {} + + /* Create a cicn 'face', and capture needed info in the face cache */ + int ux_rc; + cicn_rd_t cicn_rd; + *faceid = -1; + ux_rc = cicn_face_add (local_addr4.as_u32, local_port, remote_addr4.as_u32, + remote_port, app_face, si->sw_if_index, faceid, + &cicn_rd); + if (ux_rc != AOK) + { // TODO: look at cicn_rc.rd_cicn_rc first + rv = cicn_api_rv_from_unix_rc (ux_rc); + goto done; + } + cicn_face_db_entry_t *face; + ux_rc = cicn_face_entry_find_by_id (*faceid, &face); + if (ux_rc != AOK) + { + rv = cicn_api_rv_from_unix_rc (ux_rc); + goto done; + } + + /* Update config generation number */ + CICN_INFRA_CFG_GEN_INCR (); + + /* TODO -- output new face id on success? */ + + /* On success, start taking packets on the local port. Packets are + * delivered to our work-distribution nodes, which then pass them to + * forwarding nodes. + */ + + /* TODO -- only register the port if it's unique? */ + + /* If there are worker threads, register our distribution node, + * which will decide how packets go to forwarding threads. + */ + if (sm->worker_count > 1) + { +#if CICN_FEATURE_MULTITHREAD + udp_register_dst_port (sm->vlib_main, local_port, + icndist_node.index, 1); +#else + ASSERT (sm->worker_count <= 1); +#endif + } + else + { + /* Register the forwarding node directly otherwise (in + * single-threaded mode, e.g.) + */ + udp_register_dst_port (sm->vlib_main, local_port, icnfwd_node.index, 1); + } + + rv = CICN_VNET_API_ERROR_NONE; + +done: + + vec_free (swif_list); + + return (rv); +} + +/* + * Face add routine common to binary api and cli. + * + * Removes specified face + */ +static clib_error_t * +cicn_mgmt_face_remove (int faceid) +{ + return clib_error_return (0, "face deletion not implemented"); +} + +/* API message handler */ +static void +vl_api_cicn_api_face_add_t_handler (vl_api_cicn_api_face_add_t * mp) +{ + vl_api_cicn_api_face_add_reply_t *rmp; + vnet_api_error_t rv; + unix_shared_memory_queue_t *q; + + cicn_main_t *sm = &cicn_main; + int faceid = -1; + + ip4_address_t local_addr = + (ip4_address_t) (clib_net_to_host_u32 (mp->local_addr)); + + uint16_t local_port = clib_net_to_host_u16 (mp->local_port); + + ip4_address_t remote_addr = + (ip4_address_t) (clib_net_to_host_u32 (mp->remote_addr)); + + uint16_t remote_port = clib_net_to_host_u16 (mp->remote_port); + + REPLY_SETUP (VL_API_CICN_API_FACE_ADD_REPLY, rmp, q, mp); + + rv = + cicn_mgmt_face_add (local_addr, (int) local_port, remote_addr, + (int) remote_port, 0 /*is_app */ , &faceid); + + if (rv >= 0) + { + rmp->faceid = clib_host_to_net_i32 (faceid); + } + + REPLY_FINISH (rmp, q, rv); + + if (rv >= 0) + { + // send event: for api, defer until after api response + cicn_api_face_event_send (faceid, CICN_FACE_FLAGS_DEFAULT); + } +} + +/* API message handler */ +static void +vl_api_cicn_api_face_delete_t_handler (vl_api_cicn_api_face_delete_t * mp) +{ + vl_api_cicn_api_face_delete_reply_t *rmp; + vnet_api_error_t rv; + + cicn_main_t *sm = &cicn_main; + clib_error_t *cl_err = 0; + + int faceid = clib_net_to_host_i32 (mp->faceid); + cl_err = cicn_mgmt_face_remove (faceid); + + rv = cicn_api_rv_from_clib_error (cl_err); + REPLY_MACRO (VL_API_CICN_API_FACE_DELETE_REPLY /*, rmp, mp, rv */ ); + + // TODO: check error value or rv value + cicn_api_face_event_send (mp->faceid, CICN_FACE_FLAG_DELETED); +} + +/* API message handler */ +static void +vl_api_cicn_api_face_params_get_t_handler (vl_api_cicn_api_face_params_get_t * + mp) +{ + vl_api_cicn_api_face_params_get_reply_t *rmp; + vnet_api_error_t rv; + unix_shared_memory_queue_t *q; + + cicn_main_t *sm = &cicn_main; + + int faceid = clib_net_to_host_i32 (mp->faceid); + + REPLY_SETUP (VL_API_CICN_API_FACE_PARAMS_GET_REPLY, rmp, q, mp); + + rv = cicn_face_api_entry_params_serialize (faceid, rmp); + + REPLY_FINISH (rmp, q, rv); +} + +/* API message handler */ +static void +vl_api_cicn_api_face_props_get_t_handler (vl_api_cicn_api_face_props_get_t * + mp) +{ + vl_api_cicn_api_face_props_get_reply_t *rmp; + vnet_api_error_t rv = 0; + unix_shared_memory_queue_t *q; + + cicn_main_t *sm = &cicn_main; + + REPLY_SETUP (VL_API_CICN_API_FACE_PROPS_GET_REPLY, rmp, q, mp); + + rv = cicn_face_api_entry_props_serialize (rmp); + + REPLY_FINISH (rmp, q, rv); +} + +/* API message handler */ +static void +vl_api_cicn_api_face_stats_get_t_handler (vl_api_cicn_api_face_stats_get_t * + mp) +{ + vl_api_cicn_api_face_stats_get_reply_t *rmp; + vnet_api_error_t rv; + unix_shared_memory_queue_t *q; + + cicn_main_t *sm = &cicn_main; + + int faceid = clib_net_to_host_i32 (mp->faceid); + + REPLY_SETUP (VL_API_CICN_API_FACE_STATS_GET_REPLY, rmp, q, mp); + + rv = cicn_face_api_entry_stats_serialize (faceid, rmp); + + REPLY_FINISH (rmp, q, rv); +} + +/* API message handler */ +static void + vl_api_cicn_api_face_events_subscribe_t_handler + (vl_api_cicn_api_face_events_subscribe_t * mp) +{ + cicn_main_t *sm = &cicn_main; + vl_api_cicn_api_face_events_subscribe_reply_t *rmp; + + int rv = VNET_API_ERROR_INVALID_ARGUMENT; + + u16 enable = clib_net_to_host_u16 (mp->enable_disable); + + if (enable == 1) + { + // if the maximum number of event subscribers is not exceeded yet + if (sm->n_face_event_subscribers < + CICN_PARAM_API_EVENT_SUBSCRIBERS_MAX - 1) + { + // save the info about the event subscriber + memcpy (&(sm->face_event_subscribers[sm->n_face_event_subscribers]), + mp, sizeof (*mp)); + + sm->n_face_event_subscribers++; + + rv = CICN_VNET_API_ERROR_NONE; + } + } + else if (enable == 0) + { + rv = VNET_API_ERROR_UNSPECIFIED; + + // find the event subscriber with matching client_index + int i; + for (i = 0; i < sm->n_face_event_subscribers; i++) + { + if (mp->client_index == sm->face_event_subscribers[i].client_index) + { + // shift left the remaining items + int j; + for (j = i; j < sm->n_face_event_subscribers; j++) + { + memcpy (&(sm->face_event_subscribers[j]), + &(sm->face_event_subscribers[j + 1]), sizeof (*mp)); + rv = CICN_VNET_API_ERROR_NONE; + } + sm->n_face_event_subscribers--; + break; + } + } + } + + REPLY_MACRO (VL_API_CICN_API_FACE_EVENTS_SUBSCRIBE_REPLY /*, rmp, mp, rv */ + ); +} + +static clib_error_t * +cicn_mgmt_face_add_cli (ip4_address_t local_addr4, int local_port, + ip4_address_t remote_addr4, int remote_port, + int app_face, int *faceid) +{ + int rv; + + rv = + cicn_mgmt_face_add (local_addr4, local_port, remote_addr4, remote_port, + app_face, faceid); + + switch (rv) + { + case 0: + break; + case VNET_API_ERROR_NO_SUCH_ENTRY: + return (clib_error_return (0, "No matching interface")); + break; + + case VNET_API_ERROR_INVALID_SW_IF_INDEX: + return + clib_error_return (0, + "Invalid interface, only works on physical ports"); + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, + "Device driver doesn't support redirection"); + break; + + default: + return clib_error_return (0, "cicn_cfg_face returned %d", rv); + } + + // send event in different places for cli, api: see api case + cicn_api_face_event_send (*faceid, CICN_FACE_FLAGS_DEFAULT); + return 0; +} + +/* + * cli handler for 'cfg face local remote ' + */ +static clib_error_t * +cicn_cli_face_set_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err = 0; + cicn_main_t *sm = &cicn_main; + cicn_face_db_entry_t *face_entry = NULL; + ip4_address_t local_addr4, remote_addr4; + int local_port = 0, remote_port = 0; + int faceid = -1; + cicn_mgmt_face_op_e face_op = CICN_MGMT_FACE_OP_NONE; + const char *cfg_admin_str = NULL; + int cfg_admin_up = 0; + const char *cfg_hello_str = NULL; + int cfg_hello_enable = 0; + int app_face = 0; + int ret; + + local_addr4.as_u32 = 0; + remote_addr4.as_u32 = 0; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "id %d", &faceid)) + { + if (unformat (line_input, "delete")) + { + /* TODO -- handle delete case... */ + face_op = CICN_MGMT_FACE_OP_DELETE; + } + else if (unformat (line_input, "admin %s", &cfg_admin_str)) + { + face_op = CICN_MGMT_FACE_OP_ADMIN; + if (strcmp (cfg_admin_str, "up") == 0) + { + cfg_admin_up = 1; + } + else if (strcmp (cfg_admin_str, "down") == 0) + { + cfg_admin_up = 0; + } + else + { + return (clib_error_return + (0, "Unknown face state %s", cfg_admin_str)); + } + } + else if (unformat (line_input, "hello %s", &cfg_hello_str)) + { + face_op = CICN_MGMT_FACE_OP_HELLO; + if (strcmp (cfg_hello_str, "enable") == 0) + { + cfg_hello_enable = 1; + } + else if (strcmp (cfg_hello_str, "disable") == 0) + { + cfg_hello_enable = 0; + } + else + { + return (clib_error_return + (0, "Unknown hello option (%s)", cfg_hello_str)); + } + } + else + { + return clib_error_return (0, "Please specify face operation"); + } + } + else if (unformat (line_input, "add")) + { + face_op = CICN_MGMT_FACE_OP_CREATE; + if (unformat (line_input, "local %U:%d", + unformat_ip4_address, &local_addr4, &local_port)) + { + if (unformat (line_input, "remote %U:%d", + unformat_ip4_address, &remote_addr4, + &remote_port)) + { + if (unformat (line_input, "app_face")) + { + app_face = 1; + } + } + } + } + else + { + return clib_error_return (0, "Unknown input '%U'", + format_unformat_error, line_input); + break; + } + } + + if (faceid != -1) + { + ret = cicn_face_entry_find_by_id (faceid, &face_entry); + if (ret != AOK) + { + return clib_error_return (0, "faceid %d not valid", faceid); + } + } + + switch (face_op) + { + case CICN_MGMT_FACE_OP_CREATE: + /* Check for presence of local address/port */ + if ((local_addr4.as_u32 == 0) || (local_port == 0)) + { + return clib_error_return (0, "local address/port not specified"); + } + + /* Check for presence of remote address/port */ + if ((remote_addr4.as_u32 == 0) || (remote_port == 0)) + { + return clib_error_return (0, "remote address/port not specified"); + } + cl_err = + cicn_mgmt_face_add_cli (local_addr4, local_port, remote_addr4, + remote_port, app_face, &faceid); + if (cl_err == 0) + { + vlib_cli_output (sm->vlib_main, "Face ID: %d", faceid); + } + else + { + vlib_cli_output (sm->vlib_main, "Face add failed"); + } + break; + case CICN_MGMT_FACE_OP_DELETE: + cl_err = cicn_mgmt_face_remove (faceid); + break; + case CICN_MGMT_FACE_OP_ADMIN: + cicn_face_flags_update (face_entry, !cfg_admin_up, + CICN_FACE_FLAG_ADMIN_DOWN); + break; + case CICN_MGMT_FACE_OP_HELLO: + cl_err = cicn_hello_adj_update (faceid, cfg_hello_enable); + break; + default: + return clib_error_return (0, "Operation (%d) not implemented", face_op); + break; + } + return cl_err; +} + + +/* API message handler */ +static void +vl_api_cicn_api_fib_entry_nh_add_t_handler (vl_api_cicn_api_fib_entry_nh_add_t + * mp) +{ + vl_api_cicn_api_fib_entry_nh_add_reply_t *rmp; + vnet_api_error_t rv = CICN_VNET_API_ERROR_NONE; + + cicn_main_t *sm = &cicn_main; + + const char *prefix = (const char *) (&mp->prefix); + int faceid = clib_net_to_host_i32 (mp->faceid); + int weight = clib_net_to_host_i32 (mp->weight); + + if ((prefix == NULL) || (strlen (prefix) <= 0) + || (strlen (prefix) > CICN_PARAM_FIB_ENTRY_PFX_WF_BYTES_MAX) || + (faceid <= 0)) + { + rv = VNET_API_ERROR_INVALID_ARGUMENT; + } + + if (weight == CICN_API_FIB_ENTRY_NHOP_WGHT_UNSET) + { + weight = CICN_PARAM_FIB_ENTRY_NHOP_WGHT_DFLT; + } + if ((weight < 0) || (weight > CICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX)) + { + rv = VNET_API_ERROR_INVALID_ARGUMENT; + } + + if (rv == CICN_VNET_API_ERROR_NONE) + { + int ux_rc; + cicn_rd_t cicn_rd; + ux_rc = cicn_fib_entry_nh_update (prefix, faceid, weight, 1 /*add */ , + &cicn_rd); + if (ux_rc == AOK) + { + CICN_INFRA_CFG_GEN_INCR (); + } + switch (cicn_rd.rd_cicn_rc) + { + case CICN_RC_OK: + rv = cicn_api_rv_from_unix_rc (ux_rc); + break; + case CICN_RC_FIB_PFX_COMP_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_1; + break; + case CICN_RC_FIB_PFX_SIZE_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_2; + break; + case CICN_RC_FIB_NHOP_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_3; + break; + case CICN_RC_FACE_UNKNOWN: + rv = VNET_API_ERROR_SYSCALL_ERROR_4; + break; + default: + rv = VNET_API_ERROR_SYSCALL_ERROR_10; // should not happen + break; + } + } + + REPLY_MACRO (VL_API_CICN_API_FIB_ENTRY_NH_ADD_REPLY /*, rmp, mp, rv */ ); +} + +/* API message handler */ +static void + vl_api_cicn_api_fib_entry_nh_delete_t_handler + (vl_api_cicn_api_fib_entry_nh_delete_t * mp) +{ + vl_api_cicn_api_fib_entry_nh_delete_reply_t *rmp; + vnet_api_error_t rv = CICN_VNET_API_ERROR_NONE; + + cicn_main_t *sm = &cicn_main; + + const char *prefix = (const char *) (&mp->prefix); + int faceid = clib_net_to_host_i32 (mp->faceid); + + if ((prefix == NULL) || (strlen (prefix) <= 0) || + (strlen (prefix) > CICN_PARAM_FIB_ENTRY_PFX_WF_BYTES_MAX)) + { + rv = VNET_API_ERROR_INVALID_ARGUMENT; + } + + if (rv == CICN_VNET_API_ERROR_NONE) + { + int ux_rc; + cicn_rd_t cicn_rd; + ux_rc = + cicn_fib_entry_nh_update (prefix, faceid, 0 /*dummy */ , 0 /*!add */ , + &cicn_rd); + if (rv == 0) + { + CICN_INFRA_CFG_GEN_INCR (); + } + switch (cicn_rd.rd_cicn_rc) + { + case CICN_RC_OK: + rv = cicn_api_rv_from_unix_rc (ux_rc); + break; + case CICN_RC_FIB_PFX_COMP_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_1; + break; + case CICN_RC_FIB_PFX_SIZE_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_2; + break; + case CICN_RC_FIB_NHOP_LIMIT: + rv = VNET_API_ERROR_SYSCALL_ERROR_3; + break; + case CICN_RC_FACE_UNKNOWN: + rv = VNET_API_ERROR_SYSCALL_ERROR_4; + break; + default: + rv = VNET_API_ERROR_SYSCALL_ERROR_10; // should not happen + break; + } + } + + REPLY_MACRO (VL_API_CICN_API_FIB_ENTRY_NH_DELETE_REPLY /*, rmp, mp, rv */ ); +} + +/* API message handler */ +static void + vl_api_cicn_api_fib_entry_props_get_t_handler + (vl_api_cicn_api_fib_entry_props_get_t * mp) +{ + vl_api_cicn_api_fib_entry_props_get_reply_t *rmp; + vnet_api_error_t rv; + unix_shared_memory_queue_t *q; + + cicn_main_t *sm = &cicn_main; + + REPLY_SETUP (VL_API_CICN_API_FIB_ENTRY_PROPS_GET_REPLY, rmp, q, mp); + + rv = + cicn_fib_api_entry_props_serialize (rmp, + clib_net_to_host_i32 (mp->pagenum)); + + REPLY_FINISH (rmp, q, rv); +} + +/* + * cli handler for 'cfg fib' + */ +static clib_error_t * +cicn_cli_fib_set_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err = 0; + + int ux_rc; + cicn_rd_t cicn_rd; + + int addpfx = -1; + const char *prefix = NULL; + int faceid = 0; + int weight = CICN_PARAM_FIB_ENTRY_NHOP_WGHT_DFLT; + + /* TODO -- support next-hop weights */ + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (addpfx == -1 && unformat (line_input, "add")) + { + addpfx = 1; + } + else if (addpfx == -1 && unformat (line_input, "delete")) + { + addpfx = 0; + } + else if (addpfx != -1 && unformat (line_input, "prefix %s", &prefix)) + { + ; + } + else if (addpfx != -1 && unformat (line_input, "face %d", &faceid)) + { + ; + } + else if (addpfx == 1 && unformat (line_input, "weight %d", &weight)) + { + ; + } + else + { + return clib_error_return (0, "Unknown input '%U'", + format_unformat_error, line_input); + } + } + + /* Check parse */ + if ((prefix == NULL) || (addpfx > 0 && faceid == 0)) + { + return clib_error_return (0, "Please specify prefix and faceid..."); + } + + if (addpfx && + ((weight < 0) || (weight > CICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX))) + { + return clib_error_return (0, + "Next-hop weight must be between 0 and %d", + (int) CICN_PARAM_FIB_ENTRY_NHOP_WGHT_MAX); + } + + ux_rc = cicn_fib_entry_nh_update (prefix, faceid, weight, addpfx, &cicn_rd); + if (ux_rc == AOK) + { + CICN_INFRA_CFG_GEN_INCR (); + } + else + { + const char *subcode_str = cicn_rd_str (&cicn_rd); + cl_err = + clib_error_return (0, "Unable to modify fib: %s (%d)", subcode_str, + ux_rc); + } + + return (cl_err); +} + +/* + * cli handler for 'cicn hello' + */ +static clib_error_t * +cicn_cli_hello_protocol_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err = 0; + + cicn_main_t *sm = &cicn_main; + int interval = -1; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "interval %d", &interval)) + { + ; + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + + /* Check that hello protocol interval > 0 */ + if (interval > 0) + { + sm->cicn_hello_interval = interval / 1000.0; + sm->cicn_hello_interval_cfgd = 1; + vlib_cli_output (vm, "Hello protocol interval was set successfully", + sm->cicn_hello_interval); + } + else + { + cl_err = + clib_error_return (0, + "cicn: the hello protocol time interval must be positive"); + } + + return (cl_err); +} + +/* + * cli handler for 'cicn show' + */ +static clib_error_t * +cicn_cli_show_command_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + int i, face_p = 0, fib_p = 0, all_p, detail_p = 0, internal_p = 0; + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + /* TODO -- support specific args */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "face all")) + { + face_p = 1; + } + else if (unformat (line_input, "fib all")) + { + fib_p = 1; + } + else if (unformat (line_input, "detail")) + { + detail_p = 1; + } + else if (unformat (line_input, "internal")) + { + /* We consider 'internal' a superset, so include 'detail' too */ + internal_p = 1; + detail_p = 1; + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + + /* If nothing specified, show everything */ + if ((face_p == 0) && (fib_p == 0)) + { + all_p = 1; + } + + if (!cicn_main.is_enabled) + { + if (node_ctl_params.fib_max_size == -1 && + node_ctl_params.pit_max_size == -1 && + node_ctl_params.pit_dflt_lifetime_sec == -1 && + node_ctl_params.pit_min_lifetime_sec == -1 && + node_ctl_params.pit_max_lifetime_sec == -1 && + node_ctl_params.cs_max_size == -1) + { + cicn_cli_output ("cicn: not enabled"); + goto done; + } + vlib_cli_output (vm, "Forwarder %s: %sabled\nPreconfiguration:\n", + cicn_infra_fwdr_name.fn_str, + cicn_main.is_enabled ? "en" : "dis"); + + if (node_ctl_params.fib_max_size != -1) + { + vlib_cli_output (vm, " FIB:: max entries:%d\n,", + node_ctl_params.fib_max_size); + } + if (node_ctl_params.pit_max_size != -1) + { + vlib_cli_output (vm, " PIT:: max entries:%d\n", + node_ctl_params.pit_max_size); + } + if (node_ctl_params.pit_dflt_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: dflt lifetime: %05.3f seconds\n", + node_ctl_params.pit_dflt_lifetime_sec); + } + if (node_ctl_params.pit_min_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: min lifetime: %05.3f seconds\n", + node_ctl_params.pit_min_lifetime_sec); + } + if (node_ctl_params.pit_max_lifetime_sec != -1) + { + vlib_cli_output (vm, " PIT:: max lifetime: %05.3f seconds\n", + node_ctl_params.pit_max_lifetime_sec); + } + if (node_ctl_params.cs_max_size != -1) + { + vlib_cli_output (vm, " CS:: max entries:%d\n", + node_ctl_params.cs_max_size); + } + + goto done; + } + + /* Globals */ + vlib_cli_output (vm, + "Forwarder %s: %sabled\n" + " FIB:: max entries:%d\n" + " PIT:: max entries:%d," + " lifetime default: %05.3f sec (min:%05.3f, max:%05.3f)\n" + " CS:: max entries:%d\n", + cicn_infra_fwdr_name.fn_str, + cicn_main.is_enabled ? "en" : "dis", + cicn_main.fib.fib_capacity, + cicn_infra_shard_pit_size * cicn_main.shard_count, + ((f64) cicn_main.pit_lifetime_dflt_ms) / SEC_MS, + ((f64) cicn_main.pit_lifetime_min_ms) / SEC_MS, + ((f64) cicn_main.pit_lifetime_max_ms) / SEC_MS, + cicn_infra_shard_cs_size * cicn_main.shard_count); + + vl_api_cicn_api_node_stats_get_reply_t rm = { 0, } + , *rmp = &rm; + if (cicn_mgmt_node_stats_get (&rm) == AOK) + { + vlib_cli_output (vm, //compare vl_api_cicn_api_node_stats_get_reply_t_handler block + " PIT entries (now): %d\n" + " CS entries (now): %d\n" + " Forwarding statistics:" + " pkts_processed: %d\n" + " pkts_interest_count: %d\n" + " pkts_data_count: %d\n" + " pkts_nak_count: %d\n" + " pkts_from_cache_count: %d\n" + " pkts_nacked_interests_count: %d\n" + " pkts_nak_hoplimit_count: %d\n" + " pkts_nak_no_route_count: %d\n" + " pkts_no_pit_count: %d\n" + " pit_expired_count: %d\n" + " cs_expired_count: %d\n" + " cs_lru_count: %d\n" + " pkts_drop_no_buf: %d\n" + " interests_aggregated: %d\n" + " interests_retransmitted: %d\n", + clib_net_to_host_u64 (rmp->pit_entries_count), + clib_net_to_host_u64 (rmp->cs_entries_count), + clib_net_to_host_u64 (rmp->pkts_processed), + clib_net_to_host_u64 (rmp->pkts_interest_count), + clib_net_to_host_u64 (rmp->pkts_data_count), + clib_net_to_host_u64 (rmp->pkts_nak_count), + clib_net_to_host_u64 (rmp->pkts_from_cache_count), + clib_net_to_host_u64 + (rmp->pkts_nacked_interests_count), + clib_net_to_host_u64 (rmp->pkts_nak_hoplimit_count), + clib_net_to_host_u64 (rmp->pkts_nak_no_route_count), + clib_net_to_host_u64 (rmp->pkts_no_pit_count), + clib_net_to_host_u64 (rmp->pit_expired_count), + clib_net_to_host_u64 (rmp->cs_expired_count), + clib_net_to_host_u64 (rmp->cs_lru_count), + clib_net_to_host_u64 (rmp->pkts_drop_no_buf), + clib_net_to_host_u64 (rmp->interests_aggregated), + clib_net_to_host_u64 (rmp->interests_retx)); + } + + if (internal_p) + { + vlib_cli_output (vm, "cicn: config gen %" PRIu64 "", + cicn_infra_gshard.cfg_generation); + + for (i = 0; i <= cicn_main.worker_count; i++) + { + vlib_cli_output (vm, "cicn: worker [%d] gen %" PRIu64 "", + i, cicn_infra_shards[i].cfg_generation); + } + } + + /* TODO -- Just show all faces */ + if (face_p || all_p) + { + cicn_face_show (-1, detail_p, internal_p); + } + + /* TODO -- just show fib */ + if (fib_p || all_p) + { + cicn_fib_show (NULL, detail_p, internal_p); + } + +done: + if (all_p && internal_p) + { + vlib_cli_output (vm, + " Features: multithreading:%d, cs:%d, dpdk-cloning:%d, " + "vlib-cloning:%d\n", + CICN_FEATURE_MULTITHREAD, + CICN_FEATURE_CS, + CICN_FEATURE_DPDK_RTEMBUF_CLONING, + CICN_FEATURE_VPP_VLIB_CLONING); + } + return (0); +} + +/* + * cli handler for 'pgen' + */ +static clib_error_t * +cicn_cli_pgen_client_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + cicn_main_t *sm = &cicn_main; + ip4_address_t src_addr, dest_addr; + int local_port = 0, src_port = 0, dest_port = 0; + int rv = VNET_API_ERROR_UNIMPLEMENTED; + + if (sm->is_enabled) + { + /* That's no good - you only get one or the other */ + return (clib_error_return (0, "Already enabled for forwarding")); + } + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "port %d", &local_port)) + { + ; + } + else if (unformat (line_input, "dest %U:%d", + unformat_ip4_address, &dest_addr, &dest_port)) + { + ; + } + else if (unformat (line_input, "src %U:%d", + unformat_ip4_address, &src_addr, &src_port)) + { + ; + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + + /* Attach our packet-gen node for ip4 udp local traffic */ + if ((local_port == 0) || (dest_port == 0) || (src_port == 0)) + { + return clib_error_return (0, + "Error: must supply local port and rewrite address info"); + } + + udp_register_dst_port (sm->vlib_main, local_port, icn_pg_node.index, 1); + + sm->pgen_clt_src_addr = src_addr.as_u32; + sm->pgen_clt_src_port = htons ((uint16_t) src_port); + sm->pgen_clt_dest_addr = dest_addr.as_u32; + sm->pgen_clt_dest_port = htons ((uint16_t) dest_port); + + sm->pgen_enabled = 1; + rv = 0; + + switch (rv) + { + case 0: + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + return clib_error_return (0, "Unimplemented, NYI"); + break; + + default: + return clib_error_return (0, "cicn enable_disable returned %d", rv); + } + + return 0; +} + +/* + * cli handler for 'pgen' + */ +static clib_error_t * +cicn_cli_pgen_server_set_command_fn (vlib_main_t * vm, + unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err; + int rv = CICN_VNET_API_ERROR_NONE; + + cicn_main_t *sm = &cicn_main; + int local_port = 0; + int payload_size = 0; + + if (sm->is_enabled) + { + /* That's no good - you only get one or the other */ + return (clib_error_return (0, "Already enabled for forwarding")); + } + + /* Get a line of input. */ + unformat_input_t _line_input, *line_input = &_line_input; + if (!unformat_user (main_input, unformat_line_input, line_input)) + { + return (0); + } + + /* Parse the arguments */ + while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) + { + if (unformat (line_input, "port %d", &local_port)) + { + ; + } + else if (unformat (line_input, "size %d", &payload_size)) + { + if (payload_size > 1200) + { + return (clib_error_return (0, + "Payload size must be <= 1200 bytes...")); + } + } + else + { + return (clib_error_return + (0, "Unknown input '%U'", format_unformat_error, + line_input)); + break; + } + } + + /* Attach our packet-gen node for ip4 udp local traffic */ + if (local_port == 0 || payload_size == 0) + { + return clib_error_return (0, + "Error: must supply local port and payload size"); + } + + /* Allocate the buffer with the actual content payload TLV */ + vlib_buffer_alloc (vm, &sm->pgen_svr_buffer_idx, 1); + vlib_buffer_t *rb = NULL; + rb = vlib_get_buffer (vm, sm->pgen_svr_buffer_idx); + + /* Initialize the buffer data with zeros */ + memset (rb->data, 0, payload_size); + C_PUTINT16 (rb->data, CICN_TLV_PAYLOAD); + C_PUTINT16 (rb->data + 2, payload_size - 4); + rb->current_length = payload_size; + + /* Register the UDP port of the server */ + udp_register_dst_port (sm->vlib_main, local_port, + icn_pg_server_node.index, 1); + + sm->pgen_svr_enabled = 1; + + switch (rv) + { + case 0: + cl_err = 0; + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + cl_err = clib_error_return (0, "Unimplemented, NYI"); + break; + + default: + cl_err = clib_error_return (0, "cicn pgen server returned %d", rv); + } + + return cl_err; +} + +/* API message handler */ +static void +vl_api_cicn_api_test_run_get_t_handler (vl_api_cicn_api_test_run_get_t * mp) +{ + vl_api_cicn_api_test_run_get_reply_t *rmp; + cicn_main_t *sm = &cicn_main; + vnet_api_error_t vaec = CICN_VNET_API_ERROR_NONE; + + 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 (sm->msg_id_base + VL_API_CICN_API_TEST_RUN_GET_REPLY); + rmp->context = mp->context; + if (sm->test_cicn_api_handler == NULL) + { + vaec = VNET_API_ERROR_UNIMPLEMENTED; + } + else + { + test_cicn_api_op_t test_cicn_api_op = {.reply = rmp }; + vaec = (sm->test_cicn_api_handler) (&test_cicn_api_op); + } + + rmp->retval = clib_host_to_net_i32 (vaec); + + vl_msg_api_send_shmem (q, (u8 *) & rmp); +} + +static void +cicn_cli_test_results_output (vl_api_cicn_api_test_run_get_reply_t * rmp) +{ + u8 *strbuf = NULL; + i32 nentries = clib_net_to_host_i32 (rmp->nentries); + cicn_api_test_suite_results_t *suites = + (cicn_api_test_suite_results_t *) & rmp->suites[0]; + + int i; + for (i = 0; i < nentries; i++) + { + cicn_api_test_suite_results_t *suite = &suites[i]; + int ntests = clib_net_to_host_i32 (suite->ntests); + int nsuccesses = clib_net_to_host_i32 (suite->nsuccesses); + int nfailures = clib_net_to_host_i32 (suite->nfailures); + int nskipped = clib_net_to_host_i32 (suite->nskipped); + int j, cnt; + + strbuf = format (strbuf, + "Suite %s: %d tests: %d successes, %d failures, %d skipped\n", + suite->suitename, ntests, nsuccesses, nfailures, + nskipped); + + if (nfailures != 0) + { + strbuf = format (strbuf, " Failed Test(s):"); + for (j = 0, cnt = 0; j < 8 * sizeof (suite->failures_mask); j++) + { + if ((suite->failures_mask[j / 8] & (1 << (j % 8))) == 0) + { + continue; + } + cnt++; + strbuf = + format (strbuf, " %d%s", j + 1, + (cnt < nfailures) ? ", " : " "); + } + strbuf = format (strbuf, "\n"); + } + if (nskipped != 0) + { + strbuf = format (strbuf, " Skipped Test(s):"); + for (j = 0, cnt = 0; j < 8 * sizeof (suite->skips_mask); j++) + { + if ((suite->skips_mask[j / 8] & (1 << (j % 8))) == 0) + { + continue; + } + cnt++; + strbuf = + format (strbuf, " %d%s", j + 1, + (cnt < nskipped) ? ", " : " "); + } + strbuf = format (strbuf, "\n"); + } + } + + vec_terminate_c_string (strbuf); + vlib_cli_output (cicn_main.vlib_main, "%s", strbuf); + if (strbuf) + { + vec_free (strbuf); + } +} + +static clib_error_t * +cicn_cli_test_fn (vlib_main_t * vm, unformat_input_t * main_input, + vlib_cli_command_t * cmd) +{ + clib_error_t *cl_err; + int rv; + + cicn_main_t *sm = &cicn_main; + + if (sm->test_cicn_api_handler == NULL) + { + rv = VNET_API_ERROR_UNIMPLEMENTED; + } + else + { + // leverage API message for CLI + vl_api_cicn_api_test_run_get_reply_t rmp = { 0, }; + test_cicn_api_op_t test_cicn_api_op = {.reply = &rmp }; + rv = (sm->test_cicn_api_handler) (&test_cicn_api_op); + cicn_cli_test_results_output (test_cicn_api_op.reply); + } + + switch (rv) + { + case 0: + cl_err = 0; + break; + + case VNET_API_ERROR_UNIMPLEMENTED: + cl_err = + clib_error_return (0, "Unimplemented, test modules not linked"); + break; + + default: + cl_err = clib_error_return (0, "cicn pgen server returned %d", rv); + } + + return cl_err; +} + + + +/* List of message types that this plugin understands */ + +#define foreach_cicn_plugin_api_msg \ +_(CICN_API_NODE_PARAMS_SET, cicn_api_node_params_set) \ +_(CICN_API_NODE_PARAMS_GET, cicn_api_node_params_get) \ +_(CICN_API_NODE_STATS_GET, cicn_api_node_stats_get) \ +_(CICN_API_FACE_ADD, cicn_api_face_add) \ +_(CICN_API_FACE_DELETE, cicn_api_face_delete) \ +_(CICN_API_FACE_PARAMS_GET, cicn_api_face_params_get) \ +_(CICN_API_FACE_PROPS_GET, cicn_api_face_props_get) \ +_(CICN_API_FACE_STATS_GET, cicn_api_face_stats_get) \ +_(CICN_API_FACE_EVENTS_SUBSCRIBE, cicn_api_face_events_subscribe) \ +_(CICN_API_FIB_ENTRY_NH_ADD, cicn_api_fib_entry_nh_add) \ +_(CICN_API_FIB_ENTRY_NH_DELETE, cicn_api_fib_entry_nh_delete) \ +_(CICN_API_FIB_ENTRY_PROPS_GET, cicn_api_fib_entry_props_get) \ +_(CICN_API_TEST_RUN_GET, cicn_api_test_run_get) + +/* Set up the API message handling tables */ +clib_error_t * +cicn_api_plugin_hookup (vlib_main_t * vm) +{ + cicn_main_t *sm = &cicn_main; + + /* Get a correctly-sized block of API message decode slots */ + u8 *name = format (0, "cicn_%08x%c", api_version, 0); + sm->msg_id_base = vl_msg_api_get_msg_ids ((char *) name, + VL_MSG_FIRST_AVAILABLE); + vec_free (name); + +#define _(N,n) \ + vl_msg_api_set_handlers(sm->msg_id_base + VL_API_##N, \ + #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_cicn_plugin_api_msg; +#undef _ + + int smart_fib_update = 0; +#if CICN_FEATURE_MULTITHREAD // smart fib update believed working, not tested + smart_fib_update = 1; +#endif // CICN_FEATURE_MULTITHREAD + /* + * Thread-safe API messages + * i.e. disable thread synchronization + */ + api_main_t *am = &api_main; + if (smart_fib_update) + { + am->is_mp_safe[sm->msg_id_base + VL_API_CICN_API_FIB_ENTRY_NH_ADD] = 1; + am->is_mp_safe[sm->msg_id_base + VL_API_CICN_API_FIB_ENTRY_NH_DELETE] = + 1; + } + + return 0; +} + + +/* cli declaration for 'control' (root path of multiple commands, for help) */ +VLIB_CLI_COMMAND (cicn_cli_node_ctl_command, static) = +{ +.path = "cicn control",.short_help = "cicn control"}; + +/* cli declaration for 'control start' */ +VLIB_CLI_COMMAND (cicn_cli_node_ctl_start_set_command, static) = +{ +.path = "cicn control start",.short_help = "cicn control start",.function = + cicn_cli_node_ctl_start_set_command_fn,}; + +/* cli declaration for 'control stop' */ +VLIB_CLI_COMMAND (cicn_cli_node_ctl_stop_set_command, static) = +{ +.path = "cicn control stop",.short_help = "cicn control stop",.function = + cicn_cli_node_ctl_stop_set_command_fn,}; + +/* cli declaration for 'control param' */ +VLIB_CLI_COMMAND (cicn_cli_node_ctl_param_set_command, static) = +{ +.path = "cicn control param",.short_help = + "cicn control param { pit { size | { dfltlife | minlife | maxlife } } | fib size | cs size }\n",.function + = cicn_cli_node_ctl_param_set_command_fn,}; + +/* cli declaration for 'enable-disable'*/ +VLIB_CLI_COMMAND (cicn_cli_node_enable_disable_set_command, static) = +{ +.path = "cicn enable-disable",.short_help = + "cicn enable-disable [disable]",.function = + cicn_cli_node_enable_disable_set_command_fn,}; + +/* cli declaration for 'cfg' */ +VLIB_CLI_COMMAND (cicn_cli_set_command, static) = +{ +.path = "cicn cfg",.short_help = "cicn cfg",}; + +/* cli declaration for 'cfg name' */ +VLIB_CLI_COMMAND (cicn_cli_node_name_set_command, static) = +{ +.path = "cicn cfg name",.short_help = + "cicn cfg name [delete]",.function = + cicn_cli_node_name_set_command_fn,.long_help = + "Add (or remove) an administrative name for this router\n" "\n" + "Multiple names are allowed. (NYI...)\n",}; + +/* cli declaration for 'cfg salt' */ +VLIB_CLI_COMMAND (cicn_cli_salt_set_command, static) = +{ +.path = "cicn cfg salt",.short_help = "cicn cfg salt ",.function = + cicn_cli_salt_set_command_fn,}; + +/* cli declaration for 'cfg face' */ +VLIB_CLI_COMMAND (cicn_cli_face_set_command, static) = +{ +.path = "cicn cfg face",.short_help = + "cicn cfg face { add local remote | " + "id { delete | admin { down | up } | hello { enable | disable } }",.function + = cicn_cli_face_set_command_fn,}; + +/* cli declaration for 'cfg fib' */ +VLIB_CLI_COMMAND (cicn_cli_fib_set_command, static) = +{ +.path = "cicn cfg fib",.short_help = + "cicn cfg fib {add | delete } prefix face " + "[weight ]",.function = cicn_cli_fib_set_command_fn,}; + +/* cli declaration for 'cfg hello-protocol' */ +VLIB_CLI_COMMAND (cicn_cli_hello_protocol_set_command, static) = +{ +.path = "cicn cfg hello-protocol",.short_help = + "cicn cfg hello-protocol interval ",.function = + cicn_cli_hello_protocol_set_command_fn,}; + +/* cli declaration for 'show' */ +VLIB_CLI_COMMAND (cicn_cli_show_command, static) = +{ +.path = "cicn show",.short_help = + "cicn show [face ['all' | faceid]] " + "[fib ['all' | prefix]] " + "[detail] [internal]",.function = cicn_cli_show_command_fn,}; + +/* cli declaration for 'cicn pgen client' */ +VLIB_CLI_COMMAND (cicn_cli_pgen_client_set_command, static) = +{ +.path = "cicn pgen client",.short_help = + "cicn pgen client port src dest ",.long_help + = "Run icn in packet-gen client mode\n",.function = + cicn_cli_pgen_client_set_command_fn,}; + +/* cli declaration for 'cicn pgen server' */ +VLIB_CLI_COMMAND (cicn_cli_pgen_server_set_command, static) = +{ +.path = "cicn pgen server",.short_help = + "cicn pgen server port size ",.long_help = + "Run icn in packet-gen server mode\n",.function = + cicn_cli_pgen_server_set_command_fn,}; + +/* cli declaration for 'test' */ +VLIB_CLI_COMMAND (cicn_cli_test_command, static) = +{ +.path = "cicn test",.short_help = "cicn test",.function = cicn_cli_test_fn,}; -- cgit 1.2.3-korg