diff options
author | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:06:34 +0200 |
---|---|---|
committer | Luca Muscariello <muscariello@ieee.org> | 2022-08-04 16:31:51 +0200 |
commit | 6d22a0db96aa7f8e3102ae44d00c09e36a2e9c57 (patch) | |
tree | 79546bbf09f6fbf74db7bc89117843f06ce937ea /ctrl/libhicnctrl | |
parent | 012843b1c0bc0838e69085ed83a79ec8b6f97360 (diff) |
feat: Due to the deep modifications related to names and packet format,
this task cover a large part of the codebase and involves several changes:
- the library provides a name data structure (hicn_name_t ), which is composed
of a name prefix (hicn_name_prefix_t) and a name suffix (hicn_name_suffix_t),
and it has been extended to provide all support functions required for name
manipulation, including common prefix computation, as required for the Longest
Prefix Match (LPM)in the forwarder, in addition to Exact Prefix Match (EPM).
- all code has been rewritten to use this data structure instead of having for
instance the forwarder define its own name class (used to be Name and NameBitVector)
the code has been refactored to minimize name allocations and copies, one remaining
aspect is the difference of name storage between PIT and CS entries (respectively
in the PIT entry, and in the message buffer), which causes the packet cache
index to be updated when a PIT entry is converted into a CS entry. By storing
the name in the PIT/CS entry everytime, we might save on this operation).
- hicn-light FIB has been rewritten : code has been refactored and should now be
shorter and documented; unit tests have been drafted but more would be required
to cover all cases and match the algorithms to add/remove nodes, as specified in the doc.
all protocol details and hICN header formats are now abstracted by the library
for the forwarder (and thus header.h and protocols/*.h have been removed from
public includes, and replaced by packet.h providing protocol agnostic packet
level functions, completely replacing the compat.h header that used to provide
similar functions.
- this works by exposing a opaque buffer to the application (a kind of socket buffer)
which is used by the lib to cache the packet format and offsets of the different
layers in the buffer and provider efficient operations (the packet format is
either defined for packet construction, or guessed at ingress, and this structure
is updated accordingly only once).
Co-authored-by: Jordan Augé <jordan.auge+fdio@cisco.com>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Change-Id: I31e321897f85f0267fe8ba4720363c180564492f
Diffstat (limited to 'ctrl/libhicnctrl')
121 files changed, 12222 insertions, 7892 deletions
diff --git a/ctrl/libhicnctrl/examples/Makefile b/ctrl/libhicnctrl/examples/Makefile index ad0e46a1e..1c1eb64e1 100644 --- a/ctrl/libhicnctrl/examples/Makefile +++ b/ctrl/libhicnctrl/examples/Makefile @@ -1,7 +1,7 @@ EXEC = $(shell basename $$(pwd)) CC = gcc -CFLAGS = -std=gnu11 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing -DWITH_POLICY=1 +CFLAGS = -std=gnu11 -g -Wall -Wextra -Wpedantic -Wstrict-aliasing LDFLAGS = -lhicn -lhicnctrl SRC = $(wildcard *.c) diff --git a/ctrl/libhicnctrl/examples/create_face.c b/ctrl/libhicnctrl/examples/create_face.c index ebd451de1..2152ff1e3 100644 --- a/ctrl/libhicnctrl/examples/create_face.c +++ b/ctrl/libhicnctrl/examples/create_face.c @@ -104,10 +104,8 @@ int main() { .remote_port = 6000, .admin_state = FACE_STATE_UNDEFINED, .state = FACE_STATE_UNDEFINED, -#ifdef WITH_POLICY .priority = 0, .tags = POLICY_TAGS_EMPTY, -#endif /* WITH_POLICY */ }, }; if (netdevice_set_name(&face.face.netdevice, if_name) < 0) { diff --git a/ctrl/libhicnctrl/includes/CMakeLists.txt b/ctrl/libhicnctrl/includes/CMakeLists.txt index 1a90690a4..554021c48 100644 --- a/ctrl/libhicnctrl/includes/CMakeLists.txt +++ b/ctrl/libhicnctrl/includes/CMakeLists.txt @@ -27,8 +27,29 @@ set(Libhicnctrl_INCLUDE_DIRS set(TO_INSTALL_HEADER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/ctrl.h ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/api.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/action.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/callback.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/command.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/data.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/fw_interface.h ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/hicn-light.h - ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/hicn-light-ng.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/object.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/object_type.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/active_interface.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/base.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/cache.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/face.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/mapme.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/policy.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/punting.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/objects/subscription.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/parse.h ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn/ctrl/socket.h PARENT_SCOPE ) diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl.h b/ctrl/libhicnctrl/includes/hicn/ctrl.h index 477afd152..4decdf10f 100644 --- a/ctrl/libhicnctrl/includes/hicn/ctrl.h +++ b/ctrl/libhicnctrl/includes/hicn/ctrl.h @@ -21,5 +21,6 @@ #define HICNCTRL_H #include <hicn/ctrl/api.h> +#include <hicn/ctrl/socket.h> #endif /* HICNCTRL_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/action.h b/ctrl/libhicnctrl/includes/hicn/ctrl/action.h new file mode 100644 index 000000000..55c4ebf77 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/action.h @@ -0,0 +1,50 @@ +/* + * 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.h + * \brief Actions. + */ + +#ifndef HICNCTRL_ACTION_H +#define HICNCTRL_ACTION_H + +#define foreach_action \ + _(UNDEFINED) \ + _(CREATE) \ + _(UPDATE) \ + _(DELETE) \ + _(LIST) \ + _(GET) \ + _(SET) \ + _(SERVE) \ + _(STORE) \ + _(CLEAR) \ + _(SUBSCRIBE) \ + _(N) + +typedef enum { +#define _(x) ACTION_##x, + foreach_action +#undef _ +} hc_action_t; + +extern const char *action_str[]; + +#define action_str(x) action_str[x] + +hc_action_t action_from_str(const char *action_str); + +#endif /* HICNCTRL_ACTION_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/api.h b/ctrl/libhicnctrl/includes/hicn/ctrl/api.h index c259fc10c..21a5e548f 100644 --- a/ctrl/libhicnctrl/includes/hicn/ctrl/api.h +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/api.h @@ -58,8 +58,6 @@ * provide a set of defines to preserve backwards compatibility. At the * moment, those defines are : * - * WITH_POLICY: - * */ #ifndef HICNTRL_API @@ -69,9 +67,17 @@ #include <stdint.h> #include <stddef.h> // object_offset_t +#include <hicn/ctrl/action.h> +#include <hicn/ctrl/callback.h> +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/object_type.h> +#include <hicn/ctrl/objects.h> +#include <hicn/ctrl/socket.h> #include <hicn/util/ip_address.h> #include <hicn/face.h> #include <hicn/strategy.h> + #include <hicn/base.h> /* * This has to be common between hicn-light and hicn-plugin. We now we keep the @@ -79,13 +85,12 @@ */ #define SYMBOLIC_NAME_LEN 16 +#include <hicn/ctrl/objects.h> + #define HICN_DEFAULT_PORT 9695 #define HOTFIXMARGIN 0 -#define INVALID_FACE_ID ~0 -#define INVALID_NETDEVICE_ID ~0 - /** * \brief Defines the default size for the allocated data arrays holding the * results of API calls. @@ -105,336 +110,21 @@ })x) \ .b) #endif + +#define MAX2(x1, x2) (x1 > x2 ? x1 : x2) +#define MAX4(x1, x2, x3, x4) (MAX2(MAX2(x1, x2), MAX2(x3, x4))) +#define MAX8(x1, x2, x3, x4, x5, x6, x7, x8) \ + (MAX2(MAX4(x1, x2, x3, x4), MAX4(x5, x6, x7, x8))) + /****************************************************************************** * Message helper types and aliases ******************************************************************************/ -/* Action */ - -#define foreach_action \ - _(UNDEFINED) \ - _(CREATE) \ - _(UPDATE) \ - _(DELETE) \ - _(LIST) \ - _(SET) \ - _(SERVE) \ - _(STORE) \ - _(CLEAR) \ - _(GET) \ - _(N) - -typedef enum { -#define _(x) ACTION_##x, - foreach_action -#undef _ -} hc_action_t; - -extern const char *action_str[]; - -#define action_str(x) action_str[x] - -hc_action_t action_from_str(const char *action_str); - -/* Object type */ - -#define foreach_object \ - _(UNDEFINED) \ - _(CONNECTION) \ - _(LISTENER) \ - _(ROUTE) \ - _(FACE) \ - _(STRATEGY) \ - _(PUNTING) \ - _(POLICY) \ - _(CACHE) \ - _(MAPME) \ - _(LOCAL_PREFIX) \ - _(PROBE) \ - _(SUBSCRIPTION) \ - _(STATS) \ - _(N) - -typedef enum { -#define _(x) OBJECT_##x, - foreach_object -#undef _ -} hc_object_type_t; - -extern const char *object_str[]; - -#define object_str(x) object_str[x] - -hc_object_type_t object_from_str(const char *object_str); - -#define IS_VALID_OBJECT_TYPE(x) IS_VALID_ENUM_TYPE(OBJECT, x) -#define IS_VALID_ACTION(x) IS_VALID_ENUM_TYPE(ACTION, x) - /** * \brief hICN control message header */ typedef struct hc_msg_s hc_msg_t; typedef struct hc_result_s hc_result_t; -/****************************************************************************** - * Control Data - ******************************************************************************/ - -struct hc_data_s; -typedef int (*data_callback_t)(struct hc_data_s *, void *); - -/** - * \brief Holds the results of an hICN control request - */ -typedef struct hc_data_s { - size_t size; - size_t current; - size_t max_size_log; - size_t in_element_size; - size_t out_element_size; - u8 command_id; /**< Expected message type (should give element size) */ - u8 *buffer; - bool complete; - - /* Callbacks */ - data_callback_t - complete_cb; // XXX int (*complete_cb)(struct hc_data_s * data); - void *complete_cb_data; - int ret; -} hc_data_t; - -/** - * Create a structure holding the results of an hICN control request. - * \result The newly create data structure. - */ -hc_data_t *hc_data_create(size_t in_element_size, size_t out_element_size, - data_callback_t complete_cb); - -/** - * Free a structure holding the results of an hICN control request. - * \param [in] data - The data structure to free. - */ -void hc_data_free(hc_data_t *data); - -/** - * \brief Adds many new results at the end of the data structure, eventually - * allocating buffer space for it. - * \param [in] data - The data structure to which to add elements. - * \param [in] elements - The array of elements to add. - * \param [in] count - The number of elements to add. - * \return Error code - * - * NOTE: The size of the element should match the one declared at structure - * initialization. - */ -int hc_data_push_many(hc_data_t *data, const void *elements, size_t count); - -/** - * \brief Adds a new result at the end of the data structure, eventually - * allocating buffer space for it. - * \param [in] data - The data structure to which to add an element. - * \param [in] element - The element to add - * \return Error code - * - * NOTE: The size of the element should match the one declared at structure - * initialization. - */ -int hc_data_push(hc_data_t *data, const void *element); - -/** - * \brief Configure a callback (along with private data) to be called upon - * completion of a request - * \param [in] data - hICN control data - * \param [in] cb - Callback function - * \param [in] cb_data - Callback private data - */ -int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data); - -/** - * \brief Mark the data structure as complete. - * \param [in] data - The data structure to which to add an element. - * \return The error code resulting from callback execution if any. 0 is - * returned if the callback executed successfully, or if no callback were - * defined. - */ -int hc_data_set_complete(hc_data_t *data); - -/** - * \brief Reset the data structure holding control data - * \param [in] data - hICN control data - * \return Error code - */ -int hc_data_reset(hc_data_t *data); - -/** - * \brief Find en element in the data structure - * \param [in] data - The data structure in which to find - * \param [in] element - The element to find - * \param [out] found - A pointer to the element, or NULL if not found. - * \return Error code - */ -#define GENERATE_FIND_HEADER(TYPE) \ - int hc_##TYPE##_find(hc_data_t *data, const hc_##TYPE##_t *element, \ - hc_##TYPE##_t **found) - -#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; \ - } - -/****************************************************************************** - * Control socket - ******************************************************************************/ - -/* With UDP, the buffer should be able to receieve a full packet, and thus MTU - * (max 9000) is sufficient. Messages will be received fully one by one. - * With TCP, the buffer should be at least able to receive a message header and - * the maximum size of a data element, so any reasonable size will be correct, - * it might just optimize performance. Messages might arrive in chunks that the - * library is able to parse. - */ -#define JUMBO_MTU 9000 -#define RECV_BUFLEN 65535 - -#define foreach_forwarder_type \ - _(UNDEFINED) \ - _(HICNLIGHT) \ - _(HICNLIGHT_NG) \ - _(VPP) \ - _(N) - -typedef enum { -#define _(x) x, - foreach_forwarder_type -#undef _ -} forwarder_type_t; - -/** - * \brief Holds the state of an hICN control socket - */ -typedef struct hc_sock_s hc_sock_t; - -/** - * \brief Create an hICN control socket using the specified URL. - * \param [in] url - The URL to connect to. - * \return an hICN control socket - */ -hc_sock_t *hc_sock_create_url(const char *url); - -/** - * \brief Create an hICN control socket using the provided forwarder. - * \return an hICN control socket - */ -hc_sock_t *hc_sock_create_forwarder(forwarder_type_t forwarder); - -/** - * \brief Create an hICN control socket using the provided forwarder and a URL. - * \return an hICN control socket - */ -hc_sock_t *hc_sock_create_forwarder_url(forwarder_type_t forwarder, - const char *url); - -/** - * \brief Create an hICN control socket using the default connection type. - * \return an hICN control socket - */ -hc_sock_t *hc_sock_create(void); - -/** - * \brief Frees an hICN control socket - * \param [in] s - hICN control socket - */ -void hc_sock_free(hc_sock_t *s); - -/** - * \brief Returns the next available sequence number to use for requests to the - * API. - * \param [in] s - hICN control socket - */ -int hc_sock_get_next_seq(hc_sock_t *s); - -/** - * \brief Sets the socket as non-blocking - * \param [in] s - hICN control socket - * \return Error code - */ -int hc_sock_set_nonblocking(hc_sock_t *s); - -/** - * \brief Return the file descriptor associated to the hICN contorl sock - * \param [in] s - hICN control socket - * \return The file descriptor (positive value), or a negative integer in case - * of error - */ -int hc_sock_get_fd(hc_sock_t *s); - -/** - * \brief Connect the socket - * \return Error code - */ -int hc_sock_connect(hc_sock_t *s); - -/** - * \brief Return the offset and size of available buffer space - * \param [in] s - hICN control socket - * \param [out] buffer - Offset in buffer - * \param [out] size - Remaining size - * \return Error code - */ -int hc_sock_get_available(hc_sock_t *s, u8 **buffer, size_t *size); - -/** - * \brief Write/read iexchance on the control socket (internal helper function) - * \param [in] s - hICN control socket - * \param [in] msg - Message to send - * \param [in] msglen - Length of the message to send - * \return Error code - */ -int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq); - -/** - * \brief Helper for reading socket contents - * \param [in] s - hICN control socket - * \return Error code - */ -int hc_sock_recv(hc_sock_t *s); - -/** - * \brief Processing data received by socket - * \param [in] s - hICN control socket - * \param [in] parse - Parse function to convert remote types into lib native - * types, or NULL not to perform any translation. - * \return Error code - */ -int hc_sock_process(hc_sock_t *s, hc_data_t **data); - -/** - * \brief Callback used in async mode when data is available on the socket - * \param [in] s - hICN control socket - * \return Error code - */ -int hc_sock_callback(hc_sock_t *s, hc_data_t **data); - -/** - * \brief Reset the state of the sock (eg. to handle a reconnecton) - * \param [in] s - hICN control socket - * \return Error code - */ -int hc_sock_reset(hc_sock_t *s); - -void hc_sock_increment_woff(hc_sock_t *s, size_t bytes); - -int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, - data_callback_t complete_cb, void *complete_cb_data); - -int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms); /****************************************************************************** * Command-specific structures and functions @@ -448,7 +138,8 @@ int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms); * * We redefine command struct: * - for uniformization - * - to use enum instead of type specifiers more appropriate for packet format + * - to use enum instead of type specifiers more appropriate for packet + * format * - to use more flexible types such as for manipulating IP addresses * - host endianness * - more intuitive field name, ordering, consistency, and hierarchy removal @@ -460,9 +151,9 @@ int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms); * * RETURN DATA FIXME * - * \param [out] pdata - Pointer to the structure storing the results of the call - * (NULL if no data has been received). If the pointer is NULL, no result will - * be stored and only the error code will be exposed to the caller. It is + * \param [out] pdata - Pointer to the structure storing the results of the + * call (NULL if no data has been received). If the pointer is NULL, no result + * will be stored and only the error code will be exposed to the caller. It is * expected that the caller frees this structure using hc_data_free() after * usage. * \see hc_data_free. @@ -487,147 +178,134 @@ int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms); #define NULLTERM 1 #endif -#define INTERFACE_LEN 16 - #define MAXSZ_HC_NAME_ SYMBOLIC_NAME_LEN #define MAXSZ_HC_NAME MAXSZ_HC_NAME_ + NULLTERM #define MAXSZ_HC_ID_ 10 /* Number of digits for MAX_INT */ #define MAXSZ_HC_ID MAXSZ_HC_ID_ + NULLTERM -#define foreach_type(TYPE, VAR, data) \ - for (TYPE *VAR = (TYPE *)data->buffer; \ - VAR < (TYPE *)(data->buffer + data->size * data->out_element_size); \ - VAR++) - -typedef int (*HC_PARSE)(const u8 *, u8 *); +#if 0 +#define foreach_type(TYPE, VAR, data) \ + for (TYPE *VAR = (TYPE *)data->buffer; \ + VAR < (TYPE *)((data)->buffer + (data)->size * sizeof(TYPE)); VAR++) +#endif #define INPUT_ERROR -2 #define UNSUPPORTED_CMD_ERROR -3 /*----------------------------------------------------------------------------* - * Listeners + * Strategy *----------------------------------------------------------------------------*/ -// FIXME the listener should not require any port for hICN... -typedef struct { - char name[SYMBOLIC_NAME_LEN]; /* K.w */ // XXX clarify what used for - char interface_name[INTERFACE_LEN]; /* Kr. */ - u32 id; - face_type_t type; /* .rw */ - int family; /* .rw */ - ip_address_t local_addr; /* .rw */ - u16 local_port; /* .rw */ -} hc_listener_t; +/*----------------------------------------------------------------------------* + * WLDR + *----------------------------------------------------------------------------*/ -int hc_listener_create(hc_sock_t *s, hc_listener_t *listener); -/* listener_found might eventually be allocated, and needs to be freed */ -hc_result_t *hc_listener_create_conf(hc_sock_t *s, hc_listener_t *listener); -int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found); -int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener); -int hc_listener_list(hc_sock_t *s, hc_data_t **pdata); -hc_result_t *hc_listener_list_conf(hc_sock_t *s); +// per connection +int hc_wldr_set(hc_sock_t *s /* XXX */); -int hc_listener_validate(const hc_listener_t *listener); -int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2); +/*----------------------------------------------------------------------------* + * MAP-Me + *----------------------------------------------------------------------------*/ -#define foreach_listener(VAR, data) foreach_type(hc_listener_t, VAR, data) +/*----------------------------------------------------------------------------* + * Policies + *----------------------------------------------------------------------------*/ -#define MAXSZ_HC_LISTENER_ \ - INTERFACE_LEN + SPACE + MAXSZ_URL_ + SPACE + MAXSZ_FACE_TYPE_ -#define MAXSZ_HC_LISTENER MAXSZ_HC_LISTENER_ + NULLTERM +/*----------------------------------------------------------------------------* + * Subscription + *----------------------------------------------------------------------------*/ +// Topics -GENERATE_FIND_HEADER(listener); +#if 0 +/* Result */ -int hc_listener_snprintf(char *s, size_t size, hc_listener_t *listener); +hc_msg_t *hc_result_get_msg(hc_sock_t *s, hc_result_t *result); +int hc_result_get_cmd_id(hc_sock_t *s, hc_result_t *result); +bool hc_result_get_success(hc_sock_t *s, hc_result_t *result); +void hc_result_free(hc_result_t *result); +#endif -/*----------------------------------------------------------------------------* - * Connections - *----------------------------------------------------------------------------*/ +/* Object */ + +// FIXME +#define MAXSZ_HC_SUBSCRIPTION 1 + +#define MAXSZ_HC_OBJECT \ + MAX8(MAXSZ_HC_CONNECTION, MAXSZ_HC_LISTENER, MAXSZ_HC_ROUTE, MAXSZ_HC_FACE, \ + MAXSZ_HC_PUNTING, MAXSZ_HC_STRATEGY, MAXSZ_HC_POLICY, \ + MAXSZ_HC_SUBSCRIPTION) -/* - * NOTE : - * - interface_name is mainly used to derive listeners from connections, - * but is not itself used to create connections. - */ typedef struct { - u32 id; /* Kr. */ - char name[SYMBOLIC_NAME_LEN]; /* K.w */ - char interface_name[INTERFACE_LEN]; /* Kr. */ - face_type_t type; /* .rw */ - int family; /* .rw */ - ip_address_t local_addr; /* .rw */ - u16 local_port; /* .rw */ - ip_address_t remote_addr; /* .rw */ - u16 remote_port; /* .rw */ - face_state_t admin_state; /* .rw */ -#ifdef WITH_POLICY - uint32_t priority; /* .rw */ - policy_tags_t tags; /* .rw */ -#endif /* WITH_POLICY */ - face_state_t state; /* .r. */ -} hc_connection_t; + hc_action_t action; + hc_object_type_t object_type; + hc_object_t object; +} hc_command_t; + +// NEW API CALLS + +// XXX private ? +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); +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); +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); + +int hc_object_create(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object); +int hc_object_get(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object, hc_object_t **found); +int hc_object_delete(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object); +int hc_object_list(hc_sock_t *s, hc_object_type_t object_type, + hc_data_t **pdata); + +/* Former API */ + +int hc_listener_create(hc_sock_t *s, hc_listener_t *listener); +/* listener_found might eventually be allocated, and needs to be freed */ +int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, hc_data_t **pdata); +int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener); +int hc_listener_list(hc_sock_t *s, hc_data_t **pdata); int hc_connection_create(hc_sock_t *s, hc_connection_t *connection); -hc_result_t *hc_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection); -/* connection_found will be allocated, and must be freed */ +/* connection_found might eventually be allocated, and needs to be freed */ int hc_connection_get(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found); + hc_data_t **pdata); +int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection); int hc_connection_update_by_id(hc_sock_t *s, int hc_connection_id, hc_connection_t *connection); int hc_connection_update(hc_sock_t *s, hc_connection_t *connection_current, hc_connection_t *connection_updated); -int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection); -hc_result_t *hc_connection_delete_conf(hc_sock_t *s, - hc_connection_t *connection); +int hc_connection_list(hc_sock_t *s, hc_data_t **pdata); + +int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, + face_state_t state); +int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, + uint32_t priority); +int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, + policy_tags_t tags); + /* int hc_connection_remove_by_id(hc_sock_t * s, char * name); int hc_connection_remove_by_name(hc_sock_t * s, char * name); */ -int hc_connection_list(hc_sock_t *s, hc_data_t **pdata); - -int hc_connection_validate(const hc_connection_t *connection); -int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2); int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, face_state_t state); -#ifdef WITH_POLICY int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, uint32_t priority); int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, policy_tags_t tags); -#endif /* WITH_POLICY */ - -#define foreach_connection(VAR, data) foreach_type(hc_connection_t, VAR, data) - -#define MAXSZ_HC_CONNECTION_ \ - MAXSZ_FACE_STATE_ + INTERFACE_LEN + SPACE + 2 * MAXSZ_URL_ + \ - MAXSZ_FACE_TYPE_ + SPACES(3) -#define MAXSZ_HC_CONNECTION MAXSZ_HC_CONNECTION_ + NULLTERM - -GENERATE_FIND_HEADER(connection); -int hc_connection_snprintf(char *s, size_t size, - const hc_connection_t *connection); - -/*----------------------------------------------------------------------------* - * Faces - * - * A face is an abstraction introduced by the control library to abstract the - * forwarder implementation details. It encompasses connections and listeners - * and ensures the right dependencies are enforced, eg that we always have a - * listener when a connection is created. - * - *----------------------------------------------------------------------------*/ - -typedef struct { - face_id_t id; - char name[SYMBOLIC_NAME_LEN]; - face_t face; // or embed ? - // face_id_t parent; /* Pointer from connection to listener */ -} hc_face_t; +int hc_route_create(hc_sock_t *s, hc_route_t *route); +// hc_result_t *hc_route_create_conf(hc_sock_t *s, hc_route_t *route); +int hc_route_delete(hc_sock_t *s, hc_route_t *route); +int hc_route_list(hc_sock_t *s, hc_data_t **pdata); +int hc_route_list_async(hc_sock_t *s); /** * \brief Create a face @@ -638,194 +316,27 @@ typedef struct { * The face parameters will be updated with the face ID. */ int hc_face_create(hc_sock_t *s, hc_face_t *face); -int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_face_t **face_found); -int hc_face_delete(hc_sock_t *s, hc_face_t *face, uint8_t delete_listener); +int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_data_t **pdata); +int hc_face_delete(hc_sock_t *s, + hc_face_t *face); //, uint8_t delete_listener); int hc_face_list(hc_sock_t *s, hc_data_t **pdata); -int hc_face_list_async(hc_sock_t *s); //, hc_data_t ** pdata); +int hc_face_list_async(hc_sock_t *s); int hc_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, face_state_t state); -#ifdef WITH_POLICY int hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, uint32_t priority); int hc_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, policy_tags_t tags); -#endif /* WITH_POLICY */ - -#define foreach_face(VAR, data) foreach_type(hc_face_t, VAR, data) - -#define MAX_FACE_ID 255 -#define MAXSZ_FACE_ID_ 3 -#define MAXSZ_FACE_ID MAXSZ_FACE_ID_ + NULLTERM -#define MAXSZ_FACE_NAME_ SYMBOLIC_NAME_LEN -#define MAXSZ_FACE_NAME MAXSZ_FACE_NAME_ + NULLTERM - -#define MAXSZ_HC_FACE_ \ - MAXSZ_FACE_ID_ + MAXSZ_FACE_NAME_ + MAXSZ_FACE_ + 5 + HOTFIXMARGIN -#define MAXSZ_HC_FACE MAXSZ_HC_FACE_ + NULLTERM - -int hc_face_snprintf(char *s, size_t size, hc_face_t *face); -/*----------------------------------------------------------------------------* - * Routes - *----------------------------------------------------------------------------*/ - -typedef struct { - face_id_t face_id; /* Kr. use when name == NULL */ - char name[SYMBOLIC_NAME_LEN]; /* Kr. use by default vs face_id */ - int family; /* Krw */ - ip_address_t remote_addr; /* krw */ - u8 len; /* krw */ - u16 cost; /* .rw */ - hc_face_t face; /* TODO remove, used by hicn_plugin_api */ -} hc_route_t; - -int hc_route_create(hc_sock_t *s, hc_route_t *route); -hc_result_t *hc_route_create_conf(hc_sock_t *s, hc_route_t *route); -int hc_route_delete(hc_sock_t *s, hc_route_t *route); -int hc_route_list(hc_sock_t *s, hc_data_t **pdata); -int hc_route_list_async(hc_sock_t *s); - -#define foreach_route(VAR, data) foreach_type(hc_route_t, VAR, data) - -#define MAX_COST 65535 -#define MAXSZ_COST 5 -#define MAX_LEN 255 -#define MAXSZ_LEN 3 - -#define MAXSZ_HC_ROUTE_ \ - MAXSZ_FACE_ID + 1 + MAXSZ_COST + 1 + MAXSZ_IP_ADDRESS + 1 + MAXSZ_LEN -#define MAXSZ_HC_ROUTE MAXSZ_HC_ROUTE_ + NULLTERM - -int hc_route_snprintf(char *s, size_t size, hc_route_t *route); -int hc_route_validate(const hc_route_t *route); - -/*----------------------------------------------------------------------------* - * Punting - *----------------------------------------------------------------------------*/ - -typedef struct { - face_id_t face_id; /* Kr. */ // XXX listener id, could be NULL for all ? - int family; /* Krw */ - ip_address_t prefix; /* krw */ - u8 prefix_len; /* krw */ -} hc_punting_t; - -int hc_punting_create(hc_sock_t *s, hc_punting_t *punting); -int hc_punting_get(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found); -int hc_punting_delete(hc_sock_t *s, hc_punting_t *punting); -int hc_punting_list(hc_sock_t *s, hc_data_t **pdata); - -int hc_punting_validate(const hc_punting_t *punting); -int hc_punting_cmp(const hc_punting_t *c1, const hc_punting_t *c2); - -#define foreach_punting(VAR, data) foreach_type(hc_punting_t, VAR, data) - -#define MAXSZ_HC_PUNTING_ 0 -#define MAXSZ_HC_PUNTING MAXSZ_HC_PUNTING_ + NULLTERM - -GENERATE_FIND_HEADER(punting); - -int hc_punting_snprintf(char *s, size_t size, hc_punting_t *punting); - -/*----------------------------------------------------------------------------* - * Cache - *----------------------------------------------------------------------------*/ - -typedef struct { - uint8_t serve; // 1 = on, 0 = off - uint8_t store; // 1 = on, 0 = off -} hc_cache_t; - -typedef struct { - bool store; - bool serve; - size_t cs_size; - size_t num_stale_entries; -} hc_cache_info_t; +int hc_strategy_list(hc_sock_t *s, hc_data_t **data); +int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy); +int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy); int hc_cache_set_store(hc_sock_t *s, hc_cache_t *cache); int hc_cache_set_serve(hc_sock_t *s, hc_cache_t *cache); int hc_cache_clear(hc_sock_t *s, hc_cache_t *cache); int hc_cache_list(hc_sock_t *s, hc_data_t **pdata); -int hc_cache_snprintf(char *s, size_t size, const hc_cache_info_t *cache_info); - -/*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -#define MAXSZ_STRATEGY_NAME 255 - -typedef struct { - // The name is not set by the controller - // but populated by the daemon - char name[MAXSZ_STRATEGY_NAME]; - strategy_type_t type; - ip_address_t address, local_address; - int family, local_family; - u8 len, local_len; -} hc_strategy_t; - -int hc_strategy_list(hc_sock_t *s, hc_data_t **data); - -#define foreach_strategy(VAR, data) foreach_type(hc_strategy_t, VAR, data) - -#define MAXSZ_HC_STRATEGY_ MAXSZ_STRATEGY_NAME -#define MAXSZ_HC_STRATEGY MAXSZ_HC_STRATEGY_ + NULLTERM - -int hc_strategy_snprintf(char *s, size_t size, hc_strategy_t *strategy); - -// per prefix -int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy); -hc_result_t *hc_strategy_set_conf(hc_sock_t *s, hc_strategy_t *strategy); -int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy); -hc_result_t *hc_strategy_add_local_prefix_conf(hc_sock_t *s, - hc_strategy_t *strategy); -/*----------------------------------------------------------------------------* - * WLDR - *----------------------------------------------------------------------------*/ - -// per connection -int hc_wldr_set(hc_sock_t *s /* XXX */); - -/*----------------------------------------------------------------------------* - * MAP-Me - *----------------------------------------------------------------------------*/ - -typedef enum { - MAPME_TARGET_ENABLE, - MAPME_TARGET_DISCOVERY, - MAPME_TARGET_TIMESCALE, - MAPME_TARGET_RETX, -} mapme_target_t; - -static inline mapme_target_t mapme_target_from_str(char *mapme_target_str) { - if (strcasecmp(mapme_target_str, "enable") == 0) - return MAPME_TARGET_ENABLE; - else if (strcasecmp(mapme_target_str, "discovery") == 0) - return MAPME_TARGET_DISCOVERY; - else if (strcasecmp(mapme_target_str, "timescale") == 0) - return MAPME_TARGET_TIMESCALE; - else - return MAPME_TARGET_RETX; -} - -#define MAX_MAPME_ARG_LEN 30 - -typedef struct { - mapme_target_t target; - // Command argument stored as a string - // before being parsed into 'enabled' or 'timescale' - char unparsed_arg[MAX_MAPME_ARG_LEN]; - - uint8_t enabled; // 1 = on, 0 = off - uint32_t timescale; // Milliseconds - - ip_address_t address; - int family; - u8 len; -} hc_mapme_t; int hc_mapme_set(hc_sock_t *s, hc_mapme_t *mapme); int hc_mapme_set_discovery(hc_sock_t *s, hc_mapme_t *mapme); @@ -833,145 +344,21 @@ int hc_mapme_set_timescale(hc_sock_t *s, hc_mapme_t *mapme); int hc_mapme_set_retx(hc_sock_t *s, hc_mapme_t *mapme); int hc_mapme_send_update(hc_sock_t *s, hc_mapme_t *mapme); -/*----------------------------------------------------------------------------* - * Policies - *----------------------------------------------------------------------------*/ - -#ifdef WITH_POLICY - -typedef struct { - int family; /* Krw */ - ip_address_t remote_addr; /* krw */ - u8 len; /* krw */ - hicn_policy_t policy; /* .rw */ -} hc_policy_t; - int hc_policy_create(hc_sock_t *s, hc_policy_t *policy); int hc_policy_delete(hc_sock_t *s, hc_policy_t *policy); int hc_policy_list(hc_sock_t *s, hc_data_t **pdata); -#define foreach_policy(VAR, data) foreach_type(hc_policy_t, VAR, data) - -/* TODO */ -#define MAXSZ_HC_POLICY_ 0 -#define MAXSZ_HC_POLICY MAXSZ_HC_POLICY_ + NULLTERM - -int hc_policy_snprintf(char *s, size_t size, hc_policy_t *policy); -int hc_policy_validate(const hc_policy_t *policy); - -#endif /* WITH_POLICY */ - -/*----------------------------------------------------------------------------* - * Subscription - *----------------------------------------------------------------------------*/ -// Topics - -#undef PUNTING // TODO(eloparco): Undefined to avoid collisions - // Fix the collision - -// Used only to create 'hc_topic_t' -typedef struct { -#define _(x) char x; - foreach_object -#undef _ -} object_offset_t; - -// Flags for topic subscriptions -typedef enum { -#define _(x) TOPIC_##x = (1 << offsetof(object_offset_t, x)), - foreach_object -#undef _ -} hc_topic_t; - -static inline hc_object_type_t object_from_topic(hc_topic_t topic) { -#define _(x) \ - if (topic == TOPIC_##x) return OBJECT_##x; - foreach_object -#undef _ - return OBJECT_UNDEFINED; -} - -#define NUM_TOPICS OBJECT_N // Because a topic is created for each object -#define ALL_TOPICS ~0 - -// Subscriptions -typedef uint32_t hc_topics_t; -typedef struct { - hc_topics_t topics; -} hc_subscription_t; +int hc_punting_create(hc_sock_t *s, hc_punting_t *punting); +int hc_punting_get(hc_sock_t *s, hc_punting_t *punting, + hc_punting_t **punting_found); +int hc_punting_delete(hc_sock_t *s, hc_punting_t *punting); +int hc_punting_list(hc_sock_t *s, hc_data_t **pdata); int hc_subscription_create(hc_sock_t *s, hc_subscription_t *subscription); int hc_subscription_delete(hc_sock_t *s, hc_subscription_t *subscription); -hc_result_t *hc_subscription_create_conf(hc_sock_t *s, - hc_subscription_t *subscription); -hc_result_t *hc_subscription_delete_conf(hc_sock_t *s, - hc_subscription_t *subscription); - -/*----------------------------------------------------------------------------* - * Events - *----------------------------------------------------------------------------*/ -#define foreach_event_type \ - _(UNDEFINED) \ - _(INTERFACE_UPDATE) \ - _(N) -typedef enum { -#define _(x) EVENT_##x, - foreach_event_type -#undef _ -} event_type_t; - -extern const char *event_str[]; -#define event_str(x) event_str[x] - -typedef enum { - FLAG_INTERFACE_TYPE_WIRED = 0x1, - FLAG_INTERFACE_TYPE_WIFI = 0x2, - FLAG_INTERFACE_TYPE_CELLULAR = 0x4, -} flag_interface_type_t; - -typedef struct { - flag_interface_type_t interface_type; -} hc_event_interface_update_t; -/*----------------------------------------------------------------------------* - * Statistics - *----------------------------------------------------------------------------*/ int hc_stats_get(hc_sock_t *s, hc_data_t **pdata); // General stats int hc_stats_list(hc_sock_t *s, hc_data_t **pdata); // Per-face stats int hc_stats_snprintf(char *s, size_t size, const hicn_light_stats_t *stats); -/* Result */ - -hc_msg_t *hc_result_get_msg(hc_sock_t *s, hc_result_t *result); -int hc_result_get_cmd_id(hc_sock_t *s, hc_result_t *result); -bool hc_result_get_success(hc_sock_t *s, hc_result_t *result); -void hc_result_free(hc_result_t *result); - -/* Object */ - -typedef struct { - hc_object_type_t type; - union { - hc_connection_t connection; - hc_listener_t listener; - hc_route_t route; - hc_face_t face; - // hc_data_t *data; - hc_punting_t punting; - hc_strategy_t strategy; -#ifdef WITH_POLICY - hc_policy_t policy; -#endif /* WITH_POLICY */ - hc_subscription_t subscription; - hc_cache_t cache; - hc_mapme_t mapme; - uint8_t as_uint8; - }; -} hc_object_t; - -typedef struct { - hc_action_t action; - hc_object_t object; -} hc_command_t; - #endif /* HICNTRL_API */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/callback.h b/ctrl/libhicnctrl/includes/hicn/ctrl/callback.h new file mode 100644 index 000000000..5b7f424d6 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/callback.h @@ -0,0 +1,13 @@ +#ifndef HICNCTRL_CALLBACK_H +#define HICNCTRL_CALLBACK_H + +#include <stdbool.h> + +#include <hicn/ctrl/data.h> + +typedef int (*hc_enable_callback_t)(bool enable); +typedef void (*hc_state_callback_t)(bool enable, void *user_data); +typedef void (*hc_result_callback_t)(hc_data_t *data, void *user_data); +typedef void (*hc_notification_callback_t)(hc_data_t *data, void *user_data); + +#endif /* HICNCTRL_CALLBACK_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/command.h b/ctrl/libhicnctrl/includes/hicn/ctrl/command.h new file mode 100644 index 000000000..1824d14c2 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/command.h @@ -0,0 +1,191 @@ +#ifndef HICNLIGHT_CONFIG_COMMAND +#define HICNLIGHT_CONFIG_COMMAND + +/** + * @file command.h + * @brief Commands. + */ + +#include <stddef.h> // offsetof +#include <hicn/util/ip_address.h> + +#include <hicn/ctrl/api.h> + +/* Update sscanf accordingly in parse_cmd.c */ +#define MAX_PARAMETERS 10 +#define MAX_SCANF_PARAM_LEN 100 + +typedef int (*parser_hook_t)(void *arg); + +typedef enum { + TYPENAME_UNDEFINED, + TYPENAME_INT, + TYPENAME_UINT, + TYPENAME_INT16, + TYPENAME_UINT16, + TYPENAME_STR, + TYPENAME_SYMBOLIC_OR_ID, + TYPENAME_INTERFACE_NAME, + TYPENAME_IP_ADDRESS, + TYPENAME_IP_PREFIX, + TYPENAME_ON_OFF, + TYPENAME_ENUM, + TYPENAME_POLICY_STATE, +} parser_typename_t; + +typedef struct { + parser_typename_t name; + union { + struct { + size_t max_size; + } str; + struct { + int min; + int max; + } integer; + struct { + int (*from_str)(const char *str); + } enum_; + struct { + policy_tag_t tag; + } policy_state; + }; +} parser_type_t; + +typedef struct { + const char *name; + const char *help; + parser_type_t type; + size_t offset; + /* + * quick hack to let the functions update two or more parameters, like for + * IP_ADDRESS or IP_PREFIX types + */ + size_t offset2; + size_t offset3; +} command_parameter_t; + +typedef struct { + hc_action_t action; + hc_object_type_t object_type; + unsigned nparams; + command_parameter_t parameters[MAX_PARAMETERS]; + parser_hook_t post_hook; +} command_parser_t; + +/* + * NOTE: we now use strings everywhere to parse in the same way parameters + * coming from the commandline through getopt (strings), and those coming from + * sscanf (used to be variables, now all strings also. + */ + +#define TYPE_STRN(N) \ + (parser_type_t) { \ + .name = TYPENAME_STR, \ + .str = { \ + .max_size = N, \ + }, \ + } + +#define TYPE_INT(MIN, MAX) \ + (parser_type_t) { \ + .name = TYPENAME_INT, \ + .integer = { \ + .min = (MIN), \ + .max = (MAX), \ + }, \ + } + +#define TYPE_UINT(MIN, MAX) \ + (parser_type_t) { \ + .name = TYPENAME_UINT, \ + .integer = { \ + .min = (MIN), \ + .max = (MAX), \ + }, \ + } + +#define TYPE_INT16(MIN, MAX) \ + (parser_type_t) { \ + .name = TYPENAME_INT16, \ + .integer = { \ + .min = (MIN), \ + .max = (MAX), \ + }, \ + } + +#define TYPE_UINT16(MIN, MAX) \ + (parser_type_t) { \ + .name = TYPENAME_UINT16, \ + .integer = { \ + .min = (MIN), \ + .max = (MAX), \ + }, \ + } + +#define TYPE_SYMBOLIC_OR_ID TYPE_STRN(SYMBOLIC_NAME_LEN) + +#define TYPE_INTERFACE_NAME TYPE_STRN(INTERFACE_LEN) + +#define TYPE_IP_ADDRESS \ + (parser_type_t) { .name = TYPENAME_IP_ADDRESS, } + +#define TYPE_IP_PREFIX \ + (parser_type_t) { .name = TYPENAME_IP_PREFIX, } + +#define TYPE_ON_OFF \ + (parser_type_t) { .name = TYPENAME_ON_OFF, } + +#define TYPE_ENUM(x) \ + (parser_type_t) { \ + .name = TYPENAME_ENUM, \ + .enum_ = { \ + .from_str = (int (*)(const char *))x##_from_str, \ + }, \ + } +/* We need to allocate room for the intermediate string */ + +#define TYPE_POLICY_STATE(TAG) \ + (parser_type_t) { \ + .name = TYPENAME_POLICY_STATE, \ + .policy_state = { \ + .tag = TAG, \ + }, \ + } +/* We need to allocate room for the intermediate string */ + +/** + * \brief Register a protocol + * \param protocol Pointer to a protocol_t structure describing the protocol to + * register \return None + */ + +void command_register(const command_parser_t *command); + +/** + * \brief Search a registered protocol in the library according to its name + * \param[in] action The action of the command. + * \param[in] object The object of the command. + * \param[in] nparams The number of parameters expected in the command. + * \return A pointer to the corresponding command if any, NULL othewise + */ +const command_parser_t *command_search(hc_action_t action, + hc_object_type_t object, + unsigned nparams); + +/** + * @brief List the commands associated with the specified object and/or action. + * Use OBJECT_UNDEFINED and ACTION_UNDEFINED to list all the available objects. + * Use ACTION_UNDEFINED to list all the actions associated to the specified + * object. + * + * @param object The action of the command + * @param action The object of the command + */ +void command_list(hc_object_type_t object, hc_action_t action); + +#define COMMAND_REGISTER(MOD) \ + static void __init_##MOD(void) __attribute__((constructor)); \ + static void __init_##MOD(void) { command_register(&MOD); } + +#endif /* HICNLIGHT_CONFIG_COMMAND */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/data.h b/ctrl/libhicnctrl/includes/hicn/ctrl/data.h new file mode 100644 index 000000000..d2696db1c --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/data.h @@ -0,0 +1,150 @@ +/* + * 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.h + * \brief Request result data. + */ + +#ifndef HICNCTRL_DATA_H +#define HICNCTRL_DATA_H + +#include <stdbool.h> +#include <stddef.h> // size_t +#include <stdint.h> // uint*_t +#include <sys/types.h> +#include <unistd.h> + +#include <hicn/ctrl/object_type.h> +#include <hicn/ctrl/object.h> + +/** + * \brief Holds the results of an hICN control request + */ +typedef struct hc_data_s hc_data_t; + +/** + * Create a structure holding the results of an hICN control request. + * \result The newly create data structure. + */ +hc_data_t *hc_data_create(hc_object_type_t object_type); + +/** + * Free a structure holding the results of an hICN control request. + * \param [in] data - The data structure to free. + */ +void hc_data_free(hc_data_t *data); + +/* + * This function can fail if the current data size is bigger than the requested + * maximum size + */ +int hc_data_set_max_size(hc_data_t *data, size_t max_size); + +const uint8_t *hc_data_get_buffer(hc_data_t *data); +const uint8_t *hc_data_get_free(hc_data_t *data); +void hc_data_inc_size(hc_data_t *data); + +hc_object_type_t hc_data_get_object_type(const hc_data_t *data); + +void hc_data_set_object_type(hc_data_t *data, hc_object_type_t object_type); + +ssize_t hc_data_get_size(const hc_data_t *data); + +/* + * This is used to perform manual allocation once after initialization is the + * size of the data to store is known in advance. This does not prevent future + * reallocations (in the limit though of the value in max_size, if applicable). + */ +int hc_data_allocate(hc_data_t *data, size_t size); + +int hc_data_clear(hc_data_t *data); + +#if 0 +int hc_data_ensure_available(hc_data_t *data, size_t count); +#endif + +/** + * \brief Adds many new results at the end of the data structure, eventually + * allocating buffer space for it. + * \param [in] data - The data structure to which to add elements. + * \param [in] elements - The array of elements to add. + * \param [in] count - The number of elements to add. + * \return Error code + * + * NOTE: The size of the element should match the one declared at structure + * initialization. + */ +int hc_data_push_many(hc_data_t *data, const void *elements, size_t count); + +/** + * \brief Adds a new result at the end of the data structure, eventually + * allocating buffer space for it. + * \param [in] data - The data structure to which to add an element. + * \param [in] element - The element to add + * \return Error code + * + * NOTE: The size of the element should match the one declared at structure + * initialization. + */ +int hc_data_push(hc_data_t *data, const void *element); + +#if 0 +uint8_t *hc_data_get_next(hc_data_t *data); + +/** + * \brief Configure a callback (along with private data) to be called upon + * completion of a request + * \param [in] data - hICN control data + * \param [in] cb - Callback function + * \param [in] cb_data - Callback private data + */ +int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data); + +void hc_data_set_size(hc_data_t *data, int size); +#endif + +void hc_data_set_complete(hc_data_t *data); +bool hc_data_is_complete(const hc_data_t *data); + +void hc_data_set_error(hc_data_t *data); + +bool hc_data_get_result(hc_data_t *data); + +#if 0 +/** + * \brief Reset the data structure holding control data + * \param [in] data - hICN control data + * \return Error code + */ +int hc_data_reset(hc_data_t *data); +#endif + +#define VAR(x) __##x +#define hc_data_foreach(DATA, OBJECT, BODY) \ + do { \ + hc_object_t *OBJECT; \ + size_t VAR(size) = hc_object_size(hc_data_get_object_type(DATA)); \ + for (unsigned VAR(i) = 0; VAR(i) < hc_data_get_size(DATA); VAR(i)++) { \ + OBJECT = (hc_object_t *)(hc_data_get_buffer(DATA) + VAR(i) * VAR(size)); \ + BODY \ + } \ + } while (0) + +hc_object_t *hc_data_find(hc_data_t *data, hc_object_t *object); + +const hc_object_t *hc_data_get_object(const hc_data_t *data, off_t pos); + +#endif /* HICNCTRL_DATA_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/fw_interface.h b/ctrl/libhicnctrl/includes/hicn/ctrl/fw_interface.h new file mode 100644 index 000000000..0656de080 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/fw_interface.h @@ -0,0 +1,135 @@ +/* + * 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. + */ + +#ifndef HICNCTRL_FW_INTERFACE_H +#define HICNCTRL_FW_INTERFACE_H + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/callback.h> + +/** + * \file fw_interface.h + * \brief Forwarder interface + * + * Forwarder interface is designed to be a reusable module (that might be + * wrapped in a C++ class), providing a fw-agnostic interface with the + * following goals: + * - maintaining a permanent connection to the fw (and keep track of the + * fw state, eventually caching some aspects) + * - allowing the tracking of multiplexed requests + * - supporting a stream of concurrent notifications that might be needed to + * synchronize states. + * + * It is design to be easily integrated with the different event loops used + * across the projects (libevent for C, asio for C++). + */ + +#define foreach_fw_state \ + _(UNDEFINED) \ + _(DISABLED) /* stack is stopped */ \ + _(REQUESTED) /* stack is starting */ \ + _(AVAILABLE) /* forwarder is running */ \ + _(CONNECTING) /* XXX NEW */ \ + _(CONNECTED) /* control socket connected */ \ + _(READY) /* listener is present */ \ + _(N) + +typedef enum { +#define _(x) FW_STATE_##x, + foreach_fw_state +#undef _ +} fw_state_t; + +extern const char *fw_state_str[]; + +#define fw_state_str(x) fw_state_str[x] + +typedef struct fw_interface_s fw_interface_t; + +fw_interface_t *fw_interface_create_url(forwarder_type_t type, const char *url); +fw_interface_t *fw_interface_create(forwarder_type_t type); + +void fw_interface_free(fw_interface_t *fi); + +int fw_interface_get_fd(const fw_interface_t *fi); + +/* + * Enable the stack + */ +int fw_interface_enable(fw_interface_t *fi); + +/* + * Disable the stack + */ +int fw_interface_disable(fw_interface_t *fi); + +/* + * Request a permanent connection to the forwarder, starting it if needed. + */ +int fw_interface_connect(fw_interface_t *fi); + +/* + * Disconnect from the forwarder + */ +int fw_interface_disconnect(fw_interface_t *fi); + +fw_state_t fw_interface_get_state(const fw_interface_t *fi); + +int fw_interface_subscribe_all(fw_interface_t *fi); + +int fw_interface_unsubscribe_all(fw_interface_t *fi); + +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); + +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); + +int fw_interface_set_enable_callback(fw_interface_t *fi, + hc_enable_callback_t callback); + +int fw_interface_set_state_callback(fw_interface_t *fi, + hc_state_callback_t callback, + void *callback_data); + +int fw_interface_set_result_callback(fw_interface_t *fi, + hc_result_callback_t callback, + void *callback_data); + +int fw_interface_set_notification_callback(fw_interface_t *fi, + hc_notification_callback_t callback, + void *callback_data); + +// manage stack [android] +// - not needed for face mgr +// operations +// - create face/route : facemgr, hproxy +// - set fw strategy +// - get listeners, hicn listener port +// subscribe all +// callbacks: +// - forwarder available/unavailable +// timers & reattempts : clarify +// XXX remove_self on sock disconnect... should be in libhicnctrl + +int fw_interface_on_receive(fw_interface_t *fi, size_t count); +int fw_interface_get_recv_buffer(fw_interface_t *fi, uint8_t **buffer, + size_t *size); + +#endif /* HICNCTRL_FW_INTERFACE_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light-ng.h b/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light-ng.h deleted file mode 100644 index 783eab086..000000000 --- a/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light-ng.h +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * @file commands.h - * @brief All hicn-light commands: 14 in total. - * - * Header and payload in binary format. - */ - -#ifndef HICN_CTRL_HICNLIGHTNG_H -#define HICN_CTRL_HICNLIGHTNG_H - -#ifndef _WIN32 -#include <netinet/in.h> -#include <sys/socket.h> -#endif - -#include <stdint.h> -#include <stdlib.h> - -#include <hicn/policy.h> -#include <hicn/strategy.h> -#include <hicn/util/ip_address.h> - -#define SYMBOLIC_NAME_LEN 16 - -typedef struct in6_addr ipv6_addr_t; -typedef uint32_t ipv4_addr_t; - -typedef enum { - MESSAGE_COMMAND_SUBTYPE_UNDEFINED, - REQUEST_LIGHT = 0xc0, // this is a command - RESPONSE_LIGHT, - ACK_LIGHT, - NACK_LIGHT, - NOTIFICATION_LIGHT, - MESSAGE_COMMAND_SUBTYPE_N -} message_command_subtype_t; - -#define message_type_is_valid(message_type) \ - ((message_type != MESSAGE_TYPE_UNDEFINED) && \ - (message_type != MESSAGE_COMMAND_SUBTYPE_N)) - -#define message_type_from_uchar(x) \ - (((x) < REQUEST_LIGHT) || (((x) >= MESSAGE_COMMAND_SUBTYPE_N)) \ - ? MESSAGE_COMMAND_SUBTYPE_N \ - : (message_command_subtype_t)(x)) - -#define foreach_command_type \ - _(listener_add, LISTENER_ADD) \ - _(listener_remove, LISTENER_REMOVE) \ - _(listener_list, LISTENER_LIST) \ - _(connection_add, CONNECTION_ADD) \ - _(connection_remove, CONNECTION_REMOVE) \ - _(connection_list, CONNECTION_LIST) \ - _(connection_set_admin_state, CONNECTION_SET_ADMIN_STATE) \ - _(connection_update, CONNECTION_UPDATE) \ - _(connection_set_priority, CONNECTION_SET_PRIORITY) \ - _(connection_set_tags, CONNECTION_SET_TAGS) \ - _(route_add, ROUTE_ADD) \ - _(route_remove, ROUTE_REMOVE) \ - _(route_list, ROUTE_LIST) \ - _(cache_set_store, CACHE_SET_STORE) \ - _(cache_set_serve, CACHE_SET_SERVE) \ - _(cache_clear, CACHE_CLEAR) \ - _(cache_list, CACHE_LIST) \ - _(strategy_set, STRATEGY_SET) \ - _(strategy_add_local_prefix, STRATEGY_ADD_LOCAL_PREFIX) \ - _(wldr_set, WLDR_SET) \ - _(punting_add, PUNTING_ADD) \ - _(mapme_enable, MAPME_ENABLE) \ - _(mapme_set_discovery, MAPME_SET_DISCOVERY) \ - _(mapme_set_timescale, MAPME_SET_TIMESCALE) \ - _(mapme_set_retx, MAPME_SET_RETX) \ - _(mapme_send_update, MAPME_SEND_UPDATE) \ - _(policy_add, POLICY_ADD) \ - _(policy_remove, POLICY_REMOVE) \ - _(policy_list, POLICY_LIST) \ - _(subscription_add, SUBSCRIPTION_ADD) \ - _(subscription_remove, SUBSCRIPTION_REMOVE) \ - _(stats_get, STATS_GET) \ - _(stats_list, STATS_LIST) - -typedef enum { - COMMAND_TYPE_UNDEFINED, -#define _(l, u) COMMAND_TYPE_##u, - foreach_command_type -#undef _ - COMMAND_TYPE_N, -} command_type_t; - -extern const char *command_type_str[]; - -#define command_type_str(x) command_type_str[x] - -#define command_type_is_valid(command_type) \ - ((command_type != COMMAND_TYPE_UNDEFINED) && (command_type != COMMAND_TYPE_N)) - -#define command_type_from_uchar(x) \ - (((x) >= COMMAND_TYPE_N) ? COMMAND_TYPE_N : (command_type_t)(x)) - -/* Should be at least 8 bytes */ -typedef struct { - uint8_t message_type; - uint8_t command_id; - uint16_t length; /* Number of structures in the payload */ - uint32_t seq_num; -} cmd_header_t; - -typedef struct { - cmd_header_t header; -} msg_header_t; - -/* Listener */ - -typedef struct { - char symbolic[SYMBOLIC_NAME_LEN]; - char interface_name[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint16_t port; - uint8_t family; - uint8_t type; -} cmd_listener_add_t; - -typedef struct { - char symbolicOrListenerid[SYMBOLIC_NAME_LEN]; -} cmd_listener_remove_t; - -typedef struct { - void *_; // Otherwise empty structs result in clang build error -} cmd_listener_list_t; - -// Sync this struct with `hc_listener_t` in `api.h` -typedef struct { - char name[SYMBOLIC_NAME_LEN]; - char interface_name[SYMBOLIC_NAME_LEN]; - uint32_t id; - uint8_t type; - uint8_t family; - ip_address_t address; - uint16_t port; -} cmd_listener_list_item_t; - -/* Connection */ - -typedef struct { - char symbolic[SYMBOLIC_NAME_LEN]; - // char interface_name[SYMBOLIC_NAME_LEN]; - ip_address_t remote_ip; - ip_address_t local_ip; - uint16_t remote_port; - uint16_t local_port; - uint8_t family; - uint8_t type; - uint8_t admin_state; -#ifdef WITH_POLICY - uint32_t priority; - policy_tags_t tags; -#endif /* WITH_POLICY */ -} cmd_connection_add_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; -} cmd_connection_remove_t; - -typedef struct { - void *_; -} cmd_connection_list_t; - -// Sync this struct with `hc_connection_t` in `api.h` -typedef struct { - uint32_t id; - char name[SYMBOLIC_NAME_LEN]; - char interface_name[SYMBOLIC_NAME_LEN]; - uint8_t type; - uint8_t family; - ip_address_t local_addr; - uint16_t local_port; - ip_address_t remote_addr; - uint16_t remote_port; - uint8_t admin_state; -#ifdef WITH_POLICY - uint32_t priority; - policy_tags_t tags; -#endif /* WITH_POLICY */ - uint8_t state; -} cmd_connection_list_item_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - uint8_t admin_state; - uint8_t pad8[3]; -} cmd_connection_set_admin_state_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - uint8_t admin_state; -#ifdef WITH_POLICY - uint32_t priority; - policy_tags_t tags; -#endif /* WITH_POLICY */ -} cmd_connection_update_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - uint32_t priority; -} cmd_connection_set_priority_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - policy_tags_t tags; -} cmd_connection_set_tags_t; - -/* Route */ - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint16_t cost; - uint8_t family; - uint8_t len; -} cmd_route_add_t; - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint8_t family; - uint8_t len; -} cmd_route_remove_t; - -typedef struct { - void *_; -} cmd_route_list_t; - -// Sync this struct with `hc_route_t` in `api.h` -typedef struct { - ip_address_t address; - uint32_t connection_id; - uint16_t cost; - uint8_t family; - uint8_t len; -} cmd_route_list_item_t; - -/* Cache */ - -typedef struct { - uint8_t activate; -} cmd_cache_set_store_t; - -typedef struct { - uint8_t activate; -} cmd_cache_set_serve_t; - -typedef struct { - void *_; -} cmd_cache_clear_t; - -typedef struct { - void *_; -} cmd_cache_list_t; - -typedef struct { - uint8_t store_in_cs; - uint8_t serve_from_cs; - uint32_t cs_size; - uint32_t num_stale_entries; -} cmd_cache_list_reply_t; - -typedef struct { - cmd_header_t header; - cmd_cache_list_reply_t payload; -} msg_cache_list_reply_t; - -/* Statistics */ - -// General stats -typedef struct { - void *_; -} cmd_stats_get_t; - -typedef struct { - cmd_header_t header; - hicn_light_stats_t payload; -} msg_stats_get_reply_t; - -// Per-face stats -typedef struct { - void *_; -} cmd_stats_list_t; - -typedef struct { - uint32_t id; - connection_stats_t stats; -} cmd_stats_list_item_t; - -typedef struct { - cmd_header_t header; - cmd_stats_list_item_t payload; -} msg_stats_list_reply_t; - -/* WLDR */ - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - uint8_t activate; -} cmd_wldr_set_t; - -/* Strategy */ - -typedef struct { - ip_address_t address; - uint8_t family; - uint8_t len; - uint8_t type; - uint8_t related_prefixes; - union { - struct { - ip_address_t addresses[MAX_FWD_STRATEGY_RELATED_PREFIXES]; - uint8_t lens[MAX_FWD_STRATEGY_RELATED_PREFIXES]; - uint8_t families[MAX_FWD_STRATEGY_RELATED_PREFIXES]; - } low_latency; - }; -} cmd_strategy_set_t; - -typedef struct { - uint8_t type; - ip_address_t address; - uint8_t family; - uint8_t len; - ip_address_t local_address; - uint8_t local_family; - uint8_t local_len; -} cmd_strategy_add_local_prefix_t; - -/* Punting */ - -typedef struct { - char symbolic_or_connid[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint8_t family; - uint8_t len; -} cmd_punting_add_t; - -/* MAP-Me */ - -typedef struct { - uint8_t activate; -} cmd_mapme_activator_t; - -typedef cmd_mapme_activator_t cmd_mapme_enable_t; -typedef cmd_mapme_activator_t cmd_mapme_set_discovery_t; - -typedef struct { - uint32_t timePeriod; -} cmd_mapme_timing_t; - -typedef cmd_mapme_timing_t cmd_mapme_set_timescale_t; -typedef cmd_mapme_timing_t cmd_mapme_set_retx_t; - -typedef struct { - void *_; -} cmd_mapme_send_update_t; - -/* Policy */ - -typedef struct { - ip_address_t address; - uint8_t family; - uint8_t len; - hicn_policy_t policy; -} cmd_policy_add_t; - -typedef struct { - ip_address_t address; - uint8_t family; - uint8_t len; -} cmd_policy_remove_t; - -typedef struct { - void *_; -} cmd_policy_list_t; - -typedef struct { - ip_address_t address; - uint8_t family; - uint8_t len; - hicn_policy_t policy; -} cmd_policy_list_item_t; - -/* Subscription */ - -typedef struct { - uint32_t topics; -} cmd_subscription_add_t; - -typedef struct { - uint32_t topics; -} cmd_subscription_remove_t; - -/* Full messages */ - -#define _(l, u) \ - typedef struct { \ - cmd_header_t header; \ - cmd_##l##_t payload; \ - } msg_##l##_t; -foreach_command_type; -#undef _ - -typedef struct { - cmd_header_t header; - cmd_listener_list_item_t payload; -} msg_listener_list_reply_t; - -typedef struct { - cmd_header_t header; - cmd_connection_list_item_t payload; -} msg_connection_list_reply_t; - -typedef struct { - cmd_header_t header; - cmd_route_list_item_t payload; -} msg_route_list_reply_t; - -typedef struct { - cmd_header_t header; - cmd_policy_list_item_t payload; -} msg_policy_list_reply_t; - -//===== size of commands ====== -// REMINDER: when a new_command is added, the following switch has to be -// updated. -static inline int command_get_payload_len(command_type_t command_type) { - switch (command_type) { -#define _(l, u) \ - case COMMAND_TYPE_##u: \ - return sizeof(cmd_##l##_t); - foreach_command_type -#undef _ - case COMMAND_TYPE_UNDEFINED : case COMMAND_TYPE_N : return 0; - } -} -#endif /* HICN_CTRL_HICNLIGHTNG_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light.h b/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light.h index 69ede1985..34667cc1b 100644 --- a/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light.h +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/hicn-light.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2021-2022 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -20,8 +20,8 @@ * Header and payload in binary format. */ -#ifndef HICN_CTRL_HICNLIGHT_H -#define HICN_CTRL_HICNLIGHT_H +#ifndef HICN_CTRL_HICNLIGHTNG_H +#define HICN_CTRL_HICNLIGHTNG_H #ifndef _WIN32 #include <netinet/in.h> @@ -31,404 +31,506 @@ #include <stdint.h> #include <stdlib.h> -#include <hicn/util/ip_address.h> -#ifdef WITH_POLICY #include <hicn/policy.h> -#endif /* WITH_POLICY */ +#include <hicn/strategy.h> +#include <hicn/util/ip_address.h> + +#include "api.h" #define SYMBOLIC_NAME_LEN 16 -#define MAX_FWD_STRATEGY_RELATED_PREFIXES 10 typedef struct in6_addr ipv6_addr_t; typedef uint32_t ipv4_addr_t; typedef enum { + MESSAGE_COMMAND_SUBTYPE_UNDEFINED, REQUEST_LIGHT = 0xc0, // this is a command RESPONSE_LIGHT, ACK_LIGHT, NACK_LIGHT, - LAST_MSG_TYPE_VALUE -} message_type; + NOTIFICATION_LIGHT, + MESSAGE_COMMAND_SUBTYPE_N +} message_command_subtype_t; + +#define message_type_is_valid(message_type) \ + ((message_type != MESSAGE_TYPE_UNDEFINED) && \ + (message_type != MESSAGE_COMMAND_SUBTYPE_N)) + +#define message_type_from_uchar(x) \ + (((x) < REQUEST_LIGHT) || (((x) >= MESSAGE_COMMAND_SUBTYPE_N)) \ + ? MESSAGE_COMMAND_SUBTYPE_N \ + : (message_command_subtype_t)(x)) + +#define foreach_command_type \ + _(listener_add, LISTENER_ADD) \ + _(listener_remove, LISTENER_REMOVE) \ + _(listener_list, LISTENER_LIST) \ + _(connection_add, CONNECTION_ADD) \ + _(connection_remove, CONNECTION_REMOVE) \ + _(connection_list, CONNECTION_LIST) \ + _(connection_update, CONNECTION_UPDATE) \ + _(route_add, ROUTE_ADD) \ + _(route_remove, ROUTE_REMOVE) \ + _(route_list, ROUTE_LIST) \ + _(cache_set_store, CACHE_SET_STORE) \ + _(cache_set_serve, CACHE_SET_SERVE) \ + _(cache_clear, CACHE_CLEAR) \ + _(cache_list, CACHE_LIST) \ + _(strategy_set, STRATEGY_SET) \ + _(strategy_add_local_prefix, STRATEGY_ADD_LOCAL_PREFIX) \ + _(wldr_set, WLDR_SET) \ + _(punting_add, PUNTING_ADD) \ + _(mapme_enable, MAPME_ENABLE) \ + _(mapme_set_discovery, MAPME_SET_DISCOVERY) \ + _(mapme_set_timescale, MAPME_SET_TIMESCALE) \ + _(mapme_set_retx, MAPME_SET_RETX) \ + _(mapme_send_update, MAPME_SEND_UPDATE) \ + _(policy_add, POLICY_ADD) \ + _(policy_remove, POLICY_REMOVE) \ + _(policy_list, POLICY_LIST) \ + _(active_interface_update, ACTIVE_INTERFACE_UPDATE) \ + _(subscription_add, SUBSCRIPTION_ADD) \ + _(subscription_remove, SUBSCRIPTION_REMOVE) \ + _(stats_get, STATS_GET) \ + _(stats_list, STATS_LIST) typedef enum { - ADD_LISTENER = 0, - ADD_CONNECTION, - LIST_CONNECTIONS, - ADD_ROUTE, - LIST_ROUTES, - REMOVE_CONNECTION, - REMOVE_LISTENER, - REMOVE_ROUTE, - CACHE_STORE, - CACHE_SERVE, - CACHE_CLEAR, - SET_STRATEGY, - SET_WLDR, - ADD_PUNTING, - LIST_LISTENERS, - MAPME_ENABLE, - MAPME_DISCOVERY, - MAPME_TIMESCALE, - MAPME_RETX, - MAPME_SEND_UPDATE, - CONNECTION_SET_ADMIN_STATE, -#ifdef WITH_POLICY - ADD_POLICY, - LIST_POLICIES, - REMOVE_POLICY, - UPDATE_CONNECTION, - CONNECTION_SET_PRIORITY, - CONNECTION_SET_TAGS, -#endif /* WITH_POLICY */ - LAST_COMMAND_VALUE -} command_id; + COMMAND_TYPE_UNDEFINED, +#define _(l, u) COMMAND_TYPE_##u, + foreach_command_type +#undef _ + COMMAND_TYPE_N, +} command_type_t; -typedef enum { - ADDR_INET = 1, - ADDR_INET6, - ADDR_LINK, - ADDR_IFACE, - ADDR_UNIX /* PF_UNIX */ -} address_type; +extern const char *command_type_str[]; -typedef enum { - UDP_CONN, - TCP_CONN, - GRE_CONN, // not implemented - HICN_CONN -} connection_type; +#define command_type_str(x) command_type_str[x] -typedef enum { ACTIVATE_ON, ACTIVATE_OFF } activate_type; +#define command_type_is_valid(command_type) \ + ((command_type != COMMAND_TYPE_UNDEFINED) && (command_type != COMMAND_TYPE_N)) -//========== HEADER ========== +#define command_type_from_uchar(x) \ + (((x) >= COMMAND_TYPE_N) ? COMMAND_TYPE_N : (command_type_t)(x)) +/* Should be at least 8 bytes */ typedef struct { - uint8_t messageType; - uint8_t commandID; - uint16_t length; // tells the number of structures in the payload - uint32_t seqNum; -} header_control_message; -// for the moment has to be at least 8 bytes - -// SIZE=8 + uint8_t message_type; + uint8_t command_id; + uint16_t length; /* Number of structures in the payload */ + uint32_t seq_num; +} cmd_header_t; -//========== [00] ADD LISTENER ========== +typedef struct { + cmd_header_t header; +} msg_header_t; -typedef enum { ETHER_MODE, IP_MODE, HICN_MODE } listener_mode; +/* Listener */ typedef struct { char symbolic[SYMBOLIC_NAME_LEN]; - char interfaceName[SYMBOLIC_NAME_LEN]; - ip_address_t address; + char interface_name[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t address; uint16_t port; - // uint16_t etherType; - uint8_t addressType; - uint8_t listenerMode; - uint8_t connectionType; -} add_listener_command; + uint8_t family; + uint8_t type; +} cmd_listener_add_t; -// SIZE=56 +typedef struct { + char symbolicOrListenerid[SYMBOLIC_NAME_LEN]; +} cmd_listener_remove_t; + +typedef struct { + void *_; // Otherwise empty structs result in clang build error +} cmd_listener_list_t; -//========== [01] ADD CONNECTION ========== +/* Connection */ typedef struct { char symbolic[SYMBOLIC_NAME_LEN]; - // char interfaceName[SYMBOLIC_NAME_LEN]; - ip_address_t remoteIp; - ip_address_t localIp; - uint16_t remotePort; - uint16_t localPort; - uint8_t ipType; - uint8_t connectionType; + // char interface_name[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t remote_ip; + hicn_ip_address_t local_ip; + uint16_t remote_port; + uint16_t local_port; + uint8_t family; + uint8_t type; uint8_t admin_state; -#ifdef WITH_POLICY + uint8_t __pad; uint32_t priority; policy_tags_t tags; -#endif /* WITH_POLICY */ -} add_connection_command; +} cmd_connection_add_t; -// SIZE=56 +typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; +} cmd_connection_remove_t; -//========== [02] LIST CONNECTIONS ========== +typedef struct { + void *_; +} cmd_connection_list_t; -typedef enum { - CONN_GRE, - CONN_TCP, - CONN_UDP, - CONN_MULTICAST, - CONN_L2, - CONN_HICN -} list_connections_type; +typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + uint8_t admin_state; + uint8_t pad8[3]; +} cmd_connection_set_admin_state_t; -typedef enum { - IFACE_UP = 0, - IFACE_DOWN = 1, - IFACE_UNKNOWN = 2 // not used actually -} connection_state; +typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + uint8_t admin_state; + uint32_t priority; + policy_tags_t tags; +} cmd_connection_update_t; typedef struct { - add_connection_command connectionData; - uint32_t connid; - uint8_t state; - char interfaceName[SYMBOLIC_NAME_LEN]; - char connectionName[SYMBOLIC_NAME_LEN]; -} list_connections_command; + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + uint32_t priority; +} cmd_connection_set_priority_t; -// SIZE=80 +typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + policy_tags_t tags; +} cmd_connection_set_tags_t; -//========== [03] ADD ROUTE ========== +/* Route */ typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - ip_address_t address; + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t address; uint16_t cost; - uint8_t addressType; + uint8_t family; uint8_t len; -} add_route_command; - -// SIZE=36 - -//========== [04] LIST ROUTE ========== +} cmd_route_add_t; typedef struct { - ip_address_t address; - uint32_t connid; - uint16_t cost; - uint8_t addressType; + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t address; + uint8_t family; uint8_t len; -} list_routes_command; +} cmd_route_remove_t; -// SIZE=24 +typedef struct { + void *_; +} cmd_route_list_t; + +/* Cache */ -//========== [05] REMOVE CONNECTION ========== typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; -} remove_connection_command; + uint8_t activate; +} cmd_cache_set_store_t; -//========== [06] REMOVE LISTENER ========== typedef struct { - char symbolicOrListenerid[SYMBOLIC_NAME_LEN]; -} remove_listener_command; + uint8_t activate; +} cmd_cache_set_serve_t; -// SIZE=16 +typedef struct { + void *_; +} cmd_cache_clear_t; -//========== [07] REMOVE ROUTE ========== +typedef struct { + void *_; +} cmd_cache_list_t; typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint8_t addressType; - uint8_t len; -} remove_route_command; + uint8_t store_in_cs; + uint8_t serve_from_cs; + uint32_t cs_size; + uint32_t num_stale_entries; +} cmd_cache_list_reply_t; -// SIZE=36 +typedef struct { + cmd_header_t header; + cmd_cache_list_reply_t payload; +} msg_cache_list_reply_t; -//========== [08] CACHE STORE ========== +/* WLDR */ typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; uint8_t activate; -} cache_store_command; +} cmd_wldr_set_t; -// SIZE=1 +/* Strategy */ -//========== [09] CACHE SERVE ========== +typedef struct { + hicn_ip_address_t address; + uint8_t family; + uint8_t len; + uint8_t type; + uint8_t related_prefixes; + union { + struct { + hicn_ip_address_t addresses[MAX_FWD_STRATEGY_RELATED_PREFIXES]; + uint8_t lens[MAX_FWD_STRATEGY_RELATED_PREFIXES]; + uint8_t families[MAX_FWD_STRATEGY_RELATED_PREFIXES]; + } low_latency; + }; +} cmd_strategy_set_t; typedef struct { - uint8_t activate; -} cache_serve_command; + uint8_t type; + hicn_ip_address_t address; + uint8_t family; + uint8_t len; + hicn_ip_address_t local_address; + uint8_t local_family; + uint8_t local_len; +} cmd_strategy_add_local_prefix_t; -// SIZE=1 +/* Punting */ -//========== [10] SET STRATEGY ========== +typedef struct { + char symbolic_or_connid[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t address; + uint8_t family; + uint8_t len; +} cmd_punting_add_t; -typedef enum { - SET_STRATEGY_LOADBALANCER, - SET_STRATEGY_RANDOM, - SET_STRATEGY_LOW_LATENCY, - LAST_STRATEGY_VALUE -} strategy_type; +/* MAP-Me */ typedef struct { - ip_address_t address; - uint8_t strategyType; - uint8_t addressType; - uint8_t len; - uint8_t related_prefixes; - ip_address_t addresses[MAX_FWD_STRATEGY_RELATED_PREFIXES]; - uint8_t lens[MAX_FWD_STRATEGY_RELATED_PREFIXES]; - uint8_t addresses_type[MAX_FWD_STRATEGY_RELATED_PREFIXES]; -} set_strategy_command; + uint8_t activate; +} cmd_mapme_activator_t; + +typedef cmd_mapme_activator_t cmd_mapme_enable_t; +typedef cmd_mapme_activator_t cmd_mapme_set_discovery_t; -// SIZE=208 +typedef struct { + uint32_t timePeriod; +} cmd_mapme_timing_t; -//========== [11] SET WLDR ========== +typedef cmd_mapme_timing_t cmd_mapme_set_timescale_t; +typedef cmd_mapme_timing_t cmd_mapme_set_retx_t; typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - uint8_t activate; -} set_wldr_command; + void *_; +} cmd_mapme_send_update_t; -// SIZE=17 +/* Policy */ -//========== [12] ADD PUNTING ========== +typedef struct { + hicn_ip_address_t address; + uint8_t family; + uint8_t len; + hicn_policy_t policy; +} cmd_policy_add_t; typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - ip_address_t address; - uint8_t addressType; + hicn_ip_address_t address; + uint8_t family; uint8_t len; -} add_punting_command; +} cmd_policy_remove_t; -// SIZE=36 +typedef struct { + void *_; +} cmd_policy_list_t; -//========== [13] LIST LISTENER ========== +/* Subscription */ typedef struct { - ip_address_t address; - char listenerName[SYMBOLIC_NAME_LEN]; - char interfaceName[SYMBOLIC_NAME_LEN]; - uint32_t connid; - uint16_t port; - uint8_t addressType; - uint8_t encapType; -} list_listeners_command; + uint32_t topics; +} cmd_subscription_add_t; -// SIZE=56 +typedef struct { + uint32_t topics; +} cmd_subscription_remove_t; -//========== [14] MAPME ========== +/* Statistics */ -// (enable/discovery/timescale/retx) +// General stats +typedef struct { + void *_; +} cmd_stats_get_t; +// Per-face stats typedef struct { - uint8_t activate; -} mapme_activator_command; + void *_; +} cmd_stats_list_t; + +typedef void *cmd_active_interface_update_t; + +/* Full messages */ + +#define _(l, u) \ + typedef struct { \ + cmd_header_t header; \ + cmd_##l##_t payload; \ + } msg_##l##_t; +foreach_command_type +#undef _ + + /* Serialized version of hc_listener_t */ + typedef struct { + char name[SYMBOLIC_NAME_LEN]; + char interface_name[INTERFACE_LEN]; + hicn_ip_address_t local_addr; + uint32_t id; + uint16_t local_port; + uint8_t type; + uint8_t family; +} cmd_listener_list_item_t; + +static_assert(sizeof(cmd_listener_list_item_t) == 56, ""); -// SIZE=1 +typedef struct { + cmd_header_t header; + cmd_listener_list_item_t payload; +} msg_listener_list_reply_t; +/* Serialized version of hc_connection_t */ typedef struct { - uint32_t timePeriod; -} mapme_timing_command; + char name[SYMBOLIC_NAME_LEN]; + char interface_name[INTERFACE_LEN]; + hicn_ip_address_t local_addr; + hicn_ip_address_t remote_addr; + uint32_t id; + uint32_t priority; + uint16_t local_port; + uint16_t remote_port; + uint8_t netdevice_type; + uint8_t type; + uint8_t family; + uint8_t admin_state; + uint8_t tags; + uint8_t state; + uint8_t __pad[6]; +} cmd_connection_list_item_t; + +static_assert(POLICY_TAG_N <= 8, ""); +static_assert(sizeof(cmd_connection_list_item_t) == 88, ""); typedef struct { - ip_address_t address; - uint8_t addressType; - uint8_t len; -} mapme_send_update_command; + cmd_header_t header; + cmd_connection_list_item_t payload; +} msg_connection_list_reply_t; -// SIZE=1 +typedef msg_connection_list_reply_t msg_connection_notify_t; typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; + char interface_name[INTERFACE_LEN]; + hicn_ip_address_t local_addr; + hicn_ip_address_t remote_addr; + uint32_t id; + uint32_t priority; + uint16_t local_port; + uint16_t remote_port; + uint8_t netdevice_type; + uint8_t type; + uint8_t family; uint8_t admin_state; - uint8_t pad8[3]; -} connection_set_admin_state_command; + uint8_t tags; + uint8_t state; + uint8_t __pad[6]; +} cmd_face_list_item_t; -#ifdef WITH_POLICY +static_assert(sizeof(cmd_face_list_item_t) == 72, ""); typedef struct { - ip_address_t address; - uint8_t addressType; + char face_name[SYMBOLIC_NAME_LEN]; + hicn_ip_address_t remote_addr; + uint32_t face_id; + uint16_t cost; + uint8_t family; uint8_t len; - hicn_policy_t policy; -} add_policy_command; + cmd_face_list_item_t face; +} cmd_route_list_item_t; + +static_assert(sizeof(cmd_route_list_item_t) == 112, ""); typedef struct { - ip_address_t address; - uint8_t addressType; - uint8_t len; - hicn_policy_t policy; -} list_policies_command; + cmd_header_t header; + cmd_route_list_item_t payload; +} msg_route_list_reply_t; + +typedef msg_route_list_reply_t msg_route_notify_t; typedef struct { - ip_address_t address; - uint8_t addressType; + uint8_t state; + uint8_t disabled; + uint16_t __pad; +} _policy_tag_state_t; + +typedef struct { + uint32_t throughput; + uint32_t latency; + uint32_t loss_rate; +} _interface_stats_t; + +typedef struct { + _interface_stats_t wired; + _interface_stats_t wifi; + _interface_stats_t cellular; + _interface_stats_t all; +} _policy_stats_t; + +typedef struct { + char app_name[APP_NAME_LEN]; + _policy_tag_state_t tags[POLICY_TAG_N]; + _policy_stats_t stats; + uint8_t __pad[4]; +} _hicn_policy_t; + +static_assert(sizeof(_hicn_policy_t) == 208, ""); + +typedef struct { + uint8_t policy[208]; + hicn_ip_address_t remote_addr; + uint8_t family; uint8_t len; -} remove_policy_command; + uint8_t __pad[6]; +} cmd_policy_list_item_t; + +static_assert(sizeof(cmd_policy_list_item_t) == 232, ""); typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - uint8_t admin_state; - uint32_t priority; - policy_tags_t tags; -} update_connection_command; + cmd_header_t header; + cmd_policy_list_item_t payload; +} msg_policy_list_reply_t; + +typedef msg_policy_list_reply_t msg_policy_notify_t; +/* Those are needed to build but not used */ typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - uint32_t priority; -} connection_set_priority_command; + uint8_t _; +} cmd_strategy_list_item_t; +typedef struct { + uint8_t _; +} cmd_subscription_list_item_t; + +/* Statistics */ typedef struct { - char symbolicOrConnid[SYMBOLIC_NAME_LEN]; - policy_tags_t tags; -} connection_set_tags_command; + cmd_header_t header; + hicn_light_stats_t payload; +} msg_stats_get_reply_t; -#endif /* WITH_POLICY */ +typedef struct { + uint32_t id; + connection_stats_t stats; +} cmd_stats_list_item_t; + +typedef struct { + cmd_header_t header; + cmd_stats_list_item_t payload; +} msg_stats_list_reply_t; //===== size of commands ====== // REMINDER: when a new_command is added, the following switch has to be // updated. -static inline int payloadLengthDaemon(command_id id) { - switch (id) { - case ADD_LISTENER: - return sizeof(add_listener_command); - case ADD_CONNECTION: - return sizeof(add_connection_command); - case LIST_CONNECTIONS: - return 0; // list connections: payload always 0 - case ADD_ROUTE: - return sizeof(add_route_command); - case LIST_ROUTES: - return 0; // list routes: payload always 0 - case REMOVE_CONNECTION: - return sizeof(remove_connection_command); - case REMOVE_LISTENER: - return sizeof(remove_listener_command); - case REMOVE_ROUTE: - return sizeof(remove_route_command); - case CACHE_STORE: - return sizeof(cache_store_command); - case CACHE_SERVE: - return sizeof(cache_serve_command); - case CACHE_CLEAR: - return 0; // cache clear - case SET_STRATEGY: - return sizeof(set_strategy_command); - case SET_WLDR: - return sizeof(set_wldr_command); - case ADD_PUNTING: - return sizeof(add_punting_command); - case LIST_LISTENERS: - return 0; // list listeners: payload always 0 - case MAPME_ENABLE: - return sizeof(mapme_activator_command); - case MAPME_DISCOVERY: - return sizeof(mapme_activator_command); - case MAPME_TIMESCALE: - return sizeof(mapme_timing_command); - case MAPME_RETX: - return sizeof(mapme_timing_command); - case MAPME_SEND_UPDATE: - return sizeof(mapme_send_update_command); - case CONNECTION_SET_ADMIN_STATE: - return sizeof(connection_set_admin_state_command); -#ifdef WITH_POLICY - case ADD_POLICY: - return sizeof(add_policy_command); - case LIST_POLICIES: - return 0; // list policies: payload always 0 - case REMOVE_POLICY: - return sizeof(remove_policy_command); - case UPDATE_CONNECTION: - return sizeof(update_connection_command); - case CONNECTION_SET_PRIORITY: - return sizeof(connection_set_priority_command); - case CONNECTION_SET_TAGS: - return sizeof(connection_set_tags_command); -#endif /* WITH_POLICY */ - case LAST_COMMAND_VALUE: - return 0; - default: - return 0; +static inline int command_get_payload_len(command_type_t command_type) { + switch (command_type) { +#define _(l, u) \ + case COMMAND_TYPE_##u: \ + return sizeof(cmd_##l##_t); + foreach_command_type +#undef _ + case COMMAND_TYPE_UNDEFINED : case COMMAND_TYPE_N : return 0; } } -#endif /* HICN_CTRL_HICNLIGHT_H */ + +ssize_t hc_light_command_serialize(hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg); + +int hc_sock_initialize_module(hc_sock_t *s); + +#endif /* HICN_CTRL_HICNLIGHTNG_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/object.h b/ctrl/libhicnctrl/includes/hicn/ctrl/object.h new file mode 100644 index 000000000..c659d1824 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/object.h @@ -0,0 +1,76 @@ +/* + * 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 object.h + * \brief API object representation. + */ + +#ifndef HICNCTRL_OBJECT_H +#define HICNCTRL_OBJECT_H + +#include <hicn/ctrl/object_type.h> + +#include <hicn/ctrl/objects/listener.h> +#include <hicn/ctrl/objects/connection.h> +#include <hicn/ctrl/objects/route.h> +#include <hicn/ctrl/objects/punting.h> +#include <hicn/ctrl/objects/strategy.h> +#include <hicn/ctrl/objects/policy.h> +#include <hicn/ctrl/objects/subscription.h> +#include <hicn/ctrl/objects/cache.h> +#include <hicn/ctrl/objects/mapme.h> +#include <hicn/ctrl/objects/active_interface.h> + +typedef union { + hc_connection_t connection; + hc_listener_t listener; + hc_route_t route; + hc_face_t face; + // hc_data_t *data; + hc_punting_t punting; + hc_strategy_t strategy; + hc_policy_t policy; + hc_subscription_t subscription; + hc_cache_t cache; + hc_mapme_t mapme; + hc_active_interface_t active_interface; + uint8_t as_uint8; +} hc_object_t; + +#define MAXSZ_OBJECT_T MAX + +#define IS_VALID_ACTION(x) IS_VALID_ENUM_TYPE(ACTION, x) + +bool hc_object_is_empty(const hc_object_t *object); + +size_t hc_object_size(hc_object_type_t object_type); + +int hc_object_validate(hc_object_type_t object_type, hc_object_t *object, + bool allow_partial); +int hc_object_cmp(hc_object_type_t object_type, hc_object_t *object1, + hc_object_t *object2); +int hc_object_snprintf(char *s, size_t size, hc_object_type_t object_type, + hc_object_t *object); + +#define foreach_object(VAR, data) foreach_type(hc_object_t, VAR, data) + +#define foreach_type(TYPE, VAR, DATA) \ + for (TYPE *VAR = (TYPE *)hc_data_get_buffer(DATA); \ + VAR < (TYPE *)(hc_data_get_buffer(DATA) + \ + hc_data_get_size(DATA) * sizeof(TYPE)); \ + VAR++) + +#endif /* HICNCTRL_OBJECT_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/object_type.h b/ctrl/libhicnctrl/includes/hicn/ctrl/object_type.h new file mode 100644 index 000000000..39a2d188e --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/object_type.h @@ -0,0 +1,57 @@ +/* + * 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.h + * \brief Object type. + */ + +#ifndef HICNCTRL_OBJECT_TYPE_H +#define HICNCTRL_OBJECT_TYPE_H + +#define foreach_object_type \ + _(UNDEFINED) \ + _(CONNECTION) \ + _(LISTENER) \ + _(ROUTE) \ + _(FACE) \ + _(STRATEGY) \ + _(PUNTING) \ + _(POLICY) \ + _(CACHE) \ + _(MAPME) \ + _(WLDR) \ + _(LOCAL_PREFIX) \ + _(PROBE) \ + _(SUBSCRIPTION) \ + _(ACTIVE_INTERFACE) \ + _(STATS) \ + _(N) + +typedef enum { +#define _(x) OBJECT_TYPE_##x, + foreach_object_type +#undef _ +} hc_object_type_t; + +extern const char *object_type_str[]; + +#define object_type_str(x) object_type_str[x] + +hc_object_type_t object_type_from_str(const char *object_str); + +#define IS_VALID_OBJECT_TYPE(x) IS_VALID_ENUM_TYPE(OBJECT_TYPE, x) + +#endif /* HICNCTRL_OBJECT_TYPE_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects.h new file mode 100644 index 000000000..4f560a2f3 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects.h @@ -0,0 +1,11 @@ +#ifndef HICNCTRL_OBJECTS_H +#define HICNCTRL_OBJECTS_H + +#include <hicn/ctrl/objects/connection.h> +#include <hicn/ctrl/objects/face.h> +#include <hicn/ctrl/objects/listener.h> +#include <hicn/ctrl/objects/route.h> +#include <hicn/ctrl/objects/strategy.h> +#include <hicn/ctrl/objects/subscription.h> + +#endif /* HICNCTRL_OBJECTS_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/active_interface.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/active_interface.h new file mode 100644 index 000000000..56a1d8cd5 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/active_interface.h @@ -0,0 +1,44 @@ +/* + * 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 objects/active_interface.h + * \brief Route. + */ + +#ifndef HICNCTRL_OBJECTS_ACTIVE_INTERFACE_H +#define HICNCTRL_OBJECTS_ACTIVE_INTERFACE_H + +#include <hicn/ctrl/objects/face.h> + +typedef struct { + hicn_ip_prefix_t prefix; + netdevice_flags_t interface_types; +} hc_active_interface_t; + +#define foreach_active_interface(VAR, data) \ + foreach_type(hc_active_interface_t, VAR, data) + +// XXX WRONG +#define MAXSZ_HC_ACTIVE_INTERFACE_ \ + MAXSZ_FACE_ID + 1 + MAXSZ_COST + 1 + MAXSZ_IP_ADDRESS + 1 + MAXSZ_LEN +#define MAXSZ_HC_ACTIVE_INTERFACE MAXSZ_HC_ACTIVE_INTERFACE_ + NULLTERM + +int hc_active_interface_snprintf(char *s, size_t size, + const hc_active_interface_t *active_interface); +int hc_active_interface_validate(const hc_active_interface_t *active_interface, + bool allow_partial); + +#endif diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/base.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/base.h new file mode 100644 index 000000000..fc40f680e --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/base.h @@ -0,0 +1,25 @@ +/* + * 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 base.h + * \brief Base definitions for objects. + */ +#ifndef HICNCTRL_OBJECTS_BASE +#define HICNCTRL_OBJECTS_BASE + +#define INTERFACE_LEN 16 + +#endif /* HICNCTRL_OBJECTS_BASE */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/cache.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/cache.h new file mode 100644 index 000000000..1f8691be6 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/cache.h @@ -0,0 +1,38 @@ +/* + * 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 objects/cache.h + * \brief Cache. + */ + +#ifndef HICNCTRL_OBJECTS_CACHE_H +#define HICNCTRL_OBJECTS_CACHE_H + +typedef struct { + uint8_t serve; // 1 = on, 0 = off + uint8_t store; // 1 = on, 0 = off +} hc_cache_t; + +typedef struct { + bool store; + bool serve; + size_t cs_size; + size_t num_stale_entries; +} hc_cache_info_t; + +int hc_cache_snprintf(char *s, size_t size, const hc_cache_info_t *cache_info); + +#endif /* HICNCTRL_OBJECTS_CACHE_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/connection.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/connection.h new file mode 100644 index 000000000..771b48c20 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/connection.h @@ -0,0 +1,64 @@ +/* + * 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 objects/connection.h + * \brief Connection. + */ + +#ifndef HICNCTRL_OBJECTS_CONNECTION_H +#define HICNCTRL_OBJECTS_CONNECTION_H + +#include <stdint.h> +#include <hicn/face.h> + +#include "base.h" + +/* + * NOTE : + * - interface_name is mainly used to derive listeners from connections, + * but is not itself used to create connections. + */ +typedef struct { + uint32_t id; /* Kr. */ + char name[SYMBOLIC_NAME_LEN]; /* K.w */ + char interface_name[INTERFACE_LEN]; /* Kr. */ + netdevice_type_t netdevice_type; /* .r. */ + face_type_t type; /* .rw */ + int family; /* .rw */ + hicn_ip_address_t local_addr; /* .rw */ + uint16_t local_port; /* .rw */ + hicn_ip_address_t remote_addr; /* .rw */ + uint16_t remote_port; /* .rw */ + face_state_t admin_state; /* .rw */ + uint32_t priority; /* .rw */ + policy_tags_t tags; /* .rw */ + face_state_t state; /* .r. */ +} hc_connection_t; + +#define foreach_connection(VAR, data) foreach_type(hc_connection_t, VAR, data) + +#define MAXSZ_HC_CONNECTION_ \ + MAXSZ_FACE_STATE_ + INTERFACE_LEN + SPACE + 2 * MAXSZ_URL_ + \ + MAXSZ_FACE_TYPE_ + SPACES(3) +#define MAXSZ_HC_CONNECTION MAXSZ_HC_CONNECTION_ + NULLTERM + +int hc_connection_validate(const hc_connection_t *connection, + bool allow_partial); +int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2); +int hc_connection_snprintf(char *s, size_t size, + const hc_connection_t *connection); + +#endif /* HICNCTRL_OBJECTS_CONNECTION_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/face.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/face.h new file mode 100644 index 000000000..1aa122f37 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/face.h @@ -0,0 +1,49 @@ +/* + * 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 objects/face.h + * \brief Face. + * + * A face is an abstraction introduced by the control library to abstract the + * forwarder implementation details. It encompasses connections and listeners + * and ensures the right dependencies are enforced, eg that we always have a + * listener when a connection is created. + */ + +#ifndef HICNCTRL_OBJECTS_FACE_H +#define HICNCTRL_OBJECTS_FACE_H + +#include <hicn/face.h> + +#include "base.h" + +typedef face_t hc_face_t; + +#define foreach_face(VAR, data) foreach_type(hc_face_t, VAR, data) + +#define MAX_FACE_ID 255 +#define MAXSZ_FACE_ID_ 3 +#define MAXSZ_FACE_ID MAXSZ_FACE_ID_ + NULLTERM +#define MAXSZ_FACE_NAME_ SYMBOLIC_NAME_LEN +#define MAXSZ_FACE_NAME MAXSZ_FACE_NAME_ + NULLTERM + +#define MAXSZ_HC_FACE_ \ + MAXSZ_FACE_ID_ + MAXSZ_FACE_NAME_ + MAXSZ_FACE_ + 5 + HOTFIXMARGIN +#define MAXSZ_HC_FACE MAXSZ_HC_FACE_ + NULLTERM + +int hc_face_snprintf(char *s, size_t size, const hc_face_t *face); + +#endif /* HICNCTRL_OBJECTS_FACE_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/listener.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/listener.h new file mode 100644 index 000000000..0fb74f558 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/listener.h @@ -0,0 +1,52 @@ +/* + * 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 objects/listener.h + * \brief Listener. + */ + +#ifndef HICNCTRL_OBJECTS_LISTENER_H +#define HICNCTRL_OBJECTS_LISTENER_H + +#include <stddef.h> // offsetof +#include <stdint.h> +#include <hicn/face.h> + +#include "base.h" + +// FIXME the listener should not require any port for hICN... +typedef struct { + char name[SYMBOLIC_NAME_LEN]; /* K.w */ + char interface_name[INTERFACE_LEN]; /* Kr. */ + uint32_t id; /* Kr. */ + face_type_t type; /* .rw */ + int family; /* .rw */ + hicn_ip_address_t local_addr; /* .rw */ + uint16_t local_port; /* .rw */ +} hc_listener_t; + +int hc_listener_validate(const hc_listener_t *listener, bool allow_partial); +int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2); + +#define foreach_listener(VAR, data) foreach_type(hc_listener_t, VAR, data) + +#define MAXSZ_HC_LISTENER_ \ + INTERFACE_LEN + SPACE + MAXSZ_URL_ + SPACE + MAXSZ_FACE_TYPE_ +#define MAXSZ_HC_LISTENER MAXSZ_HC_LISTENER_ + NULLTERM + +int hc_listener_snprintf(char *s, size_t size, const hc_listener_t *listener); + +#endif /* HICNCTRL_OBJECTS_LISTENER_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/mapme.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/mapme.h new file mode 100644 index 000000000..37623ebfe --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/mapme.h @@ -0,0 +1,58 @@ +/* + * 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 objects/mapme.h + * \brief MAP-Me. + */ + +#ifndef HICNCTRL_OBJECTS_MAPME_H +#define HICNCTRL_OBJECTS_MAPME_H + +typedef enum { + MAPME_TARGET_ENABLE, + MAPME_TARGET_DISCOVERY, + MAPME_TARGET_TIMESCALE, + MAPME_TARGET_RETX, +} mapme_target_t; + +static inline mapme_target_t mapme_target_from_str(char *mapme_target_str) { + if (strcasecmp(mapme_target_str, "enable") == 0) + return MAPME_TARGET_ENABLE; + else if (strcasecmp(mapme_target_str, "discovery") == 0) + return MAPME_TARGET_DISCOVERY; + else if (strcasecmp(mapme_target_str, "timescale") == 0) + return MAPME_TARGET_TIMESCALE; + else + return MAPME_TARGET_RETX; +} + +#define MAX_MAPME_ARG_LEN 30 + +typedef struct { + mapme_target_t target; + // Command argument stored as a string + // before being parsed into 'enabled' or 'timescale' + char unparsed_arg[MAX_MAPME_ARG_LEN]; + + uint8_t enabled; // 1 = on, 0 = off + uint32_t timescale; // Milliseconds + + hicn_ip_address_t address; + int family; + u8 len; +} hc_mapme_t; + +#endif /* HICNCTRL_OBJECTS_MAPME_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/policy.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/policy.h new file mode 100644 index 000000000..437387e4a --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/policy.h @@ -0,0 +1,40 @@ +/* + * 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 objects/policy.h + * \brief Policy. + */ + +#ifndef HICNCTRL_OBJECTS_POLICY_H +#define HICNCTRL_OBJECTS_POLICY_H + +typedef struct { + int family; /* Krw */ + hicn_ip_address_t remote_addr; /* krw */ + uint8_t len; /* krw */ + hicn_policy_t policy; /* .rw */ +} hc_policy_t; + +#define foreach_policy(VAR, data) foreach_type(hc_policy_t, VAR, data) + +/* TODO */ +#define MAXSZ_HC_POLICY_ 0 +#define MAXSZ_HC_POLICY MAXSZ_HC_POLICY_ + NULLTERM + +int hc_policy_snprintf(char *s, size_t size, hc_policy_t *policy); +int hc_policy_validate(const hc_policy_t *policy); + +#endif /* HICNCTRL_OBJECTS_POLICY_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/punting.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/punting.h new file mode 100644 index 000000000..d18e596b1 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/punting.h @@ -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 objects/punting.h + * \brief Punting + */ + +#ifndef HICNCTRL_OBJECTS_PUNTING_H +#define HICNCTRL_OBJECTS_PUNTING_H + +typedef struct { + face_id_t face_id; /* Kr. */ // XXX listener id, could be NULL for all ? + int family; /* Krw */ + hicn_ip_address_t prefix; /* krw */ + u8 prefix_len; /* krw */ +} hc_punting_t; + +int hc_punting_validate(const hc_punting_t *punting); +int hc_punting_cmp(const hc_punting_t *c1, const hc_punting_t *c2); + +#define foreach_punting(VAR, data) foreach_type(hc_punting_t, VAR, data) + +#define MAXSZ_HC_PUNTING_ 0 +#define MAXSZ_HC_PUNTING MAXSZ_HC_PUNTING_ + NULLTERM + +int hc_punting_snprintf(char *s, size_t size, hc_punting_t *punting); + +#endif /* HICNCTRL_OBJECTS_PUNTING_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/route.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/route.h new file mode 100644 index 000000000..fb68e9430 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/route.h @@ -0,0 +1,52 @@ +/* + * 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 objects/route.h + * \brief Route. + */ + +#ifndef HICNCTRL_OBJECTS_ROUTE_H +#define HICNCTRL_OBJECTS_ROUTE_H + +#include <hicn/ctrl/objects/face.h> + +typedef struct { + face_id_t face_id; /* Kr. ID (used when face and face_name == NULL) */ + char face_name[SYMBOLIC_NAME_LEN]; /* Kr. a name or an ID (if integer), used + if face is NULL */ + int family; /* Krw */ + hicn_ip_address_t remote_addr; /* krw */ + uint8_t len; /* krw */ + uint16_t cost; /* .rw */ + hc_face_t face; /* use by default if not NULL, otherwise look at face_name, + then face_id */ +} hc_route_t; + +#define foreach_route(VAR, data) foreach_type(hc_route_t, VAR, data) + +#define MAX_COST 65535 +#define MAXSZ_COST 5 +#define MAX_LEN 255 +#define MAXSZ_LEN 3 + +#define MAXSZ_HC_ROUTE_ \ + MAXSZ_FACE_ID + 1 + MAXSZ_COST + 1 + MAXSZ_IP_ADDRESS + 1 + MAXSZ_LEN +#define MAXSZ_HC_ROUTE MAXSZ_HC_ROUTE_ + NULLTERM + +int hc_route_snprintf(char *s, size_t size, const hc_route_t *route); +int hc_route_validate(const hc_route_t *route, bool allow_partial); + +#endif /* HICNCTRL_OBJECTS_ROUTE_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/strategy.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/strategy.h new file mode 100644 index 000000000..208f4620b --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/strategy.h @@ -0,0 +1,46 @@ + +/* + * 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 objects/strategy.h + * \brief Strategy. + */ + +#ifndef HICNCTRL_OBJECTS_STRATEGY_H +#define HICNCTRL_OBJECTS_STRATEGY_H + +#include <hicn/strategy.h> + +#define MAXSZ_STRATEGY_NAME 255 + +typedef struct { + // The name is not set by the controller + // but populated by the daemon + char name[MAXSZ_STRATEGY_NAME]; + strategy_type_t type; + hicn_ip_address_t address, local_address; + int family, local_family; + u8 len, local_len; +} hc_strategy_t; + +#define foreach_strategy(VAR, data) foreach_type(hc_strategy_t, VAR, data) + +#define MAXSZ_HC_STRATEGY_ MAXSZ_STRATEGY_NAME +#define MAXSZ_HC_STRATEGY MAXSZ_HC_STRATEGY_ + NULLTERM + +int hc_strategy_snprintf(char *s, size_t size, const hc_strategy_t *strategy); + +#endif /* HICNCTRL_OBJECTS_STRATEGY_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/objects/subscription.h b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/subscription.h new file mode 100644 index 000000000..861341160 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/objects/subscription.h @@ -0,0 +1,83 @@ +/* + * 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 objects/subscription.h + * \brief Subscription. + */ + +#ifndef HICNCTRL_OBJECTS_SUBSCRIPTION_H +#define HICNCTRL_OBJECTS_SUBSCRIPTION_H + +#include <limits.h> +#include <stddef.h> +#include <hicn/ctrl/object_type.h> + +#undef PUNTING // TODO(eloparco): Undefined to avoid collisions + // Fix the collision + +// Used only to create 'hc_topic_t' +typedef struct { +#define _(x) char x; + foreach_object_type +#undef _ +} object_offset_t; + +// Flags for topic subscriptions +typedef enum { +#define _(x) TOPIC_##x = (1 << offsetof(object_offset_t, x)), + foreach_object_type +#undef _ + TOPIC_ALL = INT_MAX, +} hc_topic_t; + +static inline hc_object_type_t object_from_topic(hc_topic_t topic) { +#define _(x) \ + if (topic == TOPIC_##x) return OBJECT_TYPE_##x; + foreach_object_type +#undef _ + return OBJECT_TYPE_UNDEFINED; +} + +static inline hc_topic_t topic_from_object_type(hc_object_type_t object_type) { + if (object_type == OBJECT_TYPE_UNDEFINED) return TOPIC_ALL; +#define _(x) \ + if (object_type == OBJECT_TYPE_##x) return TOPIC_##x; + foreach_object_type +#undef _ + return TOPIC_UNDEFINED; +} + +#define NUM_TOPICS OBJECT_TYPE_N // Because a topic is created for each object +#define ALL_TOPICS ~0 + +// Subscriptions +typedef uint32_t hc_topics_t; +typedef struct { + hc_topics_t topics; +} hc_subscription_t; + +#if 0 +typedef struct { + netdevice_type_t interface_type; +} hc_event_interface_update_t; + +typedef struct { + ip_prefix_t prefix; + netdevice_type_t interface_type; +} hc_event_active_interface_update_t; +#endif + +#endif /* HICNCTRL_OBJECTS_SUBSCRIPTION_H */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/parse.h b/ctrl/libhicnctrl/includes/hicn/ctrl/parse.h new file mode 100644 index 000000000..8921d25ed --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/parse.h @@ -0,0 +1,116 @@ +#ifndef HICNLIGHT_PARSE_CMD +#define HICNLIGHT_PARSE_CMD + +#include <hicn/ctrl/api.h> + +#include "command.h" + +/* Update sscanf accordingly in parse_cmd.c */ +#define MAX_PARAMETERS 10 +#define MAX_SCANF_PARAM_LEN 100 + +typedef int (*parser_hook_t)(void* arg); + +#if 0 +typedef struct { + const char* name; + const char* help; + parser_type_t type; + size_t offset; + /* + * quick hack to let the functions update two or more parameters, like for + * IP_ADDRESS or IP_PREFIX types + */ + size_t offset2; + size_t offset3; +} command_parameter_t; + +typedef struct { + hc_action_t action; + hc_object_type_t object; + unsigned nparams; + command_parameter_t parameters[MAX_PARAMETERS]; + parser_hook_t post_hook; +} command_parser_t; + +#define TYPE_STRN(N) \ + (parser_type_t) { \ + .name = TYPENAME_STR, \ + .str = { \ + .max_size = N, \ + }, \ + } +#define TYPE_FMT_STRN(N) "%s" + +#define TYPE_INT(MIN, MAX) \ + (parser_type_t) { \ + .name = TYPENAME_INT, \ + .sint = { \ + .min = (MIN), \ + .max = (MAX), \ + }, \ + } +#define TYPE_FMT_INT "%d" + +#define TYPE_UINT(min, max) \ + (parser_type_t) { \ + .name = TYPENAME_UINT, \ + .uint = { \ + .min = min, \ + .max = max, \ + }, \ + } +#define TYPE_FMT_UINT "%u" + +#define TYPE_SYMBOLIC_OR_ID TYPE_STRN(SYMBOLIC_NAME_LEN) +#define TYPE_FMT_SYMBOLIC_OR_ID "%s" + +#define TYPE_INTERFACE_NAME TYPE_STRN(INTERFACE_LEN) +#define TYPE_FMT_INTERFACE_NAME "%s" + +#define TYPE_IP_ADDRESS \ + (parser_type_t) { .name = TYPENAME_IP_ADDRESS, } +#define TYPE_FMT_IP_ADDRESS "%s" + +#define TYPE_IP_PREFIX \ + (parser_type_t) { .name = TYPENAME_IP_PREFIX, } +#define TYPE_FMT_IP_PREFIX "%s" + +#define TYPE_ON_OFF \ + (parser_type_t) { .name = TYPENAME_ON_OFF, } +#define TYPE_FMT_ON_OFF "%s" + +#define TYPE_ENUM(x) \ + (parser_type_t) { \ + .name = TYPENAME_ENUM, \ + .enum_ = { \ + .from_str = (int (*)(const char*))x##_from_str, \ + }, \ + } +/* We need to allocate room for the intermediate string */ +#define TYPE_FMT_ENUM "%s" + +#define TYPE_POLICY_STATE(TAG) \ + (parser_type_t) { \ + .name = TYPENAME_POLICY_STATE, \ + .policy_state = { \ + .tag = TAG, \ + }, \ + } +/* We need to allocate room for the intermediate string */ +#define TYPE_FMT_POLICY_STATE "%s" +#endif + +int parse_getopt_args(const command_parser_t* parser, int argc, char* argv[], + hc_command_t* command); + +int parse(const char* cmd, hc_command_t* command); +int help(const char* cmd); + +/** + * @brief Convert the action enum to the action name used in the commands (e.g. + * from ACTION_CREATE to "add"). + */ +const char* action_to_cmd_action(hc_action_t action); + +#endif /* HICNLIGHT_PARSE_CMD */ diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/route.h b/ctrl/libhicnctrl/includes/hicn/ctrl/route.h index 81f011d4d..f1801d772 100644 --- a/ctrl/libhicnctrl/includes/hicn/ctrl/route.h +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/route.h @@ -34,15 +34,15 @@ typedef struct hicn_route_s hicn_route_t; #define MAX_ROUTE_COST 255 #define IS_VALID_ROUTE_COST(x) ((x >= MIN_ROUTE_COST) && (x <= MAX_ROUTE_COST)) -hicn_route_t* hicn_route_create(ip_prefix_t* prefix, face_id_t face_id, +hicn_route_t* hicn_route_create(hicn_ip_prefix_t* prefix, face_id_t face_id, route_cost_t cost); hicn_route_t* hicn_route_dup(const hicn_route_t* route); void hicn_route_free(hicn_route_t* route); int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2); -int hicn_route_get_prefix(const hicn_route_t* route, ip_prefix_t* prefix); -int hicn_route_set_prefix(hicn_route_t* route, const ip_prefix_t prefix); +int hicn_route_get_prefix(const hicn_route_t* route, hicn_ip_prefix_t* prefix); +int hicn_route_set_prefix(hicn_route_t* route, const hicn_ip_prefix_t prefix); int hicn_route_get_cost(const hicn_route_t* route, int* cost); int hicn_route_set_cost(hicn_route_t* route, const int cost); diff --git a/ctrl/libhicnctrl/includes/hicn/ctrl/socket.h b/ctrl/libhicnctrl/includes/hicn/ctrl/socket.h new file mode 100644 index 000000000..2503fadd0 --- /dev/null +++ b/ctrl/libhicnctrl/includes/hicn/ctrl/socket.h @@ -0,0 +1,181 @@ +/* + * 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.h + * \brief Control socket + */ + +#ifndef HICNCTRL_SOCKET_H +#define HICNCTRL_SOCKET_H + +#include <hicn/ctrl/data.h> + +/* With UDP, the buffer should be able to receieve a full packet, and thus MTU + * (max 9000) is sufficient. Messages will be received fully one by one. + * With TCP, the buffer should be at least able to receive a message header and + * the maximum size of a data element, so any reasonable size will be correct, + * it might just optimize performance. Messages might arrive in chunks that the + * library is able to parse. + */ +#define JUMBO_MTU 9000 +#define RECV_BUFLEN 65535 + +#define foreach_forwarder_type \ + _(UNDEFINED) \ + _(HICNLIGHT) \ + _(VPP) \ + _(N) + +typedef enum { +#define _(x) FORWARDER_TYPE_##x, + foreach_forwarder_type +#undef _ +} forwarder_type_t; + +extern const char *forwarder_type_str[]; + +#define forwarder_type_str(x) forwarder_type_str[x] + +forwarder_type_t forwarder_type_from_str(const char *str); + +/** + * \brief Holds the state of an hICN control socket + */ +typedef struct hc_sock_s hc_sock_t; + +/** + * \brief Create an hICN control socket using the specified URL. + * \param [in] url - The URL to connect to. + * \return an hICN control socket + */ +hc_sock_t *hc_sock_create_url(const char *url); + +/** + * \brief Create an hICN control socket using the provided forwarder. + * \return an hICN control socket + */ +hc_sock_t *hc_sock_create_forwarder(forwarder_type_t forwarder); + +/** + * \brief Create an hICN control socket using the provided forwarder and a + * URL. \return an hICN control socket + */ +hc_sock_t *hc_sock_create_forwarder_url(forwarder_type_t forwarder, + const char *url); + +/** + * \brief Create an hICN control socket using the default connection type. + * XXX doc + * \return an hICN control socket + */ +hc_sock_t *hc_sock_create(forwarder_type_t forwarder, const char *url); + +/** + * \brief Frees an hICN control socket + * \param [in] s - hICN control socket + */ +void hc_sock_free(hc_sock_t *s); + +/** + * \brief Returns the next available sequence number to use for requests to + * the API. \param [in] s - hICN control socket + */ +int hc_sock_get_next_seq(hc_sock_t *s); + +/** + * \brief Sets the socket as non-blocking + * \param [in] s - hICN control socket + * \return Error code + */ +int hc_sock_set_nonblocking(hc_sock_t *s); + +/** + * \brief Return the file descriptor associated to the hICN contorl sock + * \param [in] s - hICN control socket + * \return The file descriptor (positive value), or a negative integer in case + * of error + */ +int hc_sock_get_fd(hc_sock_t *s); + +/** + * \brief Connect the socket + * \return Error code + */ +int hc_sock_connect(hc_sock_t *s); + +/** + * \brief Return the offset and size of available buffer space + * \param [in] s - hICN control socket + * \param [out] buffer - Offset in buffer + * \param [out] size - Remaining size + * \return Error code + */ +int hc_sock_get_recv_buffer(hc_sock_t *s, uint8_t **buffer, size_t *size); + +#if 0 +/** + * \brief Write/read iexchance on the control socket (internal helper + * function) \param [in] s - hICN control socket \param [in] msg - Message to + * send \param [in] msglen - Length of the message to send \return Error code + */ +int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq); +#endif + +/** + * \brief Processing data received by socket + * \param [in] s - hICN control socket + * \param [in] parse - Parse function to convert remote types into lib native + * types, or NULL not to perform any translation. + * \return Error code + */ +int hc_sock_process(hc_sock_t *s, hc_data_t **data); + +int hc_sock_receive(hc_sock_t *s, hc_data_t **data); +int hc_sock_receive_all(hc_sock_t *s, hc_data_t **data); + +#if 0 +/** + * \brief Callback used in async mode when data is available on the socket + * \param [in] s - hICN control socket + * \return Error code + */ +int hc_sock_callback(hc_sock_t *s, hc_data_t **data); +#endif + +/** + * \brief Reset the state of the sock (eg. to handle a reconnecton) + * \param [in] s - hICN control socket + * \return Error code + */ +int hc_sock_reset(hc_sock_t *s); + +void hc_sock_increment_woff(hc_sock_t *s, size_t bytes); + +#if 0 +int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, + data_callback_t complete_cb, void *complete_cb_data); + +#endif + +int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms); + +int hc_sock_set_async(hc_sock_t *s); + +int hc_sock_is_async(hc_sock_t *s); + +int hc_sock_on_receive(hc_sock_t *s, size_t count); + +#endif /* HICNCTRL_SOCKET_H */ diff --git a/ctrl/libhicnctrl/src/CMakeLists.txt b/ctrl/libhicnctrl/src/CMakeLists.txt index 1bec03d50..a9a7d3db4 100644 --- a/ctrl/libhicnctrl/src/CMakeLists.txt +++ b/ctrl/libhicnctrl/src/CMakeLists.txt @@ -15,11 +15,50 @@ # Source files ############################################################## set(SOURCE_FILES - route.c + action.c api.c + command.c + commands/command_cache.c + commands/command_connection.c + commands/command_face.c + commands/command_listener.c + commands/command_mapme.c + commands/command_policy.c + commands/command_punting.c + commands/command_route.c + commands/command_stats.c + commands/command_strategy.c + commands/command_subscription.c + data.c + fw_interface.c + object.c + object_type.c + object_vft.c + objects/active_interface.c + objects/base.c + objects/connection.c + objects/face.c + objects/listener.c + objects/route.c + objects/strategy.c + objects/subscription.c + parse.c + request.c + route.c + socket.c ) set(HEADER_FILES + object_vft.h + objects/active_interface.h + objects/base.h + objects/connection.h + objects/face.h + objects/listener.h + objects/route.h + objects/strategy.h + objects/subscription.h + request.h api_private.h ) @@ -58,13 +97,33 @@ endif () ############################################################## +# Compiler options +############################################################## +set(COMPILER_OPTIONS + ${DEFAULT_COMPILER_OPTIONS} +) + +############################################################## # Do not use modules if Android ############################################################## if (${CMAKE_SYSTEM_NAME} MATCHES Android OR ${CMAKE_SYSTEM_NAME} MATCHES iOS) list(APPEND SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light_common.c - ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light_ng_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/face.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/strategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/subscription.c + ) + list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/face.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/modules/hicn_light/subscription.h ) else() add_subdirectory(modules) @@ -72,14 +131,6 @@ endif() ############################################################## -# Compiler options -############################################################## -set(COMPILER_OPTIONS - ${DEFAULT_COMPILER_OPTIONS} -) - - -############################################################## # Build main hicnctrl library ############################################################## build_library(${LIBHICNCTRL} @@ -96,6 +147,12 @@ build_library(${LIBHICNCTRL} COMPILE_OPTIONS ${COMPILER_OPTIONS} ) +############################################################## +# Unit tests +############################################################## +if (${BUILD_TESTS}) + add_subdirectory(test) +endif() ############################################################## # Cmake config files diff --git a/ctrl/libhicnctrl/src/action.c b/ctrl/libhicnctrl/src/action.c new file mode 100644 index 000000000..c8834d90c --- /dev/null +++ b/ctrl/libhicnctrl/src/action.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file action.c + * \brief Implementation of actions. + */ + +#include <strings.h> + +#include <hicn/ctrl/action.h> + +const char *action_str[] = { +#define _(x) [ACTION_##x] = #x, + foreach_action +#undef _ +}; + +hc_action_t action_from_str(const char *action_str) { +#define _(x) \ + if (strcasecmp(action_str, #x) == 0) \ + return ACTION_##x; \ + else + foreach_action +#undef _ + if (strcasecmp(action_str, "add") == 0) return ACTION_CREATE; + else if (strcasecmp(action_str, "remove") == 0) return ACTION_DELETE; + else return ACTION_UNDEFINED; +} diff --git a/ctrl/libhicnctrl/src/api.c b/ctrl/libhicnctrl/src/api.c index d68dc830e..c93853dd1 100644 --- a/ctrl/libhicnctrl/src/api.c +++ b/ctrl/libhicnctrl/src/api.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2021-2022 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -26,6 +26,13 @@ #include <math.h> // log2 #include "api_private.h" +#include "object_vft.h" +#include "request.h" + +#include <hicn/ctrl/socket.h> +#include "socket_private.h" + +#define ENOIMPL 42 #if 0 /* /!\ Please update constants in public header file upon changes */ @@ -60,685 +67,282 @@ connection_type_from_str(const char * str) * Control Data ******************************************************************************/ -hc_data_t *hc_data_create(size_t in_element_size, size_t out_element_size, - data_callback_t complete_cb) { - hc_data_t *data = malloc(sizeof(hc_data_t)); - if (!data) goto ERR_MALLOC; - - /* FIXME Could be NULL thanks to realloc provided size is 0 */ - data->max_size_log = DEFAULT_SIZE_LOG; - data->in_element_size = in_element_size; - data->out_element_size = out_element_size; - data->size = 0; - data->complete = false; - data->command_id = 0; // TODO this could also be a busy mark in the socket - /* No callback needed in blocking code for instance */ - data->complete_cb = complete_cb; - - data->buffer = malloc((1 << data->max_size_log) * data->out_element_size); - if (!data->buffer) goto ERR_BUFFER; - data->ret = 0; - data->current = 0; - - return data; - -ERR_BUFFER: - hc_data_free(data); -ERR_MALLOC: - return NULL; -} - -void hc_data_free(hc_data_t *data) { - if (data) free(data->buffer); - free(data); -} - -int hc_data_ensure_available(hc_data_t *data, size_t count) { - size_t new_size_log = - (data->size + count - 1 > 0) ? log2(data->size + count - 1) + 1 : 0; - if (new_size_log > data->max_size_log) { - data->max_size_log = new_size_log; - data->buffer = - realloc(data->buffer, (1 << new_size_log) * data->out_element_size); - if (!data->buffer) return -1; - } +/*----------------------------------------------------------------------------* + * Object model + *----------------------------------------------------------------------------*/ - return 0; -} +/*----------------------------------------------------------------------------* + * Entry point + *----------------------------------------------------------------------------*/ -int hc_data_push_many(hc_data_t *data, const void *elements, size_t count) { - if (hc_data_ensure_available(data, count) < 0) return -1; +int hc_sock_on_init(hc_sock_t *s, hc_request_t *request) { + int rc; + ssize_t size; - memcpy(data->buffer + data->size * data->out_element_size, elements, - count * data->out_element_size); - data->size += count; + uint8_t *buffer; - return 0; -} + size = s->ops.prepare(s, request, &buffer); + if (size < 0) goto ERR_PREPARE; -int hc_data_push(hc_data_t *data, const void *element) { - return hc_data_push_many(data, element, 1); -} + if (size == 0) return 1; /* Done */ -/** - * - * NOTE: This function make sure there is enough room available in the data - * structure. - */ -u8 *hc_data_get_next(hc_data_t *data) { - if (hc_data_ensure_available(data, 1) < 0) return NULL; + assert(hc_request_get_data(hc_request_get_current(request))); - return data->buffer + data->size * data->out_element_size; -} + rc = s->ops.send(s, buffer, size); + if (rc < 0) goto ERR_SEND; -int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data) { - data->complete_cb = cb; - data->complete_cb_data = cb_data; return 0; -} -int hc_data_set_complete(hc_data_t *data) { - data->complete = true; - data->ret = 0; - if (data->complete_cb) return data->complete_cb(data, data->complete_cb_data); - return 0; -} - -int hc_data_set_error(hc_data_t *data) { - data->ret = -1; - return 0; +ERR_PREPARE: +ERR_SEND: + return -1; } -int hc_data_reset(hc_data_t *data) { - data->size = 0; - return 0; -} +int hc_sock_on_receive(hc_sock_t *s, size_t count) { + int rc; -static hc_sock_t *_open_module(const char *name, const char *url) { - char complete_name[128]; -#ifdef __APPLE__ - snprintf(complete_name, 128, "%s.dylib", name); -#elif defined(__linux__) - snprintf(complete_name, 128, "%s.so", name); -#else -#error "System not supported for dynamic lynking" + DEBUG("hc_sock_on_receive: calling process with count=%ld", count); + rc = s->ops.process(s, count); + if (rc < 0) goto ERR_PROCESS; + + hc_request_t *request = hc_sock_get_request(s); + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + if (hc_data_is_complete(data)) { + /* + * We only notice a request is complete when trying to send the second + * time... either the state machine reaches the end, or in case of generic + * requests, we mark it as such. + */ +#if 0 + if (hc_request_is_complete(current_request)) { + if (!hc_request_pop(request)) { + /* Free request context */ + /* In case of error, data is NULL */ + // XXX bug if we free request XXX + // hc_sock_free_request(s, request); + if (!hc_request_is_subscription(request)) + hc_request_set_complete(request); + return 1; /* Done */ + } + } else { #endif - - void *handle = 0; - const char *error = 0; - hc_sock_t *(*creator)(const char *) = 0; - hc_sock_t *ret = 0; - - // open module - handle = dlopen(complete_name, RTLD_LAZY); - if (!handle) { - if ((error = dlerror()) != 0) { - ERROR("%s", error); + ON_INIT: + rc = hc_sock_on_init(s, request); + if (rc < 0) goto ERR_INIT; + if (rc == 1) { + if (!hc_request_pop(request)) { + /* Free request context */ + /* In case of error, data is NULL */ + // hc_sock_free_request(s, request); + if (!hc_request_is_subscription(request)) + hc_request_set_complete(request); + return 1; /* Done */ + } + goto ON_INIT; } - return 0; - } - - // get factory method - creator = - (hc_sock_t * (*)(const char *)) dlsym(handle, "_hc_sock_create_url"); - if (!creator) { - if ((error = dlerror()) != 0) { - ERROR("%s", error); +#if 0 } - return 0; - } - - ret = (*creator)(NULL); - ret->handle = handle; - - return ret; -} - -hc_sock_t *hc_sock_create_forwarder_url(forwarder_type_t forwarder, - const char *url) { - switch (forwarder) { - case HICNLIGHT: - return _open_module("hicnlightctrl_module", url); - case HICNLIGHT_NG: - return _open_module("hicnlightngctrl_module", url); - case VPP: - return _open_module("vppctrl_module", url); - default: - return NULL; - } -} - -#ifdef ANDROID -// In android we do not load a module at runtime -// but we link the hicnlight implmentation directly -// to the main library -extern hc_sock_t *_hc_sock_create_url(const char *url); #endif - -hc_sock_t *hc_sock_create_forwarder(forwarder_type_t forwarder) { -#ifdef ANDROID - assert(forwarder == HICNLIGHT_NG); - hc_sock_t *ret = _hc_sock_create_url(NULL); - ret->handle = NULL; - return ret; -#else - return hc_sock_create_forwarder_url(forwarder, NULL); -#endif -} - -hc_sock_t *hc_sock_create(void) { - return hc_sock_create_forwarder(HICNLIGHT_NG); -} - -void hc_sock_free(hc_sock_t *s) { - void *handle = s->handle; - s->hc_sock_free(s); - - if (handle) { - dlclose(handle); - } -} - -int hc_sock_get_next_seq(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } - -int hc_sock_set_nonblocking(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } - -int hc_sock_get_fd(hc_sock_t *s) { return s->hc_sock_get_fd(s); } - -int hc_sock_connect(hc_sock_t *s) { return s->hc_sock_connect(s); } - -int hc_sock_get_available(hc_sock_t *s, u8 **buffer, size_t *size) { - return s->hc_sock_get_available(s, buffer, size); -} - -int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq) { - return s->hc_sock_send(s, msg, msglen, seq); -} - -int hc_sock_recv(hc_sock_t *s) { return s->hc_sock_recv(s); } - -int hc_sock_process(hc_sock_t *s, hc_data_t **data) { - return s->hc_sock_process(s, data); -} - -int hc_sock_callback(hc_sock_t *s, hc_data_t **data) { - return s->hc_sock_callback(s, data); -} - -int hc_sock_reset(hc_sock_t *s) { return s->hc_sock_reset(s); } - -void hc_sock_increment_woff(hc_sock_t *s, size_t bytes) { - s->hc_sock_increment_woff(s, bytes); -} - -int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, - data_callback_t complete_cb, void *complete_cb_data) { - return s->hc_sock_prepare_send(s, result, complete_cb, complete_cb_data); -} - -int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms) { - return s->hc_sock_set_recv_timeout_ms(s, timeout_ms); -} - -/*----------------------------------------------------------------------------* - * LISTENER - *----------------------------------------------------------------------------*/ - -int hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_create(s, listener); -} - -hc_result_t *hc_listener_create_conf(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_create_conf(s, listener); -} - -int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found) { - return s->hc_listener_get(s, listener, listener_found); -} - -int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - return s->hc_listener_delete(s, listener); -} - -int hc_listener_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_listener_list(s, pdata); -} - -GENERATE_FIND(listener); - -/* LISTENER VALIDATE */ - -int hc_listener_validate(const hc_listener_t *listener) { - if (!IS_VALID_NAME(listener->name)) { - ERROR("[hc_listener_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_INTERFACE_NAME(listener->interface_name)) { - ERROR("[hc_listener_validate] Invalid interface_name specified"); - return -1; - } - if (!IS_VALID_TYPE(listener->type)) { - ERROR("[hc_listener_validate] Invalid type specified"); - return -1; - } - if (!IS_VALID_FAMILY(listener->family)) { - ERROR("[hc_listener_validate] Invalid family specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&listener->local_addr, listener->family)) { - ERROR("[hc_listener_validate] Invalid local_addr specified"); - return -1; - } - if (!IS_VALID_PORT(listener->local_port)) { - ERROR("[hc_listener_validate] Invalid local_port specified"); - return -1; } + return 0; /* Continue processing */ - return 0; +ERR_INIT: +ERR_PROCESS: + return -1; } -/* LISTENER CMP */ - -int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2) { +// -1 error +// 0 = request is not yet complete +// 1 request is complete +int hc_sock_receive(hc_sock_t *s, hc_data_t **pdata) { int rc; + DEBUG("Waiting for data..."); + rc = s->ops.recv(s); + if (rc < 0) return -1; - rc = INT_CMP(l1->type, l2->type); - if (rc != 0) return rc; - - rc = INT_CMP(l1->family, l2->family); - if (rc != 0) return rc; - - rc = strncmp(l1->interface_name, l2->interface_name, INTERFACE_LEN); - if (rc != 0) return rc; - - rc = ip_address_cmp(&l1->local_addr, &l2->local_addr, l1->family); - if (rc != 0) return rc; - - rc = INT_CMP(l1->local_port, l2->local_port); - if (rc != 0) return rc; - - return rc; -} - -/* LISTENER SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_listener_snprintf(char *s, size_t size, hc_listener_t *listener) { - char local[MAXSZ_URL]; - int rc; - rc = url_snprintf(local, MAXSZ_URL, listener->family, &listener->local_addr, - listener->local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_listener_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - - return snprintf(s, size, "%s %s %s interface=%s", listener->name, local, - face_type_str(listener->type), listener->interface_name); -} + rc = hc_sock_on_receive(s, 0); + if (rc < 0) return -1; -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -int hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { - return s->hc_connection_create(s, connection); -} - -hc_result_t *hc_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - return s->hc_connection_create_conf(s, connection); -} - -int hc_connection_get(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found) { - return s->hc_connection_get(s, connection, connection_found); -} - -int hc_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - return s->hc_connection_update_by_id(s, hc_connection_id, connection); -} - -int hc_connection_update(hc_sock_t *s, hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - return s->hc_connection_update(s, connection_current, connection_updated); -} - -int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - return s->hc_connection_delete(s, connection); -} - -hc_result_t *hc_connection_delete_conf(hc_sock_t *s, - hc_connection_t *connection) { - return s->hc_connection_delete_conf(s, connection); -} - -int hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_connection_list(s, pdata); -} - -int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state) { - return s->hc_connection_set_admin_state(s, conn_id_or_name, state); -} + hc_request_t *request = hc_sock_get_request(s); + /* + * If notification, display it, ideally callback. What to do with + * allocated data ? + */ + // XXX problem we display object on ACK... but not subsequent + // notifications + // XXX we should rely on callback here in addition, even for a synchronous + // request + if (hc_request_is_subscription(request)) { + hc_data_t *data = hc_request_get_data(request); + assert(data); + hc_object_t *obj = (hc_object_t *)hc_data_get_buffer(data); + char buf[MAXSZ_HC_OBJECT]; + hc_object_type_t object_type = hc_data_get_object_type(data); + if (hc_object_snprintf(buf, sizeof(buf), object_type, obj) > 0) { + ; + INFO("%s %s", object_type_str(object_type), buf); + } + } -#ifdef WITH_POLICY -int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return s->hc_connection_set_priority(s, conn_id_or_name, priority); -} + // XXX need same for async + if (rc != 1) return 0; -int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return s->hc_connection_set_tags(s, conn_id_or_name, tags); + hc_request_t *current_request = hc_request_get_current(request); + if (hc_request_is_complete(current_request)) { + /* We either return the (last) allocated data, or free it */ + if (pdata) { + *pdata = hc_request_get_data(request); + } else { + hc_request_reset_data(request); + } + hc_request_on_complete(request); + // hc_sock_free_request(s, request); + } + return 1; } -#endif // WITH_POLICY - -GENERATE_FIND(connection); -/* CONNECTION VALIDATE */ +int hc_sock_receive_all(hc_sock_t *s, hc_data_t **pdata) { + for (;;) { + int rc = hc_sock_receive(s, pdata); + if (rc < 0) return -1; -int hc_connection_validate(const hc_connection_t *connection) { - if (connection->name[0] != '\0' && !IS_VALID_NAME(connection->name)) { - ERROR("[hc_connection_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_INTERFACE_NAME(connection->interface_name)) { - ERROR("[hc_connection_validate] Invalid interface_name specified"); - return -1; - } - if (!IS_VALID_TYPE(connection->type)) { - ERROR("[hc_connection_validate] Invalid type specified"); - return -1; - } - if (!IS_VALID_FAMILY(connection->family)) { - ERROR("[hc_connection_validate] Invalid family specified"); - return -1; + /* If request is complete, stop */ + if (rc == 1) break; } - if (!IS_VALID_ADDRESS(&connection->local_addr, connection->family)) { - ERROR("[hc_connection_validate] Invalid local_addr specified"); - return -1; - } - if (!IS_VALID_PORT(connection->local_port)) { - ERROR("[hc_connection_validate] Invalid local_port specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&connection->remote_addr, connection->family)) { - ERROR("[hc_connection_validate] Invalid remote_addr specified"); - return -1; - } - if (!IS_VALID_PORT(connection->remote_port)) { - ERROR("[hc_connection_validate] Invalid remote_port specified"); - return -1; - } - return 0; } -/* CONNECTION CMP */ - -/* - * hICN light uses ports even for hICN connections, but their value is ignored. - * As connections are specific to hicn-light, we can safely use IP and ports for - * comparison independently of the face type. +/** + * @return <0 in case of error + * -1 : validation error + * -2 : error during send + * -3 : error receiving or parsing + * + * If the caller provider a non-NULL hc_data_t pointer to receive results + * back, it is responsible for freeing it. */ -int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2) { - int rc; - - rc = INT_CMP(c1->type, c2->type); - if (rc != 0) return rc; - - rc = INT_CMP(c1->family, c2->family); - if (rc != 0) return rc; - - rc = strncmp(c1->interface_name, c2->interface_name, INTERFACE_LEN); - if (rc != 0) return rc; - - rc = ip_address_cmp(&c1->local_addr, &c2->local_addr, c1->family); - if (rc != 0) return rc; - - rc = INT_CMP(c1->local_port, c2->local_port); - if (rc != 0) return rc; - - rc = ip_address_cmp(&c1->remote_addr, &c2->remote_addr, c1->family); - if (rc != 0) return rc; +int _hc_execute(hc_sock_t *s, hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_result_callback_t callback, + void *callback_data, hc_data_t **pdata) { + assert(!(hc_sock_is_async(s) && pdata)); - rc = INT_CMP(c1->remote_port, c2->remote_port); - if (rc != 0) return rc; - - return rc; -} - -/* CONNECTION SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_connection_snprintf(char *s, size_t size, - const hc_connection_t *connection) { - char local[MAXSZ_URL]; - char remote[MAXSZ_URL]; - int rc; - - // assert(connection->connection_state) - if (strcmp(connection->name, "SELF") == 0) { - return snprintf(s, size, "%s", connection->name); + if (hc_sock_is_async(s) && !s->ops.get_fd) { + return -1; /* No async support */ } - rc = url_snprintf(local, MAXSZ_URL, connection->family, - &connection->local_addr, connection->local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = url_snprintf(remote, MAXSZ_URL, connection->family, - &connection->remote_addr, connection->remote_port); - if (rc >= MAXSZ_URL) - WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - - return snprintf(s, size, "%s %s %s %s", connection->name, local, remote, - face_type_str(connection->type)); -} - -int hc_face_create(hc_sock_t *s, hc_face_t *face) { - return s->hc_face_create(s, face); -} - -int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_face_t **face_found) { - return s->hc_face_get(s, face, face_found); -} - -int hc_face_delete(hc_sock_t *s, hc_face_t *face, uint8_t delete_listener) { - return s->hc_face_delete(s, face, delete_listener); -} - -int hc_face_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_face_list(s, pdata); -} - -int hc_face_list_async(hc_sock_t *s) { return s->hc_face_list_async(s); } + // XXX no need to pass pdata to the request + // XXX sync socket, no multiplexed requests, no notifications + /* + * The request will contain all state needed to identify and demultiplex + * replies and notifications arriving on the socket. We assume there is at + * most a single request/reply in progress for a given request, and that + * requests involving multiple queries will run them sequentially. The use + * of a sequence number that is transported by the requests and reply is + * thus sufficient to disambiguate them. + */ + hc_request_t *request = hc_sock_create_request(s, action, object_type, object, + callback, callback_data); + if (!request) { + goto ERR_REQUEST; + } -int hc_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state) { - return s->hc_face_set_admin_state(s, conn_id_or_name, state); -} + if (hc_request_requires_object(request)) { + if (hc_object_is_empty(object) || + hc_object_validate(object_type, object, true) < 0) { + goto ERR_VALIDATE; + } + } else { + if (object && !hc_object_is_empty(object)) { + goto ERR_CHECK; + } + } -#ifdef WITH_POLICY -int hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return s->hc_face_set_priority(s, conn_id_or_name, priority); -} + /* Workaround for non-fd based modules */ + if (s->ops.prepare && s->ops.send && s->ops.recv && s->ops.process) { + if (hc_sock_on_init(s, request) < 0) goto ERR_INIT; -int hc_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return s->hc_face_set_tags(s, conn_id_or_name, tags); -} -#endif /* WITH_POLICY */ + if (hc_sock_is_async(s)) return 0; -/* /!\ Please update constants in header file upon changes */ -int hc_face_snprintf(char *s, size_t size, hc_face_t *face) { - /* URLs are also big enough to contain IP addresses in the hICN case */ - char local[MAXSZ_URL]; - char remote[MAXSZ_URL]; -#ifdef WITH_POLICY - char tags[MAXSZ_POLICY_TAGS]; -#endif /* WITH_POLICY */ - int rc; + /* Case in which no reply is expected */ + if (!pdata) return 0; - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_HICN_LISTENER: - rc = ip_address_snprintf(local, MAXSZ_URL, &face->face.local_addr, - face->face.family); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = ip_address_snprintf(remote, MAXSZ_URL, &face->face.remote_addr, - face->face.family); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - break; - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - rc = url_snprintf(local, MAXSZ_URL, face->face.family, - &face->face.local_addr, face->face.local_port); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - rc = url_snprintf(remote, MAXSZ_URL, face->face.family, - &face->face.remote_addr, face->face.remote_port); - if (rc >= MAXSZ_URL) - WARN("[hc_face_snprintf] Unexpected truncation of URL string"); - if (rc < 0) return rc; - break; - default: - return -1; + if (hc_sock_receive_all(s, pdata) < 0) goto ERR_RECV; + } else if (s->ops.prepare) { + // hc_data_t *data = hc_data_create(OBJECT_TYPE_LISTENER); + // hc_data_push(data, NULL); + // No nested requests for now... + ssize_t size = s->ops.prepare(s, request, NULL); + _ASSERT(size == 0); /* Done */ + if (hc_request_is_complete(request)) { + if (pdata) { + *pdata = hc_request_get_data(request); + } else { + hc_request_reset_data(request); + } + hc_request_on_complete(request); + } } - // [#ID NAME] TYPE LOCAL_URL REMOTE_URL STATE/ADMIN_STATE (TAGS) -#ifdef WITH_POLICY - rc = policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->face.tags); - if (rc >= MAXSZ_POLICY_TAGS) - WARN("[hc_face_snprintf] Unexpected truncation of policy tags string"); - if (rc < 0) return rc; - - return snprintf( - s, size, "[#%d %s] %s %s %s %s %s/%s [%d] (%s)", face->id, face->name, - face->face.netdevice.index != NETDEVICE_UNDEFINED_INDEX - ? face->face.netdevice.name - : "*", - face_type_str(face->face.type), local, remote, - face_state_str(face->face.state), face_state_str(face->face.admin_state), - face->face.priority, tags); -#else - return snprintf(s, size, "[#%d %s] %s %s %s %s %s/%s", face->id, face->name, - face->face.netdevice.index != NETDEVICE_UNDEFINED_INDEX - ? face->face.netdevice.name - : "*", - face_type_str(face->face.type), local, remote, - face_state_str(face->face.state), - face_state_str(face->face.admin_state)); -#endif /* WITH_POLICY */ -} - -/*----------------------------------------------------------------------------* - * ROUTE - *----------------------------------------------------------------------------*/ - -int hc_route_create(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_create(s, route); -} + return 0; -hc_result_t *hc_route_create_conf(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_create_conf(s, route); +ERR_RECV: + hc_request_reset_data(request); +ERR_INIT: + hc_sock_free_request(s, request, true); +ERR_CHECK: +ERR_REQUEST: +ERR_VALIDATE: + if (pdata) *pdata = NULL; + return -1; } -int hc_route_delete(hc_sock_t *s, hc_route_t *route) { - return s->hc_route_delete(s, route); +int hc_execute(hc_sock_t *s, hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_data_t **pdata) { + return _hc_execute(s, action, object_type, object, NULL, NULL, pdata); } -int hc_route_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_route_list(s, pdata); +int hc_execute_async(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, + hc_result_callback_t callback, void *callback_data) { + return _hc_execute(s, action, object_type, object, callback, callback_data, + NULL); } -int hc_route_list_async(hc_sock_t *s) { return s->hc_route_list_async(s); } - -/* ROUTE SNPRINTF */ - -/* /!\ Please update constants in header file upon changes */ -int hc_route_snprintf(char *s, size_t size, hc_route_t *route) { - /* interface cost prefix length */ - - char prefix[MAXSZ_IP_ADDRESS]; - int rc; - - rc = ip_address_snprintf(prefix, MAXSZ_IP_ADDRESS, &route->remote_addr, - route->family); - if (rc >= MAXSZ_IP_ADDRESS) - ; - if (rc < 0) return rc; - - return snprintf(s, size, "%s %*d %s %*d conn_id=%d", route->name, MAXSZ_COST, - route->cost, prefix, MAXSZ_LEN, route->len, route->face_id); -} +/* This function has to be called after the first execute until data and + * request are complete */ +// execute is just setting things up so that we can keep on calling this +// function repeatedly until completion. +// +// in the caller, we don't know how much we will receive in advance... so in +// asio for instance, we will use async_receive rather than async_read. +// XXX the question remains about the buffers... -int hc_route_validate(const hc_route_t *route) { - if (!IS_VALID_ID(route->connection_id)) { - ERROR("[hc_route_validate] Invalid connection id"); - return -1; - } - if (route->name[0] == '\0') { - if (!IS_VALID_FACE_ID(route->face_id)) { - ERROR("[hc_route_validate] Invalid face_id"); - return -1; - } - } else if (!IS_VALID_NAME(route->name) && !IS_VALID_STR_ID(route->name)) { - ERROR("[hc_route_validate] Invalid name specified"); - return -1; - } - if (!IS_VALID_FAMILY(route->family)) { - ERROR("[hc_route_validate] Invalid family specified"); - return -1; - } - if (!IS_VALID_ADDRESS(&route->remote_addr, route->family)) { - ERROR("[hc_route_validate] Invalid remote_addr specified"); - return -1; - } - if (!IS_VALID_ROUTE_COST(route->cost)) { - ERROR("[hc_route_validate] Invalid cost"); - return -1; - } - if (!IS_VALID_PREFIX_LEN(route->len)) { - ERROR("[hc_route_validate] Invalid len"); - return -1; - } +/* + * request -> write command + * + * SYNC : hc_data_t + * ASYNC : provide socket-level callback + * + * socket available -> read -> parse -> populate data + * data complete -> + */ - return 0; -} +/****************************************************************************** + * OBJECT-SPECIFIC FUNCTIONS (backwards compatibility) + ******************************************************************************/ /*----------------------------------------------------------------------------* * FACE + * + * This is an abstraction provided for when the module does not implement + *it. Alternative is to move it to hicn light *----------------------------------------------------------------------------*/ +#if 0 + /* FACE -> LISTENER */ -int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener) { - const face_t *f = &face->face; - - switch (f->type) { - case FACE_TYPE_HICN_LISTENER: - break; - case FACE_TYPE_TCP_LISTENER: - break; - case FACE_TYPE_UDP_LISTENER: - break; - default: - return -1; - } - return -1; /* XXX Not implemented */ -} /* LISTENER -> FACE */ @@ -748,225 +352,10 @@ int hc_listener_to_face(const hc_listener_t *listener, hc_face_t *face) { /* FACE -> CONNECTION */ -int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, - bool generate_name) { - int rc; - - const face_t *f = &face->face; - switch (f->type) { - case FACE_TYPE_HICN: - *connection = (hc_connection_t){ - .type = FACE_TYPE_HICN, - .family = f->family, - .local_addr = f->local_addr, - .local_port = 0, - .remote_addr = f->remote_addr, - .remote_port = 0, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", - f->netdevice.name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of symbolic " - "name string"); - break; - case FACE_TYPE_TCP: - *connection = (hc_connection_t){ - .type = FACE_TYPE_TCP, - .family = f->family, - .local_addr = f->local_addr, - .local_port = f->local_port, - .remote_addr = f->remote_addr, - .remote_port = f->remote_port, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - if (generate_name) { - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "tcp%u", RANDBYTE()); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of " - "symbolic name string"); - } else { - memset(connection->name, 0, SYMBOLIC_NAME_LEN); - } - break; - case FACE_TYPE_UDP: - *connection = (hc_connection_t){ - .type = FACE_TYPE_UDP, - .family = f->family, - .local_addr = f->local_addr, - .local_port = f->local_port, - .remote_addr = f->remote_addr, - .remote_port = f->remote_port, - .admin_state = f->admin_state, - .state = f->state, -#ifdef WITH_POLICY - .priority = f->priority, - .tags = f->tags, -#endif /* WITH_POLICY */ - }; - if (generate_name) { - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "udp%u", RANDBYTE()); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_face_to_connection] Unexpected truncation of " - "symbolic name string"); - } else { - memset(connection->name, 0, SYMBOLIC_NAME_LEN); - } - snprintf(connection->interface_name, INTERFACE_LEN, "%s", - f->netdevice.name); - break; - default: - return -1; - } - - connection->id = face->id; - rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", - f->netdevice.name); - if (rc >= INTERFACE_LEN) - WARN( - "hc_face_to_connection] Unexpected truncation of interface name " - "string"); - - return 0; -} /* CONNECTION -> FACE */ - -int hc_connection_to_face(const hc_connection_t *connection, hc_face_t *face) { - int rc; - switch (connection->type) { - case FACE_TYPE_TCP: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_TCP, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - .remote_addr = connection->remote_addr, - .remote_port = connection->remote_port, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - case FACE_TYPE_UDP: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_UDP, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - .remote_addr = connection->remote_addr, - .remote_port = connection->remote_port, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - case FACE_TYPE_HICN: - *face = (hc_face_t){ - .id = connection->id, - .face = - { - .type = FACE_TYPE_HICN, - .family = connection->family, - .netdevice.index = NETDEVICE_UNDEFINED_INDEX, // XXX - .local_addr = connection->local_addr, - .remote_addr = connection->remote_addr, - .admin_state = connection->admin_state, - .state = connection->state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }, - }; - break; - default: - return -1; - } - face->face.netdevice.name[0] = '\0'; - face->face.netdevice.index = 0; - rc = snprintf(face->name, SYMBOLIC_NAME_LEN, "%s", connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_to_face] Unexpected truncation of symbolic name " - "string"); - rc = snprintf(face->face.netdevice.name, INTERFACE_LEN, "%s", - connection->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_to_face] Unexpected truncation of interface name " - "string"); - netdevice_update_index(&face->face.netdevice); - return 0; -} - /* CONNECTION -> LISTENER */ -int hc_connection_to_local_listener(const hc_connection_t *connection, - hc_listener_t *listener) { - int rc; - - face_type_t listener_type; - switch (connection->type) { - case FACE_TYPE_UDP: - listener_type = FACE_TYPE_UDP_LISTENER; - break; - case FACE_TYPE_TCP: - listener_type = FACE_TYPE_TCP_LISTENER; - break; - default: - return -1; - } - - *listener = (hc_listener_t){ - .id = ~0, - .type = listener_type, - .family = connection->family, - .local_addr = connection->local_addr, - .local_port = connection->local_port, - }; - rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "lst%u", - RANDBYTE()); // generate name - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_to_local_listener] Unexpected truncation of " - "symbolic name string"); - rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", - connection->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_to_local_listener] Unexpected truncation of " - "interface name string"); - - return 0; -} /*----------------------------------------------------------------------------* * Punting @@ -1013,7 +402,7 @@ int hc_punting_cmp(const hc_punting_t *p1, const hc_punting_t *p2) { rc = INT_CMP(p1->family, p2->family); if (rc != 0) return rc; - rc = ip_address_cmp(&p1->prefix, &p2->prefix, p1->family); + rc = ip_address_cmp(&p1->prefix, &p2->prefix); if (rc != 0) return rc; rc = INT_CMP(p1->prefix_len, p2->prefix_len); @@ -1022,10 +411,12 @@ int hc_punting_cmp(const hc_punting_t *p1, const hc_punting_t *p2) { return rc; } +#if 0 int hc_punting_parse(void *in, hc_punting_t *punting) { ERROR("hc_punting_parse not (yet) implemented."); return -1; } +#endif int hc_punting_snprintf(char *s, size_t size, hc_punting_t *punting) { ERROR("hc_punting_snprintf not (yet) implemented."); @@ -1083,36 +474,6 @@ int hc_stats_snprintf(char *s, size_t size, const hicn_light_stats_t *stats) { } /*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -int hc_strategy_list(hc_sock_t *s, hc_data_t **data) { - return s->hc_strategy_list(s, data); -} - -int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_set(s, strategy); -} - -int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_add_local_prefix(s, strategy); -} - -hc_result_t *hc_strategy_set_conf(hc_sock_t *s, hc_strategy_t *strategy) { - return s->hc_strategy_set_conf(s, strategy); -} - -hc_result_t *hc_strategy_add_local_prefix_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - return s->hc_strategy_add_local_prefix_conf(s, strategy); -} - -/* /!\ Please update constants in header file upon changes */ -int hc_strategy_snprintf(char *s, size_t size, hc_strategy_t *strategy) { - return snprintf(s, size, "%s", strategy->name); -} - -/*----------------------------------------------------------------------------* * WLDR *----------------------------------------------------------------------------*/ @@ -1146,67 +507,55 @@ int hc_mapme_send_update(hc_sock_t *s, hc_mapme_t *mapme) { * Policy *----------------------------------------------------------------------------*/ -#ifdef WITH_POLICY /* POLICY SNPRINTF */ /* /!\ Please update constants in header file upon changes */ int hc_policy_snprintf(char *s, size_t size, hc_policy_t *policy) { return 0; } -int hc_policy_validate(const hc_policy_t *policy) { +int hc_policy_validate(const hc_policy_t *policy, bool allow_partial) { if (!IS_VALID_FAMILY(policy->family)) return -1; return 0; } -#endif /* WITH_POLICY */ +#endif /*----------------------------------------------------------------------------* - * SUBSCRIPTION + * VFT *----------------------------------------------------------------------------*/ -int hc_subscription_create(hc_sock_t *s, hc_subscription_t *subscription) { - return s->hc_subscription_create(s, subscription); -} - -int hc_subscription_delete(hc_sock_t *s, hc_subscription_t *subscription) { - return s->hc_subscription_delete(s, subscription); -} -hc_result_t *hc_subscription_create_conf(hc_sock_t *s, - hc_subscription_t *subscription) { - return s->hc_subscription_create_conf(s, subscription); -} - -hc_result_t *hc_subscription_delete_conf(hc_sock_t *s, - hc_subscription_t *subscription) { - return s->hc_subscription_delete_conf(s, subscription); -} - -/*----------------------------------------------------------------------------* - * STATISTICS - *----------------------------------------------------------------------------*/ -int hc_stats_get(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_stats_get(s, pdata); +int hc_object_create(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object) { + return hc_execute(s, ACTION_CREATE, object_type, object, NULL); } -int hc_stats_list(hc_sock_t *s, hc_data_t **pdata) { - return s->hc_stats_list(s, pdata); +int hc_object_get(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object, hc_object_t **found) { + return hc_execute(s, ACTION_GET, object_type, object, NULL); } -/*----------------------------------------------------------------------------* - * Result - *----------------------------------------------------------------------------*/ - -hc_msg_t *hc_result_get_msg(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_msg(result); +int hc_object_find(hc_sock_t *s, hc_object_type_t object_type, hc_data_t *data, + const hc_object_t *element, hc_object_t **found) { +// XXX NOT IMPLEMENTED +#if 0 + foreach_type(hc_object_t, x, data) { + if (hc_object_cmp(x, element) == 0) { + *found = x; + return 0; + } + }; +#endif + *found = NULL; /* this is optional */ + return 0; } -int hc_result_get_cmd_id(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_cmd_id(result); +int hc_object_delete(hc_sock_t *s, hc_object_type_t object_type, + hc_object_t *object) { + return hc_execute(s, ACTION_DELETE, object_type, object, NULL); } -bool hc_result_get_success(hc_sock_t *s, hc_result_t *result) { - return s->hc_result_get_success(result); +int hc_object_list(hc_sock_t *s, hc_object_type_t object_type, + hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, object_type, NULL, pdata); } - -void hc_result_free(hc_result_t *result) { free(result); } diff --git a/ctrl/libhicnctrl/src/api_private.h b/ctrl/libhicnctrl/src/api_private.h index c708e1eb5..53be809da 100644 --- a/ctrl/libhicnctrl/src/api_private.h +++ b/ctrl/libhicnctrl/src/api_private.h @@ -20,30 +20,22 @@ #include <hicn/util/token.h> #include <hicn/util/log.h> #include <hicn/util/map.h> +#ifndef HICN_VPP_PLUGIN #include <hicn/util/sstrncpy.h> +#endif #include <hicn/validation.h> #include <ctype.h> -#define INT_CMP(x, y) ((x > y) ? 1 : (x < y) ? -1 : 0) #define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) -#if 0 -#ifdef __APPLE__ -#define RANDBYTE() (u8)(arc4random() & 0xFF) -#else -#define RANDBYTE() (u8)(random() & 0xFF) -#endif -#endif -#define RANDBYTE() (u8)(rand() & 0xFF) - /* * Input validation */ -static inline bool IS_VALID_ADDRESS(const ip_address_t *addr, int family) { +static inline bool IS_VALID_ADDRESS(const hicn_ip_address_t *addr, int family) { char addr_str[INET6_ADDRSTRLEN]; - return !ip_address_empty(addr) && - ip_address_ntop(addr, addr_str, INET6_ADDRSTRLEN, family) >= 0; + return !hicn_ip_address_empty(addr) && + hicn_ip_address_ntop(addr, addr_str, INET6_ADDRSTRLEN, family) >= 0; } static inline bool IS_VALID_PREFIX_LEN(u8 len) { @@ -82,7 +74,6 @@ typedef struct hc_sock_impl_s hc_sock_impl_t; int hc_data_ensure_available(hc_data_t *data, size_t count); u8 *hc_data_get_next(hc_data_t *data); -int hc_data_set_error(hc_data_t *data); int hc_listener_to_face(const hc_listener_t *listener, hc_face_t *face); @@ -96,178 +87,4 @@ int hc_connection_to_local_listener(const hc_connection_t *connection, int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, bool generate_name); -struct hc_sock_s { - int (*hc_sock_get_next_seq)(hc_sock_t *s); - int (*hc_sock_set_nonblocking)(hc_sock_t *s); - int (*hc_sock_get_fd)(hc_sock_t *s); - int (*hc_sock_connect)(hc_sock_t *s); - int (*hc_sock_get_available)(hc_sock_t *s, u8 **buffer, size_t *size); - int (*hc_sock_send)(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq); - int (*hc_sock_recv)(hc_sock_t *s); - int (*hc_sock_process)(hc_sock_t *s, hc_data_t **data); - int (*hc_sock_callback)(hc_sock_t *s, hc_data_t **data); - int (*hc_sock_reset)(hc_sock_t *s); - void (*hc_sock_free)(hc_sock_t *s); - void (*hc_sock_increment_woff)(hc_sock_t *s, size_t bytes); - int (*hc_sock_prepare_send)(hc_sock_t *s, hc_result_t *result, - data_callback_t complete_cb, - void *complete_cb_data); - int (*hc_sock_set_recv_timeout_ms)(hc_sock_t *s, long timeout_ms); - int (*hc_listener_create)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_create_async)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_get)(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found); - int (*hc_listener_delete)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_delete_async)(hc_sock_t *s, hc_listener_t *listener); - int (*hc_listener_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_listener_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_listener_validate)(const hc_listener_t *listener); - int (*hc_listener_cmp)(const hc_listener_t *l1, const hc_listener_t *l2); - int (*hc_listener_parse)(void *in, hc_listener_t *listener); - - int (*hc_connection_create)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_create_async)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_get)(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found); - int (*hc_connection_update_by_id)(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection); - int (*hc_connection_update)(hc_sock_t *s, hc_connection_t *connection_current, - hc_connection_t *connection_updated); - int (*hc_connection_delete)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_delete_async)(hc_sock_t *s, hc_connection_t *connection); - int (*hc_connection_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_connection_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_connection_validate)(const hc_connection_t *connection); - int (*hc_connection_cmp)(const hc_connection_t *c1, - const hc_connection_t *c2); - int (*hc_connection_parse)(void *in, hc_connection_t *connection); - int (*hc_connection_set_admin_state)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - int (*hc_connection_set_admin_state_async)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - -#ifdef WITH_POLICY - int (*hc_connection_set_priority)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_connection_set_priority_async)(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority); - int (*hc_connection_set_tags)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); - int (*hc_connection_set_tags_async)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); -#endif // WITH_POLICY - - int (*hc_connection_snprintf)(char *s, size_t size, - const hc_connection_t *connection); - - int (*hc_face_create)(hc_sock_t *s, hc_face_t *face); - int (*hc_face_get)(hc_sock_t *s, hc_face_t *face, hc_face_t **face_found); - int (*hc_face_delete)(hc_sock_t *s, hc_face_t *face, uint8_t delete_listener); - int (*hc_face_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_face_list_async)(hc_sock_t *s); - int (*hc_face_set_admin_state)(hc_sock_t *s, const char *conn_id_or_name, - face_state_t state); - int (*hc_face_set_admin_state_async)(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state); - -#ifdef WITH_POLICY - int (*hc_face_set_priority)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_face_set_priority_async)(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority); - int (*hc_face_set_tags)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); - int (*hc_face_set_tags_async)(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags); -#endif // WITH_POLICY - - int (*hc_face_snprintf)(char *s, size_t size, hc_face_t *face); - - int (*hc_route_parse)(void *in, hc_route_t *route); - int (*hc_route_create)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_create_async)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_delete)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_delete_async)(hc_sock_t *s, hc_route_t *route); - int (*hc_route_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_route_list_async)(hc_sock_t *s); - - int (*hc_punting_create)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_create_async)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_get)(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found); - int (*hc_punting_delete)(hc_sock_t *s, hc_punting_t *punting); - int (*hc_punting_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_punting_validate)(const hc_punting_t *punting); - int (*hc_punting_cmp)(const hc_punting_t *c1, const hc_punting_t *c2); - int (*hc_punting_parse)(void *in, hc_punting_t *punting); - - int (*hc_cache_parse)(void *in, hc_cache_info_t *cache_info); - int (*hc_cache_set_store)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_set_serve)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_clear)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_cache_set_store_async)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_set_serve_async)(hc_sock_t *s, hc_cache_t *cache); - int (*hc_cache_snprintf)(char *s, size_t size, - const hc_cache_info_t *cache_info); - - int (*hc_strategy_list)(hc_sock_t *s, hc_data_t **data); - int (*hc_strategy_snprintf)(char *s, size_t size, hc_strategy_t *strategy); - int (*hc_strategy_set)(hc_sock_t *s, hc_strategy_t *strategy); - int (*hc_strategy_add_local_prefix)(hc_sock_t *s, hc_strategy_t *strategy); - - int (*hc_wldr_set)(hc_sock_t *s /* XXX */); - - int (*hc_mapme_set)(hc_sock_t *s, int enabled); - int (*hc_mapme_set_discovery)(hc_sock_t *s, int enabled); - int (*hc_mapme_set_timescale)(hc_sock_t *s, uint32_t timescale); - int (*hc_mapme_set_retx)(hc_sock_t *s, uint32_t timescale); - int (*hc_mapme_send_update)(hc_sock_t *s, hc_mapme_t *mapme); - -#ifdef WITH_POLICY - int (*hc_policy_parse)(void *in, hc_policy_t *policy); - int (*hc_policy_create)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_create_async)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_delete)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_delete_async)(hc_sock_t *s, hc_policy_t *policy); - int (*hc_policy_list)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_policy_list_async)(hc_sock_t *s, hc_data_t **pdata); - int (*hc_policy_snprintf)(char *s, size_t size, hc_policy_t *policy); -#endif // WITH_POLICY - - int (*hc_subscription_create)(hc_sock_t *s, hc_subscription_t *subscription); - int (*hc_subscription_delete)(hc_sock_t *s, hc_subscription_t *subscription); - - int (*hc_stats_get)(hc_sock_t *s, hc_data_t **data); - int (*hc_stats_list)(hc_sock_t *s, hc_data_t **data); - - hc_result_t *(*hc_listener_create_conf)(hc_sock_t *s, - hc_listener_t *listener); - hc_result_t *(*hc_listener_list_conf)(hc_sock_t *s, hc_data_t **pdata); - hc_result_t *(*hc_connection_create_conf)(hc_sock_t *s, - hc_connection_t *connection); - hc_result_t *(*hc_connection_delete_conf)(hc_sock_t *s, - hc_connection_t *connection); - hc_result_t *(*hc_route_create_conf)(hc_sock_t *s, hc_route_t *route); - hc_result_t *(*hc_strategy_set_conf)(hc_sock_t *s, hc_strategy_t *strategy); - hc_result_t *(*hc_strategy_add_local_prefix_conf)(hc_sock_t *s, - hc_strategy_t *strategy); - hc_result_t *(*hc_subscription_create_conf)(hc_sock_t *s, - hc_subscription_t *subscription); - hc_result_t *(*hc_subscription_delete_conf)(hc_sock_t *s, - hc_subscription_t *subscription); - - hc_msg_t *(*hc_result_get_msg)(hc_result_t *result); - bool (*hc_result_get_success)(hc_result_t *result); - int (*hc_result_get_cmd_id)(hc_result_t *result); - void (*hc_result_free)(hc_result_t *result); - - // Reference to module containing the implementation - void *handle; -}; - #endif // HICN_API_PRIVATE_H diff --git a/ctrl/libhicnctrl/src/cli.h b/ctrl/libhicnctrl/src/cli.h new file mode 100644 index 000000000..b04448a6e --- /dev/null +++ b/ctrl/libhicnctrl/src/cli.h @@ -0,0 +1,4 @@ +#include <hicn/ctrl.h> + +typedef int (*command_function)(hc_sock_t *, hc_command_t *); +extern command_function command_vft[][ACTION_N]; diff --git a/ctrl/libhicnctrl/src/command.c b/ctrl/libhicnctrl/src/command.c new file mode 100644 index 000000000..77b6eb7e8 --- /dev/null +++ b/ctrl/libhicnctrl/src/command.c @@ -0,0 +1,127 @@ + +/** + * @file command.c + * @brief Implementation of commands. + */ + +#ifdef __linux__ +#define _GNU_SOURCE +#endif /* __linux__ */ +#include <search.h> /* tfind, tdestroy, twalk */ +#include <stdio.h> +#include <ctype.h> +#include <hicn/ctrl/command.h> + +#include <hicn/util/log.h> + +#include <hicn/ctrl/parse.h> + +/* Commands are registered in the following tree. */ +static void *commands_root = NULL; /**< Tree ordered by name */ + +#ifdef __linux__ +static void nothing_to_free() {} + +__attribute__((destructor)) static void command_clear() { + tdestroy(commands_root, nothing_to_free); +} +#endif /* __linux__ */ + +static int _command_compare(const command_parser_t *c1, + const command_parser_t *c2) { + if (c1->object_type != c2->object_type) + return c2->object_type - c1->object_type; + if (c1->action != c2->action) return c2->action - c1->action; + if (c1->nparams != c2->nparams) return c2->nparams - c1->nparams; + return 0; +} + +#define command_compare (int (*)(const void *, const void *))(_command_compare) + +void command_register(const command_parser_t *command) { + // Insert the command in the tree if the keys does not exist yet + tsearch(command, &commands_root, command_compare); +} + +const command_parser_t *command_search(const hc_action_t action, + hc_object_type_t object_type, + unsigned nparams) { + command_parser_t **command, search; + + search.action = action; + search.object_type = object_type; + search.nparams = nparams; + command = tfind(&search, &commands_root, command_compare); + + return command ? *command : NULL; +} + +static inline void to_lowercase(char *p) { + for (; *p; ++p) *p = tolower(*p); +} + +typedef struct { + hc_object_type_t object_type; + hc_action_t action; +} cmd_search_params_t; + +static hc_object_type_t prev_obj = OBJECT_TYPE_UNDEFINED; +static hc_action_t prev_action = ACTION_UNDEFINED; +static void traversal_action(const void *nodep, VISIT which, + void *cmd_params0) { + cmd_search_params_t *cmd_params = cmd_params0; + + // Execute this function during inorder traversal + if (which != postorder && which != leaf) return; + + command_parser_t *datap; + datap = *(command_parser_t **)nodep; + char *obj_str = strdup(object_type_str(datap->object_type)); + to_lowercase(obj_str); + + // List all objects + if (cmd_params->object_type == OBJECT_TYPE_UNDEFINED && + cmd_params->action == ACTION_UNDEFINED) { + if (datap->object_type == prev_obj) goto FREE_STR; + prev_obj = datap->object_type; + + printf("\thelp %s\n", obj_str); + goto FREE_STR; + } + + // List actions for specific object + if (datap->object_type != cmd_params->object_type) goto FREE_STR; + if (cmd_params->action == ACTION_UNDEFINED) { + if (datap->action == prev_action) goto FREE_STR; + prev_action = datap->action; + + printf("\thelp %s %s\n", obj_str, action_to_cmd_action(datap->action)); + goto FREE_STR; + } + + // List commands for specific object and action + if (datap->action != cmd_params->action) goto FREE_STR; + printf(" %s %s ", action_to_cmd_action(datap->action), obj_str); + for (int i = 0; i < datap->nparams; i++) + printf("<%s> ", datap->parameters[i].name); + printf("\n\n"); + // List options' details + if (datap->nparams == 0) goto FREE_STR; + for (int i = 0; i < datap->nparams; i++) + printf("%16s: %s\n", datap->parameters[i].name, datap->parameters[i].help); + printf("\n"); + +FREE_STR: + free(obj_str); +} + +void command_list(hc_object_type_t object_type, hc_action_t action) { +#if defined(__linux__) && !defined(__ANDROID__) + cmd_search_params_t cmd_params = {.object_type = object_type, + .action = action}; + twalk_r(commands_root, traversal_action, &cmd_params); +#else + fprintf(stderr, "twalk_r() function only available on linux"); + (void)traversal_action; +#endif +} diff --git a/ctrl/libhicnctrl/src/commands/command_cache.c b/ctrl/libhicnctrl/src/commands/command_cache.c new file mode 100644 index 000000000..124fcd761 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_cache.c @@ -0,0 +1,54 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define serve \ + { \ + .name = "serve", \ + .help = \ + "Enables/disables replies from local content store. Either the " \ + "string 'on' or 'off'", \ + .type = TYPE_ON_OFF, .offset = offsetof(hc_cache_t, serve), \ + } + +#define store \ + { \ + .name = "store", \ + .help = \ + "enables/disables the storage of incoming data packets in the local " \ + "content store. Either the string 'on' or 'off'", \ + .type = TYPE_ON_OFF, .offset = offsetof(hc_cache_t, store), \ + } + +/* Commands */ + +static const command_parser_t command_cache_set_serve = { + .action = ACTION_SERVE, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 1, + .parameters = {serve}, +}; +COMMAND_REGISTER(command_cache_set_serve); + +static const command_parser_t command_cache_set_store = { + .action = ACTION_STORE, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 1, + .parameters = {store}, +}; +COMMAND_REGISTER(command_cache_set_store); + +static const command_parser_t command_cache_clear = { + .action = ACTION_CLEAR, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 0, +}; +COMMAND_REGISTER(command_cache_clear); + +static const command_parser_t command_cache_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_CACHE, + .nparams = 0, +}; +COMMAND_REGISTER(command_cache_list); diff --git a/ctrl/libhicnctrl/src/commands/command_connection.c b/ctrl/libhicnctrl/src/commands/command_connection.c new file mode 100644 index 000000000..30b3c3bf1 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_connection.c @@ -0,0 +1,147 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define type_hicn \ + { \ + .name = "type", .help = "connection type (hICN)", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_connection_t, type), \ + } + +#define type_tcp_udp \ + { \ + .name = "type", .help = "connection type [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_connection_t, type), \ + } + +#define symbolic \ + { \ + .name = "symbolic", \ + .help = "symbolic name, e.g. 'conn1' (must be unique, start with alpha)", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_connection_t, name), \ + } + +#define local_address \ + { \ + .name = "local_addr", .help = "local IP address on which to bind.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_connection_t, local_addr), \ + .offset2 = offsetof(hc_connection_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_connection_t, local_port), \ + } + +#define remote_address \ + { \ + .name = "remote_address", \ + .help = "The IPv4 or IPv6 or hostname of the remote system.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_connection_t, remote_addr), \ + .offset2 = offsetof(hc_connection_t, family), \ + } + +#define remote_port \ + { \ + .name = "remote_port", .help = "Remote port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_connection_t, remote_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_connection_t, interface_name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The connection symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_connection_t, name), \ + } + +/* Commands */ + +int on_connection_create(hc_connection_t* connection) { + connection->admin_state = FACE_STATE_UP; + return 0; +} + +#if 0 +static command_parser_t command_connection_create4 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 4, + .parameters = {type_hicn, symbolic, local_address, remote_address}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create4); + +static const command_parser_t command_connection_create5 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_CONNECTION, + .nparams = 5, + .parameters = {type_hicn, symbolic, local_address, remote_address, + interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create5); +#endif + +static const command_parser_t command_connection_create4 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 4, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create4); + +static const command_parser_t command_connection_create5 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 5, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create5); + +static const command_parser_t command_connection_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 6, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + local_address, local_port}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create6); + +static const command_parser_t command_connection_create7 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 7, + .parameters = {type_tcp_udp, symbolic, remote_address, remote_port, + local_address, local_port, interface}, + .post_hook = (parser_hook_t)on_connection_create, +}; +COMMAND_REGISTER(command_connection_create7); + +static const command_parser_t command_connection_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 0, +}; +COMMAND_REGISTER(command_connection_list); + +static const command_parser_t command_connection_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_CONNECTION, + .nparams = 1, + .parameters = {symbolic_or_id}, +}; +COMMAND_REGISTER(command_connection_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_face.c b/ctrl/libhicnctrl/src/commands/command_face.c new file mode 100644 index 000000000..68a6abefe --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_face.c @@ -0,0 +1,112 @@ +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define type_hicn \ + { \ + .name = "type", .help = "face type (hICN)", .type = TYPE_ENUM(face_type), \ + .offset = offsetof(hc_face_t, type), \ + } + +#define type_tcp_udp \ + { \ + .name = "type", .help = "face type [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_face_t, type), \ + } + +#define local_address \ + { \ + .name = "local_addr", .help = "local IP address on which to bind.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_face_t, local_addr), \ + .offset2 = offsetof(hc_face_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_face_t, local_port), \ + } + +#define remote_address \ + { \ + .name = "remote_address", \ + .help = "The IPv4 or IPv6 or hostname of the remote system.", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_face_t, remote_addr), \ + .offset2 = offsetof(hc_face_t, family), \ + } + +#define remote_port \ + { \ + .name = "remote_port", .help = "Remote port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_face_t, remote_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_face_t, netdevice) + offsetof(netdevice_t, name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The face symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_face_t, name), \ + } + +/* Commands */ + +int on_face_create(hc_face_t* face) { + face->admin_state = FACE_STATE_UP; + return 0; +} + +static command_parser_t command_face_create3 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 3, + .parameters = {type_hicn, local_address, remote_address}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create3); + +#if 0 +static const command_parser_t command_face_create4 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_FACE, + .nparams = 4, + .parameters = {type_hicn, local_address, remote_address, + interface}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create4); +#endif + +static const command_parser_t command_face_create5 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 5, + .parameters = {type_tcp_udp, remote_address, remote_port, local_address, + local_port}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create5); + +static const command_parser_t command_face_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_FACE, + .nparams = 6, + .parameters = {type_tcp_udp, remote_address, remote_port, local_address, + local_port, interface}, + .post_hook = (parser_hook_t)on_face_create, +}; +COMMAND_REGISTER(command_face_create6); + +static const command_parser_t command_face_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_FACE, + .nparams = 0, +}; +COMMAND_REGISTER(command_face_list); diff --git a/ctrl/libhicnctrl/src/commands/command_listener.c b/ctrl/libhicnctrl/src/commands/command_listener.c new file mode 100644 index 000000000..bba4f4541 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_listener.c @@ -0,0 +1,111 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define protocol_hicn \ + { \ + .name = "protocol", .help = "Protocol [hicn].", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_listener_t, type), \ + } + +#define protocol_tcp_udp \ + { \ + .name = "protocol", .help = "Protocol [tcp | udp]", \ + .type = TYPE_ENUM(face_type), .offset = offsetof(hc_listener_t, type), \ + } + +#define symbolic \ + { \ + .name = "symbolic", \ + .help = \ + "User defined name for listener, must start with alpha and be " \ + "alphanum", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_listener_t, name), \ + } + +#define local_address \ + { \ + .name = "local_addr", \ + .help = \ + "IPv4 or IPv6 address (or prefix protocol = hicn) assigend to the " \ + "local interface", \ + .type = TYPE_IP_ADDRESS, .offset = offsetof(hc_listener_t, local_addr), \ + .offset2 = offsetof(hc_listener_t, family), \ + } + +#define local_port \ + { \ + .name = "local_port", .help = "Local port.", \ + .type = TYPE_UINT16(1, UINT16_MAX), \ + .offset = offsetof(hc_listener_t, local_port), \ + } + +#define interface \ + { \ + .name = "interface", .help = "Interface on which to bind", \ + .type = TYPE_INTERFACE_NAME, \ + .offset = offsetof(hc_listener_t, interface_name), \ + } + +#define symbolic_or_id \ + { \ + .name = "symbolic", .help = "The listener symbolic name or id", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_listener_t, name), \ + } + +/* Commands */ + +/* The parse sets the wrong face_type_t for listener, we fix that here */ +int on_listener_create(hc_listener_t* listener) { + switch (listener->type) { + case FACE_TYPE_UDP: + listener->type = FACE_TYPE_UDP_LISTENER; + break; + case FACE_TYPE_TCP: + listener->type = FACE_TYPE_TCP_LISTENER; + break; + case FACE_TYPE_HICN: + listener->type = FACE_TYPE_HICN_LISTENER; + break; + default: + break; + } + return 0; +} + +#if 0 +static const command_parser_t command_listener_create4 = { + .action = ACTION_CREATE, + .object = OBJECT_TYPE_LISTENER, + .nparams = 4, + .parameters = {protocol_hicn, symbolic, local_address, interface}, + .post_hook = (parser_hook_t)on_listener_create, +}; +COMMAND_REGISTER(command_listener_create4); +#endif + +static const command_parser_t command_listener_create6 = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 5, + .parameters = {protocol_tcp_udp, symbolic, local_address, local_port, + interface}, + .post_hook = (parser_hook_t)on_listener_create, +}; +COMMAND_REGISTER(command_listener_create6); + +static const command_parser_t command_listener_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 0, +}; +COMMAND_REGISTER(command_listener_list); + +static const command_parser_t command_listener_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_LISTENER, + .nparams = 1, + .parameters = {symbolic_or_id}, +}; +COMMAND_REGISTER(command_listener_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_mapme.c b/ctrl/libhicnctrl/src/commands/command_mapme.c new file mode 100644 index 000000000..c67b7704f --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_mapme.c @@ -0,0 +1,59 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define target \ + { \ + .name = "target", \ + .help = \ + "Target for the set action, e.g. enable, discovery, timescale, retx", \ + .type = TYPE_ENUM(mapme_target), .offset = offsetof(hc_mapme_t, target), \ + } + +#define value \ + { \ + .name = "value", \ + .help = "Value to set for the target, e.g. 'on', 'off', milliseconds", \ + .type = TYPE_STRN(4), .offset = offsetof(hc_mapme_t, unparsed_arg), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_mapme_t, address), \ + .offset2 = offsetof(hc_mapme_t, len), \ + .offset3 = offsetof(hc_mapme_t, family), \ + } + +/* Commands */ + +// Parse the raw string argument into 'timescale' or 'enabled', +// necessary since the command dispatch is based on the number +// of arguments and not their type +int parse_args(hc_mapme_t* mapme) { + mapme->timescale = atoi(mapme->unparsed_arg); + + if (strcasecmp(mapme->unparsed_arg, "off") == 0) mapme->enabled = 0; + if (strcasecmp(mapme->unparsed_arg, "on") == 0) mapme->enabled = 1; + + return 0; +} + +static const command_parser_t command_mapme_set = { + .action = ACTION_SET, + .object_type = OBJECT_TYPE_MAPME, + .nparams = 2, + .parameters = {target, value}, + .post_hook = (parser_hook_t)parse_args, +}; +COMMAND_REGISTER(command_mapme_set); + +static const command_parser_t command_mapme_update = { + .action = ACTION_UPDATE, + .object_type = OBJECT_TYPE_MAPME, + .nparams = 1, + .parameters = {prefix}, +}; +COMMAND_REGISTER(command_mapme_update); diff --git a/ctrl/libhicnctrl/src/commands/command_policy.c b/ctrl/libhicnctrl/src/commands/command_policy.c new file mode 100644 index 000000000..2fc7a0a42 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_policy.c @@ -0,0 +1,52 @@ +#if 0 +#include <hicn/policy.h> + +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_policy_t, remote_addr), \ + .offset2 = offsetof(hc_policy_t, len), \ + .offset3 = offsetof(hc_policy_t, family), \ + } + +#define app_name \ + { \ + .name = "app_name", \ + .help = "The application name associated to this policy", \ + .type = TYPE_STR, .offset = offsetof(hc_policy_t, policy.app_name), \ + } + +/* Commands */ + +static const command_parser_t command_policy_create = { + .action = ACTION_CREATE, + .object = OBJECT_POLICY, + .nparams = 2 + POLICY_TAG_N, + .parameters = {prefix, app_name, +#define _(x, y) \ + { \ + .name = "flag:" #x, \ + .help = \ + "A value among [neutral|require|prefer|avoid|prohibit] with an " \ + "optional '!' character prefix for disabling changes", \ + .type = TYPE_POLICY_STATE(POLICY_TAG_##x), \ + .offset = offsetof(hc_policy_t, policy.tags), \ + }, + foreach_policy_tag +#undef _ + }, +}; +COMMAND_REGISTER(command_policy_create); + +static const command_parser_t command_policy_list = { + .action = ACTION_LIST, + .object = OBJECT_POLICY, + .nparams = 0, +}; +COMMAND_REGISTER(command_policy_list); +#endif diff --git a/ctrl/libhicnctrl/src/commands/command_punting.c b/ctrl/libhicnctrl/src/commands/command_punting.c new file mode 100644 index 000000000..b845c52ee --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_punting.c @@ -0,0 +1,40 @@ +#if 0 +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define symbolic_or_id \ + { \ + .name = "symbolic_or_id", \ + .help = \ + "The symbolic name for an egress, or the egress punting id (see " \ + "'help list puntings')", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_punting_t, face_id), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "Prefix to add as a punting rule. (example 1234::0/64)", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_punting_t, prefix), \ + .offset2 = offsetof(hc_punting_t, prefix_len), \ + .offset3 = offsetof(hc_punting_t, family), \ + } + +/* Commands */ + +static const command_parser_t command_punting_create = { + .action = ACTION_CREATE, + .object = OBJECT_PUNTING, + .nparams = 2, + .parameters = {symbolic_or_id, prefix}, +}; +COMMAND_REGISTER(command_punting_create); + +static const command_parser_t command_punting_list = { + .action = ACTION_LIST, + .object = OBJECT_PUNTING, + .nparams = 0, +}; +COMMAND_REGISTER(command_punting_list); +#endif diff --git a/ctrl/libhicnctrl/src/commands/command_route.c b/ctrl/libhicnctrl/src/commands/command_route.c new file mode 100644 index 000000000..8e7db8192 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_route.c @@ -0,0 +1,53 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define symbolic_or_id \ + { \ + .name = "symbolic_or_id", \ + .help = \ + "The symbolic name for an egress, or the egress route id (see 'help " \ + "list routes')", \ + .type = TYPE_SYMBOLIC_OR_ID, .offset = offsetof(hc_route_t, face_name), \ + } + +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_route_t, remote_addr), \ + .offset2 = offsetof(hc_route_t, len), \ + .offset3 = offsetof(hc_route_t, family), \ + } + +#define cost \ + { \ + .name = "cost", .help = "Positive integer representing cost.", \ + .type = TYPE_INT(1, 255), .offset = offsetof(hc_route_t, cost), \ + } + +/* Commands */ + +static const command_parser_t command_route_create = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 3, + .parameters = {symbolic_or_id, prefix, cost}, +}; +COMMAND_REGISTER(command_route_create); + +static const command_parser_t command_route_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 0, +}; +COMMAND_REGISTER(command_route_list); + +static const command_parser_t command_route_remove = { + .action = ACTION_DELETE, + .object_type = OBJECT_TYPE_ROUTE, + .nparams = 2, + .parameters = {symbolic_or_id, prefix}, +}; +COMMAND_REGISTER(command_route_remove); diff --git a/ctrl/libhicnctrl/src/commands/command_stats.c b/ctrl/libhicnctrl/src/commands/command_stats.c new file mode 100644 index 000000000..7c58b105e --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_stats.c @@ -0,0 +1,18 @@ +#include <math.h> +#include <hicn/ctrl/command.h> + +/* Commands */ + +static const command_parser_t command_stats_get = { + .action = ACTION_GET, + .object_type = OBJECT_TYPE_STATS, + .nparams = 0, +}; +COMMAND_REGISTER(command_stats_get); + +static const command_parser_t command_stats_list = { + .action = ACTION_LIST, + .object_type = OBJECT_TYPE_STATS, + .nparams = 0, +}; +COMMAND_REGISTER(command_stats_list); diff --git a/ctrl/libhicnctrl/src/commands/command_strategy.c b/ctrl/libhicnctrl/src/commands/command_strategy.c new file mode 100644 index 000000000..5605822e7 --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_strategy.c @@ -0,0 +1,47 @@ +#include <hicn/ctrl/command.h> + +/* Parameters */ +#define prefix \ + { \ + .name = "prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_strategy_t, address), \ + .offset2 = offsetof(hc_strategy_t, len), \ + .offset3 = offsetof(hc_strategy_t, family), \ + } + +#define strategy \ + { \ + .name = "strategy", \ + .help = \ + "Strategy type (e.g. 'random', 'loadbalancer', 'low_latency', " \ + "'replication', 'bestpath').", \ + .type = TYPE_ENUM(strategy_type), .offset = offsetof(hc_strategy_t, type), \ + } + +#define local_prefix \ + { \ + .name = "local_prefix", \ + .help = "The hicn name as IPv4 or IPv6 address (e.g 1234::0/64).", \ + .type = TYPE_IP_PREFIX, .offset = offsetof(hc_strategy_t, local_address), \ + .offset2 = offsetof(hc_strategy_t, local_len), \ + .offset3 = offsetof(hc_strategy_t, local_family), \ + } + +/* Commands */ + +static const command_parser_t command_strategy_list = { + .action = ACTION_SET, + .object_type = OBJECT_TYPE_STRATEGY, + .nparams = 2, + .parameters = {prefix, strategy}, +}; +COMMAND_REGISTER(command_strategy_list); + +static const command_parser_t local_prefix_add = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_LOCAL_PREFIX, + .nparams = 3, + .parameters = {prefix, strategy, local_prefix}, +}; +COMMAND_REGISTER(local_prefix_add); diff --git a/ctrl/libhicnctrl/src/commands/command_subscription.c b/ctrl/libhicnctrl/src/commands/command_subscription.c new file mode 100644 index 000000000..886ee454a --- /dev/null +++ b/ctrl/libhicnctrl/src/commands/command_subscription.c @@ -0,0 +1,26 @@ +#include <limits.h> + +#include <hicn/ctrl/command.h> + +/* Parameters */ + +#define topics \ + { \ + .name = "topics", \ + .help = \ + "Topics to subscribe to, e.g. 6 (110 in binary) means topic 2 (10 in " \ + "binary, TOPIC_CONNECTION) and topic 4 (100 in binary, " \ + "TOPIC_LISTENER).", \ + .type = TYPE_INT(1, INT_MAX), \ + .offset = offsetof(hc_subscription_t, topics), \ + } + +/* Commands */ + +static const command_parser_t command_subscription_create = { + .action = ACTION_CREATE, + .object_type = OBJECT_TYPE_SUBSCRIPTION, + .nparams = 1, + .parameters = {topics}, +}; +COMMAND_REGISTER(command_subscription_create); diff --git a/ctrl/libhicnctrl/src/data.c b/ctrl/libhicnctrl/src/data.c new file mode 100644 index 000000000..605e23601 --- /dev/null +++ b/ctrl/libhicnctrl/src/data.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file data.c + * \brief Implementation of request result data. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +#include <hicn/util/log.h> + +#define MIN_ALLOC_SIZE 8 +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +struct hc_data_s { + hc_object_type_t object_type; + bool complete; + + /** + * >=0 success, indicates the number of records in array + * <0 error + */ + ssize_t size; + size_t alloc_size; /** Allocated size (a power of 2 when managed + automatically) */ + size_t max_size; /** Maximum size defined at creation (0 = unlimited) */ + + uint8_t *buffer; +}; + +void _hc_data_clear(hc_data_t *data) { + data->complete = false; + data->buffer = NULL; + data->max_size = 0; + data->alloc_size = 0; + data->size = 0; +} + +hc_data_t *hc_data_create(hc_object_type_t object_type) { + hc_data_t *data = malloc(sizeof(hc_data_t)); + if (!data) return NULL; + + data->object_type = object_type; + + _hc_data_clear(data); + + return data; + // data->buffer = malloc((1 << data->max_size_log) * data->out_element_size); + // if (!data->buffer) goto ERR_BUFFER; +} + +void hc_data_free(hc_data_t *data) { + assert(data); + + if (data->buffer) free(data->buffer); + free(data); +} + +int hc_data_set_max_size(hc_data_t *data, size_t max_size) { + if (data->size > max_size) return -1; + data->max_size = max_size; + return 0; +} + +const uint8_t *hc_data_get_buffer(hc_data_t *data) { return data->buffer; } + +const uint8_t *hc_data_get_free(hc_data_t *data) { + if (!data) return NULL; + if (data->max_size > 0 && data->size >= data->max_size) return NULL; + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + return data->buffer + data->size * object_size; +} + +void hc_data_inc_size(hc_data_t *data) { data->size++; } + +hc_object_type_t hc_data_get_object_type(const hc_data_t *data) { + return data->object_type; +} + +void hc_data_set_object_type(hc_data_t *data, hc_object_type_t object_type) { + data->object_type = object_type; +} + +ssize_t hc_data_get_size(const hc_data_t *data) { return data->size; } + +int hc_data_clear(hc_data_t *data) { + free(data->buffer); + _hc_data_clear(data); + return 0; +} + +int _hc_data_allocate(hc_data_t *data, size_t size) { + data->buffer = + realloc(data->buffer, size * hc_object_size(data->object_type)); + if (!data->buffer) goto ERR; + data->alloc_size = size; + return 0; +ERR: + data->alloc_size = 0; + return -1; +} + +int hc_data_allocate(hc_data_t *data, size_t size) { + /* Do not allocate twice */ + if (data->buffer) return -1; + if (data->max_size > 0 && size > data->max_size) return -1; + return _hc_data_allocate(data, size); +} + +int hc_data_ensure_available(hc_data_t *data, size_t count) { + size_t new_size = data->size + count; + if (new_size < data->alloc_size) return 0; + if (data->max_size > 0 && new_size > data->max_size) return -1; + + size_t new_alloc_size = MAX(MIN_ALLOC_SIZE, next_pow2(new_size)); + if (data->max_size > 0 && new_alloc_size > data->max_size) + new_alloc_size = data->max_size; + + return _hc_data_allocate(data, new_alloc_size); +} + +int hc_data_push_many(hc_data_t *data, const void *elements, size_t count) { + if (!data) return -1; + if (!elements) return -1; + if (count < 1) return -1; + + if (hc_data_ensure_available(data, count) < 0) return -1; + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + + uint8_t *dst = data->buffer + data->size * object_size; + memcpy(dst, elements, count * object_size); + data->size += count; + + return 0; +} + +int hc_data_push(hc_data_t *data, const void *element) { + return hc_data_push_many(data, element, 1); +} +#if 0 +/** + * + * NOTE: This function make sure there is enough room available in the data + * structure. + */ +u8 *hc_data_get_next(hc_data_t *data) { + if (hc_data_ensure_available(data, 1) < 0) return NULL; + + return data->buffer + data->size * data->out_element_size; +} + +int hc_data_set_callback(hc_data_t *data, data_callback_t cb, void *cb_data) { + data->complete_cb = cb; + data->complete_cb_data = cb_data; + return 0; +} +#endif + +void hc_data_set_complete(hc_data_t *data) { data->complete = true; } + +bool hc_data_is_complete(const hc_data_t *data) { return data->complete; } + +void hc_data_set_error(hc_data_t *data) { + data->size = -1; + data->complete = true; +} + +bool hc_data_get_result(hc_data_t *data) { return (data->size >= 0); } + +hc_object_t *hc_data_find(hc_data_t *data, hc_object_t *object) { + hc_object_type_t object_type = hc_data_get_object_type(data); + hc_data_foreach(data, found, { + if (hc_object_cmp(object_type, object, found) == 0) return found; + }); + return NULL; +} + +const hc_object_t *hc_data_get_object(const hc_data_t *data, off_t pos) { + size_t size = hc_data_get_size(data); + if (pos >= size) return NULL; + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_object_size(object_type); + + return (const hc_object_t *)(data->buffer + pos * object_size); +} + +#if 0 +int hc_data_reset(hc_data_t *data) { + data->size = 0; + return 0; +} +#endif diff --git a/ctrl/libhicnctrl/src/fw_interface.c b/ctrl/libhicnctrl/src/fw_interface.c new file mode 100644 index 000000000..6d5e5fb34 --- /dev/null +++ b/ctrl/libhicnctrl/src/fw_interface.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file fw_interface.c + * \brief Implementation of fw interface + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/callback.h> +#include <hicn/util/log.h> +#include <hicn/ctrl/fw_interface.h> + +const char *fw_state_str[] = { +#define _(x) [FW_STATE_##x] = #x, + foreach_fw_state +#undef _ +}; + +struct fw_interface_s { + /* + * Type of forwarder to which we are connecting/connected : HICNLIGHT, VPP + */ + forwarder_type_t type; + fw_state_t state; + + hc_sock_t *sock; + char *url; + + bool has_subscribe_all; + + hc_enable_callback_t enable_callback; + hc_state_callback_t state_callback; + void *state_callback_data; + hc_result_callback_t result_callback; + void *result_callback_data; + hc_notification_callback_t notification_callback; + void *notification_callback_data; +}; + +fw_interface_t *fw_interface_create_url(forwarder_type_t type, + const char *url) { + fw_interface_t *fi = malloc(sizeof(fw_interface_t)); + if (!fi) goto ERR_MALLOC; + + fi->type = type; + /* Let's assume for now the forwarder is always on */ + fi->state = FW_STATE_AVAILABLE; + fi->sock = NULL; + fi->has_subscribe_all = false; + fi->url = NULL; + + // XXX make a single request to probe for forwarder size? + + return fi; + +ERR_MALLOC: + return NULL; +} + +fw_interface_t *fw_interface_create(forwarder_type_t type) { + return fw_interface_create_url(type, NULL); +} + +void fw_interface_free(fw_interface_t *fi) { + fw_interface_disconnect(fi); + free(fi); +} + +int fw_interface_get_fd(const fw_interface_t *fi) { + if (!fi) return 0; + return hc_sock_get_fd(fi->sock); +} + +int fw_interface_set_enable_callback(fw_interface_t *fi, + hc_enable_callback_t callback) { + fi->enable_callback = callback; + return 0; +} + +int fw_interface_set_state_callback(fw_interface_t *fi, + hc_state_callback_t callback, + void *callback_data) { + fi->state_callback = callback; + fi->state_callback_data = callback_data; + return 0; +} + +int fw_interface_set_result_callback(fw_interface_t *fi, + hc_result_callback_t callback, + void *callback_data) { + fi->result_callback = callback; + fi->result_callback_data = callback_data; + return 0; +} + +int fw_interface_set_notification_callback(fw_interface_t *fi, + hc_notification_callback_t callback, + void *callback_data) { + fi->notification_callback = callback; + fi->notification_callback_data = callback_data; + return 0; +} + +int fw_interface_enable(fw_interface_t *fi) { + // TODO + return 0; +} + +int fw_interface_disable(fw_interface_t *fi) { + // TODO + return 0; +} + +// XXX blocking or non blocking ? +int fw_interface_reschedule_connect(fw_interface_t *fi) { + INFO("Scheduling reconnect..."); + // XXX TODO timer + return 0; +} + +int _fw_interface_connect(fw_interface_t *fi, bool reattempt) { + fi->sock = hc_sock_create(fi->type, fi->url); + if (!fi->sock) goto ERR_SOCK; + + if (hc_sock_set_async(fi->sock) < 0) goto ERR_ASYNC; + + if (hc_sock_connect(fi->sock) < 0) { + ERROR("Error connecting to forwarder"); + return -1; + } + + return 0; + +ERR_ASYNC: + hc_sock_free(fi->sock); +ERR_SOCK: + + if (reattempt) return fw_interface_reschedule_connect(fi); + return -1; +} + +int fw_interface_connect(fw_interface_t *fi) { + switch (fi->state) { + case FW_STATE_UNDEFINED: + // XXX connect, enable, (poll)? + break; + case FW_STATE_DISABLED: + fw_interface_enable(fi); + break; + case FW_STATE_REQUESTED: + // XXX waiting ? polling connect ? + break; + case FW_STATE_AVAILABLE: + _fw_interface_connect(fi, true); + // XXX + break; + case FW_STATE_CONNECTING: + case FW_STATE_CONNECTED: + case FW_STATE_READY: + /* Nothing to do */ + return 0; + case FW_STATE_N: + return -1; + } + return 0; +} + +int _fw_interface_disconnect(fw_interface_t *fi) { + if (fi->has_subscribe_all) fw_interface_unsubscribe_all(fi); + hc_sock_free(fi->sock); + return 0; +} + +int fw_interface_disconnect(fw_interface_t *fi) { + switch (fi->state) { + case FW_STATE_UNDEFINED: + case FW_STATE_DISABLED: + case FW_STATE_REQUESTED: + case FW_STATE_AVAILABLE: + /* Nothing to do */ + return 0; + case FW_STATE_CONNECTING: + case FW_STATE_CONNECTED: + case FW_STATE_READY: + _fw_interface_disconnect(fi); + return 0; + case FW_STATE_N: + return -1; + } + return 0; +} + +fw_state_t fw_interface_get_state(const fw_interface_t *fi) { + return fi->state; +} + +bool fw_interface_is_connected(const fw_interface_t *fi) { + return ((fi->state == FW_STATE_CONNECTED) || (fi->state == FW_STATE_READY)); +} + +bool fw_interface_is_ready(const fw_interface_t *fi) { + return (fi->state == FW_STATE_READY); +} + +int fw_interface_subscribe_all(fw_interface_t *fi) { + INFO("fw_interface_subscribe_all"); + int rc = hc_execute_async(fi->sock, ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED, + NULL, fi->notification_callback, + fi->notification_callback_data); + if (rc < 0) { + return -1; + } + fi->has_subscribe_all = true; + return 0; +} + +int fw_interface_unsubscribe_all(fw_interface_t *fi) { + fi->has_subscribe_all = false; + return 0; +} + +// face manager : upon completion, same as notification, CREATE/GET FACE +// hproxy = event = function to call to proceed through state machine (also +// depends if we handle face+route), for notifications, telemetry. +// NOTE we should have a notif for our own events. how to handle ? +// XXX user_data .... or user_callback +int fw_interface_execute(fw_interface_t *fi, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, + hc_data_t **pdata) { + return hc_execute(fi->sock, action, object_type, object, pdata); +} + +int fw_interface_execute_async(fw_interface_t *fi, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + if (!callback) { + callback = fi->result_callback; + callback_data = fi->result_callback_data; + } + return hc_execute_async(fi->sock, action, object_type, object, callback, + callback_data); +} + +int fw_interface_on_receive(fw_interface_t *fi, size_t count) { + return hc_sock_on_receive(fi->sock, count); +} + +int fw_interface_get_recv_buffer(fw_interface_t *fi, uint8_t **buffer, + size_t *size) { + return hc_sock_get_recv_buffer(fi->sock, buffer, size); +} diff --git a/ctrl/libhicnctrl/src/hicnctrl.c b/ctrl/libhicnctrl/src/hicnctrl.c index 99d67b19f..c771bde69 100644 --- a/ctrl/libhicnctrl/src/hicnctrl.c +++ b/ctrl/libhicnctrl/src/hicnctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 Cisco and/or its affiliates. + * Copyright (c) 2021-2022 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -24,20 +24,18 @@ #include <hicn/ctrl.h> #include <hicn/util/ip_address.h> +#include <hicn/util/log.h> #include <hicn/util/token.h> #include <hicn/validation.h> +#include <hicn/ctrl/parse.h> + #define die(LABEL, MESSAGE) \ do { \ printf(MESSAGE "\n"); \ - rc = -1; \ goto ERR_##LABEL; \ } while (0) -const char HICNLIGHT_PARAM[] = "hicnlight"; -const char HICNLIGHT_NG_PARAM[] = "hicnlightng"; -const char VPP_PARAM[] = "vpp"; - void usage_header() { fprintf(stderr, "Usage:\n"); } void usage_face_create(const char *prog, bool header, bool verbose) { @@ -201,69 +199,66 @@ void usage(const char *prog) { usage_connection(prog, false, true); } -#if 0 -typedef struct { - hc_action_t action; - hc_object_t object; - union { - hc_face_t face; - hc_route_t route; - hc_connection_t connection; - hc_listener_t listener; - }; -} hc_command_t; -#endif +/* + * We only allow settings commands and object types once, with the default + * action being set to CREATE + */ +#define set_command(ACTION, OBJECT_TYPE) \ + do { \ + if ((ACTION) != ACTION_UNDEFINED) { \ + if (command->action != ACTION_CREATE) goto USAGE; \ + command->action = (ACTION); \ + } \ + if ((OBJECT_TYPE) != OBJECT_TYPE_UNDEFINED) { \ + if (command->object_type != OBJECT_TYPE_UNDEFINED) goto USAGE; \ + command->object_type = (OBJECT_TYPE); \ + } \ + } while (0) int parse_options(int argc, char *argv[], hc_command_t *command, forwarder_type_t *forwarder) { - command->object.type = OBJECT_UNDEFINED; + command->object_type = OBJECT_TYPE_UNDEFINED; command->action = ACTION_CREATE; int opt; - int family; - while ((opt = getopt(argc, argv, "dflcrFLCRShz:")) != -1) { + while ((opt = getopt(argc, argv, "cCdfFlLrRsShz:")) != -1) { switch (opt) { case 'z': - if (strncmp(optarg, VPP_PARAM, strlen(VPP_PARAM)) == 0) { - *forwarder = VPP; - } else if (strncmp(optarg, HICNLIGHT_PARAM, strlen(HICNLIGHT_PARAM))) { - *forwarder = HICNLIGHT; - } else if (strncmp(optarg, HICNLIGHT_NG_PARAM, - strlen(HICNLIGHT_NG_PARAM))) { - *forwarder = HICNLIGHT_NG; - } else { - usage(argv[0]); - exit(EXIT_FAILURE); - } + *forwarder = forwarder_type_from_str(optarg); + if (*forwarder == FORWARDER_TYPE_UNDEFINED) goto USAGE; break; case 'd': - command->action = ACTION_DELETE; + set_command(ACTION_DELETE, OBJECT_TYPE_UNDEFINED); + break; + case 's': + set_command(ACTION_SUBSCRIBE, OBJECT_TYPE_UNDEFINED); break; case 'f': - command->object.type = OBJECT_FACE; + set_command(ACTION_UNDEFINED, OBJECT_TYPE_FACE); break; case 'c': - command->object.type = OBJECT_CONNECTION; + set_command(ACTION_UNDEFINED, OBJECT_TYPE_CONNECTION); + break; + case 'l': + set_command(ACTION_UNDEFINED, OBJECT_TYPE_LISTENER); + break; + case 'r': + set_command(ACTION_UNDEFINED, OBJECT_TYPE_ROUTE); break; case 'F': - command->action = ACTION_LIST; - command->object.type = OBJECT_FACE; + set_command(ACTION_LIST, OBJECT_TYPE_FACE); break; case 'L': - command->action = ACTION_LIST; - command->object.type = OBJECT_LISTENER; + set_command(ACTION_LIST, OBJECT_TYPE_LISTENER); break; case 'C': - command->action = ACTION_LIST; - command->object.type = OBJECT_CONNECTION; + set_command(ACTION_LIST, OBJECT_TYPE_CONNECTION); break; case 'R': - command->action = ACTION_LIST; - command->object.type = OBJECT_ROUTE; + set_command(ACTION_LIST, OBJECT_TYPE_ROUTE); break; case 'S': - command->action = ACTION_LIST; - command->object.type = OBJECT_STRATEGY; + set_command(ACTION_LIST, OBJECT_TYPE_STRATEGY); break; default: /* "h" */ usage(argv[0]); @@ -271,535 +266,129 @@ int parse_options(int argc, char *argv[], hc_command_t *command, } } - if (command->object.type == OBJECT_UNDEFINED) { - fprintf(stderr, - "Missing object specification: connection | listener | route\n"); - return -1; + // XXX The rest could be made a single parse function + + /* A default action is always defined, let's verify we have an object type, + * unless we are subscribing to notifications. In that case, we can monitor + * all objects. + * XXX handle later + */ + if ((command->object_type == OBJECT_TYPE_UNDEFINED) && + (command->action != ACTION_SUBSCRIBE)) { + ERROR("Missing object specification"); + goto USAGE; } - /* Parse and validate parameters for add/delete */ - switch (command->object.type) { - case OBJECT_FACE: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 5) && (argc - optind != 6)) { - usage_face_create(argv[0], true, false); - goto ERR_PARAM; - } - /* NAME will be autogenerated (and currently not used) */ - // snprintf(command->face.name, SYMBOLIC_NAME_LEN, "%s", - // argv[optind++]); - command->object.face.face.type = face_type_from_str(argv[optind++]); - if (command->object.face.face.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.face.face.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.face.face.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.local_addr) < 0) - goto ERR_PARAM; - command->object.face.face.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.face.face.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.remote_addr) < 0) - goto ERR_PARAM; - command->object.face.face.remote_port = atoi(argv[optind++]); - if (argc != optind) { - // netdevice_set_name(&command->object.face.face.netdevice, - // argv[optind++]); - command->object.face.face.netdevice.index = atoi(argv[optind++]); - } - - break; - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 5) && - (argc - optind != 6)) { - usage_face_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.face.id = atoi(argv[optind++]); - snprintf(command->object.face.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - //} else if (is_symbolic_name(argv[optind])) { - // snprintf(command->object.face.name, SYMBOLIC_NAME_LEN, "%s", - // argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.face.face.type = face_type_from_str(argv[optind++]); - if (command->object.face.face.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.face.face.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.face.face.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.local_addr) < 0) - goto ERR_PARAM; - command->object.face.face.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.face.face.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.face.face.remote_addr) < 0) - goto ERR_PARAM; - command->object.face.face.remote_port = atoi(argv[optind++]); - if (argc != optind) { - command->object.face.face.netdevice.index = atoi(argv[optind++]); - // netdevice_set_name(&command->object.face.face.netdevice, - // argv[optind++]); - } - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_face_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_ROUTE: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 2) && (argc - optind != 3)) { - usage_route_create(argv[0], true, false); - goto ERR_PARAM; - } - - command->object.route.face_id = atoi(argv[optind++]); - - { - ip_prefix_t prefix; - ip_prefix_pton(argv[optind++], &prefix); - command->object.route.family = prefix.family; - command->object.route.remote_addr = prefix.address; - command->object.route.len = prefix.len; - } - - if (argc != optind) { - printf("parse cost\n"); - command->object.route.cost = atoi(argv[optind++]); - } - break; - - case ACTION_DELETE: - if (argc - optind != 2) { - usage_route_delete(argv[0], true, false); - goto ERR_PARAM; - } - - command->object.route.face_id = atoi(argv[optind++]); - - { - ip_prefix_t prefix; - ip_prefix_pton(argv[optind++], &prefix); - command->object.route.family = prefix.family; - command->object.route.remote_addr = prefix.address; - command->object.route.len = prefix.len; - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_route_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_STRATEGY: - switch (command->action) { - case ACTION_LIST: - if (argc - optind != 0) { - usage_forwarding_strategy_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_LISTENER: - switch (command->action) { - case ACTION_CREATE: - if ((argc - optind != 4) && (argc - optind != 5)) { - usage_listener_create(argv[0], true, false); - goto ERR_PARAM; - } - - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - command->object.listener.type = face_type_from_str(argv[optind++]); - if (command->object.listener.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.listener.family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.listener.family)) goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.listener.local_addr) < 0) - goto ERR_PARAM; - command->object.listener.local_port = atoi(argv[optind++]); - if (argc != optind) { - snprintf(command->object.listener.interface_name, INTERFACE_LEN, - "%s", argv[optind++]); - } - break; - - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 3) && - (argc - optind != 4)) { - usage_listener_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.listener.id = atoi(argv[optind++]); - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else if (is_symbolic_name(argv[optind], SYMBOLIC_NAME_LEN)) { - snprintf(command->object.listener.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.listener.type = face_type_from_str(argv[optind++]); - if (command->object.listener.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.listener.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.listener.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.listener.local_addr) < 0) - goto ERR_PARAM; - command->object.listener.local_port = atoi(argv[optind++]); - if (argc != optind) { - snprintf(command->object.listener.interface_name, INTERFACE_LEN, - "%s", argv[optind++]); - } - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_listener_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; - - case OBJECT_CONNECTION: - switch (command->action) { - case ACTION_CREATE: - /* NAME TYPE LOCAL_ADDRESS LOCAL_PORT REMOTE_ADDRESS REMOTE_PORT */ - if ((argc - optind != 6) && (argc - optind != 7)) { - usage_connection_create(argv[0], true, false); - goto ERR_PARAM; - } - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - command->object.connection.type = face_type_from_str(argv[optind++]); - if (command->object.connection.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.connection.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.connection.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.local_addr) < 0) - goto ERR_PARAM; - command->object.connection.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.connection.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.remote_addr) < 0) - goto ERR_PARAM; - command->object.connection.remote_port = atoi(argv[optind++]); - - break; - - case ACTION_DELETE: - if ((argc - optind != 1) && (argc - optind != 5) && - (argc - optind != 6)) { - usage_connection_delete(argv[0], true, false); - goto ERR_PARAM; - } - - if (argc - optind == 1) { - /* Id or name */ - if (is_number(argv[optind], SYMBOLIC_NAME_LEN)) { - command->object.connection.id = atoi(argv[optind++]); - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else if (is_symbolic_name(argv[optind], SYMBOLIC_NAME_LEN)) { - snprintf(command->object.connection.name, SYMBOLIC_NAME_LEN, "%s", - argv[optind++]); - } else { - fprintf(stderr, "Invalid argument\n"); - goto ERR_PARAM; - } - } else { - command->object.connection.type = - face_type_from_str(argv[optind++]); - if (command->object.connection.type == FACE_TYPE_UNDEFINED) - goto ERR_PARAM; - command->object.connection.family = - ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(command->object.connection.family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.local_addr) < 0) - goto ERR_PARAM; - command->object.connection.local_port = atoi(argv[optind++]); - family = ip_address_get_family(argv[optind]); - if (!IS_VALID_FAMILY(family) || - (command->object.connection.family != family)) - goto ERR_PARAM; - if (ip_address_pton(argv[optind++], - &command->object.connection.remote_addr) < 0) - goto ERR_PARAM; - command->object.connection.remote_port = atoi(argv[optind++]); - } - break; - - case ACTION_LIST: - if (argc - optind != 0) { - usage_connection_list(argv[0], true, false); - goto ERR_PARAM; - } - break; - - default: - goto ERR_COMMAND; - } - break; + /* Check the adequation between the number of parameters and the command */ + size_t nparams = argc - optind; + if (nparams > 0) { + if (command->action == ACTION_LIST) command->action = ACTION_GET; + } else { + if ((command->action != ACTION_LIST) && + (command->action != ACTION_SUBSCRIBE)) + goto USAGE; + } - default: - goto ERR_COMMAND; + /* + * This checks is important even with 0 parameters as it checks whether the + * command exists. + */ + if (command->action != ACTION_SUBSCRIBE) { + const command_parser_t *parser = + command_search(command->action, command->object_type, nparams); + if (!parser) { + ERROR("Could not find parser for command '%s %s'", + action_str(command->action), object_type_str(command->object_type)); + return -1; + } + + if (nparams > 0) { + if (parse_getopt_args(parser, argc - optind, argv + optind, command) < + 0) { + ERROR("Error parsing command arguments"); + goto USAGE; + } + } } return 0; -ERR_PARAM: -ERR_COMMAND: - return -1; +USAGE: + usage(argv[0]); + exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { - hc_data_t *data; int rc = 1; hc_command_t command = {0}; - char buf_listener[MAXSZ_HC_LISTENER]; - char buf_connection[MAXSZ_HC_CONNECTION]; - char buf_route[MAXSZ_HC_ROUTE]; - char buf_strategy[MAXSZ_HC_STRATEGY]; + char buf[MAXSZ_HC_OBJECT]; - forwarder_type_t forwarder = HICNLIGHT; + forwarder_type_t forwarder = FORWARDER_TYPE_VPP; if (parse_options(argc, argv, &command, &forwarder) < 0) die(OPTIONS, "Bad arguments"); - hc_sock_t *s = hc_sock_create_forwarder(forwarder); + hc_sock_t *s = hc_sock_create(forwarder, /* url= */ NULL); if (!s) die(SOCKET, "Error creating socket."); if (hc_sock_connect(s) < 0) die(CONNECT, "Error connecting to the forwarder."); - switch (command.object.type) { - case OBJECT_FACE: - switch (command.action) { - case ACTION_CREATE: - if (hc_face_create(s, &command.object.face) < 0) - die(COMMAND, "Error creating face"); - printf("OK\n"); - break; - - case ACTION_DELETE: - if (hc_face_delete(s, &command.object.face, 1) < 0) - die(COMMAND, "Error creating face"); - printf("OK\n"); - break; - - case ACTION_LIST: - if (hc_face_list(s, &data) < 0) - die(COMMAND, "Error getting connections."); - - printf("Faces:\n"); - foreach_face(f, data) { - if (hc_face_snprintf(buf_connection, MAXSZ_HC_FACE, f) >= - MAXSZ_HC_FACE) - die(COMMAND, "Display error"); - printf("[%s] %s\n", f->name, buf_connection); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for connection"); - break; - } - break; - - case OBJECT_ROUTE: - switch (command.action) { - case ACTION_CREATE: - if (hc_route_create(s, &command.object.route) < 0) - die(COMMAND, "Error creating route"); - printf("OK\n"); - break; - - case ACTION_DELETE: - if (hc_route_delete(s, &command.object.route) < 0) - die(COMMAND, "Error creating route"); - printf("OK\n"); - break; - - case ACTION_LIST: - if (hc_route_list(s, &data) < 0) - die(COMMAND, "Error getting routes."); - - printf("Routes:\n"); - foreach_route(r, data) { - if (hc_route_snprintf(buf_route, MAXSZ_HC_ROUTE, r) >= - MAXSZ_HC_ROUTE) - die(COMMAND, "Display error"); - printf("%s\n", buf_route); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for route"); - break; - } - break; - - case OBJECT_STRATEGY: - switch (command.action) { - case ACTION_LIST: - if (hc_strategy_list(s, &data) < 0) - die(COMMAND, "Error getting routes."); - - printf("Forwarding strategies:\n"); - foreach_strategy(st, data) { - if (hc_strategy_snprintf(buf_strategy, MAXSZ_HC_STRATEGY, st) >= - MAXSZ_HC_STRATEGY) - die(COMMAND, "Display error"); - printf("%s\n", buf_strategy); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for strategy"); - break; - } - break; - - case OBJECT_LISTENER: - switch (command.action) { - case ACTION_CREATE: - if (hc_listener_create(s, &command.object.listener) < 0) - die(COMMAND, "Error creating listener"); - printf("OK\n"); - break; - case ACTION_DELETE: - if (hc_listener_delete(s, &command.object.listener) < 0) - die(COMMAND, "Error deleting listener"); - printf("OK\n"); - break; - case ACTION_LIST: - if (hc_listener_list(s, &data) < 0) - die(COMMAND, "Error getting listeners."); - - printf("Listeners:\n"); - foreach_listener(l, data) { - if (hc_listener_snprintf(buf_listener, MAXSZ_HC_LISTENER + 17, l) >= - MAXSZ_HC_LISTENER) - die(COMMAND, "Display error"); - printf("[%d] %s\n", l->id, buf_listener); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for listener"); - break; - } - break; - - case OBJECT_CONNECTION: - switch (command.action) { - case ACTION_CREATE: - if (hc_connection_create(s, &command.object.connection) < 0) - die(COMMAND, "Error creating connection"); - printf("OK\n"); - break; - case ACTION_DELETE: - if (hc_connection_delete(s, &command.object.connection) < 0) - die(COMMAND, "Error creating connection"); - printf("OK\n"); - break; - case ACTION_LIST: - if (hc_connection_list(s, &data) < 0) - die(COMMAND, "Error getting connections."); - - printf("Connections:\n"); - foreach_connection(c, data) { - if (hc_connection_snprintf(buf_connection, MAXSZ_HC_CONNECTION, - c) >= MAXSZ_HC_CONNECTION) - die(COMMAND, "Display error"); - printf("[%s] %s\n", c->name, buf_connection); - } - - hc_data_free(data); - break; - default: - die(COMMAND, "Unsupported command for connection"); - break; - } - break; + hc_data_t *data = NULL; + + rc = hc_execute(s, command.action, command.object_type, &command.object, + &data); + + if (rc < 0) { + switch (rc) { + case INPUT_ERROR: + ERROR("Wrong input parameters"); + break; + case UNSUPPORTED_CMD_ERROR: + ERROR("Unsupported command"); + break; + default: + ERROR("Error executing command"); + break; + } + goto ERR_COMMAND; + } + + if (!data) goto ERR_QUERY; + + if (!hc_data_get_result(data)) goto ERR_DATA; - default: - die(COMMAND, "Unsupported object"); - break; + size_t size = hc_data_get_size(data); + if (size > 0) { + printf("Success: got %ld %s\n", size, object_type_str(command.object_type)); + } else { + printf("Success.\n"); } + if (command.action == ACTION_LIST) { + hc_data_foreach(data, obj, { + rc = hc_object_snprintf(buf, MAXSZ_HC_OBJECT, command.object_type, obj); + if (rc < 0) + WARN("Display error"); + else if (rc >= MAXSZ_HC_OBJECT) + WARN("Output truncated"); + else + printf("%s\n", buf); + }); + } + + hc_data_free(data); + hc_sock_free(s); + return EXIT_SUCCESS; + +ERR_DATA: + hc_data_free(data); +ERR_QUERY: ERR_COMMAND: ERR_CONNECT: hc_sock_free(s); ERR_SOCKET: ERR_OPTIONS: - return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS; + printf("Error.\n"); + return EXIT_FAILURE; } diff --git a/ctrl/libhicnctrl/src/module.h b/ctrl/libhicnctrl/src/module.h new file mode 100644 index 000000000..44ba5ddbb --- /dev/null +++ b/ctrl/libhicnctrl/src/module.h @@ -0,0 +1,131 @@ +#ifndef HICNCTRL_MODULE_H +#define HICNCTRL_MODULE_H + +#include <stdint.h> + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/socket.h> + +#include "request.h" + +/* + * execute is used for sync code (eg. in VPP), while serialize/parse for + * sync/async code (eg. in hicn-light). + */ +typedef int (*hc_execute_t)(hc_sock_t *, hc_object_t *, hc_data_t *); +typedef int (*hc_serialize_t)(const hc_object_t *, uint8_t *); + +typedef struct { + int (*parse)(const uint8_t *buffer, size_t size, hc_object_t *object); + size_t serialized_size; + hc_serialize_t serialize[ACTION_N]; + hc_execute_t execute[ACTION_N]; +} hc_module_object_ops_t; + +#define HC_MODULE_OBJECT_OPS_EMPTY \ + (hc_module_object_ops_t) { \ + .parse = NULL, .serialized_size = 0, \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + } + +#define DECLARE_MODULE_OBJECT_OPS_H(PREFIX, NAME) \ + extern const hc_module_object_ops_t PREFIX##_##NAME##_module_ops; + +/* Underscore'd functions take a hc_object_t as a parameter */ + +#define HC_MODULE_OBJECT_OPS(PREFIX, NAME) \ + (hc_module_object_ops_t) { \ + .parse = _##PREFIX##_##NAME##_parse, \ + .serialized_size = sizeof(cmd_##NAME##_list_item_t), \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \ + } \ + } + +#define DECLARE_MODULE_OBJECT_OPS(PREFIX, NAME) \ + const hc_module_object_ops_t PREFIX##_##NAME##_module_ops = { \ + .parse = _##PREFIX##_##NAME##_parse, \ + .serialized_size = sizeof(cmd_##NAME##_list_item_t), \ + .execute = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + .serialize = { \ + [ACTION_CREATE] = PREFIX##_##NAME##_serialize_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_serialize_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_serialize_list, \ + }}; + +#define DECLARE_VPP_MODULE_OBJECT_OPS(PREFIX, NAME) \ + const hc_module_object_ops_t PREFIX##_##NAME##_module_ops = { \ + .execute = \ + { \ + [ACTION_CREATE] = PREFIX##_##NAME##_create, \ + [ACTION_DELETE] = PREFIX##_##NAME##_delete, \ + [ACTION_LIST] = PREFIX##_##NAME##_list, \ + }, \ + .serialize = \ + { \ + [ACTION_CREATE] = NULL, \ + [ACTION_DELETE] = NULL, \ + [ACTION_LIST] = NULL, \ + }, \ + }; + +typedef struct { + /** Create module-specific data storage */ + void *(*create_data)(const char *); + + /** Release module-specific data storage */ + void (*free_data)(void *); + + /** Retrieve underlying file descriptor */ + int (*get_fd)(hc_sock_t *); + + /** Retrieve underlying receive buffer */ + int (*get_recv_buffer)(hc_sock_t *, uint8_t **buffer, size_t *size); + + /** Connect control socket to the forwarder */ + int (*connect)(hc_sock_t *); + + /** Disconnect control socket from forwarder */ + int (*disconnect)(hc_sock_t *); + + /** Populate the TX buffer with the serialization of the next request to be + * sent */ + ssize_t (*prepare)(hc_sock_t *, hc_request_t *, uint8_t **buffer); + + /** Send the content of the TX buffer */ + int (*send)(hc_sock_t *, uint8_t *buffer, size_t size); + + /** Receive responses in the RX buffer */ + int (*recv)(hc_sock_t *); + + /** Process the content of the RX buffer to populate result data */ + int (*process)(hc_sock_t *, size_t count); + + hc_module_object_ops_t object_vft[OBJECT_TYPE_N]; +} hc_sock_ops_t; + +#endif /* HICNCTRL_MODULE_H */ diff --git a/ctrl/libhicnctrl/src/module_object.c b/ctrl/libhicnctrl/src/module_object.c new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object.c @@ -0,0 +1 @@ + diff --git a/ctrl/libhicnctrl/src/module_object.h b/ctrl/libhicnctrl/src/module_object.h new file mode 100644 index 000000000..5081aa715 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object.h @@ -0,0 +1,10 @@ +#ifndef HICNCTRL_MODULES_OBJECT_H +#define HICNCTRL_MODULES_OBJECT_H + +ssize_t hc_object_serialize(hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, hc_msg_t *msg); + +int hc_object_parse(hc_object_type_t object_type, uint8_t *buffer, + hc_object_t *object); + +#endif /* HICNCTRL_MODULES_OBJECT_H */ diff --git a/ctrl/libhicnctrl/src/module_object_vft.h b/ctrl/libhicnctrl/src/module_object_vft.h new file mode 100644 index 000000000..2651b50a3 --- /dev/null +++ b/ctrl/libhicnctrl/src/module_object_vft.h @@ -0,0 +1,4 @@ +#ifndef HICNCTRL_MODULES_OBJECT_MODULE_VFT_H +#define HICNCTRL_MODULES_OBJECT_MODULE_VFT_H + +#endif /* HICNCTRL_MODULES_OBJECT_MODULE_VFT_H */ diff --git a/ctrl/libhicnctrl/src/modules/CMakeLists.txt b/ctrl/libhicnctrl/src/modules/CMakeLists.txt index 8f7916d14..b85ef29dc 100644 --- a/ctrl/libhicnctrl/src/modules/CMakeLists.txt +++ b/ctrl/libhicnctrl/src/modules/CMakeLists.txt @@ -14,13 +14,28 @@ ############################################################## # Hicn Light NG Module ############################################################## -list(APPEND HICNLIGHTNG_MODULE_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light_common.c - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light_ng_api.c +list(APPEND HICNLIGHT_MODULE_SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/connection.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/face.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/strategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/subscription.c ) -build_module(hicnlightngctrl_module - SOURCES ${HICNLIGHTNG_MODULE_SOURCE_FILES} +list(APPEND HICNLIGHT_MODULE_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/face.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/subscription.h + ) + +build_module(hicnlightctrl_module + SOURCES ${HICNLIGHT_MODULE_SOURCE_FILES} ${HICNLIGHT_MODULE_HEADER_FILES} DEPENDS ${DEPENDENCIES} COMPONENT ${LIBHICNCTRL_COMPONENT} LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} @@ -44,12 +59,21 @@ if(BUILD_HICNPLUGIN AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux") ) endif() + list(APPEND HICN_PLUGIN_SOURCE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin_api.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_plugin/route.c + ) + + list(APPEND HICN_PLUGIN_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/base.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/hicn_light/route.h ) build_module(vppctrl_module - SOURCES ${HICN_PLUGIN_SOURCE_FILES} + SOURCES ${HICN_PLUGIN_SOURCE_FILES} ${HICN_PLUGIN_HEADER_FILES} DEPENDS ${DEPENDENCIES} LINK_LIBRARIES PRIVATE ${HICN_LIBRARIES} @@ -58,6 +82,6 @@ if(BUILD_HICNPLUGIN AND ${CMAKE_SYSTEM_NAME} MATCHES "Linux") COMPONENT ${LIBHICNCTRL_COMPONENT_MODULES} INCLUDE_DIRS PRIVATE ${INCLUDE_DIRS} DEFINITIONS PRIVATE ${COMPILER_DEFINITIONS} - COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} ${MARCH_COMPILER_OPTIONS} "-DHICN_VPP_PLUGIN=1" ) endif() diff --git a/ctrl/libhicnctrl/src/modules/hicn_light.c b/ctrl/libhicnctrl/src/modules/hicn_light.c new file mode 100644 index 000000000..1af6c79e2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light.c @@ -0,0 +1,1255 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_light.c + * \brief Implementation of hicn-light module. + */ + +#include <assert.h> // assert +#include <fcntl.h> // fcntl +#include <stdbool.h> +#include <stdio.h> // snprintf +#include <string.h> // memmove, strcasecmp +#include <sys/socket.h> // socket +#include <sys/types.h> // getpid +#include <unistd.h> // close, fcntl, getpid + +#ifdef __linux__ +#include <sys/syscall.h> +#define gettid() syscall(SYS_gettid) +#endif /* __linux__ */ + +#include <strings.h> + +#include <hicn/ctrl/hicn-light.h> +#include <hicn/ctrl/socket.h> + +#include "../api_private.h" +#include "../objects/connection.h" // hc_connection_has_local +#include "../objects/listener.h" // hc_listener_is_local +#include "../objects/route.h" // hc_route_has_face +#include "../request.h" +#include "../socket_private.h" +#include "hicn_light.h" + +#include "hicn_light/base.h" +#include "hicn_light/connection.h" +#include "hicn_light/listener.h" +#include "hicn_light/face.h" +#include "hicn_light/route.h" +#include "hicn_light/strategy.h" +#include "hicn_light/subscription.h" + +#pragma GCC diagnostic ignored "-Warray-bounds" + +#define DEFAULT_SOCK_RECV_TIMEOUT_MS 100 + +#define PORT 9695 + +#define BOOLSTR(x) ((x) ? "true" : "false") + +hc_sock_light_data_t *hc_sock_light_data_create(const char *url) { + hc_sock_light_data_t *s = malloc(sizeof(hc_sock_light_data_t)); + if (!s) goto ERR_MALLOC; + + s->roff = s->woff = 0; + s->remaining = 0; + s->got_header = false; + + s->url = url ? strdup(url) : NULL; + + s->fd = socket(AF_INET, SOCK_DGRAM, 0); + if (s->fd < 0) goto ERR_SOCKET; + +#if 0 + struct timeval tv = {.tv_sec = 0, + .tv_usec = DEFAULT_SOCK_RECV_TIMEOUT_MS * 1000}; + if (setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + perror("setsockopt"); + goto ERR_TIMEOUT; + } +#endif + + return s; + +#if 0 +ERR_TIMEOUT: +#endif + close(s->fd); +ERR_SOCKET: + if (s->url) free(s->url); + free(s); +ERR_MALLOC: + return NULL; +} + +void hc_sock_light_data_free(hc_sock_light_data_t *data) { + if (data->url) free(data->url); + free(data); +} + +static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; + +/****************************************************************************** + * Control socket + ******************************************************************************/ + +#define AVAILABLE(s) ((s)->woff - (s)->roff) + +/** + * \brief Parse a connection URL into a sockaddr + * \param [in] url - URL + * \param [out] sa - Resulting struct sockaddr, expected zero'ed. + * \return 0 if parsing succeeded, a negative error value otherwise. + */ +static int hicnlight_parse_url(const char *url, struct sockaddr *sa) { + /* FIXME URL parsing is currently not implemented */ + _ASSERT(!url); + +#ifdef __linux__ + srand(time(NULL) ^ getpid() ^ gettid()); +#else + srand((unsigned int)(time(NULL) ^ getpid())); +#endif /* __linux__ */ + + /* + * A temporary solution is to inspect the sa_family fields of the passed in + * sockaddr, which defaults to AF_UNSPEC (0) and thus creates an IPv4/TCP + * connection to localhost. + */ + switch (sa->sa_family) { + case AF_UNSPEC: + case AF_INET: { + struct sockaddr_in *sai = (struct sockaddr_in *)sa; + sai->sin_family = AF_INET; + sai->sin_port = htons(PORT); + sai->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + break; + } + case AF_INET6: { + struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)sa; + sai6->sin6_family = AF_INET6; + sai6->sin6_port = htons(PORT); + sai6->sin6_addr = loopback_addr; + break; + } + default: + return -1; + } + + return 0; +} + +/* + * Return codes: + * < 0 : error; invalid buffer data -> flush + * otherwise, seq_num of the identified request + */ +static int hicnlight_process_header(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + hc_object_type_t object_type = OBJECT_TYPE_UNDEFINED; + + /* Check we have at least a header's worth of data, and consume it */ + if (AVAILABLE(s) < sizeof(hc_msg_header_t)) return 0; + + hc_msg_t *msg = (hc_msg_t *)(s->buf + s->roff); + + // INFO("Processing header header %s", command_type_str(msg->hdr.command_id)); + s->roff += sizeof(hc_msg_header_t); + s->got_header = true; + + /* How many elements are we expecting in the reply ? */ + s->remaining = msg->header.length; + + /* Identify request being parsed */ + int seq = msg->header.seq_num; + hc_request_t *request = NULL; + if (hc_sock_map_get(sock->map, seq, &request) < 0) { + ERROR("[hc_sock_light_process] Error searching for matching request"); + return -1; + } + if (!request) { + ERROR("[hc_sock_light_process] No request matching sequence number"); + return -1; + } + sock->current_request = request; + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + _ASSERT(data); + + switch (msg->header.message_type) { + case ACK_LIGHT: + _ASSERT(s->remaining == 0); + + s->got_header = false; + if (!hc_request_is_subscription(request)) hc_data_set_complete(data); + break; + + case NACK_LIGHT: + _ASSERT(s->remaining == 0); + + s->got_header = false; + hc_data_set_error(data); + break; + + case RESPONSE_LIGHT: + if (s->remaining == 0) { + /* Empty response (i.e. containing 0 elements) */ + s->got_header = false; + hc_data_set_complete(data); + return 0; + } + + /* Allocate buffer for response */ + if (hc_data_allocate(data, s->remaining) < 0) { + ERROR("[hc_sock_light_process] Cannot allocate result buffer"); + return -99; + } + break; + + case NOTIFICATION_LIGHT: { + _ASSERT(s->remaining == 1); + /* + * Assumption: the whole notification data is returned in a single read + * and we immediately parse it. + */ + // XXX assert enough buffer for object type + validate returned object + object_type = (hc_object_type_t)msg->header.command_id; + hc_data_clear(data); + hc_data_set_object_type(data, object_type); + if (hc_data_allocate(data, s->remaining) < 0) { + ERROR("[hc_sock_light_process] Cannot allocate result buffer"); + return -1; + } + + hc_data_push(data, s->buf + s->roff); + + s->roff += AVAILABLE(s); + + hc_request_on_notification(request); + + /* + * The buffer is cleared just before the next notification, which means + * it will have to be released upon exit. Otherwise we break the code + * dumping the notification synchronously (eg. hicnctrl -s). + */ + // hc_data_clear(data); + + s->got_header = false; + break; + } + + default: + ERROR("[hc_sock_light_process] Invalid response received"); + return -99; + } + + return 0; +} + +size_t hc_light_object_size(hc_object_type_t object_type); + +static int hicnlight_process_payload(hc_sock_t *sock) { + int err = 0; + int rc; + + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + hc_request_t *request = hc_sock_get_request(sock); + hc_request_t *current_request = hc_request_get_current(request); + hc_data_t *data = hc_request_get_data(current_request); + + hc_object_type_t object_type = hc_data_get_object_type(data); + size_t object_size = hc_light_object_size(object_type); + if (object_size == 0) return -1; + + /* We only process full elements (size is stored in data) */ + size_t num_chunks = AVAILABLE(s) / object_size; + + /* Check whether we have enough data to process */ + if (num_chunks == 0) return 0; + + /* Safeguard: _ASSERT(num_chunks < s->remaining); */ + if (num_chunks > s->remaining) { + WARN( + "[hicnlight_process_payload] Unexpected num_chunks > " + "s->remaining"); + num_chunks = s->remaining; + } + + for (size_t i = 0; i < num_chunks; i++) { + /* + * Get storage offset in hc_data_t, which we assume is correctly + * provisioned. + * XXX + */ + u8 *src = s->buf + s->roff; + hc_object_t *dst = (hc_object_t *)hc_data_get_free(data); + if (!dst) { + ERROR("[hc_sock_light_process] Error in hc_data_get_next"); + err = -2; + break; + } + + // XXX we might want to display even incomplete data when printing (eg. + // string truncation), and be very strict when processing. + rc = hc_sock_parse_object(sock, hc_data_get_object_type(data), src, + object_size, dst); + s->roff += object_size; + if (rc < 0) { + ERROR("Error parsing received object"); + continue; + } + hc_data_inc_size(data); + } + + /* + * If we are not expecting any more data, mark the reply as complete + */ + s->remaining -= num_chunks; + if (s->remaining == 0) { + s->got_header = false; + hc_data_set_complete(data); + } + + return err; +} + +/*---------------------------------------------------------------------------- + * Socket operations + *----------------------------------------------------------------------------*/ + +static int hicnlight_get_fd(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + return s->fd; +} + +static int hicnlight_get_recv_buffer(hc_sock_t *sock, uint8_t **buffer, + size_t *size) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + *buffer = s->buf + s->woff; + *size = RECV_BUFLEN - s->woff; + + return 0; +} + +static int hicnlight_connect(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + struct sockaddr_storage ss; + memset(&ss, 0, sizeof(struct sockaddr_storage)); + + if (hicnlight_parse_url(s->url, (struct sockaddr *)&ss) < 0) goto ERR_PARSE; + + size_t size = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + if (connect(s->fd, (struct sockaddr *)&ss, (socklen_t)size) < 0) { + perror("connect error"); + goto ERR_CONNECT; + } + return 0; + +ERR_CONNECT: +ERR_PARSE: + return -1; +} + +static int hicnlight_disconnect(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + /* Remove the connection created to send the command. + * + * Note this is done as a best effort and we don't expect to receive any + * answer from the forwarder (hence the NULL pdata pointer in the request). + */ + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection.id = 0; + int rc = + strcpy_s(object.connection.name, sizeof(object.connection.name), "SELF"); + if (rc == EOK) + hc_execute_async(sock, ACTION_DELETE, OBJECT_TYPE_CONNECTION, &object, NULL, + NULL); + + close(s->fd); + + return 0; +} + +static ssize_t hicnlight_prepare_generic(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + _ASSERT(!hc_request_get_data(current_request)); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + _ASSERT(hc_request_get_data(current_request) == NULL); + hc_data_t *data = hc_data_create(object_type); + if (!data) { + ERROR("[hicnlight_prepare_generic] Could not create data storage"); + return -1; + } + hc_request_set_data(current_request, data); + + /* Serialize request into message */ + DEBUG("Calling serialize on %s %s", action_str(action), + object_type_str(object_type)); + ssize_t msg_len = hc_sock_serialize_object(sock, action, object_type, object, + (uint8_t *)&s->msg); + if (msg_len < 0) { + ERROR("[hicnlight_prepare_generic] Could not serialize command %s %s", + action_str(action), object_type_str(object_type)); + return INPUT_ERROR; + } + + s->msg.header.seq_num = hc_request_get_seq(current_request); + + *buffer = (uint8_t *)&s->msg; + return msg_len; +} + +static int hicnlight_send(hc_sock_t *sock, uint8_t *buffer, size_t size) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + + int rc = (int)send(s->fd, buffer, size, 0); + if (rc < 0) { + perror("[hicnlight_send] Error sending message"); + return -1; + } + + // XXX regular behaviour for others + return 0; +} + +// Example : face create udp +// 1) face to connection (immediate) +// connection to local listener (immediate) : why not both at the same +// time listener get +// listener create / nothing +// connection create +// connection get (if needed for populating face_id for instance, aka +// if we +// need to return data) + +static ssize_t hicnlight_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer); + +static ssize_t hicnlight_prepare_subrequest( + hc_sock_t *sock, hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object, uint8_t **buffer) { + WITH_DEBUG({ + if (object) { + char buf[MAXSZ_HC_OBJECT]; + hc_object_snprintf(buf, sizeof(buf), object_type, object); + DEBUG("Creating subrequest %s/%s %s", action_str(action), + object_type_str(object_type), buf); + } + }); + hc_request_make_subrequest(request, action, object_type, object); + return hicnlight_prepare(sock, request, buffer); +} + +/* + * XXX shall we update the object in the request for faces ? it is not done + * for other objects, but for faces it is needed to further add a route !!! + */ +static ssize_t hicnlight_prepare_face_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_face_t *face = &object->face; + + // XXX those objects are created on stack and expected to be valid across + // several calls. A quick fix is to make them static + static hc_object_t connection; + static hc_object_t listener; + + hc_request_state_t state; + const hc_connection_t *conn; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_face_create > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + + switch (face->type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_CREATE); + goto NEXT; + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_LISTENER_CREATE); + goto NEXT; + case FACE_TYPE_UNDEFINED: + case FACE_TYPE_N: + return -99; // Not implemented + } + + case REQUEST_STATE_FACE_CREATE_CONNECTION_CREATE: + if (hc_face_to_connection(face, &connection.connection, true) < 0) { + ERROR("[hc_face_create] Could not convert face to connection."); + return -1; + } + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_CHECK); + + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_CONNECTION, &connection, + buffer); + + case REQUEST_STATE_FACE_CREATE_CONNECTION_CHECK: + /* + * If the newly created face_id was not need, we would only + * need to return the same data result, which contains a ack/nack, + * simply updating the object type. + * + * With the current API, once the connection is created, our only + * solution is to list all connections and compare with the current one + * to find the created connection ID, and thus face ID. + */ + /* Has the connection been successfully created ? */ + if (!data || !hc_data_get_result(data)) return -1; + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_GET); + goto NEXT; + + case REQUEST_STATE_FACE_CREATE_CONNECTION_GET: + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_CONNECTION_VERIFY); + return hicnlight_prepare_subrequest(sock, request, ACTION_GET, + OBJECT_TYPE_CONNECTION, &connection, + buffer); + + case REQUEST_STATE_FACE_CREATE_CONNECTION_VERIFY: + if (!data || hc_data_get_size(data) != 1) return -1; + + /* Newly created connection was found */ + conn = (hc_connection_t *)hc_data_get_buffer(data); + DEBUG("created connection id=%d", conn->id); + object->face.id = conn->id; + + break; + + case REQUEST_STATE_FACE_CREATE_LISTENER_CREATE: + if (hc_face_to_listener(face, &listener.listener) < 0) { + ERROR("Could not convert face to listener."); + return -1; + } + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_CREATE_LISTENER_CHECK); + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_LISTENER, &listener, + buffer); + + break; + + case REQUEST_STATE_FACE_CREATE_LISTENER_CHECK: + /* + * No need for face id here, simply return the hc_data_t structure + * with the ack/nack, and the proper object type + */ + if (!data) return -1; + hc_data_set_object_type(data, OBJECT_TYPE_FACE); + break; + +#if 0 + case REQUEST_STATE_COMPLETE: + hc_data_set_complete(data); + break; +#endif + + default: + return -1; + } + return 0; +} + +static ssize_t hicnlight_prepare_face_list(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_face_t face; + + _ASSERT(action == ACTION_LIST); + _ASSERT(object_type == OBJECT_TYPE_FACE); + + hc_request_state_t state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_face_list > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + + hc_request_set_state(current_request, + REQUEST_STATE_FACE_LIST_CONNECTION_LIST); + return hicnlight_prepare_subrequest( + sock, request, ACTION_LIST, OBJECT_TYPE_CONNECTION, object, buffer); + + case REQUEST_STATE_FACE_LIST_CONNECTION_LIST: + _ASSERT(data); + /* + * 'list connection' succeeded, we just need to allocate hc_data_t, + * create faces from connections, and return the data structure as if it + * was created by the query + */ + hc_data_t *face_data = hc_data_create(object_type); + hc_data_allocate(face_data, hc_data_get_size(data)); + foreach_connection(c, data) { + if (hc_face_from_connection(c, &face) < 0) { + ERROR("[hc_face_list] Could not convert connection to face."); + return -1; + } + hc_data_push(face_data, &face); + } + hc_data_set_complete(face_data); + + hc_request_reset_data(request); + hc_request_set_data(request, face_data); + + /* FACE/LIST could be part of FACE/GET */ + break; + + default: + return -1; + } + + return 0; +} + +static ssize_t hicnlight_prepare_get(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + hc_data_t *data = hc_request_get_data(current_request); + hc_object_t *found; + + hc_request_state_t state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_get > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + _ASSERT(!data); + hc_request_set_state(current_request, REQUEST_STATE_GET_LIST); + return hicnlight_prepare_subrequest(sock, request, ACTION_LIST, + object_type, NULL, buffer); + case REQUEST_STATE_GET_LIST: + _ASSERT(data); + + found = hc_data_find(data, object); + hc_data_t *found_data = hc_data_create(object_type); + if (found) { + hc_data_allocate(found_data, 1); + hc_data_push(found_data, found); + } + hc_data_set_complete(found_data); + hc_request_reset_data(current_request); + hc_request_set_data(current_request, found_data); + return 0; + default: + return -1; /* Unexpected */ + } +} + +// XXX This should process the content of pdata (unless at init), and +// terminate by sending something +static ssize_t hicnlight_prepare_face(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(request); + + _ASSERT(object_type == OBJECT_TYPE_FACE); + + switch (action) { + case ACTION_CREATE: + return hicnlight_prepare_face_create(sock, request, buffer); + case ACTION_LIST: + return hicnlight_prepare_face_list(sock, request, buffer); + default: + return -99; // Not implemented + } + return 0; +} + +static ssize_t hicnlight_prepare_connection_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(action == ACTION_CREATE); + _ASSERT(object_type == OBJECT_TYPE_CONNECTION); + + hc_data_t *data = hc_request_get_data(current_request); + + size_t size; + unsigned pos; + static hc_object_t listener; + const hc_object_t *obj_listener; + hc_data_t *listener_data = NULL; + + hc_request_state_t state; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_connection_create > %s", + hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + /* Two behaviours depending on the content of local_addr and local_port: + * - empty : create connection on all existing listeners, and raise an + * error if none + * - otherwise, check whether a corresponding listener exists, and + * create it if necessary + * + * We assume connection has been already validated. + */ + if (hc_connection_has_local(&object->connection)) { + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_GET); + } else { + /* + * At least part of the local socket specification is missing, match + * against existing listeners + */ + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_LIST); + } + goto NEXT; + + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_LIST: + + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_ITERATE); + // XXX We are currently assuming an object is present for rewrite, fix + // this + return hicnlight_prepare_subrequest(sock, request, ACTION_LIST, + OBJECT_TYPE_LISTENER, NULL, buffer); + + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_ITERATE: + /* + * NOTE: we could create all connections in parallel to speed up + * processing + */ + size = hc_data_get_size(data); + if (size < 0) return -1; + if (size == 0) + /* We are done, we cannot create a connection, return a Nack */ + ; // XXX TODO + // + /* Save the list of listeners for later iteration */ + listener_data = data; + hc_request_clear_data(current_request); // don't free data + data = NULL; + hc_request_set_state_count(current_request, 0); + hc_request_set_state(current_request, REQUEST_STATE_CONNECTION_CREATE_N); + goto NEXT; /* Start iteration */ + + case REQUEST_STATE_CONNECTION_CREATE_N: + /* + * IMPORTANT + * + * For now we only create a connection with the first non-local + * listener. + * + * Creating N connections in a single commands requires other + * changes to the code that we might done later: + * - ack/nack is not sufficient, all create function should return the + * list of created connections + * - this would allow us to avoid a GET at the end of face creation to + * retrieve the connection id. + * - face create should correspond to N connection create (should work + * out of the box provided we don't expect a single connection back). + * - route+face creation might then create N faces, and thus we would + * have to add N routes. + */ + assert(listener_data); + + // We need to back it up as the subrequest will clear the results + pos = hc_request_get_state_count(current_request); + size = hc_data_get_size(listener_data); + /* We have data if pos > 0, and we did not skipped previous ones */ + if (data && !hc_data_get_result(data)) { + INFO("Failed to create connection for listener %d / %d", pos - 1, size); + // XXX we might allow connections that already exist... how to manage + // the names + return -1; + } + + /* + * Previous connection was successfully created, let's continue but + * first check whether we reached the last one, which would complete the + * request. + */ + if (pos >= size) { + hc_data_free(listener_data); + hc_request_set_state(request, REQUEST_STATE_COMPLETE); + goto NEXT; + } + + /* Sending count'th connection creation */ + obj_listener = hc_data_get_object(listener_data, pos); + + // Filter which listener we use + // same protocol ? ip ? port ? + // avoid local ? + if (hc_listener_is_local(&obj_listener->listener)) { + /* Skip listener */ + DEBUG("Skipped local listener"); + hc_request_set_state_count(current_request, pos + 1); + goto NEXT; + } + + DEBUG("Creating connection with listener # %d / %d", pos, size); + /* We complement missing information from listener */ + // XXX is_family, etc. + object->connection.family = obj_listener->listener.family; + object->connection.local_addr = obj_listener->listener.local_addr; + object->connection.local_port = obj_listener->listener.local_port; + snprintf(object->connection.interface_name, INTERFACE_LEN, "%s", + obj_listener->listener.interface_name); + + hc_request_set_state_count(current_request, pos + 1); + return hicnlight_prepare_subrequest( + sock, request, ACTION_CREATE, OBJECT_TYPE_CONNECTION, object, buffer); + + /* Request listener to further check existence */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_GET: + /* Ensure we have a corresponding local listener */ + if (hc_connection_to_local_listener(&object->connection, + &listener.listener) < 0) { + ERROR( + "[hicnlight_prepare_connection_create] Could not convert face " + "to " + "local listener."); + return -1; + } + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_VERIFY); + return hicnlight_prepare_subrequest( + sock, request, ACTION_GET, OBJECT_TYPE_LISTENER, &listener, buffer); + + break; + + /* Check whether listener exists in GET results */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_VERIFY: + if (!data) return -1; + switch (hc_data_get_size(data)) { + case 0: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_CREATE); + break; + case 1: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE); + break; + default: + return -1; + } + goto NEXT; + + /* Create associated listener */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_CREATE: + hc_request_set_state(current_request, + REQUEST_STATE_CONNECTION_CREATE_LISTENER_CHECK); + return hicnlight_prepare_subrequest(sock, request, ACTION_CREATE, + OBJECT_TYPE_LISTENER, &listener, + buffer); + + /* Check whether listener creation succeeded */ + case REQUEST_STATE_CONNECTION_CREATE_LISTENER_CHECK: + if (!data || hc_data_get_result(data)) return -1; + hc_request_set_state(current_request, REQUEST_STATE_CONNECTION_CREATE); + goto NEXT; + + /* Create connection */ + case REQUEST_STATE_CONNECTION_CREATE: + /* + * Break recursion by directly calling hicnlight_prepare_generic on + * the initial request, that can now be executed since all + * prerequisites are validated. + */ + // return hicnlight_prepare_subrequest( + // sock, request, ACTION_CREATE, OBJECT_TYPE_CONNECTION, object, + // buffer); + hc_request_reset_data(current_request); + hc_request_set_state(current_request, REQUEST_STATE_COMPLETE); + return hicnlight_prepare_generic(sock, request, buffer); + + case REQUEST_STATE_COMPLETE: + if (data) { + hc_data_set_complete(data); + } else { + /* + * No connection has been created, and we freed the data due to + * subrequest + */ + data = hc_data_create(OBJECT_TYPE_CONNECTION); + hc_data_set_error(data); + } + break; + + default: + return -1; + } + return 0; +} + +static ssize_t hicnlight_prepare_route_create(hc_sock_t *sock, + hc_request_t *request, + uint8_t **buffer) { + hc_request_t *current_request = hc_request_get_current(request); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(action == ACTION_CREATE); + _ASSERT(object_type == OBJECT_TYPE_ROUTE); + + hc_data_t *data = hc_request_get_data(current_request); + const hc_object_t *face_obj; + + hc_request_state_t state; + +NEXT: + state = hc_request_get_state(current_request); + DEBUG("hicnlight_prepare_route_create > %s", hc_request_state_str(state)); + + switch (state) { + case REQUEST_STATE_INIT: + if (hc_route_has_face(&object->route)) + hc_request_set_state(current_request, + REQUEST_STATE_ROUTE_CREATE_FACE_CREATE); + else + hc_request_set_state(current_request, REQUEST_STATE_ROUTE_CREATE); + goto NEXT; + + case REQUEST_STATE_ROUTE_CREATE_FACE_CREATE: + hc_request_set_state(current_request, + REQUEST_STATE_ROUTE_CREATE_FACE_CHECK); + INFO(">>>>>>subrequest create face"); + return hicnlight_prepare_subrequest( + sock, request, ACTION_CREATE, OBJECT_TYPE_FACE, + (hc_object_t *)&object->route.face, buffer); + + case REQUEST_STATE_ROUTE_CREATE_FACE_CHECK: + if (!data) return -1; + int rc = hc_data_get_result(data); + if (rc < 0) return -1; + + if (hc_data_get_size(data) != 1) return -1; + + face_obj = hc_data_get_object(data, 0); + DEBUG("Created face id=%d", face_obj->face.id); + object->route.face_id = face_obj->face.id; + + hc_request_set_state(current_request, REQUEST_STATE_ROUTE_CREATE); + goto NEXT; + + /* Create route */ + case REQUEST_STATE_ROUTE_CREATE: + /* + * Break recursion by directly calling hicnlight_prepare_generic on the + * initial request, that can now be executed since all prerequisites are + * validated. + */ + hc_request_set_state(current_request, REQUEST_STATE_COMPLETE); + return hicnlight_prepare_generic(sock, request, buffer); + + case REQUEST_STATE_COMPLETE: + hc_data_set_complete(data); + break; + + default: + return -1; + } + return 0; +} + +static int hicnlight_recv(hc_sock_t *sock) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + int rc; + + /* + * This condition should be ensured to guarantee correct processing of + * messages. With TCP, we need at least a header as we will receive part of + * the stream. With UDP, we need the be able to receive the full datagram, + * otherwise the rest will be lost. + * + * Let's be sure to always be able to receive at least 1 JUMBO_MTU, which + * should be fine for al situations. + */ + _ASSERT(RECV_BUFLEN - s->woff > JUMBO_MTU); + + rc = (int)recv(s->fd, s->buf + s->woff, RECV_BUFLEN - s->woff, 0); + if (rc == 0) { + /* Connection has been closed */ + return 0; + } + if (rc < 0) { + /* + * Let's not return 0 which currently means the socket has been closed + */ + if (errno == EWOULDBLOCK) { + // XXX TODO ?if (hc_request_get_action(request) == ACTION_SUBSCRIBE) + // return 0; + return -1; + } + if (errno == EINTR) { + WARN("recv has been stopped by signal"); + return -1; + } + perror("hc_sock_light_recv"); + return -1; + } + DEBUG("Received rc=%ld bytes", rc); + s->woff += rc; + + return rc; +} + +/* + * + * @param [in] data - hc_data_t structure allocated for the request + * + * This function is the entry point for all requests, and from there we will + * decide whether + * + */ +static ssize_t hicnlight_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + // XXX when do we create data... once for every step + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + static hc_object_t object_subscribe; + + DEBUG("[hicnlight_prepare] %s %s", action_str(action), + object_type_str(object_type)); + + /* + * Here the request is in progress and we just need to iterate through the + * FSM, or complete it. + */ + /* + * Specific treatment for + * CREATE/ROUTE with face + * SUBSCRIBE/(*) + * GET/(*) + * (*)/FACE + */ + + /* + * Special treatment for faces. + * + * This function will be called multiple times in order to process the + * complex request, involving several calls to the API. The process is + * responsible for going through the related state machine, and complete the + * request when appropriate. + */ + if (object_type == OBJECT_TYPE_FACE) + return hicnlight_prepare_face(sock, request, buffer); + + switch (action) { + case ACTION_CREATE: + switch (object_type) { + case OBJECT_TYPE_ROUTE: + /* Route might require face creation */ + return hicnlight_prepare_route_create(sock, request, buffer); + case OBJECT_TYPE_CONNECTION: + /* Connection could have no corresponging listener, or no local info + * provided */ + return hicnlight_prepare_connection_create(sock, request, buffer); + default: + break; + } + break; + + case ACTION_GET: + return hicnlight_prepare_get(sock, request, buffer); + + case ACTION_SUBSCRIBE: + /* Transform subscription queries */ + memset(&object_subscribe, 0, sizeof(hc_object_t)); + object->subscription.topics = topic_from_object_type(object_type); + + hc_request_set(request, ACTION_CREATE, OBJECT_TYPE_SUBSCRIPTION, object); + break; + + default: + break; + } + + /* + * Generic requests should complete after a single call to hicnlight_send, + * with *pdata = NULL. If *pdata is not NULL, that means the request has + * completed and we can close it. + * It is the responsability of each state machine to complete the request + * otherwise. + */ +#if 1 + hc_data_t *data = hc_request_get_data(current_request); + if (data) { + hc_request_set_complete(current_request); + return 0; + } +#endif + + return hicnlight_prepare_generic(sock, request, buffer); +} + +/* + * This function processes incoming data in the ring buffer. Multiple requests + * might be interleaves, including regular requests and notifications. + * Responses might arrive fragment over several read events, but our + * assumption is that fragments arrive consecutively and are not interleaves + * with fragments from other requests... otherwise we would have to way to + * reconstruct a message. + * + * count != 0 when an external process has added data to the ring buffer + * without updating indices + */ +static int hicnlight_process(hc_sock_t *sock, size_t count) { + hc_sock_light_data_t *s = (hc_sock_light_data_t *)sock->data; + int rc; + + if (count > 0) s->woff += count; + + /* + * We loop consuming messages until there is no more data in the ring + * buffer, or that we can find an entire message. Messages are received + * sequentially, and we keep track of incomplete requests in s->cur_request. + */ + while (AVAILABLE(s) > 0) { + if (!s->got_header) { + rc = hicnlight_process_header(sock); + } else { + rc = hicnlight_process_payload(sock); + } + if (rc < 0) break; + } + + if ((rc == -99) || (s->roff == s->woff)) { + /* Flush buffer */ + s->woff = 0; + } else { + /* Clean up read data from buffer */ + memmove(s->buf, s->buf + s->roff, AVAILABLE(s)); + s->woff -= s->roff; + } + s->roff = 0; + + return rc; +} + +hc_sock_ops_t hc_sock_light = (hc_sock_ops_t) { + .create_data = (void *(*)(const char *))hc_sock_light_data_create, + .free_data = (void (*)(void *))hc_sock_light_data_free, + .get_fd = hicnlight_get_fd, .get_recv_buffer = hicnlight_get_recv_buffer, + .connect = hicnlight_connect, .disconnect = hicnlight_disconnect, + .prepare = hicnlight_prepare, .send = hicnlight_send, .recv = hicnlight_recv, + .process = hicnlight_process, +#if 0 + .object_vft = { + [OBJECT_TYPE_LISTENER] = HC_MODULE_OBJECT_OPS(hicnlight, listener), + [OBJECT_TYPE_CONNECTION] = HC_MODULE_OBJECT_OPS(hicnlight, connection), + [OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY, + [OBJECT_TYPE_ROUTE] = HC_MODULE_OBJECT_OPS(hicnlight, route), + [OBJECT_TYPE_STRATEGY] = HC_MODULE_OBJECT_OPS(hicnlight, strategy), + [OBJECT_TYPE_SUBSCRIPTION] = HC_MODULE_OBJECT_OPS(hicnlight, subscription), +} +#endif +}; + +size_t hc_light_object_size(hc_object_type_t object_type) { + hc_module_object_ops_t *vft = &hc_sock_light.object_vft[object_type]; + if (!vft) return 0; + return vft->serialized_size; +} + +ssize_t hc_light_command_serialize(hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + hc_module_object_ops_t *vft = &hc_sock_light.object_vft[object_type]; + if (!vft || !vft->serialize[action]) return 0; + return vft->serialize[action](object, msg); +} + +// Public constructor + +int hc_sock_initialize_module(hc_sock_t *s) { + // + /* + * We do this because initialization in the static struct fails with + * 'initializer element is not constant' + */ +#if 1 + hc_sock_light.object_vft[OBJECT_TYPE_LISTENER] = + hicnlight_listener_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_CONNECTION] = + hicnlight_connection_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY; + hc_sock_light.object_vft[OBJECT_TYPE_ROUTE] = hicnlight_route_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_STRATEGY] = + hicnlight_strategy_module_ops; + hc_sock_light.object_vft[OBJECT_TYPE_SUBSCRIPTION] = + hicnlight_subscription_module_ops; +#endif + + if (s) s->ops = hc_sock_light; + return 0; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light.h b/ctrl/libhicnctrl/src/modules/hicn_light.h new file mode 100644 index 000000000..0bdcf0b30 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_light.h + * \brief hicn-light module. + */ + +#ifndef HICNCTRL_MODULES_HICN_LIGHT_H +#define HICNCTRL_MODULES_HICN_LIGHT_H + +#include "hicn_light/base.h" + +typedef struct { + char *url; + int fd; + + /* Send buffer */ + hc_msg_t msg; + + /* Partial receive buffer */ + u8 buf[RECV_BUFLEN]; + size_t roff; /**< Read offset */ + size_t woff; /**< Write offset */ + + bool got_header; + /* + * Because received messages are potentially unbounded in size, we might not + * guarantee that we can store a full packet before processing it. We must + * implement a very simple state machine remembering the current parsing + * status in order to partially process the packet. + */ + size_t remaining; + u32 send_id; + + /* Next sequence number to be used for requests */ + int seq; +} hc_sock_light_data_t; + +extern hc_sock_light_data_t *hc_sock_light_data_create(const char *url); +extern void hc_sock_light_data_free(hc_sock_light_data_t *data); + +ssize_t hc_light_command_serialize(hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg); + +#endif /* HICNCTRL_MODULES_HICN_LIGHT_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/base.h b/ctrl/libhicnctrl/src/modules/hicn_light/base.h new file mode 100644 index 000000000..fb6a68147 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/base.h @@ -0,0 +1,60 @@ +#ifndef HICNCTRL_MODULES_HICNLIGHT_BASE_H +#define HICNCTRL_MODULES_HICNLIGHT_BASE_H + +#include <hicn/ctrl/hicn-light.h> + +#if 1 +#ifdef __APPLE__ +#define RANDBYTE() (u8)(arc4random() & 0xFF) +#else +#define RANDBYTE() (u8)(random() & 0xFF) +#endif +#else +#define RANDBYTE() (u8)(rand() & 0xFF) +#endif + +#define foreach_hc_command \ + _(connection_add) \ + _(connection_remove) \ + _(connection_list) \ + _(listener_add) \ + _(listener_remove) \ + _(listener_list) \ + _(route_add) \ + _(route_remove) \ + _(route_list) \ + _(cache_set_store) \ + _(cache_set_serve) \ + _(cache_clear) \ + _(cache_list) \ + _(strategy_set) \ + _(strategy_add_local_prefix) \ + _(wldr_set) \ + _(punting_add) \ + _(mapme_activator) \ + _(mapme_timing) \ + _(subscription_add) \ + _(subscription_remove) + +#if 0 +const char *command_type_str[] = { +#define _(l, u) [COMMAND_TYPE_##u] = STRINGIZE(u), + foreach_command_type +#undef _ +}; +#endif + +typedef union { +#define _(x) cmd_##x##_t x; + foreach_hc_command +#undef _ +} hc_msg_payload_t; + +typedef cmd_header_t hc_msg_header_t; + +typedef struct hc_msg_s { + hc_msg_header_t header; + hc_msg_payload_t payload; +} hc_msg_t; + +#endif /* HICNCTRL_MODULES_HICNLIGHT_BASE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/cache.c b/ctrl/libhicnctrl/src/modules/hicn_light/cache.c new file mode 100644 index 000000000..e085142d7 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/cache.c @@ -0,0 +1,178 @@ +#include "cache.h" + +/* CACHE SET STORE */ + +static int _hcng_cache_set_store_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_set_store_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_SET_STORE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = cache->store, + }}; + + hc_command_params_t params = { + .cmd = ACTION_STORE, + .cmd_id = COMMAND_TYPE_CACHE_SET_STORE, + .size_in = sizeof(cmd_cache_set_store_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_store_internal(s, cache, false); +} + +static int _hcng_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_store_internal(s, cache, true); +} + +/* CACHE SET SERVE */ + +static int _hcng_cache_set_serve_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_set_serve_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_SET_SERVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = cache->serve, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SERVE, + .cmd_id = COMMAND_TYPE_CACHE_SET_SERVE, + .size_in = sizeof(cmd_cache_set_serve_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; /// added +} + +static int _hcng_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_serve_internal(s, cache, false); +} + +static int _hcng_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_set_serve_internal(s, cache, true); +} + +/* CACHE CLEAR */ + +static int _hcng_cache_clear_internal(hc_sock_t *socket, hc_cache_t *cache, + bool async) { +#if 0 + msg_cache_clear_t msg = {.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_CLEAR, + .length = 1, + .seq_num = 0, + }}; + + hc_command_params_t params = { + .cmd = ACTION_CLEAR, + .cmd_id = COMMAND_TYPE_CACHE_CLEAR, + .size_in = sizeof(cmd_cache_clear_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_cache_clear(hc_sock_t *s, hc_cache_t *cache) { + return _hcng_cache_clear_internal(s, cache, false); +} + +/* CACHE PARSE */ + +static int hc_cache_parse(void *in, hc_cache_info_t *cache_info) { + cmd_cache_list_reply_t *item = (cmd_cache_list_reply_t *)in; + *cache_info = (hc_cache_info_t){.store = item->store_in_cs, + .serve = item->serve_from_cs, + .cs_size = item->cs_size, + .num_stale_entries = item->num_stale_entries}; + + return 0; +} + +/* CACHE LIST */ + +static hc_result_t *_hcng_cache_list_serialize(hc_sock_t *socket, + hc_data_t **pdata, bool async) { + hc_result_t *res = malloc(sizeof(*res)); + DEBUG("[hc_cache_list] async=%s", BOOLSTR(async)); + + msg_cache_list_t msg = {.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CACHE_LIST, + .length = 0, + .seq_num = 0, + }}; + + hc_command_params_t params = { + .cmd = ACTION_LIST, + .cmd_id = COMMAND_TYPE_CACHE_LIST, + .size_in = sizeof(cmd_cache_list_reply_t), + .size_out = sizeof(hc_cache_info_t), + .parse = (HC_PARSE)hc_cache_parse, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.cache_list = msg.payload, + }, + .params = params, + .async = async, + .success = true, + }; + return res; +} + +static int _hcng_cache_list_internal(hc_sock_t *socket, hc_data_t **pdata, + bool async) { +#if 0 + hc_result_t *result = _hcng_cache_list_serialize(socket, pdata, async); + + int ret = INPUT_ERROR; + if (result->success) { + ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, pdata, + result->async); + } + + hc_result_free(result); + return ret; +#endif + return 0; // XXX added +} + +static int _hcng_cache_list(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_cache_list_internal(s, pdata, false); +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/connection.c b/ctrl/libhicnctrl/src/modules/hicn_light/connection.c new file mode 100644 index 000000000..c7d06415e --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <stdint.h> + +#include <hicn/util/log.h> + +#include "base.h" +#include "connection.h" +#include "../../object_private.h" + +int hc_connection_to_local_listener(const hc_connection_t *connection, + hc_listener_t *listener) { + int rc; + + face_type_t listener_type; + switch (connection->type) { + case FACE_TYPE_UDP: + listener_type = FACE_TYPE_UDP_LISTENER; + break; + case FACE_TYPE_TCP: + listener_type = FACE_TYPE_TCP_LISTENER; + break; + default: + return -1; + } + + *listener = (hc_listener_t){ + .id = ~0, + .type = listener_type, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + }; + rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "lst%u", + RANDBYTE()); // generate name + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_connection_to_local_listener] Unexpected truncation of " + "symbolic name string"); + rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", + connection->interface_name); + if (rc >= INTERFACE_LEN) + WARN( + "[hc_connection_to_local_listener] Unexpected truncation of " + "interface name string"); + + return 0; +} + +/* CONNECTION PARSE */ + +static int hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_connection_t *connection) { + int rc; + + if (size != sizeof(cmd_connection_list_item_t)) return -1; + cmd_connection_list_item_t *item = (cmd_connection_list_item_t *)buffer; + + if (!IS_VALID_ID(item->id)) { + ERROR("[hc_connection_parse] Invalid id received"); + return -1; + } + + if (!IS_VALID_NAME(item->name)) { + ERROR("[hc_connection_parse] Invalid name received"); + return -1; + } + if (!IS_VALID_INTERFACE_NAME(item->interface_name)) { + ERROR("[hc_connection_parse] Invalid interface_name received"); + return -1; + } + + if (!IS_VALID_TYPE(item->type)) { + ERROR("[hc_connection_parse] Invalid type received"); + return -1; + } + + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_connection_parse] Invalid family received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->local_address)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + if (!IS_VALID_PORT(ntohs(item->local_port))) { + ERROR("[hc_connection_parse] Invalid port received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->remote_address)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + if (!IS_VALID_PORT(ntohs(item->remote_port))) { + ERROR("[hc_connection_parse] Invalid port received"); + return -1; + } + + if (!IS_VALID_FACE_STATE(item->admin_state)) { + ERROR("[hc_connection_parse] Invalid admin_state received"); + return -1; + } + + // PRIORITY + // TAGS + + if (!IS_VALID_FACE_STATE(item->state)) { + ERROR("[hc_connection_parse] Invalid state received"); + return -1; + } + + *connection = (hc_connection_t){ + .id = item->id, + .type = (face_type_t)item->type, + .family = (int)item->family, + .local_addr = item->local_addr, + .local_port = ntohs(item->local_port), + .remote_addr = item->remote_addr, + .remote_port = ntohs(item->remote_port), + .admin_state = (face_state_t)item->admin_state, + .priority = item->priority, + .tags = item->tags, + .state = (face_state_t)item->state, + }; + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", item->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", + item->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + if (hc_connection_validate(connection, false) < 0) return -1; + return 0; +} + +int _hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_connection_parse(buffer, size, &object->connection); +} + +/* CONNECTION CREATE */ + +int hicnlight_connection_serialize_create(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_add_t *msg = (msg_connection_add_t *)packet; + *msg = (msg_connection_add_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .remote_ip = connection->remote_addr, + .local_ip = connection->local_addr, + .remote_port = htons(connection->remote_port), + .local_port = htons(connection->local_port), + .family = (uint8_t)connection->family, + .type = (uint8_t)connection->type, + .admin_state = (uint8_t)connection->admin_state, + .__pad = 0, + .priority = connection->priority, + .tags = connection->tags, + }}; + + rc = snprintf(msg->payload.symbolic, SYMBOLIC_NAME_LEN, "%s", + connection->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_connection_add_t); +} + +/* CONNECTION DELETE */ + +int hicnlight_connection_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_remove_t *msg = (msg_connection_remove_t *)packet; + *msg = (msg_connection_remove_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + }; + + if (connection->id) { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + connection->id); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); + } else if (*connection->name) { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + connection->name); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); +#if 0 + } else { + hc_connection_t *connection_found; + if (hc_connection_get(socket, connection, &connection_found) < 0) + return res; + if (!connection_found) return res; + rc = snprintf(payload->symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + connection_found->id); + // XXX + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_delete] Unexpected truncation of symbolic name " + "string"); + free(connection_found); +#endif + } + + return sizeof(msg_connection_remove_t); +} + +// XXX How to update a connection XXX +// Key attributes are mandatory +// Enum can be undefined +// family UNSPEC +// ip address NULL +// port NULL +// priority = int ????????? specific negative value == unspec +// tags = bitmap ????????? 0xFFFFFF special value == unspec + +// u32 id; /* Kr. */ +// char name[SYMBOLIC_NAME_LEN]; /* K.w */ +// char interface_name[INTERFACE_LEN]; /* Kr. */ +// +// netdevice_type_t netdevice_type; undefined +// face_type_t type; +// int family; +// ip_address_t local_addr; +// u16 local_port; +// ip_address_t remote_addr; +// u16 remote_port; +// face_state_t admin_state; +// uint32_t priority; /* .rw */ +// policy_tags_t tags; /* .rw */ +// face_state_t state; /* .r. */ +int hicnlight_connection_serialize_update(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_connection_t *connection = &object->connection; + + msg_connection_update_t *msg = (msg_connection_update_t *)packet; + *msg = (msg_connection_update_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_UPDATE, + .length = 1, + .seq_num = 0, + }, + .payload = { + //.remote_ip = connection->remote_updater, + //.local_ip = connection->local_updater, + //.remote_port = htons(connection->remote_port), + //.local_port = htons(connection->local_port), + //.family = connection->family, + //.type = connection->type, + .admin_state = connection->admin_state, + .priority = connection->priority, + .tags = connection->tags, + }}; + + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + connection->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + // snprintf(msg.payload.interface_name, INTERFACE_NAME_LEN, "%s", + // connection->interface_name); + + return sizeof(msg_connection_update_t); +} + +#if 0 +/* CONNECTION SET ADMIN STATE */ + +static int _hicnlight_connection_set_admin_state_internal( + hc_sock_t *socket, const char *conn_id_or_name, face_state_t state, + bool async) { + int rc; + DEBUG( + "[hc_connection_set_admin_state] connection_id/name=%s admin_state=%s " + "async=%s", + conn_id_or_name, face_state_str(state), BOOLSTR(async)); + + struct { + cmd_header_t hdr; + cmd_connection_set_admin_state_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .admin_state = state, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_admin_state] Unexpected truncation of symbolic " + "name string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, + .size_in = sizeof(cmd_connection_set_admin_state_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +} + +static int _hicnlight_connection_set_admin_state(hc_sock_t *s, + const char *conn_id_or_name, + face_state_t state) { + return _hicnlight_connection_set_admin_state_internal(s, conn_id_or_name, state, + false); +} + +static int _hicnlight_connection_set_admin_state_async(hc_sock_t *s, + const char *conn_id_or_name, + face_state_t state) { + return _hicnlight_connection_set_admin_state_internal(s, conn_id_or_name, state, + true); +} + + +static int _hicnlight_connection_set_priority_internal(hc_sock_t *socket, + const char *conn_id_or_name, + uint32_t priority, + bool async) { + int rc; + DEBUG( + "[hc_connection_set_priority] connection_id/name=%s priority=%d " + "async=%s", + conn_id_or_name, priority, BOOLSTR(async)); + struct { + cmd_header_t hdr; + cmd_connection_set_priority_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .priority = priority, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_priority] Unexpected truncation of symbolic " + "name " + "string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, + .size_in = sizeof(cmd_connection_set_priority_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +} + +static int _hicnlight_connection_set_priority(hc_sock_t *s, + const char *conn_id_or_name, + uint32_t priority) { + return _hicnlight_connection_set_priority_internal(s, conn_id_or_name, priority, + false); +} + +static int _hicnlight_connection_set_priority_async(hc_sock_t *s, + const char *conn_id_or_name, + uint32_t priority) { + return _hicnlight_connection_set_priority_internal(s, conn_id_or_name, priority, + true); +} + + +static int _hicnlight_connection_set_tags_internal(hc_sock_t *s, + const char *conn_id_or_name, + policy_tags_t tags, bool async) { + int rc; + DEBUG("[hc_connection_set_tags] connection_id/name=%s tags=%d async=%s", + conn_id_or_name, tags, BOOLSTR(async)); + struct { + cmd_header_t hdr; + cmd_connection_set_tags_t payload; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_SET_TAGS, + .length = 1, + .seq_num = 0, + }, + .payload = + { + .tags = tags, + }, + }; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[_hc_connection_set_tags] Unexpected truncation of symbolic name " + "string"); + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_CONNECTION_SET_TAGS, + .size_in = sizeof(cmd_connection_set_tags_t), + .size_out = 0, + .parse = NULL, + }; + + return _hicnlight_execute_command(s, (hc_msg_t *)&msg, sizeof(msg), ¶ms, NULL, + async); +} + +static int _hicnlight_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, + policy_tags_t tags) { + return _hicnlight_connection_set_tags_internal(s, conn_id_or_name, tags, false); +} + +static int _hicnlight_connection_set_tags_async(hc_sock_t *s, + const char *conn_id_or_name, + policy_tags_t tags) { + return _hicnlight_connection_set_tags_internal(s, conn_id_or_name, tags, true); +} +#endif + +/* CONNECTION LIST */ + +int hicnlight_connection_serialize_list(const hc_object_t *object, + uint8_t *packet) { + msg_connection_list_t *msg = (msg_connection_list_t *)packet; + *msg = (msg_connection_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_CONNECTION_LIST, + .length = 0, + .seq_num = 0, + }}; + return sizeof(msg_header_t); // Do not use msg_connection_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, connection); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/connection.h b/ctrl/libhicnctrl/src/modules/hicn_light/connection.h new file mode 100644 index 000000000..77204e6b2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/connection.h @@ -0,0 +1,27 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H +#define HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H + +#include "../../module.h" + +int hc_connection_to_local_listener(const hc_connection_t *connection, + hc_listener_t *listener); + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, connection); +// extern const hc_module_object_ops_t hicnlight_connection_module_ops; + +#else + +int _hicnlight_connection_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_connection_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_connection_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_connection_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif +#endif /* HICNCTRL_MODULE_HICNLIGHT_CONNECTION_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/face.c b/ctrl/libhicnctrl/src/modules/hicn_light/face.c new file mode 100644 index 000000000..0d9475420 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.c @@ -0,0 +1,482 @@ +#include <hicn/ctrl/objects/listener.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "face.h" + +int hc_face_from_connection(const hc_connection_t *connection, + hc_face_t *face) { + int rc; + switch (connection->type) { + case FACE_TYPE_TCP: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_TCP, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + .remote_addr = connection->remote_addr, + .remote_port = connection->remote_port, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + case FACE_TYPE_UDP: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_UDP, + .family = connection->family, + .local_addr = connection->local_addr, + .local_port = connection->local_port, + .remote_addr = connection->remote_addr, + .remote_port = connection->remote_port, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + case FACE_TYPE_HICN: + *face = (hc_face_t){ + .id = connection->id, + .type = FACE_TYPE_HICN, + .family = connection->family, + .netdevice.index = NETDEVICE_UNDEFINED_INDEX, // XXX + .local_addr = connection->local_addr, + .remote_addr = connection->remote_addr, + .admin_state = connection->admin_state, + .state = connection->state, + .priority = connection->priority, + .tags = connection->tags, + }; + break; + default: + return -1; + } + face->netdevice.name[0] = '\0'; + face->netdevice.index = 0; + rc = snprintf(face->name, SYMBOLIC_NAME_LEN, "%s", connection->name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_connection_to_face] Unexpected truncation of symbolic name " + "string"); + rc = snprintf(face->netdevice.name, INTERFACE_LEN, "%s", + connection->interface_name); + if (rc >= INTERFACE_LEN) + WARN( + "[hc_connection_to_face] Unexpected truncation of interface name " + "string"); + netdevice_update_index(&face->netdevice); + return 0; +} + +int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, + bool generate_name) { + int rc; + + switch (face->type) { + case FACE_TYPE_HICN: + *connection = (hc_connection_t){ + .type = FACE_TYPE_HICN, + .family = face->family, + .local_addr = face->local_addr, + .local_port = 0, + .remote_addr = face->remote_addr, + .remote_port = 0, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", + face->netdevice.name); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of symbolic " + "name string"); + break; + case FACE_TYPE_TCP: + *connection = (hc_connection_t){ + .type = FACE_TYPE_TCP, + .family = face->family, + .local_addr = face->local_addr, + .local_port = face->local_port, + .remote_addr = face->remote_addr, + .remote_port = face->remote_port, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + if (generate_name) { + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "tcp%u", RANDBYTE()); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of " + "symbolic name string"); + } else { + memset(connection->name, 0, SYMBOLIC_NAME_LEN); + } + break; + case FACE_TYPE_UDP: + *connection = (hc_connection_t){ + .type = FACE_TYPE_UDP, + .family = face->family, + .local_addr = face->local_addr, + .local_port = face->local_port, + .remote_addr = face->remote_addr, + .remote_port = face->remote_port, + .admin_state = face->admin_state, + .state = face->state, + .priority = face->priority, + .tags = face->tags, + }; + if (generate_name) { + rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "udp%u", RANDBYTE()); + if (rc >= SYMBOLIC_NAME_LEN) + WARN( + "[hc_face_to_connection] Unexpected truncation of " + "symbolic name string"); + } else { + memset(connection->name, 0, SYMBOLIC_NAME_LEN); + } + snprintf(connection->interface_name, INTERFACE_LEN, "%s", + face->netdevice.name); + break; + default: + return -1; + } + + connection->id = face->id; + rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", + face->netdevice.name); + if (rc >= INTERFACE_LEN) + WARN( + "hc_face_to_connection] Unexpected truncation of interface name " + "string"); + + return 0; +} + +int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener) { + switch (face->type) { + case FACE_TYPE_HICN_LISTENER: + break; + case FACE_TYPE_TCP_LISTENER: + break; + case FACE_TYPE_UDP_LISTENER: + break; + default: + return -1; + } + return -1; /* XXX Not implemented */ +} + +#if 0 +/*----------------------------------------------------------------------------* + * Face + * + * Face support is not directly available in hicn-light, but we can offer such + * an interface through a combination of listeners and connections. The code + * starts with some conversion functions between faces/listeners/connections. + * + * We also need to make sure that there always exist a (single) listener when + *a connection is created, and in the hICN face case, that there is a single + * connection attached to this listener. + * + *----------------------------------------------------------------------------*/ + +/* FACE CREATE */ + +static int _hcng_face_create(hc_sock_t *socket, hc_face_t *face) { +#if 0 + hc_listener_t listener; + hc_listener_t *listener_found; + + hc_connection_t connection; + hc_connection_t *connection_found; + + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_create] Unexpected truncation of face string"); + DEBUG("[hc_face_create] face=%s", face_s); + + switch (face->face.type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + if (hc_face_to_connection(face, &connection, true) < 0) { + ERROR("[hc_face_create] Could not convert face to connection."); + return -1; + } + + /* Ensure we have a corresponding local listener */ + if (hc_connection_to_local_listener(&connection, &listener) < 0) { + ERROR("[hc_face_create] Could not convert face to local listener."); + return -1; + } + + if (_hcng_listener_get(socket, &listener, &listener_found) < 0) { + ERROR("[hc_face_create] Could not retrieve listener"); + return -1; + } + + if (!listener_found) { + /* We need to create the listener if it does not exist */ + if (hc_listener_create(socket, &listener) < 0) { + ERROR("[hc_face_create] Could not create listener."); + free(listener_found); + return -1; + } + } else { + free(listener_found); + } + + /* Create corresponding connection */ + if (_hcng_connection_create(socket, &connection) < 0) { + ERROR("[hc_face_create] Could not create connection."); + return -1; + } + + /* + * Once the connection is created, we need to list all connections + * and compare with the current one to find the created face ID. + */ + if (_hcng_connection_get(socket, &connection, &connection_found) < 0) { + ERROR("[hc_face_create] Could not retrieve connection"); + return -1; + } + + if (!connection_found) { + ERROR("[hc_face_create] Could not find newly created connection."); + return -1; + } + + face->id = connection_found->id; + free(connection_found); + + break; + + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + if (hc_face_to_listener(face, &listener) < 0) { + ERROR("Could not convert face to listener."); + return -1; + } + if (hc_listener_create(socket, &listener) < 0) { + ERROR("[hc_face_create] Could not create listener."); + return -1; + } + break; + default: + ERROR("[hc_face_create] Unknwon face type."); + + return -1; + }; + +#endif + return 0; +} + +static int _hcng_face_get(hc_sock_t *socket, hc_face_t *face, + hc_face_t **face_found) { +#if 0 + hc_listener_t listener; + hc_listener_t *listener_found; + + hc_connection_t connection; + hc_connection_t *connection_found; + + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_get] Unexpected truncation of face string"); + DEBUG("[hc_face_get] face=%s", face_s); + + switch (face->face.type) { + case FACE_TYPE_HICN: + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + if (hc_face_to_connection(face, &connection, false) < 0) return -1; + if (_hcng_connection_get(socket, &connection, &connection_found) < 0) + return -1; + if (!connection_found) { + *face_found = NULL; + return 0; + } + *face_found = malloc(sizeof(hc_face_t)); + hc_connection_to_face(connection_found, *face_found); + free(connection_found); + break; + + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + if (hc_face_to_listener(face, &listener) < 0) return -1; + if (_hcng_listener_get(socket, &listener, &listener_found) < 0) return -1; + if (!listener_found) { + *face_found = NULL; + return 0; + } + *face_found = malloc(sizeof(hc_face_t)); + hc_listener_to_face(listener_found, *face_found); + free(listener_found); + break; + + default: + return -1; + } + +#endif + return 0; +} + +/* FACE DELETE */ + +static int _hcng_face_delete(hc_sock_t *socket, hc_face_t *face, + uint8_t delete_listener) { +#if 0 + char face_s[MAXSZ_HC_FACE]; + int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); + if (rc >= MAXSZ_HC_FACE) + WARN("[hc_face_delete] Unexpected truncation of face string"); + DEBUG("[hc_face_delete] face=%s", face_s); + + hc_connection_t connection; + if (hc_face_to_connection(face, &connection, false) < 0) { + ERROR("[hc_face_delete] Could not convert face to connection."); + return -1; + } + + if (_hcng_connection_delete(socket, &connection) < 0) { + ERROR("[hc_face_delete] Error removing connection"); + return -1; + } + + if (!delete_listener) { + return 0; + } + + /* If this is the last connection attached to the listener, remove it */ + + hc_data_t *connections; + hc_listener_t listener = {{0}}; + + /* + * Ensure we have a corresponding local listener + * NOTE: hc_face_to_listener is not appropriate + */ + if (hc_connection_to_local_listener(&connection, &listener) < 0) { + ERROR("[hc_face_create] Could not convert face to local listener."); + return -1; + } +#if 1 + /* + * The name is generated to prepare listener creation, we need it to be + * empty for deletion. The id should not need to be reset though. + */ + listener.id = 0; + memset(listener.name, 0, sizeof(listener.name)); +#endif + if (_hcng_connection_list(socket, &connections) < 0) { + ERROR("[hc_face_delete] Error getting the list of listeners"); + return -1; + } + + bool delete = true; + foreach_connection(c, connections) { + if ((ip_address_cmp(&c->local_addr, &listener.local_addr, c->family) == + 0) && + (c->local_port == listener.local_port) && + (strcmp(c->interface_name, listener.interface_name) == 0)) { + delete = false; + } + } + + if (delete) { + if (_hcng_listener_delete(socket, &listener) < 0) { + ERROR("[hc_face_delete] Error removing listener"); + return -1; + } + } + + hc_data_free(connections); + +#endif + return 0; +} + +/* FACE LIST */ + +static int _hcng_face_list(hc_sock_t *socket, hc_data_t **pdata) { +#if 0 + hc_data_t *connection_data; + hc_face_t face; + + DEBUG("[hc_face_list]"); + + if (_hcng_connection_list(socket, &connection_data) < 0) { + ERROR("[hc_face_list] Could not list connections."); + return -1; + } + + hc_data_t *face_data = + hc_data_create(sizeof(hc_connection_t), sizeof(hc_face_t), NULL); + foreach_connection(c, connection_data) { + if (hc_connection_to_face(c, &face) < 0) { + ERROR("[hc_face_list] Could not convert connection to face."); + goto ERR; + } + hc_data_push(face_data, &face); + } + + *pdata = face_data; + hc_data_free(connection_data); + DEBUG("[hc_face_list] done"); + return 0; + +ERR: + hc_data_free(connection_data); + DEBUG("[hc_face_list] error"); +#endif + return -1; +} + +static int hc_connection_parse_to_face(void *in, hc_face_t *face) { + hc_connection_t connection; + + if (hcng_connection_parse(in, &connection) < 0) { + ERROR("[hc_connection_parse_to_face] Could not parse connection"); + return -1; + } + + if (hc_connection_to_face(&connection, face) < 0) { + ERROR( + "[hc_connection_parse_to_face] Could not convert connection to " + "face."); + return -1; + } + + return 0; +} + +static int _hcng_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, + face_state_t admin_state) { + return hc_connection_set_admin_state(s, conn_id_or_name, admin_state); +} + +static int _hcng_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, + uint32_t priority) { + return hc_connection_set_priority(s, conn_id_or_name, priority); +} + +static int _hcng_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, + policy_tags_t tags) { + return hc_connection_set_tags(s, conn_id_or_name, tags); +} + +#endif diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/face.h b/ctrl/libhicnctrl/src/modules/hicn_light/face.h new file mode 100644 index 000000000..9e1cd48c2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/face.h @@ -0,0 +1,14 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_FACE +#define HICNCTRL_MODULE_HICNLIGHT_FACE + +#include <hicn/ctrl/objects/connection.h> +#include <hicn/ctrl/objects/face.h> + +int hc_face_from_connection(const hc_connection_t *connection, hc_face_t *face); + +int hc_face_to_connection(const hc_face_t *face, hc_connection_t *connection, + bool generate_name); + +int hc_face_to_listener(const hc_face_t *face, hc_listener_t *listener); + +#endif /* HICNCTRL_MODULES_HICNLIGHT_FACE */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/listener.c b/ctrl/libhicnctrl/src/modules/hicn_light/listener.c new file mode 100644 index 000000000..68d4b8fcd --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <hicn/ctrl/api.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "../../object_private.h" +#include "listener.h" + +static int hicnlight_listener_parse(const u8 *buffer, size_t size, + hc_listener_t *listener) { + int rc; + + if (size != sizeof(cmd_listener_list_item_t)) return -1; + cmd_listener_list_item_t *item = (cmd_listener_list_item_t *)buffer; + + if (!IS_VALID_ADDRESS(item->local_address)) { + ERROR("[hc_listener_parse] Invalid address received"); + return -1; + } + if (!IS_VALID_NAME(item->name)) { + ERROR("[hc_listener_parse] Invalid name received"); + return -1; + } + if (!IS_VALID_INTERFACE_NAME(item->interface_name)) { + ERROR("[hc_listener_parse] Invalid interface_name received"); + return -1; + } + if (!IS_VALID_ID(item->id)) { + ERROR("[hc_listener_parse] Invalid id received"); + return -1; + } + if (!IS_VALID_PORT(ntohs(item->local_port))) { + ERROR("[hc_listener_parse] Invalid port received"); + return -1; + } + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_listener_parse] Invalid family received"); + return -1; + } + if (!IS_VALID_TYPE(item->type)) { + ERROR("[hc_listener_parse] Invalid type received"); + return -1; + } + // if (!(IS_VALID_CONNECTION_TYPE(item->type))) + // return -1; + + *listener = (hc_listener_t){ + .id = item->id, + .type = (face_type_t)(item->type), + .family = (int)(item->family), + .local_addr = + item->local_addr, // UNION_CAST(item->local_addr, ip_address_t), + .local_port = ntohs(item->local_port), + }; + + rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "%s", item->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", + item->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + if (hc_listener_validate(listener, false) < 0) return -1; + return 0; +} + +int _hicnlight_listener_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_listener_parse(buffer, size, &object->listener); +} + +/* LISTENER CREATE */ + +int hicnlight_listener_serialize_create(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_listener_t *listener = &object->listener; + + msg_listener_add_t *msg = (msg_listener_add_t *)packet; + *msg = (msg_listener_add_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_ADD, + .length = 1, + .seq_num = 0, + }, + + .payload = { + .address = listener->local_addr, + .port = htons(listener->local_port), + .family = (uint8_t)listener->family, + .type = (uint8_t)listener->type, + }}; + + rc = snprintf(msg->payload.symbolic, SYMBOLIC_NAME_LEN, "%s", listener->name); + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + rc = snprintf(msg->payload.interface_name, INTERFACE_LEN, "%s", + listener->interface_name); + if ((rc < 0) || (rc >= INTERFACE_LEN)) return -1; + + return sizeof(msg_listener_add_t); +} + +/* LISTENER DELETE */ + +int hicnlight_listener_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + int rc; + const hc_listener_t *listener = &object->listener; + + msg_listener_remove_t *msg = (msg_listener_remove_t *)packet; + *msg = (msg_listener_remove_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_REMOVE, + .length = 1, + .seq_num = 0, + }}; + + if (listener->id) { + rc = snprintf(msg->payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", + listener->id); + } else if (*listener->name) { + rc = snprintf(msg->payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%s", + listener->name); + } else { + return -1; + } + + // For now we only support delete by name or id +#if 0 + hc_listener_t *listener_found; + if (hc_listener_get(socket, listener, &listener_found) < 0) return -1; + if (!listener_found) return -1; + rc = snprintf(payload->symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", + listener_found->id); + free(listener_found); + } +#endif + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_listener_remove_t); +} + +/* LISTENER LIST */ + +int hicnlight_listener_serialize_list(const hc_object_t *object, + uint8_t *packet) { + msg_listener_list_t *msg = (msg_listener_list_t *)packet; + *msg = (msg_listener_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_LISTENER_LIST, + .length = 0, + .seq_num = 0, + }}; + + return sizeof(msg_header_t); // Do not use msg_listener_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, listener); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/listener.h b/ctrl/libhicnctrl/src/modules/hicn_light/listener.h new file mode 100644 index 000000000..27ef8434d --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/listener.h @@ -0,0 +1,21 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_LISTENER_H +#define HICNCTRL_MODULE_HICNLIGHT_LISTENER_H + +#include "../../module.h" + +#if 1 +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, listener); +#else + +int _hicnlight_listener_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_listener_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_listener_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_listener_serialize_list(const hc_object_t *object, + uint8_t *packet); +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c new file mode 100644 index 000000000..de75d82a8 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/mapme.c @@ -0,0 +1,139 @@ +#include "mapme.h" + +static int _hcng_mapme_set(hc_sock_t *socket, int enabled) { +#if 0 + msg_mapme_enable_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_ENABLE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = enabled, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_ENABLE, + .size_in = sizeof(cmd_mapme_enable_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_discovery(hc_sock_t *socket, int enabled) { +#if 0 + msg_mapme_enable_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, + .length = 1, + .seq_num = 0, + }, + .payload = { + .activate = enabled, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, + .size_in = sizeof(cmd_mapme_set_discovery_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_timescale(hc_sock_t *socket, uint32_t timescale) { +#if 0 + msg_mapme_set_timescale_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .timePeriod = timescale, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, + .size_in = sizeof(cmd_mapme_set_timescale_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_set_retx(hc_sock_t *socket, uint32_t timescale) { +#if 0 + msg_mapme_set_retx_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SET_RETX, + .length = 1, + .seq_num = 0, + }, + .payload = { + .timePeriod = timescale, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_MAPME_SET_RETX, + .size_in = sizeof(msg_mapme_set_retx_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} + +static int _hcng_mapme_send_update(hc_sock_t *socket, hc_mapme_t *mapme) { +#if 0 + if (!IS_VALID_FAMILY(mapme->family)) return -1; + + msg_mapme_send_update_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .length = 1, + .seq_num = 0, + }, + }; + + hc_command_params_t params = { + .cmd = ACTION_UPDATE, + .cmd_id = COMMAND_TYPE_MAPME_SEND_UPDATE, + .size_in = sizeof(msg_mapme_send_update_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, false); +#endif + return 0; // XXX added +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/policy.c b/ctrl/libhicnctrl/src/modules/hicn_light/policy.c new file mode 100644 index 000000000..d48ce014d --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/policy.c @@ -0,0 +1,162 @@ +#include "policy.h" + +/* POLICY CREATE */ + +static int _hcng_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, + bool async) { +#if 0 + if (!IS_VALID_FAMILY(policy->family)) return -1; + + struct { + cmd_header_t hdr; + cmd_policy_add_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + COMMAND_TYPE_POLICY_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = policy->remote_addr, + .family = policy->family, + .len = policy->len, + .policy = policy->policy, + }}; + + hc_command_params_t params = { + .cmd = ACTION_CREATE, + .cmd_id = COMMAND_TYPE_POLICY_ADD, + .size_in = sizeof(cmd_policy_add_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_create(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_create_internal(s, policy, false); +} + +static int _hcng_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_create_internal(s, policy, true); +} + +/* POLICY DELETE */ + +static int _hcng_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, + bool async) { +#if 0 + if (!IS_VALID_FAMILY(policy->family)) return -1; + + struct { + cmd_header_t hdr; + cmd_policy_remove_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_POLICY_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = policy->remote_addr, + .family = policy->family, + .len = policy->len, + }}; + + hc_command_params_t params = { + .cmd = ACTION_DELETE, + .cmd_id = COMMAND_TYPE_POLICY_REMOVE, + .size_in = sizeof(cmd_policy_remove_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_delete(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_delete_internal(s, policy, false); +} + +static int _hcng_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { + return _hcng_policy_delete_internal(s, policy, true); +} + +/* POLICY PARSE */ + +static int hc_policy_parse(void *in, hc_policy_t *policy) { + hc_policy_t *item = (hc_policy_t *)in; + + if (!IS_VALID_ADDRESS(item->address)) { + ERROR("[hc_policy_parse] Invalid address"); + return -1; + } + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_policy_parse] Invalid family"); + return -1; + } + if (!IS_VALID_PREFIX_LEN(item->len)) { + ERROR("[hc_policy_parse] Invalid len"); + return -1; + } + if (!IS_VALID_POLICY(item->policy)) { + ERROR("[hc_policy_parse] Invalid policy"); + return -1; + } + + *policy = (hc_policy_t){ + .family = item->family, + .remote_addr = item->remote_addr, + .len = item->len, + .policy = item->policy, + }; + return 0; +} + +/* POLICY LIST */ + +static int _hcng_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, + bool async) { +#if 0 + struct { + cmd_header_t hdr; + } msg = { + .hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_POLICY_LIST, + .length = 0, + .seq_num = 0, + }, + }; + + hc_command_params_t params = { + .cmd = ACTION_LIST, + .cmd_id = COMMAND_TYPE_POLICY_LIST, + .size_in = sizeof(hc_policy_t), + .size_out = sizeof(hc_policy_t), + .parse = (HC_PARSE)hc_policy_parse, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + pdata, async); +#endif + return 0; // XXX added +} + +static int _hcng_policy_list(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_policy_list_internal(s, pdata, false); +} + +static int _hcng_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { + return _hcng_policy_list_internal(s, pdata, true); +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/punting.c b/ctrl/libhicnctrl/src/modules/hicn_light/punting.c new file mode 100644 index 000000000..7886ff839 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/punting.c @@ -0,0 +1,74 @@ +#include "punting.h" + +static int _hcng_punting_create_internal(hc_sock_t *socket, + hc_punting_t *punting, bool async) { +#if 0 + int rc; + + if (hc_punting_validate(punting) < 0) return -1; + + struct { + cmd_header_t hdr; + cmd_punting_add_t payload; + } msg = {.hdr = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_PUNTING_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = punting->prefix, + .family = punting->family, + .len = punting->prefix_len, + }}; + rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + punting->face_id); + if (rc >= SYMBOLIC_NAME_LEN) + WARN("[_hc_punting_create] Unexpected truncation of symbolic name string"); + + hc_command_params_t params = { + .cmd = ACTION_CREATE, + .cmd_id = COMMAND_TYPE_PUNTING_ADD, + .size_in = sizeof(cmd_punting_add_t), + .size_out = 0, + .parse = NULL, + }; + + return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, + NULL, async); +#endif + return 0; // XXX added +} + +static int _hcng_punting_create(hc_sock_t *s, hc_punting_t *punting) { + return _hcng_punting_create_internal(s, punting, false); +} + +static int _hcng_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { + return _hcng_punting_create_internal(s, punting, true); +} + +static int _hcng_punting_get(hc_sock_t *s, hc_punting_t *punting, + hc_punting_t **punting_found) { + ERROR("hc_punting_get not (yet) implemented."); + return -1; +} + +static int _hcng_punting_delete(hc_sock_t *s, hc_punting_t *punting) { + ERROR("hc_punting_delete not (yet) implemented."); + return -1; +} + +#if 0 +static int hc_punting_parse(void * in, hc_punting_t * punting) +{ + ERROR("hc_punting_parse not (yet) implemented."); + return -1; +} +#endif + +static int _hcng_punting_list(hc_sock_t *s, hc_data_t **pdata) { + ERROR("hc_punting_list not (yet) implemented."); + return -1; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/route.c b/ctrl/libhicnctrl/src/modules/hicn_light/route.c new file mode 100644 index 000000000..1adfe4f36 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/route.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_light/route.c + * \brief Implementation of route object VFT for hicn_light. + */ + +#include <assert.h> +#include <hicn/ctrl/api.h> +#include <hicn/util/log.h> +#include "base.h" +#include "route.h" +#include <hicn/ctrl/hicn-light.h> + +#include "../../object_private.h" + +static int hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_route_t *route) { + if (size != sizeof(cmd_route_list_item_t)) return -1; + cmd_route_list_item_t *item = (cmd_route_list_item_t *)buffer; + + if (!IS_VALID_NAME(item->face_name)) { + ERROR("[hc_connection_parse] Invalid face_name received"); + return -1; + } + + if (!IS_VALID_ID(item->face_id)) { + ERROR("[hc_connection_parse] Invalid face_id received"); + return -1; + } + + if (!IS_VALID_FAMILY(item->family)) { + ERROR("[hc_listener_parse] Invalid family received"); + return -1; + } + + if (!IS_VALID_ADDRESS(item->remote_addr)) { + ERROR("[hc_connection_parse] Invalid address received"); + return -1; + } + + // LEN + // COST + + *route = (hc_route_t){ + .face_name = "", /* This is not reported back */ + .face_id = item->face_id, + .family = (int)(item->family), + .remote_addr = item->remote_addr, + .len = item->len, + .cost = item->cost, + }; + + if (hc_route_validate(route, false) < 0) return -1; + return 0; +} + +int _hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_route_parse(buffer, size, &object->route); +} + +/* ROUTE CREATE */ + +int hicnlight_route_serialize_create(const hc_object_t *object, + uint8_t *packet) { + const hc_route_t *route = &object->route; + int rc; + + msg_route_add_t *msg = (msg_route_add_t *)packet; + *msg = (msg_route_add_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = route->remote_addr, + .cost = route->cost, + .family = route->family, + .len = route->len, + }}; + + /* + * The route commands expects the ID or name as part of the + * symbolic_or_connid attribute. + */ + if (route->face_name[0] != '\0') { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + route->face_name); + } else { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + route->face_id); + } + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_route_add_t); +} + +/* ROUTE DELETE */ + +int hicnlight_route_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + const hc_route_t *route = &object->route; + int rc; + + msg_route_remove_t *msg = (msg_route_remove_t *)packet; + memset(msg, 0, sizeof(msg_route_remove_t)); + *msg = (msg_route_remove_t){.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = { + .family = (uint8_t)route->family, + .len = route->len, + }}; + + /* + * Direct copy as part of the previous assignment does not work correctly... + * to be investigated + */ + memcpy(&msg->payload.address, &route->remote_addr, sizeof(hicn_ip_address_t)); + + /* + * The route commands expects the ID or name as part of the + * symbolic_or_connid attribute. + */ + if (route->face_name[0] != '\0') { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", + route->face_name); + } else { + rc = snprintf(msg->payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", + route->face_id); + } + + if ((rc < 0) || (rc >= SYMBOLIC_NAME_LEN)) return -1; + + return sizeof(msg_route_remove_t); +} + +/* ROUTE LIST */ + +int hicnlight_route_serialize_list(const hc_object_t *object, uint8_t *packet) { + msg_route_list_t *msg = (msg_route_list_t *)packet; + *msg = (msg_route_list_t){.header = { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_ROUTE_LIST, + .length = 0, + .seq_num = 0, + }}; + + return sizeof(msg_header_t); // Do not use msg_route_list_t +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, route); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/route.h b/ctrl/libhicnctrl/src/modules/hicn_light/route.h new file mode 100644 index 000000000..e86e8b8c3 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/route.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_light/route.h + * \brief route object VFT for hicn_light. + */ + +#ifndef HICNCTRL_MODULE_HICNLIGHT_ROUTE_H +#define HICNCTRL_MODULE_HICNLIGHT_ROUTE_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, route); + +#else + +int _hicnlight_route_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); +int hicnlight_route_serialize_create(const hc_object_t *object, + uint8_t *packet); +int hicnlight_route_serialize_delete(const hc_object_t *object, + uint8_t *packet); +int hicnlight_route_serialize_list(const hc_object_t *object, uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/stats.h b/ctrl/libhicnctrl/src/modules/hicn_light/stats.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/stats.h diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c new file mode 100644 index 000000000..35e241f3e --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.c @@ -0,0 +1,205 @@ +#include <assert.h> +#include "strategy.h" + +#include <hicn/ctrl/hicn-light.h> + +static int hicnlight_strategy_parse(const u8 *buffer, size_t size, + hc_strategy_t *strategy) { + return -1; +} + +int _hicnlight_strategy_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_strategy_parse(buffer, size, &object->strategy); +} + +int hicnlight_strategy_serialize_create(const hc_object_t *object, + uint8_t *packet) { + return -1; +} + +int hicnlight_strategy_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + return -1; +} + +int hicnlight_strategy_serialize_list(const hc_object_t *object, + uint8_t *packet) { + assert(!object); + return -1; +} +#if 0 +// per prefix +static hc_result_t *_strategy_set_serialize(hc_sock_t *socket, + hc_strategy_t *strategy) { + return -1; + hc_result_t *res = malloc(sizeof(*res)); + char strategy_s[MAXSZ_HC_STRATEGY]; + strncpy(strategy->name, strategy_str(strategy->type), + MAXSZ_STRATEGY_NAME - 1); + + int rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); + if (rc >= MAXSZ_HC_STRATEGY) + WARN("[hicnlight_strategy_create] Unexpected truncation of strategy string"); + DEBUG("[hicnlight_strategy_create] strategy=%s", strategy_s); + + if (!IS_VALID_FAMILY(strategy->family) || + !IS_VALID_STRATEGY_TYPE(strategy->type)) { + res->success = false; + return res; + } + + msg_strategy_set_t msg = {.header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_SET, + .length = 1, + .seq_num = 0, + }, + .payload = { + .address = strategy->address, + .family = strategy->family, + .len = strategy->len, + .type = strategy->type, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_STRATEGY_SET, + .size_in = sizeof(cmd_strategy_set_t), + .size_out = 0, + .parse = NULL, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.strategy_set = msg.payload, + }, + .params = params, + .async = false, + .success = true, + }; + return res; +} +#endif + +#if 0 +static hc_result_t *_strategy_add_local_prefix_serialize( + hc_sock_t *socket, hc_strategy_t *strategy) { + hc_result_t *res = malloc(sizeof(*res)); + char strategy_s[MAXSZ_HC_STRATEGY]; + strncpy(strategy->name, strategy_str(strategy->type), + MAXSZ_STRATEGY_NAME - 1); + + int rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); + if (rc >= MAXSZ_HC_STRATEGY) + WARN("[hicnlight_strategy_create] Unexpected truncation of strategy string"); + DEBUG("[hicnlight_strategy_create] strategy=%s", strategy_s); + + if (!IS_VALID_FAMILY(strategy->family) || + !IS_VALID_STRATEGY_TYPE(strategy->type) || + !IS_VALID_FAMILY(strategy->local_family)) { + res->success = false; + return res; + } + + msg_strategy_add_local_prefix_t msg = { + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, + .length = 1, + .seq_num = 0, + }, + .payload = { + .type = strategy->type, + .address = strategy->address, + .family = strategy->family, + .len = strategy->len, + .local_address = strategy->local_address, + .local_family = strategy->local_family, + .local_len = strategy->local_len, + }}; + + hc_command_params_t params = { + .cmd = ACTION_SET, + .cmd_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, + .size_in = sizeof(cmd_strategy_add_local_prefix_t), + .size_out = 0, + .parse = NULL, + }; + + *res = (hc_result_t){ + .msg = + (hc_msg_t){ + .header = msg.header, + .payload.strategy_add_local_prefix = msg.payload, + }, + .params = params, + .async = false, + .success = true, + }; + return res; +} +#endif + +#if 0 +static int hicnlight_strategy_set(hc_sock_t *socket, hc_strategy_t *strategy) { + hc_result_t *result = _strategy_set_serialize(socket, strategy); + + int ret = INPUT_ERROR; + if (result->success) { + ret = hicnlight_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, NULL, + result->async); + } + + hc_result_free(result); + return ret; + return -1; // XXX added +} + +static int hicnlight_strategy_add_local_prefix(hc_sock_t *socket, + hc_strategy_t *strategy) { + hc_result_t *result = _strategy_add_local_prefix_serialize(socket, strategy); + + int ret = INPUT_ERROR; + if (result->success) { + ret = hicnlight_execute_command(socket, (hc_msg_t *)&result->msg, + sizeof(result->msg), &result->params, NULL, + result->async); + } + + hc_result_free(result); + return ret; + return -1; // XXX added +} + +/* How to retrieve that from the forwarder ? */ +static const char *strategies[] = { + "random", + "load_balancer", +}; + +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) + +static int hicnlight_strategy_list(hc_sock_t *s, hc_data_t **data) { + int rc; + + *data = hc_data_create(0, sizeof(hc_strategy_t), NULL); + + for (unsigned i = 0; i < ARRAY_SIZE(strategies); i++) { + hc_strategy_t *strategy = (hc_strategy_t *)hc_data_get_next(*data); + if (!strategy) return -1; + rc = snprintf(strategy->name, MAXSZ_STRATEGY_NAME, "%s", strategies[i]); + if (rc >= MAXSZ_STRATEGY_NAME) + WARN("[hc_strategy_list] Unexpected truncation of strategy name string"); + (*data)->size++; + } + return -1; +} +#endif + +DECLARE_MODULE_OBJECT_OPS(hicnlight, strategy); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h new file mode 100644 index 000000000..6b1933960 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/strategy.h @@ -0,0 +1,24 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H +#define HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, strategy); + +int _hicnlight_strategy_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_strategy_serialize_create(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_strategy_serialize_delete(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_strategy_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_STRATEGY_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c new file mode 100644 index 000000000..d00055e89 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.c @@ -0,0 +1,66 @@ +#include <assert.h> +#include <hicn/ctrl/api.h> +#include <hicn/util/log.h> + +#include "base.h" +#include "../../object_private.h" +#include "subscription.h" + +static int hicnlight_subscription_parse(const u8 *buffer, size_t size, + hc_subscription_t *subscription) { + /* We should never have to parse subscriptions */ + return -1; +} + +int _hicnlight_subscription_parse(const uint8_t *buffer, size_t size, + hc_object_t *object) { + return hicnlight_subscription_parse(buffer, size, &object->subscription); +} + +/* SUBSCRIPTION CREATE */ + +int hicnlight_subscription_serialize_create(const hc_object_t *object, + uint8_t *packet) { + const hc_subscription_t *subscription = &object->subscription; + + msg_subscription_add_t *msg = (msg_subscription_add_t *)packet; + *msg = (msg_subscription_add_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_SUBSCRIPTION_ADD, + .length = 1, + .seq_num = 0, + }, + .payload = {.topics = subscription->topics}}; + + return sizeof(msg_subscription_add_t); +} + +/* SUBSCRIPTION DELETE */ + +int hicnlight_subscription_serialize_delete(const hc_object_t *object, + uint8_t *packet) { + const hc_subscription_t *subscription = &object->subscription; + + msg_subscription_remove_t *msg = (msg_subscription_remove_t *)packet; + *msg = (msg_subscription_remove_t){ + .header = + { + .message_type = REQUEST_LIGHT, + .command_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, + .length = 1, + .seq_num = 0, + }, + .payload = {.topics = subscription->topics}}; + + return sizeof(msg_subscription_remove_t); +} + +int hicnlight_subscription_serialize_list(const hc_object_t *object, + uint8_t *packet) { + assert(!object); + return -1; +} + +DECLARE_MODULE_OBJECT_OPS(hicnlight, subscription); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h new file mode 100644 index 000000000..a4edf556b --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/subscription.h @@ -0,0 +1,26 @@ +#ifndef HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H +#define HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H + +#include "../../module.h" + +#if 1 + +DECLARE_MODULE_OBJECT_OPS_H(hicnlight, subscription); + +#else + +int _hicnlight_subscription_parse(const uint8_t *buffer, size_t size, + hc_object_t *object); + +int hicnlight_subscription_serialize_create(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_subscription_serialize_delete(const hc_object_t *object, + uint8_t *packet); + +int hicnlight_subscription_serialize_list(const hc_object_t *object, + uint8_t *packet); + +#endif + +#endif /* HICNCTRL_MODULE_HICNLIGHT_SUBSCRIPTION_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c b/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c new file mode 100644 index 000000000..7d1812ab2 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light/wldr.c @@ -0,0 +1,3 @@ +#include "wldr.h" + +static int _hcng_wldr_set(hc_sock_t *s /* XXX */) { return 0; } diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_common.h b/ctrl/libhicnctrl/src/modules/hicn_light_common.h deleted file mode 100644 index d24b5bb2d..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_light_common.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef HICNCTRL_HICN_LIGHT_COMMON -#define HICNCTRL_HICN_LIGHT_COMMON - -#include <assert.h> // assert - -#include <hicn/util/khash.h> -#include "api_private.h" - -#define PORT 9695 - -#define BOOLSTR(x) ((x) ? "true" : "false") - -/* - * Internal state associated to a pending request - */ -typedef struct { - int seq; - hc_data_t *data; - int (*parse)(const u8 *src, u8 *dst); -} hc_sock_request_t; - -/** - * Messages to the forwarder might be multiplexed thanks to the seqNum fields in - * the header_control_message structure. The forwarder simply answers back the - * original sequence number. We maintain a map of such sequence number to - * outgoing queries so that replied can be demultiplexed and treated - * appropriately. - */ -KHASH_MAP_INIT_INT(sock_map, hc_sock_request_t *); - -struct hc_sock_light_s { - /* This must be the first element of the struct */ - hc_sock_t vft; - - char *url; - int fd; - - /* Partial receive buffer */ - u8 buf[RECV_BUFLEN]; - size_t roff; /**< Read offset */ - size_t woff; /**< Write offset */ - - /* - * Because received messages are potentially unbounded in size, we might not - * guarantee that we can store a full packet before processing it. We must - * implement a very simple state machine remembering the current parsing - * status in order to partially process the packet. - */ - size_t remaining; - u32 send_id; - - /* Next sequence number to be used for requests */ - int seq; - - /* Request being parsed (NULL if none) */ - hc_sock_request_t *cur_request; - - bool async; - kh_sock_map_t *map; -}; - -typedef struct hc_sock_light_s hc_sock_light_t; - -#define TO_HC_SOCK_LIGHT(s) (hc_sock_light_t *)(s) - -hc_sock_request_t *hc_sock_request_create(int seq, hc_data_t *data, - HC_PARSE parse); - -void hc_sock_light_request_free(hc_sock_request_t *request); - -/* - * list was working with all seq set to 0, but it seems hicnLightControl uses - * 1, and replies with the same seqno - */ -#define HICN_CTRL_SEND_SEQ_INIT 1 -#define HICN_CTRL_RECV_SEQ_INIT 1 - -#define MAX(x, y) ((x > y) ? x : y) - -static const struct in6_addr loopback_addr = IN6ADDR_LOOPBACK_INIT; - -#endif /* HICNCTRL_HICN_LIGHT_COMMON */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c b/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c deleted file mode 100644 index 488b2edbf..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c +++ /dev/null @@ -1,3238 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * \file api.c - * \brief Implementation of hICN control library API - */ - -#include <assert.h> // assert -#include <fcntl.h> // fcntl -#include <stdbool.h> -#include <stdio.h> // snprintf -#include <string.h> // memmove, strcasecmp -#include <sys/socket.h> // socket -#include <sys/types.h> // getpid -#include <unistd.h> // close, fcntl -#include <unistd.h> // getpid - -#include "api_private.h" -#ifdef __linux__ -#include <sys/syscall.h> -#define gettid() syscall(SYS_gettid) -#endif /* __linux__ */ -#include <hicn/ctrl/hicn-light-ng.h> -#include <strings.h> - -#include "hicn_light_common.h" -#include <hicn/util/sstrncpy.h> - -#pragma GCC diagnostic ignored "-Warray-bounds" - -#if 0 -#ifdef __APPLE__ -#define RANDBYTE() (u8)(arc4random() & 0xFF) -#else -#define RANDBYTE() (u8)(random() & 0xFF) -#endif -#endif -#define RANDBYTE() (u8)(rand() & 0xFF) - -/** - * \brief Defines the default size for the allocated data arrays holding the - * results of API calls. - * - * This size should not be too small to avoid wasting memoyy, but also not too - * big to avoid unnecessary realloc's. Later on this size is doubled at each - * reallocation. - */ -#define DEFAULT_SIZE_LOG 3 - -#define connection_state_to_face_state(x) ((face_state_t)(x)) -#define face_state_to_connection_state(x) ((hc_connection_state_t)(x)) - -/****************************************************************************** - * Message helper types and aliases - ******************************************************************************/ - -#define foreach_hc_command \ - _(connection_add) \ - _(connection_remove) \ - _(connection_list) \ - _(listener_add) \ - _(listener_remove) \ - _(listener_list) \ - _(route_add) \ - _(route_remove) \ - _(route_list) \ - _(cache_set_store) \ - _(cache_set_serve) \ - _(cache_clear) \ - _(cache_list) \ - _(strategy_set) \ - _(strategy_add_local_prefix) \ - _(wldr_set) \ - _(punting_add) \ - _(mapme_activator) \ - _(mapme_timing) \ - _(subscription_add) \ - _(subscription_remove) \ - _(stats_get) \ - _(stats_list) - -const char *command_type_str[] = { -#define _(l, u) [COMMAND_TYPE_##u] = STRINGIZE(u), - foreach_command_type -#undef _ -}; - -typedef cmd_header_t hc_msg_header_t; - -typedef union { -#define _(x) cmd_##x##_t x; - foreach_hc_command -#undef _ -} hc_msg_payload_t; - -typedef struct hc_msg_s { - hc_msg_header_t hdr; - hc_msg_payload_t payload; -} hc_msg_t; - -/****************************************************************************** - * Control socket - ******************************************************************************/ - -#define AVAILABLE(s) ((s)->woff - (s)->roff) -#define DEFAULT_SOCK_RECV_TIMEOUT_MS 100 - -/** - * \brief Parse a connection URL into a sockaddr - * \param [in] url - URL - * \param [out] sa - Resulting struct sockaddr, expected zero'ed. - * \return 0 if parsing succeeded, a negative error value otherwise. - */ -static int _hcng_sock_light_parse_url(const char *url, struct sockaddr *sa) { - /* FIXME URL parsing is currently not implemented */ - assert(!url); - -#ifdef __linux__ - srand(time(NULL) ^ getpid() ^ gettid()); -#else - srand((unsigned int)(time(NULL) ^ getpid())); -#endif /* __linux__ */ - - /* - * A temporary solution is to inspect the sa_family fields of the passed in - * sockaddr, which defaults to AF_UNSPEC (0) and thus creates an IPv4/TCP - * connection to localhost. - */ - switch (sa->sa_family) { - case AF_UNSPEC: - case AF_INET: { - struct sockaddr_in *sai = (struct sockaddr_in *)sa; - sai->sin_family = AF_INET; - sai->sin_port = htons(PORT); - sai->sin_addr.s_addr = htonl(INADDR_LOOPBACK); - break; - } - case AF_INET6: { - struct sockaddr_in6 *sai6 = (struct sockaddr_in6 *)sa; - sai6->sin6_family = AF_INET6; - sai6->sin6_port = htons(PORT); - sai6->sin6_addr = loopback_addr; - break; - } - default: - return -1; - } - - return 0; -} - -static int _hcng_sock_light_reset(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - s->roff = s->woff = 0; - s->remaining = 0; - return 0; -} - -void _hcng_sock_light_free(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - unsigned k_seq; - hc_sock_request_t *v_request; - kh_foreach(s->map, k_seq, v_request, - { hc_sock_light_request_free(v_request); }); - - kh_destroy_sock_map(s->map); - if (s->url) free(s->url); - close(s->fd); - free(s); -} - -static void _hcng_sock_increment_woff(hc_sock_t *socket, size_t bytes) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - s->woff += bytes; -} - -static int _hcng_sock_light_get_next_seq(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return s->seq++; -} - -static int _hcng_sock_light_set_nonblocking(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return (fcntl(s->fd, F_SETFL, fcntl(s->fd, F_GETFL) | O_NONBLOCK) < 0); -} - -static int _hcng_sock_light_get_fd(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - return s->fd; -} - -static int _hcng_sock_light_connect(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - struct sockaddr_storage ss; - memset(&ss, 0, sizeof(struct sockaddr_storage)); - - if (_hcng_sock_light_parse_url(s->url, (struct sockaddr *)&ss) < 0) - goto ERR_PARSE; - - size_t size = ss.ss_family == AF_INET ? sizeof(struct sockaddr_in) - : sizeof(struct sockaddr_in6); - if (connect(s->fd, (struct sockaddr *)&ss, (socklen_t)size) < 0) { - perror("connect error"); - goto ERR_CONNECT; - } - return 0; - -ERR_CONNECT: -ERR_PARSE: - return -1; -} - -static int _hcng_sock_light_send(hc_sock_t *socket, hc_msg_t *msg, - size_t msglen, uint32_t seq) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc; - msg->hdr.seq_num = seq; - rc = (int)send(s->fd, msg, msglen, 0); - if (rc < 0) { - perror("hc_sock_light_send"); - return -1; - } - return 0; -} - -static int _hcng_sock_light_get_available(hc_sock_t *socket, u8 **buffer, - size_t *size) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - *buffer = s->buf + s->woff; - *size = RECV_BUFLEN - s->woff; - - return 0; -} - -static int _hcng_sock_light_recv(hc_sock_t *socket) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc; - - /* - * This condition should be ensured to guarantee correct processing of - * messages. With TCP, we need at least a header as we will receive part of - * the stream. With UDP, we need the be able to receive the full datagram, - * otherwise the rest will be lost. - * - * Let's be sure to always be able to receive at least 1 JUMBO_MTU, which - * should be fine for al situations. - */ - assert(RECV_BUFLEN - s->woff > JUMBO_MTU); - - rc = (int)recv(s->fd, s->buf + s->woff, RECV_BUFLEN - s->woff, 0); - if (rc == 0) { - /* Connection has been closed */ - return 0; - } - if (rc < 0) { - /* - * Let's not return 0 which currently means the socket has been closed - */ - if (errno == EWOULDBLOCK) return -1; - if (errno == EINTR) { - WARN("recv has been stopped by signal"); - return -1; - } - perror("hc_sock_light_recv"); - return -1; - } - s->woff += rc; - return rc; -} - -static void _hcng_sock_light_mark_complete(hc_sock_light_t *s, - hc_data_t **pdata) { - hc_data_t *data = s->cur_request->data; - - khiter_t k = kh_get_sock_map(s->map, s->cur_request->seq); - if (k == kh_end(s->map)) { - ERROR("[hc_sock_light_mark_complete] Error removing request from map"); - } else { - kh_del_sock_map(s->map, k); - } - - hc_data_set_complete(data); - if (pdata) *pdata = data; - - /* Free current request */ - hc_sock_light_request_free(s->cur_request); - s->cur_request = NULL; -} - -static int _hcng_sock_light_process_notification(hc_sock_light_t *s, - hc_data_t **pdata) { - /* For now, notifications are not associated to requests */ - assert(!s->cur_request); - - /* - * Assumption: the whole notification data is returned in a single read and we - * immediately parse it. - * - * XXX This is only valid for UDP sockets. - */ - size_t notification_size = AVAILABLE(s); - - *pdata = hc_data_create(0, /* in_element_size, 0 = no parsing */ - notification_size, /* out_element_size */ - NULL); /* complete_cb */ - - /* Copy the packet payload as the single entry in hc_data_t */ - hc_data_push_many(*pdata, s->buf + s->roff, 1); - - return (int)notification_size; -} - -/* - * Notifications have no sequence number and are not linked to any request - */ -static hc_sock_request_t *_hcng_sock_light_get_request(hc_sock_light_t *s, - int seq) { - hc_sock_request_t *request; - /* Retrieve request from sock map */ - khiter_t k = kh_get_sock_map(s->map, seq); - if (k == kh_end(s->map)) { - ERROR( - "[_hcng_sock_light_get_request] Error searching for matching request"); - return NULL; - } - request = kh_val(s->map, k); - - if (!request) { - ERROR("[_hcng_sock_light_get_request] No request matching sequence number"); - return NULL; - } - return request; -} - -/* - * Return codes: - * 0 success, or not enough data yet to do something - * > 0 : notification type - * -99 invalid buffer data -> flush - */ -static int _hcng_sock_light_process_header(hc_sock_light_t *s, - hc_data_t **pdata) { - int rc; - - /* Check we have at least a header's worth of data, and consume it */ - if (AVAILABLE(s) < sizeof(hc_msg_header_t)) return 0; - - hc_msg_t *msg = (hc_msg_t *)(s->buf + s->roff); - - // INFO("Processing header header %s", command_type_str(msg->hdr.command_id)); - s->roff += sizeof(hc_msg_header_t); - - if (msg->hdr.message_type != NOTIFICATION_LIGHT) { - s->cur_request = _hcng_sock_light_get_request(s, msg->hdr.seq_num); - if (!s->cur_request) return -99; - } - - /* How many elements are we expecting in the reply ? */ - s->remaining = msg->hdr.length; - hc_data_t *request_data; - - switch (msg->hdr.message_type) { - case ACK_LIGHT: - assert(s->remaining == 1); // sic - assert(!pdata); - _hcng_sock_light_mark_complete(s, pdata); - break; - - case NACK_LIGHT: - assert(!pdata); - assert(s->remaining == 1); // sic - request_data = s->cur_request->data; - _hcng_sock_light_mark_complete(s, pdata); - hc_data_set_error(request_data); - break; - - case RESPONSE_LIGHT: - assert(pdata); - - if (s->remaining == 0) { - /* Empty response (i.e. containing 0 elements) */ - _hcng_sock_light_mark_complete(s, pdata); - return 0; - } - - /* Make room in hc_data_t... to avoid multiple calls */ - rc = hc_data_ensure_available(s->cur_request->data, s->remaining); - if (rc < 0) { - ERROR("[hc_sock_light_process] Error in hc_data_ensure_available"); - return -99; - } - break; - - case NOTIFICATION_LIGHT: { - assert(pdata); - assert(s->remaining == 0); - - /* This returns the notification size */ - size_t notification_size = - _hcng_sock_light_process_notification(s, pdata); - s->roff += notification_size; - return msg->hdr.command_id; - } - - default: - ERROR("[hc_sock_light_process] Invalid response received"); - return -99; - } - - return 0; -} - -static int _hcng_sock_light_process_payload(hc_sock_light_t *s, - hc_data_t **pdata) { - int err = 0; - int rc; - - hc_data_t *data = s->cur_request->data; - - /* We only process full elements (size is stored in data) */ - size_t num_chunks = AVAILABLE(s) / data->in_element_size; - - /* Check whether we have enough data to process */ - if (num_chunks == 0) return 0; - - /* Safeguard: assert(num_chunks < s->remaining); */ - if (num_chunks > s->remaining) { - WARN( - "[_hcng_sock_light_process_payload] Unexpected num_chunks > " - "s->remaining"); - num_chunks = s->remaining; - } - - if (!s->cur_request->parse) { - /* If we don't need to parse results, then we can directly push - * all of them into the result data structure */ - hc_data_push_many(data, s->buf + s->roff, num_chunks); - } else { - /* Iterate on chunks of data */ - for (int i = 0; i < num_chunks; i++) { - /* Get storage offset in hc_data_t */ - u8 *dst = hc_data_get_next(data); - if (!dst) { - ERROR("[hc_sock_light_process] Error in hc_data_get_next"); - err = -2; - break; - } - - /* Parse element #i */ - rc = s->cur_request->parse(s->buf + s->roff + i * data->in_element_size, - dst); - if (rc < 0) { - ERROR("[hc_sock_light_process] Error in parse"); - err = -1; - /* In this case we let the loop complete to collect other results */ - } - data->size++; - } - } - - s->roff += num_chunks * data->in_element_size; - - /* - * If we are not expecting any more data, mark the reply as complete - */ - s->remaining -= num_chunks; - if (s->remaining == 0) _hcng_sock_light_mark_complete(s, pdata); - - return err; -} - -/* - * Process messages as they are received in the ring buffer. There can be - * interleaved queries and replies (they are identified by sequence number), - * and the assumption is that a reply can arrive over mutiple packets (in - * other terms, it is possible that not all data from the reply is available - * in the buffer at a given time). However, we assume that a full query is - * received at once. - */ -static int _hcng_sock_light_process(hc_sock_t *socket, hc_data_t **data) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int rc = 0; - - /* - * We loop consuming messages until there is no more data in the buffer, - * or that we can find an entire message. Messages are received - * sequentially, and we keep track of incomplete requests in s->cur_request. - */ - while (AVAILABLE(s) > 0) { - if (!s->cur_request) { - rc = _hcng_sock_light_process_header(s, data); - } else { - rc = _hcng_sock_light_process_payload(s, data); - } - if (rc < 0) break; - } - - if ((rc == -99) || (s->roff == s->woff)) { - /* Flush buffer */ - s->woff = 0; - } else { - /* Clean up read data from buffer */ - memmove(s->buf, s->buf + s->roff, AVAILABLE(s)); - s->woff -= s->roff; - } - s->roff = 0; - - return rc; -} - -static int _hcng_sock_light_callback(hc_sock_t *socket, hc_data_t **pdata) { - hc_data_t *data = NULL; - int rc = 0; - - for (;;) { - int n = _hcng_sock_light_recv(socket); - if (n == 0) goto ERR_EOF; - if (n < 0) { - switch (errno) { - case ECONNRESET: - case ENODEV: - /* Forwarder restarted */ - WARN("Forwarder likely restarted: not (yet) implemented"); - goto ERR; - case EWOULDBLOCK: - // DEBUG("Would block... stop reading from socket"); - goto END; - case EINTR: - WARN("callback has been stopped by signal"); - goto ERR; - default: - perror("hc_sock_light_callback"); - goto ERR; - } - } - rc = _hcng_sock_light_process(socket, &data); - if (rc < 0) goto ERR; - if (rc > 0) // i.e. rc = notification type - goto END; - } -END: - if (pdata) - *pdata = data; - else - hc_data_free(data); - return rc; - -ERR: - hc_data_free(data); -ERR_EOF: - return -1; -} - -/****************************************************************************** - * Command-specific structures and functions - ******************************************************************************/ - -typedef int (*HC_PARSE)(const u8 *, u8 *); - -typedef struct { - hc_action_t cmd; - command_type_t cmd_id; - size_t size_in; - size_t size_out; - HC_PARSE parse; -} hc_command_params_t; - -typedef struct hc_result_s { - hc_msg_t msg; - hc_command_params_t params; - bool async; - bool success; -} hc_result_t; - -int _hcng_sock_prepare_send(hc_sock_t *socket, hc_result_t *result, - data_callback_t complete_cb, - void *complete_cb_data) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - // Prepare data - hc_data_t *data = - hc_data_create(result->params.size_in, result->params.size_out, NULL); - if (!data) { - ERROR("[_hcng_sock_prepare_send] Could not create data storage"); - goto ERR_DATA; - } - hc_data_set_callback(data, complete_cb, complete_cb_data); - - // Update the sequence number - int seq = _hcng_sock_light_get_next_seq(socket); - result->msg.hdr.seq_num = seq; // Like in _hcng_sock_light_send - - // Create state used to process the request - hc_sock_request_t *request = NULL; - request = hc_sock_request_create(seq, data, result->params.parse); - if (!request) { - ERROR("[_hcng_sock_prepare_send] Could not create request state"); - goto ERR_REQUEST; - } - - int rc; - khiter_t k = kh_put_sock_map(s->map, seq, &rc); - if (rc != KH_ADDED && rc != KH_RESET) { - ERROR("[_hcng_sock_prepare_send] Error adding request state to map"); - goto ERR_MAP; - } - kh_value(s->map, k) = request; - - return sizeof(result->msg); - -ERR_MAP: - hc_sock_light_request_free(request); -ERR_REQUEST: - hc_data_free(data); -ERR_DATA: - return -99; -} - -int _hcng_sock_set_recv_timeout_ms(hc_sock_t *socket, long timeout_ms) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - - struct timeval tv; - tv.tv_sec = 0; - tv.tv_usec = (int)(timeout_ms * 1000); // Convert ms into us - if (setsockopt(s->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { - perror("setsockopt"); - return -1; - } - - return 0; -} - -static int _hcng_execute_command(hc_sock_t *socket, hc_msg_t *msg, - size_t msg_len, hc_command_params_t *params, - hc_data_t **pdata, bool async) { - hc_sock_light_t *s = TO_HC_SOCK_LIGHT(socket); - int ret; - if (async) assert(!pdata); - - /* Sanity check */ - switch (params->cmd) { - case ACTION_CREATE: - assert(params->size_in != 0); /* payload repeated */ - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_DELETE: - assert(params->size_in != 0); /* payload repeated */ - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_GET: - case ACTION_LIST: - assert(params->size_in != 0); - assert(params->size_out != 0); - // TODO(eloparco): Parsing should not be necessary after - // (pending) refatoring - // assert(params->parse != NULL); - break; - case ACTION_SET: - case ACTION_SERVE: - case ACTION_STORE: - case ACTION_UPDATE: - assert(params->size_in != 0); - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - case ACTION_CLEAR: - assert(params->size_in == 0); - assert(params->size_out == 0); - assert(params->parse == NULL); - break; - default: - return -1; - } - - // hc_sock_light_reset(s); - - /* XXX data will at least store the result (complete) */ - hc_data_t *data = hc_data_create(params->size_in, params->size_out, NULL); - if (!data) { - ERROR("[_hcng_execute_command] Could not create data storage"); - goto ERR_DATA; - } - - int seq = _hcng_sock_light_get_next_seq(socket); - - /* Create state used to process the request */ - hc_sock_request_t *request = NULL; - request = hc_sock_request_create(seq, data, params->parse); - if (!request) { - ERROR("[_hcng_execute_command] Could not create request state"); - goto ERR_REQUEST; - } - - /* Add state to map */ - int rc; - khiter_t k = kh_put_sock_map(s->map, seq, &rc); - if (rc != KH_ADDED && rc != KH_RESET) { - ERROR("[_hcng_execute_command] Error adding request state to map"); - goto ERR_MAP; - } - kh_value(s->map, k) = request; - - if (_hcng_sock_light_send(socket, msg, msg_len, seq) < 0) { - ERROR("[_hcng_execute_command] Error sending message"); - goto ERR_PROCESS; - } - - if (async) return 0; - - /* - * Dangerous zone, we might be doing blocking operations on a non-blocking - * UDP socket - */ - int retries = 0; - while (!data->complete) { - /* - * As the socket is non blocking it might happen that we need to read - * several times before success... - */ - int n = _hcng_sock_light_recv(socket); - if (n == 0) goto ERR_EOF; - if (n < 0) { - if ((errno == EWOULDBLOCK) && (retries < 10)) { /* Max 500ms */ - DEBUG("read = EWOULDBLOCK... sleeping for 50ms (max 500ms)"); - usleep(50000); /* 50ms */ - retries++; - continue; - } - break; - } - int rc = _hcng_sock_light_process(socket, pdata); - switch (rc) { - case 0: - case -1: - break; - case -99: - ERROR("[_hcng_execute_command] Error processing socket results"); - goto ERR; - default: - ERROR("[_hcng_execute_command] Unexpected return value"); - goto ERR; - } - } - -ERR_EOF: - ret = data->ret; - if (!data->complete) return -1; - if (!pdata) hc_data_free(data); - - return ret; - -ERR_PROCESS: -ERR_MAP: - hc_sock_light_request_free(request); -ERR: -ERR_REQUEST: - hc_data_free(data); -ERR_DATA: - return -99; -} - -/*----------------------------------------------------------------------------* - * Listeners - *----------------------------------------------------------------------------*/ - -/* LISTENER CREATE */ - -static hc_result_t *_listener_create_serialize(hc_sock_t *s, - hc_listener_t *listener, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[_hcng_listener_create] Unexpected truncation of listener string"); - DEBUG("[_hcng_listener_create] listener=%s async=%s", listener_s, - BOOLSTR(async)); - - if (hc_listener_validate(listener) < 0) { - res->success = false; - return res; - } - - msg_listener_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = listener->local_addr, - .port = htons(listener->local_port), - .family = listener->family, - .type = listener->type, - }}; - - rc = snprintf(msg.payload.symbolic, SYMBOLIC_NAME_LEN, "%s", listener->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_create] Unexpected truncation of symbolic name " - "string"); - - rc = snprintf(msg.payload.interface_name, INTERFACE_LEN, "%s", - listener->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[_hc_listener_create] Unexpected truncation of interface name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_LISTENER_ADD, - .size_in = sizeof(cmd_listener_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.listener_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_listener_create_conf(hc_sock_t *s, - hc_listener_t *listener) { - return _listener_create_serialize(s, listener, false); -} - -static int _hcng_listener_create_internal(hc_sock_t *socket, - hc_listener_t *listener, bool async) { - hc_result_t *result = _listener_create_serialize(socket, listener, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - free(result); - DEBUG("[_hcng_listener_create] done or error"); - return ret; -} - -static int _hcng_listener_create(hc_sock_t *s, hc_listener_t *listener) { - DEBUG("[_hcng_listener_create]"); - return _hcng_listener_create_internal(s, listener, false); -} - -static int _hcng_listener_create_async(hc_sock_t *s, hc_listener_t *listener) { - DEBUG("[_hcng_listener_create_async]"); - return _hcng_listener_create_internal(s, listener, true); -} - -/* LISTENER PARSE */ - -static int hc_listener_parse(void *in, hc_listener_t *listener) { - int rc; - cmd_listener_list_item_t *item = (cmd_listener_list_item_t *)in; - - if (!IS_VALID_ID(item->id)) { - ERROR("[hc_listener_parse] Invalid id received"); - return -1; - } - - *listener = (hc_listener_t){ - .id = item->id, - .type = item->type, - .family = item->family, - .local_addr = UNION_CAST(item->address, ip_address_t), - .local_port = ntohs(item->port), - }; - rc = snprintf(listener->name, SYMBOLIC_NAME_LEN, "%s", item->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[hc_listener_parse] Unexpected truncation of symbolic name string"); - rc = snprintf(listener->interface_name, INTERFACE_LEN, "%s", - item->interface_name); - if (rc >= INTERFACE_LEN) - WARN("[hc_listener_parse] Unexpected truncation of interface name string"); - - if (hc_listener_validate(listener) < 0) return -1; - return 0; -} - -/* LISTENER LIST */ - -static hc_result_t *_hcng_listener_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_listener_list] async=%s", BOOLSTR(async)); - - msg_listener_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_LISTENER_LIST, - .size_in = sizeof(cmd_listener_list_item_t), - .size_out = sizeof(hc_listener_t), - .parse = (HC_PARSE)hc_listener_parse, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.listener_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_listener_list_conf(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_listener_list_serialize(s, pdata, false); -} - -static int _hcng_listener_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_listener_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_listener_list] done or error"); - return ret; -} - -static int _hcng_listener_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_listener_list]"); - return _hcng_listener_list_internal(s, pdata, false); -} - -static int _hcng_listener_list_async(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_listener_list_as-nc]"); - return _hcng_listener_list_internal(s, pdata, true); -} - -/* LISTENER GET */ - -static int _hcng_listener_get(hc_sock_t *socket, hc_listener_t *listener, - hc_listener_t **listener_found) { - hc_data_t *listeners; - hc_listener_t *found; - - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[hc_listener_get] Unexpected truncation of listener string"); - DEBUG("[hc_listener_get] listener=%s", listener_s); - - if (_hcng_listener_list(socket, &listeners) < 0) return -1; - - /* Test */ - if (hc_listener_find(listeners, listener, &found) < 0) { - hc_data_free(listeners); - return -1; - } - - if (found) { - *listener_found = malloc(sizeof(hc_listener_t)); - if (!*listener_found) return -1; - **listener_found = *found; - } else { - *listener_found = NULL; - } - - hc_data_free(listeners); - - return 0; -} - -/* LISTENER DELETE */ - -static int _hcng_listener_delete_internal(hc_sock_t *socket, - hc_listener_t *listener, bool async) { - char listener_s[MAXSZ_HC_LISTENER]; - int rc = hc_listener_snprintf(listener_s, MAXSZ_HC_LISTENER, listener); - if (rc >= MAXSZ_HC_LISTENER) - WARN("[_hcng_listener_delete] Unexpected truncation of listener string"); - DEBUG("[_hcng_listener_delete] listener=%s async=%s", listener_s, - BOOLSTR(async)); - - msg_listener_remove_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_LISTENER_REMOVE, - .length = 1, - .seq_num = 0, - }}; - - if (listener->id) { - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", - listener->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - } else if (*listener->name) { - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%s", - listener->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - } else { - hc_listener_t *listener_found; - if (_hcng_listener_get(socket, listener, &listener_found) < 0) return -1; - if (!listener_found) return -1; - rc = snprintf(msg.payload.symbolicOrListenerid, SYMBOLIC_NAME_LEN, "%d", - listener_found->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_listener_delete] Unexpected truncation of symbolic name " - "string"); - free(listener_found); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_LISTENER_REMOVE, - .size_in = sizeof(cmd_listener_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - return _hcng_listener_delete_internal(s, listener, false); -} - -static int _hcng_listener_delete_async(hc_sock_t *s, hc_listener_t *listener) { - return _hcng_listener_delete_internal(s, listener, true); -} - -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -/* CONNECTION CREATE */ - -static hc_result_t *_connection_create_serialize(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN( - "[_hcng_connection_create] Unexpected truncation of connection " - "string"); - DEBUG("[_hcng_connection_create] connection=%s async=%s", connection_s, - BOOLSTR(async)); - - if (hc_connection_validate(connection) < 0) { - res->success = false; - return res; - } - - msg_connection_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .remote_ip = connection->remote_addr, - .local_ip = connection->local_addr, - .remote_port = htons(connection->remote_port), - .local_port = htons(connection->local_port), - .family = connection->family, - .type = connection->type, - .admin_state = connection->admin_state, -#ifdef WITH_POLICY - .priority = connection->priority, - .tags = connection->tags, -#endif /* WITH_POLICY */ - }}; - rc = - snprintf(msg.payload.symbolic, SYMBOLIC_NAME_LEN, "%s", connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_create] Unexpected truncation of symbolic name " - "string"); - // snprintf(msg.payload.interface_name, INTERFACE_NAME_LEN, "%s", - // connection->interface_name); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_CONNECTION_ADD, - .size_in = sizeof(cmd_connection_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.connection_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - return _connection_create_serialize(s, connection, false); -} - -static int _hcng_connection_create_internal(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *result = _connection_create_serialize(socket, connection, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_connection_create] done or error"); - return ret; -} - -static int _hcng_connection_create(hc_sock_t *s, hc_connection_t *connection) { - DEBUG("[_hcng_connection_create]"); - return _hcng_connection_create_internal(s, connection, false); -} - -static int _hcng_connection_create_async(hc_sock_t *s, - hc_connection_t *connection) { - DEBUG("[_hcng_connection_create_async]"); - return _hcng_connection_create_internal(s, connection, true); -} - -/* CONNECTION PARSE */ - -static int hc_connection_parse(void *in, hc_connection_t *connection) { - int rc; - cmd_connection_list_item_t *item = (cmd_connection_list_item_t *)in; - - if (!IS_VALID_ID(item->id)) { - ERROR("[hc_connection_parse] Invalid id received"); - return -1; - } - - *connection = (hc_connection_t){ - .id = item->id, - .type = item->type, - .family = item->family, - .local_addr = item->local_addr, - .local_port = ntohs(item->local_port), - .remote_addr = item->remote_addr, - .remote_port = ntohs(item->remote_port), - .admin_state = item->admin_state, -#ifdef WITH_POLICY - .priority = item->priority, - .tags = item->tags, -#endif /* WITH_POLICY */ - .state = item->state, - }; - rc = snprintf(connection->name, SYMBOLIC_NAME_LEN, "%s", item->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[hc_connection_parse] Unexpected truncation of symbolic name " - "string"); - rc = snprintf(connection->interface_name, INTERFACE_LEN, "%s", - item->interface_name); - if (rc >= INTERFACE_LEN) - WARN( - "[hc_connection_parse] Unexpected truncation of interface name " - "string"); - - if (hc_connection_validate(connection) < 0) return -1; - return 0; -} - -/* CONNECTION LIST */ - -static int _hcng_connection_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - DEBUG("[hc_connection_list] async=%s", BOOLSTR(async)); - - msg_connection_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CONNECTION_LIST, - .size_in = sizeof(cmd_connection_list_item_t), - .size_out = sizeof(hc_connection_t), - .parse = (HC_PARSE)hc_connection_parse, - }; - - int ret = _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), - ¶ms, pdata, async); - - DEBUG("[hc_connection_list] done or error"); - return ret; -} - -static int _hcng_connection_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[hc_connection_list]"); - return _hcng_connection_list_internal(s, pdata, false); -} - -static int _hcng_connection_list_async(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[hc_connection_list_async]"); - return _hcng_connection_list_internal(s, pdata, true); -} - -/* CONNECTION GET */ - -static int _hcng_connection_get(hc_sock_t *socket, hc_connection_t *connection, - hc_connection_t **connection_found) { - hc_data_t *connections; - hc_connection_t *found; - - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN("[hc_connection_get] Unexpected truncation of connection string"); - DEBUG("[hc_connection_get] connection=%s", connection_s); - - if (_hcng_connection_list(socket, &connections) < 0) return -1; - - /* Test */ - if (hc_connection_find(connections, connection, &found) < 0) { - hc_data_free(connections); - return -1; - } - - if (found) { - *connection_found = malloc(sizeof(hc_connection_t)); - if (!*connection_found) return -1; - **connection_found = *found; - } else { - *connection_found = NULL; - } - - hc_data_free(connections); - - return 0; -} - -/* CONNECTION DELETE */ - -static hc_result_t *_hcng_connection_delete_serialize( - hc_sock_t *socket, hc_connection_t *connection, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - res->success = false; - - char connection_s[MAXSZ_HC_CONNECTION]; - int rc = - hc_connection_snprintf(connection_s, MAXSZ_HC_CONNECTION, connection); - if (rc >= MAXSZ_HC_CONNECTION) - WARN( - "[_hcng_connection_delete] Unexpected truncation of connection " - "string"); - DEBUG("[_hcng_connection_delete] connection=%s async=%s", connection_s, - BOOLSTR(async)); - - msg_connection_remove_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_REMOVE, - .length = 1, - .seq_num = 0, - }, - }; - - if (connection->id) { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - connection->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - } else if (*connection->name) { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - connection->name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - } else { - hc_connection_t *connection_found; - if (hc_connection_get(socket, connection, &connection_found) < 0) - return res; - if (!connection_found) return res; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - connection_found->id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_delete] Unexpected truncation of symbolic name " - "string"); - free(connection_found); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_CONNECTION_REMOVE, - .size_in = sizeof(cmd_connection_remove_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.connection_remove = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_connection_delete_conf(hc_sock_t *s, - hc_connection_t *connection) { - return _hcng_connection_delete_serialize(s, connection, false); -} - -static int _hcng_connection_delete_internal(hc_sock_t *socket, - hc_connection_t *connection, - bool async) { - hc_result_t *result = - _hcng_connection_delete_serialize(socket, connection, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - return _hcng_connection_delete_internal(s, connection, false); -} - -static int _hcng_connection_delete_async(hc_sock_t *s, - hc_connection_t *connection) { - return _hcng_connection_delete_internal(s, connection, true); -} - -/* CONNECTION UPDATE */ - -static int _hcng_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - // Not implemented - return -1; -} - -static int _hcng_connection_update(hc_sock_t *s, - hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - // Not implemented - return -1; -} - -/* CONNECTION SET ADMIN STATE */ - -static int _hcng_connection_set_admin_state_internal( - hc_sock_t *socket, const char *conn_id_or_name, face_state_t state, - bool async) { - int rc; - DEBUG( - "[hc_connection_set_admin_state] connection_id/name=%s admin_state=%s " - "async=%s", - conn_id_or_name, face_state_str(state), BOOLSTR(async)); - - struct { - cmd_header_t hdr; - cmd_connection_set_admin_state_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .admin_state = state, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_admin_state] Unexpected truncation of symbolic " - "name string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_ADMIN_STATE, - .size_in = sizeof(cmd_connection_set_admin_state_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_connection_set_admin_state(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - return _hcng_connection_set_admin_state_internal(s, conn_id_or_name, state, - false); -} - -static int _hcng_connection_set_admin_state_async(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - return _hcng_connection_set_admin_state_internal(s, conn_id_or_name, state, - true); -} - -#ifdef WITH_POLICY - -static int _hcng_connection_set_priority_internal(hc_sock_t *socket, - const char *conn_id_or_name, - uint32_t priority, - bool async) { - int rc; - DEBUG( - "[hc_connection_set_priority] connection_id/name=%s priority=%d " - "async=%s", - conn_id_or_name, priority, BOOLSTR(async)); - struct { - cmd_header_t hdr; - cmd_connection_set_priority_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .priority = priority, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_priority] Unexpected truncation of symbolic " - "name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_PRIORITY, - .size_in = sizeof(cmd_connection_set_priority_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_connection_set_priority(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - return _hcng_connection_set_priority_internal(s, conn_id_or_name, priority, - false); -} - -static int _hcng_connection_set_priority_async(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - return _hcng_connection_set_priority_internal(s, conn_id_or_name, priority, - true); -} - -#endif // WITH_POLICY - -static int _hcng_connection_set_tags_internal(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags, bool async) { - int rc; - DEBUG("[hc_connection_set_tags] connection_id/name=%s tags=%d async=%s", - conn_id_or_name, tags, BOOLSTR(async)); - struct { - cmd_header_t hdr; - cmd_connection_set_tags_t payload; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_SET_TAGS, - .length = 1, - .seq_num = 0, - }, - .payload = - { - .tags = tags, - }, - }; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - conn_id_or_name); - if (rc >= SYMBOLIC_NAME_LEN) - WARN( - "[_hc_connection_set_tags] Unexpected truncation of symbolic name " - "string"); - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_CONNECTION_SET_TAGS, - .size_in = sizeof(cmd_connection_set_tags_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(s, (hc_msg_t *)&msg, sizeof(msg), ¶ms, NULL, - async); -} - -static int _hcng_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return _hcng_connection_set_tags_internal(s, conn_id_or_name, tags, false); -} - -static int _hcng_connection_set_tags_async(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags) { - return _hcng_connection_set_tags_internal(s, conn_id_or_name, tags, true); -} - -/*----------------------------------------------------------------------------* - * Routes - *----------------------------------------------------------------------------*/ - -/* ROUTE CREATE */ - -static hc_result_t *_route_create_serialize(hc_sock_t *socket, - hc_route_t *route, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - char route_s[MAXSZ_HC_ROUTE]; - int rc = hc_route_snprintf(route_s, MAXSZ_HC_ROUTE, route); - if (rc >= MAXSZ_HC_ROUTE) - WARN("[_hc_route_create] Unexpected truncation of route string"); - if (rc < 0) - WARN("[_hc_route_create] Error building route string"); - else - DEBUG("[hc_route_create] route=%s async=%s", route_s, BOOLSTR(async)); - - if (hc_route_validate(route) < 0) { - res->success = false; - return res; - } - - msg_route_add_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = route->remote_addr, - .cost = route->cost, - .family = route->family, - .len = route->len, - }}; - - /* - * The route commands expects the ID or name as part of the - * symbolic_or_connid attribute. - */ - if (route->name[0] != '\0') { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - route->name); - } else { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - route->face_id); - } - - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[_hc_route_create] Unexpected truncation of symbolic name string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_ROUTE_ADD, - .size_in = sizeof(cmd_route_add_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.route_add = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_route_create_conf(hc_sock_t *s, hc_route_t *route) { - return _route_create_serialize(s, route, false); -} - -static int _hcng_route_create_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - hc_result_t *result = _route_create_serialize(socket, route, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_route_create(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_create_internal(s, route, false); -} - -static int _hcng_route_create_async(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_create_internal(s, route, true); -} - -/* ROUTE DELETE */ - -static int _hcng_route_delete_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - char route_s[MAXSZ_HC_ROUTE]; - int rc = hc_route_snprintf(route_s, MAXSZ_HC_ROUTE, route); - if (rc >= MAXSZ_HC_ROUTE) - WARN("[_hc_route_delete] Unexpected truncation of route string"); - DEBUG("[hc_route_delete] route=%s async=%s", route_s, BOOLSTR(async)); - - if (!IS_VALID_FAMILY(route->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_route_remove_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = route->remote_addr, - .family = route->family, - .len = route->len, - }}; - - /* - * The route commands expects the ID or name as part of the - * symbolic_or_connid attribute. - */ - if (route->name[0] != '\0') { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%s", - route->name); - } else { - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - route->face_id); - } - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_ROUTE_REMOVE, - .size_in = sizeof(cmd_route_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_route_delete(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_delete_internal(s, route, false); -} - -static int _hcng_route_delete_async(hc_sock_t *s, hc_route_t *route) { - return _hcng_route_delete_internal(s, route, true); -} - -/* ROUTE PARSE */ - -static int hc_route_parse(void *in, hc_route_t *route) { - cmd_route_list_item_t *item = (cmd_route_list_item_t *)in; - - *route = (hc_route_t){ - .name = "", /* This is not reported back */ - .face_id = item->connection_id, - .family = item->family, - .remote_addr = item->address, - .len = item->len, - .cost = item->cost, - }; - - if (hc_route_validate(route) < 0) return -1; - return 0; -} - -/* ROUTE LIST */ - -static int _hcng_route_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - // DEBUG("[hc_route_list] async=%s", BOOLSTR(async)); - msg_route_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_ROUTE_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_ROUTE_LIST, - .size_in = sizeof(cmd_route_list_item_t), - .size_out = sizeof(hc_route_t), - .parse = (HC_PARSE)hc_route_parse, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - pdata, async); -} - -static int _hcng_route_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_route_list_internal(s, pdata, false); -} - -static int _hcng_route_list_async(hc_sock_t *s) { - return _hcng_route_list_internal(s, NULL, true); -} - -/*----------------------------------------------------------------------------* - * Face - * - * Face support is not directly available in hicn-light, but we can offer such - * an interface through a combination of listeners and connections. The code - * starts with some conversion functions between faces/listeners/connections. - * - * We also need to make sure that there always exist a (single) listener when - *a connection is created, and in the hICN face case, that there is a single - * connection attached to this listener. - * - *----------------------------------------------------------------------------*/ - -/* FACE CREATE */ - -static int _hcng_face_create(hc_sock_t *socket, hc_face_t *face) { - hc_listener_t listener; - hc_listener_t *listener_found; - - hc_connection_t connection; - hc_connection_t *connection_found; - - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_create] Unexpected truncation of face string"); - DEBUG("[hc_face_create] face=%s", face_s); - - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - if (hc_face_to_connection(face, &connection, false) < 0) { - ERROR("[hc_face_create] Could not convert face to connection."); - return -1; - } - - /* Ensure we have a corresponding local listener */ - if (hc_connection_to_local_listener(&connection, &listener) < 0) { - ERROR("[hc_face_create] Could not convert face to local listener."); - return -1; - } - - if (_hcng_listener_get(socket, &listener, &listener_found) < 0) { - ERROR("[hc_face_create] Could not retrieve listener"); - return -1; - } - - if (!listener_found) { - /* We need to create the listener if it does not exist */ - if (hc_listener_create(socket, &listener) < 0) { - ERROR("[hc_face_create] Could not create listener."); - free(listener_found); - return -1; - } - } else { - free(listener_found); - } - - /* Create corresponding connection */ - if (_hcng_connection_create(socket, &connection) < 0) { - ERROR("[hc_face_create] Could not create connection."); - return -1; - } - - /* - * Once the connection is created, we need to list all connections - * and compare with the current one to find the created face ID. - */ - if (_hcng_connection_get(socket, &connection, &connection_found) < 0) { - ERROR("[hc_face_create] Could not retrieve connection"); - return -1; - } - - if (!connection_found) { - ERROR("[hc_face_create] Could not find newly created connection."); - return -1; - } - - face->id = connection_found->id; - free(connection_found); - - break; - - case FACE_TYPE_HICN_LISTENER: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - if (hc_face_to_listener(face, &listener) < 0) { - ERROR("Could not convert face to listener."); - return -1; - } - if (hc_listener_create(socket, &listener) < 0) { - ERROR("[hc_face_create] Could not create listener."); - return -1; - } - break; - default: - ERROR("[hc_face_create] Unknwon face type."); - - return -1; - }; - - return 0; -} - -static int _hcng_face_get(hc_sock_t *socket, hc_face_t *face, - hc_face_t **face_found) { - hc_listener_t listener; - hc_listener_t *listener_found; - - hc_connection_t connection; - hc_connection_t *connection_found; - - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_get] Unexpected truncation of face string"); - DEBUG("[hc_face_get] face=%s", face_s); - - switch (face->face.type) { - case FACE_TYPE_HICN: - case FACE_TYPE_TCP: - case FACE_TYPE_UDP: - if (hc_face_to_connection(face, &connection, false) < 0) return -1; - if (_hcng_connection_get(socket, &connection, &connection_found) < 0) - return -1; - if (!connection_found) { - *face_found = NULL; - return 0; - } - *face_found = malloc(sizeof(hc_face_t)); - hc_connection_to_face(connection_found, *face_found); - free(connection_found); - break; - - case FACE_TYPE_HICN_LISTENER: - case FACE_TYPE_TCP_LISTENER: - case FACE_TYPE_UDP_LISTENER: - if (hc_face_to_listener(face, &listener) < 0) return -1; - if (_hcng_listener_get(socket, &listener, &listener_found) < 0) return -1; - if (!listener_found) { - *face_found = NULL; - return 0; - } - *face_found = malloc(sizeof(hc_face_t)); - hc_listener_to_face(listener_found, *face_found); - free(listener_found); - break; - - default: - return -1; - } - - return 0; -} - -/* FACE DELETE */ - -static int _hcng_face_delete(hc_sock_t *socket, hc_face_t *face, - uint8_t delete_listener) { - char face_s[MAXSZ_HC_FACE]; - int rc = hc_face_snprintf(face_s, MAXSZ_HC_FACE, face); - if (rc >= MAXSZ_HC_FACE) - WARN("[hc_face_delete] Unexpected truncation of face string"); - DEBUG("[hc_face_delete] face=%s", face_s); - - hc_connection_t connection; - if (hc_face_to_connection(face, &connection, false) < 0) { - ERROR("[hc_face_delete] Could not convert face to connection."); - return -1; - } - - if (_hcng_connection_delete(socket, &connection) < 0) { - ERROR("[hc_face_delete] Error removing connection"); - return -1; - } - - if (!delete_listener) { - return 0; - } - - /* If this is the last connection attached to the listener, remove it */ - - hc_data_t *connections; - hc_listener_t listener = {{0}}; - - /* - * Ensure we have a corresponding local listener - * NOTE: hc_face_to_listener is not appropriate - */ - if (hc_connection_to_local_listener(&connection, &listener) < 0) { - ERROR("[hc_face_create] Could not convert face to local listener."); - return -1; - } -#if 1 - /* - * The name is generated to prepare listener creation, we need it to be - * empty for deletion. The id should not need to be reset though. - */ - listener.id = 0; - memset(listener.name, 0, sizeof(listener.name)); -#endif - if (_hcng_connection_list(socket, &connections) < 0) { - ERROR("[hc_face_delete] Error getting the list of listeners"); - return -1; - } - - bool delete = true; - foreach_connection(c, connections) { - if ((ip_address_cmp(&c->local_addr, &listener.local_addr, c->family) == - 0) && - (c->local_port == listener.local_port) && - (strcmp(c->interface_name, listener.interface_name) == 0)) { - delete = false; - } - } - - if (delete) { - if (_hcng_listener_delete(socket, &listener) < 0) { - ERROR("[hc_face_delete] Error removing listener"); - return -1; - } - } - - hc_data_free(connections); - - return 0; -} - -/* FACE LIST */ - -static int _hcng_face_list(hc_sock_t *socket, hc_data_t **pdata) { - hc_data_t *connection_data; - hc_face_t face; - - DEBUG("[hc_face_list]"); - - if (_hcng_connection_list(socket, &connection_data) < 0) { - ERROR("[hc_face_list] Could not list connections."); - return -1; - } - - hc_data_t *face_data = - hc_data_create(sizeof(hc_connection_t), sizeof(hc_face_t), NULL); - foreach_connection(c, connection_data) { - if (hc_connection_to_face(c, &face) < 0) { - ERROR("[hc_face_list] Could not convert connection to face."); - goto ERR; - } - hc_data_push(face_data, &face); - } - - *pdata = face_data; - hc_data_free(connection_data); - DEBUG("[hc_face_list] done"); - return 0; - -ERR: - hc_data_free(connection_data); - DEBUG("[hc_face_list] error"); - return -1; -} - -static int hc_connection_parse_to_face(void *in, hc_face_t *face) { - hc_connection_t connection; - - if (hc_connection_parse(in, &connection) < 0) { - ERROR("[hc_connection_parse_to_face] Could not parse connection"); - return -1; - } - - if (hc_connection_to_face(&connection, face) < 0) { - ERROR( - "[hc_connection_parse_to_face] Could not convert connection to " - "face."); - return -1; - } - - return 0; -} - -static int _hcng_face_list_async(hc_sock_t *socket) { - struct { - cmd_header_t hdr; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CONNECTION_LIST, - .length = 0, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CONNECTION_LIST, - .size_in = sizeof(cmd_connection_list_item_t), - .size_out = sizeof(hc_face_t), - .parse = (HC_PARSE)hc_connection_parse_to_face, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, true); -} - -static int _hcng_face_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, - face_state_t admin_state) { - return hc_connection_set_admin_state(s, conn_id_or_name, admin_state); -} - -#ifdef WITH_POLICY -static int _hcng_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - return hc_connection_set_priority(s, conn_id_or_name, priority); -} - -static int _hcng_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - return hc_connection_set_tags(s, conn_id_or_name, tags); -} -#endif // WITH_POLICY - -/*----------------------------------------------------------------------------* - * Punting - *----------------------------------------------------------------------------*/ - -static int _hcng_punting_create_internal(hc_sock_t *socket, - hc_punting_t *punting, bool async) { - int rc; - - if (hc_punting_validate(punting) < 0) return -1; - - struct { - cmd_header_t hdr; - cmd_punting_add_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_PUNTING_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = punting->prefix, - .family = punting->family, - .len = punting->prefix_len, - }}; - rc = snprintf(msg.payload.symbolic_or_connid, SYMBOLIC_NAME_LEN, "%d", - punting->face_id); - if (rc >= SYMBOLIC_NAME_LEN) - WARN("[_hc_punting_create] Unexpected truncation of symbolic name string"); - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_PUNTING_ADD, - .size_in = sizeof(cmd_punting_add_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_punting_create(hc_sock_t *s, hc_punting_t *punting) { - return _hcng_punting_create_internal(s, punting, false); -} - -static int _hcng_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { - return _hcng_punting_create_internal(s, punting, true); -} - -static int _hcng_punting_get(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found) { - ERROR("hc_punting_get not (yet) implemented."); - return -1; -} - -static int _hcng_punting_delete(hc_sock_t *s, hc_punting_t *punting) { - ERROR("hc_punting_delete not (yet) implemented."); - return -1; -} - -#if 0 -static int hc_punting_parse(void * in, hc_punting_t * punting) -{ - ERROR("hc_punting_parse not (yet) implemented."); - return -1; -} -#endif - -static int _hcng_punting_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("hc_punting_list not (yet) implemented."); - return -1; -} - -/*----------------------------------------------------------------------------* - * Cache - *----------------------------------------------------------------------------*/ - -/* CACHE SET STORE */ - -static int _hcng_cache_set_store_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_set_store_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_SET_STORE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = cache->store, - }}; - - hc_command_params_t params = { - .cmd = ACTION_STORE, - .cmd_id = COMMAND_TYPE_CACHE_SET_STORE, - .size_in = sizeof(cmd_cache_set_store_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_store_internal(s, cache, false); -} - -static int _hcng_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_store_internal(s, cache, true); -} - -/* CACHE SET SERVE */ - -static int _hcng_cache_set_serve_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_set_serve_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_SET_SERVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = cache->serve, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SERVE, - .cmd_id = COMMAND_TYPE_CACHE_SET_SERVE, - .size_in = sizeof(cmd_cache_set_serve_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_serve_internal(s, cache, false); -} - -static int _hcng_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_set_serve_internal(s, cache, true); -} - -/* CACHE CLEAR */ - -static int _hcng_cache_clear_internal(hc_sock_t *socket, hc_cache_t *cache, - bool async) { - msg_cache_clear_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_CLEAR, - .length = 1, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_CLEAR, - .cmd_id = COMMAND_TYPE_CACHE_CLEAR, - .size_in = sizeof(cmd_cache_clear_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_cache_clear(hc_sock_t *s, hc_cache_t *cache) { - return _hcng_cache_clear_internal(s, cache, false); -} - -/* CACHE PARSE */ - -static int hc_cache_parse(void *in, hc_cache_info_t *cache_info) { - cmd_cache_list_reply_t *item = (cmd_cache_list_reply_t *)in; - *cache_info = (hc_cache_info_t){.store = item->store_in_cs, - .serve = item->serve_from_cs, - .cs_size = item->cs_size, - .num_stale_entries = item->num_stale_entries}; - - return 0; -} - -/* CACHE LIST */ - -static hc_result_t *_hcng_cache_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_cache_list] async=%s", BOOLSTR(async)); - - msg_cache_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_CACHE_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_CACHE_LIST, - .size_in = sizeof(cmd_cache_list_reply_t), - .size_out = sizeof(hc_cache_info_t), - .parse = (HC_PARSE)hc_cache_parse, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.cache_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_cache_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_cache_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - return ret; -} - -static int _hcng_cache_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_cache_list_internal(s, pdata, false); -} - -/*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -// per prefix -static hc_result_t *_strategy_set_serialize(hc_sock_t *socket, - hc_strategy_t *strategy) { - hc_result_t *res = malloc(sizeof(*res)); - - char strategy_s[MAXSZ_HC_STRATEGY]; - int rc = strcpy_s(strategy->name, MAXSZ_STRATEGY_NAME, - strategy_str(strategy->type)); - if (rc != EOK) goto ERR; - rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); - if (rc >= MAXSZ_HC_STRATEGY) - WARN("[_hcng_strategy_create] Unexpected truncation of strategy string"); - DEBUG("[_hcng_strategy_create] strategy=%s", strategy_s); - - if (!IS_VALID_FAMILY(strategy->family) || - !IS_VALID_STRATEGY_TYPE(strategy->type)) { - goto ERR; - } - - msg_strategy_set_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STRATEGY_SET, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = strategy->address, - .family = strategy->family, - .len = strategy->len, - .type = strategy->type, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_STRATEGY_SET, - .size_in = sizeof(cmd_strategy_set_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.strategy_set = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; - -ERR: - res->success = false; - return res; -} - -static hc_result_t *_strategy_add_local_prefix_serialize( - hc_sock_t *socket, hc_strategy_t *strategy) { - hc_result_t *res = malloc(sizeof(*res)); - - char strategy_s[MAXSZ_HC_STRATEGY]; - int rc = strcpy_s(strategy->name, MAXSZ_STRATEGY_NAME, - strategy_str(strategy->type)); - if (rc != EOK) goto ERR; - rc = hc_strategy_snprintf(strategy_s, MAXSZ_HC_STRATEGY, strategy); - if (rc >= MAXSZ_HC_STRATEGY) - WARN("[_hcng_strategy_create] Unexpected truncation of strategy string"); - DEBUG("[_hcng_strategy_create] strategy=%s", strategy_s); - - if (!IS_VALID_FAMILY(strategy->family) || - !IS_VALID_STRATEGY_TYPE(strategy->type) || - !IS_VALID_FAMILY(strategy->local_family)) { - goto ERR; - } - - msg_strategy_add_local_prefix_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, - .length = 1, - .seq_num = 0, - }, - .payload = { - .type = strategy->type, - .address = strategy->address, - .family = strategy->family, - .len = strategy->len, - .local_address = strategy->local_address, - .local_family = strategy->local_family, - .local_len = strategy->local_len, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_STRATEGY_ADD_LOCAL_PREFIX, - .size_in = sizeof(cmd_strategy_add_local_prefix_t), - .size_out = 0, - .parse = NULL, - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.strategy_add_local_prefix = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; - -ERR: - res->success = false; - return res; -} - -static hc_result_t *_hcng_strategy_set_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - return _strategy_set_serialize(s, strategy); -} - -static int _hcng_strategy_set(hc_sock_t *socket, hc_strategy_t *strategy) { - hc_result_t *result = _strategy_set_serialize(socket, strategy); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -static hc_result_t *_hcng_strategy_add_local_prefix_conf( - hc_sock_t *s, hc_strategy_t *strategy) { - return _strategy_add_local_prefix_serialize(s, strategy); -} - -static int _hcng_strategy_add_local_prefix(hc_sock_t *socket, - hc_strategy_t *strategy) { - hc_result_t *result = _strategy_add_local_prefix_serialize(socket, strategy); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/* How to retrieve that from the forwarder ? */ -static const char *strategies[] = { - "random", - "load_balancer", -}; - -#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*array)) - -static int _hcng_strategy_list(hc_sock_t *s, hc_data_t **data) { - int rc; - - *data = hc_data_create(0, sizeof(hc_strategy_t), NULL); - - for (unsigned i = 0; i < ARRAY_SIZE(strategies); i++) { - hc_strategy_t *strategy = (hc_strategy_t *)hc_data_get_next(*data); - if (!strategy) return -1; - rc = snprintf(strategy->name, MAXSZ_STRATEGY_NAME, "%s", strategies[i]); - if (rc >= MAXSZ_STRATEGY_NAME) - WARN("[hc_strategy_list] Unexpected truncation of strategy name string"); - (*data)->size++; - } - - return 0; -} - -/*----------------------------------------------------------------------------* - * WLDR - *----------------------------------------------------------------------------*/ - -// per connection -static int _hcng_wldr_set(hc_sock_t *s /* XXX */) { return 0; } - -/*----------------------------------------------------------------------------* - * MAP-Me - *----------------------------------------------------------------------------*/ - -static int _hcng_mapme_set(hc_sock_t *socket, int enabled) { - msg_mapme_enable_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_ENABLE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = enabled, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_ENABLE, - .size_in = sizeof(cmd_mapme_enable_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_discovery(hc_sock_t *socket, int enabled) { - msg_mapme_enable_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, - .length = 1, - .seq_num = 0, - }, - .payload = { - .activate = enabled, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_DISCOVERY, - .size_in = sizeof(cmd_mapme_set_discovery_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_timescale(hc_sock_t *socket, uint32_t timescale) { - msg_mapme_set_timescale_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .timePeriod = timescale, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_TIMESCALE, - .size_in = sizeof(cmd_mapme_set_timescale_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_set_retx(hc_sock_t *socket, uint32_t timescale) { - msg_mapme_set_retx_t msg = {.header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SET_RETX, - .length = 1, - .seq_num = 0, - }, - .payload = { - .timePeriod = timescale, - }}; - - hc_command_params_t params = { - .cmd = ACTION_SET, - .cmd_id = COMMAND_TYPE_MAPME_SET_RETX, - .size_in = sizeof(msg_mapme_set_retx_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -static int _hcng_mapme_send_update(hc_sock_t *socket, hc_mapme_t *mapme) { - if (!IS_VALID_FAMILY(mapme->family)) return -1; - - msg_mapme_send_update_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_MAPME_SEND_UPDATE, - .length = 1, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_UPDATE, - .cmd_id = COMMAND_TYPE_MAPME_SEND_UPDATE, - .size_in = sizeof(msg_mapme_send_update_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, false); -} - -/*----------------------------------------------------------------------------* - * Policy - *----------------------------------------------------------------------------*/ - -#ifdef WITH_POLICY - -/* POLICY CREATE */ - -static int _hcng_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - if (!IS_VALID_FAMILY(policy->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_policy_add_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - COMMAND_TYPE_POLICY_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = policy->remote_addr, - .family = policy->family, - .len = policy->len, - .policy = policy->policy, - }}; - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_POLICY_ADD, - .size_in = sizeof(cmd_policy_add_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_policy_create(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_create_internal(s, policy, false); -} - -static int _hcng_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_create_internal(s, policy, true); -} - -/* POLICY DELETE */ - -static int _hcng_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - if (!IS_VALID_FAMILY(policy->family)) return -1; - - struct { - cmd_header_t hdr; - cmd_policy_remove_t payload; - } msg = {.hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_POLICY_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = { - .address = policy->remote_addr, - .family = policy->family, - .len = policy->len, - }}; - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_POLICY_REMOVE, - .size_in = sizeof(cmd_policy_remove_t), - .size_out = 0, - .parse = NULL, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - NULL, async); -} - -static int _hcng_policy_delete(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_delete_internal(s, policy, false); -} - -static int _hcng_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { - return _hcng_policy_delete_internal(s, policy, true); -} - -/* POLICY PARSE */ - -static int hc_policy_parse(void *in, hc_policy_t *policy) { - cmd_policy_list_item_t *item = (cmd_policy_list_item_t *)in; - - if (!IS_VALID_ADDRESS(&item->address, item->family)) { - ERROR("[hc_policy_parse] Invalid address"); - return -1; - } - if (!IS_VALID_FAMILY(item->family)) { - ERROR("[hc_policy_parse] Invalid family"); - return -1; - } - if (!IS_VALID_PREFIX_LEN(item->len)) { - ERROR("[hc_policy_parse] Invalid len"); - return -1; - } - if (!IS_VALID_POLICY(item->policy)) { - ERROR("[hc_policy_parse] Invalid policy"); - return -1; - } - - *policy = (hc_policy_t){ - .family = item->family, - .remote_addr = item->address, - .len = item->len, - .policy = item->policy, - }; - return 0; -} - -/* POLICY LIST */ - -static int _hcng_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - struct { - cmd_header_t hdr; - } msg = { - .hdr = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_POLICY_LIST, - .length = 0, - .seq_num = 0, - }, - }; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_POLICY_LIST, - .size_in = sizeof(cmd_policy_list_item_t), - .size_out = sizeof(hc_policy_t), - .parse = (HC_PARSE)hc_policy_parse, - }; - - return _hcng_execute_command(socket, (hc_msg_t *)&msg, sizeof(msg), ¶ms, - pdata, async); -} - -static int _hcng_policy_list(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_policy_list_internal(s, pdata, false); -} - -static int _hcng_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { - return _hcng_policy_list_internal(s, pdata, true); -} - -#endif /* WITH_POLICY */ - -/*----------------------------------------------------------------------------* - * Subscriptioins - *----------------------------------------------------------------------------*/ - -/* SUBSCRIPTION CREATE */ - -static hc_result_t *_subscription_create_serialize( - hc_sock_t *s, hc_subscription_t *subscription) { - msg_subscription_add_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_SUBSCRIPTION_ADD, - .length = 1, - .seq_num = 0, - }, - .payload = {.topics = subscription->topics}}; - - hc_command_params_t params = { - .cmd = ACTION_CREATE, - .cmd_id = COMMAND_TYPE_SUBSCRIPTION_ADD, - .size_in = sizeof(cmd_subscription_add_t), - .size_out = 0, - .parse = NULL, - }; - - hc_result_t *res = malloc(sizeof(*res)); - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.subscription_add = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_subscription_create_conf( - hc_sock_t *s, hc_subscription_t *subscription) { - return _subscription_create_serialize(s, subscription); -} - -static int _hcng_subscription_create(hc_sock_t *socket, - hc_subscription_t *subscriiption) { - hc_result_t *result = _subscription_create_serialize(socket, subscriiption); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/* SUBSCRIPTION DELETE */ - -static hc_result_t *_subscription_delete_serialize( - hc_sock_t *s, hc_subscription_t *subscription) { - msg_subscription_remove_t msg = { - .header = - { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, - .length = 1, - .seq_num = 0, - }, - .payload = {.topics = subscription->topics}}; - - hc_command_params_t params = { - .cmd = ACTION_DELETE, - .cmd_id = COMMAND_TYPE_SUBSCRIPTION_REMOVE, - .size_in = sizeof(cmd_subscription_remove_t), - .size_out = 0, - .parse = NULL, - }; - - hc_result_t *res = malloc(sizeof(*res)); - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.subscription_remove = msg.payload, - }, - .params = params, - .async = false, - .success = true, - }; - return res; -} - -static hc_result_t *_hcng_subscription_delete_conf( - hc_sock_t *s, hc_subscription_t *subscription) { - return _subscription_delete_serialize(s, subscription); -} - -static int _hcng_subscription_delete(hc_sock_t *socket, - hc_subscription_t *subscriiption) { - hc_result_t *result = _subscription_delete_serialize(socket, subscriiption); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, NULL, - result->async); - } - - hc_result_free(result); - return ret; -} - -/*----------------------------------------------------------------------------* - * Statistics - *----------------------------------------------------------------------------*/ - -/* STATS GET */ - -static hc_result_t *_hcng_stats_get_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_stats_get] async=%s", BOOLSTR(async)); - - msg_stats_get_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STATS_GET, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_GET, - .cmd_id = COMMAND_TYPE_STATS_GET, - .size_in = sizeof(hicn_light_stats_t), - .size_out = sizeof(hicn_light_stats_t), - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.stats_get = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_stats_get_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_stats_get_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_stats_get] done or error"); - return ret; -} - -static int _hcng_stats_get(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_stats_get]"); - return _hcng_stats_get_internal(s, pdata, false); -} - -/* STATS LIST */ - -static hc_result_t *_hcng_stats_list_serialize(hc_sock_t *socket, - hc_data_t **pdata, bool async) { - hc_result_t *res = malloc(sizeof(*res)); - DEBUG("[hc_stats_list] async=%s", BOOLSTR(async)); - - msg_stats_list_t msg = {.header = { - .message_type = REQUEST_LIGHT, - .command_id = COMMAND_TYPE_STATS_LIST, - .length = 0, - .seq_num = 0, - }}; - - hc_command_params_t params = { - .cmd = ACTION_LIST, - .cmd_id = COMMAND_TYPE_STATS_LIST, - .size_in = sizeof(cmd_stats_list_item_t), - .size_out = sizeof(cmd_stats_list_item_t), - }; - - *res = (hc_result_t){ - .msg = - (hc_msg_t){ - .hdr = msg.header, - .payload.stats_list = msg.payload, - }, - .params = params, - .async = async, - .success = true, - }; - return res; -} - -static int _hcng_stats_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_result_t *result = _hcng_stats_list_serialize(socket, pdata, async); - - int ret = INPUT_ERROR; - if (result->success) { - ret = _hcng_execute_command(socket, (hc_msg_t *)&result->msg, - sizeof(result->msg), &result->params, pdata, - result->async); - } - - hc_result_free(result); - DEBUG("[_hcng_stats_list] done or error"); - return ret; -} - -static int _hcng_stats_list(hc_sock_t *s, hc_data_t **pdata) { - DEBUG("[_hcng_stats_list]"); - return _hcng_stats_list_internal(s, pdata, false); -} - -/* RESULT */ -hc_msg_t *_hcng_result_get_msg(hc_result_t *result) { return &result->msg; } -int _hcng_result_get_cmd_id(hc_result_t *result) { - return result->params.cmd_id; -} -bool _hcng_result_get_success(hc_result_t *result) { return result->success; } - -static hc_sock_t hc_sock_light_ng_interface = (hc_sock_t){ - .hc_sock_get_next_seq = _hcng_sock_light_get_next_seq, - .hc_sock_set_nonblocking = _hcng_sock_light_set_nonblocking, - .hc_sock_get_fd = _hcng_sock_light_get_fd, - .hc_sock_connect = _hcng_sock_light_connect, - .hc_sock_get_available = _hcng_sock_light_get_available, - .hc_sock_send = _hcng_sock_light_send, - .hc_sock_recv = _hcng_sock_light_recv, - .hc_sock_process = _hcng_sock_light_process, - .hc_sock_callback = _hcng_sock_light_callback, - .hc_sock_reset = _hcng_sock_light_reset, - .hc_sock_free = _hcng_sock_light_free, - .hc_sock_increment_woff = _hcng_sock_increment_woff, - .hc_sock_prepare_send = _hcng_sock_prepare_send, - .hc_sock_set_recv_timeout_ms = _hcng_sock_set_recv_timeout_ms, - .hc_listener_create = _hcng_listener_create, - .hc_listener_create_async = _hcng_listener_create_async, - .hc_listener_get = _hcng_listener_get, - .hc_listener_delete = _hcng_listener_delete, - .hc_listener_delete_async = _hcng_listener_delete_async, - .hc_listener_list = _hcng_listener_list, - .hc_listener_list_async = _hcng_listener_list_async, - .hc_connection_create = _hcng_connection_create, - .hc_connection_create_async = _hcng_connection_create_async, - .hc_connection_get = _hcng_connection_get, - .hc_connection_update_by_id = _hcng_connection_update_by_id, - .hc_connection_update = _hcng_connection_update, - .hc_connection_delete = _hcng_connection_delete, - .hc_connection_delete_async = _hcng_connection_delete_async, - .hc_connection_list = _hcng_connection_list, - .hc_connection_list_async = _hcng_connection_list_async, - .hc_connection_set_admin_state = _hcng_connection_set_admin_state, - .hc_connection_set_admin_state_async = - _hcng_connection_set_admin_state_async, - -#ifdef WITH_POLICY - .hc_connection_set_priority = _hcng_connection_set_priority, - .hc_connection_set_priority_async = _hcng_connection_set_priority_async, - .hc_connection_set_tags = _hcng_connection_set_tags, - .hc_connection_set_tags_async = _hcng_connection_set_tags_async, -#endif // WITH_POLICY - - .hc_face_create = _hcng_face_create, - .hc_face_get = _hcng_face_get, - .hc_face_delete = _hcng_face_delete, - .hc_face_list = _hcng_face_list, - .hc_face_list_async = _hcng_face_list_async, - .hc_face_set_admin_state = _hcng_face_set_admin_state, - -#ifdef WITH_POLICY - .hc_face_set_priority = _hcng_face_set_priority, - .hc_face_set_tags = _hcng_face_set_tags, -#endif // WITH_POLICY - .hc_subscription_create = _hcng_subscription_create, - .hc_subscription_delete = _hcng_subscription_delete, - - .hc_stats_get = _hcng_stats_get, - .hc_stats_list = _hcng_stats_list, - - .hc_route_create = _hcng_route_create, - .hc_route_create_async = _hcng_route_create_async, - .hc_route_delete = _hcng_route_delete, - .hc_route_delete_async = _hcng_route_delete_async, - .hc_route_list = _hcng_route_list, - .hc_route_list_async = _hcng_route_list_async, - - .hc_punting_create = _hcng_punting_create, - .hc_punting_create_async = _hcng_punting_create_async, - .hc_punting_get = _hcng_punting_get, - .hc_punting_delete = _hcng_punting_delete, - .hc_punting_list = _hcng_punting_list, - - .hc_cache_set_store = _hcng_cache_set_store, - .hc_cache_set_store_async = _hcng_cache_set_store_async, - .hc_cache_set_serve = _hcng_cache_set_serve, - .hc_cache_set_serve_async = _hcng_cache_set_serve_async, - .hc_cache_clear = _hcng_cache_clear, - .hc_cache_list = _hcng_cache_list, - - .hc_strategy_list = _hcng_strategy_list, - .hc_strategy_set = _hcng_strategy_set, - .hc_strategy_add_local_prefix = _hcng_strategy_add_local_prefix, - .hc_wldr_set = _hcng_wldr_set, - - .hc_mapme_set = _hcng_mapme_set, - .hc_mapme_set_discovery = _hcng_mapme_set_discovery, - .hc_mapme_set_timescale = _hcng_mapme_set_timescale, - .hc_mapme_set_retx = _hcng_mapme_set_retx, - .hc_mapme_send_update = _hcng_mapme_send_update, - -#ifdef WITH_POLICY - .hc_policy_create = _hcng_policy_create, - .hc_policy_create_async = _hcng_policy_create_async, - .hc_policy_delete = _hcng_policy_delete, - .hc_policy_delete_async = _hcng_policy_delete_async, - .hc_policy_list = _hcng_policy_list, - .hc_policy_list_async = _hcng_policy_list_async, -#endif // WITH_POLICY - - .hc_listener_create_conf = _hcng_listener_create_conf, - .hc_listener_list_conf = _hcng_listener_list_conf, - .hc_connection_create_conf = _hcng_connection_create_conf, - .hc_connection_delete_conf = _hcng_connection_delete_conf, - .hc_route_create_conf = _hcng_route_create_conf, - .hc_strategy_set_conf = _hcng_strategy_set_conf, - .hc_strategy_add_local_prefix_conf = _hcng_strategy_add_local_prefix_conf, - .hc_subscription_create_conf = _hcng_subscription_create_conf, - .hc_subscription_delete_conf = _hcng_subscription_delete_conf, - - .hc_result_get_msg = _hcng_result_get_msg, - .hc_result_get_cmd_id = _hcng_result_get_cmd_id, - .hc_result_get_success = _hcng_result_get_success, -}; - -// Public contructors - -hc_sock_t *_hc_sock_create_url(const char *url) { - hc_sock_light_t *s = malloc(sizeof(hc_sock_light_t)); - if (!s) goto ERR_MALLOC; - - s->vft = hc_sock_light_ng_interface; - s->url = url ? strdup(url) : NULL; - - s->fd = socket(AF_INET, SOCK_DGRAM, 0); - if (s->fd < 0) goto ERR_SOCKET; - - if (_hcng_sock_set_recv_timeout_ms((hc_sock_t *)s, - DEFAULT_SOCK_RECV_TIMEOUT_MS) < 0) - goto ERR_SOCKET; - - if (_hcng_sock_light_reset((hc_sock_t *)s) < 0) goto ERR_RESET; - - s->seq = 0; - s->cur_request = NULL; - - s->map = kh_init_sock_map(); - if (!s->map) goto ERR_MAP; - - return (hc_sock_t *)(s); - - // hc_sock_light_map_free(s->map); -ERR_MAP: -ERR_RESET: - if (s->url) free(s->url); - close(s->fd); -ERR_SOCKET: - free(s); -ERR_MALLOC: - return NULL; -} diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin.c b/ctrl/libhicnctrl/src/modules/hicn_plugin.c new file mode 100644 index 000000000..b3963b46c --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file api.c + * \brief Implementation of hICN control library API + */ + +#include <assert.h> // assert +#include <fcntl.h> // fcntl +#include <math.h> // log2 +#include <stdbool.h> +#include <stdio.h> // snprintf +#include <string.h> // memmove, strcasecmp +#include <sys/socket.h> // socket +#include <unistd.h> // close, fcntl + +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/socket.h> + +#include <vapi/vapi_safe.h> +#include <vppinfra/clib.h> +#include <vpp_plugins/hicn/error.h> + +#include "../socket_private.h" + +#include "hicn_plugin/base.h" // hc_sock_vpp_data_t +#include "hicn_plugin/listener.h" +#include "hicn_plugin/route.h" + +/****************************************************************************** + * Message helper types and aliases + ******************************************************************************/ + +#if 0 + +#define foreach_hc_command \ + _(hicn_api_node_params_set) \ + _(hicn_api_node_params_set_reply) \ + _(hicn_api_node_params_get_reply) \ + _(hicn_api_node_stats_get_reply) \ + _(hicn_api_face_get) \ + _(hicn_api_faces_details) \ + _(hicn_api_face_stats_details) \ + _(hicn_api_face_get_reply) \ + _(hicn_api_route_get) \ + _(hicn_api_route_get_reply) \ + _(hicn_api_routes_details) \ + _(hicn_api_strategies_get_reply) \ + _(hicn_api_strategy_get) \ + _(hicn_api_strategy_get_reply) + + +typedef vapi_type_msg_header2_t hc_msg_header_t; + +typedef union { +#define _(a) vapi_payload_##a a; + foreach_hc_command +#undef _ +} hc_msg_payload_t; + +typedef struct __attribute__((__packed__)) { + hc_msg_header_t hdr; + hc_msg_payload_t payload; +} hc_hicnp_t; + +typedef void (*NTOH)(void *msg); + +typedef struct __attribute__((__packed__)) { + hc_data_t *data; + uint32_t curr_msg; +} callback_ctx_t; + +typedef struct __attribute__((__packed__)) { + hc_hicnp_t *msg; + vapi_cb_t callback; + callback_ctx_t *callback_ctx; + NTOH ntoh; +} hc_msg_s; + +/****************************************************************************** + * Control socket + ******************************************************************************/ + +static void vpp_free(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + if (s->url) free(s->url); + free(s); + + vapi_disconnect_safe(); +} + +static int vpp_get_next_seq(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + return vapi_gen_req_context(s->g_vapi_ctx_instance); +} + +static int vpp_set_nonblocking(hc_sock_t *socket) { + hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); + return 0; +} + +static int vpp_callback(hc_sock_t *socket, hc_data_t **pdata) { + // NOT IMPLEMENTED + return -1; +} + +static int vpp_reset(hc_sock_t *socket) { + // NOT IMPLEMENTED + return -1; +} +#endif + +/*----------------------------------------------------------------------------* + * Listeners + *----------------------------------------------------------------------------*/ + +/****************************************************************************** + * Module functions + ******************************************************************************/ + +hc_sock_vpp_data_t *hc_sock_vpp_data_create(const char *url) { + hc_sock_vpp_data_t *s = malloc(sizeof(hc_sock_vpp_data_t)); + if (!s) goto ERR_MALLOC; + + s->roff = s->woff = 0; + s->url = url ? strdup(url) : NULL; + + return s; + +ERR_MALLOC: + return NULL; +} + +void hc_sock_vpp_data_free(hc_sock_vpp_data_t *s) { + vapi_disconnect_safe(); + + if (s->url) free(s->url); + free(s); +} + +static int vpp_connect(hc_sock_t *sock) { + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + vapi_error_e rv = + vapi_connect_safe(&s->g_vapi_ctx_instance, hc_sock_is_async(sock)); + if (rv != VAPI_OK) goto ERR_CONNECT; + + return 0; + +ERR_CONNECT: + ERROR("[hc_sock_connect] connection failed"); + return -1; +} + +static ssize_t vpp_prepare(hc_sock_t *sock, hc_request_t *request, + uint8_t **buffer) { + assert(!buffer); + + // XXX all the beginning is generic and could be shared across multiple + // modules + + /* Dispatch to subrequest if any */ + hc_request_t *current_request = hc_request_get_current(request); + + _ASSERT(!hc_request_get_data(current_request)); + + hc_action_t action = hc_request_get_action(current_request); + hc_object_type_t object_type = hc_request_get_object_type(current_request); + hc_object_t *object = hc_request_get_object(current_request); + + _ASSERT(hc_request_get_data(current_request) == NULL); + hc_data_t *data = hc_data_create(object_type); + if (!data) { + ERROR("[vpp_prepare] Could not create data storage"); + goto ERR; + } + hc_request_set_data(current_request, data); + + hc_module_object_ops_t *vft = &sock->ops.object_vft[object_type]; + if (!vft) goto ERR; + hc_execute_t execute = vft->execute[action]; + if (!execute) goto ERR; + int rc = execute(sock, object, data); + if (rc < 0) goto ERR; + + /* The result is fully contained in data */ + (void)rc; + + hc_request_set_complete(request); + return 0; + +ERR: + hc_data_set_error(data); + hc_request_set_complete(request); + return 0; +} + +static hc_sock_ops_t hc_sock_vpp = (hc_sock_ops_t){ + .create_data = (void *(*)(const char *))hc_sock_vpp_data_create, + .free_data = (void (*)(void *))hc_sock_vpp_data_free, + .get_fd = NULL, // not fd based + .get_recv_buffer = NULL, // no async support + .connect = vpp_connect, + .prepare = vpp_prepare, + .send = NULL, + .recv = NULL, + .process = NULL, +}; + +ssize_t vpp_command_serialize(hc_action_t action, hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + return hc_sock_vpp.object_vft[object_type].serialize[action](object, msg); +} + +// Public constructor + +int hc_sock_initialize_module(hc_sock_t *s) { + s->ops = hc_sock_vpp; + // XXX shall we memset the VFT ? + /* LISTENER: CREATE, GET, DELETE not implemented, LIST ok */ + s->ops.object_vft[OBJECT_TYPE_LISTENER] = vpp_listener_module_ops; + /* CONNECTION : CREATE, GET, UPDATE, DELETE, LIST, SET_* not + implemented */ + s->ops.object_vft[OBJECT_TYPE_CONNECTION] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_FACE] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_PUNTING] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_CACHE] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_MAPME] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_WLDR] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_POLICY] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_ROUTE] = vpp_route_module_ops; + s->ops.object_vft[OBJECT_TYPE_STRATEGY] = HC_MODULE_OBJECT_OPS_EMPTY; + s->ops.object_vft[OBJECT_TYPE_SUBSCRIPTION] = HC_MODULE_OBJECT_OPS_EMPTY; + return 0; +} diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h new file mode 100644 index 000000000..05565e938 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/base.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_plugin/base.h + * \brief Base structures for hICN plugin module + */ + +#include <vapi/vapi_safe.h> +#include "../../module.h" +#include "../../socket_private.h" + +typedef struct { + vapi_ctx_t g_vapi_ctx_instance; + char *url; + + size_t roff; /**< Read offset */ + size_t woff; /**< Write offset */ + u32 buffer[RECV_BUFLEN]; + /* Next sequence number to be used for requests */ + int seq; + + bool async; +} hc_sock_vpp_data_t; diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c new file mode 100644 index 000000000..f0aa4e884 --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_plugin/listener.c + * \brief Implementation of listener object VFT for hicn_plugin. + */ + +#include <hicn/util/vector.h> + +#include "base.h" +#include "listener.h" + +struct listener_data_s { + hc_listener_t listener; + hc_data_t *data; +}; + +/** + * This is a callback used to append in callback_ctx which is a hc_data_t + * designed to hold hc_listener_t, a list of listener, each corresponding to an + * IP address (v4 then v6) of the interfaces, and thus build a list of hICN + * listeners. + */ +static vapi_error_e process_ip_info(struct vapi_ctx_s *ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_address_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + if (reply && is_last) printf("COUCOU\n"); + if (is_last) return 0; + + struct listener_data_s *ld = (struct listener_data_s *)callback_ctx; + + if (reply->prefix.address.af == ADDRESS_IP4) { + memcpy(&(ld->listener.local_addr), reply->prefix.address.un.ip4, + IPV4_ADDR_LEN); + ld->listener.family = AF_INET; + } else { + memcpy(&(ld->listener.local_addr), reply->prefix.address.un.ip6, + IPV6_ADDR_LEN); + ld->listener.family = AF_INET6; + } + ld->listener.local_port = 0; + + ld->listener.id = reply->sw_if_index; + hc_data_t *data = ld->data; + hc_listener_t *listener = &ld->listener; + hc_data_push(data, listener); + + return rv; +} + +/* LISTENER LIST */ + +typedef struct { + u32 swif; + char interface_name[INTERFACE_LEN]; +} hc_vapi_interface_t; + +/* + * A pointer to hc_data_t is passed in the callback context + * Objective is to store a vector of hc_vapi_interface_t inside + */ +static vapi_error_e on_listener_list_complete_cb( + struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_sw_interface_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (is_last) return 0; + + hc_vapi_interface_t **vpp_interfaces_vec = + (hc_vapi_interface_t **)callback_ctx; + + hc_vapi_interface_t interface = {.swif = reply->sw_if_index}; + // XXX bug + memcpy(interface.interface_name, reply->interface_name, INTERFACE_LEN); + + vector_push(*vpp_interfaces_vec, interface); + + return rv; +} + +static int _vpp_listener_list(hc_sock_t *sock, hc_data_t *data) { + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + int retval = -1; // VAPI_OK; + + hc_vapi_interface_t *vpp_interfaces_vec = NULL; + vector_init(vpp_interfaces_vec, 0, 0); + + vapi_lock(); + + vapi_msg_sw_interface_dump *msg = + vapi_alloc_sw_interface_dump(s->g_vapi_ctx_instance); + if (!msg) { + retval = VAPI_ENOMEM; + goto ERR_MSG; + } + msg->payload.sw_if_index = ~0; + msg->payload.name_filter_valid = 0; + + /* Retrieve the list of interfaces in vpp_interfaces_vec */ + int ret = + vapi_sw_interface_dump(s->g_vapi_ctx_instance, msg, + on_listener_list_complete_cb, &vpp_interfaces_vec); + + if (ret != VAPI_OK) goto ERR_LIST_INTERFACES; + + /* Query the forwarder for each interface */ + // stored in data->buffer == hc_vapi_interface_t* [] + // 2 calls for each interface + // + // This function is called twice for each interface, to get resp. the v4 and + // v6 IP addresses associated to it: + // ip_address_dump(sw_if_index, is_ipv6) + // + // Function call : + // vapi_msg_XXX *msg = vapi_alloc_XXX(s->g_vapi_ctx_instance); + // msg->payload.ATTR = VALUE; + // [...] + // int ret = vapi_XXX((s->g_vapi_ctx_instance, msg, CALLBACK, USER_DATA); + // + // CALLBACK = process_ip_info + // USER_DATA = data2 + // + // We can assume the callbacks are executed before the function returns, and + // that there is no async code. + // + int rc; + + hc_vapi_interface_t *interface; + vapi_msg_ip_address_dump *msg2; + + struct listener_data_s ld; + vector_foreach(vpp_interfaces_vec, interface, { + memset(&ld, 0, sizeof(struct listener_data_s)); + ld.listener.type = FACE_TYPE_HICN; + rc = snprintf(ld.listener.interface_name, INTERFACE_LEN, "%s", + interface->interface_name); + if (rc < 0 || rc >= INTERFACE_LEN) goto ERR_FOREACH; + + ld.data = data; + + for (unsigned i = 0; i < 2; i++) { + msg2 = vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); + msg2->payload.sw_if_index = interface->swif; + msg2->payload.is_ipv6 = i; + retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg2, + process_ip_info, &ld); + if (ret != VAPI_OK) goto ERR_GET_IP; + } + }); + retval = 0; +ERR_GET_IP: +ERR_FOREACH: + vector_free(vpp_interfaces_vec); +ERR_LIST_INTERFACES: +ERR_MSG: + vapi_unlock(); + return retval; +} + +#define vpp_listener_create NULL +#define vpp_listener_delete NULL + +static int vpp_listener_list(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + assert(!object || hc_object_is_empty(object)); + return _vpp_listener_list(sock, data); +} + +DECLARE_VPP_MODULE_OBJECT_OPS(vpp, listener); diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_common.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h index d1fb33993..f75c58db6 100644 --- a/ctrl/libhicnctrl/src/modules/hicn_light_common.c +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/listener.h @@ -13,18 +13,16 @@ * limitations under the License. */ -#include "hicn_light_common.h" +/** + * \file modules/hicn_plugin/listener.h + * \brief listener object VFT for hicn_plugin. + */ + +#ifndef HICNCTRL_MODULE_VPP_LISTENER_H +#define HICNCTRL_MODULE_VPP_LISTENER_H -hc_sock_request_t *hc_sock_request_create(int seq, hc_data_t *data, - HC_PARSE parse) { - assert(data); +#include "../../module.h" - hc_sock_request_t *request = malloc(sizeof(hc_sock_request_t)); - if (!request) return NULL; - request->seq = seq; - request->data = data; - request->parse = parse; - return request; -} +DECLARE_MODULE_OBJECT_OPS_H(vpp, listener); -void hc_sock_light_request_free(hc_sock_request_t *request) { free(request); } +#endif /* HICNCTRL_MODULE_VPP_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c new file mode 100644 index 000000000..45aced9cb --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.c @@ -0,0 +1,541 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_plugin/route.c + * \brief Implementation of route object VFT for hicn_plugin. + */ + +#include "base.h" +#include "route.h" + +static vapi_error_e create_udp_tunnel_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_udp_tunnel_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply->retval != VAPI_OK) return reply->retval; + + u32 *uei = (u32 *)callback_ctx; + *uei = reply->uei; + + return reply->retval; +} + +static vapi_error_e parse_route_create( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_ip_route_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply->retval != VAPI_OK) return reply->retval; + + return reply->retval; +} + +static vapi_error_e hicn_enable_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_enable_disable_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + face_id_t *faceid = (face_id_t *)callback_ctx; + + if (reply->nfaces) { + *faceid = reply->faceids[0]; + } + + return reply->retval; +} + +static int _vpp_route_create(hc_sock_t *sock, hc_route_t *route) { + if (!IS_VALID_FAMILY(route->family)) return -1; + + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + int ret = -1; + vapi_lock(); + + vapi_msg_ip_route_add_del *msg = + vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); + + msg->payload.is_add = 1; + if (route->family == AF_INET) { + memcpy(&msg->payload.route.prefix.address.un.ip4[0], &route->remote_addr.v4, + 4); + msg->payload.route.prefix.address.af = ADDRESS_IP4; + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&msg->payload.route.prefix.address.un.ip6[0], &route->remote_addr.v6, + 16); + msg->payload.route.prefix.address.af = ADDRESS_IP6; + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg->payload.route.prefix.len = route->len; + + msg->payload.route.paths[0].sw_if_index = ~0; + msg->payload.route.paths[0].table_id = 0; + + hc_face_t *face = &(route->face); + + face->netdevice.index = ~0; + face->id = INVALID_FACE_ID; + + switch (face->type) { + case FACE_TYPE_HICN: { + if (hicn_ip_address_is_v4(&(face->remote_addr))) { + memcpy(&(msg->payload.route.paths[0].nh.address.ip4), + &face->remote_addr.v4, sizeof(ip4_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&(msg->payload.route.paths[0].nh.address.ip6), + &face->remote_addr.v6, sizeof(ip6_address_t)); + msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; + msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + + break; + } + case FACE_TYPE_UDP: { + vapi_msg_hicn_api_udp_tunnel_add_del *msg2 = NULL; + u32 uei = ~0; + + if (hicn_ip_address_is_v4(&(face->remote_addr)) && + hicn_ip_address_is_v4(&(face->local_addr))) { + msg2 = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); + memcpy(msg2->payload.src_addr.un.ip4, &face->local_addr.v4, + sizeof(ip4_address_t)); + msg2->payload.src_addr.af = ADDRESS_IP4; + + memcpy(msg2->payload.dst_addr.un.ip4, &face->remote_addr.v4, + sizeof(ip4_address_t)); + msg2->payload.dst_addr.af = ADDRESS_IP4; + + } else if (!hicn_ip_address_is_v4(&(route->face.remote_addr)) && + !hicn_ip_address_is_v4(&(route->face.local_addr))) { + msg2 = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); + memcpy(msg2->payload.src_addr.un.ip6, &face->local_addr.v6, + sizeof(ip6_address_t)); + msg2->payload.src_addr.af = ADDRESS_IP6; + + memcpy(msg2->payload.dst_addr.un.ip6, &face->remote_addr.v6, + sizeof(ip6_address_t)); + msg2->payload.dst_addr.af = ADDRESS_IP6; + } else { + // NOT IMPLEMENTED + ret = -1; + goto done; + } + + msg2->payload.src_port = face->local_port; + msg2->payload.dst_port = face->remote_port; + msg2->payload.is_add = 1; + + int ret = vapi_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance, msg2, + create_udp_tunnel_cb, &uei); + + if (ret) { + ERROR("Error in vapi_hicn_api_udp_tunnel_add_del"); + vapi_msg_free(s->g_vapi_ctx_instance, msg); + goto done; + } + + msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; + msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + msg->payload.route.paths[0].nh.obj_id = uei; + + face->netdevice.index = uei; + + break; + } + default: + ret = -1; + goto done; + } + + ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, msg, parse_route_create, + NULL); + + if (ret) { + ERROR("Error in vapi_ip_route_add_del"); + goto done; + } + + vapi_msg_hicn_api_enable_disable *msg3 = + vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); + + if (route->family == AF_INET) { + memcpy(&msg3->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); + msg3->payload.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg3->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); + msg3->payload.prefix.address.af = ADDRESS_IP6; + } + + msg3->payload.prefix.len = route->len; + msg3->payload.enable_disable = 1; + + ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg3, + hicn_enable_cb, &face->id); + + if (ret) { + ERROR("Error in vapi_hicn_api_enable_disable"); + } + +done: + vapi_unlock(); + return ret; +} + +static vapi_error_e hicn_disable_cb( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_enable_disable_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + return reply->retval; +} + +static vapi_error_e parse_route_delete( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_ip_route_add_del_reply *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + return reply->retval; +} + +static int _vpp_route_delete(hc_sock_t *sock, hc_route_t *route) { + if (!IS_VALID_FAMILY(route->family)) return -1; + + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + vapi_lock(); + + vapi_msg_hicn_api_enable_disable *msg = + vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); + + if (route->family == AF_INET) { + memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); + msg->payload.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); + msg->payload.prefix.address.af = ADDRESS_IP6; + } + + msg->payload.prefix.len = route->len; + msg->payload.enable_disable = 0; + + vapi_error_e ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, + hicn_disable_cb, NULL); + + if (ret) { + ERROR("Error in vapi_hicn_api_enable_disable in route delete"); + goto done; + } + + vapi_msg_ip_route_add_del *msg2 = + vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); + + msg2->payload.is_add = 0; + if (route->family == AF_INET) { + memcpy(&msg2->payload.route.prefix.address.un.ip4[0], + &route->remote_addr.v4, 4); + msg2->payload.route.prefix.address.af = ADDRESS_IP4; + } else { + memcpy(&msg2->payload.route.prefix.address.un.ip6[0], + &route->remote_addr.v6, 16); + msg2->payload.route.prefix.address.af = ADDRESS_IP6; + } + + msg2->payload.route.prefix.len = route->len; + + msg2->payload.route.paths[0].sw_if_index = ~0; + msg2->payload.route.paths[0].table_id = 0; + + hc_face_t *face = &(route->face); + switch (face->type) { + case FACE_TYPE_HICN: { + if (hicn_ip_address_is_v4(&(face->remote_addr))) { + memcpy(&(msg2->payload.route.paths[0].nh.address.ip4), + &face->remote_addr.v4, sizeof(ip4_address_t)); + msg2->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; + } else { + memcpy(&(msg2->payload.route.paths[0].nh.address.ip6), + &face->remote_addr.v6, sizeof(ip6_address_t)); + msg2->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; + } + + msg2->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; + msg2->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + + break; + } + case FACE_TYPE_UDP: { + msg2->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; + msg2->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; + msg2->payload.route.paths[0].nh.obj_id = face->netdevice.index; + break; + } + default: + return -1; + } + + ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, msg2, parse_route_delete, + NULL); + + if (ret) { + ERROR("Error in vapi_ip_route_add_del in route delete"); + goto done; + } + +done: + + vapi_unlock(); + return ret; +} + +/* ROUTE LIST */ + +static vapi_error_e parse_udp_encap_list( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_udp_encap_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + hc_face_t *face = (hc_face_t *)callback_ctx; + + if (face->netdevice.index == reply->udp_encap.id) { + switch (reply->udp_encap.src_ip.af) { + case ADDRESS_IP4: { + memcpy(&face->local_addr.v4, &(reply->udp_encap.src_ip.un.ip4), + sizeof(ip4_address_t)); + memcpy(&face->remote_addr.v4, &(reply->udp_encap.dst_ip.un.ip4), + sizeof(ip4_address_t)); + break; + } + case ADDRESS_IP6: { + memcpy(&face->local_addr.v6, &(reply->udp_encap.src_ip.un.ip6), + sizeof(ip6_address_t)); + memcpy(&face->remote_addr.v6, &(reply->udp_encap.dst_ip.un.ip6), + sizeof(ip6_address_t)); + break; + } + default: + break; + } + + face->local_port = reply->udp_encap.src_port; + face->remote_port = reply->udp_encap.dst_port; + } + return rv; +} + +static int _fill_face_with_info(hc_face_t *face, vapi_type_fib_path *path) { + switch (path->type) { + case FIB_API_PATH_FLAG_NONE: { + face->type = FACE_TYPE_HICN; + switch (path->proto) { + case FIB_API_PATH_NH_PROTO_IP4: + memcpy(&face->remote_addr.v4, &(path->nh.address.ip4), + sizeof(ipv4_address_t)); + break; + case FIB_API_PATH_NH_PROTO_IP6: + memcpy(&face->remote_addr.v6, &(path->nh.address.ip6), + sizeof(ipv6_address_t)); + break; + default: + break; + } + face->netdevice.index = path->sw_if_index; + } break; + case FIB_API_PATH_TYPE_UDP_ENCAP: { + face->type = FACE_TYPE_UDP; + face->netdevice.index = clib_net_to_host_u32(path->nh.obj_id); + // Let's make the compiler happy + (void)parse_udp_encap_list; + // vapi_msg_udp_encap_dump *msg; + // msg = vapi_alloc_udp_encap_dump(s->g_vapi_ctx_instance); + // vapi_udp_encap_dump(s->g_vapi_ctx_instance, msg, parse_udp_encap_list, + // face); + } break; + default: + return -1; + } + return 0; +} + +static vapi_error_e parse_route_list(vapi_ctx_t ctx, void *callback_ctx, + vapi_error_e rv, bool is_last, + vapi_payload_ip_route_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + // XXX DEBUG XXX + if (reply && is_last) printf("COUCOU\n"); + + if (is_last) return 0; + + hc_data_t *data = (hc_data_t *)callback_ctx; + + /* + * Implementation: + * A route has n paths... we iterate for each path and search for a + * corresponding face in the hc_data_t result struct... and we fill the face + * info with the route path. + * + * TODO + * - comment on paths + * - explain the jump to END, this was previously implemented with a + * boolean flags skipping all remaining tests in the function... + */ + for (int j = 0; j < reply->route.n_paths; j++) { + hc_data_foreach(data, obj, { + hc_route_t *route = &obj->route; + + if (hicn_ip_address_is_v4(&(route->remote_addr)) && + memcmp(route->remote_addr.v4.as_u8, + reply->route.prefix.address.un.ip4, + sizeof(ipv4_address_t)) == 0 && + route->len == reply->route.prefix.len && route->face_id == ~0) { + _fill_face_with_info(&(route->face), &reply->route.paths[j]); + goto END; + + } else if (memcmp(route->remote_addr.v6.as_u8, + reply->route.prefix.address.un.ip6, + sizeof(ipv6_address_t)) == 0 && + route->len == reply->route.prefix.len && + route->face_id == ~0) { + _fill_face_with_info(&(route->face), &reply->route.paths[j]); + goto END; + } + }); + } + +END: + return rv; +} + +/** + * Populates the hc_data_t structure passed as the context with... + */ +static vapi_error_e parse_hicn_route_list( + vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, + vapi_payload_hicn_api_routes_details *reply) { + if (reply == NULL || rv != VAPI_OK) return rv; + + if (reply && is_last) printf("COUCOU\n"); + if (is_last) return 0; + + hc_data_t *data = (hc_data_t *)callback_ctx; + + for (int i = 0; i < reply->nfaces; i++) { + hc_route_t route; + memset(&route, 0, sizeof(hc_route_t)); + + /* + * We set the face_id to ~0 to act as a marker in parse_route_list that + * the route is missing face information. + */ + route.face_id = ~0; + route.cost = 1; + route.len = reply->prefix.len; + if (reply->prefix.address.af == ADDRESS_IP6) { + memcpy(route.remote_addr.v6.as_u8, reply->prefix.address.un.ip6, 16); + route.family = AF_INET6; + } else { + memcpy(route.remote_addr.v4.as_u8, reply->prefix.address.un.ip4, 4); + route.family = AF_INET; + } + + hc_data_push(data, &route); + } + + return rv; +} + +/* + * hicn_api_routes_dump + * ip_route_dump + * + * @returns hc_data_t<hc_route_t> + */ +static int _vpp_route_list(hc_sock_t *sock, hc_data_t *data) { + int ret; + hc_sock_vpp_data_t *s = (hc_sock_vpp_data_t *)sock->data; + + vapi_lock(); + + /* Start by retrieving hicn routes (we have no face information at this + * stage)... */ + vapi_msg_hicn_api_routes_dump *msg; + msg = vapi_alloc_hicn_api_routes_dump(s->g_vapi_ctx_instance); + if (!msg) goto ERR_MSG; + + ret = vapi_hicn_api_routes_dump(s->g_vapi_ctx_instance, msg, + parse_hicn_route_list, data); + if (ret != VAPI_OK) goto ERR_API; + + /* + * ... an complement them using IP (v4 and v6 routes). Similar routes will + * be aggregated, based on IP prefix, in parse_*_route_list. + */ + vapi_msg_ip_route_dump *msg2; + for (unsigned i = 0; i < 2; i++) { + msg2 = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); + if (!msg2) goto ERR_MSG; + + msg2->payload.table.table_id = 0; + msg2->payload.table.is_ip6 = i; + + ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, msg2, parse_route_list, + data); + if (ret != VAPI_OK) goto ERR_API; + } + + goto END; + +ERR_MSG: + ret = VAPI_ENOMEM; + goto END; + +ERR_API: +END: + vapi_unlock(); + return ret; +} + +static int vpp_route_create(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + int rc = _vpp_route_create(sock, &object->route); + if (rc < 0) + hc_data_set_complete(data); + else + hc_data_set_error(data); + return rc; +} + +static int vpp_route_delete(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + int rc = _vpp_route_delete(sock, &object->route); + if (rc < 0) + hc_data_set_complete(data); + else + hc_data_set_error(data); + return rc; +} + +static int vpp_route_list(hc_sock_t *sock, hc_object_t *object, + hc_data_t *data) { + assert(!object || hc_object_is_empty(object)); + return _vpp_route_list(sock, data); +} + +DECLARE_VPP_MODULE_OBJECT_OPS(vpp, route); diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h new file mode 100644 index 000000000..652d3e89a --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_plugin/route.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file modules/hicn_plugin/route.h + * \brief route object VFT for hicn_plugin. + */ + +#ifndef HICNCTRL_MODULE_VPP_ROUTE_H +#define HICNCTRL_MODULE_VPP_ROUTE_H + +#include "../../module.h" + +DECLARE_MODULE_OBJECT_OPS_H(vpp, route); + +#endif /* HICNCTRL_MODULE_VPP_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c b/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c deleted file mode 100644 index 6d1baa786..000000000 --- a/ctrl/libhicnctrl/src/modules/hicn_plugin_api.c +++ /dev/null @@ -1,1402 +0,0 @@ -/* - * Copyright (c) 2021 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * \file api.c - * \brief Implementation of hICN control library API - */ - -#include <assert.h> // assert -#include <fcntl.h> // fcntl -#include <math.h> // log2 -#include <stdbool.h> -#include <stdio.h> // snprintf -#include <string.h> // memmove, strcasecmp -#include <sys/socket.h> // socket -#include <unistd.h> // close, fcntl -#include <vapi/vapi_safe.h> -#include <vppinfra/clib.h> -#include <vpp_plugins/hicn/error.h> - -#include "api_private.h" - -/** - * Messages to the forwarder might be multiplexed thanks to the seqNum fields in - * the header_control_message structure. The forwarder simply answers back the - * original sequence number. We maintain a map of such sequence number to - * outgoing queries so that replied can be demultiplexed and treated - * appropriately. - */ -/* TYPEDEF_MAP_H(hc_sock_map, int, hc_sock_request_t *); */ -/* TYPEDEF_MAP(hc_sock_map, int, hc_sock_request_t *, int_cmp, int_snprintf, */ -/* generic_snprintf); */ - -struct hc_sock_vpp_s { - /* This must be the first element of the struct */ - hc_sock_t vft; - - vapi_ctx_t g_vapi_ctx_instance; - char *url; - - size_t roff; /**< Read offset */ - size_t woff; /**< Write offset */ - u32 buffer[RECV_BUFLEN]; - /* Next sequence number to be used for requests */ - int seq; - - bool async; -}; - -typedef struct hc_sock_vpp_s hc_sock_vpp_t; - -#define TO_HC_SOCK_VPP(s) (hc_sock_vpp_t *)(s) - -/****************************************************************************** - * Message helper types and aliases - ******************************************************************************/ - -#define foreach_hc_command \ - _(hicn_api_node_params_set) \ - _(hicn_api_node_params_set_reply) \ - _(hicn_api_node_params_get_reply) \ - _(hicn_api_node_stats_get_reply) \ - _(hicn_api_face_get) \ - _(hicn_api_faces_details) \ - _(hicn_api_face_stats_details) \ - _(hicn_api_face_get_reply) \ - _(hicn_api_route_get) \ - _(hicn_api_route_get_reply) \ - _(hicn_api_routes_details) \ - _(hicn_api_strategies_get_reply) \ - _(hicn_api_strategy_get) \ - _(hicn_api_strategy_get_reply) - -typedef vapi_type_msg_header2_t hc_msg_header_t; - -typedef union { -#define _(a) vapi_payload_##a a; - foreach_hc_command -#undef _ -} hc_msg_payload_t; - -typedef struct __attribute__((__packed__)) { - hc_msg_header_t hdr; - hc_msg_payload_t payload; -} hc_hicnp_t; - -typedef void (*NTOH)(void *msg); - -typedef struct __attribute__((__packed__)) { - hc_data_t *data; - uint32_t curr_msg; -} callback_ctx_t; - -typedef struct __attribute__((__packed__)) { - hc_hicnp_t *hicnp_msg; - vapi_cb_t callback; - callback_ctx_t *callback_ctx; - NTOH ntoh; -} hc_msg_s; - -/****************************************************************************** - * Control socket - ******************************************************************************/ - -static void _hc_sock_vpp_free(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - if (s->url) free(s->url); - free(s); - - vapi_disconnect_safe(); -} - -static int _hc_sock_vpp_get_next_seq(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - return vapi_gen_req_context(s->g_vapi_ctx_instance); -} - -static int _hc_sock_vpp_set_nonblocking(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - s->async = 1; - return 0; -} - -static int _hc_sock_vpp_get_fd(hc_sock_t *s) { return 1; } - -static int _hc_sock_vpp_connect(hc_sock_t *socket) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - vapi_error_e rv = vapi_connect_safe(&s->g_vapi_ctx_instance, s->async); - if (rv != VAPI_OK) goto ERR_CONNECT; - - return 0; - -ERR_CONNECT: - ERROR("[hc_sock_connect] connection failed"); - return -1; -} - -static int _hc_sock_vpp_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, - uint32_t seq) { - return -1; -} - -static int _hc_sock_vpp_get_available(hc_sock_t *s, u8 **buffer, size_t *size) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_recv(hc_sock_t *s) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_process(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_callback(hc_sock_t *socket, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_sock_vpp_reset(hc_sock_t *socket) { - // NOT IMPLEMENTED - return -1; -} - -/****************************************************************************** - * Command-specific structures and functions - ******************************************************************************/ - -/*----------------------------------------------------------------------------* - * Listeners - *----------------------------------------------------------------------------*/ - -/* LISTENER CREATE */ - -static int _hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_listener_create_async(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -/* LISTENER GET */ -static int _hc_listener_get(hc_sock_t *s, hc_listener_t *listener, - hc_listener_t **listener_found) { - // NOT IMPLEMENTED - return -1; -} - -/* LISTENER DELETE */ - -static int _hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_listener_delete_async(hc_sock_t *s, hc_listener_t *listener) { - // NOT IMPLEMENTED - return -1; -} - -static vapi_error_e process_ip_info(struct vapi_ctx_s *ctx, void *callback_ctx, - vapi_error_e rv, bool is_last, - vapi_payload_ip_address_details *reply) { - if (is_last) return 0; - hc_data_t *data = (hc_data_t *)callback_ctx; - - if (data->size == data->current) { - data->buffer = - realloc(data->buffer, sizeof(hc_listener_t) * data->size * 2); - - if (!data->buffer) return VAPI_ENOMEM; - - data->size *= 2; - } - - hc_listener_t *listener = - (hc_listener_t *)(data->buffer + data->current * sizeof(hc_listener_t)); - memset(listener, 0, sizeof(hc_listener_t)); - - if (reply->prefix.address.af == ADDRESS_IP4) { - memcpy(listener->local_addr.v4.as_u8, reply->prefix.address.un.ip4, - IPV4_ADDR_LEN); - listener->family = AF_INET; - } else { - memcpy(listener->local_addr.v6.as_u8, reply->prefix.address.un.ip6, - IPV6_ADDR_LEN); - listener->family = AF_INET6; - } - - listener->id = reply->sw_if_index; - data->current++; - return rv; -} - -typedef struct { - u32 swif; - char interface_name[INTERFACE_LEN]; -} hc_vapi_interface_t; - -static vapi_error_e listener_list_complete_cb( - struct vapi_ctx_s *ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_sw_interface_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (is_last) return 0; - - hc_data_t *data = (hc_data_t *)callback_ctx; - - if (data->size == data->current) { - data->buffer = - realloc(data->buffer, sizeof(hc_vapi_interface_t) * data->size * 2); - - if (!data->buffer) return VAPI_ENOMEM; - - data->size *= 2; - } - - hc_vapi_interface_t *swif = - &((hc_vapi_interface_t *)data->buffer)[data->current]; - - swif[0].swif = reply->sw_if_index; - memcpy(swif[0].interface_name, reply->interface_name, INTERFACE_LEN); - - data->current++; - - return rv; -} - -/* LISTENER LIST */ -static int _hc_listener_list(hc_sock_t *socket, hc_data_t **pdata) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - int retval = VAPI_OK; - vapi_lock(); - vapi_msg_sw_interface_dump *hicnp_msg; - hicnp_msg = vapi_alloc_sw_interface_dump(s->g_vapi_ctx_instance); - - if (!hicnp_msg) { - retval = VAPI_ENOMEM; - goto END; - } - - hicnp_msg->payload.sw_if_index = ~0; - hicnp_msg->payload.name_filter_valid = 0; - - hc_data_t *data = hc_data_create(0, sizeof(hc_vapi_interface_t), NULL); - - if (!data) { - retval = -1; - goto END; - } - - hc_data_t *data2 = hc_data_create(0, 1, NULL); - - if (!data2) { - retval = -1; - goto END; - } - - data->buffer = malloc(sizeof(hc_vapi_interface_t)); - data->size = 1; - - if (!data->buffer) { - free(data); - retval = -1; - goto FREE_DATA; - } - - int ret = vapi_sw_interface_dump(s->g_vapi_ctx_instance, hicnp_msg, - listener_list_complete_cb, data); - - if (ret != VAPI_OK) { - free(data->buffer); - free(data); - retval = -1; - goto FREE_DATA_BUFFER; - } - - data2->buffer = malloc(sizeof(hc_listener_t)); - data2->size = 1; - data2->out_element_size = 1; - - if (!data2->buffer) { - free(data2->buffer); - retval = -1; - goto CLEAN; - } - - /* Query the forwarder for each interface */ - for (int i = 0; i < data->current; i++) { - int index = data2->current; - vapi_msg_ip_address_dump *msg = - vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); - msg->payload.sw_if_index = ((hc_vapi_interface_t *)data->buffer)[i].swif; - msg->payload.is_ipv6 = 0; - retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg, process_ip_info, - data2); - vapi_msg_ip_address_dump *msg2 = - vapi_alloc_ip_address_dump(s->g_vapi_ctx_instance); - - if (retval) goto CLEAN; - - msg2->payload.sw_if_index = ((hc_vapi_interface_t *)data->buffer)[i].swif; - msg2->payload.is_ipv6 = 1; - retval = vapi_ip_address_dump(s->g_vapi_ctx_instance, msg2, process_ip_info, - data2); - for (size_t j = index; j < data2->current; j++) { - memcpy(((hc_listener_t *)(data2->buffer))[j].interface_name, - ((hc_vapi_interface_t *)(data->buffer))[i].interface_name, - INTERFACE_LEN); - ((hc_listener_t *)(data2->buffer))[j].type = FACE_TYPE_HICN; - } - - if (retval) goto CLEAN; - } - -CLEAN: -FREE_DATA_BUFFER: - free(data->buffer); -FREE_DATA: - free(data); - - data2->size = data2->current; - data2->out_element_size = sizeof(hc_listener_t); - *pdata = data2; -END: - vapi_unlock(); - return retval; -} - -static int _hc_listener_list_async(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -/*----------------------------------------------------------------------------* - * CONNECTION - *----------------------------------------------------------------------------*/ - -/* CONNECTION CREATE */ - -static int _hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_create_async(hc_sock_t *s, - hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION GET */ - -static int _hc_connection_get(hc_sock_t *s, hc_connection_t *connection, - hc_connection_t **connection_found) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_update_by_id(hc_sock_t *s, int hc_connection_id, - hc_connection_t *connection) { - // Not implemented - return -1; -} - -static int _hc_connection_update(hc_sock_t *s, - hc_connection_t *connection_current, - hc_connection_t *connection_updated) { - // Not implemented - return -1; -} - -/* CONNECTION DELETE */ - -static int _hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_delete_async(hc_sock_t *s, - hc_connection_t *connection) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION LIST */ - -static int _hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_list_async(hc_sock_t *s, hc_data_t **pdata) { - // NOT IMPLEMENTED - return -1; -} - -/* CONNECTION SET ADMIN STATE */ - -static int _hc_connection_set_admin_state(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_admin_state_async(hc_sock_t *s, - const char *conn_id_or_name, - face_state_t state) { - // NOT IMPLEMENTED - return -1; -} - -#ifdef WITH_POLICY - -static int _hc_connection_set_priority(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_priority_async(hc_sock_t *s, - const char *conn_id_or_name, - uint32_t priority) { - // NOT IMPLEMENTED - return -1; -} - -#endif // WITH_POLICY - -static int _hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - // NOT IMPLEMENTED - return -1; -} - -static int _hc_connection_set_tags_async(hc_sock_t *s, - const char *conn_id_or_name, - policy_tags_t tags) { - // NOT IMPLEMENTED - return -1; -} - -/*----------------------------------------------------------------------------* - * Routes - *----------------------------------------------------------------------------*/ - -static vapi_error_e create_udp_tunnel_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_udp_tunnel_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (reply->retval != VAPI_OK) return reply->retval; - - u32 *uei = (u32 *)callback_ctx; - *uei = reply->uei; - - return reply->retval; -} - -/* ROUTE CREATE */ -static vapi_error_e parse_route_create( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_ip_route_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - if (reply->retval != VAPI_OK) return reply->retval; - - return reply->retval; -} - -static vapi_error_e hicn_enable_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_enable_disable_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - face_id_t *faceid = (face_id_t *)callback_ctx; - - if (reply->nfaces) { - *faceid = reply->faceids[0]; - } - - return reply->retval; -} - -static int _hc_route_create_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - if (!IS_VALID_FAMILY(route->family)) return -1; - - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - int ret = -1; - vapi_lock(); - - vapi_msg_ip_route_add_del *hicnp_msg = - vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); - - hicnp_msg->payload.is_add = 1; - if (route->family == AF_INET) { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip4[0], - &route->remote_addr.v4, 4); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP4; - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip6[0], - &route->remote_addr.v6, 16); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP6; - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.prefix.len = route->len; - - hicnp_msg->payload.route.paths[0].sw_if_index = ~0; - hicnp_msg->payload.route.paths[0].table_id = 0; - - hc_face_t *face = &(route->face); - - face->face.netdevice.index = ~0; - face->id = INVALID_FACE_ID; - - switch (face->face.type) { - case FACE_TYPE_HICN: { - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr)))) { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip4), - &face->face.remote_addr.v4, sizeof(ip4_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip6), - &face->face.remote_addr.v6, sizeof(ip6_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - - break; - } - case FACE_TYPE_UDP: { - vapi_msg_hicn_api_udp_tunnel_add_del *msg = NULL; - u32 uei = ~0; - - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr))) && - ip_address_is_v4((ip_address_t *)(&(face->face.local_addr)))) { - msg = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); - memcpy(msg->payload.src_addr.un.ip4, &face->face.local_addr.v4, - sizeof(ip4_address_t)); - msg->payload.src_addr.af = ADDRESS_IP4; - - memcpy(msg->payload.dst_addr.un.ip4, &face->face.remote_addr.v4, - sizeof(ip4_address_t)); - msg->payload.dst_addr.af = ADDRESS_IP4; - - } else if (!ip_address_is_v4( - (ip_address_t *)(&(route->face.face.remote_addr))) && - !ip_address_is_v4( - (ip_address_t *)(&(route->face.face.local_addr)))) { - msg = vapi_alloc_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance); - memcpy(msg->payload.src_addr.un.ip6, &face->face.local_addr.v6, - sizeof(ip6_address_t)); - msg->payload.src_addr.af = ADDRESS_IP6; - - memcpy(msg->payload.dst_addr.un.ip6, &face->face.remote_addr.v6, - sizeof(ip6_address_t)); - msg->payload.dst_addr.af = ADDRESS_IP6; - } else { - // NOT IMPLEMENTED - ret = -1; - goto done; - } - - msg->payload.src_port = face->face.local_port; - msg->payload.dst_port = face->face.remote_port; - msg->payload.is_add = 1; - - int ret = vapi_hicn_api_udp_tunnel_add_del(s->g_vapi_ctx_instance, msg, - create_udp_tunnel_cb, &uei); - - if (ret) { - ERROR("Error in vapi_hicn_api_udp_tunnel_add_del"); - vapi_msg_free(s->g_vapi_ctx_instance, hicnp_msg); - goto done; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - hicnp_msg->payload.route.paths[0].nh.obj_id = uei; - - face->face.netdevice.index = uei; - - break; - } - default: - ret = -1; - goto done; - } - - ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, hicnp_msg, - parse_route_create, NULL); - - if (ret) { - ERROR("Error in vapi_ip_route_add_del"); - goto done; - } - - vapi_msg_hicn_api_enable_disable *msg = - vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); - - if (route->family == AF_INET) { - memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); - msg->payload.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); - msg->payload.prefix.address.af = ADDRESS_IP6; - } - - msg->payload.prefix.len = route->len; - msg->payload.enable_disable = 1; - - ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, - hicn_enable_cb, &face->id); - - if (ret) { - ERROR("Error in vapi_hicn_api_enable_disable"); - } - -done: - vapi_unlock(); - return ret; -} - -static int _hc_route_create(hc_sock_t *s, hc_route_t *route) { - return _hc_route_create_internal(s, route, false); -} - -static int _hc_route_create_async(hc_sock_t *s, hc_route_t *route) { - return _hc_route_create_internal(s, route, true); -} - -/* ROUTE DELETE */ -static vapi_error_e parse_route_delete( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_ip_route_add_del_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - return reply->retval; -} - -static vapi_error_e hicn_disable_cb( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_enable_disable_reply *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - return reply->retval; -} - -static int _hc_route_delete_internal(hc_sock_t *socket, hc_route_t *route, - bool async) { - if (!IS_VALID_FAMILY(route->family)) return -1; - - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - - vapi_lock(); - - vapi_msg_hicn_api_enable_disable *msg = - vapi_alloc_hicn_api_enable_disable(s->g_vapi_ctx_instance); - - if (route->family == AF_INET) { - memcpy(&msg->payload.prefix.address.un.ip4[0], &route->remote_addr.v4, 4); - msg->payload.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&msg->payload.prefix.address.un.ip6[0], &route->remote_addr.v6, 16); - msg->payload.prefix.address.af = ADDRESS_IP6; - } - - msg->payload.prefix.len = route->len; - msg->payload.enable_disable = 0; - - vapi_error_e ret = vapi_hicn_api_enable_disable(s->g_vapi_ctx_instance, msg, - hicn_disable_cb, NULL); - - if (ret) { - ERROR("Error in vapi_hicn_api_enable_disable in route delete"); - goto done; - } - - vapi_msg_ip_route_add_del *hicnp_msg = - vapi_alloc_ip_route_add_del(s->g_vapi_ctx_instance, 1); - - hicnp_msg->payload.is_add = 0; - if (route->family == AF_INET) { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip4[0], - &route->remote_addr.v4, 4); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP4; - } else { - memcpy(&hicnp_msg->payload.route.prefix.address.un.ip6[0], - &route->remote_addr.v6, 16); - hicnp_msg->payload.route.prefix.address.af = ADDRESS_IP6; - } - - hicnp_msg->payload.route.prefix.len = route->len; - - hicnp_msg->payload.route.paths[0].sw_if_index = ~0; - hicnp_msg->payload.route.paths[0].table_id = 0; - - hc_face_t *face = &(route->face); - switch (face->face.type) { - case FACE_TYPE_HICN: { - if (ip_address_is_v4((ip_address_t *)(&(face->face.remote_addr)))) { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip4), - &face->face.remote_addr.v4, sizeof(ip4_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP4; - } else { - memcpy(&(hicnp_msg->payload.route.paths[0].nh.address.ip6), - &face->face.remote_addr.v6, sizeof(ip6_address_t)); - hicnp_msg->payload.route.paths[0].proto = FIB_API_PATH_NH_PROTO_IP6; - } - - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_NORMAL; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - - break; - } - case FACE_TYPE_UDP: { - hicnp_msg->payload.route.paths[0].type = FIB_API_PATH_TYPE_UDP_ENCAP; - hicnp_msg->payload.route.paths[0].flags = FIB_API_PATH_FLAG_NONE; - hicnp_msg->payload.route.paths[0].nh.obj_id = face->face.netdevice.index; - break; - } - default: - return -1; - } - - ret = vapi_ip_route_add_del(s->g_vapi_ctx_instance, hicnp_msg, - parse_route_delete, NULL); - - if (ret) { - ERROR("Error in vapi_ip_route_add_del in route delete"); - goto done; - } - -done: - - vapi_unlock(); - return ret; -} - -static int _hc_route_delete(hc_sock_t *s, hc_route_t *route) { - return _hc_route_delete_internal(s, route, false); -} - -static int _hc_route_delete_async(hc_sock_t *s, hc_route_t *route) { - return _hc_route_delete_internal(s, route, true); -} - -static vapi_error_e parse_udp_encap_list( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_udp_encap_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hc_face_t *face = (hc_face_t *)callback_ctx; - - if (face->face.netdevice.index == reply->udp_encap.id) { - switch (reply->udp_encap.src_ip.af) { - case ADDRESS_IP4: { - memcpy(&face->face.local_addr.v4, &(reply->udp_encap.src_ip.un.ip4), - sizeof(ip4_address_t)); - memcpy(&face->face.remote_addr.v4, &(reply->udp_encap.dst_ip.un.ip4), - sizeof(ip4_address_t)); - break; - } - case ADDRESS_IP6: { - memcpy(&face->face.local_addr.v6, &(reply->udp_encap.src_ip.un.ip6), - sizeof(ip6_address_t)); - memcpy(&face->face.remote_addr.v6, &(reply->udp_encap.dst_ip.un.ip6), - sizeof(ip6_address_t)); - break; - } - default: - break; - } - - face->face.local_port = reply->udp_encap.src_port; - face->face.remote_port = reply->udp_encap.dst_port; - } - return rv; -} - -static int _fill_face_with_info(hc_face_t *face, vapi_type_fib_path *path, - hc_sock_t *s) { - switch (path->type) { - case FIB_API_PATH_FLAG_NONE: { - face->face.type = FACE_TYPE_HICN; - switch (path->proto) { - case FIB_API_PATH_NH_PROTO_IP4: - memcpy(&face->face.remote_addr.v4, &(path->nh.address.ip4), - sizeof(ip4_address_t)); - break; - case FIB_API_PATH_NH_PROTO_IP6: - memcpy(&face->face.remote_addr.v6, &(path->nh.address.ip6), - sizeof(ip6_address_t)); - break; - default: - break; - } - face->face.netdevice.index = path->sw_if_index; - } break; - case FIB_API_PATH_TYPE_UDP_ENCAP: { - face->face.type = FACE_TYPE_UDP; - face->face.netdevice.index = clib_net_to_host_u32(path->nh.obj_id); - // Let's make the compiler happy - (void)parse_udp_encap_list; - // vapi_msg_udp_encap_dump *msg; - // msg = vapi_alloc_udp_encap_dump(s->g_vapi_ctx_instance); - // vapi_udp_encap_dump(s->g_vapi_ctx_instance, msg, parse_udp_encap_list, - // face); - } break; - default: - return -1; - } - return 0; -} - -/* ROUTE LIST */ -typedef struct hicn_route_socket_s { - hc_data_t *data; - hc_sock_t *s; -} hicn_route_socket_t; - -static vapi_error_e parse_route_list(vapi_ctx_t ctx, void *callback_ctx, - vapi_error_e rv, bool is_last, - vapi_payload_ip_route_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hicn_route_socket_t *rs = (hicn_route_socket_t *)callback_ctx; - hc_data_t *data = rs->data; - - u8 found = false; - for (int j = 0; j < reply->route.n_paths; j++) { - for (int i = 0; i < data->size && !found; i++) { - hc_route_t *route = &((hc_route_t *)(data->buffer))[i]; - - if (ip_address_is_v4((ip_address_t *)&(route->remote_addr)) && - memcmp(route->remote_addr.v4.as_u8, - reply->route.prefix.address.un.ip4, - sizeof(ip4_address_t)) == 0 && - route->len == reply->route.prefix.len && route->face_id == ~0) { - _fill_face_with_info(&(route->face), &reply->route.paths[j], rs->s); - found = true; - } else if (memcmp(route->remote_addr.v6.as_u8, - reply->route.prefix.address.un.ip6, - sizeof(ip6_address_t)) == 0 && - route->len == reply->route.prefix.len && - route->face_id == ~0) { - _fill_face_with_info(&(route->face), &reply->route.paths[j], rs->s); - found = true; - } - } - } - - return rv; -} - -static vapi_error_e parse_hicn_route_list( - vapi_ctx_t ctx, void *callback_ctx, vapi_error_e rv, bool is_last, - vapi_payload_hicn_api_routes_details *reply) { - if (reply == NULL || rv != VAPI_OK) return rv; - - hc_data_t *data = (hc_data_t *)callback_ctx; - - int empty_spots = data->size - data->current; - if (empty_spots < reply->nfaces) { - int new_size = data->size + (reply->nfaces - empty_spots); - data->buffer = realloc(data->buffer, sizeof(hc_route_t) * (new_size)); - if (!data->buffer) return VAPI_ENOMEM; - - data->size = new_size; - } - - for (int i = 0; i < reply->nfaces; i++) { - hc_route_t *route = &((hc_route_t *)(data->buffer))[data->current]; - route->face_id = ~0; - route->cost = 1; - route->len = reply->prefix.len; - if (reply->prefix.address.af == ADDRESS_IP6) { - memcpy(route->remote_addr.v6.as_u8, reply->prefix.address.un.ip6, 16); - } else { - memcpy(route->remote_addr.v4.as_u8, reply->prefix.address.un.ip4, 4); - } - route->family = - reply->prefix.address.af == ADDRESS_IP6 ? AF_INET6 : AF_INET; - data->current++; - } - - return rv; -} - -static int _hc_route_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - hc_sock_vpp_t *s = TO_HC_SOCK_VPP(socket); - vapi_lock(); - - vapi_msg_hicn_api_routes_dump *msg; - msg = vapi_alloc_hicn_api_routes_dump(s->g_vapi_ctx_instance); - - hc_data_t *data = hc_data_create(0, sizeof(hc_route_t), NULL); - int ret = VAPI_OK; - - if (!data) { - ret = -1; - goto err; - } - - data->buffer = malloc(sizeof(hc_route_t)); - data->size = 1; - - if (!data->buffer) { - ret = -1; - goto err_free; - } - - ret = vapi_hicn_api_routes_dump(s->g_vapi_ctx_instance, msg, - parse_hicn_route_list, data); - - if (ret != VAPI_OK) goto err_free; - - vapi_msg_ip_route_dump *hicnp_msg; - hicnp_msg = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); - hicnp_msg->payload.table.table_id = 0; - hicnp_msg->payload.table.is_ip6 = 1; - - hicn_route_socket_t ctx = { - .data = data, - .s = socket, - }; - - ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, hicnp_msg, parse_route_list, - &ctx); - - hicnp_msg = vapi_alloc_ip_route_dump(s->g_vapi_ctx_instance); - hicnp_msg->payload.table.table_id = 0; - hicnp_msg->payload.table.is_ip6 = 0; - - ret = vapi_ip_route_dump(s->g_vapi_ctx_instance, hicnp_msg, parse_route_list, - &ctx); - - if (ret != VAPI_OK) goto err_free; - - *pdata = data; - - vapi_unlock(); - return ret; - -err_free: - free(data); -err: - vapi_unlock(); - return ret; -} - -static int _hc_route_list(hc_sock_t *s, hc_data_t **pdata) { - return _hc_route_list_internal(s, pdata, false); -} - -static int _hc_route_list_async(hc_sock_t *s) { - return _hc_route_list_internal(s, NULL, true); -} - -/*----------------------------------------------------------------------------* - * Face - * - * Face support is not directly available in hicn-light, but we can offer such - * an interface through a combination of listeners and connections. The code - * starts with some conversion functions between faces/listeners/connections. - * - * We also need to make sure that there always exist a (single) listener when a - * connection is created, and in the hICN face case, that there is a single - * connection attached to this listener. - * - *----------------------------------------------------------------------------*/ - -static int _hc_face_create(hc_sock_t *socket, hc_face_t *face) { - ERROR("Face creation not implemented."); - return -1; -} - -static int _hc_face_get(hc_sock_t *socket, hc_face_t *face, - hc_face_t **face_found) { - ERROR("Face deletion not implemented."); - return -1; -} - -static int _hc_face_delete(hc_sock_t *s, hc_face_t *face, - uint8_t delete_listener) { - ERROR("Face deletion not implemented."); - return -1; -} - -/* FACE LIST */ - -static int _hc_face_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("Face list not implemented."); - return -1; -} - -static int _hc_face_list_async(hc_sock_t *s) { return 0; } - -static int _hc_face_set_admin_state( - hc_sock_t *s, const char *conn_id_or_name, // XXX wrong identifier - face_state_t admin_state) { - return 0; -} - -#ifdef WITH_POLICY -static int _hc_face_set_priority(hc_sock_t *s, const char *conn_id_or_name, - uint32_t priority) { - ERROR("Face set priority not implemented."); - return -1; -} - -static int _hc_face_set_tags(hc_sock_t *s, const char *conn_id_or_name, - policy_tags_t tags) { - ERROR("Face set tags not implemented."); - return -1; -} -#endif // WITH_POLICY - -/*----------------------------------------------------------------------------* - * Punting - *----------------------------------------------------------------------------*/ - -static int _hc_punting_create_internal(hc_sock_t *s, hc_punting_t *punting, - bool async) { - return -1; -} - -static int _hc_punting_create(hc_sock_t *s, hc_punting_t *punting) { - return _hc_punting_create_internal(s, punting, false); -} - -static int _hc_punting_create_async(hc_sock_t *s, hc_punting_t *punting) { - return _hc_punting_create_internal(s, punting, true); -} - -static int _hc_punting_get(hc_sock_t *s, hc_punting_t *punting, - hc_punting_t **punting_found) { - ERROR("hc_punting_get not (yet) implemented."); - return -1; -} - -static int _hc_punting_delete(hc_sock_t *s, hc_punting_t *punting) { - ERROR("hc_punting_delete not (yet) implemented."); - return -1; -} - -static int _hc_punting_list(hc_sock_t *s, hc_data_t **pdata) { - ERROR("hc_punting_list not (yet) implemented."); - return -1; -} - -/*----------------------------------------------------------------------------* - * Cache - *----------------------------------------------------------------------------*/ - -static int _hc_cache_set_store_internal(hc_sock_t *s, hc_cache_t *cache, - bool async) { - return 0; -} - -static int _hc_cache_set_store(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_store_internal(s, cache, false); -} - -static int _hc_cache_set_store_async(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_store_internal(s, cache, true); -} - -static int _hc_cache_set_serve_internal(hc_sock_t *s, hc_cache_t *cache, - bool async) { - return 0; -} - -static int _hc_cache_set_serve(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_serve_internal(s, cache, false); -} - -static int _hc_cache_set_serve_async(hc_sock_t *s, hc_cache_t *cache) { - return _hc_cache_set_serve_internal(s, cache, true); -} - -/*----------------------------------------------------------------------------* - * Strategy - *----------------------------------------------------------------------------*/ - -// per prefix -static int _hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { return 0; } - -static int _hc_strategy_add_local_prefix(hc_sock_t *s, - hc_strategy_t *strategy) { - return 0; -} - -static int _hc_strategy_list(hc_sock_t *s, hc_data_t **data) { return 0; } - -/*----------------------------------------------------------------------------* - * WLDR - *----------------------------------------------------------------------------*/ - -// per connection -static int _hc_wldr_set(hc_sock_t *s /* XXX */) { return 0; } - -/*----------------------------------------------------------------------------* - * MAP-Me - *----------------------------------------------------------------------------*/ - -static int _hc_mapme_set(hc_sock_t *s, int enabled) { return 0; } - -static int _hc_mapme_set_discovery(hc_sock_t *s, int enabled) { return 0; } - -static int _hc_mapme_set_timescale(hc_sock_t *s, uint32_t timescale) { - return 0; -} - -static int _hc_mapme_set_retx(hc_sock_t *s, uint32_t timescale) { return 0; } - -/*----------------------------------------------------------------------------* - * Policy - *----------------------------------------------------------------------------*/ - -#ifdef WITH_POLICY - -/* POLICY CREATE */ - -static int _hc_policy_create_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - return -1; -} - -static int _hc_policy_create(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_create_internal(s, policy, false); -} - -static int _hc_policy_create_async(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_create_internal(s, policy, true); -} - -/* POLICY DELETE */ - -static int _hc_policy_delete_internal(hc_sock_t *socket, hc_policy_t *policy, - bool async) { - return -1; -} - -static int _hc_policy_delete(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_delete_internal(s, policy, false); -} - -static int _hc_policy_delete_async(hc_sock_t *s, hc_policy_t *policy) { - return _hc_policy_delete_internal(s, policy, true); -} - -/* POLICY LIST */ - -static int _hc_policy_list_internal(hc_sock_t *socket, hc_data_t **pdata, - bool async) { - return -1; -} - -static int _hc_policy_list(hc_sock_t *s, hc_data_t **pdata) { - return _hc_policy_list_internal(s, pdata, false); -} - -static int _hc_policy_list_async(hc_sock_t *s, hc_data_t **pdata) { - return _hc_policy_list_internal(s, pdata, true); -} - -#endif /* WITH_POLICY */ - -/*----------------------------------------------------------------------------* - * Configuration - *----------------------------------------------------------------------------*/ - -typedef struct hc_result_s { - void *_; -} hc_result_t; - -static hc_result_t *_hc_listener_create_conf(hc_sock_t *s, - hc_listener_t *listener) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_connection_create_conf(hc_sock_t *s, - hc_connection_t *connection) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_route_create_conf(hc_sock_t *s, hc_route_t *route) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_strategy_set_conf(hc_sock_t *s, - hc_strategy_t *strategy) { - ERROR("Not implemented."); - return NULL; -} -static hc_result_t *_hc_strategy_add_local_prefix_conf( - hc_sock_t *s, hc_strategy_t *strategy) { - ERROR("Not implemented."); - return NULL; -} - -hc_msg_t *_hc_result_get_msg(hc_result_t *result) { - ERROR("Not implemented."); - return NULL; -} -int _hc_result_get_cmd_id(hc_result_t *result) { - ERROR("Not implemented."); - return -1; -} -bool _hc_result_get_success(hc_result_t *result) { - ERROR("Not implemented."); - return false; -} -void _hc_result_free(hc_result_t *result) { free(result); } - -static hc_sock_t hc_sock_vpp_interface = (hc_sock_t){ - .hc_sock_get_next_seq = _hc_sock_vpp_get_next_seq, - .hc_sock_set_nonblocking = _hc_sock_vpp_set_nonblocking, - .hc_sock_get_fd = _hc_sock_vpp_get_fd, - .hc_sock_connect = _hc_sock_vpp_connect, - .hc_sock_get_available = _hc_sock_vpp_get_available, - .hc_sock_send = _hc_sock_vpp_send, - .hc_sock_recv = _hc_sock_vpp_recv, - .hc_sock_process = _hc_sock_vpp_process, - .hc_sock_callback = _hc_sock_vpp_callback, - .hc_sock_reset = _hc_sock_vpp_reset, - .hc_sock_free = _hc_sock_vpp_free, - .hc_listener_create = _hc_listener_create, - .hc_listener_create_async = _hc_listener_create_async, - .hc_listener_get = _hc_listener_get, - .hc_listener_delete = _hc_listener_delete, - .hc_listener_delete_async = _hc_listener_delete_async, - .hc_listener_list = _hc_listener_list, - .hc_listener_list_async = _hc_listener_list_async, - .hc_connection_create = _hc_connection_create, - .hc_connection_create_async = _hc_connection_create_async, - .hc_connection_get = _hc_connection_get, - .hc_connection_update_by_id = _hc_connection_update_by_id, - .hc_connection_update = _hc_connection_update, - .hc_connection_delete = _hc_connection_delete, - .hc_connection_delete_async = _hc_connection_delete_async, - .hc_connection_list = _hc_connection_list, - .hc_connection_list_async = _hc_connection_list_async, - .hc_connection_set_admin_state = _hc_connection_set_admin_state, - .hc_connection_set_admin_state_async = _hc_connection_set_admin_state_async, - -#ifdef WITH_POLICY - .hc_connection_set_priority = _hc_connection_set_priority, - .hc_connection_set_priority_async = _hc_connection_set_priority_async, - .hc_connection_set_tags = _hc_connection_set_tags, - .hc_connection_set_tags_async = _hc_connection_set_tags_async, -#endif // WITH_POLICY - - .hc_face_create = _hc_face_create, - .hc_face_get = _hc_face_get, - .hc_face_delete = _hc_face_delete, - .hc_face_list = _hc_face_list, - .hc_face_list_async = _hc_face_list_async, - .hc_face_set_admin_state = _hc_face_set_admin_state, - -#ifdef WITH_POLICY - .hc_face_set_priority = _hc_face_set_priority, - .hc_face_set_tags = _hc_face_set_tags, -#endif // WITH_POLICY - - .hc_route_create = _hc_route_create, - .hc_route_create_async = _hc_route_create_async, - .hc_route_delete = _hc_route_delete, - .hc_route_delete_async = _hc_route_delete_async, - .hc_route_list = _hc_route_list, - .hc_route_list_async = _hc_route_list_async, - - .hc_punting_create = _hc_punting_create, - .hc_punting_create_async = _hc_punting_create_async, - .hc_punting_get = _hc_punting_get, - .hc_punting_delete = _hc_punting_delete, - .hc_punting_list = _hc_punting_list, - - .hc_cache_set_store = _hc_cache_set_store, - .hc_cache_set_store_async = _hc_cache_set_store_async, - .hc_cache_set_serve = _hc_cache_set_serve, - .hc_cache_set_serve_async = _hc_cache_set_serve_async, - - .hc_strategy_list = _hc_strategy_list, - .hc_strategy_set = _hc_strategy_set, - .hc_strategy_add_local_prefix = _hc_strategy_add_local_prefix, - .hc_wldr_set = _hc_wldr_set, - - .hc_mapme_set = _hc_mapme_set, - .hc_mapme_set_discovery = _hc_mapme_set_discovery, - .hc_mapme_set_timescale = _hc_mapme_set_timescale, - .hc_mapme_set_retx = _hc_mapme_set_retx, - -#ifdef WITH_POLICY - .hc_policy_create = _hc_policy_create, - .hc_policy_create_async = _hc_policy_create_async, - .hc_policy_delete = _hc_policy_delete, - .hc_policy_delete_async = _hc_policy_delete_async, - .hc_policy_list = _hc_policy_list, - .hc_policy_list_async = _hc_policy_list_async, -#endif // WITH_POLICY - - .hc_listener_create_conf = _hc_listener_create_conf, - .hc_connection_create_conf = _hc_connection_create_conf, - .hc_route_create_conf = _hc_route_create_conf, - .hc_strategy_set_conf = _hc_strategy_set_conf, - .hc_strategy_add_local_prefix_conf = _hc_strategy_add_local_prefix_conf, - - .hc_result_get_msg = _hc_result_get_msg, - .hc_result_get_cmd_id = _hc_result_get_cmd_id, - .hc_result_get_success = _hc_result_get_success, - .hc_result_free = _hc_result_free, -}; - -hc_sock_t *_hc_sock_create_url(const char *url) { - if (url) { - // NOT IMPLEMENTED - return NULL; - } - - hc_sock_vpp_t *s = malloc(sizeof(hc_sock_vpp_t)); - - if (!s) goto ERR_SOCK; - - memset(s, 0, sizeof(hc_sock_vpp_t)); - - s->vft = hc_sock_vpp_interface; - - // By default the socket is blocking -- not async - s->async = 0; - - return (hc_sock_t *)(s); - -ERR_SOCK: - return NULL; -} diff --git a/ctrl/libhicnctrl/src/object.c b/ctrl/libhicnctrl/src/object.c new file mode 100644 index 000000000..34f21509a --- /dev/null +++ b/ctrl/libhicnctrl/src/object.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file object.c + * \brief Implementation of API object representation. + */ + +#include <hicn/ctrl/action.h> +#include <hicn/ctrl/object.h> + +#include "objects/base.h" // iszero +#include "object_vft.h" + +bool hc_object_is_empty(const hc_object_t *object) { + return iszero(object, sizeof(hc_object_t)); +} + +int hc_object_validate(hc_object_type_t object_type, hc_object_t *object, + bool allow_partial) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->validate(object, allow_partial); +} + +int hc_object_cmp(hc_object_type_t object_type, hc_object_t *object1, + hc_object_t *object2) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->cmp(object1, object2); +} + +int hc_object_snprintf(char *s, size_t size, hc_object_type_t object_type, + hc_object_t *object) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->obj_snprintf(s, size, object); +} + +size_t hc_object_size(hc_object_type_t object_type) { + const hc_object_ops_t *vft = object_vft[object_type]; + if (!vft) return -1; + return vft->size; +} diff --git a/ctrl/libhicnctrl/src/object_private.h b/ctrl/libhicnctrl/src/object_private.h new file mode 100644 index 000000000..2be12fd5e --- /dev/null +++ b/ctrl/libhicnctrl/src/object_private.h @@ -0,0 +1,35 @@ +#ifndef HICNCTRL_OBJECT_PRIVATE_H +#define HICNCTRL_OBJECT_PRIVATE_H + +#define INT_CMP(x, y) ((x > y) ? 1 : (x < y) ? -1 : 0) + +// XXX Those are always true +#define IS_VALID_ADDRESS(x) (1) +#define IS_VALID_CONNECTION_ID(x) (1) // XXX ID +#define IS_VALID_ROUTE_COST(x) (1) +#define IS_VALID_PREFIX_LEN(x) (1) +#define IS_VALID_POLICY(x) (1) +#define IS_VALID_ID(x) (1) +#define IS_VALID_INTERFACE_NAME(x) (1) +#define IS_VALID_NAME(x) (1) +#define IS_VALID_TYPE(x) IS_VALID_ENUM_TYPE(FACE_TYPE, x) +#define IS_VALID_FACE_STATE(x) (1) + +#define IS_VALID_ADDR_TYPE(x) ((x >= ADDR_INET) && (x <= ADDR_UNIX)) + +#define IS_VALID_CONNECTION_TYPE(x) IS_VALID_ENUM_TYPE(CONNECTION_TYPE, x) + +#define GENERATE_FIND(TYPE) \ + int hc_##TYPE##_find(hc_data_t *data, const hc_##TYPE##_t *element, \ + hc_##TYPE##_t **found) { \ + foreach_type(hc_##TYPE##_t, x, data) { \ + if (hc_##TYPE##_cmp(x, element) == 0) { \ + *found = x; \ + return 0; \ + } \ + }; \ + *found = NULL; /* this is optional */ \ + return 0; \ + } + +#endif /* HICNCTRL_OBJECT_PRIVATE_H */ diff --git a/ctrl/libhicnctrl/src/object_type.c b/ctrl/libhicnctrl/src/object_type.c new file mode 100644 index 000000000..0303fce75 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_type.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file object_type.c + * \brief Implementation of object type. + */ + +#include <strings.h> + +#include <hicn/ctrl/object_type.h> + +const char *object_type_str[] = { +#define _(x) [OBJECT_TYPE_##x] = #x, + foreach_object_type +#undef _ +}; + +hc_object_type_t object_type_from_str(const char *object_str) { +#define _(x) \ + if (strcasecmp(object_str, #x) == 0) \ + return OBJECT_TYPE_##x; \ + else + foreach_object_type +#undef _ + return OBJECT_TYPE_UNDEFINED; +} diff --git a/ctrl/libhicnctrl/src/object_vft.c b/ctrl/libhicnctrl/src/object_vft.c new file mode 100644 index 000000000..808c0f913 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_vft.c @@ -0,0 +1,19 @@ +#include "object_vft.h" + +#include "objects/listener.h" +#include "objects/connection.h" +#include "objects/route.h" +#include "objects/face.h" +#include "objects/strategy.h" +#include "objects/subscription.h" +#include "objects/active_interface.h" + +const hc_object_ops_t *object_vft[] = { + [OBJECT_TYPE_LISTENER] = &hc_listener_ops, + [OBJECT_TYPE_CONNECTION] = &hc_connection_ops, + [OBJECT_TYPE_ROUTE] = &hc_route_ops, + [OBJECT_TYPE_FACE] = &hc_face_ops, + [OBJECT_TYPE_STRATEGY] = &hc_strategy_ops, + [OBJECT_TYPE_SUBSCRIPTION] = &hc_subscription_ops, + [OBJECT_TYPE_ACTIVE_INTERFACE] = &hc_active_interface_ops, +}; diff --git a/ctrl/libhicnctrl/src/object_vft.h b/ctrl/libhicnctrl/src/object_vft.h new file mode 100644 index 000000000..e27fe5d36 --- /dev/null +++ b/ctrl/libhicnctrl/src/object_vft.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file object_vft.h + * \brief Object VFT. + */ + +#ifndef HICNCTRL_OBJECT_VFT +#define HICNCTRL_OBJECT_VFT + +#include <stddef.h> // size_t +#include <hicn/ctrl/object_type.h> +#include <hicn/ctrl/object.h> + +typedef struct { + hc_object_type_t object_type; + const char *object_name; + size_t size; + int (*validate)(const hc_object_t *object, bool allow_partial); + int (*cmp)(const hc_object_t *object1, const hc_object_t *object2); + /* cannot be named snprintf as it collides with a macro in clang/iOS */ + int (*obj_snprintf)(char *s, size_t size, const hc_object_t *object); +} hc_object_ops_t; + +#define DECLARE_OBJECT_OPS_H(TYPE, NAME) \ + extern const hc_object_ops_t hc_##NAME##_ops; + +#define DECLARE_OBJECT_OPS(TYPE, NAME) \ + const hc_object_ops_t hc_##NAME##_ops = { \ + .object_type = TYPE, \ + .object_name = #NAME, \ + .size = sizeof(hc_##NAME##_t), \ + .validate = _hc_##NAME##_validate, \ + .cmp = _hc_##NAME##_cmp, \ + .obj_snprintf = _hc_##NAME##_snprintf, \ + }; + +extern const hc_object_ops_t *object_vft[]; + +#endif /* HICNCTRL_OBJECT_VFT */ diff --git a/ctrl/libhicnctrl/src/objects/active_interface.c b/ctrl/libhicnctrl/src/objects/active_interface.c new file mode 100644 index 000000000..796123963 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/active_interface.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file active_interface.c + * \brief Implementation of active_interface object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/active_interface.h> +#include <hicn/util/log.h> +#include <hicn/util/ip_address.h> + +#include "../object_private.h" +#include "../object_vft.h" + +/* ACTIVE_INTERFACE VALIDATE */ + +int hc_active_interface_validate(const hc_active_interface_t *active_interface, + bool allow_partial) { + return 0; // XXX TODO +} + +int _hc_active_interface_validate(const hc_object_t *object, + bool allow_partial) { + return hc_active_interface_validate(&object->active_interface, allow_partial); +} + +/* ACTIVE_INTERFACE CMP */ + +// XXX TODO +int hc_active_interface_cmp(const hc_active_interface_t *active_interface1, + const hc_active_interface_t *active_interface2) { + return -1; +} + +int _hc_active_interface_cmp(const hc_object_t *object1, + const hc_object_t *object2) { + return hc_active_interface_cmp(&object1->active_interface, + &object2->active_interface); +} + +/* ACTIVE_INTERFACE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_active_interface_snprintf( + char *s, size_t size, const hc_active_interface_t *active_interface) { + int rc; + char *pos = s; + + rc = hicn_ip_prefix_snprintf(pos, size, &active_interface->prefix); + if ((rc < 0) || (rc >= size)) return rc; + pos += rc; + size -= rc; + + for (netdevice_type_t type = NETDEVICE_TYPE_UNDEFINED + 1; + type < NETDEVICE_TYPE_N; type++) { + if (!netdevice_flags_has(active_interface->interface_types, type)) continue; + rc = snprintf(pos, size, " %s", netdevice_type_str(type)); + if ((rc < 0) || (rc >= size)) return (int)(pos - s + rc); + + pos += rc; + size -= rc; + } + return (int)(pos - s); +} + +int _hc_active_interface_snprintf(char *s, size_t size, + const hc_object_t *object) { + return hc_active_interface_snprintf(s, size, &object->active_interface); +} + +DECLARE_OBJECT_OPS(OBJECT_TYPE_ACTIVE_INTERFACE, active_interface); diff --git a/ctrl/libhicnctrl/src/objects/active_interface.h b/ctrl/libhicnctrl/src/objects/active_interface.h new file mode 100644 index 000000000..973b08e40 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/active_interface.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file active_interface.h + * \brief Route. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H +#define HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_ACTIVE_INTERFACE, active_interface); + +#endif /* HICNCTRL_IMPL_OBJECTS_ACTIVE_INTERFACE_H */ diff --git a/ctrl/libhicnctrl/src/objects/base.c b/ctrl/libhicnctrl/src/objects/base.c new file mode 100644 index 000000000..86e4bfb72 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/base.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file base.c + * \brief Implementation of base functions for object APIs. + */ + +#include <stdbool.h> + +#include "base.h" + +#include <hicn/util/log.h> + +bool iszero(const void *ptr, int bytes) { + char *bptr = (char *)ptr; + while (bytes--) + if (*bptr++) return false; + return true; +} + +bool isempty(const char *str) { return str[0] == '\0'; } diff --git a/ctrl/libhicnctrl/src/objects/base.h b/ctrl/libhicnctrl/src/objects/base.h new file mode 100644 index 000000000..eb85483e9 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/base.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file base.h + * \brief Base functions for object APIs. + */ + +#ifndef HICNCTRL_OBJECTS_BASE_H +#define HICNCTRL_OBJECTS_BASE_H + +bool iszero(const void *ptr, int bytes); +bool isempty(const char *str); + +#endif /* HICNCTRL_OBJECTS_BASE_H */ diff --git a/ctrl/libhicnctrl/src/objects/connection.c b/ctrl/libhicnctrl/src/objects/connection.c new file mode 100644 index 000000000..14e763396 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/connection.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file connection.c + * \brief Implementation of connection object. + */ + +#include <assert.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/connection.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" +#include "base.h" + +bool hc_connection_is_local(const hc_connection_t *connection) { + return (strncmp(connection->interface_name, "lo", INTERFACE_LEN) == 0); +} + +bool hc_connection_has_local(const hc_connection_t *connection) { + assert(connection); + return IS_VALID_PORT(connection->local_port) && + IS_VALID_ADDRESS(connection->local_addr); +} + +/* CONNECTION VALIDATE */ + +int hc_connection_validate(const hc_connection_t *connection, + bool allow_partial) { + int has_id = 0; + int has_name = 0; + int has_interface_name = 0; + int has_netdevice_type = 0; + int has_type = 0; + int has_family = 0; + int has_local_addr = 0; + int has_local_port = 0; + int has_remote_addr = 0; + int has_remote_port = 0; + int has_admin_state = 0; + int has_priority = 0; + int has_tags = 0; + int has_state = 0; + + if (connection->id == ~0) { + ERROR("[hc_listener_validate] Invalid id specified"); + return -1; + } + has_id = 1; + + if (!isempty(connection->name)) { + if (!IS_VALID_NAME(connection->name)) { + ERROR("[hc_connection_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (!isempty(connection->interface_name)) { + if (!IS_VALID_INTERFACE_NAME(connection->interface_name)) { + ERROR("[hc_connection_validate] Invalid interface_name specified"); + return -1; + } + has_interface_name = 1; + } + + if (connection->type != FACE_TYPE_UNDEFINED) { + if (!IS_VALID_TYPE(connection->type)) { + ERROR("[hc_connection_validate] Invalid type specified"); + return -1; + } + has_type = 1; + } + + if (connection->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(connection->family)) { + ERROR("[hc_connection_validate] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&connection->local_addr)) { + if (!IS_VALID_ADDRESS(connection->local_addr)) { + ERROR("[hc_connection_validate] Invalid local_addr specified"); + return -1; + } + has_local_addr = 1; + } + + if (connection->local_port != 0) { + if (!IS_VALID_PORT(connection->local_port)) { + ERROR("[hc_connection_validate] Invalid local_port specified"); + return -1; + } + has_local_port = 1; + } + + if (!hicn_ip_address_empty(&connection->remote_addr)) { + if (!IS_VALID_ADDRESS(connection->remote_addr)) { + ERROR("[hc_connection_validate] Invalid remote_addr specified"); + return -1; + } + has_remote_addr = 1; + } + + if (connection->remote_port != 0) { + if (!IS_VALID_PORT(connection->remote_port)) { + ERROR("[hc_connection_validate] Invalid remote_port specified"); + return -1; + } + has_remote_port = 1; + } + + int has_key = has_id || has_name; + int has_mandatory_attributes = has_interface_name && has_type && has_family && + has_local_addr && has_local_port && + has_remote_addr && has_remote_port; + int has_optional_attributes = + has_netdevice_type && has_admin_state && has_state; + has_optional_attributes = has_optional_attributes && has_priority && has_tags; + + if (allow_partial) { + if (has_key && !has_mandatory_attributes && !has_optional_attributes) + return 0; + else if (has_mandatory_attributes) + return 0; + else + return -1; + } else { + if (has_key && has_mandatory_attributes) return 0; + return -1; + } +} + +int _hc_connection_validate(const hc_object_t *object, bool allow_partial) { + return hc_connection_validate(&object->connection, allow_partial); +} + +/* CONNECTION CMP */ + +/* + * hICN light uses ports even for hICN connections, but their value is + * ignored. As connections are specific to hicn-light, we can safely use IP + * and ports for comparison independently of the face type. + */ +int hc_connection_cmp(const hc_connection_t *c1, const hc_connection_t *c2) { + int rc; + + rc = INT_CMP(c1->type, c2->type); + if (rc != 0) return rc; + + rc = INT_CMP(c1->family, c2->family); + if (rc != 0) return rc; + + rc = strncmp(c1->interface_name, c2->interface_name, INTERFACE_LEN); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&c1->local_addr, &c2->local_addr); + if (rc != 0) return rc; + + rc = INT_CMP(c1->local_port, c2->local_port); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&c1->remote_addr, &c2->remote_addr); + if (rc != 0) return rc; + + rc = INT_CMP(c1->remote_port, c2->remote_port); + if (rc != 0) return rc; + + return rc; +} + +int _hc_connection_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_connection_cmp(&object1->connection, &object2->connection); +} + +/* CONNECTION SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_connection_snprintf(char *s, size_t size, + const hc_connection_t *connection) { + char local[MAXSZ_URL]; + char remote[MAXSZ_URL]; + int rc; + + // assert(connection->connection_state) + if (strcmp(connection->name, "SELF") == 0) { + return snprintf(s, size, "%s", connection->name); + } + + rc = url_snprintf(local, MAXSZ_URL, &connection->local_addr, + connection->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = url_snprintf(remote, MAXSZ_URL, &connection->remote_addr, + connection->remote_port); + if (rc >= MAXSZ_URL) + WARN("[hc_connection_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + + return snprintf(s, size, "%s %s %s %s", connection->name, local, remote, + face_type_str(connection->type)); +} + +int _hc_connection_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_connection_snprintf(s, size, &object->connection); +} + +int hc_connection_create(hc_sock_t *s, hc_connection_t *connection) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_get(hc_sock_t *s, hc_connection_t *connection, + hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_CONNECTION, &object, pdata); +} + +int hc_connection_delete(hc_sock_t *s, hc_connection_t *connection) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.connection = *connection; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_CONNECTION, NULL, pdata); +} + +int hc_connection_set_admin_state(hc_sock_t *s, const char *conn_id_or_name, + face_state_t state) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.admin_state = state; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_set_priority(hc_sock_t *s, const char *conn_id_or_name, + uint32_t priority) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.priority = priority; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +int hc_connection_set_tags(hc_sock_t *s, const char *conn_id_or_name, + + policy_tags_t tags) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + int rc = snprintf(object.connection.name, SYMBOLIC_NAME_LEN, "%s", + conn_id_or_name); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) return -1; + object.connection.tags = tags; + return hc_execute(s, ACTION_UPDATE, OBJECT_TYPE_CONNECTION, &object, NULL); +} + +GENERATE_FIND(connection); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_CONNECTION, connection); diff --git a/ctrl/libhicnctrl/src/objects/connection.h b/ctrl/libhicnctrl/src/objects/connection.h new file mode 100644 index 000000000..4a4c78f09 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/connection.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file connection.h + * \brief Connection. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_CONNECTION_H +#define HICNCTRL_IMPL_OBJECTS_CONNECTION_H + +#include "../object_vft.h" + +bool hc_connection_is_local(const hc_connection_t *connection); +bool hc_connection_has_local(const hc_connection_t *connection); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_CONNECTION, connection); + +#endif /* HICNCTRL_IMPL_OBJECTS_CONNECTION_H */ diff --git a/ctrl/libhicnctrl/src/objects/face.c b/ctrl/libhicnctrl/src/objects/face.c new file mode 100644 index 000000000..c535ff4c5 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/face.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file face.c + * \brief Implementation of face object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/face.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" + +bool hc_face_has_netdevice(const hc_face_t *face) { + return netdevice_is_empty(&face->netdevice); +} + +/* FACE VALIDATE */ + +int hc_face_validate(const hc_face_t *face, bool allow_partial) { + if ((!allow_partial || !hc_face_has_netdevice(face)) && + !IS_VALID_INTERFACE_NAME(face->interface_name)) { + ERROR("[hc_face_validate] Invalid interface_name specified"); + return -1; + } + if (!IS_VALID_TYPE(face->type)) { + ERROR("[hc_face_validate] Invalid type specified"); + return -1; + } + if ((!allow_partial || face->family != AF_UNSPEC) && + !IS_VALID_FAMILY(face->family)) { + ERROR("[hc_face_validate] Invalid family specified"); + return -1; + } + if ((!allow_partial || !hicn_ip_address_empty(&face->local_addr)) && + !IS_VALID_ADDRESS(face->local_addr)) { + ERROR("[hc_face_validate] Invalid local_addr specified"); + return -1; + } + if ((!allow_partial || !(face->local_port == 0)) && + !IS_VALID_PORT(face->local_port)) { + ERROR("[hc_face_validate] Invalid local_port specified"); + return -1; + } + if (!IS_VALID_ADDRESS(face->remote_addr)) { + ERROR("[hc_face_validate] Invalid remote_addr specified"); + return -1; + } + if (!IS_VALID_PORT(face->remote_port)) { + ERROR("[hc_face_validate] Invalid remote_port specified"); + return -1; + } + return 0; +} + +int _hc_face_validate(const hc_object_t *object, bool allow_partial) { + return hc_face_validate(&object->face, allow_partial); +} + +/* FACE CMP */ + +int hc_face_cmp(const hc_face_t *c1, const hc_face_t *c2) { + return -99; // Not implemented +} + +int _hc_face_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_face_cmp(&object1->face, &object2->face); +} + +/* FACE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_face_snprintf(char *s, size_t size, const hc_face_t *face) { + /* URLs are also big enough to contain IP addresses in the hICN case */ + char local[MAXSZ_URL]; + char remote[MAXSZ_URL]; + char tags[MAXSZ_POLICY_TAGS]; + int rc; + + switch (face->type) { + case FACE_TYPE_HICN: + case FACE_TYPE_HICN_LISTENER: + rc = hicn_ip_address_snprintf(local, MAXSZ_URL, &face->local_addr); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = hicn_ip_address_snprintf(remote, MAXSZ_URL, &face->remote_addr); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + break; + case FACE_TYPE_TCP: + case FACE_TYPE_UDP: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UDP_LISTENER: + rc = url_snprintf(local, MAXSZ_URL, &face->local_addr, face->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + rc = url_snprintf(remote, MAXSZ_URL, &face->remote_addr, + face->remote_port); + if (rc >= MAXSZ_URL) + WARN("[hc_face_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + break; + default: + return -1; + } + + // [#ID NAME] TYPE LOCAL_URL REMOTE_URL STATE/ADMIN_STATE (TAGS) + rc = policy_tags_snprintf(tags, MAXSZ_POLICY_TAGS, face->tags); + if (rc >= MAXSZ_POLICY_TAGS) + WARN("[hc_face_snprintf] Unexpected truncation of policy tags string"); + if (rc < 0) return rc; + + return snprintf( + s, size, "[#%d %s] %s %s %s %s %s/%s [%d] (%s)", face->id, face->name, + face->netdevice.index != NETDEVICE_UNDEFINED_INDEX ? face->netdevice.name + : "*", + face_type_str(face->type), local, remote, face_state_str(face->state), + face_state_str(face->admin_state), face->priority, tags); +} + +int _hc_face_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_face_snprintf(s, size, &object->face); +} + +int hc_face_create(hc_sock_t *s, hc_face_t *face) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_FACE, &object, NULL); +} + +int hc_face_get(hc_sock_t *s, hc_face_t *face, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_FACE, &object, pdata); +} + +int hc_face_delete(hc_sock_t *s, hc_face_t *face) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.face = *face; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_FACE, &object, NULL); +} + +int hc_face_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_FACE, NULL, pdata); +} + +int hc_face_list_async(hc_sock_t *s) { + return hc_execute_async(s, ACTION_LIST, OBJECT_TYPE_FACE, NULL, NULL, NULL); +} + +GENERATE_FIND(face); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_FACE, face); diff --git a/ctrl/libhicnctrl/src/objects/face.h b/ctrl/libhicnctrl/src/objects/face.h new file mode 100644 index 000000000..08f90f195 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/face.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file face.h + * \brief Face. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_FACE_H +#define HICNCTRL_IMPL_OBJECTS_FACE_H + +#include "../object_vft.h" + +int hc_face_validate(const hc_face_t *face, bool allow_partial); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_FACE, face); + +#endif /* HICNCTRL_IMPL_OBJECTS_FACE_H */ diff --git a/ctrl/libhicnctrl/src/objects/listener.c b/ctrl/libhicnctrl/src/objects/listener.c new file mode 100644 index 000000000..660a4931d --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/listener.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file listener.c + * \brief Implementation of listener. + */ + +#include <string.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/listener.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" +#include "base.h" + +bool hc_listener_is_local(const hc_listener_t *listener) { + return (strncmp(listener->interface_name, "lo", INTERFACE_LEN) == 0); +} + +/* LISTENER VALIDATE */ + +int hc_listener_validate(const hc_listener_t *listener, bool allow_partial) { + // if a field is specified it should be valid + // then we allow different specification, by key or by attributes, if + // allow_partial + + int has_id = 0; + int has_name = 0; + int has_interface_name = 0; + int has_type = 0; + int has_family = 0; + int has_local_addr = 0; + int has_local_port = 0; + + if (listener->id == ~0) { + ERROR("[hc_listener_validate] Invalid id specified"); + return -1; + } + has_id = 1; + + if (!isempty(listener->name)) { + if (!IS_VALID_NAME(listener->name)) { + ERROR("[hc_listener_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (!isempty(listener->interface_name)) { + if (!IS_VALID_INTERFACE_NAME(listener->interface_name)) { + ERROR("[hc_listener_validate] Invalid interface_name specified"); + return -1; + } + has_interface_name = 1; + } + + if (listener->type != FACE_TYPE_UNDEFINED) { + if (!IS_VALID_TYPE(listener->type)) { + ERROR("[hc_listener_validate] Invalid type specified"); + return -1; + } + has_type = 1; + } + + if (listener->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(listener->family)) { + ERROR("[hc_listener_validat] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&listener->local_addr)) { + if (!IS_VALID_ADDRESS(listener->local_addr)) { + ERROR("[hc_listener_validate] Invalid local_addr specified"); + return -1; + } + has_local_addr = 1; + } + + if (listener->local_port != 0) { + if (!IS_VALID_PORT(listener->local_port)) { + ERROR("[hc_listener_validate] Invalid local_port specified"); + return -1; + } + has_local_port = 1; + } + + if (allow_partial) { + if ((has_id || has_name) && !has_type && !has_family && !has_local_port && + !has_local_port) + return 0; + else if (has_name && has_type && has_family && has_local_addr && + has_local_port) + return 0; + else + return -1; + } else { + /* name is optional */ + if (has_id && has_interface_name && has_type && has_family && + has_local_addr && has_local_port) + return 0; + return -1; + } +} + +int _hc_listener_validate(const hc_object_t *object, bool allow_partial) { + return hc_listener_validate(&object->listener, allow_partial); +} + +/* LISTENER CMP */ + +int hc_listener_cmp(const hc_listener_t *l1, const hc_listener_t *l2) { + int rc; + + rc = INT_CMP(l1->type, l2->type); + if (rc != 0) return rc; + + rc = INT_CMP(l1->family, l2->family); + if (rc != 0) return rc; + + rc = strncmp(l1->interface_name, l2->interface_name, INTERFACE_LEN); + if (rc != 0) return rc; + + rc = hicn_ip_address_cmp(&l1->local_addr, &l2->local_addr); + if (rc != 0) return rc; + + rc = INT_CMP(l1->local_port, l2->local_port); + if (rc != 0) return rc; + + return rc; +} + +int _hc_listener_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_listener_cmp(&object1->listener, &object2->listener); +} + +/* LISTENER SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_listener_snprintf(char *s, size_t size, const hc_listener_t *listener) { + char local[MAXSZ_URL]; + int rc; + rc = url_snprintf(local, MAXSZ_URL, &listener->local_addr, + listener->local_port); + if (rc >= MAXSZ_URL) + WARN("[hc_listener_snprintf] Unexpected truncation of URL string"); + if (rc < 0) return rc; + + return snprintf(s, size, "%s %s %s interface=%s", listener->name, local, + face_type_str(listener->type), listener->interface_name); +} + +int _hc_listener_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_listener_snprintf(s, size, &object->listener); +} + +/* OPERATIONS */ + +int hc_listener_create(hc_sock_t *s, hc_listener_t *listener) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_LISTENER, &object, NULL); +} + +int hc_listener_get(hc_sock_t *s, hc_listener_t *listener, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_LISTENER, &object, pdata); +} + +int hc_listener_delete(hc_sock_t *s, hc_listener_t *listener) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_LISTENER, &object, NULL); +} + +int hc_listener_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_LISTENER, NULL, pdata); +} + +GENERATE_FIND(listener); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_LISTENER, listener); diff --git a/ctrl/libhicnctrl/src/objects/listener.h b/ctrl/libhicnctrl/src/objects/listener.h new file mode 100644 index 000000000..515c8f0ad --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/listener.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file listener.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_LISTENER_H +#define HICNCTRL_IMPL_OBJECTS_LISTENER_H + +#include "../object_vft.h" + +bool hc_listener_is_local(const hc_listener_t *listener); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_LISTENER, listener); + +#endif /* HICNCTRL_IMPL_OBJECTS_LISTENER_H */ diff --git a/ctrl/libhicnctrl/src/objects/route.c b/ctrl/libhicnctrl/src/objects/route.c new file mode 100644 index 000000000..44f39bcd7 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/route.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file route.c + * \brief Implementation of route object. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/route.h> +#include <hicn/util/log.h> + +#include "../object_private.h" +#include "../object_vft.h" +#include "face.h" +#include "base.h" + +bool hc_route_has_face(const hc_route_t *route) { + return !iszero(&route->face, sizeof(hc_face_t)); +} + +/* ROUTE VALIDATE */ + +int hc_route_validate(const hc_route_t *route, bool allow_partial) { + int has_id = 0; + int has_name = 0; + int has_face = 0; + + int has_family = 0; + int has_remote_addr = 0; + + if (!IS_VALID_CONNECTION_ID(route->face_id)) { + ERROR("[hc_route_validate] Invalid face id"); + return -1; + } + has_id = 1; + + if (!isempty(route->face_name)) { + if (!IS_VALID_NAME(route->face_name)) { + ERROR("[hc_route_validate] Invalid name specified"); + return -1; + } + has_name = 1; + } + + if (route->family != AF_UNSPEC) { + if (!IS_VALID_FAMILY(route->family)) { + ERROR("[hc_route_validate] Invalid family specified"); + return -1; + } + has_family = 1; + } + + if (!hicn_ip_address_empty(&route->remote_addr)) { + if (!IS_VALID_ADDRESS(route->remote_addr)) { + ERROR("[hc_route_validate] Invalid remote_addr specified"); + return -1; + } + has_remote_addr = 1; + } + + if (!IS_VALID_ROUTE_COST(route->cost)) { + ERROR("[hc_route_validate] Invalid cost"); + return -1; + } + + if (!IS_VALID_PREFIX_LEN(route->len)) { + ERROR("[hc_route_validate] Invalid len"); + return -1; + } + + if (hc_route_has_face(route)) { + if (!hc_face_validate(&route->face, allow_partial)) { + ERROR("[hc_route_validate] Invalid face"); + return -1; + } + has_face = 1; + } + + int has_face_info = has_id || has_name || has_face; + + if (!has_face_info) return -1; + if (allow_partial && (has_name + has_face != 1)) return -1; + + if (has_face_info && has_family && has_remote_addr) return 0; + + return -1; +} + +int _hc_route_validate(const hc_object_t *object, bool allow_partial) { + return hc_route_validate(&object->route, allow_partial); +} + +/* ROUTE CMP */ + +// XXX TODO +int hc_route_cmp(const hc_route_t *route1, const hc_route_t *route2) { + return -1; +} + +int _hc_route_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_route_cmp(&object1->route, &object2->route); +} + +/* ROUTE SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_route_snprintf(char *s, size_t size, const hc_route_t *route) { + /* interface cost prefix length */ + + char prefix[MAXSZ_IP_ADDRESS]; + int rc; + + rc = hicn_ip_address_snprintf(prefix, MAXSZ_IP_ADDRESS, &route->remote_addr); + if (rc >= MAXSZ_IP_ADDRESS) + ; + if (rc < 0) return rc; + + return snprintf(s, size, "%d (%s) %*d %s %*d", route->face_id, + route->face_name, MAXSZ_COST, route->cost, prefix, MAXSZ_LEN, + route->len); +} + +int _hc_route_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_route_snprintf(s, size, &object->route); +} + +int hc_route_create(hc_sock_t *s, hc_route_t *route) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_ROUTE, &object, NULL); +} + +int hc_route_get(hc_sock_t *s, hc_route_t *route, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_ROUTE, &object, pdata); +} + +int hc_route_delete(hc_sock_t *s, hc_route_t *route) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.route = *route; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_ROUTE, &object, NULL); +} + +int hc_route_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_ROUTE, NULL, pdata); +} + +int hc_route_list_async(hc_sock_t *s) { + return hc_execute_async(s, ACTION_LIST, OBJECT_TYPE_ROUTE, NULL, NULL, NULL); +} + +// XXX difference between GET and FIND +GENERATE_FIND(route); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_ROUTE, route); diff --git a/ctrl/libhicnctrl/src/objects/route.h b/ctrl/libhicnctrl/src/objects/route.h new file mode 100644 index 000000000..854bd70a6 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/route.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file route.h + * \brief Route. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_ROUTE_H +#define HICNCTRL_IMPL_OBJECTS_ROUTE_H + +#include "../object_vft.h" + +bool hc_route_has_face(const hc_route_t* route); + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_ROUTE, route); + +#endif /* HICNCTRL_IMPL_OBJECTS_ROUTE_H */ diff --git a/ctrl/libhicnctrl/src/objects/stats.c b/ctrl/libhicnctrl/src/objects/stats.c new file mode 100644 index 000000000..2c3135d3c --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/stats.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file stats.c + * \brief Implementation of stats. + */ + +#include <string.h> + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/stats.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +int hc_stats_snprintf(char *s, size_t size, const hc_stats_t *stats) { +#if 0 + INFO("Connection #%d:", conn_stats->id); + INFO("\tinterests received: %d pkts (%d bytes)", + conn_stats->stats.interests.rx_pkts, + conn_stats->stats.interests.rx_bytes); + INFO("\tinterests transmitted: %d pkts (%d bytes)", + conn_stats->stats.interests.tx_pkts, + conn_stats->stats.interests.tx_bytes); + INFO("\tdata received: %d pkts (%d bytes)", + conn_stats->stats.data.rx_pkts, + conn_stats->stats.data.rx_bytes); + INFO("\tdata transmitted: %d pkts (%d bytes)", + conn_stats->stats.data.tx_pkts, + conn_stats->stats.data.tx_bytes); +#endif + return 0; +} + +int hc_stats_get(hc_sock_t *s, hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.listener = *listener; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_STATS, &object, pdata); +} + +int hc_stats_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_STATS, NULL, pdata); +} diff --git a/ctrl/libhicnctrl/src/objects/strategy.c b/ctrl/libhicnctrl/src/objects/strategy.c new file mode 100644 index 000000000..0e7a5787e --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/strategy.c @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file strategy.c + * \brief Implementation of strategy. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/strategy.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +/* STREATEGY VALIDATE */ + +int hc_strategy_validate(const hc_strategy_t *strategy, bool allow_partial) { + // TODO verify name + return 0; +} + +int _hc_strategy_validate(const hc_object_t *object, bool allow_partial) { + return hc_strategy_validate(&object->strategy, allow_partial); +} + +/* STRATEGY CMP */ + +int hc_strategy_cmp(const hc_strategy_t *s1, const hc_strategy_t *s2) { + return strcmp(s1->name, s2->name); +} + +int _hc_strategy_cmp(const hc_object_t *object1, const hc_object_t *object2) { + return hc_strategy_cmp(&object1->strategy, &object2->strategy); +} + +/* STRATEGY SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_strategy_snprintf(char *s, size_t size, const hc_strategy_t *strategy) { + return snprintf(s, size, "%s", strategy->name); +} + +int _hc_strategy_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_strategy_snprintf(s, size, &object->strategy); +} + +/* OPERATIONS */ + +int hc_strategy_create(hc_sock_t *s, hc_strategy_t *strategy) { return -1; } + +int hc_strategy_get(hc_sock_t *s, hc_strategy_t *strategy, hc_data_t **pdata) { + return -1; +} + +int hc_strategy_delete(hc_sock_t *s, hc_strategy_t *strategy) { return -1; } + +int hc_strategy_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_STRATEGY, NULL, pdata); +} + +/* new api */ + +int hc_strategy_set(hc_sock_t *s, hc_strategy_t *strategy) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.strategy = *strategy; + return hc_execute(s, ACTION_SET, OBJECT_TYPE_STRATEGY, &object, NULL); +} + +#if 0 +int hc_strategy_add_local_prefix(hc_sock_t *s, hc_strategy_t *strategy) { + return s->hc_strategy_add_local_prefix(s, strategy); +} +#endif + +GENERATE_FIND(strategy); + +DECLARE_OBJECT_OPS(OBJECT_TYPE_STRATEGY, strategy); diff --git a/ctrl/libhicnctrl/src/objects/strategy.h b/ctrl/libhicnctrl/src/objects/strategy.h new file mode 100644 index 000000000..0cc4f525d --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/strategy.h @@ -0,0 +1,29 @@ + +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file listener.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_STRATEGY_H +#define HICNCTRL_IMPL_OBJECTS_STRATEGY_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_STRATEGY, strategy); + +#endif /* HICNCTRL_IMPL_OBJECTS_STRATEGY_H */ diff --git a/ctrl/libhicnctrl/src/objects/subscription.c b/ctrl/libhicnctrl/src/objects/subscription.c new file mode 100644 index 000000000..087e42ffb --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/subscription.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file subscription.c + * \brief Implementation of subscription. + */ + +#include <hicn/ctrl/api.h> +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/subscription.h> +#include <hicn/util/log.h> + +#include "../object_vft.h" +#include "../object_private.h" + +/* SUBSCRIPTION VALIDATE */ + +int hc_subscription_validate(const hc_subscription_t *subscription, + bool allow_partial) { + /* Any topic is considered valid */ + return 0; +} + +int _hc_subscription_validate(const hc_object_t *object, bool allow_partial) { + return hc_subscription_validate(&object->subscription, allow_partial); +} + +/* LISTENER CMP */ + +int hc_subscription_cmp(const hc_subscription_t *l1, + const hc_subscription_t *l2) { + return -1; +} + +int _hc_subscription_cmp(const hc_object_t *object1, + const hc_object_t *object2) { + return hc_subscription_cmp(&object1->subscription, &object2->subscription); +} + +/* SUBSCRIPTION SNPRINTF */ + +/* /!\ Please update constants in header file upon changes */ +int hc_subscription_snprintf(char *s, size_t size, + const hc_subscription_t *subscription) { + return -1; +} + +int _hc_subscription_snprintf(char *s, size_t size, const hc_object_t *object) { + return hc_subscription_snprintf(s, size, &object->subscription); +} + +/* OPERATIONS */ + +int hc_subscription_create(hc_sock_t *s, hc_subscription_t *subscription) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_CREATE, OBJECT_TYPE_SUBSCRIPTION, &object, NULL); +} + +int hc_subscription_get(hc_sock_t *s, hc_subscription_t *subscription, + hc_data_t **pdata) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_GET, OBJECT_TYPE_SUBSCRIPTION, &object, pdata); +} + +int hc_subscription_delete(hc_sock_t *s, hc_subscription_t *subscription) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + object.subscription = *subscription; + return hc_execute(s, ACTION_DELETE, OBJECT_TYPE_SUBSCRIPTION, &object, NULL); +} + +int hc_subscription_list(hc_sock_t *s, hc_data_t **pdata) { + return hc_execute(s, ACTION_LIST, OBJECT_TYPE_SUBSCRIPTION, NULL, pdata); +} + +GENERATE_FIND(subscription); +DECLARE_OBJECT_OPS(OBJECT_TYPE_SUBSCRIPTION, subscription); diff --git a/ctrl/libhicnctrl/src/objects/subscription.h b/ctrl/libhicnctrl/src/objects/subscription.h new file mode 100644 index 000000000..6eb7583c6 --- /dev/null +++ b/ctrl/libhicnctrl/src/objects/subscription.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file subscription.h + * \brief Listener. + */ + +#ifndef HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H +#define HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H + +#include "../object_vft.h" + +DECLARE_OBJECT_OPS_H(OBJECT_TYPE_SUBSCRIPTION, subscription); + +#endif /* HICNCTRL_IMPL_OBJECTS_SUBSCRIPTION_H */ diff --git a/ctrl/libhicnctrl/src/parse.c b/ctrl/libhicnctrl/src/parse.c new file mode 100644 index 000000000..ca19d8280 --- /dev/null +++ b/ctrl/libhicnctrl/src/parse.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> + +//#include <hicn/ctrl/cli.h> +#include <hicn/ctrl/action.h> +#include <hicn/util/log.h> +#include <hicn/util/sstrncpy.h> +#include <hicn/ctrl/parse.h> + +/* + * As there is no portable way to generate a va_list to use with sscanf to + * support a variable number of arguments, and no way to use a variable array + * initialize in a nested struct, we use a fixed maximum number of parameters + * + * NOTE: update sscanf accordingly + */ + +//#include "command.h" + +const char *action_to_cmd_action(hc_action_t action) { + switch (action) { + case ACTION_CREATE: + return "add"; + case ACTION_UPDATE: + return "update"; + case ACTION_DELETE: + return "remove"; + case ACTION_LIST: + return "list"; + case ACTION_SET: + return "set"; + case ACTION_SERVE: + return "serve"; + case ACTION_STORE: + return "store"; + case ACTION_CLEAR: + return "clear"; + default: + return "UNDEFINED"; + } +} + +/* + * This function now assumes all parameteres in src are received as string + */ +int parser_type_func(const parser_type_t *type, const char *src, void *dst, + void *dst2, void *dst3) { + hicn_ip_address_t addr; + char *addr_str; + char *len_str; + int len, rc; + long tmp; + + switch (type->name) { + case TYPENAME_INT: + tmp = strtol(src, NULL, 10); + if ((tmp < INT32_MIN) || (tmp > INT32_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + if (tmp > INT_MAX) return -1; + *(int *)dst = (int)tmp; + break; + case TYPENAME_UINT: + tmp = strtol(src, NULL, 10); + if ((tmp < 0) || (tmp > UINT32_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + if (tmp > UINT_MAX) return -1; + *(unsigned *)dst = (unsigned)tmp; + break; + case TYPENAME_INT16: + tmp = strtol(src, NULL, 10); + if ((tmp < INT16_MIN) || (tmp > INT16_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + *(int16_t *)dst = tmp; + break; + case TYPENAME_UINT16: + tmp = strtol(src, NULL, 10); + if ((tmp < 0) || (tmp > UINT16_MAX)) { + ERROR("Input number (%d) not matching type 'signed integer'", tmp); + return -1; + } + if ((tmp < type->integer.min) || (tmp > type->integer.max)) { + ERROR("Input number (%d) not inside range [%d, %d]", tmp, + type->integer.min, type->integer.max); + return -1; + } + *(uint16_t *)dst = tmp; + break; + case TYPENAME_STR: + rc = strcpy_s(dst, type->str.max_size, src); + if (rc != EOK) { + ERROR("Input string is too long"); + return -1; + } + break; + case TYPENAME_IP_ADDRESS: + rc = hicn_ip_address_pton((char *)src, &addr); + if (rc < 0) { + ERROR("Wrong IP address format"); + return -1; + } + + *(hicn_ip_address_t *)dst = addr; + *(int *)dst2 = hicn_ip_address_str_get_family((char *)src); + break; + case TYPENAME_ON_OFF: + if (strcmp((char *)src, "off") == 0) { + *(unsigned *)dst = 0; + } else if (strcmp((char *)src, "on") == 0) { + *(unsigned *)dst = 1; + } else { + ERROR("on/off are the only possible values"); + return -1; + } + break; + case TYPENAME_IP_PREFIX: + addr_str = strtok((char *)src, "/"); + len_str = strtok(NULL, " "); + rc = hicn_ip_address_pton((char *)src, &addr); + if (rc < 0) { + ERROR("Wrong IP address format"); + return -1; + } + len = atoi(len_str); + + *(hicn_ip_address_t *)dst = addr; + *(int *)dst2 = len; + *(int *)dst3 = hicn_ip_address_str_get_family(addr_str); + break; + case TYPENAME_ENUM: + /* Enum index from string */ + assert(type->enum_.from_str); + *(int *)dst = type->enum_.from_str((char *)src); + break; + case TYPENAME_POLICY_STATE: { + assert(IS_VALID_POLICY_TAG(type->policy_state.tag)); + policy_tag_t tag = type->policy_state.tag; + /* Format string is "%ms" */ + const char *str = *(const char **)src; + policy_tag_state_t *pts = ((policy_tag_state_t *)dst); + pts[tag].disabled = (str[0] == '!') ? 1 : 0; + pts[tag].state = policy_state_from_str(str + pts[tag].disabled); + break; + } + case TYPENAME_UNDEFINED: + default: + ERROR("Unknown format"); + return -1; + } + return 0; +} + +// NEW FUNCTION +int parse_getopt_args(const command_parser_t *parser, int argc, char *argv[], + hc_command_t *command) { + /* Loop over remaining commandline positional arguments */ + for (unsigned i = 0; i < argc; i++) { + const command_parameter_t *p = &parser->parameters[i]; + + if (parser_type_func(&p->type, argv[i], + &command->object.as_uint8 + p->offset, + &command->object.as_uint8 + p->offset2, + &command->object.as_uint8 + p->offset3) < 0) { + ERROR("Error during parsing of parameter '%s' value", p->name); + goto ERR; + } + } + if (parser->post_hook) parser->post_hook(&command->object.as_uint8); + return 0; + +ERR: + return -1; +} + +/* Build a format string to parse all params as a string */ +int parse_params(const command_parser_t *parser, const char *params_s, + hc_command_t *command) { + char fmt[1024]; + int n; + size_t size = 0; + char *pos = fmt; + /* Update MAX_PARAMETERS accordingly in command.h */ + char sscanf_params[MAX_PARAMETERS][MAX_SCANF_PARAM_LEN]; + + unsigned count = 0; + for (unsigned i = 0; i < parser->nparams; i++) { + const char *_fmt = "%s"; + n = snprintf(pos, 1024 - size, "%s", _fmt); + pos += n; + + *pos = ' '; + pos++; + + size += n + 1; + count++; + } + *pos = '\0'; + DEBUG("parser format: %s", fmt); + + int ret = sscanf(params_s, fmt, sscanf_params[0], sscanf_params[1], + sscanf_params[2], sscanf_params[3], sscanf_params[4], + sscanf_params[5], sscanf_params[6], sscanf_params[7], + sscanf_params[8], sscanf_params[9]); + if (ret != parser->nparams) { + ERROR("Parsing failed: check for string used where integer was expected"); + goto ERR; + } + + for (unsigned i = 0; i < count; i++) { + const command_parameter_t *p = &parser->parameters[i]; + if (parser_type_func(&p->type, sscanf_params[i], + &command->object.as_uint8 + p->offset, + &command->object.as_uint8 + p->offset2, + &command->object.as_uint8 + p->offset3) < 0) { + ERROR("Error during parsing of parameter '%s' value", p->name); + goto ERR; + } + } + return 0; + +ERR: + return -1; +} + +int parse(const char *cmd, hc_command_t *command) { + int nparams = 0; + char action_s[MAX_SCANF_PARAM_LEN]; + char object_s[MAX_SCANF_PARAM_LEN]; + char params_s[MAX_SCANF_PARAM_LEN]; + + // if n = 2 later, params_s is uninitialized + memset(params_s, 0, MAX_SCANF_PARAM_LEN * sizeof(char)); + + errno = 0; + int n = sscanf(cmd, "%s %s%[^\n]s", action_s, object_s, params_s); + if ((n < 2) || (n > 3)) { + if (errno != 0) perror("scanf"); + return -1; + } + + command->action = action_from_str(action_s); + command->object_type = object_type_from_str(object_s); + + if (strnlen_s(params_s, MAX_SCANF_PARAM_LEN) > 0) { + for (char *ptr = params_s; (ptr = strchr(ptr, ' ')) != NULL; ptr++) + nparams++; + } + + /* + * This checks is important even with 0 parameters as it checks whether the + * command exists. + */ + const command_parser_t *parser = + command_search(command->action, command->object_type, nparams); + if (!parser) { + ERROR("Could not find parser for command '%s %s'", action_s, object_s); + return -1; + } + + if (strnlen_s(params_s, MAX_SCANF_PARAM_LEN) > 0) { + if (parse_params(parser, params_s, command) < 0) return -1; + } + + if (parser->post_hook) parser->post_hook(&command->object.as_uint8); + return 0; +} + +int help(const char *cmd) { + int nparams = 1; + char action_s[MAX_SCANF_PARAM_LEN]; + char object_s[MAX_SCANF_PARAM_LEN]; + char params_s[MAX_SCANF_PARAM_LEN]; + hc_object_type_t object_type = OBJECT_TYPE_UNDEFINED; + hc_action_t action = ACTION_UNDEFINED; + + int n = sscanf(cmd, "help %[^\n]s", params_s); + + // No arguments provided to the help command: just list available objects + if (n != 1) goto CMD_LIST; + + // Count number of provided parameters + for (char *ptr = params_s; (ptr = strchr(ptr, ' ')) != NULL; ptr++) nparams++; + if (nparams > 2) { + fprintf(stderr, "Error: too many arguments.\n"); + return -1; + } + + // Object specified: list actions available for that object + if (nparams == 1) { + object_type = object_type_from_str(params_s); + if (object_type == OBJECT_TYPE_UNDEFINED) { + fprintf(stderr, "Error: undefined object type.\n"); + return -1; + } + + goto CMD_LIST; + } + + // Object and action specified: list detailed commands + n = sscanf(params_s, "%s %[^\n]s", object_s, action_s); + assert(n == 2); + object_type = object_type_from_str(object_s); + if (object_type == OBJECT_TYPE_UNDEFINED) { + fprintf(stderr, "Error: undefined object type.\n"); + return -1; + } + action = action_from_str(action_s); + assert(action != ACTION_UNDEFINED); + +CMD_LIST: + printf("Available commands:\n"); + command_list(object_type, action); + return 0; +} + +#if 0 // tests +/* For the tests, we will need to test all non-compliant inputs */ +const char * cmds[] = { + "add connection hicn conn1 8.8.8.8 127.0.0.1 eth0", + "add connection udp <symbolic> <remote_ip> <port> <local_ip> <port> eth0", + "add listener udp lst1 127.0.0.1 9695 eth0", + //"add face", + "add route 3 b001::/16 1", + //"add punting", + //"add strategy", + "add policy b001::/16 webex require avoid prohibit !prohibit neutral !require prefer", + "list connection", // need pluralize + "list listener", + "list face", + "list route", + "list punting", + "list strategy", + "list policy", + "remove connection 1", + "remove listener 1", + //"remove face", + "remove route 1 b001::/16", + //"remove punting", + //"remove policy", + "set debug", + "unset debug", + "set strategy b001::/16 random", // related prefixes (10 max) ? + "set strategy b001::/16 load_balancer", + "set strategy b001::/16 low_latency", + "set strategy b001::/16 replication", + "set strategy b001::/16 bestpath", + "set wldr <on|off> <connection_id>", // on-off vs unset + "cache clear", + "cache store on/off", // set/unset + "cache serve on/off", + "mapme enable on/off", + "mapme discovery on/off", + "mapme timescale 500ms", + "mapme retx 500ms", + "update connection conn1 WT", +}; + +#define array_size(x) sizeof(x) / sizeof(typeof(x[0])) +int main() +{ + for (unsigned i = 0; i < array_size(cmds); i++) { + printf("PARSING [%d] %s\n", i, cmds[i]); + if (parse(cmds[i]) < 0) { + ERROR("Could not parse command: %s\n", cmds[i]); + continue; + } + } + exit(EXIT_SUCCESS); + +ERR: + exit(EXIT_FAILURE); +} +#endif diff --git a/ctrl/libhicnctrl/src/request.c b/ctrl/libhicnctrl/src/request.c new file mode 100644 index 000000000..0e1d07b04 --- /dev/null +++ b/ctrl/libhicnctrl/src/request.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file request.c + * \brief Implementation of pending requests. + */ + +#include <assert.h> +#include <stdlib.h> + +#include <hicn/util/log.h> + +#include "request.h" + +const char *hc_request_state_str[] = { +#define _(x) [REQUEST_STATE_##x] = #x, + foreach_request_state +#undef _ +}; + +struct hc_request_s { + int seq; + hc_action_t action; + hc_object_type_t object_type; + hc_object_t *object; + +#if 0 + int (*parse)(const uint8_t *src, uint8_t *dst); +#endif + + /* Callbacks */ + hc_result_callback_t callback; + void *callback_data; + + /* Temp data used for the execution of the request */ + hc_data_t *data; + + /* Nested requests support + * + * In order to answer complex requests, involving a combination of requests to + * the forwarder, we will allow maintaining a Finite State Machine in the + * requests (and a tree of requests) + * + * The entry point for the modules will always remain the initial request, + * however, we will chain nested requests in their parent fields, and point to + * the one currently under execution in current. + * */ + hc_request_state_t state; + unsigned state_count; /* Usefor for iterative requests */ + hc_request_t *parent; + hc_request_t *current; +}; + +hc_request_t *hc_request_create(int seq, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + hc_request_t *request = malloc(sizeof(hc_request_t)); + if (!request) return NULL; + request->seq = seq; + + request->action = action; + request->object_type = object_type; + request->object = object; + + request->callback = callback; + request->callback_data = callback_data; + + request->data = NULL; + + request->state = REQUEST_STATE_INIT; + request->state_count = 0; + request->parent = NULL; + request->current = NULL; + + return request; +} + +void hc_request_free(hc_request_t *request) { free(request); } + +void hc_request_set(hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object) { + request->action = action; + request->object_type = object_type; + request->object = object; +} + +int hc_request_get_seq(const hc_request_t *request) { return request->seq; } + +hc_request_t *hc_request_get_current(hc_request_t *request) { + return request->current ? request->current : request; +} + +hc_request_t *hc_request_pop(hc_request_t *request) { + hc_request_t *current_request = hc_request_get_current(request); + hc_request_t *parent = current_request->parent; + request->current = parent; + if (parent) { + parent->data = current_request->data; + /* We only free the current_request if it was not the root */ + hc_request_free(current_request); + } + return parent; +} + +hc_request_state_t hc_request_get_state(const hc_request_t *request) { + return request->state; +} + +void hc_request_set_state(hc_request_t *request, hc_request_state_t state) { + request->state = state; +} + +int hc_request_get_state_count(const hc_request_t *request) { + return request->state_count; +} + +void hc_request_set_state_count(hc_request_t *request, unsigned count) { + request->state_count = count; +} + +hc_action_t hc_request_get_action(const hc_request_t *request) { + return request->action; +} + +hc_object_type_t hc_request_get_object_type(const hc_request_t *request) { + return request->object_type; +} + +hc_object_t *hc_request_get_object(const hc_request_t *request) { + return request->object; +} + +hc_data_t *hc_request_get_data(const hc_request_t *request) { + return request->data; +} + +void hc_request_set_data(hc_request_t *request, hc_data_t *data) { + assert(!request->data); + request->data = data; +} + +void hc_request_reset_data(hc_request_t *request) { + if (!request->data) return; + hc_data_free(request->data); + request->data = NULL; +} + +bool hc_request_is_subscription(const hc_request_t *request) { + hc_action_t action = hc_request_get_action(request); + hc_object_type_t object_type = hc_request_get_object_type(request); + return (action == ACTION_SUBSCRIBE) || + (action == ACTION_CREATE && object_type == OBJECT_TYPE_SUBSCRIPTION); +} + +bool hc_request_requires_object(const hc_request_t *request) { + hc_action_t action = hc_request_get_action(request); + return (action != ACTION_LIST) && (action != ACTION_SUBSCRIBE); +} + +void hc_request_clear_data(hc_request_t *request) { request->data = NULL; } + +void hc_request_set_complete(hc_request_t *request) { + request->state = REQUEST_STATE_COMPLETE; +} + +bool hc_request_is_complete(const hc_request_t *request) { + return request->state == REQUEST_STATE_COMPLETE; +} + +void hc_request_on_complete(hc_request_t *request) { + // request->state = REQUEST_STATE_COMPLETE; + if (!request->callback) return; + request->callback(request->data, request->callback_data); +} + +void hc_request_on_notification(hc_request_t *request) { + if (!request->callback) return; + request->callback(request->data, request->callback_data); +} + +hc_request_t *hc_request_make_subrequest(hc_request_t *request, + hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object) { + hc_request_t *sr = + hc_request_create(request->seq, action, object_type, object, + request->callback, request->callback_data); + + /* The parent is either the current one, or the request itself if NULL */ + hc_request_t *current_request = hc_request_get_current(request); + hc_request_reset_data(current_request); + sr->parent = current_request; + request->current = sr; + return sr; +} diff --git a/ctrl/libhicnctrl/src/request.h b/ctrl/libhicnctrl/src/request.h new file mode 100644 index 000000000..32f9f72b9 --- /dev/null +++ b/ctrl/libhicnctrl/src/request.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file request.h + * \brief Pending requests. + */ + +#ifndef HC_REQUEST_H +#define HC_REQUEST_H + +#include <stdbool.h> + +#include <hicn/ctrl/action.h> +#include <hicn/ctrl/callback.h> +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> + +#if 0 +typedef int (*HC_PARSE)(const uint8_t *, uint8_t *); +#endif + +#define foreach_request_state \ + _(UNDEFINED) \ + _(INIT) \ + _(CONNECTION_CREATE_LISTENER_LIST) \ + _(CONNECTION_CREATE_LISTENER_ITERATE) \ + _(CONNECTION_CREATE_LISTENER_GET) \ + _(CONNECTION_CREATE_LISTENER_VERIFY) \ + _(CONNECTION_CREATE_LISTENER_CREATE) \ + _(CONNECTION_CREATE_LISTENER_CHECK) \ + _(CONNECTION_CREATE) \ + _(CONNECTION_CREATE_N) \ + _(FACE_CREATE_CONNECTION_CREATE) \ + _(FACE_CREATE_CONNECTION_CHECK) \ + _(FACE_CREATE_CONNECTION_GET) \ + _(FACE_CREATE_CONNECTION_VERIFY) \ + _(FACE_CREATE_LISTENER_CREATE) \ + _(FACE_CREATE_LISTENER_CHECK) \ + _(FACE_LIST_CONNECTION_LIST) \ + _(ROUTE_CREATE_FACE_CREATE) \ + _(ROUTE_CREATE_FACE_CHECK) \ + _(ROUTE_CREATE) \ + _(GET_LIST) \ + _(COMPLETE) \ + _(N) + +typedef enum { +#define _(x) REQUEST_STATE_##x, + foreach_request_state +#undef _ +} hc_request_state_t; + +extern const char *hc_request_state_str[]; + +#define hc_request_state_str(x) hc_request_state_str[x] + +/* + * Internal state associated to a pending request + */ +typedef struct hc_request_s hc_request_t; + +hc_request_t *hc_request_create(int seq, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data); + +void hc_request_free(hc_request_t *request); + +void hc_request_set(hc_request_t *request, hc_action_t action, + hc_object_type_t object_type, hc_object_t *object); + +int hc_request_get_seq(const hc_request_t *request); +hc_request_t *hc_request_get_current(hc_request_t *request); +hc_request_t *hc_request_pop(hc_request_t *request); + +hc_request_state_t hc_request_get_state(const hc_request_t *request); +void hc_request_set_state(hc_request_t *request, hc_request_state_t state); + +int hc_request_get_state_count(const hc_request_t *request); +void hc_request_set_state_count(hc_request_t *request, unsigned count); + +hc_action_t hc_request_get_action(const hc_request_t *request); +hc_object_type_t hc_request_get_object_type(const hc_request_t *request); +hc_object_t *hc_request_get_object(const hc_request_t *request); +hc_data_t *hc_request_get_data(const hc_request_t *request); +void hc_request_set_data(hc_request_t *request, hc_data_t *data); +void hc_request_reset_data(hc_request_t *request); + +bool hc_request_is_subscription(const hc_request_t *request); +bool hc_request_requires_object(const hc_request_t *request); + +// do not free data which might be invalid +// XXX to be removed if we replace "ensure_data_size_and_free" functions and the +// like, with equivalent functions acting on request +void hc_request_clear_data(hc_request_t *request); + +void hc_request_set_complete(hc_request_t *request); +bool hc_request_is_complete(const hc_request_t *request); + +void hc_request_on_complete(hc_request_t *request); +void hc_request_on_notification(hc_request_t *request); + +/* + * Same seq & callbacks + */ +hc_request_t *hc_request_make_subrequest(hc_request_t *request, + hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object); + +#endif /* HC_REQUEST_H */ diff --git a/ctrl/libhicnctrl/src/route.c b/ctrl/libhicnctrl/src/route.c index f59dbce7c..71e39d7ad 100644 --- a/ctrl/libhicnctrl/src/route.c +++ b/ctrl/libhicnctrl/src/route.c @@ -25,12 +25,12 @@ #define DEFAULT_HICN_ROUTE_COST 1 struct hicn_route_s { - ip_prefix_t prefix; + hicn_ip_prefix_t prefix; face_id_t face_id; route_cost_t cost; /* Optional, 0 means no value, defaults to 1 */ }; -hicn_route_t* hicn_route_create(ip_prefix_t* prefix, face_id_t face_id, +hicn_route_t* hicn_route_create(hicn_ip_prefix_t* prefix, face_id_t face_id, route_cost_t cost) { hicn_route_t* route = malloc(sizeof(hicn_route_t)); if (!route) return NULL; @@ -52,7 +52,7 @@ void hicn_route_free(hicn_route_t* route) { free(route); } int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2) { int rc; - rc = ip_prefix_cmp(&route1->prefix, &route2->prefix); + rc = hicn_ip_prefix_cmp(&route1->prefix, &route2->prefix); if (rc != 0) return rc; return (route1->face_id > route2->face_id) ? 1 @@ -60,12 +60,12 @@ int hicn_route_cmp(const hicn_route_t* route1, const hicn_route_t* route2) { : 0; } -int hicn_route_get_prefix(const hicn_route_t* route, ip_prefix_t* prefix) { +int hicn_route_get_prefix(const hicn_route_t* route, hicn_ip_prefix_t* prefix) { *prefix = route->prefix; return 0; } -int hicn_route_set_prefix(hicn_route_t* route, const ip_prefix_t prefix) { +int hicn_route_set_prefix(hicn_route_t* route, const hicn_ip_prefix_t prefix) { route->prefix = prefix; return 0; } @@ -83,6 +83,6 @@ int hicn_route_set_cost(hicn_route_t* route, const int cost) { /* /!\ Please update constants in header file upon changes */ size_t hicn_route_snprintf(char* s, size_t size, const hicn_route_t* route) { char prefix_s[MAXSZ_IP_PREFIX]; - ip_prefix_ntop(&route->prefix, prefix_s, MAXSZ_IP_PREFIX); + hicn_ip_prefix_ntop(&route->prefix, prefix_s, MAXSZ_IP_PREFIX); return snprintf(s, size, "%s [%d]", prefix_s, route->cost); } diff --git a/ctrl/libhicnctrl/src/socket.c b/ctrl/libhicnctrl/src/socket.c new file mode 100644 index 000000000..c3636075d --- /dev/null +++ b/ctrl/libhicnctrl/src/socket.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file socket.c + * \brief Implementation of control socket. + */ + +#include <dlfcn.h> +#include <stdio.h> + +#include <hicn/ctrl/socket.h> +#include <hicn/util/log.h> + +#ifdef ANDROID +/* + * In android we do not load a module at runtime but we link the hicnlight + * implementation directly to the main library. + */ +#include "modules/hicn_light.h" +#endif /* ANDROID */ + +#include "socket_private.h" + +TYPEDEF_MAP(hc_sock_map, int, hc_request_t *, int_cmp, int_snprintf, + generic_snprintf); + +const char *forwarder_type_str[] = { +#define _(x) [FORWARDER_TYPE_##x] = #x, + foreach_forwarder_type +#undef _ +}; + +forwarder_type_t forwarder_type_from_str(const char *str) { + for (forwarder_type_t i = FORWARDER_TYPE_UNDEFINED + 1; i < FORWARDER_TYPE_N; + i++) { + if (strcasecmp(str, forwarder_type_str[i]) == 0) return i; + } + return FORWARDER_TYPE_UNDEFINED; +} + +#ifndef ANDROID +static int hc_sock_set_ops(hc_sock_t *s, const char *name, const char *url) { + char complete_name[128]; +#ifdef __APPLE__ + sprintf(complete_name, "%s.dylib", name); +#elif defined(__linux__) + snprintf(complete_name, 128, "%s.so", name); +#else +#error "System not supported for dynamic lynking" +#endif + + void *handle = 0; + const char *error = 0; + int (*initialize_module)(hc_sock_t *) = 0; + int rc = 0; + + // open module + handle = dlopen(complete_name, RTLD_LAZY); + if (!handle) { + if ((error = dlerror()) != 0) { + ERROR("%s", error); + } + goto ERR_DL; + return -1; + } + s->handle = handle; + + // get factory method + initialize_module = + (int (*)(hc_sock_t *))dlsym(handle, "hc_sock_initialize_module"); + if (!initialize_module) { + if ((error = dlerror()) != 0) { + ERROR("%s", error); + } + goto ERR_INIT; + } + initialize_module(s); + + return rc; +ERR_INIT: + dlclose(s->handle); + s->handle = NULL; +ERR_DL: + return -1; +} +#endif /* ! ANDROID */ + +int hc_sock_is_async(hc_sock_t *s) { return s->async; } + +int hc_sock_set_async(hc_sock_t *s) { + s->async = true; + return 0; +} + +hc_sock_t *hc_sock_create(forwarder_type_t forwarder, const char *url) { +#ifndef ANDROID + int rc; +#endif + + hc_sock_t *s = malloc(sizeof(hc_sock_t)); + if (!s) goto ERR_MALLOC; + +#ifdef ANDROID + assert(forwarder == HICNLIGHT); + s->data = hc_sock_light_data_create(url); + s->handle = NULL; +#else + switch (forwarder) { + case FORWARDER_TYPE_HICNLIGHT: + rc = hc_sock_set_ops(s, "hicnlightctrl_module", url); + break; + case FORWARDER_TYPE_VPP: + rc = hc_sock_set_ops(s, "vppctrl_module", url); + break; + default: + goto ERR_INIT; + } + + if (rc < 0) goto ERR_INIT; + + s->data = s->ops.create_data(url); +#endif + + if (!s->data) goto ERR_DATA; + + s->map = hc_sock_map_create(); + if (!s->map) goto ERR_MAP; + + s->async = false; + + s->seq_request = 0; + s->current_request = NULL; + + return s; + +ERR_MAP: +#ifdef ANDROID + hc_sock_light_data_free(s->data); +#else + ; // XXX VFT() free +#endif +ERR_DATA: +#ifndef ANDROID +ERR_INIT: +#endif + free(s); +ERR_MALLOC: + return NULL; +} + +void hc_sock_free(hc_sock_t *s) { + if (s->ops.disconnect) s->ops.disconnect(s); +#ifdef ANDROID + hc_sock_light_data_free(s->data); +#else + if (s->ops.free_data) s->ops.free_data(s->data); + if (s->handle) { + dlclose(s->handle); + } +#endif /* ANDROID */ + + hc_request_t **request_array = NULL; + int n = hc_sock_map_get_value_array(s->map, &request_array); + if (n < 0) { + ERROR("Could not retrieve pending request array for freeing up resources"); + } else { + for (unsigned i = 0; i < n; i++) { + hc_request_t *request = request_array[i]; + if (hc_sock_map_remove(s->map, hc_request_get_seq(request), NULL) < 0) + ERROR("[hc_sock_light_process] Error removing request from map"); + hc_request_free(request); + } + free(request_array); + } + + hc_sock_map_free(s->map); + + free(s); +} + +#if 0 +int hc_sock_get_next_seq(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } + +int hc_sock_set_nonblocking(hc_sock_t *s) { return s->hc_sock_get_next_seq(s); } + +#endif + +int hc_sock_get_fd(hc_sock_t *s) { return s->ops.get_fd(s); } + +int hc_sock_connect(hc_sock_t *s) { return s->ops.connect(s); } + +int hc_sock_get_recv_buffer(hc_sock_t *s, u8 **buffer, size_t *size) { + return s->ops.get_recv_buffer(s, buffer, size); +} +#if 0 + +int hc_sock_send(hc_sock_t *s, hc_msg_t *msg, size_t msglen, uint32_t seq) { + return s->hc_sock_send(s, msg, msglen, seq); +} + +int hc_sock_recv(hc_sock_t *s) { return s->ops.recv(s); } +#endif + +#if 0 +int hc_sock_process(hc_sock_t *s, hc_data_t **data) { + return s->hc_sock_process(s, data); +} + +int hc_sock_callback(hc_sock_t *s, hc_data_t **data) { + return s->hc_sock_callback(s, data); +} + +int hc_sock_reset(hc_sock_t *s) { return s->hc_sock_reset(s); } + +void hc_sock_increment_woff(hc_sock_t *s, size_t bytes) { + s->hc_sock_increment_woff(s, bytes); +} + +int hc_sock_prepare_send(hc_sock_t *s, hc_result_t *result, + data_callback_t complete_cb, void *complete_cb_data) { + return s->hc_sock_prepare_send(s, result, complete_cb, complete_cb_data); +} + +int hc_sock_set_recv_timeout_ms(hc_sock_t *s, long timeout_ms) { + return s->hc_sock_set_recv_timeout_ms(s, timeout_ms); +} +#endif + +hc_request_t *hc_sock_create_request(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data) { + /* Create request state */ + int seq = s->seq_request++; + hc_request_t *request = hc_request_create(seq, action, object_type, object, + callback, callback_data); + if (!request) goto ERR_MALLOC; + + hc_request_set_state(request, REQUEST_STATE_INIT); + + // Add state to map + if (hc_sock_map_add(s->map, seq, request) < 0) { + ERROR("[hc_sock_create_request] Error adding request state to map"); + goto ERR_MAP; + } + + return request; + +ERR_MAP: + free(request); +ERR_MALLOC: + return NULL; +} + +hc_request_t *hc_sock_get_request(hc_sock_t *s) { return s->current_request; } + +void hc_sock_free_request(hc_sock_t *s, hc_request_t *request, bool recursive) { + if (hc_sock_map_remove(s->map, hc_request_get_seq(request), NULL) < 0) { + ERROR("[hc_sock_free_request] Error removing request from map"); + } + if (recursive) { + hc_request_t *r = NULL; + do { + r = hc_request_pop(request); + } while (r); + } + hc_request_free(request); + s->current_request = NULL; +} + +/** + * TODO: return code: + * -1 object not found + * -2 action not found + * -3 error during serialization + * + * @return the size of the created message + */ +ssize_t hc_sock_serialize_object(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg) { + hc_serialize_t fn = (s->ops.object_vft[object_type]).serialize[action]; + if (!fn) return -1; + return fn(object, msg); +} + +int hc_sock_parse_object(hc_sock_t *s, hc_object_type_t object_type, + uint8_t *buffer, size_t size, hc_object_t *object) { + return s->ops.object_vft[object_type].parse(buffer, size, object); +} diff --git a/ctrl/libhicnctrl/src/socket_private.h b/ctrl/libhicnctrl/src/socket_private.h new file mode 100644 index 000000000..30f3bcb6e --- /dev/null +++ b/ctrl/libhicnctrl/src/socket_private.h @@ -0,0 +1,47 @@ +#ifndef HICNCTRL_SOCKET_PRIVATE_H +#define HICNCTRL_SOCKET_PRIVATE_H + +#include <hicn/util/map.h> +#include <hicn/ctrl/socket.h> + +#include "module.h" + +TYPEDEF_MAP_H(hc_sock_map, int, hc_request_t *); + +struct hc_sock_s { + int request_seq; + hc_sock_map_t *map; + + bool async; + int seq_request; + + /* + * Stores the current request being parsed in case of fragmented reception or + * analysis (as it is the case now) between header and payload + */ + hc_request_t *current_request; + + hc_sock_ops_t ops; + + void *data; + void *handle; +}; + +hc_request_t *hc_sock_create_request(hc_sock_t *s, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, + hc_result_callback_t callback, + void *callback_data); + +hc_request_t *hc_sock_get_request(hc_sock_t *s); + +void hc_sock_free_request(hc_sock_t *s, hc_request_t *request, bool recursive); + +ssize_t hc_sock_serialize_object(hc_sock_t *sock, hc_action_t action, + hc_object_type_t object_type, + hc_object_t *object, uint8_t *msg); + +int hc_sock_parse_object(hc_sock_t *sock, hc_object_type_t object_type, + uint8_t *buffer, size_t size, hc_object_t *object); + +#endif /* HICNCTRL_SOCKET_PRIVATE_H */ diff --git a/ctrl/libhicnctrl/src/test/CMakeLists.txt b/ctrl/libhicnctrl/src/test/CMakeLists.txt new file mode 100644 index 000000000..7fdff476d --- /dev/null +++ b/ctrl/libhicnctrl/src/test/CMakeLists.txt @@ -0,0 +1,51 @@ +# Copyright (c) 2021-2022 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################################################## +# Test sources +############################################################## +list(APPEND TESTS_SRC + main.cc + common.cc + test_data.cc + test_hicnlight_listener.cc + test_hicnlight_connection.cc + test_hicnlight_route.cc + ../modules/hicn_light/connection.c + ../modules/hicn_light/face.c + ../modules/hicn_light/listener.c + ../modules/hicn_light/route.c +) + +############################################################## +# Build single unit test executable and add it to test list +############################################################## +build_executable(libhicnctrl_tests + NO_INSTALL + SOURCES ${TESTS_SRC} + LINK_LIBRARIES + ${LIBRARIES} + ${LIBHICNCTRL_SHARED} + ${GTEST_LIBRARIES} + pthread + INCLUDE_DIRS + $<TARGET_PROPERTY:${LIBHICNCTRL_SHARED},INCLUDE_DIRECTORIES> + ${GTEST_INCLUDE_DIRS} + DEPENDS gtest ${LIBHICNCTRL_SHARED} + COMPONENT ${LIBHICNCTRL_COMPONENT} + DEFINITIONS ${COMPILER_DEFINITIONS} + COMPILE_OPTIONS ${COMPILER_OPTIONS} + LINK_FLAGS ${LINK_FLAGS} +) + +add_test_internal(libhicnctrl_tests) diff --git a/ctrl/libhicnctrl/src/test/common.cc b/ctrl/libhicnctrl/src/test/common.cc new file mode 100644 index 000000000..075281d2e --- /dev/null +++ b/ctrl/libhicnctrl/src/test/common.cc @@ -0,0 +1,13 @@ +#include "common.h" + +std::string dump_buffer(const char *name, uint8_t *buffer, size_t size) { + std::ostringstream oss; + oss << "const std::vector<uint8_t> " << name << " = {"; + for (size_t i = 0; i < size; i++) { + if (i > 0) oss << ", "; + oss << "0x" << std::setw(2) << std::setfill('0') << std::hex + << static_cast<int>(buffer[i]); + } + oss << "};" << std::endl; + return oss.str(); +} diff --git a/ctrl/libhicnctrl/src/test/common.h b/ctrl/libhicnctrl/src/test/common.h new file mode 100644 index 000000000..8927c86ec --- /dev/null +++ b/ctrl/libhicnctrl/src/test/common.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICNCTRL_TEST_COMMON +#define HICNCTRL_TEST_COMMON + +#define BUFSIZE 8192 + +#include <gtest/gtest.h> + +class TestHicnLight : public ::testing::Test { + protected: + TestHicnLight() {} + virtual ~TestHicnLight() {} + virtual void SetUp() {} + virtual void TearDown() {} +}; + +class TestHicnLightSerialize : public TestHicnLight { + // TestHicnLightSerialize() {} + // virtual ~TestHicnLightSerialize() {} +}; + +class TestHicnLightParse : public TestHicnLight { + // TestHicnLightParse() {} + // virtual ~TestHicnLightParse() {} +}; + +std::string dump_buffer(const char *name, uint8_t *buffer, size_t size); + +#define EXPECT_PAYLOAD_EQ(BUFFER, SIZE, PAYLOAD) \ + EXPECT_EQ(memcmp((BUFFER), &(PAYLOAD)[0], PAYLOAD.size()), 0) \ + << dump_buffer(#PAYLOAD, (BUFFER), SIZE); + +#define EXPECT_PAYLOAD_EQ_STRUCT(BUFFER, SIZE, PAYLOAD) \ + EXPECT_EQ(memcmp((BUFFER), (PAYLOAD), SIZE), 0) \ + << dump_buffer("expected:", (uint8_t *)(BUFFER), SIZE); + +#endif /* HICNCTRL_TEST_COMMON */ diff --git a/ctrl/libhicnctrl/src/test/main.cc b/ctrl/libhicnctrl/src/test/main.cc new file mode 100644 index 000000000..49cc28f66 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/main.cc @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/ctrl/libhicnctrl/src/test/test_data.cc b/ctrl/libhicnctrl/src/test/test_data.cc new file mode 100644 index 000000000..46debb0e7 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_data.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <sstream> + +extern "C" { +#include <hicn/ctrl/objects.h> +#include <hicn/ctrl/data.h> +#include <hicn/ctrl/object.h> +} + +#include "common.h" + +namespace { + +TEST_F(TestHicnLight, TestHicnLightData) { + hc_object_t object; + memset(&object, 0, sizeof(hc_object_t)); + + int rc; + hc_data_t* data = hc_data_create(OBJECT_TYPE_FACE); + hc_data_set_max_size(data, 2); + + ASSERT_EQ(hc_data_get_size(data), 0) << "Initial data size should be zero"; + + /* Try to allocate more than max */ + rc = hc_data_allocate(data, 5); + ASSERT_EQ(rc, -1) << "Allocating above max_size should fail"; + + /* Allocate room for two objects */ + rc = hc_data_allocate(data, 2); + ASSERT_EQ(rc, 0) << "Allocating data the first time should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 0) + << "Initial size should be 0 after allocation"; + + /* Try to allocate twice */ + rc = hc_data_allocate(data, 2); + ASSERT_EQ(rc, -1) << "Allocating data multiple times should fail"; + + ASSERT_EQ(hc_data_get_size(data), 0) + << "Size after failed push should remain unchanged"; + + /* Push a first object */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "First push should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 1) + << "Data size first successful push should be 1"; + + /* Push a second object */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "Second push should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 2) + << "Data size after second successful push should be 2"; + + /* Push a third object, exceeding the allocated size */ + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, -1) << "Third push on full data of size 2 should fail"; + + /* Clear */ + rc = hc_data_clear(data); + ASSERT_EQ(rc, 0) << "Clear should always succeed"; + + rc = hc_data_push(data, &object); + ASSERT_EQ(rc, 0) << "Pushing element after reallocation should succeed"; + + ASSERT_EQ(hc_data_get_size(data), 1) << "Size after first push should be one"; + // XXX + + /* Try to push an invalid object */ + // XXX so far NULL + rc = hc_data_push(data, NULL); + ASSERT_EQ(rc, -1) << "Pushing invalid element should fail"; + + ASSERT_EQ(hc_data_get_size(data), 1) + << "Size after push failure should remain unchanged"; + // XXX + + hc_data_free(data); +} + +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc new file mode 100644 index 000000000..53dd88ac3 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_connection.cc @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <sstream> + +#include <hicn/ctrl/object.h> + +#include "../modules/hicn_light/connection.h" +#include "common.h" + +namespace { + +const hc_object_t valid_connection = { + .connection = {.id = 0, + .name = {'l', 's', 't', 0}, + .interface_name = {'l', 'o', 0}, + .netdevice_type = NETDEVICE_TYPE_WIRED, + .type = FACE_TYPE_UDP, + .family = AF_INET, + .local_addr = IPV4_LOOPBACK, + .local_port = 9695, + .remote_addr = IPV4_LOOPBACK, + .remote_port = 9695, + .admin_state = FACE_STATE_UP, + .priority = 0, + .tags = POLICY_TAGS_EMPTY, + .state = FACE_STATE_UP}}; + +const std::vector<uint8_t> valid_connection_create_payload = { + /* header */ + 0xc0, 0x04, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* ip_address_t remote_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* uint16_t remote_port = 9695; */ + 0x25, 0xdf, + /* int family = AF_INET; */ + 0x02, + /* face_type_t type = FACE_TYPE_UDP_LISTENER; */ + 0x05, + /* uint8_t admin_state = FACE_STATE_UP; */ + 0x02, + /* Padding ? */ + 0x00, + /* uint32_t priority = 0; */ + 0x00, 0x00, 0x00, 0x00, + /* policy_tags_t tags; */ + 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_connection_delete_payload = { + 0xc0, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x73, 0x74, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_connection_list_payload = { + 0xc0, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.connection, &valid_connection, sizeof(hc_connection_t)); + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + // XXX debug + // THIS HAS UNINIT VALUES + std::cout << "n=" << n << std::endl; + EXPECT_EQ(memcmp(buf, buf, 60), 0); + EXPECT_EQ(memcmp(buf, buf, 62), 0); + EXPECT_EQ(memcmp(buf, buf, 64), 0); // XXX we start having issues + EXPECT_EQ(memcmp(buf, buf, 66), 0); + EXPECT_EQ(memcmp(buf, buf, 68), 0); + EXPECT_EQ(memcmp(buf, buf, 70), 0); + EXPECT_EQ(memcmp(buf, buf, 72), 0); + // XXX debug + + EXPECT_EQ(n, valid_connection_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_create_payload); +} + +// TODO +// - create with id != 0 +// - create with invalid fields, non zero-terminated strings, etc. + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.connection, &valid_connection, sizeof(hc_connection_t)); + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_connection_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeConnectionList) { + uint8_t buf[BUFSIZE]; + + hc_serialize_t fn = hicnlight_connection_module_ops.serialize[ACTION_LIST]; + size_t n = fn(NULL, buf); + + EXPECT_EQ(n, valid_connection_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_connection_list_payload); +} + +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc new file mode 100644 index 000000000..fb3df39a8 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_listener.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sstream> + +extern "C" { +#include <hicn/ctrl/object.h> +#include <hicn/ctrl/objects/listener.h> +#include "../modules/hicn_light/listener.h" +} + +#include "common.h" + +namespace { + +static const hc_object_t valid_listener = { + .listener = {.name = {'l', 's', 't', 0}, + .interface_name = {'l', 'o', 0}, + .id = 0, + .type = FACE_TYPE_UDP_LISTENER, + .family = AF_INET, + .local_addr = IPV4_LOOPBACK, + .local_port = 9695}}; + +const std::vector<uint8_t> valid_listener_create_payload = { + /* header */ + 0xc0, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* char interface_name[INTERFACE_LEN] = "lo"; */ + 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* int family = AF_INET; */ + 0x02, + /* face_type_t type = FACE_TYPE_UDP_LISTENER; */ + 0x06}; + +const std::vector<uint8_t> valid_listener_delete_payload = { + /* header */ + 0xc0, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + /* char symbolicOrListenerid[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +const std::vector<uint8_t> valid_listener_list_payload = { + /* header */ + 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* @see <hicn/ctrl/objects/listener.h> */ +const std::vector<uint8_t> valid_listener_payload = { + /* char name[SYMBOLIC_NAME_LEN] = "lst"; */ + 0x6c, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* char interface_name[INTERFACE_LEN] = "lo"; */ + 0x6c, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* ip_address_t local_addr = [padding] 127.0.0.1 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint32_t id = 0; */ + 0x00, 0x00, 0x00, 0x00, + /* uint16_t local_port = 9695; */ + 0x25, 0xdf, + /* face_type_t type = FACE_TYPE_UDP */ + 0x06, + /* int family = AF_INET; */ + 0x02}; + +TEST_F(TestHicnLightParse, TestHicnLightParseListener) { + /* Parse payload into an object */ + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + int rc = hicnlight_listener_module_ops.parse( + &valid_listener_payload[0], valid_listener_payload.size(), &obj); + EXPECT_EQ(rc, 0); + EXPECT_EQ(hc_listener_cmp(&obj.listener, &valid_listener.listener), 0); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_create_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeListenerList) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.listener, &valid_listener, sizeof(hc_listener_t)); + + hc_serialize_t fn = hicnlight_listener_module_ops.serialize[ACTION_LIST]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_listener_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_listener_list_payload); +} +} // namespace diff --git a/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc b/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc new file mode 100644 index 000000000..d48066ba2 --- /dev/null +++ b/ctrl/libhicnctrl/src/test/test_hicnlight_route.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2021-2022 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <sstream> + +extern "C" { +#include <hicn/ctrl/object.h> +#include "../modules/hicn_light/route.h" +} + +#include "common.h" + +namespace { + +const hc_object_t valid_route = { + .route = {.face_id = 1, + .face_name = {0}, // NULL, use face_id instead + .family = AF_INET, + .remote_addr = IPV4_LOOPBACK, + .len = 16, + .cost = 1, + .face = {0}}}; + +const std::vector<uint8_t> valid_route_create_payload = { + /* uint8_t message_type = REQUEST_LIGHT */ + 0xc0, + /* uint8_t command_id = COMMAND_TYPE_ROUTE_ADD */ + 0x08, + /* uint16_t length = 1 */ + 0x01, 0x00, + /* uint32_t seq_num = 0 */ + 0x00, 0x00, 0x00, 0x00, + /* char symbolic_or_connid[16] = "1\0" */ + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* hicn_ip_address_t address = {0, 0, 0, 127.0.0.1} */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* */ + 0x01, + /* */ + 0x00, + /* */ + 0x02, + /* */ + 0x10}; + +const std::vector<uint8_t> valid_route_delete_payload = { + /* uint8_t message_type = REQUEST_LIGHT */ + 0xc0, + /* uint8_t command_id = COMMAND_TYPE_ROUTE_REMOVE */ + 0x09, + /* uint16_t length = 1 */ + 0x01, 0x00, + /* uint32_t seq_num = 0 */ + 0x00, 0x00, 0x00, 0x00, + + /* char symbolic_or_connid[16] = "1\0" */ + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* hicn_ip_address_t address = {0, 0, 0, 127.0.0.1} */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x00, 0x01, + /* uint8_t family = AF_INET (2) */ + 0x02, + /* uint8_t len = 16 */ + 0x10, + /* 2-byte padding */ + 0x00, 0x00}; + +const std::vector<uint8_t> valid_route_list_payload = {0xc0, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00}; + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteCreate) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_CREATE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_create_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_create_payload); +} + +// TODO +// - create with id != 0 +// - create with invalid fields, non zero-terminated strings, etc. + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteDelete) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_DELETE]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_delete_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_delete_payload); +} + +TEST_F(TestHicnLightSerialize, TestHicnLightSerializeRouteList) { + uint8_t buf[BUFSIZE]; + + hc_object_t obj; + memset(&obj, 0, sizeof(hc_object_t)); + memcpy(&obj.route, &valid_route, sizeof(hc_route_t)); + + hc_serialize_t fn = hicnlight_route_module_ops.serialize[ACTION_LIST]; + size_t n = fn(&obj, buf); + + EXPECT_EQ(n, valid_route_list_payload.size()); + EXPECT_PAYLOAD_EQ(buf, n, valid_route_list_payload); +} + +} // namespace |