aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/core')
-rwxr-xr-xhicn-light/src/core/CMakeLists.txt55
-rwxr-xr-xhicn-light/src/core/connection.c265
-rwxr-xr-xhicn-light/src/core/connection.h148
-rwxr-xr-xhicn-light/src/core/connectionList.c68
-rwxr-xr-xhicn-light/src/core/connectionList.h70
-rwxr-xr-xhicn-light/src/core/connectionManager.c196
-rwxr-xr-xhicn-light/src/core/connectionManager.h37
-rwxr-xr-xhicn-light/src/core/connectionTable.c224
-rwxr-xr-xhicn-light/src/core/connectionTable.h99
-rwxr-xr-xhicn-light/src/core/dispatcher.c435
-rwxr-xr-xhicn-light/src/core/dispatcher.h286
-rwxr-xr-xhicn-light/src/core/forwarder.c499
-rwxr-xr-xhicn-light/src/core/forwarder.h287
-rwxr-xr-xhicn-light/src/core/logger.c173
-rwxr-xr-xhicn-light/src/core/logger.h168
-rwxr-xr-xhicn-light/src/core/mapMe.c816
-rwxr-xr-xhicn-light/src/core/mapMe.h89
-rwxr-xr-xhicn-light/src/core/message.c297
-rwxr-xr-xhicn-light/src/core/message.h180
-rwxr-xr-xhicn-light/src/core/messageHandler.h580
-rwxr-xr-xhicn-light/src/core/messagePacketType.h32
-rwxr-xr-xhicn-light/src/core/name.c236
-rwxr-xr-xhicn-light/src/core/name.h105
-rwxr-xr-xhicn-light/src/core/nameBitvector.c383
-rwxr-xr-xhicn-light/src/core/nameBitvector.h63
-rwxr-xr-xhicn-light/src/core/numberSet.c203
-rwxr-xr-xhicn-light/src/core/numberSet.h157
-rwxr-xr-xhicn-light/src/core/streamBuffer.c142
-rwxr-xr-xhicn-light/src/core/streamBuffer.h129
-rwxr-xr-xhicn-light/src/core/system.h60
-rwxr-xr-xhicn-light/src/core/ticks.h31
-rwxr-xr-xhicn-light/src/core/wldr.c182
-rwxr-xr-xhicn-light/src/core/wldr.h52
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, &params);
+ 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, &params);
+
+ // XXX Dispatch message dependenging on type
+ switch (params.type) {
+ case UPDATE:
+ case NOTIFICATION:
+ mapMe_onSpecialInterest(mapme, msgBuffer, conn_id, &prefix, &params);
+ break;
+ case UPDATE_ACK:
+ case NOTIFICATION_ACK:
+ mapMe_onSpecialInterestAck(mapme, msgBuffer, conn_id, &prefix, &params);
+ 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