diff options
Diffstat (limited to 'hicn-light/src/hicn/core')
70 files changed, 10270 insertions, 6145 deletions
diff --git a/hicn-light/src/hicn/core/CMakeLists.txt b/hicn-light/src/hicn/core/CMakeLists.txt index 1b13be91f..94295bdf1 100644 --- a/hicn-light/src/hicn/core/CMakeLists.txt +++ b/hicn-light/src/hicn/core/CMakeLists.txt @@ -1,9 +1,9 @@ -# Copyright (c) 2017-2019 Cisco and/or its affiliates. +# Copyright (c) 2021-2022 Cisco and/or its affiliates. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at: # -# http://www.apache.org/licenses/LICENSE-2.0 +# 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, @@ -12,49 +12,59 @@ # limitations under the License. list(APPEND HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.h - ${CMAKE_CURRENT_SOURCE_DIR}/connectionState.h - ${CMAKE_CURRENT_SOURCE_DIR}/ticks.h - ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.h - ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/address.h + ${CMAKE_CURRENT_SOURCE_DIR}/address_pair.h ${CMAKE_CURRENT_SOURCE_DIR}/connection.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection_vft.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection_table.h + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.h + ${CMAKE_CURRENT_SOURCE_DIR}/fib_entry.h + ${CMAKE_CURRENT_SOURCE_DIR}/fib.h ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.h - ${CMAKE_CURRENT_SOURCE_DIR}/logger.h - ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.h - ${CMAKE_CURRENT_SOURCE_DIR}/message.h - ${CMAKE_CURRENT_SOURCE_DIR}/messagePacketType.h - ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.h - ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.h - ${CMAKE_CURRENT_SOURCE_DIR}/system.h + ${CMAKE_CURRENT_SOURCE_DIR}/listener.h + ${CMAKE_CURRENT_SOURCE_DIR}/listener_table.h + ${CMAKE_CURRENT_SOURCE_DIR}/listener_vft.h + ${CMAKE_CURRENT_SOURCE_DIR}/msgbuf.h + ${CMAKE_CURRENT_SOURCE_DIR}/msgbuf_pool.h + ${CMAKE_CURRENT_SOURCE_DIR}/packet_cache.h + ${CMAKE_CURRENT_SOURCE_DIR}/pit.h + ${CMAKE_CURRENT_SOURCE_DIR}/policy_stats.h + ${CMAKE_CURRENT_SOURCE_DIR}/strategy.h + ${CMAKE_CURRENT_SOURCE_DIR}/strategy_vft.h + ${CMAKE_CURRENT_SOURCE_DIR}/subscription.h + ${CMAKE_CURRENT_SOURCE_DIR}/ticks.h + + # ${CMAKE_CURRENT_SOURCE_DIR}/system.h ${CMAKE_CURRENT_SOURCE_DIR}/mapme.h ${CMAKE_CURRENT_SOURCE_DIR}/wldr.h - ${CMAKE_CURRENT_SOURCE_DIR}/messageHandler.h - ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.h - ${CMAKE_CURRENT_SOURCE_DIR}/name.h + ${CMAKE_CURRENT_SOURCE_DIR}/nexthops.h ) list(APPEND SOURCE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/address.c + ${CMAKE_CURRENT_SOURCE_DIR}/address_pair.c ${CMAKE_CURRENT_SOURCE_DIR}/connection.c - ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.c - ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.c - ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.c - ${CMAKE_CURRENT_SOURCE_DIR}/dispatcher.c + ${CMAKE_CURRENT_SOURCE_DIR}/connection_table.c + ${CMAKE_CURRENT_SOURCE_DIR}/connection_vft.c + ${CMAKE_CURRENT_SOURCE_DIR}/content_store.c + ${CMAKE_CURRENT_SOURCE_DIR}/fib.c + ${CMAKE_CURRENT_SOURCE_DIR}/fib_entry.c ${CMAKE_CURRENT_SOURCE_DIR}/forwarder.c - ${CMAKE_CURRENT_SOURCE_DIR}/logger.c - ${CMAKE_CURRENT_SOURCE_DIR}/message.c - ${CMAKE_CURRENT_SOURCE_DIR}/numberSet.c - ${CMAKE_CURRENT_SOURCE_DIR}/streamBuffer.c + ${CMAKE_CURRENT_SOURCE_DIR}/listener.c + ${CMAKE_CURRENT_SOURCE_DIR}/listener_table.c + ${CMAKE_CURRENT_SOURCE_DIR}/listener_vft.c ${CMAKE_CURRENT_SOURCE_DIR}/mapme.c + ${CMAKE_CURRENT_SOURCE_DIR}/msgbuf.c + ${CMAKE_CURRENT_SOURCE_DIR}/msgbuf_pool.c + ${CMAKE_CURRENT_SOURCE_DIR}/nexthops.c + ${CMAKE_CURRENT_SOURCE_DIR}/packet_cache.c + ${CMAKE_CURRENT_SOURCE_DIR}/pit.c + ${CMAKE_CURRENT_SOURCE_DIR}/policy_stats.c + ${CMAKE_CURRENT_SOURCE_DIR}/strategy.c + ${CMAKE_CURRENT_SOURCE_DIR}/strategy_vft.c + ${CMAKE_CURRENT_SOURCE_DIR}/subscription.c ${CMAKE_CURRENT_SOURCE_DIR}/wldr.c - ${CMAKE_CURRENT_SOURCE_DIR}/nameBitvector.c - ${CMAKE_CURRENT_SOURCE_DIR}/name.c ) set(SOURCE_FILES ${SOURCE_FILES} PARENT_SCOPE) set(HEADER_FILES ${HEADER_FILES} PARENT_SCOPE) - -set(TO_INSTALL_HEADER_FILES - ${TO_INSTALL_HEADER_FILES} - ${HEADER_FILES} - PARENT_SCOPE -) diff --git a/hicn-light/src/hicn/core/address.c b/hicn-light/src/hicn/core/address.c new file mode 100644 index 000000000..65664fa17 --- /dev/null +++ b/hicn-light/src/hicn/core/address.c @@ -0,0 +1,66 @@ +/* + * 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 address.c + * \brief Implementation of Address + */ + +#include <hicn/core/address.h> +#include <hicn/util/sstrncpy.h> + +int address_from_ip_port(address_t *address, int family, + hicn_ip_address_t *addr, uint16_t port) { + switch (family) { + case AF_INET: + *address = ADDRESS4(ntohl(addr->v4.as_inaddr.s_addr), ntohs(port)); + break; + case AF_INET6: + *address = ADDRESS6(addr->v6.as_in6addr, ntohs(port)); + break; + default: + return -1; + } + return 0; +} + +const char *_address_family_str[] = { + [AF_INET] = "AF_INET", + [AF_INET6] = "AF_INET6", +}; + +int address_to_string(const address_t *address, char *addr_str, int *port) { + const int SUCCESS = 0; + char port_str[NI_MAXSERV]; + struct sockaddr_storage addr = address->as_ss; + socklen_t addr_len = sizeof(addr); + + int result = + getnameinfo((struct sockaddr *)&addr, addr_len, addr_str, NI_MAXHOST, + port_str, sizeof(port_str), NI_NUMERICHOST | NI_NUMERICSERV); + + if (result != SUCCESS) { + strcpy_s(addr_str, NI_MAXHOST, "N/A"); + if (port != NULL) *port = -1; + return result; + } + + if (port != NULL) *port = atoi(port_str); + return SUCCESS; +} + +address_t _ADDRESS4_LOCALHOST(uint16_t port) { + return ADDRESS4_LOCALHOST(port); +} diff --git a/hicn-light/src/hicn/core/address.h b/hicn-light/src/hicn/core/address.h new file mode 100644 index 000000000..38cd1e87c --- /dev/null +++ b/hicn-light/src/hicn/core/address.h @@ -0,0 +1,141 @@ +/* + * 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 address.h + * \brief Address + */ + +#ifndef HICNLIGHT_ADDRESS_H +#define HICNLIGHT_ADDRESS_H + +#include <netinet/in.h> + +#include <string.h> // memcmp +#include <hicn/util/ip_address.h> +#include <netinet/in.h> + +typedef union { + struct sockaddr_in as_sin; + struct sockaddr_in6 as_sin6; + struct sockaddr as_sa; + struct sockaddr_storage as_ss; +} address_t; + +#define address_equals(a, b) (memcmp(a, b, sizeof(address_t)) == 0) + +#define address_family(address) ((address)->as_ss.ss_family) + +#define address4(address) ((struct sockaddr_in *)(address)) +#define address6(address) ((struct sockaddr_in6 *)(address)) +#define address_sa(address) ((struct sockaddr *)(address)) + +#define address4_ip(address) (address4(address)->sin_addr) +#define address6_ip(address) (address6(address)->sin6_addr) +#define address6_scope_id(address) (address4_ptr(address)->sin6_scope_id) + +#define address_socklen(address) \ + (((address)->as_ss.ss_family == AF_INET) ? sizeof(struct sockaddr_in) \ + : sizeof(struct sockaddr_in6)) + +#define address4_is_local(address) \ + ((htonl((address4_ip(address)).s_addr) & 0xFF000000) == 0x7F000000) + +static inline bool _address6_is_local(struct sockaddr_in6 *sin6) { + return IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr); +} + +#define address6_is_local(address) (_address6_is_local(address6(address))) + +#define address_is_local(address) \ + ((address)->as_ss.ss_family == AF_INET) ? address4_is_local(address) \ + : address6_is_local(address) + +int address_from_ip_port(address_t *address, int family, + hicn_ip_address_t *addr, uint16_t port); + +static inline address_t ADDRESS4(in_addr_t in_addr, int port) { + address_t address = { + .as_sin = + { + .sin_family = AF_INET, + .sin_port = htons(port), + .sin_addr = {.s_addr = htonl(in_addr)}, + }, + }; + + return address; +} + +#define ADDRESS4_LOCALHOST(port) ADDRESS4(INADDR_LOOPBACK, (port)) + +/** + * @brief Helper function to avoid macro expansion in c++ tests. Wrapper around + * 'ADDRESS4_LOCALHOST()'. + * + * @param port + * @return address_t + */ +address_t _ADDRESS4_LOCALHOST(uint16_t port); + +#define ADDRESS4_ANY(port) ADDRESS4(INADDR_ANY, (port)) + +static inline address_t ADDRESS6(struct in6_addr in_addr, int port) { + address_t address = { + .as_sin6 = + { + .sin6_family = AF_INET6, + .sin6_port = htons(port), + .sin6_addr = in_addr, + }, + }; + + return address; +} + +#define ADDRESS6_LOCALHOST(port) ADDRESS6(in6addr_loopback, (port)) +#define ADDRESS6_ANY(port) ADDRESS6((struct in6_addr)IN6ADDR_ANY_INIT, port) + +#define ADDRESS_ANY(family, port) \ + ((family == AF_INET) ? ADDRESS4_ANY(port) : ADDRESS6_ANY(port)) + +extern const char *_address_family_str[]; + +#define address_family_str(address) \ + (_address_family_str[address_family(address)]) + +#define address4_empty(address) (address4_ip(address).s_addr == 0) +#define address6_empty(address) \ + (memcmp(address6_ip(address).s6_addr, &in6addr_any, \ + sizeof(struct in6_addr)) == 0) +#define address_empty(address) \ + (address_family(address) == AF_INET ? address4_empty(address) \ + : address6_empty(address)) + +/** + * @brief Return the string representation and the port of the IP address + * provided. + * + * @param[in] address Address to obtain the string representation from. + * @param[in, out] buffer String to store the string representation of the + * address. It contains "N/A" in case of failure (see return value). + * @param[in, out] port Integer to store the the port. It contains -1 in case of + * failure (see return value). If NULL, it will not be used to store the port. + * @return int 0 if success, failure otherwise. + */ + +int address_to_string(const address_t *address, char *buffer, int *port); + +#endif /* HICNLIGHT_ADDRESS_H */ diff --git a/hicn-light/src/hicn/core/address_pair.c b/hicn-light/src/hicn/core/address_pair.c new file mode 100644 index 000000000..c4f8b397b --- /dev/null +++ b/hicn-light/src/hicn/core/address_pair.c @@ -0,0 +1,43 @@ +/* + * 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 address_pair.c + * \brief Implementation of Address pair + */ + +#include "address_pair.h" + +address_pair_t address_pair_factory(address_t local, address_t remote) { + address_pair_t pair; + memset(&pair, 0, sizeof(address_pair_t)); + + pair.local = local; + pair.remote = remote; + return pair; +} + +int address_pair_from_ip_port(address_pair_t* pair, int family, + hicn_ip_address_t* local_addr, + uint16_t local_port, + hicn_ip_address_t* remote_addr, + uint16_t remote_port) { + memset(pair, 0, sizeof(*pair)); + if (address_from_ip_port(&pair->local, family, local_addr, local_port) < 0) + return -1; + if (address_from_ip_port(&pair->remote, family, remote_addr, remote_port) < 0) + return -1; + return 0; +} diff --git a/hicn-light/src/hicn/core/address_pair.h b/hicn-light/src/hicn/core/address_pair.h new file mode 100644 index 000000000..b2872ad35 --- /dev/null +++ b/hicn-light/src/hicn/core/address_pair.h @@ -0,0 +1,66 @@ +/* + * 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 address_pair.h + * \brief Address pair + */ + +#ifndef HICNLIGHT_ADDRESS_PAIR_H +#define HICNLIGHT_ADDRESS_PAIR_H + +#include <hicn/util/ip_address.h> + +#include "address.h" + +typedef struct { + address_t local; + address_t remote; +} address_pair_t; + +/** + * @brief Create an address pair starting from local and remote addresses. + * + * @param local The local address to use in the pair + * @param remote The remote address to use in the pair + * @return address_pair_t The address pair created + */ +address_pair_t address_pair_factory(address_t local, address_t remote); + +int address_pair_from_ip_port(address_pair_t* pair, int family, + hicn_ip_address_t* local_addr, + uint16_t local_port, + hicn_ip_address_t* remote_addr, + uint16_t remote_port); + +static inline int address_pair_equals(const address_pair_t* pair1, + const address_pair_t* pair2) { + return address_equals(&pair1->local, &pair2->local) && + address_equals(&pair1->remote, &pair2->remote); +} + +#define address_pair_get_local(pair) (&(pair)->local) +#define address_pair_get_remote(pair) (&(pair)->remote) + +#define address_pair_get_local_family(pair) \ + (address_family(address_pair_get_local(pair))) +#define address_pair_get_remote_family(pair) \ + (address_family(address_pair_get_remote(pair))) +#define address_pair_get_family(pair) address_pair_get_local_family(pair) + +#define address_pair_is_valid(pair) \ + (address_pair_get_local_family(pair) == address_pair_get_remote_family(pair)) + +#endif /* HICNLIGHT_ADDRESS_PAIR_H */ diff --git a/hicn-light/src/hicn/core/connection.c b/hicn-light/src/hicn/core/connection.c index c2ac71a5f..a9d632d52 100644 --- a/hicn-light/src/hicn/core/connection.c +++ b/hicn-light/src/hicn/core/connection.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2022 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,347 +13,358 @@ * limitations under the License. */ -#include <limits.h> -#include <hicn/hicn-light/config.h> -#include <stdio.h> +/** + * @file connection.c + * @brief Implementation of hICN connections + */ -#include <hicn/core/connection.h> -#include <hicn/core/connectionState.h> -#include <hicn/core/messageHandler.h> -#include <hicn/core/ticks.h> -#include <hicn/core/wldr.h> -#include <hicn/io/addressPair.h> -#include <hicn/io/ioOperations.h> +#include <assert.h> -#include <parc/algol/parc_Memory.h> -#include <parc/assert/parc_Assert.h> -#ifdef WITH_POLICY +#include <hicn/core/forwarder.h> +#include <hicn/core/listener.h> +#include <hicn/core/wldr.h> #include <hicn/policy.h> -#endif /* WITH_POLICY */ +#include <hicn/util/log.h> + +#include "connection.h" +#include "connection_vft.h" + +// This is called by configuration +connection_t *connection_create(face_type_t type, const char *name, + const address_pair_t *pair, + const forwarder_t *forwarder) { + assert(face_type_is_valid(type)); + assert(pair); + assert(forwarder); + + /* initialized so that gcc-9 does not complain */ + face_type_t listener_type = FACE_TYPE_UNDEFINED; + switch (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: + return NULL; /* Not implemented */ + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_UDP_LISTENER: + case FACE_TYPE_TCP_LISTENER: + case FACE_TYPE_UNDEFINED: + case FACE_TYPE_N: + return NULL; + } -struct connection { + listener_table_t *ltable = forwarder_get_listener_table(forwarder); + listener_key_t key = listener_key_factory(pair->local, listener_type); - const AddressPair *addressPair; - IoOperations *ops; + listener_t *listener = listener_table_get_by_key(ltable, &key); + if (!listener) { + WITH_ERROR({ + char addr_str[NI_MAXHOST]; + int port; + address_to_string(&pair->local, addr_str, &port); + ERROR("Could not find listener to match address %s:%d", addr_str, port); + }) - unsigned refCount; + return NULL; + } - unsigned counter; + connection_table_t *table = + forwarder_get_connection_table(listener->forwarder); + unsigned connection_id = listener_create_connection(listener, name, pair); + if (!connection_id_is_valid(connection_id)) return NULL; + return connection_table_at(table, connection_id); +} - bool wldrAutoStart; // if true, wldr can be set automatically - // by default this value is set to true. - // if wldr is activated using a command (config - // file/hicnLightControl) this value is set to false so - // that a base station can not disable wldr at the client - Wldr *wldr; +netdevice_type_t connection_get_netdevice_type(const char *interface_name) { + if (strncmp(interface_name, "lo", 2) == 0) { + return NETDEVICE_TYPE_LOOPBACK; + } + if ((strncmp(interface_name, "eth", 3) == 0) || + (strncmp(interface_name, "en", 2) == 0)) { + /* eth* en* enx* */ + return NETDEVICE_TYPE_WIRED; + } + if (strncmp(interface_name, "wl", 2) == 0) { + /* wlan* wlp* wlx* */ + return NETDEVICE_TYPE_WIFI; + } + if (strncmp(interface_name, "rmnet_ipa", 9) == 0) { + /* Qualcomm IPA driver */ + return NETDEVICE_TYPE_UNDEFINED; + } + if ((strncmp(interface_name, "rmnet", 5) == 0) || + (strncmp(interface_name, "rev_rmnet", 9) == 0) || + (strncmp(interface_name, "ccmni", 5) == 0)) { + /* + * rmnet* (Qualcomm) ccmni* (MediaTek) + */ + return NETDEVICE_TYPE_CELLULAR; + } + /* usb0 might be cellular (eg Zenfone2) */ + /* what about tethering */ + /* tun* dummy* ... */ + /* bnet* pan* hci* for bluetooth */ + return NETDEVICE_TYPE_UNDEFINED; +} +/** + * @brief Initializes a connection + * + * @param [out] connection - Allocated connection buffer (eg. from pool) to be + * initialized. + * @param [in] forwarder - forwarder_t to which the connection is associated. + * This parameter needs to be non-NULL for connections receiving packets, such + * as TCP connections which are very close to UDP listeners, and unlike + * bound UDP connections). + * @param [in] fd - A fd specific to the connection, or 0 if the connection + * should inherit the fd of the listener. + * @return 0 if no error, -1 otherwise + */ +int connection_initialize(connection_t *connection, face_type_t type, + const char *name, const char *interface_name, int fd, + const address_pair_t *pair, bool local, + unsigned connection_id, listener_t *listener) { + int rc; + + assert(connection); + /* Interface name can be NULL eg always for TCP connnections */ + assert(pair); + // assert(address_pair_is_valid(pair)); TODO: local addr in the pair is not + // initialized for now + + if (fd == 0) WARN("Connection is not connected"); + + *connection = (connection_t){ + .id = connection_id, + .name = strdup(name), + .type = type, + .interface_name = strdup(interface_name), + .pair = *pair, + .fd = ((fd != 0) ? fd : listener_get_fd(listener)), + .connected = (fd != 0), + // .up = true, + .local = local, + // XXX UDP should start UP, TCP DOWN until remove side answer ? + .state = FACE_STATE_UNDEFINED, + .admin_state = FACE_STATE_UP, #ifdef WITH_POLICY - policy_tags_t tags; + .priority = 0, #endif /* WITH_POLICY */ -}; - -Connection *connection_Create(IoOperations *ops) { - parcAssertNotNull(ops, "Parameter ops must be non-null"); - Connection *conn = parcMemory_AllocateAndClear(sizeof(Connection)); - parcAssertNotNull(conn, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Connection)); - conn->addressPair = ioOperations_GetAddressPair(ops); - conn->ops = ops; - conn->refCount = 1; - conn->wldr = NULL; + .listener = listener, + .closed = false, - conn->wldrAutoStart = true; - conn->counter = 0; + /* WLDR */ + .wldr = NULL, + .wldr_autostart = true, + }; + connection->stats.conn_id = connection_id; - /* By default, a connection will aim at the UP state */ - connection_SetAdminState(conn, CONNECTION_STATE_UP); + connection->interface_type = + connection_get_netdevice_type(connection->interface_name); #ifdef WITH_POLICY - conn->tags = POLICY_TAGS_EMPTY; -#endif /* WITH_POLICY */ + connection_clear_tags(connection); + switch (connection->interface_type) { +#if 0 + case NETDEVICE_TYPE_LOOPBACK: + connection_add_tag(connection, POLICY_TAG_LOOPBACK); + break; +#endif + case NETDEVICE_TYPE_WIRED: + connection_add_tag(connection, POLICY_TAG_WIRED); + break; + case NETDEVICE_TYPE_WIFI: + connection_add_tag(connection, POLICY_TAG_WIFI); + break; + case NETDEVICE_TYPE_CELLULAR: + connection_add_tag(connection, POLICY_TAG_CELLULAR); + default: + break; + } +#endif - return conn; -} + connection->data = + malloc(connection_vft[get_protocol(connection->type)]->data_size); + if (!connection->data) goto ERR_DATA; -Connection *connection_Acquire(Connection *connection) { - parcAssertNotNull(connection, "Parameter conn must be non-null"); - connection->refCount++; - return connection; -} + assert(connection_has_valid_id(connection)); -void connection_Release(Connection **connectionPtr) { - parcAssertNotNull(connectionPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*connectionPtr, - "Parameter must dereference to non-null pointer"); - Connection *conn = *connectionPtr; - - parcAssertTrue( - conn->refCount > 0, - "Invalid state, connection reference count should be positive, got 0."); - conn->refCount--; - if (conn->refCount == 0) { - // don't destroy addressPair, its part of ops. - ioOperations_Release(&conn->ops); - if (conn->wldr != NULL) { - wldr_Destroy(&(conn->wldr)); - } - parcMemory_Deallocate((void **)&conn); + rc = connection_vft[get_protocol(connection->type)]->initialize(connection); + if (rc < 0) { + goto ERR_VFT; } - *connectionPtr = NULL; -} - -bool connection_Send(const Connection *conn, Message *message) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - parcAssertNotNull(message, "Parameter message must be non-null"); - if (ioOperations_IsUp(conn->ops)) { - if (message_GetType(message) == MessagePacketType_ContentObject) { - uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); - message_UpdatePathLabel(message, connectionId); + if (connection->connected) { + /* + * The file descriptor is created by the listener. We assume for now that + * all connections get their own fd, and we have to register it. + * + * TODO the connection has no more read callback, so we call the one from + * the listener. + */ + loop_fd_event_create(&connection->event_data, MAIN_LOOP, fd, listener, + (fd_callback_t)listener_read_callback, connection->id, + NULL); + + if (!connection->event_data) { + goto ERR_REGISTER_FD; } - if (conn->wldr != NULL) { - wldr_SetLabel(conn->wldr, message); - } else { - message_ResetWldrLabel(message); + + if (loop_fd_event_register(connection->event_data) < 0) { + goto ERR_REGISTER_FD; } - return ioOperations_Send(conn->ops, NULL, message); } - return false; -} - -bool connection_SendIOVBuffer(const Connection *conn, struct iovec *msg, - size_t size) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - parcAssertNotNull(msg, "Parameter message must be non-null"); - - return ioOperations_SendIOVBuffer(conn->ops, msg, size); -} -bool connection_SendBuffer(const Connection *conn, u8 * buffer, size_t length) -{ - struct iovec iov[1]; - iov[0].iov_base = buffer; - iov[0].iov_len = length; - return connection_SendIOVBuffer(conn, iov, 1); -} - -void connection_Probe(Connection *conn, uint8_t * probe) { - ioOperations_SendProbe(conn->ops, probe); -} - -void connection_HandleProbe(Connection *conn, uint8_t *probe){ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - parcAssertNotNull(probe, "Parameter pkt must be non-null"); - - if(messageHandler_IsInterest(probe)){ - messageHandler_CreateProbeReply(probe, HF_INET6_TCP); - ioOperations_SendProbe(conn->ops, probe); + return 0; + +ERR_REGISTER_FD: +#ifndef _WIN32 + close(fd); +#else + closesocket(fd); +#endif +ERR_VFT: + free(connection->data); +ERR_DATA: + free(connection->interface_name); + free(connection->name); + return -1; +} + +int connection_finalize(connection_t *connection) { + assert(connection); + assert(connection_has_valid_type(connection)); + + if (connection->connected) { + loop_event_unregister(connection->event_data); + loop_event_free(connection->event_data); } -} - -IoOperations *connection_GetIoOperations(const Connection *conn) { - return conn->ops; -} - -unsigned connection_GetConnectionId(const Connection *conn) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - return ioOperations_GetConnectionId(conn->ops); -} - -const AddressPair *connection_GetAddressPair(const Connection *conn) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - return ioOperations_GetAddressPair(conn->ops); -} - -bool connection_IsUp(const Connection *conn) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) return false; - return ioOperations_IsUp(conn->ops); -} -bool connection_IsLocal(const Connection *conn) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - return ioOperations_IsLocal(conn->ops); -} + if (connection->fd != 0) { // Only if connected socket +#ifndef _WIN32 + close(connection->fd); +#else + closesocket(connection->fd); +#endif + } -const void *connection_Class(const Connection *conn) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - return ioOperations_Class(conn->ops); -} + if (connection->wldr) wldr_free(connection->wldr); -bool connection_ReSend(const Connection *conn, Message *message, - bool notification) { - parcAssertNotNull(conn, "Parameter conn must be non-null"); - parcAssertNotNull(message, "Parameter message must be non-null"); - bool res = false; - - if (connection_IsUp(conn)) { - // here the wldr header is alreay set: this message is a retransmission or a - // notification - - // we need to recompiute the path lable since we always store a pointer to - // the same message if this message will be sent again to someonelse, the - // new path label must be computed starting from the orignal labelorignal - // label. Notice that we heve the same problem in case of PIT aggregation. - // That case is handled insied the MessageProcessor. This is specific to - // WLDR retransmittions. This is done only for data packets - - if (message_GetType(message) == MessagePacketType_ContentObject) { - uint8_t connectionId = (uint8_t)connection_GetConnectionId(conn); - uint32_t old_path_label = message_GetPathLabel(message); - message_UpdatePathLabel(message, connectionId); - - res = ioOperations_Send(conn->ops, NULL, message); - - message_SetPathLabel(message, old_path_label); - } else { - res = ioOperations_Send(conn->ops, NULL, message); - } - } + connection_vft[get_protocol(connection->type)]->finalize(connection); - if (notification) { - // the notification is never destroyed - message_Release(&message); - } + if (connection->data) free(connection->data); + connection->data = NULL; + if (connection->interface_name) free(connection->interface_name); + connection->interface_name = NULL; + if (connection->name) free(connection->name); + connection->name = NULL; - return res; + return 0; } -void connection_AllowWldrAutoStart(Connection *conn, bool allow) { - conn->wldrAutoStart = allow; -} +bool connection_send_packet(const connection_t *connection, + const uint8_t *packet, size_t size) { + assert(connection); + assert(face_type_is_valid(connection->type)); + assert(packet); -void connection_EnableWldr(Connection *conn) { - if (!connection_IsLocal(conn)) { - if (conn->wldr == NULL) { - printf("----------------- enable wldr\n"); - conn->wldr = wldr_Init(); - } - } + return connection_vft[get_protocol(connection->type)]->send_packet( + connection, packet, size); } -void connection_DisableWldr(Connection *conn) { - if (!connection_IsLocal(conn)) { - if (conn->wldr != NULL) { - printf("----------------- disable wldr\n"); - wldr_Destroy(&(conn->wldr)); - conn->wldr = NULL; - } - } +bool _connection_send(connection_t *connection, msgbuf_t *msgbuf, bool queue) { + return connection_vft[get_protocol(connection->type)]->send(connection, + msgbuf, queue); } -bool connection_HasWldr(const Connection *conn) { - if (conn->wldr == NULL) { - return false; - } else { - return true; - } +bool connection_flush(connection_t *connection) { + return connection_vft[get_protocol(connection->type)]->flush(connection); } -bool connection_WldrAutoStartAllowed(const Connection *conn) { - return conn->wldrAutoStart; -} +bool connection_send(connection_t *connection, off_t msgbuf_id, bool queue) { + assert(connection); + assert(msgbuf_id_is_valid(msgbuf_id)); -void connection_DetectLosses(Connection *conn, Message *message) { - if (conn->wldr != NULL) wldr_DetectLosses(conn->wldr, conn, message); -} + // if (!connection_is_up(connection)) + // return false; -void connection_HandleWldrNotification(Connection *conn, Message *message) { - if (conn->wldr != NULL) - wldr_HandleWldrNotification(conn->wldr, conn, message); -} + const listener_t *listener = connection_get_listener(connection); + const forwarder_t *forwarder = listener_get_forwarder(listener); + const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); -connection_state_t connection_GetState(const Connection *conn) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return CONNECTION_STATE_UNDEFINED; - return ioOperations_GetState(conn->ops); -} +#if 0 + if (connection->wldr) + wldr_set_label(connection->wldr, msgbuf); + else + msgbuf_reset_wldr_label(msgbuf); +#endif -void connection_SetState(Connection *conn, connection_state_t state) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return; - ioOperations_SetState(conn->ops, state); + return _connection_send(connection, msgbuf, queue); } -connection_state_t connection_GetAdminState(const Connection *conn) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return CONNECTION_STATE_UNDEFINED; - return ioOperations_GetAdminState(conn->ops); -} +/* + * here the wldr header is alreay set: this message is a retransmission or a + * notification + * + * we need to recompute the path label since we always store a pointer to + * the same message if this message will be sent again to someone else, the + * new path label must be computed starting from the orignal label. Note + * that we heve the same problem in case of PIT aggregation. That case is + * handled inside the MessageProcessor. This is specific to WLDR + * retransmittions. This is done only for data packets + */ +bool connection_resend(connection_t *connection, msgbuf_t *msgbuf, + bool notification) { + assert(connection); + assert(msgbuf); -void connection_SetAdminState(Connection *conn, connection_state_t admin_state) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return; - if ((admin_state != CONNECTION_STATE_UP) && (admin_state != CONNECTION_STATE_DOWN)) - return; - ioOperations_SetAdminState(conn->ops, admin_state); -} + bool ret = false; -#ifdef WITH_POLICY -uint32_t connection_GetPriority(const Connection *conn) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return 0; - return ioOperations_GetPriority(conn->ops); -} + if (!connection_is_up(connection)) return ret; -void connection_SetPriority(Connection *conn, uint32_t priority) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return; - ioOperations_SetPriority(conn->ops, priority); -} -#endif /* WITH_POLICY */ + ret = _connection_send(connection, msgbuf, false); /* no queueing */ -const char * connection_GetInterfaceName(const Connection * conn) -{ - parcAssertNotNull(conn, "Parameter conn must be non-null"); - if (!conn->ops) - return NULL; - return ioOperations_GetInterfaceName(conn->ops); + return ret; } -#ifdef WITH_POLICY +/* WLDR */ -void connection_AddTag(Connection *conn, policy_tag_t tag) -{ - policy_tags_add(&conn->tags, tag); +void connection_wldr_allow_autostart(connection_t *connection, bool value) { + connection->wldr_autostart = value; } -void connection_RemoveTag(Connection *conn, policy_tag_t tag) -{ - policy_tags_remove(&conn->tags, tag); +bool connection_wldr_autostart_is_allowed(const connection_t *connection) { + return connection->wldr_autostart; } -policy_tags_t connection_GetTags(const Connection *conn) -{ - return conn->tags; +void connection_wldr_enable(connection_t *connection, bool value) { + if (connection_is_local(connection)) return; + if (value) { + if (connection->wldr) return; + connection->wldr = wldr_create(); + } else { + if (!connection->wldr) return; + wldr_free(connection->wldr); + } } -void connection_SetTags(Connection *conn, policy_tags_t tags) -{ - conn->tags = tags; +bool connection_has_wldr(const connection_t *connection) { + return !!connection->wldr; } -void connection_ClearTags(Connection *conn) -{ - conn->tags = POLICY_TAGS_EMPTY; +void connection_wldr_detect_losses(const connection_t *connection, + const msgbuf_t *msgbuf) { + if (!connection->wldr) return; + wldr_detect_losses(connection->wldr, connection, msgbuf); } -int connection_HasTag(const Connection *conn, policy_tag_t tag) -{ - return policy_tags_has(conn->tags, tag); +void connection_wldr_handle_notification(const connection_t *connection, + const msgbuf_t *msgbuf) { + if (!connection->wldr) return; + wldr_handle_notification(connection->wldr, connection, msgbuf); } - -#endif /* WITH_POLICY */ diff --git a/hicn-light/src/hicn/core/connection.h b/hicn-light/src/hicn/core/connection.h index b6513ea1a..e177e2039 100644 --- a/hicn-light/src/hicn/core/connection.h +++ b/hicn-light/src/hicn/core/connection.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2023 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -15,184 +15,230 @@ /** * @file connection.h - * @brief Wrapper for different types of connections - * - * A connection wraps a specific set of {@link IoOperations}. Those operations - * allow for input and output. Connections get stored in the Connection Table. - * + * @brief hICN connections */ -#ifndef connection_h -#define connection_h -#include <hicn/hicn-light/config.h> -#include <hicn/core/connectionState.h> -#include <hicn/io/ioOperations.h> -#include <hicn/utils/address.h> +#ifndef HICNLIGHT_CONNECTION_H +#define HICNLIGHT_CONNECTION_H -#ifdef WITH_MAPME -typedef enum { - CONNECTION_EVENT_CREATE, - CONNECTION_EVENT_DELETE, - CONNECTION_EVENT_UPDATE, - CONNECTION_EVENT_SET_UP, - CONNECTION_EVENT_SET_DOWN, - CONNECTION_EVENT_PRIORITY_CHANGED, - CONNECTION_EVENT_TAGS_CHANGED, -} connection_event_t; +#include <hicn/face.h> -#endif /* WITH_MAPME */ +#include "address_pair.h" +#include "listener.h" +#include "msgbuf.h" #ifdef WITH_POLICY #include <hicn/policy.h> #endif /* WITH_POLICY */ -struct connection; -typedef struct connection Connection; +#define CONNECTION_ID_UNDEFINED INVALID_FACE_ID -/** - * Creates a connection object. - */ -Connection *connection_Create(IoOperations *ops); +#define foreach_connection_event \ + _(UNDEFINED) \ + _(CREATE) \ + _(DELETE) \ + _(UPDATE) \ + _(SET_UP) \ + _(SET_DOWN) \ + _(PRIORITY_CHANGED) \ + _(TAGS_CHANGED) \ + _(N) -/** - * @function connection_Release - * @abstract Releases a reference count, destroying on last release - * @discussion - * Only frees the memory on the final reference count. The pointer will - * always be NULL'd. - */ -void connection_Release(Connection **connectionPtr); +typedef enum { +#define _(x) CONNECTION_EVENT_##x, + foreach_connection_event +#undef _ +} connection_event_t; -/** - * @function connection_Acquire - * @abstract A reference counted copy. - * @discussion - * A shallow copy, they share the same memory. - */ -Connection *connection_Acquire(Connection *connection); +struct wldr_s; + +typedef struct { + unsigned id; + char* name; + char* interface_name; + netdevice_type_t interface_type; + face_type_t type; + address_pair_t pair; + // bool up; + bool local; + face_state_t state; + face_state_t admin_state; +#ifdef WITH_POLICY + policy_tags_t tags; + uint32_t priority; +#endif /* WITH_POLICY */ -/** - * @function connection_Send - * @abstract Sends the message on the connection - * @return true if message sent, false if connection not up - */ -bool connection_Send(const Connection *conn, Message *message); + int fd; + bool connected; // true if the connection is connected and has its own fd + event_t* event_data; + + void* data; + + listener_t* listener; + // struct forwarder_s * forwarder; // recv only + bool closed; + + /* WLDR */ + + bool wldr_autostart; + /* + * if true, wldr can be set automatically by default this value is set to + * true. if wldr is activated using a command (config file/hicnLightControl) + * this value is set to false so that a base station can not disable wldr at + * the client. + */ + struct wldr_s* wldr; + + connection_stats_t stats; +} connection_t; + +#if 1 +#define connection_get_id(C) ((C)->id) +#define connection_id_is_valid(ID) (ID != CONNECTION_ID_UNDEFINED) +#define connection_get_name(C) ((C)->name) +#define connection_get_type(C) ((C)->type) +#define connection_has_valid_id(C) \ + (connection_id_is_valid(connection_get_id(C))) +#define connection_has_valid_type(C) \ + (face_type_is_valid(connection_get_type(C))) +#define connection_get_pair(C) (&(C)->pair) +#define connection_get_local(C) (address_pair_get_local(connection_get_pair(C))) +#define connection_get_remote(C) \ + (address_pair_get_remote(connection_get_pair(C))) +#define connection_get_local(C) (address_pair_get_local(connection_get_pair(C))) +#define connection_get_remote(C) \ + (address_pair_get_remote(connection_get_pair(C))) +#define connection_is_up(C) ((C)->state == FACE_STATE_UP) +#define connection_is_closed(C) ((C)->closed == true) +#define connection_is_local(C) ((C)->local) +#define connection_get_state(C) ((C)->state) +#define connection_set_state(C, STATE) (C)->state = STATE +#define connection_get_admin_state(C) ((C)->admin_state) +#define connection_set_admin_state(C, STATE) (C)->admin_state = STATE +#define connection_get_interface_name(C) ((C)->interface_name) +#define connection_get_interface_type(C) ((C)->interface_type) -/** - * @function connection_SendIOVBuffer - * @abstract Sends an IOV buffer - */ -bool connection_SendIOVBuffer(const Connection *conn, struct iovec *msg, - size_t size); +#ifdef WITH_POLICY +#define connection_get_priority(C) ((C)->priority) +#define connection_set_priority(C, PRIORITY) (C)->priority = PRIORITY +#define connection_get_tags(C) ((C)->tags) +#define connection_set_tags(C, TAGS) (C)->tags = TAGS +#define connection_has_tag(C, TAG) policy_tags_has(connection_get_tags(C), TAG) +#define connection_add_tag(C, TAG) policy_tags_add(&connection_get_tags(C), TAG) +#define connection_remove_tag(C, TAG) \ + do { \ + policy_tags_t _conn_var(tags); \ + _conn_var(tags) = connection_get_tags(C); \ + policy_tags_remove(_conn_var(tags), (TAG)); \ + connection_set_tags((C), _conn_var(tags)); \ + } while (0) +#define connection_clear_tags(C) connection_set_tags(C, POLICY_TAGS_EMPTY) -/** - * @function connection_SendBuffer - * @abstract Sends a buffer - */ -bool connection_SendBuffer(const Connection *conn, u8 * buffer, size_t length); +#endif /* WITH_POLICY */ -/** - * Return the `IoOperations` instance associated with the specified `Connection` - * instance. - * @param [in] connection The allocated connection - * @return a pointer to the IoOperations instance associated by th specified - * connection. - */ -IoOperations *connection_GetIoOperations(const Connection *conn); +#else -/** - * Returns the unique identifier of the connection - * Calls the underlying IoOperations to fetch the connection id - * @param [in] connection The allocated connection - * @return unsigned The unique connection id - */ -unsigned connection_GetConnectionId(const Connection *conn); +/* Accessors */ +static inline unsigned connection_get_id(const connection_t* connection); -/** - * Returns the (remote, local) address pair that describes the connection - * @param [in] connection The allocated connection - * @return non-null The connection's remote and local address - * @return null Should never return NULL - */ -const AddressPair *connection_GetAddressPair(const Connection *conn); +#define connection_id_is_valid(id) (id != CONNECTION_ID_UNDEFINED) +#define connection_has_valid_id(C) (connection_id_is_valid(connection_get_id(C)) -/** - * Checks if the connection is in the "up" state - * @param [in] connection The allocated connection - * @return true The connection is in the "up" state - * @return false The connection is not in the "up" state - */ -bool connection_IsUp(const Connection *conn); +static inline char* connection_get_name(const connection_t* connection); -/** - * Checks if the connection is to a Local/Loopback address - * - * A local connection is PF_LOCAL (PF_UNIX) and a loopback connection is - * 127.0.0.0/8 or ::1 for IPv6. - * - * @param [in] connection The allocated connection - * - * @retval true The connection is local or loopback - * @retval false The connection is not local or loopback - */ -bool connection_IsLocal(const Connection *conn); +static inline face_type_t connection_get_type(const connection_t* connection); -/** - * Returns an opaque pointer representing the class of the Io Operations - * - * Returns an opaque pointer that an implementation can use to detect if - * the connection is based on that class. - * - * @param [in] conn The Connection to analyze - * - * @return non-null An opaque pointer for each concrete implementation - */ -const void *connection_Class(const Connection *conn); +static inline address_pair_t* connection_get_pair( + const connection_t* connection); -bool connection_ReSend(const Connection *conn, Message *message, - bool notification); +#define connection_get_local(C) (address_pair_get_local(connection_get_pair(C))) +#define connection_get_remote(C) (address_pair_remote(connection_get_pair(C))) -void connection_Probe(Connection *conn, uint8_t *probe); +static inline bool connection_is_up(const connection_t* connection); -void connection_HandleProbe(Connection *conn, uint8_t *message); +static inline bool connection_is_local(const connection_t* connection); -void connection_AllowWldrAutoStart(Connection *conn, bool allow); +static inline face_state_t connection_get_state(const connection_t* connection); -void connection_EnableWldr(Connection *conn); +static inline void connection_set_state(connection_t* connection, + face_state_t state); -void connection_DisableWldr(Connection *conn); +static inline face_state_t connection_get_admin_state( + const connection_t* connection); -bool connection_HasWldr(const Connection *conn); +static inline void connection_set_admin_state(connection_t* connection, + face_state_t state); -bool connection_WldrAutoStartAllowed(const Connection *conn); +static inline const char* connection_get_interface_name( + const connection_t* connection); -void connection_DetectLosses(Connection *conn, Message *message); +#ifdef WITH_POLICY -void connection_HandleWldrNotification(Connection *conn, Message *message); +static inline uint32_t connection_get_priority(const connection_t* connection); -connection_state_t connection_GetState(const Connection *conn); +static inline void connection_set_priority(connection_t* connection, + uint32_t priority); -void connection_SetState(Connection *conn, connection_state_t state); +static inline policy_tags_t connection_get_tags(const connection_t* connection); -connection_state_t connection_GetAdminState(const Connection *conn); +static inline void connection_set_tags(connection_t* connection, + policy_tags_t tags); -void connection_SetAdminState(Connection *conn, connection_state_t admin_state); +#define connection_has_tag(C, TAG) policy_tags_has(connection_get_tags(C), TAG) -#ifdef WITH_POLICY -uint32_t connection_GetPriority(const Connection *conn); +#define connection_add_tag(C, TAG) policy_tags_add(connection_get_tags(X), TAG) -void connection_SetPriority(Connection *conn, uint32_t priority); -#endif /* WITH_POLICY */ +#define connection_remove_tag(C, TAG) \ + do { \ + policy_tags_t _conn_var(tags); \ + _conn_var(tags) = connection_get_tags(C); \ + policy_tags_remove(_conn_var(tags), (TAG)); \ + connection_set_tags((C), _conn_var(tags)); \ + } while (0) -const char * connection_GetInterfaceName(const Connection * conn); +#define connection_clear_tags(C) connection_set_tags(C, POLICY_TAGS_EMPTY) -#ifdef WITH_POLICY -void connection_AddTag(Connection *conn, policy_tag_t tag); -void connection_RemoveTag(Connection *conn, policy_tag_t tag); -policy_tags_t connection_GetTags(const Connection *conn); -void connection_SetTags(Connection *conn, policy_tags_t tags); -void connection_ClearTags(Connection *conn); -int connection_HasTag(const Connection *conn, policy_tag_t tag); #endif /* WITH_POLICY */ -#endif // connection_h +#endif + +connection_t* connection_create(face_type_t type, const char* name, + const address_pair_t* pair, + const struct forwarder_s* forwarder); + +int connection_initialize(connection_t* connection, face_type_t type, + const char* name, const char* interface_name, int fd, + const address_pair_t* pair, bool local, + unsigned connection_id, listener_t* listener); + +int connection_finalize(connection_t* connection); + +bool connection_send_packet(const connection_t* connection, + const uint8_t* packet, size_t size); + +bool connection_flush(connection_t* connection); + +bool connection_send(connection_t* connection, off_t msgbuf_id, bool queue); + +size_t connection_process_buffer(connection_t* connection, + const uint8_t* buffer, size_t size); + +/* WLDR */ + +void connection_wldr_allow_autostart(connection_t* connection, bool value); + +bool connection_wldr_autostart_is_allowed(const connection_t* connection); + +void connection_wldr_enable(connection_t* connection, bool value); + +bool connection_has_wldr(const connection_t* connection); + +void connection_wldr_detect_losses(const connection_t* connection, + const msgbuf_t* msgbuf); + +void connection_wldr_handle_notification(const connection_t* connection, + const msgbuf_t* msgbuf); + +#define connection_get_listener(connection) (connection->listener) + +#endif /* HICNLIGHT_CONNECTION_H */ diff --git a/hicn-light/src/hicn/core/connectionList.c b/hicn-light/src/hicn/core/connectionList.c deleted file mode 100644 index d51a9aad5..000000000 --- a/hicn-light/src/hicn/core/connectionList.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <hicn/hicn-light/config.h> -#include <stdio.h> - -#include <parc/algol/parc_ArrayList.h> -#include <parc/algol/parc_Memory.h> - -#include <parc/assert/parc_Assert.h> -#include <hicn/core/connectionList.h> - -struct connection_list { - PARCArrayList *listOfConnections; -}; - -static void connectionList_ArrayDestroyer(void **voidPtr) { - Connection **entryPtr = (Connection **)voidPtr; - connection_Release(entryPtr); -} - -ConnectionList *connectionList_Create() { - ConnectionList *list = parcMemory_AllocateAndClear(sizeof(ConnectionList)); - parcAssertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(ConnectionList)); - list->listOfConnections = parcArrayList_Create(connectionList_ArrayDestroyer); - return list; -} - -void connectionList_Destroy(ConnectionList **listPtr) { - parcAssertNotNull(listPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*listPtr, "Parameter must dereference to non-null pointer"); - ConnectionList *list = *listPtr; - parcArrayList_Destroy(&list->listOfConnections); - parcMemory_Deallocate((void **)&list); - *listPtr = NULL; -} - -void connectionList_Append(ConnectionList *list, Connection *entry) { - parcAssertNotNull(list, "Parameter list must be non-null"); - parcAssertNotNull(entry, "Parameter entry must be non-null"); - - parcArrayList_Add(list->listOfConnections, connection_Acquire(entry)); -} - -size_t connectionList_Length(const ConnectionList *list) { - parcAssertNotNull(list, "Parameter list must be non-null"); - return parcArrayList_Size(list->listOfConnections); -} - -Connection *connectionList_Get(ConnectionList *list, size_t index) { - parcAssertNotNull(list, "Parameter list must be non-null"); - Connection *original = - (Connection *)parcArrayList_Get(list->listOfConnections, index); - return original; -} diff --git a/hicn-light/src/hicn/core/connectionList.h b/hicn-light/src/hicn/core/connectionList.h deleted file mode 100644 index fbba9f6d8..000000000 --- a/hicn-light/src/hicn/core/connectionList.h +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file connectionList.h - * @brief A typesafe list of Connection objects - * - * <#Detailed Description#> - * - */ - -#ifndef connectionList_h -#define connectionList_h - -struct connection_list; -typedef struct connection_list ConnectionList; - -#include <hicn/core/connection.h> - -/** - * Creates a lis of Connection - * - * @return non-null An allocated list - * @return null An error - */ -ConnectionList *connectionList_Create(void); - -/** - * Destroys the list and all objects inside it - */ -void connectionList_Destroy(ConnectionList **listPtr); - -/** - * @function connectionList_Append - * @abstract Adds a connection entry to the list. - * @discussion - * Acquires a reference to the passed entry and stores it in the list. - */ -void connectionList_Append(ConnectionList *list, Connection *entry); - -/** - * Returns the number of items on the list - * @param [in] list The allocated list to check - * @return number The number of items on the list - */ -size_t connectionList_Length(const ConnectionList *list); - -/** - * @function connectionList_Get - * @abstract Returns the connection entry. - * @discussion - * Caller must not destroy the returned value. If you will store the - * entry in your own data structure, you should acquire your own reference. - * Will assert if you go beyond the end of the list. - * - */ -Connection *connectionList_Get(ConnectionList *list, size_t index); -#endif // connectionList_h diff --git a/hicn-light/src/hicn/core/connectionManager.c b/hicn-light/src/hicn/core/connectionManager.c deleted file mode 100644 index 709f0902a..000000000 --- a/hicn-light/src/hicn/core/connectionManager.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * The Connection Manager sets itself up as a listener to the Messenger so it - * can take action based on system events. - * - * The Connection Manager queues and then processes in a later time slice the - * messages. - * - */ - -#include <hicn/hicn-light/config.h> -#include <hicn/core/connectionManager.h> -#include <hicn/core/forwarder.h> -#include <hicn/messenger/messenger.h> -#include <hicn/messenger/messengerRecipient.h> -#include <hicn/messenger/missiveDeque.h> -#include <stdio.h> - -#include <parc/algol/parc_Memory.h> - -#include <parc/assert/parc_Assert.h> - -struct connection_manager { - Forwarder *forwarder; - Logger *logger; - - MessengerRecipient *messengerRecipient; - - // we queue missives as they come in to process in our own - // event timeslice - MissiveDeque *missiveQueue; - - // for deferred queue processing - PARCEventTimer *timerEvent; -}; - -/** - * Receives missives from the messenger, queues them, and schedules our - * execution - * - * We defer processing of missives to a later time slice - */ -static void connectionManager_MessengerCallback(MessengerRecipient *recipient, - Missive *missive); - -/** - * Event callback - * - * This is our main run loop to process our queue of messages. It is scheduled - * in {@link connectionManager_MessengerCallback} when the queue becomes - * non-empty. - * - * When we are called here, we have exclusive use of the system, so we will not - * create any message loops - * - * @param [in] fd unused, required for compliance with function prototype - * @param [in] which_event unused, required for compliance with function - * prototype - * @param [in] connManagerVoidPtr A void* to ConnectionManager - * - */ -static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, - void *connManagerVoidPtr); - -static void connectionManager_ProcessClosedMissive( - ConnectionManager *connManager, const Missive *missive); - -// ======================================================== -// Public API - -ConnectionManager *connectionManager_Create(Forwarder *forwarder) { - ConnectionManager *connManager = - parcMemory_AllocateAndClear(sizeof(ConnectionManager)); - parcAssertNotNull(connManager, - "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(ConnectionManager)); - connManager->forwarder = forwarder; - connManager->missiveQueue = missiveDeque_Create(); - connManager->logger = logger_Acquire(forwarder_GetLogger(forwarder)); - - Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); - - // creates the timer, but does not start it - PARCEventScheduler *base = - dispatcher_GetEventScheduler(forwarder_GetDispatcher(forwarder)); - connManager->timerEvent = parcEventTimer_Create( - base, 0, connectionManager_ProcessQueue, connManager); - - connManager->messengerRecipient = messengerRecipient_Create( - connManager, connectionManager_MessengerCallback); - messenger_Register(messenger, connManager->messengerRecipient); - return connManager; -} - -void connectionManager_Destroy(ConnectionManager **managerPtr) { - parcAssertNotNull(managerPtr, "Double pointer must be non-null"); - parcAssertNotNull(*managerPtr, "Double pointer must dereference to non-null"); - - ConnectionManager *connManager = *managerPtr; - - Messenger *messenger = forwarder_GetMessenger(connManager->forwarder); - parcEventTimer_Destroy(&(connManager->timerEvent)); - messenger_Unregister(messenger, connManager->messengerRecipient); - messengerRecipient_Destroy(&connManager->messengerRecipient); - missiveDeque_Release(&connManager->missiveQueue); - logger_Release(&connManager->logger); - - parcMemory_Deallocate((void **)&connManager); - *managerPtr = NULL; -} - -// ======================================================== -// Internal Functions - -static void connectionManager_MessengerCallback(MessengerRecipient *recipient, - Missive *missive) { - ConnectionManager *connManager = - messengerRecipient_GetRecipientContext(recipient); - - // we do not release our reference count, we store it until later - // We are called with our own reference, so we do not need to acquire the - // missive here. - missiveDeque_Append(connManager->missiveQueue, missive); - - if (missiveDeque_Size(connManager->missiveQueue) == 1) { - // When it becomes non-empty, schedule {@link - // connectionManager_ProcessQueue} - struct timeval immediateTimeout = {0, 0}; - parcEventTimer_Start(connManager->timerEvent, &immediateTimeout); - } -} - -static void connectionManager_ProcessQueue(int fd, PARCEventType which_event, - void *connManagerVoidPtr) { - ConnectionManager *connManager = (ConnectionManager *)connManagerVoidPtr; - - Missive *missive; - while ((missive = missiveDeque_RemoveFirst(connManager->missiveQueue)) != - NULL) { - switch (missive_GetType(missive)) { - case MissiveType_ConnectionCreate: - // hook to signal that a new connection was created - break; - case MissiveType_ConnectionUp: - // hook to signal that a new connection is up - break; - case MissiveType_ConnectionDown: - // hook to signal that a connection is down - break; - case MissiveType_ConnectionClosed: - connectionManager_ProcessClosedMissive(connManager, missive); - break; - case MissiveType_ConnectionDestroyed: - // hook to signal that a connection was destroyed - break; - default: - parcTrapUnexpectedState("Missive %p of unknown type: %d", - (void *)missive, missive_GetType(missive)); - } - missive_Release(&missive); - } -} - -static void connectionManager_ProcessClosedMissive( - ConnectionManager *connManager, const Missive *missive) { - logger_Log(connManager->logger, LoggerFacility_Core, PARCLogLevel_Debug, - __func__, "Processing CLOSED message for connid %u", - missive_GetConnectionId(missive)); - - ConnectionTable *table = forwarder_GetConnectionTable(connManager->forwarder); - const Connection *conn = - connectionTable_FindById(table, missive_GetConnectionId(missive)); - - if (conn) { - // this will destroy the connection if its the last reference count - connectionTable_Remove(table, conn); - - // remove from FIB - forwarder_RemoveConnectionIdFromRoutes(connManager->forwarder, - missive_GetConnectionId(missive)); - } -} diff --git a/hicn-light/src/hicn/core/connectionManager.h b/hicn-light/src/hicn/core/connectionManager.h deleted file mode 100644 index 34fee8717..000000000 --- a/hicn-light/src/hicn/core/connectionManager.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file connectionManager.h - * @brief The connection manager handles connection events, such as going down - * - * The connection manager listens to the event notification system. Based on - * those events, the connection manager will take specific actions. This is - * expected to be a singleton instantiated by the forwarder. - * - */ - -#ifndef connectionManager_h -#define connectionManager_h - -#include <hicn/core/forwarder.h> - -struct connection_manager; -typedef struct connection_manager ConnectionManager; - -ConnectionManager *connectionManager_Create(Forwarder *forwarder); - -void connectionManager_Destroy(ConnectionManager **managerPtr); -#endif // connectionManager_h diff --git a/hicn-light/src/hicn/core/connectionTable.c b/hicn-light/src/hicn/core/connectionTable.c deleted file mode 100644 index f8589c12b..000000000 --- a/hicn-light/src/hicn/core/connectionTable.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @header ConnectionTable - * @abstract Records all the current connections and references to them - * @discussion - * - */ - -#ifndef _WIN32 -#include <unistd.h> -#endif -#include <hicn/hicn-light/config.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <parc/assert/parc_Assert.h> - -#include <parc/algol/parc_ArrayList.h> -#include <parc/algol/parc_Hash.h> -#include <parc/algol/parc_HashCodeTable.h> -#include <parc/algol/parc_Memory.h> -#include <parc/algol/parc_TreeRedBlack.h> -#include <hicn/core/connectionTable.h> -#include <hicn/io/addressPair.h> - -struct connection_table { - // The main storage table that has a Destroy method. - // The key is an unsigned int pointer. We use an unsigned int pointer - // because we want to be able to lookup by the id alone, and not have to - // have the IoOperations everywhere. - PARCHashCodeTable *storageTableById; - - // The key is a AddressPair - // It does not have a destroy method for the data or key, - // as they are derived from the storage table. - PARCHashCodeTable *indexByAddressPair; - - // An iterable stucture organized by connection id. The keys and - // values are the same pointers as in storageTableById, so there - // are no destructors in the tree. - // The only reason to keep this tree is so we have an iterable list - // of connections, which the hash table does not give us. - PARCTreeRedBlack *listById; -}; - -static bool connectionTable_ConnectionIdEquals(const void *keyA, - const void *keyB) { - unsigned idA = *((unsigned *)keyA); - unsigned idB = *((unsigned *)keyB); - return (idA == idB); -} - -static int connectionTable_ConnectionIdCompare(const void *keyA, - const void *keyB) { - unsigned idA = *((unsigned *)keyA); - unsigned idB = *((unsigned *)keyB); - if (idA < idB) { - return -1; - } - if (idA > idB) { - return +1; - } - return 0; -} - -static bool connectionTable_AddressPairEquals(const void *keyA, - const void *keyB) { - const AddressPair *pairA = (const AddressPair *)keyA; - const AddressPair *pairB = (const AddressPair *)keyB; - - return addressPair_Equals(pairA, pairB); -} - -static HashCodeType connectionTable_ConnectionIdHashCode(const void *keyA) { - unsigned idA = *((unsigned *)keyA); - return parcHash32_Int32(idA); -} - -static HashCodeType connectionTable_AddressPairHashCode(const void *keyA) { - const AddressPair *pairA = (const AddressPair *)keyA; - return addressPair_HashCode(pairA); -} - -static void connectionTable_ConnectionIdDestroyer(void **dataPtr) { - unsigned *idA = (unsigned *)*dataPtr; - parcMemory_Deallocate((void **)&idA); - *dataPtr = NULL; -} - -static void connectionTable_ConnectionDestroyer(void **dataPtr) { - connection_Release((Connection **)dataPtr); -} - -ConnectionTable *connectionTable_Create() { - size_t initialSize = 16384; - - ConnectionTable *conntable = - parcMemory_AllocateAndClear(sizeof(ConnectionTable)); - parcAssertNotNull(conntable, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(ConnectionTable)); - - conntable->storageTableById = parcHashCodeTable_Create_Size( - connectionTable_ConnectionIdEquals, connectionTable_ConnectionIdHashCode, - connectionTable_ConnectionIdDestroyer, - connectionTable_ConnectionDestroyer, initialSize); - - // no key or data destroyer, this is an index into storageByid. - conntable->indexByAddressPair = parcHashCodeTable_Create_Size( - connectionTable_AddressPairEquals, connectionTable_AddressPairHashCode, - NULL, NULL, initialSize); - - conntable->listById = - parcTreeRedBlack_Create(connectionTable_ConnectionIdCompare, - NULL, // key free - NULL, // key copy - NULL, // value equals - NULL, // value free - NULL); // value copy - - return conntable; -} - -void connectionTable_Destroy(ConnectionTable **conntablePtr) { - parcAssertNotNull(conntablePtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*conntablePtr, - "Parameter must dereference to non-null pointer"); - - ConnectionTable *conntable = *conntablePtr; - - parcTreeRedBlack_Destroy(&conntable->listById); - parcHashCodeTable_Destroy(&conntable->indexByAddressPair); - parcHashCodeTable_Destroy(&conntable->storageTableById); - parcMemory_Deallocate((void **)&conntable); - *conntablePtr = NULL; -} - -/** - * @function connectionTable_Add - * @abstract Add a connection, takes ownership of memory - */ -void connectionTable_Add(ConnectionTable *table, Connection *connection) { - parcAssertNotNull(table, "Parameter table must be non-null"); - parcAssertNotNull(connection, "Parameter connection must be non-null"); - - unsigned *connectionIdKey = parcMemory_Allocate(sizeof(unsigned)); - parcAssertNotNull(connectionIdKey, "parcMemory_Allocate(%zu) returned NULL", - sizeof(unsigned)); - *connectionIdKey = connection_GetConnectionId(connection); - - if (parcHashCodeTable_Add(table->storageTableById, connectionIdKey, - connection)) { - parcHashCodeTable_Add(table->indexByAddressPair, - (void *)connection_GetAddressPair(connection), - connection); - parcTreeRedBlack_Insert(table->listById, connectionIdKey, connection); - } else { - parcTrapUnexpectedState( - "Could not add connection id %u -- is it a duplicate?", - *connectionIdKey); - } -} - -/** - * @function connectionTable_Remove - * @abstract Removes the connection, calling Destroy on our copy - */ -void connectionTable_Remove(ConnectionTable *table, - const Connection *connection) { - parcAssertNotNull(table, "Parameter table must be non-null"); - parcAssertNotNull(connection, "Parameter connection must be non-null"); - - unsigned connid = connection_GetConnectionId(connection); - - parcTreeRedBlack_Remove(table->listById, &connid); - parcHashCodeTable_Del(table->indexByAddressPair, - connection_GetAddressPair(connection)); - parcHashCodeTable_Del(table->storageTableById, &connid); -} - -void connectionTable_RemoveById(ConnectionTable *table, unsigned id) { - parcAssertNotNull(table, "Parameter table must be non-null"); - const Connection *connection = connectionTable_FindById(table, id); - if (connection) { - connectionTable_Remove(table, connection); - } -} - -const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, - const AddressPair *pair) { - parcAssertNotNull(table, "Parameter table must be non-null"); - return (Connection *)parcHashCodeTable_Get(table->indexByAddressPair, pair); -} - -const Connection *connectionTable_FindById(const ConnectionTable *table, - unsigned id) { - parcAssertNotNull(table, "Parameter table must be non-null"); - return (Connection *)parcHashCodeTable_Get(table->storageTableById, &id); -} - -ConnectionList *connectionTable_GetEntries(const ConnectionTable *table) { - parcAssertNotNull(table, "Parameter table must be non-null"); - ConnectionList *list = connectionList_Create(); - - PARCArrayList *values = parcTreeRedBlack_Values(table->listById); - for (size_t i = 0; i < parcArrayList_Size(values); i++) { - Connection *original = parcArrayList_Get(values, i); - connectionList_Append(list, original); - } - parcArrayList_Destroy(&values); - return list; -} diff --git a/hicn-light/src/hicn/core/connectionTable.h b/hicn-light/src/hicn/core/connectionTable.h deleted file mode 100644 index 548ef8e6e..000000000 --- a/hicn-light/src/hicn/core/connectionTable.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - */ - -#ifndef connectionTable_h -#define connectionTable_h - -#include <hicn/core/connection.h> -#include <hicn/core/connectionList.h> -#include <hicn/io/addressPair.h> -#include <hicn/io/ioOperations.h> - -struct connection_table; -typedef struct connection_table ConnectionTable; - -/** - * Creates an empty connection table - */ -ConnectionTable *connectionTable_Create(void); - -/** - * Destroys the connection table - * This will release the reference to all connections stored in the connection - * table. - * @param [in,out] conntablePtr Pointer to the allocated connection table, will - * be NULL'd - */ -void connectionTable_Destroy(ConnectionTable **conntablePtr); - -/** - * @function connectionTable_Add - * @abstract Add a connection, takes ownership of memory - */ -void connectionTable_Add(ConnectionTable *table, Connection *connection); - -/** - * @function connectionTable_Remove - * @abstract Removes the connection, calling Destroy on our copy - */ -void connectionTable_Remove(ConnectionTable *table, - const Connection *connection); - -/** - * Removes a connection from the connection table - * - * Looks up a connection by its connection ID and removes it from the connection - * table. Removing the connection will call connection_Release() on the - * connection object. - * - * @param [in] table The allocated connection table - * @param [in] id The connection ID - */ -void connectionTable_RemoveById(ConnectionTable *table, unsigned id); - -/** - * Lookup a connection by the (local, remote) addres pair - * - * @param [in] table The allocated connection table - * @param [in] pair The address pair to match, based on the inner values of the - * local and remote addresses - * - * @retval non-null The matched conneciton - * @retval null No match found or error - */ -const Connection *connectionTable_FindByAddressPair(ConnectionTable *table, - const AddressPair *pair); - -/** - * @function connectionTable_FindById - * @abstract Find a connection by its numeric id. - * @return NULL if not found - */ -const Connection *connectionTable_FindById(const ConnectionTable *table, unsigned id); - -/** - * @function connectionTable_GetEntries - * @abstract Returns a list of connections. They are reference counted copies - * from the table. - * @discussion - * An allocated list of connections in the table. Each list entry is a - * reference counted copy of the connection in the table, thus they are "live" - * objects. - */ -ConnectionList *connectionTable_GetEntries(const ConnectionTable *table); -#endif // connectionTable_h diff --git a/hicn-light/src/hicn/core/connection_table.c b/hicn-light/src/hicn/core/connection_table.c new file mode 100644 index 000000000..a6b114c01 --- /dev/null +++ b/hicn-light/src/hicn/core/connection_table.c @@ -0,0 +1,240 @@ +/* + * 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_table.c + * \brief Implementation of hICN connection table + */ + +#include <hicn/util/log.h> +#include <hicn/util/sstrncpy.h> + +#include "connection.h" +#include "connection_table.h" + +/* This is only used as a hint for first allocation, as the table is resizeable + */ +#define DEFAULT_CONNECTION_TABLE_SIZE 64 + +typedef struct { + char name[SYMBOLIC_NAME_LEN]; +} name_key_t; + +connection_table_t *_connection_table_create(size_t init_size, + size_t max_size) { + if (init_size == 0) init_size = DEFAULT_CONNECTION_TABLE_SIZE; + + connection_table_t *table = malloc(sizeof(connection_table_t)); + if (!table) return NULL; + + table->max_size = max_size; + + /* Initialize indices */ + table->id_by_pair = kh_init_ct_pair(); + table->pair_keys = slab_create(address_pair_t, SLAB_INIT_SIZE); + table->id_by_name = kh_init_ct_name(); + table->name_keys = slab_create(name_key_t, SLAB_INIT_SIZE); + + /* + * We start by allocating a reasonably-sized pool, as this will eventually + * be resized if needed. + */ + pool_init(table->connections, init_size, 0); + + return table; +} + +void connection_table_free(connection_table_t *table) { + unsigned conn_id; + kh_foreach_value(table->id_by_name, conn_id, { + connection_t *connection = connection_table_get_by_id(table, conn_id); + const char *name = connection_get_name(connection); + + INFO("Removing connection %s [%d]", name, connection->fd); + connection_finalize(connection); + }); + + kh_destroy_ct_pair(table->id_by_pair); + slab_free(table->pair_keys); + kh_destroy_ct_name(table->id_by_name); + slab_free(table->name_keys); + + pool_free(table->connections); + free(table); +} + +connection_t *connection_table_allocate(const connection_table_t *table, + const address_pair_t *pair, + const char *name) { + connection_t *conn = NULL; + pool_get(table->connections, conn); + if (!conn) return NULL; + +#ifdef __APPLE__ + // set __uint8_t sin_len to 0 + uint8_t *ptr = (uint8_t *)address_pair_get_local(pair); + *ptr = 0x0; + ptr = (uint8_t *)address_pair_get_remote(pair); + *ptr = 0x0; +#endif /* __APPLE__ */ + + off_t id = conn - table->connections; + int rc; + + // Add in name hash table + name_key_t *name_copy = slab_get(name_key_t, table->name_keys); + strcpy_s(name_copy->name, sizeof(name_key_t), name); + + khiter_t k = kh_put_ct_name(table->id_by_name, name_copy->name, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(table->id_by_name, k) = (unsigned int)id; + + // Add in pair hash table + address_pair_t *pair_copy = slab_get(address_pair_t, table->pair_keys); + memcpy(pair_copy, pair, sizeof(address_pair_t)); + + k = kh_put_ct_pair(table->id_by_pair, pair_copy, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(table->id_by_pair, k) = (unsigned int)id; + + assert(kh_size(table->id_by_name) == kh_size(table->id_by_pair)); + return conn; +} + +void connection_table_deallocate(const connection_table_t *table, + const connection_t *conn) { + const char *name = connection_get_name(conn); + const address_pair_t *pair = connection_get_pair(conn); + +#ifdef __APPLE__ + // set __uint8_t sin_len to 0 + uint8_t *ptr = (uint8_t *)address_pair_get_local(pair); + *ptr = 0x0; + ptr = (uint8_t *)address_pair_get_remote(pair); + *ptr = 0x0; +#endif /* __APPLE__ */ + + // Remove from name hash table + khiter_t k = kh_get_ct_name(table->id_by_name, name); + assert(k != kh_end(table->id_by_name)); + kh_del_ct_name(table->id_by_name, k); + slab_put(table->name_keys, kh_key(table->id_by_name, k)); + + // Remove from pair hash table + k = kh_get_ct_pair(table->id_by_pair, pair); + assert(k != kh_end(table->id_by_pair)); + kh_del_ct_pair(table->id_by_pair, k); + slab_put(table->pair_keys, kh_key(table->id_by_pair, k)); + + assert(kh_size(table->id_by_name) == kh_size(table->id_by_pair)); + pool_put(table->connections, conn); +} + +connection_t *connection_table_get_by_pair(const connection_table_t *table, + const address_pair_t *pair) { +#ifdef __APPLE__ + // set __uint8_t sin_len to 0 + uint8_t *ptr = (uint8_t *)address_pair_get_local(pair); + *ptr = 0x0; + ptr = (uint8_t *)address_pair_get_remote(pair); + *ptr = 0x0; +#endif /* __APPLE__ */ + + khiter_t k = kh_get_ct_pair(table->id_by_pair, pair); + if (k == kh_end(table->id_by_pair)) return NULL; + return table->connections + kh_val(table->id_by_pair, k); +} + +off_t connection_table_get_id_by_name(const connection_table_t *table, + const char *name) { + khiter_t k = kh_get_ct_name(table->id_by_name, name); + if (k == kh_end(table->id_by_name)) return CONNECTION_ID_UNDEFINED; + return kh_val(table->id_by_name, k); +} + +connection_t *connection_table_get_by_name(const connection_table_t *table, + const char *name) { + unsigned conn_id = (unsigned int)connection_table_get_id_by_name(table, name); + if (!connection_id_is_valid(conn_id)) return NULL; + return connection_table_at(table, conn_id); +} + +void connection_table_remove_by_id(connection_table_t *table, off_t id) { + connection_t *connection = connection_table_at(table, id); + INFO("Removing connection %d (%s)", id, connection_get_name(connection)); + + connection_table_deallocate(table, connection); +} + +void connection_table_print_by_pair(const connection_table_t *table) { + const address_pair_t *k; + unsigned v; + + char local_addr_str[NI_MAXHOST], remote_addr_str[NI_MAXHOST]; + int local_port, remote_port; + connection_t *connection; + const char *name; + + INFO("*** Connection table ***"); + kh_foreach(table->id_by_pair, k, v, { + address_to_string(&(k->local), local_addr_str, &local_port); + address_to_string(&(k->remote), remote_addr_str, &remote_port); + connection = connection_table_get_by_id(table, v); + name = connection_get_name(connection); + INFO("(%s:%d - %s:%d)\t\t\t(%u, %s)", local_addr_str, local_port, + remote_addr_str, remote_port, v, name); + }) +} + +void connection_table_print_by_name(const connection_table_t *table) { + const char *k; + unsigned v; + + connection_t *connection; + const char *name; + + INFO("*** Connection table ***"); + kh_foreach(table->id_by_name, k, v, { + connection = connection_table_get_by_id(table, v); + name = connection_get_name(connection); + INFO("(%s)\t\t\t(%u, %s)", k, v, name); + }) +} + +connection_t *_connection_table_get_by_id(connection_table_t *table, off_t id) { + return connection_table_get_by_id(table, id); +} + +static inline u16 RAND16() { return rand() & 0xFFFF; } + +int connection_table_get_random_name(const connection_table_t *table, + char *name) { + int i, n_attempts = 2 * USHRT_MAX; + for (i = 0; i < n_attempts; i++) { + int rc = snprintf(name, SYMBOLIC_NAME_LEN, "conn%u", RAND16()); + if (rc < 0 || rc >= SYMBOLIC_NAME_LEN) continue; + + // Check if generated connection name is a duplicate + khiter_t k = kh_get_ct_name(table->id_by_name, name); + if (k == kh_end(table->id_by_name)) break; + } + + if (i == n_attempts) { + ERROR("Unable to generate new unique connection name"); + return -1; + } + + return 0; +} diff --git a/hicn-light/src/hicn/core/connection_table.h b/hicn-light/src/hicn/core/connection_table.h new file mode 100644 index 000000000..dcfb1e1dd --- /dev/null +++ b/hicn-light/src/hicn/core/connection_table.h @@ -0,0 +1,257 @@ +/* + * 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 connection_table.h + * \brief hICN connection_table + * + * The connection table is composed of : + * - a pool of connections allowing access through their id in constant time; + * - a set of indices in the form of hash table for efficient index lookups: + * . by name + * . by address pair + * + * For efficient index retrieval, the header will be prepended and the + * resulting pointer will directly point to the connection pool. + */ + +#ifndef HICNLIGHT_CONNECTION_TABLE_H +#define HICNLIGHT_CONNECTION_TABLE_H + +#include <hicn/util/khash.h> +#include <hicn/util/hash.h> +#include <hicn/util/slab.h> +#include "address_pair.h" +#include "connection.h" +#include <hicn/util/pool.h> + +/* Hash functions for indices. */ +#define address_pair_hash(pair) (hash_struct(pair)) + +/* Hash table types for indices. */ +KHASH_INIT(ct_pair, const address_pair_t *, unsigned, 1, address_pair_hash, + address_pair_equals); +KHASH_MAP_INIT_STR(ct_name, unsigned); + +typedef struct { + size_t max_size; + + kh_ct_pair_t *id_by_pair; + slab_t *pair_keys; + kh_ct_name_t *id_by_name; + slab_t *name_keys; + + connection_t *connections; // pool +} connection_table_t; + +/** + * @brief Allocate a connection from the connection table. + * + * @param[in] table The connection table from which to allocate a connection. + * @param[out] connection The pointer that will hold the allocated connection. + * @param[in] pair The address pair associated to the connection (to update + * index). + * @param[in] name The name associated to the connection (to update index). + * + * NOTE: + * - This function updates all indices from the connection table if the + * allocation is successful. + * - You should always check that the returned connection is not NULL, which + * would signal that the pool is exhausted and could not be extended. + */ +connection_t *connection_table_allocate(const connection_table_t *table, + const address_pair_t *pair, + const char *name); +/** + * @brief Deallocate a connection and return it to the connection table pool. + * + * @param[in] table The connection table to which the connection is returned. + * @param[in] conn The connection that is returned to the pool. + * + * NOTE: + * - Upon returning a connection to the pool, all indices pointing to that + * connection are also cleared. + */ +void connection_table_deallocate(const connection_table_t *table, + const connection_t *conn); + +/** + * @brief Returns the length of the connection table, the number of active + * connections. + * + * @param[in] table The connection table for which we retrieve the length. + * + * @return size_t The length of the connection table. + * + * NOTE: + * - The length of the connection table, that is the number of currently active + * connections. + */ +#define connection_table_len(table) (pool_len(table->connections)) + +/** + * @brief Validate an index in the connection table. + * + * @param[in] table The connection table in which to validate an index. + * @param[in] id The index of the connection to validate. + * + * @return bool A flag indicating whether the connection index is valid or not. + */ +#define connection_table_validate_id(table, id) \ + pool_validate_id((table)->connections, (id)) + +/** + * @brief Return the connection corresponding to the specified index in the + * connection table. + * + * @param[in] table The connection table for which to retrieve the connection. + * @param[in] id The index for which to retrieve the connection. + * + * @return connection_t * The connection correponding to the specified index in + * the connection table. + * + * @see connection_table_get_by_id + * + * NOTE: + * - In this function, the index is not validated. + */ +#define connection_table_at(table, id) ((table)->connections + id) + +/** + * @brief Return the connection corresponding to the specified and validated + * index in the connection table. + * + * @param[in] table The connection table for which to retrieve the connection. + * @param[in] id The index for which to retrieve the connection. + * + * @return connection_t * The connection correponding to the specified index in + * the connection table. + * + * @see connection_table_get_by_id + * + * NOTE: + * - In this function, the index is validated. + */ +#define connection_table_get_by_id(table, id) \ + connection_table_validate_id(table, id) ? connection_table_at(table, id) \ + : NULL + +/** + * @brief Helper function to avoid macro expansion in c++ tests. Wrapper around + * 'connection_table_get_by_id'. + */ +connection_t *_connection_table_get_by_id(connection_table_t *table, off_t id); + +/** + * @brief Returns the index of a given connection in the connection table. + * + * @param[in] table The connection table from which to retrieve the index. + * @param[in] conn The connection for which to retrieve the index. + * + * @return off_t The index of the specified connection in the connection table. + */ +#define connection_table_get_connection_id(table, conn) \ + (unsigned)(conn - table->connections) + +#define connection_table_foreach(table, conn, BODY) \ + pool_foreach(table->connections, (conn), BODY) + +#define connection_table_foreach_new(table, CONN, BODY) \ + pool_foreach_typed(table->connections, connection_t *, CONN, BODY) + +#define connection_table_enumerate(table, i, conn, BODY) \ + pool_enumerate(table->connections, (i), (conn), BODY) + +/** + * @brief Create a new connection table (extended parameters). + * + * @param[in] init_size Initially allocated size (hint, 0 = use default value). + * @param[in] max_size Maximum size (0 = unlimited). + * + * @return connection_table_t* The newly created connection table. + */ +connection_table_t *_connection_table_create(size_t init_size, size_t max_size); + +/** + * @brief Create a new connection table (minimal parameters). + * + * @return connection_table_t* The newly created connection table. + */ +#define connection_table_create() _connection_table_create(0, 0) + +/** + * @brief Free a connection table + * + * @param[in] table Connection table to free + */ +void connection_table_free(connection_table_t *table); + +/** + * @brief Retrieve a connection from the connection table by address pair. + * + * @param[in] table The connection table in which to search. + * @param[in] pair The address pair to search for. + * + * @return connection_t * The connection matching the specified address pair, or + * NULL if not found. + */ +connection_t *connection_table_get_by_pair(const connection_table_t *table, + const address_pair_t *pair); + +/** + * @brief Return a connection index from the connection table by name. + * + * @param[in] table The connection table in which to search. + * @param[in] name The name to search for. + * + * @return off_t The index of the connection matching the name, or + * CONNECTION_ID_UNDEFINED if not found. + */ +off_t connection_table_get_id_by_name(const connection_table_t *table, + const char *name); + +/** + * @brief Return a connection from the connection table by name. + * + * @param[in] table The connection table in which to search. + * @param[in] name The name to search for. + * + * @return connection_t * The connection matching the name, or NULL if not + * found. + */ +connection_t *connection_table_get_by_name(const connection_table_t *table, + const char *name); + +/** + * @brief Remove a connection from the connection table by its index. + * + * @param[in] table The connection table from which to delete the connection. + * @param[in] id The index of the connection to remove. + */ +void connection_table_remove_by_id(connection_table_t *table, off_t id); + +/** + * @brief Print the connection table content. + * + * @param[in] table The connection table to print. + */ +void connection_table_print_by_pair(const connection_table_t *table); + +void connection_table_print_by_name(const connection_table_t *table); + +int connection_table_get_random_name(const connection_table_t *table, + char *name); + +#endif /* HICNLIGHT_CONNECTION_TABLE_H */ diff --git a/hicn-light/src/hicn/core/connectionState.h b/hicn-light/src/hicn/core/connection_vft.c index 9daa15c9c..fe97096d3 100644 --- a/hicn-light/src/hicn/core/connectionState.h +++ b/hicn-light/src/hicn/core/connection_vft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,24 +14,24 @@ */ /** - * @file connection_state.h - * @brief Represents the state of a connection - * + * @file connection_vft.c + * @brief Implementation of connection VFT */ -#ifndef connection_state_h -#define connection_state_h +#include "connection_vft.h" + +#ifdef __linux +extern connection_ops_t connection_hicn; +#endif -#define foreach_connection_state \ - _(UNDEFINED) \ - _(DOWN) \ - _(UP) \ - _(N) +extern connection_ops_t connection_tcp; +extern connection_ops_t connection_udp; -typedef enum { -#define _(x) CONNECTION_STATE_ ## x, -foreach_connection_state -#undef _ -} connection_state_t; +const connection_ops_t *connection_vft[] = { +#ifdef __linux + [FACE_PROTOCOL_HICN] = &connection_hicn, +#endif -#endif /* connection_state_h */ + [FACE_PROTOCOL_TCP] = &connection_tcp, + [FACE_PROTOCOL_UDP] = &connection_udp, +}; diff --git a/hicn-light/src/hicn/core/connection_vft.h b/hicn-light/src/hicn/core/connection_vft.h new file mode 100644 index 000000000..cc736905c --- /dev/null +++ b/hicn-light/src/hicn/core/connection_vft.h @@ -0,0 +1,54 @@ +/* + * 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 connection_vft.h + * @brief Connection VFT + */ + +#ifndef HICNLIGHT_CONNECTION_VFT_H +#define HICNLIGHT_CONNECTION_VFT_H + +#include "connection.h" + +typedef struct { + int (*initialize)(connection_t* connection); + void (*finalize)(connection_t* connection); + int (*get_socket)(const listener_t* listener, const address_t* local, + const address_t* remote, const char* interface_name); + bool (*flush)(connection_t* connection); + bool (*send)(connection_t* connection, msgbuf_t* msgbuf, bool queue); + bool (*send_packet)(const connection_t* connection, const uint8_t* packet, + size_t size); + // void (*read_callback)(connection_t * connection, int fd, void * data); + size_t data_size; +} connection_ops_t; + +#define DECLARE_CONNECTION(NAME) \ + const connection_ops_t connection_##NAME = { \ + .initialize = connection_##NAME##_initialize, \ + .finalize = connection_##NAME##_finalize, \ + .get_socket = listener_##NAME##_get_socket, \ + .flush = connection_##NAME##_flush, \ + .send = connection_##NAME##_send, \ + .send_packet = connection_##NAME##_send_packet, \ + .data_size = sizeof(connection_##NAME##_data_t), \ + }; + +// .read_callback = connection_ ## NAME ## _read_callback, + +extern const connection_ops_t* connection_vft[]; + +#endif /* HICNLIGHT_CONNECTION_VFT_H */ diff --git a/hicn-light/src/hicn/core/content_store.c b/hicn-light/src/hicn/core/content_store.c new file mode 100644 index 000000000..360e6d333 --- /dev/null +++ b/hicn-light/src/hicn/core/content_store.c @@ -0,0 +1,88 @@ +/* + * 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 content_store.c + * \brief Implementation of hICN content_store + */ + +#include "content_store.h" +#include "packet_cache.h" + +extern const cs_ops_t cs_lru; + +const cs_ops_t *const cs_vft[] = { + [CS_TYPE_LRU] = &cs_lru, +}; + +cs_t *_cs_create(cs_type_t type, size_t max_size) { + if (!CS_TYPE_VALID(type)) { + ERROR("[cs_create] Invalid content store type"); + return NULL; + } + + if (max_size == 0) max_size = DEFAULT_CS_SIZE; + + cs_t *cs = malloc(sizeof(cs_t)); + if (!cs) return NULL; + + cs->type = type; + cs->num_entries = 0; + cs->max_size = max_size; + cs_vft[type]->initialize(cs); + cs->stats.lru = (cs_lru_stats_t){0}; + + return cs; +} + +void cs_free(cs_t *cs) { + assert(cs); + + cs_vft[cs->type]->finalize(cs); + free(cs); +} + +void _cs_clear(cs_t **cs_ptr) { + cs_type_t cs_type = (*cs_ptr)->type; + size_t max_size = (*cs_ptr)->max_size; + + // Free and recreate the CS + cs_free(*cs_ptr); + *cs_ptr = _cs_create(cs_type, max_size); +} + +void cs_hit(cs_t *cs) { + cs->stats.lru.countHits++; + TRACE("ContentStore hit (hits %u, misses %u)", cs->stats.lru.countHits, + cs->stats.lru.countMisses); +} + +void cs_miss(cs_t *cs) { + cs->stats.lru.countMisses++; + TRACE("ContentStore miss (hits %u, misses %u)", cs->stats.lru.countHits, + cs->stats.lru.countMisses); +} + +void cs_log(cs_t *cs) { + DEBUG( + "Content store: size = %u, capacity = %u, hits = %u, misses = %u, adds = " + "%u, updates = %u, deletions = %u (with evictions = %u)", + cs->num_entries, cs->max_size, cs->stats.lru.countHits, + cs->stats.lru.countMisses, cs->stats.lru.countAdds, + cs->stats.lru.countUpdates, cs->stats.lru.countLruDeletions, + cs->stats.lru.countLruEvictions); +} + +cs_lru_stats_t cs_get_lru_stats(cs_t *cs) { return cs->stats.lru; } diff --git a/hicn-light/src/hicn/core/content_store.h b/hicn-light/src/hicn/core/content_store.h new file mode 100644 index 000000000..ceaf05e6b --- /dev/null +++ b/hicn-light/src/hicn/core/content_store.h @@ -0,0 +1,108 @@ +#ifndef HICNLIGHT_CS_H +#define HICNLIGHT_CS_H + +#include <hicn/util/pool.h> +#include "../content_store/lru.h" +#include "msgbuf_pool.h" + +#define INVALID_ENTRY_ID ~0ul /* off_t */ +#define DEFAULT_CS_SIZE 256 // Fixed CS size + +typedef struct { + off_t msgbuf_id; + struct { + off_t prev; + off_t next; + } lru; +} cs_entry_t; + +#define cs_entry_get_msgbuf_id(entry) ((entry)->msgbuf_id) + +typedef enum { + CS_TYPE_UNDEFINED, + CS_TYPE_LRU, + CS_TYPE_N, +} cs_type_t; + +#define CS_TYPE_VALID(type) (type != CS_TYPE_UNDEFINED) && (type != CS_TYPE_N) + +typedef struct { + /* The maximum allowed expiry time (will never be exceeded). */ + uint64_t max_expiry_time; // XXX part of lru ? +} cs_options_t; + +typedef struct { + cs_type_t type; + int num_entries; + size_t max_size; + cs_lru_state_t lru; + union { + cs_lru_stats_t lru; + } stats; +} cs_t; + +/** + * @brief Create a new content store (extended parameters). + * + * @param[in] type Content store type + * @param[in] max_size Maximum size (0 = use default value) + * + * @return cs_t* - The newly created content store + */ +cs_t *_cs_create(cs_type_t type, size_t max_size); + +/** + * @brief Create a new content store + * + * @param[in] size Maximum content store size + * + * @return cs_t* - The newly created content store + */ +#define cs_create(size) _cs_create(CS_TYPE_LRU, (size)) + +/** + * @brief Free a content store data structure. + * + * @param[in] pool_ptr Pointer to the content store to free + */ +void cs_free(cs_t *cs); + +/** + * @brief Clear the content of the content store (helper). + * + * @param[in, out] cs Pointer to the content store to clear + */ +void _cs_clear(cs_t **cs); + +/** + * @brief Clear the content of the content store. + * + * @param[in, out] cs Pointer to the content store to clear + */ +#define cs_clear(cs) _cs_clear((cs_t **)&cs); + +/** + * @brief Update content store statistics upon CS hit event. + * + * @param[in] cs Pointer to the content store to use + */ +void cs_hit(cs_t *cs); + +/** + * @brief Update content store statistics upon CS miss event. + * + * @param[in] cs Pointer to the content store to use + */ +void cs_miss(cs_t *cs); + +/** + * @brief Log content store statistics, e.g. the CS current and maximum size, + * the number of matches, misses, add operations, update operations, evictions. + * + * @param cs Pointer to the CS data structure to use + */ +void cs_log(cs_t *cs); + +cs_lru_stats_t cs_get_lru_stats(cs_t *cs); + +#endif /* HICNLIGHT_CS_H */ diff --git a/hicn-light/src/hicn/core/dispatcher.c b/hicn-light/src/hicn/core/dispatcher.c deleted file mode 100644 index 59951e950..000000000 --- a/hicn-light/src/hicn/core/dispatcher.c +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @header dispatcher.c - * @abstract Event dispatcher for hicn-light. Uses parcEvent - * @discussion - * Wraps the functions we use in parcEvent, along with StreamBuffer and - * Message. The dispatcher is the event loop, so it manages things like signals, - * timers, and network events. - */ - -#ifndef _WIN32 -#include <arpa/inet.h> -#include <sys/socket.h> -#include <unistd.h> -#endif -#include <errno.h> -#include <fcntl.h> -#include <signal.h> -#include <hicn/hicn-light/config.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include <parc/algol/parc_EventQueue.h> -#include <parc/algol/parc_EventTimer.h> - -#include <parc/assert/parc_Assert.h> - -#include <hicn/core/dispatcher.h> - -#include <pthread.h> - -#ifndef INPORT_ANY -#define INPORT_ANY 0 -#endif - -struct dispatcher { - PARCEventScheduler *Base; - Logger *logger; -}; - -// ========================================== -// Public API - -PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher) { - return dispatcher->Base; -} - -Dispatcher *dispatcher_Create(Logger *logger) { - Dispatcher *dispatcher = parcMemory_AllocateAndClear(sizeof(Dispatcher)); - parcAssertNotNull(dispatcher, - "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Dispatcher)); - - dispatcher->Base = parcEventScheduler_Create(); - dispatcher->logger = logger_Acquire(logger); - - parcAssertNotNull(dispatcher->Base, - "Got NULL from parcEventScheduler_Create()"); - - return dispatcher; -} - -void dispatcher_Destroy(Dispatcher **dispatcherPtr) { - parcAssertNotNull(dispatcherPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*dispatcherPtr, - "Parameter must dereference to non-null pointer"); - Dispatcher *dispatcher = *dispatcherPtr; - - logger_Release(&dispatcher->logger); - parcEventScheduler_Destroy(&(dispatcher->Base)); - parcMemory_Deallocate((void **)&dispatcher); - *dispatcherPtr = NULL; -} - -void dispatcher_Stop(Dispatcher *dispatcher) { - struct timeval delay = {0, 1000}; - - parcEventScheduler_Stop(dispatcher->Base, &delay); -} - -void dispatcher_Run(Dispatcher *dispatcher) { - parcAssertNotNull(dispatcher, "Parameter must be non-null"); - - parcEventScheduler_Start(dispatcher->Base, 0); -} - -void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(duration, "Parameter duration must be non-null"); - - parcEventScheduler_Stop(dispatcher->Base, duration); - parcEventScheduler_Start(dispatcher->Base, 0); -} - -void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count) { - parcAssertNotNull(dispatcher, "Parameter must be non-null"); - - for (unsigned i = 0; i < count; i++) { - parcEventScheduler_Start(dispatcher->Base, - PARCEventSchedulerDispatchType_LoopOnce); - } -} - -PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, - PARCEventSocket_Callback *callback, - void *user_data, int backlog, - const struct sockaddr *sa, - int socklen) { - PARCEventSocket *listener = parcEventSocket_Create( - dispatcher->Base, callback, NULL, user_data, sa, socklen); - if (listener == NULL) { - perror("Problem creating listener"); - } - return listener; -} - -void dispatcher_DestroyListener(Dispatcher *dispatcher, - PARCEventSocket **listenerPtr) { - parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*listenerPtr, - "Parameter must dereference to non-null pointer"); - parcEventSocket_Destroy(listenerPtr); -} - -PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, - SocketType fd) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - PARCEventQueue *buffer = parcEventQueue_Create( - dispatcher->Base, fd, - PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks); - parcAssertNotNull(buffer, - "Got null from parcEventBufver_Create for socket %d", fd); - return buffer; -} - -PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, - PARCEvent_Callback *callback, - void *userData) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(callback, "Parameter callback must be non-null"); - - PARCEventType flags = 0; - if (isPeriodic) { - flags |= PARCEventType_Persist; - } - PARCEventTimer *event = - parcEventTimer_Create(dispatcher->Base, flags, callback, userData); - return event; -} - -void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, - struct timeval *delay) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(timerEvent, "Parameter timerEvent must be non-null"); - int failure = parcEventTimer_Start(timerEvent, delay); - parcAssertFalse(failure < 0, "Error starting timer event %p: (%d) %s", - (void *)timerEvent, errno, strerror(errno)); -} - -void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *event) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(event, "Parameter event must be non-null"); - - int failure = parcEventTimer_Stop(event); - parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", - (void *)event, errno, strerror(errno)); -} - -void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, - PARCEventTimer **eventPtr) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(eventPtr, - "Parameter eventPtr must be non-null double pointer"); - parcAssertNotNull(*eventPtr, - "Paramter eventPtr must dereference to non-null pointer"); - - parcEventTimer_Destroy(eventPtr); - eventPtr = NULL; -} - -PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, - bool isPersistent, - PARCEvent_Callback *callback, - void *userData, int fd) { - short flags = PARCEventType_Timeout | PARCEventType_Read; - if (isPersistent) { - flags |= PARCEventType_Persist; - } - - PARCEvent *event = - parcEvent_Create(dispatcher->Base, fd, flags, callback, userData); - parcAssertNotNull(event, "Got null from parcEvent_Create for socket %d", fd); - return event; -} - -void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, - PARCEvent **eventPtr) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(eventPtr, - "Parameter eventPtr must be non-null double pointer"); - parcAssertNotNull(*eventPtr, - "Paramter eventPtr must dereference to non-null pointer"); - - parcEvent_Destroy(eventPtr); - eventPtr = NULL; -} - -void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(event, "Parameter event must be non-null"); - - int failure = parcEvent_Start(event); - parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", - (void *)event, errno, strerror(errno)); -} - -void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(event, "Parameter event must be non-null"); - - int failure = parcEvent_Stop(event); - parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", - (void *)event, errno, strerror(errno)); -} - -PARCEventSignal *dispatcher_CreateSignalEvent( - Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, - int signal) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(callback, "Parameter callback must be non-null"); - - PARCEventSignal *event = parcEventSignal_Create( - dispatcher->Base, signal, PARCEventType_Signal | PARCEventType_Persist, - callback, userData); - parcAssertNotNull(event, - "Got null event when creating signal catcher for signal %d", - signal); - - return event; -} - -void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, - PARCEventSignal **eventPtr) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(eventPtr, - "Parameter eventPtr must be non-null double pointer"); - parcAssertNotNull(*eventPtr, - "Paramter eventPtr must dereference to non-null pointer"); - - parcEventSignal_Destroy(eventPtr); - eventPtr = NULL; -} - -void dispatcher_StartSignalEvent(Dispatcher *dispatcher, - PARCEventSignal *event) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(event, "Parameter event must be non-null"); - - int failure = parcEventSignal_Start(event); - parcAssertFalse(failure < 0, "Error starting signal event %p: (%d) %s", - (void *)event, errno, strerror(errno)); -} - -void dispatcher_StopSignalEvent(Dispatcher *dispatcher, - PARCEventSignal *event) { - parcAssertNotNull(dispatcher, "Parameter dispatcher must be non-null"); - parcAssertNotNull(event, "Parameter event must be non-null"); - - int failure = parcEventSignal_Stop(event); - parcAssertFalse(failure < 0, "Error stopping signal event %p: (%d) %s", - (void *)event, errno, strerror(errno)); -} - -/** - * Bind to a local address/port then connect to peer. - */ -static bool dispatcher_StreamBufferBindAndConnect(Dispatcher *dispatcher, - PARCEventQueue *buffer, - struct sockaddr *localSock, - socklen_t localSockLength, - struct sockaddr *remoteSock, - socklen_t remoteSockLength) { - // we need to bind, then connect. Special operation, so we make our - // own fd then pass it off to the buffer event - -#ifndef _WIN32 - int fd = socket(localSock->sa_family, SOCK_STREAM, 0); - if (fd < 0) { - perror("socket"); - return -1; - } - - // Set non-blocking flag - int flags = fcntl(fd, F_GETFL, NULL); - if (flags < 0) { - perror("F_GETFL"); - close(fd); - return -1; - } - int failure = fcntl(fd, F_SETFL, flags | O_NONBLOCK); - if (failure) { - perror("F_SETFL"); - close(fd); - return -1; - } - - failure = bind(fd, localSock, localSockLength); - if (failure) { - perror("bind"); - close(fd); - return false; - } - - parcEventQueue_SetFileDescriptor(buffer, fd); - - failure = parcEventQueue_ConnectSocket(buffer, remoteSock, remoteSockLength); - if (failure && (errno != EINPROGRESS)) { - perror("connect"); - close(fd); - return false; - } -#else - SOCKET fd = socket(localSock->sa_family, SOCK_STREAM, 0); - if (fd == INVALID_SOCKET) { - perror("socket"); - return -1; - } - - // Set non-blocking flag - u_long mode = 1; - int result = ioctlsocket(fd, FIONBIO, &mode); - if (result == NO_ERROR) { - perror("ioctlsocket error"); - closesocket(fd); - WSACleanup(); - return -1; - } - - int failure = bind(fd, localSock, (int)localSockLength); - if (failure) { - perror("bind"); - closesocket(fd); - WSACleanup(); - return false; - } - - parcEventQueue_SetFileDescriptor(buffer, (int)fd); - - failure = parcEventQueue_ConnectSocket(buffer, remoteSock, remoteSockLength); - if (failure && (errno != EINPROGRESS)) { - perror("connect"); - closesocket(fd); - WSACleanup(); - return false; - } -#endif - - return true; -} - -/** - * Connect to an INET peer - * @return NULL on error, otherwise a streambuffer - */ -static PARCEventQueue *dispatcher_StreamBufferConnect_INET( - Dispatcher *dispatcher, const Address *localAddress, - const Address *remoteAddress) { - struct sockaddr_in localSock, remoteSock; - addressGetInet(localAddress, &localSock); - addressGetInet(remoteAddress, &remoteSock); - - PARCEventQueue *buffer = parcEventQueue_Create( - dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); - parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); - - bool success = dispatcher_StreamBufferBindAndConnect( - dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), - (struct sockaddr *)&remoteSock, sizeof(remoteSock)); - if (!success) { - parcEventQueue_Destroy(&buffer); - buffer = NULL; - } - - return buffer; -} - -/** - * Connect to an INET peer - * @return NULL on error, otherwise a streambuffer - */ -static PARCEventQueue * -// static StreamBuffer * -dispatcher_StreamBufferConnect_INET6(Dispatcher *dispatcher, - const Address *localAddress, - const Address *remoteAddress) { - struct sockaddr_in6 localSock, remoteSock; - addressGetInet6(localAddress, &localSock); - addressGetInet6(remoteAddress, &remoteSock); - - PARCEventQueue *buffer = parcEventQueue_Create( - dispatcher->Base, -1, PARCEventQueueOption_CloseOnFree); - parcAssertNotNull(buffer, "got null buffer from parcEventQueue_Create()"); - - bool success = dispatcher_StreamBufferBindAndConnect( - dispatcher, buffer, (struct sockaddr *)&localSock, sizeof(localSock), - (struct sockaddr *)&remoteSock, sizeof(remoteSock)); - if (!success) { - parcEventQueue_Destroy(&buffer); - buffer = NULL; - } - - return buffer; -} - -PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, - const AddressPair *pair) { - const Address *localAddress = addressPair_GetLocal(pair); - const Address *remoteAddress = addressPair_GetRemote(pair); - - // they must be of the same address family - if (addressGetType(localAddress) != addressGetType(remoteAddress)) { - char message[2048]; - char *localAddressString = addressToString(localAddress); - char *remoteAddressString = addressToString(remoteAddress); - snprintf(message, 2048, - "Remote address not same type as local address, expected %d got " - "%d\nlocal %s remote %s", - addressGetType(localAddress), addressGetType(remoteAddress), - localAddressString, remoteAddressString); - - parcMemory_Deallocate((void **)&localAddressString); - parcMemory_Deallocate((void **)&remoteAddressString); - - parcAssertTrue( - addressGetType(localAddress) == addressGetType(remoteAddress), "%s", - message); - } - - address_type type = addressGetType(localAddress); - - PARCEventQueue *result = NULL; - - switch (type) { - case ADDR_INET: - return dispatcher_StreamBufferConnect_INET(dispatcher, localAddress, - remoteAddress); - break; - case ADDR_INET6: - return dispatcher_StreamBufferConnect_INET6(dispatcher, localAddress, - remoteAddress); - break; - default: - parcTrapIllegalValue(type, "local address unsupported address type: %d", - type); - } - return result; -} diff --git a/hicn-light/src/hicn/core/dispatcher.h b/hicn-light/src/hicn/core/dispatcher.h deleted file mode 100644 index e5c2df336..000000000 --- a/hicn-light/src/hicn/core/dispatcher.h +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @header hicn-light Dispatcher - * @abstract The dispatcher is the event loop run by Forwarder. - * @discussion - * These functions manage listeners, timers, and network events inside - * the event loop. - * - * Curently, it is a thin wrapper around an event so we don't have to - * expose that implementation detail to other modules. - * - */ - -#ifndef dispatcher_h -#define dispatcher_h - -#ifndef _WIN32 -#include <sys/socket.h> -#endif -#include <stdbool.h> - -struct dispatcher; -typedef struct dispatcher Dispatcher; - -#include <parc/algol/parc_Event.h> -#include <parc/algol/parc_EventQueue.h> -#include <parc/algol/parc_EventScheduler.h> -#include <parc/algol/parc_EventSignal.h> -#include <parc/algol/parc_EventSocket.h> -#include <parc/algol/parc_EventTimer.h> -#include <parc/algol/parc_Memory.h> - -#include <hicn/core/logger.h> - -PARCEventScheduler *dispatcher_GetEventScheduler(Dispatcher *dispatcher); -/** - * Creates an event dispatcher - * - * Event dispatcher based on PARCEvent - * - * @return non-null Allocated event dispatcher - * @return null An error - */ -Dispatcher *dispatcher_Create(Logger *logger); - -/** - * Destroys event dispatcher - * - * Caller is responsible for destroying call events before destroying - * the event dispatcher. - */ -void dispatcher_Destroy(Dispatcher **dispatcherPtr); - -/** - * @function dispatcher_Stop - * @abstract Called from a different thread, tells the dispatcher to stop - * @discussion - * Called from a user thread or from an interrupt handler. - * Does not block. Use <code>dispatcher_WaitForStopped()</code> to - * block until stopped after calling this. - */ -void dispatcher_Stop(Dispatcher *dispatcher); - -/** - * @function dispatcher_WaitForStopped - * @abstract Blocks until dispatcher in stopped state - * @discussion - * Used after <code>dispatcher_Stop()</code> to wait for stop. - */ -void dispatcher_WaitForStopped(Dispatcher *dispatcher); - -/** - * @function dispatcher_Run - * @abstract Runs the forwarder, blocks. - */ -void dispatcher_Run(Dispatcher *dispatcher); - -/** - * @function dispatcher_RunDuration - * @abstract Runs forwarder for at most duration, blocks. - * @discussion - * Blocks running the forwarder for a duration. May be called - * iteratively to keep running. Duration is a minimum, actual - * runtime may be slightly longer. - */ -void dispatcher_RunDuration(Dispatcher *dispatcher, struct timeval *duration); - -/** - * @header dispatcher_RunCount - * @abstract Run the event loop for the given count cycles - * @discussion - * Runs the event loop for the given number of cycles, blocking - * until done. May be called sequentially over and over. - * - */ -void dispatcher_RunCount(Dispatcher *dispatcher, unsigned count); - -typedef int SocketType; - -typedef struct evconnlistener Listener; - -/** - * @typedef ListenerCallback - * @abstract Callback function typedef for a stream listener - * - * @constant listener is the object created by <code>forwarder_NewBind()</code> - * that received the client connection - * @constant client_socket is the client socket - * @constant user_data is the user_data passed to - * <code>forwarder_NewBind()</code> - * @constant client_addr is the client address - * @constant socklen is the length of client_addr - * @discussion <#Discussion#> - */ -typedef void(ListenerCallback)(Listener *listener, SocketType client_socket, - struct sockaddr *client_addr, int socklen, - void *user_data); - -/** - * @header forwarder_NewBind - * @abstract Allocate a new stream listener - * @discussion - * The server socket will be freed when closed and will be reusable. - * - * @param forwarder that owns the event loop - * @param cb is the callback for a new connection - * @param user_data is opaque user data passed to the callback - * @param backlog is the listen() depth, may use -1 for a default value - * @param sa is the socket address to bind to (INET, INET6, LOCAL) - * @param socklen is the sizeof the actual sockaddr (e.g. sizeof(sockaddr_un)) - */ -PARCEventSocket *dispatcher_CreateListener(Dispatcher *dispatcher, - PARCEventSocket_Callback *callback, - void *user_data, int backlog, - const struct sockaddr *sa, - int socklen); - -void dispatcher_DestroyListener(Dispatcher *dispatcher, - PARCEventSocket **listenerPtr); - -typedef struct event TimerEvent; -typedef struct event NetworkEvent; -typedef struct event SignalEvent; - -/** - * @typedef EventCallback - * @abstract A network event or a timer callback - * @constant fd The file descriptor associated with the event, may be -1 for - * timers - * @constant which_event is a bitmap of the EventType - * @constant user_data is the user_data passed to - * <code>Forwarder_CreateEvent()</code> - */ -typedef void(EventCallback)(SocketType fd, short which_event, void *user_data); - -/** - * @function dispatcher_CreateTimer - * @abstract Creates a Event for use as a timer. - * @discussion - * - * When created, the timer is idle and you need to call - * <code>forwarder_StartTimer()</code> - * - * @param isPeriodic means the timer will fire repeatidly, otherwise it is a - * one-shot and needs to be set again with <code>dispatcher_StartTimer()</code> - */ -PARCEventTimer *dispatcher_CreateTimer(Dispatcher *dispatcher, bool isPeriodic, - PARCEvent_Callback *callback, - void *userData); - -/** - * @function dispatcher_StartTimer - * @abstract Starts the timer with the given delay. - * @discussion - * If the timer is periodic, it will keep firing with the given delay - */ -void dispatcher_StartTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent, - struct timeval *delay); - -void dispatcher_StopTimer(Dispatcher *dispatcher, PARCEventTimer *timerEvent); - -/** - * @function dispatcher_DestroyTimerEvent - * @abstract Cancels the timer and frees the event - */ -void dispatcher_DestroyTimerEvent(Dispatcher *dispatcher, - PARCEventTimer **eventPtr); - -/** - * @function dispatcher_CreateNetworkEvent - * @abstract Creates a network event callback on the socket - * @discussion - * May be used on any sort of file descriptor or socket. The event is edge - * triggered and non-reentrent. This means you need to drain the events off the - * socket, as the callback will not be called again until a new event arrives. - * - * When created, the event is idle and you need to call - * <code>forwarder_StartNetworkEvent()</code> - * - * @param isPersistent means the callback will keep firing with new events, - * otherwise its a one-shot - * @param fd is the socket to monitor - */ -PARCEvent *dispatcher_CreateNetworkEvent(Dispatcher *dispatcher, - bool isPersistent, - PARCEvent_Callback *callback, - void *userData, int fd); - -void dispatcher_StartNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); -void dispatcher_StopNetworkEvent(Dispatcher *dispatcher, PARCEvent *event); - -void dispatcher_DestroyNetworkEvent(Dispatcher *dispatcher, - PARCEvent **eventPtr); - -/** - * @function dispatcher_CreateSignalEvent - * @abstract Creates a signal trap - * @discussion - * May be used on catchable signals. The event is edge triggered and - * non-reentrent. Signal events are persistent. - * - * When created, the signal trap is idle and you need to call - * <code>forwarder_StartSignalEvent()</code> - * - * @param signal is the system signal to monitor (e.g. SIGINT). - * @return <#return#> - */ -PARCEventSignal *dispatcher_CreateSignalEvent( - Dispatcher *dispatcher, PARCEventSignal_Callback *callback, void *userData, - int signal); - -void dispatcher_DestroySignalEvent(Dispatcher *dispatcher, - PARCEventSignal **eventPtr); - -void dispatcher_StartSignalEvent(Dispatcher *dispatcher, - PARCEventSignal *event); -void dispatcher_StopSignalEvent(Dispatcher *dispatcher, PARCEventSignal *event); - -// ============= -// stream buffers - -#include <hicn/core/streamBuffer.h> -#include <hicn/io/addressPair.h> - -/** - * @function dispatcher_CreateStreamBuffer - * @abstract Creates a high-function buffer around a stream socket - */ -PARCEventQueue *dispatcher_CreateStreamBufferFromSocket(Dispatcher *dispatcher, - SocketType fd); - -/** - * @function dispatcher_StreamBufferConnect - * @abstract Create a TCP tunnel to a remote peer - * @discussion - * For TCP, both address pairs need to be the same address family: both INET - * or both INET6. The remote address must have the complete socket information - * (address, port). The local socket could be wildcarded or may specify down to - * the (address, port) pair. - * - * If the local address is IPADDR_ANY and the port is 0, then it is a normal - * call to "connect" that will use whatever local IP address and whatever local - * port for the connection. If either the address or port is set, the local - * socket will first be bound (via bind(2)), and then call connect(). - * - * It is unlikely that the buffer will be connected by the time the function - * returns. The eventCallback will fire once the remote system accepts the - * conneciton. - * - * @return NULL on error, otherwise a streambuffer. - */ -PARCEventQueue *dispatcher_StreamBufferConnect(Dispatcher *dispatcher, - const AddressPair *pair); -#endif // dispatcher_h diff --git a/hicn-light/src/hicn/core/fib.c b/hicn-light/src/hicn/core/fib.c new file mode 100644 index 000000000..c44d2daf0 --- /dev/null +++ b/hicn-light/src/hicn/core/fib.c @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <hicn/hicn-light/config.h> +#include <stdio.h> + +#include <hicn/core/fib.h> + +typedef struct fib_node_s { + struct fib_node_s *child[2]; /* 0: left, 1: right */ + fib_entry_t *entry; + bool is_used; +} fib_node_t; + +#define ZERO 0 +#define ONE 1 + +static fib_node_t *fib_node_create(fib_node_t *left, fib_node_t *right, + fib_entry_t *entry, bool is_used) { + fib_node_t *node = malloc(sizeof(fib_node_t)); + if (!node) return NULL; + + *node = (fib_node_t){ + .child = {left, right}, + .entry = entry, + .is_used = is_used, + }; + + return node; +} + +static void fib_node_free(fib_node_t *node) { + if (!node) return; + + fib_node_free(node->child[ZERO]); + fib_node_free(node->child[ONE]); + + fib_entry_free(node->entry); + free(node); +} + +/******************************************************************************/ + +struct fib_s { + void *forwarder; + fib_node_t *root; + unsigned size; +}; + +fib_t *fib_create(void *forwarder) { + fib_t *fib = malloc(sizeof(fib_t)); + if (!fib) return NULL; + + fib->forwarder = forwarder; + fib->root = NULL; + fib->size = 0; + + return fib; +} + +void fib_free(fib_t *fib) { + assert(fib); + + fib_node_free(fib->root); + + free(fib); +} + +size_t fib_get_size(const fib_t *fib) { + assert(fib); + return fib->size; +} + +/* + * This struct will hold various information related to the returned node such + * as its parent and grandparent if any, as well as some already computed + * information about the prefix. + */ +typedef struct { + /* Result node ancestors (NULL if not applicable) */ + fib_node_t *parent; + fib_node_t *gparent; + fib_node_t *lpm; + /* Information related to the result node */ + hicn_prefix_t *prefix; + uint32_t prefix_len; + uint32_t match_len; +} fib_search_t; +/* + * @brief Search for longest subprefix (helper function) + * @param [in] fib - Pointer to the FIB to search + * @param [in] prefix - The prefix used for search + * @param [out] search - A pointer to a structure that will hold related search + * information, that can be NULL if this is not needed. + * + * @returns The node whose entry corresponds to the longest subprefix of the + * prefix passed in parameter, or NULL if not found. The longest prefix match is + * thus the resulting node if curr_len == prefix_len, and its parent + * otherwise. + * + * Implementation details: + * + * This function performs a descent in the tree, following branches + * corresponding to the value of the next bit, until reaching past a leaf, or + * either the current node prefix: + * when one of the two following conditions is met: + * - is not a prefix of the searched one (match_len < curr_len), or + * - is longer or equal than the inserted one (curr_len >= prefix_len) + */ +fib_node_t *fib_search(const fib_t *fib, const hicn_prefix_t *prefix, + fib_search_t *search) { + uint32_t prefix_len = hicn_prefix_get_len(prefix); + uint32_t curr_len; + uint32_t match_len; + + WITH_DEBUG({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "%s", "(error)"); + DEBUG("[fib_search] prefix=%s prefix_len=%d\n", buf, prefix_len); + }); + + fib_node_t *parent = NULL; + fib_node_t *gparent = NULL; + fib_node_t *lpm = NULL; + fib_node_t *curr = fib->root; + while (curr) { + const hicn_prefix_t *curr_prefix = fib_entry_get_prefix(curr->entry); + + curr_len = hicn_prefix_get_len(curr_prefix); + match_len = hicn_prefix_lpm(prefix, curr_prefix); + + // store the lpm + if (match_len == curr_len && curr->is_used) lpm = curr; + + // curr_len >= prefix_len l >= L + // L is a prefix of l + // > means we did not find + // = means we could have found + if (match_len < curr_len || curr_len >= prefix_len) break; + + gparent = parent; + parent = curr; + + /* The following lookup won't fail since curr_len < prefix_len */ + uint8_t next_bit = hicn_prefix_get_bit(prefix, curr_len); + curr = curr->child[next_bit]; + } + + if (search) { + search->parent = parent; + search->gparent = gparent; + search->lpm = lpm; + if (curr) { + search->prefix_len = curr_len; + search->match_len = match_len; + } + } + return curr; +} + +/* + * Helper: insert a new node between parent and child. + * + * parent == NULL means we set the root of the FIB + * child == NULL means our node has no child + */ +fib_node_t *_fib_insert(fib_t *fib, fib_entry_t *entry, fib_node_t *parent, + fib_node_t *child, bool is_used) { + fib_node_t *new_node = fib_node_create(NULL, NULL, entry, is_used); + const hicn_prefix_t *prefix = fib_entry_get_prefix(entry); + + if (!parent) { + fib->root = new_node; + } else { + const hicn_prefix_t *parent_prefix = fib_entry_get_prefix(parent->entry); + uint32_t parent_prefix_len = hicn_prefix_get_len(parent_prefix); + uint8_t next_bit = hicn_prefix_get_bit(prefix, parent_prefix_len); + parent->child[next_bit] = new_node; + } + + if (child) { + const hicn_prefix_t *child_prefix = fib_entry_get_prefix(child->entry); + uint32_t match_len = hicn_prefix_lpm(prefix, child_prefix); + uint8_t next_bit = hicn_prefix_get_bit(child_prefix, match_len); + new_node->child[next_bit] = child; + } + + if (is_used) fib->size++; + return new_node; +} + +/* + * Helper: remove a node from parent + */ +void _fib_remove(fib_t *fib, fib_node_t *curr, fib_node_t *parent) { + /* + * If we remove the node, curr has either 0 or 1 child. In the latter case, + * we attach it to parent + */ + fib_node_t *child = curr->child[ZERO] ? curr->child[ZERO] : curr->child[ONE]; + if (!parent) { + fib->root = child; + } else { + if (parent->child[ZERO] == curr) + parent->child[ZERO] = child; + else + parent->child[ONE] = child; + } + if (curr->is_used) fib->size--; + fib_node_free(curr); +} + +/* + * - Stop condition: curr == NULL. This corresponds to: + * + * (CASE 1) Our parent is a strict prefix and we simply have to create a new + * leaf child in the correct branch based on the next bit following the parent + * prefix. + * + * Otherwise, our parent node exist. Based on the stop condition, we + * either have: + * + * - Stop condition 1 : curr_len == match_len AND curr_len >= + * prefix_len l == m && l >= L + * + * 2 sub-cases: + * - l = m > L : IMPOSSIBLE L < m since m = LPM(l, L) means L >= m + * - l = m = L : insert the current node, either it exists or not + * + * We thus have: + * + * (CASE 2) The node already exist. If is not in use we turn it on and we set + * the right fib entry. + * + * The case when it is used should never occur because of the way we add + * entries in the FIB... but let's add the nexthops we wish to insert into + * the existing FIB entry. + * + * - Stop condition 2: curr_len != match_len + * l != m => l > m + * + * We have two possibilities: + * - Only one is bigger than m (case 3) + * - They are both bigger than m (case 4) + * + * (CASE 3) Only one is bigger than m + * L == m => L < l (since l != m and l >= m) + * l > L = m + * + * This means L is a prefix of l. + * l' + * / + * L + * / + * l + * + * (CASE 4) They are both bigger than m + * - l > L > m + * - L > l > m + * - L = l > m + * + * Both share L and l share l' as a common prefix, and this is not l' since + * they share the name next bit. + * + * So this case is impossible and we would have taken the other branch during + * the descent: + * + * l' + * / \ + * l L + * + * We are in a situation where e need to insert an internal node: + * + * l' + * | + * X <------ internal node + * / \ + * l L + */ +fib_entry_t *fib_add(fib_t *fib, fib_entry_t *entry) { + assert(fib); + assert(entry); + + const hicn_prefix_t *prefix = fib_entry_get_prefix(entry); + uint32_t prefix_len = hicn_prefix_get_len(prefix); + + fib_search_t search; + fib_node_t *curr = fib_search(fib, prefix, &search); + + char buf[MAXSZ_HICN_PREFIX]; + char buf2[MAXSZ_HICN_PREFIX]; + int rc; + + rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "%s", "(error)"); + if (curr) { + rc = hicn_prefix_snprintf(buf2, MAXSZ_HICN_PREFIX, + fib_entry_get_prefix(curr->entry)); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf2, MAXSZ_HICN_PREFIX, "%s", "(error)"); + } else { + snprintf(buf2, MAXSZ_HICN_PREFIX, "%s", "(null)"); + search.prefix_len = 0; + search.match_len = 0; + } + DEBUG("fib_search(%s) result: curr=%s, prefix_len=%d; match_len=%d\n", buf, + buf2, search.prefix_len, search.match_len); + + /* Case 1 */ + if (!curr) { + _fib_insert(fib, entry, search.parent, NULL, true); + goto END; + } + + /* Case 2 */ + if (search.prefix_len == search.match_len && prefix_len == search.match_len) { + const nexthops_t *nexthops = fib_entry_get_nexthops(entry); + nexthops_foreach(nexthops, nexthop, + { fib_entry_nexthops_add(curr->entry, nexthop); }); + fib_entry_free(entry); + entry = curr->entry; + curr->is_used = true; + goto END; + } + + /* Case 3 */ + if (prefix_len == search.match_len) { + _fib_insert(fib, entry, search.parent, curr, true); + goto END; + } + + /* Case 4 */ + hicn_prefix_t inner_prefix; /* dup'ed in fib_entry_create */ + hicn_prefix_copy(&inner_prefix, prefix); + + hicn_prefix_truncate(&inner_prefix, search.match_len); + + fib_entry_t *inner_entry = fib_entry_create( + &inner_prefix, STRATEGY_TYPE_UNDEFINED, NULL, fib->forwarder); + + fib_node_t *new_node = + _fib_insert(fib, inner_entry, search.parent, curr, false); + _fib_insert(fib, entry, new_node, NULL, true); + +END: +#if 0 + fib_dump(fib); +#endif + return entry; +} + +/* + * Implementation details: + * + * To find whether the fib contains a prefix, we issue a search, and based on + * the stopping conditions, we return the fib note if and only if curr + * is not NULL, and prefix_len == curr_len (== match_len) + */ +fib_node_t *fib_contains_node(const fib_t *fib, const hicn_prefix_t *prefix) { + assert(fib); + assert(prefix); + + uint32_t prefix_len = hicn_prefix_get_len(prefix); + + fib_search_t search; + fib_node_t *curr = fib_search(fib, prefix, &search); + + if (!curr) return NULL; + if (search.match_len != prefix_len) return NULL; + if (prefix_len != search.prefix_len) return NULL; + + return curr->is_used ? curr : NULL; +} + +fib_entry_t *fib_contains(const fib_t *fib, const hicn_prefix_t *prefix) { + fib_node_t *node = fib_contains_node(fib, prefix); + if (!node) return NULL; + return node->entry; +} + +/* + * @brief Remove a prefix (and the associated node) from FIB + * + * We search for + * + * Actions depend on N, the number of children of the node to remove + * Examples are build using 'left' children only, but the cases with 'right' + * children are symmetrical. + * + * Legend: + * (empty) : no children + * * : 0 or more children + * + : at least one children + * + * N == 2 - Mark the node as unused + * + * parent parent + * / \ / \ + * curr ... ==> (curr) ... + * / \ / \ + * L R L R + * + * N == 1 - Attach the child to the parent node (whether parent is used or not) + * + * a) curr has no parent (curr is the root) + * + * curr + + * / ==> + * + + * + * b) curr has a parent + * parent parent + * / \ / \ + * curr * ==> L * + * / \ + * L + * + * (parent) (parent) + * / \ / \ + * curr + ==> L + + * / \ + * L + * + * N == 0 + * + * a) curr has no parent (curr is the root) + * + * curr + * / \ ==> + * + * b) parent is unused. + * + * Assuming curr is the left child, then parent must have a + * right child, and the grand-parent must be used. + * + * gp gp gp + * / / / + * (parent) ==> (parent) ==> + + * / \ / \ + * curr + + + * / \ + * + * c) parent is used. + * + * Assuming curr is the left child, we simply remove it from + * parent, leaving parent unchanged whether it has a right child or not. + * + * parent parent + * / \ / \ + * curr * ==> * + * / \ + * + * + */ +static void fib_node_remove(fib_t *fib, const hicn_prefix_t *prefix) { + assert(fib); + assert(prefix); + + uint32_t prefix_len = hicn_prefix_get_len(prefix); + + fib_search_t search; + fib_node_t *curr = fib_search(fib, prefix, &search); + + /* + * If we reach a NULL, unused node, or a node not matching, that means the + * node does not exist + */ + if (!curr || !curr->is_used || (search.prefix_len != prefix_len)) return; + + uint8_t N = 0; + if (curr->child[ZERO]) N++; + if (curr->child[ONE]) N++; + + switch (N) { + case 2: + curr->is_used = false; + break; + + case 1: + _fib_remove(fib, curr, search.parent); + break; + + case 0: + _fib_remove(fib, curr, search.parent); + if (search.parent && !search.parent->is_used) + _fib_remove(fib, search.parent, search.gparent); + break; + } +} + +void fib_remove(fib_t *fib, const hicn_prefix_t *prefix, unsigned conn_id) { + assert(fib); + assert(prefix); + + fib_node_t *node = fib_contains_node(fib, prefix); + if (!node) return; + + fib_entry_nexthops_remove(node->entry, conn_id); + if (fib_entry_nexthops_len(node->entry) == 0) + /* When using MAP-Me, we keep empty FIB entries but we mark them as unused*/ +#ifdef WITH_MAPME + node->is_used = false; +#else + fib_node_remove(fib, name); +#endif /* WITH_MAPME */ +} + +static size_t fib_node_remove_connection_id(fib_node_t *node, unsigned conn_id, + fib_entry_t **array, size_t pos) { + if (!node) return pos; + if (node->is_used) { + fib_entry_nexthops_remove(node->entry, conn_id); + + /* When using MAP-Me, we keep empty FIB entries but we mark them as unused*/ + if (fib_entry_nexthops_len(node->entry) == 0) +#ifdef WITH_MAPME + node->is_used = false; +#else + array[pos++] = node->entry; +#endif /* WITH_MAPME */ + } + pos = fib_node_remove_connection_id(node->child[ONE], conn_id, array, pos); + pos = fib_node_remove_connection_id(node->child[ZERO], conn_id, array, pos); + return pos; +} + +void fib_remove_entry(fib_t *fib, fib_entry_t *entry) { + fib_node_remove(fib, fib_entry_get_prefix(entry)); +} + +void fib_remove_connection(fib_t *fib, unsigned conn_id, + fib_entry_t ***removed_entries, + size_t *num_removed_entries) { + assert(fib); + + fib_entry_t **array = malloc(sizeof(fib_entry_t *) * fib->size); + + size_t pos = 0; + pos = fib_node_remove_connection_id(fib->root, conn_id, array, pos); + + if (removed_entries) { + /* + * The caller is taking charge of releasing entries (as well as the returned + * array + */ + assert(num_removed_entries); + + *removed_entries = array; + *num_removed_entries = pos; + + } else { + for (int i = 0; i < pos; i++) + fib_node_remove(fib, fib_entry_get_prefix(array[i])); + } + free(array); +} + +fib_entry_t *fib_match_msgbuf(const fib_t *fib, const msgbuf_t *msgbuf) { + assert(fib); + assert(msgbuf); + + return fib_match_name(fib, msgbuf_get_name(msgbuf)); +} + +/* + * fib_search returns the longest non-strict subprefix. + * the LPM is stored in search if it exists + */ +fib_entry_t *fib_match_prefix(const fib_t *fib, const hicn_prefix_t *prefix) { + assert(fib); + assert(prefix); + + fib_search_t search; + fib_search(fib, prefix, &search); + if (!search.lpm) return NULL; + return search.lpm->entry; +} + +fib_entry_t *fib_match_name(const fib_t *fib, const hicn_name_t *name) { + hicn_prefix_t prefix; + const hicn_name_prefix_t *name_prefix = hicn_name_get_prefix(name); + prefix.name = *name_prefix; + prefix.len = hicn_name_prefix_get_len_bits(name_prefix); + return fib_match_prefix(fib, &prefix); +} + +static size_t fib_node_collect_entries(fib_node_t *node, fib_entry_t **array, + size_t pos) { + assert(array); + + if (!node) return pos; + + if (node->is_used) array[pos++] = node->entry; + + pos = fib_node_collect_entries(node->child[ONE], array, pos); + pos = fib_node_collect_entries(node->child[ZERO], array, pos); + return pos; +} + +size_t fib_get_entry_array(const fib_t *fib, fib_entry_t ***array_p) { + size_t pos = 0; + *array_p = malloc(sizeof(fib_entry_t *) * fib->size); + if (!*array_p) return pos; + pos = fib_node_collect_entries(fib->root, *array_p, pos); + return pos; +} + +bool _fib_is_valid(const fib_node_t *node) { + if (!node) return true; + + const hicn_prefix_t *prefix = fib_entry_get_prefix(node->entry); + uint32_t prefix_len = hicn_prefix_get_len(prefix); + + for (unsigned i = 0; i < 2; i++) { + const fib_node_t *child = node->child[i]; + if (!child) continue; + const hicn_prefix_t *child_prefix = fib_entry_get_prefix(child->entry); + + uint32_t match_len = hicn_prefix_lpm(prefix, child_prefix); + if (match_len != prefix_len) return false; + if (hicn_prefix_get_bit(child_prefix, match_len) != i) return false; + if (!_fib_is_valid(child)) return false; + } + return true; +} + +/* + * @brief Check that the structure of the FIB is correct : prefixes are + * correctly nested, 0 are on the left, 1 on the right, and that we have no + * more than 1 unused prefix as parents. + */ +bool fib_is_valid(const fib_t *fib) { return _fib_is_valid(fib->root); } + +/* + * Checks whether the preorder traversal of the sub-tree corresponds to the + * prefix and used arrays, starting from pos (helper) + */ +bool __fib_check_preorder(const fib_node_t *node, + const hicn_prefix_t **prefix_array, bool *used_array, + size_t size, size_t *pos) { + /* Check left subtree... */ + fib_node_t *left = node->child[ZERO]; + if (left && !__fib_check_preorder(left, prefix_array, used_array, size, pos)) + return false; + + /* ... then current node ... */ + if (*pos > size) { + ERROR("size error"); + return false; + } + + const hicn_prefix_t *prefix = fib_entry_get_prefix(node->entry); + + if (!hicn_prefix_equals(prefix, prefix_array[*pos])) { + char buf[MAXSZ_HICN_PREFIX]; + char buf2[MAXSZ_HICN_PREFIX]; + int rc; + + rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "%s", "(error)"); + rc = hicn_prefix_snprintf(buf2, MAXSZ_HICN_PREFIX, prefix_array[*pos]); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf2, MAXSZ_HICN_PREFIX, "%s", "(error)"); + ERROR("Prefix mismatch in position %ld %s != %s", *pos, buf, buf2); + return false; + } + + (*pos)++; + + /* ... then right subtree */ + fib_node_t *right = node->child[ONE]; + if (right && + !__fib_check_preorder(right, prefix_array, used_array, size, pos)) + return false; + + return true; +} + +/* + * Checks whether the preorder traversal of the trie + * corresponds to the prefix and used arrays. + */ +bool _fib_check_preorder(const fib_t *fib, const hicn_prefix_t **prefix_array, + bool *used_array, size_t size) { + if (!fib->root) return true; + size_t pos = 0; + if (!__fib_check_preorder(fib->root, prefix_array, used_array, size, &pos)) + return false; + /* We need to check that we don't miss elements */ + return pos == size; +} + +// XXX print empty node but not recurse +void _fib_dump(const fib_node_t *node, int start, int indent, int location) { + char buf[MAXSZ_HICN_PREFIX]; + + if (node) { + const hicn_prefix_t *prefix = fib_entry_get_prefix(node->entry); + int rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "%s %d", "(error)", rc); + } else { + snprintf(buf, MAXSZ_HICN_PREFIX, "%s", "(null)"); + } + + if (indent > 0) { + for (int i = 0; i < start; i++) printf(" "); + for (int i = 0; i < indent - start - 1; i++) printf("| "); + printf("|_ "); + } + printf("%s", buf); + if (node && !node->is_used) printf(" [inner]"); + printf("\n"); + + if (!node) return; + + if (location == 2) { + start++; + location = 0; + } + _fib_dump(node->child[ZERO], start, indent + 1, + (location == 0) ? 1 : location); + _fib_dump(node->child[ONE], start, indent + 1, + (location == 0) ? 2 : location); +} + +void fib_dump(const fib_t *fib) { _fib_dump(fib->root, 0, 0, 0); } diff --git a/hicn-light/src/hicn/core/fib.h b/hicn-light/src/hicn/core/fib.h new file mode 100644 index 000000000..4fb5009f5 --- /dev/null +++ b/hicn-light/src/hicn/core/fib.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HICNLIGHT_FIB_H +#define HICNLIGHT_FIB_H + +#include "fib_entry.h" +#include "msgbuf.h" +#include <hicn/name.h> + +#define _fib_var(x) _fib_##x + +typedef struct fib_s fib_t; + +fib_t *fib_create(void *forwarder); + +void fib_free(fib_t *fib); + +size_t fib_get_size(const fib_t *fib); + +fib_entry_t *fib_add(fib_t *fib, fib_entry_t *node); + +fib_entry_t *fib_contains(const fib_t *fib, const hicn_prefix_t *prefix); + +void fib_remove(fib_t *fib, const hicn_prefix_t *prefix, unsigned conn_id); + +void fib_remove_entry_connection(fib_t *fib, fib_entry_t *entry, + unsigned conn_id, fib_entry_t **removed_entry); + +void fib_remove_name_connection(fib_t *fib, const hicn_prefix_t *prefix, + unsigned conn_id); + +void fib_remove_entry(fib_t *fib, fib_entry_t *entry); + +void fib_remove_connection(fib_t *fib, unsigned conn_id, + fib_entry_t ***removed_entries, + size_t *num_removed_entries); + +fib_entry_t *fib_match_msgbuf(const fib_t *fib, const msgbuf_t *msgbuf); + +fib_entry_t *fib_match_prefix(const fib_t *fib, const hicn_prefix_t *prefix); + +fib_entry_t *fib_match_name(const fib_t *fib, const hicn_name_t *name); + +size_t fib_get_entry_array(const fib_t *fib, fib_entry_t ***array_p); + +/* + * NOTE : do not use return on the loop body to avoid leaking memory + */ +#define fib_foreach_entry(FIB, ENTRY, BODY) \ + do { \ + fib_entry_t **_fib_var(array); \ + size_t _fib_var(n) = fib_get_entry_array((FIB), &_fib_var(array)); \ + size_t _fib_var(i); \ + fib_entry_t *ENTRY; \ + for (_fib_var(i) = 0; _fib_var(i) < _fib_var(n); _fib_var(i)++) { \ + ENTRY = _fib_var(array)[_fib_var(i)]; \ + do { \ + BODY \ + } while (0); \ + } \ + free(_fib_var(array)); \ + } while (0) + +bool fib_is_valid(const fib_t *fib); +bool _fib_check_preorder(const fib_t *fib, const hicn_prefix_t **prefix_array, + bool *used_array, size_t size); + +#define fib_check_preorder(F, PA, UA) \ + _fib_check_preorder(F, PA, UA, sizeof(PA) / sizeof(hicn_prefix_t *)) + +void fib_dump(const fib_t *fib); + +#endif /* HICNLIGHT_FIB_H */ diff --git a/hicn-light/src/hicn/core/fib_entry.c b/hicn-light/src/hicn/core/fib_entry.c new file mode 100644 index 000000000..b510b68b4 --- /dev/null +++ b/hicn-light/src/hicn/core/fib_entry.c @@ -0,0 +1,667 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include <hicn/hicn-light/config.h> +#include <hicn/core/fib_entry.h> +#include <hicn/core/strategy.h> + +#ifdef WITH_MAPME +#include <hicn/core/ticks.h> +#endif /* WITH_MAPME */ + +#ifdef WITH_POLICY +#include <hicn/core/forwarder.h> +#include <hicn/policy.h> + +#ifdef WITH_MAPME +#include <hicn/core/mapme.h> +#endif /* WITH_MAPME */ + +#endif /* WITH_POLICY */ + +#ifdef WITH_POLICY_STATS +#include <hicn/core/policy_stats.h> +#endif /* WITH_POLICY_STATS */ + +fib_entry_t *fib_entry_create(const hicn_prefix_t *prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options, + const forwarder_t *forwarder) { + assert(prefix); + /* + * For tests, we allow forwarder to be NULL, some + * functions cannot be called but otherwise we need a main loop, etc. + */ + // assert(forwarder); + + fib_entry_t *entry = malloc(sizeof(fib_entry_t)); + if (!entry) goto ERR_MALLOC; + + memset(entry, 0, sizeof(*entry)); + hicn_prefix_copy(&entry->prefix, prefix); + entry->nexthops = NEXTHOPS_EMPTY; + + fib_entry_add_strategy_options(entry, STRATEGY_TYPE_BESTPATH, NULL); + fib_entry_add_strategy_options(entry, STRATEGY_TYPE_REPLICATION, NULL); + fib_entry_set_strategy(entry, strategy_type, strategy_options); + +#ifdef WITH_MAPME + entry->user_data = NULL; + entry->user_data_release = NULL; +#endif /* WITH_MAPME */ + + entry->forwarder = forwarder; + +#ifdef WITH_POLICY + entry->policy = POLICY_EMPTY; +#endif /* WITH_POLICY */ + +#ifdef WITH_POLICY_STATS + entry->policy_stats = POLICY_STATS_EMPTY; + entry->policy_counters = POLICY_COUNTERS_EMPTY; +#endif /* WITH_POLICY_STATS */ + + return entry; + +ERR_MALLOC: + return NULL; +} + +void fib_entry_free(fib_entry_t *entry) { + assert(entry); +#ifdef WITH_MAPME + if (entry->user_data) entry->user_data_release(&entry->user_data); +#endif /* WITH_MAPME */ + free(entry); +} + +void fib_entry_add_strategy_options(fib_entry_t *entry, + strategy_type_t strategy_type, + strategy_options_t *strategy_options) { + // for the moment only the best path and replication strategies support + // strategy options return for the other strategyes + if (strategy_type != STRATEGY_TYPE_BESTPATH && + strategy_type != STRATEGY_TYPE_REPLICATION) + return; + + if (!strategy_options) { + if (strategy_type == STRATEGY_TYPE_BESTPATH) { + entry->strategy.options.bestpath.local_prefixes = NULL; + } else { + entry->strategy.options.replication.local_prefixes = NULL; + } + return; + } + + local_prefixes_t *new_prefixes; + local_prefixes_t *curr_prefixes; + + if (strategy_type == STRATEGY_TYPE_BESTPATH) { + new_prefixes = strategy_options->bestpath.local_prefixes; + curr_prefixes = entry->strategy.options.bestpath.local_prefixes; + + if (!curr_prefixes) { + entry->strategy.options.bestpath.local_prefixes = create_local_prefixes(); + curr_prefixes = entry->strategy.options.bestpath.local_prefixes; + } + } else { + new_prefixes = strategy_options->replication.local_prefixes; + curr_prefixes = entry->strategy.options.replication.local_prefixes; + + if (!curr_prefixes) { + entry->strategy.options.replication.local_prefixes = + create_local_prefixes(); + curr_prefixes = entry->strategy.options.replication.local_prefixes; + } + } + + local_prefixes_add_prefixes(curr_prefixes, new_prefixes); +} + +void fib_entry_set_strategy(fib_entry_t *entry, strategy_type_t strategy_type, + strategy_options_t *strategy_options) { + // Default strategy if undefined + if (!STRATEGY_TYPE_VALID(strategy_type)) strategy_type = STRATEGY_TYPE_RANDOM; + + if (entry->strategy.type == strategy_type) { // startegy alredy set + if (strategy_type != STRATEGY_TYPE_BESTPATH) { + return; // nothing to do + } else { + strategy_initialize(&entry->strategy, entry->forwarder); + return; + } + } + + entry->strategy.type = strategy_type; + if (strategy_options) entry->strategy.options = *strategy_options; + + strategy_initialize(&entry->strategy, entry->forwarder); +} + +/* + * Filters the set of nexthops passed as parameters (and not the one stored in + * the FIB entry + */ +nexthops_t *fib_entry_filter_nexthops(fib_entry_t *entry, nexthops_t *nexthops, + unsigned ingress_id, bool prefer_local) { + assert(entry); + assert(nexthops); + + nexthops_reset(nexthops); + + // DEBUG("[fib_entry_filter_nexthops] num=%d/%d ingress_id=%d", + // nexthops_get_curlen(nexthops), nexthops_get_len(nexthops), + // ingress_id); + + /* Filter out ingress, down & administrative down faces */ + const connection_table_t *table = + forwarder_get_connection_table(entry->forwarder); + connection_t *conn; + uint_fast32_t flags; + + hicn_policy_t policy = fib_entry_get_policy(entry); + + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, nexthop == ingress_id); + nexthops_disable_if(nexthops, i, + (connection_get_admin_state(conn) == FACE_STATE_DOWN)); + nexthops_disable_if(nexthops, i, + (connection_get_state(conn) == FACE_STATE_DOWN)); + }); + + // DEBUG("After pruning, num=%d/%d", nexthops_get_curlen(nexthops), + // nexthops_get_len(nexthops)); + + if (prefer_local) { + /* Backup flags and cur_len*/ + flags = nexthops->flags; + size_t cur_len = nexthops_get_curlen(nexthops); + + /* Filter local */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, (!connection_is_local(conn))); + }); + + /* Local faces have priority */ + if (nexthops_get_curlen(nexthops) > 0) return nexthops; + + nexthops->flags = flags; + nexthops->cur_elts = cur_len; + } + + /* Filter out local */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, (connection_is_local(conn))); + +#ifdef WITH_POLICY + /* Policy filtering : next hops */ + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_WIRED))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_WIRED))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_WIFI))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_WIFI))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_CELLULAR))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_CELLULAR))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE) && + (!connection_has_tag(conn, POLICY_TAG_TRUSTED))); + nexthops_disable_if( + nexthops, i, + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_PROHIBIT) && + (connection_has_tag(conn, POLICY_TAG_TRUSTED))); +#endif /* WITH_POLICY */ + }); + + if (nexthops_get_curlen(nexthops) == 0) { + DEBUG("After REQUIRE/PROHIBIT pruning, num=%d/%d", + nexthops_get_curlen(nexthops), nexthops_get_len(nexthops)); + return nexthops; + } + + /* We have at least one matching next hop, implement heuristic */ + +#ifdef WITH_POLICY + /* + * As VPN connections might trigger duplicate uses of one interface, we start + * by filtering out interfaces based on trust status. + */ + flags = nexthops->flags; + + if ((policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE) || + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_PREFER)) { + /* Try to filter out NON TRUSTED faces */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + (!connection_has_tag(conn, POLICY_TAG_TRUSTED))); + }); + + if ((nexthops_get_curlen(nexthops) == 0) && + (policy.tags[POLICY_TAG_TRUSTED].state == POLICY_STATE_REQUIRE)) { + return nexthops; + } + + } else { + /* Try to filter out TRUSTED faces */ + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + (connection_has_tag(conn, POLICY_TAG_TRUSTED))); + }); + } + + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + + /* Other preferences */ + if (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_WIRED)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_WIFI)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_AVOID) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_has_tag(conn, POLICY_TAG_CELLULAR)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } + + if (policy.tags[POLICY_TAG_WIRED].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_WIRED)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_WIFI].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_WIFI)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } + if (policy.tags[POLICY_TAG_CELLULAR].state == POLICY_STATE_PREFER) { + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + !connection_has_tag(conn, POLICY_TAG_CELLULAR)); + }); + if (nexthops_get_curlen(nexthops) == 0) nexthops->flags = flags; + } +// XXX backup curlen ??? +#endif /* WITH_POLICY */ + + DEBUG("[fib_entry_filter_nexthops] before face priority num=%d/%d", + nexthops_get_curlen(nexthops), nexthops_get_len(nexthops)); + + /* Priority */ + uint32_t max_priority = 0; + nexthops_foreach(nexthops, nexthop, { + conn = connection_table_at(table, nexthop); + uint32_t priority = connection_get_priority(conn); + if (priority > max_priority) max_priority = priority; + }); + nexthops_enumerate(nexthops, i, nexthop, { + conn = connection_table_at(table, nexthop); + nexthops_disable_if(nexthops, i, + connection_get_priority(conn) < max_priority); + }); + + DEBUG("[fib_entry_filter_nexthops] result num=%d/%d", + nexthops_get_curlen(nexthops), nexthops_get_len(nexthops)); + + /* Nexthop priority */ + + /* + * Filter out nexthops with lowest strategy priority. + * Initializing at 0 allows to disable nexthops with a negative priority + */ + max_priority = 0; + nexthops_enumerate(nexthops, i, nexthop, { + (void)nexthop; + int priority = nexthops->state[i].priority; + if (priority > max_priority) max_priority = priority; + }); + nexthops_enumerate(nexthops, i, nexthop, { + int priority = nexthops->state[i].priority; + nexthops_disable_if(nexthops, i, (priority < max_priority)); + }); + + /* + * If multipath is disabled, we don't offer much choice to the forwarding + * strategy, but still go through it for accounting purposes. + */ + if ((policy.tags[POLICY_TAG_MULTIPATH].state == POLICY_STATE_PROHIBIT) || + (policy.tags[POLICY_TAG_MULTIPATH].state == POLICY_STATE_AVOID)) { + DEBUG( + "[fib_entry_get_nexthops_from_strategy] select single nexthops due to " + "multipath policy"); + nexthops_select_first(nexthops); + } + + return nexthops; +} + +/* + * Retrieve all candidate nexthops for sending mapme updates == all non local + * connections. We don't apply the policy at this stage. + */ +nexthops_t *fib_entry_get_mapme_nexthops(fib_entry_t *entry, + nexthops_t *new_nexthops) { + assert(new_nexthops); + + const connection_table_t *table = + forwarder_get_connection_table(entry->forwarder); + + /* We create a nexthop structure based on connections */ + // XXX This should be done close to where it is needed + connection_t *connection; + connection_table_foreach(table, connection, { + if (connection_is_local(connection)) continue; + new_nexthops->elts[nexthops_get_len(new_nexthops)] = + connection_table_get_connection_id(table, connection); + nexthops_inc(new_nexthops); + }); + + return new_nexthops; +} + +/* + * Update available next hops following policy update. + * + * The last nexthop parameter is only used if needed, otherwise the pointer to + * fib entry is returned to avoid an useless copy + */ +nexthops_t *fib_entry_get_available_nexthops(fib_entry_t *entry, + unsigned ingress_id, + nexthops_t *new_nexthops) { + // DEBUG("[fib_entry_get_available_nexthops]"); + + connection_table_t *table = forwarder_get_connection_table(entry->forwarder); + + /* + * Give absolute preference to local faces, with no policy, unless + * ingress_id == ~0, which means we are searching faces on which to + * advertise our prefix + */ + if (ingress_id == ~0) { + assert(new_nexthops); + /* We create a nexthop structure based on connections */ + // XXX This should be done close to where it is needed + connection_t *connection; + connection_table_foreach(table, connection, { + if (connection_is_local(connection)) continue; + new_nexthops->elts[nexthops_get_len(new_nexthops)] = + (unsigned int)connection_table_get_connection_id(table, connection); + nexthops_inc(new_nexthops); + }); + +#ifdef WITH_POLICY + return fib_entry_filter_nexthops(entry, new_nexthops, ingress_id, false); +#else + return new_nexthops; +#endif + } + +#ifdef WITH_POLICY + return fib_entry_filter_nexthops(entry, fib_entry_get_nexthops(entry), + ingress_id, true); +#else + return fib_entry_get_nexthops(entry); +#endif +} + +#ifdef WITH_POLICY + +hicn_policy_t fib_entry_get_policy(const fib_entry_t *entry) { + return entry->policy; +} + +void fib_entry_set_policy(fib_entry_t *entry, hicn_policy_t policy) { + INFO("fib_entry_set_policy"); + entry->policy = policy; + + forwarder_on_route_event(entry->forwarder, entry); + + // XXX generic mechanism to perform a mapme update +#if 0 +#ifdef WITH_MAPME + /* + * Skip entries that do not correspond to a producer ( / have a locally + * served prefix / have no local connection as next hop) + */ + if (!fib_entry_has_local_nexthop(entry)) return; + mapme_t *mapme = forwarder_get_mapme(entry->forwarder); +#endif /* WITH_MAPME */ +#endif +} + +#endif /* WITH_POLICY */ + +void fib_entry_nexthops_add(fib_entry_t *entry, unsigned nexthop) { + /* + * Nexthop is added to both: + * - fib_entry: it is added to the nexthops_t struct, in the elts array. + * - strategy: only used to eventually initialize internal state, might be + * empty like in the random strategy. + */ + off_t id = nexthops_add(&entry->nexthops, nexthop); + strategy_add_nexthop(&entry->strategy, &entry->nexthops, id); +} + +void fib_entry_nexthops_remove(fib_entry_t *entry, unsigned nexthop) { + off_t id = nexthops_remove(&entry->nexthops, nexthop); + strategy_remove_nexthop(&entry->strategy, &entry->nexthops, id); +} + +nexthops_t *fib_entry_get_nexthops_from_strategy(fib_entry_t *entry, + const msgbuf_t *msgbuf, + bool is_retransmission) { + assert(entry); + assert(msgbuf); + + // DEBUG("[fib_entry_get_nexthops_from_strategy]"); + + const policy_stats_mgr_t *mgr = + forwarder_get_policy_stats_mgr(entry->forwarder); + assert(mgr); + + /* Filtering */ + nexthops_t *nexthops = fib_entry_get_available_nexthops( + entry, msgbuf_get_connection_id(msgbuf), NULL); + if (nexthops_get_curlen(nexthops) == 0) return nexthops; + +#ifdef WITH_POLICY_STATS + /* + * Update statistics about loss rates. We only detect losses upon + * retransmissions, and assume for the computation that the candidate set of + * output faces is the same as previously (i.e. does not take into account + * event such as face up/down, policy update, etc. Otherwise we would need to + * know what was the previous choice ! + */ + if (is_retransmission) + policy_stats_on_retransmission(mgr, &entry->policy_counters, nexthops); +#endif /* WITH_POLICY_STATS */ + + /* + * NOTE: We might want to call a forwarding strategy even with no nexthop to + * take a fallback decision. + */ + if (nexthops_get_curlen(nexthops) == 0) return nexthops; + +#ifdef WITH_POLICY + + /* + * Filter out nexthops with lowest strategy priority. + * Initializing at 0 allows to disable nexthops with a negative priority + */ + unsigned max_priority = 0; + nexthops_enumerate(nexthops, i, nexthop, { + int priority = nexthops->state[i].priority; + if (priority > max_priority) max_priority = priority; + }); + nexthops_enumerate(nexthops, i, nexthop, { + int priority = nexthops->state[i].priority; + nexthops_disable_if(nexthops, i, (priority < max_priority)); + }); + + /* + * If multipath is disabled, we don't offer much choice to the forwarding + * strategy, but still go through it for accounting purposes. + */ + hicn_policy_t policy = fib_entry_get_policy(entry); + if ((policy.tags[POLICY_TAG_MULTIPATH].state == POLICY_STATE_PROHIBIT) || + (policy.tags[POLICY_TAG_MULTIPATH].state == POLICY_STATE_AVOID)) { + DEBUG( + "[fib_entry_get_nexthops_from_strategy] select single nexthops due to " + "multipath policy"); + nexthops_select_first(nexthops); + } + +#endif /* WITH_POLICY */ + + // DEBUG("[fib_entry_get_nexthops_from_strategy] calling lookup_nethops " + // "on strategy (num=%d)", nexthops_get_len(nexthops)); + return strategy_lookup_nexthops(&entry->strategy, nexthops, msgbuf); +} + +void fib_entry_on_data(fib_entry_t *entry, nexthops_t *data_nexthops, + const msgbuf_t *msgbuf, Ticks pitEntryCreation, + Ticks objReception) { + assert(entry); + assert(data_nexthops); + assert(msgbuf); + +#ifdef WITH_POLICY_STATS + if (pitEntryCreation != 0) { // if pitEntryCreation we no match in the pit + // was found + const policy_stats_mgr_t *mgr = + forwarder_get_policy_stats_mgr(entry->forwarder); + Ticks rtt = objReception - pitEntryCreation; + policy_stats_on_data(mgr, &entry->policy_stats, &entry->policy_counters, + data_nexthops, msgbuf, rtt); + } +#endif /* WITH_POLICY_STATS */ + + if (pitEntryCreation != 0 || + (fib_entry_strategy_type(entry) == STRATEGY_TYPE_BESTPATH)) { + strategy_on_data(&entry->strategy, &entry->nexthops, data_nexthops, msgbuf, + pitEntryCreation, objReception); + } +} + +void fib_entry_on_timeout(fib_entry_t *entry, + const nexthops_t *timeout_nexthops) { + assert(entry); + assert(timeout_nexthops); + +#ifdef WITH_POLICY_STATS + const policy_stats_mgr_t *mgr = + forwarder_get_policy_stats_mgr(entry->forwarder); + policy_stats_on_timeout(mgr, &entry->policy_counters, timeout_nexthops); +#endif /* WITH_POLICY_STATS */ + + strategy_on_timeout(&entry->strategy, &entry->nexthops, timeout_nexthops); +} + +const hicn_prefix_t *fib_entry_get_prefix(const fib_entry_t *entry) { + assert(entry); + return &(entry->prefix); +} + +/* + * Return true if we have at least one local connection as next hop + */ +bool fib_entry_has_local_nexthop(const fib_entry_t *entry) { + connection_table_t *table = forwarder_get_connection_table(entry->forwarder); + + nexthops_foreach(fib_entry_get_nexthops(entry), nexthop, { + const connection_t *conn = connection_table_at(table, nexthop); + /* Ignore non-local connections */ + if (!connection_is_local(conn)) continue; + return true; + }); + return false; +} + +bool fib_entry_has_all_local_nexthops(const fib_entry_t *entry) { + connection_table_t *table = forwarder_get_connection_table(entry->forwarder); + + int count = 0; + nexthops_foreach(fib_entry_get_nexthops(entry), nexthop, { + const connection_t *conn = connection_table_at(table, nexthop); + /* Ignore non-local connections */ + if (!connection_is_local(conn)) return false; + count++; + }); + return (count > 0); +} + +#ifdef WITH_MAPME + +void *fib_entry_get_user_data(const fib_entry_t *entry) { + assert(entry); + + return entry->user_data; +} + +void fib_entry_set_user_data(fib_entry_t *entry, const void *user_data, + void (*user_data_release)(void **)) { + assert(entry); + assert(user_data); + assert(user_data_release); + + entry->user_data = (void *)user_data; + entry->user_data_release = user_data_release; +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/hicn/core/fib_entry.h b/hicn-light/src/hicn/core/fib_entry.h new file mode 100644 index 000000000..e1e48c871 --- /dev/null +++ b/hicn-light/src/hicn/core/fib_entry.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file fib_entry.h + * @brief A forwarding entry in the FIB table + * + * A Forwarding Information Base (FIB) entry (fib_entry_t) is a + * set of nexthops for a name. It also indicates the forwarding strategy. + * + * Each nexthop contains the ConnectionId assocaited with it. This could be + * something specific like a MAC address or point-to-point tunnel. Or, it + * could be something general like a MAC group address or ip multicast overlay. + * + * See strategy.h for a description of forwarding strategies. + * In short, a strategy is the algorithm used to select one or more nexthops + * from the set of available nexthops. + * + * Each nexthop also contains a void* to a forwarding strategy data container. + * This allows a strategy to keep proprietary information about each nexthop. + * + * + */ + +#ifndef fib_entry_h +#define fib_entry_h + +#include <hicn/name.h> +#include "strategy.h" +#include "msgbuf.h" +#include "nexthops.h" +#include "policy_stats.h" + +typedef struct { + hicn_prefix_t prefix; + unsigned refcount; + nexthops_t nexthops; + + /* This is used for producer prefixes only */ + uint32_t nexthops_hash; + + strategy_entry_t strategy; + + const void *forwarder; + +#ifdef WITH_POLICY + hicn_policy_t policy; +#endif /* WITH_POLICY */ + + policy_counters_t policy_counters; + policy_stats_t policy_stats; + +#ifdef WITH_MAPME +#if 0 + /* In case of no multipath, this stores the previous decision taken by policy. + * As the list of nexthops is not expected to change, we can simply store the + * flags */ + uint_fast32_t prev_nexthops_flags; +#endif + void *user_data; + void (*user_data_release)(void **user_data); +#endif /* WITH_MAPME */ +} fib_entry_t; + +#define _fib_entry_var(x) _fib_entry_##x + +#define fib_entry_strategy_type(fib_entry) ((fib_entry)->strategy.type) + +#define fib_entry_get_nexthops(fib_entry) (&(fib_entry)->nexthops) + +#define fib_entry_nexthops_len(fib_entry) \ + (nexthops_get_len(&(fib_entry)->nexthops)) +#define fib_entry_nexthops_curlen(fib_entry) \ + (nexthops_curlen(&(fib_entry)->nexthops)) +#define fib_entry_get_nexthop(fib_entry, i) ((fib_entry)->nexthops.elts[i]) +#define fib_entry_foreach_nexthop(fib_entry, nexthop, BODY) \ + nexthops_foreach(fib_entry->nexthops, BODY) + +#define fib_entry_get_nexthops_hash(E) ((E)->nexthops_hash) +#define fib_entry_set_nexthops_hash(E, H) (E)->nexthops_hash = (H) + +static inline void fib_entry_set_nexthops(fib_entry_t *entry, + nexthops_t *nexthops) { + entry->nexthops = *nexthops; +} + +static inline void fib_entry_initialize_nexthops(fib_entry_t *entry) { + entry->nexthops = NEXTHOPS_EMPTY; +} + +static inline bool fib_entry_nexthops_changed(fib_entry_t *entry, + nexthops_t *nexthops) { + uint32_t old_hash = fib_entry_get_nexthops_hash(entry); + uint32_t hash = nexthops_get_hash(nexthops); + if (hash != old_hash) { + fib_entry_set_nexthops_hash(entry, hash); + return true; + } + return false; +}; + +struct forwarder_s; + +/* + * This does a copy of the name passed as parameter + */ +fib_entry_t *fib_entry_create(const hicn_prefix_t *prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options, + const struct forwarder_s *table); + +void fib_entry_free(fib_entry_t *entry); + +void fib_entry_add_strategy_options(fib_entry_t *entry, + strategy_type_t strategy_type, + strategy_options_t *strategy_options); + +void fib_entry_set_strategy(fib_entry_t *fib_entry, + strategy_type_t strategy_type, + strategy_options_t *strategy_options); + +void fib_entry_nexthops_add(fib_entry_t *fib_entry, unsigned nexthop); + +void fib_entry_nexthops_remove(fib_entry_t *fib_entry, unsigned nexthop); + +/** + * @function fib_entry_nexthops_get + * @abstract Returns the nexthop set of the FIB entry. You must Acquire if it + * will be saved. + * @discussion + * Returns the next hop set for the FIB entry. + */ +const nexthops_t *fib_entry_nexthops_get(const fib_entry_t *fib_entry); + +const nexthops_t *fib_entry_nexthops_getFromForwardingStrategy( + fib_entry_t *fib_entry, const msgbuf_t *interest_msgbuf, + bool is_retransmission); + +void fib_entry_on_data(fib_entry_t *fib_entry, nexthops_t *nexthops, + const msgbuf_t *object_msgbuf, Ticks pit_entry_creation, + Ticks data_reception); + +#ifdef WITH_POLICY +hicn_policy_t fib_entry_get_policy(const fib_entry_t *fib_entry); +void fib_entry_reconsider_policy(fib_entry_t *fib_entry); +void fib_entry_set_policy(fib_entry_t *fib_entry, hicn_policy_t policy); +void fib_entry_update_stats(fib_entry_t *fib_entry, uint64_t now); +#endif /* WITH_POLICY */ + +nexthops_t *fib_entry_filter_nexthops(fib_entry_t *entry, nexthops_t *nexthops, + unsigned ingress_id, bool prefer_local); + +nexthops_t *fib_entry_get_mapme_nexthops(fib_entry_t *entry, + nexthops_t *new_nexthops); + +nexthops_t *fib_entry_get_available_nexthops(fib_entry_t *fib_entry, + unsigned in_connection, + nexthops_t *new_nexthops); +void fib_entry_on_timeout(fib_entry_t *fib_entry, const nexthops_t *egressId); +nexthops_t *fib_entry_get_nexthops_from_strategy( + fib_entry_t *fib_entry, const msgbuf_t *interest_msgbuf, + bool is_retransmission); + +/** + * @function fib_entry_get_prefix + * @return The FIB entry prefix + */ +const hicn_prefix_t *fib_entry_get_prefix(const fib_entry_t *fib_entry); + +bool fib_entry_has_local_nexthop(const fib_entry_t *entry); +bool fib_entry_has_all_local_nexthops(const fib_entry_t *entry); + +#ifdef WITH_MAPME + +/** + * @function fib_entry_get_user_data + * @abstract Returns user data associated to the FIB entry. + * @param [in] fib_entry - Pointer to the FIB entry. + * @return User data as a void pointer + */ +void *fib_entry_get_user_data(const fib_entry_t *fib_entry); + +/** + * @function fib_entry_get_user_data + * @abstract Associates user data and release callback to a FIB entry. + * @param [in] fib_entry - Pointer to the FIB entry. + * @param [in] user_data - Generic pointer to user data + * @param [in@ user_data_release - Callback used to release user data upon + * change of FIB entry removal. + */ +void fib_entry_set_user_data(fib_entry_t *fib_entry, const void *user_data, + void (*user_data_release)(void **)); + +#endif /* WITH_MAPME */ + +#endif // fib_entry_h diff --git a/hicn-light/src/hicn/core/forwarder.c b/hicn-light/src/hicn/core/forwarder.c index 94e8cc885..8c276bfef 100644 --- a/hicn-light/src/hicn/core/forwarder.c +++ b/hicn-light/src/hicn/core/forwarder.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2023 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -23,6 +23,20 @@ * the event scheduler */ +/* Bypass FIB and send packet one by one */ +//#define BYPASS_FIB 1 + +/* Send packets one by one : only effective if FIB is not bypassed */ +//#define USE_SEND_PACKET 1 + +/* Batch sending: only if the previous option is undefined */ +#define USE_QUEUE true + +/* Shall we send mapme updates to advertise all local prefixes on newly created + * faces + */ +//#define ADVERTISE_PREFIXES_ON_NEW_FACES + #ifndef _WIN32 #include <arpa/inet.h> #include <sys/socket.h> @@ -30,8 +44,7 @@ #endif #include <errno.h> #include <fcntl.h> -#include <signal.h> -#include <hicn/hicn-light/config.h> +//#include <hicn/hicn-light/config.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -41,90 +54,71 @@ #define __STDC_FORMAT_MACROS #include <inttypes.h> -#include <parc/algol/parc_ArrayList.h> -#include <parc/algol/parc_Memory.h> -#include <parc/algol/parc_Object.h> -#include <parc/logging/parc_LogReporterTextStdout.h> - -#include <hicn/core/connectionManager.h> -#include <hicn/core/connectionTable.h> -#include <hicn/core/dispatcher.h> -#include <hicn/core/forwarder.h> -#include <hicn/core/messagePacketType.h> +#include "connection_table.h" +#include "fib.h" +#include "forwarder.h" +#include "listener_table.h" #ifdef WITH_MAPME -#include <hicn/core/mapme.h> +#include "mapme.h" #endif /* WITH_MAPME */ -#include <hicn/config/configuration.h> -#include <hicn/config/configurationFile.h> -#include <hicn/config/configurationListeners.h> -#include <hicn/processor/messageProcessor.h> +#include "msgbuf.h" +#include "msgbuf_pool.h" +#include "packet_cache.h" +#include "../config/configuration.h" +// #include "../config/configuration_file.h" +#include "../config/commands.h" +#include "../io/base.h" // MAX_MSG + +#ifdef WITH_POLICY_STATS +#include <hicn/core/policy_stats.h> +#endif /* WITH_POLICY_STATS */ #include <hicn/core/wldr.h> +#include <hicn/interest_manifest.h> +#include <hicn/util/log.h> -#include <parc/assert/parc_Assert.h> - -// the router's clock frequency (we now use the monotonic clock) -#define HZ 1000 - -// these will all be a little off because its all integer division -#define MSEC_PER_TICK (1000 / HZ) -#define USEC_PER_TICK (1000000 / HZ) -#define NSEC_PER_TICK ((1000000000ULL) / HZ) -#define MSEC_TO_TICKS(msec) \ - ((msec < FC_MSEC_PER_TICK) ? 1 : msec / FC_MSEC_PER_TICK) -#define NSEC_TO_TICKS(nsec) ((nsec < NSEC_PER_TICK) ? 1 : nsec / NSEC_PER_TICK) +struct forwarder_s { + // uint16_t server_port; -struct forwarder { - Dispatcher *dispatcher; - - uint16_t server_port; + // used by seed48 and nrand48 + unsigned short seed[3]; - PARCEventSignal *signal_int; - PARCEventSignal *signal_term; -#ifndef _WIN32 - PARCEventSignal *signal_usr1; -#endif - PARCEventTimer *keepalive_event; + connection_table_t *connection_table; + listener_table_t *listener_table; + configuration_t *config; - // will skew the virtual clock forward. In normal operaiton, it is 0. - Ticks clockOffset; + pkt_cache_t *pkt_cache; + fib_t *fib; + msgbuf_pool_t *msgbuf_pool; - unsigned nextConnectionid; - Messenger *messenger; - ConnectionManager *connectionManager; - ConnectionTable *connectionTable; - ListenerSet *listenerSet; - Configuration *config; +#ifdef WITH_MAPME + mapme_t *mapme; +#endif /* WITH_MAPME */ - // we'll eventually want to setup a threadpool of these - MessageProcessor *processor; + bool store_in_cs; + bool serve_from_cs; - Logger *logger; + forwarder_stats_t stats; +#ifdef WITH_POLICY_STATS + policy_stats_mgr_t policy_stats_mgr; +#endif /* WITH_POLICY_STATS */ - PARCClock *clock; + /* + * The message forwarder has to decide whether to queue incoming packets for + * batching, or trigger the transmission on the connection + */ + unsigned *pending_conn; -#if !defined(__APPLE__) - hicn_socket_helper_t - *hicnSocketHelper; // state required to manage hicn connections -#endif - // used by seed48 and nrand48 - unsigned short seed[3]; + subscription_table_t *subscriptions; -#ifdef WITH_MAPME - MapMe *mapme; -#endif /* WITH_MAPME */ + // Used to store the msgbufs that need to be released + off_t *acquired_msgbuf_ids; }; -// signal traps through the event scheduler -static void _signal_cb(int, PARCEventType, void *); - -// A no-op keepalive to prevent Libevent from exiting the dispatch loop -static void _keepalive_cb(int, PARCEventType, void *); - /** * Reseed our pseudo-random number generator. */ -static void forwarder_Seed(Forwarder *forwarder) { +static void forwarder_seed(forwarder_t *forwarder) { #ifndef _WIN32 int fd; ssize_t res; @@ -150,452 +144,1533 @@ static void forwarder_Seed(Forwarder *forwarder) { #endif } -Logger *forwarder_GetLogger(const Forwarder *forwarder) { - return forwarder->logger; -} +forwarder_t *forwarder_create(configuration_t *configuration) { + forwarder_t *forwarder = malloc(sizeof(forwarder_t)); + if (!forwarder) goto ERR_MALLOC; -// ============================================================================ -// Setup and destroy section + forwarder_seed(forwarder); + srand(forwarder->seed[0] ^ forwarder->seed[1] ^ forwarder->seed[2]); -Forwarder *forwarder_Create(Logger *logger) { - Forwarder *forwarder = parcMemory_AllocateAndClear(sizeof(Forwarder)); - parcAssertNotNull(forwarder, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Forwarder)); - memset(forwarder, 0, sizeof(Forwarder)); - forwarder_Seed(forwarder); + forwarder->config = configuration; - forwarder->clock = parcClock_Monotonic(); - forwarder->clockOffset = 0; + forwarder->listener_table = listener_table_create(); + if (!forwarder->listener_table) goto ERR_LISTENER_TABLE; - if (logger) { - forwarder->logger = logger_Acquire(logger); - logger_SetClock(forwarder->logger, forwarder->clock); - } else { - PARCLogReporter *reporter = parcLogReporterTextStdout_Create(); - forwarder->logger = logger_Create(reporter, forwarder->clock); - parcLogReporter_Release(&reporter); - } + forwarder->connection_table = connection_table_create(); + if (!forwarder->connection_table) goto ERR_CONNECTION_TABLE; - forwarder->nextConnectionid = 1; - forwarder->dispatcher = dispatcher_Create(forwarder->logger); - forwarder->messenger = messenger_Create(forwarder->dispatcher); - forwarder->connectionManager = connectionManager_Create(forwarder); - forwarder->connectionTable = connectionTable_Create(); - forwarder->listenerSet = listenerSet_Create(); - forwarder->config = configuration_Create(forwarder); - forwarder->processor = messageProcessor_Create(forwarder); - - forwarder->signal_term = dispatcher_CreateSignalEvent( - forwarder->dispatcher, _signal_cb, forwarder, SIGTERM); - dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_term); - - forwarder->signal_int = dispatcher_CreateSignalEvent( - forwarder->dispatcher, _signal_cb, forwarder, SIGINT); - dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_int); -#ifndef _WIN32 - forwarder->signal_usr1 = dispatcher_CreateSignalEvent( - forwarder->dispatcher, _signal_cb, forwarder, SIGPIPE); - dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_usr1); -#endif + forwarder->fib = fib_create(forwarder); + if (!forwarder->fib) goto ERR_FIB; -#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && \ - defined(PUNTING) - forwarder->hicnSocketHelper = hicn_create(); - if (!forwarder->hicnSocketHelper) - goto ERR_SOCKET; -#endif /* __APPLE__ */ - -#ifdef WITH_MAPME - if (!(mapme_create(&forwarder->mapme, forwarder))) - goto ERR_MAPME; -#endif /* WITH_MAPME */ + forwarder->msgbuf_pool = msgbuf_pool_create(); + if (!forwarder->msgbuf_pool) goto ERR_PACKET_POOL; + size_t objectStoreSize = configuration_get_cs_size(configuration); + forwarder->pkt_cache = pkt_cache_create(objectStoreSize); + if (!forwarder->pkt_cache) goto ERR_PKT_CACHE; - /* ignore child */ -#ifndef _WIN32 - signal(SIGCHLD, SIG_IGN); + forwarder->subscriptions = subscription_table_create(); + if (!forwarder->subscriptions) goto ERR_SUBSCRIPTION; - /* ignore tty signals */ - signal(SIGTSTP, SIG_IGN); - signal(SIGTTOU, SIG_IGN); - signal(SIGTTIN, SIG_IGN); -#endif + // the two flags for the cs are set to true by default. If the cs + // is active it always work as expected unless the use modifies this + // values using controller + if (objectStoreSize != 0) { + forwarder->store_in_cs = true; + forwarder->serve_from_cs = true; + } - // We no longer use this for ticks, but we need to have at least one event - // schedule to keep Libevent happy. +#ifdef WITH_MAPME + forwarder->mapme = mapme_create(forwarder); + if (!forwarder->mapme) goto ERR_MAPME; +#endif /* WITH_MAPME */ - struct timeval wtnow_timeout; - timerclear(&wtnow_timeout); +#ifdef WITH_POLICY_STATS + if (policy_stats_mgr_initialize(&forwarder->policy_stats_mgr, forwarder) < 0) + goto ERR_MGR; +#endif /* WITH_POLICY_STATS */ - wtnow_timeout.tv_sec = 0; - wtnow_timeout.tv_usec = 50000; // 20 Hz keepalive + memset(&forwarder->stats, 0, sizeof(forwarder_stats_t)); + vector_init(forwarder->pending_conn, MAX_MSG, 0); + vector_init(forwarder->acquired_msgbuf_ids, MAX_MSG, 0); - PARCEventScheduler *base = - dispatcher_GetEventScheduler(forwarder->dispatcher); - forwarder->keepalive_event = parcEventTimer_Create( - base, PARCEventType_Persist, _keepalive_cb, (void *)forwarder); - parcEventTimer_Start(forwarder->keepalive_event, &wtnow_timeout); + char *n_suffixes_per_split_str = getenv("N_SUFFIXES_PER_SPLIT"); + if (n_suffixes_per_split_str) + configuration_set_suffixes_per_split(forwarder_get_configuration(forwarder), + atoi(n_suffixes_per_split_str)); return forwarder; +ERR_MGR: #ifdef WITH_MAPME ERR_MAPME: #endif /* WITH_MAPME */ -#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && \ - defined(PUNTING) - hicn_free(forwarder->hicnSocketHelper); -ERR_SOCKET: -#endif - listenerSet_Destroy(&(forwarder->listenerSet)); - connectionManager_Destroy(&(forwarder->connectionManager)); - connectionTable_Destroy(&(forwarder->connectionTable)); - messageProcessor_Destroy(&(forwarder->processor)); - configuration_Destroy(&(forwarder->config)); - messenger_Destroy(&(forwarder->messenger)); - - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_int)); - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_term)); -#ifndef _WIN32 - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_usr1)); -#endif - - parcClock_Release(&forwarder->clock); - logger_Release(&forwarder->logger); - - // do the dispatcher last - dispatcher_Destroy(&(forwarder->dispatcher)); - parcMemory_Deallocate((void **)&forwarder); +ERR_SUBSCRIPTION: + subscription_table_free(forwarder->subscriptions); +ERR_PKT_CACHE: + pkt_cache_free(forwarder->pkt_cache); + + msgbuf_pool_free(forwarder->msgbuf_pool); +ERR_PACKET_POOL: + fib_free(forwarder->fib); +ERR_FIB: + connection_table_free(forwarder->connection_table); +ERR_CONNECTION_TABLE: + listener_table_free(forwarder->listener_table); +ERR_LISTENER_TABLE: + free(forwarder); +ERR_MALLOC: return NULL; } -void forwarder_Destroy(Forwarder **ptr) { - parcAssertNotNull(ptr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*ptr, "Parameter must dereference to non-null pointer"); - Forwarder *forwarder = *ptr; -#if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) && \ - defined(PUNTING) - hicn_free(forwarder->hicnSocketHelper); -#endif - parcEventTimer_Destroy(&(forwarder->keepalive_event)); - - listenerSet_Destroy(&(forwarder->listenerSet)); - connectionManager_Destroy(&(forwarder->connectionManager)); - connectionTable_Destroy(&(forwarder->connectionTable)); - messageProcessor_Destroy(&(forwarder->processor)); - configuration_Destroy(&(forwarder->config)); +void forwarder_free(forwarder_t *forwarder) { + assert(forwarder); - // the messenger is used by many of the other pieces, so destroy it last - messenger_Destroy(&(forwarder->messenger)); + policy_stats_mgr_finalize(&forwarder->policy_stats_mgr); #ifdef WITH_MAPME mapme_free(forwarder->mapme); #endif /* WITH_MAPME */ - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_int)); - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_term)); -#ifndef _WIN32 - dispatcher_DestroySignalEvent(forwarder->dispatcher, - &(forwarder->signal_usr1)); -#endif + pkt_cache_free(forwarder->pkt_cache); + msgbuf_pool_free(forwarder->msgbuf_pool); + fib_free(forwarder->fib); + connection_table_free(forwarder->connection_table); + listener_table_free(forwarder->listener_table); + subscription_table_free(forwarder->subscriptions); + configuration_free(forwarder->config); + vector_free(forwarder->pending_conn); + vector_free(forwarder->acquired_msgbuf_ids); + free(forwarder); +} + +/* + * An event occurred that might trigger an update of the FIB cache. It is + * possible that the flags have been reset following a connection add or remote. + * The objective of this function is to prepare the cache entry, and to alert of + * any change for both consumer and producer prefixes. + */ +void forwarder_on_route_event(const forwarder_t *forwarder, + fib_entry_t *entry) { + commands_notify_route(forwarder, entry); + +#ifdef ADVERTISE_PREFIXES_ON_NEW_FACES + nexthops_t new_nexthops = NEXTHOPS_EMPTY; +#endif /* ADVERTISE_PREFIXES_ON_NEW_FACES */ + + nexthops_t *nexthops = NULL; + char *prefix_type_s; + + const connection_table_t *table = + forwarder_get_connection_table(entry->forwarder); + + const hicn_prefix_t *prefix = fib_entry_get_prefix(entry); + + WITH_INFO({ + char buf[MAXSZ_HICN_PREFIX]; + hicn_prefix_snprintf(buf, MAXSZ_HICN_NAME, prefix); + INFO("fib_entry_on_event: %s", buf); + )}; + + if (!fib_entry_has_local_nexthop(entry)) { + /* Recompute FIB cache, then check whether it has changed based on hash */ + prefix_type_s = "consumer"; + nexthops = fib_entry_get_nexthops(entry); + nexthops_reset(nexthops); + fib_entry_filter_nexthops(entry, nexthops, ~0, false); +#ifdef ADVERTISE_PREFIXES_ON_NEW_FACES + } else { + /* Check available non-local connections (on which we would send MAP-Me + * updates */ + prefix_type_s = "producer"; + + nexthops = fib_entry_get_mapme_nexthops(entry, &new_nexthops); + fib_entry_filter_nexthops(entry, nexthops, ~0, true); + +#ifdef WITH_MAPME + mapme_set_adjacencies(forwarder->mapme, entry, nexthops, NULL); +#endif /* WITH_MAPME */ +#endif /* ADVERTISE_PREFIXES_ON_NEW_FACES */ + } + + if (!nexthops) + return; - parcClock_Release(&forwarder->clock); - logger_Release(&forwarder->logger); + if (!fib_entry_nexthops_changed(entry, nexthops)) return; - // do the dispatcher last - dispatcher_Destroy(&(forwarder->dispatcher)); + /* Send notification */ + WITH_INFO({ + char buf[MAXSZ_HICN_PREFIX]; + hicn_prefix_snprintf(buf, MAXSZ_HICN_NAME, prefix); + INFO("Active interfaces changed for %s prefix %s", prefix_type_s, buf); + }); - parcMemory_Deallocate((void **)&forwarder); - *ptr = NULL; + netdevice_flags_t flags = NETDEVICE_FLAGS_EMPTY; + nexthops_foreach(nexthops, nh, { + connection_t *connection = connection_table_get_by_id(table, nh); + netdevice_flags_add(flags, connection_get_interface_type(connection)); + }); + + hicn_ip_prefix_t ip_prefix; + hicn_prefix_get_ip_prefix(prefix, &ip_prefix); + commands_notify_active_interface_update(forwarder, &ip_prefix, flags); } -void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, - const char *localPath) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); +int forwarder_add_connection(const forwarder_t *forwarder, + const char *symbolic_name, face_type_t type, + address_pair_t *pair, policy_tags_t tags, + int priority, face_state_t admin_state) { + connection_table_t *table = forwarder_get_connection_table(forwarder); + connection_t *connection = connection_table_get_by_pair(table, pair); + + if (!connection) { + connection = connection_create(type, symbolic_name, pair, forwarder); + if (!connection) { + ERROR("Failed to create %s connection", face_type_str(type)); + return -1; + } + + } else { + WARN("Connection already exists"); + } + +#ifdef WITH_POLICY + connection_set_tags(connection, tags); + connection_set_priority(connection, priority); +#endif /* WITH_POLICY */ - configurationListeners_SetupAll(forwarder->config, port, localPath); + connection_set_admin_state(connection, admin_state); + return 0; } -void forwarder_SetupLocalListeners(Forwarder *forwarder, uint16_t port) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - configurationListeners_SetutpLocalIPv4(forwarder->config, port); +int forwarder_remove_connection(const forwarder_t *forwarder, + unsigned connection_id, bool finalize) { + /* Remove connection from the FIB */ + forwarder_remove_connection_id_from_routes(forwarder, connection_id); + + /* Remove connection */ + connection_table_t *table = forwarder_get_connection_table(forwarder); + + /* Hook: connection deleted through the control protocol */ + connection_t *connection = connection_table_at(table, connection_id); + forwarder_on_connection_event(forwarder, connection, CONNECTION_EVENT_DELETE); + + connection_table_remove_by_id(table, connection_id); + if (finalize) connection_finalize(connection); + + return 0; } -void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename) { - ConfigurationFile *configFile = configurationFile_Create(forwarder, filename); - if (configFile) { - configurationFile_Process(configFile); - configurationFile_Release(&configFile); - } +/* + * This is currently called from commands.c for every command sent to update + * a connection. + */ +void forwarder_on_connection_event(const forwarder_t *forwarder, + const connection_t *connection, + connection_event_t event) { + assert(connection); + + commands_notify_connection(forwarder, event, connection); + + unsigned conn_id = connection_get_id(connection); + + /* We need to send a MapMe update on the newly selected connections for + * each concerned fib_entry : connection is involved, or no more involved */ + fib_t *fib = forwarder_get_fib(forwarder); + fib_foreach_entry(fib, entry, { + const nexthops_t *nexthops = fib_entry_get_nexthops(entry); + + if (!fib_entry_has_local_nexthop(entry)) { + /* Consumer prefix */ + /* + * A new connection has no impact until it is added to FIB, which will + * be handled in a route event + */ + if (event == CONNECTION_EVENT_CREATE) break; + + /* + * For each FIB entry, trigger an event only if the connection is part + * of nexthops */ + // XXX Replace this by a function + nexthops_foreach(nexthops, nexthop, { + if (nexthop != conn_id) continue; + forwarder_on_route_event(forwarder, entry); + break; + }); + } else { + /* Producer prefix */ + if (connection_is_local(connection)) break; + + // XXX we could optimize event more + forwarder_on_route_event(forwarder, entry); + } + }); +} + +void forwarder_setup_local_listeners(forwarder_t *forwarder, uint16_t port) { + assert(forwarder); + listener_setup_local(forwarder, port); } -Configuration *forwarder_GetConfiguration(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); +configuration_t *forwarder_get_configuration(forwarder_t *forwarder) { + assert(forwarder); return forwarder->config; } -// ============================================================================ +subscription_table_t *forwarder_get_subscriptions( + const forwarder_t *forwarder) { + return forwarder->subscriptions; +} + +connection_table_t *forwarder_get_connection_table( + const forwarder_t *forwarder) { + assert(forwarder); + return forwarder->connection_table; +} -unsigned forwarder_GetNextConnectionId(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return forwarder->nextConnectionid++; +listener_table_t *forwarder_get_listener_table(const forwarder_t *forwarder) { + assert(forwarder); + return forwarder->listener_table; } -Messenger *forwarder_GetMessenger(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return forwarder->messenger; +pkt_cache_t *forwarder_get_pkt_cache(const forwarder_t *forwarder) { + assert(forwarder); + return forwarder->pkt_cache; } -Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return forwarder->dispatcher; +void forwarder_cs_set_store(forwarder_t *forwarder, bool val) { + assert(forwarder); + forwarder->store_in_cs = val; } -#ifdef WITH_POLICY -ConnectionTable *forwarder_GetConnectionTable(const Forwarder *forwarder) { -#else -ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder) { -#endif /* WITH_POLICY */ - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return forwarder->connectionTable; +bool forwarder_cs_get_store(forwarder_t *forwarder) { + assert(forwarder); + return forwarder->store_in_cs; } -ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return forwarder->listenerSet; +void forwarder_cs_set_serve(forwarder_t *forwarder, bool val) { + assert(forwarder); + forwarder->serve_from_cs = val; } -void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - messageProcessor_SetCacheStoreFlag(forwarder->processor, val); +bool forwarder_cs_get_serve(forwarder_t *forwarder) { + assert(forwarder); + return forwarder->serve_from_cs; } -bool forwarder_GetChacheStoreFlag(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return messageProcessor_GetCacheStoreFlag(forwarder->processor); +void forwarder_cs_set_size(forwarder_t *forwarder, size_t size) { + assert(forwarder); + + if (pkt_cache_set_cs_size(forwarder->pkt_cache, size) < 0) { + ERROR( + "Unable to resize the CS: provided maximum size (%u) is smaller than " + "the number of elements currently stored in the CS (%u). Clear the " + "CS " + "and retry.", + size, pkt_cache_get_cs_size(forwarder->pkt_cache)); + } } -void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - messageProcessor_SetCacheServeFlag(forwarder->processor, val); +size_t forwarder_cs_get_size(forwarder_t *forwarder) { + assert(forwarder); + return pkt_cache_get_cs_size(forwarder->pkt_cache); } -bool forwarder_GetChacheServeFlag(Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return messageProcessor_GetCacheServeFlag(forwarder->processor); +size_t forwarder_cs_get_num_stale_entries(forwarder_t *forwarder) { + assert(forwarder); + return pkt_cache_get_num_cs_stale_entries(forwarder->pkt_cache); } -void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, - struct iovec *message, unsigned ingressId) { - configuration_ReceiveCommand(forwarder->config, command, message, ingressId); +void forwarder_cs_clear(forwarder_t *forwarder) { + assert(forwarder); + + pkt_cache_cs_clear(forwarder->pkt_cache); } -void forwarder_Receive(Forwarder *forwarder, Message *message) { - parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); - parcAssertNotNull(message, "Parameter message must be non-null"); +/** + * @function forwarder_Drop + * @abstract Whenever we "drop" a message, increment counters + * @discussion + * This is a bookkeeping function. It increments the appropriate counters. + * + * The default action for a message is to destroy it in + * <code>forwarder_Receive()</code>, so this function does not need to do + * that. + * + */ +static ssize_t forwarder_drop(forwarder_t *forwarder, off_t msgbuf_id) { + forwarder->stats.countDropped++; + + const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + const msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); - // this takes ownership of the message, so we're done here + switch (msgbuf_get_type(msgbuf)) { + case HICN_PACKET_TYPE_INTEREST: + forwarder->stats.countInterestsDropped++; + break; - // this are the checks needed to implement WLDR. We set wldr only on the STAs - // and we let the AP to react according to choise of the client. - // if the STA enables wldr using the set command, the AP enable wldr as well - // otherwise, if the STA disable it the AP remove wldr - // WLDR should be enabled only on the STAs using the command line - // TODO - // disable WLDR command line on the AP - const Connection *conn = connectionTable_FindById( - forwarder->connectionTable, message_GetIngressConnectionId(message)); + case HICN_PACKET_TYPE_DATA: + forwarder->stats.countObjectsDropped++; + break; + + default: + forwarder->stats.countOtherDropped++; + break; + } + + return msgbuf_get_len(msgbuf); + // dont destroy message here, its done at end of receive +} + +#ifndef BYPASS_FIB +/* + * If the hoplimit is equal to 0, then we may only forward it to local + * applications. Otherwise, we may forward it off the system. + * + */ + +static ssize_t forwarder_forward_via_connection(forwarder_t *forwarder, + off_t msgbuf_id, + unsigned conn_id) { + connection_table_t *table = forwarder_get_connection_table(forwarder); + + const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + + connection_t *conn = connection_table_get_by_id(table, conn_id); if (!conn) { - /* - * Drop is a static method in messageProcessor which might or might not need - * to be called for accounting purposes. This call was initially absent so - * the behaviour was kept like this, as this situation is unlikely. We need - * to release memory though, as this is not done in Drop anyways. - */ - //messageProcessor_Drop(forwarder->processor, message); - message_Release(&message); - return; + forwarder->stats.countDroppedConnectionNotFound++; + WARN("forward msgbuf %lu to interface %u not found (count %u)", msgbuf_id, + conn_id, forwarder->stats.countDroppedConnectionNotFound); + return forwarder_drop(forwarder, msgbuf_id); } - if (message_HasWldr(message)) { - if (connection_HasWldr(conn)) { + /* Always queue the packet... */ + // DEBUG("Queueing packet\n"); + +#if defined(USE_SEND_PACKET) || !defined(__linux__) + + // Here we need to update the path label of a data packet before send + // it. The path label update can be done here because the packet is sent + // directly to the socket + if (msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA) + msgbuf_update_pathlabel(msgbuf, connection_get_id(conn)); + + bool success = connection_send_packet(conn, msgbuf_get_packet(msgbuf), + msgbuf_get_len(msgbuf)); +#else + + // In this case we cannot update the path label even if it is need because + // the packet is not copied and only the packet id is enqueued to the ring + // buffer associated the output interface. If the path label is updated here + // all data packets get delivered to the next hop with the same label that is + // associated to the last connection used. For this reason the path label + // update must be done before the packet is actually sent inside the different + // IO implementations. + bool success = connection_send(conn, msgbuf_id, USE_QUEUE); + +#endif + + /* ... and mark the connection as pending if this is not yet the case */ + if (!vector_contains(forwarder->pending_conn, conn_id)) + vector_push(forwarder->pending_conn, conn_id); + + if (!success) { + forwarder->stats.countSendFailures++; + + DEBUG("forward msgbuf %llu to interface %u send failure (count %u)", + msgbuf_id, conn_id, forwarder->stats.countSendFailures); + return forwarder_drop(forwarder, msgbuf_id); + } + + switch (msgbuf_get_type(msgbuf)) { + case HICN_PACKET_TYPE_INTEREST: + forwarder->stats.countInterestForwarded++; + break; + + case HICN_PACKET_TYPE_DATA: + forwarder->stats.countObjectsForwarded++; + break; + + default: + break; + } + + TRACE("forward msgbuf %p (size=%u) to interface %u", msgbuf, + msgbuf_get_len(msgbuf), conn_id); + return msgbuf_get_len(msgbuf); +} + +/** + * @function forwarder_forward_to_nexthops + * @abstract Try to forward to each nexthop listed in the NumberSet + * @discussion + * Will not forward to the ingress connection. + * + * @return The number of nexthops tried + */ +static unsigned forwarder_forward_to_nexthops(forwarder_t *forwarder, + off_t msgbuf_id, + const nexthops_t *nexthops) { + // DEBUG("[forwarder_forward_to_nexthops] num=%d/%d", + // nexthops_get_curlen(nexthops), nexthops_get_len(nexthops)); + unsigned forwardedCopies = 0; + + const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + unsigned ingressId = msgbuf_get_connection_id(msgbuf); + + nexthops_foreach(nexthops, nexthop, { + // DEBUG("[forwarder_forward_to_nexthops] - nexthop = %d"); + if (nexthop == ingressId) continue; + + forwardedCopies++; + // INFO("[forwarder_forward_to_nexthops] - nexthop = %d OK", nexthop); + forwarder_forward_via_connection(forwarder, msgbuf_id, nexthop); + }); + + return forwardedCopies; +} + +static bool forwarder_forward_via_fib(forwarder_t *forwarder, off_t msgbuf_id, + pkt_cache_verdict_t verdict, + pkt_cache_entry_t *entry) { + assert(forwarder && entry && msgbuf_id_is_valid(msgbuf_id)); + + bool ret = true; + + const msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST); + + fib_entry_t *fib_entry = fib_match_msgbuf(forwarder->fib, msgbuf); + if (!fib_entry) return false; + + nexthops_t *nexthops = fib_entry_get_nexthops(fib_entry); + + /* Backup flags and cur_len*/ + uint_fast32_t flags = nexthops->flags; + size_t cur_len = nexthops_get_curlen(nexthops); + + /* This affects the nexthops */ + nexthops = strategy_lookup_nexthops(&fib_entry->strategy, nexthops, msgbuf); + + if (nexthops_get_curlen(nexthops) == 0) { + ERROR("Message %p returned an empty next hop set", msgbuf); + ret = false; + goto END; + } + + pit_entry_t *pit_entry = &entry->u.pit_entry; + if (!pit_entry) { + ret = false; + goto END; + } + + pit_entry_set_fib_entry(pit_entry, fib_entry); + + // this requires some additional checks. It may happen that some of the output + // faces selected by the forwarding strategy are not usable. So far all the + // forwarding strategy return only valid faces (or an empty list) + nexthops_foreach(nexthops, nexthop, { + // DEBUG("Adding egress to PIT for nexthop %d", nexthop); + pit_entry_egress_add(pit_entry, nexthop); + }); + + if (forwarder_forward_to_nexthops(forwarder, msgbuf_id, nexthops) <= 0) { + ERROR("Error forwarding mMessage %p to next hops", msgbuf); + ret = false; + } + +END: + /* Restore flags & curlen */ + nexthops->flags = flags; + nexthops->cur_elts = cur_len; + + return ret; +} + +#endif /* ! BYPASS_FIB */ + +int _forwarder_forward_upon_interest( + forwarder_t *forwarder, msgbuf_pool_t *msgbuf_pool, off_t data_msgbuf_id, + off_t interest_msgbuf_id, pkt_cache_entry_t *entry, + pkt_cache_verdict_t verdict, bool is_aggregated) { + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, interest_msgbuf_id); + + // - Aggregation can be perfomed, do not forward + if (verdict == PKT_CACHE_VERDICT_AGGREGATE_INTEREST) { + forwarder_drop(forwarder, interest_msgbuf_id); + return (int)msgbuf_get_len(msgbuf); + } + + // - Data packet matching the interest was found, forward reply + if (verdict == PKT_CACHE_VERDICT_FORWARD_DATA) { + assert(forwarder->serve_from_cs == true); + + msgbuf_t *interest_msgbuf = msgbuf_pool_at(msgbuf_pool, interest_msgbuf_id); + msgbuf_t *data_msgbuf = msgbuf_pool_at(msgbuf_pool, data_msgbuf_id); + + msgbuf_reset_pathlabel(data_msgbuf); + forwarder_forward_via_connection(forwarder, data_msgbuf_id, + msgbuf_get_connection_id(interest_msgbuf)); + return (int)msgbuf_get_len(msgbuf); + } + + // - For aggregated interest, the interest forwarding is done in + // `_forwarder_forward_aggregated_interest()` + if (is_aggregated) return (int)msgbuf_get_len(msgbuf); + + // - Try to forward the interest + int rc = + forwarder_forward_via_fib(forwarder, interest_msgbuf_id, verdict, entry); + if (!rc) { + // - Not able to forward, drop the packet + forwarder->stats.countDroppedNoRoute++; + INFO("Message %lu did not match FIB, no route (count %u)", + interest_msgbuf_id, forwarder->stats.countDroppedNoRoute); + + forwarder_drop(forwarder, interest_msgbuf_id); + return -1; + } + + return (int)msgbuf_get_len(msgbuf); +} + +static void _forwarder_update_interest_stats(forwarder_t *forwarder, + pkt_cache_verdict_t verdict, + msgbuf_t *msgbuf, + bool has_expire_ts, + uint64_t expire_ts) { + long expiration = has_expire_ts ? expire_ts : -1; + switch (verdict) { + case PKT_CACHE_VERDICT_FORWARD_INTEREST: + DEBUG("Message added to PIT (expiration=%ld)", expiration); + break; + + case PKT_CACHE_VERDICT_AGGREGATE_INTEREST: + forwarder->stats.countInterestsAggregated++; + DEBUG("Message aggregated in PIT (expiration=%ld)", expiration); + break; + + case PKT_CACHE_VERDICT_RETRANSMIT_INTEREST: + forwarder->stats.countInterestsRetransmitted++; + DEBUG("Message retransmitted (expiration=%ld)", expiration); + break; + + case PKT_CACHE_VERDICT_FORWARD_DATA: + forwarder->stats.countInterestsSatisfiedFromStore++; + DEBUG("Message satisfied from content store (expiration=%ld)", + expiration); + break; + + case PKT_CACHE_VERDICT_INTEREST_EXPIRED_FORWARD_INTEREST: + forwarder->stats.countInterestsExpired++; + DEBUG("Message replaced expired interest (expiration=%ld)", expiration); + break; + + case PKT_CACHE_VERDICT_DATA_EXPIRED_FORWARD_INTEREST: + forwarder->stats.countDataExpired++; + DEBUG("Message replaced expired data (expiration=%ld)", expiration); + break; + + case PKT_CACHE_VERDICT_ERROR: + ERROR("Invalid packet cache content"); + break; + + default: + break; + } +} + +/** + * Return the interest manifest from the interest payload + */ +static int _forwarder_get_interest_manifest( + msgbuf_t *msgbuf, interest_manifest_header_t **int_manifest_header, + size_t *payload_size) { + uint8_t *payload; + + hicn_packet_buffer_t *pkbuf = msgbuf_get_pkbuf(msgbuf); + + hicn_payload_type_t payload_type; + HICN_UNUSED(int rc) = hicn_packet_get_payload_type(pkbuf, &payload_type); + // XXX ASSERT HERE !!! + if (rc != HICN_LIB_ERROR_NONE) return -1; + // assert(rc == HICN_LIB_ERROR_NONE); + + if (payload_type != HPT_MANIFEST) return -1; + + rc = hicn_packet_get_payload(pkbuf, &payload, payload_size, false); + // assert(rc == HICN_LIB_ERROR_NONE); + if (rc != HICN_LIB_ERROR_NONE) return -1; + + *int_manifest_header = (interest_manifest_header_t *)payload; + + return 0; +} + +// Manifest is split using splitting strategy, then every +// sub-manifest is sent using the forwarding strategy defined for the prefix +int _forwarder_forward_aggregated_interest( + forwarder_t *forwarder, interest_manifest_header_t *int_manifest_header, + msgbuf_t *msgbuf, off_t msgbuf_id, pkt_cache_entry_t **entries) { + assert(msgbuf_id_is_valid(msgbuf_id) && + msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST); + + int ret = -1; + + fib_entry_t *fib_entry = fib_match_msgbuf(forwarder->fib, msgbuf); + if (!fib_entry) goto END; + + nexthops_t *nexthops = fib_entry_get_nexthops(fib_entry); + if (nexthops_get_curlen(nexthops) == 0) { + ret = 0; + goto END; + } + + /* Backup flags and cur_len*/ + uint_fast32_t flags = nexthops->flags; + size_t cur_len = nexthops_get_curlen(nexthops); + + size_t n_suffixes_per_split = configuration_get_suffixes_per_split( + forwarder_get_configuration(forwarder)); + int_manifest_split_strategy_t disaggregation_strategy = + configuration_get_split_strategy(forwarder_get_configuration(forwarder)); + switch (disaggregation_strategy) { + case INT_MANIFEST_SPLIT_STRATEGY_NONE: + n_suffixes_per_split = int_manifest_header->n_suffixes + 1; + + case INT_MANIFEST_SPLIT_STRATEGY_MAX_N_SUFFIXES: { + // Generate sub-manifests: same as original manifest, + // but different suffix in the header and different bitmap + + int total_len = 0; + // Suffixes in manifest plus the one in the header + int total_suffixes = int_manifest_header->n_suffixes + 1; + + // Save copy of original bitmap to use as a reference + // to generate bitmaps for sub-manifests + hicn_uword original_bitmap[BITMAP_SIZE] = {0}; + memcpy(&original_bitmap, int_manifest_header->request_bitmap, + BITMAP_SIZE * sizeof(hicn_uword)); + + size_t suffix_index = 0; // Position of suffix in initial manifest + interest_manifest_header_t *manifest = NULL; + size_t payload_size; + while (suffix_index < total_suffixes) { + // If more than one sub-manifest, + // clone original interest manifest and update suffix + if (suffix_index > 0) { + msgbuf_t *clone; + off_t clone_id = + msgbuf_pool_clone(forwarder->msgbuf_pool, &clone, msgbuf_id); + msgbuf_pool_acquire(clone); + forwarder_acquired_msgbuf_ids_push(forwarder, clone_id); + + msgbuf_id = clone_id; + msgbuf = clone; + } + + hicn_uword curr_bitmap[BITMAP_SIZE] = {0}; + size_t first_suffix_index_in_submanifest = suffix_index; + suffix_index = interest_manifest_update_bitmap( + original_bitmap, curr_bitmap, suffix_index, total_suffixes, + n_suffixes_per_split); + size_t first_suffix_index_in_next_submanifest = suffix_index; + + // Update manifest bitmap in current msgbuf + + int ret = + _forwarder_get_interest_manifest(msgbuf, &manifest, &payload_size); + _ASSERT(ret == 0); + memcpy(manifest->request_bitmap, curr_bitmap, + BITMAP_SIZE * sizeof(hicn_uword)); + WITH_TRACE({ + bitmap_print(manifest->request_bitmap, BITMAP_SIZE); + printf("\n"); + }); + + /* + * Update PIT entries for suffixes in current sub-manifest. + * + * Note that strategy lookup affects the nexthops, and we need to + *restore the initial state before every lookup + */ + nexthops->flags = flags; + nexthops->cur_elts = cur_len; + nexthops = + strategy_lookup_nexthops(&fib_entry->strategy, nexthops, msgbuf); + + if (nexthops_get_curlen(nexthops) == 0) { + ERROR("Message %p returned an empty next hop set", msgbuf); + goto RESTORE; + } + + for (size_t i = first_suffix_index_in_submanifest; + i < first_suffix_index_in_next_submanifest; i++) { + if (!bitmap_is_set_no_check(manifest->request_bitmap, i)) continue; + + pit_entry_t *pit_entry = &(entries[i]->u.pit_entry); + if (!pit_entry) goto RESTORE; + + pit_entry_set_fib_entry(pit_entry, fib_entry); + nexthops_foreach(nexthops, nexthop, + { pit_entry_egress_add(pit_entry, nexthop); }); + } + + // Serialize manifest before sending it + interest_manifest_serialize(int_manifest_header); + + if (forwarder_forward_to_nexthops(forwarder, msgbuf_id, nexthops) <= + 0) { + ERROR("Message %p returned an empty next hop set", msgbuf); + continue; + } + + total_len += msgbuf_get_len(msgbuf); + } + + ret = total_len; + goto END; + } + + default: + break; + } + +RESTORE: + /* Restore flags & curlen */ + nexthops->flags = flags; + nexthops->cur_elts = cur_len; + +END: + return ret; +} + +static ssize_t forwarder_process_single_interest(forwarder_t *forwarder, + msgbuf_pool_t *msgbuf_pool, + msgbuf_t *msgbuf, + off_t msgbuf_id) { + pkt_cache_verdict_t verdict = PKT_CACHE_VERDICT_ERROR; + off_t data_msgbuf_id = INVALID_MSGBUF_ID; + pkt_cache_entry_t *entry = NULL; + + // Update packet cache + pkt_cache_on_interest(forwarder->pkt_cache, msgbuf_pool, msgbuf_id, &verdict, + &data_msgbuf_id, &entry, msgbuf_get_name(msgbuf), + forwarder->serve_from_cs); + + _forwarder_update_interest_stats(forwarder, verdict, msgbuf, + entry->has_expire_ts, entry->expire_ts); + + int rc = _forwarder_forward_upon_interest( + forwarder, msgbuf_pool, data_msgbuf_id, msgbuf_id, entry, verdict, false); + + // No route when trying to forward interest, remove from PIT + if (rc == -1) pkt_cache_pit_remove_entry(forwarder->pkt_cache, entry); + + return msgbuf_get_len(msgbuf); +} + +static ssize_t forwarder_process_aggregated_interest( + forwarder_t *forwarder, interest_manifest_header_t *int_manifest_header, + msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf, off_t msgbuf_id) { + pkt_cache_verdict_t verdict = PKT_CACHE_VERDICT_ERROR; + off_t data_msgbuf_id = INVALID_MSGBUF_ID; + pkt_cache_entry_t *entry = NULL; + // Save PIT entries to avoid re-doing pkt cache lookup in + // `_forwarder_forward_aggregated_interest()` + pkt_cache_entry_t *entries[BITMAP_SIZE * WORD_WIDTH]; + + int n_suffixes_to_fwd = 0; + + hicn_name_t name_copy = HICN_NAME_EMPTY; + hicn_name_copy(&name_copy, msgbuf_get_name(msgbuf)); + + // Suffixes in interest manifest also contains suffix in main name. We can + // then just iterate the interest manifest and update the suffix in the name + // struct + hicn_name_suffix_t *suffix; + unsigned long pos; + interest_manifest_foreach_suffix(int_manifest_header, suffix, pos) { + // Update name + hicn_name_set_suffix(&name_copy, *suffix); + + // Update packet cache + pkt_cache_on_interest(forwarder->pkt_cache, msgbuf_pool, msgbuf_id, + &verdict, &data_msgbuf_id, &entry, &name_copy, + forwarder->serve_from_cs); + + entries[pos] = entry; + _forwarder_update_interest_stats(forwarder, verdict, msgbuf, + entry->has_expire_ts, entry->expire_ts); + + // Here only data forwarding is performed, interest forwarding is done + // in '_forwarder_forward_aggregated_interest()' + int rc = + _forwarder_forward_upon_interest(forwarder, msgbuf_pool, data_msgbuf_id, + msgbuf_id, entry, verdict, true); + + // No route when trying to forward interest, remove from PIT + if (rc == -1) pkt_cache_pit_remove_entry(forwarder->pkt_cache, entry); + + // Unset in bitmap if no interest forwarding needed, + // otherwise increase count of suffixes to forward + if (rc == -1 || verdict == PKT_CACHE_VERDICT_AGGREGATE_INTEREST || + verdict == PKT_CACHE_VERDICT_FORWARD_DATA) { + bitmap_unset_no_check(int_manifest_header->request_bitmap, pos); + } else { + n_suffixes_to_fwd++; + } + + WITH_DEBUG({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, &name_copy); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "(error)"); + DEBUG("Next in manifest: %s", buf); + }); + } + + // Return if nothing in the manifest to forward + if (n_suffixes_to_fwd == 0) return msgbuf_get_len(msgbuf); + + return _forwarder_forward_aggregated_interest(forwarder, int_manifest_header, + msgbuf, msgbuf_id, entries); +} + +/** + * @function forwarder_process_interest + * @abstract Receive an interest from the network + * @discussion + * (1) if interest in the PIT, aggregate in PIT + * (2) if interest in the ContentStore, reply + * (3) if in the FIB, forward + * (4) drop + * + */ +static ssize_t forwarder_process_interest(forwarder_t *forwarder, + off_t msgbuf_id) { + assert(forwarder); + assert(msgbuf_id_is_valid(msgbuf_id)); + + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + const connection_table_t *table = forwarder_get_connection_table(forwarder); + connection_t *conn = + connection_table_get_by_id(table, msgbuf_get_connection_id(msgbuf)); + + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST); + + u32 n_suffixes = 0; + interest_manifest_header_t *int_manifest_header = NULL; + size_t payload_size; + int ret = _forwarder_get_interest_manifest(msgbuf, &int_manifest_header, + &payload_size); + if (ret == 0) { + // Deserialize intrest manifest + interest_manifest_deserialize(int_manifest_header); + + if (!interest_manifest_is_valid(int_manifest_header, payload_size)) + return -1; + + n_suffixes = int_manifest_header->n_suffixes; + } + + // Update stats + forwarder->stats.countInterestsReceived++; + conn->stats.interests.rx_pkts++; + conn->stats.interests.rx_bytes += msgbuf_get_len(msgbuf); + + WITH_DEBUG({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, msgbuf_get_name(msgbuf)); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "(error)"); + DEBUG("INTEREST (%s) msgbuf_id=%lu ingress=%u length=%u", buf, msgbuf_id, + msgbuf_get_connection_id(msgbuf), msgbuf_get_len(msgbuf)); + DEBUG("INTEREST (%s) suffixes=%u msgbuf_id=%lu ingress=%u length=%u", buf, + n_suffixes, msgbuf_id, msgbuf_get_connection_id(msgbuf), + msgbuf_get_len(msgbuf)); + }); + + // Cache suffixes for current prefix to (possibly) avoid double lookups + pkt_cache_save_suffixes_for_prefix( + forwarder->pkt_cache, hicn_name_get_prefix(msgbuf_get_name(msgbuf))); + + if (ret == -1) + return forwarder_process_single_interest(forwarder, msgbuf_pool, msgbuf, + msgbuf_id); + return forwarder_process_aggregated_interest(forwarder, int_manifest_header, + msgbuf_pool, msgbuf, msgbuf_id); +} + +static void _forwarder_log_on_data(forwarder_t *forwarder, + pkt_cache_verdict_t verdict) { + switch (verdict) { + case PKT_CACHE_VERDICT_FORWARD_DATA: + DEBUG("Message added to CS from PIT"); + break; + case PKT_CACHE_VERDICT_STORE_DATA: + DEBUG( + "Message added to CS (expired or no previous interest " + "pending)"); + break; + case PKT_CACHE_VERDICT_CLEAR_DATA: + break; + case PKT_CACHE_VERDICT_UPDATE_DATA: + DEBUG("Message updated in CS"); + break; + case PKT_CACHE_VERDICT_IGNORE_DATA: + DEBUG("Message not stored in CS"); + break; + case PKT_CACHE_VERDICT_ERROR: + ERROR("Invalid packet cache content"); + break; + default: + break; + } +} + +/** + * @function forwarder_process_data + * @abstract Process an in-bound content object + * @discussion + * (1) If it does not match anything in the PIT, drop it + * (2) Add to Content Store + * (3) Reverse path forward via PIT entries + * + * @param <#param1#> + */ +static ssize_t forwarder_process_data(forwarder_t *forwarder, off_t msgbuf_id) { + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + + WITH_DEBUG({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, msgbuf_get_name(msgbuf)); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "(error)"); + DEBUG("DATA (%s) msgbuf_id=%lu ingress=%u length=%u", buf, msgbuf_id, + msgbuf_get_connection_id(msgbuf), msgbuf_get_len(msgbuf)); + }); + + const connection_table_t *table = forwarder_get_connection_table(forwarder); + connection_t *conn = + connection_table_get_by_id(table, msgbuf_get_connection_id(msgbuf)); + + // Update stats + forwarder->stats.countObjectsReceived++; + conn->stats.data.rx_pkts++; + conn->stats.data.rx_bytes += msgbuf_get_len(msgbuf); + + // Cache suffixes for current prefix to (possibly) avoid double lookups + pkt_cache_save_suffixes_for_prefix( + forwarder->pkt_cache, hicn_name_get_prefix(msgbuf_get_name(msgbuf))); + + pkt_cache_verdict_t verdict = PKT_CACHE_VERDICT_ERROR; + bool wrong_egress; + nexthops_t *ingressSetUnion = pkt_cache_on_data( + forwarder->pkt_cache, msgbuf_pool, msgbuf_id, forwarder->store_in_cs, + connection_is_local(conn), &wrong_egress, &verdict); + + _forwarder_log_on_data(forwarder, verdict); + + if (wrong_egress) { // Interest sent via a connection but received from + // another + WARN("Data coming from unexpected connection, discarded"); + } else if (!ingressSetUnion) { // No match in the PIT + forwarder->stats.countDroppedNoReversePath++; + DEBUG("Message %lu did not match PIT, no reverse path", msgbuf_id); + + // NOTE : probes are not stored in PIT + if (msgbuf_is_probe(msgbuf)) { + fib_entry_t *entry = fib_match_msgbuf(forwarder->fib, msgbuf); + if (entry && fib_entry_strategy_type(entry) == STRATEGY_TYPE_BESTPATH) { + nexthops_t probe_nexthops = NEXTHOPS_EMPTY; + nexthops_add(&probe_nexthops, msgbuf_get_connection_id(msgbuf)); + fib_entry_on_data(entry, &probe_nexthops, msgbuf, 0, ticks_now()); + } + } + forwarder_drop(forwarder, msgbuf_id); + } else { + // Reverse path forward via PIT entries + forwarder_forward_to_nexthops(forwarder, msgbuf_id, ingressSetUnion); + free(ingressSetUnion); + } + + return msgbuf_get_len(msgbuf); +} + +void forwarder_flush_connections(forwarder_t *forwarder) { + // DEBUG("[forwarder_flush_connections]"); + const connection_table_t *table = forwarder_get_connection_table(forwarder); + + unsigned num_pending_conn = (unsigned)vector_len(forwarder->pending_conn); + for (unsigned i = 0; i < num_pending_conn; i++) { + unsigned conn_id = forwarder->pending_conn[i]; + connection_t *conn = connection_table_at(table, conn_id); + if (!connection_flush(conn)) { + WARN("Could not flush connection queue"); + // XXX keep track of non flushed connections... + } + } + vector_reset(forwarder->pending_conn); + // DEBUG("[forwarder_flush_connections] done"); +} + +#if WITH_WLDR +// XXX move to wldr file, worst case in connection. +void forwarder_apply_wldr(const forwarder_t *forwarder, const msgbuf_t *msgbuf, + connection_t *connection) { + // this are the checks needed to implement WLDR. We set wldr only on the + // STAs and we let the AP to react according to choice of the client. if + // the STA enables wldr using the set command, the AP enable wldr as + // well otherwise, if the STA disable it the AP remove wldr WLDR should + // be enabled only on the STAs using the command line + // TODO + // disable WLDR command line on the AP + if (msgbuf_has_wldr(msgbuf)) { + if (connection_has_wldr(connection)) { // case 1: WLDR is enabled - connection_DetectLosses((Connection *)conn, message); - } else if (!connection_HasWldr(conn) && - connection_WldrAutoStartAllowed(conn)) { + connection_wldr_detect_losses(connection, msgbuf); + } else if (!connection_has_wldr(connection) && + connection_wldr_autostart_is_allowed(connection)) { // case 2: We are on an AP. We enable WLDR - connection_EnableWldr((Connection *)conn); - connection_DetectLosses((Connection *)conn, message); + connection_wldr_enable(connection, true); + connection_wldr_detect_losses(connection, msgbuf); } // case 3: Ignore WLDR } else { - if (connection_HasWldr(conn) && connection_WldrAutoStartAllowed(conn)) { + if (connection_has_wldr(connection) && + connection_wldr_autostart_is_allowed(connection)) { // case 1: STA do not use WLDR, we disable it - connection_DisableWldr((Connection *)conn); + connection_wldr_enable(connection, false); } } - - messageProcessor_Receive(forwarder->processor, message); } +#endif -Ticks forwarder_GetTicks(const Forwarder *forwarder) { - parcAssertNotNull(forwarder, "Parameter must be non-null"); - return parcClock_GetTime(forwarder->clock) + forwarder->clockOffset; -} +bool forwarder_add_or_update_route(forwarder_t *forwarder, + hicn_ip_prefix_t *prefix, + unsigned ingress_id) { + assert(forwarder); + assert(prefix); -Ticks forwarder_NanosToTicks(uint64_t nanos) { return NSEC_TO_TICKS(nanos); } + configuration_t *config = forwarder_get_configuration(forwarder); -uint64_t forwarder_TicksToNanos(Ticks ticks) { - return (1000000000ULL) * ticks / HZ; -} + char prefix_s[MAXSZ_IP_PREFIX]; + int rc = hicn_ip_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, prefix); + assert(rc < MAXSZ_IP_PREFIX); + if (rc < 0) return false; + + INFO("Adding prefix=%s for conn_id=%d", prefix_s, ingress_id); -bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, - add_route_command *control, unsigned ifidx) { - parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); - parcAssertNotNull(control, "Parameter route must be non-null"); + strategy_type_t strategy_type = configuration_get_strategy(config, prefix_s); - // we only have one message processor - bool res = - messageProcessor_AddOrUpdateRoute(forwarder->processor, control, ifidx); + hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; + hicn_prefix_create_from_ip_address_len(&prefix->address, prefix->len, + &name_prefix); + fib_entry_t *entry = fib_contains(forwarder->fib, &name_prefix); + if (!entry) { + entry = fib_entry_create(&name_prefix, strategy_type, NULL, forwarder); + if (ingress_id != INVALID_FACE_ID) + fib_entry_nexthops_add(entry, ingress_id); + entry = fib_add(forwarder->fib, entry); - return res; + } else { + if (ingress_id != INVALID_FACE_ID) + fib_entry_nexthops_add(entry, ingress_id); + } + + forwarder_on_route_event(forwarder, entry); + + return true; } +bool forwarder_remove_route(forwarder_t *forwarder, hicn_ip_prefix_t *prefix, + unsigned ingress_id) { + assert(forwarder); + assert(prefix); -bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, - unsigned ifidx) { - parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); - parcAssertNotNull(control, "Parameter route must be non-null"); + hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; + hicn_prefix_create_from_ip_address_len(&prefix->address, prefix->len, + &name_prefix); + fib_remove(forwarder->fib, &name_prefix, ingress_id); - // we only have one message processor - return messageProcessor_RemoveRoute(forwarder->processor, control, ifidx); + return true; } #ifdef WITH_POLICY -bool forwarder_AddOrUpdatePolicy(Forwarder *forwarder, - add_policy_command *control) { - parcAssertNotNull(forwarder, "Parameter forwarder must be non-null"); - parcAssertNotNull(control, "Parameter control must be non-null"); +bool forwarder_add_or_update_policy(forwarder_t *forwarder, + hicn_ip_prefix_t *prefix, + hicn_policy_t *policy) { + assert(forwarder); + assert(prefix); + assert(policy); + + hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; + hicn_prefix_create_from_ip_address_len(&prefix->address, prefix->len, + &name_prefix); + fib_entry_t *entry = fib_contains(forwarder->fib, &name_prefix); + if (!entry) return false; - return messageProcessor_AddOrUpdatePolicy(forwarder->processor, control); + fib_entry_set_policy(entry, *policy); + + return true; } -bool forwarder_RemovePolicy(Forwarder *forwarder, remove_policy_command *control) { - parcAssertNotNull(forwarder, "Parameter forwarder must be non-null"); - parcAssertNotNull(control, "Parameter control must be non-null"); +bool forwarder_remove_policy(forwarder_t *forwarder, hicn_ip_prefix_t *prefix) { + assert(forwarder); + assert(prefix); + + hicn_prefix_t name_prefix = HICN_PREFIX_EMPTY; + hicn_prefix_create_from_ip_address_len(&prefix->address, prefix->len, + &name_prefix); + fib_entry_t *entry = fib_contains(forwarder->fib, &name_prefix); + if (!entry) return false; - return messageProcessor_RemovePolicy(forwarder->processor, control); + fib_entry_set_policy(entry, POLICY_EMPTY); + + return true; } #endif /* WITH_POLICY */ -void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, - unsigned connectionId) { - parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); - messageProcessor_RemoveConnectionIdFromRoutes(forwarder->processor, - connectionId); +void forwarder_remove_connection_id_from_routes(const forwarder_t *forwarder, + unsigned connection_id) { + fib_entry_t **removed_entries = NULL; + size_t num_removed_entries; + + assert(forwarder); + + fib_remove_connection(forwarder->fib, connection_id, &removed_entries, + &num_removed_entries); + + if (num_removed_entries > 0) { + assert(removed_entries); + + for (int i = 0; i < num_removed_entries; i++) { + fib_entry_t *entry = removed_entries[i]; + forwarder_on_route_event(forwarder, entry); + fib_remove_entry(forwarder->fib, entry); + } + free(removed_entries); + } } -void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, - strategy_type strategy, - unsigned related_prefixes_len, - Name **related_prefixes) { - parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); - parcAssertNotNull(prefix, "Parameter prefix must be non-null"); +void forwarder_add_strategy_options(forwarder_t *forwarder, + hicn_prefix_t *name_prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options) { + assert(forwarder); + assert(name_prefix); + assert(strategy_options); + assert(STRATEGY_TYPE_VALID(strategy_type)); - processor_SetStrategy(forwarder->processor, prefix, strategy, - related_prefixes_len, related_prefixes); + fib_entry_t *entry = fib_contains(forwarder->fib, name_prefix); + if (!entry) return; + + fib_entry_add_strategy_options(entry, strategy_type, strategy_options); } -FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder) { - return messageProcessor_GetFibEntries(forwarder->processor); +void forwarder_set_strategy(forwarder_t *forwarder, hicn_prefix_t *prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options) { + assert(forwarder); + assert(prefix); + assert(STRATEGY_TYPE_VALID(strategy_type)); + /* strategy_options might be NULL */ + + fib_entry_t *entry = fib_contains(forwarder->fib, prefix); + if (!entry) { + // there is no exact match. so if the forwarding strategy is not in + // the list of strategies that can be set by the transport, return + if (strategy_type != STRATEGY_TYPE_BESTPATH && + strategy_type != STRATEGY_TYPE_REPLICATION) { + return; + } + + // here it may be the transprot that wants to set the strategy, but it has + // no knowledge of the length of the prefix. so we apply the strategy at the + // matching fib entry, which later will be the one that will be used to send + // interests with this name + entry = fib_match_prefix(forwarder->fib, prefix); + if (!entry) { + return; // no fib match, return + } + } + + fib_entry_set_strategy(entry, strategy_type, strategy_options); } -void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, - size_t maximumContentStoreSize) { - messageProcessor_SetContentObjectStoreSize(forwarder->processor, - maximumContentStoreSize); +cs_t *forwarder_get_cs(const forwarder_t *forwarder) { + assert(forwarder); + + return pkt_cache_get_cs(forwarder->pkt_cache); } -void forwarder_ClearCache(Forwarder *forwarder) { - messageProcessor_ClearCache(forwarder->processor); +// IMPORTANT: Use this function ONLY for read-only operations since a +// realloc would otherwise modify the returned copy but not the original +// msgbuf ids vector in the forwarder. This constraint cannot be enforced +// by returning a (const off_t *) because the vector_t macros still cast +// to (void **). +off_t *forwarder_get_acquired_msgbuf_ids(const forwarder_t *forwarder) { + return forwarder->acquired_msgbuf_ids; } -PARCClock *forwarder_GetClock(const Forwarder *forwarder) { - return forwarder->clock; +void forwarder_acquired_msgbuf_ids_reset(const forwarder_t *forwarder) { + vector_reset(forwarder->acquired_msgbuf_ids); } -#if !defined(__APPLE__) -hicn_socket_helper_t *forwarder_GetHicnSocketHelper(Forwarder *forwarder) { - return forwarder->hicnSocketHelper; +void forwarder_acquired_msgbuf_ids_push(const forwarder_t *forwarder, + off_t msgbuf_id) { + vector_push(forwarder->acquired_msgbuf_ids, msgbuf_id); } -#endif // ======================================================= -static void _signal_cb(int sig, PARCEventType events, void *user_data) { - Forwarder *forwarder = (Forwarder *)user_data; +fib_t *forwarder_get_fib(const forwarder_t *forwarder) { + return forwarder->fib; +} + +msgbuf_pool_t *forwarder_get_msgbuf_pool(const forwarder_t *forwarder) { + return forwarder->msgbuf_pool; +} + +mapme_t *forwarder_get_mapme(const forwarder_t *forwarder) { + return forwarder->mapme; +} + +#ifdef WITH_POLICY_STATS +const policy_stats_mgr_t *forwarder_get_policy_stats_mgr( + const forwarder_t *forwarder) { + return &forwarder->policy_stats_mgr; +} +#endif /* WITH_POLICY_STATS */ + +/** + * @brief Finalize (i.e. close fd and free internal data structures) + * the current connection ("SELF") when the command is received. + * The connection cannot be removed inside the command handling + * because it is needed to return the ack back. + */ +static void _forwarder_finalize_connection_if_self(connection_t *conn, + msgbuf_t *msgbuf) { + uint8_t *packet = msgbuf_get_packet(msgbuf); + msg_connection_remove_t *msg = (msg_connection_remove_t *)packet; + cmd_connection_remove_t *control = &msg->payload; + + if (strcmp(control->symbolic_or_connid, "SELF") == 0) + connection_finalize(conn); +} - logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, - __func__, "signal %d events %d", sig, events); +ssize_t forwarder_receive(forwarder_t *forwarder, listener_t *listener, + off_t msgbuf_id, address_pair_t *pair, Ticks now) { + assert(forwarder); + /* listener can be NULL */ + assert(msgbuf_id_is_valid(msgbuf_id)); + assert(pair); + + hicn_name_t name; + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + assert(msgbuf); + + size_t size = msgbuf_get_len(msgbuf); + + const connection_table_t *table = + forwarder_get_connection_table(listener->forwarder); + + /* Connection lookup */ + if (msgbuf_get_connection_id(msgbuf) == CONNECTION_ID_UNDEFINED) { + connection_t *connection = connection_table_get_by_pair(table, pair); + unsigned conn_id = + connection + ? (unsigned)connection_table_get_connection_id(table, connection) + : CONNECTION_ID_UNDEFINED; + + assert((conn_id != CONNECTION_ID_UNDEFINED) || listener); + msgbuf->connection_id = conn_id; + } - switch ((int)sig) { - case SIGTERM: - logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, - __func__, "Caught an terminate signal; exiting cleanly."); - dispatcher_Stop(forwarder->dispatcher); + forwarder->stats.countReceived++; + + /* Initialize packet buffer stored in msgbuf through libhicn */ + msgbuf_initialize_from_packet(msgbuf); + + /* Detect packet type */ + hicn_packet_analyze(msgbuf_get_pkbuf(msgbuf)); + + msgbuf->recv_ts = now; + +RETRY: + + switch (msgbuf_get_type(msgbuf)) { + case HICN_PACKET_TYPE_INTEREST: + if (!connection_id_is_valid(msgbuf->connection_id)) { + char conn_name[SYMBOLIC_NAME_LEN]; + int rc = connection_table_get_random_name(table, conn_name); + if (rc < 0) { + ERROR("Could not create name for new connection"); + goto DROP; + } + + unsigned connection_id = + listener_create_connection(listener, conn_name, pair); + if (connection_id == CONNECTION_ID_UNDEFINED) { + ERROR("Could not create new connection"); + goto DROP; + } + msgbuf->connection_id = connection_id; + } + msgbuf->path_label = 0; // not used for interest packets + hicn_interest_get_name(msgbuf_get_pkbuf(msgbuf), &name); + msgbuf_set_name(msgbuf, &name); +#ifdef WITH_WLDR + forwarder_apply_wldr(forwarder, msgbuf, connection); +#endif /* WITH_WLDR */ + forwarder_process_interest(forwarder, msgbuf_id); + + pkt_cache_log(forwarder->pkt_cache); break; - case SIGINT: - logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, - __func__, "Caught an interrupt signal; exiting cleanly."); - dispatcher_Stop(forwarder->dispatcher); + case HICN_PACKET_TYPE_DATA: + /* This include probes */ + if (!connection_id_is_valid(msgbuf->connection_id)) { + ERROR("Invalid connection for data packet"); + goto DROP; + } + msgbuf_init_pathlabel(msgbuf); + hicn_data_get_name(msgbuf_get_pkbuf(msgbuf), &name); + msgbuf_set_name(msgbuf, &name); +#ifdef WITH_WLDR + forwarder_apply_wldr(forwarder, msgbuf, connection); +#endif /* WITH_WLDR */ + forwarder_process_data(forwarder, msgbuf_id); + + pkt_cache_log(forwarder->pkt_cache); break; -#ifndef _WIN32 - case SIGUSR1: - // dump stats + +#ifdef WITH_WLDR + case HICN_PACKET_TYPE_WLDR_NOTIFICATION: + if (!connection_id_is_valid(msgbuf->connection_id)) { + ERROR("Invalid connection for WLDR packet"); + goto DROP; + } + connection_t *connection = + connection_table_get_by_id(table, msgbuf->connection_id); + connection_wldr_handle_notification(connection, msgbuf); break; #endif - default: + case HICN_PACKET_TYPE_MAPME: + INFO("Received MAP-Me packet"); + if (!connection_id_is_valid(msgbuf->connection_id)) { + char conn_name[SYMBOLIC_NAME_LEN]; + int rc = connection_table_get_random_name(table, conn_name); + if (rc < 0) { + ERROR("Could not create name for new connection"); + goto DROP; + } + + unsigned connection_id = + listener_create_connection(listener, conn_name, pair); + if (connection_id == CONNECTION_ID_UNDEFINED) { + ERROR("Could not create new connection"); + goto DROP; + } + INFO("Created connection upon MAP-Me packet"); + msgbuf->connection_id = connection_id; + } + mapme_process(forwarder->mapme, msgbuf); break; - } -} -static void _keepalive_cb(int fd, PARCEventType what, void *user_data) { - parcAssertTrue(what & PARCEventType_Timeout, "Got unexpected tick_cb: %d", - what); - // function is just a keepalive for hicn-light, does not do anything -} + case HICN_PACKET_TYPE_COMMAND: + // Create the connection to send the ack back + if (!connection_id_is_valid(msgbuf->connection_id)) { + char conn_name[SYMBOLIC_NAME_LEN]; + int rc = connection_table_get_random_name(table, conn_name); + if (rc < 0) { + ERROR("Could not create name for new connection"); + goto DROP; + } + + unsigned connection_id = + listener_create_connection(listener, conn_name, pair); + if (connection_id == CONNECTION_ID_UNDEFINED) { + ERROR("Could not create new connection"); + goto DROP; + } + msgbuf->connection_id = connection_id; + } + + msg_header_t *msg = (msg_header_t *)msgbuf_get_packet(msgbuf); + msgbuf->command.type = msg->header.command_id; + if (!command_type_is_valid(msgbuf->command.type)) { + ERROR("Invalid command %d", msgbuf->command.type); + goto DROP; + } + + /* + * We need to retrieve the connection (in case it is useful) before + * proceeding through the removal + */ + connection_t *connection = + connection_table_get_by_id(table, msgbuf_get_connection_id(msgbuf)); + size = command_process_msgbuf(forwarder, msgbuf); + if (msgbuf->command.type == COMMAND_TYPE_CONNECTION_REMOVE) + _forwarder_finalize_connection_if_self(connection, msgbuf); + return size; -#ifdef WITH_MAPME -FIB *forwarder_getFib(Forwarder *forwarder) { - return messageProcessor_getFib(forwarder->processor); -} + default: + /* Commands are not recognized by the packet parser */ + if (msgbuf_is_command(msgbuf)) { + msgbuf_set_type(msgbuf, HICN_PACKET_TYPE_COMMAND); + goto RETRY; + } + goto DROP; + } -void forwarder_onConnectionEvent(Forwarder *forwarder, const Connection *conn, connection_event_t event) { -//#ifdef WITH_POLICY -// messageProcessor_onConnectionEvent(forwarder->processor, conn, event); -//#else - mapme_onConnectionEvent(forwarder->mapme, conn, event); -//#endif /* WITH_POLICY */ -} + return size; -void forwarder_ProcessMapMe(Forwarder *forwarder, const uint8_t *msgBuffer, - unsigned conn_id) { - mapme_Process(forwarder->mapme, msgBuffer, conn_id); +DROP: + forwarder_drop(forwarder, msgbuf_id); + return 0; } -MapMe * -forwarder_getMapmeInstance(const Forwarder *forwarder) { - return forwarder->mapme; +void forwarder_log(forwarder_t *forwarder) { + DEBUG( + "Forwarder: received = %u (interest = %u, data = %u), dropped = %u " + "(interest = %u, data = %u, other = %u), forwarded = { interests = " + "%u, " + "data = %u }, dropped = { connection_not_found = %u, send_failure " + "= " + "%u, " + "no_route_in_fib = %u }, interest processing = { aggregated = %u, " + "retransmitted = %u, satisfied_from_cs = %u, expired_interests = " + "%u, " + "expired_data = %u }, data processing = { " + "no_reverse_path = %u }\n", + forwarder->stats.countReceived, forwarder->stats.countInterestsReceived, + forwarder->stats.countObjectsReceived, forwarder->stats.countDropped, + forwarder->stats.countInterestsDropped, + forwarder->stats.countObjectsDropped, forwarder->stats.countOtherDropped, + forwarder->stats.countInterestForwarded, + forwarder->stats.countObjectsForwarded, + forwarder->stats.countDroppedConnectionNotFound, + forwarder->stats.countSendFailures, forwarder->stats.countDroppedNoRoute, + forwarder->stats.countInterestsAggregated, + forwarder->stats.countInterestsRetransmitted, + forwarder->stats.countInterestsSatisfiedFromStore, + forwarder->stats.countInterestsExpired, forwarder->stats.countDataExpired, + forwarder->stats.countDroppedNoReversePath); } -#endif /* WITH_MAPME */ +forwarder_stats_t forwarder_get_stats(forwarder_t *forwarder) { + return forwarder->stats; +} diff --git a/hicn-light/src/hicn/core/forwarder.h b/hicn-light/src/hicn/core/forwarder.h index d1815b7d4..b7ce5ff4d 100644 --- a/hicn-light/src/hicn/core/forwarder.h +++ b/hicn-light/src/hicn/core/forwarder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2022 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -18,292 +18,241 @@ * only be called within the forwarders thread of execution. */ -#ifndef forwarder_h -#define forwarder_h +#ifndef HICNLIGHT_FORWARDER_H +#define HICNLIGHT_FORWARDER_H -#ifndef _WIN32 -#include <sys/time.h> -#endif -#include <stdlib.h> - -#include <hicn/core/connectionTable.h> -#include <hicn/core/dispatcher.h> -#include <hicn/messenger/messenger.h> +//#ifndef _WIN32 +//#include <sys/time.h> +//#endif +// -#include <hicn/core/message.h> +#include <stdlib.h> +#include <sys/socket.h> // struct mmsghdr -#include <hicn/config/configuration.h> +#include "connection.h" +#include "connection_table.h" +#include "packet_cache.h" +#include "listener_table.h" +#include "msgbuf.h" +#include "msgbuf_pool.h" +#include "../config/configuration.h" +#include "subscription.h" #ifdef WITH_MAPME -#include <hicn/processor/fib.h> +#include "fib.h" #endif /* WITH_MAPME */ -#include <hicn/core/logger.h> -#include <hicn/core/ticks.h> -#include <hicn/io/listenerSet.h> - -#include <hicn/processor/fibEntryList.h> - -#include <parc/algol/parc_Clock.h> - -#if !defined(__APPLE__) -#include <hicn/socket/api.h> -#endif - #define PORT_NUMBER 9695 #define PORT_NUMBER_AS_STRING "9695" -#include <hicn/utils/commands.h> +//#include <hicn/utils/commands.h> // ============================================== -struct forwarder; -typedef struct forwarder Forwarder; +typedef struct forwarder_s forwarder_t; /** - * @function forwarder_Create - * @abstract Create the forwarder and use the provided logger for diagnostic + * @brief Create the forwarder and use the provided logger for diagnostic * output * @discussion * If the logger is null, hicn-light will create a STDOUT logger. * * @param logger may be NULL */ -Forwarder *forwarder_Create(Logger *logger); +forwarder_t *forwarder_create(configuration_t *configuration); /** - * @function forwarder_Destroy - * @abstract Destroys the forwarder, stopping all traffic and freeing all memory + * @brief Destroys the forwarder, stopping all traffic and freeing all memory */ -void forwarder_Destroy(Forwarder **ptr); +void forwarder_free(forwarder_t *forwarder); /** - * @function forwarder_SetupAllListeners - * @abstract Setup all listeners (tcp, udp, local, ether, ip multicast) on all - * interfaces - * @discussion - * Sets up all listeners on all running interfaces. This provides a quick and - * easy startup, rather than providing a configuration file or programmatic - * commands. - * - * @param port is used by TCP and UDP listeners, in host byte order - * @param localPath is the AF_UNIX path to use, if NULL no AF_UNIX listener is - * setup - */ -void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, - const char *localPath); -/** - * @function forwarder_SetupAllListeners - * @abstract Setup one tcp and one udp listener on address 127.0.0.1 and the + * @brief Setup one tcp and one udp listener on address 127.0.0.1 and the * given port */ -void forwarder_SetupLocalListeners(Forwarder *forwarder, uint16_t port); - -/** - * Configure hicn-light via a configuration file - * - * The configuration file is a set of lines, just like used in hicnLightControl. - * You need to have "add listener" lines in the file to receive connections. No - * default listeners are configured. - * - * @param [in] forwarder An alloated Forwarder - * @param [in] filename The path to the configuration file - */ -void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename); - -/** - * Returns the logger used by this forwarder - * - * If you will store the logger, you should acquire a reference to it. - * - * @param [in] forwarder An allocated hicn-light forwarder - * - * @retval non-null The logger used by hicn-light - * @retval null An error - */ -Logger *forwarder_GetLogger(const Forwarder *forwarder); - -/** - * @function forwarder_SetLogLevel - * @abstract Sets the minimum level to log - */ -void forwarder_SetLogLevel(Forwarder *forwarder, PARCLogLevel level); +void forwarder_setup_local_listeners(forwarder_t *forwarder, uint16_t port); -/** - * @function forwarder_GetNextConnectionId - * @abstract Get the next identifier for a new connection - */ -unsigned forwarder_GetNextConnectionId(Forwarder *forwarder); +configuration_t *forwarder_get_configuration(forwarder_t *forwarder); -Messenger *forwarder_GetMessenger(Forwarder *forwarder); - -Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder); +subscription_table_t *forwarder_get_subscriptions(const forwarder_t *forwarder); /** * Returns the set of currently active listeners * - * @param [in] forwarder An allocated hicn-light forwarder + * @param[in] forwarder An allocated hicn-light forwarder * * @retval non-null The set of active listeners * @retval null An error */ -ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder); +listener_table_t *forwarder_get_listener_table(const forwarder_t *forwarder); /** * Returns the forwrder's connection table * - * @param [in] forwarder An allocated hicn-light forwarder + * @param[in] forwarder An allocated hicn-light forwarder * * @retval non-null The connection tabler * @retval null An error * */ -#ifdef WITH_POLICY -ConnectionTable *forwarder_GetConnectionTable(const Forwarder *forwarder); -#else -ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder); -#endif /* WITH_POLICY */ +connection_table_t *forwarder_get_connection_table( + const forwarder_t *forwarder); -/** - * Returns a Tick-based clock - * - * Runs at approximately 1 msec per tick (see HZ in forwarder.c). - * Do not Release this clock. If you save a copy of it, create your own - * reference to it with parcClock_Acquire(). - * - * @param [in] forwarder An allocated hicn-light forwarder - * - * @retval non-null An allocated hicn-light Clock based on the Tick counter - * @retval null An error - */ -PARCClock *forwarder_GetClock(const Forwarder *forwarder); +pkt_cache_t *forwarder_get_pkt_cache(const forwarder_t *forwarder); -/** - * Direct call to get the Tick clock - * - * Runs at approximately 1 msec per tick (see HZ in forwarder.c) - * - * @param [in] forwarder An allocated hicn-light forwarder - */ -Ticks forwarder_GetTicks(const Forwarder *forwarder); +void forwarder_cs_set_store(forwarder_t *forwarder, bool val); + +bool forwarder_cs_get_store(forwarder_t *forwarder); + +void forwarder_cs_set_serve(forwarder_t *forwarder, bool val); + +bool forwarder_cs_get_serve(forwarder_t *forwarder); /** - * Convert nano seconds to Ticks + * Sets the maximum number of content objects in the content store * - * Converts nano seconds to Ticks, based on HZ (in forwarder.c) + * Implementation dependent - may wipe the cache. */ -Ticks forwarder_NanosToTicks(uint64_t nanos); - -uint64_t forwarder_TicksToNanos(Ticks ticks); - -void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, - struct iovec *message, unsigned ingressId); +void forwarder_cs_set_size(forwarder_t *forwarder, size_t size); -void forwarder_Receive(Forwarder *forwarder, Message *mesage); +size_t forwarder_cs_get_size(forwarder_t *forwarder); +size_t forwarder_cs_get_num_stale_entries(forwarder_t *forwarder); +void forwarder_cs_clear(forwarder_t *forwarder); /** - * @function forwarder_AddOrUpdateRoute - * @abstract Adds or updates a route on all the message processors + * @brief Adds or updates a route on all the message processors */ -bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, - add_route_command *control, unsigned ifidx); +bool forwarder_add_or_update_route(forwarder_t *forwarder, + hicn_ip_prefix_t *prefix, + unsigned ingress_id); /** - * @function forwarder_RemoveRoute - * @abstract Removes a route from all the message processors + * @brief Removes a route from all the message processors */ -bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, - unsigned ifidx); +bool forwarder_remove_route(forwarder_t *forwarder, hicn_ip_prefix_t *prefix, + unsigned ingress_id); #ifdef WITH_POLICY /** - * @function forwarder_AddOrUpdatePolicy - * @abstract Adds or updates a policy on the message processor + * @brief Adds or updates a policy on the message processor */ -bool forwarder_AddOrUpdatePolicy(Forwarder *forwarder, add_policy_command *control); +bool forwarder_add_or_update_policy(forwarder_t *forwarder, + hicn_ip_prefix_t *prefix, + hicn_policy_t *policy); /** - * @function forwarder_RemovePolicy - * @abstract Removes a policy from the message processor + * @brief Removes a policy from the message processor */ -bool forwarder_RemovePolicy(Forwarder *forwarder, remove_policy_command *control); +bool forwarder_remove_policy(forwarder_t *forwarder, hicn_ip_prefix_t *prefix); + #endif /* WITH_POLICY */ /** * Removes a connection id from all routes */ -void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, - unsigned connectionId); +void forwarder_remove_connection_id_from_routes(const forwarder_t *forwarder, + unsigned connection_id); -/** - * @function forwarder_GetConfiguration - * @abstract The configuration object - * @discussion - * The configuration contains all user-issued commands. It does not include - * dynamic state. - */ -Configuration *forwarder_GetConfiguration(Forwarder *forwarder); +void forwarder_add_strategy_options(forwarder_t *forwarder, + hicn_prefix_t *name_prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options); + +void forwarder_set_strategy(forwarder_t *forwarder, hicn_prefix_t *prefix, + strategy_type_t strategy_type, + strategy_options_t *strategy_options); -FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder); +cs_t *forwarder_get_cs(const forwarder_t *forwarder); + +off_t *forwarder_get_acquired_msgbuf_ids(const forwarder_t *forwarder); /** - * Sets the maximum number of content objects in the content store - * - * Implementation dependent - may wipe the cache. + * @note Acquire msgbuf ids vector ONLY for read-only operations. */ -void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, - size_t maximumContentStoreSize); - -void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val); +off_t *forwarder_get_acquired_msgbuf_ids(const forwarder_t *forwarder); -bool forwarder_GetChacheStoreFlag(Forwarder *forwarder); +void forwarder_acquired_msgbuf_ids_reset(const forwarder_t *forwarder); -void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val); +void forwarder_acquired_msgbuf_ids_push(const forwarder_t *forwarder, + off_t msgbuf_id); -bool forwarder_GetChacheServeFlag(Forwarder *forwarder); +/** + * @brief Returns the forwarder's FIB. + * @param[in] forwarder - Pointer to the forwarder. + * @returns Pointer to the hICN FIB. + */ +fib_t *forwarder_get_fib(const forwarder_t *forwarder); -void forwarder_ClearCache(Forwarder *forwarder); +/** + * @brief Return the forwarder packet pool. + * @param[in] forwarder The forwarder from which to retrieve the packet + * pool. + * @return msgbuf_pool_t * The forwarder packet pool. + */ +msgbuf_pool_t *forwarder_get_msgbuf_pool(const forwarder_t *forwarder); -void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, - strategy_type strategy, unsigned related_prefixes_len, - Name **related_prefixes); -#if !defined(__APPLE__) -hicn_socket_helper_t *forwarder_GetHicnSocketHelper(Forwarder *forwarder); -#endif #ifdef WITH_MAPME -/** - * @function forwarder_getFib - * @abstract Returns the hICN forwarder's FIB. - * @param [in] forwarder - Pointer to the hICN forwarder. - * @returns Pointer to the hICN FIB. - */ -FIB *forwarder_getFib(Forwarder *forwarder); +void forwarder_on_route_event(const forwarder_t *forwarder, fib_entry_t *entry); + +int forwarder_add_connection(const forwarder_t *forwarder, + const char *symbolic_name, face_type_t type, + address_pair_t *pair, policy_tags_t tags, + int priority, face_state_t admin_state); + +int forwarder_remove_connection(const forwarder_t *forwarder, + unsigned connection_id, bool finalize); /** - * @function forwarder_onConnectionEvent - * @abstract Callback fired upon addition of a new connection through the + * @brief Callback fired upon addition of a new connection through the * control protocol. - * @param [in] forwarder - Pointer to the hICN forwarder. - * @param [in] conn - Pointer to the newly added connection. - * @param [in] event - Connection event + * @param[in] forwarder - Pointer to the forwarder. + * @param[in] conn - Pointer to the newly added connection. + * @param[in] event - Connection event */ -void forwarder_onConnectionEvent(Forwarder *forwarder, const Connection *conn, connection_event_t event); +void forwarder_on_connection_event(const forwarder_t *forwarder, + const connection_t *connection, + connection_event_t event); /** - * @function forwarder_ProcessMapMe - * @abstract Callback fired by an hICN listener upon reception of a MAP-Me + * @brief Callback fired by an hICN listener upon reception of a MAP-Me * message. - * @param [in] forwarder - Pointer to the hICN forwarder. - * @param [in] msgBuffer - MAP-Me buffer - * @param [in] conn_id - Ingress connection id + * @param[in] forwarder - Pointer to the forwarder. + * @param[in] msgBuffer - MAP-Me buffer + * @param[in] conn_id - Ingress connection id */ -void forwarder_ProcessMapMe(Forwarder *forwarder, const uint8_t *msgBuffer, - unsigned conn_id); +void forwarder_process_mapme(const forwarder_t *forwarder, + const uint8_t *packet, unsigned conn_id); -struct mapme; -struct mapme * forwarder_getMapmeInstance(const Forwarder *forwarder); +struct mapme_s *forwarder_get_mapme(const forwarder_t *forwarder); #endif /* WITH_MAPME */ -#endif // forwarder_h +#ifdef WITH_POLICY_STATS +const policy_stats_mgr_t *forwarder_get_policy_stats_mgr( + const forwarder_t *forwarder); +#endif /* WITH_POLICY_STATS */ + +void forwarder_flush_connections(forwarder_t *forwarder); + +/** + * @brief Handles a newly received packet from a listener. + * + * NOTE: the received msgbuf is incomplete and only holds the packet content and + * size/ + */ +ssize_t forwarder_receive(forwarder_t *forwarder, listener_t *listener, + off_t msgbuf_id, address_pair_t *pair, Ticks now); + +/** + * @brief Log forwarder statistics, e.g. info about packets processed, packets + * dropped, packets forwarded, errors while forwarding, interest and data + * processing results. + * + * @param forwarder Pointer to the forwarder data structure to use + */ +void forwarder_log(forwarder_t *forwarder); + +forwarder_stats_t forwarder_get_stats(forwarder_t *forwarder); + +#endif // HICNLIGHT_FORWARDER_H diff --git a/hicn-light/src/hicn/core/listener.c b/hicn-light/src/hicn/core/listener.c new file mode 100644 index 000000000..d2f547863 --- /dev/null +++ b/hicn-light/src/hicn/core/listener.c @@ -0,0 +1,425 @@ +/* + * 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 listener.c + * @brief Implementation of hICN listeners + */ + +#include <string.h> // strdup + +#include <hicn/util/log.h> + +#include "forwarder.h" +#include "listener_vft.h" +#include "../io/base.h" + +listener_key_t listener_key_factory(address_t address, face_type_t type) { + listener_key_t key; + memset(&key, 0, sizeof(listener_key_t)); + + key.address = address; + key.type = type; + return key; +} + +listener_t *listener_create(face_type_t type, const address_t *address, + const char *interface_name, const char *name, + forwarder_t *forwarder) { + listener_table_t *table = forwarder_get_listener_table(forwarder); + + listener_key_t key = listener_key_factory(*address, type); + + listener_t *listener = listener_table_allocate(table, &key, name); + unsigned listener_id = + (unsigned int)listener_table_get_listener_id(table, listener); + + int ret = listener_initialize(listener, type, name, listener_id, address, + interface_name, forwarder); + if (ret < 0) { + listener_table_remove_by_id(table, listener_id); + listener_finalize(listener); + return NULL; + } + + WITH_INFO({ + char addr_str[NI_MAXHOST]; + int port; + address_to_string(address, addr_str, &port); + INFO("LISTENER CREATE (%s) %p created for address %s:%d", + face_type_str(listener->type), listener, addr_str, port); + listener_table_print_by_key(table); + }) + + return listener; +} + +int listener_initialize(listener_t *listener, face_type_t type, + const char *name, unsigned listener_id, + const address_t *address, const char *interface_name, + forwarder_t *forwarder) { + int rc; + + assert(listener); + assert(forwarder); + + *listener = (listener_t){ + .id = listener_id, + .name = strdup(name), + .key = listener_key_factory(*address, type), + .interface_name = strdup(interface_name), + .family = address->as_ss.ss_family, + .fd = 0, + .forwarder = forwarder, + }; + + face_protocol_t face_protocol = get_protocol(listener->type); + if (face_protocol == FACE_PROTOCOL_UNKNOWN) goto ERR_VFT; + + listener->data = malloc(listener_vft[face_protocol]->data_size); + if (!listener->data) goto ERR_DATA; + + assert(listener_has_valid_type(listener)); + + rc = listener_vft[face_protocol]->initialize(listener); + if (rc < 0) goto ERR_VFT; + + listener->fd = listener_vft[face_protocol]->get_socket(listener, address, + NULL, interface_name); + if (listener->fd < 0) { + char addr_str[NI_MAXHOST]; + int port; + address_to_string(address, addr_str, &port); + ERROR("Error creating listener %s fd: (%d) %s", addr_str, errno, + strerror(errno)); + goto ERR_FD; + } + assert(listener->fd > 0); + + // XXX data should be pre-allocated here + + loop_fd_event_create(&listener->event_data, MAIN_LOOP, listener->fd, listener, + (fd_callback_t)listener_read_callback, + CONNECTION_ID_UNDEFINED, NULL); + + if (!listener->event_data) { + goto ERR_REGISTER_FD; + } + + if (loop_fd_event_register(listener->event_data) < 0) { + goto ERR_REGISTER_FD; + } + + return 0; + +ERR_REGISTER_FD: +#ifndef _WIN32 + close(listener->fd); +#else + closesocket(listener->fd); +#endif +ERR_FD: +ERR_VFT: +ERR_DATA: + return -1; +} + +int listener_finalize(listener_t *listener) { + assert(listener); + assert(listener_has_valid_type(listener)); + + if (listener->event_data) { + loop_event_unregister(listener->event_data); + loop_event_free(listener->event_data); + } + + if (listener->fd != -1) { +#ifndef _WIN32 + close(listener->fd); +#else + closesocket(listener->fd); +#endif + } + + listener_vft[get_protocol(listener->type)]->finalize(listener); + + if (listener->data) free(listener->data); + listener->data = NULL; + if (listener->interface_name) free(listener->interface_name); + listener->interface_name = NULL; + if (listener->name) free(listener->name); + listener->name = NULL; + + return 0; +} + +int listener_get_socket(const listener_t *listener, const address_t *local, + const address_t *remote, const char *interface_name) { + assert(listener); + assert(listener_has_valid_type(listener)); + assert(local); + // assert(remote); TODO: can it be null? + + // DEBUG("[listener_get_socket]"); + + return listener_vft[get_protocol(listener->type)]->get_socket( + listener, local, remote, interface_name); +} + +/* + * This is called from the forwarder to dynamially create new connections on the + * listener, in that case, name is NULL. It is also called from + * connection_create, which is itself called from the configuration part. + */ +unsigned listener_create_connection(listener_t *listener, + const char *connection_name, + const address_pair_t *pair) { + assert(listener); + assert(listener_has_valid_type(listener)); + assert(pair); + + /* initialized so that gcc-9 does not complain */ + face_type_t connection_type = FACE_TYPE_UNDEFINED; + switch (listener->type) { + case FACE_TYPE_UDP_LISTENER: + connection_type = FACE_TYPE_UDP; + break; + case FACE_TYPE_TCP_LISTENER: + connection_type = FACE_TYPE_TCP; + break; + case FACE_TYPE_HICN: + case FACE_TYPE_HICN_LISTENER: + case FACE_TYPE_UDP: + case FACE_TYPE_TCP: + case FACE_TYPE_UNDEFINED: + case FACE_TYPE_N: + return CONNECTION_ID_UNDEFINED; + } + +#ifdef USE_CONNECTED_SOCKETS + /* + * We create a connected connection with its own fd, instead of returning + * the fd of the listener. This will allow to avoid specifying the + * destination address when sending packets, and will increase performance + * by avoiding a FIB lookup for each packet. + */ + int fd = listener_get_socket(listener, address_pair_get_local(pair), + address_pair_get_remote(pair), + listener->interface_name); + if (fd < 0) return CONNECTION_ID_UNDEFINED; +#else + int fd = 0; // means listener->fd; +#endif + bool local = address_is_local(address_pair_get_local(pair)); + + connection_table_t *table = + forwarder_get_connection_table(listener->forwarder); + connection_t *connection = + connection_table_allocate(table, pair, connection_name); + unsigned connection_id = + (unsigned int)connection_table_get_connection_id(table, connection); + + int rc = connection_initialize(connection, connection_type, connection_name, + listener->interface_name, fd, pair, local, + connection_id, listener); + if (rc < 0) { + connection_table_remove_by_id(table, connection_id); + connection_finalize(connection); + return CONNECTION_ID_UNDEFINED; + } + + WITH_INFO({ + char local_addr_str[NI_MAXHOST]; + char remote_addr_str[NI_MAXHOST]; + int local_port; + int remote_port; + address_to_string(&(pair->local), local_addr_str, &local_port); + address_to_string(&(pair->remote), remote_addr_str, &remote_port); + INFO("%s connection %p created for address pair %s:%d (local=%s) - %s:%d", + face_type_str(connection->type), connection, local_addr_str, + local_port, connection_is_local(connection) ? "true" : "false", + remote_addr_str, remote_port); + connection_table_print_by_pair(table); + }) + + forwarder_on_connection_event(listener->forwarder, connection, + CONNECTION_EVENT_CREATE); + + return connection_id; +} + +int listener_punt(const listener_t *listener, const char *prefix_s) { + assert(listener); + assert(listener_get_type(listener) == FACE_TYPE_HICN); + assert(prefix_s); + + return listener_vft[get_protocol(listener->type)]->punt(listener, prefix_s); +} + +ssize_t listener_read_single(listener_t *listener, int fd, + unsigned connection_id) { + assert(listener); + + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(listener->forwarder); + + // Preapare the msgbuf + msgbuf_t *msgbuf = NULL; + off_t msgbuf_id = msgbuf_pool_get(msgbuf_pool, &msgbuf); + if (!msgbuf_id_is_valid(msgbuf_id)) return -1; + + // Prepare the address pair + address_pair_t pair; + memset(&pair, 0, sizeof(address_pair_t)); + pair.local = listener->address; + + // Read message and populate the remote address + ssize_t n = listener_vft[get_protocol(listener->type)]->read_single( + fd, msgbuf, address_pair_get_remote(&pair)); + if (n <= 0) { + msgbuf_pool_put(msgbuf_pool, msgbuf); + return -1; + } + + msgbuf_pool_acquire(msgbuf); + msgbuf_set_connection_id(msgbuf, connection_id); + + // Process received packet + size_t processed_bytes = forwarder_receive(listener->forwarder, listener, + msgbuf_id, &pair, ticks_now()); + forwarder_log(listener->forwarder); + if (processed_bytes <= 0) ERROR("Unable to handle message"); + + /* + * The connection on which we went packets might do batching (even without + * sendmmsg), and we need to inform the system that we want to proceed to + * sending packets. + */ + forwarder_flush_connections(listener->forwarder); + msgbuf_pool_release(msgbuf_pool, &msgbuf); + return processed_bytes; +} + +ssize_t listener_read_batch(listener_t *listener, int fd, + unsigned connection_id) { + assert(listener); + + size_t total_processed_bytes = 0; + ssize_t num_msg_received = 0; + + forwarder_t *forwarder = listener->forwarder; + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(forwarder); + forwarder_acquired_msgbuf_ids_reset(forwarder); + + /* Receive messages in the loop as long as we manage to fill the buffers */ + do { + /* Prepare the msgbuf and address pair arrays */ + msgbuf_t *msgbufs[MAX_MSG]; + if (msgbuf_pool_getn(msgbuf_pool, msgbufs, MAX_MSG) < 0) { + ERROR("Unable to get message buffers"); + break; + } + + address_pair_t pair[MAX_MSG]; + address_t *address_remote[MAX_MSG]; + memset(&pair, 0, MAX_MSG * sizeof(address_pair_t)); + + off_t msgbuf_ids[MAX_MSG]; + for (unsigned i = 0; i < MAX_MSG; i++) { + // Copy the pointers to the remote addresses + address_remote[i] = address_pair_get_remote(&pair[i]); + + // Populate local addresses + pair[i].local = listener->address; + + // Do NOT rely on msgbuf pointers since a msgbuf pool rezise event may + // make them invalid, use msgbuf ids instead + msgbuf_ids[i] = msgbuf_pool_get_id(msgbuf_pool, msgbufs[i]); + } + + // Read batch and populate remote addresses + num_msg_received = listener_vft[get_protocol(listener->type)]->read_batch( + fd, msgbufs, address_remote, MAX_MSG); + + for (int i = 0; i < MAX_MSG; i++) { + // Release unused msg buffers + if (i >= num_msg_received) { + msgbuf_pool_put(msgbuf_pool, msgbufs[i]); + continue; + } + + msgbuf_pool_acquire(msgbufs[i]); + msgbuf_set_connection_id(msgbufs[i], connection_id); + forwarder_acquired_msgbuf_ids_push(forwarder, msgbuf_ids[i]); + } + + if (num_msg_received < 0) break; + TRACE("[listener_read_batch] batch size = %d", num_msg_received); + + for (unsigned i = 0; i < num_msg_received; i++) { + size_t processed_bytes = forwarder_receive( + forwarder, listener, msgbuf_ids[i], &pair[i], ticks_now()); + forwarder_log(listener->forwarder); + + total_processed_bytes += processed_bytes; + } + } while (num_msg_received == + MAX_MSG); /* backpressure based on queue size ? */ + + /* + * Signal to the forwarder that we reached the end of a batch and we need to + * flush connections out + */ + forwarder_flush_connections(forwarder); + + const off_t *acquired_msgbuf_ids = + forwarder_get_acquired_msgbuf_ids(forwarder); + for (int i = 0; i < vector_len(acquired_msgbuf_ids); i++) { + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, acquired_msgbuf_ids[i]); + msgbuf_pool_release(msgbuf_pool, &msgbuf); + } + + return total_processed_bytes; +} + +/* + * This might be called for a connection on the listener too. The listener is + * the entity that owns the buffers used for reading. + */ +ssize_t listener_read_callback(listener_t *listener, int fd, + unsigned connection_id, void *user_data) { + assert(listener); + assert(!user_data); + + /* + * As the listener callback is shared between the listener and the different + * connections created on top of it, the fd might be either of them. + */ + // assert(fd == listener->fd); + + if (listener_vft[get_protocol(listener->type)]->read_batch) + return listener_read_batch(listener, fd, connection_id); + + return listener_read_single(listener, fd, connection_id); +} + +void listener_setup_local(forwarder_t *forwarder, uint16_t port) { + address_t localhost_ipv4_addr = ADDRESS4_LOCALHOST(port); + listener_create(FACE_TYPE_UDP_LISTENER, &localhost_ipv4_addr, "lo", "lo_udp4", + forwarder); + + address_t localhost_ipv6_addr = ADDRESS6_LOCALHOST(port); + listener_create(FACE_TYPE_UDP_LISTENER, &localhost_ipv6_addr, "lo", "lo_udp6", + forwarder); +} diff --git a/hicn-light/src/hicn/core/listener.h b/hicn-light/src/hicn/core/listener.h new file mode 100644 index 000000000..76d865c5d --- /dev/null +++ b/hicn-light/src/hicn/core/listener.h @@ -0,0 +1,140 @@ +/* + * 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 listener.h + * @brief hICN listeners + */ + +#ifndef HICNLIGHT_LISTENER_H +#define HICNLIGHT_LISTENER_H + +#include <hicn/face.h> + +#include "address_pair.h" +#include "msgbuf.h" +#include <hicn/base/loop.h> + +#define LISTENER_ID_UNDEFINED ~0 + +struct forwarder_s; + +typedef struct { + address_t address; + face_type_t type; +} listener_key_t; + +static inline int listener_key_equals(const listener_key_t *key1, + const listener_key_t *key2) { + return address_equals(&key1->address, &key2->address) && + (key1->type == key2->type); +} + +/** + * @brief Create a listener key starting from an address and a face type. + * + * @param address + * @param type + * @return listener_key_t The listener key created. + * + * @note The listener key returned is resetted before intializing + * the internal fields, to allow a reliable hash generation. + */ +listener_key_t listener_key_factory(address_t address, face_type_t type); + +/* This structure holds what is in common to all listeners */ +typedef struct { + int id; + char *name; + union { + listener_key_t key; + struct { + address_t address; + face_type_t type; + }; + }; + + char *interface_name; + unsigned family; + + int fd; + event_t *event_data; + + void *data; /* Listener specific data */ + struct forwarder_s *forwarder; +} listener_t; + +#define listener_get_id(L) ((L)->id) +#define listener_get_name(L) ((L)->name) +#define listener_get_key(L) (&(L)->key) +#define listener_get_type(L) ((L)->type) +#define listener_get_interface_name(L) ((L)->interface_name) +#define listener_get_address(L) (&(L)->address) +#define listener_has_valid_type(L) (face_type_is_valid((L)->type)) +#define listener_id_is_valid(ID) (ID != LISTENER_ID_UNDEFINED) + +listener_t *listener_create(face_type_t type, const address_t *address, + const char *interface_name, const char *symbolic, + struct forwarder_s *forwarder); + +/** + * @brief Helper function used inside 'listener_create' to + * setup variables in listener struct. + * + * @see listener_create + */ +int listener_initialize(listener_t *listener, face_type_t type, + const char *name, unsigned listener_id, + const address_t *address, const char *interface_name, + struct forwarder_s *forwarder); + +int listener_finalize(listener_t *listener); + +int listener_punt(const listener_t *listener, const char *prefix_s); + +int listener_get_socket(const listener_t *listener, const address_t *local, + const address_t *remote, const char *interface_name); + +unsigned listener_create_connection(listener_t *listener, const char *name, + const address_pair_t *pair); + +void listener_setup_local(struct forwarder_s *forwarder, uint16_t port); + +void listener_process_packet(const listener_t *listener, const uint8_t *packet, + size_t size); + +ssize_t listener_read_single(listener_t *listener, int fd, + unsigned connection_id); +ssize_t listener_read_batch(listener_t *listener, int fd, + unsigned connection_id); + +/** + * @brief Callback helper function for batch reading data from listener fd. + * + * This function is usually called from the listener read callback to proceed to + * actual reading of data from the fd. + * + * @see listener_read_callback + * + * NOTE: the function returns size_t as for TCP we might need to know how much + * data we can consume from the socket. + */ +ssize_t listener_read_callback(listener_t *listener, int fd, + unsigned connection_id, void *user_data); + +#define listener_get_forwarder(listener) (listener->forwarder) +#define listener_get_fd(listener) (listener->fd) + +#endif /* HICNLIGHT_LISTENER_H */ diff --git a/hicn-light/src/hicn/core/listener_table.c b/hicn-light/src/hicn/core/listener_table.c new file mode 100644 index 000000000..4a0f74a13 --- /dev/null +++ b/hicn-light/src/hicn/core/listener_table.c @@ -0,0 +1,206 @@ +/* + * 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 listener_table.c + * \brief Implementation of hICN listener table + */ + +#include <hicn/util/log.h> +#include <hicn/util/sstrncpy.h> + +#include "listener_table.h" +#include "listener.h" + +/* This is only used as a hint for first allocation, as the table is resizeable + */ +#define DEFAULT_LISTENER_TABLE_SIZE 64 + +typedef struct { + char name[SYMBOLIC_NAME_LEN]; +} name_key_t; + +listener_table_t *_listener_table_create(size_t init_size, size_t max_size) { + if (init_size == 0) init_size = DEFAULT_LISTENER_TABLE_SIZE; + + listener_table_t *table = malloc(sizeof(listener_table_t)); + if (!table) return NULL; + + table->max_size = max_size; + + /* Initialize indices */ + table->id_by_name = kh_init_lt_name(); + table->name_keys = slab_create(name_key_t, SLAB_INIT_SIZE); + table->id_by_key = kh_init_lt_key(); + table->listener_keys = slab_create(listener_key_t, SLAB_INIT_SIZE); + + /* + * We start by allocating a reasonably-sized pool, as this will eventually + * be resized if needed. + */ + pool_init(table->listeners, init_size, 0); + + return table; +} + +void listener_table_free(listener_table_t *table) { + unsigned listener_id; + kh_foreach_value(table->id_by_key, listener_id, { + listener_t *listener = listener_table_get_by_id(table, listener_id); + const char *name = listener_get_name(listener); + INFO("Removing listener %s [%d]", name, listener->fd); + listener_finalize(listener); + }); + + kh_destroy_lt_name(table->id_by_name); + slab_free(table->name_keys); + kh_destroy_lt_key(table->id_by_key); + slab_free(table->listener_keys); + + pool_free(table->listeners); + free(table); +} + +listener_t *listener_table_allocate(const listener_table_t *table, + const listener_key_t *key, + const char *name) { + listener_t *listener = NULL; + pool_get(table->listeners, listener); + if (!listener) return NULL; + + off_t id = listener - table->listeners; + int rc; + + // Add in name hash table + name_key_t *name_copy = slab_get(name_key_t, table->name_keys); + strcpy_s(name_copy->name, sizeof(name_key_t), name); + + khiter_t k = kh_put_lt_name(table->id_by_name, name_copy->name, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(table->id_by_name, k) = (unsigned int)id; + + // Add in key hash table + listener_key_t *key_copy = slab_get(listener_key_t, table->listener_keys); + memcpy(key_copy, key, sizeof(listener_key_t)); + + k = kh_put_lt_key(table->id_by_key, key_copy, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(table->id_by_key, k) = (unsigned int)id; + + assert(kh_size(table->id_by_name) == kh_size(table->id_by_key)); + return listener; +} + +void listener_table_deallocate(const listener_table_t *table, + listener_t *listener) { + const char *name = listener_get_name(listener); + listener_key_t *key = listener_get_key(listener); + + // Remove from name hash table + khiter_t k = kh_get_lt_name(table->id_by_name, name); + assert(k != kh_end(table->id_by_name)); + kh_del_lt_name(table->id_by_name, k); + slab_put(table->name_keys, kh_key(table->id_by_name, k)); + + // Remove from key hash table + k = kh_get_lt_key(table->id_by_key, key); + assert(k != kh_end(table->id_by_key)); + kh_del_lt_key(table->id_by_key, k); + slab_put(table->listener_keys, kh_key(table->id_by_key, k)); + + assert(kh_size(table->id_by_name) == kh_size(table->id_by_key)); + pool_put(table->listeners, listener); +} + +listener_t *listener_table_get_by_address(listener_table_t *table, + face_type_t type, + const address_t *address) { + listener_key_t key = listener_key_factory(*address, type); + khiter_t k = kh_get_lt_key(table->id_by_key, &key); + if (k == kh_end(table->id_by_key)) return NULL; + return listener_table_at(table, kh_val(table->id_by_key, k)); +} + +listener_t *listener_table_get_by_key(listener_table_t *table, + const listener_key_t *key) { + khiter_t k = kh_get_lt_key(table->id_by_key, key); + if (k == kh_end(table->id_by_key)) return NULL; + return listener_table_at(table, kh_val(table->id_by_key, k)); +} + +void listener_table_remove_by_id(listener_table_t *table, off_t id) { + listener_t *listener = listener_table_at(table, id); + INFO("Removing listener %d (%s)", id, listener_get_name(listener)); + + listener_table_deallocate(table, listener); +} + +off_t listener_table_get_id_by_name(const listener_table_t *table, + const char *name) { + khiter_t k = kh_get_lt_name(table->id_by_name, name); + if (k == kh_end(table->id_by_name)) return LISTENER_ID_UNDEFINED; + return kh_val(table->id_by_name, k); +} + +listener_t *listener_table_get_by_name(listener_table_t *table, + const char *name) { + unsigned listener_id = + (unsigned int)listener_table_get_id_by_name(table, name); + if (!listener_id_is_valid(listener_id)) return NULL; + return listener_table_at(table, listener_id); +} + +listener_t *_listener_table_get_by_id(listener_table_t *table, off_t id) { + return listener_table_get_by_id(table, id); +} + +void listener_table_print_by_key(const listener_table_t *table) { + const listener_key_t *k; + unsigned v; + + char addr_str[NI_MAXHOST]; + int port; + listener_t *listener; + const char *name; + + INFO("*** Listener table ***"); + kh_foreach(table->id_by_key, k, v, { + address_to_string(&k->address, addr_str, &port); + listener = listener_table_get_by_id(table, v); + name = listener_get_name(listener); + INFO("%s:%d - %s\t\t\t\t(%u, %s)", addr_str, port, face_type_str(k->type), + v, name); + }) +} + +void listener_table_print_by_name(const listener_table_t *table) { + const char *k; + unsigned v; + + char addr_str[NI_MAXHOST]; + int port; + listener_t *listener; + const listener_key_t *key; + + INFO("*** Listener table ***"); + kh_foreach(table->id_by_name, k, v, { + listener = listener_table_get_by_id(table, v); + key = listener_get_key(listener); + address_to_string(&key->address, addr_str, &port); + + INFO("%s:%d - %s\t\t\t\t(%u, %s)", addr_str, port, face_type_str(key->type), + v, k); + }) +} diff --git a/hicn-light/src/hicn/core/listener_table.h b/hicn-light/src/hicn/core/listener_table.h new file mode 100644 index 000000000..062d1748e --- /dev/null +++ b/hicn-light/src/hicn/core/listener_table.h @@ -0,0 +1,270 @@ +/* + * 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 listener_table.h + * \brief hICN listener table + * + * The listener table is composed of: + * - a pool of listeners allowing access through their index in constant time; + * - a set of indices in the form of hash table for efficient index lookups: + * . by name + * . by key (listener_type, address) + * + * For efficient index retrieval, the header will be prepended and the + * resulting pointer will directly point to the listener pool. + */ + +#ifndef HICNLIGHT_LISTENER_TABLE_H +#define HICNLIGHT_LISTENER_TABLE_H + +#include <hicn/util/khash.h> +#include <hicn/util/hash.h> +#include <hicn/util/slab.h> +#include "address.h" +#include "listener.h" +#include <hicn/util/pool.h> + +/* Hash functions for indices */ +#define key_hash(key) (hash_struct(key)) + +/* Hash table types for indices */ +KHASH_MAP_INIT_STR(lt_name, unsigned); +KHASH_INIT(lt_key, const listener_key_t *, unsigned, 1, key_hash, + listener_key_equals); + +typedef struct { + size_t max_size; + + kh_lt_key_t *id_by_key; + slab_t *listener_keys; + kh_lt_name_t *id_by_name; + slab_t *name_keys; + + listener_t *listeners; // pool +} listener_table_t; + +/** + * @brief Allocate a listener from the listener table. + * + * @param[in] table The listener table from which to allocate a listener. + * @param[out] listener The pointer that will hold the allocated listener. + * @param[in] pair The address pair associated to the listener (to update index) + * @param[in] name The name associated to the listener (to update index) + * + * NOTE: + * - This function updates all indices from the listener table if the + * allocation is successful. + * - You should always check that the returned listener is not NULL, which + * would signal that the pool is exhausted and could not be extended. + */ +listener_t *listener_table_allocate(const listener_table_t *table, + const listener_key_t *key, + const char *name); + +/** + * @brief Deallocate a listener and return it to the listener table pool. + * + * @param[in] table The listener table to which the listener is returned. + * @param[in] conn The listener that is returned to the pool. + * + * NOTE: + * - Upon returning a listener to the pool, all indices pointing to that + * listener are also cleared. + */ +void listener_table_deallocate(const listener_table_t *table, + listener_t *listener); + +/** + * @brief Returns the length of the listener table, the number of active + * listeners. + * + * @param[in] table The listener table for which we retrieve the length. + * + * @return size_t The length of the listener table. + * + * NOTE: + * - The length of the listener table, that is the number of currently active + * listeners. + */ +#define listener_table_len(table) (pool_len(table->listeners)) + +/** + * @brief Validate an index in the listener table. + * + * @param[in] table The listener table in which to validate an index. + * @param[in] id The index of the listener to validate. + * + * @return bool A flag indicating whether the listener index is valid or not. + */ +#define listener_table_validate_id(table, id) \ + pool_validate_id(table->listeners, id) + +/** + * @brief Return the listener corresponding to the specified index in the + * listener table. + * + * @param[in] table The listener table for which to retrieve the listener. + * @param[in] id The index for which to retrieve the listener. + * + * @return listener_t * The listener correponding to the specified index in + * the listener table. + * + * @see listener_table_get_by_id + * + * NOTE: + * - In this function, the index is not validated. + */ +#define listener_table_at(table, id) ((table)->listeners + id) + +/** + * @brief Return the listener corresponding to the specified and validated + * index in the listener table. + * + * @param[in] table The listener table for which to retrieve the listener. + * @param[in] id The index for which to retrieve the listener. + * + * @return listener_t * The listener correponding to the specified index in + * the listener table. + * + * @see listener_table_get_by_id + * + * NOTE: + * - In this function, the index is validated. + */ +#define listener_table_get_by_id(table, id) \ + listener_table_validate_id(table, id) ? listener_table_at(table, id) : NULL + +/** + * @brief Helper function to avoid macro expansion in c++ tests. Wrapper around + * 'listener_table_get_by_id'. + */ +listener_t *_listener_table_get_by_id(listener_table_t *table, off_t id); + +/** + * @brief Returns the index of a given listener in the listener table. + * + * @param[in] table The listener table from which to retrieve the index. + * @param[in] conn The listener for which to retrieve the index. + * + * @return off_t The index of the specified listener in the listener table. + */ +#define listener_table_get_listener_id(table, listener) \ + (listener - table->listeners) + +#define listener_table_foreach(table, listener, BODY) \ + do { \ + listener_t *listener; \ + (void)listener; \ + pool_foreach( \ + table->listeners, listener, do { BODY } while (0)); \ + } while (0) + +#define listener_table_enumerate(table, i, conn, BODY) \ + pool_enumerate(table->listeners, (i), (conn), BODY) + +/** + * @brief Create a new listener table (extended parameters) + * + * @param[in] init_size Initially allocated size (hint, 0 = use default value) + * @param[in] max_size Maximum size (0 = unlimited) + * + * @return listener_table_t* - The newly created listener table + */ +listener_table_t *_listener_table_create(size_t init_size, size_t max_size); + +/** + * @brief Create a new listener table (minimal parameters) + * + * @return listener_table_t* - The newly created listener table + */ +#define listener_table_create() _listener_table_create(0, 0) + +/** + * @brief Free a listener table + * + * @param[in] table Listener table to free + */ +void listener_table_free(listener_table_t *table); + +/** + * @brief Retrieve a listener from the listener table by address. + * + * @param[in] table The listener table in which to search. + * @param[in] type The face type characterizing the listener to search for. + * @param[in] address The address to search for. + * + * @return listener_t * The listener matching the specified address, or + * NULL if not found. + */ +listener_t *listener_table_get_by_address(listener_table_t *table, + face_type_t type, + const address_t *address); + +/** + * @brief Retrieve a listener from the listener table by listener key (i.e. + * address + face type). + * + * @param[in] table The listener table in which to search. + * @param[in] key The listener key characterizing the listener to search for. + * + * @return listener_t * The listener matching the specified address, or + * NULL if not found. + */ +listener_t *listener_table_get_by_key(listener_table_t *table, + const listener_key_t *key); + +/** + * @brief Return a listener index from the listener table by name. + * + * @param[in] table The listener table in which to search. + * @param[in] name The name to search for. + * + * @return off_t The index of the listener matching the name, or + * LISTENER_ID_UNDEFINED if not found. + */ +off_t listener_table_get_id_by_name(const listener_table_t *table, + const char *name); + +/** + * @brief Return a listener from the listener table by name. + * + * @param[in] table The listener table in which to search. + * @param[in] name The name to search for. + * + * @return listener_t * The listener matching the name, or NULL if not + * found. + */ +listener_t *listener_table_get_by_name(listener_table_t *table, + const char *name); + +/** + * @brief Remove a listener from the listener table by its index. + * + * @param[in] table The listener table from which to delete the listener. + * @param[in] id The index of the listener to remove. + */ +void listener_table_remove_by_id(listener_table_t *table, off_t id); + +/** + * @brief Print the listener table content. + * + * @param[in] table The listener table to print. + */ +void listener_table_print_by_key(const listener_table_t *table); + +void listener_table_print_by_name(const listener_table_t *table); + +#endif /* HICNLIGHT_LISTENER_TABLE_H */ diff --git a/hicn-light/src/hicn/core/messagePacketType.h b/hicn-light/src/hicn/core/listener_vft.c index dfbb12342..600c9482f 100644 --- a/hicn-light/src/hicn/core/messagePacketType.h +++ b/hicn-light/src/hicn/core/listener_vft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -14,19 +14,23 @@ */ /** - * @file message_packet_type_h - * @brief Defines the packet type for a HICN message - * + * @file listener_vft.c + * @brief Implementation of listener VFT */ -#ifndef message_packet_type_h -#define message_packet_type_h +#include "listener_vft.h" + +#ifdef __linux__ +extern listener_ops_t listener_hicn; +#endif +extern listener_ops_t listener_tcp; +extern listener_ops_t listener_udp; -typedef enum message_type { - MessagePacketType_Unknown, - MessagePacketType_Interest, - MessagePacketType_ContentObject, - MessagePacketType_WldrNotification -} MessagePacketType; +const listener_ops_t* listener_vft[] = { +#ifdef __linux__ + [FACE_PROTOCOL_HICN] = &listener_hicn, +#endif -#endif // message_packet_type_h + [FACE_PROTOCOL_TCP] = &listener_tcp, + [FACE_PROTOCOL_UDP] = &listener_udp, +}; diff --git a/hicn-light/src/hicn/core/listener_vft.h b/hicn-light/src/hicn/core/listener_vft.h new file mode 100644 index 000000000..97f08778c --- /dev/null +++ b/hicn-light/src/hicn/core/listener_vft.h @@ -0,0 +1,58 @@ +/* + * 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 listener_vft.h + * @brief Listener VFT + */ + +#ifndef HICNLIGHT_LISTENER_VFT_H +#define HICNLIGHT_LISTENER_VFT_H + +#include <hicn/core/address_pair.h> +#include <hicn/core/connection.h> +#include <hicn/core/listener.h> +#include <hicn/face.h> + +typedef struct { + int (*initialize)(listener_t* listener); + void (*finalize)(listener_t* listener); + int (*punt)(const listener_t* listener, const char* prefix_s); + int (*get_socket)(const listener_t* listener, const address_t* local, + const address_t* remote, const char* interface_name); + int (*send)(const connection_t* connection, const address_t* dummy, + msgbuf_t* msgbuf, bool queue); + int (*send_packet)(const connection_t* connection, const uint8_t* packet, + size_t size); + ssize_t (*read_single)(int fd, msgbuf_t* msgbuf, address_t* address); + ssize_t (*read_batch)(int fd, msgbuf_t** msgbuf, address_t** address, + size_t len); + size_t data_size; +} listener_ops_t; + +#define DECLARE_LISTENER(NAME) \ + const listener_ops_t listener_##NAME = { \ + .initialize = listener_##NAME##_initialize, \ + .finalize = listener_##NAME##_finalize, \ + .punt = listener_##NAME##_punt, \ + .get_socket = listener_##NAME##_get_socket, \ + .read_single = listener_##NAME##_read_single, \ + .read_batch = listener_##NAME##_read_batch, \ + .data_size = sizeof(listener_##NAME##_data_t), \ + } + +extern const listener_ops_t* listener_vft[]; + +#endif /* HICNLIGHT_LISTENER_VFT_H */ diff --git a/hicn-light/src/hicn/core/logger.c b/hicn-light/src/hicn/core/logger.c deleted file mode 100644 index 0b9bb264c..000000000 --- a/hicn-light/src/hicn/core/logger.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _WIN32 -#include <unistd.h> -#endif - -#include <errno.h> -#include <hicn/hicn-light/config.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> - -#include <parc/assert/parc_Assert.h> - -#include <parc/algol/parc_Memory.h> -#include <parc/algol/parc_Object.h> - -#include <parc/logging/parc_Log.h> - -#include <hicn/core/forwarder.h> -#include <hicn/core/logger.h> - -struct logger { - PARCClock *clock; - - PARCLogReporter *reporter; - PARCLog *loggerArray[LoggerFacility_END]; -}; - -static const struct facility_to_string { - LoggerFacility facility; - const char *string; -} _facilityToString[] = { - {.facility = LoggerFacility_Config, .string = "Config"}, - {.facility = LoggerFacility_Core, .string = "Core"}, - {.facility = LoggerFacility_IO, .string = "IO"}, - {.facility = LoggerFacility_Message, .string = "Message"}, - {.facility = LoggerFacility_Processor, .string = "Processor"}, - {.facility = LoggerFacility_Strategy, .string = "Strategy"}, - {.facility = 0, .string = NULL}}; - -const char *logger_FacilityString(LoggerFacility facility) { - for (int i = 0; _facilityToString[i].string != NULL; i++) { - if (_facilityToString[i].facility == facility) { - return _facilityToString[i].string; - } - } - return "Unknown"; -} - -static void _allocateLoggers(Logger *logger, PARCLogReporter *reporter) { - parcTrapUnexpectedStateIf( - logger->reporter != NULL, - "Trying to allocate a reporter when the previous one is not null"); - logger->reporter = parcLogReporter_Acquire(reporter); - - char hostname[255]; - int gotHostName = gethostname(hostname, 255); - if (gotHostName < 0) { - snprintf(hostname, 255, "unknown"); - } - - for (int i = 0; i < LoggerFacility_END; i++) { - logger->loggerArray[i] = parcLog_Create(hostname, logger_FacilityString(i), - "forwarder", logger->reporter); - parcLog_SetLevel(logger->loggerArray[i], PARCLogLevel_Error); - } -} - -static void _releaseLoggers(Logger *logger) { - for (int i = 0; i < LoggerFacility_END; i++) { - parcLog_Release(&logger->loggerArray[i]); - } - parcLogReporter_Release(&logger->reporter); -} - -static void _destroyer(Logger **loggerPtr) { - Logger *logger = *loggerPtr; - _releaseLoggers(logger); - parcClock_Release(&(*loggerPtr)->clock); -} - -parcObject_ExtendPARCObject(Logger, _destroyer, NULL, NULL, NULL, NULL, NULL, - NULL); - -parcObject_ImplementAcquire(logger, Logger); - -parcObject_ImplementRelease(logger, Logger); - -Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock) { - parcAssertNotNull(reporter, "Parameter reporter must be non-null"); - parcAssertNotNull(clock, "Parameter clock must be non-null"); - - Logger *logger = parcObject_CreateAndClearInstance(Logger); - if (logger) { - logger->clock = parcClock_Acquire(clock); - _allocateLoggers(logger, reporter); - } - - return logger; -} - -void logger_SetReporter(Logger *logger, PARCLogReporter *reporter) { - parcAssertNotNull(logger, "Parameter logger must be non-null"); - - // save the log level state - PARCLogLevel savedLevels[LoggerFacility_END]; - for (int i = 0; i < LoggerFacility_END; i++) { - savedLevels[i] = parcLog_GetLevel(logger->loggerArray[i]); - } - - _releaseLoggers(logger); - - _allocateLoggers(logger, reporter); - - // restore log level state - for (int i = 0; i < LoggerFacility_END; i++) { - parcLog_SetLevel(logger->loggerArray[i], savedLevels[i]); - } -} - -void logger_SetClock(Logger *logger, PARCClock *clock) { - parcAssertNotNull(logger, "Parameter logger must be non-null"); - parcClock_Release(&logger->clock); - logger->clock = parcClock_Acquire(clock); -} - -static void _assertInvariants(const Logger *logger, LoggerFacility facility) { - parcAssertNotNull(logger, "Parameter logger must be non-null"); - parcTrapOutOfBoundsIf(facility >= LoggerFacility_END, "Invalid facility %d", - facility); -} - -void logger_SetLogLevel(Logger *logger, LoggerFacility facility, - PARCLogLevel minimumLevel) { - _assertInvariants(logger, facility); - PARCLog *log = logger->loggerArray[facility]; - parcLog_SetLevel(log, minimumLevel); -} - -bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, - PARCLogLevel level) { - _assertInvariants(logger, facility); - PARCLog *log = logger->loggerArray[facility]; - return parcLog_IsLoggable(log, level); -} - -void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, - const char *module, const char *format, ...) { - if (logger_IsLoggable(logger, facility, level)) { - // this is logged as the messageid - uint64_t logtime = parcClock_GetTime(logger->clock); - - // logger_IsLoggable asserted invariants so we know facility is in bounds - PARCLog *log = logger->loggerArray[facility]; - - va_list va; - va_start(va, format); - - parcLog_MessageVaList(log, level, logtime, format, va); - - va_end(va); - } -} diff --git a/hicn-light/src/hicn/core/logger.h b/hicn-light/src/hicn/core/logger.h deleted file mode 100644 index 8ab741f40..000000000 --- a/hicn-light/src/hicn/core/logger.h +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file logger.h - * @brief Logger for the hicn-light forwarder - * - * A facility based logger to allow selective logging from different parts of - * hicn-light - * - */ - -#ifndef logger_h -#define logger_h - -#ifndef _WIN32 -#include <sys/time.h> -#endif -#include <parc/algol/parc_Buffer.h> -#include <parc/algol/parc_Clock.h> -#include <parc/logging/parc_LogLevel.h> -#include <parc/logging/parc_LogReporter.h> -#include <stdarg.h> - -struct logger; -typedef struct logger Logger; - -/** - * CONFIG faciilty concerns anything in the /config directory - * CORE concerns anything in the /core directory - * IO concerns anything in the /io directory (listeners, connectors, tcp, - * ethernet, etc.) PROCESSOR concerns FIB, PIT, CS MESSAGE concerns message - * events, like parsing - */ -typedef enum { - LoggerFacility_Config, - LoggerFacility_Core, - LoggerFacility_IO, - LoggerFacility_Processor, - LoggerFacility_Message, - LoggerFacility_Strategy, - LoggerFacility_END // sentinel value -} LoggerFacility; - -/** - * Returns a string representation of a facility - * - * Do not free the returned value. - * - * @param [in] facility The facility to change to a string - * - * @retval string A string representation of the facility - */ -const char *logger_FacilityString(LoggerFacility facility); - -/** - * Returns a string representation of a log level - * - * Do not free the returned value. - * - * @param [in] level The level to change to a string - * - * @retval string A string representation of the level - */ -const char *logger_LevelString(PARCLogLevel level); - -/** - * Create a logger that uses a given writer and clock - * - * <#Paragraphs Of Explanation#> - * - * @param [in] writer The output writer - * @param [in] clock The clock to use for log messages - * - * @retval non-null An allocated logger - * @retval null An error - */ -Logger *logger_Create(PARCLogReporter *reporter, const PARCClock *clock); - -/** - * Release logger - */ -void logger_Release(Logger **loggerPtr); - -/** - * Acquire logger - */ -Logger *logger_Acquire(const Logger *logger); - -/** - * Sets the minimum log level for a facility - * - * The default log level is ERROR. For a message to be logged, it must be of - * equal or higher log level. - * - * @param [in] logger An allocated logger - * @param [in] facility The facility to set the log level for - * @param [in] The minimum level to log - * - */ -void logger_SetLogLevel(Logger *logger, LoggerFacility facility, - PARCLogLevel minimumLevel); - -/** - * Tests if the log level would be logged - * - * If the facility would log the given level, returns true. May be used as a - * guard around expensive logging functions. - * - * @param [in] logger An allocated logger - * @param [in] facility The facility to test - * @param [in] The level to test - * - * @retval true The given facility would log the given level - * @retval false A message of the given level would not be logged - * - */ -bool logger_IsLoggable(const Logger *logger, LoggerFacility facility, - PARCLogLevel level); - -/** - * Log a message - * - * The message will only be logged if it is loggable (logger_IsLoggable returns - * true). - * - * @param [in] logger An allocated Logger - * @param [in] facility The facility to log under - * @param [in] level The log level of the message - * @param [in] module The specific module logging the message - * @param [in] format The message with varargs - * - */ -void logger_Log(Logger *logger, LoggerFacility facility, PARCLogLevel level, - const char *module, const char *format, ...); - -/** - * Switch the logger to a new reporter - * - * Will close the old reporter and re-setup the internal loggers to use the new - * reporter. All current log level settings are preserved. - * - * @param [in] logger An allocated Logger - * @param [in] reporter An allocated PARCLogReporter - */ -void logger_SetReporter(Logger *logger, PARCLogReporter *reporter); - -/** - * Set a new clock to use with the logger - * - * The logger will start getting the time (logged as the messageid) from the - * specified clock - * - * @param [in] logger An allocated Logger - * @param [in] clock An allocated PARCClock - */ -void logger_SetClock(Logger *logger, PARCClock *clock); -#endif // logger_h diff --git a/hicn-light/src/hicn/core/mapme.c b/hicn-light/src/hicn/core/mapme.c index 3a1c9777b..acda2b8cd 100644 --- a/hicn-light/src/hicn/core/mapme.c +++ b/hicn-light/src/hicn/core/mapme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2023 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -16,6 +16,98 @@ /** * @file mapme.c * @brief MAP-Me : AnchorLess Producer Mobility Management. + * + * TODO: + * - review notification code with to integration of VPP implementation + * - reflect changes back in VPP + * - implement heuristic for update/notification selection + * + * MAP-Me hooks in forwarder + * + * A) Face table changes + * + * - face added + * + * * new local/producer face : this is a new prefix that we need to advertise + * on existing connections. + * + * We go over non-local connections an advertise the prefix through an IU + * provided that the connection satisfies the policy associated to the FIB + * entry. MAP-Me assumes the prefix already exists in the network, and the + * IU shall be discarded if the entry does not exist at the next hop. Three + * possibilities: + * . a bootstrap mechanism + * . we allow subprefixes of a prefix that is not empty by duplicating the + * FIB entry + * . we allow prefix creation in all circumstances : this is problematic + * since we might be creating spurious entries in routers for which we + * don't expect entries to be created. + * + * NOTE: because in general we will not allow for FIB entry creation, we + * cannot let the forwarder remove FIB entries with no nexthop (for instance + * after the producer leaves a point-of-attachment). This might creates + * permanent state in router's tables, but we assume it is the role of the + * routing plane to take care of routing entries. + * + * * new non-local face : a new face is available (eg. thanks to the face + * manager, after the node has connection to a new WiFi/LTE access point), + * and we thus need to advertise all local/producer prefixes onto this + * interface. + * + * For this, we currently scan the FIB for entries that have at least one + * local/producer face in nexthops, advertise the prefix on this new + * connection provided that it satisfies the associated policy. + * + * - face removed + * + * Currently, we take no action when a face is removed. It might however be a + * signal that a producer application is no more running at a given node, and + * that we can temporarily disable the forwarding towards that path. + * + * - face up / down + * + * - face nexthop added + * + * - face changed priority/tags + * + * B) Interest and Data forwarder path + * + * mapme_on_interest + * + * mapme_on_data + * + * + * EVENTS + * NH_SET + * NH_ADD + * PH_ADD + * PH_DEL + * + * C) Retransmission management + * + * Data structure + * + * mapme_on_timeout + * + * + * This allows us to define a convenient API for implementing MAP-Me: + * + * mapme_on_face_event XXX rename + * + * mapme_set_all_adjacencies(const mapme_t *mapme, fib_entry_t *entry) + * This function is used to update all the adjacencies. It needs to be called + * in case of face add/delete/change (priority/tas) and polocy + * + * mapme_set_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + * nexthops_t *nexthops) + * This function updates only the nexthops and clear the tfib. It + * needs to be called by the forwarding strategy in case of path switch + * + * mapme_update_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + * bool inc_iu_seq) + * This function is called to propagate the IU and it propagates the IU using + * the nexthops in the tfib. It needs to be used for mapme prcessing, IU + * forwarding, NATs and in case of timeouts */ #ifdef WITH_MAPME @@ -24,739 +116,779 @@ #include <hicn/core/mapme.h> #include <stdio.h> // printf +#include <hicn/base/loop.h> #include <hicn/core/connection.h> -#include <hicn/core/connectionList.h> #include <hicn/core/forwarder.h> -#include <hicn/core/logger.h> -#include <hicn/core/message.h> -#include <hicn/core/messagePacketType.h> // packet types +#include <hicn/core/msgbuf.h> #include <hicn/core/ticks.h> -#include <hicn/processor/fibEntry.h> -#include <hicn/processor/pitEntry.h> - -#include <parc/algol/parc_HashMap.h> -#include <parc/algol/parc_Iterator.h> -#include <parc/algol/parc_Unsigned.h> -#include <parc/assert/parc_Assert.h> +#include <hicn/core/fib_entry.h> +#include <hicn/core/pit.h> +#include <hicn/base/loop.h> +#include <hicn/util/log.h> #define MS2NS(x) x * 1000000 #define T2NS(x) forwarder_TicksToNanos(x) #define MAPME_DEFAULT_TU 5000 /* ms */ #define MAPME_DEFAULT_RETX 500 /* ms */ -#define MAX_RETX 3 +#define MAPME_DEFAULT_DISCOVERY false +#define MAPME_DEFAULT_PROTOCOL IPPROTO_IPV6 +#define MAPME_MAX_RETX 3 +#define MTU 1500 // XXX TODO Mutualize this define -#define NOT_A_NOTIFICATION false -#define NO_INGRESS 0 +#define DONT_QUEUE false #define TIMER_NO_REPEAT false -#define DO_DISCOVERY 1 #define MAPME_INVALID_DICOVERY_SEQ -1 +#define INIT_SEQ 0 -#define LOG_FACILITY LoggerFacility_Core +#define foreach_mapme_event \ + _(UNDEFINED) \ + _(FACE_ADD) \ + _(FACE_DEL) \ + _(NH_SET) \ + _(NH_ADD) \ + _(PH_ADD) \ + _(PH_DEL) \ + _(N) + +typedef enum { +#define _(x) MAPME_EVENT_##x, + foreach_mapme_event +#undef _ +} mapme_event_t; -#define LOG(mapme, log_level, fmt, ...) \ - do { \ - Logger *logger = forwarder_GetLogger(mapme->forwarder); \ - if (logger_IsLoggable(logger, LOG_FACILITY, log_level)) { \ - logger_Log(logger, LOG_FACILITY, log_level, __func__, fmt, \ - ##__VA_ARGS__); \ - } \ - } while (0) +/* + * We need a retransmission pool holding all necessary information for crafting + * special interests, thus including both the DPO and the prefix associated to + * it. + */ +#define NUM_RETX_ENTRIES 100 +#define NUM_RETX_SLOT 2 -#define WARN(mapme, fmt, ...) \ - LOG(mapme, PARCLogLevel_Warning, fmt, ##__VA_ARGS__) -#define ERR(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Error, fmt, ##__VA_ARGS__) -#define INFO(mapme, fmt, ...) LOG(mapme, PARCLogLevel_Info, fmt, ##__VA_ARGS__) -#define DEBUG(mapme, fmt, ...) \ - LOG(mapme, PARCLogLevel_Debug, fmt, ##__VA_ARGS__) +typedef struct { + hicn_prefix_t prefix; + fib_entry_t *entry; + uint8_t retx_count; // Number of retransmissions since last tfib addition +} mapme_retx_t; /** * MAP-Me state data structure */ -struct mapme { - uint32_t retx; /* ms */ - uint32_t Tu; /* ms */ - bool removeFibEntries; +struct mapme_s { + /* Options XXX mapme_conf_t ! */ + uint32_t retx; /* retx timeout (in ms) */ + uint32_t timescale; /* timescale (in ms) */ + bool discovery; /* discovery flag */ + int protocol; + bool enabled; /* mapme enabled/disabled */ + + /* + * Retransmissions + * Lite calendar queue with NUM_RETX_SLOT slots + */ + event_t *timer; + mapme_retx_t retx_array[NUM_RETX_SLOT][NUM_RETX_ENTRIES]; + uint8_t retx_len[NUM_RETX_SLOT]; + uint8_t cur; + uint8_t idle; - Forwarder *forwarder; + forwarder_t *forwarder; }; -static MapMe MapMeDefault = {.retx = MAPME_DEFAULT_RETX, - .Tu = MAPME_DEFAULT_TU, - .removeFibEntries = false}; +#define NEXT_SLOT(CUR) (1 - CUR) +#define CUR mapme->retx_array[mapme->cur] +#define NXT mapme->retx_array[NEXT_SLOT(mapme->cur)] +#define CURLEN mapme->retx_len[mapme->cur] +#define NXTLEN mapme->retx_len[NEXT_SLOT(mapme->cur)] + +static mapme_t mapme_default = { + .retx = MAPME_DEFAULT_RETX, + .timescale = MAPME_DEFAULT_TU, + .discovery = MAPME_DEFAULT_DISCOVERY, + .protocol = MAPME_DEFAULT_PROTOCOL, + .enabled = true, + .timer = NULL, + // .retx_array = {{ 0 }}, // memset + .retx_len = {0}, + .cur = 0, /* current slot */ + .idle = 0, +}; /******************************************************************************/ -bool mapme_create(MapMe **mapme, Forwarder *forwarder) { - *mapme = malloc(sizeof(MapMe)); - if (!mapme) goto ERR_MALLOC; +int mapme_on_timeout(void *mapme_arg, int fd, unsigned id, void *data); - /* Internal state : set default values */ - memcpy(*mapme, &MapMeDefault, sizeof(MapMe)); +mapme_t *mapme_create(void *forwarder) { + mapme_t *mapme = malloc(sizeof(mapme_t)); + if (!mapme) return NULL; - (*mapme)->forwarder = forwarder; - - /* As there is no face table and no related events, we need to install hooks - * in various places in the forwarder, where both control commands and - * signalization are processed. - */ + /* Internal state : set default values */ + memcpy(mapme, &mapme_default, sizeof(mapme_t)); + memset(mapme->retx_array, 0, NUM_RETX_SLOT * NUM_RETX_ENTRIES); - return true; + mapme->forwarder = forwarder; + loop_timer_create(&mapme->timer, MAIN_LOOP, mapme, mapme_on_timeout, NULL); + if (!mapme->timer) { + ERROR("Error allocating mapme timer."); + free(mapme); + return NULL; + } -ERR_MALLOC: - return false; + return mapme; } -void mapme_free(MapMe *mapme) -{ - free(mapme); +void mapme_free(mapme_t *mapme) { + loop_event_free(mapme->timer); + free(mapme); } /****************************************************************************** * TFIB ******************************************************************************/ -#define INVALID_SEQ 0 -#define INIT_SEQ 0 - typedef struct { + // XXX We need magic number to know whether the TFIB was initialized or not + // ... or merge it inside the real data structure. + // NOTE: in VPP we reuse the nexthops in opposite order to gain room + // XXX need enough space in user_data !! uint32_t seq; - PARCHashMap *nexthops; + nexthops_t nexthops; // XXX useless shadow structure /* Update/Notification heuristic */ - Ticks lastAckedUpdate; -} MapMeTFIB; + Ticks last_acked_update; +} mapme_tfib_t; -static MapMeTFIB *mapmeTFIB_Create() { - MapMeTFIB *tfib; - tfib = malloc(sizeof(MapMeTFIB)); - if (!tfib) goto ERR_MALLOC; +#define TFIB(FIB_ENTRY) ((mapme_tfib_t *)fib_entry_get_user_data(FIB_ENTRY)) + +static mapme_tfib_t *mapme_tfib_create() { + mapme_tfib_t *tfib; + tfib = malloc(sizeof(mapme_tfib_t)); + if (!tfib) return NULL; + + // init tfib->seq = INIT_SEQ; - tfib->lastAckedUpdate = 0; - tfib->nexthops = parcHashMap_Create(); - if (!tfib->nexthops) goto ERR_HASHMAP; + tfib->last_acked_update = 0; + nexthops_set_len(&tfib->nexthops, 0); return tfib; - -ERR_HASHMAP: - free(tfib); -ERR_MALLOC: - return NULL; } -static PARCIterator *mapmeTFIB_CreateKeyIterator(const MapMeTFIB *tfib) { - /* - * Creating an iterator on an empty HashMap seems to raise an exception - * due to : - * parcTrapOutOfMemoryIf(state->listIterator == NULL, - * "Cannot create parcLinkedList_CreateIterator"); - * in : _parcHashMap_Init - * - * All buckets are empty, as they are after creation, and as they should be, - * but the error is triggered. - */ - if (parcHashMap_Size(tfib->nexthops) == 0) - return NULL; - return parcHashMap_CreateKeyIterator(tfib->nexthops); +void mapme_release_tfib(mapme_tfib_t **tfibPtr) { + mapme_tfib_t *tfib = *tfibPtr; + free(tfib); + tfibPtr = NULL; } -void mapmeTFIB_Release(const MapMe * mapme, MapMeTFIB **tfibPtr); - /** - * @function mapme_CreateTFIB - * @abstract Associate a new TFIB entry to a FIB entry. + * @function mapme_create_tfib + * @abstract Associate a new TFIB entry to a FIB entry. If a TFIB already exists + * the new one will be used * @param [in] - Pointer to the FIB entry. * @return Boolean indicating the success of the operation. */ -static void mapme_CreateTFIB(const MapMe * mapme, FibEntry *fibEntry) { - MapMeTFIB *tfib; - - /* Make sure we don't already have an associated TFIB entry */ - tfib = fibEntry_getUserData(fibEntry); - // assertNull(tfib); - - tfib = mapmeTFIB_Create(); - fibEntry_setUserData(fibEntry, mapme, tfib, (void (*)(const void*, void**))mapmeTFIB_Release); -} +static void mapme_create_tfib(const mapme_t *mapme, fib_entry_t *entry) { + mapme_tfib_t *tfib; -#define TFIB(fibEntry) ((MapMeTFIB *)fibEntry_getUserData(fibEntry)) - -static const PARCEventTimer *mapmeTFIB_Get(const MapMeTFIB *tfib, - unsigned conn_id) { - const PARCEventTimer *timer; - const PARCBuffer *buffer; - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - buffer = parcHashMap_Get(tfib->nexthops, cid); - if (!buffer) { - timer = NULL; - goto END; - } - PARCByteArray *array = parcBuffer_Array(buffer); - timer = *((PARCEventTimer **)parcByteArray_Array(array)); -END: - parcUnsigned_Release(&cid); - return timer; + tfib = mapme_tfib_create(); + fib_entry_set_user_data(entry, tfib, (void (*)(void **))mapme_release_tfib); } -static void mapmeTFIB_Put(MapMeTFIB *tfib, unsigned conn_id, - const PARCEventTimer *timer) { - /* NOTE: Timers are not objects (the only class not being an object in - * fact), and as such, we cannot use them as values for the HashMap. - * Just like for unsigned we needed the PARC wrapper. - * There is no wrapper for pointers, so we use Arrays, which has an ubly - * syntax... - */ - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - PARCBuffer *buffer = parcBuffer_CreateFromArray(&timer, sizeof(PARCEventTimer *)); - parcHashMap_Put(tfib->nexthops, cid, buffer); - - parcUnsigned_Release(&cid); - parcBuffer_Release(&buffer); +/** + * @brief Update/Notification heuristic: + * + * NOTE: IN are currently disabled until the proper placeholder is agreed in the + * interest header. + */ +static hicn_mapme_type_t mapme_get_type_from_heuristic(const mapme_t *mapme, + fib_entry_t *entry) { +#if 0 + if (!entry) return UPDATE; + if (fib_entry_has_local_nexthop(entry)) + /* We are a producer for this entry, send update */ + return UPDATE; +#else /* Always send IU */ + return UPDATE; +#endif } -static void mapmeTFIB_Remove(const MapMe * mapme, MapMeTFIB *tfib, unsigned conn_id) { - PARCUnsigned *cid = parcUnsigned_Create(conn_id); - - /* Release timer */ - const PARCBuffer *buffer = parcHashMap_Get(tfib->nexthops, cid); - if (buffer) { - PARCByteArray *array = parcBuffer_Array(buffer); - PARCEventTimer * timer = *((PARCEventTimer **)parcByteArray_Array(array)); - if (timer) { - parcEventTimer_Stop(timer); - Dispatcher *dispatcher = forwarder_GetDispatcher(mapme->forwarder); - dispatcher_DestroyTimerEvent(dispatcher, &timer); - } +/** + * + * Here nexthops is not necessarily FIB nexthops as we might advertise given FIB + * entries on various other connections. + * + * prefix can be specified to send an update for a More Specific Prefix (MSP), + * or left NULL for the default behaviour. Note there will be no support for + * retransmission for MSP. + * + * NOTES: + * - if the face is pending an we receive an IN, maybe we should not cancel the + * timer + * - this function should never be called for Notifications. + */ +int mapme_send_to_nexthops(const mapme_t *mapme, fib_entry_t *entry, + const nexthops_t *nexthops, + const hicn_prefix_t *prefix) { + INFO("mapme send to nexthops"); + const hicn_prefix_t *mapme_prefix; + uint32_t mapme_seq; + + assert(!!prefix ^ !!entry); + + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return -1; } - parcHashMap_Remove(tfib->nexthops, cid); - parcUnsigned_Release(&cid); -} - -void mapmeTFIB_Release(const MapMe * mapme, MapMeTFIB **tfibPtr) { - MapMeTFIB *tfib = *tfibPtr; - - /* Release all TFIB entries, incl. timers */ - PARCIterator *iterator = mapmeTFIB_CreateKeyIterator(tfib); - if (iterator) { - /* No iterator is created if the TFIB is empty */ - while (parcIterator_HasNext(iterator)) { - PARCUnsigned *cid = parcIterator_Next(iterator); - unsigned conn_id = parcUnsigned_GetUnsigned(cid); - mapmeTFIB_Remove(mapme, tfib, conn_id); + if (prefix) { + INFO("mapme with given prefix"); + mapme_prefix = prefix; + mapme_seq = 1; + } else { + INFO("mapme wih fib entry prefix"); + mapme_tfib_t *tfib = TFIB(entry); + if (tfib == NULL) { + mapme_create_tfib(mapme, entry); + tfib = TFIB(entry); } - parcIterator_Release(&iterator); - } - - parcHashMap_Release(&tfib->nexthops); - free(tfib); - *tfibPtr = NULL; -} - - -int hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) { - NameBitvector *bv = name_GetContentName(name); - ip_prefix_t ip_prefix; - nameBitvector_ToIPAddress(bv, &ip_prefix); - /* The name length will be equal to ip address' prefix length */ - return hicn_prefix_create_from_ip_prefix(&ip_prefix, prefix); -} - -static Message *mapme_createMessage(const MapMe *mapme, const Name *name, - mapme_params_t *params) { - Ticks now = forwarder_GetTicks(mapme->forwarder); - Logger *logger = forwarder_GetLogger(mapme->forwarder); - - INFO(mapme, "[MAP-Me] CreateMessage type=%d seq=%d", params->type, - params->seq); - - hicn_prefix_t prefix; - int rc = hicn_prefix_from_name(name, &prefix); - if (rc < 0) { - ERR(mapme, "[MAP-Me] Failed to create lib's name"); - goto ERR_NAME; + mapme_prefix = fib_entry_get_prefix(entry); + mapme_seq = tfib->seq; } - - size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN - : HICN_MAPME_V4_HDRLEN; - uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); - - INFO(mapme, "[MAP-Me] Creating MAP-Me packet"); - size_t len = hicn_mapme_create_packet(icmp_pkt, &prefix, params); - if (len == 0) { - ERR(mapme, "[MAP-Me] Failed to create mapme packet through lib"); - goto ERR_CREATE; + char prefix_s[MAXSZ_IP_PREFIX]; + int rc = hicn_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, mapme_prefix); + assert(rc < MAXSZ_IP_PREFIX); + if (rc < 0) NULL; + + INFO("mapme send to nexthops prefix= %s", prefix_s); + + WITH_INFO({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, mapme_prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "(error)"); + INFO("sending IU/IN for name %s on nexthops", buf); + }) + + mapme_params_t params = {.protocol = mapme->protocol, + .type = mapme_get_type_from_heuristic(mapme, entry), + .seq = mapme_seq}; + + uint8_t packet[MTU]; + size_t size = hicn_mapme_create_packet(packet, mapme_prefix, ¶ms); + if (size <= 0) { + ERROR("Could not create MAP-Me packet"); + return -1; } - // hicn_packet_dump(icmp_pkt, MAPME_HDRLEN); + connection_table_t *table = forwarder_get_connection_table(mapme->forwarder); - return message_CreateFromByteArray(NO_INGRESS, icmp_pkt, - MessagePacketType_Interest, now, logger); + nexthops_foreach(nexthops, nexthop, { + const connection_t *conn = connection_table_get_by_id(table, nexthop); + assert(!connection_is_local(conn)); + connection_send_packet(conn, packet, size); + }); -ERR_CREATE: - parcMemory_Deallocate(&icmp_pkt); -ERR_NAME: - return NULL; + return 0; } -static Message *mapme_createAckMessage(const MapMe *mapme, - const uint8_t *msgBuffer, - const mapme_params_t *params) { - Ticks now = forwarder_GetTicks(mapme->forwarder); - Logger *logger = forwarder_GetLogger(mapme->forwarder); - - size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN - : HICN_MAPME_V4_HDRLEN; - uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); - memcpy(icmp_pkt, msgBuffer, size); - - size_t len = hicn_mapme_create_ack(icmp_pkt, params); - if (len != size) { - ERR(mapme, "[MAP-Me] Failed to create mapme ack packet through lib"); - goto ERR; +#if 0 +/** + * + * Here nexthops is not necessarily FIB nexthops as we might advertise given FIB + * entries on various other connections. + */ +void mapme_maybe_send_to_nexthops(const mapme_t *mapme, fib_entry_t *fib_entry, + const nexthops_t *nexthops) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return; } - return message_CreateFromByteArray( - NO_INGRESS, icmp_pkt, MessagePacketType_ContentObject, now, logger); - -ERR: - parcMemory_Deallocate(&icmp_pkt); - return NULL; -} - -struct setFacePendingArgs { - const MapMe *mapme; - const Name *name; - FibEntry *fibEntry; - unsigned conn_id; - bool send; - bool is_producer; - uint32_t num_retx; -}; - -static bool mapme_setFacePending(const MapMe *mapme, const Name *name, - FibEntry *fibEntry, unsigned conn_id, - bool send, bool is_producer, bool clear_tfib, uint32_t num_retx); - -static void mapme_setFacePendingCallback(int fd, PARCEventType which_event, - void *data) { - struct setFacePendingArgs *args = (struct setFacePendingArgs *)data; - - parcAssertTrue(which_event & PARCEventType_Timeout, - "Event incorrect, expecting %X set, got %X", - PARCEventType_Timeout, which_event); + /* Detect change */ + if (!fib_entry_nexthops_changed(fib_entry)) { + INFO("No change in nexthops"); + return; + } + fib_entry_set_prev_nexthops(fib_entry); - INFO(args->mapme, "Timeout during retransmission. Re-sending"); - mapme_setFacePending(args->mapme, args->name, args->fibEntry, args->conn_id, - args->send, args->is_producer, false, args->num_retx); - free(args); + mapme_send_to_nexthops(mapme, fib_entry, nexthops); } - -/** - * @brief Update/Notification heuristic: - * - * NOTE: IN are currently disabled until the proper placeholder is agreed in the - * interest header. - */ -static hicn_mapme_type_t mapme_getTypeFromHeuristic(const MapMe *mapme, - FibEntry *fibEntry) { -#if 0 /* interplay of IU/IN */ - if (TFIB(fibEntry)->lastAckedUpdate == 0) { - return UPDATE; - } else { - Ticks interval = now - TFIB(fibEntry)->lastAckedUpdate; - return (T2NS(interval) > MS2NS(mapme->Tu)) ? UPDATE : NOTIFICATION; - } -#else /* Always send IU */ - return UPDATE; #endif -} -static bool mapme_setFacePending(const MapMe *mapme, const Name *name, - FibEntry *fibEntry, unsigned conn_id, - bool send, bool is_producer, bool clear_tfib, uint32_t num_retx) { - int rc; +/****************************************************************************** + * MAPME API + ******************************************************************************/ - INFO(mapme, "[MAP-Me] SetFacePending connection=%d prefix=XX retx=%d", - conn_id, num_retx); +int mapme_set_all_adjacencies(const mapme_t *mapme, fib_entry_t *entry) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return -1; + } - /* NOTE: if the face is pending an we receive an IN, maybe we should not - * cancel the timer - */ - Dispatcher *dispatcher = forwarder_GetDispatcher(mapme->forwarder); - PARCEventTimer *timer; + /* Apply the policy of the fib_entry over all neighbours */ + nexthops_t new_nexthops = NEXTHOPS_EMPTY; + nexthops_t *nexthops = fib_entry_get_mapme_nexthops(entry, &new_nexthops); - /* Safeguard during retransmissions */ - if (!TFIB(fibEntry)) - return true; + /* We set force to true to avoid overriding the FIB cache */ + return mapme_set_adjacencies(mapme, entry, nexthops, NULL); +} +// XXX this will change with the FIB cache +// XXX we are sometimes incrementing tfib seq for nothing +int mapme_set_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + nexthops_t *nexthops, const hicn_prefix_t *prefix) { + INFO("mapme set adjacenies"); /* - * On the producer side, we have to clear the TFIB everytime we change the list - * of adjacencies, otherwise retransmissions will occur to preserve them. + * - entry is provided in case of a producer reannouncement + * - prefix is provided for control plane triggered updates */ - if (clear_tfib) { - /* - * It is likely we cannot iterator and remove elements from the hashmap at - * the same time, so we proceed in two steps - */ - if (parcHashMap_Size(TFIB(fibEntry)->nexthops) > 0) { - - NumberSet * conns = numberSet_Create(); - - PARCIterator *it = parcHashMap_CreateKeyIterator(TFIB(fibEntry)->nexthops); - while (parcIterator_HasNext(it)) { - PARCUnsigned *cid = parcIterator_Next(it); - unsigned conn_id = parcUnsigned_GetUnsigned(cid); - numberSet_Add(conns, conn_id); - } - parcIterator_Release(&it); - - for (size_t i = 0; i < numberSet_Length(conns); i++) { - unsigned conn_id = numberSet_GetItem(conns, i); - mapmeTFIB_Remove(mapme, TFIB(fibEntry), conn_id); - } + assert(!!prefix ^ !!entry); - numberSet_Release(&conns); - } + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return -1; } - // NOTE - // - at producer, send always true, we always send something reliably so we - // set the timer. - // - in the network, we always forward an IU, and never an IN - //if (is_producer || send) { - if (send) { - mapme_params_t params = { - .protocol = IPPROTO_IPV6, - .type = is_producer ? mapme_getTypeFromHeuristic(mapme, fibEntry) : UPDATE, - .seq = TFIB(fibEntry)->seq}; - Message *special_interest = mapme_createMessage(mapme, name, ¶ms); - if (!special_interest) { - INFO(mapme, "[MAP-Me] Could not create special interest"); - return false; - } + if (entry) { + /* Check disabled, we need to be able to send an update for a larger prefix + * that the one being served. + */ + // if (!fib_entry_has_local_nexthop(entry)) return -1; - const ConnectionTable *table = - forwarder_GetConnectionTable(mapme->forwarder); - const Connection *conn = - connectionTable_FindById((ConnectionTable *)table, conn_id); - if (conn) { - const Name * name = message_GetName(special_interest); - char * name_str = name_ToString(name); - INFO(mapme, "[MAP-Me] Sending MAP-Me packet name=%s seq=%d conn=%d", - name_str, params.seq, conn_id); - free(name_str); - connection_ReSend(conn, special_interest, NOT_A_NOTIFICATION); - } else { - INFO(mapme, "[MAP-Me] Stopped retransmissions as face went down"); - } - message_Release(&special_interest); - - if (num_retx < MAX_RETX) { - INFO(mapme, "[MAP-Me] - Scheduling retransmission\n"); - /* Schedule retransmission */ - struct setFacePendingArgs *args = - malloc(sizeof(struct setFacePendingArgs)); - if (!args) - goto ERR_MALLOC; - args->mapme = mapme; - args->name = name; - args->fibEntry = fibEntry; - args->conn_id = conn_id; - args->send = send; - args->is_producer = is_producer; - args->num_retx = num_retx + 1; - - timer = dispatcher_CreateTimer(dispatcher, TIMER_NO_REPEAT, - mapme_setFacePendingCallback, args); - struct timeval timeout = {mapme->retx / 1000, - (mapme->retx % 1000) * 1000}; - rc = parcEventTimer_Start(timer, &timeout); - if (rc < 0) { - free(args); - goto ERR_TIMER; - } - } else { - INFO(mapme, "[MAP-Me] Last retransmission."); - timer = NULL; + mapme_tfib_t *tfib = TFIB(entry); + if (tfib == NULL) { + mapme_create_tfib(mapme, entry); + tfib = TFIB(entry); } - } else { - INFO(mapme, "[MAP-Me] - not forwarding as send is False"); - timer = NULL; - } - mapmeTFIB_Remove(mapme, TFIB(fibEntry), conn_id); - mapmeTFIB_Put(TFIB(fibEntry), conn_id, timer); + /* + * We need to prevent pending updates to recreate a link which does not make + * since anymore since we edit the graph here. + */ + nexthops_clear(&tfib->nexthops); - return true; + /* We update the sequence number in all cases otherwise this won't allow + * repetition + */ + tfib->seq++; + } -ERR_TIMER: - dispatcher_DestroyTimerEvent(dispatcher, &timer); -ERR_MALLOC: - return false; + INFO("calling send to nh"); + mapme_send_to_nexthops(mapme, entry, nexthops, prefix); + return 0; } -/*------------------------------------------------------------------------------ - * Event handling - *----------------------------------------------------------------------------*/ +int mapme_set_adjacency(const mapme_t *mapme, fib_entry_t *entry, + nexthop_t nexthop, const hicn_prefix_t *prefix) { + nexthops_t nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, nexthop); -/* - * Return true if we have at least one local connection as next hop - */ -static bool mapme_hasLocalNextHops(const MapMe *mapme, - const FibEntry *fibEntry) { - const NumberSet *nexthops = fibEntry_GetNexthops(fibEntry); - const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); - - for (size_t j = 0; j < fibEntry_NexthopCount(fibEntry); j++) { - /* Retrieve Nexthop #j */ - unsigned conn_id = numberSet_GetItem(nexthops, j); - const Connection *conn = - connectionTable_FindById((ConnectionTable *)table, conn_id); - - /* Ignore non-local connections */ - if (!connection_IsLocal(conn)) continue; - /* We don't need to test against conn_added since we don't - * expect it to have any entry in the FIB */ - - return true; - } - return false; + return mapme_set_adjacencies(mapme, entry, &nexthops, prefix); } -void -mapme_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops) -{ - if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ - mapme_CreateTFIB(mapme, fibEntry); - TFIB(fibEntry)->seq++; - - const Name *name = fibEntry_GetPrefix(fibEntry); - char *name_str = name_ToString(name); - bool clear_tfib = true; - - INFO(mapme, "[MAP-Me] Candidate next hops changed"); - for (size_t j = 0; j < numberSet_Length(nexthops); j++) { - unsigned nexthop_id = numberSet_GetItem(nexthops, j); - - /* We extract the nexthop type based on tags */ - const char * nexthop_type; - ConnectionTable * table = forwarder_GetConnectionTable(mapme->forwarder); - const Connection * conn = connectionTable_FindById(table, nexthop_id); - if (connection_HasTag(conn, POLICY_TAG_WIRED)) { - nexthop_type = "WIRED"; - } else if (connection_HasTag(conn, POLICY_TAG_WIFI)) { - nexthop_type = "WIFI"; - } else if (connection_HasTag(conn, POLICY_TAG_CELLULAR)) { - nexthop_type = "CELLULAR"; - } else { - nexthop_type = "UNKNOWN"; - } - - INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d - %s (%s)", name_str, - nexthop_id, connection_GetInterfaceName(conn), nexthop_type); - mapme_setFacePending(mapme, name, fibEntry, nexthop_id, true, true, clear_tfib, 0); - clear_tfib = false; +int mapme_update_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + bool inc_iu_seq) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return -1; } - INFO(mapme, "[MAP-Me] Done sending MAP-Me update"); - free(name_str); -} - -void -mapme_maybe_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops) -{ - /* Detect change */ - NumberSet * previous_nexthops = fibEntry_GetPreviousNextHops(fibEntry); - if (numberSet_Equals(nexthops, previous_nexthops)) { - INFO(mapme, "[MAP-Me] No change in nexthops"); - return; + mapme_tfib_t *tfib = TFIB(entry); + if (tfib == NULL) { + mapme_create_tfib(mapme, entry); + tfib = TFIB(entry); } - fibEntry_SetPreviousNextHops(fibEntry, nexthops); - mapme_send_updates(mapme, fibEntry, nexthops); -} + if (inc_iu_seq) tfib->seq++; -void -mapme_reconsiderFibEntry(const MapMe *mapme, FibEntry * fibEntry) -{ - /* - * Skip entries that do not correspond to a producer ( / have a locally - * served prefix / have no local connection as next hop) - */ - if (!mapme_hasLocalNextHops(mapme, fibEntry)) - return; + mapme_send_to_nexthops(mapme, entry, &tfib->nexthops, NULL); + return 0; +} - /* Apply the policy of the fibEntry over all neighbours */ - NumberSet * available_nexthops = fibEntry_GetAvailableNextHops(fibEntry, ~0); +int mapme_send_to_nexthop(const mapme_t *mapme, fib_entry_t *entry, + unsigned nexthop) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return -1; + } - /* Advertise prefix on all available next hops (if needed) */ - mapme_send_updates(mapme, fibEntry, available_nexthops); + nexthops_t nexthops = NEXTHOPS_EMPTY; + nexthops_add(&nexthops, nexthop); - numberSet_Release(&available_nexthops); + return mapme_send_to_nexthops(mapme, entry, &nexthops, NULL); } +#if 0 /* * Callback called everytime a new connection is created by the control protocol */ -void -mapme_onConnectionEvent(const MapMe *mapme, const Connection *conn_added, connection_event_t event) { +void mapme_on_connection_event(const mapme_t *mapme, + const connection_t *conn_added, + connection_event_t event) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return; + } + /* Does the priority change impacts the default route selection; if so, * advertise the prefix on this default route. If there are many default * routes, either v4 v6, or many connections as next hops on this default * route, then send to all. */ if (conn_added) { - if (connection_IsLocal(conn_added)) - return; + if (connection_is_local(conn_added)) return; - unsigned conn_added_id = connection_GetConnectionId(conn_added); - switch(event) { + unsigned conn_added_id = connection_get_id(conn_added); + switch (event) { case CONNECTION_EVENT_CREATE: - INFO(mapme, "[MAP-Me] Connection %d got created", conn_added_id); - break; + INFO("Connection %d got created", conn_added_id); + break; case CONNECTION_EVENT_DELETE: - INFO(mapme, "[MAP-Me] Connection %d got deleted", conn_added_id); - break; + INFO("Connection %d got deleted", conn_added_id); + break; case CONNECTION_EVENT_UPDATE: - INFO(mapme, "[MAP-Me] Connection %d got updated", conn_added_id); - break; + INFO("Connection %d got updated", conn_added_id); + break; case CONNECTION_EVENT_SET_UP: - INFO(mapme, "[MAP-Me] Connection %d went up", conn_added_id); - break; + INFO("Connection %d went up", conn_added_id); + break; case CONNECTION_EVENT_SET_DOWN: - INFO(mapme, "[MAP-Me] Connection %d went down", conn_added_id); - break; + INFO("Connection %d went down", conn_added_id); + break; case CONNECTION_EVENT_TAGS_CHANGED: - INFO(mapme, "[MAP-Me] Connection %d changed tags", conn_added_id); - break; + INFO("Connection %d changed tags", conn_added_id); + break; case CONNECTION_EVENT_PRIORITY_CHANGED: - INFO(mapme, "[MAP-Me] Connection %d changed priority to %d", - conn_added_id, connection_GetPriority(conn_added)); - break; + INFO("Connection %d changed priority to %d", conn_added_id, + connection_get_priority(conn_added)); + break; } } /* We need to send a MapMe update on the newly selected connections for - * each concerned fibEntry : connection is involved, or no more involved */ - FibEntryList *fiblist = forwarder_GetFibEntries(mapme->forwarder); - - /* Iterate a first time on the FIB to get the locally served prefixes */ - for (size_t i = 0; i < fibEntryList_Length(fiblist); i++) { - FibEntry *fibEntry = (FibEntry *)fibEntryList_Get(fiblist, i); - mapme_reconsiderFibEntry(mapme, fibEntry); - } - - fibEntryList_Destroy(&fiblist); - - INFO(mapme, "[MAP-Me] Done"); + * each concerned fib_entry : connection is involved, or no more involved */ + const fib_t *fib = forwarder_get_fib(mapme->forwarder); + fib_foreach_entry(fib, entry, { mapme_set_all_adjacencies(mapme, entry); }); } +#endif /*------------------------------------------------------------------------------ * Special Interest handling *----------------------------------------------------------------------------*/ -/** - * @discussion This function is way too long and should be cut out - */ -static bool mapme_onSpecialInterest(const MapMe *mapme, - const uint8_t *msgBuffer, - unsigned conn_in_id, hicn_prefix_t *prefix, - mapme_params_t *params) { - const ConnectionTable *table = forwarder_GetConnectionTable(mapme->forwarder); - /* The cast is needed since connectionTable_FindById miss the - * const qualifier for the first parameter */ - const Connection *conn_in = - connectionTable_FindById((ConnectionTable *)table, conn_in_id); - seq_t fibSeq, seq = params->seq; - bool send = (params->type == UPDATE); - bool rv; - - Name *name = name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); - name_setLen(name, prefix->len); - char *name_str = name_ToString(name); - INFO(mapme, - "[MAP-Me] Ack'ed Special Interest on connection %d - prefix=%s type=XX " - "seq=%d", - conn_in_id, name_str, seq); - free(name_str); +#ifdef HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY +fib_entry_t *mapme_create_fib_entry(const mapme_t *mapme, + const hicn_prefix_t *prefix, + unsigned ingress_id) { + INFO(" - creating FIB entry with next hop on connection %d", ingress_id); /* - * Immediately send an acknowledgement back on the ingress connection - * We always ack, even duplicates. + * This might happen for a node hosting a producer which has moved. + * Destroying the face has led to removing all corresponding FIB + * entries. In that case, we need to correctly restore the FIB entries. + * Also in case we have an intermediate node with just a less specific prefix + * (eg. a default route), and thus an announcement with a more specific + * prefix. In that case we need to perform a FIB lookup to find the next hops + * to which the message should be propagated (before adding). */ - Message *ack = mapme_createAckMessage(mapme, msgBuffer, params); - if (!ack) { - name_Release(&name); - return false; - } - rv = connection_ReSend(conn_in, ack, NOT_A_NOTIFICATION); - message_Release(&ack); + forwarder_t *forwarder = mapme->forwarder; + + fib_t *fib = forwarder_get_fib(forwarder); + configuration_t *config = forwarder_get_configuration(forwarder); + + char prefix_s[MAXSZ_IP_PREFIX]; + int rc = hicn_prefix_snprintf(prefix_s, MAXSZ_IP_PREFIX, prefix); + assert(rc < MAXSZ_IP_PREFIX); + if (rc < 0) NULL; + + INFO("creating FIB entry for prefix %s", prefix_s); - if (!rv) { - name_Release(&name); - return false; + strategy_type_t strategy_type = configuration_get_strategy(config, prefix_s); + fib_entry_t *entry = fib_entry_create(prefix, strategy_type, NULL, forwarder); + mapme_create_tfib(mapme, entry); + + fib_entry_t *lpm = fib_match_prefix(fib, prefix); + + // Keep this after the LPM lookup + fib_add(fib, entry); + + if (!lpm) { + TFIB(entry)->seq = 0; + if (ingress_id != INVALID_FACE_ID) + fib_entry_nexthops_add(entry, ingress_id); + return entry; } - /* EPM on FIB */ - /* only the processor has access to the FIB */ - FIB *fib = forwarder_getFib(mapme->forwarder); + /* + * We make a clone of the FIB entry (zero'ing the sequence number ?) with + * the more specific name, and proceed as usual. Worst case we clone the + * default route... + */ + const nexthops_t *lpm_nexthops = fib_entry_get_nexthops(lpm); + nexthops_foreach(lpm_nexthops, nh, { fib_entry_nexthops_add(entry, nh); }); + return entry; +} +#endif - FibEntry *fibEntry = fib_Contains(fib, name); +int mapme_on_timeout(void *mapme_arg, int fd, unsigned id, void *data) { + mapme_t *mapme = mapme_arg; + assert(mapme); + assert(id == 0); + assert(!data); + /* Timeout occurred, we have to retransmit IUs for all pending + * prefixes having entries in TFIB + * + * timeouts are slotted + * | | | | + * + * ^ + * +- event occurred + * new face, wait for the second next + * (having two arrays and swapping cur and next) + * retx : put in next + */ + mapme->idle += 1; - name_Release(&name); + for (uint8_t pos = 0; pos < CURLEN; pos++) { + mapme_retx_t *retx = &CUR[pos]; - if (!fibEntry) { - INFO(mapme, "Ignored update with no FIB entry"); - return 0; -#if 0 - INFO(mapme, - "[MAP-Me] - Re-creating FIB entry with next hop on connection %d", - conn_in_id); - /* - * This might happen for a node hosting a producer which has moved. - * Destroying the face has led to removing all corresponding FIB - * entries. In that case, we need to correctly restore the FIB entries. - */ - strategy_type fwdStrategy = LAST_STRATEGY_VALUE; + if (!retx->entry) /* deleted entry */ + continue; - /* - * It might also be due to the announcement of a more specific prefix. In - * that case we need to perform a FIB lookup to find the next hops to which - * the message should be propagated. + mapme_tfib_t *tfib = TFIB(retx->entry); + assert(tfib); + + /* Re-send interest for all entries */ + mapme_update_adjacencies(mapme, retx->entry, false); + + retx->retx_count++; + /* If we exceed the numver of retransmittion it means that all tfib + * entries have seens at least HICN_PARAM_RETX_MAX of retransmission */ -#ifdef WITH_POLICY - fibEntry = fibEntry_Create(name, fwdStrategy, mapme->forwarder); -#else - fibEntry = fibEntry_Create(name, fwdStrategy); -#endif /* WITH_POLICY */ - FibEntry *lpm = fib_MatchName(fib, name); - mapme_CreateTFIB(mapme, fibEntry); - fib_Add(fib, fibEntry); - if (!lpm) { - TFIB(fibEntry)->seq = seq; - fibEntry_AddNexthop(fibEntry, conn_in_id); - return true; + if (retx->retx_count < MAPME_MAX_RETX) { + /* + * We did some retransmissions, so let's reschedule a check in the + * next slot + */ + NXT[NXTLEN++] = CUR[pos]; + mapme->idle = 0; + } else { + WARN("Maximum retransmissions exceeded"); + /* If we exceed the numver of retransmission it means that all TFIB + * entries have seens at least HICN_PARAM_RTX_MAX retransmissions. + * (Deletion might be slightly late). + * + * XXX document: when adding an entry in TFIB, we might exceed max + * retransmissions for previous entries that started retransmitting + * beforehand. + */ + nexthops_clear(&tfib->nexthops); } + } - /* - * We make a clone of the FIB entry (zero'ing the sequence number ?) with - * the more specific name, and proceed as usual. Worst case we clone the - * default route... - */ - const NumberSet *lpm_nexthops = fibEntry_GetNexthops(lpm); - for (size_t i = 0; i < numberSet_Length(lpm_nexthops); i++) { - fibEntry_AddNexthop(fibEntry, numberSet_GetItem(lpm_nexthops, i)); + /* Reset events in this slot and prepare for next one */ + CURLEN = 0; + mapme->cur = NEXT_SLOT(mapme->cur); + + /* After two empty slots, we disable the timer */ + if (mapme->idle > 1) { + loop_event_unregister(mapme->timer); + } + + return 0; +} + +static void mapme_on_event(mapme_t *mapme, mapme_event_t event, + fib_entry_t *entry, unsigned ingress_id) { + switch (event) { +#if 0 + case HICN_MAPME_EVENT_FACE_ADD: + { + /* + * A face has been added: + * - In case of a local app face, we need to advertise a new prefix + * - For another local face type, we need to advertise local + * prefixes and schedule retransmissions + */ + mapme_retx_t *retx_events = event_data; + for (uint8_t i = 0; i < vec_len (retx_events); i++) { + hicn_mapme_on_face_added(mapme, retx_events[i].dpo); + } + + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + mapme->retx, mapme, mapme_on_timeout, NULL); + mapme->idle = 0; + break; + } + case HICN_MAPME_EVENT_FACE_DEL: + if (mapme->timer_fd == -1) + mapme->timer_fd = loop_register_timer(MAIN_LOOP, + DEFAULT_TIMEOUT, mapme, mapme_on_timeout, NULL); + mapme->idle = 0; + break; +#endif + + case MAPME_EVENT_NH_SET: + /* + * An hICN FIB entry has been modified. All operations so far + * have been procedded in the nodes. Here we need to track + * retransmissions upon timeout: we mark the FIB entry as pending in + * the second-to-next slot + */ + + /* + * XXX Move this in doc + * + * The FIB entry has a new next hop, and its TFIB section has: + * - eventually previous prev hops for which a IU with a + * lower seqno has been sent + * - the prev hops that have just been added. + * + * We don't distinguish any and just send an updated IU to all + * of them. The retransmission of the latest IU to all + * facilitates the matching of ACKs to a single seqno which is + * the one stored in the FIB. + * + * Since we retransmit to all prev hops, we can remove this + * (T)FIB entry for the check at the end of the current slot. + */ + + /* Mark FIB entry as pending for second-to-next slot */ + /* + * Transmit IU for all TFIB entries with latest seqno (we have + * at least one for sure!) + */ + mapme_update_adjacencies(mapme, entry, false); + + /* Delete entry_id from retransmissions in the current slot (if present) + * ... */ + /* ... and schedule it for next slot (if not already) */ + uint8_t j; + for (j = 0; j < CURLEN; j++) { + if (CUR[j].entry == entry) CUR[j].entry = NULL; /* sufficient */ + } + for (j = 0; j < NXTLEN; j++) { + if (NXT[j].entry == entry) break; + } + if (j == NXTLEN) /* not found */ + NXT[NXTLEN++] = (mapme_retx_t){ + .entry = entry, + .retx_count = 0, + }; + + if (!loop_timer_is_enabled(mapme->timer)) { + if (loop_timer_register(mapme->timer, mapme->retx) < 0) { + ERROR("Error setting mapme timer."); + break; + } + } + mapme->idle = 0; + break; + + case MAPME_EVENT_NH_ADD: + /* + * XXX move this in doc + * + * As per the description of states, this event should add the face + * to the list of next hops, and eventually remove it from TFIB. + * This corresponds to the multipath case. + * + * In all cases, we assume the propagation was already done when the first + * interest with the same sequence number was received, so we stop here + * No change in TFIB = no IU to send + * + * No change in timers. + */ + + // XXX useless +#if 0 + /* Add ingress face as next hop */ + idle = 0; +#endif + break; + + case MAPME_EVENT_PH_ADD: + /* Back-propagation, interesting even for IN (desync) */ + mapme_send_to_nexthop(mapme, entry, ingress_id); + + mapme->idle = 0; + if (!loop_timer_is_enabled(mapme->timer)) + loop_timer_register(mapme->timer, mapme->retx); + break; + + case MAPME_EVENT_PH_DEL: + /* Ack : remove an element from TFIB */ + break; + + case MAPME_EVENT_FACE_ADD: + case MAPME_EVENT_FACE_DEL: + + case MAPME_EVENT_UNDEFINED: + case MAPME_EVENT_N: + ERROR("Unexpected event"); + break; + } +} + +static void mapme_on_interest(mapme_t *mapme, msgbuf_t *msgbuf, + unsigned ingress_id, hicn_prefix_t *prefix, + mapme_params_t *params) { + connection_table_t *table = forwarder_get_connection_table(mapme->forwarder); + + /* The cast is needed since connectionTable_FindById miss the + * const qualifier for the first parameter */ + const connection_t *conn_in = connection_table_get_by_id(table, ingress_id); + + /* + * Immediately send an acknowledgement back on the ingress connection + * We always ack, even duplicates. Clone mgsbuf to avoid to overwrite the + * received message + */ + msgbuf_t *ack; + msgbuf_pool_t *msgbuf_pool = forwarder_get_msgbuf_pool(mapme->forwarder); + off_t interest_offset = msgbuf_pool_get_id(msgbuf_pool, (msgbuf_t *)msgbuf); + msgbuf_pool_clone(msgbuf_pool, &ack, interest_offset); + + uint8_t *ack_packet = msgbuf_get_packet(ack); + size_t size = hicn_mapme_create_ack(ack_packet, params); + if (!connection_send_packet(conn_in, ack_packet, size)) { + /* We accept the packet knowing we will get a retransmit */ + ERROR("Failed to send ACK packet"); + } + + msgbuf_pool_put(msgbuf_pool, ack); + + WITH_INFO({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "%s", "(error)"); + INFO("Ack'ed interest : connection=%d prefix=%s seq=%d", ingress_id, buf, + params->seq); + }); + + /* EPM on FIB */ + const fib_t *fib = forwarder_get_fib(mapme->forwarder); + fib_entry_t *entry = fib_contains(fib, prefix); + if (!entry) { +#ifdef HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY + entry = mapme_create_fib_entry(mapme, prefix, ingress_id); + if (!entry) { + ERROR("Failed to create FIB entry"); + return; } +#else + INFO("Ignored update with no FIB entry"); + return; #endif + } - } else if (!TFIB(fibEntry)) { - /* Create TFIB associated to FIB entry */ - INFO(mapme, - "[MAP-Me] - Creating TFIB entry with default sequence number"); - mapme_CreateTFIB(mapme, fibEntry); + mapme_tfib_t *tfib = TFIB(entry); + if (tfib == NULL) { + mapme_create_tfib(mapme, entry); + tfib = TFIB(entry); } /* @@ -767,102 +899,57 @@ static bool mapme_onSpecialInterest(const MapMe *mapme, * Detection: we receive a message initially sent by ourselves, ie a message * for which the prefix has a local next hop in the FIB. */ - if (mapme_hasLocalNextHops(mapme, fibEntry)) { - INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); - return true; - } + // XXX NOT IN VPP ? - fibSeq = TFIB(fibEntry)->seq; - if (seq > fibSeq) { - INFO(mapme, - "[MAP-Me] - Higher sequence number than FIB %d, updating seq and " - "next hops", - fibSeq); - /* This has to be done first to allow processing SpecialInterestAck's */ - TFIB(fibEntry)->seq = seq; - - /* Reliably forward the IU on all prevHops */ - INFO(mapme, "[MAP-Me] - (1/3) processing prev hops"); - if (params->type == UPDATE) { - PARCIterator *iterator = mapmeTFIB_CreateKeyIterator(TFIB(fibEntry)); - if (iterator) { - /* No iterator is created if the TFIB is empty */ - while (parcIterator_HasNext(iterator)) { - PARCUnsigned *cid = parcIterator_Next(iterator); - unsigned conn_id = parcUnsigned_GetUnsigned(cid); - INFO(mapme, "[MAP-Me] - Re-sending IU to pending connection %d", - conn_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_id, false, false, false, 0); - } - parcIterator_Release(&iterator); - } - } + /* Initially we were detecting that the update completed when it returned to + * the producer, Because there might be proxies (using local/remote strategy), + * we need instead to verify if there is no non-local faces. + */ - /* nextHops -> prevHops - * - * We add to the list of pendingUpdates the current next hops, and - * eventually forward them an IU too. - * - * Exception: nextHops -> nextHops - * Because of retransmission issues, it is possible that a second interest - * (with same of higher sequence number) is receive from a next-hop - * interface. In that case, the face remains a next hop. - */ - const NumberSet *nexthops_old = fibEntry_GetNexthops(fibEntry); + if (fib_entry_has_all_local_nexthops(entry)) { + INFO("Received original interest... Update complete"); + return; + } - /* We make a copy to be able to send IU _after_ updating next hops */ - NumberSet *nexthops = numberSet_Create(); - numberSet_AddSet(nexthops, nexthops_old); + mapme_event_t event = MAPME_EVENT_UNDEFINED; + if (params->seq > tfib->seq) { + INFO( + "MAPME IU seq %d > fib_seq %d, updating seq and next hops, new " + "nexthop=%d", + params->seq, tfib->seq, ingress_id); + /* This has to be done first to allow processing ack */ + // XXX this should even be done before sending ack, as in VPP. + tfib->seq = params->seq; - /* We are considering : * -> nextHops - * - * If inFace was a previous hop, we need to cancel the timer and remove - * the entry. Also, the face should be added to next hops. + /* + * Move nexthops to TFIB... but ingress_id that lands in nexthops * - * Optimization : nextHops -> nextHops - * - no next hop to add - * - we know that inFace was not a previous hop since it was a next hop and - * this forms a partition. No need for a search + * This could might optimized for situations where nothing changes, but + * this is very unlikely if not impossible... */ - - INFO(mapme, "[MAP-Me] - (3/3) next hops ~~> prev hops"); - mapmeTFIB_Remove(mapme, TFIB(fibEntry), conn_in_id); - - /* Remove all next hops */ - for (size_t k = 0; k < numberSet_Length(nexthops); k++) { - unsigned conn_id = numberSet_GetItem(nexthops, k); - INFO(mapme, "[MAP-Me] - Replaced next hops by connection %d", conn_id); - fibEntry_RemoveNexthopByConnectionId(fibEntry, conn_id); - } - fibEntry_AddNexthop(fibEntry, conn_in_id); - - INFO(mapme, "[MAP-Me] - (2/3) processing next hops"); - bool complete = true; - for (size_t k = 0; k < numberSet_Length(nexthops); k++) { - unsigned conn_id = numberSet_GetItem(nexthops, k); - INFO(mapme, " - Next hop connection %d", conn_id); - if (conn_id == conn_in_id) { - INFO(mapme, " . Ignored this next hop since equal to ingress face"); - continue; + nexthops_t nexthops_keep = NEXTHOPS_EMPTY; + nexthops_foreach(&entry->nexthops, prevhop, { + const connection_t *conn = connection_table_get_by_id(table, prevhop); + /* Preserve local connections, migrate others to TFIB */ + if (connection_is_local(conn)) { + nexthops_add(&nexthops_keep, prevhop); + } else { + nexthops_add(&tfib->nexthops, prevhop); } + }); - INFO(mapme, "[MAP-Me] - Sending IU on current next hop connection %d", - conn_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_id, send, false, false, 0); - complete = false; - } + nexthops_remove(&tfib->nexthops, ingress_id); - /* - * The update is completed when the IU could not be sent to any - * other next hop. - */ - if (complete) INFO(mapme, "[MAP-Me] - Update completed !"); + nexthops_clear(&entry->nexthops); + nexthops_add(&entry->nexthops, ingress_id); + nexthops_foreach(&nexthops_keep, nh, + { nexthops_add(&entry->nexthops, nh); }); + + event = MAPME_EVENT_NH_SET; - numberSet_Release(&nexthops); + // XXX tell things are complete if we have no IU to send - } else if (seq == fibSeq) { + } else if (params->seq == tfib->seq) { /* * Multipath, multihoming, multiple producers or duplicate interest * @@ -873,142 +960,77 @@ static bool mapme_onSpecialInterest(const MapMe *mapme, * producer and that we received back our own IU. In that case, we just * need to Ack and ignore it. */ -#if 0 - if (mapme_hasLocalNextHops(mapme, fibEntry)) { - INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); - return true; - } -#endif + DEBUG("params.seq %d == fib_seq %d, adding nethop %d", params->seq, + tfib->seq, ingress_id); - INFO(mapme, "[MAP-Me] - Adding multipath next hop on connection %d", - conn_in_id); - fibEntry_AddNexthop(fibEntry, conn_in_id); + /* Move ingress to nexthops (and eventually remove it from TFIB) */ + nexthops_add(&entry->nexthops, ingress_id); + nexthops_remove(&tfib->nexthops, ingress_id); - } else { // seq < fibSeq + event = MAPME_EVENT_NH_ADD; + + } else { // params->seq < tfib->seq /* * Face is propagating outdated information, we can just * consider it as a prevHops. Send the special interest backwards with * the new sequence number to reconciliate this outdated part of the * arborescence. */ - INFO( - mapme, - "[MAP-Me] - Update interest %d -> %d sent backwards on connection %d", - seq, fibSeq, conn_in_id); - mapme_setFacePending(mapme, fibEntry_GetPrefix(fibEntry), fibEntry, - conn_in_id, send, false, false, 0); - } - - return true; -} - -void mapme_onSpecialInterestAck(const MapMe *mapme, const uint8_t *msgBuffer, - unsigned conn_in_id, hicn_prefix_t *prefix, - mapme_params_t *params) { - INFO(mapme, "[MAP-Me] Receive IU/IN Ack on connection %d", conn_in_id); + if (nexthops_contains(&entry->nexthops, ingress_id)) { + INFO("Ignored seq %d < fib_seq %d from current nexthop on face %d", + params->seq, tfib->seq, ingress_id); + return; + } else { + INFO("Received seq %d < fib_seq %d, sending backwards on face %d", + params->seq, tfib->seq, ingress_id); + nexthops_add(&tfib->nexthops, ingress_id); + } - Name * name = - name_CreateFromPacket(msgBuffer, MessagePacketType_ContentObject); - name_setLen((Name*) name, prefix->len); - char * name_str = name_ToString(name); - INFO(mapme, "[MAP-Me] Received ack for name prefix=%s seq=%d on conn id=%d", - name_str, params->seq, conn_in_id); - free(name_str); + event = MAPME_EVENT_PH_ADD; + } - FIB *fib = forwarder_getFib(mapme->forwarder); - FibEntry *fibEntry = fib_Contains(fib, name); + /* Don't trigger events for notification unless we need to send interests + * backwards */ + if ((params->type != UPDATE) && (event != MAPME_EVENT_PH_ADD)) return; - name_Release(&name); + mapme_on_event(mapme, event, entry, ingress_id); +} - if (!fibEntry) { +static void mapme_on_data(mapme_t *mapme, msgbuf_t *msgbuf, unsigned ingress_id, + hicn_prefix_t *prefix, mapme_params_t *params) { + WITH_INFO({ + char buf[MAXSZ_HICN_PREFIX]; + int rc = hicn_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + if (rc < 0 || rc >= MAXSZ_HICN_PREFIX) + snprintf(buf, MAXSZ_HICN_PREFIX, "(error)"); + INFO("Received ack for name prefix=%s seq=%d on conn id=%d", buf, + params->seq, ingress_id); + }) + + const fib_t *fib = forwarder_get_fib(mapme->forwarder); + fib_entry_t *entry = fib_contains(fib, prefix); + if (!entry) { + INFO("Ignored ACK with no corresponding FIB entry"); return; } - parcAssertNotNull(fibEntry, - "No corresponding FIB entry for name contained in IU Ack"); - - /* Test if the latest pending update has been ack'ed, otherwise just ignore */ - seq_t seq = params->seq; - if (seq != INVALID_SEQ) { - seq_t fibSeq = TFIB(fibEntry)->seq; - - if (seq < fibSeq) { - - /* If we receive an old ack: - * - either the connection is still a next hop and we have to ignore - * the ack until we receive a further update with higher seqno - * - or the connection is no more to be informed and the ack is - * sufficient and we can remove future retransmissions - */ - - INFO(mapme, - "[MAP-Me] - Ignored special interest Ack with seq=%u, expected %u", - seq, fibSeq); - return; - } - } + mapme_tfib_t *tfib = TFIB(entry); /* - * Ignore the Ack if no TFIB is present, or it has no corresponding entry - * with the ingress face. - * Note: previously, we were creating the TFIB entry + * As we always retransmit IU with the latest seq, we are not interested in + * ACKs with inferior seq */ - if (!TFIB(fibEntry)) { - INFO(mapme, "[MAP-Me] - Ignored ACK for prefix with no TFIB entry"); - return; - } - - PARCEventTimer *timer = - (PARCEventTimer *)mapmeTFIB_Get(TFIB(fibEntry), conn_in_id); - if (!timer) { - INFO(mapme, - "[MAP-Me] - Ignored ACK for prefix not having the Connection in " - "TFIB entry. Possible duplicate ?"); + if (params->seq < tfib->seq) { + INFO("Ignored ACK with seq %d < %d", params->seq, tfib->seq); return; } - /* Stop timer and remove entry from TFIB */ - mapmeTFIB_Remove(mapme, TFIB(fibEntry), conn_in_id); - - INFO(mapme, "[MAP-Me] - Removing TFIB entry for ack on connection %d", - conn_in_id); + nexthops_remove(&tfib->nexthops, ingress_id); + mapme_on_event(mapme, MAPME_EVENT_PH_DEL, entry, ingress_id); /* We need to update the timestamp only for IU Acks, not for IN Acks */ if (params->type == UPDATE_ACK) { - INFO(mapme, "[MAP-Me] - Updating LastAckedUpdate"); - TFIB(fibEntry)->lastAckedUpdate = forwarder_GetTicks(mapme->forwarder); - } -} - -/*----------------------------------------------------------------------------- - * Overloaded functions - *----------------------------------------------------------------------------*/ - -/* - * @abstract returns where to forward a normal interests(nexthops) defined by - * mapme, it also set the sequnence number properly if needed - */ - -/****************************************************************************** - * Public functions (exposed in the .h) - ******************************************************************************/ - -/* - * Returns true iif the message corresponds to a MAP-Me packet - */ -bool mapme_isMapMe(const uint8_t *packet) { - hicn_mapme_header_t * mapme = (hicn_mapme_header_t*)packet; - - switch(HICN_IP_VERSION(packet)) { - case 4: - if (mapme->v4.ip.protocol != IPPROTO_ICMP) - return false; - return HICN_IS_MAPME(mapme->v4.icmp_rd.type, mapme->v4.icmp_rd.code); - case 6: - if (mapme->v6.ip.nxt != IPPROTO_ICMPV6) - return false; - return HICN_IS_MAPME(mapme->v6.icmp_rd.type, mapme->v6.icmp_rd.code); - default: - return false; + INFO(" - Updating LastAckedUpdate"); + tfib->last_acked_update = ticks_now(); } } @@ -1023,25 +1045,70 @@ bool mapme_isMapMe(const uint8_t *packet) { * MAP-Me (eg. ICMP packets) and return higher level messages that can be * processed by MAP-Me core. */ -void mapme_Process(const MapMe *mapme, const uint8_t *msgBuffer, - unsigned conn_id) { +void mapme_process(mapme_t *mapme, msgbuf_t *msgbuf) { + if (mapme->enabled == false) { + WARN("MAP-Me is NOT enabled"); + return; + } + hicn_prefix_t prefix; mapme_params_t params; - hicn_mapme_parse_packet(msgBuffer, &prefix, ¶ms); + uint8_t *packet = msgbuf_get_packet(msgbuf); + unsigned conn_id = msgbuf_get_connection_id(msgbuf); + + int rc = hicn_mapme_parse_packet(packet, &prefix, ¶ms); + if (rc < 0) return; + + // XXX TYPE STR + INFO("Received interest type: %d seq: %d len:%d", params.type, params.seq, + prefix.len); + + // XXX RENAME TYPES switch (params.type) { case UPDATE: case NOTIFICATION: - mapme_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, ¶ms); + mapme_on_interest(mapme, msgbuf, conn_id, &prefix, ¶ms); break; case UPDATE_ACK: case NOTIFICATION_ACK: - mapme_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, ¶ms); + mapme_on_data(mapme, msgbuf, conn_id, &prefix, ¶ms); break; default: - ERR(mapme, "[MAP-Me] Unknown message"); + ERROR("Unknown message"); break; } } +#if 0 +/* + * Returns true iif the message corresponds to a MAP-Me packet + */ +bool mapme_match_packet(const uint8_t *packet) { + hicn_mapme_header_t *mapme = (hicn_mapme_header_t *)packet; + + switch (HICN_IP_VERSION(packet)) { + case 4: + if (mapme->v4.ip.protocol != IPPROTO_ICMP) return false; + return HICN_IS_MAPME(mapme->v4.icmp_rd.type, mapme->v4.icmp_rd.code); + case 6: + if (mapme->v6.ip.nxt != IPPROTO_ICMPV6) return false; + return HICN_IS_MAPME(mapme->v6.icmp_rd.type, mapme->v6.icmp_rd.code); + default: + return false; + } +} +#endif + +void mapme_set_enable(mapme_t *mapme, bool enable) { mapme->enabled = enable; } +void mapme_set_discovery(mapme_t *mapme, bool enable) { + mapme->discovery = enable; +} +void mapme_set_timescale(mapme_t *mapme, uint32_t time) { + mapme->timescale = time; +} +void mapme_set_retransmision(mapme_t *mapme, uint32_t time) { + mapme->retx = time; +} + #endif /* WITH_MAPME */ diff --git a/hicn-light/src/hicn/core/mapme.h b/hicn-light/src/hicn/core/mapme.h index 72f8d536a..9ece8a090 100644 --- a/hicn-light/src/hicn/core/mapme.h +++ b/hicn-light/src/hicn/core/mapme.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021-2023 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -26,29 +26,34 @@ #include <stdbool.h> #include <stdint.h> +#include <hicn/ctrl/hicn-light.h> #include <hicn/hicn.h> -#include <hicn/core/forwarder.h> -#include <hicn/core/connection.h> -#include <hicn/utils/commands.h> -struct mapme; -typedef struct mapme MapMe; +#include "connection.h" +#include "fib_entry.h" +#include "msgbuf.h" + +// Allow processing of MAP-Me requests when no FIB entry is present. +// An alternative would be to perform a LPM +#define HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY + +typedef struct mapme_s mapme_t; /** * @function mapme_create * @abstract Initializes MAP-Me state in the forwarder. * @return bool - Boolean informing about the success of MAP-Me initialization. */ -bool mapme_create(MapMe **mapme, Forwarder *Forwarder); +mapme_t *mapme_create(void *Forwarder); /** * @function mapme_free * @abstract Free MAP-Me state in the forwarder. */ -void mapme_free(MapMe *mapme); +void mapme_free(mapme_t *mapme); /** - * @function messageHandler_isMapMe + * @function messageHandler_is_mapme * @abstract Identifies MAP-Me messages * @discussion This function can be used by the forwarder to dispatch MAP-Me * message to the appropriate processing function. Ideally this would be @@ -56,48 +61,68 @@ void mapme_free(MapMe *mapme); * @param [in] msgBuffer - The buffer to match * @return A boolean indicating whether message is a MAP-Me control message. */ -bool mapme_isMapMe(const uint8_t *msgBuffer); +bool mapme_match_packet(const uint8_t *msgBuffer); /** - * @function mapme_handleMapMeMessage + * @function mapme_handlemapme_tMessage * @abstract Process a MAP-Me message. * @param [in] mapme - Pointer to the MAP-Me data structure. * @param [in] message - MAP-Me buffer * @param [in] conn_id - Ingress connection id */ -void mapme_Process(const MapMe *mapme, const uint8_t *msgBuffer, - unsigned conn_id); +void mapme_process(mapme_t *mapme, msgbuf_t *msgbuf); +/* mapme API */ /** * @function mapme_send_updates - * @abstract Trigger (if needed) the update for specified FIB entry and nexthops + * @abstract sends an update to all adjacencies. Used for face + * add/delete/changes (priority.tag) and policy * @param [in] mapme - Pointer to the MAP-Me data structure. - * @param [in] fibEntry - The FIB entry to consider - * @param [in] nexthops - NumberSet holding the next hops on which to send the - * update. + * @param [in] fib_entry - The FIB entry to consider */ -void mapme_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops); +int mapme_set_all_adjacencies(const mapme_t *mapme, fib_entry_t *entry); /** - * @function mapme_send_updates - * @abstract Trigger the update for specified FIB entry and nexthops, only if needed + * @function mapme_set_adjacencies + * @abstract sends an update to the specified adjacencies. Used by forwarding + * strategies * @param [in] mapme - Pointer to the MAP-Me data structure. - * @param [in] fibEntry - The FIB entry to consider - * @param [in] nexthops - NumberSet holding the next hops on which to send the - * update. + * @param [in] fib_entry - The FIB entry to consider + * @param [in] nexthops - next hops on which to send the update. + * @param [in] prefix - A more specific prefix (special use with no retx), or + * NULL */ -void mapme_maybe_send_updates(const MapMe * mapme, FibEntry * fibEntry, const NumberSet * nexthops); +int mapme_set_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + nexthops_t *nexthops, const hicn_prefix_t *prefix); /** - * @function mapme_reconsiderFibEntry - * @abstract Process a fib entry for changes that might trigger new updates + * @function mapme_set_adjacencies + * @abstract sends an update to the specified adjacency. Used by control plane + * commands. * @param [in] mapme - Pointer to the MAP-Me data structure. - * @param [in] fibEntry - The FIB entry to consider + * @param [in] fib_entry - The FIB entry to consider + * @param [in] nexthop - nexthop on which to send the update. + * @param [in] prefix - A more specific prefix (special use with no retx), or + * NULL */ -void mapme_reconsiderFibEntry(const MapMe *mapme, FibEntry * fibEntry); +int mapme_set_adjacency(const mapme_t *mapme, fib_entry_t *entry, + nexthop_t nexthop, const hicn_prefix_t *prefix); /** - * @function mapme_onConnectionEvent + * @function mapme_update_adjacencies + * @abstract sends an update on previuos adjacencies. Used for IU forwarding, + * NAT and timeouts + * strategies + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] fib_entry - The FIB entry to consider + * @param [in] inc_iu_seq - if true, the seq number of the tfib/mapme iu will be + * increased by one + */ +int mapme_update_adjacencies(const mapme_t *mapme, fib_entry_t *entry, + bool inc_iu_seq); + +/** + * @function mapme_on_connection_event * @abstract Callback following the addition of the face though the control * protocol. * @discussion This callback triggers the sending of control packets by MAP-Me. @@ -105,19 +130,27 @@ void mapme_reconsiderFibEntry(const MapMe *mapme, FibEntry * fibEntry); * @param [in] conn - The newly added connection. * @param [in] event - Connection event */ -void mapme_onConnectionEvent(const MapMe *mapme, const Connection *conn, connection_event_t event); +void mapme_on_connection_event(const mapme_t *mapme, const connection_t *conn, + connection_event_t event); /** - * @function mapme_getNextHops + * @function mapme_get_nexthops * @abstract return the nexthops to forward interests defined by mapme, it * covers also the case where local discovery mechanisms are trriggered. */ -NumberSet *mapme_getNextHops(const MapMe *mapme, FibEntry *fibEntry, - const Message *interest); - -hicn_mapme_type_t mapme_PktType_To_LibHicnPktType(MessagePacketType type); - -MessagePacketType mapme_LibHicnPktType_To_PktType(hicn_mapme_type_t type); +// nexthops_t * mapme_get_nexthops(const mapme_t *mapme, fib_entry_t *fib_entry, +// const msgbuf_t *interest); + +#ifdef HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY +fib_entry_t *mapme_create_fib_entry(const mapme_t *mapme, + const hicn_prefix_t *prefix, + unsigned ingress_id); +#endif /* HICN_MAPME_ALLOW_NONEXISTING_FIB_ENTRY */ + +void mapme_set_enable(mapme_t *mapme, bool enable); +void mapme_set_discovery(mapme_t *mapme, bool enable); +void mapme_set_timescale(mapme_t *mapme, uint32_t time); +void mapme_set_retransmision(mapme_t *mapme, uint32_t time); #endif /* WITH_MAPME */ diff --git a/hicn-light/src/hicn/core/message.c b/hicn-light/src/hicn/core/message.c deleted file mode 100644 index c28938320..000000000 --- a/hicn-light/src/hicn/core/message.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <errno.h> -#include <hicn/hicn-light/config.h> -#include <stdio.h> -#include <string.h> - -#include <hicn/core/forwarder.h> -#include <hicn/core/message.h> -#include <hicn/core/wldr.h> - -#include <hicn/core/messageHandler.h> - -#include <parc/algol/parc_Hash.h> -#include <parc/algol/parc_Memory.h> -#include <hicn/core/messagePacketType.h> - -#include <parc/assert/parc_Assert.h> - -#include <parc/algol/parc_EventBuffer.h> - -struct message { - Logger *logger; - - Ticks receiveTime; - unsigned ingressConnectionId; - - Name *name; - - uint8_t *messageHead; - - unsigned length; - - uint8_t packetType; - - unsigned refcount; -}; - -Message *message_Acquire(const Message *message) { - Message *copy = (Message *)message; - copy->refcount++; - return copy; -} - -Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, - unsigned ingressConnectionId, - Ticks receiveTime, Logger *logger) { - // used by applications, we can get only interest or data packets - Message *message = parcMemory_AllocateAndClear(sizeof(Message)); - parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Message)); - - message->logger = logger_Acquire(logger); - message->receiveTime = receiveTime; - message->ingressConnectionId = ingressConnectionId; - message->length = (unsigned int)dataLength; - - message->messageHead = parcMemory_AllocateAndClear(dataLength); - parcAssertNotNull(message->messageHead, - "parcMemory_AllocateAndClear(%zu) returned NULL", - dataLength); - - // copy the data because *data is destroyed in the connection. - int res = parcEventBuffer_Read(data, message->messageHead, dataLength); - if (res == -1) { - return NULL; - } - - if (messageHandler_IsInterest(message->messageHead)) { - message->packetType = MessagePacketType_Interest; - } else if (messageHandler_IsData(message->messageHead)) { - message->packetType = MessagePacketType_ContentObject; - } else { - printf("Got a packet that is not a data nor an interest, drop it!\n"); - return NULL; - } - message->name = - name_CreateFromPacket(message->messageHead, message->packetType); - - message->refcount = 1; - - return message; -} - -Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, - MessagePacketType type, Ticks receiveTime, - Logger *logger) { - Message *message = parcMemory_AllocateAndClear(sizeof(Message)); - parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Message)); - - message->logger = logger_Acquire(logger); - message->receiveTime = receiveTime; - message->ingressConnectionId = connid; - message->messageHead = pckt; - message->length = messageHandler_GetTotalPacketLength(pckt); - message->packetType = type; - - if (messageHandler_IsWldrNotification(pckt)) { - message->name = NULL; - } else { - message->name = - name_CreateFromPacket(message->messageHead, message->packetType); - } - - message->refcount = 1; - - return message; -} - -void message_Release(Message **messagePtr) { - parcAssertNotNull(messagePtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*messagePtr, - "Parameter must dereference to non-null pointer"); - - Message *message = *messagePtr; - parcAssertTrue( - message->refcount > 0, - "Invalid state: message_Release called on message with 0 references %p", - (void *)message); - - message->refcount--; - if (message->refcount == 0) { - if (logger_IsLoggable(message->logger, LoggerFacility_Message, - PARCLogLevel_Debug)) { - logger_Log(message->logger, LoggerFacility_Message, PARCLogLevel_Debug, - __func__, "Message %p destroyed", (void *)message); - } - - logger_Release(&message->logger); - if (message->name != NULL) name_Release(&message->name); - parcMemory_Deallocate((void **)&message->messageHead); - parcMemory_Deallocate((void **)&message); - } - *messagePtr = NULL; -} - -bool message_Write(PARCEventQueue *parcEventQueue, const Message *message) { - parcAssertNotNull(message, "Message parameter must be non-null"); - parcAssertNotNull(parcEventQueue, "Buffer parameter must be non-null"); - - return parcEventQueue_Write(parcEventQueue, message->messageHead, - message_Length(message)); -} - -size_t message_Length(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return message->length; -} - -bool message_HasWldr(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_HasWldr(message->messageHead); -} - -bool message_IsWldrNotification(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_IsWldrNotification(message->messageHead); -} - -void message_ResetWldrLabel(Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_ResetWldrLabel(message->messageHead); -} - -unsigned message_GetWldrLabel(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_GetWldrLabel(message->messageHead); -} - -unsigned message_GetWldrExpectedLabel(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_GetExpectedWldrLabel(message->messageHead); -} - -unsigned message_GetWldrLastReceived(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_GetWldrLastReceived(message->messageHead); -} - -void message_SetWldrLabel(Message *message, uint16_t label) { - parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_SetWldrLabel(message->messageHead, label); -} - -Message *message_CreateWldrNotification(Message *original, uint16_t expected, - uint16_t lastReceived) { - parcAssertNotNull(original, "Parameter original must be non-null"); - Message *message = parcMemory_AllocateAndClear(sizeof(Message)); - parcAssertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Message)); - message->receiveTime = original->receiveTime; - message->ingressConnectionId = original->ingressConnectionId; - message->refcount = 1; - message->logger = logger_Acquire(original->logger); - - message->length = (unsigned int)messageHandler_GetICMPPacketSize( - messageHandler_GetIPPacketType(original->messageHead)); - message->messageHead = parcMemory_AllocateAndClear(message->length); - parcAssertNotNull(message->messageHead, - "parcMemory_AllocateAndClear returned NULL"); - - message->packetType = MessagePacketType_WldrNotification; - message->name = NULL; // nobody will use the name in a notification packet, - // so we can simply set it to NULL - - // set notification stuff. - messageHandler_SetWldrNotification( - message->messageHead, original->messageHead, expected, lastReceived); - return message; -} - -unsigned message_GetIngressConnectionId(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return message->ingressConnectionId; -} - -void message_SetIngressConnectionId(Message *message, unsigned conn) { - parcAssertNotNull(message, "Parameter must be non-null"); - message->ingressConnectionId = conn; -} - -Ticks message_GetReceiveTime(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return message->receiveTime; -} - -uint32_t message_GetPathLabel(const Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - return messageHandler_GetPathLabel(message->messageHead); -} - -void message_SetPathLabel(Message *message, uint32_t label) { - parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_SetPathLabel(message->messageHead, - messageHandler_GetPathLabel(message->messageHead), label); -} - -void message_UpdatePathLabel(Message *message, uint8_t outFace) { - parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_UpdatePathLabel(message->messageHead, outFace); -} - -void message_ResetPathLabel(Message *message) { - parcAssertNotNull(message, "Parameter must be non-null"); - messageHandler_ResetPathLabel(message->messageHead); -} - -MessagePacketType message_GetType(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - return message->packetType; -} - -Name *message_GetName(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - return message->name; -} - -bool message_HasInterestLifetime(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - return messageHandler_HasInterestLifetime(message->messageHead); -} - -uint64_t message_GetInterestLifetimeTicks(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - uint64_t lifetime = messageHandler_GetInterestLifetime(message->messageHead); - return forwarder_NanosToTicks(lifetime * 1000000ULL); -} - -bool message_HasContentExpiryTime(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - return messageHandler_HasContentExpiryTime(message->messageHead); -} - -uint64_t message_GetContentExpiryTimeTicks(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - uint64_t expire = messageHandler_GetContentExpiryTime(message->messageHead); - if(expire == 0) - return message->receiveTime; - return message->receiveTime + forwarder_NanosToTicks(expire * 1000000ULL); -} - -const uint8_t *message_FixedHeader(const Message *message) { - parcAssertNotNull(message, "Parameter message must be non-null"); - return message->messageHead; -} diff --git a/hicn-light/src/hicn/core/message.h b/hicn-light/src/hicn/core/message.h deleted file mode 100644 index e77dab2b5..000000000 --- a/hicn-light/src/hicn/core/message.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @file message.h - * @brief Message is the unit of forwarding, i.e. the packets being switched - * - */ -#ifndef message_h -#define message_h - -#include <hicn/hicn-light/config.h> -#include <hicn/core/logger.h> -#include <hicn/core/messagePacketType.h> -#include <hicn/core/streamBuffer.h> - -#include <hicn/core/name.h> - -#include <parc/algol/parc_EventBuffer.h> -#include <parc/algol/parc_EventQueue.h> - -#include <hicn/utils/address.h> - -#include <hicn/core/ticks.h> - -struct message; -typedef struct message Message; - -/** - * @function message_CreateFromBuffer - * @abstract Takes ownership of the input buffer, which comprises one complete - * message - */ - -Message *message_CreateFromEventBuffer(PARCEventBuffer *data, size_t dataLength, - unsigned ingressConnectionId, - Ticks receiveTime, Logger *logger); - -/** - * @function message_CreateFromByteArray - * @abstract create a message from a byte array - */ - -Message *message_CreateFromByteArray(unsigned connid, uint8_t *pckt, - MessagePacketType type, Ticks receiveTime, - Logger *logger); - -/** - * @function message_Copy - * @abstract Get a reference counted copy - */ - -Message *message_Acquire(const Message *message); - -/** - * Releases the message and frees the memory - */ -void message_Release(Message **messagePtr); - -/** - * Writes the message to the queue - */ - -bool message_Write(PARCEventQueue *parcEventQueue, const Message *message); - -/** - * Returns the total byte length of the message - */ -size_t message_Length(const Message *message); - -bool message_HasWldr(const Message *message); - -bool message_IsWldrNotification(const Message *message); - -void message_ResetWldrLabel(Message *message); - -unsigned message_GetWldrLabel(const Message *message); - -unsigned message_GetWldrExpectedLabel(const Message *message); - -unsigned message_GetWldrLastReceived(const Message *message); - -void message_SetWldrLabel(Message *message, uint16_t label); - -Message *message_CreateWldrNotification(Message *original, uint16_t expected, - uint16_t lastReceived); -/** - * Returns the connection id of the packet input - */ -unsigned message_GetIngressConnectionId(const Message *message); - -void message_SetIngressConnectionId(Message *message, unsigned conn); - -/** - * Returns the receive time (in router ticks) of the message - */ -Ticks message_GetReceiveTime(const Message *message); - -/** - * Returns the PacketType - */ -MessagePacketType message_GetType(const Message *message); - -uint32_t message_GetPathLabel(const Message *message); -void message_SetPathLabel(Message *message, uint32_t label); -void message_UpdatePathLabel(Message *message, uint8_t outFace); -void message_ResetPathLabel(Message *message); - -// =========================================================== -// Accessors used to index and compare messages - -/** - * @function message_GetName - * @abstract The name in the message - * @discussion - * The name of the Interest or Content Object. If the caller will store the - * name, he should make a reference counted copy. - * @return The name as stored in the message object. - */ - -Name *message_GetName(const Message *message); - -/** - * Determines if the message has an Interest Lifetime parameter - * - * @param [in] message An allocated and parsed Message - * - * @retval true If an Intrerest Lifetime field exists - * @retval false If no Interest Lifetime exists - */ - -bool message_HasInterestLifetime(const Message *message); - -/** - * Returns the Interest lifetime in hicn-light Ticks - * - * the interest expires after now + returned ticks - * - * @param [in] message An allocated and parsed Message - * - * @retval integer Lifetime in forwarder Ticks - * - */ - -uint64_t message_GetInterestLifetimeTicks(const Message *message); - -/** - * checks if the expiry time is set inside the content object - */ -bool message_HasContentExpiryTime(const Message *message); - -/** - * returns the moment (in hicn-light ticks) when the content object will expire - */ -uint64_t message_GetContentExpiryTimeTicks(const Message *message); - -/** - * Returns a pointer to the beginning of the FixedHeader - * - * @param [in] message An allocated and parsed Message - * - * @return non-null The fixed header memory - * @return null No fixed header or an error - */ - -const uint8_t *message_FixedHeader(const Message *message); - -#endif // message_h diff --git a/hicn-light/src/hicn/core/messageHandler.h b/hicn-light/src/hicn/core/messageHandler.h deleted file mode 100644 index b41c9a7f0..000000000 --- a/hicn-light/src/hicn/core/messageHandler.h +++ /dev/null @@ -1,758 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef messageHandler -#define messageHandler - -#include <stdlib.h> -#ifndef _WIN32 -#include <unistd.h> // close -#endif - -#include <hicn/hicn.h> -#include <hicn/core/messagePacketType.h> - -#define H(packet) ((hicn_header_t *)packet) -#define H6(packet) (H(packet)->v6.ip) -#define H6T(packet) (H(packet)->v6.tcp) -#define H4(packet) (H(packet)->v4.ip) -#define H4T(packet) (H(packet)->v4.tcp) - -#define HICN_V6_LEN(packet) (H6(packet).len) -#define HICN_V4_LEN(packet) (H4(packet).len) - -/*** codes and types ***/ -#define IPv6_TYPE 6 -#define IPv4_TYPE 4 -#define ICMP_WLDR_TYPE 42 -#define ICMP_WLDR_CODE 0 -#define ICMP_LB_TYPE 43 - -/*** masks and constants ***/ -#define PATH_LABEL_MASK 0x8000 // 1000 0000 0000 0000 -#define NOT_PATH_LABEL_MASK 0x7fff // 0111 0000 0000 0000 -#define UINT16_T_MASK 0x0000ffff // 1111 1111 1111 1111 - -/*** HICN ALLOWED PORTS ***/ -#define CONTROL_PORT 9695 -#define HTTP_PORT 8080 - -#define IPV6_DEFAULT_VERSION 6 -#define IPV6_DEFAULT_TRAFFIC_CLASS 0 -#define IPV6_DEFAULT_FLOW_LABEL 0 - -#define expected_lbl wldr_notification_lbl.expected_lbl -#define received_lbl wldr_notification_lbl.received_lbl - -#include <hicn/core/forwarder.h> - -#ifdef WITH_MAPME -#include <hicn/core/mapme.h> -#include <hicn/socket/api.h> -#endif /* WITH_MAPME */ - -#define CONNECTION_ID_UNDEFINED -1 - -#define BFD_PORT 3784 - -static inline uint8_t messageHandler_GetIPPacketType(const uint8_t *message) { - return HICN_IP_VERSION(message); -} - -static inline void messageHandler_UpdateTCPCheckSum(uint8_t *message, - uint16_t *old_val, - uint16_t *new_val, - uint8_t size) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv4_TYPE: - for (uint8_t i = 0; i < size; i++) { - uint16_t old_csum = ~(H4T(message).csum); - uint16_t not_old_val = ~(*old_val); - uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; - - while (sum >> 16) { - sum = (sum >> 16) + (sum & UINT16_T_MASK); - } - - H4T(message).csum = ~sum; - ++old_val; - ++new_val; - } - break; - case IPv6_TYPE: - for (uint8_t i = 0; i < size; i++) { - uint16_t old_csum = ~(H6T(message).csum); - uint16_t not_old_val = ~(*old_val); - uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; - - while (sum >> 16) { - sum = (sum >> 16) + (sum & UINT16_T_MASK); - } - - H6T(message).csum = ~sum; - ++old_val; - ++new_val; - } - break; - default: - return; - } -} - -static inline void messageHandler_UpdateIPv4CheckSum(uint8_t *message, - uint16_t *old_val, - uint16_t *new_val, - uint8_t size) { - for (uint8_t i = 0; i < size; i++) { - uint16_t old_csum = ~(H4(message).csum); - uint16_t not_old_val = ~(*old_val); - uint32_t sum = (uint32_t)old_csum + not_old_val + *new_val; - - while (sum >> 16) { - sum = (sum >> 16) + (sum & UINT16_T_MASK); - } - - H4(message).csum = ~sum; - ++old_val; - ++new_val; - } -} - -static inline size_t messageHandler_GetEmptyTCPPacketSize(unsigned ipVersion) { - if (ipVersion == IPv4_TYPE) - return IPV4_HDRLEN + TCP_HDRLEN; - else if (ipVersion == IPv6_TYPE) - return IPV6_HDRLEN + TCP_HDRLEN; - else - return 0; -} - -static inline size_t messageHandler_GetICMPPacketSize(unsigned ipVersion) { - if (ipVersion == IPv4_TYPE) - return IPV4_HDRLEN + ICMP_HDRLEN; - else if (ipVersion == IPv6_TYPE) - return IPV6_HDRLEN + ICMP_HDRLEN; - else - return 0; -} - -static inline size_t messageHandler_GetIPHeaderLength(unsigned ipVersion) { - if (ipVersion == IPv4_TYPE) - return IPV4_HDRLEN; - else if (ipVersion == IPv6_TYPE) - return IPV6_HDRLEN; - else - return 0; -} - -static inline bool messageHandler_IsValidHicnPacket(const uint8_t *message) { - uint8_t version = messageHandler_GetIPPacketType(message); - if (version == IPv6_TYPE || version == IPv4_TYPE) { - return true; - } - return false; -} - -static inline uint8_t messageHandler_NextHeaderType(const uint8_t *message) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return (uint8_t)H6(message).nxt; - case IPv4_TYPE: - return (uint8_t)H4(message).protocol; - default: - return 0; - } -} - -/* Forward declarations */ -static inline void * messageHandler_GetSource(const uint8_t *message); -static inline void *messageHandler_GetDestination(const uint8_t *message); - -static const -AddressPair * -_createRecvAddressPairFromPacket(const uint8_t *msgBuffer) { - Address *packetSrcAddr = NULL; /* This one is in the packet */ - Address *localAddr = NULL; /* This one is to be determined */ - - if (messageHandler_GetIPPacketType(msgBuffer) == IPv6_TYPE) { - struct sockaddr_in6 addr_in6; - addr_in6.sin6_family = AF_INET6; - addr_in6.sin6_port = htons(1234); - addr_in6.sin6_flowinfo = 0; - addr_in6.sin6_scope_id = 0; - memcpy(&addr_in6.sin6_addr, - (struct in6_addr *)messageHandler_GetSource(msgBuffer), 16); - packetSrcAddr = addressCreateFromInet6(&addr_in6); - - /* We now determine the local address used to reach the packet src address */ - int sock = (int)socket (AF_INET6, SOCK_DGRAM, 0); - if (sock < 0) - goto ERR; - - struct sockaddr_in6 remote, local; - memset(&remote, 0, sizeof(remote)); - remote.sin6_family = AF_INET6; - remote.sin6_addr = addr_in6.sin6_addr; - remote.sin6_port = htons(1234); - - socklen_t locallen = sizeof(local); - if (connect(sock, (const struct sockaddr*)&remote, sizeof(remote)) == -1) - goto ERR; - if (getsockname(sock, (struct sockaddr*) &local, &locallen) == -1) - goto ERR; - - local.sin6_port = htons(1234); - localAddr = addressCreateFromInet6(&local); - - close(sock); - - } else if (messageHandler_GetIPPacketType(msgBuffer) == IPv4_TYPE) { - struct sockaddr_in addr_in; - addr_in.sin_family = AF_INET; - addr_in.sin_port = htons(1234); - memcpy(&addr_in.sin_addr, - (struct in_addr *)messageHandler_GetSource(msgBuffer), 4); - packetSrcAddr = addressCreateFromInet(&addr_in); - - /* We now determine the local address used to reach the packet src address */ - - int sock = (int)socket (AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - perror("Socket error"); - goto ERR; - } - - struct sockaddr_in remote, local; - memset(&remote, 0, sizeof(remote)); - remote.sin_family = AF_INET; - remote.sin_addr = addr_in.sin_addr; - remote.sin_port = htons(1234); - - socklen_t locallen = sizeof(local); - if (connect(sock, (const struct sockaddr*)&remote, sizeof(remote)) == -1) - goto ERR; - if (getsockname(sock, (struct sockaddr*) &local, &locallen) == -1) - goto ERR; - - local.sin_port = htons(1234); - localAddr = addressCreateFromInet(&local); - - close(sock); - } - /* As this is a receive pair, we swap src and dst */ - return addressPair_Create(localAddr, packetSrcAddr); - -ERR: - perror("Socket error"); - return NULL; -} - -/* Main hook handler */ - -/** - * \brief Handle incoming messages - * \param [in] forwarder - Reference to the Forwarder instance - * \param [in] packet - Packet buffer - * \param [in] conn_id - A hint on the connection ID on which the packet - * was received - * \return Flag indicating whether the packet matched a hook and was - * (successfully or not) processed. - */ -static inline bool messageHandler_handleHooks(Forwarder * forwarder, - const uint8_t * packet, ListenerOps * listener, int fd, AddressPair * pair) -{ - bool is_matched = false; - - /* BEGIN Match */ - -#ifdef WITH_MAPME - bool is_mapme = mapme_isMapMe(packet); - is_matched |= is_mapme; -#endif /* WITH_MAPME */ - - /* ... */ - - /* END Match */ - - if (!is_matched) - return false; - - /* - * Find existing connection or create a new one (we assume all processing - * requires a valid connection. - */ - - if (!pair) { - /* The hICN listener does not provide any address pair while UDP does */ - const AddressPair * pair = _createRecvAddressPairFromPacket(packet); - if (!pair) - return false; - } - - /* Find connection and eventually create it */ - const Connection * conn = connectionTable_FindByAddressPair( - forwarder_GetConnectionTable(forwarder), pair); - unsigned conn_id; - if (conn == NULL) { - conn_id = listener->createConnection(listener, fd, pair); - } else { - conn_id = connection_GetConnectionId(conn); - } - - /* BEGIN Process */ - -#ifdef WITH_MAPME - if (mapme_isMapMe(packet)) - forwarder_ProcessMapMe(forwarder, packet, conn_id); -#endif /* WITH_MAPME */ - - /* ... */ - - /* END Process */ - parcMemory_Deallocate((void **)&packet); - return true; -} - -static inline bool messageHandler_IsTCP(const uint8_t *message) { - if (messageHandler_NextHeaderType(message) != IPPROTO_TCP) return false; - return true; -} - -static inline bool messageHandler_IsInterest(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return false; - - bool flag; - hicn_packet_test_ece(HF_INET6_TCP, (hicn_header_t *)message, - &flag); // ECE flag is set to 0 in interest packets - if (flag == false) return true; - return false; -} - -static inline bool messageHandler_IsData(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return false; - - bool flag; - hicn_packet_test_ece(HF_INET6_TCP, (hicn_header_t *)message, - &flag); // ECE flag is set to 1 in data packets - if (flag == true) return true; - return false; -} - -static inline bool messageHandler_IsWldrNotification(const uint8_t *message) { - // this function returns true only if the packet is an ICMP packet in Wldr - // form. type must be equal to ICMP_WLDR_TYPE and code equal to ICMP_WLDR_CODE - uint8_t next_header = messageHandler_NextHeaderType(message); - - const uint8_t *icmp_ptr; - if (next_header == IPPROTO_ICMP) { - icmp_ptr = message + IPV4_HDRLEN; - } else if (next_header == IPPROTO_ICMPV6) { - icmp_ptr = message + IPV6_HDRLEN; - } else { - return false; - } - - uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; - uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; - if (type == ICMP_WLDR_TYPE && code == ICMP_WLDR_CODE) { - return true; - } - - return false; -} - -static inline bool messageHandler_IsLoadBalancerProbe(const uint8_t *message) { - uint8_t next_header = messageHandler_NextHeaderType(message); - - const uint8_t *icmp_ptr; - if (next_header == IPPROTO_ICMP) { - icmp_ptr = message + IPV4_HDRLEN; - } else if (next_header == IPPROTO_ICMPV6) { - icmp_ptr = message + IPV6_HDRLEN; - } else { - return false; - } - - uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; - if (type == ICMP_LB_TYPE) { - return true; - } - - return false; -} - -static inline uint16_t messageHandler_GetTotalPacketLength( - const uint8_t *message) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return ntohs((uint16_t)HICN_V6_LEN(message)) + IPV6_HDRLEN; - case IPv4_TYPE: - return ntohs((uint16_t)HICN_V4_LEN(message)); - default: - return 0; - } -} - -static inline uint32_t messageHandler_GetSegment(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return 0; - - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return ntohl((uint32_t)H6T(message).seq); - case IPv4_TYPE: - return ntohl((uint32_t)H4T(message).seq); - default: - return 0; - } -} - -static inline uint16_t messageHandler_GetExpectedWldrLabel( - const uint8_t *message) { - const uint8_t *icmp_ptr; - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - icmp_ptr = message + IPV6_HDRLEN; - break; - case IPv4_TYPE: - icmp_ptr = message + IPV4_HDRLEN; - break; - default: - return 0; - } - - return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->expected_lbl); -} - -static inline uint16_t messageHandler_GetWldrLastReceived( - const uint8_t *message) { - const uint8_t *icmp_ptr; - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - icmp_ptr = message + IPV6_HDRLEN; - break; - case IPv4_TYPE: - icmp_ptr = message + IPV4_HDRLEN; - break; - default: - return 0; - } - - return ntohs(((_icmp_wldr_header_t *)icmp_ptr)->received_lbl); -} - -static inline uint16_t messageHandler_GetWldrLabel(const uint8_t *message) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return ntohs((uint16_t)H6T(message).window); - case IPv4_TYPE: - return ntohs((uint16_t)H4T(message).window); - default: - return 0; - } -} - -static inline void messageHandler_SetWldrLabel(uint8_t *message, - uint16_t label) { - uint16_t old_val = messageHandler_GetWldrLabel(message); - - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - H6T(message).window = htons(label); - break; - case IPv4_TYPE: - H4T(message).window = htons(label); - break; - default: - break; - } - - messageHandler_UpdateTCPCheckSum(message, &old_val, &label, 1); -} - -static inline void messageHandler_ResetWldrLabel(uint8_t *message) { - messageHandler_SetWldrLabel(message, 0); -} - -static inline bool messageHandler_HasWldr(const uint8_t *message) { - if (messageHandler_IsTCP(message)) { - uint16_t lbl = messageHandler_GetWldrLabel(message); - if (lbl != 0) { - return true; - } - } - return false; -} - -static inline uint32_t messageHandler_GetPathLabel(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return 0; - - uint32_t path_label; - int res = hicn_data_get_path_label((hicn_header_t *)message, &path_label); - if (res < 0) return 0; - return path_label; -} - -static inline void messageHandler_SetPathLabel(uint8_t *message, - uint32_t old_path_label, - uint32_t new_path_label) { - if (!messageHandler_IsTCP(message)) return; - - hicn_data_set_path_label((hicn_header_t *)message, new_path_label); - - messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&old_path_label, - (uint16_t *)&new_path_label, 2); -} - -static inline void messageHandler_UpdatePathLabel(uint8_t *message, - uint8_t outFace) { - if (!messageHandler_IsTCP(message)) return; - - uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); - uint8_t pl_old_8bit = (uint8_t)(pl_old_32bit >> 24UL); - uint32_t pl_new_32bit = - (uint32_t)((((pl_old_8bit << 1) | (pl_old_8bit >> 7)) ^ outFace) << 24UL); - - messageHandler_SetPathLabel(message, pl_old_32bit, pl_new_32bit); -} - -static inline void messageHandler_ResetPathLabel(uint8_t *message) { - messageHandler_SetPathLabel(message, messageHandler_GetPathLabel(message), 0); -} - -static inline uint16_t messageHandler_GetInterestLifetime( - const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return 0; - - hicn_lifetime_t lifetime; - int res = hicn_interest_get_lifetime((hicn_header_t *)message, &lifetime); - if (res < 0) return 0; - return lifetime; -} - -static inline bool messageHandler_HasInterestLifetime(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return false; - - if (messageHandler_GetInterestLifetime(message) == 0) return false; - return true; -} - -static inline uint32_t messageHandler_GetContentExpiryTime( - const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return 0; - - uint32_t expirationTime; - int res = - hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); - if (res < 0) return 0; - return expirationTime; -} - -static inline bool messageHandler_HasContentExpiryTime(const uint8_t *message) { - if (!messageHandler_IsTCP(message)) return 0; - - uint32_t expirationTime; - int res = - hicn_data_get_expiry_time((hicn_header_t *)message, &expirationTime); - if (res < 0) return false; - - if (expirationTime == HICN_MAX_LIFETIME) return false; - - return true; -} - -static inline void *messageHandler_GetSource(const uint8_t *message) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return &H6(message).saddr; - break; - case IPv4_TYPE: - return &H4(message).saddr; - break; - default: - return NULL; - } -} - -static inline void *messageHandler_GetDestination(const uint8_t *message) { - switch (messageHandler_GetIPPacketType(message)) { - case IPv6_TYPE: - return &H6(message).daddr; - break; - case IPv4_TYPE: - return &H4(message).daddr; - break; - default: - return NULL; - } -} - -static inline void messageHandler_SetSource_IPv6(uint8_t *message, - struct in6_addr *address) { - if (messageHandler_IsTCP(message)) { - uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); - messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 8); - } - H6(message).saddr.as_in6addr = *address; -} - -static inline void messageHandler_SetDestination_IPv6( - uint8_t *message, struct in6_addr *address) { - if (messageHandler_IsTCP(message)) { - uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); - messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 8); - } - H6(message).daddr.as_in6addr = *address; -} - -static inline void messageHandler_SetSource_IPv4(uint8_t *message, - uint32_t *address) { - // update tcp checksum - uint16_t *old_src = (uint16_t *)messageHandler_GetSource(message); - if (messageHandler_IsTCP(message)) { - messageHandler_UpdateTCPCheckSum(message, old_src, (uint16_t *)address, 2); - } - // update IPv4 cheksum - // the IPv4 checksum is not part of the psudo header for TCP checksum - // calculation we can update them separetelly - messageHandler_UpdateIPv4CheckSum(message, old_src, (uint16_t *)address, 2); - - H4(message).saddr.as_u32 = *address; -} - -static inline void messageHandler_SetDestination_IPv4(uint8_t *message, - uint32_t *address) { - uint16_t *old_dst = (uint16_t *)messageHandler_GetDestination(message); - if (messageHandler_IsTCP(message)) { - messageHandler_UpdateTCPCheckSum(message, old_dst, (uint16_t *)address, 2); - } - messageHandler_UpdateIPv4CheckSum(message, old_dst, (uint16_t *)address, 2); - H4(message).daddr.as_u32 = *address; -} - -static inline void messageHandler_SetWldrNotification(uint8_t *notification, - uint8_t *original, - uint16_t expected, - uint16_t received) { - hicn_header_t *h = (hicn_header_t *)notification; - switch (messageHandler_GetIPPacketType(original)) { - case IPv6_TYPE: { - *h = (hicn_header_t){.v6 = { - .ip = - { - .version_class_flow = htonl( - (IPV6_DEFAULT_VERSION << 28) | - (IPV6_DEFAULT_TRAFFIC_CLASS << 20) | - (IPV6_DEFAULT_FLOW_LABEL & 0xfffff)), - .len = htons(ICMP_HDRLEN), - .nxt = IPPROTO_ICMPV6, - .hlim = 5, - }, - .wldr = - { - .type = ICMP_WLDR_TYPE, - .code = ICMP_WLDR_CODE, - .expected_lbl = htons(expected), - .received_lbl = htons(received), - }, - }}; - messageHandler_SetSource_IPv6( - notification, - (struct in6_addr *)messageHandler_GetDestination(original)); - messageHandler_SetDestination_IPv6( - notification, (struct in6_addr *)messageHandler_GetSource(original)); - break; - } - case IPv4_TYPE: { - break; - } - default: - break; - } -} - -static inline uint8_t * messageHandler_CreateProbePacket(hicn_format_t format, - uint32_t probe_lifetime){ - size_t header_length; - hicn_packet_get_header_length_from_format(format, &header_length); - - uint8_t *pkt = parcMemory_AllocateAndClear(header_length); - - hicn_packet_init_header(format, (hicn_header_t *) pkt); - - hicn_packet_set_dst_port(format, (hicn_header_t *) pkt, BFD_PORT); - hicn_interest_set_lifetime ((hicn_header_t *) pkt, probe_lifetime); - - return pkt; -} - -static inline void messageHandler_CreateProbeReply(uint8_t * probe, - hicn_format_t format){ - - hicn_name_t probe_name; - hicn_interest_get_name (format, - (const hicn_header_t *) probe, &probe_name); - ip_address_t probe_locator; - hicn_interest_get_locator (format, - (const hicn_header_t *) probe, &probe_locator); - - uint16_t src_prt; - uint16_t dst_prt; - hicn_packet_get_src_port(format, (const hicn_header_t *) probe, &src_prt); - hicn_packet_get_dst_port(format, (const hicn_header_t *) probe, &dst_prt); - hicn_packet_set_src_port(format, (hicn_header_t *) probe, dst_prt); - hicn_packet_set_dst_port(format, (hicn_header_t *) probe, src_prt); - - hicn_data_set_name (format, (hicn_header_t *) probe, &probe_name); - hicn_data_set_locator (format, (hicn_header_t *) probe, &probe_locator); - hicn_data_set_expiry_time ((hicn_header_t *) probe, 0); -} - -static inline hicn_name_t * messageHandler_CreateProbeName(const ip_prefix_t *address){ - hicn_name_t * name = parcMemory_AllocateAndClear(sizeof(hicn_name_t)); - hicn_name_create_from_ip_prefix(address, 0, name); - return name; -} - -static inline void messageHandler_SetProbeName(uint8_t * probe, hicn_format_t format, - hicn_name_t * name, uint32_t seq){ - hicn_name_set_seq_number (name, seq); - hicn_interest_set_name(format, (hicn_header_t *) probe, name); -} - -static inline bool messageHandler_IsAProbe(const uint8_t *packet){ - uint16_t src_prt; - uint16_t dst_prt; - hicn_packet_get_src_port (HF_INET6_TCP, (const hicn_header_t *) packet, &src_prt); - hicn_packet_get_dst_port (HF_INET6_TCP, (const hicn_header_t *) packet, &dst_prt); - - if(dst_prt == BFD_PORT){ - //interest probe - return true; - } - - if(src_prt == BFD_PORT){ - //data (could be a probe) - uint32_t expiry_time; - hicn_data_get_expiry_time ((const hicn_header_t *) packet, &expiry_time); - if(expiry_time == 0){ - //this is a probe - return true; - } - } - - return false; -} - -#endif // Metis_metis_MessageHandler diff --git a/hicn-light/src/hicn/core/msgbuf.c b/hicn-light/src/hicn/core/msgbuf.c new file mode 100644 index 000000000..c58f7a7dc --- /dev/null +++ b/hicn-light/src/hicn/core/msgbuf.c @@ -0,0 +1,55 @@ +/* + * 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 msgbuf.c + * \brief Implementation hICN message buffer + */ + +#include "msgbuf.h" +#include "../strategies/probe_generator.h" + +int msgbuf_initialize(msgbuf_t *msgbuf) { + /* + * We define the format and the storage area of the packet buffer we + * manipulate + */ + hicn_packet_buffer_t *pkbuf = msgbuf_get_pkbuf(msgbuf); + hicn_packet_set_buffer(pkbuf, msgbuf->packet, MTU, 0); + hicn_packet_init_header(pkbuf, 0); + return 0; +} + +int msgbuf_initialize_from_packet(msgbuf_t *msgbuf) { + hicn_packet_set_buffer(msgbuf_get_pkbuf(msgbuf), msgbuf->packet, MTU, + msgbuf_get_len(msgbuf)); + return 0; +} + +bool msgbuf_is_command(const msgbuf_t *msgbuf) { + return (*msgbuf->packet == REQUEST_LIGHT); +} + +bool msgbuf_is_probe(const msgbuf_t *msgbuf) { + hicn_name_t name; + hicn_name_suffix_t suffix; + + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA); + + const hicn_packet_buffer_t *pkbuf = msgbuf_get_pkbuf(msgbuf); + hicn_data_get_name(pkbuf, &name); + suffix = hicn_name_get_suffix(&name); + return (suffix >= MIN_PROBE_SUFFIX && suffix <= MAX_PROBE_SUFFIX); +} diff --git a/hicn-light/src/hicn/core/msgbuf.h b/hicn-light/src/hicn/core/msgbuf.h new file mode 100644 index 000000000..7a35929f4 --- /dev/null +++ b/hicn-light/src/hicn/core/msgbuf.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2021-2023 Cisco and/or its affiliates. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * \file msgbuf.h + * \brief hICN message buffer + */ + +#ifndef HICNLIGHT_MSGBUF +#define HICNLIGHT_MSGBUF + +#include <hicn/name.h> +#include "ticks.h" +#include <hicn/hicn.h> +#include <hicn/ctrl/hicn-light.h> + +#define MTU 1500 +#define INVALID_MSGBUF_ID ~0ul + +#define msgbuf_id_is_valid(msgbuf_id) \ + ((unsigned long)msgbuf_id != INVALID_MSGBUF_ID) + +typedef struct { + hicn_packet_buffer_t pkbuf; + unsigned connection_id; // ingress + Ticks recv_ts; // timestamp + unsigned refs; // refcount + unsigned path_label; // original path label of the received message. used + // as a base for the path label computation when the + // same data packet needs to be forwarded on multiple + // face. + + // XXX Cache storage + union { + /* Interest or data packet */ + struct { + hicn_name_t name; + u32 name_hash; // XXX should be always populate when name is assigned + } id; + /* Command packet */ + struct { + command_type_t type; + } command; + }; + uint8_t packet[MTU]; +} msgbuf_t; + +int msgbuf_initialize(msgbuf_t *msgbuf); +int msgbuf_initialize_from_packet(msgbuf_t *msgbuf); + +#define msgbuf_get_pkbuf(M) (&(M)->pkbuf) + +static inline hicn_packet_type_t msgbuf_get_type(const msgbuf_t *msgbuf) { + return hicn_packet_get_type(msgbuf_get_pkbuf(msgbuf)); +} + +static inline void msgbuf_set_type(msgbuf_t *msgbuf, hicn_packet_type_t type) { + hicn_packet_set_type(msgbuf_get_pkbuf(msgbuf), type); +} + +static inline const hicn_name_t *msgbuf_get_name(const msgbuf_t *msgbuf) { + hicn_packet_type_t type = msgbuf_get_type(msgbuf); + assert(type == HICN_PACKET_TYPE_INTEREST || type == HICN_PACKET_TYPE_DATA); + (void)type; + + return &msgbuf->id.name; +} + +#define msgbuf_get_connection_id(M) ((M)->connection_id) +#define msgbuf_set_connection_id(M, ID) (M)->connection_id = (ID) +#define msgbuf_get_packet(M) ((M)->packet) +#define msgbuf_get_command_type(M) ((M)->command.type) +#if WITH_WLDR +#define msgbuf_has_wldr(M) (messageHandler_HasWldr((M)->packet)) +#endif + +static inline void msgbuf_set_name(msgbuf_t *msgbuf, const hicn_name_t *name) { + msgbuf->id.name = *name; +} + +static inline size_t msgbuf_get_len(const msgbuf_t *msgbuf) { + return hicn_packet_get_len(msgbuf_get_pkbuf(msgbuf)); +} + +static inline void msgbuf_set_len(msgbuf_t *msgbuf, size_t len) { + int rc = hicn_packet_set_len(msgbuf_get_pkbuf(msgbuf), len); + assert(rc == HICN_LIB_ERROR_NONE); // XXX + _unused(rc); +} + +static inline u32 msgbuf_get_name_hash(const msgbuf_t *msgbuf) { + hicn_packet_type_t type = msgbuf_get_type(msgbuf); + assert(type == HICN_PACKET_TYPE_INTEREST || type == HICN_PACKET_TYPE_DATA); + _unused(type); + return msgbuf->id.name_hash; +} + +// Lifetimes/expiry times in milliseconds +static inline u32 msgbuf_get_interest_lifetime(const msgbuf_t *msgbuf) { + u32 lifetime; + int rc = hicn_interest_get_lifetime(msgbuf_get_pkbuf(msgbuf), &lifetime); + if (rc != HICN_LIB_ERROR_NONE) return 0; + return lifetime; +} + +//#define msgbuf_get_lifetime(M) +// (NSEC_TO_TICKS(messageHandler_GetInterestLifetime((M)->packet) * +// 1000000ULL)) +#define msgbuf_get_lifetime msgbuf_get_interest_lifetime + +static inline bool msgbuf_set_interest_lifetime(msgbuf_t *msgbuf, + u32 lifetime) { + int rc = hicn_interest_set_lifetime(msgbuf_get_pkbuf(msgbuf), lifetime); + return (rc == HICN_LIB_ERROR_NONE); +} + +static inline u32 msgbuf_get_data_expiry_time(const msgbuf_t *msgbuf) { + u32 lifetime; + int rc = hicn_data_get_expiry_time(msgbuf_get_pkbuf(msgbuf), &lifetime); + if (rc != HICN_LIB_ERROR_NONE) return 0; + return lifetime; +} + +static inline bool msgbuf_set_data_expiry_time(msgbuf_t *msgbuf, u32 lifetime) { + int rc = hicn_data_set_expiry_time(msgbuf_get_pkbuf(msgbuf), lifetime); + return (rc == HICN_LIB_ERROR_NONE); +} + +/* Path label */ + +static inline void msgbuf_init_pathlabel(msgbuf_t *msgbuf) { + hicn_path_label_t pl; + int rc = hicn_data_get_path_label(msgbuf_get_pkbuf(msgbuf), &pl); + assert(rc == HICN_LIB_ERROR_NONE); + _unused(rc); + msgbuf->path_label = pl; +} + +static inline int msgbuf_get_path_label(const msgbuf_t *msgbuf, + hicn_path_label_t *pl) { + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA); + return hicn_data_get_path_label(msgbuf_get_pkbuf(msgbuf), pl); +} + +static inline int msgbuf_set_path_label(msgbuf_t *msgbuf, + hicn_path_label_t pl) { + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA); + return hicn_data_set_path_label(msgbuf_get_pkbuf(msgbuf), pl); +} + +static inline int msgbuf_update_pathlabel(msgbuf_t *msgbuf, + hicn_faceid_t outface) { + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA); + + hicn_path_label_t pl, newpl; + if (msgbuf_get_path_label(msgbuf, &pl) < 0) return -1; + + update_path_label(pl, outface, &newpl); + + return msgbuf_set_path_label(msgbuf, newpl); +} + +static inline void msgbuf_reset_pathlabel(msgbuf_t *msgbuf) { + msgbuf->path_label = 0; + hicn_data_set_path_label(msgbuf_get_pkbuf(msgbuf), 0); + // ERROR ? +} + +static inline void msgbuf_modify_suffix(msgbuf_t *msgbuf, uint32_t new_suffix) { + hicn_name_t name; + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST); + hicn_interest_get_name(msgbuf_get_pkbuf(msgbuf), &name); + hicn_name_set_suffix(&name, new_suffix); + hicn_interest_set_name(msgbuf_get_pkbuf(msgbuf), &name); +} + +bool msgbuf_is_command(const msgbuf_t *msgbuf); +bool msgbuf_is_probe(const msgbuf_t *msgbuf); + +/* WLDR */ + +#if 0 +#define msgbuf_reset_wldr_label(M) (messageHandler_ResetWldrLabel((M)->packet)) +#define msgbuf_get_wldr_label(M) (messageHandler_GetWldrLabel((M)->packet)) +#define msgbuf_get_wldr_expected_label(M) \ + (messageHandler_GetWldrExpectedLabel((M)->packet)) +#define msgbuf_get_wldr_last_received(M) \ + (messageHandler_GetWldrLastReceived((M)->packet)) +#define msgbuf_set_wldr_label(M, label) \ + (messageHandler_GetWldrLabel((M)->packet, label)) +#endif + +#endif /* HICNLIGHT_MSGBUF */ diff --git a/hicn-light/src/hicn/core/msgbuf_pool.c b/hicn-light/src/hicn/core/msgbuf_pool.c new file mode 100644 index 000000000..892bd59a1 --- /dev/null +++ b/hicn-light/src/hicn/core/msgbuf_pool.c @@ -0,0 +1,123 @@ +/* + * 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 msgbuf_pool.c + * @brief Implementation of hICN packet pool. + */ + +#include <hicn/util/pool.h> +#include <hicn/util/log.h> +#include "msgbuf_pool.h" + +msgbuf_pool_t *_msgbuf_pool_create(size_t init_size, size_t max_size) { + msgbuf_pool_t *msgbuf_pool = malloc(sizeof(msgbuf_pool_t)); + + if (init_size == 0) init_size = PACKET_POOL_DEFAULT_INIT_SIZE; + + pool_init(msgbuf_pool->buffers, init_size, 0); + + return msgbuf_pool; +} + +void msgbuf_pool_free(msgbuf_pool_t *msgbuf_pool) { + pool_free(msgbuf_pool->buffers); + free(msgbuf_pool); +} + +off_t msgbuf_pool_get(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf) { + off_t id = pool_get(msgbuf_pool->buffers, *msgbuf); + (*msgbuf)->refs = 0; + return id; +} + +void msgbuf_pool_put(msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf) { + pool_put(msgbuf_pool->buffers, msgbuf); +} + +int msgbuf_pool_getn(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf, size_t n) { + // CAVEAT: Resize at the beginning otherwise the resize can be + // triggered by an intermediate msgbuf_pool_put, making the + // buffers previously retrieved invalid + uint64_t remaining_pool_space = + pool_get_free_indices_size(msgbuf_pool->buffers); + while (remaining_pool_space < n) { + _pool_resize((void **)&(msgbuf_pool->buffers), sizeof(msgbuf_t)); + + remaining_pool_space = pool_get_free_indices_size(msgbuf_pool->buffers); + } + + for (unsigned i = 0; i < n; i++) { + // If not able to get the msgbuf + if (msgbuf_pool_get(msgbuf_pool, &msgbuf[i]) < 0) { + // Release all the msgbufs retrieved so far + for (unsigned j = 0; j < i; j++) { + msgbuf_pool_put(msgbuf_pool, msgbuf[j]); + } + return -1; + } + } + return 0; +} + +off_t msgbuf_pool_get_id(msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf) { + return msgbuf - msgbuf_pool->buffers; +} + +msgbuf_t *msgbuf_pool_at(const msgbuf_pool_t *msgbuf_pool, off_t id) { + assert(msgbuf_id_is_valid(id)); + return msgbuf_pool->buffers + id; +} + +void msgbuf_pool_acquire(msgbuf_t *msgbuf) { msgbuf->refs++; }; + +void msgbuf_pool_release(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf_ptr) { + msgbuf_t *msgbuf = *msgbuf_ptr; + assert(msgbuf->refs > 0); + msgbuf->refs--; + + if (msgbuf->refs == 0) { + WITH_TRACE({ + off_t msgbuf_id = msgbuf_pool_get_id(msgbuf_pool, msgbuf); + if (msgbuf_get_type(msgbuf) != HICN_PACKET_TYPE_INTEREST && + msgbuf_get_type(msgbuf) != HICN_PACKET_TYPE_DATA) { + TRACE("Msgbuf %d (%p) - put to msgbuf pool", msgbuf_id, msgbuf); + } else { + char buf[MAXSZ_HICN_NAME]; + int rc = + hicn_name_snprintf(buf, MAXSZ_HICN_NAME, msgbuf_get_name(msgbuf)); + if (rc < 0 || rc >= MAXSZ_HICN_NAME) + snprintf(buf, MAXSZ_HICN_NAME, "%s", "(error)"); + const char *msgbuf_type_str = + msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST ? "interest" + : "data"; + TRACE("Msgbuf %d (%p) - %s (%s) put to msgbuf pool", msgbuf_id, msgbuf, + buf, msgbuf_type_str); + } + }) + + msgbuf_pool_put(msgbuf_pool, msgbuf); + *msgbuf_ptr = NULL; + } +}; + +off_t msgbuf_pool_clone(msgbuf_pool_t *msgbuf_pool, msgbuf_t **new_msgbuf, + off_t orginal_msg_id) { + msgbuf_t *original_msgbuf = msgbuf_pool_at(msgbuf_pool, orginal_msg_id); + off_t offset = pool_get(msgbuf_pool->buffers, *new_msgbuf); + memcpy(*new_msgbuf, original_msgbuf, sizeof(msgbuf_t)); + (*new_msgbuf)->refs = 0; + return offset; +} diff --git a/hicn-light/src/hicn/core/msgbuf_pool.h b/hicn-light/src/hicn/core/msgbuf_pool.h new file mode 100644 index 000000000..b8a15fd84 --- /dev/null +++ b/hicn-light/src/hicn/core/msgbuf_pool.h @@ -0,0 +1,157 @@ +/* + * 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 msgbuf_pool.h + * @brief hICN msgbuf pool. + * + * The msgbuf pool is used to store packet payloads while the packets are in + * transit, as well as holding them into the packet cache (PIT, CSS), WLDR, + * mapme, etc. + * + * Control packets might receive a special treatment in that they are eventually + * transformed into a ack/nack, but this should not affect any part of this + * design. + * + * Do we need a reference count, or simply a lock ? + * What about weak references ? + * We need to be sure that a pool element is never referenced ever again after + * it is deleted from the pool as its ID might be reaffected. + * + * It might even be better to store references to msgbuf's as they might hold + * additional information of interest about the packet... a bit like a skbuff in + * linux. Is this relevant for the packet cache ? + */ + +#ifndef HICNLIGHT_MSGBUF_POOL_H +#define HICNLIGHT_MSGBUF_POOL_H + +#include "msgbuf.h" + +#define MTU 1500 +#define PACKET_POOL_DEFAULT_INIT_SIZE 1024 + +typedef struct { + msgbuf_t *buffers; +} msgbuf_pool_t; + +/** + * @brief Allocate and initialize a msgbuf pool structure (helper). + * + * @param[in] init_size Number of buffers that can be stored in msgbuf pool. + * @param[in] max_size Maximum size. + * @return msgbuf_pool_t* Pointer to the msgbuf pool created. + * + * @note + * - 0 for init size means a default value (of 1024) + * - 0 for max_size means no limit + */ +msgbuf_pool_t *_msgbuf_pool_create(size_t init_size, size_t max_size); + +/** + * @brief Allocate and initialize a msgbuf pool data structure. + * + * @return msgbuf_pool_t* Pointer to the msgbuf pool created. + */ +#define msgbuf_pool_create() _msgbuf_pool_create(0, 0) + +/** + * @brief Free a msgbuf pool data structure. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to free. + */ +void msgbuf_pool_free(msgbuf_pool_t *msgbuf_pool); + +/** + * @brief Get a free msgbuf from the msgbuf pool data structure. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use. + * @param[in, out] msgbuf Empty msgbuf that will be used to return the + * allocated one from the msgbuf pool. + * @return off_t ID of the msgbuf requested. + */ +off_t msgbuf_pool_get(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf); + +/** + * @brief Release a msgbuf previously obtained, making it available to the + * msgbuf pool. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use. + * @param[in] msgbuf Pointer to the msgbuf to release. + */ +void msgbuf_pool_put(msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf); + +/** + * @brief Get multiple free msgbufs from the msgbuf pool data structure. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use. + * @param[in, out] msgbuf Pointer to the first empty msgbuf that will be used to + * allocate the msgbufs. + * @param[in] n Number of msgbufs requested. + * @retval 0 Success. + * @retval -1 Error. + */ +int msgbuf_pool_getn(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf, size_t n); + +/** + * @brief Get the ID corresponding to the msgbuf requested. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use. + * @param[in] msgbuf Pointer to the msgbuf to retrieve the ID for. + * @return off_t ID of the msgbuf requested. + */ +off_t msgbuf_pool_get_id(msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf); + +/** + * @brief Get the msgbuf corresponding to the ID requested. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use. + * @param[in] id Index of the msgbuf to retrieve. + * @return msgbuf_t* Pointer to the msgbuf corresponding to the ID requested. + */ +msgbuf_t *msgbuf_pool_at(const msgbuf_pool_t *msgbuf_pool, off_t id); + +/** + * @brief Acquire a buffer (by increasing its reference count). + * + * @param[in] msgbuf Pointer to the msgbuf to acquire + */ +void msgbuf_pool_acquire(msgbuf_t *msgbuf); + +/** + * @brief Release a buffer. The buffer is also put back into the msgbuf + * pool if everyone who acquired it has released its possession. + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in, out] msgbuf Pointer that holds the pointer to the msgbuf + * to release; the double indirection is used to set the msgbuf pointer + * to NULL in case it is put into the msgbuf pool. + */ +void msgbuf_pool_release(msgbuf_pool_t *msgbuf_pool, msgbuf_t **msgbuf_ptr); + +/** + * @brief Copy the original msgbuf in new msgbuf taken from the pool. The ref + * count on new msgbuf is set to 0 + * + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in,out] new__msgbuf Pointer that holds the replicatate msgbuf + * @param[in] original_msgbuf id. use id instead of the pointer becasue the + * pointer may becose invalid if the msgbuf_pool requires a resize + * @return off_t ID of the msgbuf requested. + */ +off_t msgbuf_pool_clone(msgbuf_pool_t *msgbuf_pool, msgbuf_t **new_msgbuf, + off_t orginal_msg_id); + +#endif /* HICNLIGHT_MSGBUF_POOL_H */ diff --git a/hicn-light/src/hicn/core/name.c b/hicn-light/src/hicn/core/name.c deleted file mode 100644 index b4a5e8d1b..000000000 --- a/hicn-light/src/hicn/core/name.c +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <limits.h> -#include <hicn/hicn-light/config.h> -#include <stdbool.h> -#include <stdio.h> -#include <string.h> - -#include <parc/algol/parc_BufferComposer.h> -#include <parc/algol/parc_Hash.h> -#include <parc/algol/parc_Memory.h> - -#include <hicn/core/messageHandler.h> -#include <hicn/core/name.h> - -#include <parc/algol/parc_Hash.h> - -#include <parc/assert/parc_Assert.h> - -#define IPv6_TYPE 6 -#define IPv4_TYPE 4 - -// assumption: the IPv6 address is the name, the TCP segment number is the ICN -// segment - -struct name { - NameBitvector *content_name; - uint32_t segment; - uint32_t name_hash; - // the refcount is shared between all copies - unsigned *refCountPtr; -}; - -// ===================================================== - -static unsigned _getRefCount(const Name *name) { return *name->refCountPtr; } - -static void _incrementRefCount(Name *name) { - parcAssertTrue(*name->refCountPtr > 0, - "Illegal State: Trying to increment a 0 refcount!"); - (*name->refCountPtr)++; -} - -static void _decrementRefCount(Name *name) { - parcAssertTrue(*name->refCountPtr > 0, - "Illegal State: Trying to decrement a 0 refcount!"); - (*name->refCountPtr)--; -} - -static uint32_t _computeHash(Name *name) { - parcAssertNotNull(name, "Parameter must be non-null pointer"); - - uint32_t hash1 = nameBitvector_GetHash32(name->content_name); - return parcHash32_Data_Cumulative((const uint8_t *)&name->segment, 4, hash1); -} - -// ============================================================================ - -Name *name_CreateFromPacket(const uint8_t *packet, MessagePacketType type) { - Name *name = parcMemory_AllocateAndClear(sizeof(Name)); - parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Name)); - - if (messageHandler_GetIPPacketType(packet) == IPv6_TYPE) { - if (type == MessagePacketType_Interest) { - name->content_name = nameBitvector_CreateFromIn6Addr( - (struct in6_addr *)messageHandler_GetDestination(packet), 128); - } else if (type == MessagePacketType_ContentObject) { - name->content_name = nameBitvector_CreateFromIn6Addr( - (struct in6_addr *)messageHandler_GetSource(packet), 128); - } else { - parcMemory_Deallocate((void **)&name); - return NULL; - } - } else if (messageHandler_GetIPPacketType(packet) == IPv4_TYPE) { - if (type == MessagePacketType_Interest) { - name->content_name = nameBitvector_CreateFromInAddr( - *((uint32_t *)messageHandler_GetDestination(packet)), 32); - } else if (type == MessagePacketType_ContentObject) { - name->content_name = nameBitvector_CreateFromInAddr( - *((uint32_t *)messageHandler_GetSource(packet)), 32); - } else { - parcMemory_Deallocate((void **)&name); - return NULL; - } - } else { - printf("Error: unknown message type\n"); - parcMemory_Deallocate((void **)&name); - return NULL; - } - - name->segment = messageHandler_GetSegment(packet); - name->name_hash = _computeHash(name); - - name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); - parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", - sizeof(unsigned)); - *name->refCountPtr = 1; - return name; -} - -Name *name_CreateFromAddress(address_type addressType, ip_address_t addr, - uint8_t len) { - Name *name = parcMemory_AllocateAndClear(sizeof(Name)); - parcAssertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Name)); - if (addressType == ADDR_INET) { - name->content_name = nameBitvector_CreateFromInAddr(addr.v4.as_u32, len); - } else if (addressType == ADDR_INET6) { - name->content_name = nameBitvector_CreateFromIn6Addr(&addr.v6.as_in6addr, len); - } else { - parcTrapNotImplemented("Unkown packet type"); - } - - name->segment = 0; - name->name_hash = _computeHash(name); - - name->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); - parcAssertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", - sizeof(unsigned)); - *name->refCountPtr = 1; - - return name; -} - -void name_Release(Name **namePtr) { - parcAssertNotNull(namePtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*namePtr, "Parameter must dereference to non-null pointer"); - - Name *name = *namePtr; - _decrementRefCount(name); - if (_getRefCount(name) == 0) { - parcMemory_Deallocate((void **)&(name->refCountPtr)); - nameBitvector_Destroy(&(name->content_name)); - } - parcMemory_Deallocate((void **)&name); - *namePtr = NULL; -} - -Name *name_Acquire(const Name *original) { - parcAssertNotNull(original, "Parameter must be non-null"); - Name *copy = parcMemory_AllocateAndClear(sizeof(Name)); - parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Name)); - - memcpy(copy, original, sizeof(Name)); - _incrementRefCount(copy); - - return copy; -} - -Name *name_Copy(const Name *original) { - parcAssertNotNull(original, "Parameter must be non-null"); - Name *copy = parcMemory_AllocateAndClear(sizeof(Name)); - parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Name)); - - copy->content_name = nameBitvector_Copy(original->content_name); - copy->segment = original->segment; - copy->name_hash = original->name_hash; - - copy->refCountPtr = parcMemory_Allocate(sizeof(unsigned)); - parcAssertNotNull(copy->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", - sizeof(unsigned)); - *copy->refCountPtr = 1; - - return copy; -} - -uint32_t name_HashCode(const Name *name) { - parcAssertNotNull(name, "Parameter must be non-null"); - return name->name_hash; -} - -NameBitvector *name_GetContentName(const Name *name) { - parcAssertNotNull(name, "Parameter must be non-null"); - return name->content_name; -} - -bool name_Equals(const Name *a, const Name *b) { - parcAssertNotNull(a, "Parameter a must be non-null"); - parcAssertNotNull(b, "Parameter b must be non-null"); - - /* BEGIN: Workaround for HICN-400 */ - if ((!a->content_name) || (!b->content_name)) - return false; - /* END: Workaround for HICN-400 */ - - if ((nameBitvector_Equals(a->content_name, b->content_name) && - a->segment == b->segment)) - return true; - return false; -} - -int name_Compare(const Name *a, const Name *b) { - parcAssertNotNull(a, "Parameter a must be non-null"); - parcAssertNotNull(b, "Parameter b must be non-null"); - - if (a == NULL && b == NULL) { - return 0; - } - if (a == NULL) { - return -1; - } - if (b == NULL) { - return +1; - } - - int res = nameBitvector_Compare(a->content_name, b->content_name); - - if (res != 0) { - return res; - } else { - if (a->segment < b->segment) { - return -1; - } else if (a->segment > b->segment) { - return +1; - } else { - return 0; - } - } -} - -char *name_ToString(const Name *name) { - char *output = malloc(128); - - Address *packetAddr = nameBitvector_ToAddress(name_GetContentName(name)); - - char * address_str = addressToString(packetAddr); - sprintf(output, "name: %s seq: %u", address_str, name->segment); - parcMemory_Deallocate((void **)&address_str); - - addressDestroy(&packetAddr); - - return output; -} - -void name_setLen(Name *name, uint8_t len) { - nameBitvector_setLen(name->content_name, len); - name->name_hash = _computeHash(name); -} - -#ifdef WITH_POLICY -uint32_t name_GetSuffix(const Name * name) { - return name->segment; -} - -uint8_t name_GetLen(const Name * name) { - return nameBitvector_GetLength(name->content_name); -} -#endif /* WITH_POLICY */ diff --git a/hicn-light/src/hicn/core/name.h b/hicn-light/src/hicn/core/name.h deleted file mode 100644 index db9438150..000000000 --- a/hicn-light/src/hicn/core/name.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef name_h -#define name_h - -#include <stdbool.h> -#include <stdlib.h> - -#include <hicn/core/messagePacketType.h> -#include <hicn/core/nameBitvector.h> -#include <hicn/utils/address.h> - -#include <hicn/utils/commands.h> - -struct name; -typedef struct name Name; - -/** - * Creates a name from packet - * - */ -Name *name_CreateFromPacket(const uint8_t *memory, MessagePacketType type); - -/** - * Releases one reference count, and frees memory after last reference - */ -void name_Release(Name **namePtr); - -/** - * Acquires a reference to the name so that a reference count increments. - * Notice however that this * function is used only when a new fib entry is - * created (mostly configuration time) probably here performance are not - * critical. - */ -Name *name_Acquire(const Name *original); - -/** - * returns a copy of the name - */ -Name *name_Copy(const Name *original); - -/** - * A hash value for use in hash tables - * - */ -uint32_t name_HashCode(const Name *name); - -/** - * Returns the content name without the segment value - * - */ -NameBitvector *name_GetContentName(const Name *name); - -/** - * Determine if two HicnName instances are equal. - */ -bool name_Equals(const Name *a, const Name *b); - -/** - * Compares two names and returns their ordering - * - */ -int name_Compare(const Name *a, const Name *b); - -/** - * return the name in string format (bitvector + segment number) - * - */ -char *name_ToString(const Name *name); - -/** - * @function message_setNameLen - * @abstract Sets a message name length - * @param [in] message - Interest message - * @param [in] len - Name length - */ -void name_setLen(Name *name, uint8_t len); - -/** - * Creates a name from a Address - * - */ -Name *name_CreateFromAddress(address_type addressType, ip_address_t addr, - uint8_t len); - -#ifdef WITH_POLICY -uint32_t name_GetSuffix(const Name * name); -uint8_t name_GetLen(const Name * name); -#endif /* WITH_POLICY */ - -#endif // name_h diff --git a/hicn-light/src/hicn/core/nameBitvector.c b/hicn-light/src/hicn/core/nameBitvector.c deleted file mode 100644 index 653560750..000000000 --- a/hicn-light/src/hicn/core/nameBitvector.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> - -#include <parc/algol/parc_Memory.h> -#include <parc/assert/parc_Assert.h> - -#include <hicn/core/messageHandler.h> -#include <hicn/core/nameBitvector.h> - -#include <parc/algol/parc_Hash.h> - -#include <hicn/utils/commands.h> - -#define NAME_LEN 2 - -const uint64_t BV_SIZE = 64; -const uint64_t WIDTH = 128; -const uint64_t ONE = 0x1; - -// address b000:0000:0000:0001:c000:0000:0000:0001 is encodend as follow -// [bits[0] uint64_t ] [bits[1] unit64_t ] -// ^ ^ ^ ^ -// 63 0 127 64 -// [1000 0000 ... 0000 1101] [1000 0000 ... 0000 0011] //binary -// 1 b 1 c //hex - -struct name_bitvector { - uint64_t bits[NAME_LEN]; - uint8_t len; - uint8_t IPversion; -}; - -NameBitvector *nameBitvector_CreateFromInAddr(uint32_t addr, uint8_t len) { - NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); - parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(NameBitvector)); - - bitvector->bits[0] = 0; - bitvector->bits[1] = 0; - - uint8_t addr_1 = (addr & 0xff000000) >> 24; - uint8_t addr_2 = (addr & 0x00ff0000) >> 16; - uint8_t addr_3 = (addr & 0x0000ff00) >> 8; - uint8_t addr_4 = (addr & 0x000000ff); - - bitvector->bits[0] = (bitvector->bits[0] | addr_4) << 8; - bitvector->bits[0] = (bitvector->bits[0] | addr_3) << 8; - bitvector->bits[0] = (bitvector->bits[0] | addr_2) << 8; - bitvector->bits[0] = (bitvector->bits[0] | addr_1); - bitvector->bits[0] = bitvector->bits[0] << 32; - - bitvector->len = len; - - bitvector->IPversion = IPv4_TYPE; - - return bitvector; -} - -NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, - uint8_t len) { - parcAssertNotNull(addr, "addr cannot be null"); - - NameBitvector *bitvector = parcMemory_AllocateAndClear(sizeof(NameBitvector)); - parcAssertNotNull(bitvector, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(NameBitvector)); - - bitvector->bits[0] = 0; - bitvector->bits[1] = 0; - - for (int i = 0; i < 8; ++i) { - bitvector->bits[0] = (bitvector->bits[0] << 8) | addr->s6_addr[i]; - } - - for (int i = 8; i < 16; ++i) { - bitvector->bits[1] = (bitvector->bits[1] << 8) | addr->s6_addr[i]; - } - - bitvector->len = len; - - bitvector->IPversion = IPv6_TYPE; - - return bitvector; -} - -NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, - uint8_t len) { - parcAssertNotNull(prefix, "prefix cannot be null"); - - NameBitvector *bitvector = NULL; - switch (addressGetType(prefix)) { - case ADDR_INET: { - struct sockaddr_in addr; - addressGetInet(prefix, &addr); - bitvector = nameBitvector_CreateFromInAddr(addr.sin_addr.s_addr, len); - break; - } - case ADDR_INET6: { - struct sockaddr_in6 addr; - addressGetInet6(prefix, &addr); - bitvector = nameBitvector_CreateFromIn6Addr(&addr.sin6_addr, len); - break; - } - default: - parcTrapNotImplemented("Unkown packet type"); - break; - } - - return bitvector; -} - -NameBitvector *nameBitvector_Copy(const NameBitvector *original) { - parcAssertNotNull(original, "original cannot be null"); - - NameBitvector *copy = parcMemory_AllocateAndClear(sizeof(NameBitvector)); - parcAssertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(NameBitvector)); - - copy->bits[0] = original->bits[0]; - copy->bits[1] = original->bits[1]; - copy->len = original->len; - - return copy; -} - -void nameBitvector_Destroy(NameBitvector **bitvectorPtr) { - parcAssertNotNull(bitvectorPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*bitvectorPtr, - "Parameter must dereference to non-null pointer"); - - NameBitvector *bv = *bitvectorPtr; - parcMemory_Deallocate((void **)&(bv)); - *bitvectorPtr = NULL; -} - -uint8_t nameBitvector_GetLength(const NameBitvector *name) { return name->len; } - -uint32_t nameBitvector_GetHash32(const NameBitvector *name) { - return parcHash32_Data_Cumulative((const uint8_t *)name->bits, 16, 0); -} - -bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b) { - if (a->bits[0] == b->bits[0] && a->bits[1] == b->bits[1] && a->len == b->len) - return true; - return false; -} - -int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b) { - if (a == NULL && b == NULL) { - return 0; - } - if (a == NULL) { - return -1; - } - if (b == NULL) { - return +1; - } - - if (a->bits[0] < b->bits[0]) { - return -1; - } else if (a->bits[0] > b->bits[0]) { - return +1; - } else if (a->bits[1] < b->bits[1]) { - return -1; - } else if (a->bits[1] > b->bits[1]) { - return +1; - } else if (a->len < b->len) { - return -1; - } else if (a->len > b->len) { - return +1; - } else { - return 0; - } -} - -int nameBitvector_testBit(const NameBitvector *name, uint8_t pos, bool *bit) { - if(pos >= name->len || pos > (WIDTH -1)) - return -1; - - *bit = (name->bits[pos / BV_SIZE] & (ONE << ((BV_SIZE - 1) - (pos % BV_SIZE)))); - - return 0; -} - -uint64_t _diff_bit_log2(uint64_t val) { - // base 2 log of an uint64_t. This is the same as get the position of - // the highest bit set (or most significant bit set, MSB) - uint64_t result = 0; - - if (val & 0xFFFFFFFF00000000) { - val = val >> 32; - result = result | 32; - } - if (val & 0xFFFF0000) { - val = val >> 16; - result = result | 16; - } - if (val & 0xFF00) { - val = val >> 8; - result = result | 8; - } - if (val & 0xF0) { - val = val >> 4; - result = result | 4; - } - if (val & 0xC) { - val = val >> 2; - result = result | 2; - } - if (val & 0x2) { - val = val >> 1; - result = result | 1; - } - return result; -} - -uint32_t nameBitvector_lpm(const NameBitvector *a, - const NameBitvector *b) { - uint32_t limit; - uint32_t prefix_len; - if (a->len < b->len) - limit = a->len; - else - limit = b->len; - - uint64_t diff = a->bits[0] ^ b->bits[0]; - if(diff){ - prefix_len = (uint32_t)(BV_SIZE - (_diff_bit_log2(diff) + 1)); - //printf("if 1 diff = %lu plen = %d\n", diff, prefix_len); - }else{ - prefix_len = (uint32_t)BV_SIZE; - diff = a->bits[1] ^ b->bits[1]; - if(diff){ - prefix_len += (uint32_t)(BV_SIZE - (_diff_bit_log2(diff) + 1)); - //printf("if 2 diff = %lu plen = %d\n", diff, prefix_len); - }else{ - prefix_len += (uint32_t)BV_SIZE; - } - } - - if(prefix_len < limit) - return prefix_len; - return limit; -} - -void nameBitvector_clear(NameBitvector *a, uint8_t start_from){ - for(uint8_t pos = start_from; pos < WIDTH; pos++) - a->bits[pos / BV_SIZE] &= ~(ONE << ((BV_SIZE - 1) - (pos % BV_SIZE))); -} - -int nameBitvector_ToIPAddress(const NameBitvector *name, - ip_prefix_t *prefix) { - if (name->IPversion == IPv4_TYPE) { - struct in_addr *addr = (struct in_addr *)(&prefix->address.v4.buffer); - prefix->family = AF_INET; - prefix->len = IPV4_ADDR_LEN_BITS; - - uint32_t tmp_addr = name->bits[0] >> 32ULL; - uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; - uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; - uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; - uint8_t addr_4 = (tmp_addr & 0x000000ff); - - addr->s_addr = 0; - addr->s_addr = (addr->s_addr | addr_4) << 8; - addr->s_addr = (addr->s_addr | addr_3) << 8; - addr->s_addr = (addr->s_addr | addr_2) << 8; - addr->s_addr = (addr->s_addr | addr_1); - - } else { - struct in6_addr *addr = (struct in6_addr *)(&prefix->address.v6.buffer); - prefix->family = AF_INET6; - prefix->len = name->len; // IPV6_ADDR_LEN_BITS; - - for (int i = 0; i < 8; i++) { - addr->s6_addr[i] = (uint8_t)((name->bits[0] >> 8 * (7 - i)) & 0xFF); - } - - int x = 0; - for (int i = 8; i < 16; ++i) { - addr->s6_addr[i] = (uint8_t)((name->bits[1] >> 8 * (7 - x)) & 0xFF); - x++; - } - } - return true; -} - -void nameBitvector_setLen(NameBitvector *name, uint8_t len) { name->len = len; } - -Address *nameBitvector_ToAddress(const NameBitvector *name) { - if (name->IPversion == IPv4_TYPE) { - struct sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(1234); - - uint32_t tmp_addr = name->bits[0] >> 32ULL; - uint8_t addr_1 = (tmp_addr & 0xff000000) >> 24; - uint8_t addr_2 = (tmp_addr & 0x00ff0000) >> 16; - uint8_t addr_3 = (tmp_addr & 0x0000ff00) >> 8; - uint8_t addr_4 = (tmp_addr & 0x000000ff); - - addr.sin_addr.s_addr = 0; - addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_4) << 8; - addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_3) << 8; - addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_2) << 8; - addr.sin_addr.s_addr = (addr.sin_addr.s_addr | addr_1); - - Address *packetAddr = addressCreateFromInet(&addr); - - return packetAddr; - - } else { - struct sockaddr_in6 addr; - addr.sin6_family = AF_INET6; - addr.sin6_port = htons(1234); - addr.sin6_scope_id = 0; - addr.sin6_flowinfo = 0; - - for (int i = 0; i < 8; i++) { - addr.sin6_addr.s6_addr[i] = - (uint8_t)((name->bits[0] >> 8 * (7 - i)) & 0xFF); - } - - int x = 0; - for (int i = 8; i < 16; ++i) { - addr.sin6_addr.s6_addr[i] = - (uint8_t)((name->bits[1] >> 8 * (7 - x)) & 0xFF); - x++; - } - - Address *packetAddr = addressCreateFromInet6(&addr); - - return packetAddr; - } -} - -char *nameBitvector_ToString(const NameBitvector *name) { - char *output = malloc(WIDTH); - - Address *packetAddr = nameBitvector_ToAddress(name); - - char * str = addressToString(packetAddr); - sprintf(output, "prefix: %s len: %u", str, name->len); - parcMemory_Deallocate((void **)&str); - - addressDestroy(&packetAddr); - - return output; -} diff --git a/hicn-light/src/hicn/core/nameBitvector.h b/hicn-light/src/hicn/core/nameBitvector.h deleted file mode 100644 index 19944778c..000000000 --- a/hicn-light/src/hicn/core/nameBitvector.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef name_bitvector_h -#define name_bitvector_h - -#include <hicn/hicn.h> -#include <stdint.h> -#include <stdlib.h> - -#include <hicn/utils/address.h> - -struct name_bitvector; -typedef struct name_bitvector NameBitvector; - -NameBitvector *nameBitvector_CreateFromInAddr(uint32_t addr, uint8_t len); - -NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, - uint8_t len); - -NameBitvector *nameBitvector_Copy(const NameBitvector *original); - -void nameBitvector_Destroy(NameBitvector **bitvectorPtr); - -uint8_t nameBitvector_GetLength(const NameBitvector *name); - -uint32_t nameBitvector_GetHash32(const NameBitvector *name); - -bool nameBitvector_Equals(const NameBitvector *a, const NameBitvector *b); - -int nameBitvector_Compare(const NameBitvector *a, const NameBitvector *b); - -int nameBitvector_testBit(const NameBitvector *name, uint8_t pos, bool *bit); - -uint32_t nameBitvector_lpm(const NameBitvector *a, const NameBitvector *b); - -void nameBitvector_clear(NameBitvector *a, uint8_t start_from); - -int nameBitvector_ToIPAddress(const NameBitvector *name, ip_prefix_t *prefix); -void nameBitvector_setLen(NameBitvector *name, uint8_t len); - -Address *nameBitvector_ToAddress(const NameBitvector *name); - -char *nameBitvector_ToString(const NameBitvector *name); - -#endif // name_bitvector_h diff --git a/hicn-light/src/hicn/core/nexthops.c b/hicn-light/src/hicn/core/nexthops.c new file mode 100644 index 000000000..1a6096777 --- /dev/null +++ b/hicn-light/src/hicn/core/nexthops.c @@ -0,0 +1,155 @@ +/* + * 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 nexthops.c + * \brief Nexthops implementation + */ + +#include <hicn/util/hash.h> + +#include "nexthops.h" + +int nexthops_disable(nexthops_t *nexthops, off_t offset) { + if (offset >= nexthops->num_elts) return -1; + nexthops->flags |= (1 << offset); + nexthops->cur_elts--; + return 0; +} + +int nexthops_disable_all(nexthops_t *nexthops) { + nexthops->flags = ~0; + nexthops->cur_elts = 0; + return 0; +} + +void nexthops_reset(nexthops_t *nexthops) { + nexthops->flags = 0; + nexthops->cur_elts = nexthops->num_elts; +} + +off_t nexthops_add(nexthops_t *nexthops, nexthop_t nexthop) { + off_t id; + nexthops_enumerate(nexthops, i, n, { + if (n == nexthop) return i; + }); + id = nexthops->num_elts++; + nexthops->elts[id] = nexthop; + nexthops_reset(nexthops); + return id; +} + +off_t nexthops_remove(nexthops_t *nexthops, nexthop_t nexthop) { + nexthops_enumerate(nexthops, i, n, { + if (n == nexthop) { + nexthops->num_elts--; + nexthops->elts[i] = nexthops->elts[nexthops->num_elts]; + nexthops->state[i] = nexthops->state[nexthops->num_elts]; + nexthops_reset(nexthops); + return i; + } + }); + return INVALID_NEXTHOP; +} + +bool nexthops_contains(nexthops_t *nexthops, unsigned nexthop) { + nexthops_foreach(nexthops, n, { + if (n == nexthop) return true; + }); + return false; +} + +off_t nexthops_find(nexthops_t *nexthops, unsigned nexthop) { + nexthops_enumerate(nexthops, i, n, { + if (n == nexthop) return i; + }); + return INVALID_NEXTHOP; +} + +unsigned nexthops_get_one(nexthops_t *nexthops) { + nexthops_foreach(nexthops, n, { return n; }); + return INVALID_NEXTHOP; +} + +int nexthops_select(nexthops_t *nexthops, off_t i) { + if (i >= nexthops->num_elts) return -1; + nexthops->flags = ~0; /* all 1, could be limited to num_elts */ + nexthops->flags &= ~(1 << (i)); + nexthops->cur_elts = 1; + return 0; +} + +#ifdef WITH_POLICY + +void nexthops_set_priority(nexthops_t *nexthops, nexthop_t nexthop, + int priority) { + nexthops_enumerate(nexthops, i, nh, { + if (nexthop == nh) nexthops_set_priority_by_id(nexthops, i, priority); + }); +} + +void nexthops_set_priority_by_id(nexthops_t *nexthops, off_t i, int priority) { + nexthops->state[i].priority = priority; +} + +void nexthops_reset_priority(nexthops_t *nexthops, nexthop_t nexthop) { + nexthops_set_priority(nexthops, nexthop, DEFAULT_PRIORITY); +} + +void nexthops_reset_priority_by_id(nexthops_t *nexthops, off_t i) { + nexthops_set_priority_by_id(nexthops, i, DEFAULT_PRIORITY); +} + +void nexthops_reset_priorities(nexthops_t *nexthops) { + nexthops_enumerate(nexthops, i, nh, { + (void)nh; + nexthops_reset_priority(nexthops, i); + }); +} + +bool nexthops_equal(nexthops_t *a, nexthops_t *b) { + if (nexthops_get_len(a) != nexthops_get_len(b)) return false; + nexthops_foreach(a, n, { + if (!nexthops_contains(b, n)) return false; + }); + return true; +} + +void nexthops_copy(nexthops_t *src, nexthops_t *dst) { + for (unsigned i = 0; i < MAX_NEXTHOPS; i++) { + dst->elts[i] = src->elts[i]; + dst->state[i] = src->state[i]; + } + dst->num_elts = src->num_elts; + dst->flags = src->flags; + dst->cur_elts = src->cur_elts; +} + +/* Adapted from Jenkins hash (commutative) */ +uint32_t nexthops_get_hash(nexthops_t *nexthops) { + uint32_t hash = 0; + + nexthops_foreach(nexthops, nh, { + hash += nh; + hash += hash << 10; + hash ^= hash >> 6; + }); + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; +} + +#endif /* WITH_POLICY */ diff --git a/hicn-light/src/hicn/core/nexthops.h b/hicn-light/src/hicn/core/nexthops.h new file mode 100644 index 000000000..232c74388 --- /dev/null +++ b/hicn-light/src/hicn/core/nexthops.h @@ -0,0 +1,182 @@ +/* + * 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 nexthops.h + * \brief Nexthops + * + * An implementation of the nexthop data structure for the FIB entry. + * + * Note that the position of nexthops in this structure can be reordered. This + * is not an issue for the strategy state since the state if bound to the + * nexthop information, but an external module should not keep any reference to + * the offset of the nexthop. + */ + +#ifndef HICNLIGHT_NEXTHOPS_H +#define HICNLIGHT_NEXTHOPS_H + +#include <stdint.h> +#include <stdbool.h> +#include <stdlib.h> + +#include <hicn/util/log.h> + +#include "connection.h" +#include "strategy_vft.h" + +#define _nexthops_var(x) _nexthops_##x + +typedef unsigned nexthop_t; +#define NEXTHOP(x) ((nexthop_t)x) + +#define INVALID_NEXTHOP NEXTHOP(CONNECTION_ID_UNDEFINED) + +/* + * This allows storage within a single integer + * 32 or 64 nexthops should be sufficient + * Eventually replace this with a resizeable vector + */ +#define MAX_NEXTHOPS (sizeof(uint_fast32_t) * 8) + +typedef struct nexthops_s { + unsigned elts[MAX_NEXTHOPS]; + strategy_nexthop_state_t state[MAX_NEXTHOPS]; + size_t num_elts; + + /* + * flags is used during the processing of nexthops by the policy framework, + * and its meaning is local to the related functions. + * It is a mask where a bit set to 1 in position N indicates that the Nth + * elements in the elts array (elts[N]) is disabled. + * The number of enabled next hops is reflected in cur_elts, and it is equal + * to num_elts if no nexthop is disabled. This could be replaced by an + * efficient function counting the number of 1 bits in flags. + */ + uint_fast32_t flags; + size_t cur_elts; +} nexthops_t; + +#define NEXTHOPS_EMPTY \ + (nexthops_t) { \ + .elts = {0}, .state = {STRATEGY_NEXTHOP_STATE_EMPTY}, .num_elts = 0, \ + .flags = 0, .cur_elts = 0, \ + } + +#define nexthops_state(NH, i) ((NH)->state[(i)]) + +#define nexthops_get_len(NH) ((NH)->num_elts) + +#define nexthops_set_len(NH, LEN) \ + do { \ + (NH)->num_elts = LEN; \ + (NH)->cur_elts = LEN; \ + } while (0) + +#define nexthops_get_curlen(NH) ((NH)->cur_elts) + +#define nexthops_inc(NH) \ + do { \ + (NH)->num_elts++; \ + (NH)->cur_elts++; \ + } while (0) + +int nexthops_disable(nexthops_t *nexthops, off_t offset); +int nexthops_disable_all(nexthops_t *nexthops); + +#define nexthops_disable_if(NH, i, condition) \ + do { \ + if (condition) { \ + nexthops_disable((NH), (i)); \ + } \ + } while (0) + +#define nexthops_is_disabled(NH, i) ((NH)->flags & (1 << (i))) + +void nexthops_reset(nexthops_t *nexthops); + +#define nexthops_enumerate(NH, I, X, BODY) \ + do { \ + nexthop_t X; \ + (void)X; \ + unsigned I; \ + (void)I; \ + for ((I) = 0; (I) < nexthops_get_len(NH); (I)++) { \ + if (nexthops_is_disabled((NH), (I))) continue; \ + X = (NH)->elts[(I)]; \ + do { \ + BODY \ + } while (0); \ + } \ + } while (0) + +#define nexthops_foreach(NH, X, BODY) \ + do { \ + nexthops_enumerate((NH), _nexthops_var(i), X, {BODY}); \ + } while (0) + +off_t nexthops_add(nexthops_t *nexthops, nexthop_t nexthop); + +off_t nexthops_remove(nexthops_t *nexthops, nexthop_t nexthop); + +#define nexthops_clear(NH) \ + do { \ + nexthops_set_len(NH, 0); \ + (NH)->flags = 0; \ + } while (0) + +bool nexthops_contains(nexthops_t *nexthops, unsigned nexthop); + +off_t nexthops_find(nexthops_t *nexthops, unsigned nexthop); + +unsigned nexthops_get_one(nexthops_t *nexthops); + +int nexthops_select(nexthops_t *nexthops, off_t i); + +/* + * This selects the first available element, irrespective of the current state + * of flags + */ +#define nexthops_select_first(NH) nexthops_select((NH), 0) + +#ifdef WITH_POLICY + +#define DEFAULT_PRIORITY 0 +#define DISABLED_PRIORITY -1 + +void nexthops_set_priority(nexthops_t *nexthops, nexthop_t nexthop, + int priority); + +void nexthops_set_priority_by_id(nexthops_t *nexthops, off_t i, int priority); + +void nexthops_reset_priority_by_id(nexthops_t *nexthops, off_t i); + +void nexthops_reset_priority(nexthops_t *nexthops, nexthop_t nexthop); + +void nexthops_reset_priorities(nexthops_t *nexthops); + +/* + * returns true if the list of next hops contained in a is the same of b + * returns false otherwise + */ +bool nexthops_equal(nexthops_t *a, nexthops_t *b); + +void nexthops_copy(nexthops_t *src, nexthops_t *dst); + +#endif /* WITH_POLICY */ + +uint32_t nexthops_get_hash(nexthops_t *nexthops); + +#endif /* HICNLIGHT_NEXTHOPS_H */ diff --git a/hicn-light/src/hicn/core/numberSet.c b/hicn-light/src/hicn/core/numberSet.c deleted file mode 100644 index 106e13be6..000000000 --- a/hicn-light/src/hicn/core/numberSet.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <parc/algol/parc_ArrayList.h> -#include <parc/algol/parc_Memory.h> -#include <hicn/hicn-light/config.h> -#include <hicn/core/numberSet.h> -#include <stdio.h> - -#include <parc/assert/parc_Assert.h> - -struct number_set { - Number *arrayOfNumbers; - size_t length; - size_t limit; - unsigned refcount; -}; - -static void numberSet_Expand(NumberSet *set); - -NumberSet *numberSet_Create() { - NumberSet *set = parcMemory_AllocateAndClear(sizeof(NumberSet)); - parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(NumberSet)); - set->arrayOfNumbers = parcMemory_AllocateAndClear(sizeof(Number) * 16); - parcAssertNotNull((set->arrayOfNumbers), - "parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(Number) * 16); - set->length = 0; - set->limit = 16; - set->refcount = 1; - return set; -} - -NumberSet *numberSet_Acquire(const NumberSet *original) { - parcAssertNotNull(original, "Parameter original must be non-null"); - NumberSet *copy = (NumberSet *)original; - copy->refcount++; - return copy; -} - -void numberSet_Release(NumberSet **setPtr) { - parcAssertNotNull(setPtr, "Parameter must be non-null double pointer"); - parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer"); - - NumberSet *set = *setPtr; - parcAssertTrue( - set->refcount > 0, - "Invalid state: calling destroy on an object with 0 reference count"); - set->refcount--; - - if (set->refcount == 0) { - parcMemory_Deallocate((void **)&(set->arrayOfNumbers)); - parcMemory_Deallocate((void **)&set); - *setPtr = NULL; - } -} - -/** - * @function numberSet_AddNoChecks - * @abstract Add a number we know is not already in the set - * @discussion - * Used by other functions that already know the number is unique in the set, - * Does not do the expensive Contains check. - */ -static void numberSet_AddNoChecks(NumberSet *set, Number number) { - if (set->length == set->limit) { - numberSet_Expand(set); - } - - set->arrayOfNumbers[set->length] = number; - set->length++; -} - -bool numberSet_Add(NumberSet *set, Number number) { - parcAssertNotNull(set, "Parameter set must be non-null"); - if (numberSet_Contains(set, number)) { - return false; - } - - numberSet_AddNoChecks(set, number); - return true; -} - -size_t numberSet_Length(const NumberSet *set) { - parcAssertNotNull(set, "Parameter set must be non-null"); - return set->length; -} - -Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex) { - parcAssertNotNull(set, "Parameter set must be non-null"); - parcAssertTrue(ordinalIndex < set->length, - "Limit beyond end of set, length %zu got %zu", set->length, - ordinalIndex); - - return set->arrayOfNumbers[ordinalIndex]; -} - -bool numberSet_Contains(const NumberSet *set, Number number) { - parcAssertNotNull(set, "Parameter set must be non-null"); - for (size_t i = 0; i < set->length; i++) { - if (set->arrayOfNumbers[i] == number) { - return true; - } - } - return false; -} - -void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd) { - parcAssertNotNull(destinationSet, - "Parameter destinationSet must be non-null"); - parcAssertNotNull(setToAdd, "Parameter setToAdd must be non-null"); - - for (size_t i = 0; i < setToAdd->length; i++) { - numberSet_Add(destinationSet, setToAdd->arrayOfNumbers[i]); - } -} - -NumberSet *numberSet_Subtract(const NumberSet *minuend, - const NumberSet *subtrahend) { - // because the underlying ADT is not sorted, this is pretty ineffient, could - // be O(n^2). - - NumberSet *difference = numberSet_Create(); - - for (size_t i = 0; i < minuend->length; i++) { - bool unique = true; - for (size_t j = 0; j < subtrahend->length && unique; j++) { - if (minuend->arrayOfNumbers[i] == subtrahend->arrayOfNumbers[j]) { - unique = false; - } - } - - if (unique) { - numberSet_AddNoChecks(difference, minuend->arrayOfNumbers[i]); - } - } - return difference; -} - -bool numberSet_Equals(const NumberSet *a, const NumberSet *b) { - if (a == NULL && b == NULL) { - return true; - } - - if (a == NULL || b == NULL) { - return false; - } - - if (a->length == b->length) { - for (size_t i = 0; i < a->length; i++) { - bool found = false; - for (size_t j = 0; j < b->length && !found; j++) { - if (a->arrayOfNumbers[i] == b->arrayOfNumbers[j]) { - found = true; - } - } - if (!found) { - return false; - } - } - return true; - } - - return false; -} - -void numberSet_Remove(NumberSet *set, Number number) { - parcAssertNotNull(set, "Parameter set must be non-null"); - for (size_t i = 0; i < set->length; i++) { - if (set->arrayOfNumbers[i] == number) { - set->length--; - if (set->length > 0) { - // move the last element to the removed element to keep the array - // packed. - set->arrayOfNumbers[i] = set->arrayOfNumbers[set->length]; - } - return; - } - } -} - -// ===================================================== - -static void numberSet_Expand(NumberSet *set) { - size_t newlimit = set->limit * 2; - size_t newbytes = newlimit * sizeof(Number); - - set->arrayOfNumbers = parcMemory_Reallocate(set->arrayOfNumbers, newbytes); - set->limit = newlimit; -} diff --git a/hicn-light/src/hicn/core/numberSet.h b/hicn-light/src/hicn/core/numberSet.h deleted file mode 100644 index 91a965d7b..000000000 --- a/hicn-light/src/hicn/core/numberSet.h +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * @brief Stores a set of numbers. - * - * Useful for things like the reverse path of a PIT - * or the forward paths of a FIB. Does not allow duplicates. - * - */ - -#ifndef numberSet_h -#define numberSet_h - -#include <stdbool.h> -#include <stdint.h> -#include <stdlib.h> - -struct number_set; -typedef struct number_set NumberSet; - -typedef uint32_t Number; - -/** - * @function numberList_Create - * @abstract A new list of numbers - */ -NumberSet *numberSet_Create(void); - -/** - * Obtains a reference counted copy of the original - * The reference count is increased by one. It must be released with - * NumberSet_Release(). - * @param [in] original An allocated NumberSet - * @return non-null The reference counted copy - */ -NumberSet *numberSet_Acquire(const NumberSet *original); - -/** - * Releases one reference count and destroys the memory after last release - * The pointer will be NULLed after release regardless if the memory was - * destroyed. - * @param [in,out] setPtr A pointer to a NumberSet. Will be NULL'd after - * release. - */ -void numberSet_Release(NumberSet **setPtr); - -/** - * @function numberList_Append - * @abstract Add a number to the end of the list - * @discussion - * No check for duplicates is done - * @return true if added, false if a duplicate - */ -bool numberSet_Add(NumberSet *set, Number number); - -/** - * @function numberList_Length - * @abstract The count of numbers in the list - */ -size_t numberSet_Length(const NumberSet *set); - -/** - * @function numberSet_GetItem - * @abstract Retrieves an item based on the ordinal index - * @discussion - * Will assert if the ordinalIndex is out of bounds. - */ -Number numberSet_GetItem(const NumberSet *set, size_t ordinalIndex); - -/** - * @function numberSet_Contains - * @abstract Checks for set membership - * @return true if the set contains the number, false otherwise - */ -bool numberSet_Contains(const NumberSet *set, Number number); - -/** - * @function numberSet_AddSet - * @abstract Adds one set to another set - * @discussion - * Adds <code>setToAdd</code> to <code>destinationSet</code> - * @return true if the set contains the number, false otherwise - */ -void numberSet_AddSet(NumberSet *destinationSet, const NumberSet *setToAdd); - -/** - * @function numberSet_Subtract - * @abstract Computes set difference <code>difference = minuend - - * subtrahend</code>, returns a new number set. - * @discussion - * <code>minuend</code> and <code>subtrahend</code> are not modified. A new - * difference set is created. - * - * Returns the elements in <code>minuend</code> that are not in - * <code>subtrahend</code>. - * - * @param minuend The set from which to subtract - * @param subrahend The set begin removed from minuend - * @return The set difference. May be empty, but will not be NULL. - */ -NumberSet *numberSet_Subtract(const NumberSet *minuend, - const NumberSet *subtrahend); - -/** - * Determine if two NumberSet instances are equal. - * - * Two NumberSet instances are equal if, and only if, - * they are the same size and contain the same elements. Empty sets are - * equal. NULL equals NULL, but does not equal non-NULL. - * - * The following equivalence relations on non-null `NumberSet` instances are - * maintained: - * - * * It is reflexive: for any non-null reference value x, `NumberSet_Equals(x, - * x)` must return true. - * - * * It is symmetric: for any non-null reference values x and y, - * `numberSet_Equals(x, y)` must return true if and only if - * `numberSet_Equals(y, x)` returns true. - * - * * It is transitive: for any non-null reference values x, y, and z, if - * `numberSet_Equals(x, y)` returns true and - * `numberSet_Equals(y, z)` returns true, - * then `numberSet_Equals(x, z)` must return true. - * - * * It is consistent: for any non-null reference values x and y, multiple - * invocations of `numberSet_Equals(x, y)` consistently return true or - * consistently return false. - * - * * For any non-null reference value x, `numberSet_Equals(x, NULL)` must - * return false. - * - * @param a A pointer to a `NumberSet` instance. - * @param b A pointer to a `NumberSet` instance. - * @return true if the two `NumberSet` instances are equal. - */ -bool numberSet_Equals(const NumberSet *a, const NumberSet *b); - -/** - * @function numberSet_Remove - * @abstract Removes the number from the set - */ -void numberSet_Remove(NumberSet *set, Number number); -#endif // numberSet_h diff --git a/hicn-light/src/hicn/core/packet_cache.c b/hicn-light/src/hicn/core/packet_cache.c new file mode 100644 index 000000000..937b0480b --- /dev/null +++ b/hicn-light/src/hicn/core/packet_cache.c @@ -0,0 +1,884 @@ +/* + * 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 packet_cache.c + * \brief Implementation of hICN packet cache + * + * _get_suffixes : first level lookup to return the suffixes + * + * _remove_suffix : Remove suffix from the two level packet cache structure + * + * __add_suffix : Add a packet cache entry in the second level of the + * two-level data structure _add_suffix : Add a packet cache entry in the both + * the first and second level of the two-level data tructure (helper) + * + * __get_suffix : Lookup in the second level of the packet cache + * + * _get_suffix : Lookup in both the first and second levels of the packet cache + * + * ---- + * + * pkt_cache_save_suffixes_for_prefix : always done at packet reception to keep + * the latest suffixes + * + * pkt_cache_reset_suffixes_for_prefix + * + * ---- + * + * pkt_cache_allocate + * + * pkt_cache_add_to_index + * + * pkt_cache_remove_from_index + * + * pkt_cache_pit_remove_entry + * + * pkt_cache_cs_remove_entry + * + * pkt_cache_add_to_pit + * pkt_cache_add_to_cs + * + * _pkt_cache_add_to_pit + * used by pkt_cache_add_to_pit + * plt_cache_update_pit + * _pkt_cache_add_to_cs + * + * pkt_cache_pit_to_cs + * pkt_cache_cs_to_pit + * + * pkt_cache_update_pit : when an interest expired + * pkt_cache_update_cs + * + * pkt_cache_try_aggregate_in_pit + * + * + * + */ + +#include <hicn/core/forwarder.h> +#include "packet_cache.h" + +const char *_pkt_cache_verdict_str[] = { +#define _(x) [PKT_CACHE_VERDICT_##x] = #x, + foreach_kh_verdict +#undef _ +}; + +/****************************************************************************** + * Low-level operations on the hash table + ******************************************************************************/ + +/** + * Free the two level packet cache structure (helper) + */ +void _prefix_map_free(kh_pkt_cache_prefix_t *prefix_to_suffixes) { + kh_pkt_cache_suffix_t *suffix; + kh_foreach_value(prefix_to_suffixes, suffix, + { kh_destroy_pkt_cache_suffix(suffix); }); + kh_destroy_pkt_cache_prefix(prefix_to_suffixes); +} + +/** + * Perform the first level lookup to return the suffixes (helper) + */ +kh_pkt_cache_suffix_t *_get_suffixes(kh_pkt_cache_prefix_t *prefix_to_suffixes, + const hicn_name_prefix_t *prefix, + bool create, slab_t *prefix_keys) { + khiter_t k = kh_get_pkt_cache_prefix(prefix_to_suffixes, prefix); + + /* Return suffixes if found... */ + if (k != kh_end(prefix_to_suffixes)) { + kh_pkt_cache_suffix_t *suffixes = kh_val(prefix_to_suffixes, k); + return suffixes; + } + + if (!create) return NULL; + + /* ... otherwise populate the first level and return the newly added entry. + */ + kh_pkt_cache_suffix_t *suffixes = kh_init_pkt_cache_suffix(); + + hicn_name_prefix_t *prefix_copy = slab_get(hicn_name_prefix_t, prefix_keys); + memcpy(prefix_copy, prefix, sizeof(hicn_name_prefix_t)); + + int rc; + k = kh_put_pkt_cache_prefix(prefix_to_suffixes, prefix_copy, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(prefix_to_suffixes, k) = suffixes; + return suffixes; +} + +/** + * Remove suffix from the two level packet cache structure (helper) + */ +void _remove_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, + const hicn_name_suffix_t suffix, slab_t *prefix_keys) { + kh_pkt_cache_suffix_t *suffixes = + _get_suffixes(prefixes, prefix, false, prefix_keys); + assert(suffixes != NULL); + + khiter_t k = kh_get_pkt_cache_suffix(suffixes, suffix); + assert(k != kh_end(suffixes)); + kh_del_pkt_cache_suffix(suffixes, k); + + // TODO(eloparco): Remove prefix if no associated suffixes? +} + +/** + * Add a packet cache entry in the second level of the two-level data structure + * (helper) + */ +void __add_suffix(kh_pkt_cache_suffix_t *suffixes, hicn_name_suffix_t suffix, + unsigned val) { + // INFO("suffix add suffixes=%p suffix=%d val=%d", suffixes, suffix, val); + int rc; + khiter_t k = kh_put_pkt_cache_suffix(suffixes, suffix, &rc); + assert(rc == KH_ADDED || rc == KH_RESET); + kh_value(suffixes, k) = val; +} + +/** + * Add a packet cache entry in the both the first and second level of the + * two-level data tructure (helper) + */ +void _add_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, + const hicn_name_suffix_t suffix, unsigned val, + slab_t *prefix_keys) { + kh_pkt_cache_suffix_t *suffixes = + _get_suffixes(prefixes, prefix, true, prefix_keys); + assert(suffixes != NULL); + + __add_suffix(suffixes, suffix, val); +} + +/** + * Lookup in the second level of the packet cache (helper) + */ +unsigned __get_suffix(kh_pkt_cache_suffix_t *suffixes, + hicn_name_suffix_t suffix) { + khiter_t k = kh_get_pkt_cache_suffix(suffixes, suffix); + + // Not Found + if (k == kh_end(suffixes)) { + return HICN_INVALID_SUFFIX; + } + + unsigned index = kh_val(suffixes, k); + return index; +} + +unsigned _get_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, + hicn_name_suffix_t suffix, slab_t *prefix_keys) { + /* create is false as this function is always called by lookup */ + kh_pkt_cache_suffix_t *suffixes = + _get_suffixes(prefixes, prefix, false, prefix_keys); + if (!suffixes) { + return HICN_INVALID_SUFFIX; + } + return __get_suffix(suffixes, suffix); +} + +/** + * Lookup in both the first and second levels of the packet cache (helper) + */ +unsigned _get_suffix_from_name(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_t *name, slab_t *prefix_keys) { + const hicn_name_prefix_t *prefix = hicn_name_get_prefix(name); + const hicn_name_suffix_t suffix = hicn_name_get_suffix(name); + + return _get_suffix(prefixes, prefix, suffix, prefix_keys); +} + +void pkt_cache_save_suffixes_for_prefix(pkt_cache_t *pkt_cache, + const hicn_name_prefix_t *prefix) { + // Cached prefix matches the current one + if (hicn_name_prefix_equals(&pkt_cache->cached_prefix, prefix)) return; + + char buf[MAXSZ_HICN_PREFIX]; + hicn_name_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, &pkt_cache->cached_prefix); + hicn_name_prefix_snprintf(buf, MAXSZ_HICN_PREFIX, prefix); + + // Update cached prefix information + pkt_cache->cached_prefix = *prefix; + pkt_cache->cached_suffixes = + _get_suffixes(pkt_cache->prefix_to_suffixes, prefix, true, + pkt_cache->prefix_keys); // XXX + // +} + +void pkt_cache_reset_suffixes_for_prefix(pkt_cache_t *pkt_cache) { + pkt_cache->cached_suffixes = NULL; +} + +/****************************************************************************** + * Public API + ******************************************************************************/ + +pkt_cache_t *pkt_cache_create(size_t cs_size) { + pkt_cache_t *pkt_cache = (pkt_cache_t *)malloc(sizeof(pkt_cache_t)); + + pkt_cache->pit = pit_create(); + if (!pkt_cache->pit) return NULL; + pkt_cache->cs = cs_create(cs_size); + if (!pkt_cache->cs) return NULL; + + pkt_cache->prefix_to_suffixes = kh_init_pkt_cache_prefix(); + pkt_cache->prefix_keys = slab_create(hicn_name_prefix_t, SLAB_INIT_SIZE); + pool_init(pkt_cache->entries, DEFAULT_PKT_CACHE_SIZE, 0); + + pkt_cache->cached_prefix = HICN_NAME_PREFIX_EMPTY; + pkt_cache->cached_suffixes = NULL; + + return pkt_cache; +} + +void pkt_cache_free(pkt_cache_t *pkt_cache) { + assert(pkt_cache); + + // Free prefix hash table and pool + _prefix_map_free(pkt_cache->prefix_to_suffixes); + slab_free(pkt_cache->prefix_keys); + pool_free(pkt_cache->entries); + + // Free PIT and CS + pit_free(pkt_cache->pit); + cs_free(pkt_cache->cs); + + free(pkt_cache); +} + +kh_pkt_cache_suffix_t *pkt_cache_get_suffixes(const pkt_cache_t *pkt_cache, + const hicn_name_prefix_t *prefix, + bool create, + slab_t *prefix_keys) { + return _get_suffixes(pkt_cache->prefix_to_suffixes, prefix, create, + prefix_keys); +} + +pkt_cache_entry_t *pkt_cache_allocate(pkt_cache_t *pkt_cache) { + pkt_cache_entry_t *entry = NULL; + pool_get(pkt_cache->entries, entry); + assert(entry); + return entry; +} + +void pkt_cache_add_to_index(const pkt_cache_t *pkt_cache, + const pkt_cache_entry_t *entry) { + off_t id = entry - pkt_cache->entries; + + /* It is important that the name used for the index is the one in the packet + * cache entry, which is common for PIT and CS + */ + const hicn_name_t *name = &entry->name; + + if (pkt_cache->cached_suffixes) { + __add_suffix(pkt_cache->cached_suffixes, hicn_name_get_suffix(name), + (unsigned int)id); + } else { + _add_suffix(pkt_cache->prefix_to_suffixes, hicn_name_get_prefix(name), + hicn_name_get_suffix(name), (unsigned int)id, + pkt_cache->prefix_keys); + } +} + +/** + * Remove a name pointer to the packet cache index (helper) + */ +void pkt_cache_remove_from_index(const pkt_cache_t *pkt_cache, + const hicn_name_t *name) { + _remove_suffix(pkt_cache->prefix_to_suffixes, hicn_name_get_prefix(name), + hicn_name_get_suffix(name), pkt_cache->prefix_keys); + +// TODO +#if 0 + khiter_t k = kh_get_pkt_cache_name(pkt_cache->index_by_name, name); + assert(k != kh_end(pkt_cache->index_by_name)); + kh_del(pkt_cache_name, pkt_cache->index_by_name, k); +#endif +} + +pit_t *pkt_cache_get_pit(pkt_cache_t *pkt_cache) { return pkt_cache->pit; } + +cs_t *pkt_cache_get_cs(pkt_cache_t *pkt_cache) { return pkt_cache->cs; } + +pkt_cache_entry_t *pkt_cache_lookup(pkt_cache_t *pkt_cache, + const hicn_name_t *name, + msgbuf_pool_t *msgbuf_pool, + pkt_cache_lookup_t *lookup_result, + off_t *entry_id, + bool is_serve_from_cs_enabled) { + unsigned index = HICN_INVALID_SUFFIX; + if (pkt_cache->cached_suffixes) { + index = + __get_suffix(pkt_cache->cached_suffixes, hicn_name_get_suffix(name)); + } else { + index = _get_suffix_from_name(pkt_cache->prefix_to_suffixes, name, + pkt_cache->prefix_keys); + } + + if (index == HICN_INVALID_SUFFIX) { + *lookup_result = PKT_CACHE_LU_NONE; + return NULL; + } + + pkt_cache_entry_t *entry = pkt_cache_at(pkt_cache, index); + assert(entry); + bool expired = false; + if (entry->has_expire_ts && ticks_now() >= entry->expire_ts) { + expired = true; + } + + if (entry->entry_type == PKT_CACHE_CS_TYPE) { + if (expired) + *lookup_result = PKT_CACHE_LU_DATA_EXPIRED; + else + *lookup_result = PKT_CACHE_LU_DATA_NOT_EXPIRED; + } else { // PKT_CACHE_PIT_TYPE + if (expired) + *lookup_result = PKT_CACHE_LU_INTEREST_EXPIRED; + else + *lookup_result = PKT_CACHE_LU_INTEREST_NOT_EXPIRED; + } + + *entry_id = index; + return entry; +} + +void pkt_cache_cs_remove_entry(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, bool is_evicted) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_CS_TYPE); + + off_t msgbuf_id = entry->u.cs_entry.msgbuf_id; + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + + // XXX const hicn_name_t *name = msgbuf_get_name(msgbuf); + _remove_suffix(pkt_cache->prefix_to_suffixes, + hicn_name_get_prefix(&entry->name), + hicn_name_get_suffix(&entry->name), pkt_cache->prefix_keys); + + // Do not update the LRU cache for evicted entries + if (!is_evicted) cs_vft[pkt_cache->cs->type]->remove_entry(pkt_cache, entry); + + pkt_cache->cs->num_entries--; + pool_put(pkt_cache->entries, entry); + + WITH_DEBUG({ + char buf[MAXSZ_HICN_NAME]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, &entry->name); + if (rc < 0 || rc >= MAXSZ_HICN_NAME) + snprintf(buf, MAXSZ_HICN_NAME, "%s", "(error)"); + DEBUG("Packet %s removed from CS", buf); + }) + + msgbuf_pool_release(msgbuf_pool, &msgbuf); +} + +void pkt_cache_pit_remove_entry(pkt_cache_t *pkt_cache, + pkt_cache_entry_t *entry) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_PIT_TYPE); + + const hicn_name_t *name = &entry->name; + _remove_suffix(pkt_cache->prefix_to_suffixes, hicn_name_get_prefix(name), + hicn_name_get_suffix(name), pkt_cache->prefix_keys); + + pool_put(pkt_cache->entries, entry); + + WITH_DEBUG({ + char buf[MAXSZ_HICN_NAME]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, name); + if (rc < 0 || rc >= MAXSZ_HICN_NAME) + snprintf(buf, MAXSZ_HICN_NAME, "%s", "(error)"); + DEBUG("Packet %s removed from PIT", buf); + }) +} + +void _pkt_cache_add_to_cs(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf, + off_t msgbuf_id, off_t entry_id) { + entry->u.cs_entry = + (cs_entry_t){.msgbuf_id = msgbuf_id, + .lru = {.prev = INVALID_ENTRY_ID, .next = INVALID_ENTRY_ID}}; + Ticks now = ticks_now(); + entry->create_ts = now; + entry->expire_ts = now + msgbuf_get_data_expiry_time(msgbuf); + entry->has_expire_ts = true; + entry->entry_type = PKT_CACHE_CS_TYPE; + + pkt_cache->cs->num_entries++; + + int tail_id = (int)(pkt_cache->cs->lru.tail); + int result = cs_vft[pkt_cache->cs->type]->add_entry(pkt_cache, entry_id); + if (result == LRU_EVICTION) { + // Remove tail (already removed from LRU cache) + pkt_cache_entry_t *tail = pkt_cache_entry_at(pkt_cache, tail_id); + assert(tail->entry_type == PKT_CACHE_CS_TYPE); + pkt_cache_cs_remove_entry(pkt_cache, tail, msgbuf_pool, true); + } + + // Acquired by CS + msgbuf_pool_acquire(msgbuf); +} + +void pkt_cache_pit_to_cs(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf, + off_t msgbuf_id, off_t entry_id) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_PIT_TYPE); + + _pkt_cache_add_to_cs(pkt_cache, entry, msgbuf_pool, msgbuf, msgbuf_id, + entry_id); +} + +/** + * entry : newly allocated cache entry + * msgbuf : used for name, ingress connection id and lifetime + */ +void _pkt_cache_add_to_pit(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + const msgbuf_t *msgbuf) { + entry->u.pit_entry = (pit_entry_t){ + .ingressIdSet = NEXTHOPS_EMPTY, + .egressIdSet = NEXTHOPS_EMPTY, + .fib_entry = NULL, + }; + pit_entry_ingress_add(&entry->u.pit_entry, msgbuf_get_connection_id(msgbuf)); + + entry->create_ts = ticks_now(); + entry->expire_ts = ticks_now() + msgbuf_get_interest_lifetime(msgbuf); + entry->has_expire_ts = true; + entry->entry_type = PKT_CACHE_PIT_TYPE; +} + +void pkt_cache_cs_to_pit(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, const msgbuf_t *msgbuf, + off_t msgbuf_id, off_t entry_id) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_CS_TYPE); + + // Release data associated with expired CS entry + off_t cs_entry_msgbuf_id = entry->u.cs_entry.msgbuf_id; + msgbuf_t *cs_entry_msgbuf = msgbuf_pool_at(msgbuf_pool, cs_entry_msgbuf_id); + msgbuf_pool_release(msgbuf_pool, &cs_entry_msgbuf); + + cs_vft[pkt_cache->cs->type]->remove_entry(pkt_cache, entry); + _pkt_cache_add_to_pit(pkt_cache, entry, msgbuf); + pkt_cache->cs->num_entries--; +} + +void pkt_cache_update_cs(pkt_cache_t *pkt_cache, msgbuf_pool_t *msgbuf_pool, + pkt_cache_entry_t *entry, msgbuf_t *msgbuf, + off_t msgbuf_id) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_CS_TYPE); + + // Release previous msgbuf and acquire new one + msgbuf_t *prev_msgbuf = + msgbuf_pool_at(msgbuf_pool, entry->u.cs_entry.msgbuf_id); + msgbuf_pool_release(msgbuf_pool, &prev_msgbuf); + msgbuf_pool_acquire(msgbuf); + + entry->u.cs_entry.msgbuf_id = msgbuf_id; + entry->create_ts = ticks_now(); + entry->expire_ts = ticks_now() + msgbuf_get_data_expiry_time(msgbuf); + entry->has_expire_ts = true; + + cs_vft[pkt_cache->cs->type]->update_entry(pkt_cache, entry); +} + +pkt_cache_entry_t *pkt_cache_add_to_pit(pkt_cache_t *pkt_cache, + const msgbuf_t *msgbuf, + const hicn_name_t *name) { + assert(pkt_cache); + + pkt_cache_entry_t *entry = pkt_cache_allocate(pkt_cache); + entry->name = *name; + _pkt_cache_add_to_pit(pkt_cache, entry, msgbuf); + pkt_cache_add_to_index(pkt_cache, entry); + return entry; +} + +pkt_cache_entry_t *pkt_cache_add_to_cs(pkt_cache_t *pkt_cache, + msgbuf_pool_t *msgbuf_pool, + msgbuf_t *msgbuf, off_t msgbuf_id) { + assert(pkt_cache); + + pkt_cache_entry_t *entry = pkt_cache_allocate(pkt_cache); + const hicn_name_t *name = msgbuf_get_name(msgbuf); + entry->name = *name; + off_t entry_id = pkt_cache_get_entry_id(pkt_cache, entry); + _pkt_cache_add_to_cs(pkt_cache, entry, msgbuf_pool, msgbuf, msgbuf_id, + entry_id); + pkt_cache_add_to_index(pkt_cache, entry); + return entry; +} + +void pkt_cache_update_pit(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + const msgbuf_t *msgbuf) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_PIT_TYPE); + + pit_entry_t *pit_entry = &entry->u.pit_entry; + fib_entry_t *fib_entry = pit_entry_get_fib_entry(pit_entry); + if (fib_entry) + fib_entry_on_timeout(fib_entry, pit_entry_get_egress(pit_entry)); + + _pkt_cache_add_to_pit(pkt_cache, entry, msgbuf); +} + +bool pkt_cache_try_aggregate_in_pit(pkt_cache_t *pkt_cache, + pkt_cache_entry_t *entry, + const msgbuf_t *msgbuf, + const hicn_name_t *name) { + assert(pkt_cache); + assert(entry); + assert(entry->entry_type == PKT_CACHE_PIT_TYPE); + + pit_entry_t *pit_entry = &entry->u.pit_entry; + + // Extend entry lifetime + Ticks expire_ts = ticks_now() + msgbuf_get_interest_lifetime(msgbuf); + if (expire_ts > entry->expire_ts) entry->expire_ts = expire_ts; + + // Check if the reverse path is already present + // in the PIT entry (i.e. it is a retransmission) + unsigned connection_id = msgbuf_get_connection_id(msgbuf); + bool is_aggregated = !pit_entry_ingress_contains(pit_entry, connection_id); + + // If the strategy to use for this packet is STRATEGY_TYPE_LOCAL_REMOTE we may + // want to forward the packet even if it should be aggregated. This is useful + // when a local consumer socket and a remote one are asking for the same + // content. If the interest coming from the remote socket is received before + // the one from the local socket, the second interest is aggregated in the PIT + // and no interest is satisfied (IRIS app). + // Forward the interest if: 1) the strategy in the fib_entry stored in the pit + // entry is STRATEGY_TYPE_LOCAL_REMOTE 2) the ingress connection of the + // interest if a local connection 3) the ingress list of pit entry has no + // other local ingress connection if all these conditions are satisfied, send + // the interest. if is_aggregated = false avoid the check (it will be useless) + + if (is_aggregated) { + fib_entry_t *fib_entry = pit_entry_get_fib_entry(pit_entry); + if (fib_entry && + (fib_entry_strategy_type(fib_entry) == STRATEGY_TYPE_LOCAL_REMOTE)) { + // the strategy is STRATEGY_TYPE_LOCAL_REMOTE, check the input face + connection_table_t *table = + forwarder_get_connection_table(fib_entry->forwarder); + if (table) { + connection_t *msg_conn = + connection_table_get_by_id(table, connection_id); + if (msg_conn && connection_is_local(msg_conn)) { + // The face is local, check that no other input face is local. With + // this check only the first local consumer is able to send + // the interest, while the other interests will be aggregated + // (however this is quite inefficient) + nexthops_t *ingressIdSet = pit_entry_get_ingress(pit_entry); + bool in_local_connextion_exists = false; + nexthops_enumerate(ingressIdSet, i, nexthop, { + connection_t *in_conn = connection_table_get_by_id(table, nexthop); + if (in_conn && connection_is_local(in_conn)) { + in_local_connextion_exists = true; + break; + } + }); + if (!in_local_connextion_exists) is_aggregated = false; + } + } + } + } + + if (is_aggregated) pit_entry_ingress_add(pit_entry, connection_id); + + WITH_DEBUG({ + char buf[MAXSZ_HICN_NAME]; + int rc = hicn_name_snprintf(buf, MAXSZ_HICN_NAME, msgbuf_get_name(msgbuf)); + if (rc < 0 || rc >= MAXSZ_HICN_NAME) + snprintf(buf, MAXSZ_HICN_NAME, "%s", "(error)"); + if (is_aggregated) { + DEBUG("Interest %s already existing (expiry %lu): aggregate", buf, + entry->expire_ts); + } else { + DEBUG("Interest %s already existing (expiry %lu): retransmit", buf, + entry->expire_ts); + } + }) + + return is_aggregated; +} + +nexthops_t *pkt_cache_on_data(pkt_cache_t *pkt_cache, + msgbuf_pool_t *msgbuf_pool, off_t msgbuf_id, + bool is_cs_store_enabled, + bool is_connection_local, bool *wrong_egress, + pkt_cache_verdict_t *verdict) { + assert(pkt_cache); + assert(msgbuf_id_is_valid(msgbuf_id)); + + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_DATA); + + *wrong_egress = false; + off_t entry_id; + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *entry = + pkt_cache_lookup(pkt_cache, msgbuf_get_name(msgbuf), msgbuf_pool, + &lookup_result, &entry_id, true); + + pit_entry_t *pit_entry; + fib_entry_t *fib_entry; + nexthops_t *nexthops = NULL; + nexthops_t *nexthops_copy; + switch (lookup_result) { + case PKT_CACHE_LU_INTEREST_NOT_EXPIRED: + pit_entry = &entry->u.pit_entry; + fib_entry = pit_entry_get_fib_entry(pit_entry); + if (fib_entry) + fib_entry_on_data(fib_entry, pit_entry_get_egress(pit_entry), msgbuf, + entry->create_ts, ticks_now()); + + // Check if the data is coming from the exepected connection + nexthops_t *egressIdSet = pit_entry_get_egress(pit_entry); + unsigned egress_connection = msgbuf_get_connection_id(msgbuf); + if (!nexthops_contains(egressIdSet, egress_connection)) { + *wrong_egress = true; + return NULL; + } + + // XXX TODO : be sure nexthops are valid b/c pit entry is removed + // XXX TODO eventually pass holding structure as parameter + nexthops = pit_entry_get_ingress(pit_entry); + assert(nexthops); + + nexthops_copy = (nexthops_t *)malloc(sizeof(*nexthops_copy)); + *nexthops_copy = *nexthops; + + if (is_cs_store_enabled) { + pkt_cache_pit_to_cs(pkt_cache, entry, msgbuf_pool, msgbuf, msgbuf_id, + entry_id); + *verdict = PKT_CACHE_VERDICT_FORWARD_DATA; + } else { + pkt_cache_pit_remove_entry(pkt_cache, entry); + *verdict = PKT_CACHE_VERDICT_CLEAR_DATA; + } + + return nexthops_copy; + + // Data packets are stored in the content store even in the case + // where there is no match in the PIT, to allow applications to push + // content to the forwarder's CS. This behavior is allowed only for + // local faces. + case PKT_CACHE_LU_INTEREST_EXPIRED: + if (is_cs_store_enabled && is_connection_local) { + pkt_cache_pit_to_cs(pkt_cache, entry, msgbuf_pool, msgbuf, msgbuf_id, + entry_id); + *verdict = PKT_CACHE_VERDICT_STORE_DATA; + } else { + pkt_cache_pit_remove_entry(pkt_cache, entry); + *verdict = PKT_CACHE_VERDICT_CLEAR_DATA; + } + return NULL; + + case PKT_CACHE_LU_NONE: + *verdict = PKT_CACHE_VERDICT_IGNORE_DATA; + if (is_cs_store_enabled && is_connection_local) { + pkt_cache_add_to_cs(pkt_cache, msgbuf_pool, msgbuf, msgbuf_id); + *verdict = PKT_CACHE_VERDICT_STORE_DATA; + } + return NULL; + + case PKT_CACHE_LU_DATA_EXPIRED: + if (is_cs_store_enabled && is_connection_local) { + pkt_cache_update_cs(pkt_cache, msgbuf_pool, entry, msgbuf, msgbuf_id); + *verdict = PKT_CACHE_VERDICT_UPDATE_DATA; + } else { + pkt_cache_cs_remove_entry(pkt_cache, entry, msgbuf_pool, false); + *verdict = PKT_CACHE_VERDICT_CLEAR_DATA; + } + return NULL; + + case PKT_CACHE_LU_DATA_NOT_EXPIRED: + *verdict = PKT_CACHE_VERDICT_IGNORE_DATA; + if (is_cs_store_enabled && is_connection_local) { + pkt_cache_update_cs(pkt_cache, msgbuf_pool, entry, msgbuf, msgbuf_id); + *verdict = PKT_CACHE_VERDICT_UPDATE_DATA; + } + return NULL; + + default: + ERROR("Invalid packet cache content"); + return NULL; + } +} + +void pkt_cache_on_interest(pkt_cache_t *pkt_cache, msgbuf_pool_t *msgbuf_pool, + off_t msgbuf_id, pkt_cache_verdict_t *verdict, + off_t *data_msgbuf_id, pkt_cache_entry_t **entry_ptr, + const hicn_name_t *name, + bool is_serve_from_cs_enabled) { + assert(pkt_cache); + assert(msgbuf_id_is_valid(msgbuf_id)); + + msgbuf_t *msgbuf = msgbuf_pool_at(msgbuf_pool, msgbuf_id); + assert(msgbuf_get_type(msgbuf) == HICN_PACKET_TYPE_INTEREST); + + off_t entry_id; + pkt_cache_lookup_t lookup_result; + pkt_cache_entry_t *entry = + pkt_cache_lookup(pkt_cache, name, msgbuf_pool, &lookup_result, &entry_id, + is_serve_from_cs_enabled); + *entry_ptr = entry; + + cs_entry_t *cs_entry = NULL; + bool is_cs_miss = true; + bool is_aggregated; + switch (lookup_result) { + case PKT_CACHE_LU_NONE: + entry = pkt_cache_add_to_pit(pkt_cache, msgbuf, name); + *entry_ptr = entry; + + *verdict = PKT_CACHE_VERDICT_FORWARD_INTEREST; + break; + + case PKT_CACHE_LU_DATA_NOT_EXPIRED: + if (!is_serve_from_cs_enabled) goto PKT_CACHE_LU_DATA_EXPIRED; + + cs_entry = &entry->u.cs_entry; + *data_msgbuf_id = cs_entry->msgbuf_id; + + *verdict = PKT_CACHE_VERDICT_FORWARD_DATA; + is_cs_miss = false; + break; + + case PKT_CACHE_LU_INTEREST_NOT_EXPIRED: + is_aggregated = + pkt_cache_try_aggregate_in_pit(pkt_cache, entry, msgbuf, name); + + *verdict = is_aggregated ? PKT_CACHE_VERDICT_AGGREGATE_INTEREST + : PKT_CACHE_VERDICT_RETRANSMIT_INTEREST; + break; + + case PKT_CACHE_LU_INTEREST_EXPIRED: + pkt_cache_update_pit(pkt_cache, entry, msgbuf); + + *verdict = PKT_CACHE_VERDICT_INTEREST_EXPIRED_FORWARD_INTEREST; + break; + + case PKT_CACHE_LU_DATA_EXPIRED: + PKT_CACHE_LU_DATA_EXPIRED: + pkt_cache_cs_to_pit(pkt_cache, entry, msgbuf_pool, msgbuf, msgbuf_id, + entry_id); + + *verdict = PKT_CACHE_VERDICT_DATA_EXPIRED_FORWARD_INTEREST; + break; + + default: + *verdict = PKT_CACHE_VERDICT_ERROR; + } + is_cs_miss ? cs_miss(pkt_cache->cs) : cs_hit(pkt_cache->cs); +} + +void pkt_cache_cs_clear(pkt_cache_t *pkt_cache) { + assert(pkt_cache); + + kh_pkt_cache_suffix_t *v_suffixes; + u32 k_suffix; + u32 v_pkt_cache_entry_id; + kh_foreach_value(pkt_cache->prefix_to_suffixes, v_suffixes, { + kh_foreach(v_suffixes, k_suffix, v_pkt_cache_entry_id, { + pkt_cache_entry_t *entry = pkt_cache_at(pkt_cache, v_pkt_cache_entry_id); + if (entry->entry_type == PKT_CACHE_CS_TYPE) { + // Remove from hash table + khiter_t k = kh_get_pkt_cache_suffix(v_suffixes, k_suffix); + assert(k != kh_end(v_suffixes)); + kh_del_pkt_cache_suffix(v_suffixes, k); + + // Remove from pool + pool_put(pkt_cache->entries, entry); + } + }); + }); + + // Reset cached prefix + pkt_cache->cached_prefix = HICN_NAME_PREFIX_EMPTY; + pkt_cache->cached_suffixes = NULL; + + // Re-create CS + cs_clear(pkt_cache->cs); +} + +size_t pkt_cache_get_num_cs_stale_entries(pkt_cache_t *pkt_cache) { + size_t num_stale_entries = 0; + Ticks now = ticks_now(); + pkt_cache_entry_t *entry; + + pool_foreach(pkt_cache->entries, entry, { + if (entry->entry_type == PKT_CACHE_CS_TYPE && entry->has_expire_ts && + now >= entry->expire_ts) { + num_stale_entries++; + } + }); + + return num_stale_entries; +} + +int pkt_cache_set_cs_size(pkt_cache_t *pkt_cache, size_t size) { + if (pkt_cache->cs->num_entries > size) return -1; + + pkt_cache->cs->max_size = size; + return 0; +} + +size_t pkt_cache_get_size(pkt_cache_t *pkt_cache) { + return pool_len(pkt_cache->entries); +} + +size_t pkt_cache_get_cs_size(pkt_cache_t *pkt_cache) { + return pkt_cache->cs->num_entries; +} + +size_t pkt_cache_get_pit_size(pkt_cache_t *pkt_cache) { + uint64_t pkt_cache_size = pkt_cache_get_size(pkt_cache); + uint64_t pit_size = pkt_cache_size - pkt_cache_get_cs_size(pkt_cache); + return pit_size; +} + +void pkt_cache_log(pkt_cache_t *pkt_cache) { + DEBUG("Packet cache: total size = %lu, PIT size = %lu, CS size = %u", + pkt_cache_get_size(pkt_cache), pkt_cache_get_pit_size(pkt_cache), + pkt_cache_get_cs_size(pkt_cache)); + + cs_log(pkt_cache->cs); +} + +pkt_cache_stats_t pkt_cache_get_stats(pkt_cache_t *pkt_cache) { + cs_lru_stats_t lru_stats = cs_get_lru_stats(pkt_cache_get_cs(pkt_cache)); + pkt_cache_stats_t stats = { + .n_pit_entries = (uint32_t)pkt_cache_get_pit_size(pkt_cache), + .n_cs_entries = (uint32_t)pkt_cache_get_cs_size(pkt_cache), + .n_lru_evictions = (uint32_t)lru_stats.countLruEvictions, + }; + + return stats; +} diff --git a/hicn-light/src/hicn/core/packet_cache.h b/hicn-light/src/hicn/core/packet_cache.h new file mode 100644 index 000000000..1abdc57c2 --- /dev/null +++ b/hicn-light/src/hicn/core/packet_cache.h @@ -0,0 +1,502 @@ +/* + * 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 packet_cache.h + * \brief hICN packet cache + * + * The packet cache is a data structure that merges together the PIT and the CS, + * to which it holds a reference. + * It contains PIT and CS entries, indexed in a two-level hash table. + * + * Each entry has shared fields, e.g. entry type (PIT or CS) and timestamps, + * which are used by both PIT and CS entries. In addition, a C union holds + * the PIT or CS specific fields. + * + * Having a single entry that can hold PIT or CS entries allows to reduce + * the number of lookups. + * + * A prefix hash table <prefix, suffix_hashtable> stores the suffixes associated + * to each prefix, where each value in the map points to a separate hash + * table <suffix, packet_cache_reference> that can be used to retrieved the + * packet cache entry. + * When an interest/data packet is received, the prefix and the associated + * suffixes are saved; if the next packet cache operation involves the same + * prefix, no additional lookups in the prefix hash hashtable are needed. + */ + +#ifndef HICNLIGHT_PACKET_CACHE_H +#define HICNLIGHT_PACKET_CACHE_H + +#include <hicn/util/khash.h> +#include <hicn/util/slab.h> +#include "content_store.h" +#include "pit.h" +#include "msgbuf_pool.h" +#include "../content_store/lru.h" + +#define DEFAULT_PKT_CACHE_SIZE 2048 + +typedef enum { PKT_CACHE_PIT_TYPE, PKT_CACHE_CS_TYPE } pkt_cache_entry_type_t; + +#define foreach_kh_verdict \ + _(FORWARD_INTEREST) \ + _(AGGREGATE_INTEREST) \ + _(RETRANSMIT_INTEREST) \ + _(FORWARD_DATA) \ + _(INTEREST_EXPIRED_FORWARD_INTEREST) \ + _(DATA_EXPIRED_FORWARD_INTEREST) \ + _(STORE_DATA) \ + _(CLEAR_DATA) \ + _(UPDATE_DATA) \ + _(IGNORE_DATA) \ + _(ERROR) + +typedef enum { +#define _(x) PKT_CACHE_VERDICT_##x, + foreach_kh_verdict +#undef _ +} pkt_cache_verdict_t; + +extern const char *_pkt_cache_verdict_str[]; + +#define pkt_cache_verdict_str(x) _pkt_cache_verdict_str[x] + +#define foreach_kh_lookup \ + _(INTEREST_NOT_EXPIRED) \ + _(INTEREST_EXPIRED) \ + _(DATA_NOT_EXPIRED) \ + _(DATA_EXPIRED) \ + _(NONE) + +typedef enum { +#define _(x) PKT_CACHE_LU_##x, + foreach_kh_lookup +#undef _ +} pkt_cache_lookup_t; + +KHASH_MAP_INIT_INT(pkt_cache_suffix, unsigned); +KHASH_INIT(pkt_cache_prefix, const hicn_name_prefix_t *, + kh_pkt_cache_suffix_t *, 1, hicn_name_prefix_get_hash, + hicn_name_prefix_equals); + +typedef struct { + hicn_name_t name; + pkt_cache_entry_type_t entry_type; + + Ticks create_ts; + Ticks expire_ts; + // TODO(eloparco): Is it necessary? + // Now it is always set to true + bool has_expire_ts; + + union { + pit_entry_t pit_entry; + cs_entry_t cs_entry; + } u; +} pkt_cache_entry_t; + +typedef struct { + pit_t *pit; + cs_t *cs; + pkt_cache_entry_t *entries; + kh_pkt_cache_prefix_t *prefix_to_suffixes; + slab_t *prefix_keys; + + // Cached prefix info to avoid double lookups, + // used for both single interest speculation and interest manifest + hicn_name_prefix_t cached_prefix; + kh_pkt_cache_suffix_t *cached_suffixes; +} pkt_cache_t; + +/** + * @brief Create a new packet cache. + * + * @return pkt_cache_t* The newly created packet cache + */ +pkt_cache_t *pkt_cache_create(size_t cs_size); + +/** + * @brief Add an entry with the specified name to the packet cache. + * + * @param[in] pkt_cache Pointer to the pool data structure to use. + * @param[in, out] entry Empty entry that will be used to return the + * allocated one from the msgbuf pool. + * @param[in] name hicn_name_t to use + * + * NOTE: unlike other pools, PIT and CS entries allocation does not update the + * index as the key is a hicn_name_t * which should point to : + * - the name inside the PIT entry (which is thus not set during allocation), + * - the name inside the msgbuf_t in the CS entry, which is always available + * during the lifetime of the cache entry. + * + * The index is therefore updated in pkt_cache_add_to_pit and + * pkt_cache_add_to_cs functions. + * + * + */ +pkt_cache_entry_t *pkt_cache_allocate(pkt_cache_t *pkt_cache); + +void pkt_cache_add_to_index(const pkt_cache_t *pkt_cache, + const pkt_cache_entry_t *entry); + +void pkt_cache_remove_from_index(const pkt_cache_t *pkt_cache, + const hicn_name_t *name); + +/** + * @brief Free a packet cache data structure. + * + * @param[in] pkt_cache Pointer to packet cache data structure to free. + */ +void pkt_cache_free(pkt_cache_t *pkt_cache); + +/** + * @brief Get a reference to the PIT data structure contained in the packet + * cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to get the + * PIT from. + */ +pit_t *pkt_cache_get_pit(pkt_cache_t *pkt_cache); + +/** + * @brief Get a reference to the CS data structure contained in the packet + * cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to get the + * CS from. + */ +cs_t *pkt_cache_get_cs(pkt_cache_t *pkt_cache); + +/** + * @brief Return the total packet cache size (i.e. PIT + CS). + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + */ +size_t pkt_cache_get_size(pkt_cache_t *pkt_cache); + +/** + * @brief Return the number of stale entries (i.e. expired) in the CS. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + */ +size_t pkt_cache_get_num_cs_stale_entries(pkt_cache_t *pkt_cache); + +/** + * @brief Change the maximum capacity of the content store (LRU eviction will + * be used after reaching the provided size) + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] size Maximum size of the content store + * @return int 0 if success, -1 if the provided maximum size is smaller than + * the number of elements currently stored in the CS + */ +int pkt_cache_set_cs_size(pkt_cache_t *pkt_cache, size_t size); + +/** + * @brief Return the content store size. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + */ +size_t pkt_cache_get_cs_size(pkt_cache_t *pkt_cache); + +/** + * @brief Return the PIT size. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + */ +size_t pkt_cache_get_pit_size(pkt_cache_t *pkt_cache); + +#define pkt_cache_entry_get_create_ts(E) ((E)->create_ts) +#define pkt_cache_entry_get_expire_ts(E) ((E)->expire_ts) +#define pkt_cache_entry_set_expire_ts(E, EXPIRY_TIME) \ + (entry)->expire_ts = EXPIRY_TIME +#define pkt_cache_get_entry_id(pkt_cache, entry) (entry - pkt_cache->entries) +#define pkt_cache_entry_at(pkt_cache, id) (&(pkt_cache)->entries[id]) +#define pkt_cache_at(pkt_cache, i) (pkt_cache->entries + i) + +/** + * @brief Retrieve from the packet cache the entry associated with the + * specified name. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to retrieve + * the entry from + * @param[in] name Packet name to use for the lookup + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in, out] lookup_result Pointer to store the result of the lookup + * @param[in, out] entry_id Pointer to store the entry_id in case of a lookup + * match + * @param[in] is_serve_from_cs_enabled Boolean to specify if the forwarder is + * allowed to serve contents from the CS + * @return pkt_cache_entry_t* Entry retrieved, NULL if none found + */ +pkt_cache_entry_t *pkt_cache_lookup(pkt_cache_t *pkt_cache, + const hicn_name_t *name, + msgbuf_pool_t *msgbuf_pool, + pkt_cache_lookup_t *lookup_result, + off_t *entry_id, + bool is_serve_from_cs_enabled); + +/** + * @brief Clear the content of the CS (PIT entries are left unmodified). + * + * @param pkt_cache Pointer to the packet cache data structure to use + */ +void pkt_cache_cs_clear(pkt_cache_t *pkt_cache); + +/** + * @brief Log packet cache statistics. + * + * @param pkt_cache Pointer to the packet cache data structure to use + */ +void pkt_cache_log(pkt_cache_t *pkt_cache); + +pkt_cache_stats_t pkt_cache_get_stats(pkt_cache_t *pkt_cache); + +// TODO(eloparco): To implement +void pkt_cache_print(const pkt_cache_t *pkt_cache); + +/************** Packet cache entry operations *************/ + +/** + * @brief Remove a content store entry from the packet cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] entry Pointer to the content store entry to remove + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in] is_evicted Boolean to specify if the content store entry has + * already been evicted from the LRU cache + */ +void pkt_cache_cs_remove_entry(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, bool is_evicted); + +/** + * @brief Remove a PIT entry from the packet cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] entry Pointer to the PIT entry to remove + */ +void pkt_cache_pit_remove_entry(pkt_cache_t *pkt_cache, + pkt_cache_entry_t *entry); + +/** + * @brief Convert a PIT entry to a CS entry. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in, out] entry Pointer to the PIT entry to replace + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in] msgbuf Pointer to the msgbuf associated with the CS entry to + * insert + * @param[in] msgbuf_id Msgbuf ID (i.e. ID in the msgbuf pool) associated with + * the CS entry to insert + * @param[in] entry_id Entry ID (i.e. ID in the packet cache pool of entries) + * associated with the PIT entry to replace + */ +void pkt_cache_pit_to_cs(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, msgbuf_t *msgbuf, + off_t msgbuf_id, off_t entry_id); + +/** + * @brief Convert a CS entry to a PIT entry. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in, out] entry Pointer to the CS entry to replace + * @param[in] msgbuf Pointer to the msgbuf associated with the PIT entry to + * insert + * @param[in] msgbuf_id Msgbuf ID (i.e. ID in the msgbuf pool) associated with + * the PIT entry to insert + * @param[in] entry_id Entry ID (i.e. ID in the packet cache pool of entries) + * associated with the CS entry to replace + */ +void pkt_cache_cs_to_pit(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + msgbuf_pool_t *msgbuf_pool, const msgbuf_t *msgbuf, + off_t msgbuf_id, off_t entry_id); + +/** + * @brief Add PIT entry to the packet cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] msgbuf Pointer to the msgbuf associated with the PIT entry to + * insert + * @param[in] name Interest name to use; in case of aggregated interests, it is + * different from the name stored in the msgbuf + * @return pkt_cache_entry_t* Pointer to the packet cache (PIT) entry created + */ +pkt_cache_entry_t *pkt_cache_add_to_pit(pkt_cache_t *pkt_cache, + const msgbuf_t *msgbuf, + const hicn_name_t *name); + +/** + * @brief Add CS entry to the packet cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in] msgbuf Pointer to the msgbuf associated with the CS entry to + * insert + * @param[in] msgbuf_id Msgbuf ID (i.e. ID in the msgbuf pool) associated with + * the CS entry to insert + * @return pkt_cache_entry_t* Pointer to the packet cache (CS) entry created + */ +pkt_cache_entry_t *pkt_cache_add_to_cs(pkt_cache_t *pkt_cache, + msgbuf_pool_t *msgbuf_pool, + msgbuf_t *msgbuf, off_t msgbuf_id); + +/** + * @brief Update PIT entry in the packet cache in case of an expired PIT + * entry. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in, out] entry Pointer to the PIT entry to update + * @param[in] msgbuf Pointer to the msgbuf associated with the PIT entry to + * update + */ +void pkt_cache_update_pit(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry, + const msgbuf_t *msgbuf); + +/** + * @brief Update CS entry in the packet cache. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] msgbuf_pool Pointer to the msgbuf pool data structure to use + * @param[in, out] entry Pointer to the CS entry to update + * @param[in] msgbuf Pointer to the msgbuf associated with the CS entry to + * update + * @param msgbuf_id Msgbuf ID (i.e. ID in the msgbuf pool) associated with the + * CS entry to update + */ +void pkt_cache_update_cs(pkt_cache_t *pkt_cache, msgbuf_pool_t *msgbuf_pool, + pkt_cache_entry_t *entry, msgbuf_t *msgbuf, + off_t msgbuf_id); + +/** + * @brief Update PIT entry in the packet cache in case of retransmission or + * aggregation. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in, out] entry Pointer to the PIT entry to update + * @param[in] msgbuf Pointer to the msgbuf associated with the PIT entry to + * update + * @param[in] name Interest name to use; in case of aggregated interests, it is + * different from the name stored in the msgbuf + * @return true If aggregation (interest sent from a connection not stored in + * the PIT entry) + * @return false If retransmission (interest sent from a connection already + * stored in the PIT entry) + */ +bool pkt_cache_try_aggregate_in_pit(pkt_cache_t *pkt_cache, + pkt_cache_entry_t *entry, + const msgbuf_t *msgbuf, + const hicn_name_t *name); + +/** + * @brief Cache prefix info (prefix + associated suffixes) to speed up lookups. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + * @param[in] prefix Name prefix to cache + */ +void pkt_cache_save_suffixes_for_prefix(pkt_cache_t *pkt_cache, + const hicn_name_prefix_t *prefix); + +/** + * @brief Reset cached prefix info to force double lookups. + * + * @param[in] pkt_cache Pointer to the packet cache data structure to use + */ +void pkt_cache_reset_suffixes_for_prefix(pkt_cache_t *pkt_cache); + +/************ Handle data/interest packets received *******/ + +/** + * @brief Handle data packet reception. + * @details Perform packet cache lookup and execute operations based on it. + * If: + * - INTEREST not expired: Convert PIT entry to CS entry; return the + * nexthops (that can be used to forward the data + * packet now stored in the CS) + * - INTEREST expired: Convert PIT entry to CS entry + * - DATA expired/not expired: Update the CS + * - No match: Add data packet to CS + */ +nexthops_t *pkt_cache_on_data(pkt_cache_t *pkt_cache, + msgbuf_pool_t *msgbuf_pool, off_t msgbuf_id, + bool is_cs_store_enabled, + bool is_connection_local, bool *wrong_egress, + pkt_cache_verdict_t *verdict); + +/** + * @brief Handle interest packet reception. + * @details Perform packet cache lookup and execute operations based on it. + * If: + * - No match: Do nothing + * - DATA not expired: get data message from CS + * - INTEREST not expired: Aggregate or retransmit the interest received; + * - INTEREST expired: Update the PIT; + * - DATA expired: Convert CS entry to PIT entry; + */ +void pkt_cache_on_interest(pkt_cache_t *pkt_cache, msgbuf_pool_t *msgbuf_pool, + off_t msgbuf_id, pkt_cache_verdict_t *verdict, + off_t *data_msgbuf_id, pkt_cache_entry_t **entry_ptr, + const hicn_name_t *name, + bool is_serve_from_cs_enabled); + +/********* Low-level operations on the hash table *********/ +#ifdef WITH_TESTS +unsigned __get_suffix(kh_pkt_cache_suffix_t *suffixes, + hicn_name_suffix_t suffix); +unsigned _get_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, + hicn_name_suffix_t suffix, slab_t *prefix_keys); +void __add_suffix(kh_pkt_cache_suffix_t *suffixes, hicn_name_suffix_t suffix, + unsigned val); +void _add_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, + const hicn_name_suffix_t suffix, unsigned val, slab_t *slab); +void _remove_suffix(kh_pkt_cache_prefix_t *prefixes, + const hicn_name_prefix_t *prefix, hicn_name_suffix_t suffix, + slab_t *slab); +void _prefix_map_free(kh_pkt_cache_prefix_t *prefix_to_suffixes); +kh_pkt_cache_suffix_t *_get_suffixes(kh_pkt_cache_prefix_t *prefix_to_suffixes, + const hicn_name_prefix_t *prefix, + bool create, slab_t *prefix_keys); +#endif + +/************** Content Store *****************************/ + +typedef struct { + const char *name; + void (*initialize)(cs_t *cs); + void (*finalize)(cs_t *cs); + int (*add_entry)(pkt_cache_t *pkt_cache, off_t entry_id); + void (*update_entry)(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry); + int (*remove_entry)(pkt_cache_t *pkt_cache, pkt_cache_entry_t *entry); +} cs_ops_t; +extern const cs_ops_t *const cs_vft[]; + +/** + * @brief Initialize the virtual function table used for the + * CS cache strategy (e.g. LRU). + * + */ +#define DECLARE_CS(NAME) \ + const cs_ops_t cs_##NAME = { \ + .name = #NAME, \ + .initialize = cs_##NAME##_initialize, \ + .finalize = cs_##NAME##_finalize, \ + .add_entry = cs_##NAME##_add_entry, \ + .update_entry = cs_##NAME##_update_entry, \ + .remove_entry = cs_##NAME##_remove_entry, \ + } + +#endif /* HICNLIGHT_PACKET_CACHE_H */ diff --git a/hicn-light/src/hicn/core/pit.c b/hicn-light/src/hicn/core/pit.c new file mode 100644 index 000000000..322e53674 --- /dev/null +++ b/hicn-light/src/hicn/core/pit.c @@ -0,0 +1,74 @@ +/* + * 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. + */ + +/** + * The pending interest table. + * + * Interest aggregation strategy: + * - The first Interest for a name is forwarded + * - A second Interest for a name from a different reverse path may be + * aggregated + * - A second Interest for a name from an existing Interest is forwarded + * - The Interest Lifetime is like a subscription time. A reverse path entry is + * removed once the lifetime is exceeded. + * - Whan an Interest arrives or is aggregated, the Lifetime for that reverse + * hop is extended. As a simplification, we only keep a single lifetime not per + * reverse hop. + * + */ + +#include "pit.h" + +Ticks pit_calculate_lifetime(pit_t* pit, const msgbuf_t* msgbuf) { + uint64_t lifetime = msgbuf_get_lifetime(msgbuf); + if (lifetime == 0) lifetime = NSEC_TO_TICKS(DEFAULT_INTEREST_LIFETIME); + + return ticks_now() + lifetime; +} + +/* This is only used as a hint for first allocation, as the table is resizeable + */ +#define DEFAULT_PIT_SIZE 65535 + +pit_t* _pit_create(size_t init_size, size_t max_size) { + pit_t* pit = malloc(sizeof(pit_t)); + if (!pit) return NULL; + + if (init_size == 0) init_size = DEFAULT_PIT_SIZE; + + pit->max_size = max_size; + return pit; +} + +void pit_free(pit_t* pit) { + assert(pit); + free(pit); +} + +// void pit_print(const pit_t *pit) { +// const Name *k; +// unsigned v; +// pit_entry_t * entry; +// Ticks expire_ts; + +// printf("*** PIT ***\n"); +// kh_foreach(pit->index_by_name, k, v, { +// char *name_str = name_ToString(k); +// entry = pit_at(pit, v); +// expire_ts = pit_entry_get_expire_ts(entry); +// printf("%s\t\t\texpire=%lu\n", name_str, expire_ts); +// free(name_str); +// }) +// } diff --git a/hicn-light/src/hicn/core/pit.h b/hicn-light/src/hicn/core/pit.h new file mode 100644 index 000000000..f2e901490 --- /dev/null +++ b/hicn-light/src/hicn/core/pit.h @@ -0,0 +1,73 @@ +/* + * 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 pit.h + * @brief hICN Pending Interest Table (PIT) + */ + +#ifndef HICNLIGHT_PIT_H +#define HICNLIGHT_PIT_H + +#include <hicn/core/fib.h> + +typedef struct { + nexthops_t ingressIdSet; + nexthops_t egressIdSet; + fib_entry_t* fib_entry; +} pit_entry_t; + +#define pit_entry_get_ingress(E) (&((E)->ingressIdSet)) +#define pit_entry_get_egress(E) (&((E)->egressIdSet)) +#define pit_entry_get_fib_entry(E) ((E)->fib_entry) +#define pit_entry_set_fib_entry(E, FIB_ENTRY) ((E)->fib_entry = FIB_ENTRY) + +#define pit_entry_ingress_add(E, NH) \ + nexthops_add(pit_entry_get_ingress(E), (NH)) + +#define pit_entry_ingress_contains(E, NH) \ + nexthops_contains(pit_entry_get_ingress(E), (NH)) + +#define pit_entry_egress_add(E, NH) nexthops_add(pit_entry_get_egress(E), (NH)) + +typedef struct { + // TODO(eloparco): How to handle PIT size? + size_t max_size; +} pit_t; + +#define DEFAULT_INTEREST_LIFETIME 4000000000ULL + +Ticks pit_calculate_lifetime(pit_t* pit, const msgbuf_t* msgbuf); + +/** + * @brief Allocate a new PIT data structure (extended parameters) + * + * @param init_size Initial size (0 = default) + * @param max_size Maximum size (0 = unbounded) + * + * @return pit_t* Newly allocated PIT data structure + */ +pit_t* _pit_create(size_t init_size, size_t max_size); + +/** + * @brief Allocate a new PIT data structure + * + * @return pit_t* Newly allocated PIT data structure + */ +#define pit_create() _pit_create(0, 0) + +void pit_free(pit_t* pit); + +#endif /* HICNLIGHT_PIT_H */ diff --git a/hicn-light/src/hicn/core/policy_stats.c b/hicn-light/src/hicn/core/policy_stats.c new file mode 100644 index 000000000..8706a9244 --- /dev/null +++ b/hicn-light/src/hicn/core/policy_stats.c @@ -0,0 +1,202 @@ +/* + * 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. + */ + +#ifdef WITH_POLICY_STATS + +// This has to be included first because of _GNU_SOURCE +#include <hicn/core/forwarder.h> + +#include <hicn/core/connection_table.h> +#include <hicn/base/loop.h> +#include <hicn/util/log.h> +#include <hicn/core/ticks.h> +#include <hicn/policy.h> +#include <hicn/core/fib.h> + +#include "policy_stats.h" + +#define ALPHA 0.9 +#define STATS_INTERVAL 1000 /* ms */ + +static int policy_stats_mgr_tick(void* mgr_arg, int fd, unsigned id, + void* data) { + policy_stats_mgr_t* mgr = mgr_arg; + assert(mgr); + assert(id == 0); + assert(!data); + + uint64_t now = ticks_now(); + + /* Loop over FIB entries to compute statistics from counters */ + const fib_t* fib = forwarder_get_fib(mgr->forwarder); + + fib_foreach_entry(fib, entry, { + policy_stats_update(&entry->policy_stats, &entry->policy_counters, now); + }); + + return 0; +} + +int policy_stats_mgr_initialize(policy_stats_mgr_t* mgr, void* forwarder) { + mgr->forwarder = forwarder; + + loop_timer_create(&mgr->timer, MAIN_LOOP, mgr, policy_stats_mgr_tick, NULL); + if (!mgr->timer) { + ERROR("Error allocating prefix stats mgr timer."); + return -1; + } + + if (loop_timer_register(mgr->timer, STATS_INTERVAL) < 0) { + ERROR("Error registering prefix stats mgr timer."); + return -1; + } + + return 0; +} + +void policy_stats_mgr_finalize(policy_stats_mgr_t* mgr) { + loop_event_unregister(mgr->timer); + loop_event_free(mgr->timer); +} + +void policy_stats_on_retransmission(const policy_stats_mgr_t* mgr, + policy_counters_t* counters, + const nexthops_t* nexthops) { + connection_table_t* table = forwarder_get_connection_table(mgr->forwarder); + nexthops_foreach(nexthops, nexthop, { +#ifdef WITH_POLICY + const connection_t* conn = connection_table_at(table, nexthop); + + if (connection_has_tag(conn, POLICY_TAG_WIRED)) + counters->wired.num_losses++; + if (connection_has_tag(conn, POLICY_TAG_WIFI)) counters->wifi.num_losses++; + if (connection_has_tag(conn, POLICY_TAG_CELLULAR)) + counters->cellular.num_losses++; +#endif /* WITH_POLICY */ + counters->all.num_losses++; + }); +} + +#define UPDATE_TAG_STATS(TAG, NAME) \ + do { \ + if (connection_has_tag(conn, TAG)) { \ + counters->NAME.num_packets++; \ + counters->NAME.num_bytes += msg_size; \ + stats->NAME.latency = \ + ALPHA * stats->NAME.latency + (1 - ALPHA) * (double)rtt; \ + counters->NAME.latency_idle = 0; \ + } \ + } while (0) + +/* Update statistic counters upon Data packet reception */ +void policy_stats_on_data(const policy_stats_mgr_t* mgr, policy_stats_t* stats, + policy_counters_t* counters, + const nexthops_t* nexthops, const msgbuf_t* msgbuf, + Ticks rtt) { +#ifdef WITH_POLICY + forwarder_t* forwarder = mgr->forwarder; + connection_table_t* table = forwarder_get_connection_table(forwarder); +#endif /* WITH_POLICY */ + + size_t msg_size = msgbuf_get_len(msgbuf); + + nexthops_foreach(nexthops, nexthop, { +#ifdef WITH_POLICY + const connection_t* conn = connection_table_at(table, nexthop); + if (!conn) continue; + + UPDATE_TAG_STATS(POLICY_TAG_WIRED, wired); + UPDATE_TAG_STATS(POLICY_TAG_WIFI, wifi); + UPDATE_TAG_STATS(POLICY_TAG_CELLULAR, cellular); +#endif /* WITH_POLICY */ + }); + + stats->all.latency = ALPHA * stats->all.latency + (1 - ALPHA) * (double)rtt; + counters->all.latency_idle = 0; + counters->all.num_packets++; + counters->all.num_bytes += msg_size; +} + +void policy_stats_on_timeout(const policy_stats_mgr_t* mgr, + policy_counters_t* counters, + const nexthops_t* nexthops) { +#ifdef WITH_POLICY + connection_table_t* table = forwarder_get_connection_table(mgr->forwarder); + + nexthops_foreach(nexthops, nexthop, { + const connection_t* conn = connection_table_at(table, nexthop); + if (!conn) continue; + if (connection_has_tag(conn, POLICY_TAG_WIRED)) + counters->wired.num_losses++; + if (connection_has_tag(conn, POLICY_TAG_WIFI)) counters->wifi.num_losses++; + if (connection_has_tag(conn, POLICY_TAG_CELLULAR)) + counters->cellular.num_losses++; + }); +#endif /* WITH_POLICY */ + + counters->all.num_losses++; +} + +#define UPDATE_TYPE(TYPE) \ + do { \ + /* (a) throughput */ \ + if (counters->TYPE.num_bytes > 0) { \ + throughput = counters->TYPE.num_bytes / (now - counters->last_update); \ + throughput = throughput * 8 / 1024; \ + if (throughput < 0) throughput = 0; \ + } else { \ + throughput = 0; \ + } \ + stats->TYPE.throughput = \ + ALPHA * stats->TYPE.throughput + (1 - ALPHA) * throughput; \ + \ + /* (b) loss rate */ \ + if ((counters->TYPE.num_losses > 0) && (counters->TYPE.num_packets > 0)) { \ + loss_rate = counters->TYPE.num_losses / counters->TYPE.num_packets; \ + loss_rate *= 100; \ + } else { \ + loss_rate = 0; \ + } \ + stats->TYPE.loss_rate = \ + ALPHA * stats->TYPE.loss_rate + (1 - ALPHA) * loss_rate; \ + \ + /* (c) latency */ \ + counters->TYPE.latency_idle++; \ + if (counters->TYPE.latency_idle > 1) stats->TYPE.latency = 0; \ + \ + /* (d) Reset counters */ \ + counters->TYPE.num_bytes = 0; \ + counters->TYPE.num_losses = 0; \ + counters->TYPE.num_packets = 0; \ + } while (0) + +void policy_stats_update(policy_stats_t* stats, policy_counters_t* counters, + uint64_t now) { + double throughput; + double loss_rate; + + if (now == counters->last_update) return; + +#ifdef WITH_POLICY + UPDATE_TYPE(wired); + UPDATE_TYPE(wifi); + UPDATE_TYPE(cellular); +#endif /* WITH_POLICY */ + UPDATE_TYPE(all); + + counters->last_update = now; +} + +#endif /* WITH_POLICY_STATS */ diff --git a/hicn-light/src/hicn/core/policy_stats.h b/hicn-light/src/hicn/core/policy_stats.h new file mode 100644 index 000000000..aee68e515 --- /dev/null +++ b/hicn-light/src/hicn/core/policy_stats.h @@ -0,0 +1,92 @@ + +#ifndef HICNLIGHT_POLICY_STATS_H +#define HICNLIGHT_POLICY_STATS_H + +#ifdef WITH_POLICY_STATS + +#include <hicn/policy.h> + +typedef struct policy_stats_mgr_s { + void* forwarder; + event_t* timer; +} policy_stats_mgr_t; + +#if 0 + +/* PER-INTERFACE POLICY STATS */ + +typedef struct { + float throughput; + float latency; + float loss_rate; +} interface_stats_t; + +/* POLICY STATS */ + +typedef struct { + interface_stats_t wired; + interface_stats_t wifi; + interface_stats_t cellular; + interface_stats_t all; +} policy_stats_t; + +typedef struct { + uint32_t num_packets; + uint32_t num_bytes; + uint32_t num_losses; + uint32_t latency_idle; +} interface_counters_t; + +typedef struct { + interface_counters_t wired; + interface_counters_t wifi; + interface_counters_t cellular; + interface_counters_t all; + uint64_t last_update; +} policy_counters_t; + +#define INTERFACE_STATS_EMPTY \ + (interface_stats_t) { .throughput = 0, .latency = 0, .loss_rate = 0, } + +#define POLICY_STATS_EMPTY \ + (policy_stats_t) { \ + .wired = INTERFACE_STATS_EMPTY, .wifi = INTERFACE_STATS_EMPTY, \ + .cellular = INTERFACE_STATS_EMPTY, .all = INTERFACE_STATS_EMPTY, \ + } + +#define INTERFACE_COUNTERS_EMPTY \ + (interface_counters_t) { \ + .num_packets = 0, .num_bytes = 0, .num_losses = 0, .latency_idle = 0, \ + } + +#define POLICY_COUNTERS_EMPTY \ + (policy_counters_t) { \ + .wired = INTERFACE_COUNTERS_EMPTY, .wifi = INTERFACE_COUNTERS_EMPTY, \ + .cellular = INTERFACE_COUNTERS_EMPTY, .all = INTERFACE_COUNTERS_EMPTY, \ + .last_update = 0, \ + } +#endif + +int policy_stats_mgr_initialize(policy_stats_mgr_t* mgr, void* forwarder); + +void policy_stats_mgr_finalize(policy_stats_mgr_t* mgr); + +void policy_stats_on_retransmission(const policy_stats_mgr_t* mgr, + policy_counters_t* countrs, + const nexthops_t* nexthops); + +void policy_stats_on_data(const policy_stats_mgr_t* mgr, policy_stats_t* stats, + policy_counters_t* counters, + const nexthops_t* nexthops, const msgbuf_t* msgbuf, + Ticks rtt); + +void policy_stats_on_timeout(const policy_stats_mgr_t* mgr, + policy_counters_t* counters, + const nexthops_t* nexthops); + +void policy_stats_update(policy_stats_t* stats, policy_counters_t* counters, + uint64_t now); + +#endif /* WITH_POLICY_STATS */ + +#endif /* HICNLIGHT_POLICY_STATS_H */ diff --git a/hicn-light/src/hicn/core/strategy.c b/hicn-light/src/hicn/core/strategy.c new file mode 100644 index 000000000..46c83ef02 --- /dev/null +++ b/hicn-light/src/hicn/core/strategy.c @@ -0,0 +1,60 @@ +/* + * 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 strategy.c + * \brief Implementation of hICN forwarding strategy + */ + +#include "nexthops.h" +#include "strategy.h" +#include "strategy_vft.h" + +int strategy_initialize(strategy_entry_t *entry, const void *forwarder) { + return strategy_vft[entry->type]->initialize(entry, forwarder); +} + +int strategy_finalize(strategy_entry_t *entry) { + return strategy_vft[entry->type]->finalize(entry); +} + +nexthops_t *strategy_lookup_nexthops(strategy_entry_t *entry, + nexthops_t *nexthops, + const msgbuf_t *msgbuf) { + return strategy_vft[entry->type]->lookup_nexthops(entry, nexthops, msgbuf); +} + +int strategy_add_nexthop(strategy_entry_t *entry, nexthops_t *nexthops, + off_t offset) { + return strategy_vft[entry->type]->add_nexthop(entry, nexthops, offset); +} + +int strategy_remove_nexthop(strategy_entry_t *entry, nexthops_t *nexthops, + off_t offset) { + return strategy_vft[entry->type]->remove_nexthop(entry, nexthops, offset); +} + +int strategy_on_data(strategy_entry_t *entry, nexthops_t *nexthops, + const nexthops_t *data_nexthops, const msgbuf_t *msgbuf, + Ticks pitEntryCreation, Ticks objReception) { + return strategy_vft[entry->type]->on_data( + entry, nexthops, data_nexthops, msgbuf, pitEntryCreation, objReception); +} + +int strategy_on_timeout(strategy_entry_t *entry, nexthops_t *nexthops, + const nexthops_t *timeout_nexthops) { + return strategy_vft[entry->type]->on_timeout(entry, nexthops, + timeout_nexthops); +} diff --git a/hicn-light/src/hicn/core/strategy.h b/hicn-light/src/hicn/core/strategy.h new file mode 100644 index 000000000..9f1a7dac7 --- /dev/null +++ b/hicn-light/src/hicn/core/strategy.h @@ -0,0 +1,81 @@ +/* + * 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 strategy.h + * \brief hICN forwarding strategy + */ +#ifndef HICNLIGHT_STRATEGY_H +#define HICNLIGHT_STRATEGY_H + +/* + * Forwarding strategy + * + * The forwarding strategy decides to elect one or several next hops among those + * available in the FIB entry, after an eventual application of the policy. This + * means it should be aware of the different flags set in the nexthops_t data + * structure by previous forwarding steps, that might have excluded certain + * nexthops. + * + * A strategy is defined by its type and comes with : + * - options, initialized at setup and that might eventually be updated (this + * is allowed on a per-strategy basis. + * - a state (eventually) empty, that is used to inform its decisions, and + * might be updated for each interest sent (lookup_nexthops), data received + * (on_data) or timeout event (on_timeout). + * + * All this information (type, options, state) is made available through a + * strategy_entry_t which is stored together with nexthops in the FIB entry. + * + * Per-nexthop strategy informaton is stored in the nexthops table itself. As it + * would be difficult and suboptimal to provide a correct strategy-dependent + * initialization in the FIB nad nexthops data structures, it is thus the + * responsibility of the forwarding strategy to initialize its state and nexthop + * related state when appropriate (eg. at initialization, or when a nexthop is + * added). + */ + +#include "nexthops.h" +#include "strategy_vft.h" + +typedef struct strategy_entry_s { + const void *forwarder; + strategy_type_t type; + strategy_options_t options; + strategy_state_t state; +} strategy_entry_t; + +int strategy_initialize(strategy_entry_t *entry, const void *forwarder); + +int strategy_finalize(strategy_entry_t *entry); + +nexthops_t *strategy_lookup_nexthops(strategy_entry_t *entry, + nexthops_t *nexthops, + const msgbuf_t *msgbuf); + +int strategy_add_nexthop(strategy_entry_t *entry, nexthops_t *nexthops, + off_t offset); + +int strategy_remove_nexthop(strategy_entry_t *entry, nexthops_t *nexthops, + off_t offset); + +int strategy_on_data(strategy_entry_t *entry, nexthops_t *nexthops, + const nexthops_t *data_nexthops, const msgbuf_t *msgbuf, + Ticks pitEntryCreation, Ticks objReception); + +int strategy_on_timeout(strategy_entry_t *entry, nexthops_t *nexthops, + const nexthops_t *timeout_nexthops); + +#endif /* HICNLIGHT_STRATEGY_H */ diff --git a/hicn-light/src/hicn/core/strategy_vft.c b/hicn-light/src/hicn/core/strategy_vft.c new file mode 100644 index 000000000..0af035c88 --- /dev/null +++ b/hicn-light/src/hicn/core/strategy_vft.c @@ -0,0 +1,39 @@ +/* + * 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 strategy_vft.c + * \brief Implementation of hICN forwarding strategy VFT + */ + +#include "strategy_vft.h" + +extern const strategy_ops_t strategy_load_balancer; +extern const strategy_ops_t strategy_random; +extern const strategy_ops_t strategy_replication; +extern const strategy_ops_t strategy_bestpath; +extern const strategy_ops_t strategy_low_latency; +extern const strategy_ops_t strategy_local_remote; + +const strategy_ops_t *const strategy_vft[] = { + [STRATEGY_TYPE_LOADBALANCER] = &strategy_load_balancer, + [STRATEGY_TYPE_RANDOM] = &strategy_random, + [STRATEGY_TYPE_REPLICATION] = &strategy_replication, + [STRATEGY_TYPE_BESTPATH] = &strategy_bestpath, + [STRATEGY_TYPE_LOCAL_REMOTE] = &strategy_local_remote, +#if 0 + [STRATEGY_TYPE_LOW_LATENCY] = &strategy_low_latency, +#endif +}; diff --git a/hicn-light/src/hicn/core/strategy_vft.h b/hicn-light/src/hicn/core/strategy_vft.h new file mode 100644 index 000000000..55e61db17 --- /dev/null +++ b/hicn-light/src/hicn/core/strategy_vft.h @@ -0,0 +1,124 @@ +/* + * 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_vft.h + * \brief hICN forwarding strategy VFT + */ +#ifndef HICNLIGHT_STRATEGY_VFT_H +#define HICNLIGHT_STRATEGY_VFT_H + +#include "msgbuf.h" + +#include "../strategies/best_path.h" +#include "../strategies/load_balancer.h" +#include "../strategies/random.h" +#include "../strategies/replication.h" + +typedef union { + strategy_load_balancer_options_t load_balancer; + strategy_random_options_t random; + strategy_replication_options_t replication; + strategy_bestpath_options_t bestpath; +} strategy_options_t; + +typedef struct { +#ifdef WITH_POLICY + int priority; +#endif /* WITH_POLICY */ + union { + strategy_load_balancer_nexthop_state_t load_balancer; + strategy_random_nexthop_state_t random; + strategy_replication_nexthop_state_t replication; + strategy_bestpath_nexthop_state_t bestpath; + }; +} strategy_nexthop_state_t; + +#define STRATEGY_NEXTHOP_STATE_EMPTY \ + { \ + 0, { \ + { 0 } \ + } \ + } + +typedef union { + strategy_load_balancer_state_t load_balancer; + strategy_random_state_t random; + strategy_replication_state_t replication; + strategy_bestpath_state_t bestpath; +} strategy_state_t; +// XXX This has to be merged with nexthops +// XXX How to avoid errors due to pool id reuse (eg on_data) ? + +/** + * @typedef strategy_ops_t + * @abstract Forwarding strategy implementation + * @constant receiveObject is called when we receive an object and have a + * measured round trip time. This allows a strategy to update its performance + * data. + * @constant lookupNexthop Find the set of nexthops to use for the Interest. + * May be empty, should not be NULL. Must be destroyed. + * @constant addNexthop Add a nexthop to the list of available nexthops with a + * routing protocol-specific cost. + * @constant destroy cleans up the strategy, freeing all memory and state. A + * strategy is reference counted, so the final destruction only happens after + * the last reference is released. + * @discussion <#Discussion#> + */ + +struct strategy_entry_s; +struct nexthops_s; + +typedef struct { + const char *name; + + int (*initialize)(struct strategy_entry_s *entry, const void *forwarder); + + int (*finalize)(struct strategy_entry_s *entry); + + struct nexthops_s *(*lookup_nexthops)(struct strategy_entry_s *entry, + struct nexthops_s *nexthops, + const msgbuf_t *msgbuf); + + int (*add_nexthop)(struct strategy_entry_s *strategy, + struct nexthops_s *nexthops, off_t offset); + + int (*remove_nexthop)(struct strategy_entry_s *entry, + struct nexthops_s *nexthops, off_t offset); + + int (*on_data)(struct strategy_entry_s *entry, struct nexthops_s *nexthops, + const struct nexthops_s *data_nexthops, const msgbuf_t *msgbuf, + Ticks pitEntryCreation, Ticks objReception); + + int (*on_timeout)(struct strategy_entry_s *entry, struct nexthops_s *nexthops, + const struct nexthops_s *timeout_nexthops); + +} strategy_ops_t; + +extern const strategy_ops_t *const strategy_vft[]; + +#define DECLARE_STRATEGY(NAME) \ + const strategy_ops_t strategy_##NAME = { \ + .name = #NAME, \ + .initialize = strategy_##NAME##_initialize, \ + .finalize = strategy_##NAME##_finalize, \ + .add_nexthop = strategy_##NAME##_add_nexthop, \ + .remove_nexthop = strategy_##NAME##_remove_nexthop, \ + .lookup_nexthops = strategy_##NAME##_lookup_nexthops, \ + .on_data = strategy_##NAME##_on_data, \ + .on_timeout = strategy_##NAME##_on_timeout, \ + } + +#endif /* HICNLIGHT_STRATEGY_VFT_H */ diff --git a/hicn-light/src/hicn/core/streamBuffer.c b/hicn-light/src/hicn/core/streamBuffer.c index c30139498..401b7c873 100644 --- a/hicn-light/src/hicn/core/streamBuffer.c +++ b/hicn-light/src/hicn/core/streamBuffer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/hicn-light/src/hicn/core/streamBuffer.h b/hicn-light/src/hicn/core/streamBuffer.h index 27e793176..0caa10bea 100644 --- a/hicn-light/src/hicn/core/streamBuffer.h +++ b/hicn-light/src/hicn/core/streamBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: diff --git a/hicn-light/src/hicn/core/subscription.c b/hicn-light/src/hicn/core/subscription.c new file mode 100644 index 000000000..fb954a245 --- /dev/null +++ b/hicn-light/src/hicn/core/subscription.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#include "subscription.h" +#include <hicn/util/vector.h> +#include <hicn/util/log.h> + +/*----------------------------------------------------------------------------* + * Topics and events + *----------------------------------------------------------------------------*/ + +bool topics_contains(hc_topics_t topic_list, hc_topic_t topic) { + return ((topic_list) & (topic)); +} + +#define topic_is_set(topic_list, topic_index) \ + ((topic_list) & (1 << (topic_index))) + +/*----------------------------------------------------------------------------* + * Subscriptions + *----------------------------------------------------------------------------*/ + +struct subscription_table_s { + unsigned *table[TOPIC_N]; +}; + +subscription_table_t *subscription_table_create() { + subscription_table_t *subscriptions = malloc(sizeof(subscription_table_t)); + for (int i = 0; i < NUM_TOPICS; i++) + vector_init(subscriptions->table[i], 0, 0); + + return subscriptions; +} + +void subscription_table_free(subscription_table_t *subscriptions) { + for (int i = 0; i < NUM_TOPICS; i++) vector_free(subscriptions->table[i]); + free(subscriptions); +} + +int subscription_table_add_topics_for_connection( + subscription_table_t *subscriptions, hc_topics_t topics, + unsigned connection_id) { + bool is_subscription_already_present = false; + for (int topic_index = 0; topic_index < NUM_TOPICS; topic_index++) { + if (topic_is_set(topics, topic_index)) { + int num_duplicates = vector_remove_unordered( + subscriptions->table[topic_index], connection_id); + + int ret = vector_push(subscriptions->table[topic_index], connection_id); + if (ret < 0) { + ERROR("Unable to perform subscription for connection %d, topic %s", + connection_id, object_type_str(topic_index)); + return -1; + } + + if (num_duplicates > 0) { + DEBUG("Connection %d had already a subscription for topic %s", + connection_id, object_type_str(topic_index)); + is_subscription_already_present = true; + } + } + } + return is_subscription_already_present ? -2 : 0; +} + +int subscription_table_remove_topics_for_connection( + subscription_table_t *subscriptions, hc_topics_t topics, + unsigned connection_id) { + int num_subscriptions_removed = 0; + for (int topic_index = 0; topic_index < NUM_TOPICS; topic_index++) { + if (topic_is_set(topics, topic_index)) { + int num_duplicates = vector_remove_unordered( + subscriptions->table[topic_index], connection_id); + if (num_duplicates <= 0) { + continue; + } + num_subscriptions_removed++; + } + } + return num_subscriptions_removed; +} + +hc_topics_t subscription_table_get_topics_for_connection( + subscription_table_t *subscriptions, unsigned connection_id) { + hc_topics_t topics = 0; + for (int topic_index = 0; topic_index < NUM_TOPICS; topic_index++) { + unsigned *conn_id; + bool found = false; + vector_foreach(subscriptions->table[topic_index], conn_id, { + if (*conn_id == connection_id) { + found = true; + break; + } + }); + if (found) topics |= (1 << topic_index); + } + return topics; +} + +unsigned *subscription_table_get_connections_for_topic( + subscription_table_t *subscriptions, hc_topic_t topic) { + int topic_index = object_from_topic(topic); + return subscriptions->table[topic_index]; +} + +void subscription_table_print(subscription_table_t *subscriptions) { + for (int topic_index = OBJECT_TYPE_UNDEFINED + 1; topic_index < NUM_TOPICS; + topic_index++) { + printf("topic %s (%lu subscription/s) from connection/s: [ ", + object_type_str(topic_index), + (unsigned long)vector_len(subscriptions->table[topic_index])); + unsigned *connection_id; + vector_foreach(subscriptions->table[topic_index], connection_id, + { printf("%d ", *connection_id); }); + printf("]\n"); + } +} diff --git a/hicn-light/src/hicn/core/subscription.h b/hicn-light/src/hicn/core/subscription.h new file mode 100644 index 000000000..74ddd9e18 --- /dev/null +++ b/hicn-light/src/hicn/core/subscription.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021 Cisco and/or its affiliates. + */ + +#ifndef HICNLIGHT_SUBSCRIPTION_H +#define HICNLIGHT_SUBSCRIPTION_H + +#include <hicn/ctrl/api.h> +#include <stddef.h> + +/*----------------------------------------------------------------------------* + * Topics + *----------------------------------------------------------------------------*/ + +bool topics_contains(hc_topics_t topic_list, hc_topic_t topic); + +/*----------------------------------------------------------------------------* + * Subscriptions + *----------------------------------------------------------------------------*/ + +typedef struct subscription_table_s subscription_table_t; + +subscription_table_t *subscription_table_create(); + +void subscription_table_free(subscription_table_t *subscriptions); + +/** + * @brief Add topic subscriptions for a connection. + * + * @param subscriptions The pointer to the subscription table + * @param topics Topics the connection wants to subscribe to + * @param connection_id Identifier of the connection + * @return int 0 for success, -1 for error, -2 if already esisting subscription + * for at least one of the topic for + */ +int subscription_table_add_topics_for_connection( + subscription_table_t *subscriptions, hc_topics_t topics, + unsigned connection_id); + +/** + * @brief Remove topic subscriptions for a connection. + * + * @param subscriptions The pointer to the subscription table + * @param topics Topics the connection wants to unsubscribe to + * @param connection_id Identifier of the connection + * @return int Number of removed subscriptions + */ +int subscription_table_remove_topics_for_connection( + subscription_table_t *subscriptions, hc_topics_t topics, + unsigned connection_id); + +/** + * @brief Get the topics a connection has subscribed to. + * + * @param subscriptions The pointer to the subscription table + * @param connection_id Identifier of the connection + * @return hc_topics_t + */ +hc_topics_t subscription_table_get_topics_for_connection( + subscription_table_t *subscriptions, unsigned connection_id); + +/** + * @brief Get the connections that have a subscription for the specified topic. + * + * @param subscriptions The pointer to the subscription table + * @param topic Topic to retrieve the subscriptions for + * @return unsigned* Array containing the connection ids associated with the + * specified topic + */ +unsigned *subscription_table_get_connections_for_topic( + subscription_table_t *subscriptions, hc_topic_t topic); + +/** + * @brief Print the subscription table containing, for each topic, the list + * of connections with a subsctiption. + * + * @param subscriptions The pointer to the subscription table + */ +void subscription_table_print(subscription_table_t *subscriptions); + +#endif // HICNLIGHT_SUBSCRIPTION_H
\ No newline at end of file diff --git a/hicn-light/src/hicn/core/system.h b/hicn-light/src/hicn/core/system.h index be6c3e7cf..860133dd3 100644 --- a/hicn-light/src/hicn/core/system.h +++ b/hicn-light/src/hicn/core/system.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -25,13 +25,12 @@ #define system_h #include <hicn/core/forwarder.h> -#include <hicn/utils/interfaceSet.h> /** * @function system_Interfaces * @abstract The system network interfaces */ -InterfaceSet *system_Interfaces(Forwarder *forwarder); +InterfaceSet *system_Interfaces(forwarder_t *forwarder); /** * Returns the MTU of the named interface @@ -43,7 +42,7 @@ InterfaceSet *system_Interfaces(Forwarder *forwarder); * @return positive the MTU the kernel reports * */ -unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName); +unsigned system_InterfaceMtu(forwarder_t *forwarder, const char *interfaceName); /** * Returns the LINK address of the specified interface @@ -55,6 +54,6 @@ unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName); * @retval null The interface does not exist * */ -Address *system_GetMacAddressByName(Forwarder *forwarder, +Address *system_GetMacAddressByName(forwarder_t *forwarder, const char *interfaceName); #endif diff --git a/hicn-light/src/hicn/core/ticks.h b/hicn-light/src/hicn/core/ticks.h index 8750abde5..0955569df 100644 --- a/hicn-light/src/hicn/core/ticks.h +++ b/hicn-light/src/hicn/core/ticks.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -25,7 +25,42 @@ #define __STDC_FORMAT_MACROS #include <stdint.h> +#include <time.h> + +#include <sys/param.h> // HZ + +#ifdef __APPLE__ +#include <mach/clock.h> +#include <mach/mach.h> +#define HZ 1000 +#endif typedef uint64_t Ticks; +// these will all be a little off because its all integer division +#define NSEC_PER_TICK ((1000000000ULL) / HZ) +#define NSEC_TO_TICKS(nsec) ((nsec < NSEC_PER_TICK) ? 1 : nsec / NSEC_PER_TICK) + +#define TICKS_TO_NSEC(ticks) ((1000000000ULL) * ticks / HZ) + +static inline Ticks ticks_now() { +#if __linux__ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC_RAW, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1e6; +#elif _WIN32 + struct timespec ts; + _clock_gettime(TIME_UTC, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1e6; +#else + clock_serv_t clockService; + mach_timespec_t ts; + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &clockService); + clock_get_time(clockService, &ts); + mach_port_deallocate(mach_task_self(), clockService); +#endif + + return ts.tv_sec * 1000 + ts.tv_nsec / 1e6; +} + #endif // ticks_h diff --git a/hicn-light/src/hicn/core/wldr.c b/hicn-light/src/hicn/core/wldr.c index ad3663d0d..aa1dbf4f9 100644 --- a/hicn-light/src/hicn/core/wldr.c +++ b/hicn-light/src/hicn/core/wldr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -13,93 +13,100 @@ * limitations under the License. */ -#include <parc/assert/parc_Assert.h> -#include <parc/logging/parc_LogReporterTextStdout.h> #include <hicn/core/connection.h> #include <hicn/core/forwarder.h> #include <hicn/core/wldr.h> #include <stdint.h> #include <stdio.h> -struct wldr_buffer { - Message *message; +typedef struct { + msgbuf_t *msgbuf; uint8_t rtx_counter; -}; - -typedef struct wldr_buffer WldrBuffer; +} wldr_buffer_t; -struct wldr_state { +struct wldr_s { uint16_t expected_label; uint16_t next_label; - WldrBuffer *buffer[BUFFER_SIZE]; + wldr_buffer_t *buffer[BUFFER_SIZE]; }; -Wldr *wldr_Init() { - Wldr *wldr = parcMemory_AllocateAndClear(sizeof(Wldr)); +wldr_t *wldr_create() { +#if 0 + wldr_t * wldr = parcMemory_AllocateAndClear(sizeof(Wldr)); parcAssertNotNull(wldr, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(Wldr)); wldr->expected_label = 1; wldr->next_label = 1; for (int i = 0; i < BUFFER_SIZE; i++) { - WldrBuffer *entry = parcMemory_AllocateAndClear(sizeof(WldrBuffer)); + wldr_buffer_t *entry = parcMemory_AllocateAndClear(sizeof(wldr_buffer_t)); parcAssertNotNull( entry, - "WldrBuffer init: parcMemory_AllocateAndClear(%zu) returned NULL", - sizeof(WldrBuffer)); - entry->message = NULL; + "wldr_buffer_t init: parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(wldr_buffer_t)); + entry->msgbuf = NULL; entry->rtx_counter = 0; wldr->buffer[i] = entry; } return wldr; +#else + return NULL; +#endif } -void wldr_ResetState(Wldr *wldr) { +void wldr_resset_state(wldr_t *wldr) { +#if 0 wldr->expected_label = 1; wldr->next_label = 1; for (int i = 0; i < BUFFER_SIZE; i++) { - wldr->buffer[i]->message = NULL; + wldr->buffer[i]->msgbuf = NULL; wldr->buffer[i]->rtx_counter = 0; } +#endif } -void wldr_Destroy(Wldr **wldrPtr) { - Wldr *wldr = *wldrPtr; +void wldr_free(wldr_t *wldr) { +#if 0 + wldr_t * wldr = *wldrPtr; for (unsigned i = 0; i < BUFFER_SIZE; i++) { - if (wldr->buffer[i]->message != NULL) { - message_Release(&(wldr->buffer[i]->message)); + if (wldr->buffer[i]->msgbuf != NULL) { + message_Release(&(wldr->buffer[i]->msgbuf)); parcMemory_Deallocate((void **)&(wldr->buffer[i])); } } parcMemory_Deallocate((void **)&wldr); *wldrPtr = NULL; +#endif } -static void _wldr_RetransmitPacket(Wldr *wldr, const Connection *conn, +#if 0 +static void _wldr_RetransmitPacket(wldr_t * wldr, const connection_t * conn, uint16_t label) { - if (wldr->buffer[label % BUFFER_SIZE]->message == NULL) { + if (wldr->buffer[label % BUFFER_SIZE]->msgbuf == NULL) { // the required message for retransmission is not in the buffer return; } if (wldr->buffer[label % BUFFER_SIZE]->rtx_counter < MAX_RTX) { - Message *msg = wldr->buffer[label % BUFFER_SIZE]->message; + msgbuf_t *msg = wldr->buffer[label % BUFFER_SIZE]->msgbuf; message_SetWldrLabel(msg, wldr->next_label); - if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { - message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf != NULL) { + msgbuf_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf)); } - wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = msg; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf = msg; wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = wldr->buffer[label % BUFFER_SIZE]->rtx_counter + 1; - message_Acquire(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message); + message_Acquire(wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf); wldr->next_label++; connection_ReSend(conn, msg, false); } } +#endif -static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, - Message *message, uint16_t expected_lbl, +#if 0 +static void _wldr_SendWldrNotification(wldr_t * wldr, const connection_t * conn, + msgbuf_t *msgbuf, uint16_t expected_lbl, uint16_t received_lbl) { // here we need to create a new packet that is used to send the wldr // notification to the prevoius hop. the destionation address of the @@ -113,38 +120,43 @@ static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, // this way the notification packet will be dispaced to the right connection // at the next hop. - Message *notification = - message_CreateWldrNotification(message, expected_lbl, received_lbl); + msgbuf_t *notification = + message_CreateWldrNotification(msgbuf, expected_lbl, received_lbl); parcAssertNotNull(notification, "Got null from CreateWldrNotification"); connection_ReSend(conn, notification, true); } +#endif -void wldr_SetLabel(Wldr *wldr, Message *message) { +void wldr_set_label(wldr_t *wldr, msgbuf_t *msgbuf) { +#if 0 // in this function we send the packet for the first time // 1) we set the wldr label - message_SetWldrLabel(message, wldr->next_label); + message_SetWldrLabel(msgbuf, wldr->next_label); // 2) we store the pointer to packet in the buffer - if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf != NULL) { // release an old message if necessary - message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf)); } // we need to acquire the message to avoid that it gets destroyed - message_Acquire(message); + message_Acquire(msgbuf); - wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = message; + wldr->buffer[wldr->next_label % BUFFER_SIZE]->msgbuf = msgbuf; wldr->buffer[wldr->next_label % BUFFER_SIZE]->rtx_counter = 0; wldr->next_label++; if (wldr->next_label == 0) // we alwasy skip label 0 beacause it means that wldr is not active wldr->next_label++; +#endif } -void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { - if (message_HasWldr(message)) { +void wldr_detect_losses(wldr_t *wldr, const connection_t *connection, + const msgbuf_t *msgbuf) { +#if 0 + if (message_HasWldr(msgbuf)) { // this is a normal wldr packet - uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(message); + uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(msgbuf); if (pkt_lbl != wldr->expected_label) { // if the received packet label is 1 and the expected packet label > // pkt_lbl usually we are in the case where a remote note disconnected for @@ -153,7 +165,7 @@ void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { // synch the labels if ((pkt_lbl != 1) || (wldr->expected_label < pkt_lbl)) { - _wldr_SendWldrNotificaiton(wldr, conn, message, wldr->expected_label, + _wldr_SendWldrNotificaiton(wldr, conn, msgbuf, wldr->expected_label, pkt_lbl); } @@ -165,12 +177,14 @@ void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { wldr->expected_label++; // for the next_label we want to skip 0 } } +#endif } -void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, - Message *message) { - uint16_t expected_lbl = (uint16_t)message_GetWldrExpectedLabel(message); - uint16_t received_lbl = (uint16_t)message_GetWldrLastReceived(message); +void wldr_handle_notification(wldr_t *wldr, const connection_t *connection, + const msgbuf_t *msgbuf) { +#if 0 + uint16_t expected_lbl = (uint16_t)message_GetWldrExpectedLabel(msgbuf); + uint16_t received_lbl = (uint16_t)message_GetWldrLastReceived(msgbuf); if ((wldr->next_label - expected_lbl) > BUFFER_SIZE) { // the packets are not in the buffer anymore return; @@ -179,4 +193,5 @@ void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, _wldr_RetransmitPacket(wldr, conn, expected_lbl); expected_lbl++; } +#endif } diff --git a/hicn-light/src/hicn/core/wldr.h b/hicn-light/src/hicn/core/wldr.h index e21889f63..7eccf39cd 100644 --- a/hicn-light/src/hicn/core/wldr.h +++ b/hicn-light/src/hicn/core/wldr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019 Cisco and/or its affiliates. + * Copyright (c) 2021 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: @@ -18,7 +18,7 @@ #include <hicn/hicn-light/config.h> #include <hicn/core/connection.h> -#include <hicn/core/message.h> +#include <hicn/core/msgbuf.h> #define BUFFER_SIZE 8192 #define MAX_RTX 3 @@ -34,19 +34,19 @@ // ATTENTION!!! in order to detect a notificaiton the // source and destination ports must be set to 0 -struct wldr_state; -typedef struct wldr_state Wldr; +typedef struct wldr_s wldr_t; -Wldr *wldr_Init(); +wldr_t *wldr_create(); -void wldr_Destroy(Wldr **wldrPtr); +void wldr_free(wldr_t *wldr); -void wldr_ResetState(Wldr *wldr); +void wldr_reset_state(wldr_t *wldr); -void wldr_SetLabel(Wldr *wldr, Message *message); +void wldr_set_label(wldr_t *wldr, msgbuf_t *msgbuf); -void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message); +void wldr_detect_losses(wldr_t *wldr, const connection_t *connection, + const msgbuf_t *msgbuf); -void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, - Message *message); +void wldr_handle_notification(wldr_t *wldr, const connection_t *connection, + const msgbuf_t *msgbuf); #endif // wldr_h |