From 277a0d296ebf3ffe06eb7aa1f97bcf673fd856a2 Mon Sep 17 00:00:00 2001 From: Jordan Augé Date: Wed, 23 Oct 2019 17:18:44 +0200 Subject: [HICN-351] Support for new RemoveListener API in libhicnctrl + added missing commands in CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ifc743b65493b87a5156bca7ad63f8cae516460d8 Signed-off-by: Jordan Augé --- ctrl/libhicnctrl/src/api.c | 55 +++++- ctrl/libhicnctrl/src/cli.c | 457 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 412 insertions(+), 100 deletions(-) (limited to 'ctrl/libhicnctrl/src') diff --git a/ctrl/libhicnctrl/src/api.c b/ctrl/libhicnctrl/src/api.c index 983dd9b5e..27c552dfa 100644 --- a/ctrl/libhicnctrl/src/api.c +++ b/ctrl/libhicnctrl/src/api.c @@ -2048,13 +2048,64 @@ hc_face_get(hc_sock_t * s, hc_face_t * face, hc_face_t ** face_found) int hc_face_delete(hc_sock_t * s, hc_face_t * face) { - /* XXX We currently do not delete the listener */ hc_connection_t connection; if (hc_face_to_connection(face, &connection, false) < 0) { ERROR("[hc_face_delete] Could not convert face to connection."); return -1; } - return hc_connection_delete(s, &connection); + + if (hc_connection_delete(s, &connection) < 0) { + ERROR("[hc_face_delete] Error removing connection"); + return -1; + } + + /* If this is the last connection attached to the listener, remove it */ + + hc_data_t * connections; + hc_listener_t listener = {0}; + + /* + * Ensure we have a corresponding local listener + * NOTE: hc_face_to_listener is not appropriate + */ + if (hc_connection_to_local_listener(&connection, &listener) < 0) { + ERROR("[hc_face_create] Could not convert face to local listener."); + return -1; + } +#if 1 + /* + * The name is generated to prepare listener creation, we need it to be + * empty for deletion. The id should not need to be reset though. + */ + listener.id = 0; + memset(listener.name, 0, sizeof(listener.name)); +#endif + if (hc_connection_list(s, &connections) < 0) { + ERROR("[hc_face_delete] Error getting the list of listeners"); + return -1; + } + + bool delete = true; + foreach_connection(c, connections) { + if ((ip_address_cmp(&c->local_addr, &listener.local_addr, c->family) == 0) && + (c->local_port == listener.local_port) && + (strcmp(c->interface_name, listener.interface_name) == 0)) { + delete = false; + } + } + + if (delete) { + if (hc_listener_delete(s, &listener) < 0) { + ERROR("[hc_face_delete] Error removing listener"); + return -1; + } + } + + hc_data_free(connections); + + return 0; + + } /* FACE LIST */ diff --git a/ctrl/libhicnctrl/src/cli.c b/ctrl/libhicnctrl/src/cli.c index b8e90f40e..edaa93057 100644 --- a/ctrl/libhicnctrl/src/cli.c +++ b/ctrl/libhicnctrl/src/cli.c @@ -17,6 +17,7 @@ * \file cli.c * \brief Command line interface */ +#include // isalpha isalnum #include #include #include // getopt @@ -34,10 +35,11 @@ #define foreach_object \ _(UNDEFINED) \ - _(LISTENER) \ - _(CONNECTION) \ + _(FACE) \ _(ROUTE) \ _(STRATEGY) \ + _(LISTENER) \ + _(CONNECTION) \ _(N) typedef enum { @@ -48,7 +50,26 @@ foreach_object void usage(const char * prog) { - fprintf(stderr, "Usage: %s [ [-d] [-l|-c|-r] PARAMETERS | [-L|-C|-R] ]\n", prog); + fprintf(stderr, "Usage: %s [ [-d] [-f|-l|-c|-r] PARAMETERS | [-F|-L|-C|-R] ]\n", prog); + fprintf(stderr, "\n"); + fprintf(stderr, "High-level commands\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "%s -f \n", prog); + fprintf(stderr, " Create a face on specified address and port.\n"); + fprintf(stderr, "%s -fc ...\n", prog); + fprintf(stderr, " Delete a face...\n"); + fprintf(stderr, "%s -F\n", prog); + fprintf(stderr, " List all faces.\n"); + fprintf(stderr, "%s -r ...>\n", prog); + fprintf(stderr, " Create a route...\n"); + fprintf(stderr, "%s -dr ...\n", prog); + fprintf(stderr, " Delete a route...\n"); + fprintf(stderr, "%s -R\n", prog); + fprintf(stderr, " List all routes.\n"); + fprintf(stderr, "%s -S\n", prog); + fprintf(stderr, " List all availble forwarding strategies.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Low level commands (hicn-light specific)\n"); fprintf(stderr, "\n"); fprintf(stderr, "%s -l
\n", prog); fprintf(stderr, " Create a listener on specified address and port.\n"); @@ -56,73 +77,122 @@ void usage(const char * prog) fprintf(stderr, " Delete a listener...\n"); fprintf(stderr, "%s -L\n", prog); fprintf(stderr, " List all listeners.\n"); - fprintf(stderr, "%s -c \n", prog); + fprintf(stderr, "%s -c \n", prog); fprintf(stderr, " Create a connection on specified address and port.\n"); fprintf(stderr, "%s -dc ...\n", prog); fprintf(stderr, " Delete a connection...\n"); fprintf(stderr, "%s -C\n", prog); fprintf(stderr, " List all connections.\n"); - fprintf(stderr, "%s -r ...>\n", prog); - fprintf(stderr, " Create a route...\n"); - fprintf(stderr, "%s -dr ...\n", prog); - fprintf(stderr, " Delete a route...\n"); - fprintf(stderr, "%s -R\n", prog); - fprintf(stderr, " List all routes.\n"); - fprintf(stderr, "%s -S\n", prog); - fprintf(stderr, " List all availble forwarding strategies.\n"); } typedef struct { hc_action_t action; hc_object_t object; union { + hc_face_t face; + hc_route_t route; hc_connection_t connection; hc_listener_t listener; - hc_route_t route; }; } hc_command_t; +/** + * Return true if string is purely an integer + */ +static inline +bool +is_number(const char *string) { + size_t len = strlen(string); + for (size_t i = 0; i < len; i++) + if (!isdigit(string[i])) + return false; + return true; +} + +/** + * A symbolic name must be at least 1 character and must begin with an alpha. + * The remainder must be an alphanum. + */ +static inline +bool +is_symbolic_name(const char *name) +{ + size_t len = strlen(name); + if (len <= 0) + return false; + if (!isalpha(name[0])) + return false; + for (size_t i = 1; i < len; i++) { + if (!isalnum(name[i])) + return false; + } + return true; +} + +face_type_t +face_type_from_str(const char * str) +{ +#define _(x) \ + if (strcasecmp(str, STRINGIZE(x)) == 0) \ + return FACE_TYPE_ ## x; \ + else +foreach_face_type +#undef _ + return FACE_TYPE_UNDEFINED; +} + + int parse_options(int argc, char *argv[], hc_command_t * command) { command->object = OBJECT_UNDEFINED; command->action = ACTION_CREATE; - int nargs = 0; /* default for list */ + int nargs = -1; /* unset */ int opt; int family; - while ((opt = getopt(argc, argv, "dlcrLCRSh")) != -1) { + while ((opt = getopt(argc, argv, "dflcrFLCRSh")) != -1) { switch (opt) { case 'd': command->action = ACTION_DELETE; break; + case 'f': + command->object = OBJECT_FACE; + break; case 'l': command->object = OBJECT_LISTENER; - nargs = 5; break; case 'c': command->object = OBJECT_CONNECTION; - nargs = 6; break; case 'r': command->object = OBJECT_ROUTE; nargs = 0; // XXX break; + case 'F': + command->action = ACTION_LIST; + command->object = OBJECT_FACE; + nargs = 0; + break; case 'L': command->action = ACTION_LIST; command->object = OBJECT_LISTENER; + nargs = 0; break; case 'C': command->action = ACTION_LIST; command->object = OBJECT_CONNECTION; + nargs = 0; break; case 'R': command->action = ACTION_LIST; command->object = OBJECT_ROUTE; + nargs = 0; break; case 'S': command->action = ACTION_LIST; command->object = OBJECT_STRATEGY; + nargs = 0; break; default: /* "h" */ usage(argv[0]); @@ -130,25 +200,126 @@ parse_options(int argc, char *argv[], hc_command_t * command) } } - if (command->action == ACTION_DELETE) - nargs = 1; - - /* Each option expects a different number of arguments */ - if ((command->object == OBJECT_UNDEFINED) || (optind != argc - nargs)) { - //printf("Object requires %d arguments [optind=%d != args=%d - nargs=%d\n", nargs, optind, argc, nargs); + if (command->object == OBJECT_UNDEFINED) { + fprintf(stderr, "Missing object specification: connection | listener | route\n"); return -1; } + if (nargs == 0) - return 0; + return 0; /* Parse and validate parameters for add/delete */ switch(command->object) { + case OBJECT_FACE: + switch(command->action) { + case ACTION_CREATE: + if ((argc - optind != 6) && (argc - optind != 7)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -f TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + /* NAME will be autogenerated (and currently not used) */ + //snprintf(command->face.name, NAME_LEN, "%s", argv[optind++]); + command->face.face.type = face_type_from_str(argv[optind++]); + if (command->face.face.type == FACE_TYPE_UNDEFINED) + goto ERR_PARAM; + command->face.face.family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(command->face.face.family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->face.face.local_addr) < 0) + goto ERR_PARAM; + command->face.face.local_port = atoi(argv[optind++]); + family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(family) || (command->face.face.family != family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->face.face.remote_addr) < 0) + goto ERR_PARAM; + command->face.face.remote_port = atoi(argv[optind++]); + if (argc != optind) { + netdevice_set_name(&command->face.face.netdevice, argv[optind++]); + } + + break; + case ACTION_DELETE: + if ((argc - optind != 1) && (argc - optind != 5) && (argc - optind != 6)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -ld ID\n", argv[0]); + //fprintf(stderr, "%s -ld NAME\n", argv[0]); + fprintf(stderr, "%s -ld TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + + if (argc - optind == 1) { + /* Id or name */ + if (is_number(argv[optind])) { + command->face.id = atoi(argv[optind++]); + snprintf(command->face.name, NAME_LEN, "%s", argv[optind++]); + //} else if (is_symbolic_name(argv[optind])) { + // snprintf(command->face.name, NAME_LEN, "%s", argv[optind++]); + } else { + fprintf(stderr, "Invalid argument\n"); + goto ERR_PARAM; + } + } else { + command->face.face.type = face_type_from_str(argv[optind++]); + if (command->face.face.type == FACE_TYPE_UNDEFINED) + goto ERR_PARAM; + command->face.face.family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(command->face.face.family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->face.face.local_addr) < 0) + goto ERR_PARAM; + command->face.face.local_port = atoi(argv[optind++]); + family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(family) || (command->face.face.family != family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->face.face.remote_addr) < 0) + goto ERR_PARAM; + command->face.face.remote_port = atoi(argv[optind++]); + if (argc != optind) { + netdevice_set_name(&command->face.face.netdevice, argv[optind++]); + } + } + break; + default: + goto ERR_COMMAND; + break; + } + break; + + case OBJECT_ROUTE: + switch(command->action) { + case ACTION_CREATE: + goto ERR_COMMAND; + break; + case ACTION_DELETE: + goto ERR_COMMAND; + break; + default: + goto ERR_COMMAND; + break; + } + break; + + case OBJECT_STRATEGY: + switch(command->action) { + case ACTION_LIST: + break; + default: + goto ERR_COMMAND; + break; + } + break; + case OBJECT_LISTENER: switch(command->action) { case ACTION_CREATE: - /* NAME TYPE LOCAL_ADDRESS LOCAL_PORT */ - snprintf(command->listener.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]); - // conn type + if ((argc - optind != 4) && (argc - optind != 5)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -l NAME TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + snprintf(command->listener.name, NAME_LEN, "%s", argv[optind++]); command->listener.type = connection_type_from_str(argv[optind++]); if (command->listener.type == CONNECTION_TYPE_UNDEFINED) goto ERR_PARAM; @@ -158,23 +329,63 @@ parse_options(int argc, char *argv[], hc_command_t * command) if (ip_address_pton(argv[optind++], &command->listener.local_addr) < 0) goto ERR_PARAM; command->listener.local_port = atoi(argv[optind++]); -#ifdef __linux__ - snprintf(command->listener.interface_name, INTERFACE_LEN, "%s", argv[optind++]); -#endif + if (argc != optind) { + snprintf(command->listener.interface_name, INTERFACE_LEN, "%s", argv[optind++]); + } break; + case ACTION_DELETE: - goto ERR_COMMAND; + if ((argc - optind != 1) && (argc - optind != 3) && (argc - optind != 4)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -ld ID\n", argv[0]); + fprintf(stderr, "%s -ld NAME\n", argv[0]); + fprintf(stderr, "%s -ld TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + + if (argc - optind == 1) { + /* Id or name */ + if (is_number(argv[optind])) { + command->listener.id = atoi(argv[optind++]); + snprintf(command->listener.name, NAME_LEN, "%s", argv[optind++]); + } else if (is_symbolic_name(argv[optind])) { + snprintf(command->listener.name, NAME_LEN, "%s", argv[optind++]); + } else { + fprintf(stderr, "Invalid argument\n"); + goto ERR_PARAM; + } + } else { + command->listener.type = connection_type_from_str(argv[optind++]); + if (command->listener.type == CONNECTION_TYPE_UNDEFINED) + goto ERR_PARAM; + command->listener.family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(command->listener.family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->listener.local_addr) < 0) + goto ERR_PARAM; + command->listener.local_port = atoi(argv[optind++]); + if (argc != optind) { + snprintf(command->listener.interface_name, INTERFACE_LEN, "%s", argv[optind++]); + } + } break; + default: goto ERR_COMMAND; break; } break; + case OBJECT_CONNECTION: switch(command->action) { case ACTION_CREATE: /* NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT */ - snprintf(command->connection.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]); + if ((argc - optind != 6) && (argc - optind != 7)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -c NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + snprintf(command->connection.name, NAME_LEN, "%s", argv[optind++]); command->connection.type = connection_type_from_str(argv[optind++]); if (command->connection.type == CONNECTION_TYPE_UNDEFINED) goto ERR_PARAM; @@ -191,45 +402,51 @@ parse_options(int argc, char *argv[], hc_command_t * command) goto ERR_PARAM; command->connection.remote_port = atoi(argv[optind++]); - { - char buf_connection[MAXSZ_HC_CONNECTION]; - if (hc_connection_snprintf(buf_connection, MAXSZ_HC_CONNECTION, &command->connection) >= MAXSZ_HC_CONNECTION) - printf("PARSED !!\n"); - else - printf("PARSED %s\n", buf_connection); - } - - break; - case ACTION_DELETE: - goto ERR_COMMAND; - break; - default: - goto ERR_COMMAND; - break; - } - break; - case OBJECT_ROUTE: - switch(command->action) { - case ACTION_CREATE: - goto ERR_COMMAND; break; case ACTION_DELETE: - goto ERR_COMMAND; - break; - default: - goto ERR_COMMAND; - break; - } - break; - case OBJECT_STRATEGY: - switch(command->action) { - case ACTION_LIST: + if ((argc - optind != 1) && (argc - optind != 5) && (argc - optind != 6)) { + fprintf(stderr, "Usage:\n"); + fprintf(stderr, "%s -ld ID\n", argv[0]); + fprintf(stderr, "%s -ld NAME\n", argv[0]); + fprintf(stderr, "%s -ld TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", argv[0]); + goto ERR_PARAM; + } + + if (argc - optind == 1) { + /* Id or name */ + if (is_number(argv[optind])) { + command->connection.id = atoi(argv[optind++]); + snprintf(command->connection.name, NAME_LEN, "%s", argv[optind++]); + } else if (is_symbolic_name(argv[optind])) { + snprintf(command->connection.name, NAME_LEN, "%s", argv[optind++]); + } else { + fprintf(stderr, "Invalid argument\n"); + goto ERR_PARAM; + } + } else { + command->connection.type = connection_type_from_str(argv[optind++]); + if (command->connection.type == CONNECTION_TYPE_UNDEFINED) + goto ERR_PARAM; + command->connection.family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(command->connection.family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->connection.local_addr) < 0) + goto ERR_PARAM; + command->connection.local_port = atoi(argv[optind++]); + family = ip_address_get_family(argv[optind]); + if (!IS_VALID_FAMILY(family) || (command->connection.family != family)) + goto ERR_PARAM; + if (ip_address_pton(argv[optind++], &command->connection.remote_addr) < 0) + goto ERR_PARAM; + command->connection.remote_port = atoi(argv[optind++]); + } break; default: goto ERR_COMMAND; break; } break; + default: goto ERR_COMMAND; break; @@ -246,7 +463,7 @@ int main(int argc, char *argv[]) { hc_data_t * data; int rc = 1; - hc_command_t command; + hc_command_t command = {0}; char buf_listener[MAXSZ_HC_LISTENER]; char buf_connection[MAXSZ_HC_CONNECTION]; char buf_route[MAXSZ_HC_ROUTE]; @@ -263,51 +480,29 @@ int main(int argc, char *argv[]) die(CONNECT, "Error connecting to the forwarder."); switch(command.object) { - case OBJECT_LISTENER: + case OBJECT_FACE: switch(command.action) { case ACTION_CREATE: - if (hc_listener_create(s, &command.listener) < 0) - die(COMMAND, "Error creating listener"); + if (hc_face_create(s, &command.face) < 0) + die(COMMAND, "Error creating face"); printf("OK\n"); break; - case ACTION_DELETE: - die(COMMAND, "Not implemented."); - break; - case ACTION_LIST: - if (hc_listener_list(s, &data) < 0) - die(COMMAND, "Error getting listeners."); - printf("Listeners:\n"); - foreach_listener(l, data) { - if (hc_listener_snprintf(buf_listener, MAXSZ_HC_LISTENER+17, l) >= MAXSZ_HC_LISTENER) - die(COMMAND, "Display error"); - printf("[%d] %s\n", l->id, buf_listener); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for listener"); - break; - } - break; - case OBJECT_CONNECTION: - switch(command.action) { - case ACTION_CREATE: - die(COMMAND, "Not implemented."); - break; case ACTION_DELETE: - die(COMMAND, "Not implemented."); + if (hc_face_delete(s, &command.face) < 0) + die(COMMAND, "Error creating face"); + printf("OK\n"); break; + case ACTION_LIST: - if (hc_connection_list(s, &data) < 0) + if (hc_face_list(s, &data) < 0) die(COMMAND, "Error getting connections."); - printf("Connections:\n"); - foreach_connection(c, data) { - if (hc_connection_snprintf(buf_connection, MAXSZ_HC_CONNECTION, c) >= MAXSZ_HC_CONNECTION) + printf("Faces:\n"); + foreach_face(f, data) { + if (hc_face_snprintf(buf_connection, MAXSZ_HC_FACE, f) >= MAXSZ_HC_FACE) die(COMMAND, "Display error"); - printf("[%s] %s\n", c->name, buf_connection); + printf("[%s] %s\n", f->name, buf_connection); } hc_data_free(data); @@ -317,6 +512,7 @@ int main(int argc, char *argv[]) break; } break; + case OBJECT_ROUTE: switch(command.action) { case ACTION_CREATE: @@ -343,6 +539,7 @@ int main(int argc, char *argv[]) break; } break; + case OBJECT_STRATEGY: switch(command.action) { case ACTION_LIST: @@ -363,6 +560,70 @@ int main(int argc, char *argv[]) break; } break; + + case OBJECT_LISTENER: + switch(command.action) { + case ACTION_CREATE: + if (hc_listener_create(s, &command.listener) < 0) + die(COMMAND, "Error creating listener"); + printf("OK\n"); + break; + case ACTION_DELETE: + if (hc_listener_delete(s, &command.listener) < 0) + die(COMMAND, "Error deleting listener"); + printf("OK\n"); + break; + break; + case ACTION_LIST: + if (hc_listener_list(s, &data) < 0) + die(COMMAND, "Error getting listeners."); + + printf("Listeners:\n"); + foreach_listener(l, data) { + if (hc_listener_snprintf(buf_listener, MAXSZ_HC_LISTENER+17, l) >= MAXSZ_HC_LISTENER) + die(COMMAND, "Display error"); + printf("[%d] %s\n", l->id, buf_listener); + } + + hc_data_free(data); + break; + default: + die(COMMAND, "Unsupported command for listener"); + break; + } + break; + + case OBJECT_CONNECTION: + switch(command.action) { + case ACTION_CREATE: + if (hc_connection_create(s, &command.connection) < 0) + die(COMMAND, "Error creating connection"); + printf("OK\n"); + break; + case ACTION_DELETE: + if (hc_connection_delete(s, &command.connection) < 0) + die(COMMAND, "Error creating connection"); + printf("OK\n"); + break; + case ACTION_LIST: + if (hc_connection_list(s, &data) < 0) + die(COMMAND, "Error getting connections."); + + printf("Connections:\n"); + foreach_connection(c, data) { + if (hc_connection_snprintf(buf_connection, MAXSZ_HC_CONNECTION, c) >= MAXSZ_HC_CONNECTION) + die(COMMAND, "Display error"); + printf("[%s] %s\n", c->name, buf_connection); + } + + hc_data_free(data); + break; + default: + die(COMMAND, "Unsupported command for connection"); + break; + } + break; + default: die(COMMAND, "Unsupported object"); break; -- cgit 1.2.3-korg