diff options
Diffstat (limited to 'ctrl/libhicnctrl/src')
91 files changed, 10118 insertions, 6413 deletions
diff --git a/ctrl/libhicnctrl/src/CMakeLists.txt b/ctrl/libhicnctrl/src/CMakeLists.txt index 1bec03d50..a9a7d3db4 100644 --- a/ctrl/libhicnctrl/src/CMakeLists.txt +++ b/ctrl/libhicnctrl/src/CMakeLists.txt @@ -15,11 +15,50 @@ # Source files ############################################################## set(SOURCE_FILES - route.c + action.c api.c + command.c + commands/command_cache.c + commands/command_connection.c + commands/command_face.c + commands/command_listener.c + commands/command_mapme.c + commands/command_policy.c + commands/command_punting.c + commands/command_route.c + commands/command_stats.c + commands/command_strategy.c + commands/command_subscription.c + data.c + fw_interface.c + object.c + object_type.c + object_vft.c + objects/active_interface.c + objects/base.c + objects/connection.c + objects/face.c + objects/listener.c + objects/route.c + objects/strategy.c + objects/subscription.c + parse.c + request.c + route.c + socket.c ) set(HEADER_FILES + object_vft.h + objects/active_interface.h + objects/base.h + objects/connection.h + objects/face.h + objects/listener.h + objects/route.h + objects/strategy.h + objects/subscription.h + request.h api_private.h ) @@ -58,13 +97,33 @@ endif () ############################################################## +# Compiler options +############################################################## +set(COMPILER_OPTIONS + ${DEFAULT_COMPILER_OPTIONS} +) + +############################################################## # Do not use modules if Android ############################################################## if (${CMAKE_SYSTEM_NAME} MATCHES Android OR ${CMAKE_SYSTEM_NAME} MATCHES iOS) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light_common.c - ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light_ng_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/face.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/strategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/subscription.c + ) + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/face.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/subscription.h ) else() add_subdirectory(modules) @@ -72,14 +131,6 @@ endif() ############################################################## -# Compiler options -############################################################## -set(COMPILER_OPTIONS - ${DEFAULT_COMPILER_OPTIONS} -) - - -############################################################## # Build main hicnctrl library ############################################################## build_library(${LIBHICNCTRL} @@ -96,6 +147,12 @@ build_library(${LIBHICNCTRL} COMPILE_OPTIONS ${COMPILER_OPTIONS} ) +############################################################## +# Unit tests +############################################################## +if (${BUILD_TESTS}) + add_subdirectory(test) +endif() ############################################################## # Cmake config files diff --git a/ctrl/libhicnctrl/src/action.c b/ctrl/libhicnctrl/src/action.c new file mode 100644 index 000000000..c8834d90c --- /dev/null +++ b/ctrl/libhicnctrl/src/action.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file action.c + * \brief Implementation of actions. + */ + +#include <strings.h> + +#include <hicn/ctrl/action.h> + +const char *action_str[] = { +#define _(x) [ACTION_##x] = #x, + foreach_action +#undef _ +}; + +hc_action_t action_from_str(const char *action_str) { +#define _(x) \ + if (strcasecmp(action_str, #x) == 0) \ + return ACTION_##x; \ + else + foreach_action +#undef _ + if (strcasecmp(action_str, "add") == 0) return ACTION_CREATE; + else if (strcasecmp(action_str, "remove") == 0) return ACTION_DELETE; + else return ACTION_UNDEFINED; +} diff --git a/ctrl/libhicnctrl/src/api.c b/ctrl/libhicnctrl/src/api.c index d68dc830e..c93853dd1 100644 --- a/ctrl/libhicnctrl/src/api.c +++ b/ctrl/libhicnctrl/src/api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2021-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: @@ -26,6 +26,13 @@ #include <math.h> // log2 #include "api_private.h" +#include "object_vft.h" +#include "request.h" + +#include <hicn/ctrl/socket.h> +#include "socket_private.h" + +#define ENOIMPL 42 #if 0 /* /!\ Please update constants in public header file upon changes */ @@ -60,685 +67,282 @@ connection_type_from_str(const char * str) * Control Data ******************************************************************************/ -hc_data_t *hc_data_create(size_t in_element_size, size_t out_element_size, - data_callback_t complete_cb) { - hc_data_t *data = malloc(sizeof(hc_data_t)); - if (!data) goto ERR_MALLOC; - - /* FIXME Could be NULL thanks to realloc provided size is 0 */ - data->max_size_log = DEFAULT_SIZE_LOG; - data->in_element_size = in_element_size; - data->out_element_size = out_element_size; - data->size = 0; - data->complete = false; - data->command_id = 0; // TODO this could also be a busy mark in the socket - /* No callback needed in blocking code for instance */ - data->complete_cb = complete_cb; - - data->buffer = malloc((1 << data->max_size_log) * data->out_element_size); - if (!data->buffer) goto ERR_BUFFER; - data->ret = 0; - data->current = 0; - - return data; - -ERR_BUFFER: - hc_data_free(data); -ERR_MALLOC: - return NULL; -} - -void hc_data_free(hc_data_t *data) { - if (data) free(data->buffer); - free(data); -} - -int hc_data_ensure_available(hc_data_t *data, size_t count) { - size_t new_size_log = - (data->size + count - 1 > 0) ? log2(data->size + count - 1) + 1 : 0; - if (new_size_log > data->max_size_log) { - data->max_size_log = new_size_log; - data->buffer = - realloc(data->buffer, (1 << new_size_log) * data->out_element_size); - if (!data->buffer) return -1; - } +/*----------------------------------------------------------------------------* + * Object model + *----------------------------------------------------------------------------*/ - return 0; -} +/*----------------------------------------------------------------------------* + * Entry point + *----------------------------------------------------------------------------*/ -int hc_data_push_many(hc_data_t *data, const void *elements, size_t count) { - if (hc_data_ensure_available(data, count) < 0) return -1; +int hc_sock_on_init(hc_sock_t *s, hc_request_t *request) { + int rc; + ssize_t size; - memcpy(data->buffer + data->size * data->out_element_size, elements, - count * data->out_element_size); - data->size += count; + uint8_t *buffer; - return 0; -} + size = s->ops.prepare(s, request, &buffer); + if (size < 0) goto ERR_PREPARE; -int hc_data_push(hc_data_t *data, const void *element) { - return hc_data_push_many(data, element, 1); -} + if (size == 0) return 1; /* Done */ -/** - * - * NOTE: This function make sure there is enough room available in the data - * structure. - */ -u8 *hc_data_get_next(hc_data_t *data) { - if (hc_data_ensure_available(data, 1) < 0) return NULL; + assert(hc_request_get_data(hc_request_get_current(request))); - return data->buffer + data->size * data->out_element_size; -} + rc = s->ops.send(s, buffer, size); + if (rc < 0) goto ERR_SEND; -int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data) { - data->complete_cb = cb; - data->complete_cb_data = cb_data; return 0; -} -int hc_data_set_complete(hc_data_t *data) { - data->complete = true; - data->ret = 0; - if (data->complete_cb) return data->complete_cb(data, data->complete_cb_data); - return 0; -} - -int hc_data_set_error(hc_data_t *data) { - data->ret = -1; - return 0; +ERR_PREPARE: +ERR_SEND: + return -1; } -int hc_data_reset(hc_data_t *data) { - data->size = 0; - return 0; -} +int hc_sock_on_receive(hc_sock_t *s, size_t count) { + int rc; -static hc_sock_t *_open_module(const char *name, const char *url) { - char complete_name[128]; -#ifdef __APPLE__ - snprintf(complete_name, 128, "%s.dylib", name); -#elif defined(__linux__) - snprintf(complete_name, 128, "%s.so", name); -#else -#error "System not supported for dynamic lynking" + DEBUG("hc_sock_on_receive: calling process with count=%ld", count); + rc = s->ops.process(s, count); + if (rc < 0) goto ERR_PROCESS; + + hc_request_t *request = hc_sock_get_request(s); + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + if (hc_data_is_complete(data)) { + /* + * We only notice a request is complete when trying to send the second + * time... either the state machine reaches the end, or in case of generic + * requests, we mark it as such. + */ +#if 0 + if (hc_request_is_complete(current_request)) { + if (!hc_request_pop(request)) { + /* Free request context */ + /* In case of error, data is NULL */ + // XXX bug if we free request XXX + // hc_sock_free_request(s, request); + if (!hc_request_is_subscription(request)) + hc_request_set_complete(request); + return 1; /* Done */ + } + } else { #endif - - void *handle = 0; - const char *error = 0; - hc_sock_t *(*creator)(const char *) = 0; - hc_sock_t *ret = 0; - - // open module - handle = dlopen(complete_name, RTLD_LAZY); - if (!handle) { - if ((error = dlerror()) != 0) { - ERROR("%s", error); + ON_INIT: + rc = hc_sock_on_init(s, request); + if (rc < 0) goto ERR_INIT; + if (rc == 1) { + if (!hc_request_pop(request)) { + /* Free request context */ + /* In case of error, data is NULL */ + // hc_sock_free_request(s, request); + if (!hc_request_is_subscription(request)) + hc_request_set_complete(request); + return 1; /* Done */ + } + goto ON_INIT; } - return 0; - } - - // get factory method - creator = - (hc_sock_t * (*)(const char *)) dlsym(handle, "_hc_sock_create_url"); - if (!creator) { - if ((error = dlerror()) != 0) { - ERROR("%s", error); +#if 0 } - return 0; - } - - ret = (*creator)(NULL); - ret->handle = handle; - - return ret; -} - -hc_sock_t *hc_sock_create_forwarder_url(forwarder_type_t forwarder, - const char *url) { - switch (forwarder) { - case HICNLIGHT: - return _open_module("hicnlightctrl_module", url); - case HICNLIGHT_NG: - return _open_module("hicnlightngctrl_module", url); - case VPP: - return _open_module("vppctrl_module", url); - default: - return NULL; - } -} - -#ifdef ANDROID -// In android we do not load a module at runtime -// but we link the hicnlight implmentation directly -// to the main library -extern hc_sock_t *_hc_sock_create_url(const char *url); #endif - -hc_sock_t *hc_sock_create_forwarder(forwarder_type_t forwarder) { -#ifdef ANDROID - assert(forwarder == HICNLIGHT_NG); - hc_sock_t *ret = _hc_sock_create_url(NULL); - ret->handle = NULL; - return ret; -#else - return hc_sock_create_forwarder_url(forwarder, NULL); -#endif -} - -hc_sock_t *hc_sock_create(void) { - return hc_sock_create_forwarder(HICNLIGHT_NG); -} - -void hc_sock_free(hc_sock_t *s) { - void *handle = s->handle; - s->hc_sock_free(s); - - if (handle) { - dlclose(handle); - } -} - -int hc_sock_get_next_seq(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } - -int hc_sock_set_nonblocking(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } - -int hc_sock_get_fd(hc_sock_t *s) { return s->hc_sock_get_fd(s); } - -int hc_sock_connect(hc_sock_t *s) { return s->hc_sock_connect(s); } - -int hc_sock_get_available(hc_sock_t *s, u8 **buffer, size_t *size) { - return s->hc_sock_get_available(s, buffer, size); -} - -int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq) { - return s->hc_sock_send(s, msg, msglen, seq); -} - -int hc_sock_recv(hc_sock_t *s) { return s->hc_sock_recv(s); } - -int hc_sock_process(hc_sock_t *s, hc_data_t **data) { - return s->hc_sock_process(s, data); -} - -int hc_sock_callback(hc_sock_t *s, hc_data_t **data) { - return s->hc_sock_callback(s, data); -} - -int hc_sock_reset(hc_sock_t *s) { return s->hc_sock_reset(s); } - -void hc_sock_increment_woff(hc_sock_t *s, size_t bytes) { - s->hc_sock_increment_woff(s, bytes); -} - -int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, - data_callback_t complete_cb, void *complete_cb_data) { - return s->hc_sock_prepare_send(s, result, complete_cb, complete_cb_data); -} - -int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms) { - return s->hc_sock_set_recv_timeout_ms(s, timeout_ms); -} - -/*----------------------------------------------------------------------------* - * LISTENER - *----------------------------------------------------------------------------*/ - -int hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_create(s, listener); -} - -hc_result_t *hc_listener_create_conf(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_create_conf(s, listener); -} - -int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found) { - return s->hc_listener_get(s, listener, listener_found); -} - -int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_delete(s, listener); -} - -int hc_listener_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_listener_list(s, pdata); -} - -GENERATE_FIND(listener); - -/* LISTENER VALIDATE */ - -int hc_listener_validate(const hc_listener_t *listener) { - if (!IS_VALID_NAME(listener->name)) { - ERROR("[hc_listener_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_INTERFACE_NAME(listener->interface_name)) { - ERROR("[hc_listener_validate] Invalid interface_name specified"); - return -1; - } - if (!IS_VALID_TYPE(listener->type)) { - ERROR("[hc_listener_validate] Invalid type specified"); - return -1; - } - if (!IS_VALID_FAMILY(listener->family)) { - ERROR("[hc_listener_validate] Invalid family specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&listener->local_addr, listener->family)) { - ERROR("[hc_listener_validate] Invalid local_addr specified"); - return -1; - } - if (!IS_VALID_PORT(listener->local_port)) { - ERROR("[hc_listener_validate] Invalid local_port specified"); - return -1; } + return 0; /* Continue processing */ - return 0; +ERR_INIT: +ERR_PROCESS: + return -1; } -/* LISTENER CMP */ - -int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2) { +// -1 error +// 0 = request is not yet complete +// 1 request is complete +int hc_sock_receive(hc_sock_t *s, hc_data_t **pdata) { int rc; + DEBUG("Waiting for data..."); + rc = s->ops.recv(s); + if (rc < 0) return -1; - rc = INT_CMP(l1->type, l2->type); - if (rc != 0) return rc; - - rc = INT_CMP(l1->family, l2->family); - if (rc != 0) return rc; - - rc = strncmp(l1->interface_name, l2->interface_name, INTERFACE_LEN); - if (rc != 0) return rc; - - rc = ip_address_cmp(&l1->local_addr, &l2->local_addr, l1->family); - if (rc != 0) return rc; - - rc = INT_CMP(l1->local_port, l2->local_port); - if (rc != 0) return rc; - - return rc; -} - -/* LISTENER SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_listener_snprintf(char *s, size_t size, hc_listener_t *listener) { - char local[MAXSZ_URL]; - int rc; - rc = url_snprintf(local, MAXSZ_URL, listener->family, &listener->local_addr, - listener->local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_listener_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - - return snprintf(s, size, "%s %s %s interface=%s", listener->name, local, - face_type_str(listener->type), listener->interface_name); -} + rc = hc_sock_on_receive(s, 0); + if (rc < 0) return -1; -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -int hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { - return s->hc_connection_create(s, connection); -} - -hc_result_t *hc_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - return s->hc_connection_create_conf(s, connection); -} - -int hc_connection_get(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found) { - return s->hc_connection_get(s, connection, connection_found); -} - -int hc_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - return s->hc_connection_update_by_id(s, hc_connection_id, connection); -} - -int hc_connection_update(hc_sock_t *s, hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - return s->hc_connection_update(s, connection_current, connection_updated); -} - -int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - return s->hc_connection_delete(s, connection); -} - -hc_result_t *hc_connection_delete_conf(hc_sock_t *s, - hc_connection_t *connection) { - return s->hc_connection_delete_conf(s, connection); -} - -int hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_connection_list(s, pdata); -} - -int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state) { - return s->hc_connection_set_admin_state(s, conn_id_or_name, state); -} + hc_request_t *request = hc_sock_get_request(s); + /* + * If notification, display it, ideally callback. What to do with + * allocated data ? + */ + // XXX problem we display object on ACK... but not subsequent + // notifications + // XXX we should rely on callback here in addition, even for a synchronous + // request + if (hc_request_is_subscription(request)) { + hc_data_t *data = hc_request_get_data(request); + assert(data); + hc_object_t *obj = (hc_object_t *)hc_data_get_buffer(data); + char buf[MAXSZ_HC_OBJECT]; + hc_object_type_t object_type = hc_data_get_object_type(data); + if (hc_object_snprintf(buf, sizeof(buf), object_type, obj) > 0) { + ; + INFO("%s %s", object_type_str(object_type), buf); + } + } -#ifdef WITH_POLICY -int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return s->hc_connection_set_priority(s, conn_id_or_name, priority); -} + // XXX need same for async + if (rc != 1) return 0; -int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return s->hc_connection_set_tags(s, conn_id_or_name, tags); + hc_request_t *current_request = hc_request_get_current(request); + if (hc_request_is_complete(current_request)) { + /* We either return the (last) allocated data, or free it */ + if (pdata) { + *pdata = hc_request_get_data(request); + } else { + hc_request_reset_data(request); + } + hc_request_on_complete(request); + // hc_sock_free_request(s, request); + } + return 1; } -#endif // WITH_POLICY - -GENERATE_FIND(connection); -/* CONNECTION VALIDATE */ +int hc_sock_receive_all(hc_sock_t *s, hc_data_t **pdata) { + for (;;) { + int rc = hc_sock_receive(s, pdata); + if (rc < 0) return -1; -int hc_connection_validate(const hc_connection_t *connection) { - if (connection->name[0] != '\0' && !IS_VALID_NAME(connection->name)) { - ERROR("[hc_connection_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_INTERFACE_NAME(connection->interface_name)) { - ERROR("[hc_connection_validate] Invalid interface_name specified"); - return -1; - } - if (!IS_VALID_TYPE(connection->type)) { - ERROR("[hc_connection_validate] Invalid type specified"); - return -1; - } - if (!IS_VALID_FAMILY(connection->family)) { - ERROR("[hc_connection_validate] Invalid family specified"); - return -1; + /* If request is complete, stop */ + if (rc == 1) break; } - if (!IS_VALID_ADDRESS(&connection->local_addr, connection->family)) { - ERROR("[hc_connection_validate] Invalid local_addr specified"); - return -1; - } - if (!IS_VALID_PORT(connection->local_port)) { - ERROR("[hc_connection_validate] Invalid local_port specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&connection->remote_addr, connection->family)) { - ERROR("[hc_connection_validate] Invalid remote_addr specified"); - return -1; - } - if (!IS_VALID_PORT(connection->remote_port)) { - ERROR("[hc_connection_validate] Invalid remote_port specified"); - return -1; - } - return 0; } -/* CONNECTION CMP */ - -/* - * hICN light uses ports even for hICN connections, but their value is ignored. - * As connections are specific to hicn-light, we can safely use IP and ports for - * comparison independently of the face type. +/** + * @return <0 in case of error + * -1 : validation error + * -2 : error during send + * -3 : error receiving or parsing + * + * If the caller provider a non-NULL hc_data_t pointer to receive results + * back, it is responsible for freeing it. */ -int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2) { - int rc; - - rc = INT_CMP(c1->type, c2->type); - if (rc != 0) return rc; - - rc = INT_CMP(c1->family, c2->family); - if (rc != 0) return rc; - - rc = strncmp(c1->interface_name, c2->interface_name, INTERFACE_LEN); - if (rc != 0) return rc; - - rc = ip_address_cmp(&c1->local_addr, &c2->local_addr, c1->family); - if (rc != 0) return rc; - - rc = INT_CMP(c1->local_port, c2->local_port); - if (rc != 0) return rc; - - rc = ip_address_cmp(&c1->remote_addr, &c2->remote_addr, c1->family); - if (rc != 0) return rc; +int _hc_execute(hc_sock_t *s, hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_result_callback_t callback, + void *callback_data, hc_data_t **pdata) { + assert(!(hc_sock_is_async(s) && pdata)); - rc = INT_CMP(c1->remote_port, c2->remote_port); - if (rc != 0) return rc; - - return rc; -} - -/* CONNECTION SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_connection_snprintf(char *s, size_t size, - const hc_connection_t *connection) { - char local[MAXSZ_URL]; - char remote[MAXSZ_URL]; - int rc; - - // assert(connection->connection_state) - if (strcmp(connection->name, "SELF") == 0) { - return snprintf(s, size, "%s", connection->name); + if (hc_sock_is_async(s) && !s->ops.get_fd) { + return -1; /* No async support */ } - rc = url_snprintf(local, MAXSZ_URL, connection->family, - &connection->local_addr, connection->local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = url_snprintf(remote, MAXSZ_URL, connection->family, - &connection->remote_addr, connection->remote_port); - if (rc >= MAXSZ_URL) - WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - - return snprintf(s, size, "%s %s %s %s", connection->name, local, remote, - face_type_str(connection->type)); -} - -int hc_face_create(hc_sock_t *s, hc_face_t *face) { - return s->hc_face_create(s, face); -} - -int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_face_t **face_found) { - return s->hc_face_get(s, face, face_found); -} - -int hc_face_delete(hc_sock_t *s, hc_face_t *face, uint8_t delete_listener) { - return s->hc_face_delete(s, face, delete_listener); -} - -int hc_face_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_face_list(s, pdata); -} - -int hc_face_list_async(hc_sock_t *s) { return s->hc_face_list_async(s); } + // XXX no need to pass pdata to the request + // XXX sync socket, no multiplexed requests, no notifications + /* + * The request will contain all state needed to identify and demultiplex + * replies and notifications arriving on the socket. We assume there is at + * most a single request/reply in progress for a given request, and that + * requests involving multiple queries will run them sequentially. The use + * of a sequence number that is transported by the requests and reply is + * thus sufficient to disambiguate them. + */ + hc_request_t *request = hc_sock_create_request(s, action, object_type, object, + callback, callback_data); + if (!request) { + goto ERR_REQUEST; + } -int hc_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state) { - return s->hc_face_set_admin_state(s, conn_id_or_name, state); -} + if (hc_request_requires_object(request)) { + if (hc_object_is_empty(object) || + hc_object_validate(object_type, object, true) < 0) { + goto ERR_VALIDATE; + } + } else { + if (object && !hc_object_is_empty(object)) { + goto ERR_CHECK; + } + } -#ifdef WITH_POLICY -int hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return s->hc_face_set_priority(s, conn_id_or_name, priority); -} + /* Workaround for non-fd based modules */ + if (s->ops.prepare && s->ops.send && s->ops.recv && s->ops.process) { + if (hc_sock_on_init(s, request) < 0) goto ERR_INIT; -int hc_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return s->hc_face_set_tags(s, conn_id_or_name, tags); -} -#endif /* WITH_POLICY */ + if (hc_sock_is_async(s)) return 0; -/* /!\ Please update constants in header file upon changes */ -int hc_face_snprintf(char *s, size_t size, hc_face_t *face) { - /* URLs are also big enough to contain IP addresses in the hICN case */ - char local[MAXSZ_URL]; - char remote[MAXSZ_URL]; -#ifdef WITH_POLICY - char tags[MAXSZ_POLICY_TAGS]; -#endif /* WITH_POLICY */ - int rc; + /* Case in which no reply is expected */ + if (!pdata) return 0; - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_HICN_LISTENER: - rc = ip_address_snprintf(local, MAXSZ_URL, &face->face.local_addr, - face->face.family); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = ip_address_snprintf(remote, MAXSZ_URL, &face->face.remote_addr, - face->face.family); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - break; - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - rc = url_snprintf(local, MAXSZ_URL, face->face.family, - &face->face.local_addr, face->face.local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = url_snprintf(remote, MAXSZ_URL, face->face.family, - &face->face.remote_addr, face->face.remote_port); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - break; - default: - return -1; + if (hc_sock_receive_all(s, pdata) < 0) goto ERR_RECV; + } else if (s->ops.prepare) { + // hc_data_t *data = hc_data_create(OBJECT_TYPE_LISTENER); + // hc_data_push(data, NULL); + // No nested requests for now... + ssize_t size = s->ops.prepare(s, request, NULL); + _ASSERT(size == 0); /* Done */ + if (hc_request_is_complete(request)) { + if (pdata) { + *pdata = hc_request_get_data(request); + } else { + hc_request_reset_data(request); + } + hc_request_on_complete(request); + } } - // [#ID NAME] TYPE LOCAL_URL REMOTE_URL STATE/ADMIN_STATE (TAGS) -#ifdef WITH_POLICY - rc = policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->face.tags); - if (rc >= MAXSZ_POLICY_TAGS) - WARN("[hc_face_snprintf] Unexpected truncation of policy tags string"); - if (rc < 0) return rc; - - return snprintf( - s, size, "[#%d %s] %s %s %s %s %s/%s [%d] (%s)", face->id, face->name, - face->face.netdevice.index != NETDEVICE_UNDEFINED_INDEX - ? face->face.netdevice.name - : "*", - face_type_str(face->face.type), local, remote, - face_state_str(face->face.state), face_state_str(face->face.admin_state), - face->face.priority, tags); -#else - return snprintf(s, size, "[#%d %s] %s %s %s %s %s/%s", face->id, face->name, - face->face.netdevice.index != NETDEVICE_UNDEFINED_INDEX - ? face->face.netdevice.name - : "*", - face_type_str(face->face.type), local, remote, - face_state_str(face->face.state), - face_state_str(face->face.admin_state)); -#endif /* WITH_POLICY */ -} - -/*----------------------------------------------------------------------------* - * ROUTE - *----------------------------------------------------------------------------*/ - -int hc_route_create(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_create(s, route); -} + return 0; -hc_result_t *hc_route_create_conf(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_create_conf(s, route); +ERR_RECV: + hc_request_reset_data(request); +ERR_INIT: + hc_sock_free_request(s, request, true); +ERR_CHECK: +ERR_REQUEST: +ERR_VALIDATE: + if (pdata) *pdata = NULL; + return -1; } -int hc_route_delete(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_delete(s, route); +int hc_execute(hc_sock_t *s, hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_data_t **pdata) { + return _hc_execute(s, action, object_type, object, NULL, NULL, pdata); } -int hc_route_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_route_list(s, pdata); +int hc_execute_async(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, + hc_result_callback_t callback, void *callback_data) { + return _hc_execute(s, action, object_type, object, callback, callback_data, + NULL); } -int hc_route_list_async(hc_sock_t *s) { return s->hc_route_list_async(s); } - -/* ROUTE SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_route_snprintf(char *s, size_t size, hc_route_t *route) { - /* interface cost prefix length */ - - char prefix[MAXSZ_IP_ADDRESS]; - int rc; - - rc = ip_address_snprintf(prefix, MAXSZ_IP_ADDRESS, &route->remote_addr, - route->family); - if (rc >= MAXSZ_IP_ADDRESS) - ; - if (rc < 0) return rc; - - return snprintf(s, size, "%s %*d %s %*d conn_id=%d", route->name, MAXSZ_COST, - route->cost, prefix, MAXSZ_LEN, route->len, route->face_id); -} +/* This function has to be called after the first execute until data and + * request are complete */ +// execute is just setting things up so that we can keep on calling this +// function repeatedly until completion. +// +// in the caller, we don't know how much we will receive in advance... so in +// asio for instance, we will use async_receive rather than async_read. +// XXX the question remains about the buffers... -int hc_route_validate(const hc_route_t *route) { - if (!IS_VALID_ID(route->connection_id)) { - ERROR("[hc_route_validate] Invalid connection id"); - return -1; - } - if (route->name[0] == '\0') { - if (!IS_VALID_FACE_ID(route->face_id)) { - ERROR("[hc_route_validate] Invalid face_id"); - return -1; - } - } else if (!IS_VALID_NAME(route->name) && !IS_VALID_STR_ID(route->name)) { - ERROR("[hc_route_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_FAMILY(route->family)) { - ERROR("[hc_route_validate] Invalid family specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&route->remote_addr, route->family)) { - ERROR("[hc_route_validate] Invalid remote_addr specified"); - return -1; - } - if (!IS_VALID_ROUTE_COST(route->cost)) { - ERROR("[hc_route_validate] Invalid cost"); - return -1; - } - if (!IS_VALID_PREFIX_LEN(route->len)) { - ERROR("[hc_route_validate] Invalid len"); - return -1; - } +/* + * request -> write command + * + * SYNC : hc_data_t + * ASYNC : provide socket-level callback + * + * socket available -> read -> parse -> populate data + * data complete -> + */ - return 0; -} +/****************************************************************************** + * OBJECT-SPECIFIC FUNCTIONS (backwards compatibility) + ******************************************************************************/ /*----------------------------------------------------------------------------* * FACE + * + * This is an abstraction provided for when the module does not implement + *it. Alternative is to move it to hicn light *----------------------------------------------------------------------------*/ +#if 0 + /* FACE -> LISTENER */ -int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener) { - const face_t *f = &face->face; - - switch (f->type) { - case FACE_TYPE_HICN_LISTENER: - break; - case FACE_TYPE_TCP_LISTENER: - break; - case FACE_TYPE_UDP_LISTENER: - break; - default: - return -1; - } - return -1; /* XXX Not implemented */ -} /* LISTENER -> FACE */ @@ -748,225 +352,10 @@ int hc_listener_to_face(const hc_listener_t *listener, hc_face_t *face) { /* FACE -> CONNECTION */ -int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, - bool generate_name) { - int rc; - - const face_t *f = &face->face; - switch (f->type) { - case FACE_TYPE_HICN: - *connection = (hc_connection_t){ - .type = FACE_TYPE_HICN, - .family = f->family, - .local_addr = f->local_addr, - .local_port = 0, - .remote_addr = f->remote_addr, - .remote_port = 0, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", - f->netdevice.name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of symbolic " - "name string"); - break; - case FACE_TYPE_TCP: - *connection = (hc_connection_t){ - .type = FACE_TYPE_TCP, - .family = f->family, - .local_addr = f->local_addr, - .local_port = f->local_port, - .remote_addr = f->remote_addr, - .remote_port = f->remote_port, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - if (generate_name) { - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "tcp%u", RANDBYTE()); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of " - "symbolic name string"); - } else { - memset(connection->name, 0, SYMBOLIC_NAME_LEN); - } - break; - case FACE_TYPE_UDP: - *connection = (hc_connection_t){ - .type = FACE_TYPE_UDP, - .family = f->family, - .local_addr = f->local_addr, - .local_port = f->local_port, - .remote_addr = f->remote_addr, - .remote_port = f->remote_port, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - if (generate_name) { - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "udp%u", RANDBYTE()); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of " - "symbolic name string"); - } else { - memset(connection->name, 0, SYMBOLIC_NAME_LEN); - } - snprintf(connection->interface_name, INTERFACE_LEN, "%s", - f->netdevice.name); - break; - default: - return -1; - } - - connection->id = face->id; - rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", - f->netdevice.name); - if (rc >= INTERFACE_LEN) - WARN( - "hc_face_to_connection] Unexpected truncation of interface name " - "string"); - - return 0; -} /* CONNECTION -> FACE */ - -int hc_connection_to_face(const hc_connection_t *connection, hc_face_t *face) { - int rc; - switch (connection->type) { - case FACE_TYPE_TCP: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_TCP, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - .remote_addr = connection->remote_addr, - .remote_port = connection->remote_port, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - case FACE_TYPE_UDP: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_UDP, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - .remote_addr = connection->remote_addr, - .remote_port = connection->remote_port, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - case FACE_TYPE_HICN: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_HICN, - .family = connection->family, - .netdevice.index = NETDEVICE_UNDEFINED_INDEX, // XXX - .local_addr = connection->local_addr, - .remote_addr = connection->remote_addr, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - default: - return -1; - } - face->face.netdevice.name[0] = '\0'; - face->face.netdevice.index = 0; - rc = snprintf(face->name, SYMBOLIC_NAME_LEN, "%s", connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_to_face] Unexpected truncation of symbolic name " - "string"); - rc = snprintf(face->face.netdevice.name, INTERFACE_LEN, "%s", - connection->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_to_face] Unexpected truncation of interface name " - "string"); - netdevice_update_index(&face->face.netdevice); - return 0; -} - /* CONNECTION -> LISTENER */ -int hc_connection_to_local_listener(const hc_connection_t *connection, - hc_listener_t *listener) { - int rc; - - face_type_t listener_type; - switch (connection->type) { - case FACE_TYPE_UDP: - listener_type = FACE_TYPE_UDP_LISTENER; - break; - case FACE_TYPE_TCP: - listener_type = FACE_TYPE_TCP_LISTENER; - break; - default: - return -1; - } - - *listener = (hc_listener_t){ - .id = ~0, - .type = listener_type, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - }; - rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "lst%u", - RANDBYTE()); // generate name - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_to_local_listener] Unexpected truncation of " - "symbolic name string"); - rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", - connection->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_to_local_listener] Unexpected truncation of " - "interface name string"); - - return 0; -} /*----------------------------------------------------------------------------* * Punting @@ -1013,7 +402,7 @@ int hc_punting_cmp(const hc_punting_t *p1, const hc_punting_t *p2) { rc = INT_CMP(p1->family, p2->family); if (rc != 0) return rc; - rc = ip_address_cmp(&p1->prefix, &p2->prefix, p1->family); + rc = ip_address_cmp(&p1->prefix, &p2->prefix); if (rc != 0) return rc; rc = INT_CMP(p1->prefix_len, p2->prefix_len); @@ -1022,10 +411,12 @@ int hc_punting_cmp(const hc_punting_t *p1, const hc_punting_t *p2) { return rc; } +#if 0 int hc_punting_parse(void *in, hc_punting_t *punting) { ERROR("hc_punting_parse not (yet) implemented."); return -1; } +#endif int hc_punting_snprintf(char *s, size_t size, hc_punting_t *punting) { ERROR("hc_punting_snprintf not (yet) implemented."); @@ -1083,36 +474,6 @@ int hc_stats_snprintf(char *s, size_t size, const hicn_light_stats_t *stats) { } /*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -int hc_strategy_list(hc_sock_t *s, hc_data_t **data) { - return s->hc_strategy_list(s, data); -} - -int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_set(s, strategy); -} - -int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_add_local_prefix(s, strategy); -} - -hc_result_t *hc_strategy_set_conf(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_set_conf(s, strategy); -} - -hc_result_t *hc_strategy_add_local_prefix_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - return s->hc_strategy_add_local_prefix_conf(s, strategy); -} - -/* /!\ Please update constants in header file upon changes */ -int hc_strategy_snprintf(char *s, size_t size, hc_strategy_t *strategy) { - return snprintf(s, size, "%s", strategy->name); -} - -/*----------------------------------------------------------------------------* * WLDR *----------------------------------------------------------------------------*/ @@ -1146,67 +507,55 @@ int hc_mapme_send_update(hc_sock_t *s, hc_mapme_t *mapme) { * Policy *----------------------------------------------------------------------------*/ -#ifdef WITH_POLICY /* POLICY SNPRINTF */ /* /!\ Please update constants in header file upon changes */ int hc_policy_snprintf(char *s, size_t size, hc_policy_t *policy) { return 0; } -int hc_policy_validate(const hc_policy_t *policy) { +int hc_policy_validate(const hc_policy_t *policy, bool allow_partial) { if (!IS_VALID_FAMILY(policy->family)) return -1; return 0; } -#endif /* WITH_POLICY */ +#endif /*----------------------------------------------------------------------------* - * SUBSCRIPTION + * VFT *----------------------------------------------------------------------------*/ -int hc_subscription_create(hc_sock_t *s, hc_subscription_t *subscription) { - return s->hc_subscription_create(s, subscription); -} - -int hc_subscription_delete(hc_sock_t *s, hc_subscription_t *subscription) { - return s->hc_subscription_delete(s, subscription); -} -hc_result_t *hc_subscription_create_conf(hc_sock_t *s, - hc_subscription_t *subscription) { - return s->hc_subscription_create_conf(s, subscription); -} - -hc_result_t *hc_subscription_delete_conf(hc_sock_t *s, - hc_subscription_t *subscription) { - return s->hc_subscription_delete_conf(s, subscription); -} - -/*----------------------------------------------------------------------------* - * STATISTICS - *----------------------------------------------------------------------------*/ -int hc_stats_get(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_stats_get(s, pdata); +int hc_object_create(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object) { + return hc_execute(s, ACTION_CREATE, object_type, object, NULL); } -int hc_stats_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_stats_list(s, pdata); +int hc_object_get(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object, hc_object_t **found) { + return hc_execute(s, ACTION_GET, object_type, object, NULL); } -/*----------------------------------------------------------------------------* - * Result - *----------------------------------------------------------------------------*/ - -hc_msg_t *hc_result_get_msg(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_msg(result); +int hc_object_find(hc_sock_t *s, hc_object_type_t object_type, hc_data_t *data, + const hc_object_t *element, hc_object_t **found) { +// XXX NOT IMPLEMENTED +#if 0 + foreach_type(hc_object_t, x, data) { + if (hc_object_cmp(x, element) == 0) { + *found = x; + return 0; + } + }; +#endif + *found = NULL; /* this is optional */ + return 0; } -int hc_result_get_cmd_id(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_cmd_id(result); +int hc_object_delete(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object) { + return hc_execute(s, ACTION_DELETE, object_type, object, NULL); } -bool hc_result_get_success(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_success(result); +int hc_object_list(hc_sock_t *s, hc_object_type_t object_type, + hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, object_type, NULL, pdata); } - -void hc_result_free(hc_result_t *result) { free(result); } diff --git a/ctrl/libhicnctrl/src/api_private.h b/ctrl/libhicnctrl/src/api_private.h index c708e1eb5..53be809da 100644 --- a/ctrl/libhicnctrl/src/api_private.h +++ b/ctrl/libhicnctrl/src/api_private.h @@ -20,30 +20,22 @@ #include <hicn/util/token.h> #include <hicn/util/log.h> #include <hicn/util/map.h> +#ifndef HICN_VPP_PLUGIN #include <hicn/util/sstrncpy.h> +#endif #include <hicn/validation.h> #include <ctype.h> -#define INT_CMP(x, y) ((x > y) ? 1 : (x < y) ? -1 : 0) #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) -#if 0 -#ifdef __APPLE__ -#define RANDBYTE() (u8)(arc4random() & 0xFF) -#else -#define RANDBYTE() (u8)(random() & 0xFF) -#endif -#endif -#define RANDBYTE() (u8)(rand() & 0xFF) - /* * Input validation */ -static inline bool IS_VALID_ADDRESS(const ip_address_t *addr, int family) { +static inline bool IS_VALID_ADDRESS(const hicn_ip_address_t *addr, int family) { char addr_str[INET6_ADDRSTRLEN]; - return !ip_address_empty(addr) && - ip_address_ntop(addr, addr_str, INET6_ADDRSTRLEN, family) >= 0; + return !hicn_ip_address_empty(addr) && + hicn_ip_address_ntop(addr, addr_str, INET6_ADDRSTRLEN, family) >= 0; } static inline bool IS_VALID_PREFIX_LEN(u8 len) { @@ -82,7 +74,6 @@ typedef struct hc_sock_impl_s hc_sock_impl_t; int hc_data_ensure_available(hc_data_t *data, size_t count); u8 *hc_data_get_next(hc_data_t *data); -int hc_data_set_error(hc_data_t *data); int hc_listener_to_face(const hc_listener_t *listener, hc_face_t *face); @@ -96,178 +87,4 @@ int hc_connection_to_local_listener(const hc_connection_t *connection, int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, bool generate_name); -struct hc_sock_s { - int (*hc_sock_get_next_seq)(hc_sock_t *s); - int (*hc_sock_set_nonblocking)(hc_sock_t *s); - int (*hc_sock_get_fd)(hc_sock_t *s); - int (*hc_sock_connect)(hc_sock_t *s); - int (*hc_sock_get_available)(hc_sock_t *s, u8 **buffer, size_t *size); - int (*hc_sock_send)(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq); - int (*hc_sock_recv)(hc_sock_t *s); - int (*hc_sock_process)(hc_sock_t *s, hc_data_t **data); - int (*hc_sock_callback)(hc_sock_t *s, hc_data_t **data); - int (*hc_sock_reset)(hc_sock_t *s); - void (*hc_sock_free)(hc_sock_t *s); - void (*hc_sock_increment_woff)(hc_sock_t *s, size_t bytes); - int (*hc_sock_prepare_send)(hc_sock_t *s, hc_result_t *result, - data_callback_t complete_cb, - void *complete_cb_data); - int (*hc_sock_set_recv_timeout_ms)(hc_sock_t *s, long timeout_ms); - int (*hc_listener_create)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_create_async)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_get)(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found); - int (*hc_listener_delete)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_delete_async)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_listener_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_listener_validate)(const hc_listener_t *listener); - int (*hc_listener_cmp)(const hc_listener_t *l1, const hc_listener_t *l2); - int (*hc_listener_parse)(void *in, hc_listener_t *listener); - - int (*hc_connection_create)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_create_async)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_get)(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found); - int (*hc_connection_update_by_id)(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection); - int (*hc_connection_update)(hc_sock_t *s, hc_connection_t *connection_current, - hc_connection_t *connection_updated); - int (*hc_connection_delete)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_delete_async)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_connection_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_connection_validate)(const hc_connection_t *connection); - int (*hc_connection_cmp)(const hc_connection_t *c1, - const hc_connection_t *c2); - int (*hc_connection_parse)(void *in, hc_connection_t *connection); - int (*hc_connection_set_admin_state)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - int (*hc_connection_set_admin_state_async)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - -#ifdef WITH_POLICY - int (*hc_connection_set_priority)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_connection_set_priority_async)(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority); - int (*hc_connection_set_tags)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); - int (*hc_connection_set_tags_async)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); -#endif // WITH_POLICY - - int (*hc_connection_snprintf)(char *s, size_t size, - const hc_connection_t *connection); - - int (*hc_face_create)(hc_sock_t *s, hc_face_t *face); - int (*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, uint8_t delete_listener); - int (*hc_face_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_face_list_async)(hc_sock_t *s); - int (*hc_face_set_admin_state)(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state); - int (*hc_face_set_admin_state_async)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - -#ifdef WITH_POLICY - int (*hc_face_set_priority)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_face_set_priority_async)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_face_set_tags)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); - int (*hc_face_set_tags_async)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); -#endif // WITH_POLICY - - int (*hc_face_snprintf)(char *s, size_t size, hc_face_t *face); - - int (*hc_route_parse)(void *in, hc_route_t *route); - int (*hc_route_create)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_create_async)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_delete)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_delete_async)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_route_list_async)(hc_sock_t *s); - - int (*hc_punting_create)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_create_async)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_get)(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found); - int (*hc_punting_delete)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_punting_validate)(const hc_punting_t *punting); - int (*hc_punting_cmp)(const hc_punting_t *c1, const hc_punting_t *c2); - int (*hc_punting_parse)(void *in, hc_punting_t *punting); - - int (*hc_cache_parse)(void *in, hc_cache_info_t *cache_info); - int (*hc_cache_set_store)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_set_serve)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_clear)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_cache_set_store_async)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_set_serve_async)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_snprintf)(char *s, size_t size, - const hc_cache_info_t *cache_info); - - int (*hc_strategy_list)(hc_sock_t *s, hc_data_t **data); - int (*hc_strategy_snprintf)(char *s, size_t size, hc_strategy_t *strategy); - int (*hc_strategy_set)(hc_sock_t *s, hc_strategy_t *strategy); - int (*hc_strategy_add_local_prefix)(hc_sock_t *s, hc_strategy_t *strategy); - - int (*hc_wldr_set)(hc_sock_t *s /* XXX */); - - int (*hc_mapme_set)(hc_sock_t *s, int enabled); - int (*hc_mapme_set_discovery)(hc_sock_t *s, int enabled); - int (*hc_mapme_set_timescale)(hc_sock_t *s, uint32_t timescale); - int (*hc_mapme_set_retx)(hc_sock_t *s, uint32_t timescale); - int (*hc_mapme_send_update)(hc_sock_t *s, hc_mapme_t *mapme); - -#ifdef WITH_POLICY - int (*hc_policy_parse)(void *in, hc_policy_t *policy); - int (*hc_policy_create)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_create_async)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_delete)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_delete_async)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_policy_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_policy_snprintf)(char *s, size_t size, hc_policy_t *policy); -#endif // WITH_POLICY - - int (*hc_subscription_create)(hc_sock_t *s, hc_subscription_t *subscription); - int (*hc_subscription_delete)(hc_sock_t *s, hc_subscription_t *subscription); - - int (*hc_stats_get)(hc_sock_t *s, hc_data_t **data); - int (*hc_stats_list)(hc_sock_t *s, hc_data_t **data); - - hc_result_t *(*hc_listener_create_conf)(hc_sock_t *s, - hc_listener_t *listener); - hc_result_t *(*hc_listener_list_conf)(hc_sock_t *s, hc_data_t **pdata); - hc_result_t *(*hc_connection_create_conf)(hc_sock_t *s, - hc_connection_t *connection); - hc_result_t *(*hc_connection_delete_conf)(hc_sock_t *s, - hc_connection_t *connection); - hc_result_t *(*hc_route_create_conf)(hc_sock_t *s, hc_route_t *route); - hc_result_t *(*hc_strategy_set_conf)(hc_sock_t *s, hc_strategy_t *strategy); - hc_result_t *(*hc_strategy_add_local_prefix_conf)(hc_sock_t *s, - hc_strategy_t *strategy); - hc_result_t *(*hc_subscription_create_conf)(hc_sock_t *s, - hc_subscription_t *subscription); - hc_result_t *(*hc_subscription_delete_conf)(hc_sock_t *s, - hc_subscription_t *subscription); - - hc_msg_t *(*hc_result_get_msg)(hc_result_t *result); - bool (*hc_result_get_success)(hc_result_t *result); - int (*hc_result_get_cmd_id)(hc_result_t *result); - void (*hc_result_free)(hc_result_t *result); - - // Reference to module containing the implementation - void *handle; -}; - #endif // HICN_API_PRIVATE_H diff --git a/ctrl/libhicnctrl/src/cli.h b/ctrl/libhicnctrl/src/cli.h new file mode 100644 index 000000000..b04448a6e --- /dev/null +++ b/ctrl/libhicnctrl/src/cli.h @@ -0,0 +1,4 @@ +#include <hicn/ctrl.h> + +typedef int (*command_function)(hc_sock_t *, hc_command_t *); +extern command_function command_vft[][ACTION_N]; diff --git a/ctrl/libhicnctrl/src/command.c b/ctrl/libhicnctrl/src/command.c new file mode 100644 index 000000000..77b6eb7e8 --- /dev/null +++ b/ctrl/libhicnctrl/src/command.c @@ -0,0 +1,127 @@ + +/** + * @file command.c + * @brief Implementation of commands. + */ + +#ifdef __linux__ +#define _GNU_SOURCE +#endif /* __linux__ */ +#include <search.h> /* tfind, tdestroy, twalk */ +#include <stdio.h> +#include <ctype.h> +#include <hicn/ctrl/command.h> + +#include <hicn/util/log.h> + +#include <hicn/ctrl/parse.h> + +/* Commands are registered in the following tree. */ +static void *commands_root = NULL; /**< Tree ordered by name */ + +#ifdef __linux__ +static void nothing_to_free() {} + +__attribute__((destructor)) static void command_clear() { + tdestroy(commands_root, nothing_to_free); +} +#endif /* __linux__ */ + +static int _command_compare(const command_parser_t *c1, + const command_parser_t *c2) { + if (c1->object_type != c2->object_type) + return c2->object_type - c1->object_type; + if (c1->action != c2->action) return c2->action - c1->action; + if (c1->nparams != c2->nparams) return c2->nparams - c1->nparams; + return 0; +} + +#define command_compare (int (*)(const void *, const void *))(_command_compare) + +void command_register(const command_parser_t *command) { + // Insert the command in the tree if the keys does not exist yet + tsearch(command, &commands_root, command_compare); +} + +const command_parser_t *command_search(const hc_action_t action, + hc_object_type_t object_type, + unsigned nparams) { + command_parser_t **command, search; + + search.action = action; + search.object_type = object_type; + search.nparams = nparams; + command = tfind(&search, &commands_root, command_compare); + + return command ? *command : NULL; +} + +static inline void to_lowercase(char *p) { + for (; *p; ++p) *p = tolower(*p); +} + +typedef struct { + hc_object_type_t object_type; + hc_action_t action; +} cmd_search_params_t; + +static hc_object_type_t prev_obj = OBJECT_TYPE_UNDEFINED; +static hc_action_t prev_action = ACTION_UNDEFINED; +static void traversal_action(const void *nodep, VISIT which, + void *cmd_params0) { + cmd_search_params_t *cmd_params = cmd_params0; + + // Execute this function during inorder traversal + if (which != postorder && which != leaf) return; + + command_parser_t *datap; + datap = *(command_parser_t **)nodep; + char *obj_str = strdup(object_type_str(datap->object_type)); + to_lowercase(obj_str); + + // List all objects + if (cmd_params->object_type == OBJECT_TYPE_UNDEFINED && + cmd_params->action == ACTION_UNDEFINED) { + if (datap->object_type == prev_obj) goto FREE_STR; + prev_obj = datap->object_type; + + printf("\thelp %s\n", obj_str); + goto FREE_STR; + } + + // List actions for specific object + if (datap->object_type != cmd_params->object_type) goto FREE_STR; + if (cmd_params->action == ACTION_UNDEFINED) { + if (datap->action == prev_action) goto FREE_STR; + prev_action = datap->action; + + printf("\thelp %s %s\n", obj_str, action_to_cmd_action(datap->action)); + goto FREE_STR; + } + + // List commands for specific object and action + if (datap->action != cmd_params->action) goto FREE_STR; + printf(" %s %s ", action_to_cmd_action(datap->action), obj_str); + for (int i = 0; i < datap->nparams; i++) + printf("<%s> ", datap->parameters[i].name); + printf("\n\n"); + // List options' details + if (datap->nparams == 0) goto FREE_STR; + for (int i = 0; i < datap->nparams; i++) + printf("%16s: %s\n", datap->parameters[i].name, datap->parameters[i].help); + printf("\n"); + +FREE_STR: + free(obj_str); +} + +void command_list(hc_object_type_t object_type, hc_action_t action) { +#if defined(__linux__) && !defined(__ANDROID__) + cmd_search_params_t cmd_params = {.object_type = object_type, + .action = action}; + twalk_r(commands_root, traversal_action, &cmd_params); +#else + fprintf(stderr, "twalk_r() function only available on linux"); + (void)traversal_action; +#endif +} diff --git a/ctrl/libhicnctrl/src/commands/command_cache.c b/ctrl/libhicnctrl/src/commands/command_cache.c new file mode 100644 index 000000000..124fcd761 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_cache.c @@ -0,0 +1,54 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define serve \ + { \ + .name = "serve", \ + .help = \ + "Enables/disables replies from local content store. Either the " \ + "string 'on' or 'off'", \ + .type = TYPE_ON_OFF, .offset = offsetof(hc_cache_t, serve), \ + } + +#define store \ + { \ + .name = "store", \ + .help = \ + "enables/disables the storage of incoming data packets in the local " \ + "content store. Either the string 'on' or 'off'", \ + .type = TYPE_ON_OFF, .offset = offsetof(hc_cache_t, store), \ + } + +/* Commands */ + +static const command_parser_t command_cache_set_serve = { + .action = ACTION_SERVE, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 1, + .parameters = {serve}, +}; +COMMAND_REGISTER(command_cache_set_serve); + +static const command_parser_t command_cache_set_store = { + .action = ACTION_STORE, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 1, + .parameters = {store}, +}; +COMMAND_REGISTER(command_cache_set_store); + +static const command_parser_t command_cache_clear = { + .action = ACTION_CLEAR, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 0, +}; +COMMAND_REGISTER(command_cache_clear); + +static const command_parser_t command_cache_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 0, +}; +COMMAND_REGISTER(command_cache_list); diff --git a/ctrl/libhicnctrl/src/commands/command_connection.c b/ctrl/libhicnctrl/src/commands/command_connection.c new file mode 100644 index 000000000..30b3c3bf1 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_connection.c @@ -0,0 +1,147 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define type_hicn \ + { \ + .name = "type", .help = "connection type (hICN)", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_connection_t, type), \ + } + +#define type_tcp_udp \ + { \ + .name = "type", .help = "connection type [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_connection_t, type), \ + } + +#define symbolic \ + { \ + .name = "symbolic", \ + .help = "symbolic name, e.g. 'conn1' (must be unique, start with alpha)", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_connection_t, name), \ + } + +#define local_address \ + { \ + .name = "local_addr", .help = "local IP address on which to bind.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_connection_t, local_addr), \ + .offset2 = offsetof(hc_connection_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_connection_t, local_port), \ + } + +#define remote_address \ + { \ + .name = "remote_address", \ + .help = "The IPv4 or IPv6 or hostname of the remote system.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_connection_t, remote_addr), \ + .offset2 = offsetof(hc_connection_t, family), \ + } + +#define remote_port \ + { \ + .name = "remote_port", .help = "Remote port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_connection_t, remote_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_connection_t, interface_name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The connection symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_connection_t, name), \ + } + +/* Commands */ + +int on_connection_create(hc_connection_t* connection) { + connection->admin_state = FACE_STATE_UP; + return 0; +} + +#if 0 +static command_parser_t command_connection_create4 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 4, + .parameters = {type_hicn, symbolic, local_address, remote_address}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create4); + +static const command_parser_t command_connection_create5 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_CONNECTION, + .nparams = 5, + .parameters = {type_hicn, symbolic, local_address, remote_address, + interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create5); +#endif + +static const command_parser_t command_connection_create4 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 4, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create4); + +static const command_parser_t command_connection_create5 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 5, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create5); + +static const command_parser_t command_connection_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 6, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + local_address, local_port}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create6); + +static const command_parser_t command_connection_create7 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 7, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + local_address, local_port, interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create7); + +static const command_parser_t command_connection_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 0, +}; +COMMAND_REGISTER(command_connection_list); + +static const command_parser_t command_connection_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 1, + .parameters = {symbolic_or_id}, +}; +COMMAND_REGISTER(command_connection_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_face.c b/ctrl/libhicnctrl/src/commands/command_face.c new file mode 100644 index 000000000..68a6abefe --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_face.c @@ -0,0 +1,112 @@ +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define type_hicn \ + { \ + .name = "type", .help = "face type (hICN)", .type = TYPE_ENUM(face_type), \ + .offset = offsetof(hc_face_t, type), \ + } + +#define type_tcp_udp \ + { \ + .name = "type", .help = "face type [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_face_t, type), \ + } + +#define local_address \ + { \ + .name = "local_addr", .help = "local IP address on which to bind.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_face_t, local_addr), \ + .offset2 = offsetof(hc_face_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_face_t, local_port), \ + } + +#define remote_address \ + { \ + .name = "remote_address", \ + .help = "The IPv4 or IPv6 or hostname of the remote system.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_face_t, remote_addr), \ + .offset2 = offsetof(hc_face_t, family), \ + } + +#define remote_port \ + { \ + .name = "remote_port", .help = "Remote port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_face_t, remote_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_face_t, netdevice) + offsetof(netdevice_t, name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The face symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_face_t, name), \ + } + +/* Commands */ + +int on_face_create(hc_face_t* face) { + face->admin_state = FACE_STATE_UP; + return 0; +} + +static command_parser_t command_face_create3 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 3, + .parameters = {type_hicn, local_address, remote_address}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create3); + +#if 0 +static const command_parser_t command_face_create4 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_FACE, + .nparams = 4, + .parameters = {type_hicn, local_address, remote_address, + interface}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create4); +#endif + +static const command_parser_t command_face_create5 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 5, + .parameters = {type_tcp_udp, remote_address, remote_port, local_address, + local_port}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create5); + +static const command_parser_t command_face_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 6, + .parameters = {type_tcp_udp, remote_address, remote_port, local_address, + local_port, interface}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create6); + +static const command_parser_t command_face_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_FACE, + .nparams = 0, +}; +COMMAND_REGISTER(command_face_list); diff --git a/ctrl/libhicnctrl/src/commands/command_listener.c b/ctrl/libhicnctrl/src/commands/command_listener.c new file mode 100644 index 000000000..bba4f4541 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_listener.c @@ -0,0 +1,111 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define protocol_hicn \ + { \ + .name = "protocol", .help = "Protocol [hicn].", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_listener_t, type), \ + } + +#define protocol_tcp_udp \ + { \ + .name = "protocol", .help = "Protocol [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_listener_t, type), \ + } + +#define symbolic \ + { \ + .name = "symbolic", \ + .help = \ + "User defined name for listener, must start with alpha and be " \ + "alphanum", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_listener_t, name), \ + } + +#define local_address \ + { \ + .name = "local_addr", \ + .help = \ + "IPv4 or IPv6 address (or prefix protocol = hicn) assigend to the " \ + "local interface", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_listener_t, local_addr), \ + .offset2 = offsetof(hc_listener_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_listener_t, local_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_listener_t, interface_name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The listener symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_listener_t, name), \ + } + +/* Commands */ + +/* The parse sets the wrong face_type_t for listener, we fix that here */ +int on_listener_create(hc_listener_t* listener) { + switch (listener->type) { + case FACE_TYPE_UDP: + listener->type = FACE_TYPE_UDP_LISTENER; + break; + case FACE_TYPE_TCP: + listener->type = FACE_TYPE_TCP_LISTENER; + break; + case FACE_TYPE_HICN: + listener->type = FACE_TYPE_HICN_LISTENER; + break; + default: + break; + } + return 0; +} + +#if 0 +static const command_parser_t command_listener_create4 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_LISTENER, + .nparams = 4, + .parameters = {protocol_hicn, symbolic, local_address, interface}, + .post_hook = (parser_hook_t)on_listener_create, +}; +COMMAND_REGISTER(command_listener_create4); +#endif + +static const command_parser_t command_listener_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 5, + .parameters = {protocol_tcp_udp, symbolic, local_address, local_port, + interface}, + .post_hook = (parser_hook_t)on_listener_create, +}; +COMMAND_REGISTER(command_listener_create6); + +static const command_parser_t command_listener_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 0, +}; +COMMAND_REGISTER(command_listener_list); + +static const command_parser_t command_listener_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 1, + .parameters = {symbolic_or_id}, +}; +COMMAND_REGISTER(command_listener_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_mapme.c b/ctrl/libhicnctrl/src/commands/command_mapme.c new file mode 100644 index 000000000..c67b7704f --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_mapme.c @@ -0,0 +1,59 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define target \ + { \ + .name = "target", \ + .help = \ + "Target for the set action, e.g. enable, discovery, timescale, retx", \ + .type = TYPE_ENUM(mapme_target), .offset = offsetof(hc_mapme_t, target), \ + } + +#define value \ + { \ + .name = "value", \ + .help = "Value to set for the target, e.g. 'on', 'off', milliseconds", \ + .type = TYPE_STRN(4), .offset = offsetof(hc_mapme_t, unparsed_arg), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_mapme_t, address), \ + .offset2 = offsetof(hc_mapme_t, len), \ + .offset3 = offsetof(hc_mapme_t, family), \ + } + +/* Commands */ + +// Parse the raw string argument into 'timescale' or 'enabled', +// necessary since the command dispatch is based on the number +// of arguments and not their type +int parse_args(hc_mapme_t* mapme) { + mapme->timescale = atoi(mapme->unparsed_arg); + + if (strcasecmp(mapme->unparsed_arg, "off") == 0) mapme->enabled = 0; + if (strcasecmp(mapme->unparsed_arg, "on") == 0) mapme->enabled = 1; + + return 0; +} + +static const command_parser_t command_mapme_set = { + .action = ACTION_SET, + .object_type = OBJECT_TYPE_MAPME, + .nparams = 2, + .parameters = {target, value}, + .post_hook = (parser_hook_t)parse_args, +}; +COMMAND_REGISTER(command_mapme_set); + +static const command_parser_t command_mapme_update = { + .action = ACTION_UPDATE, + .object_type = OBJECT_TYPE_MAPME, + .nparams = 1, + .parameters = {prefix}, +}; +COMMAND_REGISTER(command_mapme_update); diff --git a/ctrl/libhicnctrl/src/commands/command_policy.c b/ctrl/libhicnctrl/src/commands/command_policy.c new file mode 100644 index 000000000..2fc7a0a42 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_policy.c @@ -0,0 +1,52 @@ +#if 0 +#include <hicn/policy.h> + +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_policy_t, remote_addr), \ + .offset2 = offsetof(hc_policy_t, len), \ + .offset3 = offsetof(hc_policy_t, family), \ + } + +#define app_name \ + { \ + .name = "app_name", \ + .help = "The application name associated to this policy", \ + .type = TYPE_STR, .offset = offsetof(hc_policy_t, policy.app_name), \ + } + +/* Commands */ + +static const command_parser_t command_policy_create = { + .action = ACTION_CREATE, + .object = OBJECT_POLICY, + .nparams = 2 + POLICY_TAG_N, + .parameters = {prefix, app_name, +#define _(x, y) \ + { \ + .name = "flag:" #x, \ + .help = \ + "A value among [neutral|require|prefer|avoid|prohibit] with an " \ + "optional '!' character prefix for disabling changes", \ + .type = TYPE_POLICY_STATE(POLICY_TAG_##x), \ + .offset = offsetof(hc_policy_t, policy.tags), \ + }, + foreach_policy_tag +#undef _ + }, +}; +COMMAND_REGISTER(command_policy_create); + +static const command_parser_t command_policy_list = { + .action = ACTION_LIST, + .object = OBJECT_POLICY, + .nparams = 0, +}; +COMMAND_REGISTER(command_policy_list); +#endif diff --git a/ctrl/libhicnctrl/src/commands/command_punting.c b/ctrl/libhicnctrl/src/commands/command_punting.c new file mode 100644 index 000000000..b845c52ee --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_punting.c @@ -0,0 +1,40 @@ +#if 0 +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define symbolic_or_id \ + { \ + .name = "symbolic_or_id", \ + .help = \ + "The symbolic name for an egress, or the egress punting id (see " \ + "'help list puntings')", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_punting_t, face_id), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "Prefix to add as a punting rule. (example 1234::0/64)", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_punting_t, prefix), \ + .offset2 = offsetof(hc_punting_t, prefix_len), \ + .offset3 = offsetof(hc_punting_t, family), \ + } + +/* Commands */ + +static const command_parser_t command_punting_create = { + .action = ACTION_CREATE, + .object = OBJECT_PUNTING, + .nparams = 2, + .parameters = {symbolic_or_id, prefix}, +}; +COMMAND_REGISTER(command_punting_create); + +static const command_parser_t command_punting_list = { + .action = ACTION_LIST, + .object = OBJECT_PUNTING, + .nparams = 0, +}; +COMMAND_REGISTER(command_punting_list); +#endif diff --git a/ctrl/libhicnctrl/src/commands/command_route.c b/ctrl/libhicnctrl/src/commands/command_route.c new file mode 100644 index 000000000..8e7db8192 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_route.c @@ -0,0 +1,53 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define symbolic_or_id \ + { \ + .name = "symbolic_or_id", \ + .help = \ + "The symbolic name for an egress, or the egress route id (see 'help " \ + "list routes')", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_route_t, face_name), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_route_t, remote_addr), \ + .offset2 = offsetof(hc_route_t, len), \ + .offset3 = offsetof(hc_route_t, family), \ + } + +#define cost \ + { \ + .name = "cost", .help = "Positive integer representing cost.", \ + .type = TYPE_INT(1, 255), .offset = offsetof(hc_route_t, cost), \ + } + +/* Commands */ + +static const command_parser_t command_route_create = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 3, + .parameters = {symbolic_or_id, prefix, cost}, +}; +COMMAND_REGISTER(command_route_create); + +static const command_parser_t command_route_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 0, +}; +COMMAND_REGISTER(command_route_list); + +static const command_parser_t command_route_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 2, + .parameters = {symbolic_or_id, prefix}, +}; +COMMAND_REGISTER(command_route_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_stats.c b/ctrl/libhicnctrl/src/commands/command_stats.c new file mode 100644 index 000000000..7c58b105e --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_stats.c @@ -0,0 +1,18 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Commands */ + +static const command_parser_t command_stats_get = { + .action = ACTION_GET, + .object_type = OBJECT_TYPE_STATS, + .nparams = 0, +}; +COMMAND_REGISTER(command_stats_get); + +static const command_parser_t command_stats_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_STATS, + .nparams = 0, +}; +COMMAND_REGISTER(command_stats_list); diff --git a/ctrl/libhicnctrl/src/commands/command_strategy.c b/ctrl/libhicnctrl/src/commands/command_strategy.c new file mode 100644 index 000000000..5605822e7 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_strategy.c @@ -0,0 +1,47 @@ +#include <hicn/ctrl/command.h> + +/* Parameters */ +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_strategy_t, address), \ + .offset2 = offsetof(hc_strategy_t, len), \ + .offset3 = offsetof(hc_strategy_t, family), \ + } + +#define strategy \ + { \ + .name = "strategy", \ + .help = \ + "Strategy type (e.g. 'random', 'loadbalancer', 'low_latency', " \ + "'replication', 'bestpath').", \ + .type = TYPE_ENUM(strategy_type), .offset = offsetof(hc_strategy_t, type), \ + } + +#define local_prefix \ + { \ + .name = "local_prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_strategy_t, local_address), \ + .offset2 = offsetof(hc_strategy_t, local_len), \ + .offset3 = offsetof(hc_strategy_t, local_family), \ + } + +/* Commands */ + +static const command_parser_t command_strategy_list = { + .action = ACTION_SET, + .object_type = OBJECT_TYPE_STRATEGY, + .nparams = 2, + .parameters = {prefix, strategy}, +}; +COMMAND_REGISTER(command_strategy_list); + +static const command_parser_t local_prefix_add = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_LOCAL_PREFIX, + .nparams = 3, + .parameters = {prefix, strategy, local_prefix}, +}; +COMMAND_REGISTER(local_prefix_add); diff --git a/ctrl/libhicnctrl/src/commands/command_subscription.c b/ctrl/libhicnctrl/src/commands/command_subscription.c new file mode 100644 index 000000000..886ee454a --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_subscription.c @@ -0,0 +1,26 @@ +#include <limits.h> + +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define topics \ + { \ + .name = "topics", \ + .help = \ + "Topics to subscribe to, e.g. 6 (110 in binary) means topic 2 (10 in " \ + "binary, TOPIC_CONNECTION) and topic 4 (100 in binary, " \ + "TOPIC_LISTENER).", \ + .type = TYPE_INT(1, INT_MAX), \ + .offset = offsetof(hc_subscription_t, topics), \ + } + +/* Commands */ + +static const command_parser_t command_subscription_create = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_SUBSCRIPTION, + .nparams = 1, + .parameters = {topics}, +}; +COMMAND_REGISTER(command_subscription_create); diff --git a/ctrl/libhicnctrl/src/data.c b/ctrl/libhicnctrl/src/data.c new file mode 100644 index 000000000..605e23601 --- /dev/null +++ b/ctrl/libhicnctrl/src/data.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file data.c + * \brief Implementation of request result data. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +#include <hicn/util/log.h> + +#define MIN_ALLOC_SIZE 8 +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +struct hc_data_s { + hc_object_type_t object_type; + bool complete; + + /** + * >=0 success, indicates the number of records in array + * <0 error + */ + ssize_t size; + size_t alloc_size; /** Allocated size (a power of 2 when managed + automatically) */ + size_t max_size; /** Maximum size defined at creation (0 = unlimited) */ + + uint8_t *buffer; +}; + +void _hc_data_clear(hc_data_t *data) { + data->complete = false; + data->buffer = NULL; + data->max_size = 0; + data->alloc_size = 0; + data->size = 0; +} + +hc_data_t *hc_data_create(hc_object_type_t object_type) { + hc_data_t *data = malloc(sizeof(hc_data_t)); + if (!data) return NULL; + + data->object_type = object_type; + + _hc_data_clear(data); + + return data; + // data->buffer = malloc((1 << data->max_size_log) * data->out_element_size); + // if (!data->buffer) goto ERR_BUFFER; +} + +void hc_data_free(hc_data_t *data) { + assert(data); + + if (data->buffer) free(data->buffer); + free(data); +} + +int hc_data_set_max_size(hc_data_t *data, size_t max_size) { + if (data->size > max_size) return -1; + data->max_size = max_size; + return 0; +} + +const uint8_t *hc_data_get_buffer(hc_data_t *data) { return data->buffer; } + +const uint8_t *hc_data_get_free(hc_data_t *data) { + if (!data) return NULL; + if (data->max_size > 0 && data->size >= data->max_size) return NULL; + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + return data->buffer + data->size * object_size; +} + +void hc_data_inc_size(hc_data_t *data) { data->size++; } + +hc_object_type_t hc_data_get_object_type(const hc_data_t *data) { + return data->object_type; +} + +void hc_data_set_object_type(hc_data_t *data, hc_object_type_t object_type) { + data->object_type = object_type; +} + +ssize_t hc_data_get_size(const hc_data_t *data) { return data->size; } + +int hc_data_clear(hc_data_t *data) { + free(data->buffer); + _hc_data_clear(data); + return 0; +} + +int _hc_data_allocate(hc_data_t *data, size_t size) { + data->buffer = + realloc(data->buffer, size * hc_object_size(data->object_type)); + if (!data->buffer) goto ERR; + data->alloc_size = size; + return 0; +ERR: + data->alloc_size = 0; + return -1; +} + +int hc_data_allocate(hc_data_t *data, size_t size) { + /* Do not allocate twice */ + if (data->buffer) return -1; + if (data->max_size > 0 && size > data->max_size) return -1; + return _hc_data_allocate(data, size); +} + +int hc_data_ensure_available(hc_data_t *data, size_t count) { + size_t new_size = data->size + count; + if (new_size < data->alloc_size) return 0; + if (data->max_size > 0 && new_size > data->max_size) return -1; + + size_t new_alloc_size = MAX(MIN_ALLOC_SIZE, next_pow2(new_size)); + if (data->max_size > 0 && new_alloc_size > data->max_size) + new_alloc_size = data->max_size; + + return _hc_data_allocate(data, new_alloc_size); +} + +int hc_data_push_many(hc_data_t *data, const void *elements, size_t count) { + if (!data) return -1; + if (!elements) return -1; + if (count < 1) return -1; + + if (hc_data_ensure_available(data, count) < 0) return -1; + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + + uint8_t *dst = data->buffer + data->size * object_size; + memcpy(dst, elements, count * object_size); + data->size += count; + + return 0; +} + +int hc_data_push(hc_data_t *data, const void *element) { + return hc_data_push_many(data, element, 1); +} +#if 0 +/** + * + * NOTE: This function make sure there is enough room available in the data + * structure. + */ +u8 *hc_data_get_next(hc_data_t *data) { + if (hc_data_ensure_available(data, 1) < 0) return NULL; + + return data->buffer + data->size * data->out_element_size; +} + +int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data) { + data->complete_cb = cb; + data->complete_cb_data = cb_data; + return 0; +} +#endif + +void hc_data_set_complete(hc_data_t *data) { data->complete = true; } + +bool hc_data_is_complete(const hc_data_t *data) { return data->complete; } + +void hc_data_set_error(hc_data_t *data) { + data->size = -1; + data->complete = true; +} + +bool hc_data_get_result(hc_data_t *data) { return (data->size >= 0); } + +hc_object_t *hc_data_find(hc_data_t *data, hc_object_t *object) { + hc_object_type_t object_type = hc_data_get_object_type(data); + hc_data_foreach(data, found, { + if (hc_object_cmp(object_type, object, found) == 0) return found; + }); + return NULL; +} + +const hc_object_t *hc_data_get_object(const hc_data_t *data, off_t pos) { + size_t size = hc_data_get_size(data); + if (pos >= size) return NULL; + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + + return (const hc_object_t *)(data->buffer + pos * object_size); +} + +#if 0 +int hc_data_reset(hc_data_t *data) { + data->size = 0; + return 0; +} +#endif diff --git a/ctrl/libhicnctrl/src/fw_interface.c b/ctrl/libhicnctrl/src/fw_interface.c new file mode 100644 index 000000000..6d5e5fb34 --- /dev/null +++ b/ctrl/libhicnctrl/src/fw_interface.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file fw_interface.c + * \brief Implementation of fw interface + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/callback.h> +#include <hicn/util/log.h> +#include <hicn/ctrl/fw_interface.h> + +const char *fw_state_str[] = { +#define _(x) [FW_STATE_##x] = #x, + foreach_fw_state +#undef _ +}; + +struct fw_interface_s { + /* + * Type of forwarder to which we are connecting/connected : HICNLIGHT, VPP + */ + forwarder_type_t type; + fw_state_t state; + + hc_sock_t *sock; + char *url; + + bool has_subscribe_all; + + hc_enable_callback_t enable_callback; + hc_state_callback_t state_callback; + void *state_callback_data; + hc_result_callback_t result_callback; + void *result_callback_data; + hc_notification_callback_t notification_callback; + void *notification_callback_data; +}; + +fw_interface_t *fw_interface_create_url(forwarder_type_t type, + const char *url) { + fw_interface_t *fi = malloc(sizeof(fw_interface_t)); + if (!fi) goto ERR_MALLOC; + + fi->type = type; + /* Let's assume for now the forwarder is always on */ + fi->state = FW_STATE_AVAILABLE; + fi->sock = NULL; + fi->has_subscribe_all = false; + fi->url = NULL; + + // XXX make a single request to probe for forwarder size? + + return fi; + +ERR_MALLOC: + return NULL; +} + +fw_interface_t *fw_interface_create(forwarder_type_t type) { + return fw_interface_create_url(type, NULL); +} + +void fw_interface_free(fw_interface_t *fi) { + fw_interface_disconnect(fi); + free(fi); +} + +int fw_interface_get_fd(const fw_interface_t *fi) { + if (!fi) return 0; + return hc_sock_get_fd(fi->sock); +} + +int fw_interface_set_enable_callback(fw_interface_t *fi, + hc_enable_callback_t callback) { + fi->enable_callback = callback; + return 0; +} + +int fw_interface_set_state_callback(fw_interface_t *fi, + hc_state_callback_t callback, + void *callback_data) { + fi->state_callback = callback; + fi->state_callback_data = callback_data; + return 0; +} + +int fw_interface_set_result_callback(fw_interface_t *fi, + hc_result_callback_t callback, + void *callback_data) { + fi->result_callback = callback; + fi->result_callback_data = callback_data; + return 0; +} + +int fw_interface_set_notification_callback(fw_interface_t *fi, + hc_notification_callback_t callback, + void *callback_data) { + fi->notification_callback = callback; + fi->notification_callback_data = callback_data; + return 0; +} + +int fw_interface_enable(fw_interface_t *fi) { + // TODO + return 0; +} + +int fw_interface_disable(fw_interface_t *fi) { + // TODO + return 0; +} + +// XXX blocking or non blocking ? +int fw_interface_reschedule_connect(fw_interface_t *fi) { + INFO("Scheduling reconnect..."); + // XXX TODO timer + return 0; +} + +int _fw_interface_connect(fw_interface_t *fi, bool reattempt) { + fi->sock = hc_sock_create(fi->type, fi->url); + if (!fi->sock) goto ERR_SOCK; + + if (hc_sock_set_async(fi->sock) < 0) goto ERR_ASYNC; + + if (hc_sock_connect(fi->sock) < 0) { + ERROR("Error connecting to forwarder"); + return -1; + } + + return 0; + +ERR_ASYNC: + hc_sock_free(fi->sock); +ERR_SOCK: + + if (reattempt) return fw_interface_reschedule_connect(fi); + return -1; +} + +int fw_interface_connect(fw_interface_t *fi) { + switch (fi->state) { + case FW_STATE_UNDEFINED: + // XXX connect, enable, (poll)? + break; + case FW_STATE_DISABLED: + fw_interface_enable(fi); + break; + case FW_STATE_REQUESTED: + // XXX waiting ? polling connect ? + break; + case FW_STATE_AVAILABLE: + _fw_interface_connect(fi, true); + // XXX + break; + case FW_STATE_CONNECTING: + case FW_STATE_CONNECTED: + case FW_STATE_READY: + /* Nothing to do */ + return 0; + case FW_STATE_N: + return -1; + } + return 0; +} + +int _fw_interface_disconnect(fw_interface_t *fi) { + if (fi->has_subscribe_all) fw_interface_unsubscribe_all(fi); + hc_sock_free(fi->sock); + return 0; +} + +int fw_interface_disconnect(fw_interface_t *fi) { + switch (fi->state) { + case FW_STATE_UNDEFINED: + case FW_STATE_DISABLED: + case FW_STATE_REQUESTED: + case FW_STATE_AVAILABLE: + /* Nothing to do */ + return 0; + case FW_STATE_CONNECTING: + case FW_STATE_CONNECTED: + case FW_STATE_READY: + _fw_interface_disconnect(fi); + return 0; + case FW_STATE_N: + return -1; + } + return 0; +} + +fw_state_t fw_interface_get_state(const fw_interface_t *fi) { + return fi->state; +} + +bool fw_interface_is_connected(const fw_interface_t *fi) { + return ((fi->state == FW_STATE_CONNECTED) || (fi->state == FW_STATE_READY)); +} + +bool fw_interface_is_ready(const fw_interface_t *fi) { + return (fi->state == FW_STATE_READY); +} + +int fw_interface_subscribe_all(fw_interface_t *fi) { + INFO("fw_interface_subscribe_all"); + int rc = hc_execute_async(fi->sock, ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED, + NULL, fi->notification_callback, + fi->notification_callback_data); + if (rc < 0) { + return -1; + } + fi->has_subscribe_all = true; + return 0; +} + +int fw_interface_unsubscribe_all(fw_interface_t *fi) { + fi->has_subscribe_all = false; + return 0; +} + +// face manager : upon completion, same as notification, CREATE/GET FACE +// hproxy = event = function to call to proceed through state machine (also +// depends if we handle face+route), for notifications, telemetry. +// NOTE we should have a notif for our own events. how to handle ? +// XXX user_data .... or user_callback +int fw_interface_execute(fw_interface_t *fi, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, + hc_data_t **pdata) { + return hc_execute(fi->sock, action, object_type, object, pdata); +} + +int fw_interface_execute_async(fw_interface_t *fi, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + if (!callback) { + callback = fi->result_callback; + callback_data = fi->result_callback_data; + } + return hc_execute_async(fi->sock, action, object_type, object, callback, + callback_data); +} + +int fw_interface_on_receive(fw_interface_t *fi, size_t count) { + return hc_sock_on_receive(fi->sock, count); +} + +int fw_interface_get_recv_buffer(fw_interface_t *fi, uint8_t **buffer, + size_t *size) { + return hc_sock_get_recv_buffer(fi->sock, buffer, size); +} diff --git a/ctrl/libhicnctrl/src/hicnctrl.c b/ctrl/libhicnctrl/src/hicnctrl.c index 99d67b19f..c771bde69 100644 --- a/ctrl/libhicnctrl/src/hicnctrl.c +++ b/ctrl/libhicnctrl/src/hicnctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2021-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: @@ -24,20 +24,18 @@ #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"); \ - rc = -1; \ goto ERR_##LABEL; \ } while (0) -const char HICNLIGHT_PARAM[] = "hicnlight"; -const char HICNLIGHT_NG_PARAM[] = "hicnlightng"; -const char VPP_PARAM[] = "vpp"; - void usage_header() { fprintf(stderr, "Usage:\n"); } void usage_face_create(const char *prog, bool header, bool verbose) { @@ -201,69 +199,66 @@ void usage(const char *prog) { usage_connection(prog, false, true); } -#if 0 -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_command_t; -#endif +/* + * 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_UNDEFINED; + command->object_type = OBJECT_TYPE_UNDEFINED; command->action = ACTION_CREATE; int opt; - int family; - while ((opt = getopt(argc, argv, "dflcrFLCRShz:")) != -1) { + while ((opt = getopt(argc, argv, "cCdfFlLrRsShz:")) != -1) { switch (opt) { case 'z': - if (strncmp(optarg, VPP_PARAM, strlen(VPP_PARAM)) == 0) { - *forwarder = VPP; - } else if (strncmp(optarg, HICNLIGHT_PARAM, strlen(HICNLIGHT_PARAM))) { - *forwarder = HICNLIGHT; - } else if (strncmp(optarg, HICNLIGHT_NG_PARAM, - strlen(HICNLIGHT_NG_PARAM))) { - *forwarder = HICNLIGHT_NG; - } else { - usage(argv[0]); - exit(EXIT_FAILURE); - } + *forwarder = forwarder_type_from_str(optarg); + if (*forwarder == FORWARDER_TYPE_UNDEFINED) goto USAGE; break; case 'd': - command->action = ACTION_DELETE; + set_command(ACTION_DELETE, OBJECT_TYPE_UNDEFINED); + break; + case 's': + set_command(ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED); break; case 'f': - command->object.type = OBJECT_FACE; + set_command(ACTION_UNDEFINED, OBJECT_TYPE_FACE); break; case 'c': - command->object.type = OBJECT_CONNECTION; + 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': - command->action = ACTION_LIST; - command->object.type = OBJECT_FACE; + set_command(ACTION_LIST, OBJECT_TYPE_FACE); break; case 'L': - command->action = ACTION_LIST; - command->object.type = OBJECT_LISTENER; + set_command(ACTION_LIST, OBJECT_TYPE_LISTENER); break; case 'C': - command->action = ACTION_LIST; - command->object.type = OBJECT_CONNECTION; + set_command(ACTION_LIST, OBJECT_TYPE_CONNECTION); break; case 'R': - command->action = ACTION_LIST; - command->object.type = OBJECT_ROUTE; + set_command(ACTION_LIST, OBJECT_TYPE_ROUTE); break; case 'S': - command->action = ACTION_LIST; - command->object.type = OBJECT_STRATEGY; + set_command(ACTION_LIST, OBJECT_TYPE_STRATEGY); break; default: /* "h" */ usage(argv[0]); @@ -271,535 +266,129 @@ int parse_options(int argc, char *argv[], hc_command_t *command, } } - if (command->object.type == OBJECT_UNDEFINED) { - fprintf(stderr, - "Missing object specification: connection | listener | route\n"); - return -1; + // 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; } - /* Parse and validate parameters for add/delete */ - switch (command->object.type) { - case OBJECT_FACE: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 5) && (argc - optind != 6)) { - usage_face_create(argv[0], true, false); - goto ERR_PARAM; - } - /* NAME will be autogenerated (and currently not used) */ - // snprintf(command->face.name, SYMBOLIC_NAME_LEN, "%s", - // argv[optind++]); - command->object.face.face.type = face_type_from_str(argv[optind++]); - if (command->object.face.face.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.face.face.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.face.face.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.local_addr) < 0) - goto ERR_PARAM; - command->object.face.face.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.face.face.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.remote_addr) < 0) - goto ERR_PARAM; - command->object.face.face.remote_port = atoi(argv[optind++]); - if (argc != optind) { - // netdevice_set_name(&command->object.face.face.netdevice, - // argv[optind++]); - command->object.face.face.netdevice.index = atoi(argv[optind++]); - } - - break; - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 5) && - (argc - optind != 6)) { - usage_face_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.face.id = atoi(argv[optind++]); - snprintf(command->object.face.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - //} else if (is_symbolic_name(argv[optind])) { - // snprintf(command->object.face.name, SYMBOLIC_NAME_LEN, "%s", - // argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.face.face.type = face_type_from_str(argv[optind++]); - if (command->object.face.face.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.face.face.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.face.face.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.local_addr) < 0) - goto ERR_PARAM; - command->object.face.face.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.face.face.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.remote_addr) < 0) - goto ERR_PARAM; - command->object.face.face.remote_port = atoi(argv[optind++]); - if (argc != optind) { - command->object.face.face.netdevice.index = atoi(argv[optind++]); - // netdevice_set_name(&command->object.face.face.netdevice, - // argv[optind++]); - } - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_face_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_ROUTE: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 2) && (argc - optind != 3)) { - usage_route_create(argv[0], true, false); - goto ERR_PARAM; - } - - command->object.route.face_id = atoi(argv[optind++]); - - { - ip_prefix_t prefix; - ip_prefix_pton(argv[optind++], &prefix); - command->object.route.family = prefix.family; - command->object.route.remote_addr = prefix.address; - command->object.route.len = prefix.len; - } - - if (argc != optind) { - printf("parse cost\n"); - command->object.route.cost = atoi(argv[optind++]); - } - break; - - case ACTION_DELETE: - if (argc - optind != 2) { - usage_route_delete(argv[0], true, false); - goto ERR_PARAM; - } - - command->object.route.face_id = atoi(argv[optind++]); - - { - ip_prefix_t prefix; - ip_prefix_pton(argv[optind++], &prefix); - command->object.route.family = prefix.family; - command->object.route.remote_addr = prefix.address; - command->object.route.len = prefix.len; - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_route_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_STRATEGY: - switch (command->action) { - case ACTION_LIST: - if (argc - optind != 0) { - usage_forwarding_strategy_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_LISTENER: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 4) && (argc - optind != 5)) { - usage_listener_create(argv[0], true, false); - goto ERR_PARAM; - } - - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - command->object.listener.type = face_type_from_str(argv[optind++]); - if (command->object.listener.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.listener.family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.listener.family)) goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.listener.local_addr) < 0) - goto ERR_PARAM; - command->object.listener.local_port = atoi(argv[optind++]); - if (argc != optind) { - snprintf(command->object.listener.interface_name, INTERFACE_LEN, - "%s", argv[optind++]); - } - break; - - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 3) && - (argc - optind != 4)) { - usage_listener_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.listener.id = atoi(argv[optind++]); - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else if (is_symbolic_name(argv[optind], SYMBOLIC_NAME_LEN)) { - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.listener.type = face_type_from_str(argv[optind++]); - if (command->object.listener.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.listener.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.listener.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.listener.local_addr) < 0) - goto ERR_PARAM; - command->object.listener.local_port = atoi(argv[optind++]); - if (argc != optind) { - snprintf(command->object.listener.interface_name, INTERFACE_LEN, - "%s", argv[optind++]); - } - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_listener_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_CONNECTION: - switch (command->action) { - case ACTION_CREATE: - /* NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT */ - if ((argc - optind != 6) && (argc - optind != 7)) { - usage_connection_create(argv[0], true, false); - goto ERR_PARAM; - } - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - command->object.connection.type = face_type_from_str(argv[optind++]); - if (command->object.connection.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.connection.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.connection.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.local_addr) < 0) - goto ERR_PARAM; - command->object.connection.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.connection.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.remote_addr) < 0) - goto ERR_PARAM; - command->object.connection.remote_port = atoi(argv[optind++]); - - break; - - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 5) && - (argc - optind != 6)) { - usage_connection_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.connection.id = atoi(argv[optind++]); - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else if (is_symbolic_name(argv[optind], SYMBOLIC_NAME_LEN)) { - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.connection.type = - face_type_from_str(argv[optind++]); - if (command->object.connection.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.connection.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.connection.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.local_addr) < 0) - goto ERR_PARAM; - command->object.connection.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.connection.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.remote_addr) < 0) - goto ERR_PARAM; - command->object.connection.remote_port = atoi(argv[optind++]); - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_connection_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; + /* 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; + } - default: - goto ERR_COMMAND; + /* + * 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; -ERR_PARAM: -ERR_COMMAND: - return -1; +USAGE: + usage(argv[0]); + exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { - hc_data_t *data; int rc = 1; hc_command_t command = {0}; - char buf_listener[MAXSZ_HC_LISTENER]; - char buf_connection[MAXSZ_HC_CONNECTION]; - char buf_route[MAXSZ_HC_ROUTE]; - char buf_strategy[MAXSZ_HC_STRATEGY]; + char buf[MAXSZ_HC_OBJECT]; - forwarder_type_t forwarder = HICNLIGHT; + forwarder_type_t forwarder = FORWARDER_TYPE_VPP; if (parse_options(argc, argv, &command, &forwarder) < 0) die(OPTIONS, "Bad arguments"); - hc_sock_t *s = hc_sock_create_forwarder(forwarder); + 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."); - switch (command.object.type) { - case OBJECT_FACE: - switch (command.action) { - case ACTION_CREATE: - if (hc_face_create(s, &command.object.face) < 0) - die(COMMAND, "Error creating face"); - printf("OK\n"); - break; - - case ACTION_DELETE: - if (hc_face_delete(s, &command.object.face, 1) < 0) - die(COMMAND, "Error creating face"); - printf("OK\n"); - break; - - case ACTION_LIST: - if (hc_face_list(s, &data) < 0) - die(COMMAND, "Error getting connections."); - - 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", f->name, buf_connection); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for connection"); - break; - } - break; - - case OBJECT_ROUTE: - switch (command.action) { - case ACTION_CREATE: - if (hc_route_create(s, &command.object.route) < 0) - die(COMMAND, "Error creating route"); - printf("OK\n"); - break; - - case ACTION_DELETE: - if (hc_route_delete(s, &command.object.route) < 0) - die(COMMAND, "Error creating route"); - printf("OK\n"); - break; - - case ACTION_LIST: - if (hc_route_list(s, &data) < 0) - die(COMMAND, "Error getting routes."); - - printf("Routes:\n"); - foreach_route(r, data) { - if (hc_route_snprintf(buf_route, MAXSZ_HC_ROUTE, r) >= - MAXSZ_HC_ROUTE) - die(COMMAND, "Display error"); - printf("%s\n", buf_route); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for route"); - break; - } - break; - - case OBJECT_STRATEGY: - switch (command.action) { - case ACTION_LIST: - if (hc_strategy_list(s, &data) < 0) - die(COMMAND, "Error getting routes."); - - printf("Forwarding strategies:\n"); - foreach_strategy(st, data) { - if (hc_strategy_snprintf(buf_strategy, MAXSZ_HC_STRATEGY, st) >= - MAXSZ_HC_STRATEGY) - die(COMMAND, "Display error"); - printf("%s\n", buf_strategy); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for strategy"); - break; - } - break; - - case OBJECT_LISTENER: - switch (command.action) { - case ACTION_CREATE: - if (hc_listener_create(s, &command.object.listener) < 0) - die(COMMAND, "Error creating listener"); - printf("OK\n"); - break; - case ACTION_DELETE: - if (hc_listener_delete(s, &command.object.listener) < 0) - die(COMMAND, "Error deleting listener"); - printf("OK\n"); - 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.object.connection) < 0) - die(COMMAND, "Error creating connection"); - printf("OK\n"); - break; - case ACTION_DELETE: - if (hc_connection_delete(s, &command.object.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; + 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; - default: - die(COMMAND, "Unsupported object"); - break; + 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: - return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS; + printf("Error.\n"); + return EXIT_FAILURE; } diff --git a/ctrl/libhicnctrl/src/module.h b/ctrl/libhicnctrl/src/module.h new file mode 100644 index 000000000..44ba5ddbb --- /dev/null +++ b/ctrl/libhicnctrl/src/module.h @@ -0,0 +1,131 @@ +#ifndef HICNCTRL_MODULE_H +#define HICNCTRL_MODULE_H + +#include <stdint.h> + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/socket.h> + +#include "request.h" + +/* + * execute is used for sync code (eg. in VPP), while serialize/parse for + * sync/async code (eg. in hicn-light). + */ +typedef int (*hc_execute_t)(hc_sock_t *, hc_object_t *, hc_data_t *); +typedef int (*hc_serialize_t)(const hc_object_t *, uint8_t *); + +typedef struct { + int (*parse)(const uint8_t *buffer, size_t size, hc_object_t *object); + size_t serialized_size; + hc_serialize_t serialize[ACTION_N]; + hc_execute_t execute[ACTION_N]; +} hc_module_object_ops_t; + +#define HC_MODULE_OBJECT_OPS_EMPTY \ + (hc_module_object_ops_t) { \ + .parse = NULL, .serialized_size = 0, \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + } + +#define DECLARE_MODULE_OBJECT_OPS_H(PREFIX, NAME) \ + extern const hc_module_object_ops_t PREFIX##_##NAME##_module_ops; + +/* Underscore'd functions take a hc_object_t as a parameter */ + +#define HC_MODULE_OBJECT_OPS(PREFIX, NAME) \ + (hc_module_object_ops_t) { \ + .parse = _##PREFIX##_##NAME##_parse, \ + .serialized_size = sizeof(cmd_##NAME##_list_item_t), \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \ + } \ + } + +#define DECLARE_MODULE_OBJECT_OPS(PREFIX, NAME) \ + const hc_module_object_ops_t PREFIX##_##NAME##_module_ops = { \ + .parse = _##PREFIX##_##NAME##_parse, \ + .serialized_size = sizeof(cmd_##NAME##_list_item_t), \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \ + }}; + +#define DECLARE_VPP_MODULE_OBJECT_OPS(PREFIX, NAME) \ + const hc_module_object_ops_t PREFIX##_##NAME##_module_ops = { \ + .execute = \ + { \ + [ACTION_CREATE] = PREFIX##_##NAME##_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_list, \ + }, \ + .serialize = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + }; + +typedef struct { + /** Create module-specific data storage */ + void *(*create_data)(const char *); + + /** Release module-specific data storage */ + void (*free_data)(void *); + + /** Retrieve underlying file descriptor */ + int (*get_fd)(hc_sock_t *); + + /** Retrieve underlying receive buffer */ + int (*get_recv_buffer)(hc_sock_t *, uint8_t **buffer, size_t *size); + + /** Connect control socket to the forwarder */ + int (*connect)(hc_sock_t *); + + /** Disconnect control socket from forwarder */ + int (*disconnect)(hc_sock_t *); + + /** Populate the TX buffer with the serialization of the next request to be + * sent */ + ssize_t (*prepare)(hc_sock_t *, hc_request_t *, uint8_t **buffer); + + /** Send the content of the TX buffer */ + int (*send)(hc_sock_t *, uint8_t *buffer, size_t size); + + /** Receive responses in the RX buffer */ + int (*recv)(hc_sock_t *); + + /** Process the content of the RX buffer to populate result data */ + int (*process)(hc_sock_t *, size_t count); + + hc_module_object_ops_t object_vft[OBJECT_TYPE_N]; +} hc_sock_ops_t; + +#endif /* HICNCTRL_MODULE_H */ diff --git a/ctrl/libhicnctrl/src/module_object.c b/ctrl/libhicnctrl/src/module_object.c new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object.c @@ -0,0 +1 @@ + diff --git a/ctrl/libhicnctrl/src/module_object.h b/ctrl/libhicnctrl/src/module_object.h new file mode 100644 index 000000000..5081aa715 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object.h @@ -0,0 +1,10 @@ +#ifndef HICNCTRL_MODULES_OBJECT_H +#define HICNCTRL_MODULES_OBJECT_H + +ssize_t hc_object_serialize(hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_msg_t *msg); + +int hc_object_parse(hc_object_type_t object_type, uint8_t *buffer, + hc_object_t *object); + +#endif /* HICNCTRL_MODULES_OBJECT_H */ diff --git a/ctrl/libhicnctrl/src/module_object_vft.h b/ctrl/libhicnctrl/src/module_object_vft.h new file mode 100644 index 000000000..2651b50a3 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object_vft.h @@ -0,0 +1,4 @@ +#ifndef HICNCTRL_MODULES_OBJECT_MODULE_VFT_H +#define HICNCTRL_MODULES_OBJECT_MODULE_VFT_H + +#endif /* HICNCTRL_MODULES_OBJECT_MODULE_VFT_H */ diff --git a/ctrl/libhicnctrl/src/modules/CMakeLists.txt b/ctrl/libhicnctrl/src/modules/CMakeLists.txt index 8f7916d14..b85ef29dc 100644 --- a/ctrl/libhicnctrl/src/modules/CMakeLists.txt +++ b/ctrl/libhicnctrl/src/modules/CMakeLists.txt @@ -14,13 +14,28 @@ ############################################################## # Hicn Light NG Module ############################################################## -list(APPEND HICNLIGHTNG_MODULE_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light_common.c - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light_ng_api.c +list(APPEND HICNLIGHT_MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/face.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/strategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/subscription.c ) -build_module(hicnlightngctrl_module - SOURCES ${HICNLIGHTNG_MODULE_SOURCE_FILES} +list(APPEND HICNLIGHT_MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/face.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/subscription.h + ) + +build_module(hicnlightctrl_module + SOURCES ${HICNLIGHT_MODULE_SOURCE_FILES} ${HICNLIGHT_MODULE_HEADER_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBHICNCTRL_COMPONENT} LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} @@ -44,12 +59,21 @@ if(BUILD_HICNPLUGIN AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux") ) endif() + list(APPEND HICN_PLUGIN_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/route.c + ) + + list(APPEND HICN_PLUGIN_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/base.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.h ) build_module(vppctrl_module - SOURCES ${HICN_PLUGIN_SOURCE_FILES} + SOURCES ${HICN_PLUGIN_SOURCE_FILES} ${HICN_PLUGIN_HEADER_FILES} DEPENDS ${DEPENDENCIES} LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} @@ -58,6 +82,6 @@ if(BUILD_HICNPLUGIN AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux") COMPONENT ${LIBHICNCTRL_COMPONENT_MODULES} INCLUDE_DIRS PRIVATE ${INCLUDE_DIRS} DEFINITIONS PRIVATE ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} "-DHICN_VPP_PLUGIN=1" ) endif() diff --git a/ctrl/libhicnctrl/src/modules/hicn_light.c b/ctrl/libhicnctrl/src/modules/hicn_light.c new file mode 100644 index 000000000..1af6c79e2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light.c @@ -0,0 +1,1255 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file modules/hicn_light.c + * \brief Implementation of hicn-light module. + */ + +#include <assert.h> // assert +#include <fcntl.h> // fcntl +#include <stdbool.h> +#include <stdio.h> // snprintf +#include <string.h> // memmove, strcasecmp +#include <sys/socket.h> // socket +#include <sys/types.h> // getpid +#include <unistd.h> // close, fcntl, getpid + +#ifdef __linux__ +#include <sys/syscall.h> +#define gettid() syscall(SYS_gettid) +#endif /* __linux__ */ + +#include <strings.h> + +#include <hicn/ctrl/hicn-light.h> +#include <hicn/ctrl/socket.h> + +#include "../api_private.h" +#include "../objects/connection.h" // hc_connection_has_local +#include "../objects/listener.h" // hc_listener_is_local +#include "../objects/route.h" // hc_route_has_face +#include "../request.h" +#include "../socket_private.h" +#include "hicn_light.h" + +#include "hicn_light/base.h" +#include "hicn_light/connection.h" +#include "hicn_light/listener.h" +#include "hicn_light/face.h" +#include "hicn_light/route.h" +#include "hicn_light/strategy.h" +#include "hicn_light/subscription.h" + +#pragma GCC diagnostic ignored "-Warray-bounds" + +#define DEFAULT_SOCK_RECV_TIMEOUT_MS 100 + +#define PORT 9695 + +#define BOOLSTR(x) ((x) ? "true" : "false") + +hc_sock_light_data_t *hc_sock_light_data_create(const char *url) { + hc_sock_light_data_t *s = malloc(sizeof(hc_sock_light_data_t)); + if (!s) goto ERR_MALLOC; + + s->roff = s->woff = 0; + s->remaining = 0; + s->got_header = false; + + s->url = url ? strdup(url) : NULL; + + s->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (s->fd < 0) goto ERR_SOCKET; + +#if 0 + struct timeval tv = {.tv_sec = 0, + .tv_usec = DEFAULT_SOCK_RECV_TIMEOUT_MS * 1000}; + if (setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + perror("setsockopt"); + goto ERR_TIMEOUT; + } +#endif + + return s; + +#if 0 +ERR_TIMEOUT: +#endif + close(s->fd); +ERR_SOCKET: + if (s->url) free(s->url); + free(s); +ERR_MALLOC: + return NULL; +} + +void hc_sock_light_data_free(hc_sock_light_data_t *data) { + if (data->url) free(data->url); + free(data); +} + +static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; + +/****************************************************************************** + * Control socket + ******************************************************************************/ + +#define AVAILABLE(s) ((s)->woff - (s)->roff) + +/** + * \brief Parse a connection URL into a sockaddr + * \param [in] url - URL + * \param [out] sa - Resulting struct sockaddr, expected zero'ed. + * \return 0 if parsing succeeded, a negative error value otherwise. + */ +static int hicnlight_parse_url(const char *url, struct sockaddr *sa) { + /* FIXME URL parsing is currently not implemented */ + _ASSERT(!url); + +#ifdef __linux__ + srand(time(NULL) ^ getpid() ^ gettid()); +#else + srand((unsigned int)(time(NULL) ^ getpid())); +#endif /* __linux__ */ + + /* + * A temporary solution is to inspect the sa_family fields of the passed in + * sockaddr, which defaults to AF_UNSPEC (0) and thus creates an IPv4/TCP + * connection to localhost. + */ + switch (sa->sa_family) { + case AF_UNSPEC: + case AF_INET: { + struct sockaddr_in *sai = (struct sockaddr_in *)sa; + sai->sin_family = AF_INET; + sai->sin_port = htons(PORT); + sai->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)sa; + sai6->sin6_family = AF_INET6; + sai6->sin6_port = htons(PORT); + sai6->sin6_addr = loopback_addr; + break; + } + default: + return -1; + } + + return 0; +} + +/* + * Return codes: + * < 0 : error; invalid buffer data -> flush + * otherwise, seq_num of the identified request + */ +static int hicnlight_process_header(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + hc_object_type_t object_type = OBJECT_TYPE_UNDEFINED; + + /* Check we have at least a header's worth of data, and consume it */ + if (AVAILABLE(s) < sizeof(hc_msg_header_t)) return 0; + + hc_msg_t *msg = (hc_msg_t *)(s->buf + s->roff); + + // INFO("Processing header header %s", command_type_str(msg->hdr.command_id)); + s->roff += sizeof(hc_msg_header_t); + s->got_header = true; + + /* How many elements are we expecting in the reply ? */ + s->remaining = msg->header.length; + + /* Identify request being parsed */ + int seq = msg->header.seq_num; + hc_request_t *request = NULL; + if (hc_sock_map_get(sock->map, seq, &request) < 0) { + ERROR("[hc_sock_light_process] Error searching for matching request"); + return -1; + } + if (!request) { + ERROR("[hc_sock_light_process] No request matching sequence number"); + return -1; + } + sock->current_request = request; + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + _ASSERT(data); + + switch (msg->header.message_type) { + case ACK_LIGHT: + _ASSERT(s->remaining == 0); + + s->got_header = false; + if (!hc_request_is_subscription(request)) hc_data_set_complete(data); + break; + + case NACK_LIGHT: + _ASSERT(s->remaining == 0); + + s->got_header = false; + hc_data_set_error(data); + break; + + case RESPONSE_LIGHT: + if (s->remaining == 0) { + /* Empty response (i.e. containing 0 elements) */ + s->got_header = false; + hc_data_set_complete(data); + return 0; + } + + /* Allocate buffer for response */ + if (hc_data_allocate(data, s->remaining) < 0) { + ERROR("[hc_sock_light_process] Cannot allocate result buffer"); + return -99; + } + break; + + case NOTIFICATION_LIGHT: { + _ASSERT(s->remaining == 1); + /* + * Assumption: the whole notification data is returned in a single read + * and we immediately parse it. + */ + // XXX assert enough buffer for object type + validate returned object + object_type = (hc_object_type_t)msg->header.command_id; + hc_data_clear(data); + hc_data_set_object_type(data, object_type); + if (hc_data_allocate(data, s->remaining) < 0) { + ERROR("[hc_sock_light_process] Cannot allocate result buffer"); + return -1; + } + + hc_data_push(data, s->buf + s->roff); + + s->roff += AVAILABLE(s); + + hc_request_on_notification(request); + + /* + * The buffer is cleared just before the next notification, which means + * it will have to be released upon exit. Otherwise we break the code + * dumping the notification synchronously (eg. hicnctrl -s). + */ + // hc_data_clear(data); + + s->got_header = false; + break; + } + + default: + ERROR("[hc_sock_light_process] Invalid response received"); + return -99; + } + + return 0; +} + +size_t hc_light_object_size(hc_object_type_t object_type); + +static int hicnlight_process_payload(hc_sock_t *sock) { + int err = 0; + int rc; + + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + hc_request_t *request = hc_sock_get_request(sock); + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_light_object_size(object_type); + if (object_size == 0) return -1; + + /* We only process full elements (size is stored in data) */ + size_t num_chunks = AVAILABLE(s) / object_size; + + /* Check whether we have enough data to process */ + if (num_chunks == 0) return 0; + + /* Safeguard: _ASSERT(num_chunks < s->remaining); */ + if (num_chunks > s->remaining) { + WARN( + "[hicnlight_process_payload] Unexpected num_chunks > " + "s->remaining"); + num_chunks = s->remaining; + } + + for (size_t i = 0; i < num_chunks; i++) { + /* + * Get storage offset in hc_data_t, which we assume is correctly + * provisioned. + * XXX + */ + u8 *src = s->buf + s->roff; + hc_object_t *dst = (hc_object_t *)hc_data_get_free(data); + if (!dst) { + ERROR("[hc_sock_light_process] Error in hc_data_get_next"); + err = -2; + break; + } + + // XXX we might want to display even incomplete data when printing (eg. + // string truncation), and be very strict when processing. + rc = hc_sock_parse_object(sock, hc_data_get_object_type(data), src, + object_size, dst); + s->roff += object_size; + if (rc < 0) { + ERROR("Error parsing received object"); + continue; + } + hc_data_inc_size(data); + } + + /* + * If we are not expecting any more data, mark the reply as complete + */ + s->remaining -= num_chunks; + if (s->remaining == 0) { + s->got_header = false; + hc_data_set_complete(data); + } + + return err; +} + +/*---------------------------------------------------------------------------- + * Socket operations + *----------------------------------------------------------------------------*/ + +static int hicnlight_get_fd(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + return s->fd; +} + +static int hicnlight_get_recv_buffer(hc_sock_t *sock, uint8_t **buffer, + size_t *size) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + *buffer = s->buf + s->woff; + *size = RECV_BUFLEN - s->woff; + + return 0; +} + +static int hicnlight_connect(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + struct sockaddr_storage ss; + memset(&ss, 0, sizeof(struct sockaddr_storage)); + + if (hicnlight_parse_url(s->url, (struct sockaddr *)&ss) < 0) goto ERR_PARSE; + + size_t size = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + if (connect(s->fd, (struct sockaddr *)&ss, (socklen_t)size) < 0) { + perror("connect error"); + goto ERR_CONNECT; + } + return 0; + +ERR_CONNECT: +ERR_PARSE: + return -1; +} + +static int hicnlight_disconnect(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + /* Remove the connection created to send the command. + * + * Note this is done as a best effort and we don't expect to receive any + * answer from the forwarder (hence the NULL pdata pointer in the request). + */ + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection.id = 0; + int rc = + strcpy_s(object.connection.name, sizeof(object.connection.name), "SELF"); + if (rc == EOK) + hc_execute_async(sock, ACTION_DELETE, OBJECT_TYPE_CONNECTION, &object, NULL, + NULL); + + close(s->fd); + + return 0; +} + +static ssize_t hicnlight_prepare_generic(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + _ASSERT(!hc_request_get_data(current_request)); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + _ASSERT(hc_request_get_data(current_request) == NULL); + hc_data_t *data = hc_data_create(object_type); + if (!data) { + ERROR("[hicnlight_prepare_generic] Could not create data storage"); + return -1; + } + hc_request_set_data(current_request, data); + + /* Serialize request into message */ + DEBUG("Calling serialize on %s %s", action_str(action), + object_type_str(object_type)); + ssize_t msg_len = hc_sock_serialize_object(sock, action, object_type, object, + (uint8_t *)&s->msg); + if (msg_len < 0) { + ERROR("[hicnlight_prepare_generic] Could not serialize command %s %s", + action_str(action), object_type_str(object_type)); + return INPUT_ERROR; + } + + s->msg.header.seq_num = hc_request_get_seq(current_request); + + *buffer = (uint8_t *)&s->msg; + return msg_len; +} + +static int hicnlight_send(hc_sock_t *sock, uint8_t *buffer, size_t size) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + int rc = (int)send(s->fd, buffer, size, 0); + if (rc < 0) { + perror("[hicnlight_send] Error sending message"); + return -1; + } + + // XXX regular behaviour for others + return 0; +} + +// Example : face create udp +// 1) face to connection (immediate) +// connection to local listener (immediate) : why not both at the same +// time listener get +// listener create / nothing +// connection create +// connection get (if needed for populating face_id for instance, aka +// if we +// need to return data) + +static ssize_t hicnlight_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer); + +static ssize_t hicnlight_prepare_subrequest( + hc_sock_t *sock, hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, uint8_t **buffer) { + WITH_DEBUG({ + if (object) { + char buf[MAXSZ_HC_OBJECT]; + hc_object_snprintf(buf, sizeof(buf), object_type, object); + DEBUG("Creating subrequest %s/%s %s", action_str(action), + object_type_str(object_type), buf); + } + }); + hc_request_make_subrequest(request, action, object_type, object); + return hicnlight_prepare(sock, request, buffer); +} + +/* + * XXX shall we update the object in the request for faces ? it is not done + * for other objects, but for faces it is needed to further add a route !!! + */ +static ssize_t hicnlight_prepare_face_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_face_t *face = &object->face; + + // XXX those objects are created on stack and expected to be valid across + // several calls. A quick fix is to make them static + static hc_object_t connection; + static hc_object_t listener; + + hc_request_state_t state; + const hc_connection_t *conn; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_face_create > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + + switch (face->type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_CREATE); + goto NEXT; + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_LISTENER_CREATE); + goto NEXT; + case FACE_TYPE_UNDEFINED: + case FACE_TYPE_N: + return -99; // Not implemented + } + + case REQUEST_STATE_FACE_CREATE_CONNECTION_CREATE: + if (hc_face_to_connection(face, &connection.connection, true) < 0) { + ERROR("[hc_face_create] Could not convert face to connection."); + return -1; + } + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_CHECK); + + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_CONNECTION, &connection, + buffer); + + case REQUEST_STATE_FACE_CREATE_CONNECTION_CHECK: + /* + * If the newly created face_id was not need, we would only + * need to return the same data result, which contains a ack/nack, + * simply updating the object type. + * + * With the current API, once the connection is created, our only + * solution is to list all connections and compare with the current one + * to find the created connection ID, and thus face ID. + */ + /* Has the connection been successfully created ? */ + if (!data || !hc_data_get_result(data)) return -1; + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_GET); + goto NEXT; + + case REQUEST_STATE_FACE_CREATE_CONNECTION_GET: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_VERIFY); + return hicnlight_prepare_subrequest(sock, request, ACTION_GET, + OBJECT_TYPE_CONNECTION, &connection, + buffer); + + case REQUEST_STATE_FACE_CREATE_CONNECTION_VERIFY: + if (!data || hc_data_get_size(data) != 1) return -1; + + /* Newly created connection was found */ + conn = (hc_connection_t *)hc_data_get_buffer(data); + DEBUG("created connection id=%d", conn->id); + object->face.id = conn->id; + + break; + + case REQUEST_STATE_FACE_CREATE_LISTENER_CREATE: + if (hc_face_to_listener(face, &listener.listener) < 0) { + ERROR("Could not convert face to listener."); + return -1; + } + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_LISTENER_CHECK); + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_LISTENER, &listener, + buffer); + + break; + + case REQUEST_STATE_FACE_CREATE_LISTENER_CHECK: + /* + * No need for face id here, simply return the hc_data_t structure + * with the ack/nack, and the proper object type + */ + if (!data) return -1; + hc_data_set_object_type(data, OBJECT_TYPE_FACE); + break; + +#if 0 + case REQUEST_STATE_COMPLETE: + hc_data_set_complete(data); + break; +#endif + + default: + return -1; + } + return 0; +} + +static ssize_t hicnlight_prepare_face_list(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_face_t face; + + _ASSERT(action == ACTION_LIST); + _ASSERT(object_type == OBJECT_TYPE_FACE); + + hc_request_state_t state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_face_list > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_LIST_CONNECTION_LIST); + return hicnlight_prepare_subrequest( + sock, request, ACTION_LIST, OBJECT_TYPE_CONNECTION, object, buffer); + + case REQUEST_STATE_FACE_LIST_CONNECTION_LIST: + _ASSERT(data); + /* + * 'list connection' succeeded, we just need to allocate hc_data_t, + * create faces from connections, and return the data structure as if it + * was created by the query + */ + hc_data_t *face_data = hc_data_create(object_type); + hc_data_allocate(face_data, hc_data_get_size(data)); + foreach_connection(c, data) { + if (hc_face_from_connection(c, &face) < 0) { + ERROR("[hc_face_list] Could not convert connection to face."); + return -1; + } + hc_data_push(face_data, &face); + } + hc_data_set_complete(face_data); + + hc_request_reset_data(request); + hc_request_set_data(request, face_data); + + /* FACE/LIST could be part of FACE/GET */ + break; + + default: + return -1; + } + + return 0; +} + +static ssize_t hicnlight_prepare_get(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_object_t *found; + + hc_request_state_t state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_get > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + hc_request_set_state(current_request, REQUEST_STATE_GET_LIST); + return hicnlight_prepare_subrequest(sock, request, ACTION_LIST, + object_type, NULL, buffer); + case REQUEST_STATE_GET_LIST: + _ASSERT(data); + + found = hc_data_find(data, object); + hc_data_t *found_data = hc_data_create(object_type); + if (found) { + hc_data_allocate(found_data, 1); + hc_data_push(found_data, found); + } + hc_data_set_complete(found_data); + hc_request_reset_data(current_request); + hc_request_set_data(current_request, found_data); + return 0; + default: + return -1; /* Unexpected */ + } +} + +// XXX This should process the content of pdata (unless at init), and +// terminate by sending something +static ssize_t hicnlight_prepare_face(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(request); + + _ASSERT(object_type == OBJECT_TYPE_FACE); + + switch (action) { + case ACTION_CREATE: + return hicnlight_prepare_face_create(sock, request, buffer); + case ACTION_LIST: + return hicnlight_prepare_face_list(sock, request, buffer); + default: + return -99; // Not implemented + } + return 0; +} + +static ssize_t hicnlight_prepare_connection_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(action == ACTION_CREATE); + _ASSERT(object_type == OBJECT_TYPE_CONNECTION); + + hc_data_t *data = hc_request_get_data(current_request); + + size_t size; + unsigned pos; + static hc_object_t listener; + const hc_object_t *obj_listener; + hc_data_t *listener_data = NULL; + + hc_request_state_t state; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_connection_create > %s", + hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + /* Two behaviours depending on the content of local_addr and local_port: + * - empty : create connection on all existing listeners, and raise an + * error if none + * - otherwise, check whether a corresponding listener exists, and + * create it if necessary + * + * We assume connection has been already validated. + */ + if (hc_connection_has_local(&object->connection)) { + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_GET); + } else { + /* + * At least part of the local socket specification is missing, match + * against existing listeners + */ + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_LIST); + } + goto NEXT; + + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_LIST: + + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_ITERATE); + // XXX We are currently assuming an object is present for rewrite, fix + // this + return hicnlight_prepare_subrequest(sock, request, ACTION_LIST, + OBJECT_TYPE_LISTENER, NULL, buffer); + + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_ITERATE: + /* + * NOTE: we could create all connections in parallel to speed up + * processing + */ + size = hc_data_get_size(data); + if (size < 0) return -1; + if (size == 0) + /* We are done, we cannot create a connection, return a Nack */ + ; // XXX TODO + // + /* Save the list of listeners for later iteration */ + listener_data = data; + hc_request_clear_data(current_request); // don't free data + data = NULL; + hc_request_set_state_count(current_request, 0); + hc_request_set_state(current_request, REQUEST_STATE_CONNECTION_CREATE_N); + goto NEXT; /* Start iteration */ + + case REQUEST_STATE_CONNECTION_CREATE_N: + /* + * IMPORTANT + * + * For now we only create a connection with the first non-local + * listener. + * + * Creating N connections in a single commands requires other + * changes to the code that we might done later: + * - ack/nack is not sufficient, all create function should return the + * list of created connections + * - this would allow us to avoid a GET at the end of face creation to + * retrieve the connection id. + * - face create should correspond to N connection create (should work + * out of the box provided we don't expect a single connection back). + * - route+face creation might then create N faces, and thus we would + * have to add N routes. + */ + assert(listener_data); + + // We need to back it up as the subrequest will clear the results + pos = hc_request_get_state_count(current_request); + size = hc_data_get_size(listener_data); + /* We have data if pos > 0, and we did not skipped previous ones */ + if (data && !hc_data_get_result(data)) { + INFO("Failed to create connection for listener %d / %d", pos - 1, size); + // XXX we might allow connections that already exist... how to manage + // the names + return -1; + } + + /* + * Previous connection was successfully created, let's continue but + * first check whether we reached the last one, which would complete the + * request. + */ + if (pos >= size) { + hc_data_free(listener_data); + hc_request_set_state(request, REQUEST_STATE_COMPLETE); + goto NEXT; + } + + /* Sending count'th connection creation */ + obj_listener = hc_data_get_object(listener_data, pos); + + // Filter which listener we use + // same protocol ? ip ? port ? + // avoid local ? + if (hc_listener_is_local(&obj_listener->listener)) { + /* Skip listener */ + DEBUG("Skipped local listener"); + hc_request_set_state_count(current_request, pos + 1); + goto NEXT; + } + + DEBUG("Creating connection with listener # %d / %d", pos, size); + /* We complement missing information from listener */ + // XXX is_family, etc. + object->connection.family = obj_listener->listener.family; + object->connection.local_addr = obj_listener->listener.local_addr; + object->connection.local_port = obj_listener->listener.local_port; + snprintf(object->connection.interface_name, INTERFACE_LEN, "%s", + obj_listener->listener.interface_name); + + hc_request_set_state_count(current_request, pos + 1); + return hicnlight_prepare_subrequest( + sock, request, ACTION_CREATE, OBJECT_TYPE_CONNECTION, object, buffer); + + /* Request listener to further check existence */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_GET: + /* Ensure we have a corresponding local listener */ + if (hc_connection_to_local_listener(&object->connection, + &listener.listener) < 0) { + ERROR( + "[hicnlight_prepare_connection_create] Could not convert face " + "to " + "local listener."); + return -1; + } + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_VERIFY); + return hicnlight_prepare_subrequest( + sock, request, ACTION_GET, OBJECT_TYPE_LISTENER, &listener, buffer); + + break; + + /* Check whether listener exists in GET results */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_VERIFY: + if (!data) return -1; + switch (hc_data_get_size(data)) { + case 0: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_CREATE); + break; + case 1: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE); + break; + default: + return -1; + } + goto NEXT; + + /* Create associated listener */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_CREATE: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_CHECK); + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_LISTENER, &listener, + buffer); + + /* Check whether listener creation succeeded */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_CHECK: + if (!data || hc_data_get_result(data)) return -1; + hc_request_set_state(current_request, REQUEST_STATE_CONNECTION_CREATE); + goto NEXT; + + /* Create connection */ + case REQUEST_STATE_CONNECTION_CREATE: + /* + * Break recursion by directly calling hicnlight_prepare_generic on + * the initial request, that can now be executed since all + * prerequisites are validated. + */ + // return hicnlight_prepare_subrequest( + // sock, request, ACTION_CREATE, OBJECT_TYPE_CONNECTION, object, + // buffer); + hc_request_reset_data(current_request); + hc_request_set_state(current_request, REQUEST_STATE_COMPLETE); + return hicnlight_prepare_generic(sock, request, buffer); + + case REQUEST_STATE_COMPLETE: + if (data) { + hc_data_set_complete(data); + } else { + /* + * No connection has been created, and we freed the data due to + * subrequest + */ + data = hc_data_create(OBJECT_TYPE_CONNECTION); + hc_data_set_error(data); + } + break; + + default: + return -1; + } + return 0; +} + +static ssize_t hicnlight_prepare_route_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(action == ACTION_CREATE); + _ASSERT(object_type == OBJECT_TYPE_ROUTE); + + hc_data_t *data = hc_request_get_data(current_request); + const hc_object_t *face_obj; + + hc_request_state_t state; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_route_create > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + if (hc_route_has_face(&object->route)) + hc_request_set_state(current_request, + REQUEST_STATE_ROUTE_CREATE_FACE_CREATE); + else + hc_request_set_state(current_request, REQUEST_STATE_ROUTE_CREATE); + goto NEXT; + + case REQUEST_STATE_ROUTE_CREATE_FACE_CREATE: + hc_request_set_state(current_request, + REQUEST_STATE_ROUTE_CREATE_FACE_CHECK); + INFO(">>>>>>subrequest create face"); + return hicnlight_prepare_subrequest( + sock, request, ACTION_CREATE, OBJECT_TYPE_FACE, + (hc_object_t *)&object->route.face, buffer); + + case REQUEST_STATE_ROUTE_CREATE_FACE_CHECK: + if (!data) return -1; + int rc = hc_data_get_result(data); + if (rc < 0) return -1; + + if (hc_data_get_size(data) != 1) return -1; + + face_obj = hc_data_get_object(data, 0); + DEBUG("Created face id=%d", face_obj->face.id); + object->route.face_id = face_obj->face.id; + + hc_request_set_state(current_request, REQUEST_STATE_ROUTE_CREATE); + goto NEXT; + + /* Create route */ + case REQUEST_STATE_ROUTE_CREATE: + /* + * Break recursion by directly calling hicnlight_prepare_generic on the + * initial request, that can now be executed since all prerequisites are + * validated. + */ + hc_request_set_state(current_request, REQUEST_STATE_COMPLETE); + return hicnlight_prepare_generic(sock, request, buffer); + + case REQUEST_STATE_COMPLETE: + hc_data_set_complete(data); + break; + + default: + return -1; + } + return 0; +} + +static int hicnlight_recv(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + int rc; + + /* + * This condition should be ensured to guarantee correct processing of + * messages. With TCP, we need at least a header as we will receive part of + * the stream. With UDP, we need the be able to receive the full datagram, + * otherwise the rest will be lost. + * + * Let's be sure to always be able to receive at least 1 JUMBO_MTU, which + * should be fine for al situations. + */ + _ASSERT(RECV_BUFLEN - s->woff > JUMBO_MTU); + + rc = (int)recv(s->fd, s->buf + s->woff, RECV_BUFLEN - s->woff, 0); + if (rc == 0) { + /* Connection has been closed */ + return 0; + } + if (rc < 0) { + /* + * Let's not return 0 which currently means the socket has been closed + */ + if (errno == EWOULDBLOCK) { + // XXX TODO ?if (hc_request_get_action(request) == ACTION_SUBSCRIBE) + // return 0; + return -1; + } + if (errno == EINTR) { + WARN("recv has been stopped by signal"); + return -1; + } + perror("hc_sock_light_recv"); + return -1; + } + DEBUG("Received rc=%ld bytes", rc); + s->woff += rc; + + return rc; +} + +/* + * + * @param [in] data - hc_data_t structure allocated for the request + * + * This function is the entry point for all requests, and from there we will + * decide whether + * + */ +static ssize_t hicnlight_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + // XXX when do we create data... once for every step + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + static hc_object_t object_subscribe; + + DEBUG("[hicnlight_prepare] %s %s", action_str(action), + object_type_str(object_type)); + + /* + * Here the request is in progress and we just need to iterate through the + * FSM, or complete it. + */ + /* + * Specific treatment for + * CREATE/ROUTE with face + * SUBSCRIBE/(*) + * GET/(*) + * (*)/FACE + */ + + /* + * Special treatment for faces. + * + * This function will be called multiple times in order to process the + * complex request, involving several calls to the API. The process is + * responsible for going through the related state machine, and complete the + * request when appropriate. + */ + if (object_type == OBJECT_TYPE_FACE) + return hicnlight_prepare_face(sock, request, buffer); + + switch (action) { + case ACTION_CREATE: + switch (object_type) { + case OBJECT_TYPE_ROUTE: + /* Route might require face creation */ + return hicnlight_prepare_route_create(sock, request, buffer); + case OBJECT_TYPE_CONNECTION: + /* Connection could have no corresponging listener, or no local info + * provided */ + return hicnlight_prepare_connection_create(sock, request, buffer); + default: + break; + } + break; + + case ACTION_GET: + return hicnlight_prepare_get(sock, request, buffer); + + case ACTION_SUBSCRIBE: + /* Transform subscription queries */ + memset(&object_subscribe, 0, sizeof(hc_object_t)); + object->subscription.topics = topic_from_object_type(object_type); + + hc_request_set(request, ACTION_CREATE, OBJECT_TYPE_SUBSCRIPTION, object); + break; + + default: + break; + } + + /* + * Generic requests should complete after a single call to hicnlight_send, + * with *pdata = NULL. If *pdata is not NULL, that means the request has + * completed and we can close it. + * It is the responsability of each state machine to complete the request + * otherwise. + */ +#if 1 + hc_data_t *data = hc_request_get_data(current_request); + if (data) { + hc_request_set_complete(current_request); + return 0; + } +#endif + + return hicnlight_prepare_generic(sock, request, buffer); +} + +/* + * This function processes incoming data in the ring buffer. Multiple requests + * might be interleaves, including regular requests and notifications. + * Responses might arrive fragment over several read events, but our + * assumption is that fragments arrive consecutively and are not interleaves + * with fragments from other requests... otherwise we would have to way to + * reconstruct a message. + * + * count != 0 when an external process has added data to the ring buffer + * without updating indices + */ +static int hicnlight_process(hc_sock_t *sock, size_t count) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + int rc; + + if (count > 0) s->woff += count; + + /* + * We loop consuming messages until there is no more data in the ring + * buffer, or that we can find an entire message. Messages are received + * sequentially, and we keep track of incomplete requests in s->cur_request. + */ + while (AVAILABLE(s) > 0) { + if (!s->got_header) { + rc = hicnlight_process_header(sock); + } else { + rc = hicnlight_process_payload(sock); + } + if (rc < 0) break; + } + + if ((rc == -99) || (s->roff == s->woff)) { + /* Flush buffer */ + s->woff = 0; + } else { + /* Clean up read data from buffer */ + memmove(s->buf, s->buf + s->roff, AVAILABLE(s)); + s->woff -= s->roff; + } + s->roff = 0; + + return rc; +} + +hc_sock_ops_t hc_sock_light = (hc_sock_ops_t) { + .create_data = (void *(*)(const char *))hc_sock_light_data_create, + .free_data = (void (*)(void *))hc_sock_light_data_free, + .get_fd = hicnlight_get_fd, .get_recv_buffer = hicnlight_get_recv_buffer, + .connect = hicnlight_connect, .disconnect = hicnlight_disconnect, + .prepare = hicnlight_prepare, .send = hicnlight_send, .recv = hicnlight_recv, + .process = hicnlight_process, +#if 0 + .object_vft = { + [OBJECT_TYPE_LISTENER] = HC_MODULE_OBJECT_OPS(hicnlight, listener), + [OBJECT_TYPE_CONNECTION] = HC_MODULE_OBJECT_OPS(hicnlight, connection), + [OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_ROUTE] = HC_MODULE_OBJECT_OPS(hicnlight, route), + [OBJECT_TYPE_STRATEGY] = HC_MODULE_OBJECT_OPS(hicnlight, strategy), + [OBJECT_TYPE_SUBSCRIPTION] = HC_MODULE_OBJECT_OPS(hicnlight, subscription), +} +#endif +}; + +size_t hc_light_object_size(hc_object_type_t object_type) { + hc_module_object_ops_t *vft = &hc_sock_light.object_vft[object_type]; + if (!vft) return 0; + return vft->serialized_size; +} + +ssize_t hc_light_command_serialize(hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + hc_module_object_ops_t *vft = &hc_sock_light.object_vft[object_type]; + if (!vft || !vft->serialize[action]) return 0; + return vft->serialize[action](object, msg); +} + +// Public constructor + +int hc_sock_initialize_module(hc_sock_t *s) { + // + /* + * We do this because initialization in the static struct fails with + * 'initializer element is not constant' + */ +#if 1 + hc_sock_light.object_vft[OBJECT_TYPE_LISTENER] = + hicnlight_listener_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_CONNECTION] = + hicnlight_connection_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_ROUTE] = hicnlight_route_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_STRATEGY] = + hicnlight_strategy_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_SUBSCRIPTION] = + hicnlight_subscription_module_ops; +#endif + + if (s) s->ops = hc_sock_light; + return 0; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light.h b/ctrl/libhicnctrl/src/modules/hicn_light.h new file mode 100644 index 000000000..0bdcf0b30 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 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 modules/hicn_light.h + * \brief hicn-light module. + */ + +#ifndef HICNCTRL_MODULES_HICN_LIGHT_H +#define HICNCTRL_MODULES_HICN_LIGHT_H + +#include "hicn_light/base.h" + +typedef struct { + char *url; + int fd; + + /* Send buffer */ + hc_msg_t msg; + + /* Partial receive buffer */ + u8 buf[RECV_BUFLEN]; + size_t roff; /**< Read offset */ + size_t woff; /**< Write offset */ + + bool got_header; + /* + * Because received messages are potentially unbounded in size, we might not + * guarantee that we can store a full packet before processing it. We must + * implement a very simple state machine remembering the current parsing + * status in order to partially process the packet. + */ + size_t remaining; + u32 send_id; + + /* Next sequence number to be used for requests */ + int seq; +} hc_sock_light_data_t; + +extern hc_sock_light_data_t *hc_sock_light_data_create(const char *url); +extern void hc_sock_light_data_free(hc_sock_light_data_t *data); + +ssize_t hc_light_command_serialize(hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg); + +#endif /* HICNCTRL_MODULES_HICN_LIGHT_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/base.h b/ctrl/libhicnctrl/src/modules/hicn_light/base.h new file mode 100644 index 000000000..fb6a68147 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/base.h @@ -0,0 +1,60 @@ +#ifndef HICNCTRL_MODULES_HICNLIGHT_BASE_H +#define HICNCTRL_MODULES_HICNLIGHT_BASE_H + +#include <hicn/ctrl/hicn-light.h> + +#if 1 +#ifdef __APPLE__ +#define RANDBYTE() (u8)(arc4random() & 0xFF) +#else +#define RANDBYTE() (u8)(random() & 0xFF) +#endif +#else +#define RANDBYTE() (u8)(rand() & 0xFF) +#endif + +#define foreach_hc_command \ + _(connection_add) \ + _(connection_remove) \ + _(connection_list) \ + _(listener_add) \ + _(listener_remove) \ + _(listener_list) \ + _(route_add) \ + _(route_remove) \ + _(route_list) \ + _(cache_set_store) \ + _(cache_set_serve) \ + _(cache_clear) \ + _(cache_list) \ + _(strategy_set) \ + _(strategy_add_local_prefix) \ + _(wldr_set) \ + _(punting_add) \ + _(mapme_activator) \ + _(mapme_timing) \ + _(subscription_add) \ + _(subscription_remove) + +#if 0 +const char *command_type_str[] = { +#define _(l, u) [COMMAND_TYPE_##u] = STRINGIZE(u), + foreach_command_type +#undef _ +}; +#endif + +typedef union { +#define _(x) cmd_##x##_t x; + foreach_hc_command +#undef _ +} hc_msg_payload_t; + +typedef cmd_header_t hc_msg_header_t; + +typedef struct hc_msg_s { + hc_msg_header_t header; + hc_msg_payload_t payload; +} hc_msg_t; + +#endif /* HICNCTRL_MODULES_HICNLIGHT_BASE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/cache.c b/ctrl/libhicnctrl/src/modules/hicn_light/cache.c new file mode 100644 index 000000000..e085142d7 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/cache.c @@ -0,0 +1,178 @@ +#include "cache.h" + +/* CACHE SET STORE */ + +static int _hcng_cache_set_store_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_set_store_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_SET_STORE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = cache->store, + }}; + + hc_command_params_t params = { + .cmd = ACTION_STORE, + .cmd_id = COMMAND_TYPE_CACHE_SET_STORE, + .size_in = sizeof(cmd_cache_set_store_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_store_internal(s, cache, false); +} + +static int _hcng_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_store_internal(s, cache, true); +} + +/* CACHE SET SERVE */ + +static int _hcng_cache_set_serve_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_set_serve_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_SET_SERVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = cache->serve, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SERVE, + .cmd_id = COMMAND_TYPE_CACHE_SET_SERVE, + .size_in = sizeof(cmd_cache_set_serve_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; /// added +} + +static int _hcng_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_serve_internal(s, cache, false); +} + +static int _hcng_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_serve_internal(s, cache, true); +} + +/* CACHE CLEAR */ + +static int _hcng_cache_clear_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_clear_t msg = {.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_CLEAR, + .length = 1, + .seq_num = 0, + }}; + + hc_command_params_t params = { + .cmd = ACTION_CLEAR, + .cmd_id = COMMAND_TYPE_CACHE_CLEAR, + .size_in = sizeof(cmd_cache_clear_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_cache_clear(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_clear_internal(s, cache, false); +} + +/* CACHE PARSE */ + +static int hc_cache_parse(void *in, hc_cache_info_t *cache_info) { + cmd_cache_list_reply_t *item = (cmd_cache_list_reply_t *)in; + *cache_info = (hc_cache_info_t){.store = item->store_in_cs, + .serve = item->serve_from_cs, + .cs_size = item->cs_size, + .num_stale_entries = item->num_stale_entries}; + + return 0; +} + +/* CACHE LIST */ + +static hc_result_t *_hcng_cache_list_serialize(hc_sock_t *socket, + hc_data_t **pdata, bool async) { + hc_result_t *res = malloc(sizeof(*res)); + DEBUG("[hc_cache_list] async=%s", BOOLSTR(async)); + + msg_cache_list_t msg = {.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_LIST, + .length = 0, + .seq_num = 0, + }}; + + hc_command_params_t params = { + .cmd = ACTION_LIST, + .cmd_id = COMMAND_TYPE_CACHE_LIST, + .size_in = sizeof(cmd_cache_list_reply_t), + .size_out = sizeof(hc_cache_info_t), + .parse = (HC_PARSE)hc_cache_parse, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.cache_list = msg.payload, + }, + .params = params, + .async = async, + .success = true, + }; + return res; +} + +static int _hcng_cache_list_internal(hc_sock_t *socket, hc_data_t **pdata, + bool async) { +#if 0 + hc_result_t *result = _hcng_cache_list_serialize(socket, pdata, async); + + int ret = INPUT_ERROR; + if (result->success) { + ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, pdata, + result->async); + } + + hc_result_free(result); + return ret; +#endif + return 0; // XXX added +} + +static int _hcng_cache_list(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_cache_list_internal(s, pdata, false); +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/connection.c b/ctrl/libhicnctrl/src/modules/hicn_light/connection.c new file mode 100644 index 000000000..c7d06415e --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.c @@ -0,0 +1,498 @@ +/* + * 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 <stdint.h> + +#include <hicn/util/log.h> + +#include "base.h" +#include "connection.h" +#include "../../object_private.h" + +int hc_connection_to_local_listener(const hc_connection_t *connection, + hc_listener_t *listener) { + int rc; + + face_type_t listener_type; + switch (connection->type) { + case FACE_TYPE_UDP: + listener_type = FACE_TYPE_UDP_LISTENER; + break; + case FACE_TYPE_TCP: + listener_type = FACE_TYPE_TCP_LISTENER; + break; + default: + return -1; + } + + *listener = (hc_listener_t){ + .id = ~0, + .type = listener_type, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + }; + rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "lst%u", + RANDBYTE()); // generate name + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_connection_to_local_listener] Unexpected truncation of " + "symbolic name string"); + rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", + connection->interface_name); + if (rc >= INTERFACE_LEN) + WARN( + "[hc_connection_to_local_listener] Unexpected truncation of " + "interface name string"); + + return 0; +} + +/* CONNECTION PARSE */ + +static int hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_connection_t *connection) { + int rc; + + if (size != sizeof(cmd_connection_list_item_t)) return -1; + cmd_connection_list_item_t *item = (cmd_connection_list_item_t *)buffer; + + if (!IS_VALID_ID(item->id)) { + ERROR("[hc_connection_parse] Invalid id received"); + return -1; + } + + if (!IS_VALID_NAME(item->name)) { + ERROR("[hc_connection_parse] Invalid name received"); + return -1; + } + if (!IS_VALID_INTERFACE_NAME(item->interface_name)) { + ERROR("[hc_connection_parse] Invalid interface_name received"); + return -1; + } + + if (!IS_VALID_TYPE(item->type)) { + ERROR("[hc_connection_parse] Invalid type received"); + return -1; + } + + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_connection_parse] Invalid family received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->local_address)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + if (!IS_VALID_PORT(ntohs(item->local_port))) { + ERROR("[hc_connection_parse] Invalid port received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->remote_address)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + if (!IS_VALID_PORT(ntohs(item->remote_port))) { + ERROR("[hc_connection_parse] Invalid port received"); + return -1; + } + + if (!IS_VALID_FACE_STATE(item->admin_state)) { + ERROR("[hc_connection_parse] Invalid admin_state received"); + return -1; + } + + // PRIORITY + // TAGS + + if (!IS_VALID_FACE_STATE(item->state)) { + ERROR("[hc_connection_parse] Invalid state received"); + return -1; + } + + *connection = (hc_connection_t){ + .id = item->id, + .type = (face_type_t)item->type, + .family = (int)item->family, + .local_addr = item->local_addr, + .local_port = ntohs(item->local_port), + .remote_addr = item->remote_addr, + .remote_port = ntohs(item->remote_port), + .admin_state = (face_state_t)item->admin_state, + .priority = item->priority, + .tags = item->tags, + .state = (face_state_t)item->state, + }; + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", item->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", + item->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + if (hc_connection_validate(connection, false) < 0) return -1; + return 0; +} + +int _hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_connection_parse(buffer, size, &object->connection); +} + +/* CONNECTION CREATE */ + +int hicnlight_connection_serialize_create(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_add_t *msg = (msg_connection_add_t *)packet; + *msg = (msg_connection_add_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .remote_ip = connection->remote_addr, + .local_ip = connection->local_addr, + .remote_port = htons(connection->remote_port), + .local_port = htons(connection->local_port), + .family = (uint8_t)connection->family, + .type = (uint8_t)connection->type, + .admin_state = (uint8_t)connection->admin_state, + .__pad = 0, + .priority = connection->priority, + .tags = connection->tags, + }}; + + rc = snprintf(msg->payload.symbolic, SYMBOLIC_NAME_LEN, "%s", + connection->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_connection_add_t); +} + +/* CONNECTION DELETE */ + +int hicnlight_connection_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_remove_t *msg = (msg_connection_remove_t *)packet; + *msg = (msg_connection_remove_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + }; + + if (connection->id) { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + connection->id); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); + } else if (*connection->name) { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + connection->name); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); +#if 0 + } else { + hc_connection_t *connection_found; + if (hc_connection_get(socket, connection, &connection_found) < 0) + return res; + if (!connection_found) return res; + rc = snprintf(payload->symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + connection_found->id); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); + free(connection_found); +#endif + } + + return sizeof(msg_connection_remove_t); +} + +// XXX How to update a connection XXX +// Key attributes are mandatory +// Enum can be undefined +// family UNSPEC +// ip address NULL +// port NULL +// priority = int ????????? specific negative value == unspec +// tags = bitmap ????????? 0xFFFFFF special value == unspec + +// u32 id; /* Kr. */ +// char name[SYMBOLIC_NAME_LEN]; /* K.w */ +// char interface_name[INTERFACE_LEN]; /* Kr. */ +// +// netdevice_type_t netdevice_type; undefined +// face_type_t type; +// int family; +// ip_address_t local_addr; +// u16 local_port; +// ip_address_t remote_addr; +// u16 remote_port; +// face_state_t admin_state; +// uint32_t priority; /* .rw */ +// policy_tags_t tags; /* .rw */ +// face_state_t state; /* .r. */ +int hicnlight_connection_serialize_update(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_update_t *msg = (msg_connection_update_t *)packet; + *msg = (msg_connection_update_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_UPDATE, + .length = 1, + .seq_num = 0, + }, + .payload = { + //.remote_ip = connection->remote_updater, + //.local_ip = connection->local_updater, + //.remote_port = htons(connection->remote_port), + //.local_port = htons(connection->local_port), + //.family = connection->family, + //.type = connection->type, + .admin_state = connection->admin_state, + .priority = connection->priority, + .tags = connection->tags, + }}; + + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + connection->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + // snprintf(msg.payload.interface_name, INTERFACE_NAME_LEN, "%s", + // connection->interface_name); + + return sizeof(msg_connection_update_t); +} + +#if 0 +/* CONNECTION SET ADMIN STATE */ + +static int _hicnlight_connection_set_admin_state_internal( + hc_sock_t *socket, const char *conn_id_or_name, face_state_t state, + bool async) { + int rc; + DEBUG( + "[hc_connection_set_admin_state] connection_id/name=%s admin_state=%s " + "async=%s", + conn_id_or_name, face_state_str(state), BOOLSTR(async)); + + struct { + cmd_header_t hdr; + cmd_connection_set_admin_state_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .admin_state = state, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_admin_state] Unexpected truncation of symbolic " + "name string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, + .size_in = sizeof(cmd_connection_set_admin_state_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +} + +static int _hicnlight_connection_set_admin_state(hc_sock_t *s, + const char *conn_id_or_name, + face_state_t state) { + return _hicnlight_connection_set_admin_state_internal(s, conn_id_or_name, state, + false); +} + +static int _hicnlight_connection_set_admin_state_async(hc_sock_t *s, + const char *conn_id_or_name, + face_state_t state) { + return _hicnlight_connection_set_admin_state_internal(s, conn_id_or_name, state, + true); +} + + +static int _hicnlight_connection_set_priority_internal(hc_sock_t *socket, + const char *conn_id_or_name, + uint32_t priority, + bool async) { + int rc; + DEBUG( + "[hc_connection_set_priority] connection_id/name=%s priority=%d " + "async=%s", + conn_id_or_name, priority, BOOLSTR(async)); + struct { + cmd_header_t hdr; + cmd_connection_set_priority_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .priority = priority, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_priority] Unexpected truncation of symbolic " + "name " + "string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, + .size_in = sizeof(cmd_connection_set_priority_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +} + +static int _hicnlight_connection_set_priority(hc_sock_t *s, + const char *conn_id_or_name, + uint32_t priority) { + return _hicnlight_connection_set_priority_internal(s, conn_id_or_name, priority, + false); +} + +static int _hicnlight_connection_set_priority_async(hc_sock_t *s, + const char *conn_id_or_name, + uint32_t priority) { + return _hicnlight_connection_set_priority_internal(s, conn_id_or_name, priority, + true); +} + + +static int _hicnlight_connection_set_tags_internal(hc_sock_t *s, + const char *conn_id_or_name, + policy_tags_t tags, bool async) { + int rc; + DEBUG("[hc_connection_set_tags] connection_id/name=%s tags=%d async=%s", + conn_id_or_name, tags, BOOLSTR(async)); + struct { + cmd_header_t hdr; + cmd_connection_set_tags_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_TAGS, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .tags = tags, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_tags] Unexpected truncation of symbolic name " + "string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_TAGS, + .size_in = sizeof(cmd_connection_set_tags_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(s, (hc_msg_t *)&msg, sizeof(msg), ¶ms, NULL, + async); +} + +static int _hicnlight_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, + policy_tags_t tags) { + return _hicnlight_connection_set_tags_internal(s, conn_id_or_name, tags, false); +} + +static int _hicnlight_connection_set_tags_async(hc_sock_t *s, + const char *conn_id_or_name, + policy_tags_t tags) { + return _hicnlight_connection_set_tags_internal(s, conn_id_or_name, tags, true); +} +#endif + +/* CONNECTION LIST */ + +int hicnlight_connection_serialize_list(const hc_object_t *object, + uint8_t *packet) { + msg_connection_list_t *msg = (msg_connection_list_t *)packet; + *msg = (msg_connection_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_LIST, + .length = 0, + .seq_num = 0, + }}; + return sizeof(msg_header_t); // Do not use msg_connection_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, connection); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/connection.h b/ctrl/libhicnctrl/src/modules/hicn_light/connection.h new file mode 100644 index 000000000..77204e6b2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.h @@ -0,0 +1,27 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H +#define HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H + +#include "../../module.h" + +int hc_connection_to_local_listener(const hc_connection_t *connection, + hc_listener_t *listener); + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, connection); +// extern const hc_module_object_ops_t hicnlight_connection_module_ops; + +#else + +int _hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_connection_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_connection_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_connection_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif +#endif /* HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/face.c b/ctrl/libhicnctrl/src/modules/hicn_light/face.c new file mode 100644 index 000000000..0d9475420 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.c @@ -0,0 +1,482 @@ +#include <hicn/ctrl/objects/listener.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "face.h" + +int hc_face_from_connection(const hc_connection_t *connection, + hc_face_t *face) { + int rc; + switch (connection->type) { + case FACE_TYPE_TCP: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_TCP, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + .remote_addr = connection->remote_addr, + .remote_port = connection->remote_port, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + case FACE_TYPE_UDP: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_UDP, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + .remote_addr = connection->remote_addr, + .remote_port = connection->remote_port, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + case FACE_TYPE_HICN: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_HICN, + .family = connection->family, + .netdevice.index = NETDEVICE_UNDEFINED_INDEX, // XXX + .local_addr = connection->local_addr, + .remote_addr = connection->remote_addr, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + default: + return -1; + } + face->netdevice.name[0] = '\0'; + face->netdevice.index = 0; + rc = snprintf(face->name, SYMBOLIC_NAME_LEN, "%s", connection->name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_connection_to_face] Unexpected truncation of symbolic name " + "string"); + rc = snprintf(face->netdevice.name, INTERFACE_LEN, "%s", + connection->interface_name); + if (rc >= INTERFACE_LEN) + WARN( + "[hc_connection_to_face] Unexpected truncation of interface name " + "string"); + netdevice_update_index(&face->netdevice); + return 0; +} + +int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, + bool generate_name) { + int rc; + + switch (face->type) { + case FACE_TYPE_HICN: + *connection = (hc_connection_t){ + .type = FACE_TYPE_HICN, + .family = face->family, + .local_addr = face->local_addr, + .local_port = 0, + .remote_addr = face->remote_addr, + .remote_port = 0, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", + face->netdevice.name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of symbolic " + "name string"); + break; + case FACE_TYPE_TCP: + *connection = (hc_connection_t){ + .type = FACE_TYPE_TCP, + .family = face->family, + .local_addr = face->local_addr, + .local_port = face->local_port, + .remote_addr = face->remote_addr, + .remote_port = face->remote_port, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + if (generate_name) { + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "tcp%u", RANDBYTE()); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of " + "symbolic name string"); + } else { + memset(connection->name, 0, SYMBOLIC_NAME_LEN); + } + break; + case FACE_TYPE_UDP: + *connection = (hc_connection_t){ + .type = FACE_TYPE_UDP, + .family = face->family, + .local_addr = face->local_addr, + .local_port = face->local_port, + .remote_addr = face->remote_addr, + .remote_port = face->remote_port, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + if (generate_name) { + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "udp%u", RANDBYTE()); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of " + "symbolic name string"); + } else { + memset(connection->name, 0, SYMBOLIC_NAME_LEN); + } + snprintf(connection->interface_name, INTERFACE_LEN, "%s", + face->netdevice.name); + break; + default: + return -1; + } + + connection->id = face->id; + rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", + face->netdevice.name); + if (rc >= INTERFACE_LEN) + WARN( + "hc_face_to_connection] Unexpected truncation of interface name " + "string"); + + return 0; +} + +int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener) { + switch (face->type) { + case FACE_TYPE_HICN_LISTENER: + break; + case FACE_TYPE_TCP_LISTENER: + break; + case FACE_TYPE_UDP_LISTENER: + break; + default: + return -1; + } + return -1; /* XXX Not implemented */ +} + +#if 0 +/*----------------------------------------------------------------------------* + * Face + * + * Face support is not directly available in hicn-light, but we can offer such + * an interface through a combination of listeners and connections. The code + * starts with some conversion functions between faces/listeners/connections. + * + * We also need to make sure that there always exist a (single) listener when + *a connection is created, and in the hICN face case, that there is a single + * connection attached to this listener. + * + *----------------------------------------------------------------------------*/ + +/* FACE CREATE */ + +static int _hcng_face_create(hc_sock_t *socket, hc_face_t *face) { +#if 0 + hc_listener_t listener; + hc_listener_t *listener_found; + + hc_connection_t connection; + hc_connection_t *connection_found; + + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_create] Unexpected truncation of face string"); + DEBUG("[hc_face_create] face=%s", face_s); + + switch (face->face.type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + if (hc_face_to_connection(face, &connection, true) < 0) { + ERROR("[hc_face_create] Could not convert face to connection."); + return -1; + } + + /* Ensure we have a corresponding local listener */ + if (hc_connection_to_local_listener(&connection, &listener) < 0) { + ERROR("[hc_face_create] Could not convert face to local listener."); + return -1; + } + + if (_hcng_listener_get(socket, &listener, &listener_found) < 0) { + ERROR("[hc_face_create] Could not retrieve listener"); + return -1; + } + + if (!listener_found) { + /* We need to create the listener if it does not exist */ + if (hc_listener_create(socket, &listener) < 0) { + ERROR("[hc_face_create] Could not create listener."); + free(listener_found); + return -1; + } + } else { + free(listener_found); + } + + /* Create corresponding connection */ + if (_hcng_connection_create(socket, &connection) < 0) { + ERROR("[hc_face_create] Could not create connection."); + return -1; + } + + /* + * Once the connection is created, we need to list all connections + * and compare with the current one to find the created face ID. + */ + if (_hcng_connection_get(socket, &connection, &connection_found) < 0) { + ERROR("[hc_face_create] Could not retrieve connection"); + return -1; + } + + if (!connection_found) { + ERROR("[hc_face_create] Could not find newly created connection."); + return -1; + } + + face->id = connection_found->id; + free(connection_found); + + break; + + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + if (hc_face_to_listener(face, &listener) < 0) { + ERROR("Could not convert face to listener."); + return -1; + } + if (hc_listener_create(socket, &listener) < 0) { + ERROR("[hc_face_create] Could not create listener."); + return -1; + } + break; + default: + ERROR("[hc_face_create] Unknwon face type."); + + return -1; + }; + +#endif + return 0; +} + +static int _hcng_face_get(hc_sock_t *socket, hc_face_t *face, + hc_face_t **face_found) { +#if 0 + hc_listener_t listener; + hc_listener_t *listener_found; + + hc_connection_t connection; + hc_connection_t *connection_found; + + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_get] Unexpected truncation of face string"); + DEBUG("[hc_face_get] face=%s", face_s); + + switch (face->face.type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + if (hc_face_to_connection(face, &connection, false) < 0) return -1; + if (_hcng_connection_get(socket, &connection, &connection_found) < 0) + return -1; + if (!connection_found) { + *face_found = NULL; + return 0; + } + *face_found = malloc(sizeof(hc_face_t)); + hc_connection_to_face(connection_found, *face_found); + free(connection_found); + break; + + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + if (hc_face_to_listener(face, &listener) < 0) return -1; + if (_hcng_listener_get(socket, &listener, &listener_found) < 0) return -1; + if (!listener_found) { + *face_found = NULL; + return 0; + } + *face_found = malloc(sizeof(hc_face_t)); + hc_listener_to_face(listener_found, *face_found); + free(listener_found); + break; + + default: + return -1; + } + +#endif + return 0; +} + +/* FACE DELETE */ + +static int _hcng_face_delete(hc_sock_t *socket, hc_face_t *face, + uint8_t delete_listener) { +#if 0 + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_delete] Unexpected truncation of face string"); + DEBUG("[hc_face_delete] face=%s", face_s); + + 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; + } + + if (_hcng_connection_delete(socket, &connection) < 0) { + ERROR("[hc_face_delete] Error removing connection"); + return -1; + } + + if (!delete_listener) { + return 0; + } + + /* 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 (_hcng_connection_list(socket, &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 (_hcng_listener_delete(socket, &listener) < 0) { + ERROR("[hc_face_delete] Error removing listener"); + return -1; + } + } + + hc_data_free(connections); + +#endif + return 0; +} + +/* FACE LIST */ + +static int _hcng_face_list(hc_sock_t *socket, hc_data_t **pdata) { +#if 0 + hc_data_t *connection_data; + hc_face_t face; + + DEBUG("[hc_face_list]"); + + if (_hcng_connection_list(socket, &connection_data) < 0) { + ERROR("[hc_face_list] Could not list connections."); + return -1; + } + + hc_data_t *face_data = + hc_data_create(sizeof(hc_connection_t), sizeof(hc_face_t), NULL); + foreach_connection(c, connection_data) { + if (hc_connection_to_face(c, &face) < 0) { + ERROR("[hc_face_list] Could not convert connection to face."); + goto ERR; + } + hc_data_push(face_data, &face); + } + + *pdata = face_data; + hc_data_free(connection_data); + DEBUG("[hc_face_list] done"); + return 0; + +ERR: + hc_data_free(connection_data); + DEBUG("[hc_face_list] error"); +#endif + return -1; +} + +static int hc_connection_parse_to_face(void *in, hc_face_t *face) { + hc_connection_t connection; + + if (hcng_connection_parse(in, &connection) < 0) { + ERROR("[hc_connection_parse_to_face] Could not parse connection"); + return -1; + } + + if (hc_connection_to_face(&connection, face) < 0) { + ERROR( + "[hc_connection_parse_to_face] Could not convert connection to " + "face."); + return -1; + } + + return 0; +} + +static int _hcng_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, + face_state_t admin_state) { + return hc_connection_set_admin_state(s, conn_id_or_name, admin_state); +} + +static int _hcng_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, + uint32_t priority) { + return hc_connection_set_priority(s, conn_id_or_name, priority); +} + +static int _hcng_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, + policy_tags_t tags) { + return hc_connection_set_tags(s, conn_id_or_name, tags); +} + +#endif diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/face.h b/ctrl/libhicnctrl/src/modules/hicn_light/face.h new file mode 100644 index 000000000..9e1cd48c2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.h @@ -0,0 +1,14 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_FACE +#define HICNCTRL_MODULE_HICNLIGHT_FACE + +#include <hicn/ctrl/objects/connection.h> +#include <hicn/ctrl/objects/face.h> + +int hc_face_from_connection(const hc_connection_t *connection, hc_face_t *face); + +int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, + bool generate_name); + +int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener); + +#endif /* HICNCTRL_MODULES_HICNLIGHT_FACE */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/listener.c b/ctrl/libhicnctrl/src/modules/hicn_light/listener.c new file mode 100644 index 000000000..68d4b8fcd --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.c @@ -0,0 +1,176 @@ +/* + * 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 <hicn/ctrl/api.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "../../object_private.h" +#include "listener.h" + +static int hicnlight_listener_parse(const u8 *buffer, size_t size, + hc_listener_t *listener) { + int rc; + + if (size != sizeof(cmd_listener_list_item_t)) return -1; + cmd_listener_list_item_t *item = (cmd_listener_list_item_t *)buffer; + + if (!IS_VALID_ADDRESS(item->local_address)) { + ERROR("[hc_listener_parse] Invalid address received"); + return -1; + } + if (!IS_VALID_NAME(item->name)) { + ERROR("[hc_listener_parse] Invalid name received"); + return -1; + } + if (!IS_VALID_INTERFACE_NAME(item->interface_name)) { + ERROR("[hc_listener_parse] Invalid interface_name received"); + return -1; + } + if (!IS_VALID_ID(item->id)) { + ERROR("[hc_listener_parse] Invalid id received"); + return -1; + } + if (!IS_VALID_PORT(ntohs(item->local_port))) { + ERROR("[hc_listener_parse] Invalid port received"); + return -1; + } + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_listener_parse] Invalid family received"); + return -1; + } + if (!IS_VALID_TYPE(item->type)) { + ERROR("[hc_listener_parse] Invalid type received"); + return -1; + } + // if (!(IS_VALID_CONNECTION_TYPE(item->type))) + // return -1; + + *listener = (hc_listener_t){ + .id = item->id, + .type = (face_type_t)(item->type), + .family = (int)(item->family), + .local_addr = + item->local_addr, // UNION_CAST(item->local_addr, ip_address_t), + .local_port = ntohs(item->local_port), + }; + + rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "%s", item->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", + item->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + if (hc_listener_validate(listener, false) < 0) return -1; + return 0; +} + +int _hicnlight_listener_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_listener_parse(buffer, size, &object->listener); +} + +/* LISTENER CREATE */ + +int hicnlight_listener_serialize_create(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_listener_t *listener = &object->listener; + + msg_listener_add_t *msg = (msg_listener_add_t *)packet; + *msg = (msg_listener_add_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_ADD, + .length = 1, + .seq_num = 0, + }, + + .payload = { + .address = listener->local_addr, + .port = htons(listener->local_port), + .family = (uint8_t)listener->family, + .type = (uint8_t)listener->type, + }}; + + rc = snprintf(msg->payload.symbolic, SYMBOLIC_NAME_LEN, "%s", listener->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(msg->payload.interface_name, INTERFACE_LEN, "%s", + listener->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + return sizeof(msg_listener_add_t); +} + +/* LISTENER DELETE */ + +int hicnlight_listener_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_listener_t *listener = &object->listener; + + msg_listener_remove_t *msg = (msg_listener_remove_t *)packet; + *msg = (msg_listener_remove_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_REMOVE, + .length = 1, + .seq_num = 0, + }}; + + if (listener->id) { + rc = snprintf(msg->payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", + listener->id); + } else if (*listener->name) { + rc = snprintf(msg->payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%s", + listener->name); + } else { + return -1; + } + + // For now we only support delete by name or id +#if 0 + hc_listener_t *listener_found; + if (hc_listener_get(socket, listener, &listener_found) < 0) return -1; + if (!listener_found) return -1; + rc = snprintf(payload->symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", + listener_found->id); + free(listener_found); + } +#endif + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_listener_remove_t); +} + +/* LISTENER LIST */ + +int hicnlight_listener_serialize_list(const hc_object_t *object, + uint8_t *packet) { + msg_listener_list_t *msg = (msg_listener_list_t *)packet; + *msg = (msg_listener_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_LIST, + .length = 0, + .seq_num = 0, + }}; + + return sizeof(msg_header_t); // Do not use msg_listener_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, listener); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/listener.h b/ctrl/libhicnctrl/src/modules/hicn_light/listener.h new file mode 100644 index 000000000..27ef8434d --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.h @@ -0,0 +1,21 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_LISTENER_H +#define HICNCTRL_MODULE_HICNLIGHT_LISTENER_H + +#include "../../module.h" + +#if 1 +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, listener); +#else + +int _hicnlight_listener_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_listener_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_listener_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_listener_serialize_list(const hc_object_t *object, + uint8_t *packet); +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c new file mode 100644 index 000000000..de75d82a8 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c @@ -0,0 +1,139 @@ +#include "mapme.h" + +static int _hcng_mapme_set(hc_sock_t *socket, int enabled) { +#if 0 + msg_mapme_enable_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_ENABLE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = enabled, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_ENABLE, + .size_in = sizeof(cmd_mapme_enable_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_discovery(hc_sock_t *socket, int enabled) { +#if 0 + msg_mapme_enable_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = enabled, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, + .size_in = sizeof(cmd_mapme_set_discovery_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_timescale(hc_sock_t *socket, uint32_t timescale) { +#if 0 + msg_mapme_set_timescale_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .timePeriod = timescale, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, + .size_in = sizeof(cmd_mapme_set_timescale_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_retx(hc_sock_t *socket, uint32_t timescale) { +#if 0 + msg_mapme_set_retx_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_RETX, + .length = 1, + .seq_num = 0, + }, + .payload = { + .timePeriod = timescale, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_RETX, + .size_in = sizeof(msg_mapme_set_retx_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_send_update(hc_sock_t *socket, hc_mapme_t *mapme) { +#if 0 + if (!IS_VALID_FAMILY(mapme->family)) return -1; + + msg_mapme_send_update_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .length = 1, + .seq_num = 0, + }, + }; + + hc_command_params_t params = { + .cmd = ACTION_UPDATE, + .cmd_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .size_in = sizeof(msg_mapme_send_update_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/policy.c b/ctrl/libhicnctrl/src/modules/hicn_light/policy.c new file mode 100644 index 000000000..d48ce014d --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/policy.c @@ -0,0 +1,162 @@ +#include "policy.h" + +/* POLICY CREATE */ + +static int _hcng_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, + bool async) { +#if 0 + if (!IS_VALID_FAMILY(policy->family)) return -1; + + struct { + cmd_header_t hdr; + cmd_policy_add_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + COMMAND_TYPE_POLICY_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = policy->remote_addr, + .family = policy->family, + .len = policy->len, + .policy = policy->policy, + }}; + + hc_command_params_t params = { + .cmd = ACTION_CREATE, + .cmd_id = COMMAND_TYPE_POLICY_ADD, + .size_in = sizeof(cmd_policy_add_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_create(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_create_internal(s, policy, false); +} + +static int _hcng_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_create_internal(s, policy, true); +} + +/* POLICY DELETE */ + +static int _hcng_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, + bool async) { +#if 0 + if (!IS_VALID_FAMILY(policy->family)) return -1; + + struct { + cmd_header_t hdr; + cmd_policy_remove_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_POLICY_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = policy->remote_addr, + .family = policy->family, + .len = policy->len, + }}; + + hc_command_params_t params = { + .cmd = ACTION_DELETE, + .cmd_id = COMMAND_TYPE_POLICY_REMOVE, + .size_in = sizeof(cmd_policy_remove_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_delete(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_delete_internal(s, policy, false); +} + +static int _hcng_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_delete_internal(s, policy, true); +} + +/* POLICY PARSE */ + +static int hc_policy_parse(void *in, hc_policy_t *policy) { + hc_policy_t *item = (hc_policy_t *)in; + + if (!IS_VALID_ADDRESS(item->address)) { + ERROR("[hc_policy_parse] Invalid address"); + return -1; + } + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_policy_parse] Invalid family"); + return -1; + } + if (!IS_VALID_PREFIX_LEN(item->len)) { + ERROR("[hc_policy_parse] Invalid len"); + return -1; + } + if (!IS_VALID_POLICY(item->policy)) { + ERROR("[hc_policy_parse] Invalid policy"); + return -1; + } + + *policy = (hc_policy_t){ + .family = item->family, + .remote_addr = item->remote_addr, + .len = item->len, + .policy = item->policy, + }; + return 0; +} + +/* POLICY LIST */ + +static int _hcng_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, + bool async) { +#if 0 + struct { + cmd_header_t hdr; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_POLICY_LIST, + .length = 0, + .seq_num = 0, + }, + }; + + hc_command_params_t params = { + .cmd = ACTION_LIST, + .cmd_id = COMMAND_TYPE_POLICY_LIST, + .size_in = sizeof(hc_policy_t), + .size_out = sizeof(hc_policy_t), + .parse = (HC_PARSE)hc_policy_parse, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + pdata, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_list(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_policy_list_internal(s, pdata, false); +} + +static int _hcng_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_policy_list_internal(s, pdata, true); +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/punting.c b/ctrl/libhicnctrl/src/modules/hicn_light/punting.c new file mode 100644 index 000000000..7886ff839 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/punting.c @@ -0,0 +1,74 @@ +#include "punting.h" + +static int _hcng_punting_create_internal(hc_sock_t *socket, + hc_punting_t *punting, bool async) { +#if 0 + int rc; + + if (hc_punting_validate(punting) < 0) return -1; + + struct { + cmd_header_t hdr; + cmd_punting_add_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_PUNTING_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = punting->prefix, + .family = punting->family, + .len = punting->prefix_len, + }}; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + punting->face_id); + if (rc >= SYMBOLIC_NAME_LEN) + WARN("[_hc_punting_create] Unexpected truncation of symbolic name string"); + + hc_command_params_t params = { + .cmd = ACTION_CREATE, + .cmd_id = COMMAND_TYPE_PUNTING_ADD, + .size_in = sizeof(cmd_punting_add_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_punting_create(hc_sock_t *s, hc_punting_t *punting) { + return _hcng_punting_create_internal(s, punting, false); +} + +static int _hcng_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { + return _hcng_punting_create_internal(s, punting, true); +} + +static int _hcng_punting_get(hc_sock_t *s, hc_punting_t *punting, + hc_punting_t **punting_found) { + ERROR("hc_punting_get not (yet) implemented."); + return -1; +} + +static int _hcng_punting_delete(hc_sock_t *s, hc_punting_t *punting) { + ERROR("hc_punting_delete not (yet) implemented."); + return -1; +} + +#if 0 +static int hc_punting_parse(void * in, hc_punting_t * punting) +{ + ERROR("hc_punting_parse not (yet) implemented."); + return -1; +} +#endif + +static int _hcng_punting_list(hc_sock_t *s, hc_data_t **pdata) { + ERROR("hc_punting_list not (yet) implemented."); + return -1; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/route.c b/ctrl/libhicnctrl/src/modules/hicn_light/route.c new file mode 100644 index 000000000..1adfe4f36 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/route.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file modules/hicn_light/route.c + * \brief Implementation of route object VFT for hicn_light. + */ + +#include <assert.h> +#include <hicn/ctrl/api.h> +#include <hicn/util/log.h> +#include "base.h" +#include "route.h" +#include <hicn/ctrl/hicn-light.h> + +#include "../../object_private.h" + +static int hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_route_t *route) { + if (size != sizeof(cmd_route_list_item_t)) return -1; + cmd_route_list_item_t *item = (cmd_route_list_item_t *)buffer; + + if (!IS_VALID_NAME(item->face_name)) { + ERROR("[hc_connection_parse] Invalid face_name received"); + return -1; + } + + if (!IS_VALID_ID(item->face_id)) { + ERROR("[hc_connection_parse] Invalid face_id received"); + return -1; + } + + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_listener_parse] Invalid family received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->remote_addr)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + // LEN + // COST + + *route = (hc_route_t){ + .face_name = "", /* This is not reported back */ + .face_id = item->face_id, + .family = (int)(item->family), + .remote_addr = item->remote_addr, + .len = item->len, + .cost = item->cost, + }; + + if (hc_route_validate(route, false) < 0) return -1; + return 0; +} + +int _hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_route_parse(buffer, size, &object->route); +} + +/* ROUTE CREATE */ + +int hicnlight_route_serialize_create(const hc_object_t *object, + uint8_t *packet) { + const hc_route_t *route = &object->route; + int rc; + + msg_route_add_t *msg = (msg_route_add_t *)packet; + *msg = (msg_route_add_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = route->remote_addr, + .cost = route->cost, + .family = route->family, + .len = route->len, + }}; + + /* + * The route commands expects the ID or name as part of the + * symbolic_or_connid attribute. + */ + if (route->face_name[0] != '\0') { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + route->face_name); + } else { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + route->face_id); + } + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_route_add_t); +} + +/* ROUTE DELETE */ + +int hicnlight_route_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + const hc_route_t *route = &object->route; + int rc; + + msg_route_remove_t *msg = (msg_route_remove_t *)packet; + memset(msg, 0, sizeof(msg_route_remove_t)); + *msg = (msg_route_remove_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .family = (uint8_t)route->family, + .len = route->len, + }}; + + /* + * Direct copy as part of the previous assignment does not work correctly... + * to be investigated + */ + memcpy(&msg->payload.address, &route->remote_addr, sizeof(hicn_ip_address_t)); + + /* + * The route commands expects the ID or name as part of the + * symbolic_or_connid attribute. + */ + if (route->face_name[0] != '\0') { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + route->face_name); + } else { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + route->face_id); + } + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_route_remove_t); +} + +/* ROUTE LIST */ + +int hicnlight_route_serialize_list(const hc_object_t *object, uint8_t *packet) { + msg_route_list_t *msg = (msg_route_list_t *)packet; + *msg = (msg_route_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_LIST, + .length = 0, + .seq_num = 0, + }}; + + return sizeof(msg_header_t); // Do not use msg_route_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, route); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/route.h b/ctrl/libhicnctrl/src/modules/hicn_light/route.h new file mode 100644 index 000000000..e86e8b8c3 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/route.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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 modules/hicn_light/route.h + * \brief route object VFT for hicn_light. + */ + +#ifndef HICNCTRL_MODULE_HICNLIGHT_ROUTE_H +#define HICNCTRL_MODULE_HICNLIGHT_ROUTE_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, route); + +#else + +int _hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); +int hicnlight_route_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_route_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_route_serialize_list(const hc_object_t *object, uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/stats.h b/ctrl/libhicnctrl/src/modules/hicn_light/stats.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/stats.h diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c new file mode 100644 index 000000000..35e241f3e --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c @@ -0,0 +1,205 @@ +#include <assert.h> +#include "strategy.h" + +#include <hicn/ctrl/hicn-light.h> + +static int hicnlight_strategy_parse(const u8 *buffer, size_t size, + hc_strategy_t *strategy) { + return -1; +} + +int _hicnlight_strategy_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_strategy_parse(buffer, size, &object->strategy); +} + +int hicnlight_strategy_serialize_create(const hc_object_t *object, + uint8_t *packet) { + return -1; +} + +int hicnlight_strategy_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + return -1; +} + +int hicnlight_strategy_serialize_list(const hc_object_t *object, + uint8_t *packet) { + assert(!object); + return -1; +} +#if 0 +// per prefix +static hc_result_t *_strategy_set_serialize(hc_sock_t *socket, + hc_strategy_t *strategy) { + return -1; + hc_result_t *res = malloc(sizeof(*res)); + char strategy_s[MAXSZ_HC_STRATEGY]; + strncpy(strategy->name, strategy_str(strategy->type), + MAXSZ_STRATEGY_NAME - 1); + + int rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); + if (rc >= MAXSZ_HC_STRATEGY) + WARN("[hicnlight_strategy_create] Unexpected truncation of strategy string"); + DEBUG("[hicnlight_strategy_create] strategy=%s", strategy_s); + + if (!IS_VALID_FAMILY(strategy->family) || + !IS_VALID_STRATEGY_TYPE(strategy->type)) { + res->success = false; + return res; + } + + msg_strategy_set_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_SET, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = strategy->address, + .family = strategy->family, + .len = strategy->len, + .type = strategy->type, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_STRATEGY_SET, + .size_in = sizeof(cmd_strategy_set_t), + .size_out = 0, + .parse = NULL, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.strategy_set = msg.payload, + }, + .params = params, + .async = false, + .success = true, + }; + return res; +} +#endif + +#if 0 +static hc_result_t *_strategy_add_local_prefix_serialize( + hc_sock_t *socket, hc_strategy_t *strategy) { + hc_result_t *res = malloc(sizeof(*res)); + char strategy_s[MAXSZ_HC_STRATEGY]; + strncpy(strategy->name, strategy_str(strategy->type), + MAXSZ_STRATEGY_NAME - 1); + + int rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); + if (rc >= MAXSZ_HC_STRATEGY) + WARN("[hicnlight_strategy_create] Unexpected truncation of strategy string"); + DEBUG("[hicnlight_strategy_create] strategy=%s", strategy_s); + + if (!IS_VALID_FAMILY(strategy->family) || + !IS_VALID_STRATEGY_TYPE(strategy->type) || + !IS_VALID_FAMILY(strategy->local_family)) { + res->success = false; + return res; + } + + msg_strategy_add_local_prefix_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, + .length = 1, + .seq_num = 0, + }, + .payload = { + .type = strategy->type, + .address = strategy->address, + .family = strategy->family, + .len = strategy->len, + .local_address = strategy->local_address, + .local_family = strategy->local_family, + .local_len = strategy->local_len, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, + .size_in = sizeof(cmd_strategy_add_local_prefix_t), + .size_out = 0, + .parse = NULL, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.strategy_add_local_prefix = msg.payload, + }, + .params = params, + .async = false, + .success = true, + }; + return res; +} +#endif + +#if 0 +static int hicnlight_strategy_set(hc_sock_t *socket, hc_strategy_t *strategy) { + hc_result_t *result = _strategy_set_serialize(socket, strategy); + + int ret = INPUT_ERROR; + if (result->success) { + ret = hicnlight_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, NULL, + result->async); + } + + hc_result_free(result); + return ret; + return -1; // XXX added +} + +static int hicnlight_strategy_add_local_prefix(hc_sock_t *socket, + hc_strategy_t *strategy) { + hc_result_t *result = _strategy_add_local_prefix_serialize(socket, strategy); + + int ret = INPUT_ERROR; + if (result->success) { + ret = hicnlight_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, NULL, + result->async); + } + + hc_result_free(result); + return ret; + return -1; // XXX added +} + +/* How to retrieve that from the forwarder ? */ +static const char *strategies[] = { + "random", + "load_balancer", +}; + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) + +static int hicnlight_strategy_list(hc_sock_t *s, hc_data_t **data) { + int rc; + + *data = hc_data_create(0, sizeof(hc_strategy_t), NULL); + + for (unsigned i = 0; i < ARRAY_SIZE(strategies); i++) { + hc_strategy_t *strategy = (hc_strategy_t *)hc_data_get_next(*data); + if (!strategy) return -1; + rc = snprintf(strategy->name, MAXSZ_STRATEGY_NAME, "%s", strategies[i]); + if (rc >= MAXSZ_STRATEGY_NAME) + WARN("[hc_strategy_list] Unexpected truncation of strategy name string"); + (*data)->size++; + } + return -1; +} +#endif + +DECLARE_MODULE_OBJECT_OPS(hicnlight, strategy); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h new file mode 100644 index 000000000..6b1933960 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h @@ -0,0 +1,24 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H +#define HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, strategy); + +int _hicnlight_strategy_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_strategy_serialize_create(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_strategy_serialize_delete(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_strategy_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c new file mode 100644 index 000000000..d00055e89 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c @@ -0,0 +1,66 @@ +#include <assert.h> +#include <hicn/ctrl/api.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "../../object_private.h" +#include "subscription.h" + +static int hicnlight_subscription_parse(const u8 *buffer, size_t size, + hc_subscription_t *subscription) { + /* We should never have to parse subscriptions */ + return -1; +} + +int _hicnlight_subscription_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_subscription_parse(buffer, size, &object->subscription); +} + +/* SUBSCRIPTION CREATE */ + +int hicnlight_subscription_serialize_create(const hc_object_t *object, + uint8_t *packet) { + const hc_subscription_t *subscription = &object->subscription; + + msg_subscription_add_t *msg = (msg_subscription_add_t *)packet; + *msg = (msg_subscription_add_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_SUBSCRIPTION_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = {.topics = subscription->topics}}; + + return sizeof(msg_subscription_add_t); +} + +/* SUBSCRIPTION DELETE */ + +int hicnlight_subscription_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + const hc_subscription_t *subscription = &object->subscription; + + msg_subscription_remove_t *msg = (msg_subscription_remove_t *)packet; + *msg = (msg_subscription_remove_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = {.topics = subscription->topics}}; + + return sizeof(msg_subscription_remove_t); +} + +int hicnlight_subscription_serialize_list(const hc_object_t *object, + uint8_t *packet) { + assert(!object); + return -1; +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, subscription); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h new file mode 100644 index 000000000..a4edf556b --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h @@ -0,0 +1,26 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H +#define HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, subscription); + +#else + +int _hicnlight_subscription_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_subscription_serialize_create(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_subscription_serialize_delete(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_subscription_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c b/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c new file mode 100644 index 000000000..7d1812ab2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c @@ -0,0 +1,3 @@ +#include "wldr.h" + +static int _hcng_wldr_set(hc_sock_t *s /* XXX */) { return 0; } diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_common.h b/ctrl/libhicnctrl/src/modules/hicn_light_common.h deleted file mode 100644 index d24b5bb2d..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_light_common.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HICNCTRL_HICN_LIGHT_COMMON -#define HICNCTRL_HICN_LIGHT_COMMON - -#include <assert.h> // assert - -#include <hicn/util/khash.h> -#include "api_private.h" - -#define PORT 9695 - -#define BOOLSTR(x) ((x) ? "true" : "false") - -/* - * Internal state associated to a pending request - */ -typedef struct { - int seq; - hc_data_t *data; - int (*parse)(const u8 *src, u8 *dst); -} hc_sock_request_t; - -/** - * Messages to the forwarder might be multiplexed thanks to the seqNum fields in - * the header_control_message structure. The forwarder simply answers back the - * original sequence number. We maintain a map of such sequence number to - * outgoing queries so that replied can be demultiplexed and treated - * appropriately. - */ -KHASH_MAP_INIT_INT(sock_map, hc_sock_request_t *); - -struct hc_sock_light_s { - /* This must be the first element of the struct */ - hc_sock_t vft; - - char *url; - int fd; - - /* Partial receive buffer */ - u8 buf[RECV_BUFLEN]; - size_t roff; /**< Read offset */ - size_t woff; /**< Write offset */ - - /* - * Because received messages are potentially unbounded in size, we might not - * guarantee that we can store a full packet before processing it. We must - * implement a very simple state machine remembering the current parsing - * status in order to partially process the packet. - */ - size_t remaining; - u32 send_id; - - /* Next sequence number to be used for requests */ - int seq; - - /* Request being parsed (NULL if none) */ - hc_sock_request_t *cur_request; - - bool async; - kh_sock_map_t *map; -}; - -typedef struct hc_sock_light_s hc_sock_light_t; - -#define TO_HC_SOCK_LIGHT(s) (hc_sock_light_t *)(s) - -hc_sock_request_t *hc_sock_request_create(int seq, hc_data_t *data, - HC_PARSE parse); - -void hc_sock_light_request_free(hc_sock_request_t *request); - -/* - * list was working with all seq set to 0, but it seems hicnLightControl uses - * 1, and replies with the same seqno - */ -#define HICN_CTRL_SEND_SEQ_INIT 1 -#define HICN_CTRL_RECV_SEQ_INIT 1 - -#define MAX(x, y) ((x > y) ? x : y) - -static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; - -#endif /* HICNCTRL_HICN_LIGHT_COMMON */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c b/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c deleted file mode 100644 index 488b2edbf..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c +++ /dev/null @@ -1,3238 +0,0 @@ -/* - * Copyright (c) 2021 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 api.c - * \brief Implementation of hICN control library API - */ - -#include <assert.h> // assert -#include <fcntl.h> // fcntl -#include <stdbool.h> -#include <stdio.h> // snprintf -#include <string.h> // memmove, strcasecmp -#include <sys/socket.h> // socket -#include <sys/types.h> // getpid -#include <unistd.h> // close, fcntl -#include <unistd.h> // getpid - -#include "api_private.h" -#ifdef __linux__ -#include <sys/syscall.h> -#define gettid() syscall(SYS_gettid) -#endif /* __linux__ */ -#include <hicn/ctrl/hicn-light-ng.h> -#include <strings.h> - -#include "hicn_light_common.h" -#include <hicn/util/sstrncpy.h> - -#pragma GCC diagnostic ignored "-Warray-bounds" - -#if 0 -#ifdef __APPLE__ -#define RANDBYTE() (u8)(arc4random() & 0xFF) -#else -#define RANDBYTE() (u8)(random() & 0xFF) -#endif -#endif -#define RANDBYTE() (u8)(rand() & 0xFF) - -/** - * \brief Defines the default size for the allocated data arrays holding the - * results of API calls. - * - * This size should not be too small to avoid wasting memoyy, but also not too - * big to avoid unnecessary realloc's. Later on this size is doubled at each - * reallocation. - */ -#define DEFAULT_SIZE_LOG 3 - -#define connection_state_to_face_state(x) ((face_state_t)(x)) -#define face_state_to_connection_state(x) ((hc_connection_state_t)(x)) - -/****************************************************************************** - * Message helper types and aliases - ******************************************************************************/ - -#define foreach_hc_command \ - _(connection_add) \ - _(connection_remove) \ - _(connection_list) \ - _(listener_add) \ - _(listener_remove) \ - _(listener_list) \ - _(route_add) \ - _(route_remove) \ - _(route_list) \ - _(cache_set_store) \ - _(cache_set_serve) \ - _(cache_clear) \ - _(cache_list) \ - _(strategy_set) \ - _(strategy_add_local_prefix) \ - _(wldr_set) \ - _(punting_add) \ - _(mapme_activator) \ - _(mapme_timing) \ - _(subscription_add) \ - _(subscription_remove) \ - _(stats_get) \ - _(stats_list) - -const char *command_type_str[] = { -#define _(l, u) [COMMAND_TYPE_##u] = STRINGIZE(u), - foreach_command_type -#undef _ -}; - -typedef cmd_header_t hc_msg_header_t; - -typedef union { -#define _(x) cmd_##x##_t x; - foreach_hc_command -#undef _ -} hc_msg_payload_t; - -typedef struct hc_msg_s { - hc_msg_header_t hdr; - hc_msg_payload_t payload; -} hc_msg_t; - -/****************************************************************************** - * Control socket - ******************************************************************************/ - -#define AVAILABLE(s) ((s)->woff - (s)->roff) -#define DEFAULT_SOCK_RECV_TIMEOUT_MS 100 - -/** - * \brief Parse a connection URL into a sockaddr - * \param [in] url - URL - * \param [out] sa - Resulting struct sockaddr, expected zero'ed. - * \return 0 if parsing succeeded, a negative error value otherwise. - */ -static int _hcng_sock_light_parse_url(const char *url, struct sockaddr *sa) { - /* FIXME URL parsing is currently not implemented */ - assert(!url); - -#ifdef __linux__ - srand(time(NULL) ^ getpid() ^ gettid()); -#else - srand((unsigned int)(time(NULL) ^ getpid())); -#endif /* __linux__ */ - - /* - * A temporary solution is to inspect the sa_family fields of the passed in - * sockaddr, which defaults to AF_UNSPEC (0) and thus creates an IPv4/TCP - * connection to localhost. - */ - switch (sa->sa_family) { - case AF_UNSPEC: - case AF_INET: { - struct sockaddr_in *sai = (struct sockaddr_in *)sa; - sai->sin_family = AF_INET; - sai->sin_port = htons(PORT); - sai->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - break; - } - case AF_INET6: { - struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)sa; - sai6->sin6_family = AF_INET6; - sai6->sin6_port = htons(PORT); - sai6->sin6_addr = loopback_addr; - break; - } - default: - return -1; - } - - return 0; -} - -static int _hcng_sock_light_reset(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - s->roff = s->woff = 0; - s->remaining = 0; - return 0; -} - -void _hcng_sock_light_free(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - unsigned k_seq; - hc_sock_request_t *v_request; - kh_foreach(s->map, k_seq, v_request, - { hc_sock_light_request_free(v_request); }); - - kh_destroy_sock_map(s->map); - if (s->url) free(s->url); - close(s->fd); - free(s); -} - -static void _hcng_sock_increment_woff(hc_sock_t *socket, size_t bytes) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - s->woff += bytes; -} - -static int _hcng_sock_light_get_next_seq(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return s->seq++; -} - -static int _hcng_sock_light_set_nonblocking(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return (fcntl(s->fd, F_SETFL, fcntl(s->fd, F_GETFL) | O_NONBLOCK) < 0); -} - -static int _hcng_sock_light_get_fd(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return s->fd; -} - -static int _hcng_sock_light_connect(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - struct sockaddr_storage ss; - memset(&ss, 0, sizeof(struct sockaddr_storage)); - - if (_hcng_sock_light_parse_url(s->url, (struct sockaddr *)&ss) < 0) - goto ERR_PARSE; - - size_t size = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) - : sizeof(struct sockaddr_in6); - if (connect(s->fd, (struct sockaddr *)&ss, (socklen_t)size) < 0) { - perror("connect error"); - goto ERR_CONNECT; - } - return 0; - -ERR_CONNECT: -ERR_PARSE: - return -1; -} - -static int _hcng_sock_light_send(hc_sock_t *socket, hc_msg_t *msg, - size_t msglen, uint32_t seq) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc; - msg->hdr.seq_num = seq; - rc = (int)send(s->fd, msg, msglen, 0); - if (rc < 0) { - perror("hc_sock_light_send"); - return -1; - } - return 0; -} - -static int _hcng_sock_light_get_available(hc_sock_t *socket, u8 **buffer, - size_t *size) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - *buffer = s->buf + s->woff; - *size = RECV_BUFLEN - s->woff; - - return 0; -} - -static int _hcng_sock_light_recv(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc; - - /* - * This condition should be ensured to guarantee correct processing of - * messages. With TCP, we need at least a header as we will receive part of - * the stream. With UDP, we need the be able to receive the full datagram, - * otherwise the rest will be lost. - * - * Let's be sure to always be able to receive at least 1 JUMBO_MTU, which - * should be fine for al situations. - */ - assert(RECV_BUFLEN - s->woff > JUMBO_MTU); - - rc = (int)recv(s->fd, s->buf + s->woff, RECV_BUFLEN - s->woff, 0); - if (rc == 0) { - /* Connection has been closed */ - return 0; - } - if (rc < 0) { - /* - * Let's not return 0 which currently means the socket has been closed - */ - if (errno == EWOULDBLOCK) return -1; - if (errno == EINTR) { - WARN("recv has been stopped by signal"); - return -1; - } - perror("hc_sock_light_recv"); - return -1; - } - s->woff += rc; - return rc; -} - -static void _hcng_sock_light_mark_complete(hc_sock_light_t *s, - hc_data_t **pdata) { - hc_data_t *data = s->cur_request->data; - - khiter_t k = kh_get_sock_map(s->map, s->cur_request->seq); - if (k == kh_end(s->map)) { - ERROR("[hc_sock_light_mark_complete] Error removing request from map"); - } else { - kh_del_sock_map(s->map, k); - } - - hc_data_set_complete(data); - if (pdata) *pdata = data; - - /* Free current request */ - hc_sock_light_request_free(s->cur_request); - s->cur_request = NULL; -} - -static int _hcng_sock_light_process_notification(hc_sock_light_t *s, - hc_data_t **pdata) { - /* For now, notifications are not associated to requests */ - assert(!s->cur_request); - - /* - * Assumption: the whole notification data is returned in a single read and we - * immediately parse it. - * - * XXX This is only valid for UDP sockets. - */ - size_t notification_size = AVAILABLE(s); - - *pdata = hc_data_create(0, /* in_element_size, 0 = no parsing */ - notification_size, /* out_element_size */ - NULL); /* complete_cb */ - - /* Copy the packet payload as the single entry in hc_data_t */ - hc_data_push_many(*pdata, s->buf + s->roff, 1); - - return (int)notification_size; -} - -/* - * Notifications have no sequence number and are not linked to any request - */ -static hc_sock_request_t *_hcng_sock_light_get_request(hc_sock_light_t *s, - int seq) { - hc_sock_request_t *request; - /* Retrieve request from sock map */ - khiter_t k = kh_get_sock_map(s->map, seq); - if (k == kh_end(s->map)) { - ERROR( - "[_hcng_sock_light_get_request] Error searching for matching request"); - return NULL; - } - request = kh_val(s->map, k); - - if (!request) { - ERROR("[_hcng_sock_light_get_request] No request matching sequence number"); - return NULL; - } - return request; -} - -/* - * Return codes: - * 0 success, or not enough data yet to do something - * > 0 : notification type - * -99 invalid buffer data -> flush - */ -static int _hcng_sock_light_process_header(hc_sock_light_t *s, - hc_data_t **pdata) { - int rc; - - /* Check we have at least a header's worth of data, and consume it */ - if (AVAILABLE(s) < sizeof(hc_msg_header_t)) return 0; - - hc_msg_t *msg = (hc_msg_t *)(s->buf + s->roff); - - // INFO("Processing header header %s", command_type_str(msg->hdr.command_id)); - s->roff += sizeof(hc_msg_header_t); - - if (msg->hdr.message_type != NOTIFICATION_LIGHT) { - s->cur_request = _hcng_sock_light_get_request(s, msg->hdr.seq_num); - if (!s->cur_request) return -99; - } - - /* How many elements are we expecting in the reply ? */ - s->remaining = msg->hdr.length; - hc_data_t *request_data; - - switch (msg->hdr.message_type) { - case ACK_LIGHT: - assert(s->remaining == 1); // sic - assert(!pdata); - _hcng_sock_light_mark_complete(s, pdata); - break; - - case NACK_LIGHT: - assert(!pdata); - assert(s->remaining == 1); // sic - request_data = s->cur_request->data; - _hcng_sock_light_mark_complete(s, pdata); - hc_data_set_error(request_data); - break; - - case RESPONSE_LIGHT: - assert(pdata); - - if (s->remaining == 0) { - /* Empty response (i.e. containing 0 elements) */ - _hcng_sock_light_mark_complete(s, pdata); - return 0; - } - - /* Make room in hc_data_t... to avoid multiple calls */ - rc = hc_data_ensure_available(s->cur_request->data, s->remaining); - if (rc < 0) { - ERROR("[hc_sock_light_process] Error in hc_data_ensure_available"); - return -99; - } - break; - - case NOTIFICATION_LIGHT: { - assert(pdata); - assert(s->remaining == 0); - - /* This returns the notification size */ - size_t notification_size = - _hcng_sock_light_process_notification(s, pdata); - s->roff += notification_size; - return msg->hdr.command_id; - } - - default: - ERROR("[hc_sock_light_process] Invalid response received"); - return -99; - } - - return 0; -} - -static int _hcng_sock_light_process_payload(hc_sock_light_t *s, - hc_data_t **pdata) { - int err = 0; - int rc; - - hc_data_t *data = s->cur_request->data; - - /* We only process full elements (size is stored in data) */ - size_t num_chunks = AVAILABLE(s) / data->in_element_size; - - /* Check whether we have enough data to process */ - if (num_chunks == 0) return 0; - - /* Safeguard: assert(num_chunks < s->remaining); */ - if (num_chunks > s->remaining) { - WARN( - "[_hcng_sock_light_process_payload] Unexpected num_chunks > " - "s->remaining"); - num_chunks = s->remaining; - } - - if (!s->cur_request->parse) { - /* If we don't need to parse results, then we can directly push - * all of them into the result data structure */ - hc_data_push_many(data, s->buf + s->roff, num_chunks); - } else { - /* Iterate on chunks of data */ - for (int i = 0; i < num_chunks; i++) { - /* Get storage offset in hc_data_t */ - u8 *dst = hc_data_get_next(data); - if (!dst) { - ERROR("[hc_sock_light_process] Error in hc_data_get_next"); - err = -2; - break; - } - - /* Parse element #i */ - rc = s->cur_request->parse(s->buf + s->roff + i * data->in_element_size, - dst); - if (rc < 0) { - ERROR("[hc_sock_light_process] Error in parse"); - err = -1; - /* In this case we let the loop complete to collect other results */ - } - data->size++; - } - } - - s->roff += num_chunks * data->in_element_size; - - /* - * If we are not expecting any more data, mark the reply as complete - */ - s->remaining -= num_chunks; - if (s->remaining == 0) _hcng_sock_light_mark_complete(s, pdata); - - return err; -} - -/* - * Process messages as they are received in the ring buffer. There can be - * interleaved queries and replies (they are identified by sequence number), - * and the assumption is that a reply can arrive over mutiple packets (in - * other terms, it is possible that not all data from the reply is available - * in the buffer at a given time). However, we assume that a full query is - * received at once. - */ -static int _hcng_sock_light_process(hc_sock_t *socket, hc_data_t **data) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc = 0; - - /* - * We loop consuming messages until there is no more data in the buffer, - * or that we can find an entire message. Messages are received - * sequentially, and we keep track of incomplete requests in s->cur_request. - */ - while (AVAILABLE(s) > 0) { - if (!s->cur_request) { - rc = _hcng_sock_light_process_header(s, data); - } else { - rc = _hcng_sock_light_process_payload(s, data); - } - if (rc < 0) break; - } - - if ((rc == -99) || (s->roff == s->woff)) { - /* Flush buffer */ - s->woff = 0; - } else { - /* Clean up read data from buffer */ - memmove(s->buf, s->buf + s->roff, AVAILABLE(s)); - s->woff -= s->roff; - } - s->roff = 0; - - return rc; -} - -static int _hcng_sock_light_callback(hc_sock_t *socket, hc_data_t **pdata) { - hc_data_t *data = NULL; - int rc = 0; - - for (;;) { - int n = _hcng_sock_light_recv(socket); - if (n == 0) goto ERR_EOF; - if (n < 0) { - switch (errno) { - case ECONNRESET: - case ENODEV: - /* Forwarder restarted */ - WARN("Forwarder likely restarted: not (yet) implemented"); - goto ERR; - case EWOULDBLOCK: - // DEBUG("Would block... stop reading from socket"); - goto END; - case EINTR: - WARN("callback has been stopped by signal"); - goto ERR; - default: - perror("hc_sock_light_callback"); - goto ERR; - } - } - rc = _hcng_sock_light_process(socket, &data); - if (rc < 0) goto ERR; - if (rc > 0) // i.e. rc = notification type - goto END; - } -END: - if (pdata) - *pdata = data; - else - hc_data_free(data); - return rc; - -ERR: - hc_data_free(data); -ERR_EOF: - return -1; -} - -/****************************************************************************** - * Command-specific structures and functions - ******************************************************************************/ - -typedef int (*HC_PARSE)(const u8 *, u8 *); - -typedef struct { - hc_action_t cmd; - command_type_t cmd_id; - size_t size_in; - size_t size_out; - HC_PARSE parse; -} hc_command_params_t; - -typedef struct hc_result_s { - hc_msg_t msg; - hc_command_params_t params; - bool async; - bool success; -} hc_result_t; - -int _hcng_sock_prepare_send(hc_sock_t *socket, hc_result_t *result, - data_callback_t complete_cb, - void *complete_cb_data) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - // Prepare data - hc_data_t *data = - hc_data_create(result->params.size_in, result->params.size_out, NULL); - if (!data) { - ERROR("[_hcng_sock_prepare_send] Could not create data storage"); - goto ERR_DATA; - } - hc_data_set_callback(data, complete_cb, complete_cb_data); - - // Update the sequence number - int seq = _hcng_sock_light_get_next_seq(socket); - result->msg.hdr.seq_num = seq; // Like in _hcng_sock_light_send - - // Create state used to process the request - hc_sock_request_t *request = NULL; - request = hc_sock_request_create(seq, data, result->params.parse); - if (!request) { - ERROR("[_hcng_sock_prepare_send] Could not create request state"); - goto ERR_REQUEST; - } - - int rc; - khiter_t k = kh_put_sock_map(s->map, seq, &rc); - if (rc != KH_ADDED && rc != KH_RESET) { - ERROR("[_hcng_sock_prepare_send] Error adding request state to map"); - goto ERR_MAP; - } - kh_value(s->map, k) = request; - - return sizeof(result->msg); - -ERR_MAP: - hc_sock_light_request_free(request); -ERR_REQUEST: - hc_data_free(data); -ERR_DATA: - return -99; -} - -int _hcng_sock_set_recv_timeout_ms(hc_sock_t *socket, long timeout_ms) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = (int)(timeout_ms * 1000); // Convert ms into us - if (setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - perror("setsockopt"); - return -1; - } - - return 0; -} - -static int _hcng_execute_command(hc_sock_t *socket, hc_msg_t *msg, - size_t msg_len, hc_command_params_t *params, - hc_data_t **pdata, bool async) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int ret; - if (async) assert(!pdata); - - /* Sanity check */ - switch (params->cmd) { - case ACTION_CREATE: - assert(params->size_in != 0); /* payload repeated */ - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_DELETE: - assert(params->size_in != 0); /* payload repeated */ - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_GET: - case ACTION_LIST: - assert(params->size_in != 0); - assert(params->size_out != 0); - // TODO(eloparco): Parsing should not be necessary after - // (pending) refatoring - // assert(params->parse != NULL); - break; - case ACTION_SET: - case ACTION_SERVE: - case ACTION_STORE: - case ACTION_UPDATE: - assert(params->size_in != 0); - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_CLEAR: - assert(params->size_in == 0); - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - default: - return -1; - } - - // hc_sock_light_reset(s); - - /* XXX data will at least store the result (complete) */ - hc_data_t *data = hc_data_create(params->size_in, params->size_out, NULL); - if (!data) { - ERROR("[_hcng_execute_command] Could not create data storage"); - goto ERR_DATA; - } - - int seq = _hcng_sock_light_get_next_seq(socket); - - /* Create state used to process the request */ - hc_sock_request_t *request = NULL; - request = hc_sock_request_create(seq, data, params->parse); - if (!request) { - ERROR("[_hcng_execute_command] Could not create request state"); - goto ERR_REQUEST; - } - - /* Add state to map */ - int rc; - khiter_t k = kh_put_sock_map(s->map, seq, &rc); - if (rc != KH_ADDED && rc != KH_RESET) { - ERROR("[_hcng_execute_command] Error adding request state to map"); - goto ERR_MAP; - } - kh_value(s->map, k) = request; - - if (_hcng_sock_light_send(socket, msg, msg_len, seq) < 0) { - ERROR("[_hcng_execute_command] Error sending message"); - goto ERR_PROCESS; - } - - if (async) return 0; - - /* - * Dangerous zone, we might be doing blocking operations on a non-blocking - * UDP socket - */ - int retries = 0; - while (!data->complete) { - /* - * As the socket is non blocking it might happen that we need to read - * several times before success... - */ - int n = _hcng_sock_light_recv(socket); - if (n == 0) goto ERR_EOF; - if (n < 0) { - if ((errno == EWOULDBLOCK) && (retries < 10)) { /* Max 500ms */ - DEBUG("read = EWOULDBLOCK... sleeping for 50ms (max 500ms)"); - usleep(50000); /* 50ms */ - retries++; - continue; - } - break; - } - int rc = _hcng_sock_light_process(socket, pdata); - switch (rc) { - case 0: - case -1: - break; - case -99: - ERROR("[_hcng_execute_command] Error processing socket results"); - goto ERR; - default: - ERROR("[_hcng_execute_command] Unexpected return value"); - goto ERR; - } - } - -ERR_EOF: - ret = data->ret; - if (!data->complete) return -1; - if (!pdata) hc_data_free(data); - - return ret; - -ERR_PROCESS: -ERR_MAP: - hc_sock_light_request_free(request); -ERR: -ERR_REQUEST: - hc_data_free(data); -ERR_DATA: - return -99; -} - -/*----------------------------------------------------------------------------* - * Listeners - *----------------------------------------------------------------------------*/ - -/* LISTENER CREATE */ - -static hc_result_t *_listener_create_serialize(hc_sock_t *s, - hc_listener_t *listener, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[_hcng_listener_create] Unexpected truncation of listener string"); - DEBUG("[_hcng_listener_create] listener=%s async=%s", listener_s, - BOOLSTR(async)); - - if (hc_listener_validate(listener) < 0) { - res->success = false; - return res; - } - - msg_listener_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = listener->local_addr, - .port = htons(listener->local_port), - .family = listener->family, - .type = listener->type, - }}; - - rc = snprintf(msg.payload.symbolic, SYMBOLIC_NAME_LEN, "%s", listener->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_create] Unexpected truncation of symbolic name " - "string"); - - rc = snprintf(msg.payload.interface_name, INTERFACE_LEN, "%s", - listener->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[_hc_listener_create] Unexpected truncation of interface name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_LISTENER_ADD, - .size_in = sizeof(cmd_listener_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.listener_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_listener_create_conf(hc_sock_t *s, - hc_listener_t *listener) { - return _listener_create_serialize(s, listener, false); -} - -static int _hcng_listener_create_internal(hc_sock_t *socket, - hc_listener_t *listener, bool async) { - hc_result_t *result = _listener_create_serialize(socket, listener, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - free(result); - DEBUG("[_hcng_listener_create] done or error"); - return ret; -} - -static int _hcng_listener_create(hc_sock_t *s, hc_listener_t *listener) { - DEBUG("[_hcng_listener_create]"); - return _hcng_listener_create_internal(s, listener, false); -} - -static int _hcng_listener_create_async(hc_sock_t *s, hc_listener_t *listener) { - DEBUG("[_hcng_listener_create_async]"); - return _hcng_listener_create_internal(s, listener, true); -} - -/* LISTENER PARSE */ - -static int hc_listener_parse(void *in, hc_listener_t *listener) { - int rc; - cmd_listener_list_item_t *item = (cmd_listener_list_item_t *)in; - - if (!IS_VALID_ID(item->id)) { - ERROR("[hc_listener_parse] Invalid id received"); - return -1; - } - - *listener = (hc_listener_t){ - .id = item->id, - .type = item->type, - .family = item->family, - .local_addr = UNION_CAST(item->address, ip_address_t), - .local_port = ntohs(item->port), - }; - rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "%s", item->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[hc_listener_parse] Unexpected truncation of symbolic name string"); - rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", - item->interface_name); - if (rc >= INTERFACE_LEN) - WARN("[hc_listener_parse] Unexpected truncation of interface name string"); - - if (hc_listener_validate(listener) < 0) return -1; - return 0; -} - -/* LISTENER LIST */ - -static hc_result_t *_hcng_listener_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_listener_list] async=%s", BOOLSTR(async)); - - msg_listener_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_LISTENER_LIST, - .size_in = sizeof(cmd_listener_list_item_t), - .size_out = sizeof(hc_listener_t), - .parse = (HC_PARSE)hc_listener_parse, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.listener_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_listener_list_conf(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_listener_list_serialize(s, pdata, false); -} - -static int _hcng_listener_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_listener_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_listener_list] done or error"); - return ret; -} - -static int _hcng_listener_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_listener_list]"); - return _hcng_listener_list_internal(s, pdata, false); -} - -static int _hcng_listener_list_async(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_listener_list_as-nc]"); - return _hcng_listener_list_internal(s, pdata, true); -} - -/* LISTENER GET */ - -static int _hcng_listener_get(hc_sock_t *socket, hc_listener_t *listener, - hc_listener_t **listener_found) { - hc_data_t *listeners; - hc_listener_t *found; - - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[hc_listener_get] Unexpected truncation of listener string"); - DEBUG("[hc_listener_get] listener=%s", listener_s); - - if (_hcng_listener_list(socket, &listeners) < 0) return -1; - - /* Test */ - if (hc_listener_find(listeners, listener, &found) < 0) { - hc_data_free(listeners); - return -1; - } - - if (found) { - *listener_found = malloc(sizeof(hc_listener_t)); - if (!*listener_found) return -1; - **listener_found = *found; - } else { - *listener_found = NULL; - } - - hc_data_free(listeners); - - return 0; -} - -/* LISTENER DELETE */ - -static int _hcng_listener_delete_internal(hc_sock_t *socket, - hc_listener_t *listener, bool async) { - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[_hcng_listener_delete] Unexpected truncation of listener string"); - DEBUG("[_hcng_listener_delete] listener=%s async=%s", listener_s, - BOOLSTR(async)); - - msg_listener_remove_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_REMOVE, - .length = 1, - .seq_num = 0, - }}; - - if (listener->id) { - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", - listener->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - } else if (*listener->name) { - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%s", - listener->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - } else { - hc_listener_t *listener_found; - if (_hcng_listener_get(socket, listener, &listener_found) < 0) return -1; - if (!listener_found) return -1; - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", - listener_found->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - free(listener_found); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_LISTENER_REMOVE, - .size_in = sizeof(cmd_listener_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - return _hcng_listener_delete_internal(s, listener, false); -} - -static int _hcng_listener_delete_async(hc_sock_t *s, hc_listener_t *listener) { - return _hcng_listener_delete_internal(s, listener, true); -} - -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -/* CONNECTION CREATE */ - -static hc_result_t *_connection_create_serialize(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN( - "[_hcng_connection_create] Unexpected truncation of connection " - "string"); - DEBUG("[_hcng_connection_create] connection=%s async=%s", connection_s, - BOOLSTR(async)); - - if (hc_connection_validate(connection) < 0) { - res->success = false; - return res; - } - - msg_connection_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .remote_ip = connection->remote_addr, - .local_ip = connection->local_addr, - .remote_port = htons(connection->remote_port), - .local_port = htons(connection->local_port), - .family = connection->family, - .type = connection->type, - .admin_state = connection->admin_state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }}; - rc = - snprintf(msg.payload.symbolic, SYMBOLIC_NAME_LEN, "%s", connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_create] Unexpected truncation of symbolic name " - "string"); - // snprintf(msg.payload.interface_name, INTERFACE_NAME_LEN, "%s", - // connection->interface_name); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_CONNECTION_ADD, - .size_in = sizeof(cmd_connection_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.connection_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - return _connection_create_serialize(s, connection, false); -} - -static int _hcng_connection_create_internal(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *result = _connection_create_serialize(socket, connection, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_connection_create] done or error"); - return ret; -} - -static int _hcng_connection_create(hc_sock_t *s, hc_connection_t *connection) { - DEBUG("[_hcng_connection_create]"); - return _hcng_connection_create_internal(s, connection, false); -} - -static int _hcng_connection_create_async(hc_sock_t *s, - hc_connection_t *connection) { - DEBUG("[_hcng_connection_create_async]"); - return _hcng_connection_create_internal(s, connection, true); -} - -/* CONNECTION PARSE */ - -static int hc_connection_parse(void *in, hc_connection_t *connection) { - int rc; - cmd_connection_list_item_t *item = (cmd_connection_list_item_t *)in; - - if (!IS_VALID_ID(item->id)) { - ERROR("[hc_connection_parse] Invalid id received"); - return -1; - } - - *connection = (hc_connection_t){ - .id = item->id, - .type = item->type, - .family = item->family, - .local_addr = item->local_addr, - .local_port = ntohs(item->local_port), - .remote_addr = item->remote_addr, - .remote_port = ntohs(item->remote_port), - .admin_state = item->admin_state, -#ifdef WITH_POLICY - .priority = item->priority, - .tags = item->tags, -#endif /* WITH_POLICY */ - .state = item->state, - }; - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", item->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_parse] Unexpected truncation of symbolic name " - "string"); - rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", - item->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_parse] Unexpected truncation of interface name " - "string"); - - if (hc_connection_validate(connection) < 0) return -1; - return 0; -} - -/* CONNECTION LIST */ - -static int _hcng_connection_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - DEBUG("[hc_connection_list] async=%s", BOOLSTR(async)); - - msg_connection_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CONNECTION_LIST, - .size_in = sizeof(cmd_connection_list_item_t), - .size_out = sizeof(hc_connection_t), - .parse = (HC_PARSE)hc_connection_parse, - }; - - int ret = _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), - ¶ms, pdata, async); - - DEBUG("[hc_connection_list] done or error"); - return ret; -} - -static int _hcng_connection_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[hc_connection_list]"); - return _hcng_connection_list_internal(s, pdata, false); -} - -static int _hcng_connection_list_async(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[hc_connection_list_async]"); - return _hcng_connection_list_internal(s, pdata, true); -} - -/* CONNECTION GET */ - -static int _hcng_connection_get(hc_sock_t *socket, hc_connection_t *connection, - hc_connection_t **connection_found) { - hc_data_t *connections; - hc_connection_t *found; - - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN("[hc_connection_get] Unexpected truncation of connection string"); - DEBUG("[hc_connection_get] connection=%s", connection_s); - - if (_hcng_connection_list(socket, &connections) < 0) return -1; - - /* Test */ - if (hc_connection_find(connections, connection, &found) < 0) { - hc_data_free(connections); - return -1; - } - - if (found) { - *connection_found = malloc(sizeof(hc_connection_t)); - if (!*connection_found) return -1; - **connection_found = *found; - } else { - *connection_found = NULL; - } - - hc_data_free(connections); - - return 0; -} - -/* CONNECTION DELETE */ - -static hc_result_t *_hcng_connection_delete_serialize( - hc_sock_t *socket, hc_connection_t *connection, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - res->success = false; - - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN( - "[_hcng_connection_delete] Unexpected truncation of connection " - "string"); - DEBUG("[_hcng_connection_delete] connection=%s async=%s", connection_s, - BOOLSTR(async)); - - msg_connection_remove_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_REMOVE, - .length = 1, - .seq_num = 0, - }, - }; - - if (connection->id) { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - connection->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - } else if (*connection->name) { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - } else { - hc_connection_t *connection_found; - if (hc_connection_get(socket, connection, &connection_found) < 0) - return res; - if (!connection_found) return res; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - connection_found->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - free(connection_found); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_CONNECTION_REMOVE, - .size_in = sizeof(cmd_connection_remove_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.connection_remove = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_connection_delete_conf(hc_sock_t *s, - hc_connection_t *connection) { - return _hcng_connection_delete_serialize(s, connection, false); -} - -static int _hcng_connection_delete_internal(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *result = - _hcng_connection_delete_serialize(socket, connection, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - return _hcng_connection_delete_internal(s, connection, false); -} - -static int _hcng_connection_delete_async(hc_sock_t *s, - hc_connection_t *connection) { - return _hcng_connection_delete_internal(s, connection, true); -} - -/* CONNECTION UPDATE */ - -static int _hcng_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - // Not implemented - return -1; -} - -static int _hcng_connection_update(hc_sock_t *s, - hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - // Not implemented - return -1; -} - -/* CONNECTION SET ADMIN STATE */ - -static int _hcng_connection_set_admin_state_internal( - hc_sock_t *socket, const char *conn_id_or_name, face_state_t state, - bool async) { - int rc; - DEBUG( - "[hc_connection_set_admin_state] connection_id/name=%s admin_state=%s " - "async=%s", - conn_id_or_name, face_state_str(state), BOOLSTR(async)); - - struct { - cmd_header_t hdr; - cmd_connection_set_admin_state_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .admin_state = state, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_admin_state] Unexpected truncation of symbolic " - "name string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, - .size_in = sizeof(cmd_connection_set_admin_state_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_connection_set_admin_state(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - return _hcng_connection_set_admin_state_internal(s, conn_id_or_name, state, - false); -} - -static int _hcng_connection_set_admin_state_async(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - return _hcng_connection_set_admin_state_internal(s, conn_id_or_name, state, - true); -} - -#ifdef WITH_POLICY - -static int _hcng_connection_set_priority_internal(hc_sock_t *socket, - const char *conn_id_or_name, - uint32_t priority, - bool async) { - int rc; - DEBUG( - "[hc_connection_set_priority] connection_id/name=%s priority=%d " - "async=%s", - conn_id_or_name, priority, BOOLSTR(async)); - struct { - cmd_header_t hdr; - cmd_connection_set_priority_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .priority = priority, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_priority] Unexpected truncation of symbolic " - "name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, - .size_in = sizeof(cmd_connection_set_priority_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_connection_set_priority(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - return _hcng_connection_set_priority_internal(s, conn_id_or_name, priority, - false); -} - -static int _hcng_connection_set_priority_async(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - return _hcng_connection_set_priority_internal(s, conn_id_or_name, priority, - true); -} - -#endif // WITH_POLICY - -static int _hcng_connection_set_tags_internal(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags, bool async) { - int rc; - DEBUG("[hc_connection_set_tags] connection_id/name=%s tags=%d async=%s", - conn_id_or_name, tags, BOOLSTR(async)); - struct { - cmd_header_t hdr; - cmd_connection_set_tags_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_TAGS, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .tags = tags, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_tags] Unexpected truncation of symbolic name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_TAGS, - .size_in = sizeof(cmd_connection_set_tags_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(s, (hc_msg_t *)&msg, sizeof(msg), ¶ms, NULL, - async); -} - -static int _hcng_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return _hcng_connection_set_tags_internal(s, conn_id_or_name, tags, false); -} - -static int _hcng_connection_set_tags_async(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags) { - return _hcng_connection_set_tags_internal(s, conn_id_or_name, tags, true); -} - -/*----------------------------------------------------------------------------* - * Routes - *----------------------------------------------------------------------------*/ - -/* ROUTE CREATE */ - -static hc_result_t *_route_create_serialize(hc_sock_t *socket, - hc_route_t *route, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char route_s[MAXSZ_HC_ROUTE]; - int rc = hc_route_snprintf(route_s, MAXSZ_HC_ROUTE, route); - if (rc >= MAXSZ_HC_ROUTE) - WARN("[_hc_route_create] Unexpected truncation of route string"); - if (rc < 0) - WARN("[_hc_route_create] Error building route string"); - else - DEBUG("[hc_route_create] route=%s async=%s", route_s, BOOLSTR(async)); - - if (hc_route_validate(route) < 0) { - res->success = false; - return res; - } - - msg_route_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = route->remote_addr, - .cost = route->cost, - .family = route->family, - .len = route->len, - }}; - - /* - * The route commands expects the ID or name as part of the - * symbolic_or_connid attribute. - */ - if (route->name[0] != '\0') { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - route->name); - } else { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - route->face_id); - } - - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[_hc_route_create] Unexpected truncation of symbolic name string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_ROUTE_ADD, - .size_in = sizeof(cmd_route_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.route_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_route_create_conf(hc_sock_t *s, hc_route_t *route) { - return _route_create_serialize(s, route, false); -} - -static int _hcng_route_create_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - hc_result_t *result = _route_create_serialize(socket, route, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_route_create(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_create_internal(s, route, false); -} - -static int _hcng_route_create_async(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_create_internal(s, route, true); -} - -/* ROUTE DELETE */ - -static int _hcng_route_delete_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - char route_s[MAXSZ_HC_ROUTE]; - int rc = hc_route_snprintf(route_s, MAXSZ_HC_ROUTE, route); - if (rc >= MAXSZ_HC_ROUTE) - WARN("[_hc_route_delete] Unexpected truncation of route string"); - DEBUG("[hc_route_delete] route=%s async=%s", route_s, BOOLSTR(async)); - - if (!IS_VALID_FAMILY(route->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_route_remove_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = route->remote_addr, - .family = route->family, - .len = route->len, - }}; - - /* - * The route commands expects the ID or name as part of the - * symbolic_or_connid attribute. - */ - if (route->name[0] != '\0') { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - route->name); - } else { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - route->face_id); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_ROUTE_REMOVE, - .size_in = sizeof(cmd_route_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_route_delete(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_delete_internal(s, route, false); -} - -static int _hcng_route_delete_async(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_delete_internal(s, route, true); -} - -/* ROUTE PARSE */ - -static int hc_route_parse(void *in, hc_route_t *route) { - cmd_route_list_item_t *item = (cmd_route_list_item_t *)in; - - *route = (hc_route_t){ - .name = "", /* This is not reported back */ - .face_id = item->connection_id, - .family = item->family, - .remote_addr = item->address, - .len = item->len, - .cost = item->cost, - }; - - if (hc_route_validate(route) < 0) return -1; - return 0; -} - -/* ROUTE LIST */ - -static int _hcng_route_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - // DEBUG("[hc_route_list] async=%s", BOOLSTR(async)); - msg_route_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_ROUTE_LIST, - .size_in = sizeof(cmd_route_list_item_t), - .size_out = sizeof(hc_route_t), - .parse = (HC_PARSE)hc_route_parse, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - pdata, async); -} - -static int _hcng_route_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_route_list_internal(s, pdata, false); -} - -static int _hcng_route_list_async(hc_sock_t *s) { - return _hcng_route_list_internal(s, NULL, true); -} - -/*----------------------------------------------------------------------------* - * Face - * - * Face support is not directly available in hicn-light, but we can offer such - * an interface through a combination of listeners and connections. The code - * starts with some conversion functions between faces/listeners/connections. - * - * We also need to make sure that there always exist a (single) listener when - *a connection is created, and in the hICN face case, that there is a single - * connection attached to this listener. - * - *----------------------------------------------------------------------------*/ - -/* FACE CREATE */ - -static int _hcng_face_create(hc_sock_t *socket, hc_face_t *face) { - hc_listener_t listener; - hc_listener_t *listener_found; - - hc_connection_t connection; - hc_connection_t *connection_found; - - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_create] Unexpected truncation of face string"); - DEBUG("[hc_face_create] face=%s", face_s); - - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - if (hc_face_to_connection(face, &connection, false) < 0) { - ERROR("[hc_face_create] Could not convert face to connection."); - return -1; - } - - /* Ensure we have a corresponding local listener */ - if (hc_connection_to_local_listener(&connection, &listener) < 0) { - ERROR("[hc_face_create] Could not convert face to local listener."); - return -1; - } - - if (_hcng_listener_get(socket, &listener, &listener_found) < 0) { - ERROR("[hc_face_create] Could not retrieve listener"); - return -1; - } - - if (!listener_found) { - /* We need to create the listener if it does not exist */ - if (hc_listener_create(socket, &listener) < 0) { - ERROR("[hc_face_create] Could not create listener."); - free(listener_found); - return -1; - } - } else { - free(listener_found); - } - - /* Create corresponding connection */ - if (_hcng_connection_create(socket, &connection) < 0) { - ERROR("[hc_face_create] Could not create connection."); - return -1; - } - - /* - * Once the connection is created, we need to list all connections - * and compare with the current one to find the created face ID. - */ - if (_hcng_connection_get(socket, &connection, &connection_found) < 0) { - ERROR("[hc_face_create] Could not retrieve connection"); - return -1; - } - - if (!connection_found) { - ERROR("[hc_face_create] Could not find newly created connection."); - return -1; - } - - face->id = connection_found->id; - free(connection_found); - - break; - - case FACE_TYPE_HICN_LISTENER: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - if (hc_face_to_listener(face, &listener) < 0) { - ERROR("Could not convert face to listener."); - return -1; - } - if (hc_listener_create(socket, &listener) < 0) { - ERROR("[hc_face_create] Could not create listener."); - return -1; - } - break; - default: - ERROR("[hc_face_create] Unknwon face type."); - - return -1; - }; - - return 0; -} - -static int _hcng_face_get(hc_sock_t *socket, hc_face_t *face, - hc_face_t **face_found) { - hc_listener_t listener; - hc_listener_t *listener_found; - - hc_connection_t connection; - hc_connection_t *connection_found; - - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_get] Unexpected truncation of face string"); - DEBUG("[hc_face_get] face=%s", face_s); - - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - if (hc_face_to_connection(face, &connection, false) < 0) return -1; - if (_hcng_connection_get(socket, &connection, &connection_found) < 0) - return -1; - if (!connection_found) { - *face_found = NULL; - return 0; - } - *face_found = malloc(sizeof(hc_face_t)); - hc_connection_to_face(connection_found, *face_found); - free(connection_found); - break; - - case FACE_TYPE_HICN_LISTENER: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - if (hc_face_to_listener(face, &listener) < 0) return -1; - if (_hcng_listener_get(socket, &listener, &listener_found) < 0) return -1; - if (!listener_found) { - *face_found = NULL; - return 0; - } - *face_found = malloc(sizeof(hc_face_t)); - hc_listener_to_face(listener_found, *face_found); - free(listener_found); - break; - - default: - return -1; - } - - return 0; -} - -/* FACE DELETE */ - -static int _hcng_face_delete(hc_sock_t *socket, hc_face_t *face, - uint8_t delete_listener) { - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_delete] Unexpected truncation of face string"); - DEBUG("[hc_face_delete] face=%s", face_s); - - 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; - } - - if (_hcng_connection_delete(socket, &connection) < 0) { - ERROR("[hc_face_delete] Error removing connection"); - return -1; - } - - if (!delete_listener) { - return 0; - } - - /* 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 (_hcng_connection_list(socket, &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 (_hcng_listener_delete(socket, &listener) < 0) { - ERROR("[hc_face_delete] Error removing listener"); - return -1; - } - } - - hc_data_free(connections); - - return 0; -} - -/* FACE LIST */ - -static int _hcng_face_list(hc_sock_t *socket, hc_data_t **pdata) { - hc_data_t *connection_data; - hc_face_t face; - - DEBUG("[hc_face_list]"); - - if (_hcng_connection_list(socket, &connection_data) < 0) { - ERROR("[hc_face_list] Could not list connections."); - return -1; - } - - hc_data_t *face_data = - hc_data_create(sizeof(hc_connection_t), sizeof(hc_face_t), NULL); - foreach_connection(c, connection_data) { - if (hc_connection_to_face(c, &face) < 0) { - ERROR("[hc_face_list] Could not convert connection to face."); - goto ERR; - } - hc_data_push(face_data, &face); - } - - *pdata = face_data; - hc_data_free(connection_data); - DEBUG("[hc_face_list] done"); - return 0; - -ERR: - hc_data_free(connection_data); - DEBUG("[hc_face_list] error"); - return -1; -} - -static int hc_connection_parse_to_face(void *in, hc_face_t *face) { - hc_connection_t connection; - - if (hc_connection_parse(in, &connection) < 0) { - ERROR("[hc_connection_parse_to_face] Could not parse connection"); - return -1; - } - - if (hc_connection_to_face(&connection, face) < 0) { - ERROR( - "[hc_connection_parse_to_face] Could not convert connection to " - "face."); - return -1; - } - - return 0; -} - -static int _hcng_face_list_async(hc_sock_t *socket) { - struct { - cmd_header_t hdr; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_LIST, - .length = 0, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CONNECTION_LIST, - .size_in = sizeof(cmd_connection_list_item_t), - .size_out = sizeof(hc_face_t), - .parse = (HC_PARSE)hc_connection_parse_to_face, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, true); -} - -static int _hcng_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t admin_state) { - return hc_connection_set_admin_state(s, conn_id_or_name, admin_state); -} - -#ifdef WITH_POLICY -static int _hcng_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return hc_connection_set_priority(s, conn_id_or_name, priority); -} - -static int _hcng_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return hc_connection_set_tags(s, conn_id_or_name, tags); -} -#endif // WITH_POLICY - -/*----------------------------------------------------------------------------* - * Punting - *----------------------------------------------------------------------------*/ - -static int _hcng_punting_create_internal(hc_sock_t *socket, - hc_punting_t *punting, bool async) { - int rc; - - if (hc_punting_validate(punting) < 0) return -1; - - struct { - cmd_header_t hdr; - cmd_punting_add_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_PUNTING_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = punting->prefix, - .family = punting->family, - .len = punting->prefix_len, - }}; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - punting->face_id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[_hc_punting_create] Unexpected truncation of symbolic name string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_PUNTING_ADD, - .size_in = sizeof(cmd_punting_add_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_punting_create(hc_sock_t *s, hc_punting_t *punting) { - return _hcng_punting_create_internal(s, punting, false); -} - -static int _hcng_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { - return _hcng_punting_create_internal(s, punting, true); -} - -static int _hcng_punting_get(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found) { - ERROR("hc_punting_get not (yet) implemented."); - return -1; -} - -static int _hcng_punting_delete(hc_sock_t *s, hc_punting_t *punting) { - ERROR("hc_punting_delete not (yet) implemented."); - return -1; -} - -#if 0 -static int hc_punting_parse(void * in, hc_punting_t * punting) -{ - ERROR("hc_punting_parse not (yet) implemented."); - return -1; -} -#endif - -static int _hcng_punting_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("hc_punting_list not (yet) implemented."); - return -1; -} - -/*----------------------------------------------------------------------------* - * Cache - *----------------------------------------------------------------------------*/ - -/* CACHE SET STORE */ - -static int _hcng_cache_set_store_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_set_store_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_SET_STORE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = cache->store, - }}; - - hc_command_params_t params = { - .cmd = ACTION_STORE, - .cmd_id = COMMAND_TYPE_CACHE_SET_STORE, - .size_in = sizeof(cmd_cache_set_store_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_store_internal(s, cache, false); -} - -static int _hcng_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_store_internal(s, cache, true); -} - -/* CACHE SET SERVE */ - -static int _hcng_cache_set_serve_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_set_serve_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_SET_SERVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = cache->serve, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SERVE, - .cmd_id = COMMAND_TYPE_CACHE_SET_SERVE, - .size_in = sizeof(cmd_cache_set_serve_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_serve_internal(s, cache, false); -} - -static int _hcng_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_serve_internal(s, cache, true); -} - -/* CACHE CLEAR */ - -static int _hcng_cache_clear_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_clear_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_CLEAR, - .length = 1, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_CLEAR, - .cmd_id = COMMAND_TYPE_CACHE_CLEAR, - .size_in = sizeof(cmd_cache_clear_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_clear(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_clear_internal(s, cache, false); -} - -/* CACHE PARSE */ - -static int hc_cache_parse(void *in, hc_cache_info_t *cache_info) { - cmd_cache_list_reply_t *item = (cmd_cache_list_reply_t *)in; - *cache_info = (hc_cache_info_t){.store = item->store_in_cs, - .serve = item->serve_from_cs, - .cs_size = item->cs_size, - .num_stale_entries = item->num_stale_entries}; - - return 0; -} - -/* CACHE LIST */ - -static hc_result_t *_hcng_cache_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_cache_list] async=%s", BOOLSTR(async)); - - msg_cache_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CACHE_LIST, - .size_in = sizeof(cmd_cache_list_reply_t), - .size_out = sizeof(hc_cache_info_t), - .parse = (HC_PARSE)hc_cache_parse, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.cache_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_cache_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_cache_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_cache_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_cache_list_internal(s, pdata, false); -} - -/*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -// per prefix -static hc_result_t *_strategy_set_serialize(hc_sock_t *socket, - hc_strategy_t *strategy) { - hc_result_t *res = malloc(sizeof(*res)); - - char strategy_s[MAXSZ_HC_STRATEGY]; - int rc = strcpy_s(strategy->name, MAXSZ_STRATEGY_NAME, - strategy_str(strategy->type)); - if (rc != EOK) goto ERR; - rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); - if (rc >= MAXSZ_HC_STRATEGY) - WARN("[_hcng_strategy_create] Unexpected truncation of strategy string"); - DEBUG("[_hcng_strategy_create] strategy=%s", strategy_s); - - if (!IS_VALID_FAMILY(strategy->family) || - !IS_VALID_STRATEGY_TYPE(strategy->type)) { - goto ERR; - } - - msg_strategy_set_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STRATEGY_SET, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = strategy->address, - .family = strategy->family, - .len = strategy->len, - .type = strategy->type, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_STRATEGY_SET, - .size_in = sizeof(cmd_strategy_set_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.strategy_set = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; - -ERR: - res->success = false; - return res; -} - -static hc_result_t *_strategy_add_local_prefix_serialize( - hc_sock_t *socket, hc_strategy_t *strategy) { - hc_result_t *res = malloc(sizeof(*res)); - - char strategy_s[MAXSZ_HC_STRATEGY]; - int rc = strcpy_s(strategy->name, MAXSZ_STRATEGY_NAME, - strategy_str(strategy->type)); - if (rc != EOK) goto ERR; - rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); - if (rc >= MAXSZ_HC_STRATEGY) - WARN("[_hcng_strategy_create] Unexpected truncation of strategy string"); - DEBUG("[_hcng_strategy_create] strategy=%s", strategy_s); - - if (!IS_VALID_FAMILY(strategy->family) || - !IS_VALID_STRATEGY_TYPE(strategy->type) || - !IS_VALID_FAMILY(strategy->local_family)) { - goto ERR; - } - - msg_strategy_add_local_prefix_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, - .length = 1, - .seq_num = 0, - }, - .payload = { - .type = strategy->type, - .address = strategy->address, - .family = strategy->family, - .len = strategy->len, - .local_address = strategy->local_address, - .local_family = strategy->local_family, - .local_len = strategy->local_len, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, - .size_in = sizeof(cmd_strategy_add_local_prefix_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.strategy_add_local_prefix = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; - -ERR: - res->success = false; - return res; -} - -static hc_result_t *_hcng_strategy_set_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - return _strategy_set_serialize(s, strategy); -} - -static int _hcng_strategy_set(hc_sock_t *socket, hc_strategy_t *strategy) { - hc_result_t *result = _strategy_set_serialize(socket, strategy); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static hc_result_t *_hcng_strategy_add_local_prefix_conf( - hc_sock_t *s, hc_strategy_t *strategy) { - return _strategy_add_local_prefix_serialize(s, strategy); -} - -static int _hcng_strategy_add_local_prefix(hc_sock_t *socket, - hc_strategy_t *strategy) { - hc_result_t *result = _strategy_add_local_prefix_serialize(socket, strategy); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/* How to retrieve that from the forwarder ? */ -static const char *strategies[] = { - "random", - "load_balancer", -}; - -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) - -static int _hcng_strategy_list(hc_sock_t *s, hc_data_t **data) { - int rc; - - *data = hc_data_create(0, sizeof(hc_strategy_t), NULL); - - for (unsigned i = 0; i < ARRAY_SIZE(strategies); i++) { - hc_strategy_t *strategy = (hc_strategy_t *)hc_data_get_next(*data); - if (!strategy) return -1; - rc = snprintf(strategy->name, MAXSZ_STRATEGY_NAME, "%s", strategies[i]); - if (rc >= MAXSZ_STRATEGY_NAME) - WARN("[hc_strategy_list] Unexpected truncation of strategy name string"); - (*data)->size++; - } - - return 0; -} - -/*----------------------------------------------------------------------------* - * WLDR - *----------------------------------------------------------------------------*/ - -// per connection -static int _hcng_wldr_set(hc_sock_t *s /* XXX */) { return 0; } - -/*----------------------------------------------------------------------------* - * MAP-Me - *----------------------------------------------------------------------------*/ - -static int _hcng_mapme_set(hc_sock_t *socket, int enabled) { - msg_mapme_enable_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_ENABLE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = enabled, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_ENABLE, - .size_in = sizeof(cmd_mapme_enable_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_discovery(hc_sock_t *socket, int enabled) { - msg_mapme_enable_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = enabled, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, - .size_in = sizeof(cmd_mapme_set_discovery_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_timescale(hc_sock_t *socket, uint32_t timescale) { - msg_mapme_set_timescale_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .timePeriod = timescale, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, - .size_in = sizeof(cmd_mapme_set_timescale_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_retx(hc_sock_t *socket, uint32_t timescale) { - msg_mapme_set_retx_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_RETX, - .length = 1, - .seq_num = 0, - }, - .payload = { - .timePeriod = timescale, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_RETX, - .size_in = sizeof(msg_mapme_set_retx_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_send_update(hc_sock_t *socket, hc_mapme_t *mapme) { - if (!IS_VALID_FAMILY(mapme->family)) return -1; - - msg_mapme_send_update_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, - .length = 1, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_UPDATE, - .cmd_id = COMMAND_TYPE_MAPME_SEND_UPDATE, - .size_in = sizeof(msg_mapme_send_update_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -/*----------------------------------------------------------------------------* - * Policy - *----------------------------------------------------------------------------*/ - -#ifdef WITH_POLICY - -/* POLICY CREATE */ - -static int _hcng_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - if (!IS_VALID_FAMILY(policy->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_policy_add_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - COMMAND_TYPE_POLICY_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = policy->remote_addr, - .family = policy->family, - .len = policy->len, - .policy = policy->policy, - }}; - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_POLICY_ADD, - .size_in = sizeof(cmd_policy_add_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_policy_create(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_create_internal(s, policy, false); -} - -static int _hcng_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_create_internal(s, policy, true); -} - -/* POLICY DELETE */ - -static int _hcng_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - if (!IS_VALID_FAMILY(policy->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_policy_remove_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_POLICY_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = policy->remote_addr, - .family = policy->family, - .len = policy->len, - }}; - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_POLICY_REMOVE, - .size_in = sizeof(cmd_policy_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_policy_delete(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_delete_internal(s, policy, false); -} - -static int _hcng_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_delete_internal(s, policy, true); -} - -/* POLICY PARSE */ - -static int hc_policy_parse(void *in, hc_policy_t *policy) { - cmd_policy_list_item_t *item = (cmd_policy_list_item_t *)in; - - if (!IS_VALID_ADDRESS(&item->address, item->family)) { - ERROR("[hc_policy_parse] Invalid address"); - return -1; - } - if (!IS_VALID_FAMILY(item->family)) { - ERROR("[hc_policy_parse] Invalid family"); - return -1; - } - if (!IS_VALID_PREFIX_LEN(item->len)) { - ERROR("[hc_policy_parse] Invalid len"); - return -1; - } - if (!IS_VALID_POLICY(item->policy)) { - ERROR("[hc_policy_parse] Invalid policy"); - return -1; - } - - *policy = (hc_policy_t){ - .family = item->family, - .remote_addr = item->address, - .len = item->len, - .policy = item->policy, - }; - return 0; -} - -/* POLICY LIST */ - -static int _hcng_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - struct { - cmd_header_t hdr; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_POLICY_LIST, - .length = 0, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_POLICY_LIST, - .size_in = sizeof(cmd_policy_list_item_t), - .size_out = sizeof(hc_policy_t), - .parse = (HC_PARSE)hc_policy_parse, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - pdata, async); -} - -static int _hcng_policy_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_policy_list_internal(s, pdata, false); -} - -static int _hcng_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_policy_list_internal(s, pdata, true); -} - -#endif /* WITH_POLICY */ - -/*----------------------------------------------------------------------------* - * Subscriptioins - *----------------------------------------------------------------------------*/ - -/* SUBSCRIPTION CREATE */ - -static hc_result_t *_subscription_create_serialize( - hc_sock_t *s, hc_subscription_t *subscription) { - msg_subscription_add_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_SUBSCRIPTION_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = {.topics = subscription->topics}}; - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_SUBSCRIPTION_ADD, - .size_in = sizeof(cmd_subscription_add_t), - .size_out = 0, - .parse = NULL, - }; - - hc_result_t *res = malloc(sizeof(*res)); - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.subscription_add = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_subscription_create_conf( - hc_sock_t *s, hc_subscription_t *subscription) { - return _subscription_create_serialize(s, subscription); -} - -static int _hcng_subscription_create(hc_sock_t *socket, - hc_subscription_t *subscriiption) { - hc_result_t *result = _subscription_create_serialize(socket, subscriiption); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/* SUBSCRIPTION DELETE */ - -static hc_result_t *_subscription_delete_serialize( - hc_sock_t *s, hc_subscription_t *subscription) { - msg_subscription_remove_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = {.topics = subscription->topics}}; - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, - .size_in = sizeof(cmd_subscription_remove_t), - .size_out = 0, - .parse = NULL, - }; - - hc_result_t *res = malloc(sizeof(*res)); - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.subscription_remove = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_subscription_delete_conf( - hc_sock_t *s, hc_subscription_t *subscription) { - return _subscription_delete_serialize(s, subscription); -} - -static int _hcng_subscription_delete(hc_sock_t *socket, - hc_subscription_t *subscriiption) { - hc_result_t *result = _subscription_delete_serialize(socket, subscriiption); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/*----------------------------------------------------------------------------* - * Statistics - *----------------------------------------------------------------------------*/ - -/* STATS GET */ - -static hc_result_t *_hcng_stats_get_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_stats_get] async=%s", BOOLSTR(async)); - - msg_stats_get_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STATS_GET, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_GET, - .cmd_id = COMMAND_TYPE_STATS_GET, - .size_in = sizeof(hicn_light_stats_t), - .size_out = sizeof(hicn_light_stats_t), - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.stats_get = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_stats_get_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_stats_get_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_stats_get] done or error"); - return ret; -} - -static int _hcng_stats_get(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_stats_get]"); - return _hcng_stats_get_internal(s, pdata, false); -} - -/* STATS LIST */ - -static hc_result_t *_hcng_stats_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_stats_list] async=%s", BOOLSTR(async)); - - msg_stats_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STATS_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_STATS_LIST, - .size_in = sizeof(cmd_stats_list_item_t), - .size_out = sizeof(cmd_stats_list_item_t), - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.stats_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_stats_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_stats_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_stats_list] done or error"); - return ret; -} - -static int _hcng_stats_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_stats_list]"); - return _hcng_stats_list_internal(s, pdata, false); -} - -/* RESULT */ -hc_msg_t *_hcng_result_get_msg(hc_result_t *result) { return &result->msg; } -int _hcng_result_get_cmd_id(hc_result_t *result) { - return result->params.cmd_id; -} -bool _hcng_result_get_success(hc_result_t *result) { return result->success; } - -static hc_sock_t hc_sock_light_ng_interface = (hc_sock_t){ - .hc_sock_get_next_seq = _hcng_sock_light_get_next_seq, - .hc_sock_set_nonblocking = _hcng_sock_light_set_nonblocking, - .hc_sock_get_fd = _hcng_sock_light_get_fd, - .hc_sock_connect = _hcng_sock_light_connect, - .hc_sock_get_available = _hcng_sock_light_get_available, - .hc_sock_send = _hcng_sock_light_send, - .hc_sock_recv = _hcng_sock_light_recv, - .hc_sock_process = _hcng_sock_light_process, - .hc_sock_callback = _hcng_sock_light_callback, - .hc_sock_reset = _hcng_sock_light_reset, - .hc_sock_free = _hcng_sock_light_free, - .hc_sock_increment_woff = _hcng_sock_increment_woff, - .hc_sock_prepare_send = _hcng_sock_prepare_send, - .hc_sock_set_recv_timeout_ms = _hcng_sock_set_recv_timeout_ms, - .hc_listener_create = _hcng_listener_create, - .hc_listener_create_async = _hcng_listener_create_async, - .hc_listener_get = _hcng_listener_get, - .hc_listener_delete = _hcng_listener_delete, - .hc_listener_delete_async = _hcng_listener_delete_async, - .hc_listener_list = _hcng_listener_list, - .hc_listener_list_async = _hcng_listener_list_async, - .hc_connection_create = _hcng_connection_create, - .hc_connection_create_async = _hcng_connection_create_async, - .hc_connection_get = _hcng_connection_get, - .hc_connection_update_by_id = _hcng_connection_update_by_id, - .hc_connection_update = _hcng_connection_update, - .hc_connection_delete = _hcng_connection_delete, - .hc_connection_delete_async = _hcng_connection_delete_async, - .hc_connection_list = _hcng_connection_list, - .hc_connection_list_async = _hcng_connection_list_async, - .hc_connection_set_admin_state = _hcng_connection_set_admin_state, - .hc_connection_set_admin_state_async = - _hcng_connection_set_admin_state_async, - -#ifdef WITH_POLICY - .hc_connection_set_priority = _hcng_connection_set_priority, - .hc_connection_set_priority_async = _hcng_connection_set_priority_async, - .hc_connection_set_tags = _hcng_connection_set_tags, - .hc_connection_set_tags_async = _hcng_connection_set_tags_async, -#endif // WITH_POLICY - - .hc_face_create = _hcng_face_create, - .hc_face_get = _hcng_face_get, - .hc_face_delete = _hcng_face_delete, - .hc_face_list = _hcng_face_list, - .hc_face_list_async = _hcng_face_list_async, - .hc_face_set_admin_state = _hcng_face_set_admin_state, - -#ifdef WITH_POLICY - .hc_face_set_priority = _hcng_face_set_priority, - .hc_face_set_tags = _hcng_face_set_tags, -#endif // WITH_POLICY - .hc_subscription_create = _hcng_subscription_create, - .hc_subscription_delete = _hcng_subscription_delete, - - .hc_stats_get = _hcng_stats_get, - .hc_stats_list = _hcng_stats_list, - - .hc_route_create = _hcng_route_create, - .hc_route_create_async = _hcng_route_create_async, - .hc_route_delete = _hcng_route_delete, - .hc_route_delete_async = _hcng_route_delete_async, - .hc_route_list = _hcng_route_list, - .hc_route_list_async = _hcng_route_list_async, - - .hc_punting_create = _hcng_punting_create, - .hc_punting_create_async = _hcng_punting_create_async, - .hc_punting_get = _hcng_punting_get, - .hc_punting_delete = _hcng_punting_delete, - .hc_punting_list = _hcng_punting_list, - - .hc_cache_set_store = _hcng_cache_set_store, - .hc_cache_set_store_async = _hcng_cache_set_store_async, - .hc_cache_set_serve = _hcng_cache_set_serve, - .hc_cache_set_serve_async = _hcng_cache_set_serve_async, - .hc_cache_clear = _hcng_cache_clear, - .hc_cache_list = _hcng_cache_list, - - .hc_strategy_list = _hcng_strategy_list, - .hc_strategy_set = _hcng_strategy_set, - .hc_strategy_add_local_prefix = _hcng_strategy_add_local_prefix, - .hc_wldr_set = _hcng_wldr_set, - - .hc_mapme_set = _hcng_mapme_set, - .hc_mapme_set_discovery = _hcng_mapme_set_discovery, - .hc_mapme_set_timescale = _hcng_mapme_set_timescale, - .hc_mapme_set_retx = _hcng_mapme_set_retx, - .hc_mapme_send_update = _hcng_mapme_send_update, - -#ifdef WITH_POLICY - .hc_policy_create = _hcng_policy_create, - .hc_policy_create_async = _hcng_policy_create_async, - .hc_policy_delete = _hcng_policy_delete, - .hc_policy_delete_async = _hcng_policy_delete_async, - .hc_policy_list = _hcng_policy_list, - .hc_policy_list_async = _hcng_policy_list_async, -#endif // WITH_POLICY - - .hc_listener_create_conf = _hcng_listener_create_conf, - .hc_listener_list_conf = _hcng_listener_list_conf, - .hc_connection_create_conf = _hcng_connection_create_conf, - .hc_connection_delete_conf = _hcng_connection_delete_conf, - .hc_route_create_conf = _hcng_route_create_conf, - .hc_strategy_set_conf = _hcng_strategy_set_conf, - .hc_strategy_add_local_prefix_conf = _hcng_strategy_add_local_prefix_conf, - .hc_subscription_create_conf = _hcng_subscription_create_conf, - .hc_subscription_delete_conf = _hcng_subscription_delete_conf, - - .hc_result_get_msg = _hcng_result_get_msg, - .hc_result_get_cmd_id = _hcng_result_get_cmd_id, - .hc_result_get_success = _hcng_result_get_success, -}; - -// Public contructors - -hc_sock_t *_hc_sock_create_url(const char *url) { - hc_sock_light_t *s = malloc(sizeof(hc_sock_light_t)); - if (!s) goto ERR_MALLOC; - - s->vft = hc_sock_light_ng_interface; - s->url = url ? strdup(url) : NULL; - - s->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (s->fd < 0) goto ERR_SOCKET; - - if (_hcng_sock_set_recv_timeout_ms((hc_sock_t *)s, - DEFAULT_SOCK_RECV_TIMEOUT_MS) < 0) - goto ERR_SOCKET; - - if (_hcng_sock_light_reset((hc_sock_t *)s) < 0) goto ERR_RESET; - - s->seq = 0; - s->cur_request = NULL; - - s->map = kh_init_sock_map(); - if (!s->map) goto ERR_MAP; - - return (hc_sock_t *)(s); - - // hc_sock_light_map_free(s->map); -ERR_MAP: -ERR_RESET: - if (s->url) free(s->url); - close(s->fd); -ERR_SOCKET: - free(s); -ERR_MALLOC: - return NULL; -} diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin.c b/ctrl/libhicnctrl/src/modules/hicn_plugin.c new file mode 100644 index 000000000..b3963b46c --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2021 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 api.c + * \brief Implementation of hICN control library API + */ + +#include <assert.h> // assert +#include <fcntl.h> // fcntl +#include <math.h> // log2 +#include <stdbool.h> +#include <stdio.h> // snprintf +#include <string.h> // memmove, strcasecmp +#include <sys/socket.h> // socket +#include <unistd.h> // close, fcntl + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/socket.h> + +#include <vapi/vapi_safe.h> +#include <vppinfra/clib.h> +#include <vpp_plugins/hicn/error.h> + +#include "../socket_private.h" + +#include "hicn_plugin/base.h" // hc_sock_vpp_data_t +#include "hicn_plugin/listener.h" +#include "hicn_plugin/route.h" + +/****************************************************************************** + * Message helper types and aliases + ******************************************************************************/ + +#if 0 + +#define foreach_hc_command \ + _(hicn_api_node_params_set) \ + _(hicn_api_node_params_set_reply) \ + _(hicn_api_node_params_get_reply) \ + _(hicn_api_node_stats_get_reply) \ + _(hicn_api_face_get) \ + _(hicn_api_faces_details) \ + _(hicn_api_face_stats_details) \ + _(hicn_api_face_get_reply) \ + _(hicn_api_route_get) \ + _(hicn_api_route_get_reply) \ + _(hicn_api_routes_details) \ + _(hicn_api_strategies_get_reply) \ + _(hicn_api_strategy_get) \ + _(hicn_api_strategy_get_reply) + + +typedef vapi_type_msg_header2_t hc_msg_header_t; + +typedef union { +#define _(a) vapi_payload_##a a; + foreach_hc_command +#undef _ +} hc_msg_payload_t; + +typedef struct __attribute__((__packed__)) { + hc_msg_header_t hdr; + hc_msg_payload_t payload; +} hc_hicnp_t; + +typedef void (*NTOH)(void *msg); + +typedef struct __attribute__((__packed__)) { + hc_data_t *data; + uint32_t curr_msg; +} callback_ctx_t; + +typedef struct __attribute__((__packed__)) { + hc_hicnp_t *msg; + vapi_cb_t callback; + callback_ctx_t *callback_ctx; + NTOH ntoh; +} hc_msg_s; + +/****************************************************************************** + * Control socket + ******************************************************************************/ + +static void vpp_free(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + if (s->url) free(s->url); + free(s); + + vapi_disconnect_safe(); +} + +static int vpp_get_next_seq(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + return vapi_gen_req_context(s->g_vapi_ctx_instance); +} + +static int vpp_set_nonblocking(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + return 0; +} + +static int vpp_callback(hc_sock_t *socket, hc_data_t **pdata) { + // NOT IMPLEMENTED + return -1; +} + +static int vpp_reset(hc_sock_t *socket) { + // NOT IMPLEMENTED + return -1; +} +#endif + +/*----------------------------------------------------------------------------* + * Listeners + *----------------------------------------------------------------------------*/ + +/****************************************************************************** + * Module functions + ******************************************************************************/ + +hc_sock_vpp_data_t *hc_sock_vpp_data_create(const char *url) { + hc_sock_vpp_data_t *s = malloc(sizeof(hc_sock_vpp_data_t)); + if (!s) goto ERR_MALLOC; + + s->roff = s->woff = 0; + s->url = url ? strdup(url) : NULL; + + return s; + +ERR_MALLOC: + return NULL; +} + +void hc_sock_vpp_data_free(hc_sock_vpp_data_t *s) { + vapi_disconnect_safe(); + + if (s->url) free(s->url); + free(s); +} + +static int vpp_connect(hc_sock_t *sock) { + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + vapi_error_e rv = + vapi_connect_safe(&s->g_vapi_ctx_instance, hc_sock_is_async(sock)); + if (rv != VAPI_OK) goto ERR_CONNECT; + + return 0; + +ERR_CONNECT: + ERROR("[hc_sock_connect] connection failed"); + return -1; +} + +static ssize_t vpp_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + assert(!buffer); + + // XXX all the beginning is generic and could be shared across multiple + // modules + + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + _ASSERT(!hc_request_get_data(current_request)); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(hc_request_get_data(current_request) == NULL); + hc_data_t *data = hc_data_create(object_type); + if (!data) { + ERROR("[vpp_prepare] Could not create data storage"); + goto ERR; + } + hc_request_set_data(current_request, data); + + hc_module_object_ops_t *vft = &sock->ops.object_vft[object_type]; + if (!vft) goto ERR; + hc_execute_t execute = vft->execute[action]; + if (!execute) goto ERR; + int rc = execute(sock, object, data); + if (rc < 0) goto ERR; + + /* The result is fully contained in data */ + (void)rc; + + hc_request_set_complete(request); + return 0; + +ERR: + hc_data_set_error(data); + hc_request_set_complete(request); + return 0; +} + +static hc_sock_ops_t hc_sock_vpp = (hc_sock_ops_t){ + .create_data = (void *(*)(const char *))hc_sock_vpp_data_create, + .free_data = (void (*)(void *))hc_sock_vpp_data_free, + .get_fd = NULL, // not fd based + .get_recv_buffer = NULL, // no async support + .connect = vpp_connect, + .prepare = vpp_prepare, + .send = NULL, + .recv = NULL, + .process = NULL, +}; + +ssize_t vpp_command_serialize(hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + return hc_sock_vpp.object_vft[object_type].serialize[action](object, msg); +} + +// Public constructor + +int hc_sock_initialize_module(hc_sock_t *s) { + s->ops = hc_sock_vpp; + // XXX shall we memset the VFT ? + /* LISTENER: CREATE, GET, DELETE not implemented, LIST ok */ + s->ops.object_vft[OBJECT_TYPE_LISTENER] = vpp_listener_module_ops; + /* CONNECTION : CREATE, GET, UPDATE, DELETE, LIST, SET_* not + implemented */ + s->ops.object_vft[OBJECT_TYPE_CONNECTION] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_ROUTE] = vpp_route_module_ops; + s->ops.object_vft[OBJECT_TYPE_STRATEGY] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_SUBSCRIPTION] = HC_MODULE_OBJECT_OPS_EMPTY; + return 0; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h new file mode 100644 index 000000000..05565e938 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 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 modules/hicn_plugin/base.h + * \brief Base structures for hICN plugin module + */ + +#include <vapi/vapi_safe.h> +#include "../../module.h" +#include "../../socket_private.h" + +typedef struct { + vapi_ctx_t g_vapi_ctx_instance; + char *url; + + size_t roff; /**< Read offset */ + size_t woff; /**< Write offset */ + u32 buffer[RECV_BUFLEN]; + /* Next sequence number to be used for requests */ + int seq; + + bool async; +} hc_sock_vpp_data_t; diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c new file mode 100644 index 000000000..f0aa4e884 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 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 modules/hicn_plugin/listener.c + * \brief Implementation of listener object VFT for hicn_plugin. + */ + +#include <hicn/util/vector.h> + +#include "base.h" +#include "listener.h" + +struct listener_data_s { + hc_listener_t listener; + hc_data_t *data; +}; + +/** + * This is a callback used to append in callback_ctx which is a hc_data_t + * designed to hold hc_listener_t, a list of listener, each corresponding to an + * IP address (v4 then v6) of the interfaces, and thus build a list of hICN + * listeners. + */ +static vapi_error_e process_ip_info(struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_address_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + if (reply && is_last) printf("COUCOU\n"); + if (is_last) return 0; + + struct listener_data_s *ld = (struct listener_data_s *)callback_ctx; + + if (reply->prefix.address.af == ADDRESS_IP4) { + memcpy(&(ld->listener.local_addr), reply->prefix.address.un.ip4, + IPV4_ADDR_LEN); + ld->listener.family = AF_INET; + } else { + memcpy(&(ld->listener.local_addr), reply->prefix.address.un.ip6, + IPV6_ADDR_LEN); + ld->listener.family = AF_INET6; + } + ld->listener.local_port = 0; + + ld->listener.id = reply->sw_if_index; + hc_data_t *data = ld->data; + hc_listener_t *listener = &ld->listener; + hc_data_push(data, listener); + + return rv; +} + +/* LISTENER LIST */ + +typedef struct { + u32 swif; + char interface_name[INTERFACE_LEN]; +} hc_vapi_interface_t; + +/* + * A pointer to hc_data_t is passed in the callback context + * Objective is to store a vector of hc_vapi_interface_t inside + */ +static vapi_error_e on_listener_list_complete_cb( + struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_sw_interface_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (is_last) return 0; + + hc_vapi_interface_t **vpp_interfaces_vec = + (hc_vapi_interface_t **)callback_ctx; + + hc_vapi_interface_t interface = {.swif = reply->sw_if_index}; + // XXX bug + memcpy(interface.interface_name, reply->interface_name, INTERFACE_LEN); + + vector_push(*vpp_interfaces_vec, interface); + + return rv; +} + +static int _vpp_listener_list(hc_sock_t *sock, hc_data_t *data) { + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + int retval = -1; // VAPI_OK; + + hc_vapi_interface_t *vpp_interfaces_vec = NULL; + vector_init(vpp_interfaces_vec, 0, 0); + + vapi_lock(); + + vapi_msg_sw_interface_dump *msg = + vapi_alloc_sw_interface_dump(s->g_vapi_ctx_instance); + if (!msg) { + retval = VAPI_ENOMEM; + goto ERR_MSG; + } + msg->payload.sw_if_index = ~0; + msg->payload.name_filter_valid = 0; + + /* Retrieve the list of interfaces in vpp_interfaces_vec */ + int ret = + vapi_sw_interface_dump(s->g_vapi_ctx_instance, msg, + on_listener_list_complete_cb, &vpp_interfaces_vec); + + if (ret != VAPI_OK) goto ERR_LIST_INTERFACES; + + /* Query the forwarder for each interface */ + // stored in data->buffer == hc_vapi_interface_t* [] + // 2 calls for each interface + // + // This function is called twice for each interface, to get resp. the v4 and + // v6 IP addresses associated to it: + // ip_address_dump(sw_if_index, is_ipv6) + // + // Function call : + // vapi_msg_XXX *msg = vapi_alloc_XXX(s->g_vapi_ctx_instance); + // msg->payload.ATTR = VALUE; + // [...] + // int ret = vapi_XXX((s->g_vapi_ctx_instance, msg, CALLBACK, USER_DATA); + // + // CALLBACK = process_ip_info + // USER_DATA = data2 + // + // We can assume the callbacks are executed before the function returns, and + // that there is no async code. + // + int rc; + + hc_vapi_interface_t *interface; + vapi_msg_ip_address_dump *msg2; + + struct listener_data_s ld; + vector_foreach(vpp_interfaces_vec, interface, { + memset(&ld, 0, sizeof(struct listener_data_s)); + ld.listener.type = FACE_TYPE_HICN; + rc = snprintf(ld.listener.interface_name, INTERFACE_LEN, "%s", + interface->interface_name); + if (rc < 0 || rc >= INTERFACE_LEN) goto ERR_FOREACH; + + ld.data = data; + + for (unsigned i = 0; i < 2; i++) { + msg2 = vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); + msg2->payload.sw_if_index = interface->swif; + msg2->payload.is_ipv6 = i; + retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg2, + process_ip_info, &ld); + if (ret != VAPI_OK) goto ERR_GET_IP; + } + }); + retval = 0; +ERR_GET_IP: +ERR_FOREACH: + vector_free(vpp_interfaces_vec); +ERR_LIST_INTERFACES: +ERR_MSG: + vapi_unlock(); + return retval; +} + +#define vpp_listener_create NULL +#define vpp_listener_delete NULL + +static int vpp_listener_list(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + assert(!object || hc_object_is_empty(object)); + return _vpp_listener_list(sock, data); +} + +DECLARE_VPP_MODULE_OBJECT_OPS(vpp, listener); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_common.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h index d1fb33993..f75c58db6 100644 --- a/ctrl/libhicnctrl/src/modules/hicn_light_common.c +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h @@ -13,18 +13,16 @@ * limitations under the License. */ -#include "hicn_light_common.h" +/** + * \file modules/hicn_plugin/listener.h + * \brief listener object VFT for hicn_plugin. + */ + +#ifndef HICNCTRL_MODULE_VPP_LISTENER_H +#define HICNCTRL_MODULE_VPP_LISTENER_H -hc_sock_request_t *hc_sock_request_create(int seq, hc_data_t *data, - HC_PARSE parse) { - assert(data); +#include "../../module.h" - hc_sock_request_t *request = malloc(sizeof(hc_sock_request_t)); - if (!request) return NULL; - request->seq = seq; - request->data = data; - request->parse = parse; - return request; -} +DECLARE_MODULE_OBJECT_OPS_H(vpp, listener); -void hc_sock_light_request_free(hc_sock_request_t *request) { free(request); } +#endif /* HICNCTRL_MODULE_VPP_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c new file mode 100644 index 000000000..45aced9cb --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2021 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 modules/hicn_plugin/route.c + * \brief Implementation of route object VFT for hicn_plugin. + */ + +#include "base.h" +#include "route.h" + +static vapi_error_e create_udp_tunnel_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_udp_tunnel_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply->retval != VAPI_OK) return reply->retval; + + u32 *uei = (u32 *)callback_ctx; + *uei = reply->uei; + + return reply->retval; +} + +static vapi_error_e parse_route_create( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_ip_route_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply->retval != VAPI_OK) return reply->retval; + + return reply->retval; +} + +static vapi_error_e hicn_enable_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_enable_disable_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + face_id_t *faceid = (face_id_t *)callback_ctx; + + if (reply->nfaces) { + *faceid = reply->faceids[0]; + } + + return reply->retval; +} + +static int _vpp_route_create(hc_sock_t *sock, hc_route_t *route) { + if (!IS_VALID_FAMILY(route->family)) return -1; + + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + int ret = -1; + vapi_lock(); + + vapi_msg_ip_route_add_del *msg = + vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); + + msg->payload.is_add = 1; + if (route->family == AF_INET) { + memcpy(&msg->payload.route.prefix.address.un.ip4[0], &route->remote_addr.v4, + 4); + msg->payload.route.prefix.address.af = ADDRESS_IP4; + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&msg->payload.route.prefix.address.un.ip6[0], &route->remote_addr.v6, + 16); + msg->payload.route.prefix.address.af = ADDRESS_IP6; + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg->payload.route.prefix.len = route->len; + + msg->payload.route.paths[0].sw_if_index = ~0; + msg->payload.route.paths[0].table_id = 0; + + hc_face_t *face = &(route->face); + + face->netdevice.index = ~0; + face->id = INVALID_FACE_ID; + + switch (face->type) { + case FACE_TYPE_HICN: { + if (hicn_ip_address_is_v4(&(face->remote_addr))) { + memcpy(&(msg->payload.route.paths[0].nh.address.ip4), + &face->remote_addr.v4, sizeof(ip4_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&(msg->payload.route.paths[0].nh.address.ip6), + &face->remote_addr.v6, sizeof(ip6_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; + msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + + break; + } + case FACE_TYPE_UDP: { + vapi_msg_hicn_api_udp_tunnel_add_del *msg2 = NULL; + u32 uei = ~0; + + if (hicn_ip_address_is_v4(&(face->remote_addr)) && + hicn_ip_address_is_v4(&(face->local_addr))) { + msg2 = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); + memcpy(msg2->payload.src_addr.un.ip4, &face->local_addr.v4, + sizeof(ip4_address_t)); + msg2->payload.src_addr.af = ADDRESS_IP4; + + memcpy(msg2->payload.dst_addr.un.ip4, &face->remote_addr.v4, + sizeof(ip4_address_t)); + msg2->payload.dst_addr.af = ADDRESS_IP4; + + } else if (!hicn_ip_address_is_v4(&(route->face.remote_addr)) && + !hicn_ip_address_is_v4(&(route->face.local_addr))) { + msg2 = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); + memcpy(msg2->payload.src_addr.un.ip6, &face->local_addr.v6, + sizeof(ip6_address_t)); + msg2->payload.src_addr.af = ADDRESS_IP6; + + memcpy(msg2->payload.dst_addr.un.ip6, &face->remote_addr.v6, + sizeof(ip6_address_t)); + msg2->payload.dst_addr.af = ADDRESS_IP6; + } else { + // NOT IMPLEMENTED + ret = -1; + goto done; + } + + msg2->payload.src_port = face->local_port; + msg2->payload.dst_port = face->remote_port; + msg2->payload.is_add = 1; + + int ret = vapi_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance, msg2, + create_udp_tunnel_cb, &uei); + + if (ret) { + ERROR("Error in vapi_hicn_api_udp_tunnel_add_del"); + vapi_msg_free(s->g_vapi_ctx_instance, msg); + goto done; + } + + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; + msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + msg->payload.route.paths[0].nh.obj_id = uei; + + face->netdevice.index = uei; + + break; + } + default: + ret = -1; + goto done; + } + + ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, msg, parse_route_create, + NULL); + + if (ret) { + ERROR("Error in vapi_ip_route_add_del"); + goto done; + } + + vapi_msg_hicn_api_enable_disable *msg3 = + vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); + + if (route->family == AF_INET) { + memcpy(&msg3->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); + msg3->payload.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg3->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); + msg3->payload.prefix.address.af = ADDRESS_IP6; + } + + msg3->payload.prefix.len = route->len; + msg3->payload.enable_disable = 1; + + ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg3, + hicn_enable_cb, &face->id); + + if (ret) { + ERROR("Error in vapi_hicn_api_enable_disable"); + } + +done: + vapi_unlock(); + return ret; +} + +static vapi_error_e hicn_disable_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_enable_disable_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + return reply->retval; +} + +static vapi_error_e parse_route_delete( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_ip_route_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + return reply->retval; +} + +static int _vpp_route_delete(hc_sock_t *sock, hc_route_t *route) { + if (!IS_VALID_FAMILY(route->family)) return -1; + + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + vapi_lock(); + + vapi_msg_hicn_api_enable_disable *msg = + vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); + + if (route->family == AF_INET) { + memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); + msg->payload.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); + msg->payload.prefix.address.af = ADDRESS_IP6; + } + + msg->payload.prefix.len = route->len; + msg->payload.enable_disable = 0; + + vapi_error_e ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, + hicn_disable_cb, NULL); + + if (ret) { + ERROR("Error in vapi_hicn_api_enable_disable in route delete"); + goto done; + } + + vapi_msg_ip_route_add_del *msg2 = + vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); + + msg2->payload.is_add = 0; + if (route->family == AF_INET) { + memcpy(&msg2->payload.route.prefix.address.un.ip4[0], + &route->remote_addr.v4, 4); + msg2->payload.route.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg2->payload.route.prefix.address.un.ip6[0], + &route->remote_addr.v6, 16); + msg2->payload.route.prefix.address.af = ADDRESS_IP6; + } + + msg2->payload.route.prefix.len = route->len; + + msg2->payload.route.paths[0].sw_if_index = ~0; + msg2->payload.route.paths[0].table_id = 0; + + hc_face_t *face = &(route->face); + switch (face->type) { + case FACE_TYPE_HICN: { + if (hicn_ip_address_is_v4(&(face->remote_addr))) { + memcpy(&(msg2->payload.route.paths[0].nh.address.ip4), + &face->remote_addr.v4, sizeof(ip4_address_t)); + msg2->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&(msg2->payload.route.paths[0].nh.address.ip6), + &face->remote_addr.v6, sizeof(ip6_address_t)); + msg2->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg2->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; + msg2->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + + break; + } + case FACE_TYPE_UDP: { + msg2->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; + msg2->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + msg2->payload.route.paths[0].nh.obj_id = face->netdevice.index; + break; + } + default: + return -1; + } + + ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, msg2, parse_route_delete, + NULL); + + if (ret) { + ERROR("Error in vapi_ip_route_add_del in route delete"); + goto done; + } + +done: + + vapi_unlock(); + return ret; +} + +/* ROUTE LIST */ + +static vapi_error_e parse_udp_encap_list( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_udp_encap_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + hc_face_t *face = (hc_face_t *)callback_ctx; + + if (face->netdevice.index == reply->udp_encap.id) { + switch (reply->udp_encap.src_ip.af) { + case ADDRESS_IP4: { + memcpy(&face->local_addr.v4, &(reply->udp_encap.src_ip.un.ip4), + sizeof(ip4_address_t)); + memcpy(&face->remote_addr.v4, &(reply->udp_encap.dst_ip.un.ip4), + sizeof(ip4_address_t)); + break; + } + case ADDRESS_IP6: { + memcpy(&face->local_addr.v6, &(reply->udp_encap.src_ip.un.ip6), + sizeof(ip6_address_t)); + memcpy(&face->remote_addr.v6, &(reply->udp_encap.dst_ip.un.ip6), + sizeof(ip6_address_t)); + break; + } + default: + break; + } + + face->local_port = reply->udp_encap.src_port; + face->remote_port = reply->udp_encap.dst_port; + } + return rv; +} + +static int _fill_face_with_info(hc_face_t *face, vapi_type_fib_path *path) { + switch (path->type) { + case FIB_API_PATH_FLAG_NONE: { + face->type = FACE_TYPE_HICN; + switch (path->proto) { + case FIB_API_PATH_NH_PROTO_IP4: + memcpy(&face->remote_addr.v4, &(path->nh.address.ip4), + sizeof(ipv4_address_t)); + break; + case FIB_API_PATH_NH_PROTO_IP6: + memcpy(&face->remote_addr.v6, &(path->nh.address.ip6), + sizeof(ipv6_address_t)); + break; + default: + break; + } + face->netdevice.index = path->sw_if_index; + } break; + case FIB_API_PATH_TYPE_UDP_ENCAP: { + face->type = FACE_TYPE_UDP; + face->netdevice.index = clib_net_to_host_u32(path->nh.obj_id); + // Let's make the compiler happy + (void)parse_udp_encap_list; + // vapi_msg_udp_encap_dump *msg; + // msg = vapi_alloc_udp_encap_dump(s->g_vapi_ctx_instance); + // vapi_udp_encap_dump(s->g_vapi_ctx_instance, msg, parse_udp_encap_list, + // face); + } break; + default: + return -1; + } + return 0; +} + +static vapi_error_e parse_route_list(vapi_ctx_t ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_route_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + // XXX DEBUG XXX + if (reply && is_last) printf("COUCOU\n"); + + if (is_last) return 0; + + hc_data_t *data = (hc_data_t *)callback_ctx; + + /* + * Implementation: + * A route has n paths... we iterate for each path and search for a + * corresponding face in the hc_data_t result struct... and we fill the face + * info with the route path. + * + * TODO + * - comment on paths + * - explain the jump to END, this was previously implemented with a + * boolean flags skipping all remaining tests in the function... + */ + for (int j = 0; j < reply->route.n_paths; j++) { + hc_data_foreach(data, obj, { + hc_route_t *route = &obj->route; + + if (hicn_ip_address_is_v4(&(route->remote_addr)) && + memcmp(route->remote_addr.v4.as_u8, + reply->route.prefix.address.un.ip4, + sizeof(ipv4_address_t)) == 0 && + route->len == reply->route.prefix.len && route->face_id == ~0) { + _fill_face_with_info(&(route->face), &reply->route.paths[j]); + goto END; + + } else if (memcmp(route->remote_addr.v6.as_u8, + reply->route.prefix.address.un.ip6, + sizeof(ipv6_address_t)) == 0 && + route->len == reply->route.prefix.len && + route->face_id == ~0) { + _fill_face_with_info(&(route->face), &reply->route.paths[j]); + goto END; + } + }); + } + +END: + return rv; +} + +/** + * Populates the hc_data_t structure passed as the context with... + */ +static vapi_error_e parse_hicn_route_list( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_routes_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply && is_last) printf("COUCOU\n"); + if (is_last) return 0; + + hc_data_t *data = (hc_data_t *)callback_ctx; + + for (int i = 0; i < reply->nfaces; i++) { + hc_route_t route; + memset(&route, 0, sizeof(hc_route_t)); + + /* + * We set the face_id to ~0 to act as a marker in parse_route_list that + * the route is missing face information. + */ + route.face_id = ~0; + route.cost = 1; + route.len = reply->prefix.len; + if (reply->prefix.address.af == ADDRESS_IP6) { + memcpy(route.remote_addr.v6.as_u8, reply->prefix.address.un.ip6, 16); + route.family = AF_INET6; + } else { + memcpy(route.remote_addr.v4.as_u8, reply->prefix.address.un.ip4, 4); + route.family = AF_INET; + } + + hc_data_push(data, &route); + } + + return rv; +} + +/* + * hicn_api_routes_dump + * ip_route_dump + * + * @returns hc_data_t<hc_route_t> + */ +static int _vpp_route_list(hc_sock_t *sock, hc_data_t *data) { + int ret; + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + vapi_lock(); + + /* Start by retrieving hicn routes (we have no face information at this + * stage)... */ + vapi_msg_hicn_api_routes_dump *msg; + msg = vapi_alloc_hicn_api_routes_dump(s->g_vapi_ctx_instance); + if (!msg) goto ERR_MSG; + + ret = vapi_hicn_api_routes_dump(s->g_vapi_ctx_instance, msg, + parse_hicn_route_list, data); + if (ret != VAPI_OK) goto ERR_API; + + /* + * ... an complement them using IP (v4 and v6 routes). Similar routes will + * be aggregated, based on IP prefix, in parse_*_route_list. + */ + vapi_msg_ip_route_dump *msg2; + for (unsigned i = 0; i < 2; i++) { + msg2 = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); + if (!msg2) goto ERR_MSG; + + msg2->payload.table.table_id = 0; + msg2->payload.table.is_ip6 = i; + + ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, msg2, parse_route_list, + data); + if (ret != VAPI_OK) goto ERR_API; + } + + goto END; + +ERR_MSG: + ret = VAPI_ENOMEM; + goto END; + +ERR_API: +END: + vapi_unlock(); + return ret; +} + +static int vpp_route_create(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + int rc = _vpp_route_create(sock, &object->route); + if (rc < 0) + hc_data_set_complete(data); + else + hc_data_set_error(data); + return rc; +} + +static int vpp_route_delete(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + int rc = _vpp_route_delete(sock, &object->route); + if (rc < 0) + hc_data_set_complete(data); + else + hc_data_set_error(data); + return rc; +} + +static int vpp_route_list(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + assert(!object || hc_object_is_empty(object)); + return _vpp_route_list(sock, data); +} + +DECLARE_VPP_MODULE_OBJECT_OPS(vpp, route); diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h new file mode 100644 index 000000000..652d3e89a --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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 modules/hicn_plugin/route.h + * \brief route object VFT for hicn_plugin. + */ + +#ifndef HICNCTRL_MODULE_VPP_ROUTE_H +#define HICNCTRL_MODULE_VPP_ROUTE_H + +#include "../../module.h" + +DECLARE_MODULE_OBJECT_OPS_H(vpp, route); + +#endif /* HICNCTRL_MODULE_VPP_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c b/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c deleted file mode 100644 index 6d1baa786..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c +++ /dev/null @@ -1,1402 +0,0 @@ -/* - * Copyright (c) 2021 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 api.c - * \brief Implementation of hICN control library API - */ - -#include <assert.h> // assert -#include <fcntl.h> // fcntl -#include <math.h> // log2 -#include <stdbool.h> -#include <stdio.h> // snprintf -#include <string.h> // memmove, strcasecmp -#include <sys/socket.h> // socket -#include <unistd.h> // close, fcntl -#include <vapi/vapi_safe.h> -#include <vppinfra/clib.h> -#include <vpp_plugins/hicn/error.h> - -#include "api_private.h" - -/** - * Messages to the forwarder might be multiplexed thanks to the seqNum fields in - * the header_control_message structure. The forwarder simply answers back the - * original sequence number. We maintain a map of such sequence number to - * outgoing queries so that replied can be demultiplexed and treated - * appropriately. - */ -/* TYPEDEF_MAP_H(hc_sock_map, int, hc_sock_request_t *); */ -/* TYPEDEF_MAP(hc_sock_map, int, hc_sock_request_t *, int_cmp, int_snprintf, */ -/* generic_snprintf); */ - -struct hc_sock_vpp_s { - /* This must be the first element of the struct */ - hc_sock_t vft; - - vapi_ctx_t g_vapi_ctx_instance; - char *url; - - size_t roff; /**< Read offset */ - size_t woff; /**< Write offset */ - u32 buffer[RECV_BUFLEN]; - /* Next sequence number to be used for requests */ - int seq; - - bool async; -}; - -typedef struct hc_sock_vpp_s hc_sock_vpp_t; - -#define TO_HC_SOCK_VPP(s) (hc_sock_vpp_t *)(s) - -/****************************************************************************** - * Message helper types and aliases - ******************************************************************************/ - -#define foreach_hc_command \ - _(hicn_api_node_params_set) \ - _(hicn_api_node_params_set_reply) \ - _(hicn_api_node_params_get_reply) \ - _(hicn_api_node_stats_get_reply) \ - _(hicn_api_face_get) \ - _(hicn_api_faces_details) \ - _(hicn_api_face_stats_details) \ - _(hicn_api_face_get_reply) \ - _(hicn_api_route_get) \ - _(hicn_api_route_get_reply) \ - _(hicn_api_routes_details) \ - _(hicn_api_strategies_get_reply) \ - _(hicn_api_strategy_get) \ - _(hicn_api_strategy_get_reply) - -typedef vapi_type_msg_header2_t hc_msg_header_t; - -typedef union { -#define _(a) vapi_payload_##a a; - foreach_hc_command -#undef _ -} hc_msg_payload_t; - -typedef struct __attribute__((__packed__)) { - hc_msg_header_t hdr; - hc_msg_payload_t payload; -} hc_hicnp_t; - -typedef void (*NTOH)(void *msg); - -typedef struct __attribute__((__packed__)) { - hc_data_t *data; - uint32_t curr_msg; -} callback_ctx_t; - -typedef struct __attribute__((__packed__)) { - hc_hicnp_t *hicnp_msg; - vapi_cb_t callback; - callback_ctx_t *callback_ctx; - NTOH ntoh; -} hc_msg_s; - -/****************************************************************************** - * Control socket - ******************************************************************************/ - -static void _hc_sock_vpp_free(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - if (s->url) free(s->url); - free(s); - - vapi_disconnect_safe(); -} - -static int _hc_sock_vpp_get_next_seq(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - return vapi_gen_req_context(s->g_vapi_ctx_instance); -} - -static int _hc_sock_vpp_set_nonblocking(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - s->async = 1; - return 0; -} - -static int _hc_sock_vpp_get_fd(hc_sock_t *s) { return 1; } - -static int _hc_sock_vpp_connect(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - vapi_error_e rv = vapi_connect_safe(&s->g_vapi_ctx_instance, s->async); - if (rv != VAPI_OK) goto ERR_CONNECT; - - return 0; - -ERR_CONNECT: - ERROR("[hc_sock_connect] connection failed"); - return -1; -} - -static int _hc_sock_vpp_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, - uint32_t seq) { - return -1; -} - -static int _hc_sock_vpp_get_available(hc_sock_t *s, u8 **buffer, size_t *size) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_recv(hc_sock_t *s) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_process(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_callback(hc_sock_t *socket, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_reset(hc_sock_t *socket) { - // NOT IMPLEMENTED - return -1; -} - -/****************************************************************************** - * Command-specific structures and functions - ******************************************************************************/ - -/*----------------------------------------------------------------------------* - * Listeners - *----------------------------------------------------------------------------*/ - -/* LISTENER CREATE */ - -static int _hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_listener_create_async(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -/* LISTENER GET */ -static int _hc_listener_get(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found) { - // NOT IMPLEMENTED - return -1; -} - -/* LISTENER DELETE */ - -static int _hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_listener_delete_async(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static vapi_error_e process_ip_info(struct vapi_ctx_s *ctx, void *callback_ctx, - vapi_error_e rv, bool is_last, - vapi_payload_ip_address_details *reply) { - if (is_last) return 0; - hc_data_t *data = (hc_data_t *)callback_ctx; - - if (data->size == data->current) { - data->buffer = - realloc(data->buffer, sizeof(hc_listener_t) * data->size * 2); - - if (!data->buffer) return VAPI_ENOMEM; - - data->size *= 2; - } - - hc_listener_t *listener = - (hc_listener_t *)(data->buffer + data->current * sizeof(hc_listener_t)); - memset(listener, 0, sizeof(hc_listener_t)); - - if (reply->prefix.address.af == ADDRESS_IP4) { - memcpy(listener->local_addr.v4.as_u8, reply->prefix.address.un.ip4, - IPV4_ADDR_LEN); - listener->family = AF_INET; - } else { - memcpy(listener->local_addr.v6.as_u8, reply->prefix.address.un.ip6, - IPV6_ADDR_LEN); - listener->family = AF_INET6; - } - - listener->id = reply->sw_if_index; - data->current++; - return rv; -} - -typedef struct { - u32 swif; - char interface_name[INTERFACE_LEN]; -} hc_vapi_interface_t; - -static vapi_error_e listener_list_complete_cb( - struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_sw_interface_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (is_last) return 0; - - hc_data_t *data = (hc_data_t *)callback_ctx; - - if (data->size == data->current) { - data->buffer = - realloc(data->buffer, sizeof(hc_vapi_interface_t) * data->size * 2); - - if (!data->buffer) return VAPI_ENOMEM; - - data->size *= 2; - } - - hc_vapi_interface_t *swif = - &((hc_vapi_interface_t *)data->buffer)[data->current]; - - swif[0].swif = reply->sw_if_index; - memcpy(swif[0].interface_name, reply->interface_name, INTERFACE_LEN); - - data->current++; - - return rv; -} - -/* LISTENER LIST */ -static int _hc_listener_list(hc_sock_t *socket, hc_data_t **pdata) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - int retval = VAPI_OK; - vapi_lock(); - vapi_msg_sw_interface_dump *hicnp_msg; - hicnp_msg = vapi_alloc_sw_interface_dump(s->g_vapi_ctx_instance); - - if (!hicnp_msg) { - retval = VAPI_ENOMEM; - goto END; - } - - hicnp_msg->payload.sw_if_index = ~0; - hicnp_msg->payload.name_filter_valid = 0; - - hc_data_t *data = hc_data_create(0, sizeof(hc_vapi_interface_t), NULL); - - if (!data) { - retval = -1; - goto END; - } - - hc_data_t *data2 = hc_data_create(0, 1, NULL); - - if (!data2) { - retval = -1; - goto END; - } - - data->buffer = malloc(sizeof(hc_vapi_interface_t)); - data->size = 1; - - if (!data->buffer) { - free(data); - retval = -1; - goto FREE_DATA; - } - - int ret = vapi_sw_interface_dump(s->g_vapi_ctx_instance, hicnp_msg, - listener_list_complete_cb, data); - - if (ret != VAPI_OK) { - free(data->buffer); - free(data); - retval = -1; - goto FREE_DATA_BUFFER; - } - - data2->buffer = malloc(sizeof(hc_listener_t)); - data2->size = 1; - data2->out_element_size = 1; - - if (!data2->buffer) { - free(data2->buffer); - retval = -1; - goto CLEAN; - } - - /* Query the forwarder for each interface */ - for (int i = 0; i < data->current; i++) { - int index = data2->current; - vapi_msg_ip_address_dump *msg = - vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); - msg->payload.sw_if_index = ((hc_vapi_interface_t *)data->buffer)[i].swif; - msg->payload.is_ipv6 = 0; - retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg, process_ip_info, - data2); - vapi_msg_ip_address_dump *msg2 = - vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); - - if (retval) goto CLEAN; - - msg2->payload.sw_if_index = ((hc_vapi_interface_t *)data->buffer)[i].swif; - msg2->payload.is_ipv6 = 1; - retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg2, process_ip_info, - data2); - for (size_t j = index; j < data2->current; j++) { - memcpy(((hc_listener_t *)(data2->buffer))[j].interface_name, - ((hc_vapi_interface_t *)(data->buffer))[i].interface_name, - INTERFACE_LEN); - ((hc_listener_t *)(data2->buffer))[j].type = FACE_TYPE_HICN; - } - - if (retval) goto CLEAN; - } - -CLEAN: -FREE_DATA_BUFFER: - free(data->buffer); -FREE_DATA: - free(data); - - data2->size = data2->current; - data2->out_element_size = sizeof(hc_listener_t); - *pdata = data2; -END: - vapi_unlock(); - return retval; -} - -static int _hc_listener_list_async(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -/* CONNECTION CREATE */ - -static int _hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_create_async(hc_sock_t *s, - hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION GET */ - -static int _hc_connection_get(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - // Not implemented - return -1; -} - -static int _hc_connection_update(hc_sock_t *s, - hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - // Not implemented - return -1; -} - -/* CONNECTION DELETE */ - -static int _hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_delete_async(hc_sock_t *s, - hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION LIST */ - -static int _hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_list_async(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION SET ADMIN STATE */ - -static int _hc_connection_set_admin_state(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_admin_state_async(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - // NOT IMPLEMENTED - return -1; -} - -#ifdef WITH_POLICY - -static int _hc_connection_set_priority(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_priority_async(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - // NOT IMPLEMENTED - return -1; -} - -#endif // WITH_POLICY - -static int _hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_tags_async(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags) { - // NOT IMPLEMENTED - return -1; -} - -/*----------------------------------------------------------------------------* - * Routes - *----------------------------------------------------------------------------*/ - -static vapi_error_e create_udp_tunnel_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_udp_tunnel_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (reply->retval != VAPI_OK) return reply->retval; - - u32 *uei = (u32 *)callback_ctx; - *uei = reply->uei; - - return reply->retval; -} - -/* ROUTE CREATE */ -static vapi_error_e parse_route_create( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_ip_route_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (reply->retval != VAPI_OK) return reply->retval; - - return reply->retval; -} - -static vapi_error_e hicn_enable_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_enable_disable_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - face_id_t *faceid = (face_id_t *)callback_ctx; - - if (reply->nfaces) { - *faceid = reply->faceids[0]; - } - - return reply->retval; -} - -static int _hc_route_create_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - if (!IS_VALID_FAMILY(route->family)) return -1; - - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - int ret = -1; - vapi_lock(); - - vapi_msg_ip_route_add_del *hicnp_msg = - vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); - - hicnp_msg->payload.is_add = 1; - if (route->family == AF_INET) { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip4[0], - &route->remote_addr.v4, 4); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP4; - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip6[0], - &route->remote_addr.v6, 16); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP6; - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.prefix.len = route->len; - - hicnp_msg->payload.route.paths[0].sw_if_index = ~0; - hicnp_msg->payload.route.paths[0].table_id = 0; - - hc_face_t *face = &(route->face); - - face->face.netdevice.index = ~0; - face->id = INVALID_FACE_ID; - - switch (face->face.type) { - case FACE_TYPE_HICN: { - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr)))) { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip4), - &face->face.remote_addr.v4, sizeof(ip4_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip6), - &face->face.remote_addr.v6, sizeof(ip6_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - - break; - } - case FACE_TYPE_UDP: { - vapi_msg_hicn_api_udp_tunnel_add_del *msg = NULL; - u32 uei = ~0; - - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr))) && - ip_address_is_v4((ip_address_t *)(&(face->face.local_addr)))) { - msg = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); - memcpy(msg->payload.src_addr.un.ip4, &face->face.local_addr.v4, - sizeof(ip4_address_t)); - msg->payload.src_addr.af = ADDRESS_IP4; - - memcpy(msg->payload.dst_addr.un.ip4, &face->face.remote_addr.v4, - sizeof(ip4_address_t)); - msg->payload.dst_addr.af = ADDRESS_IP4; - - } else if (!ip_address_is_v4( - (ip_address_t *)(&(route->face.face.remote_addr))) && - !ip_address_is_v4( - (ip_address_t *)(&(route->face.face.local_addr)))) { - msg = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); - memcpy(msg->payload.src_addr.un.ip6, &face->face.local_addr.v6, - sizeof(ip6_address_t)); - msg->payload.src_addr.af = ADDRESS_IP6; - - memcpy(msg->payload.dst_addr.un.ip6, &face->face.remote_addr.v6, - sizeof(ip6_address_t)); - msg->payload.dst_addr.af = ADDRESS_IP6; - } else { - // NOT IMPLEMENTED - ret = -1; - goto done; - } - - msg->payload.src_port = face->face.local_port; - msg->payload.dst_port = face->face.remote_port; - msg->payload.is_add = 1; - - int ret = vapi_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance, msg, - create_udp_tunnel_cb, &uei); - - if (ret) { - ERROR("Error in vapi_hicn_api_udp_tunnel_add_del"); - vapi_msg_free(s->g_vapi_ctx_instance, hicnp_msg); - goto done; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - hicnp_msg->payload.route.paths[0].nh.obj_id = uei; - - face->face.netdevice.index = uei; - - break; - } - default: - ret = -1; - goto done; - } - - ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, hicnp_msg, - parse_route_create, NULL); - - if (ret) { - ERROR("Error in vapi_ip_route_add_del"); - goto done; - } - - vapi_msg_hicn_api_enable_disable *msg = - vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); - - if (route->family == AF_INET) { - memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); - msg->payload.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); - msg->payload.prefix.address.af = ADDRESS_IP6; - } - - msg->payload.prefix.len = route->len; - msg->payload.enable_disable = 1; - - ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, - hicn_enable_cb, &face->id); - - if (ret) { - ERROR("Error in vapi_hicn_api_enable_disable"); - } - -done: - vapi_unlock(); - return ret; -} - -static int _hc_route_create(hc_sock_t *s, hc_route_t *route) { - return _hc_route_create_internal(s, route, false); -} - -static int _hc_route_create_async(hc_sock_t *s, hc_route_t *route) { - return _hc_route_create_internal(s, route, true); -} - -/* ROUTE DELETE */ -static vapi_error_e parse_route_delete( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_ip_route_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - return reply->retval; -} - -static vapi_error_e hicn_disable_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_enable_disable_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - return reply->retval; -} - -static int _hc_route_delete_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - if (!IS_VALID_FAMILY(route->family)) return -1; - - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - - vapi_lock(); - - vapi_msg_hicn_api_enable_disable *msg = - vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); - - if (route->family == AF_INET) { - memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); - msg->payload.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); - msg->payload.prefix.address.af = ADDRESS_IP6; - } - - msg->payload.prefix.len = route->len; - msg->payload.enable_disable = 0; - - vapi_error_e ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, - hicn_disable_cb, NULL); - - if (ret) { - ERROR("Error in vapi_hicn_api_enable_disable in route delete"); - goto done; - } - - vapi_msg_ip_route_add_del *hicnp_msg = - vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); - - hicnp_msg->payload.is_add = 0; - if (route->family == AF_INET) { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip4[0], - &route->remote_addr.v4, 4); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip6[0], - &route->remote_addr.v6, 16); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP6; - } - - hicnp_msg->payload.route.prefix.len = route->len; - - hicnp_msg->payload.route.paths[0].sw_if_index = ~0; - hicnp_msg->payload.route.paths[0].table_id = 0; - - hc_face_t *face = &(route->face); - switch (face->face.type) { - case FACE_TYPE_HICN: { - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr)))) { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip4), - &face->face.remote_addr.v4, sizeof(ip4_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip6), - &face->face.remote_addr.v6, sizeof(ip6_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - - break; - } - case FACE_TYPE_UDP: { - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - hicnp_msg->payload.route.paths[0].nh.obj_id = face->face.netdevice.index; - break; - } - default: - return -1; - } - - ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, hicnp_msg, - parse_route_delete, NULL); - - if (ret) { - ERROR("Error in vapi_ip_route_add_del in route delete"); - goto done; - } - -done: - - vapi_unlock(); - return ret; -} - -static int _hc_route_delete(hc_sock_t *s, hc_route_t *route) { - return _hc_route_delete_internal(s, route, false); -} - -static int _hc_route_delete_async(hc_sock_t *s, hc_route_t *route) { - return _hc_route_delete_internal(s, route, true); -} - -static vapi_error_e parse_udp_encap_list( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_udp_encap_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hc_face_t *face = (hc_face_t *)callback_ctx; - - if (face->face.netdevice.index == reply->udp_encap.id) { - switch (reply->udp_encap.src_ip.af) { - case ADDRESS_IP4: { - memcpy(&face->face.local_addr.v4, &(reply->udp_encap.src_ip.un.ip4), - sizeof(ip4_address_t)); - memcpy(&face->face.remote_addr.v4, &(reply->udp_encap.dst_ip.un.ip4), - sizeof(ip4_address_t)); - break; - } - case ADDRESS_IP6: { - memcpy(&face->face.local_addr.v6, &(reply->udp_encap.src_ip.un.ip6), - sizeof(ip6_address_t)); - memcpy(&face->face.remote_addr.v6, &(reply->udp_encap.dst_ip.un.ip6), - sizeof(ip6_address_t)); - break; - } - default: - break; - } - - face->face.local_port = reply->udp_encap.src_port; - face->face.remote_port = reply->udp_encap.dst_port; - } - return rv; -} - -static int _fill_face_with_info(hc_face_t *face, vapi_type_fib_path *path, - hc_sock_t *s) { - switch (path->type) { - case FIB_API_PATH_FLAG_NONE: { - face->face.type = FACE_TYPE_HICN; - switch (path->proto) { - case FIB_API_PATH_NH_PROTO_IP4: - memcpy(&face->face.remote_addr.v4, &(path->nh.address.ip4), - sizeof(ip4_address_t)); - break; - case FIB_API_PATH_NH_PROTO_IP6: - memcpy(&face->face.remote_addr.v6, &(path->nh.address.ip6), - sizeof(ip6_address_t)); - break; - default: - break; - } - face->face.netdevice.index = path->sw_if_index; - } break; - case FIB_API_PATH_TYPE_UDP_ENCAP: { - face->face.type = FACE_TYPE_UDP; - face->face.netdevice.index = clib_net_to_host_u32(path->nh.obj_id); - // Let's make the compiler happy - (void)parse_udp_encap_list; - // vapi_msg_udp_encap_dump *msg; - // msg = vapi_alloc_udp_encap_dump(s->g_vapi_ctx_instance); - // vapi_udp_encap_dump(s->g_vapi_ctx_instance, msg, parse_udp_encap_list, - // face); - } break; - default: - return -1; - } - return 0; -} - -/* ROUTE LIST */ -typedef struct hicn_route_socket_s { - hc_data_t *data; - hc_sock_t *s; -} hicn_route_socket_t; - -static vapi_error_e parse_route_list(vapi_ctx_t ctx, void *callback_ctx, - vapi_error_e rv, bool is_last, - vapi_payload_ip_route_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hicn_route_socket_t *rs = (hicn_route_socket_t *)callback_ctx; - hc_data_t *data = rs->data; - - u8 found = false; - for (int j = 0; j < reply->route.n_paths; j++) { - for (int i = 0; i < data->size && !found; i++) { - hc_route_t *route = &((hc_route_t *)(data->buffer))[i]; - - if (ip_address_is_v4((ip_address_t *)&(route->remote_addr)) && - memcmp(route->remote_addr.v4.as_u8, - reply->route.prefix.address.un.ip4, - sizeof(ip4_address_t)) == 0 && - route->len == reply->route.prefix.len && route->face_id == ~0) { - _fill_face_with_info(&(route->face), &reply->route.paths[j], rs->s); - found = true; - } else if (memcmp(route->remote_addr.v6.as_u8, - reply->route.prefix.address.un.ip6, - sizeof(ip6_address_t)) == 0 && - route->len == reply->route.prefix.len && - route->face_id == ~0) { - _fill_face_with_info(&(route->face), &reply->route.paths[j], rs->s); - found = true; - } - } - } - - return rv; -} - -static vapi_error_e parse_hicn_route_list( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_routes_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hc_data_t *data = (hc_data_t *)callback_ctx; - - int empty_spots = data->size - data->current; - if (empty_spots < reply->nfaces) { - int new_size = data->size + (reply->nfaces - empty_spots); - data->buffer = realloc(data->buffer, sizeof(hc_route_t) * (new_size)); - if (!data->buffer) return VAPI_ENOMEM; - - data->size = new_size; - } - - for (int i = 0; i < reply->nfaces; i++) { - hc_route_t *route = &((hc_route_t *)(data->buffer))[data->current]; - route->face_id = ~0; - route->cost = 1; - route->len = reply->prefix.len; - if (reply->prefix.address.af == ADDRESS_IP6) { - memcpy(route->remote_addr.v6.as_u8, reply->prefix.address.un.ip6, 16); - } else { - memcpy(route->remote_addr.v4.as_u8, reply->prefix.address.un.ip4, 4); - } - route->family = - reply->prefix.address.af == ADDRESS_IP6 ? AF_INET6 : AF_INET; - data->current++; - } - - return rv; -} - -static int _hc_route_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - vapi_lock(); - - vapi_msg_hicn_api_routes_dump *msg; - msg = vapi_alloc_hicn_api_routes_dump(s->g_vapi_ctx_instance); - - hc_data_t *data = hc_data_create(0, sizeof(hc_route_t), NULL); - int ret = VAPI_OK; - - if (!data) { - ret = -1; - goto err; - } - - data->buffer = malloc(sizeof(hc_route_t)); - data->size = 1; - - if (!data->buffer) { - ret = -1; - goto err_free; - } - - ret = vapi_hicn_api_routes_dump(s->g_vapi_ctx_instance, msg, - parse_hicn_route_list, data); - - if (ret != VAPI_OK) goto err_free; - - vapi_msg_ip_route_dump *hicnp_msg; - hicnp_msg = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); - hicnp_msg->payload.table.table_id = 0; - hicnp_msg->payload.table.is_ip6 = 1; - - hicn_route_socket_t ctx = { - .data = data, - .s = socket, - }; - - ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, hicnp_msg, parse_route_list, - &ctx); - - hicnp_msg = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); - hicnp_msg->payload.table.table_id = 0; - hicnp_msg->payload.table.is_ip6 = 0; - - ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, hicnp_msg, parse_route_list, - &ctx); - - if (ret != VAPI_OK) goto err_free; - - *pdata = data; - - vapi_unlock(); - return ret; - -err_free: - free(data); -err: - vapi_unlock(); - return ret; -} - -static int _hc_route_list(hc_sock_t *s, hc_data_t **pdata) { - return _hc_route_list_internal(s, pdata, false); -} - -static int _hc_route_list_async(hc_sock_t *s) { - return _hc_route_list_internal(s, NULL, true); -} - -/*----------------------------------------------------------------------------* - * Face - * - * Face support is not directly available in hicn-light, but we can offer such - * an interface through a combination of listeners and connections. The code - * starts with some conversion functions between faces/listeners/connections. - * - * We also need to make sure that there always exist a (single) listener when a - * connection is created, and in the hICN face case, that there is a single - * connection attached to this listener. - * - *----------------------------------------------------------------------------*/ - -static int _hc_face_create(hc_sock_t *socket, hc_face_t *face) { - ERROR("Face creation not implemented."); - return -1; -} - -static int _hc_face_get(hc_sock_t *socket, hc_face_t *face, - hc_face_t **face_found) { - ERROR("Face deletion not implemented."); - return -1; -} - -static int _hc_face_delete(hc_sock_t *s, hc_face_t *face, - uint8_t delete_listener) { - ERROR("Face deletion not implemented."); - return -1; -} - -/* FACE LIST */ - -static int _hc_face_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("Face list not implemented."); - return -1; -} - -static int _hc_face_list_async(hc_sock_t *s) { return 0; } - -static int _hc_face_set_admin_state( - hc_sock_t *s, const char *conn_id_or_name, // XXX wrong identifier - face_state_t admin_state) { - return 0; -} - -#ifdef WITH_POLICY -static int _hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - ERROR("Face set priority not implemented."); - return -1; -} - -static int _hc_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - ERROR("Face set tags not implemented."); - return -1; -} -#endif // WITH_POLICY - -/*----------------------------------------------------------------------------* - * Punting - *----------------------------------------------------------------------------*/ - -static int _hc_punting_create_internal(hc_sock_t *s, hc_punting_t *punting, - bool async) { - return -1; -} - -static int _hc_punting_create(hc_sock_t *s, hc_punting_t *punting) { - return _hc_punting_create_internal(s, punting, false); -} - -static int _hc_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { - return _hc_punting_create_internal(s, punting, true); -} - -static int _hc_punting_get(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found) { - ERROR("hc_punting_get not (yet) implemented."); - return -1; -} - -static int _hc_punting_delete(hc_sock_t *s, hc_punting_t *punting) { - ERROR("hc_punting_delete not (yet) implemented."); - return -1; -} - -static int _hc_punting_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("hc_punting_list not (yet) implemented."); - return -1; -} - -/*----------------------------------------------------------------------------* - * Cache - *----------------------------------------------------------------------------*/ - -static int _hc_cache_set_store_internal(hc_sock_t *s, hc_cache_t *cache, - bool async) { - return 0; -} - -static int _hc_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_store_internal(s, cache, false); -} - -static int _hc_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_store_internal(s, cache, true); -} - -static int _hc_cache_set_serve_internal(hc_sock_t *s, hc_cache_t *cache, - bool async) { - return 0; -} - -static int _hc_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_serve_internal(s, cache, false); -} - -static int _hc_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_serve_internal(s, cache, true); -} - -/*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -// per prefix -static int _hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { return 0; } - -static int _hc_strategy_add_local_prefix(hc_sock_t *s, - hc_strategy_t *strategy) { - return 0; -} - -static int _hc_strategy_list(hc_sock_t *s, hc_data_t **data) { return 0; } - -/*----------------------------------------------------------------------------* - * WLDR - *----------------------------------------------------------------------------*/ - -// per connection -static int _hc_wldr_set(hc_sock_t *s /* XXX */) { return 0; } - -/*----------------------------------------------------------------------------* - * MAP-Me - *----------------------------------------------------------------------------*/ - -static int _hc_mapme_set(hc_sock_t *s, int enabled) { return 0; } - -static int _hc_mapme_set_discovery(hc_sock_t *s, int enabled) { return 0; } - -static int _hc_mapme_set_timescale(hc_sock_t *s, uint32_t timescale) { - return 0; -} - -static int _hc_mapme_set_retx(hc_sock_t *s, uint32_t timescale) { return 0; } - -/*----------------------------------------------------------------------------* - * Policy - *----------------------------------------------------------------------------*/ - -#ifdef WITH_POLICY - -/* POLICY CREATE */ - -static int _hc_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - return -1; -} - -static int _hc_policy_create(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_create_internal(s, policy, false); -} - -static int _hc_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_create_internal(s, policy, true); -} - -/* POLICY DELETE */ - -static int _hc_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - return -1; -} - -static int _hc_policy_delete(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_delete_internal(s, policy, false); -} - -static int _hc_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_delete_internal(s, policy, true); -} - -/* POLICY LIST */ - -static int _hc_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - return -1; -} - -static int _hc_policy_list(hc_sock_t *s, hc_data_t **pdata) { - return _hc_policy_list_internal(s, pdata, false); -} - -static int _hc_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { - return _hc_policy_list_internal(s, pdata, true); -} - -#endif /* WITH_POLICY */ - -/*----------------------------------------------------------------------------* - * Configuration - *----------------------------------------------------------------------------*/ - -typedef struct hc_result_s { - void *_; -} hc_result_t; - -static hc_result_t *_hc_listener_create_conf(hc_sock_t *s, - hc_listener_t *listener) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_route_create_conf(hc_sock_t *s, hc_route_t *route) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_strategy_set_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_strategy_add_local_prefix_conf( - hc_sock_t *s, hc_strategy_t *strategy) { - ERROR("Not implemented."); - return NULL; -} - -hc_msg_t *_hc_result_get_msg(hc_result_t *result) { - ERROR("Not implemented."); - return NULL; -} -int _hc_result_get_cmd_id(hc_result_t *result) { - ERROR("Not implemented."); - return -1; -} -bool _hc_result_get_success(hc_result_t *result) { - ERROR("Not implemented."); - return false; -} -void _hc_result_free(hc_result_t *result) { free(result); } - -static hc_sock_t hc_sock_vpp_interface = (hc_sock_t){ - .hc_sock_get_next_seq = _hc_sock_vpp_get_next_seq, - .hc_sock_set_nonblocking = _hc_sock_vpp_set_nonblocking, - .hc_sock_get_fd = _hc_sock_vpp_get_fd, - .hc_sock_connect = _hc_sock_vpp_connect, - .hc_sock_get_available = _hc_sock_vpp_get_available, - .hc_sock_send = _hc_sock_vpp_send, - .hc_sock_recv = _hc_sock_vpp_recv, - .hc_sock_process = _hc_sock_vpp_process, - .hc_sock_callback = _hc_sock_vpp_callback, - .hc_sock_reset = _hc_sock_vpp_reset, - .hc_sock_free = _hc_sock_vpp_free, - .hc_listener_create = _hc_listener_create, - .hc_listener_create_async = _hc_listener_create_async, - .hc_listener_get = _hc_listener_get, - .hc_listener_delete = _hc_listener_delete, - .hc_listener_delete_async = _hc_listener_delete_async, - .hc_listener_list = _hc_listener_list, - .hc_listener_list_async = _hc_listener_list_async, - .hc_connection_create = _hc_connection_create, - .hc_connection_create_async = _hc_connection_create_async, - .hc_connection_get = _hc_connection_get, - .hc_connection_update_by_id = _hc_connection_update_by_id, - .hc_connection_update = _hc_connection_update, - .hc_connection_delete = _hc_connection_delete, - .hc_connection_delete_async = _hc_connection_delete_async, - .hc_connection_list = _hc_connection_list, - .hc_connection_list_async = _hc_connection_list_async, - .hc_connection_set_admin_state = _hc_connection_set_admin_state, - .hc_connection_set_admin_state_async = _hc_connection_set_admin_state_async, - -#ifdef WITH_POLICY - .hc_connection_set_priority = _hc_connection_set_priority, - .hc_connection_set_priority_async = _hc_connection_set_priority_async, - .hc_connection_set_tags = _hc_connection_set_tags, - .hc_connection_set_tags_async = _hc_connection_set_tags_async, -#endif // WITH_POLICY - - .hc_face_create = _hc_face_create, - .hc_face_get = _hc_face_get, - .hc_face_delete = _hc_face_delete, - .hc_face_list = _hc_face_list, - .hc_face_list_async = _hc_face_list_async, - .hc_face_set_admin_state = _hc_face_set_admin_state, - -#ifdef WITH_POLICY - .hc_face_set_priority = _hc_face_set_priority, - .hc_face_set_tags = _hc_face_set_tags, -#endif // WITH_POLICY - - .hc_route_create = _hc_route_create, - .hc_route_create_async = _hc_route_create_async, - .hc_route_delete = _hc_route_delete, - .hc_route_delete_async = _hc_route_delete_async, - .hc_route_list = _hc_route_list, - .hc_route_list_async = _hc_route_list_async, - - .hc_punting_create = _hc_punting_create, - .hc_punting_create_async = _hc_punting_create_async, - .hc_punting_get = _hc_punting_get, - .hc_punting_delete = _hc_punting_delete, - .hc_punting_list = _hc_punting_list, - - .hc_cache_set_store = _hc_cache_set_store, - .hc_cache_set_store_async = _hc_cache_set_store_async, - .hc_cache_set_serve = _hc_cache_set_serve, - .hc_cache_set_serve_async = _hc_cache_set_serve_async, - - .hc_strategy_list = _hc_strategy_list, - .hc_strategy_set = _hc_strategy_set, - .hc_strategy_add_local_prefix = _hc_strategy_add_local_prefix, - .hc_wldr_set = _hc_wldr_set, - - .hc_mapme_set = _hc_mapme_set, - .hc_mapme_set_discovery = _hc_mapme_set_discovery, - .hc_mapme_set_timescale = _hc_mapme_set_timescale, - .hc_mapme_set_retx = _hc_mapme_set_retx, - -#ifdef WITH_POLICY - .hc_policy_create = _hc_policy_create, - .hc_policy_create_async = _hc_policy_create_async, - .hc_policy_delete = _hc_policy_delete, - .hc_policy_delete_async = _hc_policy_delete_async, - .hc_policy_list = _hc_policy_list, - .hc_policy_list_async = _hc_policy_list_async, -#endif // WITH_POLICY - - .hc_listener_create_conf = _hc_listener_create_conf, - .hc_connection_create_conf = _hc_connection_create_conf, - .hc_route_create_conf = _hc_route_create_conf, - .hc_strategy_set_conf = _hc_strategy_set_conf, - .hc_strategy_add_local_prefix_conf = _hc_strategy_add_local_prefix_conf, - - .hc_result_get_msg = _hc_result_get_msg, - .hc_result_get_cmd_id = _hc_result_get_cmd_id, - .hc_result_get_success = _hc_result_get_success, - .hc_result_free = _hc_result_free, -}; - -hc_sock_t *_hc_sock_create_url(const char *url) { - if (url) { - // NOT IMPLEMENTED - return NULL; - } - - hc_sock_vpp_t *s = malloc(sizeof(hc_sock_vpp_t)); - - if (!s) goto ERR_SOCK; - - memset(s, 0, sizeof(hc_sock_vpp_t)); - - s->vft = hc_sock_vpp_interface; - - // By default the socket is blocking -- not async - s->async = 0; - - return (hc_sock_t *)(s); - -ERR_SOCK: - return NULL; -} diff --git a/ctrl/libhicnctrl/src/object.c b/ctrl/libhicnctrl/src/object.c new file mode 100644 index 000000000..34f21509a --- /dev/null +++ b/ctrl/libhicnctrl/src/object.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file object.c + * \brief Implementation of API object representation. + */ + +#include <hicn/ctrl/action.h> +#include <hicn/ctrl/object.h> + +#include "objects/base.h" // iszero +#include "object_vft.h" + +bool hc_object_is_empty(const hc_object_t *object) { + return iszero(object, sizeof(hc_object_t)); +} + +int hc_object_validate(hc_object_type_t object_type, hc_object_t *object, + bool allow_partial) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->validate(object, allow_partial); +} + +int hc_object_cmp(hc_object_type_t object_type, hc_object_t *object1, + hc_object_t *object2) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->cmp(object1, object2); +} + +int hc_object_snprintf(char *s, size_t size, hc_object_type_t object_type, + hc_object_t *object) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->obj_snprintf(s, size, object); +} + +size_t hc_object_size(hc_object_type_t object_type) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->size; +} diff --git a/ctrl/libhicnctrl/src/object_private.h b/ctrl/libhicnctrl/src/object_private.h new file mode 100644 index 000000000..2be12fd5e --- /dev/null +++ b/ctrl/libhicnctrl/src/object_private.h @@ -0,0 +1,35 @@ +#ifndef HICNCTRL_OBJECT_PRIVATE_H +#define HICNCTRL_OBJECT_PRIVATE_H + +#define INT_CMP(x, y) ((x > y) ? 1 : (x < y) ? -1 : 0) + +// XXX Those are always true +#define IS_VALID_ADDRESS(x) (1) +#define IS_VALID_CONNECTION_ID(x) (1) // XXX ID +#define IS_VALID_ROUTE_COST(x) (1) +#define IS_VALID_PREFIX_LEN(x) (1) +#define IS_VALID_POLICY(x) (1) +#define IS_VALID_ID(x) (1) +#define IS_VALID_INTERFACE_NAME(x) (1) +#define IS_VALID_NAME(x) (1) +#define IS_VALID_TYPE(x) IS_VALID_ENUM_TYPE(FACE_TYPE, x) +#define IS_VALID_FACE_STATE(x) (1) + +#define IS_VALID_ADDR_TYPE(x) ((x >= ADDR_INET) && (x <= ADDR_UNIX)) + +#define IS_VALID_CONNECTION_TYPE(x) IS_VALID_ENUM_TYPE(CONNECTION_TYPE, x) + +#define GENERATE_FIND(TYPE) \ + int hc_##TYPE##_find(hc_data_t *data, const hc_##TYPE##_t *element, \ + hc_##TYPE##_t **found) { \ + foreach_type(hc_##TYPE##_t, x, data) { \ + if (hc_##TYPE##_cmp(x, element) == 0) { \ + *found = x; \ + return 0; \ + } \ + }; \ + *found = NULL; /* this is optional */ \ + return 0; \ + } + +#endif /* HICNCTRL_OBJECT_PRIVATE_H */ diff --git a/ctrl/libhicnctrl/src/object_type.c b/ctrl/libhicnctrl/src/object_type.c new file mode 100644 index 000000000..0303fce75 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_type.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file object_type.c + * \brief Implementation of object type. + */ + +#include <strings.h> + +#include <hicn/ctrl/object_type.h> + +const char *object_type_str[] = { +#define _(x) [OBJECT_TYPE_##x] = #x, + foreach_object_type +#undef _ +}; + +hc_object_type_t object_type_from_str(const char *object_str) { +#define _(x) \ + if (strcasecmp(object_str, #x) == 0) \ + return OBJECT_TYPE_##x; \ + else + foreach_object_type +#undef _ + return OBJECT_TYPE_UNDEFINED; +} diff --git a/ctrl/libhicnctrl/src/object_vft.c b/ctrl/libhicnctrl/src/object_vft.c new file mode 100644 index 000000000..808c0f913 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_vft.c @@ -0,0 +1,19 @@ +#include "object_vft.h" + +#include "objects/listener.h" +#include "objects/connection.h" +#include "objects/route.h" +#include "objects/face.h" +#include "objects/strategy.h" +#include "objects/subscription.h" +#include "objects/active_interface.h" + +const hc_object_ops_t *object_vft[] = { + [OBJECT_TYPE_LISTENER] = &hc_listener_ops, + [OBJECT_TYPE_CONNECTION] = &hc_connection_ops, + [OBJECT_TYPE_ROUTE] = &hc_route_ops, + [OBJECT_TYPE_FACE] = &hc_face_ops, + [OBJECT_TYPE_STRATEGY] = &hc_strategy_ops, + [OBJECT_TYPE_SUBSCRIPTION] = &hc_subscription_ops, + [OBJECT_TYPE_ACTIVE_INTERFACE] = &hc_active_interface_ops, +}; diff --git a/ctrl/libhicnctrl/src/object_vft.h b/ctrl/libhicnctrl/src/object_vft.h new file mode 100644 index 000000000..e27fe5d36 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_vft.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file object_vft.h + * \brief Object VFT. + */ + +#ifndef HICNCTRL_OBJECT_VFT +#define HICNCTRL_OBJECT_VFT + +#include <stddef.h> // size_t +#include <hicn/ctrl/object_type.h> +#include <hicn/ctrl/object.h> + +typedef struct { + hc_object_type_t object_type; + const char *object_name; + size_t size; + int (*validate)(const hc_object_t *object, bool allow_partial); + int (*cmp)(const hc_object_t *object1, const hc_object_t *object2); + /* cannot be named snprintf as it collides with a macro in clang/iOS */ + int (*obj_snprintf)(char *s, size_t size, const hc_object_t *object); +} hc_object_ops_t; + +#define DECLARE_OBJECT_OPS_H(TYPE, NAME) \ + extern const hc_object_ops_t hc_##NAME##_ops; + +#define DECLARE_OBJECT_OPS(TYPE, NAME) \ + const hc_object_ops_t hc_##NAME##_ops = { \ + .object_type = TYPE, \ + .object_name = #NAME, \ + .size = sizeof(hc_##NAME##_t), \ + .validate = _hc_##NAME##_validate, \ + .cmp = _hc_##NAME##_cmp, \ + .obj_snprintf = _hc_##NAME##_snprintf, \ + }; + +extern const hc_object_ops_t *object_vft[]; + +#endif /* HICNCTRL_OBJECT_VFT */ diff --git a/ctrl/libhicnctrl/src/objects/active_interface.c b/ctrl/libhicnctrl/src/objects/active_interface.c new file mode 100644 index 000000000..796123963 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/active_interface.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file active_interface.c + * \brief Implementation of active_interface object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/active_interface.h> +#include <hicn/util/log.h> +#include <hicn/util/ip_address.h> + +#include "../object_private.h" +#include "../object_vft.h" + +/* ACTIVE_INTERFACE VALIDATE */ + +int hc_active_interface_validate(const hc_active_interface_t *active_interface, + bool allow_partial) { + return 0; // XXX TODO +} + +int _hc_active_interface_validate(const hc_object_t *object, + bool allow_partial) { + return hc_active_interface_validate(&object->active_interface, allow_partial); +} + +/* ACTIVE_INTERFACE CMP */ + +// XXX TODO +int hc_active_interface_cmp(const hc_active_interface_t *active_interface1, + const hc_active_interface_t *active_interface2) { + return -1; +} + +int _hc_active_interface_cmp(const hc_object_t *object1, + const hc_object_t *object2) { + return hc_active_interface_cmp(&object1->active_interface, + &object2->active_interface); +} + +/* ACTIVE_INTERFACE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_active_interface_snprintf( + char *s, size_t size, const hc_active_interface_t *active_interface) { + int rc; + char *pos = s; + + rc = hicn_ip_prefix_snprintf(pos, size, &active_interface->prefix); + if ((rc < 0) || (rc >= size)) return rc; + pos += rc; + size -= rc; + + for (netdevice_type_t type = NETDEVICE_TYPE_UNDEFINED + 1; + type < NETDEVICE_TYPE_N; type++) { + if (!netdevice_flags_has(active_interface->interface_types, type)) continue; + rc = snprintf(pos, size, " %s", netdevice_type_str(type)); + if ((rc < 0) || (rc >= size)) return (int)(pos - s + rc); + + pos += rc; + size -= rc; + } + return (int)(pos - s); +} + +int _hc_active_interface_snprintf(char *s, size_t size, + const hc_object_t *object) { + return hc_active_interface_snprintf(s, size, &object->active_interface); +} + +DECLARE_OBJECT_OPS(OBJECT_TYPE_ACTIVE_INTERFACE, active_interface); diff --git a/ctrl/libhicnctrl/src/objects/active_interface.h b/ctrl/libhicnctrl/src/objects/active_interface.h new file mode 100644 index 000000000..973b08e40 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/active_interface.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file active_interface.h + * \brief Route. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H +#define HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_ACTIVE_INTERFACE, active_interface); + +#endif /* HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H */ diff --git a/ctrl/libhicnctrl/src/objects/base.c b/ctrl/libhicnctrl/src/objects/base.c new file mode 100644 index 000000000..86e4bfb72 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/base.c @@ -0,0 +1,34 @@ +/* + * 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. + */ + +/** + * \file base.c + * \brief Implementation of base functions for object APIs. + */ + +#include <stdbool.h> + +#include "base.h" + +#include <hicn/util/log.h> + +bool iszero(const void *ptr, int bytes) { + char *bptr = (char *)ptr; + while (bytes--) + if (*bptr++) return false; + return true; +} + +bool isempty(const char *str) { return str[0] == '\0'; } diff --git a/ctrl/libhicnctrl/src/objects/base.h b/ctrl/libhicnctrl/src/objects/base.h new file mode 100644 index 000000000..eb85483e9 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/base.h @@ -0,0 +1,27 @@ +/* + * 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. + */ + +/** + * \file base.h + * \brief Base functions for object APIs. + */ + +#ifndef HICNCTRL_OBJECTS_BASE_H +#define HICNCTRL_OBJECTS_BASE_H + +bool iszero(const void *ptr, int bytes); +bool isempty(const char *str); + +#endif /* HICNCTRL_OBJECTS_BASE_H */ diff --git a/ctrl/libhicnctrl/src/objects/connection.c b/ctrl/libhicnctrl/src/objects/connection.c new file mode 100644 index 000000000..14e763396 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/connection.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file connection.c + * \brief Implementation of connection object. + */ + +#include <assert.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/connection.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" +#include "base.h" + +bool hc_connection_is_local(const hc_connection_t *connection) { + return (strncmp(connection->interface_name, "lo", INTERFACE_LEN) == 0); +} + +bool hc_connection_has_local(const hc_connection_t *connection) { + assert(connection); + return IS_VALID_PORT(connection->local_port) && + IS_VALID_ADDRESS(connection->local_addr); +} + +/* CONNECTION VALIDATE */ + +int hc_connection_validate(const hc_connection_t *connection, + bool allow_partial) { + int has_id = 0; + int has_name = 0; + int has_interface_name = 0; + int has_netdevice_type = 0; + int has_type = 0; + int has_family = 0; + int has_local_addr = 0; + int has_local_port = 0; + int has_remote_addr = 0; + int has_remote_port = 0; + int has_admin_state = 0; + int has_priority = 0; + int has_tags = 0; + int has_state = 0; + + if (connection->id == ~0) { + ERROR("[hc_listener_validate] Invalid id specified"); + return -1; + } + has_id = 1; + + if (!isempty(connection->name)) { + if (!IS_VALID_NAME(connection->name)) { + ERROR("[hc_connection_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (!isempty(connection->interface_name)) { + if (!IS_VALID_INTERFACE_NAME(connection->interface_name)) { + ERROR("[hc_connection_validate] Invalid interface_name specified"); + return -1; + } + has_interface_name = 1; + } + + if (connection->type != FACE_TYPE_UNDEFINED) { + if (!IS_VALID_TYPE(connection->type)) { + ERROR("[hc_connection_validate] Invalid type specified"); + return -1; + } + has_type = 1; + } + + if (connection->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(connection->family)) { + ERROR("[hc_connection_validate] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&connection->local_addr)) { + if (!IS_VALID_ADDRESS(connection->local_addr)) { + ERROR("[hc_connection_validate] Invalid local_addr specified"); + return -1; + } + has_local_addr = 1; + } + + if (connection->local_port != 0) { + if (!IS_VALID_PORT(connection->local_port)) { + ERROR("[hc_connection_validate] Invalid local_port specified"); + return -1; + } + has_local_port = 1; + } + + if (!hicn_ip_address_empty(&connection->remote_addr)) { + if (!IS_VALID_ADDRESS(connection->remote_addr)) { + ERROR("[hc_connection_validate] Invalid remote_addr specified"); + return -1; + } + has_remote_addr = 1; + } + + if (connection->remote_port != 0) { + if (!IS_VALID_PORT(connection->remote_port)) { + ERROR("[hc_connection_validate] Invalid remote_port specified"); + return -1; + } + has_remote_port = 1; + } + + int has_key = has_id || has_name; + int has_mandatory_attributes = has_interface_name && has_type && has_family && + has_local_addr && has_local_port && + has_remote_addr && has_remote_port; + int has_optional_attributes = + has_netdevice_type && has_admin_state && has_state; + has_optional_attributes = has_optional_attributes && has_priority && has_tags; + + if (allow_partial) { + if (has_key && !has_mandatory_attributes && !has_optional_attributes) + return 0; + else if (has_mandatory_attributes) + return 0; + else + return -1; + } else { + if (has_key && has_mandatory_attributes) return 0; + return -1; + } +} + +int _hc_connection_validate(const hc_object_t *object, bool allow_partial) { + return hc_connection_validate(&object->connection, allow_partial); +} + +/* CONNECTION CMP */ + +/* + * hICN light uses ports even for hICN connections, but their value is + * ignored. As connections are specific to hicn-light, we can safely use IP + * and ports for comparison independently of the face type. + */ +int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2) { + int rc; + + rc = INT_CMP(c1->type, c2->type); + if (rc != 0) return rc; + + rc = INT_CMP(c1->family, c2->family); + if (rc != 0) return rc; + + rc = strncmp(c1->interface_name, c2->interface_name, INTERFACE_LEN); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&c1->local_addr, &c2->local_addr); + if (rc != 0) return rc; + + rc = INT_CMP(c1->local_port, c2->local_port); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&c1->remote_addr, &c2->remote_addr); + if (rc != 0) return rc; + + rc = INT_CMP(c1->remote_port, c2->remote_port); + if (rc != 0) return rc; + + return rc; +} + +int _hc_connection_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_connection_cmp(&object1->connection, &object2->connection); +} + +/* CONNECTION SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_connection_snprintf(char *s, size_t size, + const hc_connection_t *connection) { + char local[MAXSZ_URL]; + char remote[MAXSZ_URL]; + int rc; + + // assert(connection->connection_state) + if (strcmp(connection->name, "SELF") == 0) { + return snprintf(s, size, "%s", connection->name); + } + + rc = url_snprintf(local, MAXSZ_URL, &connection->local_addr, + connection->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = url_snprintf(remote, MAXSZ_URL, &connection->remote_addr, + connection->remote_port); + if (rc >= MAXSZ_URL) + WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + + return snprintf(s, size, "%s %s %s %s", connection->name, local, remote, + face_type_str(connection->type)); +} + +int _hc_connection_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_connection_snprintf(s, size, &object->connection); +} + +int hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_get(hc_sock_t *s, hc_connection_t *connection, + hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_CONNECTION, &object, pdata); +} + +int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_CONNECTION, NULL, pdata); +} + +int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, + face_state_t state) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.admin_state = state; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, + uint32_t priority) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.priority = priority; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, + + policy_tags_t tags) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.tags = tags; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +GENERATE_FIND(connection); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_CONNECTION, connection); diff --git a/ctrl/libhicnctrl/src/objects/connection.h b/ctrl/libhicnctrl/src/objects/connection.h new file mode 100644 index 000000000..4a4c78f09 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/connection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file connection.h + * \brief Connection. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_CONNECTION_H +#define HICNCTRL_IMPL_OBJECTS_CONNECTION_H + +#include "../object_vft.h" + +bool hc_connection_is_local(const hc_connection_t *connection); +bool hc_connection_has_local(const hc_connection_t *connection); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_CONNECTION, connection); + +#endif /* HICNCTRL_IMPL_OBJECTS_CONNECTION_H */ diff --git a/ctrl/libhicnctrl/src/objects/face.c b/ctrl/libhicnctrl/src/objects/face.c new file mode 100644 index 000000000..c535ff4c5 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/face.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file face.c + * \brief Implementation of face object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/face.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" + +bool hc_face_has_netdevice(const hc_face_t *face) { + return netdevice_is_empty(&face->netdevice); +} + +/* FACE VALIDATE */ + +int hc_face_validate(const hc_face_t *face, bool allow_partial) { + if ((!allow_partial || !hc_face_has_netdevice(face)) && + !IS_VALID_INTERFACE_NAME(face->interface_name)) { + ERROR("[hc_face_validate] Invalid interface_name specified"); + return -1; + } + if (!IS_VALID_TYPE(face->type)) { + ERROR("[hc_face_validate] Invalid type specified"); + return -1; + } + if ((!allow_partial || face->family != AF_UNSPEC) && + !IS_VALID_FAMILY(face->family)) { + ERROR("[hc_face_validate] Invalid family specified"); + return -1; + } + if ((!allow_partial || !hicn_ip_address_empty(&face->local_addr)) && + !IS_VALID_ADDRESS(face->local_addr)) { + ERROR("[hc_face_validate] Invalid local_addr specified"); + return -1; + } + if ((!allow_partial || !(face->local_port == 0)) && + !IS_VALID_PORT(face->local_port)) { + ERROR("[hc_face_validate] Invalid local_port specified"); + return -1; + } + if (!IS_VALID_ADDRESS(face->remote_addr)) { + ERROR("[hc_face_validate] Invalid remote_addr specified"); + return -1; + } + if (!IS_VALID_PORT(face->remote_port)) { + ERROR("[hc_face_validate] Invalid remote_port specified"); + return -1; + } + return 0; +} + +int _hc_face_validate(const hc_object_t *object, bool allow_partial) { + return hc_face_validate(&object->face, allow_partial); +} + +/* FACE CMP */ + +int hc_face_cmp(const hc_face_t *c1, const hc_face_t *c2) { + return -99; // Not implemented +} + +int _hc_face_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_face_cmp(&object1->face, &object2->face); +} + +/* FACE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_face_snprintf(char *s, size_t size, const hc_face_t *face) { + /* URLs are also big enough to contain IP addresses in the hICN case */ + char local[MAXSZ_URL]; + char remote[MAXSZ_URL]; + char tags[MAXSZ_POLICY_TAGS]; + int rc; + + switch (face->type) { + case FACE_TYPE_HICN: + case FACE_TYPE_HICN_LISTENER: + rc = hicn_ip_address_snprintf(local, MAXSZ_URL, &face->local_addr); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = hicn_ip_address_snprintf(remote, MAXSZ_URL, &face->remote_addr); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + break; + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + rc = url_snprintf(local, MAXSZ_URL, &face->local_addr, face->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = url_snprintf(remote, MAXSZ_URL, &face->remote_addr, + face->remote_port); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + break; + default: + return -1; + } + + // [#ID NAME] TYPE LOCAL_URL REMOTE_URL STATE/ADMIN_STATE (TAGS) + rc = policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags); + if (rc >= MAXSZ_POLICY_TAGS) + WARN("[hc_face_snprintf] Unexpected truncation of policy tags string"); + if (rc < 0) return rc; + + return snprintf( + s, size, "[#%d %s] %s %s %s %s %s/%s [%d] (%s)", face->id, face->name, + face->netdevice.index != NETDEVICE_UNDEFINED_INDEX ? face->netdevice.name + : "*", + face_type_str(face->type), local, remote, face_state_str(face->state), + face_state_str(face->admin_state), face->priority, tags); +} + +int _hc_face_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_face_snprintf(s, size, &object->face); +} + +int hc_face_create(hc_sock_t *s, hc_face_t *face) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_FACE, &object, NULL); +} + +int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_FACE, &object, pdata); +} + +int hc_face_delete(hc_sock_t *s, hc_face_t *face) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_FACE, &object, NULL); +} + +int hc_face_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_FACE, NULL, pdata); +} + +int hc_face_list_async(hc_sock_t *s) { + return hc_execute_async(s, ACTION_LIST, OBJECT_TYPE_FACE, NULL, NULL, NULL); +} + +GENERATE_FIND(face); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_FACE, face); diff --git a/ctrl/libhicnctrl/src/objects/face.h b/ctrl/libhicnctrl/src/objects/face.h new file mode 100644 index 000000000..08f90f195 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/face.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file face.h + * \brief Face. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_FACE_H +#define HICNCTRL_IMPL_OBJECTS_FACE_H + +#include "../object_vft.h" + +int hc_face_validate(const hc_face_t *face, bool allow_partial); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_FACE, face); + +#endif /* HICNCTRL_IMPL_OBJECTS_FACE_H */ diff --git a/ctrl/libhicnctrl/src/objects/listener.c b/ctrl/libhicnctrl/src/objects/listener.c new file mode 100644 index 000000000..660a4931d --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/listener.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file listener.c + * \brief Implementation of listener. + */ + +#include <string.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/listener.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" +#include "base.h" + +bool hc_listener_is_local(const hc_listener_t *listener) { + return (strncmp(listener->interface_name, "lo", INTERFACE_LEN) == 0); +} + +/* LISTENER VALIDATE */ + +int hc_listener_validate(const hc_listener_t *listener, bool allow_partial) { + // if a field is specified it should be valid + // then we allow different specification, by key or by attributes, if + // allow_partial + + int has_id = 0; + int has_name = 0; + int has_interface_name = 0; + int has_type = 0; + int has_family = 0; + int has_local_addr = 0; + int has_local_port = 0; + + if (listener->id == ~0) { + ERROR("[hc_listener_validate] Invalid id specified"); + return -1; + } + has_id = 1; + + if (!isempty(listener->name)) { + if (!IS_VALID_NAME(listener->name)) { + ERROR("[hc_listener_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (!isempty(listener->interface_name)) { + if (!IS_VALID_INTERFACE_NAME(listener->interface_name)) { + ERROR("[hc_listener_validate] Invalid interface_name specified"); + return -1; + } + has_interface_name = 1; + } + + if (listener->type != FACE_TYPE_UNDEFINED) { + if (!IS_VALID_TYPE(listener->type)) { + ERROR("[hc_listener_validate] Invalid type specified"); + return -1; + } + has_type = 1; + } + + if (listener->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(listener->family)) { + ERROR("[hc_listener_validat] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&listener->local_addr)) { + if (!IS_VALID_ADDRESS(listener->local_addr)) { + ERROR("[hc_listener_validate] Invalid local_addr specified"); + return -1; + } + has_local_addr = 1; + } + + if (listener->local_port != 0) { + if (!IS_VALID_PORT(listener->local_port)) { + ERROR("[hc_listener_validate] Invalid local_port specified"); + return -1; + } + has_local_port = 1; + } + + if (allow_partial) { + if ((has_id || has_name) && !has_type && !has_family && !has_local_port && + !has_local_port) + return 0; + else if (has_name && has_type && has_family && has_local_addr && + has_local_port) + return 0; + else + return -1; + } else { + /* name is optional */ + if (has_id && has_interface_name && has_type && has_family && + has_local_addr && has_local_port) + return 0; + return -1; + } +} + +int _hc_listener_validate(const hc_object_t *object, bool allow_partial) { + return hc_listener_validate(&object->listener, allow_partial); +} + +/* LISTENER CMP */ + +int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2) { + int rc; + + rc = INT_CMP(l1->type, l2->type); + if (rc != 0) return rc; + + rc = INT_CMP(l1->family, l2->family); + if (rc != 0) return rc; + + rc = strncmp(l1->interface_name, l2->interface_name, INTERFACE_LEN); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&l1->local_addr, &l2->local_addr); + if (rc != 0) return rc; + + rc = INT_CMP(l1->local_port, l2->local_port); + if (rc != 0) return rc; + + return rc; +} + +int _hc_listener_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_listener_cmp(&object1->listener, &object2->listener); +} + +/* LISTENER SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_listener_snprintf(char *s, size_t size, const hc_listener_t *listener) { + char local[MAXSZ_URL]; + int rc; + rc = url_snprintf(local, MAXSZ_URL, &listener->local_addr, + listener->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_listener_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + + return snprintf(s, size, "%s %s %s interface=%s", listener->name, local, + face_type_str(listener->type), listener->interface_name); +} + +int _hc_listener_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_listener_snprintf(s, size, &object->listener); +} + +/* OPERATIONS */ + +int hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_LISTENER, &object, NULL); +} + +int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_LISTENER, &object, pdata); +} + +int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_LISTENER, &object, NULL); +} + +int hc_listener_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_LISTENER, NULL, pdata); +} + +GENERATE_FIND(listener); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_LISTENER, listener); diff --git a/ctrl/libhicnctrl/src/objects/listener.h b/ctrl/libhicnctrl/src/objects/listener.h new file mode 100644 index 000000000..515c8f0ad --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/listener.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file listener.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_LISTENER_H +#define HICNCTRL_IMPL_OBJECTS_LISTENER_H + +#include "../object_vft.h" + +bool hc_listener_is_local(const hc_listener_t *listener); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_LISTENER, listener); + +#endif /* HICNCTRL_IMPL_OBJECTS_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/objects/route.c b/ctrl/libhicnctrl/src/objects/route.c new file mode 100644 index 000000000..44f39bcd7 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/route.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file route.c + * \brief Implementation of route object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/route.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" +#include "face.h" +#include "base.h" + +bool hc_route_has_face(const hc_route_t *route) { + return !iszero(&route->face, sizeof(hc_face_t)); +} + +/* ROUTE VALIDATE */ + +int hc_route_validate(const hc_route_t *route, bool allow_partial) { + int has_id = 0; + int has_name = 0; + int has_face = 0; + + int has_family = 0; + int has_remote_addr = 0; + + if (!IS_VALID_CONNECTION_ID(route->face_id)) { + ERROR("[hc_route_validate] Invalid face id"); + return -1; + } + has_id = 1; + + if (!isempty(route->face_name)) { + if (!IS_VALID_NAME(route->face_name)) { + ERROR("[hc_route_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (route->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(route->family)) { + ERROR("[hc_route_validate] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&route->remote_addr)) { + if (!IS_VALID_ADDRESS(route->remote_addr)) { + ERROR("[hc_route_validate] Invalid remote_addr specified"); + return -1; + } + has_remote_addr = 1; + } + + if (!IS_VALID_ROUTE_COST(route->cost)) { + ERROR("[hc_route_validate] Invalid cost"); + return -1; + } + + if (!IS_VALID_PREFIX_LEN(route->len)) { + ERROR("[hc_route_validate] Invalid len"); + return -1; + } + + if (hc_route_has_face(route)) { + if (!hc_face_validate(&route->face, allow_partial)) { + ERROR("[hc_route_validate] Invalid face"); + return -1; + } + has_face = 1; + } + + int has_face_info = has_id || has_name || has_face; + + if (!has_face_info) return -1; + if (allow_partial && (has_name + has_face != 1)) return -1; + + if (has_face_info && has_family && has_remote_addr) return 0; + + return -1; +} + +int _hc_route_validate(const hc_object_t *object, bool allow_partial) { + return hc_route_validate(&object->route, allow_partial); +} + +/* ROUTE CMP */ + +// XXX TODO +int hc_route_cmp(const hc_route_t *route1, const hc_route_t *route2) { + return -1; +} + +int _hc_route_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_route_cmp(&object1->route, &object2->route); +} + +/* ROUTE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_route_snprintf(char *s, size_t size, const hc_route_t *route) { + /* interface cost prefix length */ + + char prefix[MAXSZ_IP_ADDRESS]; + int rc; + + rc = hicn_ip_address_snprintf(prefix, MAXSZ_IP_ADDRESS, &route->remote_addr); + if (rc >= MAXSZ_IP_ADDRESS) + ; + if (rc < 0) return rc; + + return snprintf(s, size, "%d (%s) %*d %s %*d", route->face_id, + route->face_name, MAXSZ_COST, route->cost, prefix, MAXSZ_LEN, + route->len); +} + +int _hc_route_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_route_snprintf(s, size, &object->route); +} + +int hc_route_create(hc_sock_t *s, hc_route_t *route) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_ROUTE, &object, NULL); +} + +int hc_route_get(hc_sock_t *s, hc_route_t *route, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_ROUTE, &object, pdata); +} + +int hc_route_delete(hc_sock_t *s, hc_route_t *route) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_ROUTE, &object, NULL); +} + +int hc_route_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_ROUTE, NULL, pdata); +} + +int hc_route_list_async(hc_sock_t *s) { + return hc_execute_async(s, ACTION_LIST, OBJECT_TYPE_ROUTE, NULL, NULL, NULL); +} + +// XXX difference between GET and FIND +GENERATE_FIND(route); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_ROUTE, route); diff --git a/ctrl/libhicnctrl/src/objects/route.h b/ctrl/libhicnctrl/src/objects/route.h new file mode 100644 index 000000000..854bd70a6 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/route.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file route.h + * \brief Route. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_ROUTE_H +#define HICNCTRL_IMPL_OBJECTS_ROUTE_H + +#include "../object_vft.h" + +bool hc_route_has_face(const hc_route_t* route); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_ROUTE, route); + +#endif /* HICNCTRL_IMPL_OBJECTS_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/objects/stats.c b/ctrl/libhicnctrl/src/objects/stats.c new file mode 100644 index 000000000..2c3135d3c --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/stats.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file stats.c + * \brief Implementation of stats. + */ + +#include <string.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/stats.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +int hc_stats_snprintf(char *s, size_t size, const hc_stats_t *stats) { +#if 0 + INFO("Connection #%d:", conn_stats->id); + INFO("\tinterests received: %d pkts (%d bytes)", + conn_stats->stats.interests.rx_pkts, + conn_stats->stats.interests.rx_bytes); + INFO("\tinterests transmitted: %d pkts (%d bytes)", + conn_stats->stats.interests.tx_pkts, + conn_stats->stats.interests.tx_bytes); + INFO("\tdata received: %d pkts (%d bytes)", + conn_stats->stats.data.rx_pkts, + conn_stats->stats.data.rx_bytes); + INFO("\tdata transmitted: %d pkts (%d bytes)", + conn_stats->stats.data.tx_pkts, + conn_stats->stats.data.tx_bytes); +#endif + return 0; +} + +int hc_stats_get(hc_sock_t *s, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_STATS, &object, pdata); +} + +int hc_stats_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_STATS, NULL, pdata); +} diff --git a/ctrl/libhicnctrl/src/objects/strategy.c b/ctrl/libhicnctrl/src/objects/strategy.c new file mode 100644 index 000000000..0e7a5787e --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/strategy.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file strategy.c + * \brief Implementation of strategy. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/strategy.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +/* STREATEGY VALIDATE */ + +int hc_strategy_validate(const hc_strategy_t *strategy, bool allow_partial) { + // TODO verify name + return 0; +} + +int _hc_strategy_validate(const hc_object_t *object, bool allow_partial) { + return hc_strategy_validate(&object->strategy, allow_partial); +} + +/* STRATEGY CMP */ + +int hc_strategy_cmp(const hc_strategy_t *s1, const hc_strategy_t *s2) { + return strcmp(s1->name, s2->name); +} + +int _hc_strategy_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_strategy_cmp(&object1->strategy, &object2->strategy); +} + +/* STRATEGY SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_strategy_snprintf(char *s, size_t size, const hc_strategy_t *strategy) { + return snprintf(s, size, "%s", strategy->name); +} + +int _hc_strategy_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_strategy_snprintf(s, size, &object->strategy); +} + +/* OPERATIONS */ + +int hc_strategy_create(hc_sock_t *s, hc_strategy_t *strategy) { return -1; } + +int hc_strategy_get(hc_sock_t *s, hc_strategy_t *strategy, hc_data_t **pdata) { + return -1; +} + +int hc_strategy_delete(hc_sock_t *s, hc_strategy_t *strategy) { return -1; } + +int hc_strategy_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_STRATEGY, NULL, pdata); +} + +/* new api */ + +int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.strategy = *strategy; + return hc_execute(s, ACTION_SET, OBJECT_TYPE_STRATEGY, &object, NULL); +} + +#if 0 +int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy) { + return s->hc_strategy_add_local_prefix(s, strategy); +} +#endif + +GENERATE_FIND(strategy); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_STRATEGY, strategy); diff --git a/ctrl/libhicnctrl/src/objects/strategy.h b/ctrl/libhicnctrl/src/objects/strategy.h new file mode 100644 index 000000000..0cc4f525d --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/strategy.h @@ -0,0 +1,29 @@ + +/* + * Copyright (c) 2021-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. + */ + +/** + * \file listener.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_STRATEGY_H +#define HICNCTRL_IMPL_OBJECTS_STRATEGY_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_STRATEGY, strategy); + +#endif /* HICNCTRL_IMPL_OBJECTS_STRATEGY_H */ diff --git a/ctrl/libhicnctrl/src/objects/subscription.c b/ctrl/libhicnctrl/src/objects/subscription.c new file mode 100644 index 000000000..087e42ffb --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/subscription.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file subscription.c + * \brief Implementation of subscription. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/subscription.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +/* SUBSCRIPTION VALIDATE */ + +int hc_subscription_validate(const hc_subscription_t *subscription, + bool allow_partial) { + /* Any topic is considered valid */ + return 0; +} + +int _hc_subscription_validate(const hc_object_t *object, bool allow_partial) { + return hc_subscription_validate(&object->subscription, allow_partial); +} + +/* LISTENER CMP */ + +int hc_subscription_cmp(const hc_subscription_t *l1, + const hc_subscription_t *l2) { + return -1; +} + +int _hc_subscription_cmp(const hc_object_t *object1, + const hc_object_t *object2) { + return hc_subscription_cmp(&object1->subscription, &object2->subscription); +} + +/* SUBSCRIPTION SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_subscription_snprintf(char *s, size_t size, + const hc_subscription_t *subscription) { + return -1; +} + +int _hc_subscription_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_subscription_snprintf(s, size, &object->subscription); +} + +/* OPERATIONS */ + +int hc_subscription_create(hc_sock_t *s, hc_subscription_t *subscription) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_SUBSCRIPTION, &object, NULL); +} + +int hc_subscription_get(hc_sock_t *s, hc_subscription_t *subscription, + hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_SUBSCRIPTION, &object, pdata); +} + +int hc_subscription_delete(hc_sock_t *s, hc_subscription_t *subscription) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_SUBSCRIPTION, &object, NULL); +} + +int hc_subscription_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_SUBSCRIPTION, NULL, pdata); +} + +GENERATE_FIND(subscription); +DECLARE_OBJECT_OPS(OBJECT_TYPE_SUBSCRIPTION, subscription); diff --git a/ctrl/libhicnctrl/src/objects/subscription.h b/ctrl/libhicnctrl/src/objects/subscription.h new file mode 100644 index 000000000..6eb7583c6 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/subscription.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file subscription.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H +#define HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_SUBSCRIPTION, subscription); + +#endif /* HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H */ 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 diff --git a/ctrl/libhicnctrl/src/request.c b/ctrl/libhicnctrl/src/request.c new file mode 100644 index 000000000..0e1d07b04 --- /dev/null +++ b/ctrl/libhicnctrl/src/request.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file request.c + * \brief Implementation of pending requests. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <hicn/util/log.h> + +#include "request.h" + +const char *hc_request_state_str[] = { +#define _(x) [REQUEST_STATE_##x] = #x, + foreach_request_state +#undef _ +}; + +struct hc_request_s { + int seq; + hc_action_t action; + hc_object_type_t object_type; + hc_object_t *object; + +#if 0 + int (*parse)(const uint8_t *src, uint8_t *dst); +#endif + + /* Callbacks */ + hc_result_callback_t callback; + void *callback_data; + + /* Temp data used for the execution of the request */ + hc_data_t *data; + + /* Nested requests support + * + * In order to answer complex requests, involving a combination of requests to + * the forwarder, we will allow maintaining a Finite State Machine in the + * requests (and a tree of requests) + * + * The entry point for the modules will always remain the initial request, + * however, we will chain nested requests in their parent fields, and point to + * the one currently under execution in current. + * */ + hc_request_state_t state; + unsigned state_count; /* Usefor for iterative requests */ + hc_request_t *parent; + hc_request_t *current; +}; + +hc_request_t *hc_request_create(int seq, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + hc_request_t *request = malloc(sizeof(hc_request_t)); + if (!request) return NULL; + request->seq = seq; + + request->action = action; + request->object_type = object_type; + request->object = object; + + request->callback = callback; + request->callback_data = callback_data; + + request->data = NULL; + + request->state = REQUEST_STATE_INIT; + request->state_count = 0; + request->parent = NULL; + request->current = NULL; + + return request; +} + +void hc_request_free(hc_request_t *request) { free(request); } + +void hc_request_set(hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object) { + request->action = action; + request->object_type = object_type; + request->object = object; +} + +int hc_request_get_seq(const hc_request_t *request) { return request->seq; } + +hc_request_t *hc_request_get_current(hc_request_t *request) { + return request->current ? request->current : request; +} + +hc_request_t *hc_request_pop(hc_request_t *request) { + hc_request_t *current_request = hc_request_get_current(request); + hc_request_t *parent = current_request->parent; + request->current = parent; + if (parent) { + parent->data = current_request->data; + /* We only free the current_request if it was not the root */ + hc_request_free(current_request); + } + return parent; +} + +hc_request_state_t hc_request_get_state(const hc_request_t *request) { + return request->state; +} + +void hc_request_set_state(hc_request_t *request, hc_request_state_t state) { + request->state = state; +} + +int hc_request_get_state_count(const hc_request_t *request) { + return request->state_count; +} + +void hc_request_set_state_count(hc_request_t *request, unsigned count) { + request->state_count = count; +} + +hc_action_t hc_request_get_action(const hc_request_t *request) { + return request->action; +} + +hc_object_type_t hc_request_get_object_type(const hc_request_t *request) { + return request->object_type; +} + +hc_object_t *hc_request_get_object(const hc_request_t *request) { + return request->object; +} + +hc_data_t *hc_request_get_data(const hc_request_t *request) { + return request->data; +} + +void hc_request_set_data(hc_request_t *request, hc_data_t *data) { + assert(!request->data); + request->data = data; +} + +void hc_request_reset_data(hc_request_t *request) { + if (!request->data) return; + hc_data_free(request->data); + request->data = NULL; +} + +bool hc_request_is_subscription(const hc_request_t *request) { + hc_action_t action = hc_request_get_action(request); + hc_object_type_t object_type = hc_request_get_object_type(request); + return (action == ACTION_SUBSCRIBE) || + (action == ACTION_CREATE && object_type == OBJECT_TYPE_SUBSCRIPTION); +} + +bool hc_request_requires_object(const hc_request_t *request) { + hc_action_t action = hc_request_get_action(request); + return (action != ACTION_LIST) && (action != ACTION_SUBSCRIBE); +} + +void hc_request_clear_data(hc_request_t *request) { request->data = NULL; } + +void hc_request_set_complete(hc_request_t *request) { + request->state = REQUEST_STATE_COMPLETE; +} + +bool hc_request_is_complete(const hc_request_t *request) { + return request->state == REQUEST_STATE_COMPLETE; +} + +void hc_request_on_complete(hc_request_t *request) { + // request->state = REQUEST_STATE_COMPLETE; + if (!request->callback) return; + request->callback(request->data, request->callback_data); +} + +void hc_request_on_notification(hc_request_t *request) { + if (!request->callback) return; + request->callback(request->data, request->callback_data); +} + +hc_request_t *hc_request_make_subrequest(hc_request_t *request, + hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object) { + hc_request_t *sr = + hc_request_create(request->seq, action, object_type, object, + request->callback, request->callback_data); + + /* The parent is either the current one, or the request itself if NULL */ + hc_request_t *current_request = hc_request_get_current(request); + hc_request_reset_data(current_request); + sr->parent = current_request; + request->current = sr; + return sr; +} diff --git a/ctrl/libhicnctrl/src/request.h b/ctrl/libhicnctrl/src/request.h new file mode 100644 index 000000000..32f9f72b9 --- /dev/null +++ b/ctrl/libhicnctrl/src/request.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file request.h + * \brief Pending requests. + */ + +#ifndef HC_REQUEST_H +#define HC_REQUEST_H + +#include <stdbool.h> + +#include <hicn/ctrl/action.h> +#include <hicn/ctrl/callback.h> +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> + +#if 0 +typedef int (*HC_PARSE)(const uint8_t *, uint8_t *); +#endif + +#define foreach_request_state \ + _(UNDEFINED) \ + _(INIT) \ + _(CONNECTION_CREATE_LISTENER_LIST) \ + _(CONNECTION_CREATE_LISTENER_ITERATE) \ + _(CONNECTION_CREATE_LISTENER_GET) \ + _(CONNECTION_CREATE_LISTENER_VERIFY) \ + _(CONNECTION_CREATE_LISTENER_CREATE) \ + _(CONNECTION_CREATE_LISTENER_CHECK) \ + _(CONNECTION_CREATE) \ + _(CONNECTION_CREATE_N) \ + _(FACE_CREATE_CONNECTION_CREATE) \ + _(FACE_CREATE_CONNECTION_CHECK) \ + _(FACE_CREATE_CONNECTION_GET) \ + _(FACE_CREATE_CONNECTION_VERIFY) \ + _(FACE_CREATE_LISTENER_CREATE) \ + _(FACE_CREATE_LISTENER_CHECK) \ + _(FACE_LIST_CONNECTION_LIST) \ + _(ROUTE_CREATE_FACE_CREATE) \ + _(ROUTE_CREATE_FACE_CHECK) \ + _(ROUTE_CREATE) \ + _(GET_LIST) \ + _(COMPLETE) \ + _(N) + +typedef enum { +#define _(x) REQUEST_STATE_##x, + foreach_request_state +#undef _ +} hc_request_state_t; + +extern const char *hc_request_state_str[]; + +#define hc_request_state_str(x) hc_request_state_str[x] + +/* + * Internal state associated to a pending request + */ +typedef struct hc_request_s hc_request_t; + +hc_request_t *hc_request_create(int seq, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data); + +void hc_request_free(hc_request_t *request); + +void hc_request_set(hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object); + +int hc_request_get_seq(const hc_request_t *request); +hc_request_t *hc_request_get_current(hc_request_t *request); +hc_request_t *hc_request_pop(hc_request_t *request); + +hc_request_state_t hc_request_get_state(const hc_request_t *request); +void hc_request_set_state(hc_request_t *request, hc_request_state_t state); + +int hc_request_get_state_count(const hc_request_t *request); +void hc_request_set_state_count(hc_request_t *request, unsigned count); + +hc_action_t hc_request_get_action(const hc_request_t *request); +hc_object_type_t hc_request_get_object_type(const hc_request_t *request); +hc_object_t *hc_request_get_object(const hc_request_t *request); +hc_data_t *hc_request_get_data(const hc_request_t *request); +void hc_request_set_data(hc_request_t *request, hc_data_t *data); +void hc_request_reset_data(hc_request_t *request); + +bool hc_request_is_subscription(const hc_request_t *request); +bool hc_request_requires_object(const hc_request_t *request); + +// do not free data which might be invalid +// XXX to be removed if we replace "ensure_data_size_and_free" functions and the +// like, with equivalent functions acting on request +void hc_request_clear_data(hc_request_t *request); + +void hc_request_set_complete(hc_request_t *request); +bool hc_request_is_complete(const hc_request_t *request); + +void hc_request_on_complete(hc_request_t *request); +void hc_request_on_notification(hc_request_t *request); + +/* + * Same seq & callbacks + */ +hc_request_t *hc_request_make_subrequest(hc_request_t *request, + hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object); + +#endif /* HC_REQUEST_H */ diff --git a/ctrl/libhicnctrl/src/route.c b/ctrl/libhicnctrl/src/route.c index f59dbce7c..71e39d7ad 100644 --- a/ctrl/libhicnctrl/src/route.c +++ b/ctrl/libhicnctrl/src/route.c @@ -25,12 +25,12 @@ #define DEFAULT_HICN_ROUTE_COST 1 struct hicn_route_s { - ip_prefix_t prefix; + hicn_ip_prefix_t prefix; face_id_t face_id; route_cost_t cost; /* Optional, 0 means no value, defaults to 1 */ }; -hicn_route_t* hicn_route_create(ip_prefix_t* prefix, face_id_t face_id, +hicn_route_t* hicn_route_create(hicn_ip_prefix_t* prefix, face_id_t face_id, route_cost_t cost) { hicn_route_t* route = malloc(sizeof(hicn_route_t)); if (!route) return NULL; @@ -52,7 +52,7 @@ void hicn_route_free(hicn_route_t* route) { free(route); } int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2) { int rc; - rc = ip_prefix_cmp(&route1->prefix, &route2->prefix); + rc = hicn_ip_prefix_cmp(&route1->prefix, &route2->prefix); if (rc != 0) return rc; return (route1->face_id > route2->face_id) ? 1 @@ -60,12 +60,12 @@ int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2) { : 0; } -int hicn_route_get_prefix(const hicn_route_t* route, ip_prefix_t* prefix) { +int hicn_route_get_prefix(const hicn_route_t* route, hicn_ip_prefix_t* prefix) { *prefix = route->prefix; return 0; } -int hicn_route_set_prefix(hicn_route_t* route, const ip_prefix_t prefix) { +int hicn_route_set_prefix(hicn_route_t* route, const hicn_ip_prefix_t prefix) { route->prefix = prefix; return 0; } @@ -83,6 +83,6 @@ int hicn_route_set_cost(hicn_route_t* route, const int cost) { /* /!\ Please update constants in header file upon changes */ size_t hicn_route_snprintf(char* s, size_t size, const hicn_route_t* route) { char prefix_s[MAXSZ_IP_PREFIX]; - ip_prefix_ntop(&route->prefix, prefix_s, MAXSZ_IP_PREFIX); + hicn_ip_prefix_ntop(&route->prefix, prefix_s, MAXSZ_IP_PREFIX); return snprintf(s, size, "%s [%d]", prefix_s, route->cost); } diff --git a/ctrl/libhicnctrl/src/socket.c b/ctrl/libhicnctrl/src/socket.c new file mode 100644 index 000000000..c3636075d --- /dev/null +++ b/ctrl/libhicnctrl/src/socket.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021-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. + */ + +/** + * \file socket.c + * \brief Implementation of control socket. + */ + +#include <dlfcn.h> +#include <stdio.h> + +#include <hicn/ctrl/socket.h> +#include <hicn/util/log.h> + +#ifdef ANDROID +/* + * In android we do not load a module at runtime but we link the hicnlight + * implementation directly to the main library. + */ +#include "modules/hicn_light.h" +#endif /* ANDROID */ + +#include "socket_private.h" + +TYPEDEF_MAP(hc_sock_map, int, hc_request_t *, int_cmp, int_snprintf, + generic_snprintf); + +const char *forwarder_type_str[] = { +#define _(x) [FORWARDER_TYPE_##x] = #x, + foreach_forwarder_type +#undef _ +}; + +forwarder_type_t forwarder_type_from_str(const char *str) { + for (forwarder_type_t i = FORWARDER_TYPE_UNDEFINED + 1; i < FORWARDER_TYPE_N; + i++) { + if (strcasecmp(str, forwarder_type_str[i]) == 0) return i; + } + return FORWARDER_TYPE_UNDEFINED; +} + +#ifndef ANDROID +static int hc_sock_set_ops(hc_sock_t *s, const char *name, const char *url) { + char complete_name[128]; +#ifdef __APPLE__ + sprintf(complete_name, "%s.dylib", name); +#elif defined(__linux__) + snprintf(complete_name, 128, "%s.so", name); +#else +#error "System not supported for dynamic lynking" +#endif + + void *handle = 0; + const char *error = 0; + int (*initialize_module)(hc_sock_t *) = 0; + int rc = 0; + + // open module + handle = dlopen(complete_name, RTLD_LAZY); + if (!handle) { + if ((error = dlerror()) != 0) { + ERROR("%s", error); + } + goto ERR_DL; + return -1; + } + s->handle = handle; + + // get factory method + initialize_module = + (int (*)(hc_sock_t *))dlsym(handle, "hc_sock_initialize_module"); + if (!initialize_module) { + if ((error = dlerror()) != 0) { + ERROR("%s", error); + } + goto ERR_INIT; + } + initialize_module(s); + + return rc; +ERR_INIT: + dlclose(s->handle); + s->handle = NULL; +ERR_DL: + return -1; +} +#endif /* ! ANDROID */ + +int hc_sock_is_async(hc_sock_t *s) { return s->async; } + +int hc_sock_set_async(hc_sock_t *s) { + s->async = true; + return 0; +} + +hc_sock_t *hc_sock_create(forwarder_type_t forwarder, const char *url) { +#ifndef ANDROID + int rc; +#endif + + hc_sock_t *s = malloc(sizeof(hc_sock_t)); + if (!s) goto ERR_MALLOC; + +#ifdef ANDROID + assert(forwarder == HICNLIGHT); + s->data = hc_sock_light_data_create(url); + s->handle = NULL; +#else + switch (forwarder) { + case FORWARDER_TYPE_HICNLIGHT: + rc = hc_sock_set_ops(s, "hicnlightctrl_module", url); + break; + case FORWARDER_TYPE_VPP: + rc = hc_sock_set_ops(s, "vppctrl_module", url); + break; + default: + goto ERR_INIT; + } + + if (rc < 0) goto ERR_INIT; + + s->data = s->ops.create_data(url); +#endif + + if (!s->data) goto ERR_DATA; + + s->map = hc_sock_map_create(); + if (!s->map) goto ERR_MAP; + + s->async = false; + + s->seq_request = 0; + s->current_request = NULL; + + return s; + +ERR_MAP: +#ifdef ANDROID + hc_sock_light_data_free(s->data); +#else + ; // XXX VFT() free +#endif +ERR_DATA: +#ifndef ANDROID +ERR_INIT: +#endif + free(s); +ERR_MALLOC: + return NULL; +} + +void hc_sock_free(hc_sock_t *s) { + if (s->ops.disconnect) s->ops.disconnect(s); +#ifdef ANDROID + hc_sock_light_data_free(s->data); +#else + if (s->ops.free_data) s->ops.free_data(s->data); + if (s->handle) { + dlclose(s->handle); + } +#endif /* ANDROID */ + + hc_request_t **request_array = NULL; + int n = hc_sock_map_get_value_array(s->map, &request_array); + if (n < 0) { + ERROR("Could not retrieve pending request array for freeing up resources"); + } else { + for (unsigned i = 0; i < n; i++) { + hc_request_t *request = request_array[i]; + if (hc_sock_map_remove(s->map, hc_request_get_seq(request), NULL) < 0) + ERROR("[hc_sock_light_process] Error removing request from map"); + hc_request_free(request); + } + free(request_array); + } + + hc_sock_map_free(s->map); + + free(s); +} + +#if 0 +int hc_sock_get_next_seq(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } + +int hc_sock_set_nonblocking(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } + +#endif + +int hc_sock_get_fd(hc_sock_t *s) { return s->ops.get_fd(s); } + +int hc_sock_connect(hc_sock_t *s) { return s->ops.connect(s); } + +int hc_sock_get_recv_buffer(hc_sock_t *s, u8 **buffer, size_t *size) { + return s->ops.get_recv_buffer(s, buffer, size); +} +#if 0 + +int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq) { + return s->hc_sock_send(s, msg, msglen, seq); +} + +int hc_sock_recv(hc_sock_t *s) { return s->ops.recv(s); } +#endif + +#if 0 +int hc_sock_process(hc_sock_t *s, hc_data_t **data) { + return s->hc_sock_process(s, data); +} + +int hc_sock_callback(hc_sock_t *s, hc_data_t **data) { + return s->hc_sock_callback(s, data); +} + +int hc_sock_reset(hc_sock_t *s) { return s->hc_sock_reset(s); } + +void hc_sock_increment_woff(hc_sock_t *s, size_t bytes) { + s->hc_sock_increment_woff(s, bytes); +} + +int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, + data_callback_t complete_cb, void *complete_cb_data) { + return s->hc_sock_prepare_send(s, result, complete_cb, complete_cb_data); +} + +int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms) { + return s->hc_sock_set_recv_timeout_ms(s, timeout_ms); +} +#endif + +hc_request_t *hc_sock_create_request(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + /* Create request state */ + int seq = s->seq_request++; + hc_request_t *request = hc_request_create(seq, action, object_type, object, + callback, callback_data); + if (!request) goto ERR_MALLOC; + + hc_request_set_state(request, REQUEST_STATE_INIT); + + // Add state to map + if (hc_sock_map_add(s->map, seq, request) < 0) { + ERROR("[hc_sock_create_request] Error adding request state to map"); + goto ERR_MAP; + } + + return request; + +ERR_MAP: + free(request); +ERR_MALLOC: + return NULL; +} + +hc_request_t *hc_sock_get_request(hc_sock_t *s) { return s->current_request; } + +void hc_sock_free_request(hc_sock_t *s, hc_request_t *request, bool recursive) { + if (hc_sock_map_remove(s->map, hc_request_get_seq(request), NULL) < 0) { + ERROR("[hc_sock_free_request] Error removing request from map"); + } + if (recursive) { + hc_request_t *r = NULL; + do { + r = hc_request_pop(request); + } while (r); + } + hc_request_free(request); + s->current_request = NULL; +} + +/** + * TODO: return code: + * -1 object not found + * -2 action not found + * -3 error during serialization + * + * @return the size of the created message + */ +ssize_t hc_sock_serialize_object(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + hc_serialize_t fn = (s->ops.object_vft[object_type]).serialize[action]; + if (!fn) return -1; + return fn(object, msg); +} + +int hc_sock_parse_object(hc_sock_t *s, hc_object_type_t object_type, + uint8_t *buffer, size_t size, hc_object_t *object) { + return s->ops.object_vft[object_type].parse(buffer, size, object); +} diff --git a/ctrl/libhicnctrl/src/socket_private.h b/ctrl/libhicnctrl/src/socket_private.h new file mode 100644 index 000000000..30f3bcb6e --- /dev/null +++ b/ctrl/libhicnctrl/src/socket_private.h @@ -0,0 +1,47 @@ +#ifndef HICNCTRL_SOCKET_PRIVATE_H +#define HICNCTRL_SOCKET_PRIVATE_H + +#include <hicn/util/map.h> +#include <hicn/ctrl/socket.h> + +#include "module.h" + +TYPEDEF_MAP_H(hc_sock_map, int, hc_request_t *); + +struct hc_sock_s { + int request_seq; + hc_sock_map_t *map; + + bool async; + int seq_request; + + /* + * Stores the current request being parsed in case of fragmented reception or + * analysis (as it is the case now) between header and payload + */ + hc_request_t *current_request; + + hc_sock_ops_t ops; + + void *data; + void *handle; +}; + +hc_request_t *hc_sock_create_request(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data); + +hc_request_t *hc_sock_get_request(hc_sock_t *s); + +void hc_sock_free_request(hc_sock_t *s, hc_request_t *request, bool recursive); + +ssize_t hc_sock_serialize_object(hc_sock_t *sock, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg); + +int hc_sock_parse_object(hc_sock_t *sock, hc_object_type_t object_type, + uint8_t *buffer, size_t size, hc_object_t *object); + +#endif /* HICNCTRL_SOCKET_PRIVATE_H */ diff --git a/ctrl/libhicnctrl/src/test/CMakeLists.txt b/ctrl/libhicnctrl/src/test/CMakeLists.txt new file mode 100644 index 000000000..7fdff476d --- /dev/null +++ b/ctrl/libhicnctrl/src/test/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (c) 2021-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. + +############################################################## +# Test sources +############################################################## +list(APPEND TESTS_SRC + main.cc + common.cc + test_data.cc + test_hicnlight_listener.cc + test_hicnlight_connection.cc + test_hicnlight_route.cc + ../modules/hicn_light/connection.c + ../modules/hicn_light/face.c + ../modules/hicn_light/listener.c + ../modules/hicn_light/route.c +) + +############################################################## +# Build single unit test executable and add it to test list +############################################################## +build_executable(libhicnctrl_tests + NO_INSTALL + SOURCES ${TESTS_SRC} + LINK_LIBRARIES + ${LIBRARIES} + ${LIBHICNCTRL_SHARED} + ${GTEST_LIBRARIES} + pthread + INCLUDE_DIRS + $<TARGET_PROPERTY:${LIBHICNCTRL_SHARED},INCLUDE_DIRECTORIES> + ${GTEST_INCLUDE_DIRS} + DEPENDS gtest ${LIBHICNCTRL_SHARED} + COMPONENT ${LIBHICNCTRL_COMPONENT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + LINK_FLAGS ${LINK_FLAGS} +) + +add_test_internal(libhicnctrl_tests) diff --git a/ctrl/libhicnctrl/src/test/common.cc b/ctrl/libhicnctrl/src/test/common.cc new file mode 100644 index 000000000..075281d2e --- /dev/null +++ b/ctrl/libhicnctrl/src/test/common.cc @@ -0,0 +1,13 @@ +#include "common.h" + +std::string dump_buffer(const char *name, uint8_t *buffer, size_t size) { + std::ostringstream oss; + oss << "const std::vector<uint8_t> " << name << " = {"; + for (size_t i = 0; i < size; i++) { + if (i > 0) oss << ", "; + oss << "0x" << std::setw(2) << std::setfill('0') << std::hex + << static_cast<int>(buffer[i]); + } + oss << "};" << std::endl; + return oss.str(); +} diff --git a/ctrl/libhicnctrl/src/test/common.h b/ctrl/libhicnctrl/src/test/common.h new file mode 100644 index 000000000..8927c86ec --- /dev/null +++ b/ctrl/libhicnctrl/src/test/common.h @@ -0,0 +1,51 @@ +/* + * 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. + */ + +#ifndef HICNCTRL_TEST_COMMON +#define HICNCTRL_TEST_COMMON + +#define BUFSIZE 8192 + +#include <gtest/gtest.h> + +class TestHicnLight : public ::testing::Test { + protected: + TestHicnLight() {} + virtual ~TestHicnLight() {} + virtual void SetUp() {} + virtual void TearDown() {} +}; + +class TestHicnLightSerialize : public TestHicnLight { + // TestHicnLightSerialize() {} + // virtual ~TestHicnLightSerialize() {} +}; + +class TestHicnLightParse : public TestHicnLight { + // TestHicnLightParse() {} + // virtual ~TestHicnLightParse() {} +}; + +std::string dump_buffer(const char *name, uint8_t *buffer, size_t size); + +#define EXPECT_PAYLOAD_EQ(BUFFER, SIZE, PAYLOAD) \ + EXPECT_EQ(memcmp((BUFFER), &(PAYLOAD)[0], PAYLOAD.size()), 0) \ + << dump_buffer(#PAYLOAD, (BUFFER), SIZE); + +#define EXPECT_PAYLOAD_EQ_STRUCT(BUFFER, SIZE, PAYLOAD) \ + EXPECT_EQ(memcmp((BUFFER), (PAYLOAD), SIZE), 0) \ + << dump_buffer("expected:", (uint8_t *)(BUFFER), SIZE); + +#endif /* HICNCTRL_TEST_COMMON */ diff --git a/ctrl/libhicnctrl/src/test/main.cc b/ctrl/libhicnctrl/src/test/main.cc new file mode 100644 index 000000000..49cc28f66 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/main.cc @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/ctrl/libhicnctrl/src/test/test_data.cc b/ctrl/libhicnctrl/src/test/test_data.cc new file mode 100644 index 000000000..46debb0e7 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_data.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 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 <gtest/gtest.h> +#include <sstream> + +extern "C" { +#include <hicn/ctrl/objects.h> +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +} + +#include "common.h" + +namespace { + +TEST_F(TestHicnLight, TestHicnLightData) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + + int rc; + hc_data_t* data = hc_data_create(OBJECT_TYPE_FACE); + hc_data_set_max_size(data, 2); + + ASSERT_EQ(hc_data_get_size(data), 0) << "Initial data size should be zero"; + + /* Try to allocate more than max */ + rc = hc_data_allocate(data, 5); + ASSERT_EQ(rc, -1) << "Allocating above max_size should fail"; + + /* Allocate room for two objects */ + rc = hc_data_allocate(data, 2); + ASSERT_EQ(rc, 0) << "Allocating data the first time should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 0) + << "Initial size should be 0 after allocation"; + + /* Try to allocate twice */ + rc = hc_data_allocate(data, 2); + ASSERT_EQ(rc, -1) << "Allocating data multiple times should fail"; + + ASSERT_EQ(hc_data_get_size(data), 0) + << "Size after failed push should remain unchanged"; + + /* Push a first object */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "First push should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 1) + << "Data size first successful push should be 1"; + + /* Push a second object */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "Second push should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 2) + << "Data size after second successful push should be 2"; + + /* Push a third object, exceeding the allocated size */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, -1) << "Third push on full data of size 2 should fail"; + + /* Clear */ + rc = hc_data_clear(data); + ASSERT_EQ(rc, 0) << "Clear should always succeed"; + + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "Pushing element after reallocation should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 1) << "Size after first push should be one"; + // XXX + + /* Try to push an invalid object */ + // XXX so far NULL + rc = hc_data_push(data, NULL); + ASSERT_EQ(rc, -1) << "Pushing invalid element should fail"; + + ASSERT_EQ(hc_data_get_size(data), 1) + << "Size after push failure should remain unchanged"; + // XXX + + hc_data_free(data); +} + +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc new file mode 100644 index 000000000..53dd88ac3 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021-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 <gtest/gtest.h> +#include <sstream> + +#include <hicn/ctrl/object.h> + +#include "../modules/hicn_light/connection.h" +#include "common.h" + +namespace { + +const hc_object_t valid_connection = { + .connection = {.id = 0, + .name = {'l', 's', 't', 0}, + .interface_name = {'l', 'o', 0}, + .netdevice_type = NETDEVICE_TYPE_WIRED, + .type = FACE_TYPE_UDP, + .family = AF_INET, + .local_addr = IPV4_LOOPBACK, + .local_port = 9695, + .remote_addr = IPV4_LOOPBACK, + .remote_port = 9695, + .admin_state = FACE_STATE_UP, + .priority = 0, + .tags = POLICY_TAGS_EMPTY, + .state = FACE_STATE_UP}}; + +const std::vector<uint8_t> valid_connection_create_payload = { + /* header */ + 0xc0, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* ip_address_t remote_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* uint16_t remote_port = 9695; */ + 0x25, 0xdf, + /* int family = AF_INET; */ + 0x02, + /* face_type_t type = FACE_TYPE_UDP_LISTENER; */ + 0x05, + /* uint8_t admin_state = FACE_STATE_UP; */ + 0x02, + /* Padding ? */ + 0x00, + /* uint32_t priority = 0; */ + 0x00, 0x00, 0x00, 0x00, + /* policy_tags_t tags; */ + 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_connection_delete_payload = { + 0xc0, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x73, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_connection_list_payload = { + 0xc0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.connection, &valid_connection, sizeof(hc_connection_t)); + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + // XXX debug + // THIS HAS UNINIT VALUES + std::cout << "n=" << n << std::endl; + EXPECT_EQ(memcmp(buf, buf, 60), 0); + EXPECT_EQ(memcmp(buf, buf, 62), 0); + EXPECT_EQ(memcmp(buf, buf, 64), 0); // XXX we start having issues + EXPECT_EQ(memcmp(buf, buf, 66), 0); + EXPECT_EQ(memcmp(buf, buf, 68), 0); + EXPECT_EQ(memcmp(buf, buf, 70), 0); + EXPECT_EQ(memcmp(buf, buf, 72), 0); + // XXX debug + + EXPECT_EQ(n, valid_connection_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_create_payload); +} + +// TODO +// - create with id != 0 +// - create with invalid fields, non zero-terminated strings, etc. + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.connection, &valid_connection, sizeof(hc_connection_t)); + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_connection_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionList) { + uint8_t buf[BUFSIZE]; + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_LIST]; + size_t n = fn(NULL, buf); + + EXPECT_EQ(n, valid_connection_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_list_payload); +} + +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc new file mode 100644 index 000000000..fb3df39a8 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021-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 <sstream> + +extern "C" { +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/listener.h> +#include "../modules/hicn_light/listener.h" +} + +#include "common.h" + +namespace { + +static const hc_object_t valid_listener = { + .listener = {.name = {'l', 's', 't', 0}, + .interface_name = {'l', 'o', 0}, + .id = 0, + .type = FACE_TYPE_UDP_LISTENER, + .family = AF_INET, + .local_addr = IPV4_LOOPBACK, + .local_port = 9695}}; + +const std::vector<uint8_t> valid_listener_create_payload = { + /* header */ + 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* char interface_name[INTERFACE_LEN] = "lo"; */ + 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* int family = AF_INET; */ + 0x02, + /* face_type_t type = FACE_TYPE_UDP_LISTENER; */ + 0x06}; + +const std::vector<uint8_t> valid_listener_delete_payload = { + /* header */ + 0xc0, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char symbolicOrListenerid[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_listener_list_payload = { + /* header */ + 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* @see <hicn/ctrl/objects/listener.h> */ +const std::vector<uint8_t> valid_listener_payload = { + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* char interface_name[INTERFACE_LEN] = "lo"; */ + 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint32_t id = 0; */ + 0x00, 0x00, 0x00, 0x00, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* face_type_t type = FACE_TYPE_UDP */ + 0x06, + /* int family = AF_INET; */ + 0x02}; + +TEST_F(TestHicnLightParse, TestHicnLightParseListener) { + /* Parse payload into an object */ + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + int rc = hicnlight_listener_module_ops.parse( + &valid_listener_payload[0], valid_listener_payload.size(), &obj); + EXPECT_EQ(rc, 0); + EXPECT_EQ(hc_listener_cmp(&obj.listener, &valid_listener.listener), 0); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_create_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerList) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_LIST]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_list_payload); +} +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc new file mode 100644 index 000000000..d48066ba2 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021-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 <gtest/gtest.h> +#include <sstream> + +extern "C" { +#include <hicn/ctrl/object.h> +#include "../modules/hicn_light/route.h" +} + +#include "common.h" + +namespace { + +const hc_object_t valid_route = { + .route = {.face_id = 1, + .face_name = {0}, // NULL, use face_id instead + .family = AF_INET, + .remote_addr = IPV4_LOOPBACK, + .len = 16, + .cost = 1, + .face = {0}}}; + +const std::vector<uint8_t> valid_route_create_payload = { + /* uint8_t message_type = REQUEST_LIGHT */ + 0xc0, + /* uint8_t command_id = COMMAND_TYPE_ROUTE_ADD */ + 0x08, + /* uint16_t length = 1 */ + 0x01, 0x00, + /* uint32_t seq_num = 0 */ + 0x00, 0x00, 0x00, 0x00, + /* char symbolic_or_connid[16] = "1\0" */ + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* hicn_ip_address_t address = {0, 0, 0, 127.0.0.1} */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* */ + 0x01, + /* */ + 0x00, + /* */ + 0x02, + /* */ + 0x10}; + +const std::vector<uint8_t> valid_route_delete_payload = { + /* uint8_t message_type = REQUEST_LIGHT */ + 0xc0, + /* uint8_t command_id = COMMAND_TYPE_ROUTE_REMOVE */ + 0x09, + /* uint16_t length = 1 */ + 0x01, 0x00, + /* uint32_t seq_num = 0 */ + 0x00, 0x00, 0x00, 0x00, + + /* char symbolic_or_connid[16] = "1\0" */ + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* hicn_ip_address_t address = {0, 0, 0, 127.0.0.1} */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint8_t family = AF_INET (2) */ + 0x02, + /* uint8_t len = 16 */ + 0x10, + /* 2-byte padding */ + 0x00, 0x00}; + +const std::vector<uint8_t> valid_route_list_payload = {0xc0, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_create_payload); +} + +// TODO +// - create with id != 0 +// - create with invalid fields, non zero-terminated strings, etc. + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteList) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_LIST]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_list_payload); +} + +} // namespace |