diff options
author | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:06:34 +0200 |
---|---|---|
committer | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:31:51 +0200 |
commit | 6d22a0db96aa7f8e3102ae44d00c09e36a2e9c57 (patch) | |
tree | 79546bbf09f6fbf74db7bc89117843f06ce937ea /ctrl/libhicnctrl/src | |
parent | 012843b1c0bc0838e69085ed83a79ec8b6f97360 (diff) |
feat: Due to the deep modifications related to names and packet format,
this task cover a large part of the codebase and involves several changes:
- the library provides a name data structure (hicn_name_t ), which is composed
of a name prefix (hicn_name_prefix_t) and a name suffix (hicn_name_suffix_t),
and it has been extended to provide all support functions required for name
manipulation, including common prefix computation, as required for the Longest
Prefix Match (LPM)in the forwarder, in addition to Exact Prefix Match (EPM).
- all code has been rewritten to use this data structure instead of having for
instance the forwarder define its own name class (used to be Name and NameBitVector)
the code has been refactored to minimize name allocations and copies, one remaining
aspect is the difference of name storage between PIT and CS entries (respectively
in the PIT entry, and in the message buffer), which causes the packet cache
index to be updated when a PIT entry is converted into a CS entry. By storing
the name in the PIT/CS entry everytime, we might save on this operation).
- hicn-light FIB has been rewritten : code has been refactored and should now be
shorter and documented; unit tests have been drafted but more would be required
to cover all cases and match the algorithms to add/remove nodes, as specified in the doc.
all protocol details and hICN header formats are now abstracted by the library
for the forwarder (and thus header.h and protocols/*.h have been removed from
public includes, and replaced by packet.h providing protocol agnostic packet
level functions, completely replacing the compat.h header that used to provide
similar functions.
- this works by exposing a opaque buffer to the application (a kind of socket buffer)
which is used by the lib to cache the packet format and offsets of the different
layers in the buffer and provider efficient operations (the packet format is
either defined for packet construction, or guessed at ingress, and this structure
is updated accordingly only once).
Co-authored-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Change-Id: I31e321897f85f0267fe8ba4720363c180564492f
Diffstat (limited to 'ctrl/libhicnctrl/src')
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 |