diff options
author | Luca Muscariello <lumuscar+fdio@cisco.com> | 2019-01-17 13:47:57 +0100 |
---|---|---|
committer | Luca Muscariello <lumuscar+fdio@cisco.com> | 2019-01-17 16:32:51 +0100 |
commit | bac3da61644515f05663789b122554dc77549286 (patch) | |
tree | 898210bc8e70371d77de7d446a26c5dd4fd1165a /hicn-light/src/core | |
parent | d5165246787301d0f13b646fda5e8a8567aef5ac (diff) |
This is the first commit of the hicn projectv19.01
Change-Id: I6f2544ad9b9f8891c88cc4bcce3cf19bd3cc863f
Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Diffstat (limited to 'hicn-light/src/core')
33 files changed, 6747 insertions, 0 deletions
diff --git a/hicn-light/src/core/CMakeLists.txt b/hicn-light/src/core/CMakeLists.txt new file mode 100755 index 000000000..1d7dc03e9 --- /dev/null +++ b/hicn-light/src/core/CMakeLists.txt @@ -0,0 +1,55 @@ +# 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. + +cmake_minimum_required(VERSION 3.5 FATAL_ERROR) + +list(APPEND HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/connectionManager.h + ${CMAKE_CURRENT_SOURCE_DIR}/ticks.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionList.h + ${CMAKE_CURRENT_SOURCE_DIR}/connectionTable.h + ${CMAKE_CURRENT_SOURCE_DIR}/connection.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}/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 +) + +list(APPEND SOURCE_FILES + ${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}/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}/mapMe.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)
\ No newline at end of file diff --git a/hicn-light/src/core/connection.c b/hicn-light/src/core/connection.c new file mode 100755 index 000000000..073b7260f --- /dev/null +++ b/hicn-light/src/core/connection.c @@ -0,0 +1,265 @@ +/* + * 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 <src/config.h> +#include <stdio.h> + +#include <src/core/connection.h> +#include <src/core/messageHandler.h> +#include <src/core/ticks.h> +#include <src/core/wldr.h> +#include <src/io/addressPair.h> +#include <src/io/ioOperations.h> + +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> + +struct connection { + const AddressPair *addressPair; + IoOperations *ops; + + unsigned refCount; + + bool probing_active; + unsigned probing_interval; + unsigned counter; + Ticks last_sent; + Ticks delay; + + 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; +}; + +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; + conn->probing_active = false; + + conn->wldrAutoStart = true; + conn->probing_interval = 0; + conn->counter = 0; + conn->last_sent = 0; + conn->delay = INT_MAX; + return conn; +} + +Connection *connection_Acquire(Connection *connection) { + parcAssertNotNull(connection, "Parameter conn must be non-null"); + connection->refCount++; + return 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); + } + *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 (conn->wldr != NULL) { + wldr_SetLabel(conn->wldr, message); + } else { + message_ResetWldrLabel(message); + } + return ioOperations_Send(conn->ops, NULL, message); + } + return false; +} + +static void _sendProbe(Connection *conn, unsigned probeType, uint8_t *message) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + Ticks now = ioOperations_SendProbe(conn->ops, probeType, message); + if (now != 0) { + conn->last_sent = now; + } + } else { + ioOperations_SendProbe(conn->ops, probeType, message); + } +} + +void connection_Probe(Connection *conn) { + _sendProbe(conn, PACKET_TYPE_PROBE_REQUEST, NULL); +} + +void connection_HandleProbe(Connection *conn, uint8_t *probe, + Ticks actualTime) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + parcAssertNotNull(probe, "Parameter pkt must be non-null"); + + uint8_t probeType = messageHandler_GetProbePacketType(probe); + if (probeType == PACKET_TYPE_PROBE_REQUEST) { + _sendProbe(conn, PACKET_TYPE_PROBE_REPLY, probe); + } else if (probeType == PACKET_TYPE_PROBE_REPLY) { + Ticks delay = actualTime - conn->last_sent; + if (delay == 0) { + delay = 1; + } + if (delay < conn->delay) { + conn->delay = delay; + } + } else { + printf("receivde unkwon probe type\n"); + } +} + +uint64_t connection_GetDelay(Connection *conn) { return (uint64_t)conn->delay; } + +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); +} + +const void *connection_Class(const Connection *conn) { + parcAssertNotNull(conn, "Parameter conn must be non-null"); + return ioOperations_Class(conn->ops); +} + +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); + } + } + + if (notification) { + // the notification is never destroyed + message_Release(&message); + } + + return res; +} + +void connection_AllowWldrAutoStart(Connection *conn, bool allow) { + conn->wldrAutoStart = allow; +} + +void connection_EnableWldr(Connection *conn) { + if (!connection_IsLocal(conn)) { + if (conn->wldr == NULL) { + printf("----------------- enable wldr\n"); + conn->wldr = wldr_Init(); + } + } +} + +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_HasWldr(const Connection *conn) { + if (conn->wldr == NULL) { + return false; + } else { + return true; + } +} + +bool connection_WldrAutoStartAllowed(const Connection *conn) { + return conn->wldrAutoStart; +} + +void connection_DetectLosses(Connection *conn, Message *message) { + if (conn->wldr != NULL) wldr_DetectLosses(conn->wldr, conn, message); +} + +void connection_HandleWldrNotification(Connection *conn, Message *message) { + if (conn->wldr != NULL) + wldr_HandleWldrNotification(conn->wldr, conn, message); +} diff --git a/hicn-light/src/core/connection.h b/hicn-light/src/core/connection.h new file mode 100755 index 000000000..b5c703527 --- /dev/null +++ b/hicn-light/src/core/connection.h @@ -0,0 +1,148 @@ +/* + * 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 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. + * + */ + +#ifndef connection_h +#define connection_h +#include <src/config.h> +#include <src/io/ioOperations.h> +#include <src/utils/address.h> + +// packet types for probing +#define PACKET_TYPE_PROBE_REQUEST 5 +#define PACKET_TYPE_PROBE_REPLY 6 + +struct connection; +typedef struct connection Connection; + +/** + * Creates a connection object. + */ +Connection *connection_Create(IoOperations *ops); + +/** + * @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); + +/** + * @function connection_Acquire + * @abstract A reference counted copy. + * @discussion + * A shallow copy, they share the same memory. + */ +Connection *connection_Acquire(Connection *connection); + +/** + * @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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +/** + * 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); + +bool connection_ReSend(const Connection *conn, Message *message, + bool notification); + +void connection_Probe(Connection *conn); + +void connection_HandleProbe(Connection *conn, uint8_t *message, + Ticks actualTime); + +uint64_t connection_GetDelay(Connection *conn); + +void connection_AllowWldrAutoStart(Connection *conn, bool allow); + +void connection_EnableWldr(Connection *conn); + +void connection_DisableWldr(Connection *conn); + +bool connection_HasWldr(const Connection *conn); + +bool connection_WldrAutoStartAllowed(const Connection *conn); + +void connection_DetectLosses(Connection *conn, Message *message); + +void connection_HandleWldrNotification(Connection *conn, Message *message); +#endif // connection_h diff --git a/hicn-light/src/core/connectionList.c b/hicn-light/src/core/connectionList.c new file mode 100755 index 000000000..b2913fa05 --- /dev/null +++ b/hicn-light/src/core/connectionList.c @@ -0,0 +1,68 @@ +/* + * 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 <src/config.h> +#include <stdio.h> + +#include <parc/algol/parc_ArrayList.h> +#include <parc/algol/parc_Memory.h> + +#include <parc/assert/parc_Assert.h> +#include <src/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/core/connectionList.h b/hicn-light/src/core/connectionList.h new file mode 100755 index 000000000..cdca12993 --- /dev/null +++ b/hicn-light/src/core/connectionList.h @@ -0,0 +1,70 @@ +/* + * 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 <src/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/core/connectionManager.c b/hicn-light/src/core/connectionManager.c new file mode 100755 index 000000000..2089e1495 --- /dev/null +++ b/hicn-light/src/core/connectionManager.c @@ -0,0 +1,196 @@ +/* + * 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 <src/config.h> +#include <src/core/connectionManager.h> +#include <src/core/forwarder.h> +#include <src/messenger/messenger.h> +#include <src/messenger/messengerRecipient.h> +#include <src/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/core/connectionManager.h b/hicn-light/src/core/connectionManager.h new file mode 100755 index 000000000..b77553e0d --- /dev/null +++ b/hicn-light/src/core/connectionManager.h @@ -0,0 +1,37 @@ +/* + * 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 <src/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/core/connectionTable.c b/hicn-light/src/core/connectionTable.c new file mode 100755 index 000000000..ba0942ddb --- /dev/null +++ b/hicn-light/src/core/connectionTable.c @@ -0,0 +1,224 @@ +/* + * 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 + * + */ + +#include <src/config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.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 <src/core/connectionTable.h> +#include <src/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(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/core/connectionTable.h b/hicn-light/src/core/connectionTable.h new file mode 100755 index 000000000..30517ae1d --- /dev/null +++ b/hicn-light/src/core/connectionTable.h @@ -0,0 +1,99 @@ +/* + * 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 <src/core/connection.h> +#include <src/core/connectionList.h> +#include <src/io/addressPair.h> +#include <src/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(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/core/dispatcher.c b/hicn-light/src/core/dispatcher.c new file mode 100755 index 000000000..078087c59 --- /dev/null +++ b/hicn-light/src/core/dispatcher.c @@ -0,0 +1,435 @@ +/* + * 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. + */ + +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> +#include <signal.h> +#include <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <sys/socket.h> + +#include <parc/algol/parc_EventQueue.h> +#include <parc/algol/parc_EventTimer.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/core/dispatcher.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 + + 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; + } + 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/core/dispatcher.h b/hicn-light/src/core/dispatcher.h new file mode 100755 index 000000000..35d804a00 --- /dev/null +++ b/hicn-light/src/core/dispatcher.h @@ -0,0 +1,286 @@ +/* + * 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 + +#include <stdbool.h> +#include <sys/socket.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 <src/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 <src/core/streamBuffer.h> +#include <src/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/core/forwarder.c b/hicn-light/src/core/forwarder.c new file mode 100755 index 000000000..cb94af3b5 --- /dev/null +++ b/hicn-light/src/core/forwarder.c @@ -0,0 +1,499 @@ +/* + * 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. + */ + +/** + * Event based router + * + * This module is the glue around the event scheduler. + * Its the packet i/o module. + * + * Packet processing is done in dispatcher.c, which is the actual wrapper around + * the event scheduler + */ + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include <arpa/inet.h> +#include <sys/socket.h> + +#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 <src/core/connectionManager.h> +#include <src/core/connectionTable.h> +#include <src/core/dispatcher.h> +#include <src/core/forwarder.h> +#include <src/core/messagePacketType.h> +#ifdef WITH_MAPME +#include <src/core/mapMe.h> +#endif /* WITH_MAPME */ +#include <src/config/configuration.h> +#include <src/config/configurationFile.h> +#include <src/config/configurationListeners.h> +#include <src/processor/messageProcessor.h> + +#include <src/core/wldr.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 { + Dispatcher *dispatcher; + + uint16_t server_port; + + PARCEventSignal *signal_int; + PARCEventSignal *signal_term; + PARCEventSignal *signal_usr1; + PARCEventTimer *keepalive_event; + + // will skew the virtual clock forward. In normal operaiton, it is 0. + Ticks clockOffset; + + unsigned nextConnectionid; + Messenger *messenger; + ConnectionManager *connectionManager; + ConnectionTable *connectionTable; + ListenerSet *listenerSet; + Configuration *config; + + // we'll eventually want to setup a threadpool of these + MessageProcessor *processor; + + Logger *logger; + + PARCClock *clock; + + hicn_socket_helper_t + *hicnSocketHelper; // state required to manage hicn connections + + // used by seed48 and nrand48 + unsigned short seed[3]; + +#ifdef WITH_MAPME + MapMe *mapme; +#endif /* WITH_MAPME */ +}; + +// 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) { + int fd; + ssize_t res; + + res = -1; + fd = open("/dev/urandom", O_RDONLY); + if (fd != -1) { + res = read(fd, forwarder->seed, sizeof(forwarder->seed)); + close(fd); + } + if (res != sizeof(forwarder->seed)) { + forwarder->seed[1] = (unsigned short)getpid(); /* better than no entropy */ + forwarder->seed[2] = (unsigned short)time(NULL); + } + /* + * The call to seed48 is needed by cygwin, and should be harmless + * on other platforms. + */ + seed48(forwarder->seed); +} + +Logger *forwarder_GetLogger(const Forwarder *forwarder) { + return forwarder->logger; +} + +// ============================================================================ +// Setup and destroy section + +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->clock = parcClock_Monotonic(); + forwarder->clockOffset = 0; + + 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->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); + + forwarder->signal_usr1 = dispatcher_CreateSignalEvent( + forwarder->dispatcher, _signal_cb, forwarder, SIGPIPE); + dispatcher_StartSignalEvent(forwarder->dispatcher, forwarder->signal_usr1); + +#ifndef __APPLE__ + forwarder->hicnSocketHelper = hicn_create(); + if (forwarder->hicnSocketHelper == NULL) return NULL; +#endif /* __APPLE__ */ + /* ignore child */ + signal(SIGCHLD, SIG_IGN); + + /* ignore tty signals */ + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + + // We no longer use this for ticks, but we need to have at least one event + // schedule to keep Libevent happy. + + struct timeval wtnow_timeout; + timerclear(&wtnow_timeout); + + wtnow_timeout.tv_sec = 0; + wtnow_timeout.tv_usec = 50000; // 20 Hz keepalive + +#ifdef WITH_MAPME + if (!(mapMe_Init(&forwarder->mapme, forwarder))) return NULL; +#endif /* WITH_MAPME */ + + 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); + + return forwarder; +} + +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__) + hicn_destroy(); +#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)); + + // the messenger is used by many of the other pieces, so destroy it last + messenger_Destroy(&(forwarder->messenger)); + + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_int)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_term)); + dispatcher_DestroySignalEvent(forwarder->dispatcher, + &(forwarder->signal_usr1)); + + parcClock_Release(&forwarder->clock); + logger_Release(&forwarder->logger); + + // do the dispatcher last + dispatcher_Destroy(&(forwarder->dispatcher)); + + parcMemory_Deallocate((void **)&forwarder); + *ptr = NULL; +} + +void forwarder_SetupAllListeners(Forwarder *forwarder, uint16_t port, + const char *localPath) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + + configurationListeners_SetupAll(forwarder->config, port, localPath); +} + +void forwarder_SetupFromConfigFile(Forwarder *forwarder, const char *filename) { + ConfigurationFile *configFile = configurationFile_Create(forwarder, filename); + if (configFile) { + configurationFile_Process(configFile); + configurationFile_Release(&configFile); + } +} + +Configuration *forwarder_GetConfiguration(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->config; +} + +// ============================================================================ + +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->nextConnectionid++; +} + +Messenger *forwarder_GetMessenger(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->messenger; +} + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->dispatcher; +} + +ConnectionTable *forwarder_GetConnectionTable(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->connectionTable; +} + +ListenerSet *forwarder_GetListenerSet(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return forwarder->listenerSet; +} + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheStoreFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheStoreFlag(forwarder->processor); +} + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + messageProcessor_SetCacheServeFlag(forwarder->processor, val); +} + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return messageProcessor_GetCacheServeFlag(forwarder->processor); +} + +void forwarder_ReceiveCommand(Forwarder *forwarder, command_id command, + struct iovec *message, unsigned ingressId) { + configuration_ReceiveCommand(forwarder->config, command, message, ingressId); +} + +void forwarder_Receive(Forwarder *forwarder, Message *message) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + + // this takes ownership of the message, so we're done here + + // 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)); + + if (!conn) { + return; + } + + if (message_HasWldr(message)) { + if (connection_HasWldr(conn)) { + // case 1: WLDR is enabled + connection_DetectLosses((Connection *)conn, message); + } else if (!connection_HasWldr(conn) && + connection_WldrAutoStartAllowed(conn)) { + // case 2: We are on an AP. We enable WLDR + connection_EnableWldr((Connection *)conn); + connection_DetectLosses((Connection *)conn, message); + } + // case 3: Ignore WLDR + } else { + if (connection_HasWldr(conn) && connection_WldrAutoStartAllowed(conn)) { + // case 1: STA do not use WLDR, we disable it + connection_DisableWldr((Connection *)conn); + } + } + + messageProcessor_Receive(forwarder->processor, message); +} + +Ticks forwarder_GetTicks(const Forwarder *forwarder) { + parcAssertNotNull(forwarder, "Parameter must be non-null"); + return parcClock_GetTime(forwarder->clock) + forwarder->clockOffset; +} + +Ticks forwarder_NanosToTicks(uint64_t nanos) { return NSEC_TO_TICKS(nanos); } + +uint64_t forwarder_TicksToNanos(Ticks ticks) { + return (1000000000ULL) * ticks / HZ; +} + +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"); + + // we only have one message processor + bool res = + messageProcessor_AddOrUpdateRoute(forwarder->processor, control, ifidx); + + return res; +} + +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"); + + // we only have one message processor + return messageProcessor_RemoveRoute(forwarder->processor, control, ifidx); +} + +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + messageProcessor_RemoveConnectionIdFromRoutes(forwarder->processor, + connectionId); +} + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy) { + parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + // if (strategy == NULL) { + // strategy = SET_STRATEGY_RANDOM; + // } + + processor_SetStrategy(forwarder->processor, prefix, strategy); +} + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder) { + return messageProcessor_GetFibEntries(forwarder->processor); +} + +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize) { + messageProcessor_SetContentObjectStoreSize(forwarder->processor, + maximumContentStoreSize); +} + +void forwarder_ClearCache(Forwarder *forwarder) { + messageProcessor_ClearCache(forwarder->processor); +} + +PARCClock *forwarder_GetClock(const Forwarder *forwarder) { + return forwarder->clock; +} + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder) { + return forwarder->hicnSocketHelper; +} + +// ======================================================= + +static void _signal_cb(int sig, PARCEventType events, void *user_data) { + Forwarder *forwarder = (Forwarder *)user_data; + + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "signal %d events %d", sig, events); + + switch ((int)sig) { + case SIGTERM: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an terminate signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGINT: + logger_Log(forwarder->logger, LoggerFacility_Core, PARCLogLevel_Warning, + __func__, "Caught an interrupt signal; exiting cleanly."); + dispatcher_Stop(forwarder->dispatcher); + break; + + case SIGUSR1: + // dump stats + break; + + default: + 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 +} + +#ifdef WITH_MAPME +FIB *forwarder_getFib(Forwarder *forwarder) { + return messageProcessor_getFib(forwarder->processor); +} + +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn) { + mapMe_onConnectionAdded(forwarder->mapme, conn); +} + +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn) {} + +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id) { + mapMe_Process(forwarder->mapme, msgBuffer, conn_id); +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/forwarder.h b/hicn-light/src/core/forwarder.h new file mode 100755 index 000000000..ad3f9756b --- /dev/null +++ b/hicn-light/src/core/forwarder.h @@ -0,0 +1,287 @@ +/* + * 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 methods in this header are for the non-threaded forwarder. They should + * only be called within the forwarders thread of execution. + */ + +#ifndef forwarder_h +#define forwarder_h + +#include <stdlib.h> +#include <sys/time.h> + +#include <src/core/connectionTable.h> +#include <src/core/dispatcher.h> +#include <src/messenger/messenger.h> + +#include <src/core/message.h> + +#include <src/config/configuration.h> + +#ifdef WITH_MAPME +#include <src/processor/fib.h> +#endif /* WITH_MAPME */ + +#include <src/core/logger.h> +#include <src/core/ticks.h> +#include <src/io/listenerSet.h> + +#include <src/processor/fibEntryList.h> + +#include <parc/algol/parc_Clock.h> + +#include <src/socket/api.h> + +#define PORT_NUMBER 9695 +#define PORT_NUMBER_AS_STRING "9695" + +#include <src/utils/commands.h> + +// ============================================== + +struct forwarder; +typedef struct forwarder Forwarder; + + +/** + * @function forwarder_Create + * @abstract 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); + +/** + * @function forwarder_Destroy + * @abstract Destroys the forwarder, stopping all traffic and freeing all memory + */ +void forwarder_Destroy(Forwarder **ptr); + +/** + * @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); + +/** + * 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); + +/** + * @function forwarder_GetNextConnectionId + * @abstract Get the next identifier for a new connection + */ +unsigned forwarder_GetNextConnectionId(Forwarder *forwarder); + +Messenger *forwarder_GetMessenger(Forwarder *forwarder); + +Dispatcher *forwarder_GetDispatcher(Forwarder *forwarder); + +/** + * Returns the set of currently active listeners + * + * @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); + +/** + * Returns the forwrder's connection table + * + * @param [in] forwarder An allocated hicn-light forwarder + * + * @retval non-null The connection tabler + * @retval null An error + * + */ +ConnectionTable *forwarder_GetConnectionTable(Forwarder *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); + +/** + * 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); + +/** + * Convert nano seconds to Ticks + * + * Converts nano seconds to Ticks, based on HZ (in forwarder.c) + */ +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_Receive(Forwarder *forwarder, Message *mesage); + +/** + * @function forwarder_AddOrUpdateRoute + * @abstract Adds or updates a route on all the message processors + */ +bool forwarder_AddOrUpdateRoute(Forwarder *forwarder, + add_route_command *control, unsigned ifidx); + +/** + * @function forwarder_RemoveRoute + * @abstract Removes a route from all the message processors + */ +bool forwarder_RemoveRoute(Forwarder *forwarder, remove_route_command *control, + unsigned ifidx); + +/** + * Removes a connection id from all routes + */ +void forwarder_RemoveConnectionIdFromRoutes(Forwarder *forwarder, + unsigned connectionId); + +/** + * @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); + +FibEntryList *forwarder_GetFibEntries(Forwarder *forwarder); + +/** + * Sets the maximum number of content objects in the content store + * + * Implementation dependent - may wipe the cache. + */ +void forwarder_SetContentObjectStoreSize(Forwarder *forwarder, + size_t maximumContentStoreSize); + +void forwarder_SetChacheStoreFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheStoreFlag(Forwarder *forwarder); + +void forwarder_SetChacheServeFlag(Forwarder *forwarder, bool val); + +bool forwarder_GetChacheServeFlag(Forwarder *forwarder); + +void forwarder_ClearCache(Forwarder *forwarder); + +void forwarder_SetStrategy(Forwarder *forwarder, Name *prefix, + strategy_type strategy); + +hicn_socket_helper_t *forwarder_GetHIcnSocketHelper(Forwarder *forwarder); + +#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); + +/** + * @function forwarder_onConnectionAdded + * @abstract 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. + */ +void forwarder_onConnectionAdded(Forwarder *forwarder, const Connection *conn); + +/** + * @function forwarder_onConnectionRemoved + * @abstract Callback fired upon removal of a connection through the control + * protocol. + * @param [in] forwarder - Pointer to the hICN forwarder. + * @param [in] conn - Pointer to the removed connection. + */ +void forwarder_onConnectionRemoved(Forwarder *forwarder, + const Connection *conn); + +/** + * @function forwarder_ProcessMapMe + * @abstract 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 + */ +void forwarder_ProcessMapMe(Forwarder *forwarder, uint8_t *msgBuffer, + unsigned conn_id); + +#endif /* WITH_MAPME */ + +#endif // forwarder_h diff --git a/hicn-light/src/core/logger.c b/hicn-light/src/core/logger.c new file mode 100755 index 000000000..cac3000e2 --- /dev/null +++ b/hicn-light/src/core/logger.c @@ -0,0 +1,173 @@ +/* + * 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 <src/config.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.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 <src/core/forwarder.h> +#include <src/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 = 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/core/logger.h b/hicn-light/src/core/logger.h new file mode 100755 index 000000000..e2ab7e147 --- /dev/null +++ b/hicn-light/src/core/logger.h @@ -0,0 +1,168 @@ +/* + * 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 + +#include <parc/algol/parc_Buffer.h> +#include <parc/logging/parc_LogLevel.h> +#include <parc/logging/parc_LogReporter.h> +#include <stdarg.h> +#include <sys/time.h> + +#include <parc/algol/parc_Clock.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_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/core/mapMe.c b/hicn-light/src/core/mapMe.c new file mode 100755 index 000000000..4444bcf15 --- /dev/null +++ b/hicn-light/src/core/mapMe.c @@ -0,0 +1,816 @@ +/* + * 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 mapMe.c + * @brief MAP-Me : AnchorLess Producer Mobility Management. + */ + +#ifdef WITH_MAPME + +#include <hicn/hicn.h> +#include <src/core/mapMe.h> +#include <stdio.h> // printf + +#include <src/core/connectionList.h> +#include <src/core/forwarder.h> +#include <src/core/logger.h> +#include <src/core/message.h> +#include <src/core/messagePacketType.h> // packet types +#include <src/core/ticks.h> +#include <src/processor/fibEntry.h> +#include <src/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> + +#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 NOT_A_NOTIFICATION false +#define NO_INGRESS 0 +#define TIMER_NO_REPEAT false + +#define DO_DISCOVERY 1 +#define MAPME_INVALID_DICOVERY_SEQ -1 + +#define LOG_FACILITY LoggerFacility_Core + +#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) + +#define WARN(mapme, fmt, ...) \ + LOG(mapme, PARCLogLevel_Warning, fmt, ##__VA_ARGS__) +#define ERROR(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__) + +/** + * MAP-Me state data structure + */ +struct mapme { + uint32_t retx; /* ms */ + uint32_t Tu; /* ms */ + bool removeFibEntries; + + Forwarder *forwarder; +}; + +static MapMe MapMeDefault = {.retx = MAPME_DEFAULT_RETX, + .Tu = MAPME_DEFAULT_TU, + .removeFibEntries = false}; + +/******************************************************************************/ + +#include <src/core/connection.h> + +bool mapMe_Init(MapMe **mapme, Forwarder *forwarder) { + *mapme = malloc(sizeof(MapMe)); + if (!mapme) goto ERR_MALLOC; + + /* Internal state : set default values */ + memcpy(*mapme, &MapMeDefault, sizeof(MapMe)); + + (*mapme)->forwarder = forwarder; + + /* Install hook on Face events to onConnectionAdded */ + // see. config/configuration.c + + /* Install hook for signalization processing. See : + * - io/hicnListener.c + * - src/core/connection.{c,h} + */ + + ERROR((*mapme), "MapMe"); + + return true; + +ERR_MALLOC: + return false; +} + +/****************************************************************************** + * TFIB + ******************************************************************************/ + +#define INVALID_SEQ 0 +#define INIT_SEQ 1 + +typedef struct { + uint32_t seq; + PARCHashMap *nexthops; + /* Update/Notification heuristic */ + Ticks lastAckedUpdate; // XXX This is only for producer !!! +} MapMeTFIB; + +static MapMeTFIB *mapMeTFIB_Create() { + MapMeTFIB *tfib; + tfib = malloc(sizeof(MapMeTFIB)); + if (!tfib) goto ERR_MALLOC; + tfib->seq = 0; + tfib->lastAckedUpdate = 0; + tfib->nexthops = parcHashMap_Create(); + if (!tfib->nexthops) goto ERR_HASHMAP; + + return tfib; + +ERR_HASHMAP: + free(tfib); +ERR_MALLOC: + return NULL; +} + +void mapMeTFIB_Release(MapMeTFIB **tfibPtr) { + MapMeTFIB *tfib = *tfibPtr; + parcHashMap_Release(&tfib->nexthops); + free(tfib); + *tfibPtr = NULL; +} + +/** + * @function mapMe_CreateTFIB + * @abstract Associate a new TFIB entry to a FIB entry. + * @param [in] - Pointer to the FIB entry. + * @return Boolean indicating the success of the operation. + */ +static void mapMe_CreateTFIB(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, tfib, (void (*)(void **))mapMeTFIB_Release); +} + +#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) return NULL; + PARCByteArray *array = parcBuffer_Array(buffer); + timer = *((PARCEventTimer **)parcByteArray_Array(array)); + parcUnsigned_Release(&cid); + return timer; +} + +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); +} + +static void mapMeTFIB_Remove(MapMeTFIB *tfib, unsigned conn_id) { + // Who releases the timer ? + PARCUnsigned *cid = parcUnsigned_Create(conn_id); + parcHashMap_Remove(tfib->nexthops, cid); + parcUnsigned_Release(&cid); +} + +static PARCIterator *mapMeTFIB_CreateKeyIterator(const MapMeTFIB *tfib) { + return parcHashMap_CreateKeyIterator(tfib->nexthops); +} + +int hicn_prefix_from_name(const Name *name, hicn_prefix_t *prefix) { + NameBitvector *bv = name_GetContentName(name); + ip_address_t ip_address; + nameBitvector_ToIPAddress(bv, &ip_address); + + /* The name length will be equal to ip address' prefix length */ + return hicn_prefix_create_from_ip_address(&ip_address, prefix); +} + +static Message *mapMe_createMessage(const MapMe *mapme, const Name *name, + mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(forwarder_GetLogger(mapme->forwarder)); + + INFO(mapme, "[MAP-Me] CreateMessage type=%d seq=%d", params->type, + params->seq); + + size_t size = (params->protocol == IPPROTO_IPV6) ? HICN_MAPME_V6_HDRLEN + : HICN_MAPME_V4_HDRLEN; + uint8_t *icmp_pkt = parcMemory_AllocateAndClear(size); + + hicn_prefix_t prefix; + int rc = hicn_prefix_from_name(name, &prefix); + if (rc < 0) { + ERROR(mapme, "[MAP-Me] Failed to create lib's name"); + goto ERR_NAME; + } + + INFO(mapme, "[MAP-Me] Creating MAP-Me packet"); + size_t len = hicn_mapme_create_packet(icmp_pkt, &prefix, params); + if (len != 0) { + ERROR(mapme, "[MAP-Me] Failed to create mapme packet through lib"); + goto ERR_CREATE; + } + + // hicn_packet_dump(icmp_pkt, MAPME_HDRLEN); + + return message_CreateFromByteArray(NO_INGRESS, icmp_pkt, + MessagePacketType_Interest, now, logger); + +ERR_CREATE: +ERR_NAME: + return NULL; +} + +static Message *mapMe_createAckMessage(const MapMe *mapme, + const uint8_t *msgBuffer, + const mapme_params_t *params) { + Ticks now = forwarder_GetTicks(mapme->forwarder); + Logger *logger = logger_Acquire(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) { + ERROR(mapme, "[MAP-Me] Failed to create mapme ack packet through lib"); + return NULL; + } + + return message_CreateFromByteArray( + NO_INGRESS, icmp_pkt, MessagePacketType_ContentObject, now, logger); +} + +struct setFacePendingArgs { + const MapMe *mapme; + const Name *name; + FibEntry *fibEntry; + unsigned conn_id; + bool send; + bool is_first; + uint32_t num_retx; +}; + +static bool mapMe_setFacePending(const MapMe *mapme, const Name *name, + FibEntry *fibEntry, unsigned conn_id, + bool send, bool is_first, 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); + + INFO(args->mapme, "Timeout during retransmission. Re-sending"); + mapMe_setFacePending(args->mapme, args->name, args->fibEntry, args->conn_id, + args->send, args->is_first, args->num_retx); +} + +/** + * @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_first, uint32_t num_retx) { + int rc; + + INFO(mapme, "[MAP-Me] SetFacePending connection=%d prefix=XX retx=%d", + conn_id, num_retx); + + /* 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; + + // 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_first || send) { + // XXX + mapme_params_t params = { + .protocol = IPPROTO_IPV6, + .type = is_first ? 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; + } + + const ConnectionTable *table = + forwarder_GetConnectionTable(mapme->forwarder); + const Connection *conn = + connectionTable_FindById((ConnectionTable *)table, conn_id); + if (conn) { + INFO(mapme, "[MAP-Me] Sending MAP-Me packet"); + connection_ReSend(conn, special_interest, NOT_A_NOTIFICATION); + } else { + INFO(mapme, "[MAP-Me] Stopped retransmissions as face went down"); + } + + 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_first = is_first; + 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) goto ERR_TIMER; + } else { + INFO(mapme, "[MAP-Me] Last retransmission."); + timer = NULL; + } + } else { + INFO(mapme, "[MAP-Me] - not forwarding as send is False"); + timer = NULL; + } + + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_id); + if (oldTimer) { + INFO(mapme, "[MAP-Me] - Found old timer, would need to cancel !"); + // parcEventTimer_Stop(oldTimer); + } + INFO(mapme, "[MAP-Me] - Putting new timer in TFIB"); + if (timer) mapMeTFIB_Put(TFIB(fibEntry), conn_id, timer); + + return true; + +ERR_MALLOC: +ERR_TIMER: + return false; +} + +/*------------------------------------------------------------------------------ + * Event handling + *----------------------------------------------------------------------------*/ + +/* + * 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; +} + +/* + * Callback called everytime a new connection is created by the control protocol + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn_added) { + /* bool ret; */ + FibEntryList *fiblist; + + /* Ignore local connections corresponding to applications for now */ + if (connection_IsLocal(conn_added)) return; + + unsigned conn_added_id = connection_GetConnectionId(conn_added); + INFO(mapme, "[MAP-Me] New connection %d", conn_added_id); + + /* + * Iterate on FIB to find locally served prefix + * Ideally, we want to avoid a FIB scan everytime a face is added/removed + */ + fiblist = forwarder_GetFibEntries(mapme->forwarder); + for (size_t i = 0; i < fibEntryList_Length(fiblist); i++) { + FibEntry *fibEntry = (FibEntry *)fibEntryList_Get(fiblist, i); + const Name *name = fibEntry_GetPrefix(fibEntry); + + /* Skip entries that have no local connection as next hop */ + if (!mapMe_hasLocalNextHops(mapme, fibEntry)) continue; + + /* This entry corresponds to a locally served prefix, set + * Special Interest */ + if (!TFIB(fibEntry)) /* Create TFIB associated to FIB entry */ + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq++; + + char *name_str = name_ToString(name); + INFO(mapme, "[MAP-Me] sending IU/IN for name %s on connection %d", name_str, + conn_added_id); + free(name_str); + + mapMe_setFacePending(mapme, name, fibEntry, conn_added_id, true, true, 0); + } +} + +/*------------------------------------------------------------------------------ + * 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); + 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); + + /* + * Immediately send an acknowledgement back on the ingress connection + * We always ack, even duplicates. + */ + Message *ack = mapMe_createAckMessage(mapme, msgBuffer, params); + if (!ack) goto ERR_ACK_CREATE; + rv = connection_ReSend(conn_in, ack, NOT_A_NOTIFICATION); + if (!rv) goto ERR_ACK_SEND; + message_Release(&ack); + + /* EPM on FIB */ + /* only the processor has access to the FIB */ + FIB *fib = forwarder_getFib(mapme->forwarder); + + FibEntry *fibEntry = fib_Contains(fib, name); + if (!fibEntry) { + 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; + + fibEntry = fibEntry_Create(name, fwdStrategy); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + mapMe_CreateTFIB(fibEntry); + TFIB(fibEntry)->seq = seq; // INIT_SEQ; + fib_Add(fib, fibEntry); + return true; // with proper seq, we are done + + } else if (!TFIB(fibEntry)) { + /* Create TFIB associated to FIB entry */ + INFO(mapme, + "[MAP-Me] - Creating TFIB entry with default sequence number"); + mapMe_CreateTFIB(fibEntry); + } + + 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)); + 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, 0); + } + parcIterator_Release(&iterator); + } + + /* 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); + + /* We make a copy to be able to send IU _after_ updating next hops */ + NumberSet *nexthops = numberSet_Create(); + numberSet_AddSet(nexthops, nexthops_old); + + /* 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. + * + * 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 + */ + + INFO(mapme, "[MAP-Me] - (3/3) next hops ~~> prev hops"); + PARCEventTimer *oldTimer = + (PARCEventTimer *)mapMeTFIB_Get(TFIB(fibEntry), conn_in_id); + if (oldTimer) { + /* This happens if we receive an IU while we are still sending + * one in the other direction + */ + INFO(mapme, "[MAP-Me] - Canceled pending timer"); + parcEventTimer_Stop(oldTimer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + } + + /* Remove all next hops */ + for (size_t k = 0; k < numberSet_Length(nexthops_old); k++) { + unsigned conn_id = numberSet_GetItem(nexthops_old, k); + INFO(mapme, "[MAP-Me] - Replaced next hops by connection %d", conn_id); + fibEntry_RemoveNexthopByConnectionId(fibEntry, conn_id); + } + fibEntry_AddNexthopByConnectionId(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; + } + + 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, 0); + complete = false; + } + + /* + * The update is completed when the IU could not be sent to any + * other next hop. + */ + if (complete) INFO(mapme, "[MAP-Me] - Update completed !"); + + numberSet_Release(&nexthops); + + } else if (seq == fibSeq) { + /* + * Multipath, multihoming, multiple producers or duplicate interest + * + * 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 + * + * It might happen that the previous AP has still a connection to the + * producer and that we received back our own IU. In that case, we just + * need to Ack and ignore it. + */ + if (mapMe_hasLocalNextHops(mapme, fibEntry)) { + INFO(mapme, "[MAP-Me] - Received original interest... Update complete"); + return true; + } + + INFO(mapme, "[MAP-Me] - Adding multipath next hop on connection %d", + conn_in_id); + fibEntry_AddNexthopByConnectionId(fibEntry, conn_in_id); + + } else { // seq < fibSeq + /* + * 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, 0); + } + + return true; + +ERR_ACK_SEND: + message_Release(&ack); +ERR_ACK_CREATE: + return false; +} + +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); + + const Name *name = + name_CreateFromPacket(msgBuffer, MessagePacketType_Interest); + + FIB *fib = forwarder_getFib(mapme->forwarder); + FibEntry *fibEntry = fib_Contains(fib, name); + 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) { + INFO(mapme, + "[MAP-Me] - Ignored special interest Ack with seq=%u, expected %u", + seq, fibSeq); + return; + } + } + + /* + * 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 + */ + 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 ?"); + return; + } + + /* Stop timer and remove entry from TFIB */ + parcEventTimer_Stop(timer); + mapMeTFIB_Remove(TFIB(fibEntry), conn_in_id); + + INFO(mapme, "[MAP-Me] - Removing TFIB entry for ack on connection %d", + conn_in_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 *msgBuffer) { + uint8_t next_header = messageHandler_NextHeaderType(msgBuffer); + + const uint8_t *icmp_ptr; + if (next_header == IPPROTO_ICMP) { + icmp_ptr = msgBuffer + IPV4_HDRLEN; + } else if (next_header == IPPROTO_ICMPV6) { + icmp_ptr = msgBuffer + IPV6_HDRLEN; + } else { + return false; + } + + uint8_t type = ((_icmp_header_t *)icmp_ptr)->type; + uint8_t code = ((_icmp_header_t *)icmp_ptr)->code; + if (HICN_IS_MAPME(type, code)) return true; + + return false; +} + +/** + * @discussion The exact type of the MapMe message is determined after + * reception. In hICN, Interest Update and Notifications look like regular + * Interest packets, and are first punted from the normal path by the forwarder, + * then treated as such in the Listener to reach this function. Acknowledgements + * are received as Content (Data) packets and will land here too. + * + * This function is in charge of abstracting the low-level implementation of + * 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) { + hicn_prefix_t prefix; + mapme_params_t params; + hicn_mapme_parse_packet(msgBuffer, &prefix, ¶ms); + + // XXX Dispatch message dependenging on type + switch (params.type) { + case UPDATE: + case NOTIFICATION: + mapMe_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + case UPDATE_ACK: + case NOTIFICATION_ACK: + mapMe_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, ¶ms); + break; + default: + printf("E:Unknown message\n"); + break; + } +} + +#endif /* WITH_MAPME */ diff --git a/hicn-light/src/core/mapMe.h b/hicn-light/src/core/mapMe.h new file mode 100755 index 000000000..39edd0bd7 --- /dev/null +++ b/hicn-light/src/core/mapMe.h @@ -0,0 +1,89 @@ +/* + * 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 mapMe.h + * @brief MAP-Me : AnchorLess Producer Mobility Management + */ + +#ifndef mapMe_h +#define mapMe_h + +#ifdef WITH_MAPME + +#include <stdbool.h> +#include <stdint.h> + +#include <src/io/hicnListener.h> + +#include <hicn/hicn.h> +#include <src/utils/commands.h> + +struct mapme; +typedef struct mapme MapMe; + +/** + * @function MapMe_Init + * @abstract Initializes MAP-Me state in the forwarder. + * @return bool - Boolean informing about the success of MAP-Me initialization. + */ +bool mapMe_Init(MapMe **mapme, Forwarder *Forwarder); + +/** + * @function messageHandler_isMapMe + * @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 + * done through hooks defined in the Init function. + * @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); + +/** + * @function mapMe_handleMapMeMessage + * @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); + +/** + * @function mapMe_onConnectionAdded + * @abstract Callback following the addition of the face though the control + * protocol. + * @discussion This callback triggers the sending of control packets by MAP-Me. + * @param [in] mapme - Pointer to the MAP-Me data structure. + * @param [in] conn - The newly added connection. + */ +void mapMe_onConnectionAdded(const MapMe *mapme, const Connection *conn); + +/** + * @function mapMe_getNextHops + * @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); + +#endif /* WITH_MAPME */ + +#endif // mapMe_h diff --git a/hicn-light/src/core/message.c b/hicn-light/src/core/message.c new file mode 100755 index 000000000..6c0e916d2 --- /dev/null +++ b/hicn-light/src/core/message.c @@ -0,0 +1,297 @@ +/* + * 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 <src/config.h> +#include <stdio.h> +#include <string.h> + +#include <src/core/forwarder.h> +#include <src/core/message.h> +#include <src/core/wldr.h> + +#include <src/core/messageHandler.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <src/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 = 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"); + return 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 = 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); + // XXX: what about the checksum? + 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"); + return messageHandler_SetPathLabel(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); + 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/core/message.h b/hicn-light/src/core/message.h new file mode 100755 index 000000000..88aa32480 --- /dev/null +++ b/hicn-light/src/core/message.h @@ -0,0 +1,180 @@ +/* + * 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 <src/config.h> +#include <src/core/logger.h> +#include <src/core/messagePacketType.h> +#include <src/core/streamBuffer.h> + +#include <src/core/name.h> + +#include <parc/algol/parc_EventBuffer.h> +#include <parc/algol/parc_EventQueue.h> + +#include <src/utils/address.h> + +#include <src/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/core/messageHandler.h b/hicn-light/src/core/messageHandler.h new file mode 100755 index 000000000..d63656461 --- /dev/null +++ b/hicn-light/src/core/messageHandler.h @@ -0,0 +1,580 @@ +/* + * 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> + +#include <hicn/hicn.h> +#include <src/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 +#define NEVER_EXPIRE \ + 16777216 // 2^16 (max urgent pointer) * 2^8 (max reserved + NS bits) + +/*** 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 + +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; + } +} + +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((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((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 uint8_t messageHandler_GetProbePacketType( + 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 ((_icmp_header_t *)icmp_ptr)->code; +} + +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 new_path_label) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t old_path_label; + int res = hicn_data_get_path_label((hicn_header_t *)message, &old_path_label); + if (res < 0) 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); + + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +static inline void messageHandler_ResetPathLabel(uint8_t *message) { + if (!messageHandler_IsTCP(message)) return; + + uint32_t pl_old_32bit = messageHandler_GetPathLabel(message); + uint32_t pl_new_32bit = 0; + hicn_data_set_path_label((hicn_header_t *)message, pl_new_32bit); + messageHandler_UpdateTCPCheckSum(message, (uint16_t *)&pl_old_32bit, + (uint16_t *)&pl_new_32bit, 2); +} + +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 == NEVER_EXPIRE) 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 void messageHandler_SetProbePacket(uint8_t *message, + uint8_t probeType, + struct in6_addr *src, + struct in6_addr *dst) { + hicn_header_t *h = (hicn_header_t *)message; + *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, // this should be 1, but ... just to be safe + }, + .icmp = + { + .type = ICMP_LB_TYPE, + .code = probeType, + }, + }}; + messageHandler_SetSource_IPv6(message, src); + messageHandler_SetDestination_IPv6(message, dst); +} + +#endif // Metis_metis_MessageHandler diff --git a/hicn-light/src/core/messagePacketType.h b/hicn-light/src/core/messagePacketType.h new file mode 100755 index 000000000..dfbb12342 --- /dev/null +++ b/hicn-light/src/core/messagePacketType.h @@ -0,0 +1,32 @@ +/* + * 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_packet_type_h + * @brief Defines the packet type for a HICN message + * + */ + +#ifndef message_packet_type_h +#define message_packet_type_h + +typedef enum message_type { + MessagePacketType_Unknown, + MessagePacketType_Interest, + MessagePacketType_ContentObject, + MessagePacketType_WldrNotification +} MessagePacketType; + +#endif // message_packet_type_h diff --git a/hicn-light/src/core/name.c b/hicn-light/src/core/name.c new file mode 100755 index 000000000..f6a452d27 --- /dev/null +++ b/hicn-light/src/core/name.c @@ -0,0 +1,236 @@ +/* + * 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 <src/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 <src/core/messageHandler.h> +#include <src/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, union commandAddr 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.ipv4, len); + } else if (addressType == ADDR_INET6) { + name->content_name = nameBitvector_CreateFromIn6Addr(&addr.ipv6, 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; +} + +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"); + + 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; + } + } +} + +bool name_StartsWith(const Name *name, const Name *prefix) { + parcAssertNotNull(name, "Parameter name must be non-null"); + parcAssertNotNull(prefix, "Parameter prefix must be non-null"); + + return nameBitvector_StartsWith(name->content_name, prefix->content_name); +} + +char *name_ToString(const Name *name) { + char *output = malloc(128); + + Address *packetAddr = nameBitvector_ToAddress(name_GetContentName(name)); + + sprintf(output, "name: %s seq: %u", addressToString(packetAddr), + name->segment); + + addressDestroy(&packetAddr); + + return output; +} + +void name_setLen(const Name *name, uint8_t len) { + nameBitvector_setLen(name->content_name, len); +} diff --git a/hicn-light/src/core/name.h b/hicn-light/src/core/name.h new file mode 100755 index 000000000..fb4ad7a56 --- /dev/null +++ b/hicn-light/src/core/name.h @@ -0,0 +1,105 @@ +/* + * 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 <src/core/messagePacketType.h> +#include <src/core/nameBitvector.h> +#include <src/utils/address.h> + +#include <src/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); + +/** + * 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); + +/** + * @function metsName_StartsWith + * @abstract Checks if name starts with prefix + * @discussion + * Byte-by-byte prefix comparison + * + * @return True if the name is equal to or begins with prefix + */ + +bool name_StartsWith(const Name *name, const Name *prefix); + +/** + * 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(const Name *name, uint8_t len); + +/** + * Creates a name from a Address + * + */ +Name *name_CreateFromAddress(address_type addressType, union commandAddr addr, + uint8_t len); + +#endif // name_h diff --git a/hicn-light/src/core/nameBitvector.c b/hicn-light/src/core/nameBitvector.c new file mode 100755 index 000000000..66f3eae20 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.c @@ -0,0 +1,383 @@ +/* + * 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 <src/core/messageHandler.h> +#include <src/core/nameBitvector.h> + +#include <parc/algol/parc_Hash.h> + +#include <src/utils/commands.h> + +#define BLOCKS 2 + +const uint64_t BLOCK_SIZE = 64; +const uint64_t WIDTH = 128; +const uint64_t BLOCK_ONE = 0x1; + +// the bits are encoded in the following order: +// 00100101001---101010 00100011---110100100 +// [bits[0] (uint64_t)] [bits[1] (uint64_t)] +// ^ ^ ^ ^ +// 0 63 64 127 +// address 2200::0011 is encoded as: +// 1000 1000 0000 0010 00000 ....0100 0100 +// ^ ^ +// 0 127 + +struct name_bitvector { + uint64_t bits[BLOCKS]; + uint8_t len; + uint8_t IPversion; +}; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_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 = (s_addr & 0xff000000) >> 24; + uint8_t addr_2 = (s_addr & 0x00ff0000) >> 16; + uint8_t addr_3 = (s_addr & 0x0000ff00) >> 8; + uint8_t addr_4 = (s_addr & 0x000000ff); + + bitvector->bits[1] = (bitvector->bits[1] | addr_4) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_3) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_2) << 8; + bitvector->bits[1] = (bitvector->bits[1] | addr_1); + bitvector->bits[1] = bitvector->bits[1] << 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[1] = (bitvector->bits[1] << 8) | addr->s6_addr[i]; + } + + for (int i = 8; i < 16; ++i) { + bitvector->bits[0] = (bitvector->bits[0] << 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; + } +} + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix) { + parcAssertNotNull(name, "name cannot be NULL"); + parcAssertNotNull(prefix, "prefix cannot be NULL"); + parcAssertTrue(prefix->len > 0, "prefix length can not be 0"); + + if (prefix->len > BLOCK_SIZE) + return (name->bits[1] == prefix->bits[1]) && + ((name->bits[0] ^ prefix->bits[0]) >> + (BLOCK_SIZE - (prefix->len - BLOCK_SIZE)) == + 0); + + return ((name->bits[1] ^ prefix->bits[1]) >> (BLOCK_SIZE - prefix->len) == 0); +} + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos) { + if (pos == WIDTH) pos = 127; + + uint8_t final_pos = WIDTH - name->len; + + // the bit to test is inside the name/prefix len + if (pos > final_pos) { + return (name->bits[pos / BLOCK_SIZE] & (BLOCK_ONE << (pos % BLOCK_SIZE))); + } + + // the bit to test is outside the name/prefix len + if (pos < final_pos) { + return false; + } + + // pos is equal to the name/prefix len + return true; +} + +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; +} + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, + const NameBitvector *b) { + uint8_t res = 0; + uint64_t diff = a->bits[1] ^ b->bits[1]; + if (diff) + res = 64 + _diff_bit_log2(diff); + else + res = _diff_bit_log2(a->bits[0] ^ b->bits[0]); + + // res is computed over the bitvector which is composed by 128 bit all the + // times however the prefixes may be diffrent just because the have different + // lengths example: prefix 1: 0::/30 prefix 2: 0::/20 at this point of the + // function res would be 0 since both the bitvectors are composed by 0s but the + // function will return 127-20, which is the position at which the two prefix + // are different, since prefix 2 has only 20 bits + + uint8_t len_diff; + if (a->len < b->len) + len_diff = WIDTH - a->len; + else + len_diff = WIDTH - b->len; + + if (len_diff > res) res = len_diff; + + return res; +} + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address) { + if (name->IPversion == IPv4_TYPE) { + struct in_addr *addr = (struct in_addr *)(&ip_address->buffer); + ip_address->family = AF_INET; + ip_address->prefix_len = IPV4_ADDR_LEN_BITS; + + uint32_t tmp_addr = name->bits[1] >> 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 *)(&ip_address->buffer); + ip_address->family = AF_INET6; + ip_address->prefix_len = name->len; // IPV6_ADDR_LEN_BITS; + + for (int i = 0; i < 8; i++) { + addr->s6_addr[i] = (uint8_t)((name->bits[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr->s6_addr[i] = (uint8_t)((name->bits[0] >> 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[1] >> 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[1] >> 8 * (7 - i)) & 0xFF); + } + + int x = 0; + for (int i = 8; i < 16; ++i) { + addr.sin6_addr.s6_addr[i] = + (uint8_t)((name->bits[0] >> 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); + + sprintf(output, "prefix: %s len: %u", addressToString(packetAddr), name->len); + + addressDestroy(&packetAddr); + + return output; +}
\ No newline at end of file diff --git a/hicn-light/src/core/nameBitvector.h b/hicn-light/src/core/nameBitvector.h new file mode 100755 index 000000000..28a31dc26 --- /dev/null +++ b/hicn-light/src/core/nameBitvector.h @@ -0,0 +1,63 @@ +/* + * 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 <src/utils/address.h> + +struct name_bitvector; +typedef struct name_bitvector NameBitvector; + +NameBitvector *nameBitvector_CreateFromInAddr(uint32_t s_addr, uint8_t len); + +NameBitvector *nameBitvector_CreateFromIn6Addr(struct in6_addr *addr, + uint8_t len); + +NameBitvector *nameBitvector_CreateFromAddress(const Address *prefix, + 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); + +bool nameBitvector_StartsWith(const NameBitvector *name, + const NameBitvector *prefix); + +bool nameBitvector_testBit(const NameBitvector *name, uint8_t pos); + +uint8_t nameBitvector_firstDiff(const NameBitvector *a, const NameBitvector *b); + +int nameBitvector_ToIPAddress(const NameBitvector *name, + ip_address_t *ip_address); +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/core/numberSet.c b/hicn-light/src/core/numberSet.c new file mode 100755 index 000000000..75fec1524 --- /dev/null +++ b/hicn-light/src/core/numberSet.c @@ -0,0 +1,203 @@ +/* + * 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 <src/config.h> +#include <src/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/core/numberSet.h b/hicn-light/src/core/numberSet.h new file mode 100755 index 000000000..91a965d7b --- /dev/null +++ b/hicn-light/src/core/numberSet.h @@ -0,0 +1,157 @@ +/* + * 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/core/streamBuffer.c b/hicn-light/src/core/streamBuffer.c new file mode 100755 index 000000000..7aebb5edb --- /dev/null +++ b/hicn-light/src/core/streamBuffer.c @@ -0,0 +1,142 @@ +/* + * 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 <src/config.h> +#include <stdio.h> + +#include <parc/assert/parc_Assert.h> + +#include <src/core/streamBuffer.h> + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr) { + parcAssertNotNull(bufferPtr, "Parameter must be non-null double pointer"); + parcAssertNotNull(*bufferPtr, + "Parameter must dereference to non-null pointer"); + parcEventQueue_Destroy(bufferPtr); + *bufferPtr = NULL; +} + +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (setRead) { + flags |= PARCEventType_Read; + } + + if (setWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_SetWatermark(buffer, flags, low, high); +} + +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + short flags = 0; + if (flushRead) { + flags |= PARCEventType_Read; + } + + if (flushWrite) { + flags |= PARCEventType_Write; + } + + return parcEventQueue_Flush(buffer, flags); +} + +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + + parcEventQueue_SetCallbacks(buffer, readCallback, writeCallback, + eventCallback, user_data); +} + +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (enableRead) { + flags |= PARCEventType_Read; + } + if (enableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Enable(buffer, flags); +} + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + * + * @param <#param1#> + * @return <#return#> + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite) { + parcAssertNotNull(buffer, "Parameter buffer must be non-null"); + short flags = 0; + if (disableRead) { + flags |= PARCEventType_Read; + } + if (disableWrite) { + flags |= PARCEventType_Write; + } + + parcEventQueue_Disable(buffer, flags); +} diff --git a/hicn-light/src/core/streamBuffer.h b/hicn-light/src/core/streamBuffer.h new file mode 100755 index 000000000..27e793176 --- /dev/null +++ b/hicn-light/src/core/streamBuffer.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +/** + * Wrapper around event scheduler + */ + +#ifndef streamBuffer_h +#define streamBuffer_h + +#include <parc/algol/parc_EventQueue.h> +#include <stdbool.h> + +void streamBuffer_Destroy(PARCEventQueue **bufferPtr); + +/** + * @function streamBuffer_SetWatermark + * @abstract Sets the read and/or write watermarks + * @discussion + * For a read watermark, when there is at least <code>low</code> bytes + * available to read, the read callback will be fired. If the bytes in the + * buffer exceed <code>high</code>, the stream buffer will stop reading from the + * network. + * + * For a write watermark, when the bytes in the buffer fall below + * <code>low</code>, the write callback is fired. The <code>high</code> + * watermark limits stream filters and shapers from exceeding that threashold on + * what they write to the buffer. + * + */ +void streamBuffer_SetWatermark(PARCEventQueue *buffer, bool setRead, + bool setWrite, size_t low, size_t high); + +/** + * @function streamBuffer_Flush + * @abstract The buffer will read/write more data if available + * + * @return -1 error, 0 no more data, 1 more data + */ +int streamBuffer_Flush(PARCEventQueue *buffer, bool flushRead, bool flushWrite); + +/** + * @function streamBuffer_FlushCheckpoint + * @abstract Flushes the stream, checkpointing all data in the buffer + */ +int streamBuffer_FlushCheckpoint(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @function streamBuffer_FlushFinished + * @abstract Flush the stream and indicate the end of new data + */ +int streamBuffer_FlushFinished(PARCEventQueue *buffer, bool flushRead, + bool flushWrite); + +/** + * @typedef StreamBufferReadWriteCallback + * @abstract Callback when data is available or write space available + * @constant user_data opaque data passed to + * <code>StreamBuffer_SetCallbacks()</code> + */ +typedef void(StreamBufferReadWriteCallback)(PARCEventQueue *buffer, + void *user_data); + +/** + * @typedef StreamBufferEventCallback + * @abstract Callback on error or other event on the stream buffer + * @constant what logical or of STREAM events. STREAM_READING and + * STREAM_WRITING indicate if the error was on the read or write direction. The + * conditions may be STREAM_ERROR, STREAM_EOF, STREAM_TIMEOUT, or + * STREAM_CONNECTED. + * @constant user_data opaque data passed to + * <code>StreamBuffer_SetCallbacks()</code> + */ +typedef void(StreamBufferEventCallback)(PARCEventQueue *buffer, short what, + void *user_data); + +/** + * Changes the callbacks for a buffer event. + * + * @param bufev the buffer event object for which to change callbacks + * @param readcb callback to invoke when there is data to be read, or NULL if + * no callback is desired + * @param writecb callback to invoke when the file descriptor is ready for + * writing, or NULL if no callback is desired + * @param eventcb callback to invoke when there is an event on the file + * descriptor + * @param cbarg an argument that will be supplied to each of the callbacks + * (readcb, writecb, and errorcb) + * @see parcEventQueue_Create() + */ +void streamBuffer_SetCallbacks(PARCEventQueue *buffer, + PARCEventQueue_Callback *readCallback, + PARCEventQueue_Callback *writeCallback, + PARCEventQueue_EventCallback *eventCallback, + void *user_data); + +/** + * @function StreamBuffer_EnableCallbacks + * @abstract Enables specified callbacks. Does not affect others. + * @discussion + * Enables disabled callbacks. If a callback is already enabled, has no + * effect. A "false" value does not disable it. + */ +void streamBuffer_EnableCallbacks(PARCEventQueue *buffer, bool enableRead, + bool enableWrite); + +/** + * @function StreamBuffer_DisableCallbacks + * @abstract Disables specified callbacks. Does not affect others. + * @discussion + * Disables enabled callbacks. If a callback is already disabled, has no + * effect. A "false" value does not enable it. + */ +void streamBuffer_DisableCallbacks(PARCEventQueue *buffer, bool disableRead, + bool disableWrite); +#endif // streamBuffer_h diff --git a/hicn-light/src/core/system.h b/hicn-light/src/core/system.h new file mode 100755 index 000000000..3c5c8cba2 --- /dev/null +++ b/hicn-light/src/core/system.h @@ -0,0 +1,60 @@ +/* + * 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 system.h + * @abstract System-level properties + * @discussion + * <#Discussion#> + * + */ + +#ifndef system_h +#define system_h + +#include <src/core/forwarder.h> +#include <src/utils/interfaceSet.h> + +/** + * @function system_Interfaces + * @abstract The system network interfaces + */ +InterfaceSet *system_Interfaces(Forwarder *forwarder); + +/** + * Returns the MTU of the named interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @return 0 Interface does not exist + * @return positive the MTU the kernel reports + * + */ +unsigned system_InterfaceMtu(Forwarder *forwarder, const char *interfaceName); + +/** + * Returns the LINK address of the specified interface + * + * @param [in] an allocated hicn-light forwarder + * @param [in] interfaceName The system interface name, e.g. "eth0" + * + * @retval non-null The MAC address of the interface + * @retval null The interface does not exist + * + */ +Address *system_GetMacAddressByName(Forwarder *forwarder, + const char *interfaceName); +#endif diff --git a/hicn-light/src/core/ticks.h b/hicn-light/src/core/ticks.h new file mode 100755 index 000000000..8750abde5 --- /dev/null +++ b/hicn-light/src/core/ticks.h @@ -0,0 +1,31 @@ +/* + * 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 The router periodically measures time in units of Ticks + * + * See forwarder.c HZ which specifies the tick rate. forwarder.h has functions + * to convert between ticks and milliseconds. + * + */ +#ifndef ticks_h +#define ticks_h + +#define __STDC_FORMAT_MACROS +#include <stdint.h> + +typedef uint64_t Ticks; + +#endif // ticks_h diff --git a/hicn-light/src/core/wldr.c b/hicn-light/src/core/wldr.c new file mode 100755 index 000000000..b94ae76e5 --- /dev/null +++ b/hicn-light/src/core/wldr.c @@ -0,0 +1,182 @@ +/* + * 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/assert/parc_Assert.h> +#include <parc/logging/parc_LogReporterTextStdout.h> +#include <src/core/connection.h> +#include <src/core/forwarder.h> +#include <src/core/wldr.h> +#include <stdint.h> +#include <stdio.h> + +struct wldr_buffer { + Message *message; + uint8_t rtx_counter; +}; + +typedef struct wldr_buffer WldrBuffer; + +struct wldr_state { + uint16_t expected_label; + uint16_t next_label; + WldrBuffer *buffer[BUFFER_SIZE]; +}; + +Wldr *wldr_Init() { + Wldr *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)); + parcAssertNotNull( + entry, + "WldrBuffer init: parcMemory_AllocateAndClear(%zu) returned NULL", + sizeof(WldrBuffer)); + entry->message = NULL; + entry->rtx_counter = 0; + wldr->buffer[i] = entry; + } + return wldr; +} + +void wldr_ResetState(Wldr *wldr) { + wldr->expected_label = 1; + wldr->next_label = 1; + for (int i = 0; i < BUFFER_SIZE; i++) { + wldr->buffer[i]->message = NULL; + wldr->buffer[i]->rtx_counter = 0; + } +} + +void wldr_Destroy(Wldr **wldrPtr) { + Wldr *wldr = *wldrPtr; + for (unsigned i = 0; i < BUFFER_SIZE; i++) { + if (wldr->buffer[i]->message != NULL) { + message_Release(&(wldr->buffer[i]->message)); + parcMemory_Deallocate((void **)&(wldr->buffer[i])); + } + } + parcMemory_Deallocate((void **)&wldr); + *wldrPtr = NULL; +} + +static void _wldr_RetransmitPacket(Wldr *wldr, const Connection *conn, + uint16_t label) { + if (wldr->buffer[label % BUFFER_SIZE]->message == 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; + 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)); + } + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = 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); + wldr->next_label++; + connection_ReSend(conn, msg, false); + } +} + +static void _wldr_SendWldrNotificaiton(Wldr *wldr, const Connection *conn, + Message *message, 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 + // notification is the source address of the message for which we want to + // create a notification. in fact, if message is an interest the prevoius hop + // is identified by the src. if message is a data, we need to send the + // notification message with the content name has a source address in this way + // the message will be trapped by the pounting rules in the next hop We define + // the notification as an interest message so that the NAT in the send function + // will set the src address of the local connection. Notice that in 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); + parcAssertNotNull(notification, "Got null from CreateWldrNotification"); + connection_ReSend(conn, notification, true); +} + +void wldr_SetLabel(Wldr *wldr, Message *message) { + // in this function we send the packet for the first time + // 1) we set the wldr label + message_SetWldrLabel(message, wldr->next_label); + + // 2) we store the pointer to packet in the buffer + if (wldr->buffer[wldr->next_label % BUFFER_SIZE]->message != NULL) { + // release an old message if necessary + message_Release(&(wldr->buffer[wldr->next_label % BUFFER_SIZE]->message)); + } + + // we need to acquire the message to avoid that it gets destroyed + message_Acquire(message); + + wldr->buffer[wldr->next_label % BUFFER_SIZE]->message = message; + 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++; +} + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message) { + if (message_HasWldr(message)) { + // this is a normal wldr packet + uint16_t pkt_lbl = (uint16_t)message_GetWldrLabel(message); + 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 + // a while and reconnected on this same connection, so the two nodes are + // out of synch for this reason we do not send any notification, we just + // synch the labels + + if ((pkt_lbl != 1) || (wldr->expected_label < pkt_lbl)) { + _wldr_SendWldrNotificaiton(wldr, conn, message, wldr->expected_label, + pkt_lbl); + } + + // here we always synch + wldr->expected_label = (uint16_t)(pkt_lbl + 1); + } else { + wldr->expected_label++; + if (wldr->expected_label == 0) + wldr->expected_label++; // for the next_label we want to skip 0 + } + } +} + +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); + if ((wldr->next_label - expected_lbl) > BUFFER_SIZE) { + // the packets are not in the buffer anymore + return; + } + while (expected_lbl < received_lbl) { + _wldr_RetransmitPacket(wldr, conn, expected_lbl); + expected_lbl++; + } +} diff --git a/hicn-light/src/core/wldr.h b/hicn-light/src/core/wldr.h new file mode 100755 index 000000000..1666b4d3f --- /dev/null +++ b/hicn-light/src/core/wldr.h @@ -0,0 +1,52 @@ +/* + * 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 wldr_h +#define wldr_h + +#include <src/config.h> +#include <src/core/connection.h> +#include <src/core/message.h> + +#define BUFFER_SIZE 8192 +#define MAX_RTX 3 +#define WLDR_LBL 13 +#define WLDR_NOTIFICATION 14 +#define WLDR_UNKNOWN 15 + +// NORMAL PACKET or RETRASMISSION +// WLDR_LBL: label = window size in the TCP header +// NOTIFICATION +// WLDR_NOTIFICATION: expected_label = window size in the TCP header, +// last_received_label = urgent pointer in the TCP header +// 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; + +Wldr *wldr_Init(); + +void wldr_Destroy(Wldr **wldrPtr); + +void wldr_ResetState(Wldr *wldr); + +void wldr_SetLabel(Wldr *wldr, Message *message); + +void wldr_DetectLosses(Wldr *wldr, const Connection *conn, Message *message); + +void wldr_HandleWldrNotification(Wldr *wldr, const Connection *conn, + Message *message); +#endif // wldr_h |