aboutsummaryrefslogtreecommitdiffstats
path: root/ctrl/libhicnctrl/src
diff options
context:
space:
mode:
Diffstat (limited to 'ctrl/libhicnctrl/src')
-rw-r--r--ctrl/libhicnctrl/src/CMakeLists.txt225
-rw-r--r--ctrl/libhicnctrl/src/action.c41
-rw-r--r--ctrl/libhicnctrl/src/api.c3271
-rw-r--r--ctrl/libhicnctrl/src/api_private.h78
-rw-r--r--ctrl/libhicnctrl/src/cli.c868
-rw-r--r--ctrl/libhicnctrl/src/cli.h4
-rw-r--r--ctrl/libhicnctrl/src/command.c127
-rw-r--r--ctrl/libhicnctrl/src/commands/command_cache.c74
-rw-r--r--ctrl/libhicnctrl/src/commands/command_connection.c166
-rw-r--r--ctrl/libhicnctrl/src/commands/command_face.c167
-rw-r--r--ctrl/libhicnctrl/src/commands/command_listener.c144
-rw-r--r--ctrl/libhicnctrl/src/commands/command_mapme.c78
-rw-r--r--ctrl/libhicnctrl/src/commands/command_policy.c72
-rw-r--r--ctrl/libhicnctrl/src/commands/command_punting.c60
-rw-r--r--ctrl/libhicnctrl/src/commands/command_route.c187
-rw-r--r--ctrl/libhicnctrl/src/commands/command_stats.c37
-rw-r--r--ctrl/libhicnctrl/src/commands/command_strategy.c66
-rw-r--r--ctrl/libhicnctrl/src/commands/command_subscription.c45
-rw-r--r--ctrl/libhicnctrl/src/data.c212
-rw-r--r--ctrl/libhicnctrl/src/face.c430
-rw-r--r--ctrl/libhicnctrl/src/fw_interface.c266
-rw-r--r--ctrl/libhicnctrl/src/hicn_plugin_api.c1337
-rw-r--r--ctrl/libhicnctrl/src/hicnctrl.c400
-rw-r--r--ctrl/libhicnctrl/src/libhicnctrl-config.cmake.in8
-rw-r--r--ctrl/libhicnctrl/src/module.h159
-rw-r--r--ctrl/libhicnctrl/src/module_object.c1
-rw-r--r--ctrl/libhicnctrl/src/module_object.h10
-rw-r--r--ctrl/libhicnctrl/src/modules/CMakeLists.txt109
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light.c1435
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light.h59
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/base.h49
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/cache.c178
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/connection.c503
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/connection.h11
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/face.c165
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/face.h16
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/listener.c181
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/listener.h8
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/mapme.c68
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/mapme.h28
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/policy.c162
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/punting.c74
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/route.c177
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/route.h28
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/stats.c113
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/stats.h24
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/strategy.c138
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/strategy.h8
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/subscription.c71
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/subscription.h8
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_light/wldr.c3
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin.c248
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/base.h36
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c189
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h28
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/route.c552
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/route.h28
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.c118
-rw-r--r--ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.h28
-rw-r--r--ctrl/libhicnctrl/src/object.c62
-rw-r--r--ctrl/libhicnctrl/src/object_private.h57
-rw-r--r--ctrl/libhicnctrl/src/object_type.c39
-rw-r--r--ctrl/libhicnctrl/src/object_vft.c44
-rw-r--r--ctrl/libhicnctrl/src/object_vft.h53
-rw-r--r--ctrl/libhicnctrl/src/objects/active_interface.c86
-rw-r--r--ctrl/libhicnctrl/src/objects/active_interface.h28
-rw-r--r--ctrl/libhicnctrl/src/objects/base.c34
-rw-r--r--ctrl/libhicnctrl/src/objects/base.h27
-rw-r--r--ctrl/libhicnctrl/src/objects/connection.c298
-rw-r--r--ctrl/libhicnctrl/src/objects/connection.h32
-rw-r--r--ctrl/libhicnctrl/src/objects/face.c187
-rw-r--r--ctrl/libhicnctrl/src/objects/face.h30
-rw-r--r--ctrl/libhicnctrl/src/objects/listener.c208
-rw-r--r--ctrl/libhicnctrl/src/objects/listener.h30
-rw-r--r--ctrl/libhicnctrl/src/objects/mapme.c95
-rw-r--r--ctrl/libhicnctrl/src/objects/mapme.h30
-rw-r--r--ctrl/libhicnctrl/src/objects/route.c176
-rw-r--r--ctrl/libhicnctrl/src/objects/route.h30
-rw-r--r--ctrl/libhicnctrl/src/objects/stats.c108
-rw-r--r--ctrl/libhicnctrl/src/objects/stats.h29
-rw-r--r--ctrl/libhicnctrl/src/objects/strategy.c92
-rw-r--r--ctrl/libhicnctrl/src/objects/strategy.h29
-rw-r--r--ctrl/libhicnctrl/src/objects/subscription.c94
-rw-r--r--ctrl/libhicnctrl/src/objects/subscription.h28
-rw-r--r--ctrl/libhicnctrl/src/parse.c412
-rw-r--r--ctrl/libhicnctrl/src/request.c210
-rw-r--r--ctrl/libhicnctrl/src/request.h129
-rw-r--r--ctrl/libhicnctrl/src/route.c102
-rw-r--r--ctrl/libhicnctrl/src/socket.c308
-rw-r--r--ctrl/libhicnctrl/src/socket_private.h47
-rw-r--r--ctrl/libhicnctrl/src/test/CMakeLists.txt51
-rw-r--r--ctrl/libhicnctrl/src/test/common.cc13
-rw-r--r--ctrl/libhicnctrl/src/test/common.h51
-rw-r--r--ctrl/libhicnctrl/src/test/main.cc21
-rw-r--r--ctrl/libhicnctrl/src/test/test_data.cc97
-rw-r--r--ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc132
-rw-r--r--ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc138
-rw-r--r--ctrl/libhicnctrl/src/test/test_hicnlight_route.cc133
-rw-r--r--ctrl/libhicnctrl/src/util/hash.h379
99 files changed, 11584 insertions, 5941 deletions
diff --git a/ctrl/libhicnctrl/src/CMakeLists.txt b/ctrl/libhicnctrl/src/CMakeLists.txt
index 00661a2a0..3ea5c5db9 100644
--- a/ctrl/libhicnctrl/src/CMakeLists.txt
+++ b/ctrl/libhicnctrl/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-# Copyright (c) 2017-2019 Cisco and/or its affiliates.
+# Copyright (c) 2021-2023 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -11,82 +11,193 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-list(APPEND COMPILER_DEFINITIONS
- "-DWITH_POLICY"
+##############################################################
+# Source files
+##############################################################
+set(SOURCE_FILES
+ 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/mapme.c
+ objects/route.c
+ objects/strategy.c
+ objects/stats.c
+ objects/subscription.c
+ parse.c
+ request.c
+ route.c
+ socket.c
)
set(HEADER_FILES
- api.h
- commands.h
- face.h
-)
-
-set(UTIL_HEADER_FILES
- face.h
+ object_vft.h
+ objects/active_interface.h
+ objects/base.h
+ objects/connection.h
+ objects/face.h
+ objects/listener.h
+ objects/mapme.h
+ objects/route.h
+ objects/stats.h
+ objects/strategy.h
+ objects/subscription.h
+ request.h
+ api_private.h
)
-set(SOURCE_FILES
- face.c
- route.c
-)
-
-if(BUILD_HICNPLUGIN)
- set(SOURCE_FILES
- ${SOURCE_FILES}
- hicn_plugin_api.c
- )
-else ()
- set(SOURCE_FILES
- ${SOURCE_FILES}
- api.c
- )
-endif()
+##############################################################
+# Libraries to link
+##############################################################
set(LIBRARIES
- m
- ${HICN_LIBRARIES}
+ m
+ dl
+ ${HICN_LIBRARIES}
)
+
+##############################################################
+# Include directories
+##############################################################
set(INCLUDE_DIRS
- ./
- ../includes/
- ${HICN_INCLUDE_DIRS}
+ PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PUBLIC
+ $<BUILD_INTERFACE:${Libhicnctrl_INCLUDE_DIRS}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
)
-# Android requires static libraries
+
+##############################################################
+# Library type
+##############################################################
if (DISABLE_SHARED_LIBRARIES)
- set(LIBRARIES ${LIBRARIES} ${LIBHICN_STATIC})
- set(LINK_TYPE STATIC)
+ set(LIBRARIES ${LIBRARIES} ${LIBHICN_STATIC})
+ set(LINK_TYPE STATIC)
else ()
- set(LINK_TYPE SHARED STATIC)
+ set(LINK_TYPE SHARED STATIC)
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.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/mapme.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/stats.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/mapme.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/stats.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/strategy.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/subscription.h
+ )
+else()
+ add_subdirectory(modules)
+endif()
+
+
+##############################################################
+# Build main hicnctrl library
+##############################################################
build_library(${LIBHICNCTRL}
- ${LINK_TYPE}
- SOURCES ${SOURCE_FILES}
- INSTALL_HEADERS ${TO_INSTALL_HEADER_FILES}
- LINK_LIBRARIES ${LIBRARIES}
- DEPENDS ${DEPENDENCIES}
- COMPONENT ${LIBHICNCTRL_COMPONENT}
- INCLUDE_DIRS ${INCLUDE_DIRS}
- INSTALL_ROOT_DIR hicn
- DEFINITIONS ${COMPILER_DEFINITIONS}
+ ${LINK_TYPE}
+ SOURCES ${SOURCE_FILES} ${HEADER_FILES}
+ INSTALL_HEADERS ${TO_INSTALL_HEADER_FILES}
+ LINK_LIBRARIES ${LIBRARIES}
+ DEPENDS ${DEPENDENCIES}
+ COMPONENT ${LIBHICNCTRL_COMPONENT}
+ INCLUDE_DIRS ${INCLUDE_DIRS}
+ DEFINITIONS PUBLIC ${COMPILER_DEFINITIONS}
+ VERSION ${CURRENT_VERSION}
+ EXPORT_NAME "${LIBHICNCTRL_COMPONENT}"
+ COMPILE_OPTIONS ${COMPILER_OPTIONS}
+)
+
+##############################################################
+# Unit tests
+##############################################################
+if (${BUILD_TESTS})
+ add_subdirectory(test)
+endif()
+
+##############################################################
+# Cmake config files
+##############################################################
+create_cmake_config (
+ ${LIBHICNCTRL_COMPONENT}
+ INCLUDE_DIRS ${Libhicnctrl_INCLUDE_DIRS}
+ VERSION ${CURRENT_VERSION}
+ COMPONENT ${LIBHICNCTRL_COMPONENT}
+ NAMESPACE hicn
)
+
+##############################################################
+# Build executables
+##############################################################
if (NOT DISABLE_EXECUTABLES)
+ if (DISABLE_SHARED_LIBRARIES)
+ set(LIBRARIES ${LIBRARIES} ${LIBHICNCTRL_STATIC})
+ set(DEPENDENCIES ${LIBHICNCTRL_STATIC})
+ else ()
set(LIBRARIES ${LIBRARIES} ${LIBHICN_SHARED} ${LIBHICNCTRL_SHARED})
+ set(DEPENDENCIES ${LIBHICNCTRL_SHARED})
+ endif ()
- list(APPEND DAEMON_SRC
- cli.c
- )
-
- build_executable(${HICNCTRL}
- SOURCES ${DAEMON_SRC}
- LINK_LIBRARIES ${LIBRARIES}
- DEPENDS ${LIBHICNCTRL_SHARED}
- COMPONENT ${LIBHICNCTRL_COMPONENT}
- INCLUDE_DIRS ${INCLUDE_DIRS}
- DEFINITIONS ${COMPILER_DEFINITIONS}
- LINK_FLAGS ${LINK_FLAGS}
- )
+ list(APPEND DAEMON_SRC
+ hicnctrl.c
+ )
+
+ build_executable(${HICNCTRL}
+ SOURCES ${DAEMON_SRC}
+ LINK_LIBRARIES ${LIBRARIES}
+ DEPENDS ${DEPENDENCIES}
+ COMPONENT ${LIBHICNCTRL_COMPONENT}
+ INCLUDE_DIRS ${INCLUDE_DIRS}
+ DEFINITIONS ${COMPILER_DEFINITIONS}
+ LINK_FLAGS ${LINK_FLAGS}
+ COMPILE_OPTIONS ${COMPILER_OPTIONS}
+ )
endif ()
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 8dcb978e0..c133b9123 100644
--- a/ctrl/libhicnctrl/src/api.c
+++ b/ctrl/libhicnctrl/src/api.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 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:
@@ -18,3100 +18,275 @@
* \brief Implementation of hICN control library API
*/
-#include <assert.h> // assert
-#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 <fcntl.h> // fcntl
-#include <sys/types.h> // getpid
-#include <unistd.h> // getpid
-#ifdef __linux__
-#include <sys/syscall.h>
-#define gettid() syscall(SYS_gettid)
-#endif /* __linux__ */
-
-#include <hicn/ctrl/api.h>
-#include <hicn/ctrl/commands.h>
-#include <hicn/util/token.h>
+#include <assert.h>
+#include <dlfcn.h> // dlopen
+#include <hicn/strategy.h>
#include <hicn/util/log.h>
-#include <hicn/util/map.h>
-#include <strings.h>
-
-#define PORT 9695
-
-#define INT_CMP(x, y) ((x > y) ? 1 : (x < y) ? -1 : 0)
-#define BOOLSTR(x) ((x) ? "true" : "false")
-/*
- * Internal state associated to a pending request
- */
-typedef struct {
- int seq;
- hc_data_t * data;
- /* Information used to process results */
- int size_in;
- 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.
- */
-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_s {
- 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;
- hc_sock_map_t * map;
-};
-
-
-hc_sock_request_t *
-hc_sock_request_create(int seq, hc_data_t * data, HC_PARSE parse)
-{
- assert(data);
-
- 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;
-}
+#include <hicn/ctrl/route.h>
+#include <math.h> // log2
-void
-hc_sock_request_free(hc_sock_request_t * request)
-{
- free(request);
-}
+#include "api_private.h"
+#include "object_vft.h"
+#include "request.h"
+#include <hicn/ctrl/socket.h>
+#include "socket_private.h"
-#if 0
-#ifdef __APPLE__
-#define RANDBYTE() (u8)(arc4random() & 0xFF)
-#else
-#define RANDBYTE() (u8)(random() & 0xFF)
-#endif
-#endif
-#define RANDBYTE() (u8)(rand() & 0xFF)
+#define ENOIMPL 42
-/*
- * 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
+int hc_sock_on_init(hc_sock_t *s, hc_request_t *request) {
+ int rc;
+ ssize_t size;
-#define MAX(x, y) ((x > y) ? x : y)
+ uint8_t *buffer;
-/**
- * \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
+ size = s->ops.prepare(s, request, &buffer);
+ if (size < 0) goto ERR_PREPARE;
-/**
- * In practise, we want to preserve enough room to store a full packet of
- * average expected size (say a header + N payload elements).
- */
-#define AVG_ELEMENTS (1 << DEFAULT_SIZE_LOG)
-#define AVG_BUFLEN sizeof(hc_msg_header_t) + AVG_ELEMENTS * sizeof(hc_msg_payload_t)
+ if (size == 0) return 1; /* Done */
-/*
- * We should at least have buffer space allowing to store one processable unit
- * of data, either the header of the maximum possible payload
- */
-#define MIN_BUFLEN MAX(sizeof(hc_msg_header_t), sizeof(hc_msg_payload_t))
-
-static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT;
+ assert(hc_request_get_data(hc_request_get_current(request)));
-/* /!\ Please update constants in header file upon changes */
-const char * connection_type_str[] = {
-#define _(x) [CONNECTION_TYPE_ ## x] = STRINGIZE(x),
-foreach_connection_type
-#undef _
-};
+ rc = s->ops.send(s, buffer, size);
+ if (rc < 0) goto ERR_SEND;
-#define IS_VALID_CONNECTION_TYPE(x) IS_VALID_ENUM_TYPE(CONNECTION_TYPE, x)
+ return 0;
-hc_connection_type_t
-connection_type_from_str(const char * str)
-{
- if (strcasecmp(str, "TCP") == 0)
- return CONNECTION_TYPE_TCP;
- else if (strcasecmp(str, "UDP") == 0)
- return CONNECTION_TYPE_UDP;
- else if (strcasecmp(str, "HICN") == 0)
- return CONNECTION_TYPE_HICN;
- else
- return CONNECTION_TYPE_UNDEFINED;
+ERR_PREPARE:
+ERR_SEND:
+ return -1;
}
-/* Conversions to shield lib user from heterogeneity */
-
-#define IS_VALID_LIST_CONNECTIONS_TYPE(x) ((x >= CONN_GRE) && (x <= CONN_HICN))
-
-static const hc_connection_type_t map_from_list_connections_type[] = {
- [CONN_GRE] = CONNECTION_TYPE_UNDEFINED,
- [CONN_TCP] = CONNECTION_TYPE_TCP,
- [CONN_UDP] = CONNECTION_TYPE_UDP,
- [CONN_MULTICAST] = CONNECTION_TYPE_UNDEFINED,
- [CONN_L2] = CONNECTION_TYPE_UNDEFINED,
- [CONN_HICN] = CONNECTION_TYPE_HICN,
-};
-
-typedef enum {
- ENCAP_TCP,
- ENCAP_UDP,
- ENCAP_ETHER,
- ENCAP_LOCAL,
- ENCAP_HICN
-} EncapType;
-
-#define IS_VALID_LIST_LISTENERS_TYPE(x) ((x >= ENCAP_TCP) && (x <= ENCAP_HICN))
-
-static const hc_connection_type_t map_from_encap_type[] = {
- [ENCAP_TCP] = CONNECTION_TYPE_TCP,
- [ENCAP_UDP] = CONNECTION_TYPE_UDP,
- [ENCAP_ETHER] = CONNECTION_TYPE_UNDEFINED,
- [ENCAP_LOCAL] = CONNECTION_TYPE_UNDEFINED,
- [ENCAP_HICN] = CONNECTION_TYPE_HICN,
-};
-
-static const connection_type map_to_connection_type[] = {
- [CONNECTION_TYPE_TCP] = TCP_CONN,
- [CONNECTION_TYPE_UDP] = UDP_CONN,
- [CONNECTION_TYPE_HICN] = HICN_CONN,
-};
-
-static const listener_mode map_to_listener_mode[] = {
- [CONNECTION_TYPE_TCP] = IP_MODE,
- [CONNECTION_TYPE_UDP] = IP_MODE,
- [CONNECTION_TYPE_HICN] = HICN_MODE,
-};
+int hc_sock_on_receive(hc_sock_t *s, size_t count) {
+ int rc;
-#define IS_VALID_LIST_CONNECTIONS_STATE(x) ((x >= IFACE_UP) && (x <= IFACE_UNKNOWN))
-
-/* /!\ Please update constants in header file upon changes */
-const char * connection_state_str[] = {
-#define _(x) [HC_CONNECTION_STATE_ ## x] = STRINGIZE(x),
-foreach_connection_state
-#undef _
-};
-
-/*
-#define IS_VALID_CONNECTION_STATE(x) IS_VALID_ENUM_TYPE(CONNECTION_STATE, x)
-
-static const connection_state map_to_connection_state[] = {
- [HC_CONNECTION_STATE_UP] = IFACE_UP,
- [HC_CONNECTION_STATE_DOWN] = IFACE_DOWN,
-};
-
-*/
-
-static const hc_connection_state_t map_from_list_connections_state[] = {
- [IFACE_UP] = HC_CONNECTION_STATE_UP,
- [IFACE_DOWN] = HC_CONNECTION_STATE_DOWN,
- [IFACE_UNKNOWN] = HC_CONNECTION_STATE_UNDEFINED,
-};
-
-
-#define connection_state_to_face_state(x) ((face_state_t)(x))
-#define face_state_to_connection_state(x) ((hc_connection_state_t)(x))
-
-#define IS_VALID_ADDR_TYPE(x) ((x >= ADDR_INET) && (x <= ADDR_UNIX))
-
-static const int map_from_addr_type[] = {
- [ADDR_INET] = AF_INET,
- [ADDR_INET6] = AF_INET6,
- [ADDR_LINK] = AF_UNSPEC,
- [ADDR_IFACE] = AF_UNSPEC,
- [ADDR_UNIX] = AF_UNSPEC,
-};
-
-static const address_type map_to_addr_type[] = {
- [AF_INET] = ADDR_INET,
- [AF_INET6] = ADDR_INET6,
-};
-
-/******************************************************************************
- * Message helper types and aliases
- ******************************************************************************/
-
-#define foreach_hc_command \
- _(add_connection) \
- _(remove_connection) \
- _(list_connections) \
- _(add_listener) \
- _(remove_listener) \
- _(list_listeners) \
- _(add_route) \
- _(remove_route) \
- _(list_routes) \
- _(cache_store) \
- _(cache_serve) \
- /*_(cache_clear) */ \
- _(set_strategy) \
- _(set_wldr) \
- _(add_punting) \
- _(mapme_activator) \
- _(mapme_timing)
-
-typedef header_control_message hc_msg_header_t;
-
-typedef union {
-#define _(x) x ## _command 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 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;
-
- return data;
-
-ERR_BUFFER:
- hc_data_free(data);
-ERR_MALLOC:
- return NULL;
-}
-
-void
-hc_data_free(hc_data_t * data)
-{
- if (data->buffer)
- 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;
- }
- return 0;
-}
-
-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;
-
- memcpy(data->buffer + data->size * data->out_element_size, elements,
- count * data->out_element_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);
-}
-
-/**
- *
- * 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;
-}
-
-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;
-}
-
-int
-hc_data_reset(hc_data_t * data)
-{
- data->size = 0;
- return 0;
-}
-
-/******************************************************************************
- * Control socket
- ******************************************************************************/
-
-/**
- * \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.
- */
-int
-hc_sock_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;
-}
-
-hc_sock_t *
-hc_sock_create_url(const char * url)
-{
- hc_sock_t * s = malloc(sizeof(hc_sock_t));
- if (!s)
- goto ERR_MALLOC;
-
- s->url = url ? strdup(url) : NULL;
-
- s->fd = socket(AF_INET, SOCK_STREAM, 0);
- if (s->fd < 0)
- goto ERR_SOCKET;
-
- if (hc_sock_reset(s) < 0)
- goto ERR_RESET;
-
- s->seq = 0;
- s->cur_request = NULL;
-
- s->map = hc_sock_map_create();
- if (!s->map)
- goto ERR_MAP;
-
- return s;
-
- //hc_sock_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;
-}
-
-hc_sock_t *
-hc_sock_create(void)
-{
- return hc_sock_create_url(NULL);
-}
-
-void
-hc_sock_free(hc_sock_t * s)
-{
- hc_sock_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_sock_request_t * request = request_array[i];
- if (hc_sock_map_remove(s->map, request->seq, NULL) < 0)
- ERROR("[hc_sock_process] Error removing request from map");
- hc_sock_request_free(request);
- }
- free(request_array);
- }
-
- hc_sock_map_free(s->map);
- if (s->url)
- free(s->url);
- close(s->fd);
- free(s);
-}
-
-int
-hc_sock_get_next_seq(hc_sock_t * s)
-{
- return s->seq++;
-}
-
-int
-hc_sock_set_nonblocking(hc_sock_t * s)
-{
- return (fcntl(s->fd, F_SETFL, fcntl(s->fd, F_GETFL) | O_NONBLOCK) < 0);
-}
-
-int
-hc_sock_get_fd(hc_sock_t * s)
-{
- return s->fd;
-}
-
-int
-hc_sock_connect(hc_sock_t * s)
-{
- struct sockaddr_storage ss;
- memset(&ss, 0, sizeof(struct sockaddr_storage));
-
- if (hc_sock_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) //sizeof(struct sockaddr)) < 0)
- goto ERR_CONNECT;
-
- return 0;
-
-ERR_CONNECT:
-ERR_PARSE:
- return -1;
-}
-
-int
-hc_sock_send(hc_sock_t * s, hc_msg_t * msg, size_t msglen, int seq)
-{
- int rc;
- msg->hdr.seqNum = seq;
- rc = (int)send(s->fd, msg, msglen, 0);
- if (rc < 0) {
- perror("hc_sock_send");
- return -1;
- }
- return 0;
-}
-
-int
-hc_sock_get_available(hc_sock_t * s, u8 ** buffer, size_t * size)
-{
- *buffer = s->buf + s->woff;
- *size = RECV_BUFLEN - s->woff;
-
- return 0;
-}
-
-int
-hc_sock_recv(hc_sock_t * s)
-{
- int rc;
+ 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)) {
/*
- * This condition should be ensured to guarantee correct processing of
- * messages
+ * 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.
*/
- assert(RECV_BUFLEN - s->woff > MIN_BUFLEN);
-
- 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;
- perror("hc_sock_recv");
- return -1;
- }
- s->woff += rc;
- return rc;
-}
-
-/*
- * Returns -99 in case of internal error, -1 in case of API command failure
- */
-int
-hc_sock_process(hc_sock_t * s, hc_data_t ** data)
-{
- int err = 0;
-
- /* We must have received at least one byte */
- size_t available = s->woff - s->roff;
-
- while(available > 0) {
-
- if (!s->cur_request) { // No message being parsed, alternatively (remaining == 0)
- hc_msg_t * msg = (hc_msg_t*)(s->buf + s->roff);
-
- /* We expect a message header */
- if (available < sizeof(hc_msg_header_t)) {
- break;
- }
-
- hc_sock_request_t * request = NULL;
- if (hc_sock_map_get(s->map, msg->hdr.seqNum, &request) < 0) {
- ERROR("[hc_sock_process] Error searching for matching request");
- return -99;
- }
- if (!request) {
- ERROR("[hc_sock_process] No request matching received sequence number");
- return -99;
- }
-
- s->remaining = msg->hdr.length;
- switch(msg->hdr.messageType) {
- case ACK_LIGHT:
- assert(s->remaining == 1);
- assert(!data);
- s->cur_request = request;
- break;
- case NACK_LIGHT:
- assert(s->remaining == 1);
- assert(!data);
- hc_data_set_error(request->data);
- s->cur_request = request;
- err = -1;
- break;
- case RESPONSE_LIGHT:
- assert(data);
- if (s->remaining == 0) {
- hc_data_set_complete(request->data);
- *data = request->data;
- if (hc_sock_map_remove(s->map, request->seq, NULL) < 0)
- ERROR("[hc_sock_process] Error removing request from map");
- hc_sock_request_free(request);
- } else {
- /* We only remember it if there is still data to parse */
- s->cur_request = request;
- }
- break;
- default:
- ERROR("[hc_sock_process] Invalid response received");
- return -99;
- }
-
- available -= sizeof(hc_msg_header_t);
- s->roff += sizeof(hc_msg_header_t);
- } else {
- /* We expect the complete payload, or at least a chunk of it */
- size_t num_chunks = available / s->cur_request->data->in_element_size;
- if (num_chunks == 0)
- break;
- if (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(s->cur_request->data, s->buf + s->roff, num_chunks);
- } else {
- int rc;
- rc = hc_data_ensure_available(s->cur_request->data, num_chunks);
- if (rc < 0) {
- ERROR("[hc_sock_process] Error in hc_data_ensure_available");
- return -99;
- }
- for (int i = 0; i < num_chunks; i++) {
- u8 * dst = hc_data_get_next(s->cur_request->data);
- if (!dst) {
- ERROR("[hc_sock_process] Error in hc_data_get_next");
- return -99;
- }
-
- rc = s->cur_request->parse(s->buf + s->roff + i * s->cur_request->data->in_element_size, dst);
- if (rc < 0) {
- ERROR("[hc_sock_process] Error in parse");
- err = -99; /* FIXME we let the loop complete (?) */
- }
- s->cur_request->data->size++;
- }
- }
-
- s->remaining -= num_chunks;
- available -= num_chunks * s->cur_request->data->in_element_size;
- s->roff += num_chunks * s->cur_request->data->in_element_size;
- if (s->remaining == 0) {
- if (hc_sock_map_remove(s->map, s->cur_request->seq, NULL) < 0) {
- ERROR("[hc_sock_process] Error removing request from map");
- return -99;
- }
- hc_data_set_complete(s->cur_request->data);
- if (data)
- *data = s->cur_request->data;
- hc_sock_request_free(s->cur_request);
- s->cur_request = NULL;
- }
-
- }
- }
-
- /* Make sure there is enough remaining space in the buffer */
- if (RECV_BUFLEN - s->woff < AVG_BUFLEN) {
- /*
- * There should be no overlap provided a sufficiently large BUFLEN, but
- * who knows.
- */
- memmove(s->buf, s->buf + s->roff, s->woff - s->roff);
- s->woff -= s->roff;
- s->roff = 0;
- }
-
- return err;
-}
-
-int
-hc_sock_callback(hc_sock_t * s, hc_data_t ** pdata)
-{
- hc_data_t * data;
-
- for (;;) {
- int n = hc_sock_recv(s);
- 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;
- default:
- perror("hc_sock_recv");
- goto ERR;
- }
- }
- if (hc_sock_process(s, &data) < 0) {
- goto ERR;
- }
- }
-END:
- if (pdata)
- *pdata = data;
- else
- hc_data_free(data);
- return 0;
-
-ERR:
- hc_data_free(data);
-ERR_EOF:
- return -1;
-}
-
-int
-hc_sock_reset(hc_sock_t * s)
-{
- s->roff = s->woff = 0;
- s->remaining = 0;
- return 0;
-}
-
-/******************************************************************************
- * Command-specific structures and functions
- ******************************************************************************/
-
-typedef int (*HC_PARSE)(const u8 *, u8 *);
-
-typedef struct {
- hc_action_t cmd;
- command_id cmd_id;
- size_t size_in;
- size_t size_out;
- HC_PARSE parse;
-} hc_command_params_t;
-
-int
-hc_execute_command(hc_sock_t * s, hc_msg_t * msg, size_t msg_len,
- hc_command_params_t * params, hc_data_t ** pdata, bool async)
-{
- 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_LIST:
- assert(params->size_in != 0);
- assert(params->size_out != 0);
- assert(params->parse != NULL);
- break;
- case ACTION_SET:
- assert(params->size_in != 0);
- assert(params->size_out == 0);
- assert(params->parse == NULL);
- break;
- default:
- return -1;
+ 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;
}
-
- //hc_sock_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("[hc_execute_command] Could not create data storage");
- goto ERR_DATA;
- }
-
- int seq = hc_sock_get_next_seq(s);
-
- /* 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("[hc_execute_command] Could not create request state");
- goto ERR_REQUEST;
- }
-
- /* Add state to map */
- if (hc_sock_map_add(s->map, seq, request) < 0) {
- ERROR("[hc_execute_command] Error adding request state to map");
- goto ERR_MAP;
- }
-
- if (hc_sock_send(s, msg, msg_len, seq) < 0) {
- ERROR("[hc_execute_command] Error sending message");
- goto ERR_PROCESS;
- }
-
- if (async)
- return 0;
-
- while(!data->complete) {
- /*
- * As the socket is non blocking it might happen that we need to read
- * several times before success... shall we alternate between blocking
- * and non-blocking mode ?
- */
- int n = hc_sock_recv(s);
- if (n == 0)
- goto ERR_EOF;
- if (n < 0)
- continue; //break;
- int rc = hc_sock_process(s, pdata);
- switch(rc) {
- case 0:
- break;
- case -1:
- ret = rc;
- break;
- case -99:
- ERROR("[hc_execute_command] Error processing socket results");
- goto ERR;
- break;
- default:
- ERROR("[hc_execute_command] Unexpected return value");
- goto ERR;
- }
+#if 0
}
+#endif
+ }
+ return 0; /* Continue processing */
-ERR_EOF:
- ret = data->ret;
- if (!data->complete)
- return -1;
- if (!pdata)
- hc_data_free(data);
-
- return ret;
-
+ERR_INIT:
ERR_PROCESS:
-ERR_MAP:
- hc_sock_request_free(request);
-ERR:
-ERR_REQUEST:
- hc_data_free(data);
-ERR_DATA:
- return -99;
-}
-
-/*----------------------------------------------------------------------------*
- * Listeners
- *----------------------------------------------------------------------------*/
-
-/* LISTENER CREATE */
-
-int
-_hc_listener_create(hc_sock_t * s, 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("[_hc_listener_create] Unexpected truncation of listener string");
- DEBUG("[_hc_listener_create] listener=%s async=%s", listener_s,
- BOOLSTR(async));
-
- if (!IS_VALID_FAMILY(listener->family))
- return -1;
-
- if (!IS_VALID_CONNECTION_TYPE(listener->type))
- return -1;
-
- struct {
- header_control_message hdr;
- add_listener_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = ADD_LISTENER,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = listener->local_addr,
- .port = htons(listener->local_port),
- .addressType = (u8)map_to_addr_type[listener->family],
- .listenerMode = (u8)map_to_listener_mode[listener->type],
- .connectionType = (u8)map_to_connection_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.interfaceName, 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 = ADD_LISTENER,
- .size_in = sizeof(add_listener_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_listener_create(hc_sock_t * s, hc_listener_t * listener)
-{
- return _hc_listener_create(s, listener, false);
-}
-
-int
-hc_listener_create_async(hc_sock_t * s, hc_listener_t * listener)
-{
- return _hc_listener_create(s, listener, true);
-}
-
-/* LISTENER GET */
-
-int
-hc_listener_get(hc_sock_t * s, 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 (hc_listener_list(s, &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 */
-
-int
-_hc_listener_delete(hc_sock_t * s, 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("[_hc_listener_delete] Unexpected truncation of listener string");
- DEBUG("[_hc_listener_delete] listener=%s async=%s", listener_s,
- BOOLSTR(async));
-
- struct {
- header_control_message hdr;
- remove_listener_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = REMOVE_LISTENER,
- .length = 1,
- .seqNum = 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 (hc_listener_get(s, 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 = REMOVE_LISTENER,
- .size_in = sizeof(remove_listener_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_listener_delete(hc_sock_t * s, hc_listener_t * listener)
-{
- return _hc_listener_delete(s, listener, false);
-}
-
-int
-hc_listener_delete_async(hc_sock_t * s, hc_listener_t * listener)
-{
- return _hc_listener_delete(s, listener, true);
-}
-
-
-/* LISTENER LIST */
-
-int
-_hc_listener_list(hc_sock_t * s, hc_data_t ** pdata, bool async)
-{
- DEBUG("[hc_listener_list] async=%s", BOOLSTR(async));
-
- struct {
- header_control_message hdr;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = LIST_LISTENERS,
- .length = 0,
- .seqNum = 0,
- },
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_LIST,
- .cmd_id = LIST_LISTENERS,
- .size_in = sizeof(list_listeners_command),
- .size_out = sizeof(hc_listener_t),
- .parse = (HC_PARSE)hc_listener_parse,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, pdata, async);
-}
-
-int
-hc_listener_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_listener_list(s, pdata, false);
-}
-
-int
-hc_listener_list_async(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_listener_list(s, pdata, true);
-}
-
-/* LISTENER VALIDATE */
-
-int
-hc_listener_validate(const hc_listener_t * listener)
-{
- if (!IS_VALID_FAMILY(listener->family))
- return -1;
-
- if (!IS_VALID_CONNECTION_TYPE(listener->type))
- return -1;
-
- return 0;
-}
-
-/* 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 = 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 PARSE */
-
-int
-hc_listener_parse(void * in, hc_listener_t * listener)
-{
- int rc;
-
- list_listeners_command * cmd = (list_listeners_command *)in;
-
- if (!IS_VALID_LIST_LISTENERS_TYPE(cmd->encapType))
- return -1;
-
- hc_connection_type_t type = map_from_encap_type[cmd->encapType];
- if (type == CONNECTION_TYPE_UNDEFINED)
- return -1;
-
- if (!IS_VALID_ADDR_TYPE(cmd->addressType))
- return -1;
-
- int family = map_from_addr_type[cmd->addressType];
- if (!IS_VALID_FAMILY(family))
- return -1;
-
- *listener = (hc_listener_t) {
- .id = cmd->connid,
- .type = type,
- .family = family,
- .local_addr = UNION_CAST(cmd->address, ip_address_t),
- .local_port = ntohs(cmd->port),
- };
- rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "%s", cmd->listenerName);
- if (rc >= SYMBOLIC_NAME_LEN)
- WARN("[hc_listener_parse] Unexpected truncation of symbolic name string");
- rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", cmd->interfaceName);
- if (rc >= INTERFACE_LEN)
- WARN("[hc_listener_parse] Unexpected truncation of interface name string");
- return 0;
-}
-
-GENERATE_FIND(listener)
-
-/* 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", listener->interface_name, local,
- connection_type_str[listener->type]);
-}
-
-/*----------------------------------------------------------------------------*
- * CONNECTION
- *----------------------------------------------------------------------------*/
-
-/* CONNECTION CREATE */
-
-int
-_hc_connection_create(hc_sock_t * s, hc_connection_t * connection, bool async)
-{
- 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_create] Unexpected truncation of connection string");
- DEBUG("[_hc_connection_create] connection=%s async=%s", connection_s, BOOLSTR(async));
-
- if (hc_connection_validate(connection) < 0)
- return -1;
-
- struct {
- header_control_message hdr;
- add_connection_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = ADD_CONNECTION,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .remoteIp = connection->remote_addr,
- .localIp = connection->local_addr,
- .remotePort = htons(connection->remote_port),
- .localPort = htons(connection->local_port),
- .ipType = (u8)map_to_addr_type[connection->family],
- .connectionType = (u8)map_to_connection_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.interfaceName, INTERFACE_NAME_LEN, "%s", connection->interface_name);
-
- hc_command_params_t params = {
- .cmd = ACTION_CREATE,
- .cmd_id = ADD_CONNECTION,
- .size_in = sizeof(add_connection_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_connection_create(hc_sock_t * s, hc_connection_t * connection)
-{
- return _hc_connection_create(s, connection, false);
-}
-
-int
-hc_connection_create_async(hc_sock_t * s, hc_connection_t * connection)
-{
- return _hc_connection_create(s, connection, true);
-}
-
-/* CONNECTION GET */
-
-int
-hc_connection_get(hc_sock_t * s, 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 (hc_connection_list(s, &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 */
-
-int
-_hc_connection_delete(hc_sock_t * s, hc_connection_t * connection, bool async)
-{
- 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_delete] Unexpected truncation of connection string");
- DEBUG("[_hc_connection_delete] connection=%s async=%s", connection_s, BOOLSTR(async));
-
- struct {
- header_control_message hdr;
- remove_connection_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = REMOVE_CONNECTION,
- .length = 1,
- .seqNum = 0,
- },
- };
-
- if (connection->id) {
- rc = snprintf(msg.payload.symbolicOrConnid, 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.symbolicOrConnid, SYMBOLIC_NAME_LEN, "%s", connection->name);
- if (rc >= SYMBOLIC_NAME_LEN)
- WARN("[_hc_connection_delete] Unexpected truncation of symbolic name string");
+ return -1;
+}
+
+// -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 = hc_sock_on_receive(s, 0);
+ if (rc < 0) return -1;
+
+ 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);
+ }
+ }
+
+ // XXX need same for async
+ if (rc != 1) return 0;
+
+ 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_connection_t * connection_found;
- if (hc_connection_get(s, connection, &connection_found) < 0)
- return -1;
- if (!connection_found)
- return -1;
- rc = snprintf(msg.payload.symbolicOrConnid, 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_request_reset_data(request);
}
-
- hc_command_params_t params = {
- .cmd = ACTION_DELETE,
- .cmd_id = REMOVE_CONNECTION,
- .size_in = sizeof(remove_connection_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
+ hc_request_on_complete(request);
+ // hc_sock_free_request(s, request);
+ }
+ return 1;
}
-int
-hc_connection_delete(hc_sock_t * s, hc_connection_t * connection)
-{
- return _hc_connection_delete(s, connection, false);
-}
+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_delete_async(hc_sock_t * s, hc_connection_t * connection)
-{
- return _hc_connection_delete(s, connection, true);
+ /* If request is complete, stop */
+ if (rc == 1) break;
+ }
+ return 0;
}
-/* CONNECTION LIST */
-
-int
-_hc_connection_list(hc_sock_t * s, hc_data_t ** pdata, bool async)
-{
- DEBUG("[hc_connection_list] async=%s", BOOLSTR(async));
-
- struct {
- header_control_message hdr;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = LIST_CONNECTIONS,
- .length = 0,
- .seqNum = 0,
- },
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_LIST,
- .cmd_id = LIST_CONNECTIONS,
- .size_in = sizeof(list_connections_command),
- .size_out = sizeof(hc_connection_t),
- .parse = (HC_PARSE)hc_connection_parse,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, pdata, async);
-}
-
-int
-hc_connection_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_connection_list(s, pdata, false);
-}
-
-int
-hc_connection_list_async(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_connection_list(s, pdata, true);
-}
-
-/* CONNECTION VALIDATE */
-
-int
-hc_connection_validate(const hc_connection_t * connection)
-{
- if (!IS_VALID_FAMILY(connection->family))
- return -1;
-
- if (!IS_VALID_CONNECTION_TYPE(connection->type))
- return -1;
-
- /* TODO assert both local and remote have the right family */
-
- 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;
-
- rc = INT_CMP(c1->remote_port, c2->remote_port);
- if (rc != 0)
- return rc;
-
- return rc;
-}
-
-/* CONNECTION PARSE */
-
-int
-hc_connection_parse(void * in, hc_connection_t * connection)
-{
- int rc;
- list_connections_command * cmd = (list_connections_command *)in;
-
- if (!IS_VALID_LIST_CONNECTIONS_TYPE(cmd->connectionData.connectionType))
- return -1;
-
- hc_connection_type_t type = map_from_list_connections_type[cmd->connectionData.connectionType];
- if (type == CONNECTION_TYPE_UNDEFINED)
- return -1;
-
- if (!IS_VALID_LIST_CONNECTIONS_STATE(cmd->state))
- return -1;
-
- hc_connection_state_t state = map_from_list_connections_state[cmd->state];
- if (state == HC_CONNECTION_STATE_UNDEFINED)
- return -1;
-
- if (!IS_VALID_ADDR_TYPE(cmd->connectionData.ipType))
- return -1;
-
- int family = map_from_addr_type[cmd->connectionData.ipType];
- if (!IS_VALID_FAMILY(family))
- return -1;
-
- *connection = (hc_connection_t) {
- .id = cmd->connid,
- .type = type,
- .family = family,
- .local_addr = cmd->connectionData.localIp,
- //.local_addr = UNION_CAST(cmd->connectionData.localIp, ip_address_t),
- .local_port = ntohs(cmd->connectionData.localPort),
- .remote_addr = cmd->connectionData.remoteIp,
- //.remote_addr = UNION_CAST(cmd->connectionData.remoteIp, ip_address_t),
- .remote_port = ntohs(cmd->connectionData.remotePort),
- .admin_state = cmd->connectionData.admin_state,
-#ifdef WITH_POLICY
- .priority = cmd->connectionData.priority,
- .tags = cmd->connectionData.tags,
-#endif /* WITH_POLICY */
- .state = state,
- };
- rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", cmd->connectionData.symbolic);
- if (rc >= SYMBOLIC_NAME_LEN)
- WARN("[hc_connection_parse] Unexpected truncation of symbolic name string");
- rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", cmd->interfaceName);
- if (rc >= INTERFACE_LEN)
- WARN("[hc_connection_parse] Unexpected truncation of interface name string");
- return 0;
-}
-
-GENERATE_FIND(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)
-
- 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 %s",
- connection_state_str[connection->state],
- connection->interface_name,
- local,
- remote,
- connection_type_str[connection->type]);
-}
-
-/* CONNECTION SET ADMIN STATE */
-
-int
-_hc_connection_set_admin_state(hc_sock_t * s, 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 {
- header_control_message hdr;
- connection_set_admin_state_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = CONNECTION_SET_ADMIN_STATE,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .admin_state = state,
- },
- };
- rc = snprintf(msg.payload.symbolicOrConnid, 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 = CONNECTION_SET_ADMIN_STATE,
- .size_in = sizeof(connection_set_admin_state_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_connection_set_admin_state(hc_sock_t * s, const char * conn_id_or_name,
- face_state_t state)
-{
- return _hc_connection_set_admin_state(s, conn_id_or_name, state, false);
-}
-
-int
-hc_connection_set_admin_state_async(hc_sock_t * s, const char * conn_id_or_name,
- face_state_t state)
-{
- return _hc_connection_set_admin_state(s, conn_id_or_name, state, true);
-}
-
-int
-_hc_connection_set_priority(hc_sock_t * s, 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 {
- header_control_message hdr;
- connection_set_priority_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = CONNECTION_SET_PRIORITY,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .priority = priority,
- },
- };
- rc = snprintf(msg.payload.symbolicOrConnid, 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 = CONNECTION_SET_PRIORITY,
- .size_in = sizeof(connection_set_priority_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_connection_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, false);
-}
-
-int
-hc_connection_set_priority_async(hc_sock_t * s, const char * conn_id_or_name,
- uint32_t priority)
-{
- return _hc_connection_set_priority(s, conn_id_or_name, priority, true);
-}
-
-int
-_hc_connection_set_tags(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 {
- header_control_message hdr;
- connection_set_tags_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = CONNECTION_SET_TAGS,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .tags = tags,
- },
- };
- rc = snprintf(msg.payload.symbolicOrConnid, 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 = CONNECTION_SET_TAGS,
- .size_in = sizeof(connection_set_tags_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_connection_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, false);
-}
-
-int
-hc_connection_set_tags_async(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, true);
-}
-
-/*----------------------------------------------------------------------------*
- * Routes
- *----------------------------------------------------------------------------*/
-
-/* ROUTE CREATE */
-
-int
-_hc_route_create(hc_sock_t * s, 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_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 (!IS_VALID_FAMILY(route->family))
- return -1;
-
- struct {
- header_control_message hdr;
- add_route_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = ADD_ROUTE,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = route->remote_addr,
- .cost = route->cost,
- .addressType = (u8)map_to_addr_type[route->family],
- .len = route->len,
- }
- };
-
- /*
- * The route commands expects the ID (or name that we don't use) as part of
- * the symbolicOrConnid attribute.
- */
- rc = snprintf(msg.payload.symbolicOrConnid, 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 = ADD_ROUTE,
- .size_in = sizeof(add_route_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_route_create(hc_sock_t * s, hc_route_t * route)
-{
- return _hc_route_create(s, route, false);
-}
-
-int
-hc_route_create_async(hc_sock_t * s, hc_route_t * route)
-{
- return _hc_route_create(s, route, true);
-}
-
-/* ROUTE DELETE */
-
-int
-_hc_route_delete(hc_sock_t * s, 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 {
- header_control_message hdr;
- remove_route_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = REMOVE_ROUTE,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = route->remote_addr,
- .addressType = (u8)map_to_addr_type[route->family],
- .len = route->len,
- }
- };
-
- /*
- * The route commands expects the ID (or name that we don't use) as part of
- * the symbolicOrConnid attribute.
- */
- snprintf(msg.payload.symbolicOrConnid, SYMBOLIC_NAME_LEN, "%d", route->face_id);
-
- hc_command_params_t params = {
- .cmd = ACTION_DELETE,
- .cmd_id = REMOVE_ROUTE,
- .size_in = sizeof(remove_route_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_route_delete(hc_sock_t * s, hc_route_t * route)
-{
- return _hc_route_delete(s, route, false);
-}
-
-int
-hc_route_delete_async(hc_sock_t * s, hc_route_t * route)
-{
- return _hc_route_delete(s, route, true);
-}
-
-/* ROUTE LIST */
-
-int
-_hc_route_list(hc_sock_t * s, hc_data_t ** pdata, bool async)
-{
- //DEBUG("[hc_route_list] async=%s", BOOLSTR(async));
-
- struct {
- header_control_message hdr;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = LIST_ROUTES,
- .length = 0,
- .seqNum = 0,
- },
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_LIST,
- .cmd_id = LIST_ROUTES,
- .size_in = sizeof(list_routes_command),
- .size_out = sizeof(hc_route_t),
- .parse = (HC_PARSE)hc_route_parse,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, pdata, async);
+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));
+
+ if (hc_sock_is_async(s) && !s->ops.get_fd) {
+ return -1; /* No async support */
+ }
+
+ // 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;
+ }
+
+ 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;
+ }
+ }
+
+ /* 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;
+
+ if (hc_sock_is_async(s)) return 0;
+
+ 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);
+ }
+ }
+
+ return 0;
+
+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_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_route_list(s, pdata, false);
+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_async(hc_sock_t * s)
-{
- return _hc_route_list(s, NULL, true);
-}
-
-/* ROUTE PARSE */
-
-int
-hc_route_parse(void * in, hc_route_t * route)
-{
- list_routes_command * cmd = (list_routes_command *) in;
-
- if (!IS_VALID_ADDR_TYPE(cmd->addressType)) {
- ERROR("[hc_route_parse] Invalid address type");
- return -1;
- }
-
- int family = map_from_addr_type[cmd->addressType];
- if (!IS_VALID_FAMILY(family)) {
- ERROR("[hc_route_parse] Invalid address family");
- return -1;
- }
-
- *route = (hc_route_t) {
- .face_id = cmd->connid,
- .family = family,
- .remote_addr = UNION_CAST(cmd->address, ip_address_t),
- .len = cmd->len,
- .cost = cmd->cost,
- };
- return 0;
-}
-
-/* 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, "%*d %*d %s %*d", MAXSZ_FACE_ID, route->face_id,
- MAXSZ_COST, route->cost, prefix, MAXSZ_LEN, route->len);
+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);
}
/*----------------------------------------------------------------------------*
- * 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.
- *
+ * VFT
*----------------------------------------------------------------------------*/
-/* 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 */
-
-int
-hc_listener_to_face(const hc_listener_t * listener, hc_face_t * face)
-{
- return -1; /* XXX Not implemented */
-}
-
-/* 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 = CONNECTION_TYPE_HICN,
- .family = f->family,
- .local_addr = f->local_addr,
- .local_port = 0,
- .remote_addr = f->remote_addr,
- .remote_port = 0,
- .admin_state = face_state_to_connection_state(f->admin_state),
- .state = face_state_to_connection_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 = CONNECTION_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 = face_state_to_connection_state(f->admin_state),
- .state = face_state_to_connection_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 = CONNECTION_TYPE_UDP,
- .family = AF_INET,
- .local_addr = f->local_addr,
- .local_port = f->local_port,
- .remote_addr = f->remote_addr,
- .remote_port = f->remote_port,
- .admin_state = face_state_to_connection_state(f->admin_state),
- .state = face_state_to_connection_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;
- }
-
- 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 CONNECTION_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_state_to_face_state(connection->admin_state),
- .state = connection_state_to_face_state(connection->state),
-#ifdef WITH_POLICY
- .priority = connection->priority,
- .tags = connection->tags,
-#endif /* WITH_POLICY */
- },
- };
- break;
- case CONNECTION_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_state_to_face_state(connection->admin_state),
- .state = connection_state_to_face_state(connection->state),
-#ifdef WITH_POLICY
- .priority = connection->priority,
- .tags = connection->tags,
-#endif /* WITH_POLICY */
- },
- };
- break;
- case CONNECTION_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_state_to_face_state(connection->admin_state),
- .state = connection_state_to_face_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;
- *listener = (hc_listener_t) {
- .id = ~0,
- .type = connection->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;
-}
-
-/* FACE CREATE */
-
-int
-hc_face_create(hc_sock_t * s, 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, 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 (hc_listener_get(s, &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(s, &listener) < 0) {
- ERROR("[hc_face_create] Could not create listener.");
- free(listener_found);
- return -1;
- }
- } else {
- free(listener_found);
- }
-
- /* Create corresponding connection */
- if (hc_connection_create(s, &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 (hc_connection_get(s, &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(s, &listener) < 0) {
- ERROR("[hc_face_create] Could not create listener.");
- return -1;
- }
- return -1;
- break;
- default:
- ERROR("[hc_face_create] Unknwon face type.");
-
- return -1;
- };
-
- return 0;
+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_face_get(hc_sock_t * s, 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 (hc_connection_get(s, &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 (hc_listener_get(s, &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;
-
+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);
}
-/* FACE DELETE */
-
-int
-hc_face_delete(hc_sock_t * s, hc_face_t * face)
-{
- 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 (hc_connection_delete(s, &connection) < 0) {
- ERROR("[hc_face_delete] Error removing connection");
- return -1;
- }
-
- /* If this is the last connection attached to the listener, remove it */
-
- hc_data_t * connections;
- hc_listener_t listener = {{0}};
-
- /*
- * Ensure we have a corresponding local listener
- * NOTE: hc_face_to_listener is not appropriate
- */
- if (hc_connection_to_local_listener(&connection, &listener) < 0) {
- ERROR("[hc_face_create] Could not convert face to local listener.");
- return -1;
+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;
}
-#if 1
- /*
- * The name is generated to prepare listener creation, we need it to be
- * empty for deletion. The id should not need to be reset though.
- */
- listener.id = 0;
- memset(listener.name, 0, sizeof(listener.name));
+ };
#endif
- if (hc_connection_list(s, &connections) < 0) {
- ERROR("[hc_face_delete] Error getting the list of listeners");
- return -1;
- }
-
- bool delete = true;
- foreach_connection(c, connections) {
- if ((ip_address_cmp(&c->local_addr, &listener.local_addr, c->family) == 0) &&
- (c->local_port == listener.local_port) &&
- (strcmp(c->interface_name, listener.interface_name) == 0)) {
- delete = false;
- }
- }
-
- if (delete) {
- if (hc_listener_delete(s, &listener) < 0) {
- ERROR("[hc_face_delete] Error removing listener");
- return -1;
- }
- }
-
- hc_data_free(connections);
-
- return 0;
-
-
-}
-
-/* FACE LIST */
-
-int
-hc_face_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- hc_data_t * connection_data;
- hc_face_t face;
-
- //DEBUG("[hc_face_list]");
-
- if (hc_connection_list(s, &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);
- return 0;
-
-ERR:
- hc_data_free(connection_data);
- return -1;
-}
-
-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;
-}
-
-
-int
-hc_face_list_async(hc_sock_t * s)
-{
- struct {
- header_control_message hdr;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = LIST_CONNECTIONS,
- .length = 0,
- .seqNum = 0,
- },
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_LIST,
- .cmd_id = LIST_CONNECTIONS,
- .size_in = sizeof(list_connections_command),
- .size_out = sizeof(hc_face_t),
- .parse = (HC_PARSE)hc_connection_parse_to_face,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, true);
-}
-
-/* /!\ 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;
-
- 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;
- }
-
- // [#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 */
-}
-
-int
-hc_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);
-}
-
-int
-hc_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);
-}
-
-int
-hc_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);
-}
-
-/*----------------------------------------------------------------------------*
- * Punting
- *----------------------------------------------------------------------------*/
-
-int
-_hc_punting_create(hc_sock_t * s, hc_punting_t * punting, bool async)
-{
- int rc;
-
- if (hc_punting_validate(punting) < 0)
- return -1;
-
- struct {
- header_control_message hdr;
- add_punting_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = ADD_PUNTING,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = punting->prefix,
- .addressType = (u8)map_to_addr_type[punting->family],
- .len = punting->prefix_len,
- }
- };
- rc = snprintf(msg.payload.symbolicOrConnid, 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 = ADD_PUNTING,
- .size_in = sizeof(add_punting_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_punting_create(hc_sock_t * s, hc_punting_t * punting)
-{
- return _hc_punting_create(s, punting, false);
-}
-
-int
-hc_punting_create_async(hc_sock_t * s, hc_punting_t * punting)
-{
- return _hc_punting_create(s, punting, true);
-}
-
-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;
-}
-
-int hc_punting_delete(hc_sock_t * s, hc_punting_t * punting)
-{
- ERROR("hc_punting_delete not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- ERROR("hc_punting_list not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_validate(const hc_punting_t * punting)
-{
- if (!IS_VALID_FAMILY(punting->family))
- return -1;
-
- /*
- * We might use the zero value to add punting on all faces but this is not
- * (yet) implemented
- */
- if (punting->face_id == 0) {
- ERROR("Punting on all faces is not (yet) implemented.");
- return -1;
- }
-
- return 0;
-}
-
-int hc_punting_cmp(const hc_punting_t * p1, const hc_punting_t * p2)
-{
- int rc;
-
- rc = INT_CMP(p1->face_id, p2->face_id);
- if (rc != 0)
- return rc;
-
- rc = INT_CMP(p1->family, p2->family);
- if (rc != 0)
- return rc;
-
- rc = ip_address_cmp(&p1->prefix, &p2->prefix, p1->family);
- if (rc != 0)
- return rc;
-
- rc = INT_CMP(p1->prefix_len, p2->prefix_len);
- if (rc != 0)
- return rc;
-
- return rc;
-}
-
-int hc_punting_parse(void * in, hc_punting_t * punting)
-{
- ERROR("hc_punting_parse not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_snprintf(char * s, size_t size, hc_punting_t * punting)
-{
- ERROR("hc_punting_snprintf not (yet) implemented.");
- return -1;
-}
-
-
-/*----------------------------------------------------------------------------*
- * Cache
- *----------------------------------------------------------------------------*/
-
-int
-_hc_cache_set_store(hc_sock_t * s, int enabled, bool async)
-{
- struct {
- header_control_message hdr;
- cache_store_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = CACHE_STORE,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .activate = enabled,
- }
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_SET,
- .cmd_id = CACHE_STORE,
- .size_in = sizeof(cache_store_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
+ *found = NULL; /* this is optional */
+ return 0;
}
-int
-hc_cache_set_store(hc_sock_t * s, int enabled)
-{
- return _hc_cache_set_store(s, enabled, false);
+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);
}
-int
-hc_cache_set_store_async(hc_sock_t * s, int enabled)
-{
- return _hc_cache_set_store(s, enabled, true);
+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);
}
-
-int
-_hc_cache_set_serve(hc_sock_t * s, int enabled, bool async)
-{
- struct {
- header_control_message hdr;
- cache_serve_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = CACHE_SERVE,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .activate = enabled,
- }
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_SET,
- .cmd_id = CACHE_SERVE,
- .size_in = sizeof(cache_serve_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_cache_set_serve(hc_sock_t * s, int enabled)
-{
- return _hc_cache_set_serve(s, enabled, false);
-}
-
-int
-hc_cache_set_serve_async(hc_sock_t * s, int enabled)
-{
- return _hc_cache_set_serve(s, enabled, true);
-}
-
-/*----------------------------------------------------------------------------*
- * Strategy
- *----------------------------------------------------------------------------*/
-
-// per prefix
-int
-hc_strategy_set(hc_sock_t * s /* XXX */)
-{
- return 0;
-}
-
-/* How to retrieve that from the forwarder ? */
-static const char * strategies[] = {
- "random",
- "load_balancer",
-};
-
-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
-
-int
-hc_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_HC_STRATEGY, "%s", strategies[i]);
- if (rc >= MAXSZ_HC_STRATEGY)
- WARN("[hc_strategy_list] Unexpected truncation of strategy name string");
- (*data)->size++;
- }
-
- return 0;
-}
-
-/* /!\ 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
- *----------------------------------------------------------------------------*/
-
-// per connection
-int
-hc_wldr_set(hc_sock_t * s /* XXX */)
-{
- return 0;
-}
-
-/*----------------------------------------------------------------------------*
- * MAP-Me
- *----------------------------------------------------------------------------*/
-
-int
-hc_mapme_set(hc_sock_t * s, int enabled)
-{
- return 0;
-}
-
-int
-hc_mapme_set_discovery(hc_sock_t * s, int enabled)
-{
- return 0;
-}
-
-int
-hc_mapme_set_timescale(hc_sock_t * s, double timescale)
-{
- return 0;
-}
-
-int
-hc_mapme_set_retx(hc_sock_t * s, double timescale)
-{
- return 0;
-}
-
-/*----------------------------------------------------------------------------*
- * Policy
- *----------------------------------------------------------------------------*/
-
-#ifdef WITH_POLICY
-
-/* POLICY CREATE */
-
-int
-_hc_policy_create(hc_sock_t * s, hc_policy_t * policy, bool async)
-{
- if (!IS_VALID_FAMILY(policy->family))
- return -1;
-
- struct {
- header_control_message hdr;
- add_policy_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = ADD_POLICY,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = policy->remote_addr,
- .addressType = (u8)map_to_addr_type[policy->family],
- .len = policy->len,
- .policy = policy->policy,
- }
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_CREATE,
- .cmd_id = ADD_POLICY,
- .size_in = sizeof(add_policy_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_policy_create(hc_sock_t * s, hc_policy_t * policy)
-{
- return _hc_policy_create(s, policy, false);
-}
-
-int
-hc_policy_create_async(hc_sock_t * s, hc_policy_t * policy)
-{
- return _hc_policy_create(s, policy, true);
-}
-
-/* POLICY DELETE */
-
-int
-_hc_policy_delete(hc_sock_t * s, hc_policy_t * policy, bool async)
-{
- if (!IS_VALID_FAMILY(policy->family))
- return -1;
-
- struct {
- header_control_message hdr;
- remove_policy_command payload;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = REMOVE_POLICY,
- .length = 1,
- .seqNum = 0,
- },
- .payload = {
- .address = policy->remote_addr,
- .addressType = (u8)map_to_addr_type[policy->family],
- .len = policy->len,
- }
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_DELETE,
- .cmd_id = REMOVE_POLICY,
- .size_in = sizeof(remove_policy_command),
- .size_out = 0,
- .parse = NULL,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, NULL, async);
-}
-
-int
-hc_policy_delete(hc_sock_t * s, hc_policy_t * policy)
-{
- return _hc_policy_delete(s, policy, false);
-}
-
-int
-hc_policy_delete_async(hc_sock_t * s, hc_policy_t * policy)
-{
- return _hc_policy_delete(s, policy, true);
-}
-
-/* POLICY LIST */
-
-int
-_hc_policy_list(hc_sock_t * s, hc_data_t ** pdata, bool async)
-{
- struct {
- header_control_message hdr;
- } msg = {
- .hdr = {
- .messageType = REQUEST_LIGHT,
- .commandID = LIST_POLICIES,
- .length = 0,
- .seqNum = 0,
- },
- };
-
- hc_command_params_t params = {
- .cmd = ACTION_LIST,
- .cmd_id = LIST_POLICIES,
- .size_in = sizeof(list_policies_command),
- .size_out = sizeof(hc_policy_t),
- .parse = (HC_PARSE)hc_policy_parse,
- };
-
- return hc_execute_command(s, (hc_msg_t*)&msg, sizeof(msg), &params, pdata, async);
-}
-
-int
-hc_policy_list(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_policy_list(s, pdata, false);
-}
-
-int
-hc_policy_list_async(hc_sock_t * s, hc_data_t ** pdata)
-{
- return _hc_policy_list(s, pdata, true);
-}
-
-/* POLICY PARSE */
-
-int
-hc_policy_parse(void * in, hc_policy_t * policy)
-{
- list_policies_command * cmd = (list_policies_command *) in;
-
- if (!IS_VALID_ADDR_TYPE(cmd->addressType))
- return -1;
-
- int family = map_from_addr_type[cmd->addressType];
- if (!IS_VALID_FAMILY(family))
- return -1;
-
- *policy = (hc_policy_t) {
- .family = family,
- .remote_addr = UNION_CAST(cmd->address, ip_address_t),
- .len = cmd->len,
- .policy = cmd->policy,
- };
- return 0;
-}
-
-/* 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;
-}
-
-#endif /* WITH_POLICY */
diff --git a/ctrl/libhicnctrl/src/api_private.h b/ctrl/libhicnctrl/src/api_private.h
new file mode 100644
index 000000000..2f483ad2d
--- /dev/null
+++ b/ctrl/libhicnctrl/src/api_private.h
@@ -0,0 +1,78 @@
+/*
+ * 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 HICN_API_PRIVATE_H
+#define HICN_API_PRIVATE_H
+
+#include <hicn/ctrl/api.h>
+#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 ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
+
+/*
+ * Input validation
+ */
+
+static inline bool IS_VALID_ADDRESS(const hicn_ip_address_t *addr, int family) {
+ char addr_str[INET6_ADDRSTRLEN];
+ 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) {
+ return len <= MAX_IPV6_PREFIX_LEN;
+}
+
+// https://github.com/shemminger/iproute2/blob/50b668bdbf0ebc270495eb4b352d0c3982159d0a/lib/utils.c#L825
+static inline bool IS_VALID_INTERFACE_NAME(const char *name) {
+ size_t len = strnlen_s(name, INTERFACE_LEN);
+ if (len == 0 || len >= IFNAMSIZ) return true;
+
+ while (*name) {
+ if (*name == '/' || isspace(*name)) return false;
+ ++name;
+ }
+
+ return true;
+}
+
+static inline bool IS_VALID_NAME(const char *name) {
+ return is_symbolic_name(name, SYMBOLIC_NAME_LEN);
+}
+
+static inline bool IS_VALID_STR_ID(const char *name) {
+ return is_number(name, SYMBOLIC_NAME_LEN);
+}
+
+#define IS_VALID_TYPE(x) IS_VALID_ENUM_TYPE(FACE_TYPE, x)
+#define IS_VALID_ADDR_TYPE(x) ((x >= ADDR_INET) && (x <= ADDR_UNIX))
+#define IS_VALID_FACE_ID(x) ((x) != INVALID_FACE_ID)
+
+#define IS_VALID_ID(x) (1)
+#define IS_VALID_POLICY(x) (1)
+
+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);
+
+#endif // HICN_API_PRIVATE_H
diff --git a/ctrl/libhicnctrl/src/cli.c b/ctrl/libhicnctrl/src/cli.c
deleted file mode 100644
index fc81139d6..000000000
--- a/ctrl/libhicnctrl/src/cli.c
+++ /dev/null
@@ -1,868 +0,0 @@
-/*
- * Copyright (c) 2017-2019 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 cli.c
- * \brief Command line interface
- */
-#include <ctype.h> // isalpha isalnum
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h> // getopt
-
-#include <hicn/ctrl.h>
-#include <hicn/util/ip_address.h>
-#include <hicn/util/token.h>
-
-
-#define die(LABEL, MESSAGE) do { \
- printf(MESSAGE "\n"); \
- rc = -1; \
- goto ERR_ ## LABEL; \
-} while(0)
-
-#define foreach_object \
- _(UNDEFINED) \
- _(FACE) \
- _(ROUTE) \
- _(STRATEGY) \
- _(LISTENER) \
- _(CONNECTION) \
- _(N)
-
-typedef enum {
-#define _(x) OBJECT_ ## x,
-foreach_object
-#undef _
-} hc_object_t;
-
-void
-usage_header()
-{
- fprintf(stderr, "Usage:\n");
-}
-
-void
-usage_face_create(const char * prog, bool header, bool verbose)
-{
-
- if (header)
- usage_header();
- fprintf(stderr, "%s -f TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Create a face on specified address and port.\n");
-}
-
-void
-usage_face_delete(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -df ID\n", prog);
- //fprintf(stderr, "%s -df NAME\n", prog);
- fprintf(stderr, "%s -df TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Delete a face...\n");
-}
-
-void
-usage_face_list(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -F\n", prog);
- if (verbose)
- fprintf(stderr, " List all faces.\n");
-}
-
-void
-usage_face(const char * prog, bool header, bool verbose)
-{
- usage_face_create(prog, header, verbose);
- usage_face_delete(prog, header, verbose);
- usage_face_list(prog, header, verbose);
-}
-
-void
-usage_route_create(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -r FACE_ID PREFIX [COST]\n", prog);
- //fprintf(stderr, "%s -r [FACE_ID|NAME] PREFIX [COST]\n", prog);
- if (verbose)
- fprintf(stderr, " Create a route...\n");
-}
-
-void
-usage_route_delete(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -dr FACE_ID PREFIX\n", prog);
- //fprintf(stderr, "%s -dr [FACE_ID|NAME] PREFIX\n", prog);
- if (verbose)
- fprintf(stderr, " Delete a route...\n");
-}
-
-void
-usage_route_list(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -R\n", prog);
- if (verbose)
- fprintf(stderr, " List all routes.\n");
-}
-
-void
-usage_route(const char * prog, bool header, bool verbose)
-{
- usage_route_create(prog, header, verbose);
- usage_route_delete(prog, header, verbose);
- usage_route_list(prog, header, verbose);
-}
-
-void
-usage_forwarding_strategy_create(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
-}
-void
-usage_forwarding_strategy_delete(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
-}
-
-void
-usage_forwarding_strategy_list(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -S\n", prog);
- if (verbose)
- fprintf(stderr, " List all availble forwarding strategies.\n");
-}
-
-void
-usage_forwarding_strategy(const char * prog, bool header, bool verbose)
-{
- usage_forwarding_strategy_create(prog, header, verbose);
- usage_forwarding_strategy_delete(prog, header, verbose);
- usage_forwarding_strategy_list(prog, header, verbose);
-}
-
-void
-usage_listener_create(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -l NAME TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Create a listener on specified address and port.\n");
-}
-
-void
-usage_listener_delete(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -dl ID\n", prog);
- fprintf(stderr, "%s -dl NAME\n", prog);
- fprintf(stderr, "%s -dl TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Delete a listener...\n");
-}
-
-void
-usage_listener_list(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -L\n", prog);
- if (verbose)
- fprintf(stderr, " List all listeners.\n");
-}
-
-void
-usage_listener(const char * prog, bool header, bool verbose)
-{
- usage_listener_create(prog, header, verbose);
- usage_listener_delete(prog, header, verbose);
- usage_listener_list(prog, header, verbose);
-}
-void
-usage_connection_create(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -c NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Create a connection on specified address and port.\n");
-}
-
-void
-usage_connection_delete(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -dc ID\n", prog);
- fprintf(stderr, "%s -dc NAME\n", prog);
- fprintf(stderr, "%s -dc TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT [INTERFACE_NAME]\n", prog);
- if (verbose)
- fprintf(stderr, " Delete a connection...\n");
-}
-
-void
-usage_connection_list(const char * prog, bool header, bool verbose)
-{
- if (header)
- usage_header();
- fprintf(stderr, "%s -C\n", prog);
- if (verbose)
- fprintf(stderr, " List all connections.\n");
-}
-
-void
-usage_connection(const char * prog, bool header, bool verbose)
-{
- usage_connection_create(prog, header, verbose);
- usage_connection_delete(prog, header, verbose);
- usage_connection_list(prog, header, verbose);
-}
-
-void usage(const char * prog)
-{
- fprintf(stderr, "Usage: %s [ [-d] [-f|-l|-c|-r] PARAMETERS | [-F|-L|-C|-R] ]\n", prog);
- fprintf(stderr, "\n");
- fprintf(stderr, "High-level commands\n");
- fprintf(stderr, "\n");
- usage_face(prog, false, true);
- usage_route(prog, false, true);
- usage_forwarding_strategy(prog, false, true);
- fprintf(stderr, "\n");
- fprintf(stderr, "Low level commands (hicn-light specific)\n");
- fprintf(stderr, "\n");
- usage_listener(prog, false, true);
- usage_connection(prog, false, true);
-}
-
-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;
-
-/**
- * Return true if string is purely an integer
- */
-static inline
-bool
-is_number(const char *string) {
- size_t len = strlen(string);
- for (size_t i = 0; i < len; i++)
- if (!isdigit(string[i]))
- return false;
- return true;
-}
-
-/**
- * A symbolic name must be at least 1 character and must begin with an alpha.
- * The remainder must be an alphanum.
- */
-static inline
-bool
-is_symbolic_name(const char *name)
-{
- size_t len = strlen(name);
- if (len <= 0)
- return false;
- if (!isalpha(name[0]))
- return false;
- for (size_t i = 1; i < len; i++) {
- if (!isalnum(name[i]))
- return false;
- }
- return true;
-}
-
-face_type_t
-face_type_from_str(const char * str)
-{
-#define _(x) \
- if (strcasecmp(str, STRINGIZE(x)) == 0) \
- return FACE_TYPE_ ## x; \
- else
-foreach_face_type
-#undef _
- return FACE_TYPE_UNDEFINED;
-}
-
-
-int
-parse_options(int argc, char *argv[], hc_command_t * command)
-{
- command->object = OBJECT_UNDEFINED;
- command->action = ACTION_CREATE;
- int opt;
- int family;
-
- while ((opt = getopt(argc, argv, "dflcrFLCRSh")) != -1) {
- switch (opt) {
- case 'd':
- command->action = ACTION_DELETE;
- break;
- case 'f':
- command->object = OBJECT_FACE;
- break;
- case 'l':
- command->object = OBJECT_LISTENER;
- break;
- case 'c':
- command->object = OBJECT_CONNECTION;
- break;
- case 'r':
- command->object = OBJECT_ROUTE;
- break;
- case 'F':
- command->action = ACTION_LIST;
- command->object = OBJECT_FACE;
- break;
- case 'L':
- command->action = ACTION_LIST;
- command->object = OBJECT_LISTENER;
- break;
- case 'C':
- command->action = ACTION_LIST;
- command->object = OBJECT_CONNECTION;
- break;
- case 'R':
- command->action = ACTION_LIST;
- command->object = OBJECT_ROUTE;
- break;
- case 'S':
- command->action = ACTION_LIST;
- command->object = OBJECT_STRATEGY;
- break;
- default: /* "h" */
- usage(argv[0]);
- exit(EXIT_SUCCESS);
- }
- }
-
- if (command->object == OBJECT_UNDEFINED) {
- fprintf(stderr, "Missing object specification: connection | listener | route\n");
- return -1;
- }
-
- /* Parse and validate parameters for add/delete */
- switch(command->object) {
- 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->face.face.type = face_type_from_str(argv[optind++]);
- if (command->face.face.type == FACE_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->face.face.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->face.face.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->face.face.local_addr) < 0)
- goto ERR_PARAM;
- command->face.face.local_port = atoi(argv[optind++]);
- family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(family) || (command->face.face.family != family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->face.face.remote_addr) < 0)
- goto ERR_PARAM;
- command->face.face.remote_port = atoi(argv[optind++]);
- if (argc != optind) {
- //netdevice_set_name(&command->face.face.netdevice, argv[optind++]);
- command->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])) {
- command->face.id = atoi(argv[optind++]);
- snprintf(command->face.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- //} else if (is_symbolic_name(argv[optind])) {
- // snprintf(command->face.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- } else {
- fprintf(stderr, "Invalid argument\n");
- goto ERR_PARAM;
- }
- } else {
- command->face.face.type = face_type_from_str(argv[optind++]);
- if (command->face.face.type == FACE_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->face.face.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->face.face.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->face.face.local_addr) < 0)
- goto ERR_PARAM;
- command->face.face.local_port = atoi(argv[optind++]);
- family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(family) || (command->face.face.family != family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->face.face.remote_addr) < 0)
- goto ERR_PARAM;
- command->face.face.remote_port = atoi(argv[optind++]);
- if (argc != optind) {
- command->face.face.netdevice.index = atoi(argv[optind++]);
- //netdevice_set_name(&command->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;
- }
- 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->route.face_id = atoi(argv[optind++]);
-
- {
- ip_prefix_t prefix;
- ip_prefix_pton(argv[optind++], &prefix);
- command->route.family = prefix.family;
- command->route.remote_addr = prefix.address;
- command->route.len = prefix.len;
- }
-
- if (argc != optind) {
- printf("parse cost\n");
- command->route.cost = atoi(argv[optind++]);
- }
- break;
-
- case ACTION_DELETE:
- if (argc - optind != 2) {
- usage_route_delete(argv[0], true, false);
- goto ERR_PARAM;
- }
-
- command->route.face_id = atoi(argv[optind++]);
-
- {
- ip_prefix_t prefix;
- ip_prefix_pton(argv[optind++], &prefix);
- command->route.family = prefix.family;
- command->route.remote_addr = prefix.address;
- command->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;
- }
- 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;
- }
- 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->listener.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- command->listener.type = connection_type_from_str(argv[optind++]);
- if (command->listener.type == CONNECTION_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->listener.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->listener.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->listener.local_addr) < 0)
- goto ERR_PARAM;
- command->listener.local_port = atoi(argv[optind++]);
- if (argc != optind) {
- snprintf(command->listener.interface_name, INTERFACE_LEN, "%s", argv[optind++]);
- }
- break;
-
- 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])) {
- command->listener.id = atoi(argv[optind++]);
- snprintf(command->listener.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- } else if (is_symbolic_name(argv[optind])) {
- snprintf(command->listener.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- } else {
- fprintf(stderr, "Invalid argument\n");
- goto ERR_PARAM;
- }
- } else {
- command->listener.type = connection_type_from_str(argv[optind++]);
- if (command->listener.type == CONNECTION_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->listener.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->listener.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->listener.local_addr) < 0)
- goto ERR_PARAM;
- command->listener.local_port = atoi(argv[optind++]);
- if (argc != optind) {
- snprintf(command->listener.interface_name, INTERFACE_LEN, "%s", argv[optind++]);
- }
- }
- break;
-
- case ACTION_LIST:
- if (argc - optind != 0) {
- usage_listener_list(argv[0], true, false);
- goto ERR_PARAM;
- }
- break;
-
- default:
- goto ERR_COMMAND;
- break;
- }
- break;
-
- case OBJECT_CONNECTION:
- switch(command->action) {
- case ACTION_CREATE:
- /* NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT */
- if ((argc - optind != 6) && (argc - optind != 7)) {
- usage_connection_create(argv[0], true, false);
- goto ERR_PARAM;
- }
- snprintf(command->connection.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- command->connection.type = connection_type_from_str(argv[optind++]);
- if (command->connection.type == CONNECTION_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->connection.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->connection.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->connection.local_addr) < 0)
- goto ERR_PARAM;
- command->connection.local_port = atoi(argv[optind++]);
- family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(family) || (command->connection.family != family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->connection.remote_addr) < 0)
- goto ERR_PARAM;
- command->connection.remote_port = atoi(argv[optind++]);
-
- break;
-
- 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])) {
- command->connection.id = atoi(argv[optind++]);
- snprintf(command->connection.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- } else if (is_symbolic_name(argv[optind])) {
- snprintf(command->connection.name, SYMBOLIC_NAME_LEN, "%s", argv[optind++]);
- } else {
- fprintf(stderr, "Invalid argument\n");
- goto ERR_PARAM;
- }
- } else {
- command->connection.type = connection_type_from_str(argv[optind++]);
- if (command->connection.type == CONNECTION_TYPE_UNDEFINED)
- goto ERR_PARAM;
- command->connection.family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(command->connection.family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->connection.local_addr) < 0)
- goto ERR_PARAM;
- command->connection.local_port = atoi(argv[optind++]);
- family = ip_address_get_family(argv[optind]);
- if (!IS_VALID_FAMILY(family) || (command->connection.family != family))
- goto ERR_PARAM;
- if (ip_address_pton(argv[optind++], &command->connection.remote_addr) < 0)
- goto ERR_PARAM;
- command->connection.remote_port = atoi(argv[optind++]);
- }
- break;
-
- case ACTION_LIST:
- if (argc - optind != 0) {
- usage_connection_list(argv[0], true, false);
- goto ERR_PARAM;
- }
- break;
-
- default:
- goto ERR_COMMAND;
- break;
- }
- break;
-
- default:
- goto ERR_COMMAND;
- break;
- }
-
- return 0;
-
-ERR_PARAM:
-ERR_COMMAND:
- return -1;
-}
-
-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];
-
- if (parse_options(argc, argv, &command) < 0)
- die(OPTIONS, "Bad arguments");
-
- hc_sock_t * s = hc_sock_create();
- if (!s)
- die(SOCKET, "Error creating socket.");
-
- if (hc_sock_connect(s) < 0)
- die(CONNECT, "Error connecting to the forwarder.");
-
- switch(command.object) {
- case OBJECT_FACE:
- switch(command.action) {
- case ACTION_CREATE:
- if (hc_face_create(s, &command.face) < 0)
- die(COMMAND, "Error creating face");
- printf("OK\n");
- break;
-
- case ACTION_DELETE:
- if (hc_face_delete(s, &command.face) < 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.route) < 0)
- die(COMMAND, "Error creating route");
- printf("OK\n");
- break;
-
- case ACTION_DELETE:
- if (hc_route_delete(s, &command.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.listener) < 0)
- die(COMMAND, "Error creating listener");
- printf("OK\n");
- break;
- case ACTION_DELETE:
- if (hc_listener_delete(s, &command.listener) < 0)
- die(COMMAND, "Error deleting listener");
- printf("OK\n");
- break;
- break;
- case ACTION_LIST:
- if (hc_listener_list(s, &data) < 0)
- die(COMMAND, "Error getting listeners.");
-
- printf("Listeners:\n");
- foreach_listener(l, data) {
- if (hc_listener_snprintf(buf_listener, MAXSZ_HC_LISTENER+17, l) >= MAXSZ_HC_LISTENER)
- die(COMMAND, "Display error");
- printf("[%d] %s\n", l->id, buf_listener);
- }
-
- hc_data_free(data);
- break;
- default:
- die(COMMAND, "Unsupported command for listener");
- break;
- }
- break;
-
- case OBJECT_CONNECTION:
- switch(command.action) {
- case ACTION_CREATE:
- if (hc_connection_create(s, &command.connection) < 0)
- die(COMMAND, "Error creating connection");
- printf("OK\n");
- break;
- case ACTION_DELETE:
- if (hc_connection_delete(s, &command.connection) < 0)
- die(COMMAND, "Error creating connection");
- printf("OK\n");
- break;
- case ACTION_LIST:
- if (hc_connection_list(s, &data) < 0)
- die(COMMAND, "Error getting connections.");
-
- printf("Connections:\n");
- foreach_connection(c, data) {
- if (hc_connection_snprintf(buf_connection, MAXSZ_HC_CONNECTION, c) >= MAXSZ_HC_CONNECTION)
- die(COMMAND, "Display error");
- printf("[%s] %s\n", c->name, buf_connection);
- }
-
- hc_data_free(data);
- break;
- default:
- die(COMMAND, "Unsupported command for connection");
- break;
- }
- break;
-
- default:
- die(COMMAND, "Unsupported object");
- break;
- }
-
-ERR_COMMAND:
-ERR_CONNECT:
- hc_sock_free(s);
-ERR_SOCKET:
-ERR_OPTIONS:
- return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;
-}
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..22cb8baaa
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_cache.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_cache.h
+ * \brief Implementation of cache command.
+ */
+
+#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..a43934fdf
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_connection.c
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_connection.h
+ * \brief Implementation of connection command.
+ */
+#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, local_address, local_port,
+ remote_address, remote_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, local_address, local_port,
+ remote_address, remote_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..e383de1b5
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_face.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_face.h
+ * \brief Implementation of face command.
+ */
+#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.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;
+ face->id = INVALID_FACE_ID;
+ return 0;
+}
+
+#if 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);
+
+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_create3 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_FACE,
+ .nparams = 3,
+ .parameters = {type_tcp_udp, remote_address, remote_port},
+ .post_hook = (parser_hook_t)on_face_create,
+};
+COMMAND_REGISTER(command_face_create3);
+
+static const command_parser_t command_face_create4 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_FACE,
+ .nparams = 4,
+ .parameters = {type_tcp_udp, remote_address, remote_port, interface},
+ .post_hook = (parser_hook_t)on_face_create,
+};
+COMMAND_REGISTER(command_face_create4);
+
+static const command_parser_t command_face_create5 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_FACE,
+ .nparams = 5,
+ .parameters = {type_tcp_udp, local_address, local_port, remote_address,
+ remote_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, local_address, local_port, remote_address,
+ remote_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);
+
+static const command_parser_t command_face_remove6 = {
+ .action = ACTION_DELETE,
+ .object_type = OBJECT_TYPE_FACE,
+ .nparams = 6,
+ .parameters = {type_tcp_udp, local_address, local_port, remote_address,
+ remote_port, interface},
+};
+COMMAND_REGISTER(command_face_remove6);
+
+static const command_parser_t command_face_remove1 = {
+ .action = ACTION_DELETE,
+ .object_type = OBJECT_TYPE_FACE,
+ .nparams = 1,
+ .parameters = {symbolic_or_id},
+};
+COMMAND_REGISTER(command_face_remove1);
diff --git a/ctrl/libhicnctrl/src/commands/command_listener.c b/ctrl/libhicnctrl/src/commands/command_listener.c
new file mode 100644
index 000000000..604c0a672
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_listener.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_listener.h
+ * \brief Implementation of listener command.
+ */
+#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;
+ case FACE_TYPE_UDP_LISTENER:
+ case FACE_TYPE_TCP_LISTENER:
+ case FACE_TYPE_HICN_LISTENER:
+ break;
+ case FACE_TYPE_UNDEFINED:
+ case FACE_TYPE_N:
+ return -1;
+ }
+ 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_create4 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_LISTENER,
+ .nparams = 4,
+ .parameters = {protocol_tcp_udp, symbolic, local_address, local_port},
+ .post_hook = (parser_hook_t)on_listener_create,
+};
+COMMAND_REGISTER(command_listener_create4);
+
+static const command_parser_t command_listener_create5 = {
+ .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_create5);
+
+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..7dcca038c
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_mapme.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_mapme.h
+ * \brief Implementation of mapme command.
+ */
+#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..eaf949880
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_policy.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_policy.h
+ * \brief Implementation of policy command.
+ */
+
+#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..a274583b1
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_punting.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_punting.h
+ * \brief Implementation of punting command.
+ */
+
+#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..95d0a3a43
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_route.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_route.c
+ * \brief Implementation of route command.
+ */
+
+#include <math.h>
+#include <hicn/ctrl/command.h>
+#include "../objects/route.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 p_cost \
+ { \
+ .name = "cost", .help = "Positive integer representing cost.", \
+ .type = TYPE_INT(1, 255), .offset = offsetof(hc_route_t, cost), \
+ }
+
+/* Face parameters */
+
+#define type_tcp_udp \
+ { \
+ .name = "type", .help = "face type [tcp | udp]", \
+ .type = TYPE_ENUM(face_type), .offset = offsetof(hc_route_t, face.type), \
+ }
+
+#define local_address \
+ { \
+ .name = "local_addr", .help = "local IP address on which to bind.", \
+ .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_route_t, face.local_addr), \
+ .offset2 = offsetof(hc_route_t, face.family), \
+ }
+
+#define local_port \
+ { \
+ .name = "local_port", .help = "Local port.", \
+ .type = TYPE_UINT16(1, UINT16_MAX), \
+ .offset = offsetof(hc_route_t, face.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_route_t, face.remote_addr), \
+ .offset2 = offsetof(hc_route_t, face.family), \
+ }
+
+#define remote_port \
+ { \
+ .name = "remote_port", .help = "Remote port.", \
+ .type = TYPE_UINT16(1, UINT16_MAX), \
+ .offset = offsetof(hc_route_t, face.remote_port), \
+ }
+
+#define interface \
+ { \
+ .name = "interface", .help = "Interface on which to bind", \
+ .type = TYPE_INTERFACE_NAME, \
+ .offset = offsetof(hc_route_t, face.netdevice.name), \
+ }
+
+/* Commands */
+
+int on_route_parsed(hc_route_t* route) {
+ if (hc_route_has_face(route)) {
+ route->face.admin_state = FACE_STATE_UP;
+ route->face.id = INVALID_FACE_ID;
+ }
+ route->face_id = INVALID_FACE_ID; // we populate face name
+ if (route->cost == 0) route->cost = 1;
+ return 0;
+}
+
+static const command_parser_t command_route_create1 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 1,
+ .parameters = {prefix},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create1);
+
+static const command_parser_t command_route_create2 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 2,
+ .parameters = {symbolic_or_id, prefix},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create2);
+
+static const command_parser_t command_route_create3 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 3,
+ .parameters = {symbolic_or_id, prefix, p_cost},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create3);
+
+static const command_parser_t command_route_create5 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 5,
+ .parameters = {prefix, p_cost, type_tcp_udp, remote_address, remote_port},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create5);
+
+static const command_parser_t command_route_create6 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 6,
+ .parameters = {prefix, p_cost, type_tcp_udp, remote_address, remote_port,
+ interface},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create6);
+
+static const command_parser_t command_route_create7 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 7,
+ .parameters = {prefix, p_cost, type_tcp_udp, local_address, local_port,
+ remote_address, remote_port},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create7);
+
+static const command_parser_t command_route_create8 = {
+ .action = ACTION_CREATE,
+ .object_type = OBJECT_TYPE_ROUTE,
+ .nparams = 8,
+ .parameters = {prefix, p_cost, type_tcp_udp, local_address, local_port,
+ remote_address, remote_port, interface},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+COMMAND_REGISTER(command_route_create8);
+
+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},
+ .post_hook = (parser_hook_t)on_route_parsed,
+};
+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..d69f22a19
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_stats.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_stats.h
+ * \brief Implementation of stats command.
+ */
+#include <math.h>
+#include <hicn/ctrl/command.h>
+
+/* Commands */
+
+static const command_parser_t command_stats_list = {
+ .action = ACTION_LIST,
+ .object_type = OBJECT_TYPE_STATS,
+ .nparams = 0,
+};
+COMMAND_REGISTER(command_stats_list);
+
+static const command_parser_t command_face_stats_list = {
+ .action = ACTION_LIST,
+ .object_type = OBJECT_TYPE_FACE_STATS,
+ .nparams = 0,
+};
+COMMAND_REGISTER(command_face_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..2aac924c4
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_strategy.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_strategy.h
+ * \brief Implementation of strategy command.
+ */
+#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', local_remote).", \
+ .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..ee2455b1c
--- /dev/null
+++ b/ctrl/libhicnctrl/src/commands/command_subscription.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file command_subscription.h
+ * \brief Implementation of subscription command.
+ */
+#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/face.c b/ctrl/libhicnctrl/src/face.c
deleted file mode 100644
index e617ff8a4..000000000
--- a/ctrl/libhicnctrl/src/face.c
+++ /dev/null
@@ -1,430 +0,0 @@
-/*
- * Copyright (c) 2017-2019 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 abstraction
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <hicn/util/token.h>
-
-#include <hicn/ctrl/face.h>
-#include "util/hash.h"
-
-#define member_size(type, member) sizeof(((type *)0)->member)
-
-
-/* Netdevice */
-
-const char * netdevice_type_str[] = {
-#define _(x) [NETDEVICE_TYPE_ ## x] = STRINGIZE(x),
-foreach_netdevice_type
-#undef _
-};
-
-netdevice_t *
-netdevice_create_from_index(u32 index)
-{
- netdevice_t * netdevice = malloc(sizeof(netdevice_t));
- if (!netdevice)
- goto ERR_MALLOC;
-
- int rc = netdevice_set_index(netdevice, index);
- if (rc < 0)
- goto ERR_INIT;
-
- return netdevice;
-
-ERR_INIT:
- free(netdevice);
-ERR_MALLOC:
- return NULL;
-}
-
-netdevice_t *
-netdevice_create_from_name(const char * name)
-{
- netdevice_t * netdevice = malloc(sizeof(netdevice_t));
- if (!netdevice)
- goto ERR_MALLOC;
-
- int rc = netdevice_set_name(netdevice, name);
- if (rc < 0)
- goto ERR_INIT;
-
- return netdevice;
-
-ERR_INIT:
- free(netdevice);
-ERR_MALLOC:
- return NULL;
-}
-
-/**
- * \brief Update the index of the netdevice based on the name
- */
-int
-netdevice_update_index(netdevice_t * netdevice)
-{
- netdevice->index = if_nametoindex(netdevice->name);
- if (netdevice->index == 0)
- return -1;
- return 0;
-}
-
-int
-netdevice_update_name(netdevice_t * netdevice)
-{
- if (!if_indextoname(netdevice->index, netdevice->name))
- return -1;
- return 0;
-}
-
-void
-netdevice_free(netdevice_t * netdevice)
-{
- free(netdevice);
-}
-
-int
-netdevice_get_index(const netdevice_t * netdevice, u32 * index)
-{
- if (netdevice->index == 0)
- return -1;
- *index = netdevice->index;
- return 0;
-}
-
-int
-netdevice_set_index(netdevice_t * netdevice, u32 index)
-{
- netdevice->index = index;
- return netdevice_update_name(netdevice);
-}
-
-int
-netdevice_get_name(const netdevice_t * netdevice, const char ** name)
-{
- if (netdevice->name[0] == '\0')
- return -1;
- *name = netdevice->name;
- return 0;
-}
-
-int
-netdevice_set_name(netdevice_t * netdevice, const char * name)
-{
- memset(netdevice->name, 0, sizeof(netdevice->name));
- int rc = snprintf(netdevice->name, IFNAMSIZ, "%s", name);
- if (rc < 0)
- return -1;
- if (rc >= IFNAMSIZ)
- return -2; /* truncated */
- return netdevice_update_index(netdevice);
-}
-
-int
-netdevice_cmp(const netdevice_t * nd1, const netdevice_t * nd2)
-{
- return (nd1->index - nd2->index);
-}
-
-
-/* Face state */
-
-const char * face_state_str[] = {
-#define _(x) [FACE_STATE_ ## x] = STRINGIZE(x),
-foreach_face_state
-#undef _
-};
-
-
-/* Face type */
-
-const char * face_type_str[] = {
-#define _(x) [FACE_TYPE_ ## x] = STRINGIZE(x),
-foreach_face_type
-#undef _
-};
-
-
-/* Face */
-
-int
-face_initialize(face_t * face)
-{
- memset(face, 0, sizeof(face_t)); /* 0'ed for hash */
- return 1;
-}
-
-int
-face_initialize_udp(face_t * face, const char * interface_name, const
- ip_address_t * local_addr, u16 local_port,
- const ip_address_t * remote_addr, u16 remote_port,
- int family)
-{
- if (!local_addr)
- return -1;
-
- *face = (face_t) {
- .type = FACE_TYPE_UDP,
- .family = family,
- .local_addr = *local_addr,
- .local_port = local_port,
- .remote_addr = remote_addr ? *remote_addr : IP_ADDRESS_EMPTY,
- .remote_port = remote_port,
- };
-
- snprintf(face->netdevice.name, IFNAMSIZ, "%s", interface_name);
-
- return 1;
-}
-
-int
-face_initialize_udp_sa(face_t * face, const char * interface_name,
- const struct sockaddr * local_addr,
- const struct sockaddr * remote_addr)
-{
- if (!local_addr)
- return -1;
-
- if (remote_addr && (local_addr->sa_family != remote_addr->sa_family))
- return -1;
-
- switch (local_addr->sa_family) {
- case AF_INET:
- {
- struct sockaddr_in *lsai = (struct sockaddr_in *)local_addr;
- struct sockaddr_in *rsai = (struct sockaddr_in *)remote_addr;
- *face = (face_t) {
- .type = FACE_TYPE_UDP,
- .family = AF_INET,
- .local_addr.v4.as_inaddr = lsai->sin_addr,
- .local_port = lsai ? ntohs(lsai->sin_port) : 0,
- .remote_addr = IP_ADDRESS_EMPTY,
- .remote_port = rsai ? ntohs(rsai->sin_port) : 0,
- };
- if (rsai)
- face->remote_addr.v4.as_inaddr = rsai->sin_addr;
- }
- break;
- case AF_INET6:
- {
- struct sockaddr_in6 *lsai = (struct sockaddr_in6 *)local_addr;
- struct sockaddr_in6 *rsai = (struct sockaddr_in6 *)remote_addr;
- *face = (face_t) {
- .type = FACE_TYPE_UDP,
- .family = AF_INET6,
- .local_addr.v6.as_in6addr = lsai->sin6_addr,
- .local_port = lsai ? ntohs(lsai->sin6_port) : 0,
- .remote_addr = IP_ADDRESS_EMPTY,
- .remote_port = rsai ? ntohs(rsai->sin6_port) : 0,
- };
- if (rsai)
- face->remote_addr.v6.as_in6addr = rsai->sin6_addr;
- }
- break;
- default:
- return -1;
- }
-
- snprintf(face->netdevice.name, IFNAMSIZ, "%s", interface_name);
-
- return 1;
-}
-
-face_t * face_create()
-{
- face_t * face = calloc(1, sizeof(face_t)); /* 0'ed for hash */
- return face;
-}
-
-face_t * face_create_udp(const char * interface_name,
- const ip_address_t * local_addr, u16 local_port,
- const ip_address_t * remote_addr, u16 remote_port, int family)
-{
- face_t * face = face_create();
- if (face_initialize_udp(face, interface_name, local_addr, local_port, remote_addr, remote_port, family) < 0)
- goto ERR_INIT;
- return face;
-
-ERR_INIT:
- free(face);
- return NULL;
-}
-
-face_t * face_create_udp_sa(const char * interface_name,
- const struct sockaddr * local_addr,
- const struct sockaddr * remote_addr)
-{
- face_t * face = face_create();
- if (face_initialize_udp_sa(face, interface_name, local_addr, remote_addr) < 0)
- goto ERR_INIT;
- return face;
-
-ERR_INIT:
- free(face);
- return NULL;
-}
-
-void face_free(face_t * face)
-{
- free(face);
-}
-
-/**
- * \brief Compare two faces
- * \param [in] f1 - First face
- * \param [in] f2 - Second face
- * \return whether faces are equal, ie both their types are parameters are
- * equal.
- *
- * NOTE: this function implements a partial order.
- */
-int
-face_cmp(const face_t * f1, const face_t * f2)
-{
-
- int ret = f1->type - f2->type;
- if (ret != 0)
- return ret;
-
- ret = f1->family - f2->family;
- if (ret != 0)
- return ret;
-
- /*
- * FIXME As hicn-light API might not return the netdevice, we can discard the
- * comparison when one of the two is not set for now...
- */
- if ((f1->netdevice.index != 0) && (f2->netdevice.index != 0)) {
- ret = netdevice_cmp(&f1->netdevice, &f2->netdevice);
- if (ret != 0)
- return ret;
- }
-
- switch(f1->type) {
- case FACE_TYPE_HICN:
- ret = ip_address_cmp(&f1->local_addr, &f2->local_addr, f1->family);
- if (ret != 0)
- return ret;
-
- ret = ip_address_cmp(&f1->remote_addr, &f2->remote_addr, f1->family);
- if (ret != 0)
- return ret;
-
- break;
-
- case FACE_TYPE_TCP:
- case FACE_TYPE_UDP:
- ret = ip_address_cmp(&f1->local_addr, &f2->local_addr, f1->family);
- if (ret != 0)
- return ret;
-
- ret = f1->local_port - f2->local_port;
- if (ret != 0)
- return ret;
-
- ret = ip_address_cmp(&f1->remote_addr, &f2->remote_addr, f1->family);
- if (ret != 0)
- return ret;
-
- ret = f1->remote_port - f2->remote_port;
- if (ret != 0)
- return ret;
-
- break;
- default:
- break;
- }
-
- return 0;
-}
-
-unsigned int
-face_hash(const face_t * face)
-{
- /* Assuming the unused part of the struct is set to zero */
- return hash_struct(face);
-}
-
-/* /!\ Please update constants in header file upon changes */
-size_t
-face_snprintf(char * s, size_t size, const face_t * face)
-{
- switch(face->type) {
- case FACE_TYPE_HICN:
- {
- char local[MAXSZ_IP_ADDRESS];
- char remote[MAXSZ_IP_ADDRESS];
- char tags[MAXSZ_POLICY_TAGS];
-
- ip_address_snprintf(local, MAXSZ_IP_ADDRESS,
- &face->local_addr,
- face->family);
- ip_address_snprintf(remote, MAXSZ_IP_ADDRESS,
- &face->remote_addr,
- face->family);
- policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags);
- return snprintf(s, size, "%s [%s -> %s] [%s]",
- face_type_str[face->type],
- local,
- remote,
- tags);
- }
- case FACE_TYPE_UNDEFINED:
- case FACE_TYPE_TCP:
- case FACE_TYPE_UDP:
- {
- char local[MAXSZ_IP_ADDRESS];
- char remote[MAXSZ_IP_ADDRESS];
- char tags[MAXSZ_POLICY_TAGS];
-
- ip_address_snprintf(local, MAXSZ_IP_ADDRESS,
- &face->local_addr,
- face->family);
- ip_address_snprintf(remote, MAXSZ_IP_ADDRESS,
- &face->remote_addr,
- face->family);
- policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags);
-
- return snprintf(s, size, "%s [%s:%d -> %s:%d] [%s]",
- face_type_str[face->type],
- local,
- face->local_port,
- remote,
- face->remote_port,
- tags);
- }
- default:
- return -1;
- }
-
-}
-
-policy_tags_t face_get_tags(const face_t * face)
-{
- return face->tags;
-}
-
-int
-face_set_tags(face_t * face, policy_tags_t tags)
-{
- face->tags = tags;
- return 1;
-}
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/hicn_plugin_api.c b/ctrl/libhicnctrl/src/hicn_plugin_api.c
deleted file mode 100644
index 0b0a9ad54..000000000
--- a/ctrl/libhicnctrl/src/hicn_plugin_api.c
+++ /dev/null
@@ -1,1337 +0,0 @@
-/*
- * Copyright (c) 2017-2020 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 <hicn/ctrl/api.h>
-#include <hicn/ctrl/commands.h>
-#include <hicn/util/token.h>
-#include <strings.h>
-#include <vapi/hicn.api.vapi.h>
-#include <vapi/ip.api.vapi.h>
-#include <vapi/udp.api.vapi.h>
-#include <vapi/interface.api.vapi.h>
-#include <hicn/util/log.h>
-#include <hicn/util/map.h>
-#include <hicn/error.h>
-#include <vnet/ip/ip6_packet.h>
-#include <vnet/ip/ip46_address.h>
-
-#define APP_NAME "hicn_plugin"
-#define MAX_OUTSTANDING_REQUESTS 4
-#define RESPONSE_QUEUE_SIZE 2
-
-DEFINE_VAPI_MSG_IDS_HICN_API_JSON
-DEFINE_VAPI_MSG_IDS_INTERFACE_API_JSON
-DEFINE_VAPI_MSG_IDS_IP_API_JSON
-DEFINE_VAPI_MSG_IDS_UDP_API_JSON
-
-typedef struct {
- vapi_ctx_t g_vapi_ctx_instance;
- bool async;
-} vapi_skc_ctx_t;
-
-vapi_skc_ctx_t vapi_skc = {
- .g_vapi_ctx_instance = NULL,
- .async = false,
-};
-
-/**
- * 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_s {
- vapi_ctx_t g_vapi_ctx_instance;
- char *url;
- int fd;
-
- 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;
-};
-
-
-/******************************************************************************
- * 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 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->in_element_size = in_element_size;
- data->out_element_size = out_element_size;
- data->size = 0;
- data->current = 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;
-
- return data;
-
-ERR_MALLOC:
- return NULL;
-}
-
-void hc_data_free(hc_data_t *data) {
- if (data != NULL) {
- if (data->buffer) free(data->buffer);
- free(data);
- }
-}
-
-/******************************************************************************
- * Control socket
- ******************************************************************************/
-
-/**
- * \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.
- */
-int hc_sock_parse_url(const char *url, struct sockaddr *sa) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-hc_sock_t *hc_sock_create_url(const char *url) {
- // NOT IMPLEMENTED
- return NULL;
-}
-
-hc_sock_t *hc_sock_create(void) {
- hc_sock_t *s = malloc(sizeof(hc_sock_t));
-
- if (!s) goto ERR_SOCK;
-
- memset(s, 0, sizeof(hc_sock_t));
-
- //By default the socket is blocking -- not async
- s->async = 0;
-
- return s;
-
-ERR_SOCK:
- return NULL;
-}
-
-void hc_sock_free(hc_sock_t *s) {
- if (s->url) free(s->url);
- close(s->fd);
- free(s);
-
- vapi_disconnect_safe();
- vapi_skc.g_vapi_ctx_instance = NULL;
-}
-
-int hc_sock_get_next_seq(hc_sock_t *s) {
- return vapi_gen_req_context(s->g_vapi_ctx_instance);
-}
-
-int hc_sock_set_nonblocking(hc_sock_t *s) {
- s->async = 1;
- return 0;
-}
-
-int hc_sock_get_fd(hc_sock_t *s) { return 1; }
-
-int hc_sock_connect(hc_sock_t *s) {
-
- 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;
-}
-
-int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, int seq) {
- return -1;
-}
-
-int hc_sock_get_available(hc_sock_t *s, u8 **buffer, size_t *size) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_sock_recv(hc_sock_t *s) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_sock_process(hc_sock_t *s, hc_data_t **pdata) {
- //NOT IMPLEMENTED
- return -1;
-}
-
-/******************************************************************************
- * Command-specific structures and functions
- ******************************************************************************/
-
-
-/*----------------------------------------------------------------------------*
- * Listeners
- *----------------------------------------------------------------------------*/
-
-/* LISTENER CREATE */
-
-int _hc_listener_create(hc_sock_t *s, hc_listener_t *listener, bool async) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_listener_create(hc_sock_t *s, hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_listener_create_async(hc_sock_t *s, hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* LISTENER GET */
-int hc_listener_get(hc_sock_t *s, hc_listener_t *listener,
- hc_listener_t **listener_found) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* LISTENER DELETE */
-
-int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_listener_delete_async(hc_sock_t *s, hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-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));
-
- 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;
-
-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 */
-int hc_listener_list(hc_sock_t *s, hc_data_t **pdata) {
- 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 -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);
- }
-
- 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;
-}
-
-int hc_listener_list_async(hc_sock_t *s, hc_data_t **pdata) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* LISTENER VALIDATE */
-
-int hc_listener_validate(const hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* LISTENER CMP */
-
-int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* LISTENER PARSE */
-
-int hc_listener_parse(void *in, hc_listener_t *listener) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-GENERATE_FIND(listener)
-
-/* 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", listener->interface_name, local);
-}
-
-/*----------------------------------------------------------------------------*
- * CONNECTION
- *----------------------------------------------------------------------------*/
-
-/* CONNECTION CREATE */
-
-int hc_connection_create(hc_sock_t *s, hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_connection_create_async(hc_sock_t *s, hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION GET */
-
-int hc_connection_get(hc_sock_t *s, hc_connection_t *connection,
- hc_connection_t **connection_found) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION DELETE */
-
-int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_connection_delete_async(hc_sock_t *s, hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION LIST */
-
-int hc_connection_list(hc_sock_t *s, hc_data_t **pdata) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-int hc_connection_list_async(hc_sock_t *s, hc_data_t **pdata) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION VALIDATE */
-
-int hc_connection_validate(const hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* 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) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION PARSE */
-
-int hc_connection_parse(void *in, hc_connection_t *connection) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-GENERATE_FIND(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) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-/* CONNECTION SET ADMIN STATE */
-
-int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name,
- face_state_t state) {
- // NOT IMPLEMENTED
- return -1;
-}
-
-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;
-}
-
-/*----------------------------------------------------------------------------*
- * Routes
- *----------------------------------------------------------------------------*/
-
-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 */
-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;
-}
-
-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;
-
- return reply->retval;
-}
-
-int _hc_route_create(hc_sock_t *s, hc_route_t *route, bool async) {
- if (!IS_VALID_FAMILY(route->family)) return -1;
-
- int ret;
- 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;
- }
- 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 (ip46_address_is_ip4((ip46_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_FLAG_NONE;
- 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 (ip46_address_is_ip4((ip46_address_t *)(&(face->face.remote_addr))) &&
- ip46_address_is_ip4((ip46_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 (!ip46_address_is_ip4((ip46_address_t *)(&(route->face.face.remote_addr))) &&
- !ip46_address_is_ip4((ip46_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_IP4;
-
- 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) {
- 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;
- 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)
- 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, NULL);
-done:
- vapi_unlock();
- return ret;
-}
-
-int hc_route_create(hc_sock_t *s, hc_route_t *route) {
- return _hc_route_create(s, route, false);
-}
-
-int hc_route_create_async(hc_sock_t *s, hc_route_t *route) {
- return _hc_route_create(s, route, true);
-}
-
-/* ROUTE DELETE */
-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;
-}
-
-int _hc_route_delete(hc_sock_t *s, hc_route_t *route, bool async) {
- if (!IS_VALID_FAMILY(route->family)) return -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 = 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 (ip46_address_is_ip4((ip46_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_FLAG_NONE;
- 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;
- }
-
- vapi_error_e ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, hicnp_msg, parse_route_delete, NULL);
-
- vapi_unlock();
- return ret;
-}
-
-int hc_route_delete(hc_sock_t *s, hc_route_t *route) {
- return _hc_route_delete(s, route, false);
-}
-
-int hc_route_delete_async(hc_sock_t *s, hc_route_t *route) {
- return _hc_route_delete(s, route, true);
-}
-
-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;
-}
-
-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);
- //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;
-
-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(ip46_address_is_ip4((ip46_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;
-}
-
-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;
-}
-
-int _hc_route_list(hc_sock_t *s, hc_data_t **pdata, bool async) {
- 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 = s,
- };
-
- 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;
-}
-
-int hc_route_list(hc_sock_t *s, hc_data_t **pdata) {
- return _hc_route_list(s, pdata, false);
-}
-
-int hc_route_list_async(hc_sock_t *s) {
- return _hc_route_list(s, NULL, true);
-}
-
-/* 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 < 0) return rc;
-
- return snprintf(s, size, "%*d %*d %s %*d", MAXSZ_FACE_ID, route->face_id,
- MAXSZ_COST, route->cost, prefix, MAXSZ_LEN, route->len);
-}
-
-/*----------------------------------------------------------------------------*
- * 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 -> 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 */
-
-int hc_listener_to_face(const hc_listener_t *listener, hc_face_t *face) {
- return -1; /* XXX Not implemented */
-}
-
-/* FACE -> CONNECTION */
-
-int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection,
- bool generate_name) {
- return 0;
-}
-
-/* CONNECTION -> FACE */
-
-int hc_connection_to_face(const hc_connection_t *connection, hc_face_t *face) {
- return 0;
-}
-
-/* CONNECTION -> LISTENER */
-
-int hc_connection_to_local_listener(const hc_connection_t *connection,
- hc_listener_t *listener) {
- return 0;
-}
-
-int hc_face_create(hc_sock_t *s, hc_face_t *face) {
- ERROR("Face creation implemented.");
- return -1;
-}
-
-int hc_face_delete(hc_sock_t *s, hc_face_t *face) {
-
- ERROR("Face deletion not implemented.");
- return -1;
-}
-
-/* FACE LIST */
-
-int hc_face_list(hc_sock_t *s, hc_data_t **pdata) {
-
-ERROR("Face list not implemented.");
-return -1;
-}
-
-int hc_connection_parse_to_face(void *in, hc_face_t *face) { return 0; }
-
-int hc_face_list_async(hc_sock_t *s)
-{
- return 0;
-}
-
-/* /!\ Please update constants in header file upon changes */
-int hc_face_snprintf(char *s, size_t size, hc_face_t *face) { return 0; }
-
-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;
-}
-
-/*----------------------------------------------------------------------------*
- * Punting
- *----------------------------------------------------------------------------*/
-
-int _hc_punting_create(hc_sock_t *s, hc_punting_t *punting, bool async) {
- return 0;
-}
-
-int hc_punting_create(hc_sock_t *s, hc_punting_t *punting) {
- return _hc_punting_create(s, punting, false);
-}
-
-int hc_punting_create_async(hc_sock_t *s, hc_punting_t *punting) {
- return _hc_punting_create(s, punting, true);
-}
-
-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;
-}
-
-int hc_punting_delete(hc_sock_t *s, hc_punting_t *punting) {
- ERROR("hc_punting_delete not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_list(hc_sock_t *s, hc_data_t **pdata) {
- ERROR("hc_punting_list not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_validate(const hc_punting_t *punting) {
- if (!IS_VALID_FAMILY(punting->family)) return -1;
-
- /*
- * We might use the zero value to add punting on all faces but this is not
- * (yet) implemented
- */
- if (punting->face_id == 0) {
- ERROR("Punting on all faces is not (yet) implemented.");
- return -1;
- }
-
- return 0;
-}
-
-int hc_punting_cmp(const hc_punting_t *p1, const hc_punting_t *p2) {
- return ((p1->face_id == p2->face_id) && (p1->family == p2->family) &&
- (ip_address_cmp(&p1->prefix, &p2->prefix, p1->family) == 0) &&
- (p1->prefix_len == p2->prefix_len))
- ? 0
- : -1;
-}
-
-int hc_punting_parse(void *in, hc_punting_t *punting) {
- ERROR("hc_punting_parse not (yet) implemented.");
- return -1;
-}
-
-int hc_punting_snprintf(char *s, size_t size, hc_punting_t *punting) {
- ERROR("hc_punting_snprintf not (yet) implemented.");
- return -1;
-}
-
-/*----------------------------------------------------------------------------*
- * Cache
- *----------------------------------------------------------------------------*/
-
-int _hc_cache_set_store(hc_sock_t *s, int enabled, bool async) {
- return 0;
-}
-
-int hc_cache_set_store(hc_sock_t *s, int enabled) {
- return _hc_cache_set_store(s, enabled, false);
-}
-
-int hc_cache_set_store_async(hc_sock_t *s, int enabled) {
- return _hc_cache_set_store(s, enabled, true);
-}
-
-int _hc_cache_set_serve(hc_sock_t *s, int enabled, bool async) {
- return 0;
-}
-
-int hc_cache_set_serve(hc_sock_t *s, int enabled) {
- return _hc_cache_set_serve(s, enabled, false);
-}
-
-int hc_cache_set_serve_async(hc_sock_t *s, int enabled) {
- return _hc_cache_set_serve(s, enabled, true);
-}
-
-/*----------------------------------------------------------------------------*
- * Strategy
- *----------------------------------------------------------------------------*/
-
-// per prefix
-int hc_strategy_set(hc_sock_t *s /* XXX */) { return 0; }
-
-#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array))
-
-int hc_strategy_list(hc_sock_t *s, hc_data_t **data) {
- return 0;
-}
-
-/* /!\ 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
- *----------------------------------------------------------------------------*/
-
-// per connection
-int hc_wldr_set(hc_sock_t *s /* XXX */) { return 0; }
-
-/*----------------------------------------------------------------------------*
- * MAP-Me
- *----------------------------------------------------------------------------*/
-
-int hc_mapme_set(hc_sock_t *s, int enabled) { return 0; }
-
-int hc_mapme_set_discovery(hc_sock_t *s, int enabled) { return 0; }
-
-int hc_mapme_set_timescale(hc_sock_t *s, double timescale) { return 0; }
-
-int hc_mapme_set_retx(hc_sock_t *s, double timescale) { return 0; }
-
-/* Useless function defined to prevent undefined reference */
-hc_connection_type_t
-connection_type_from_str(const char * str)
-{
- if (strcasecmp(str, "TCP") == 0)
- return CONNECTION_TYPE_TCP;
- else if (strcasecmp(str, "UDP") == 0)
- return CONNECTION_TYPE_UDP;
- else if (strcasecmp(str, "HICN") == 0)
- return CONNECTION_TYPE_HICN;
- else
- return CONNECTION_TYPE_UNDEFINED;
-}
-
-/*********************** Missing Symbol in vpp libraries *************************/
-u8 *
-format_vl_api_address_union (u8 * s, va_list * args)
-{
- return NULL;
-}
diff --git a/ctrl/libhicnctrl/src/hicnctrl.c b/ctrl/libhicnctrl/src/hicnctrl.c
new file mode 100644
index 000000000..478997ec1
--- /dev/null
+++ b/ctrl/libhicnctrl/src/hicnctrl.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file hicnctrl.c
+ * \brief Command line interface
+ */
+#include <ctype.h> // isalpha isalnum
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h> // getopt
+
+#include <hicn/ctrl.h>
+#include <hicn/util/ip_address.h>
+#include <hicn/util/log.h>
+#include <hicn/util/token.h>
+#include <hicn/validation.h>
+
+#include <hicn/ctrl/parse.h>
+
+#define die(LABEL, MESSAGE) \
+ do { \
+ printf(MESSAGE "\n"); \
+ goto ERR_##LABEL; \
+ } while (0)
+
+void usage_header() { fprintf(stderr, "Usage:\n"); }
+
+void usage_face_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr,
+ "%s -f TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a face on specified address and port.\n");
+}
+
+void usage_face_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -df ID\n", prog);
+ // fprintf(stderr, "%s -df NAME\n", prog);
+ fprintf(stderr,
+ "%s -df TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a face...\n");
+}
+
+void usage_face_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -F\n", prog);
+ if (verbose) fprintf(stderr, " List all faces.\n");
+}
+
+void usage_face(const char *prog, bool header, bool verbose) {
+ usage_face_create(prog, header, verbose);
+ usage_face_delete(prog, header, verbose);
+ usage_face_list(prog, header, verbose);
+}
+
+void usage_route_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -r FACE_ID PREFIX [COST]\n", prog);
+ // fprintf(stderr, "%s -r [FACE_ID|NAME] PREFIX [COST]\n", prog);
+ if (verbose) fprintf(stderr, " Create a route...\n");
+}
+
+void usage_route_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dr FACE_ID PREFIX\n", prog);
+ // fprintf(stderr, "%s -dr [FACE_ID|NAME] PREFIX\n", prog);
+ if (verbose) fprintf(stderr, " Delete a route...\n");
+}
+
+void usage_route_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -R\n", prog);
+ if (verbose) fprintf(stderr, " List all routes.\n");
+}
+
+void usage_route(const char *prog, bool header, bool verbose) {
+ usage_route_create(prog, header, verbose);
+ usage_route_delete(prog, header, verbose);
+ usage_route_list(prog, header, verbose);
+}
+
+void usage_forwarding_strategy_create(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+}
+void usage_forwarding_strategy_delete(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+}
+
+void usage_forwarding_strategy_list(const char *prog, bool header,
+ bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -S\n", prog);
+ if (verbose)
+ fprintf(stderr, " List all availble forwarding strategies.\n");
+}
+
+void usage_forwarding_strategy(const char *prog, bool header, bool verbose) {
+ usage_forwarding_strategy_create(prog, header, verbose);
+ usage_forwarding_strategy_delete(prog, header, verbose);
+ usage_forwarding_strategy_list(prog, header, verbose);
+}
+
+void usage_listener_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -l TYPE NAME LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a listener on specified address and port.\n");
+}
+
+void usage_listener_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dl ID\n", prog);
+ fprintf(stderr, "%s -dl NAME\n", prog);
+ fprintf(stderr, "%s -dl TYPE LOCAL_ADDRESS LOCAL_PORT [INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a listener...\n");
+}
+
+void usage_listener_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -L\n", prog);
+ if (verbose) fprintf(stderr, " List all listeners.\n");
+}
+
+void usage_listener(const char *prog, bool header, bool verbose) {
+ usage_listener_create(prog, header, verbose);
+ usage_listener_delete(prog, header, verbose);
+ usage_listener_list(prog, header, verbose);
+}
+void usage_connection_create(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr,
+ "%s -c NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose)
+ fprintf(stderr, " Create a connection on specified address and port.\n");
+}
+
+void usage_connection_delete(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -dc ID\n", prog);
+ fprintf(stderr, "%s -dc NAME\n", prog);
+ fprintf(stderr,
+ "%s -dc TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT "
+ "[INTERFACE_NAME]\n",
+ prog);
+ if (verbose) fprintf(stderr, " Delete a connection...\n");
+}
+
+void usage_connection_list(const char *prog, bool header, bool verbose) {
+ if (header) usage_header();
+ fprintf(stderr, "%s -C\n", prog);
+ if (verbose) fprintf(stderr, " List all connections.\n");
+}
+
+void usage_connection(const char *prog, bool header, bool verbose) {
+ usage_connection_create(prog, header, verbose);
+ usage_connection_delete(prog, header, verbose);
+ usage_connection_list(prog, header, verbose);
+}
+
+void usage(const char *prog) {
+ fprintf(stderr,
+ "Usage: %s [ -z forwarder (hicnlight | vpp) ] [ [-d] [-f|-l|-c|-r] "
+ "PARAMETERS | [-F|-L|-C|-R] ]\n",
+ prog);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "High-level commands\n");
+ fprintf(stderr, "\n");
+ usage_face(prog, false, true);
+ usage_route(prog, false, true);
+ usage_forwarding_strategy(prog, false, true);
+ fprintf(stderr, "\n");
+ fprintf(stderr, "Low level commands (hicn-light specific)\n");
+ fprintf(stderr, "\n");
+ usage_listener(prog, false, true);
+ usage_connection(prog, false, true);
+}
+
+/*
+ * We only allow settings commands and object types once, with the default
+ * action being set to CREATE
+ */
+#define set_command(ACTION, OBJECT_TYPE) \
+ do { \
+ if ((ACTION) != ACTION_UNDEFINED) { \
+ if (command->action != ACTION_CREATE) goto USAGE; \
+ command->action = (ACTION); \
+ } \
+ if ((OBJECT_TYPE) != OBJECT_TYPE_UNDEFINED) { \
+ if (command->object_type != OBJECT_TYPE_UNDEFINED) goto USAGE; \
+ command->object_type = (OBJECT_TYPE); \
+ } \
+ } while (0)
+
+int parse_options(int argc, char *argv[], hc_command_t *command,
+ forwarder_type_t *forwarder) {
+ command->object_type = OBJECT_TYPE_UNDEFINED;
+ command->action = ACTION_CREATE;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "cCdDfFlLrRsShz:")) != -1) {
+ switch (opt) {
+ case 'z':
+ *forwarder = forwarder_type_from_str(optarg);
+ if (*forwarder == FORWARDER_TYPE_UNDEFINED) goto USAGE;
+ break;
+ case 'd':
+ set_command(ACTION_DELETE, OBJECT_TYPE_UNDEFINED);
+ break;
+ case 's':
+ set_command(ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED);
+ break;
+ case 'f':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_FACE);
+ break;
+ case 'c':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_CONNECTION);
+ break;
+ case 'l':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_LISTENER);
+ break;
+ case 'r':
+ set_command(ACTION_UNDEFINED, OBJECT_TYPE_ROUTE);
+ break;
+ case 'F':
+ set_command(ACTION_LIST, OBJECT_TYPE_FACE);
+ break;
+ case 'L':
+ set_command(ACTION_LIST, OBJECT_TYPE_LISTENER);
+ break;
+ case 'C':
+ set_command(ACTION_LIST, OBJECT_TYPE_CONNECTION);
+ break;
+ case 'R':
+ set_command(ACTION_LIST, OBJECT_TYPE_ROUTE);
+ break;
+ case 'S':
+ set_command(ACTION_LIST, OBJECT_TYPE_STRATEGY);
+ break;
+ case 'D':
+ log_conf.log_level = LOG_DEBUG;
+ break;
+ default: /* "h" */
+ usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ // XXX The rest could be made a single parse function
+
+ /* A default action is always defined, let's verify we have an object type,
+ * unless we are subscribing to notifications. In that case, we can monitor
+ * all objects.
+ * XXX handle later
+ */
+ if ((command->object_type == OBJECT_TYPE_UNDEFINED) &&
+ (command->action != ACTION_SUBSCRIBE)) {
+ ERROR("Missing object specification");
+ goto USAGE;
+ }
+
+ /* Check the adequation between the number of parameters and the command */
+ size_t nparams = argc - optind;
+ if (nparams > 0) {
+ if (command->action == ACTION_LIST) command->action = ACTION_GET;
+ } else {
+ if ((command->action != ACTION_LIST) &&
+ (command->action != ACTION_SUBSCRIBE))
+ goto USAGE;
+ }
+
+ /*
+ * This checks is important even with 0 parameters as it checks whether the
+ * command exists.
+ */
+ if (command->action != ACTION_SUBSCRIBE) {
+ const command_parser_t *parser =
+ command_search(command->action, command->object_type, nparams);
+ if (!parser) {
+ ERROR("Could not find parser for command '%s %s'",
+ action_str(command->action), object_type_str(command->object_type));
+ return -1;
+ }
+
+ if (nparams > 0) {
+ if (parse_getopt_args(parser, argc - optind, argv + optind, command) <
+ 0) {
+ ERROR("Error parsing command arguments");
+ goto USAGE;
+ }
+ }
+ }
+
+ return 0;
+
+USAGE:
+ usage(argv[0]);
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[]) {
+ int rc = 1;
+ hc_command_t command;
+ memset(&command, 0, sizeof(command));
+ char buf[MAXSZ_HC_OBJECT];
+
+ log_conf.log_level = LOG_INFO;
+
+ forwarder_type_t forwarder = FORWARDER_TYPE_HICNLIGHT;
+
+ if (parse_options(argc, argv, &command, &forwarder) < 0)
+ die(OPTIONS, "Bad arguments");
+
+ hc_sock_t *s = hc_sock_create(forwarder, /* url= */ NULL);
+ if (!s) die(SOCKET, "Error creating socket.");
+
+ if (hc_sock_connect(s) < 0)
+ die(CONNECT, "Error connecting to the forwarder.");
+
+ hc_data_t *data = NULL;
+
+ rc = hc_execute(s, command.action, command.object_type, &command.object,
+ &data);
+
+ if (rc < 0) {
+ switch (rc) {
+ case INPUT_ERROR:
+ ERROR("Wrong input parameters");
+ break;
+ case UNSUPPORTED_CMD_ERROR:
+ ERROR("Unsupported command");
+ break;
+ default:
+ ERROR("Error executing command");
+ break;
+ }
+ goto ERR_COMMAND;
+ }
+
+ if (!data) goto ERR_QUERY;
+
+ if (!hc_data_get_result(data)) goto ERR_DATA;
+
+ size_t size = hc_data_get_size(data);
+ if (size > 0) {
+ printf("Success: got %ld %s\n", size, object_type_str(command.object_type));
+ } else {
+ printf("Success.\n");
+ }
+
+ if (command.action == ACTION_LIST) {
+ hc_data_foreach(data, obj, {
+ rc = hc_object_snprintf(buf, MAXSZ_HC_OBJECT, command.object_type, obj);
+ if (rc < 0)
+ WARN("Display error");
+ else if (rc >= MAXSZ_HC_OBJECT)
+ WARN("Output truncated");
+ else
+ printf("%s\n", buf);
+ });
+ }
+
+ hc_data_free(data);
+ hc_sock_free(s);
+ return EXIT_SUCCESS;
+
+ERR_DATA:
+ hc_data_free(data);
+ERR_QUERY:
+ERR_COMMAND:
+ERR_CONNECT:
+ hc_sock_free(s);
+ERR_SOCKET:
+ERR_OPTIONS:
+ printf("Error.\n");
+ return EXIT_FAILURE;
+}
diff --git a/ctrl/libhicnctrl/src/libhicnctrl-config.cmake.in b/ctrl/libhicnctrl/src/libhicnctrl-config.cmake.in
new file mode 100644
index 000000000..a27d85cde
--- /dev/null
+++ b/ctrl/libhicnctrl/src/libhicnctrl-config.cmake.in
@@ -0,0 +1,8 @@
+@PACKAGE_INIT@
+
+set(Libhicnctrl_VERSION_MAJOR "@VERSION_MAJOR@")
+set(Libhicnctrl_VERSION_MINOR "@VERSION_MINOR@")
+set(Libhicnctrl_VERSION_PATCH "@VERSION_PATCH@")
+
+set_and_check(Libhicnctrl_INCLUDE_DIRS "@PACKAGE_Libhicnctrl_INCLUDE_DIRS@")
+include("${CMAKE_CURRENT_LIST_DIR}/libhicnctrl-targets.cmake")
diff --git a/ctrl/libhicnctrl/src/module.h b/ctrl/libhicnctrl/src/module.h
new file mode 100644
index 000000000..51fd9f942
--- /dev/null
+++ b/ctrl/libhicnctrl/src/module.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file modules.h
+ * \brief hicn-light module interface.
+ */
+
+#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, \
+ [ACTION_SET] = NULL, \
+ }, \
+ .serialize = { \
+ [ACTION_CREATE] = NULL, \
+ [ACTION_DELETE] = NULL, \
+ [ACTION_LIST] = NULL, \
+ [ACTION_SET] = 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, \
+ [ACTION_SET] = NULL, \
+ }, \
+ .serialize = { \
+ [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \
+ [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \
+ [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \
+ [ACTION_SET] = PREFIX##_##NAME##_serialize_set, \
+ } \
+ }
+
+#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, \
+ [ACTION_SET] = NULL, \
+ }, \
+ .serialize = { \
+ [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \
+ [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \
+ [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \
+ [ACTION_SET] = PREFIX##_##NAME##_serialize_set, \
+ }};
+
+#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, \
+ [ACTION_SET] = PREFIX##_##NAME##_set, \
+ }, \
+ .serialize = \
+ { \
+ [ACTION_CREATE] = NULL, \
+ [ACTION_DELETE] = NULL, \
+ [ACTION_LIST] = NULL, \
+ [ACTION_SET] = 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/modules/CMakeLists.txt b/ctrl/libhicnctrl/src/modules/CMakeLists.txt
new file mode 100644
index 000000000..f3e1a6342
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/CMakeLists.txt
@@ -0,0 +1,109 @@
+# Copyright (c) 2021-2023 Cisco and/or its affiliates.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+##############################################################
+# Hicn Light NG Module
+##############################################################
+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/mapme.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/stats.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/strategy.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/subscription.c
+)
+
+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/mapme.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/stats.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}
+ INCLUDE_DIRS PRIVATE ${INCLUDE_DIRS}
+ DEFINITIONS PRIVATE ${COMPILER_DEFINITIONS}
+ COMPILE_OPTIONS PRIVATE ${COMPILE_FLAGS}
+ COMPILE_OPTIONS ${COMPILER_OPTIONS}
+)
+
+
+##############################################################
+# VPP Module
+##############################################################
+if(BUILD_HICNPLUGIN AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
+ find_package(HicnPlugin ${CURRENT_VERSION} REQUIRED)
+ find_package(Safevapi ${CURRENT_VERSION} REQUIRED)
+ else()
+ list(APPEND DEPENDENCIES
+ ${SAFE_VAPI_SHARED}
+ )
+ endif()
+
+
+ list(APPEND HICN_PLUGIN_SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/listener.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/route.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/strategy.c
+ )
+
+ list(APPEND HICN_PLUGIN_HEADER_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/base.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/listener.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/route.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/strategy.h
+ )
+
+ ##############################################################
+ # Compiler Options
+ ##############################################################
+ set(COMPILER_OPTIONS
+ ${DEFAULT_COMPILER_OPTIONS}
+ ${MARCH_COMPILER_OPTIONS}
+ PRIVATE "-Wno-address-of-packed-member"
+ )
+
+ ##############################################################
+ # Compiler Definitions
+ ##############################################################
+ list(APPEND COMPILER_DEFINITIONS
+ PRIVATE "-DHICN_VPP_PLUGIN=1"
+ )
+
+ build_module(vppctrl_module
+ SOURCES ${HICN_PLUGIN_SOURCE_FILES} ${HICN_PLUGIN_HEADER_FILES}
+ DEPENDS ${DEPENDENCIES}
+ LINK_LIBRARIES
+ PRIVATE ${HICN_LIBRARIES}
+ PRIVATE ${HICNPLUGIN_LIBRARIES}
+ PRIVATE ${SAFE_VAPI_LIBRARIES}
+ COMPONENT ${LIBHICNCTRL_COMPONENT_MODULES}
+ INCLUDE_DIRS PRIVATE ${INCLUDE_DIRS}
+ DEFINITIONS PRIVATE ${COMPILER_DEFINITIONS}
+ COMPILE_OPTIONS ${COMPILER_OPTIONS}
+ )
+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..96cdda2d2
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light.c
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file 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 "../objects/stats.h"
+#include "../request.h"
+#include "../socket_private.h"
+#include "hicn_light.h"
+
+#include "hicn_light/base.h"
+#include "hicn_light/connection.h"
+#include "hicn_light/face.h"
+#include "hicn_light/listener.h"
+#include "hicn_light/mapme.h"
+#include "hicn_light/route.h"
+#include "hicn_light/stats.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) {
+ char ip[100];
+ char protocol[100];
+ int port = PORT;
+ if (url) {
+ int ret = sscanf(url, "%99[^:]://%99[^:]:%99d[^/]", protocol, ip, &port);
+ if (ret == EOF) return -1;
+ }
+
+#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);
+ if (url) {
+ int ret = inet_pton(AF_INET, ip, &(sai->sin_addr.s_addr));
+ if (ret != 1) return -1;
+ } else {
+ 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);
+ if (url) {
+ int ret = inet_pton(AF_INET6, ip, &(sai6->sin6_addr));
+ if (ret != 1) return -1;
+ } else {
+ 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");
+ /*
+ * Currently: Ignore packet (alternatively: return 0 to discard in case of
+ * error; move s->got_header later in this case)
+ */
+ 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);
+
+ /* We discard any result from the child request */
+ hc_request_reset_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_delete(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;
+
+ hc_request_state_t state;
+
+NEXT:
+ state = hc_request_get_state(current_request);
+ DEBUG("hicnlight_prepare_face_delete > %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_DELETE_CONNECTION_DELETE);
+ goto NEXT;
+ case FACE_TYPE_HICN_LISTENER:
+ case FACE_TYPE_TCP_LISTENER:
+ case FACE_TYPE_UDP_LISTENER:
+ case FACE_TYPE_UNDEFINED:
+ case FACE_TYPE_N:
+ return -99; // Not implemented
+ }
+
+ case REQUEST_STATE_FACE_DELETE_CONNECTION_DELETE:
+ 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_COMPLETE);
+
+ return hicnlight_prepare_subrequest(sock, request, ACTION_DELETE,
+ OBJECT_TYPE_CONNECTION, &connection,
+ buffer);
+ case REQUEST_STATE_COMPLETE:
+ break;
+
+ 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(current_request);
+ hc_request_set_data(current_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(current_request);
+
+ _ASSERT(object_type == OBJECT_TYPE_FACE);
+
+ switch (action) {
+ case ACTION_CREATE:
+ return hicnlight_prepare_face_create(sock, request, buffer);
+ case ACTION_DELETE:
+ return hicnlight_prepare_face_delete(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 connection 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);
+ 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 ssize_t hicnlight_prepare_connection_delete(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_DELETE);
+ _ASSERT(object_type == OBJECT_TYPE_CONNECTION);
+
+ hc_data_t *data = hc_request_get_data(current_request);
+ hc_request_state_t state;
+
+NEXT:
+ state = hc_request_get_state(current_request);
+ DEBUG("hicnlight_prepare_connection_delete > %s",
+ hc_request_state_str(state));
+
+ switch (state) {
+ case REQUEST_STATE_INIT:
+ /* Two behaviours depending on the content of the connection id:
+ * - Valid Id : delete connection with provided ID
+ * - Invalid Id: Retrieve ID and then delete connection
+ *
+ * We assume connection has been already validated.
+ */
+ if (hc_connection_has_valid_id(&object->connection)) {
+ // Id is valid. Proceed with deleting the connection.
+ hc_request_set_state(current_request,
+ REQUEST_STATE_CONNECTION_DELETE_WITH_ID);
+ } else {
+ // Id is not valid. Try to retrieve it using the info in the connection.
+ hc_request_set_state(current_request, REQUEST_STATE_CONNECTION_GET);
+ }
+ goto NEXT;
+
+ case REQUEST_STATE_CONNECTION_DELETE_WITH_ID:
+ // We have a valid id. Let's delete the connection.
+ 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_CONNECTION_GET:
+ // Get the connection info from the forwarder.
+ hc_request_set_state(current_request,
+ REQUEST_STATE_CONNECTION_DELETE_AFTER_GET);
+ return hicnlight_prepare_subrequest(
+ sock, request, ACTION_GET, OBJECT_TYPE_CONNECTION, object, buffer);
+
+ case REQUEST_STATE_CONNECTION_DELETE_AFTER_GET:
+ // We got the response from the forwarder. If valid, let's take the ID and
+ // delete the connection by ID.
+ 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;
+ }
+
+ _ASSERT(hc_data_get_object_type(data) == OBJECT_TYPE_CONNECTION);
+
+ object->connection.id = hc_data_get_object(data, 0)->connection.id;
+
+ hc_request_set_state(current_request,
+ REQUEST_STATE_CONNECTION_DELETE_WITH_ID);
+ goto NEXT;
+
+ case REQUEST_STATE_COMPLETE:
+ // Connection delete 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;
+
+ WITH_DEBUG({
+ char buf[MAXSZ_HC_OBJECT];
+ hc_request_state_t state = hc_request_get_state(current_request);
+ hc_object_snprintf(buf, sizeof(buf), object_type, object);
+ DEBUG("[hicnlight_prepare] %s %s [%s] %s", action_str(action),
+ object_type_str(object_type), hc_request_state_str(state), buf);
+ });
+
+ /*
+ * 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;
+
+ case ACTION_DELETE:
+ switch (object_type) {
+ case OBJECT_TYPE_CONNECTION:
+ return hicnlight_prepare_connection_delete(sock, request, buffer);
+ break;
+
+ default:
+ 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.
+ */
+ hc_data_t *data = hc_request_get_data(current_request);
+ if (data) {
+ hc_request_set_complete(current_request);
+ return 0;
+ }
+
+ 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 = -99;
+
+ 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'
+ */
+ 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] = hicnlight_mapme_module_ops;
+ 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_STATS] = hicnlight_stats_module_ops;
+ hc_sock_light.object_vft[OBJECT_TYPE_FACE_STATS] =
+ hicnlight_face_stats_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;
+
+ 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..ea6fc1bc9
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/base.h
@@ -0,0 +1,49 @@
+#ifndef HICNCTRL_MODULES_HICNLIGHT_BASE_H
+#define HICNCTRL_MODULES_HICNLIGHT_BASE_H
+
+#include <hicn/ctrl/hicn-light.h>
+
+#ifdef __APPLE__
+#define RANDBYTE() (u8)(arc4random() & 0xFF)
+#else
+#define RANDBYTE() (u8)(random() & 0xFF)
+#endif
+
+// TODO: is this used?
+#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)
+
+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), &params,
+ 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), &params,
+ 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), &params,
+ 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..d5648d794
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.c
@@ -0,0 +1,503 @@
+/*
+ * 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 local address received");
+ return -1;
+ }
+
+ if (!IS_VALID_PORT(ntohs(item->local_port))) {
+ ERROR("[hc_connection_parse] Invalid local port received");
+ return -1;
+ }
+
+ if (!IS_VALID_ADDRESS(item->remote_address)) {
+ ERROR("[hc_connection_parse] Invalid remote address received");
+ return -1;
+ }
+
+ if (!IS_VALID_PORT(ntohs(item->remote_port))) {
+ ERROR("[hc_connection_parse] Invalid remote 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), &params,
+ 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), &params,
+ 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), &params, 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
+}
+
+int hicnlight_connection_serialize_set(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+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..21f3a2fbc
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.h
@@ -0,0 +1,11 @@
+#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);
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, connection);
+
+#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..4dcd96191
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.c
@@ -0,0 +1,165 @@
+#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) {
+ return -99; /* XXX Not implemented */
+}
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..6f202b4ce
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.h
@@ -0,0 +1,16 @@
+#ifndef HICNCTRL_MODULE_HICNLIGHT_FACE
+#define HICNCTRL_MODULE_HICNLIGHT_FACE
+
+#include <hicn/ctrl/objects/connection.h>
+#include <hicn/ctrl/objects/face.h>
+
+int hc_connection_to_face(const hc_connection_t *connection, hc_face_t *face);
+
+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..46fded2ca
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.c
@@ -0,0 +1,181 @@
+/*
+ * 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
+}
+
+int hicnlight_listener_serialize_set(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+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..67eadfbb1
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.h
@@ -0,0 +1,8 @@
+#ifndef HICNCTRL_MODULE_HICNLIGHT_LISTENER_H
+#define HICNCTRL_MODULE_HICNLIGHT_LISTENER_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, listener);
+
+#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..04eb36e24
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file modules/hicn_light/mapme.c
+ * \brief Implementation of mapme object VFT for hicn_light.
+ */
+
+#include <hicn/ctrl/hicn-light.h>
+#include "mapme.h"
+
+static int hicnlight_mapme_parse(const uint8_t *buffer, size_t size,
+ hc_mapme_t *mapme) {
+ return -1;
+}
+
+int _hicnlight_mapme_parse(const uint8_t *buffer, size_t size,
+ hc_object_t *object) {
+ return hicnlight_mapme_parse(buffer, size, &object->mapme);
+}
+
+int hicnlight_mapme_serialize_create(const hc_object_t *object,
+ uint8_t *packet) {
+ const hc_mapme_t *mapme = &object->mapme;
+
+ msg_mapme_add_t *msg = (msg_mapme_add_t *)packet;
+ *msg = (msg_mapme_add_t){.header =
+ {
+ .message_type = REQUEST_LIGHT,
+ .command_id = COMMAND_TYPE_MAPME_ADD,
+ .length = 1,
+ .seq_num = 0,
+ },
+
+ .payload = {.face_id = mapme->face_id,
+ .family = mapme->family,
+ .address = mapme->address,
+ .len = mapme->len}};
+
+ return sizeof(msg_mapme_add_t);
+}
+
+int hicnlight_mapme_serialize_delete(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_mapme_serialize_list(const hc_object_t *object, uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_mapme_serialize_set(const hc_object_t *object, uint8_t *packet) {
+ return -1;
+}
+
+DECLARE_MODULE_OBJECT_OPS(hicnlight, mapme);
diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/mapme.h b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.h
new file mode 100644
index 000000000..58a943b67
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file modules/hicn_light/mapme.h
+ * \brief mapme object VFT for hicn_light.
+ */
+
+#ifndef HICNCTRL_MODULE_HICNLIGHT_MAPME_H
+#define HICNCTRL_MODULE_HICNLIGHT_MAPME_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, mapme);
+
+#endif /* HICNCTRL_MODULE_HICNLIGHT_MAPME_H */
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), &params,
+ 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), &params,
+ 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), &params,
+ 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), &params,
+ 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..38510704c
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/route.c
@@ -0,0 +1,177 @@
+/*
+ * 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_route_parse] Invalid face_name received");
+ return -1;
+ }
+
+ if (!IS_VALID_ID(item->face_id)) {
+ ERROR("[hc_route_parse] Invalid face_id received");
+ return -1;
+ }
+
+ if (!IS_VALID_FAMILY(item->family)) {
+ ERROR("[hc_route_parse] Invalid family received");
+ return -1;
+ }
+
+ if (!IS_VALID_ADDRESS(item->remote_addr)) {
+ ERROR("[hc_route_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
+}
+
+int hicnlight_route_serialize_set(const hc_object_t *object, uint8_t *packet) {
+ return -1;
+}
+
+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..825253a42
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/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_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"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, route);
+
+#endif /* HICNCTRL_MODULE_HICNLIGHT_ROUTE_H */
diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/stats.c b/ctrl/libhicnctrl/src/modules/hicn_light/stats.c
new file mode 100644
index 000000000..a60190a13
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/stats.c
@@ -0,0 +1,113 @@
+/*
+ * 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 modules/hicn_light/stats.c
+ * \brief Implementation of stats object VFT for hicn_light.
+ */
+
+#include <hicn/util/log.h>
+#include "base.h"
+#include "stats.h"
+
+/* GENERAL STATS */
+
+int hicnlight_stats_parse(const uint8_t *buffer, size_t size,
+ hc_stats_t *stats) {
+ if (size != sizeof(cmd_stats_list_item_t)) return -1;
+
+ cmd_stats_list_item_t *item = (cmd_stats_list_item_t *)buffer;
+ *stats = item->stats;
+ return 0;
+}
+
+int _hicnlight_stats_parse(const uint8_t *buffer, size_t size,
+ hc_object_t *object) {
+ return hicnlight_stats_parse(buffer, size, &object->stats);
+}
+
+int hicnlight_stats_serialize_create(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_stats_serialize_delete(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_stats_serialize_list(const hc_object_t *object, uint8_t *packet) {
+ msg_stats_list_t *msg = (msg_stats_list_t *)packet;
+ *msg = (msg_stats_list_t){.header = {
+ .message_type = REQUEST_LIGHT,
+ .command_id = COMMAND_TYPE_STATS_LIST,
+ .length = 0,
+ .seq_num = 0,
+ }};
+
+ return sizeof(msg_header_t); // Do not use msg_stats_list_t
+}
+
+int hicnlight_stats_serialize_set(const hc_object_t *object, uint8_t *packet) {
+ return -1;
+}
+
+DECLARE_MODULE_OBJECT_OPS(hicnlight, stats);
+
+/* PER-FACE STATS */
+
+int hicnlight_face_stats_parse(const uint8_t *buffer, size_t size,
+ hc_face_stats_t *stats) {
+ if (size != sizeof(cmd_face_stats_list_item_t)) return -1;
+
+ cmd_face_stats_list_item_t *item = (cmd_face_stats_list_item_t *)buffer;
+ *stats = item->stats;
+ return 0;
+}
+
+int _hicnlight_face_stats_parse(const uint8_t *buffer, size_t size,
+ hc_object_t *object) {
+ return hicnlight_face_stats_parse(buffer, size, &object->face_stats);
+}
+
+int hicnlight_face_stats_serialize_create(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_face_stats_serialize_delete(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+int hicnlight_face_stats_serialize_list(const hc_object_t *object,
+ uint8_t *packet) {
+ msg_face_stats_list_t *msg = (msg_face_stats_list_t *)packet;
+ *msg = (msg_face_stats_list_t){.header = {
+ .message_type = REQUEST_LIGHT,
+ .command_id = COMMAND_TYPE_FACE_STATS_LIST,
+ .length = 0,
+ .seq_num = 0,
+ }};
+
+ return sizeof(msg_header_t); // Do not use msg_stats_list_t
+}
+
+int hicnlight_face_stats_serialize_set(const hc_object_t *object,
+ uint8_t *packet) {
+ return -1;
+}
+
+DECLARE_MODULE_OBJECT_OPS(hicnlight, face_stats);
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..9509be86c
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/stats.h
@@ -0,0 +1,24 @@
+/*
+ * 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_MODULE_HICNLIGHT_STATS_H
+#define HICNCTRL_MODULE_HICNLIGHT_STATS_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, stats);
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, face_stats);
+
+#endif /* HICNCTRL_MODULE_HICNLIGHT_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..16ce2d047
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c
@@ -0,0 +1,138 @@
+#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;
+}
+
+int hicnlight_strategy_serialize_set(const hc_object_t *object,
+ uint8_t *packet) {
+ const hc_strategy_t *strategy = &object->strategy;
+
+ msg_strategy_set_t *msg = (msg_strategy_set_t *)packet;
+ *msg = (msg_strategy_set_t){.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,
+ }};
+
+ return sizeof(msg_strategy_set_t);
+}
+
+#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;
+}
+
+/* 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..9fa809751
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h
@@ -0,0 +1,8 @@
+#ifndef HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H
+#define HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, strategy);
+
+#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..e2de09bf5
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c
@@ -0,0 +1,71 @@
+#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;
+}
+
+int hicnlight_subscription_serialize_set(const hc_object_t *object,
+ uint8_t *packet) {
+ 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..d67bdd9ba
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h
@@ -0,0 +1,8 @@
+#ifndef HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H
+#define HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(hicnlight, subscription);
+
+#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_plugin.c b/ctrl/libhicnctrl/src/modules/hicn_plugin.c
new file mode 100644
index 000000000..b8606daf7
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin.c
@@ -0,0 +1,248 @@
+/*
+ * 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"
+#include "hicn_plugin/strategy.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] = vpp_strategy_module_ops;
+ 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..4d9792256
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c
@@ -0,0 +1,189 @@
+/*
+ * 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.v4), reply->prefix.address.un.ip4,
+ IPV4_ADDR_LEN);
+ ld->listener.family = AF_INET;
+ } else {
+ memcpy(&(ld->listener.local_addr.v6), 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);
+}
+
+static int vpp_listener_set(hc_sock_t *sock, hc_object_t *object,
+ hc_data_t *data) {
+ return -1;
+}
+
+DECLARE_VPP_MODULE_OBJECT_OPS(vpp, listener);
diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h
new file mode 100644
index 000000000..f75c58db6
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.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/listener.h
+ * \brief listener object VFT for hicn_plugin.
+ */
+
+#ifndef HICNCTRL_MODULE_VPP_LISTENER_H
+#define HICNCTRL_MODULE_VPP_LISTENER_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(vpp, listener);
+
+#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..4c9f8a638
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c
@@ -0,0 +1,552 @@
+/*
+ * 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;
+
+ 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);
+ if (msg2 == NULL) {
+ ret = -1;
+ goto done;
+ }
+ 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);
+ if (msg2 == NULL) {
+ ret = -1;
+ goto done;
+ }
+ 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);
+}
+
+static int vpp_route_set(hc_sock_t *sock, hc_object_t *object,
+ hc_data_t *data) {
+ return -1;
+}
+
+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/strategy.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.c
new file mode 100644
index 000000000..17d29a1d7
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "base.h"
+#include "strategy.h"
+
+#include <vpp_plugins/hicn/hicn_enums.h>
+
+static int _ip_prefix_encode(const hicn_ip_address_t *address,
+ int prefix_length, int family,
+ vapi_type_prefix *out) {
+ out->len = prefix_length;
+ int ret = 0;
+
+ switch (family) {
+ case AF_INET:
+ memcpy(&out->address.un.ip4[0], &address->v4, 4);
+ out->address.af = ADDRESS_IP4;
+ break;
+ case AF_INET6:
+ memcpy(&out->address.un.ip6[0], &address->v6, 16);
+ out->address.af = ADDRESS_IP6;
+ break;
+ default:
+ // This should never happen
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static vapi_enum_hicn_strategy _vpp_strategy_libhicn_to_hicnplugin_strategy(
+ strategy_type_t strategy) {
+ switch (strategy) {
+ case STRATEGY_TYPE_LOADBALANCER:
+ return HICN_STRATEGY_RR;
+ case STRATEGY_TYPE_LOCAL_REMOTE:
+ return HICN_STRATEGY_LR;
+ case STRATEGY_TYPE_REPLICATION:
+ return HICN_STRATEGY_RP;
+ case STRATEGY_TYPE_BESTPATH:
+ return HICN_STRATEGY_MW;
+ default:
+ return HICN_STRATEGY_NULL;
+ }
+}
+
+static vapi_error_e _hicn_strategy_set_cb(
+ vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last,
+ vapi_payload_hicn_api_strategy_set_reply *reply) {
+ if (reply == NULL || rv != VAPI_OK) return rv;
+ return reply->retval;
+}
+
+static int _vpp_strategy_set(hc_sock_vpp_data_t *s,
+ const hc_strategy_t *strategy) {
+ int ret = -1;
+
+ // Convert libhicn strategy enum to hicnplugin strategy enum and make sure it
+ // is valid
+ vapi_enum_hicn_strategy strategy_id =
+ _vpp_strategy_libhicn_to_hicnplugin_strategy(strategy->type);
+
+ if (strategy_id == HICN_STRATEGY_NULL) {
+ return -1;
+ }
+
+ // Construct API message
+ vapi_msg_hicn_api_strategy_set *msg =
+ vapi_alloc_hicn_api_strategy_set(s->g_vapi_ctx_instance);
+
+ // Fill it
+ msg->payload.strategy_id = strategy_id;
+ ret = _ip_prefix_encode(&strategy->address, strategy->len, strategy->family,
+ &msg->payload.prefix);
+
+ if (ret != 0) {
+ return -1;
+ }
+
+ vapi_lock();
+ ret = vapi_hicn_api_strategy_set(s->g_vapi_ctx_instance, msg,
+ _hicn_strategy_set_cb, NULL);
+ vapi_unlock();
+
+ return ret;
+}
+
+int vpp_strategy_create(hc_sock_t *sock, hc_object_t *object, hc_data_t *data) {
+ return -1;
+}
+
+int vpp_strategy_delete(hc_sock_t *sock, hc_object_t *object, hc_data_t *data) {
+ return -1;
+}
+
+int vpp_strategy_list(hc_sock_t *sock, hc_object_t *object, hc_data_t *data) {
+ return -1;
+}
+
+int vpp_strategy_set(hc_sock_t *sock, hc_object_t *object, hc_data_t *data) {
+ hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data;
+ return _vpp_strategy_set(s, &object->strategy);
+}
+
+DECLARE_VPP_MODULE_OBJECT_OPS(vpp, strategy);
diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.h
new file mode 100644
index 000000000..6dd6df6e4
--- /dev/null
+++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/strategy.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file modules/hicn_plugin/route.h
+ * \brief route object VFT for hicn_plugin.
+ */
+
+#ifndef HICNCTRL_MODULE_VPP_STRATEGY_H
+#define HICNCTRL_MODULE_VPP_STRATEGY_H
+
+#include "../../module.h"
+
+DECLARE_MODULE_OBJECT_OPS_H(vpp, strategy);
+
+#endif /* HICNCTRL_MODULE_VPP_STRATEGY_H */
diff --git a/ctrl/libhicnctrl/src/object.c b/ctrl/libhicnctrl/src/object.c
new file mode 100644
index 000000000..d9863308e
--- /dev/null
+++ b/ctrl/libhicnctrl/src/object.c
@@ -0,0 +1,62 @@
+/*
+ * 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) {
+ if (!object) {
+ if (size == 0) return -1;
+ *s = '\0';
+ return 1;
+ }
+
+ 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..1aea86639
--- /dev/null
+++ b/ctrl/libhicnctrl/src/object_private.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file object_private.h
+ * \brief Helper functions for object management.
+ */
+
+#ifndef HICNCTRL_OBJECT_PRIVATE_H
+#define HICNCTRL_OBJECT_PRIVATE_H
+
+#include <hicn/face.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) (x != INVALID_FACE_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..4dc59c378
--- /dev/null
+++ b/ctrl/libhicnctrl/src/object_vft.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file object_vft.c
+ * \brief Implementation of object VFT.
+ */
+
+#include "object_vft.h"
+
+#include "objects/listener.h"
+#include "objects/connection.h"
+#include "objects/route.h"
+#include "objects/face.h"
+#include "objects/mapme.h"
+#include "objects/stats.h"
+#include "objects/strategy.h"
+#include "objects/subscription.h"
+#include "objects/active_interface.h"
+
+const hc_object_ops_t *object_vft[] = {
+ [OBJECT_TYPE_CONNECTION] = &hc_connection_ops,
+ [OBJECT_TYPE_LISTENER] = &hc_listener_ops,
+ [OBJECT_TYPE_ROUTE] = &hc_route_ops,
+ [OBJECT_TYPE_FACE] = &hc_face_ops,
+ [OBJECT_TYPE_STRATEGY] = &hc_strategy_ops,
+ [OBJECT_TYPE_MAPME] = &hc_mapme_ops,
+ [OBJECT_TYPE_SUBSCRIPTION] = &hc_subscription_ops,
+ [OBJECT_TYPE_ACTIVE_INTERFACE] = &hc_active_interface_ops,
+ [OBJECT_TYPE_STATS] = &hc_stats_ops,
+ [OBJECT_TYPE_FACE_STATS] = &hc_face_stats_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..ca09d9a54
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/connection.c
@@ -0,0 +1,298 @@
+/*
+ * 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;
+#if 0
+ int has_interface_name = 0;
+#endif
+ 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 0
+ 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;
+ }
+#endif
+
+ 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;
+ }
+
+ /* Interface name is optional */
+ int has_key = has_id || has_name;
+ int has_mandatory_attributes = 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;
+
+ if (!isempty(c1->interface_name) && !isempty(c2->interface_name)) {
+ 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);
+
+ return rc;
+}
+
+int _hc_connection_cmp(const hc_object_t *object1, const hc_object_t *object2) {
+ return hc_connection_cmp(&object1->connection, &object2->connection);
+}
+
+int hc_connection_has_valid_id(const hc_connection_t *connection) {
+ return connection->id != INVALID_FACE_ID;
+}
+
+/* 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..1e9bf0376
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/connection.h
@@ -0,0 +1,32 @@
+/*
+ * 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);
+bool hc_connection_has_valid_id(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..5dbe1c8dc
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/face.c
@@ -0,0 +1,187 @@
+/*
+ * 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) {
+ int rc;
+ hc_object_t object;
+ memset(&object, 0, sizeof(hc_object_t));
+ object.face = *face;
+ rc = hc_execute(s, ACTION_CREATE, OBJECT_TYPE_FACE, &object, NULL);
+ face->id = object.face.id;
+ return rc;
+}
+
+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);
+}
+
+int hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name,
+ uint32_t priority) {
+ return -1; // Not implemented
+#if 0
+ hc_object_t object;
+ memset(&object, 0, sizeof(hc_object_t));
+ return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_FACE, &object, NULL);
+#endif
+}
+
+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..97a2430d8
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/listener.c
@@ -0,0 +1,208 @@
+/*
+ * 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;
+#if 0
+ int has_interface_name = 0;
+#endif
+ 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 0
+ 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;
+ }
+#endif
+
+ 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 and interface are optional */
+ if (has_id && 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;
+
+ if (!isempty(l1->interface_name) && !isempty(l2->interface_name)) {
+ 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 0;
+}
+
+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/mapme.c b/ctrl/libhicnctrl/src/objects/mapme.c
new file mode 100644
index 000000000..1acdcb35a
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/mapme.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file mapme.c
+ * \brief Implementation of mapme object.
+ */
+
+#include <hicn/ctrl/api.h>
+#include <hicn/ctrl/object.h>
+#include <hicn/ctrl/objects/mapme.h>
+#include <hicn/util/log.h>
+
+#include "../object_private.h"
+#include "../object_vft.h"
+#include "face.h"
+#include "base.h"
+
+/* MAPME VALIDATE */
+
+int hc_mapme_validate(const hc_mapme_t *mapme, bool allow_partial) {
+ int has_family = 0;
+ int has_address = 0;
+ int has_len = 0;
+
+ if (allow_partial) return 0;
+
+ if (mapme->family != AF_UNSPEC) {
+ if (!IS_VALID_FAMILY(mapme->family)) {
+ ERROR("[hc_mapme_validate] Invalid family specified");
+ return -1;
+ }
+ has_family = 1;
+ }
+
+ if (!hicn_ip_address_empty(&mapme->address)) {
+ if (!IS_VALID_ADDRESS(mapme->address)) {
+ ERROR("[hc_mapme_validate] Invalid remote_addr specified");
+ return -1;
+ }
+ has_address = 1;
+ }
+
+ if (!IS_VALID_PREFIX_LEN(mapme->len)) {
+ ERROR("[hc_mapme_validate] Invalid len");
+ return -1;
+ }
+ has_len = 1;
+ return has_family && has_address && has_len;
+}
+
+int _hc_mapme_validate(const hc_object_t *object, bool allow_partial) {
+ return hc_mapme_validate(&object->mapme, allow_partial);
+}
+
+/* MAPME CMP */
+
+int hc_mapme_cmp(const hc_mapme_t *mapme1, const hc_mapme_t *mapme2) {
+ return -1;
+}
+
+int _hc_mapme_cmp(const hc_object_t *object1, const hc_object_t *object2) {
+ return -1;
+}
+
+/* MAPME SNPRINTF */
+
+int hc_mapme_snprintf(char *s, size_t size, const hc_mapme_t *mapme) {
+ return -1;
+}
+
+int _hc_mapme_snprintf(char *s, size_t size, const hc_object_t *object) {
+ return -1;
+}
+
+int hc_mapme_create(hc_sock_t *s, hc_mapme_t *mapme) {
+ hc_object_t object;
+ memset(&object, 0, sizeof(hc_object_t));
+ object.mapme = *mapme;
+ return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_MAPME, &object, NULL);
+}
+
+DECLARE_OBJECT_OPS(OBJECT_TYPE_MAPME, mapme);
diff --git a/ctrl/libhicnctrl/src/objects/mapme.h b/ctrl/libhicnctrl/src/objects/mapme.h
new file mode 100644
index 000000000..488f97cd5
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/mapme.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file mapme.h
+ * \brief Route.
+ */
+
+#ifndef HICNCTRL_IMPL_OBJECTS_MAPME_H
+#define HICNCTRL_IMPL_OBJECTS_MAPME_H
+
+#include "../object_vft.h"
+
+bool hc_mapme_has_face(const hc_mapme_t* mapme);
+
+DECLARE_OBJECT_OPS_H(OBJECT_TYPE_MAPME, mapme);
+
+#endif /* HICNCTRL_IMPL_OBJECTS_MAPME_H */
diff --git a/ctrl/libhicnctrl/src/objects/route.c b/ctrl/libhicnctrl/src/objects/route.c
new file mode 100644
index 000000000..4bdaa0afb
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/route.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2021-2023 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * \file 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)) {
+ 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;
+ }
+
+ //::/0 is a valid remote addr
+#if 0
+ 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;
+ }
+#else
+ has_remote_addr = 1;
+#endif
+
+ if (!IS_VALID_ROUTE_COST(route->cost)) {
+ ERROR("[hc_route_validate] Invalid cost");
+ return -1;
+ }
+
+#if 0
+ if (!IS_VALID_PREFIX_LEN(route->len)) {
+ ERROR("[hc_route_validate] Invalid len");
+ return -1;
+ }
+#endif
+
+ if (hc_route_has_face(route)) {
+ if (hc_face_validate(&route->face, allow_partial) < 0) {
+ ERROR("[hc_route_validate] Invalid face");
+ return -1;
+ }
+ has_face = 1;
+ }
+
+ /* We also allow routes without faces, hence the '>' sign */
+ if (allow_partial && (has_id + has_name + has_face > 1)) return -1;
+ // if (has_face_info && has_family && has_remote_addr) return 0;
+ if (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..f9d0e1de7
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/stats.c
@@ -0,0 +1,108 @@
+/*
+ * 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 <hicn/ctrl/api.h>
+#include <hicn/util/log.h>
+
+#include "../object_vft.h"
+
+/* GENERAL STATS */
+
+int _hc_stats_validate(const hc_object_t *object, bool allow_partial) {
+ // Nothing to validate
+ return 0;
+}
+
+int _hc_stats_cmp(const hc_object_t *object1, const hc_object_t *object2) {
+ ERROR("[_hc_stats_cmp] Not implemented");
+ return -1;
+}
+
+int _hc_stats_snprintf(char *s, size_t size, const hc_object_t *object) {
+ return hc_stats_snprintf(s, size, &object->stats);
+}
+
+int hc_stats_snprintf(char *s, size_t size, const hc_stats_t *stats) {
+ return snprintf(
+ s, size,
+ "*** STATS ***\nreceived = %u (interest = %u, data = %u)\ndropped = %u "
+ "(interest = %u, data = %u, other = %u)\nforwarded = { interests = "
+ "%u, data = %u }\ndropped_reason = { connection_not_found = %u, "
+ "send_failure = %u, no_route_in_fib = %u }\ninterest processing = { "
+ "aggregated = %u, retransmitted = %u, satisfied_from_cs = %u, "
+ "expired_interests = %u, expired_data = %u }\ndata processing = { "
+ "no_reverse_path = %u }\npacket cache = {PIT size = %u, CS size = %u, "
+ "eviction = %u}",
+ stats->forwarder.countReceived, stats->forwarder.countInterestsReceived,
+ stats->forwarder.countObjectsReceived, stats->forwarder.countDropped,
+ stats->forwarder.countInterestsDropped,
+ stats->forwarder.countObjectsDropped, stats->forwarder.countOtherDropped,
+ stats->forwarder.countInterestForwarded,
+ stats->forwarder.countObjectsForwarded,
+ stats->forwarder.countDroppedConnectionNotFound,
+ stats->forwarder.countSendFailures, stats->forwarder.countDroppedNoRoute,
+ stats->forwarder.countInterestsAggregated,
+ stats->forwarder.countInterestsRetransmitted,
+ stats->forwarder.countInterestsSatisfiedFromStore,
+ stats->forwarder.countInterestsExpired, stats->forwarder.countDataExpired,
+ stats->forwarder.countDroppedNoReversePath,
+ stats->pkt_cache.n_pit_entries, stats->pkt_cache.n_cs_entries,
+ stats->pkt_cache.n_lru_evictions);
+}
+
+int hc_stats_list(hc_sock_t *s, hc_data_t **pdata) {
+ return hc_execute(s, ACTION_LIST, OBJECT_TYPE_STATS, NULL, pdata);
+}
+
+DECLARE_OBJECT_OPS(OBJECT_TYPE_STATS, stats);
+
+/* PER-FACE STATS */
+
+int _hc_face_stats_validate(const hc_object_t *object, bool allow_partial) {
+ // Nothing to validate
+ return 0;
+}
+
+int _hc_face_stats_cmp(const hc_object_t *object1, const hc_object_t *object2) {
+ ERROR("[_hc_stats_cmp] Not implemented");
+ return -1;
+}
+
+int _hc_face_stats_snprintf(char *s, size_t size, const hc_object_t *object) {
+ return hc_face_stats_snprintf(s, size, &object->face_stats);
+}
+
+int hc_face_stats_snprintf(char *s, size_t size, const hc_face_stats_t *stats) {
+ return snprintf(
+ s, size,
+ "conn #%u:\tinterests =\t{ rx packets = %u, rx bytes = %u, "
+ "tx packets = %u, tx bytes = %u }\n\t\tdata =\t\t{ rx packets "
+ "= %u, rx bytes = %u, "
+ "tx packets = %u, tx bytes = %u }",
+ stats->conn_id, stats->interests.rx_pkts, stats->interests.rx_bytes,
+ stats->interests.tx_pkts, stats->interests.tx_bytes, stats->data.rx_pkts,
+ stats->data.rx_bytes, stats->data.tx_pkts, stats->data.tx_bytes);
+}
+
+int hc_face_stats_list(hc_sock_t *s, hc_data_t **pdata) {
+ return hc_execute(s, ACTION_LIST, OBJECT_TYPE_FACE_STATS, NULL, pdata);
+}
+
+DECLARE_OBJECT_OPS(OBJECT_TYPE_FACE_STATS, face_stats); \ No newline at end of file
diff --git a/ctrl/libhicnctrl/src/objects/stats.h b/ctrl/libhicnctrl/src/objects/stats.h
new file mode 100644
index 000000000..3a4f04b2e
--- /dev/null
+++ b/ctrl/libhicnctrl/src/objects/stats.h
@@ -0,0 +1,29 @@
+/*
+ * 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 stats.h
+ * \brief Stats.
+ */
+
+#ifndef HICNCTRL_IMPL_OBJECTS_STATS_H
+#define HICNCTRL_IMPL_OBJECTS_STATS_H
+
+#include "../object_vft.h"
+
+DECLARE_OBJECT_OPS_H(OBJECT_TYPE_STATS, stats);
+DECLARE_OBJECT_OPS_H(OBJECT_TYPE_FACE_STATS, face_stats);
+
+#endif /* HICNCTRL_IMPL_OBJECTS_STATS_H */ \ No newline at end of file
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..8db55660d
--- /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 -99; /* Not implemented */
+}
+
+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..25fa98de2
--- /dev/null
+++ b/ctrl/libhicnctrl/src/parse.c
@@ -0,0 +1,412 @@
+/*
+ * 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 strategy b001::/16 local_remote",
+ "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..aa07cad00
--- /dev/null
+++ b/ctrl/libhicnctrl/src/request.h
@@ -0,0 +1,129 @@
+/*
+ * 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) \
+ _(CONNECTION_GET) \
+ _(CONNECTION_DELETE_WITH_ID) \
+ _(CONNECTION_DELETE_AFTER_GET) \
+ _(FACE_CREATE_CONNECTION_CREATE) \
+ _(FACE_DELETE_CONNECTION_DELETE) \
+ _(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 703b4763f..71e39d7ad 100644
--- a/ctrl/libhicnctrl/src/route.c
+++ b/ctrl/libhicnctrl/src/route.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * 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:
@@ -25,84 +25,64 @@
#define DEFAULT_HICN_ROUTE_COST 1
struct hicn_route_s {
- ip_prefix_t prefix;
- face_id_t face_id;
- route_cost_t cost; /* Optional, 0 means no value, defaults to 1 */
+ 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, route_cost_t cost)
-{
- hicn_route_t * route = malloc(sizeof(hicn_route_t));
- if (!route)
- return NULL;
- route->prefix = *prefix;
- route->face_id = face_id;
- route->cost = cost != 0 ? cost : DEFAULT_HICN_ROUTE_COST;
+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;
+ route->prefix = *prefix;
+ route->face_id = face_id;
+ route->cost = cost != 0 ? cost : DEFAULT_HICN_ROUTE_COST;
- return route;
+ return route;
}
-hicn_route_t *
-hicn_route_dup(const hicn_route_t * route)
-{
- hicn_route_t * new_route = malloc(sizeof(hicn_route_t));
- if (!route)
- return NULL;
- memcpy(new_route, route, sizeof(hicn_route_t));
- return new_route;
+hicn_route_t* hicn_route_dup(const hicn_route_t* route) {
+ hicn_route_t* new_route = malloc(sizeof(hicn_route_t));
+ if (!route) return NULL;
+ memcpy(new_route, route, sizeof(hicn_route_t));
+ return new_route;
}
-void hicn_route_free(hicn_route_t * route)
-{
- free(route);
-}
+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);
- if (rc != 0)
- return rc;
+int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2) {
+ int rc;
+ rc = hicn_ip_prefix_cmp(&route1->prefix, &route2->prefix);
+ if (rc != 0) return rc;
- return (route1->face_id > route2->face_id) ? 1 :
- (route1->face_id < route2->face_id) ? -1 : 0;
+ return (route1->face_id > route2->face_id) ? 1
+ : (route1->face_id < route2->face_id) ? -1
+ : 0;
}
-int
-hicn_route_get_prefix(const hicn_route_t * route, ip_prefix_t * prefix)
-{
- *prefix = route->prefix;
- return 0;
+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)
-{
- route->prefix = prefix;
- return 0;
+int hicn_route_set_prefix(hicn_route_t* route, const hicn_ip_prefix_t prefix) {
+ route->prefix = prefix;
+ return 0;
}
-int
-hicn_route_get_cost(const hicn_route_t * route, int * cost)
-{
- *cost = route->cost;
- return 0;
+int hicn_route_get_cost(const hicn_route_t* route, int* cost) {
+ *cost = route->cost;
+ return 0;
}
-int
-hicn_route_set_cost(hicn_route_t * route, const int cost)
-{
- route->cost = cost;
- return 0;
+int hicn_route_set_cost(hicn_route_t* route, const int cost) {
+ route->cost = cost;
+ return 0;
}
/* /!\ 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_PREFIX];
- ip_prefix_ntop(&route->prefix, prefix_s, MAXSZ_PREFIX);
- return snprintf(s, size, "%s [%d]", prefix_s, route->cost);
+size_t hicn_route_snprintf(char* s, size_t size, const hicn_route_t* route) {
+ char 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..956dc07c8
--- /dev/null
+++ b/ctrl/libhicnctrl/src/socket.c
@@ -0,0 +1,308 @@
+/*
+ * 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;
+}
+
+hc_sock_t *hc_sock_create_forwarder(forwarder_type_t forwarder) {
+ return hc_sock_create(forwarder, 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
diff --git a/ctrl/libhicnctrl/src/util/hash.h b/ctrl/libhicnctrl/src/util/hash.h
index 7c7bb1e3a..f3a1eedcc 100644
--- a/ctrl/libhicnctrl/src/util/hash.h
+++ b/ctrl/libhicnctrl/src/util/hash.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017-2019 Cisco and/or its affiliates.
+ * 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:
@@ -27,67 +27,88 @@
#ifndef UTIL_HASH_H
#define UTIL_HASH_H
-#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
- __BYTE_ORDER == __LITTLE_ENDIAN) || \
+#if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \
+ __BYTE_ORDER == __LITTLE_ENDIAN) || \
(defined(i386) || defined(__i386__) || defined(__i486__) || \
- defined(__i586__) || defined(__i686__) || defined(vax) || defined(MIPSEL))
-# define HASH_LITTLE_ENDIAN 1
-# define HASH_BIG_ENDIAN 0
+ defined(__i586__) || defined(__i686__) || defined(vax) || \
+ defined(MIPSEL))
+#define HASH_LITTLE_ENDIAN 1
+#define HASH_BIG_ENDIAN 0
#elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \
- __BYTE_ORDER == __BIG_ENDIAN) || \
- (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
-# define HASH_LITTLE_ENDIAN 0
-# define HASH_BIG_ENDIAN 1
+ __BYTE_ORDER == __BIG_ENDIAN) || \
+ (defined(sparc) || defined(POWERPC) || defined(mc68000) || defined(sel))
+#define HASH_LITTLE_ENDIAN 0
+#define HASH_BIG_ENDIAN 1
#else
-# define HASH_LITTLE_ENDIAN 0
-# define HASH_BIG_ENDIAN 0
+#define HASH_LITTLE_ENDIAN 0
+#define HASH_BIG_ENDIAN 0
#endif
-#define hashsize(n) ((uint32_t)1<<(n))
-#define hashmask(n) (hashsize(n)-1)
-#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
-
-#define mix(a,b,c) \
-{ \
- a -= c; a ^= rot(c, 4); c += b; \
- b -= a; b ^= rot(a, 6); a += c; \
- c -= b; c ^= rot(b, 8); b += a; \
- a -= c; a ^= rot(c,16); c += b; \
- b -= a; b ^= rot(a,19); a += c; \
- c -= b; c ^= rot(b, 4); b += a; \
-}
+#define hashsize(n) ((uint32_t)1 << (n))
+#define hashmask(n) (hashsize(n) - 1)
+#define rot(x, k) (((x) << (k)) | ((x) >> (32 - (k))))
-#define final(a,b,c) \
-{ \
- c ^= b; c -= rot(b,14); \
- a ^= c; a -= rot(c,11); \
- b ^= a; b -= rot(a,25); \
- c ^= b; c -= rot(b,16); \
- a ^= c; a -= rot(c,4); \
- b ^= a; b -= rot(a,14); \
- c ^= b; c -= rot(b,24); \
-}
+#define mix(a, b, c) \
+ { \
+ a -= c; \
+ a ^= rot(c, 4); \
+ c += b; \
+ b -= a; \
+ b ^= rot(a, 6); \
+ a += c; \
+ c -= b; \
+ c ^= rot(b, 8); \
+ b += a; \
+ a -= c; \
+ a ^= rot(c, 16); \
+ c += b; \
+ b -= a; \
+ b ^= rot(a, 19); \
+ a += c; \
+ c -= b; \
+ c ^= rot(b, 4); \
+ b += a; \
+ }
-static inline
-uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
-{
- uint32_t a,b,c; /* internal state */
- union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+#define final(a, b, c) \
+ { \
+ c ^= b; \
+ c -= rot(b, 14); \
+ a ^= c; \
+ a -= rot(c, 11); \
+ b ^= a; \
+ b -= rot(a, 25); \
+ c ^= b; \
+ c -= rot(b, 16); \
+ a ^= c; \
+ a -= rot(c, 4); \
+ b ^= a; \
+ b -= rot(a, 14); \
+ c ^= b; \
+ c -= rot(b, 24); \
+ }
+
+static inline uint32_t hashlittle(const void *key, size_t length,
+ uint32_t initval) {
+ uint32_t a, b, c; /* internal state */
+ union {
+ const void *ptr;
+ size_t i;
+ } u; /* needed for Mac Powerbook G4 */
/* Set up the internal state */
a = b = c = 0xdeadbeef + ((uint32_t)length) + initval;
u.ptr = key;
if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
- const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
/*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
- while (length > 12)
- {
+ while (length > 12) {
a += k[0];
b += k[1];
c += k[2];
- mix(a,b,c);
+ mix(a, b, c);
length -= 12;
k += 3;
}
@@ -104,136 +125,214 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
*/
#ifndef VALGRIND
- switch(length)
- {
- case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
- case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
- case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
- case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
- case 8 : b+=k[1]; a+=k[0]; break;
- case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
- case 6 : b+=k[1]&0xffff; a+=k[0]; break;
- case 5 : b+=k[1]&0xff; a+=k[0]; break;
- case 4 : a+=k[0]; break;
- case 3 : a+=k[0]&0xffffff; break;
- case 2 : a+=k[0]&0xffff; break;
- case 1 : a+=k[0]&0xff; break;
- case 0 : return c; /* zero length strings require no mixing */
+ switch (length) {
+ case 12:
+ c += k[2];
+ b += k[1];
+ a += k[0];
+ break;
+ case 11:
+ c += k[2] & 0xffffff;
+ b += k[1];
+ a += k[0];
+ break;
+ case 10:
+ c += k[2] & 0xffff;
+ b += k[1];
+ a += k[0];
+ break;
+ case 9:
+ c += k[2] & 0xff;
+ b += k[1];
+ a += k[0];
+ break;
+ case 8:
+ b += k[1];
+ a += k[0];
+ break;
+ case 7:
+ b += k[1] & 0xffffff;
+ a += k[0];
+ break;
+ case 6:
+ b += k[1] & 0xffff;
+ a += k[0];
+ break;
+ case 5:
+ b += k[1] & 0xff;
+ a += k[0];
+ break;
+ case 4:
+ a += k[0];
+ break;
+ case 3:
+ a += k[0] & 0xffffff;
+ break;
+ case 2:
+ a += k[0] & 0xffff;
+ break;
+ case 1:
+ a += k[0] & 0xff;
+ break;
+ case 0:
+ return c; /* zero length strings require no mixing */
}
#else /* make valgrind happy */
k8 = (const uint8_t *)k;
- switch(length)
- {
- case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
- case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
- case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
- case 9 : c+=k8[8]; /* fall through */
- case 8 : b+=k[1]; a+=k[0]; break;
- case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
- case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
- case 5 : b+=k8[4]; /* fall through */
- case 4 : a+=k[0]; break;
- case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
- case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
- case 1 : a+=k8[0]; break;
- case 0 : return c;
+ switch (length) {
+ case 12:
+ c += k[2];
+ b += k[1];
+ a += k[0];
+ break;
+ case 11:
+ c += ((uint32_t)k8[10]) << 16; /* fall through */
+ case 10:
+ c += ((uint32_t)k8[9]) << 8; /* fall through */
+ case 9:
+ c += k8[8]; /* fall through */
+ case 8:
+ b += k[1];
+ a += k[0];
+ break;
+ case 7:
+ b += ((uint32_t)k8[6]) << 16; /* fall through */
+ case 6:
+ b += ((uint32_t)k8[5]) << 8; /* fall through */
+ case 5:
+ b += k8[4]; /* fall through */
+ case 4:
+ a += k[0];
+ break;
+ case 3:
+ a += ((uint32_t)k8[2]) << 16; /* fall through */
+ case 2:
+ a += ((uint32_t)k8[1]) << 8; /* fall through */
+ case 1:
+ a += k8[0];
+ break;
+ case 0:
+ return c;
}
#endif /* !valgrind */
} else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
- const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
- const uint8_t *k8;
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
/*--------------- all but last block: aligned reads and different mixing */
- while (length > 12)
- {
- a += k[0] + (((uint32_t)k[1])<<16);
- b += k[2] + (((uint32_t)k[3])<<16);
- c += k[4] + (((uint32_t)k[5])<<16);
- mix(a,b,c);
+ while (length > 12) {
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ b += k[2] + (((uint32_t)k[3]) << 16);
+ c += k[4] + (((uint32_t)k[5]) << 16);
+ mix(a, b, c);
length -= 12;
k += 6;
}
/*----------------------------- handle the last (probably partial) block */
k8 = (const uint8_t *)k;
- switch(length)
- {
- case 12: c+=k[4]+(((uint32_t)k[5])<<16);
- b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
- case 10: c+=k[4];
- b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 9 : c+=k8[8]; /* fall through */
- case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
- case 6 : b+=k[2];
- a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 5 : b+=k8[4]; /* fall through */
- case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
- break;
- case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
- case 2 : a+=k[0];
- break;
- case 1 : a+=k8[0];
- break;
- case 0 : return c; /* zero length requires no mixing */
+ switch (length) {
+ case 12:
+ c += k[4] + (((uint32_t)k[5]) << 16);
+ b += k[2] + (((uint32_t)k[3]) << 16);
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ break;
+ case 11:
+ c += ((uint32_t)k8[10]) << 16; /* fall through */
+ case 10:
+ c += k[4];
+ b += k[2] + (((uint32_t)k[3]) << 16);
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ break;
+ case 9:
+ c += k8[8]; /* fall through */
+ case 8:
+ b += k[2] + (((uint32_t)k[3]) << 16);
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ break;
+ case 7:
+ b += ((uint32_t)k8[6]) << 16; /* fall through */
+ case 6:
+ b += k[2];
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ break;
+ case 5:
+ b += k8[4]; /* fall through */
+ case 4:
+ a += k[0] + (((uint32_t)k[1]) << 16);
+ break;
+ case 3:
+ a += ((uint32_t)k8[2]) << 16; /* fall through */
+ case 2:
+ a += k[0];
+ break;
+ case 1:
+ a += k8[0];
+ break;
+ case 0:
+ return c; /* zero length requires no mixing */
}
- } else { /* need to read the key one byte at a time */
+ } else { /* need to read the key one byte at a time */
const uint8_t *k = (const uint8_t *)key;
/*--------------- all but the last block: affect some 32 bits of (a,b,c) */
- while (length > 12)
- {
+ while (length > 12) {
a += k[0];
- a += ((uint32_t)k[1])<<8;
- a += ((uint32_t)k[2])<<16;
- a += ((uint32_t)k[3])<<24;
+ a += ((uint32_t)k[1]) << 8;
+ a += ((uint32_t)k[2]) << 16;
+ a += ((uint32_t)k[3]) << 24;
b += k[4];
- b += ((uint32_t)k[5])<<8;
- b += ((uint32_t)k[6])<<16;
- b += ((uint32_t)k[7])<<24;
+ b += ((uint32_t)k[5]) << 8;
+ b += ((uint32_t)k[6]) << 16;
+ b += ((uint32_t)k[7]) << 24;
c += k[8];
- c += ((uint32_t)k[9])<<8;
- c += ((uint32_t)k[10])<<16;
- c += ((uint32_t)k[11])<<24;
- mix(a,b,c);
+ c += ((uint32_t)k[9]) << 8;
+ c += ((uint32_t)k[10]) << 16;
+ c += ((uint32_t)k[11]) << 24;
+ mix(a, b, c);
length -= 12;
k += 12;
}
/*-------------------------------- last block: affect all 32 bits of (c) */
- switch(length) /* all the case statements fall through */
+ switch (length) /* all the case statements fall through */
{
- case 12: c+=((uint32_t)k[11])<<24;
- case 11: c+=((uint32_t)k[10])<<16;
- case 10: c+=((uint32_t)k[9])<<8;
- case 9 : c+=k[8];
- case 8 : b+=((uint32_t)k[7])<<24;
- case 7 : b+=((uint32_t)k[6])<<16;
- case 6 : b+=((uint32_t)k[5])<<8;
- case 5 : b+=k[4];
- case 4 : a+=((uint32_t)k[3])<<24;
- case 3 : a+=((uint32_t)k[2])<<16;
- case 2 : a+=((uint32_t)k[1])<<8;
- case 1 : a+=k[0];
- break;
- case 0 : return c;
+ case 12:
+ c += ((uint32_t)k[11]) << 24;
+ case 11:
+ c += ((uint32_t)k[10]) << 16;
+ case 10:
+ c += ((uint32_t)k[9]) << 8;
+ case 9:
+ c += k[8];
+ case 8:
+ b += ((uint32_t)k[7]) << 24;
+ case 7:
+ b += ((uint32_t)k[6]) << 16;
+ case 6:
+ b += ((uint32_t)k[5]) << 8;
+ case 5:
+ b += k[4];
+ case 4:
+ a += ((uint32_t)k[3]) << 24;
+ case 3:
+ a += ((uint32_t)k[2]) << 16;
+ case 2:
+ a += ((uint32_t)k[1]) << 8;
+ case 1:
+ a += k[0];
+ break;
+ case 0:
+ return c;
}
}
- final(a,b,c);
+ final(a, b, c);
return c;
}
@@ -241,7 +340,7 @@ uint32_t hashlittle( const void *key, size_t length, uint32_t initval)
#define HASH_INITVAL 1
//#define hash(buf, len) (hash_t)hashlittle(buf, len, HASH_INITVAL)
-#define hash(buf, len) hashlittle(buf, len, HASH_INITVAL)
+#define hash(buf, len) hashlittle(buf, len, HASH_INITVAL)
#define hash_struct(buf) hash(buf, sizeof(buf))
#endif /* UTIL_JENKINS_HASH_H */