aboutsummaryrefslogtreecommitdiffstats
path: root/ctrl/libhicnctrl/src/hicnctrl.c
diff options
context:
space:
mode:
Diffstat (limited to 'ctrl/libhicnctrl/src/hicnctrl.c')
-rw-r--r--ctrl/libhicnctrl/src/hicnctrl.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/ctrl/libhicnctrl/src/hicnctrl.c b/ctrl/libhicnctrl/src/hicnctrl.c
new file mode 100644
index 000000000..478997ec1
--- /dev/null
+++ b/ctrl/libhicnctrl/src/hicnctrl.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2021-2023 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.
+ */
+
+/**
+ * \file hicnctrl.c
+ * \brief Command line interface
+ */
+#include <ctype.h> // isalpha isalnum
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h> // getopt
+
+#include <hicn/ctrl.h>
+#include <hicn/util/ip_address.h>
+#include <hicn/util/log.h>
+#include <hicn/util/token.h>
+#include <hicn/validation.h>
+
+#include <hicn/ctrl/parse.h>
+
+#define die(LABEL, MESSAGE) \
+ do { \
+ printf(MESSAGE "\n"); \
+ goto ERR_##LABEL; \
+ } while (0)
+
+void usage_header() { fprintf(stderr, "Usage:\n"); }
+
+void usage_face_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr,
+ "%s -f TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a face on specified address and port.\n");
+}
+
+void usage_face_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -df ID\n", prog);
+ // fprintf(stderr, "%s -df NAME\n", prog);
+ fprintf(stderr,
+ "%s -df TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a face...\n");
+}
+
+void usage_face_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -F\n", prog);
+ if (verbose) fprintf(stderr, " List all faces.\n");
+}
+
+void usage_face(const char *prog, bool header, bool verbose) {
+ usage_face_create(prog, header, verbose);
+ usage_face_delete(prog, header, verbose);
+ usage_face_list(prog, header, verbose);
+}
+
+void usage_route_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -r FACE_ID PREFIX [COST]\n", prog);
+ // fprintf(stderr, "%s -r [FACE_ID|NAME] PREFIX [COST]\n", prog);
+ if (verbose) fprintf(stderr, " Create a route...\n");
+}
+
+void usage_route_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dr FACE_ID PREFIX\n", prog);
+ // fprintf(stderr, "%s -dr [FACE_ID|NAME] PREFIX\n", prog);
+ if (verbose) fprintf(stderr, " Delete a route...\n");
+}
+
+void usage_route_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -R\n", prog);
+ if (verbose) fprintf(stderr, " List all routes.\n");
+}
+
+void usage_route(const char *prog, bool header, bool verbose) {
+ usage_route_create(prog, header, verbose);
+ usage_route_delete(prog, header, verbose);
+ usage_route_list(prog, header, verbose);
+}
+
+void usage_forwarding_strategy_create(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+}
+void usage_forwarding_strategy_delete(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+}
+
+void usage_forwarding_strategy_list(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -S\n", prog);
+ if (verbose)
+ fprintf(stderr, " List all availble forwarding strategies.\n");
+}
+
+void usage_forwarding_strategy(const char *prog, bool header, bool verbose) {
+ usage_forwarding_strategy_create(prog, header, verbose);
+ usage_forwarding_strategy_delete(prog, header, verbose);
+ usage_forwarding_strategy_list(prog, header, verbose);
+}
+
+void usage_listener_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -l TYPE NAME LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a listener on specified address and port.\n");
+}
+
+void usage_listener_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dl ID\n", prog);
+ fprintf(stderr, "%s -dl NAME\n", prog);
+ fprintf(stderr, "%s -dl TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a listener...\n");
+}
+
+void usage_listener_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -L\n", prog);
+ if (verbose) fprintf(stderr, " List all listeners.\n");
+}
+
+void usage_listener(const char *prog, bool header, bool verbose) {
+ usage_listener_create(prog, header, verbose);
+ usage_listener_delete(prog, header, verbose);
+ usage_listener_list(prog, header, verbose);
+}
+void usage_connection_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr,
+ "%s -c NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a connection on specified address and port.\n");
+}
+
+void usage_connection_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dc ID\n", prog);
+ fprintf(stderr, "%s -dc NAME\n", prog);
+ fprintf(stderr,
+ "%s -dc TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a connection...\n");
+}
+
+void usage_connection_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -C\n", prog);
+ if (verbose) fprintf(stderr, " List all connections.\n");
+}
+
+void usage_connection(const char *prog, bool header, bool verbose) {
+ usage_connection_create(prog, header, verbose);
+ usage_connection_delete(prog, header, verbose);
+ usage_connection_list(prog, header, verbose);
+}
+
+void usage(const char *prog) {
+ fprintf(stderr,
+ "Usage: %s [ -z forwarder (hicnlight | vpp) ] [ [-d] [-f|-l|-c|-r] "
+ "PARAMETERS | [-F|-L|-C|-R] ]\n",
+ prog);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "High-level commands\n");
+ fprintf(stderr, "\n");
+ usage_face(prog, false, true);
+ usage_route(prog, false, true);
+ usage_forwarding_strategy(prog, false, true);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Low level commands (hicn-light specific)\n");
+ fprintf(stderr, "\n");
+ usage_listener(prog, false, true);
+ usage_connection(prog, false, true);
+}
+
+/*
+ * We only allow settings commands and object types once, with the default
+ * action being set to CREATE
+ */
+#define set_command(ACTION, OBJECT_TYPE) \
+ do { \
+ if ((ACTION) != ACTION_UNDEFINED) { \
+ if (command->action != ACTION_CREATE) goto USAGE; \
+ command->action = (ACTION); \
+ } \
+ if ((OBJECT_TYPE) != OBJECT_TYPE_UNDEFINED) { \
+ if (command->object_type != OBJECT_TYPE_UNDEFINED) goto USAGE; \
+ command->object_type = (OBJECT_TYPE); \
+ } \
+ } while (0)
+
+int parse_options(int argc, char *argv[], hc_command_t *command,
+ forwarder_type_t *forwarder) {
+ command->object_type = OBJECT_TYPE_UNDEFINED;
+ command->action = ACTION_CREATE;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "cCdDfFlLrRsShz:")) != -1) {
+ switch (opt) {
+ case 'z':
+ *forwarder = forwarder_type_from_str(optarg);
+ if (*forwarder == FORWARDER_TYPE_UNDEFINED) goto USAGE;
+ break;
+ case 'd':
+ set_command(ACTION_DELETE, OBJECT_TYPE_UNDEFINED);
+ break;
+ case 's':
+ set_command(ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED);
+ break;
+ case 'f':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_FACE);
+ break;
+ case 'c':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_CONNECTION);
+ break;
+ case 'l':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_LISTENER);
+ break;
+ case 'r':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_ROUTE);
+ break;
+ case 'F':
+ set_command(ACTION_LIST, OBJECT_TYPE_FACE);
+ break;
+ case 'L':
+ set_command(ACTION_LIST, OBJECT_TYPE_LISTENER);
+ break;
+ case 'C':
+ set_command(ACTION_LIST, OBJECT_TYPE_CONNECTION);
+ break;
+ case 'R':
+ set_command(ACTION_LIST, OBJECT_TYPE_ROUTE);
+ break;
+ case 'S':
+ set_command(ACTION_LIST, OBJECT_TYPE_STRATEGY);
+ break;
+ case 'D':
+ log_conf.log_level = LOG_DEBUG;
+ break;
+ default: /* "h" */
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ // XXX The rest could be made a single parse function
+
+ /* A default action is always defined, let's verify we have an object type,
+ * unless we are subscribing to notifications. In that case, we can monitor
+ * all objects.
+ * XXX handle later
+ */
+ if ((command->object_type == OBJECT_TYPE_UNDEFINED) &&
+ (command->action != ACTION_SUBSCRIBE)) {
+ ERROR("Missing object specification");
+ goto USAGE;
+ }
+
+ /* Check the adequation between the number of parameters and the command */
+ size_t nparams = argc - optind;
+ if (nparams > 0) {
+ if (command->action == ACTION_LIST) command->action = ACTION_GET;
+ } else {
+ if ((command->action != ACTION_LIST) &&
+ (command->action != ACTION_SUBSCRIBE))
+ goto USAGE;
+ }
+
+ /*
+ * This checks is important even with 0 parameters as it checks whether the
+ * command exists.
+ */
+ if (command->action != ACTION_SUBSCRIBE) {
+ 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_str(command->action), object_type_str(command->object_type));
+ return -1;
+ }
+
+ if (nparams > 0) {
+ if (parse_getopt_args(parser, argc - optind, argv + optind, command) <
+ 0) {
+ ERROR("Error parsing command arguments");
+ goto USAGE;
+ }
+ }
+ }
+
+ return 0;
+
+USAGE:
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[]) {
+ int rc = 1;
+ hc_command_t command;
+ memset(&command, 0, sizeof(command));
+ char buf[MAXSZ_HC_OBJECT];
+
+ log_conf.log_level = LOG_INFO;
+
+ forwarder_type_t forwarder = FORWARDER_TYPE_HICNLIGHT;
+
+ if (parse_options(argc, argv, &command, &forwarder) < 0)
+ die(OPTIONS, "Bad arguments");
+
+ hc_sock_t *s = hc_sock_create(forwarder, /* url= */ NULL);
+ if (!s) die(SOCKET, "Error creating socket.");
+
+ if (hc_sock_connect(s) < 0)
+ die(CONNECT, "Error connecting to the forwarder.");
+
+ hc_data_t *data = NULL;
+
+ rc = hc_execute(s, command.action, command.object_type, &command.object,
+ &data);
+
+ if (rc < 0) {
+ switch (rc) {
+ case INPUT_ERROR:
+ ERROR("Wrong input parameters");
+ break;
+ case UNSUPPORTED_CMD_ERROR:
+ ERROR("Unsupported command");
+ break;
+ default:
+ ERROR("Error executing command");
+ break;
+ }
+ goto ERR_COMMAND;
+ }
+
+ if (!data) goto ERR_QUERY;
+
+ if (!hc_data_get_result(data)) goto ERR_DATA;
+
+ size_t size = hc_data_get_size(data);
+ if (size > 0) {
+ printf("Success: got %ld %s\n", size, object_type_str(command.object_type));
+ } else {
+ printf("Success.\n");
+ }
+
+ if (command.action == ACTION_LIST) {
+ hc_data_foreach(data, obj, {
+ rc = hc_object_snprintf(buf, MAXSZ_HC_OBJECT, command.object_type, obj);
+ if (rc < 0)
+ WARN("Display error");
+ else if (rc >= MAXSZ_HC_OBJECT)
+ WARN("Output truncated");
+ else
+ printf("%s\n", buf);
+ });
+ }
+
+ hc_data_free(data);
+ hc_sock_free(s);
+ return EXIT_SUCCESS;
+
+ERR_DATA:
+ hc_data_free(data);
+ERR_QUERY:
+ERR_COMMAND:
+ERR_CONNECT:
+ hc_sock_free(s);
+ERR_SOCKET:
+ERR_OPTIONS:
+ printf("Error.\n");
+ return EXIT_FAILURE;
+}