diff options
author | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:06:34 +0200 |
---|---|---|
committer | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:31:51 +0200 |
commit | 6d22a0db96aa7f8e3102ae44d00c09e36a2e9c57 (patch) | |
tree | 79546bbf09f6fbf74db7bc89117843f06ce937ea /ctrl/libhicnctrl/src/parse.c | |
parent | 012843b1c0bc0838e69085ed83a79ec8b6f97360 (diff) |
feat: Due to the deep modifications related to names and packet format,
this task cover a large part of the codebase and involves several changes:
- the library provides a name data structure (hicn_name_t ), which is composed
of a name prefix (hicn_name_prefix_t) and a name suffix (hicn_name_suffix_t),
and it has been extended to provide all support functions required for name
manipulation, including common prefix computation, as required for the Longest
Prefix Match (LPM)in the forwarder, in addition to Exact Prefix Match (EPM).
- all code has been rewritten to use this data structure instead of having for
instance the forwarder define its own name class (used to be Name and NameBitVector)
the code has been refactored to minimize name allocations and copies, one remaining
aspect is the difference of name storage between PIT and CS entries (respectively
in the PIT entry, and in the message buffer), which causes the packet cache
index to be updated when a PIT entry is converted into a CS entry. By storing
the name in the PIT/CS entry everytime, we might save on this operation).
- hicn-light FIB has been rewritten : code has been refactored and should now be
shorter and documented; unit tests have been drafted but more would be required
to cover all cases and match the algorithms to add/remove nodes, as specified in the doc.
all protocol details and hICN header formats are now abstracted by the library
for the forwarder (and thus header.h and protocols/*.h have been removed from
public includes, and replaced by packet.h providing protocol agnostic packet
level functions, completely replacing the compat.h header that used to provide
similar functions.
- this works by exposing a opaque buffer to the application (a kind of socket buffer)
which is used by the lib to cache the packet format and offsets of the different
layers in the buffer and provider efficient operations (the packet format is
either defined for packet construction, or guessed at ingress, and this structure
is updated accordingly only once).
Co-authored-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Change-Id: I31e321897f85f0267fe8ba4720363c180564492f
Diffstat (limited to 'ctrl/libhicnctrl/src/parse.c')
-rw-r--r-- | ctrl/libhicnctrl/src/parse.c | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/ctrl/libhicnctrl/src/parse.c b/ctrl/libhicnctrl/src/parse.c new file mode 100644 index 000000000..ca19d8280 --- /dev/null +++ b/ctrl/libhicnctrl/src/parse.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2022 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 <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> + +//#include <hicn/ctrl/cli.h> +#include <hicn/ctrl/action.h> +#include <hicn/util/log.h> +#include <hicn/util/sstrncpy.h> +#include <hicn/ctrl/parse.h> + +/* + * As there is no portable way to generate a va_list to use with sscanf to + * support a variable number of arguments, and no way to use a variable array + * initialize in a nested struct, we use a fixed maximum number of parameters + * + * NOTE: update sscanf accordingly + */ + +//#include "command.h" + +const char *action_to_cmd_action(hc_action_t action) { + switch (action) { + case ACTION_CREATE: + return "add"; + case ACTION_UPDATE: + return "update"; + case ACTION_DELETE: + return "remove"; + case ACTION_LIST: + return "list"; + case ACTION_SET: + return "set"; + case ACTION_SERVE: + return "serve"; + case ACTION_STORE: + return "store"; + case ACTION_CLEAR: + return "clear"; + default: + return "UNDEFINED"; + } +} + +/* + * This function now assumes all parameteres in src are received as string + */ +int parser_type_func(const parser_type_t *type, const char *src, void *dst, + void *dst2, void *dst3) { + hicn_ip_address_t addr; + char *addr_str; + char *len_str; + int len, rc; + long tmp; + + switch (type->name) { + case TYPENAME_INT: + tmp = strtol(src, NULL, 10); + if ((tmp < INT32_MIN) || (tmp > INT32_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + if (tmp > INT_MAX) return -1; + *(int *)dst = (int)tmp; + break; + case TYPENAME_UINT: + tmp = strtol(src, NULL, 10); + if ((tmp < 0) || (tmp > UINT32_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + if (tmp > UINT_MAX) return -1; + *(unsigned *)dst = (unsigned)tmp; + break; + case TYPENAME_INT16: + tmp = strtol(src, NULL, 10); + if ((tmp < INT16_MIN) || (tmp > INT16_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + *(int16_t *)dst = tmp; + break; + case TYPENAME_UINT16: + tmp = strtol(src, NULL, 10); + if ((tmp < 0) || (tmp > UINT16_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + *(uint16_t *)dst = tmp; + break; + case TYPENAME_STR: + rc = strcpy_s(dst, type->str.max_size, src); + if (rc != EOK) { + ERROR("Input string is too long"); + return -1; + } + break; + case TYPENAME_IP_ADDRESS: + rc = hicn_ip_address_pton((char *)src, &addr); + if (rc < 0) { + ERROR("Wrong IP address format"); + return -1; + } + + *(hicn_ip_address_t *)dst = addr; + *(int *)dst2 = hicn_ip_address_str_get_family((char *)src); + break; + case TYPENAME_ON_OFF: + if (strcmp((char *)src, "off") == 0) { + *(unsigned *)dst = 0; + } else if (strcmp((char *)src, "on") == 0) { + *(unsigned *)dst = 1; + } else { + ERROR("on/off are the only possible values"); + return -1; + } + break; + case TYPENAME_IP_PREFIX: + addr_str = strtok((char *)src, "/"); + len_str = strtok(NULL, " "); + rc = hicn_ip_address_pton((char *)src, &addr); + if (rc < 0) { + ERROR("Wrong IP address format"); + return -1; + } + len = atoi(len_str); + + *(hicn_ip_address_t *)dst = addr; + *(int *)dst2 = len; + *(int *)dst3 = hicn_ip_address_str_get_family(addr_str); + break; + case TYPENAME_ENUM: + /* Enum index from string */ + assert(type->enum_.from_str); + *(int *)dst = type->enum_.from_str((char *)src); + break; + case TYPENAME_POLICY_STATE: { + assert(IS_VALID_POLICY_TAG(type->policy_state.tag)); + policy_tag_t tag = type->policy_state.tag; + /* Format string is "%ms" */ + const char *str = *(const char **)src; + policy_tag_state_t *pts = ((policy_tag_state_t *)dst); + pts[tag].disabled = (str[0] == '!') ? 1 : 0; + pts[tag].state = policy_state_from_str(str + pts[tag].disabled); + break; + } + case TYPENAME_UNDEFINED: + default: + ERROR("Unknown format"); + return -1; + } + return 0; +} + +// NEW FUNCTION +int parse_getopt_args(const command_parser_t *parser, int argc, char *argv[], + hc_command_t *command) { + /* Loop over remaining commandline positional arguments */ + for (unsigned i = 0; i < argc; i++) { + const command_parameter_t *p = &parser->parameters[i]; + + if (parser_type_func(&p->type, argv[i], + &command->object.as_uint8 + p->offset, + &command->object.as_uint8 + p->offset2, + &command->object.as_uint8 + p->offset3) < 0) { + ERROR("Error during parsing of parameter '%s' value", p->name); + goto ERR; + } + } + if (parser->post_hook) parser->post_hook(&command->object.as_uint8); + return 0; + +ERR: + return -1; +} + +/* Build a format string to parse all params as a string */ +int parse_params(const command_parser_t *parser, const char *params_s, + hc_command_t *command) { + char fmt[1024]; + int n; + size_t size = 0; + char *pos = fmt; + /* Update MAX_PARAMETERS accordingly in command.h */ + char sscanf_params[MAX_PARAMETERS][MAX_SCANF_PARAM_LEN]; + + unsigned count = 0; + for (unsigned i = 0; i < parser->nparams; i++) { + const char *_fmt = "%s"; + n = snprintf(pos, 1024 - size, "%s", _fmt); + pos += n; + + *pos = ' '; + pos++; + + size += n + 1; + count++; + } + *pos = '\0'; + DEBUG("parser format: %s", fmt); + + int ret = sscanf(params_s, fmt, sscanf_params[0], sscanf_params[1], + sscanf_params[2], sscanf_params[3], sscanf_params[4], + sscanf_params[5], sscanf_params[6], sscanf_params[7], + sscanf_params[8], sscanf_params[9]); + if (ret != parser->nparams) { + ERROR("Parsing failed: check for string used where integer was expected"); + goto ERR; + } + + for (unsigned i = 0; i < count; i++) { + const command_parameter_t *p = &parser->parameters[i]; + if (parser_type_func(&p->type, sscanf_params[i], + &command->object.as_uint8 + p->offset, + &command->object.as_uint8 + p->offset2, + &command->object.as_uint8 + p->offset3) < 0) { + ERROR("Error during parsing of parameter '%s' value", p->name); + goto ERR; + } + } + return 0; + +ERR: + return -1; +} + +int parse(const char *cmd, hc_command_t *command) { + int nparams = 0; + char action_s[MAX_SCANF_PARAM_LEN]; + char object_s[MAX_SCANF_PARAM_LEN]; + char params_s[MAX_SCANF_PARAM_LEN]; + + // if n = 2 later, params_s is uninitialized + memset(params_s, 0, MAX_SCANF_PARAM_LEN * sizeof(char)); + + errno = 0; + int n = sscanf(cmd, "%s %s%[^\n]s", action_s, object_s, params_s); + if ((n < 2) || (n > 3)) { + if (errno != 0) perror("scanf"); + return -1; + } + + command->action = action_from_str(action_s); + command->object_type = object_type_from_str(object_s); + + if (strnlen_s(params_s, MAX_SCANF_PARAM_LEN) > 0) { + for (char *ptr = params_s; (ptr = strchr(ptr, ' ')) != NULL; ptr++) + nparams++; + } + + /* + * This checks is important even with 0 parameters as it checks whether the + * command exists. + */ + const command_parser_t *parser = + command_search(command->action, command->object_type, nparams); + if (!parser) { + ERROR("Could not find parser for command '%s %s'", action_s, object_s); + return -1; + } + + if (strnlen_s(params_s, MAX_SCANF_PARAM_LEN) > 0) { + if (parse_params(parser, params_s, command) < 0) return -1; + } + + if (parser->post_hook) parser->post_hook(&command->object.as_uint8); + return 0; +} + +int help(const char *cmd) { + int nparams = 1; + char action_s[MAX_SCANF_PARAM_LEN]; + char object_s[MAX_SCANF_PARAM_LEN]; + char params_s[MAX_SCANF_PARAM_LEN]; + hc_object_type_t object_type = OBJECT_TYPE_UNDEFINED; + hc_action_t action = ACTION_UNDEFINED; + + int n = sscanf(cmd, "help %[^\n]s", params_s); + + // No arguments provided to the help command: just list available objects + if (n != 1) goto CMD_LIST; + + // Count number of provided parameters + for (char *ptr = params_s; (ptr = strchr(ptr, ' ')) != NULL; ptr++) nparams++; + if (nparams > 2) { + fprintf(stderr, "Error: too many arguments.\n"); + return -1; + } + + // Object specified: list actions available for that object + if (nparams == 1) { + object_type = object_type_from_str(params_s); + if (object_type == OBJECT_TYPE_UNDEFINED) { + fprintf(stderr, "Error: undefined object type.\n"); + return -1; + } + + goto CMD_LIST; + } + + // Object and action specified: list detailed commands + n = sscanf(params_s, "%s %[^\n]s", object_s, action_s); + assert(n == 2); + object_type = object_type_from_str(object_s); + if (object_type == OBJECT_TYPE_UNDEFINED) { + fprintf(stderr, "Error: undefined object type.\n"); + return -1; + } + action = action_from_str(action_s); + assert(action != ACTION_UNDEFINED); + +CMD_LIST: + printf("Available commands:\n"); + command_list(object_type, action); + return 0; +} + +#if 0 // tests +/* For the tests, we will need to test all non-compliant inputs */ +const char * cmds[] = { + "add connection hicn conn1 8.8.8.8 127.0.0.1 eth0", + "add connection udp <symbolic> <remote_ip> <port> <local_ip> <port> eth0", + "add listener udp lst1 127.0.0.1 9695 eth0", + //"add face", + "add route 3 b001::/16 1", + //"add punting", + //"add strategy", + "add policy b001::/16 webex require avoid prohibit !prohibit neutral !require prefer", + "list connection", // need pluralize + "list listener", + "list face", + "list route", + "list punting", + "list strategy", + "list policy", + "remove connection 1", + "remove listener 1", + //"remove face", + "remove route 1 b001::/16", + //"remove punting", + //"remove policy", + "set debug", + "unset debug", + "set strategy b001::/16 random", // related prefixes (10 max) ? + "set strategy b001::/16 load_balancer", + "set strategy b001::/16 low_latency", + "set strategy b001::/16 replication", + "set strategy b001::/16 bestpath", + "set wldr <on|off> <connection_id>", // on-off vs unset + "cache clear", + "cache store on/off", // set/unset + "cache serve on/off", + "mapme enable on/off", + "mapme discovery on/off", + "mapme timescale 500ms", + "mapme retx 500ms", + "update connection conn1 WT", +}; + +#define array_size(x) sizeof(x) / sizeof(typeof(x[0])) +int main() +{ + for (unsigned i = 0; i < array_size(cmds); i++) { + printf("PARSING [%d] %s\n", i, cmds[i]); + if (parse(cmds[i]) < 0) { + ERROR("Could not parse command: %s\n", cmds[i]); + continue; + } + } + exit(EXIT_SUCCESS); + +ERR: + exit(EXIT_FAILURE); +} +#endif |