diff options
Diffstat (limited to 'ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c')
-rw-r--r-- | ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c | 3111 |
1 files changed, 3111 insertions, 0 deletions
diff --git a/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c b/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c new file mode 100644 index 000000000..a58ee909e --- /dev/null +++ b/ctrl/libhicnctrl/src/modules/hicn_light_ng_api.c @@ -0,0 +1,3111 @@ +/* + * 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) + +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); + hc_sock_request_t **request_array = NULL; + int n = hc_sock_map_get_value_array(s->map, &request_array); + if (n < 0) { + ERROR("Could not retrieve pending request array for freeing up resources"); + } else { + for (unsigned i = 0; i < n; i++) { + hc_sock_request_t *request = request_array[i]; + if (hc_sock_map_remove(s->map, request->seq, NULL) < 0) + ERROR("[hc_sock_light_process] Error removing request from map"); + hc_sock_light_request_free(request); + } + free(request_array); + } + + hc_sock_map_free(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; + + if (hc_sock_map_remove(s->map, s->cur_request->seq, NULL) < 0) { + ERROR("[hc_sock_light_mark_complete] Error removing request from map"); + } + 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 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 */ + if (hc_sock_map_get(s->map, seq, &request) < 0) { + ERROR("[hc_sock_light_process] Error searching for matching request"); + return NULL; + } + if (!request) { + ERROR("[hc_sock_light_process] 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_execute_command] 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_execute_command] Could not create request state"); + goto ERR_REQUEST; + } + + // Add state to map + if (hc_sock_map_add(s->map, seq, request) < 0) { + ERROR("[_hcng_execute_command] Error adding request state to map"); + goto ERR_MAP; + } + + 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 = 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_LIST: + assert(params->size_in != 0); + assert(params->size_out != 0); + 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 */ + if (hc_sock_map_add(s->map, seq, request) < 0) { + ERROR("[_hcng_execute_command] Error adding request state to map"); + goto ERR_MAP; + } + + 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, 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; + }; + + 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; +} + +/* 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_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 = hc_sock_map_create(); + 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; +} |