aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/io')
-rwxr-xr-xhicn-light/src/io/CMakeLists.txt53
-rwxr-xr-xhicn-light/src/io/addressPair.c129
-rwxr-xr-xhicn-light/src/io/addressPair.h128
-rwxr-xr-xhicn-light/src/io/hicnConnection.c517
-rwxr-xr-xhicn-light/src/io/hicnConnection.h53
-rwxr-xr-xhicn-light/src/io/hicnListener.c725
-rwxr-xr-xhicn-light/src/io/hicnListener.h42
-rwxr-xr-xhicn-light/src/io/hicnTunnel.c106
-rwxr-xr-xhicn-light/src/io/hicnTunnel.h65
-rwxr-xr-xhicn-light/src/io/ioOperations.c63
-rwxr-xr-xhicn-light/src/io/ioOperations.h394
-rwxr-xr-xhicn-light/src/io/listener.h105
-rwxr-xr-xhicn-light/src/io/listenerSet.c132
-rwxr-xr-xhicn-light/src/io/listenerSet.h137
-rwxr-xr-xhicn-light/src/io/streamConnection.c714
-rwxr-xr-xhicn-light/src/io/streamConnection.h75
-rwxr-xr-xhicn-light/src/io/tcpListener.c233
-rwxr-xr-xhicn-light/src/io/tcpListener.h37
-rwxr-xr-xhicn-light/src/io/tcpTunnel.c43
-rwxr-xr-xhicn-light/src/io/tcpTunnel.h42
-rwxr-xr-xhicn-light/src/io/udpConnection.c406
-rwxr-xr-xhicn-light/src/io/udpConnection.h53
-rwxr-xr-xhicn-light/src/io/udpListener.c533
-rwxr-xr-xhicn-light/src/io/udpListener.h32
-rwxr-xr-xhicn-light/src/io/udpTunnel.c91
-rwxr-xr-xhicn-light/src/io/udpTunnel.h42
26 files changed, 4950 insertions, 0 deletions
diff --git a/hicn-light/src/io/CMakeLists.txt b/hicn-light/src/io/CMakeLists.txt
new file mode 100755
index 000000000..f65f0b580
--- /dev/null
+++ b/hicn-light/src/io/CMakeLists.txt
@@ -0,0 +1,53 @@
+# 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}/addressPair.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/listener.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicnListener.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicnTunnel.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/hicnConnection.h
+)
+
+list(APPEND SOURCE_FILES
+ ${CMAKE_CURRENT_SOURCE_DIR}/addressPair.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/ioOperations.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/listenerSet.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/streamConnection.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tcpListener.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/tcpTunnel.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpConnection.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpListener.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/udpTunnel.c
+)
+
+if("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
+ list(APPEND SOURCE_FILES
+ io/hicnTunnel.c
+ io/hicnConnection.c
+ io/hicnListener.c
+ )
+endif()
+
+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/io/addressPair.c b/hicn-light/src/io/addressPair.c
new file mode 100755
index 000000000..5d2017a3d
--- /dev/null
+++ b/hicn-light/src/io/addressPair.c
@@ -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.
+ */
+
+#include <src/config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/io/addressPair.h>
+
+struct address_pair {
+ Address *local;
+ Address *remote;
+};
+
+static void _addressPair_Destroy(AddressPair **addressPairPtr) {
+ AddressPair *pair = *addressPairPtr;
+
+ addressDestroy(&pair->local);
+ addressDestroy(&pair->remote);
+}
+
+parcObject_ExtendPARCObject(AddressPair, _addressPair_Destroy, NULL,
+ addressPair_ToString, addressPair_Equals, NULL,
+ addressPair_HashCode, NULL);
+
+parcObject_ImplementAcquire(addressPair, AddressPair);
+
+parcObject_ImplementRelease(addressPair, AddressPair);
+
+AddressPair *addressPair_Create(const Address *local, const Address *remote) {
+ parcAssertNotNull(local, "Parameter local must be non-null");
+ parcAssertNotNull(remote, "Parameter remote must be non-null");
+
+ AddressPair *pair = parcObject_CreateInstance(AddressPair);
+ parcAssertNotNull(pair, "Got null from parcObject_Create()");
+
+ pair->local = addressCopy(local);
+ pair->remote = addressCopy(remote);
+
+ return pair;
+}
+
+bool addressPair_Equals(const AddressPair *a, const AddressPair *b) {
+ if (a == b) {
+ return true;
+ }
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (addressEquals(a->local, b->local)) {
+ if (addressEquals(a->remote, b->remote)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local,
+ const Address *remote) {
+ if (a == NULL || local == NULL || remote == NULL) {
+ return false;
+ }
+
+ if (addressEquals(a->local, local)) {
+ if (addressEquals(a->remote, remote)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+char *addressPair_ToString(const AddressPair *pair) {
+ parcAssertNotNull(pair, "Parameter pair must be non-null");
+
+ char *local = addressToString(pair->local);
+ char *remote = addressToString(pair->remote);
+
+ char *output;
+ int failure = asprintf(&output, "{ .local=%s, .remote=%s }", local, remote);
+ parcAssertTrue(failure > -1, "Error on asprintf");
+
+ parcMemory_Deallocate((void **)&local);
+ parcMemory_Deallocate((void **)&remote);
+
+ return output;
+}
+
+const Address *addressPair_GetLocal(const AddressPair *pair) {
+ parcAssertNotNull(pair, "Parameter pair must be non-null");
+ return pair->local;
+}
+
+const Address *addressPair_GetRemote(const AddressPair *pair) {
+ parcAssertNotNull(pair, "Parameter pair must be non-null");
+ return pair->remote;
+}
+
+/**
+ * @function addressPair_HashCode
+ * @abstract Hash useful for tables. Consistent with Equals.
+ * @discussion
+ * Returns a non-cryptographic hash that is consistent with equals. That is,
+ * if a == b, then hash(a) == hash(b).
+ *
+ */
+PARCHashCode addressPair_HashCode(const AddressPair *pair) {
+ PARCHashCode hashpair[2];
+ hashpair[0] = addressHashCode(pair->local);
+ hashpair[1] = addressHashCode(pair->remote);
+ return parcHashCode_Hash((const uint8_t *)hashpair, sizeof(hashpair));
+}
diff --git a/hicn-light/src/io/addressPair.h b/hicn-light/src/io/addressPair.h
new file mode 100755
index 000000000..5152267b6
--- /dev/null
+++ b/hicn-light/src/io/addressPair.h
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+/**
+ * Used to identify a connection between a specific local address and
+ * a specific remote address.
+ */
+
+#ifndef address_Pair_h
+#define address_Pair_h
+
+#include <src/utils/address.h>
+
+struct address_pair;
+typedef struct address_pair AddressPair;
+
+/**
+ * @function addressPair_Create
+ * @abstract Creates and address pair. There is no restriction on the address
+ * types.
+ * @discussion
+ * Creates an ordered pair of addresses, where the first is considered the
+ * "local" address and the second is the "remote" address. Those designations
+ * are purely a convention used to name them, and does not imply any specifici
+ * types of operations.
+ *
+ * The two addresses may be of any address types (e.g. IPv4, IPv6, Local,
+ * Ethernet). However, some functions that use an AddressPair may require that
+ * the local and remote addresses be the same type.
+ *
+ */
+AddressPair *addressPair_Create(const Address *local, const Address *remote);
+
+/**
+ * Returns a reference counted copy of the address pair
+ *
+ * Increments the reference count and returns the same address pair
+ *
+ * @param [in] addressPair An allocated address pair
+ *
+ * @retval non-null A reference counted copy
+ * @retval null An error
+ */
+AddressPair *addressPair_Acquire(const AddressPair *addressPair);
+
+/**
+ * Releases a reference count to the object
+ *
+ * Decrements the reference count and destroys the object when it reaches 0.
+ */
+void addressPair_Release(AddressPair **pairPtr);
+
+/**
+ * Determine if two AddressPair instances are equal.
+ *
+ * Two AddressPair instances are equal if, and only if, the local and remote
+ * addresses are identical. Equality is determined by addressEquals(a->local,
+ * b->local) and Adress_Equals(a->remote, b->remote).
+ *
+ * The following equivalence relations on non-null `AddressPair` instances are
+ * maintained:
+ *
+ * * It is reflexive: for any non-null reference value x,
+ * `AddressPair_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `addressPair_Equals(x, y)` must return true if and only if
+ * `addressPair_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `addressPair_Equals(x, y)` returns true and
+ * `addressPair_Equals(y, z)` returns true,
+ * then `addressPair_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `addressPair_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `addressPair_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `AddressPair` instance.
+ * @param b A pointer to a `AddressPair` instance.
+ * @return true if the two `AddressPair` instances are equal.
+ */
+bool addressPair_Equals(const AddressPair *a, const AddressPair *b);
+
+/**
+ * @function addressPair_EqualsAddresses
+ * @abstract As AddressEquals, but "b" is broken out
+ * @discussion
+ * Equality is determined by addressEquals(a->local, local) and
+ * Adress_Equals(a->remote, remote).
+ */
+bool addressPair_EqualsAddresses(const AddressPair *a, const Address *local,
+ const Address *remote);
+
+const Address *addressPair_GetLocal(const AddressPair *pair);
+
+const Address *addressPair_GetRemote(const AddressPair *pair);
+
+/**
+ * @function addressPair_HashCode
+ * @abstract Hash useful for tables. Consistent with Equals.
+ * @discussion
+ * Returns a non-cryptographic hash that is consistent with equals. That is,
+ * if a == b, then hash(a) == hash(b).
+ */
+PARCHashCode addressPair_HashCode(const AddressPair *pair);
+
+/**
+ * @function addressPair_ToString
+ * @abstract Human readable string representation. Caller must use free(3).
+ */
+char *addressPair_ToString(const AddressPair *pair);
+#endif // address_Pair_h
diff --git a/hicn-light/src/io/hicnConnection.c b/hicn-light/src/io/hicnConnection.c
new file mode 100755
index 000000000..85cf50921
--- /dev/null
+++ b/hicn-light/src/io/hicnConnection.c
@@ -0,0 +1,517 @@
+/*
+ * 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.
+ */
+
+/**
+ * Embodies the reader/writer for a HIcn connection
+ *
+ * NB The Send() function may overflow the output buffer
+ *
+ */
+
+#include <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <src/core/message.h>
+#include <src/io/hicnConnection.h>
+
+#include <src/core/messageHandler.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+
+typedef struct hicn_state {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ // the hicn listener socket we receive packets on
+ int hicnListenerSocket;
+
+ AddressPair *addressPair;
+
+ // We need to access this all the time, so grab it out
+ // of the addressPair;
+ struct sockaddr *peerAddress;
+ socklen_t peerAddressLength;
+
+ struct sockaddr *localAddress;
+ socklen_t localAddressLength;
+
+ // this address contains one of the content names reachable
+ // throught the connection peer. We need this address beacuse it is
+ // the only way we have to conntact the next hop, since the main tun
+ // does not have address. Notice that a connection that sends probes
+ // is a connection that sends interest. In a "data" connection this
+ // value will remain NULL. We refresh the content address every time
+ // we send a probe, in this way we don't need to waste to much time in
+ // copy the address, but we can also react to the routing changes
+ struct sockaddr *probeDestAddress;
+ socklen_t probeDestAddressLength;
+ bool refreshProbeDestAddress;
+
+ bool isLocal;
+ bool isUp;
+ unsigned id;
+
+ unsigned delay;
+} _HicnState;
+
+// Prototypes
+static bool _send(IoOperations *ops, const Address *nexthop, Message *message);
+static const Address *_getRemoteAddress(const IoOperations *ops);
+static const AddressPair *_getAddressPair(const IoOperations *ops);
+static unsigned _getConnectionId(const IoOperations *ops);
+static bool _isUp(const IoOperations *ops);
+static bool _isLocal(const IoOperations *ops);
+static void _destroy(IoOperations **opsPtr);
+static list_connections_type _getConnectionType(const IoOperations *ops);
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message);
+
+/*
+ * This assigns a unique pointer to the void * which we use
+ * as a GUID for this class.
+ */
+static const void *_ioOperationsGuid = __FILE__;
+
+/*
+ * Return our GUID
+ */
+static const void *_streamConnection_Class(const IoOperations *ops) {
+ return _ioOperationsGuid;
+}
+
+static IoOperations _template = {.closure = NULL,
+ .send = &_send,
+ .getRemoteAddress = &_getRemoteAddress,
+ .getAddressPair = &_getAddressPair,
+ .getConnectionId = &_getConnectionId,
+ .isUp = &_isUp,
+ .isLocal = &_isLocal,
+ .destroy = &_destroy,
+ .class = &_streamConnection_Class,
+ .getConnectionType = &_getConnectionType,
+ .sendProbe = &_sendProbe};
+
+// =================================================================
+
+static void _setConnectionState(_HicnState *HIcn, bool isUp);
+static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair);
+static void _refreshProbeDestAddress(_HicnState *hicnConnState,
+ const uint8_t *message);
+
+IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd,
+ const AddressPair *pair, bool isLocal) {
+ IoOperations *io_ops = NULL;
+
+ _HicnState *hicnConnState = parcMemory_AllocateAndClear(sizeof(_HicnState));
+ parcAssertNotNull(hicnConnState,
+ "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_HicnState));
+
+ hicnConnState->forwarder = forwarder;
+ hicnConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+
+ bool saved = _saveSockaddr(hicnConnState, pair);
+ if (saved) {
+ hicnConnState->hicnListenerSocket = fd;
+ hicnConnState->id = forwarder_GetNextConnectionId(forwarder);
+ hicnConnState->addressPair = addressPair_Acquire(pair);
+ hicnConnState->isLocal = isLocal;
+
+ // allocate a connection
+ io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations));
+ parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(IoOperations));
+ memcpy(io_ops, &_template, sizeof(IoOperations));
+ io_ops->closure = hicnConnState;
+
+ _setConnectionState(hicnConnState, true);
+
+ if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ char *str = addressPair_ToString(hicnConnState->addressPair);
+ logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info,
+ __func__,
+ "HIcnConnection %p created for address %s (isLocal %d)",
+ (void *)hicnConnState, str, hicnConnState->isLocal);
+ free(str);
+ }
+
+ messenger_Send(
+ forwarder_GetMessenger(forwarder),
+ missive_Create(MissiveType_ConnectionCreate, hicnConnState->id));
+ messenger_Send(forwarder_GetMessenger(forwarder),
+ missive_Create(MissiveType_ConnectionUp, hicnConnState->id));
+ } else {
+ // _saveSockaddr will already log an error, no need for extra log message
+ // here
+ logger_Release(&hicnConnState->logger);
+ parcMemory_Deallocate((void **)&hicnConnState);
+ }
+
+ return io_ops;
+}
+
+// =================================================================
+// I/O Operations implementation
+
+static void _destroy(IoOperations **opsPtr) {
+ parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer");
+ parcAssertNotNull(*opsPtr,
+ "Parameter opsPtr must dereference to non-null pointer");
+
+ IoOperations *ops = *opsPtr;
+ parcAssertNotNull(ioOperations_GetClosure(ops),
+ "ops->context must not be null");
+
+ _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops);
+ addressPair_Release(&hicnConnState->addressPair);
+ parcMemory_Deallocate((void **)&(hicnConnState->peerAddress));
+ parcMemory_Deallocate((void **)&(hicnConnState->localAddress));
+ if (hicnConnState->probeDestAddress != NULL)
+ parcMemory_Deallocate((void **)&(hicnConnState->probeDestAddress));
+
+ messenger_Send(
+ forwarder_GetMessenger(hicnConnState->forwarder),
+ missive_Create(MissiveType_ConnectionDestroyed, hicnConnState->id));
+
+ if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Info,
+ __func__, "HIcnConnection %p destroyed", (void *)hicnConnState);
+ }
+
+ // XXX
+ // do not close hicListenerSocket, the listener will close
+ // that when its done
+ // should I say something to libhicn?
+
+ logger_Release(&hicnConnState->logger);
+ parcMemory_Deallocate((void **)&hicnConnState);
+ parcMemory_Deallocate((void **)&ops);
+
+ *opsPtr = NULL;
+}
+
+static bool _isUp(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _HicnState *hicnConnState =
+ (const _HicnState *)ioOperations_GetClosure(ops);
+ return hicnConnState->isUp;
+}
+
+static bool _isLocal(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _HicnState *hicnConnState =
+ (const _HicnState *)ioOperations_GetClosure(ops);
+ return hicnConnState->isLocal;
+}
+
+static const Address *_getRemoteAddress(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _HicnState *hicnConnState =
+ (const _HicnState *)ioOperations_GetClosure(ops);
+ return addressPair_GetRemote(hicnConnState->addressPair);
+}
+
+static const AddressPair *_getAddressPair(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _HicnState *hicnConnState =
+ (const _HicnState *)ioOperations_GetClosure(ops);
+ return hicnConnState->addressPair;
+}
+
+static unsigned _getConnectionId(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _HicnState *hicnConnState =
+ (const _HicnState *)ioOperations_GetClosure(ops);
+ return hicnConnState->id;
+}
+
+/**
+ * @function hicnConnection_Send
+ * @abstract Non-destructive send of the message.
+ * @discussion
+ * sends a message to the peer.
+ *
+ * @param dummy is ignored. .
+ * @return <#return#>
+ */
+static bool _send(IoOperations *ops, const Address *dummy, Message *message) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ parcAssertNotNull(message, "Parameter message must be non-null");
+ _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops);
+
+ // NAT for HICN
+ // XXX
+ if (message_GetType(message) == MessagePacketType_ContentObject) {
+ // this is a data packet. We need to put the remote address in the
+ // destination field
+
+ if (messageHandler_GetIPPacketType(message_FixedHeader(message)) ==
+ IPv6_TYPE) {
+ messageHandler_SetDestination_IPv6(
+ (uint8_t *)message_FixedHeader(message),
+ &((struct sockaddr_in6 *)hicnConnState->peerAddress)->sin6_addr);
+ } else {
+ messageHandler_SetDestination_IPv4(
+ (uint8_t *)message_FixedHeader(message),
+ &(((struct sockaddr_in *)hicnConnState->peerAddress)
+ ->sin_addr.s_addr));
+ }
+ } else if (message_GetType(message) == MessagePacketType_Interest) {
+ // this si an interest packet. We need to put the local address in the
+ // source field
+ if (messageHandler_GetIPPacketType(message_FixedHeader(message)) ==
+ IPv6_TYPE) {
+ messageHandler_SetSource_IPv6(
+ (uint8_t *)message_FixedHeader(message),
+ &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr);
+ } else {
+ messageHandler_SetSource_IPv4(
+ (uint8_t *)message_FixedHeader(message),
+ &(((struct sockaddr_in *)hicnConnState->localAddress)
+ ->sin_addr.s_addr));
+ }
+
+ // only in this case we may need to set the probeDestAddress
+ if (hicnConnState->refreshProbeDestAddress) {
+ _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message));
+ }
+
+ } else if (message_GetType(message) == MessagePacketType_WldrNotification) {
+ // here we don't need to do anything for now
+ } else {
+ // unkown packet
+ if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug,
+ __func__, "connid %u can't parse the message",
+ hicnConnState->id);
+ }
+ return false;
+ }
+
+ ssize_t writeLength =
+ write(hicnConnState->hicnListenerSocket, message_FixedHeader(message),
+ message_Length(message));
+
+ if (writeLength < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return false;
+ } else {
+ // this print is for debugging
+ printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength,
+ message_Length(message), errno, strerror(errno));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static list_connections_type _getConnectionType(const IoOperations *ops) {
+ return CONN_HICN;
+}
+
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ _HicnState *hicnConnState = (_HicnState *)ioOperations_GetClosure(ops);
+
+ if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) ||
+ (hicnConnState->localAddressLength == sizeof(struct sockaddr_in)))
+ return false;
+
+ if (hicnConnState->probeDestAddress == NULL &&
+ probeType == PACKET_TYPE_PROBE_REPLY) {
+ uint8_t *pkt = parcMemory_AllocateAndClear(
+ messageHandler_GetICMPPacketSize(IPv6_TYPE));
+ messageHandler_SetProbePacket(
+ pkt, probeType,
+ (struct in6_addr *)messageHandler_GetDestination(message),
+ (struct in6_addr *)messageHandler_GetSource(message));
+
+ ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt,
+ messageHandler_GetICMPPacketSize(IPv6_TYPE));
+
+ parcMemory_Deallocate((void **)&pkt);
+
+ if (writeLength < 0) {
+ return 0;
+ }
+
+ } else if (hicnConnState->probeDestAddress != NULL &&
+ probeType == PACKET_TYPE_PROBE_REQUEST) {
+ hicnConnState->refreshProbeDestAddress = true;
+
+ uint8_t *pkt = parcMemory_AllocateAndClear(
+ messageHandler_GetICMPPacketSize(IPv6_TYPE));
+ messageHandler_SetProbePacket(
+ pkt, probeType,
+ &((struct sockaddr_in6 *)hicnConnState->localAddress)->sin6_addr,
+ &((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr);
+
+ ssize_t writeLength = write(hicnConnState->hicnListenerSocket, pkt,
+ messageHandler_GetICMPPacketSize(IPv6_TYPE));
+
+ parcMemory_Deallocate((void **)&pkt);
+
+ if (writeLength < 0) {
+ return 0;
+ }
+
+ } else {
+ if (hicnConnState->probeDestAddress == NULL &&
+ probeType == PACKET_TYPE_PROBE_REQUEST) {
+ // this happen for the first probe
+ hicnConnState->refreshProbeDestAddress = true;
+ }
+ // do nothing
+ return 0;
+ }
+
+ return forwarder_GetTicks(hicnConnState->forwarder);
+}
+
+// =================================================================
+// Internal API
+
+static bool _saveSockaddr(_HicnState *hicnConnState, const AddressPair *pair) {
+ bool success = false;
+ const Address *remoteAddress = addressPair_GetRemote(pair);
+ const Address *localAddress = addressPair_GetLocal(pair);
+ switch (addressGetType(remoteAddress)) { // local must be of the same type
+
+ case ADDR_INET: {
+ size_t bytes = sizeof(struct sockaddr_in);
+ hicnConnState->peerAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(hicnConnState->peerAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet(remoteAddress,
+ (struct sockaddr_in *)hicnConnState->peerAddress);
+ hicnConnState->peerAddressLength = (socklen_t)bytes;
+
+ hicnConnState->localAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(hicnConnState->localAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet(localAddress,
+ (struct sockaddr_in *)hicnConnState->localAddress);
+ hicnConnState->localAddressLength = (socklen_t)bytes;
+
+ hicnConnState->probeDestAddress = NULL;
+ hicnConnState->probeDestAddressLength = (socklen_t)bytes;
+ hicnConnState->refreshProbeDestAddress = false;
+
+ success = true;
+ break;
+ }
+
+ case ADDR_INET6: {
+ size_t bytes = sizeof(struct sockaddr_in6);
+ hicnConnState->peerAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(hicnConnState->peerAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet6(remoteAddress,
+ (struct sockaddr_in6 *)hicnConnState->peerAddress);
+ hicnConnState->peerAddressLength = (socklen_t)bytes;
+
+ hicnConnState->localAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(hicnConnState->localAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet6(localAddress,
+ (struct sockaddr_in6 *)hicnConnState->localAddress);
+ hicnConnState->localAddressLength = (socklen_t)bytes;
+
+ hicnConnState->probeDestAddress = NULL;
+ hicnConnState->probeDestAddressLength = (socklen_t)bytes;
+ hicnConnState->refreshProbeDestAddress = false;
+
+ success = true;
+ break;
+ }
+
+ default:
+ if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ char *str = addressToString(remoteAddress);
+ logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Error,
+ __func__, "Remote address is not INET or INET6: %s", str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ break;
+ }
+ return success;
+}
+
+static void _refreshProbeDestAddress(_HicnState *hicnConnState,
+ const uint8_t *message) {
+ if ((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) ||
+ (hicnConnState->localAddressLength == sizeof(struct sockaddr_in)))
+ return;
+
+ if (hicnConnState->probeDestAddress == NULL) {
+ hicnConnState->probeDestAddress =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6));
+ parcAssertNotNull(hicnConnState->probeDestAddress,
+ "parcMemory_Allocate(%zu) returned NULL",
+ sizeof(struct sockaddr_in6));
+ }
+
+ ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_family =
+ AF_INET6;
+ ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_port =
+ htons(1234);
+ ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_scope_id = 0;
+ ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_flowinfo = 0;
+ ((struct sockaddr_in6 *)hicnConnState->probeDestAddress)->sin6_addr =
+ *((struct in6_addr *)messageHandler_GetDestination(message));
+ hicnConnState->refreshProbeDestAddress = false;
+}
+
+static void _setConnectionState(_HicnState *hicnConnState, bool isUp) {
+ parcAssertNotNull(hicnConnState, "Parameter HICN must be non-null");
+
+ Messenger *messenger = forwarder_GetMessenger(hicnConnState->forwarder);
+
+ bool oldStateIsUp = hicnConnState->isUp;
+ hicnConnState->isUp = isUp;
+
+ if (oldStateIsUp && !isUp) {
+ // bring connection DOWN
+ Missive *missive =
+ missive_Create(MissiveType_ConnectionDown, hicnConnState->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+
+ if (!oldStateIsUp && isUp) {
+ // bring connection UP
+ Missive *missive =
+ missive_Create(MissiveType_ConnectionUp, hicnConnState->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+}
diff --git a/hicn-light/src/io/hicnConnection.h b/hicn-light/src/io/hicnConnection.h
new file mode 100755
index 000000000..757e534ba
--- /dev/null
+++ b/hicn-light/src/io/hicnConnection.h
@@ -0,0 +1,53 @@
+/*
+ * 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 hicnConnection.h
+ * @brief Represents a HIcn connection for the connection table
+ *
+ * <#Detailed Description#>
+ *
+ */
+
+#ifndef hicnConnection_h
+#define hicnConnection_h
+
+#include <src/core/forwarder.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * Creates a HIcn connection that can send to the remote address
+ *
+ * The address pair must both be same type (i.e. INET or INET6).
+ *
+ * @param [in] an allocated hicn-light Forwarder (saves reference)
+ * @param [in] fd The socket to use
+ * @param [in] pair An allocated address pair for the connection (saves
+ * reference)
+ * @param [in] isLocal determines if the remote address is on the current system
+ *
+ * @retval non-null An allocated Io operations
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+IoOperations *hicnConnection_Create(Forwarder *forwarder, int fd,
+ const AddressPair *pair, bool isLocal);
+#endif // hicnConnection_h
diff --git a/hicn-light/src/io/hicnListener.c b/hicn-light/src/io/hicnListener.c
new file mode 100755
index 000000000..161f5b317
--- /dev/null
+++ b/hicn-light/src/io/hicnListener.c
@@ -0,0 +1,725 @@
+/*
+ * 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 <fcntl.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <unistd.h>
+
+#include <src/io/hicnConnection.h>
+#include <src/io/hicnListener.h>
+
+#include <src/core/connection.h>
+#include <src/core/connectionTable.h>
+#include <src/core/forwarder.h>
+#ifdef WITH_MAPME
+#include <src/config/symbolicNameTable.h>
+#include <src/core/mapMe.h>
+#include <src/core/message.h>
+#include <src/io/hicnTunnel.h>
+#endif /* WITH_MAPME */
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/mapMe.h>
+#include <src/core/messagePacketType.h>
+#include <src/io/listener.h>
+#include <src/socket/api.h>
+
+#define IPv6 6
+#define IPv4 4
+#define MTU_SIZE 1500 // bytes
+#define MAX_HICN_RETRY 5
+
+struct hicn_listener {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ PARCEvent *hicn_event;
+ int hicn_fd; // this is the file descriptor got from hicn library
+
+ Address *localAddress; // this is the local address or 0::0 in case of the
+ // main listener this is the address used inside
+ // forwarder to identify the listener. Notice that this
+ // address is the same as the fisical interfaces on
+ // which we create the TUN. it is NOT the TUN address
+ // which is given by libhicn after the bind operation
+ // However the user alway uses this address since is
+ // the only one available at configuration time
+
+ unsigned inetFamily;
+
+ int connection_id; // this is used only if the listener is used to receive
+ // data packets we assume that 1 connection is associated
+ // to one listener in this case so we set the connection_id
+ // we the connection is create. if this id is not set and a
+ // data packet is received, the packet is dropped
+
+ unsigned conn_id;
+};
+
+static void _destroy(ListenerOps **listenerOpsPtr);
+static unsigned _getInterfaceIndex(const ListenerOps *ops);
+static const Address *_getListenAddress(const ListenerOps *ops);
+static EncapType _getEncapType(const ListenerOps *ops);
+static int _getSocket(const ListenerOps *ops);
+
+static ListenerOps _hicnTemplate = {.context = NULL,
+ .destroy = &_destroy,
+ .getInterfaceIndex = &_getInterfaceIndex,
+ .getListenAddress = &_getListenAddress,
+ .getEncapType = &_getEncapType,
+ .getSocket = &_getSocket};
+
+static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid);
+
+static bool _isEmptyAddressIPv6(Address *address) {
+ struct sockaddr_in6 *addr6 =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6));
+ parcAssertNotNull(addr6, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(addr6));
+
+ addressGetInet6(address, addr6);
+
+ bool res = true;
+ for (int i = 0; i < 16; ++i) {
+ if (addr6->sin6_addr.s6_addr[i] != 0) {
+ res = false;
+ }
+ }
+
+ parcMemory_Deallocate((void **)&addr6);
+
+ return res;
+}
+
+static bool _isEmptyAddressIPv4(Address *address) {
+ bool res = false;
+
+ if (strcmp("inet4://0.0.0.0:1234", addressToString(address)) == 0) res = true;
+ return res;
+}
+
+ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic,
+ Address *address) {
+ HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener));
+ parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(HIcnListener));
+
+ hicn->forwarder = forwarder;
+ hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+
+ hicn->conn_id = forwarder_GetNextConnectionId(forwarder);
+ hicn->localAddress = addressCopy(address);
+
+ hicn->inetFamily = IPv4;
+
+ hicn->connection_id = -1;
+
+ hicn_socket_helper_t *hicnSocketHelper =
+ forwarder_GetHIcnSocketHelper(forwarder);
+
+ if (_isEmptyAddressIPv4(address)) {
+ hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL);
+ } else {
+ struct sockaddr_in *tmpAddr =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in));
+ parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(tmpAddr));
+ addressGetInet(address, tmpAddr);
+ char *local_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &(tmpAddr->sin_addr), local_addr, INET_ADDRSTRLEN);
+ parcMemory_Deallocate((void **)&tmpAddr);
+
+ hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr);
+
+ parcMemory_Deallocate((void **)&local_addr);
+ }
+
+ if (hicn->hicn_fd < 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(
+ hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "HIcnListener %s: error while creating an hicn listener in lib_hicn",
+ symbolic);
+ }
+ logger_Release(&hicn->logger);
+ addressDestroy(&hicn->localAddress);
+ parcMemory_Deallocate((void **)&hicn);
+ return NULL;
+ }
+
+ // Set non-blocking flag
+ int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL);
+ parcAssertTrue(flags != -1,
+ "fcntl failed to obtain file descriptor flags (%d)", errno);
+ int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK);
+ parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)",
+ errno);
+
+ hicn->hicn_event = dispatcher_CreateNetworkEvent(
+ forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb,
+ (void *)hicn, hicn->hicn_fd);
+ dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder),
+ hicn->hicn_event);
+
+ ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+
+ memcpy(ops, &_hicnTemplate, sizeof(ListenerOps));
+ ops->context = hicn;
+
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "HIcnListener %s created", symbolic);
+ }
+
+ return ops;
+ return NULL;
+}
+
+ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic,
+ Address *address) {
+ HIcnListener *hicn = parcMemory_AllocateAndClear(sizeof(HIcnListener));
+ parcAssertNotNull(hicn, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(HIcnListener));
+
+ hicn->forwarder = forwarder;
+ hicn->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+
+ hicn->conn_id = forwarder_GetNextConnectionId(forwarder);
+ hicn->localAddress = addressCopy(address);
+
+ hicn->inetFamily = IPv6;
+
+ hicn->connection_id = -1;
+
+ // the call to libhicn is the same both for the main and the normal listeners
+ // in both cases we need to set only the identifier. In the case of normal
+ // listener (listener for data packet) we let the library select the right ip
+ //address we just need to set the right type of packet
+
+ hicn_socket_helper_t *hicnSocketHelper =
+ forwarder_GetHIcnSocketHelper(forwarder);
+
+ if (_isEmptyAddressIPv6(address)) {
+ // create main listener
+ hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, NULL);
+ } else {
+ // create listener for the connetion
+ struct sockaddr_in6 *tmpAddr =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6));
+
+ parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(tmpAddr));
+ addressGetInet6(address, tmpAddr);
+
+ char *local_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN);
+ inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), local_addr, INET6_ADDRSTRLEN);
+
+ parcMemory_Deallocate((void **)&tmpAddr);
+
+ hicn->hicn_fd = hicn_socket(hicnSocketHelper, symbolic, local_addr);
+
+ parcMemory_Deallocate((void **)&local_addr);
+ }
+
+ if (hicn->hicn_fd < 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(
+ hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "HIcnListener %s: error while creating an hicn listener in lib_hicn",
+ symbolic);
+ }
+ logger_Release(&hicn->logger);
+ addressDestroy(&hicn->localAddress);
+ parcMemory_Deallocate((void **)&hicn);
+ return NULL;
+ }
+
+ // Set non-blocking flag
+ int flags = fcntl(hicn->hicn_fd, F_GETFL, NULL);
+ parcAssertTrue(flags != -1,
+ "fcntl failed to obtain file descriptor flags (%d)", errno);
+ int failure = fcntl(hicn->hicn_fd, F_SETFL, flags | O_NONBLOCK);
+ parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)",
+ errno);
+
+ hicn->hicn_event = dispatcher_CreateNetworkEvent(
+ forwarder_GetDispatcher(forwarder), true, _hicnListener_readcb,
+ (void *)hicn, hicn->hicn_fd);
+ dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder),
+ hicn->hicn_event);
+
+ ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+
+ memcpy(ops, &_hicnTemplate, sizeof(ListenerOps));
+ ops->context = hicn;
+
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "HIcnListener %s created", symbolic);
+ }
+
+ return ops;
+}
+
+bool _hicnListener_BindInet6(ListenerOps *ops, const Address *remoteAddress) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ hicn_socket_helper_t *hicnSocketHelper =
+ forwarder_GetHIcnSocketHelper(hicn->forwarder);
+
+ struct sockaddr_in6 *tmpAddr =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in6));
+ parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(tmpAddr));
+ addressGetInet6(remoteAddress, tmpAddr);
+ char *remote_addr = parcMemory_AllocateAndClear(INET6_ADDRSTRLEN);
+ inet_ntop(AF_INET6, &(tmpAddr->sin6_addr), remote_addr, INET6_ADDRSTRLEN);
+ parcMemory_Deallocate((void **)&tmpAddr);
+
+ int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr);
+
+ bool result = false;
+ if (res < 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "hicn_bild failed %d %s", res, hicn_socket_strerror(res));
+ }
+ } else {
+ result = true;
+ }
+
+ parcMemory_Deallocate((void **)&remote_addr);
+
+ return result;
+}
+
+bool _hicnListener_BindInet(ListenerOps *ops, const Address *remoteAddress) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ hicn_socket_helper_t *hicnSocketHelper =
+ forwarder_GetHIcnSocketHelper(hicn->forwarder);
+
+ struct sockaddr_in *tmpAddr =
+ parcMemory_AllocateAndClear(sizeof(struct sockaddr_in));
+ parcAssertNotNull(tmpAddr, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(tmpAddr));
+ addressGetInet(remoteAddress, tmpAddr);
+ char *remote_addr = parcMemory_AllocateAndClear(INET_ADDRSTRLEN);
+ inet_ntop(AF_INET, &(tmpAddr->sin_addr), remote_addr, INET_ADDRSTRLEN);
+ parcMemory_Deallocate((void **)&tmpAddr);
+
+ int res = hicn_bind(hicnSocketHelper, hicn->hicn_fd, remote_addr);
+ bool result = false;
+
+ if (res < 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "hicn_bild failed %d %s", res, hicn_socket_strerror(res));
+ }
+ } else {
+ result = true;
+ }
+
+ parcMemory_Deallocate((void **)&remote_addr);
+
+ return result;
+}
+
+bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress) {
+ if (addressGetType(remoteAddress) == ADDR_INET) {
+ return _hicnListener_BindInet(ops, remoteAddress);
+ } else if (addressGetType(remoteAddress) == ADDR_INET6) {
+ return _hicnListener_BindInet6(ops, remoteAddress);
+ } else {
+ printf("Bind failed: Invalid address\n");
+ return false;
+ }
+}
+
+bool hicnListener_Punting(ListenerOps *ops, const char *prefix) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ hicn_socket_helper_t *hicnSocketHelper =
+ forwarder_GetHIcnSocketHelper(hicn->forwarder);
+
+ int res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix);
+ int retry = 0;
+
+ while (res < 0 && retry < MAX_HICN_RETRY) {
+ sleep(1);
+ res = hicn_listen(hicnSocketHelper, hicn->hicn_fd, prefix);
+ retry++;
+ }
+
+ if (res < 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "hicn_listen failed %d %s", res, hicn_socket_strerror(res));
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ if (hicn) {
+ hicn->connection_id = connId;
+ return true;
+ }
+ return false;
+}
+
+static void _hicnListener_Destroy(HIcnListener **listenerPtr) {
+ parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer");
+ parcAssertNotNull(*listenerPtr,
+ "Parameter must derefernce to non-null pointer");
+
+ HIcnListener *hicn = *listenerPtr;
+
+ // close(hicn->hicn_fd); //XXX close the fd in the hicnlib (detroy listener?)
+ dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(hicn->forwarder),
+ &hicn->hicn_event);
+ logger_Release(&hicn->logger);
+ addressDestroy(&hicn->localAddress);
+ parcMemory_Deallocate((void **)&hicn);
+ *listenerPtr = NULL;
+}
+
+static void _destroy(ListenerOps **listenerOpsPtr) {
+ ListenerOps *ops = *listenerOpsPtr;
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ _hicnListener_Destroy(&hicn);
+ parcMemory_Deallocate((void **)&ops);
+ *listenerOpsPtr = NULL;
+}
+
+static unsigned _getInterfaceIndex(const ListenerOps *ops) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ return hicn->conn_id;
+}
+
+static const Address *_getListenAddress(const ListenerOps *ops) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ return hicn->localAddress;
+}
+
+static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_HICN; }
+
+static int _getSocket(const ListenerOps *ops) {
+ HIcnListener *hicn = (HIcnListener *)ops->context;
+ return hicn->hicn_fd;
+}
+
+// ===============================
+
+static void _readFrameToDiscard(HIcnListener *hicn, int fd) {
+ // we need to discard the frame. Read 1 byte. This will clear it off the
+ // stack.
+ uint8_t buffer;
+ int nread = read(fd, &buffer, 1);
+
+ if (nread > 0) {
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "Discarded frame from fd %d", fd);
+ }
+ } else if (nread < 0) {
+ printf("Error trying to discard frame from fd %d: (%d) %s", fd, errno,
+ strerror(errno));
+ if (logger_IsLoggable(hicn->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ logger_Log(hicn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Error trying to discard frame from fd %d: (%d) %s", fd, errno,
+ strerror(errno));
+ }
+ }
+}
+
+static unsigned _createNewConnection(HIcnListener *hicn, int fd,
+ const AddressPair *pair) {
+ bool isLocal = false;
+
+ // udpConnection_Create takes ownership of the pair
+ IoOperations *ops = hicnConnection_Create(hicn->forwarder, fd, pair, isLocal);
+ Connection *conn = connection_Create(ops);
+
+ connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder), conn);
+ unsigned connid = ioOperations_GetConnectionId(ops);
+
+ return connid;
+}
+
+const Connection *_findConnectionFromPacket(HIcnListener *hicn,
+ Address *packetSourceAddress) {
+ const Connection *conn = NULL;
+ if (hicn->connection_id != -1) {
+ conn = connectionTable_FindById(
+ forwarder_GetConnectionTable(hicn->forwarder), hicn->connection_id);
+ } else {
+ if (packetSourceAddress != NULL) {
+ // in this first check we try to retrieve the standard connection
+ // generated by the hicn-light
+ AddressPair *pair =
+ addressPair_Create(hicn->localAddress, packetSourceAddress);
+ conn = connectionTable_FindByAddressPair(
+ forwarder_GetConnectionTable(hicn->forwarder), pair);
+ addressPair_Release(&pair);
+ }
+ }
+
+ return conn;
+}
+
+static Address *_createAddressFromPacket(uint8_t *msgBuffer) {
+ Address *packetAddr = NULL;
+ if (messageHandler_GetIPPacketType(msgBuffer) == IPv6_TYPE) {
+ struct sockaddr_in6 addr_in6;
+ addr_in6.sin6_family = AF_INET6;
+ addr_in6.sin6_port = htons(1234);
+ addr_in6.sin6_flowinfo = 0;
+ addr_in6.sin6_scope_id = 0;
+ memcpy(&addr_in6.sin6_addr,
+ (struct in6_addr *)messageHandler_GetSource(msgBuffer), 16);
+ packetAddr = addressCreateFromInet6(&addr_in6);
+ } else if (messageHandler_GetIPPacketType(msgBuffer) == IPv4_TYPE) {
+ struct sockaddr_in addr_in;
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = htons(1234);
+ memcpy(&addr_in.sin_addr,
+ (struct in_addr *)messageHandler_GetSource(msgBuffer), 4);
+ packetAddr = addressCreateFromInet(&addr_in);
+ }
+ return packetAddr;
+}
+
+static void _handleProbeMessage(HIcnListener *hicn, uint8_t *msgBuffer) {
+ Address *packetAddr = _createAddressFromPacket(msgBuffer);
+
+ if (packetAddr != NULL) {
+ const Connection *conn = _findConnectionFromPacket(hicn, packetAddr);
+ if (conn != NULL) {
+ // we drop all the probes for a connection that does not exists
+ connection_HandleProbe((Connection *)conn, msgBuffer,
+ forwarder_GetTicks(hicn->forwarder));
+ }
+ }
+
+ addressDestroy(&packetAddr);
+ parcMemory_Deallocate((void **)&msgBuffer);
+}
+
+static void _handleWldrNotification(HIcnListener *hicn, uint8_t *msgBuffer) {
+ Address *packetAddr = _createAddressFromPacket(msgBuffer);
+
+ if (packetAddr == NULL) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return;
+ }
+
+ const Connection *conn = _findConnectionFromPacket(hicn, packetAddr);
+ if (conn == NULL) {
+ addressDestroy(&packetAddr);
+ return;
+ }
+
+ addressDestroy(&packetAddr);
+
+ Message *message = message_CreateFromByteArray(
+ connection_GetConnectionId(conn), msgBuffer,
+ MessagePacketType_WldrNotification, forwarder_GetTicks(hicn->forwarder),
+ forwarder_GetLogger(hicn->forwarder));
+
+ connection_HandleWldrNotification((Connection *)conn, message);
+
+ message_Release(&message);
+}
+
+#ifdef WITH_MAPME
+static void _handleMapMe(HIcnListener *hicn, int fd, uint8_t *msgBuffer) {
+ Address *packetAddr = _createAddressFromPacket(msgBuffer);
+
+ if (packetAddr == NULL) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return;
+ }
+
+ const Connection *conn = _findConnectionFromPacket(hicn, packetAddr);
+ unsigned conn_id;
+ if (conn == NULL) {
+ /* Unlike the interest path, we don't create virtual connections bound
+ * on the listener, whose only interest is to send data, but full
+ * tunnels to be able to route interests
+ *
+ * packetAddr is the remote address, we need to ask the lib for our
+ * local address
+ * hicn->localAddress is None as the interest is received by the main
+ * listener.
+ */
+ printf("MapMe, connection did not exist, creating\n");
+
+ /* Populate remote_address through packetAddr */
+ struct sockaddr_in6 sockaddr; // XXX IPv6 only
+ addressGetInet6(packetAddr, &sockaddr);
+ ip_address_t remote_address = {.family = AF_INET6,
+ .prefix_len = IPV6_ADDR_LEN_BITS};
+ memcpy(&remote_address.buffer, &sockaddr.sin6_addr,
+ ip_address_len(&remote_address));
+
+ /* Get local address through libhicn */
+ ip_address_t local_address;
+ int rc = hicn_get_local_address(&remote_address, &local_address);
+ if (rc < 0) {
+ printf("Error getting local address. Discarded mapme packet.\n");
+ return;
+ }
+
+ struct sockaddr_in6 addr_in6;
+ addr_in6.sin6_family = AF_INET6;
+ addr_in6.sin6_port = htons(1234);
+ addr_in6.sin6_flowinfo = 0;
+ addr_in6.sin6_scope_id = 0;
+ memcpy(&addr_in6.sin6_addr, (struct in6_addr *)&(local_address.buffer), 16);
+
+ Address *localAddr = addressCreateFromInet6(&addr_in6);
+ IoOperations *ops =
+ hicnTunnel_Create(hicn->forwarder, localAddr, packetAddr);
+
+ if (!ops) {
+ printf("Error creating tunnel. Discarded mapme packet.\n");
+ return;
+ }
+
+ conn = connection_Create(ops);
+
+ connectionTable_Add(forwarder_GetConnectionTable(hicn->forwarder),
+ (Connection *)conn);
+ }
+ conn_id = connection_GetConnectionId(conn);
+
+ addressDestroy(&packetAddr);
+
+ forwarder_ProcessMapMe(hicn->forwarder, msgBuffer, conn_id);
+}
+#endif /* WITH_MAPME */
+
+static Message *_readMessage(HIcnListener *hicn, int fd, uint8_t *msgBuffer) {
+ Message *message = NULL;
+
+ ssize_t readLength = read(fd, msgBuffer, MTU_SIZE);
+
+ if (readLength < 0) {
+ printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno));
+ return message;
+ }
+
+ size_t packetLength = messageHandler_GetTotalPacketLength(msgBuffer);
+
+ if (readLength != packetLength) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return message;
+ }
+
+ if (messageHandler_IsTCP(msgBuffer)) {
+ MessagePacketType pktType;
+ unsigned connid = 0;
+ if (messageHandler_IsData(msgBuffer)) {
+ pktType = MessagePacketType_ContentObject;
+ if (hicn->connection_id == -1) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return message;
+ } else {
+ connid = hicn->connection_id;
+ }
+ } else if (messageHandler_IsInterest(msgBuffer)) {
+ // notice that the connections for the interest (the one that we create at
+ // run time) uses as a local address 0::0, so the main tun
+ pktType = MessagePacketType_Interest;
+ Address *packetAddr = _createAddressFromPacket(msgBuffer);
+ const Connection *conn = _findConnectionFromPacket(hicn, packetAddr);
+
+ if (conn == NULL) {
+ AddressPair *pair = addressPair_Create(hicn->localAddress, packetAddr);
+ connid = _createNewConnection(hicn, fd, pair);
+ addressPair_Release(&pair);
+ } else {
+ connid = connection_GetConnectionId(conn);
+ }
+ addressDestroy(&packetAddr);
+ } else {
+ printf("Got a packet that is not a data nor an interest, drop it!\n");
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return message;
+ }
+
+ message = message_CreateFromByteArray(connid, msgBuffer, pktType,
+ forwarder_GetTicks(hicn->forwarder),
+ forwarder_GetLogger(hicn->forwarder));
+ if (message == NULL) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ }
+ } else if (messageHandler_IsWldrNotification(msgBuffer)) {
+ _handleWldrNotification(hicn, msgBuffer);
+ } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) {
+ _handleProbeMessage(hicn, msgBuffer);
+ }
+#ifdef WITH_MAPME
+ else if (mapMe_isMapMe(msgBuffer)) {
+ /* This function triggers the handling of the MAP-Me message, and we
+ * will return NULL so as to terminate the processing of this
+ * msgBuffer. */
+ _handleMapMe(hicn, fd, msgBuffer);
+ }
+#endif /* WITH_MAPME */
+
+ return message;
+}
+
+static void _receivePacket(HIcnListener *hicn, int fd) {
+ Message *msg = NULL;
+ uint8_t *msgBuffer = parcMemory_AllocateAndClear(MTU_SIZE);
+ msg = _readMessage(hicn, fd, msgBuffer);
+
+ if (msg) {
+ forwarder_Receive(hicn->forwarder, msg);
+ }
+}
+
+static void _hicnListener_readcb(int fd, PARCEventType what, void *hicnVoid) {
+ HIcnListener *hicn = (HIcnListener *)hicnVoid;
+
+ if (hicn->inetFamily == IPv4 || hicn->inetFamily == IPv6) {
+ if (what & PARCEventType_Read) {
+ _receivePacket(hicn, fd);
+ }
+ } else {
+ _readFrameToDiscard(hicn, fd);
+ }
+}
diff --git a/hicn-light/src/io/hicnListener.h b/hicn-light/src/io/hicnListener.h
new file mode 100755
index 000000000..98c5387c9
--- /dev/null
+++ b/hicn-light/src/io/hicnListener.h
@@ -0,0 +1,42 @@
+/*
+ * 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 hicnListener.h
+ * @brief Listens for in coming HIcn connections
+ *
+ *
+ */
+
+#ifndef hicnListener_h
+#define hicnListener_h
+
+#include <src/core/forwarder.h>
+#include <src/core/messageHandler.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+struct hicn_listener;
+typedef struct hicn_listener HIcnListener;
+
+ListenerOps *hicnListener_CreateInet(Forwarder *forwarder, char *symbolic,
+ Address *address);
+ListenerOps *hicnListener_CreateInet6(Forwarder *forwarder, char *symbolic,
+ Address *address);
+bool hicnListener_Punting(ListenerOps *ops, const char *prefix);
+bool hicnListener_Bind(ListenerOps *ops, const Address *remoteAddress);
+bool hicnListener_SetConnectionId(ListenerOps *ops, unsigned connId);
+// const Address *hicnListener_GetTunAddress(const ListenerOps *ops);
+#endif // hicnListener_h
diff --git a/hicn-light/src/io/hicnTunnel.c b/hicn-light/src/io/hicnTunnel.c
new file mode 100755
index 000000000..e55393137
--- /dev/null
+++ b/hicn-light/src/io/hicnTunnel.c
@@ -0,0 +1,106 @@
+/*
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/hicnConnection.h>
+#include <src/io/hicnListener.h>
+#include <src/io/hicnTunnel.h>
+
+IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder,
+ ListenerOps *localListener,
+ const Address *remoteAddress) {
+ parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null");
+ parcAssertNotNull(localListener, "Parameter localListener must be non-null");
+ parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null");
+
+ Logger *logger = forwarder_GetLogger(forwarder);
+
+ IoOperations *ops = NULL;
+ if (localListener->getEncapType(localListener) == ENCAP_HICN) {
+ const Address *localAddress =
+ localListener->getListenAddress(localListener);
+ address_type localType = addressGetType(localAddress);
+ address_type remoteType = addressGetType(remoteAddress);
+
+ if (localType == remoteType) {
+ bool res = hicnListener_Bind(localListener, remoteAddress);
+ if (res == false) {
+ if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Unable to bind local listener to remote node");
+ }
+ return ops;
+ }
+
+ // localAddress = hicnListener_GetTunAddress(localListener); //This is the
+ // true local address
+
+ AddressPair *pair = addressPair_Create(localAddress, remoteAddress);
+ bool isLocal = false;
+ int fd = localListener->getSocket(localListener);
+ ops = hicnConnection_Create(forwarder, fd, pair, isLocal);
+
+ addressPair_Release(&pair);
+ } else {
+ if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Local listener of type %s and remote type %s, cannot "
+ "establish tunnel",
+ addressTypeToString(localType),
+ addressTypeToString(remoteType));
+ }
+ }
+ } else {
+ if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Local listener %p is not type UDP, cannot establish tunnel",
+ (void *)localListener);
+ }
+ }
+
+ return ops;
+}
+
+IoOperations *hicnTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress) {
+ ListenerSet *set = forwarder_GetListenerSet(forwarder);
+ ListenerOps *listener = listenerSet_Find(set, ENCAP_HICN, localAddress);
+ IoOperations *ops = NULL;
+ if (listener) {
+ ops = hicnTunnel_CreateOnListener(forwarder, listener, remoteAddress);
+ } else {
+ if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ char *str = addressToString(localAddress);
+ logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO,
+ PARCLogLevel_Error, __func__,
+ "Could not find listener to match address %s", str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ }
+
+ if (ops) {
+ hicnListener_SetConnectionId(listener, ops->getConnectionId(ops));
+ }
+
+ return ops;
+}
diff --git a/hicn-light/src/io/hicnTunnel.h b/hicn-light/src/io/hicnTunnel.h
new file mode 100755
index 000000000..70295797c
--- /dev/null
+++ b/hicn-light/src/io/hicnTunnel.h
@@ -0,0 +1,65 @@
+/*
+ * 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 hicnTunnel.h
+ * @brief Establish a tunnel to a remote system
+ *
+ * Creates a "hicn tunnel" to a remote system. There must already be a local
+ * HICN listener for the local side of the connection.
+ *
+ */
+
+#ifndef hicnTunnel_h
+#define hicnTunnel_h
+
+#include <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ * Establishes a connection to a remote system over HICN
+ *
+ * The remoteAddress must be of the same type (i.e. v4 or v6) as the
+ * localAddress. There must be an existing HICN listener on the local address.
+ * If either of these are not true, will return NULL.
+ *
+ * The connection will go in the table immediately, and will be in the "up"
+ * state.
+ *
+ * @param [in] an allocated hicn-light Forwarder
+ * @param [in] localAddress The local IP address and port to use for the
+ * connection
+ * @param [in] remote Address the remote IP address for the connection, must
+ * include a destination port.
+ *
+ * @retval non-null An allocated Io Operations structure for the connection
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+IoOperations *hicnTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress);
+
+IoOperations *hicnTunnel_CreateOnListener(Forwarder *forwarder,
+ ListenerOps *localListener,
+ const Address *remoteAddress);
+
+#endif // hicnTunnel_h
diff --git a/hicn-light/src/io/ioOperations.c b/hicn-light/src/io/ioOperations.c
new file mode 100755
index 000000000..bbc8cec91
--- /dev/null
+++ b/hicn-light/src/io/ioOperations.c
@@ -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.
+ */
+
+#include <parc/assert/parc_Assert.h>
+#include <src/config.h>
+#include <src/io/ioOperations.h>
+#include <stdio.h>
+
+void *ioOperations_GetClosure(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ return ops->closure;
+}
+
+bool ioOperations_Send(IoOperations *ops, const Address *nexthop,
+ Message *message) {
+ return ops->send(ops, nexthop, message);
+}
+
+const Address *ioOperations_GetRemoteAddress(const IoOperations *ops) {
+ return ops->getRemoteAddress(ops);
+}
+
+const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops) {
+ return ops->getAddressPair(ops);
+}
+
+bool ioOperations_IsUp(const IoOperations *ops) { return ops->isUp(ops); }
+
+bool ioOperations_IsLocal(const IoOperations *ops) { return ops->isLocal(ops); }
+
+unsigned ioOperations_GetConnectionId(const IoOperations *ops) {
+ return ops->getConnectionId(ops);
+}
+
+void ioOperations_Release(IoOperations **opsPtr) {
+ IoOperations *ops = *opsPtr;
+ ops->destroy(opsPtr);
+}
+
+const void *ioOperations_Class(const IoOperations *ops) {
+ return ops->class(ops);
+}
+
+list_connections_type ioOperations_GetConnectionType(const IoOperations *ops) {
+ return ops->getConnectionType(ops);
+}
+
+Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message) {
+ return ops->sendProbe(ops, probeType, message);
+}
diff --git a/hicn-light/src/io/ioOperations.h b/hicn-light/src/io/ioOperations.h
new file mode 100755
index 000000000..dee66030d
--- /dev/null
+++ b/hicn-light/src/io/ioOperations.h
@@ -0,0 +1,394 @@
+/*
+ * 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.
+ */
+
+/**
+ * Defines the interface all connections use to communicate with the forwarder.
+ *
+ * @code
+ *
+ * static IoOperations _template = {
+ * .closure = NULL,
+ * .send = &_etherConnection_Send,
+ * .getRemoteAddress = &_etherConnection_GetRemoteAddress,
+ * .getAddressPair = &_etherConnection_GetAddressPair,
+ * .getConnectionId = &_etherConnection_GetConnectionId,
+ * .isUp = &_etherConnection_IsUp,
+ * .isLocal = &_etherConnection_IsLocal,
+ * .destroy = &_etherConnection_DestroyOperations,
+ * .class = &_etherConnection_Class,
+ * .getConnectionType = &_etherConnection_getConnectionType
+ * };
+ *
+ * IoOperations *
+ * etherConnection_Create(Forwarder *forwarder, GenericEther *ether,
+ * AddressPair *pair)
+ * {
+ * _EtherState *etherConnState = parcMemory_Allocate(sizeof(_EtherState));
+ * // Fill in etherConnState with instance variables
+ *
+ * IoOperations *io_ops = parcMemory_Allocate(sizeof(IoOperations));
+ * memcpy(io_ops, &_template, sizeof(IoOperations));
+ * io_ops->closure = etherConnState;
+ * // Add to connection table, send missives about connection state
+ *
+ * return op_ops;
+ * }
+ * @endcode
+ *
+ */
+
+/**
+ * I/O is built around a callback structure. The connection table contains an
+ * operations structure built around function pointers. These allow the
+ * connection table to be agnostic about underlying connections.
+ */
+
+#ifndef io_h
+#define io_h
+
+#include <src/core/message.h>
+#include <src/core/ticks.h>
+#include <src/io/addressPair.h>
+#include <src/utils/address.h>
+
+// packet types for probing
+#define PACKET_TYPE_PROBE_REQUEST 5
+#define PACKET_TYPE_PROBE_REPLY 6
+
+struct io_ops;
+typedef struct io_ops IoOperations;
+
+/**
+ * @typedef IoOperations
+ * @abstract The IO Operations structure abstracts an connection's properties
+ * and send() method
+ * @constant context Implementation specific opaque data, passed back on each
+ * call
+ * @constant send function pointer to send a message, does not destroy the
+ * message
+ * @constant getRemoteAddress function pointer to return the "to" address
+ * associated with the connection. Some connections might not have a specific
+ * peer, such as multicast, where its the group address.
+ * @constant isUp test if the connection is up, ready to send a message.
+ * @constant isLocal test if the connection is local to the host.
+ * @constant getConnectionId returns the hicn-light id for the connection.
+ * @constant destroy releases a refernce count on the connection and possibly
+ * destroys the connection.
+ * @constant class A unique identifier for each class that instantiates
+ * IoOperations.
+ * @constant getConnectionType Returns the type of connection (TCP, UDP, L2,
+ * etc.) of the underlying connection.
+ * @discussion <#Discussion#>
+ */
+struct io_ops {
+ void *closure;
+ bool (*send)(IoOperations *ops, const Address *nexthop, Message *message);
+ const Address *(*getRemoteAddress)(const IoOperations *ops);
+ const AddressPair *(*getAddressPair)(const IoOperations *ops);
+ bool (*isUp)(const IoOperations *ops);
+ bool (*isLocal)(const IoOperations *ops);
+ unsigned (*getConnectionId)(const IoOperations *ops);
+ void (*destroy)(IoOperations **opsPtr);
+ const void *(*class)(const IoOperations *ops);
+ list_connections_type (*getConnectionType)(const IoOperations *ops);
+ Ticks (*sendProbe)(IoOperations *ops, unsigned probeType, uint8_t *message);
+};
+
+/**
+ * Returns the closure of the interface
+ *
+ * The creator of the closure sets this parameter to store its state.
+ *
+ * @param [in] ops A concrete instance of the interface
+ *
+ * @return The value set by the concrete instance of the interface.
+ *
+ * Example:
+ * @clode
+ * {
+
+ * }
+ * @endcode
+ */
+void *ioOperations_GetClosure(const IoOperations *ops);
+
+/**
+ * Release all memory related to the interface and implementation
+ *
+ * This function must release all referenced memory in the concrete
+ * implementation and memory related to the IoOperations. It should NULL the
+ * input parameter.
+ *
+ * @param [in,out] opsPtr Pointer to interface. Will be NULLed.
+ *
+ * Example:
+ * @code
+ *
+ * static void
+ * _etherConnection_InternalRelease(_EtherState *etherConnState)
+ * {
+ * // release internal state of _EtherState
+ * }
+ *
+ * static void
+ * _etherConnection_Release(IoOperations **opsPtr)
+ * {
+ * IoOperations *ops = *opsPtr;
+ *
+ * _EtherState *etherConnState = (_EtherState *)
+ * ioOperations_GetClosure(ops);
+ * _etherConnection_InternalRelease(etherConnState);
+ *
+ * parcMemory_Deallocate((void **) &ops);
+ * }
+ *
+ * IoOperations *
+ * etherConnection_Create(Forwarder *forwarder, GenericEther *ether,
+ * AddressPair *pair)
+ * {
+ * size_t allocationSize = sizeof(_EtherState) + sizeof(IoOperations);
+ * IoOperations *ops = parcMemory_AllocateAndClear(allocationSize);
+ * if (ops) {
+ * // fill in other interface functions
+ * ops->destroy = &_etherConnection_Release;
+ * ops->closure = (uint8_t *) ops + sizeof(IoOperations);
+ *
+ * _EtherState *etherConnState = ioOperations_GetClosure(ops);
+ * // fill in Ethernet state
+ * }
+ * return ops;
+ * }
+ * @endcode
+ */
+void ioOperations_Release(IoOperations **opsPtr);
+
+/**
+ * Sends the specified Message out this connection
+ *
+ * The the implementation of send may queue the message, it must acquire a
+ * reference to it.
+ *
+ * @param [in] ops The connection implementation.
+ * @param [in] nexthop On multiple access networks, this parameter might be
+ * used, usually NULL.
+ * @param [in] message The message to send. If the message will be queued, it
+ * will be acquired.
+ *
+ * @return true The message was sent or queued
+ * @retrun false An error occured and the message will not be sent or queued
+ *
+ * Example:
+ * @code
+ * {
+ * if (ioOperations_IsUp(conn->ops)) {
+ * return ioOperations_Send(conn->ops, NULL, message);
+ * }
+ * }
+ * @endcode
+ */
+bool ioOperations_Send(IoOperations *ops, const Address *nexthop,
+ Message *message);
+
+/**
+ * A connection is made up of a local and a remote address. This function
+ * returns the remote address.
+ *
+ * Represents the destination endpoint of the communication.
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return non-null The remote address
+ * @return null The connection does not have a remote address
+ *
+ * Example:
+ * @code
+ * {
+ * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03,
+ * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t [])
+ * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair =
+ * addressPair_Create(local, remote); IoOperations *ops =
+ * etherConnection_Create(forwarder, ether, pair);
+ *
+ * const Address *test = ioOperations_GetRemoteAddress(ops);
+ * parcAssertTrue(addressEquals(test, remote), "Wrong remote address");
+ * ioOperations_Release(&ops);
+ * addressPair_Release(&pair);
+ * addressDestroy(&local);
+ * addressDestroy(&remote);
+ * }
+ * @endcode
+ */
+const Address *ioOperations_GetRemoteAddress(const IoOperations *ops);
+
+/**
+ * A connection is made up of a local and a remote address. This function
+ * returns the address pair.
+ *
+ * Represents the destination endpoint of the communication.
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return non-null The address pair
+ * @return null An error.
+ *
+ * Example:
+ * @code
+ * {
+ * Address *local = addressCreateFromLink((uint8_t []) { 0x01, 0x02, 0x03,
+ * 0x04, 0x05, 0x06 }, 6); Address *remote = addressCreateFromLink((uint8_t [])
+ * { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }, 6); AddressPair *pair =
+ * addressPair_Create(local, remote); IoOperations *ops =
+ * etherConnection_Create(forwarder, ether, pair);
+ *
+ * const AddressPair *test = ioOperations_GetAddressPair(ops);
+ * parcAssertTrue(addressPair(test, pair), "Wrong address pair");
+ * ioOperations_Release(&ops);
+ * addressPair_Release(&pair);
+ * addressDestroy(&local);
+ * addressDestroy(&remote);
+ * }
+ * @endcode
+ */
+const AddressPair *ioOperations_GetAddressPair(const IoOperations *ops);
+
+/**
+ * Returns true if the underlying connection is in operation
+ *
+ * An UP connection is able to send and receive packets. If a subsystem needs to
+ * take actions when a connection goes UP or DOWN, it should subscribe as a
+ * Missive listener.
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return true The connection is UP
+ * @return false The connection is not UP
+ *
+ * Example:
+ * @code
+ * {
+ * if (ioOperations_IsUp(conn->ops)) {
+ * return ioOperations_Send(conn->ops, NULL, message);
+ * }
+ * }
+ * @endcode
+ */
+bool ioOperations_IsUp(const IoOperations *ops);
+
+/**
+ * If the remote address is local to this system, returns true
+ *
+ * Will return true if an INET or INET6 connection is on localhost. Will return
+ * true for AF_UNIX. An Ethernet connection is not local.
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return true The remote address is local to the system
+ * @return false The remote address is not local
+ *
+ * Example:
+ * @code
+ * {
+ * // Is the ingress connection remote? If so check for non-zero and
+ * decrement if (!ioOperations(ingressConnectionOps) { uint8_t hoplimit =
+ * message_GetHopLimit(interestMessage); if (hoplimit == 0) {
+ * // error
+ * } else {
+ * hoplimit--;
+ * }
+ * // take actions on hoplimit
+ * }
+ * }
+ * @endcode
+ */
+bool ioOperations_IsLocal(const IoOperations *ops);
+
+/**
+ * Returns the connection ID represented by this IoOperations in the
+ * ConnectionTable.
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return number The connection ID in the connection table.
+ *
+ * Example:
+ * @code
+ * {
+ * unsigned id = ioOperations_GetConnectionId(ingressIoOps);
+ * const Connection *conn =
+ * connectionTable_FindById(forwarder->connectionTable, id);
+ * }
+ * @endcode
+ */
+unsigned ioOperations_GetConnectionId(const IoOperations *ops);
+
+/**
+ * A pointer that represents the class of the connection
+ *
+ * Each concrete implementation has a class pointer that is unique to the
+ * implementation (not instance). Each implementation is free to choose how to
+ * determine the value, so long as it is unique on the system. This is a
+ * system-local value.
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return non-null A pointer value unique to the implementation (not instance).
+ *
+ * Example:
+ * @code
+ * bool
+ * etherConnection_IsInstanceOf(const Connection *conn)
+ * {
+ * bool result = false;
+ * if (conn != NULL) {
+ * IoOperations *ops = connection_GetIoOperations(conn);
+ * const void *class = ioOperations_Class(ops);
+ * result = (class == _etherConnection_Class(ops));
+ * }
+ * return result;
+ * }
+ * @endcode
+ */
+const void *ioOperations_Class(const IoOperations *ops);
+
+/**
+ * Returns the transport type of the connection (TCP, UDP, L2, etc.).
+ *
+ * TCP and AF_UNIX are both stream connections and will both return
+ * "Connection_TCP". Ethernet will return "Connection_L2".
+ *
+ * @param [in] ops The connection implementation.
+ *
+ * @return Connection_TCP A TCP4, TCP6, or AF_UNIX connection
+ * @return Connection_UDP A UDP4 or UDP6 connection
+ * @return Connection_L2 An Ethernet connection
+ *
+ * Example:
+ * @code
+ * {
+ * ConnectionType type =
+ * ioOperations_GetConnectionType(connection_GetIoOperations(connection));
+ * Connection *Conn =
+ * Connection_Create(connection_GetConnectionId(connection), localAddress,
+ * remoteAddress, type);
+ * }
+ * @endcode
+ */
+list_connections_type ioOperations_GetConnectionType(const IoOperations *ops);
+
+Ticks ioOperations_SendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message);
+#endif // io_h
diff --git a/hicn-light/src/io/listener.h b/hicn-light/src/io/listener.h
new file mode 100755
index 000000000..ffbb513fa
--- /dev/null
+++ b/hicn-light/src/io/listener.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.
+ */
+
+/**
+ * @file listener.h
+ * @brief Provides the function abstraction of all Listeners.
+ *
+ * A listener accepts in coming packets. A Stream listener will accept the
+ * connection then pass it off to the {@link StreamConnection} class. A
+ * datagram listener will have to have its own way to multiplex packets.
+ *
+ */
+
+#ifndef listener_h
+#define listener_h
+
+#include <src/utils/address.h>
+
+struct listener_ops;
+typedef struct listener_ops ListenerOps;
+
+typedef enum {
+ ENCAP_TCP, /**< TCP encapsulation type */
+ ENCAP_UDP, /**< UDP encapsulation type */
+ ENCAP_ETHER, /**< Ethernet encapsulation type */
+ ENCAP_LOCAL, /**< A connection to a local protocol stack */
+ ENCAP_HICN
+} EncapType;
+
+struct listener_ops {
+ /**
+ * A user-defined parameter
+ */
+ void *context;
+
+ /**
+ * Called to destroy the Listener.
+ *
+ * @param [in] listenerOpsPtr Double pointer to this structure
+ */
+ void (*destroy)(ListenerOps **listenerOpsPtr);
+
+ /**
+ * Returns the interface index of the listener.
+ *
+ * @param [in] ops Pointer to this structure
+ *
+ * @return the interface index of the listener
+ */
+ unsigned (*getInterfaceIndex)(const ListenerOps *ops);
+
+ /**
+ * Returns the address pair that defines the listener (local, remote)
+ *
+ * @param [in] ops Pointer to this structure
+ *
+ * @return the (local, remote) pair of addresses
+ */
+ const Address *(*getListenAddress)(const ListenerOps *ops);
+
+ /**
+ * Returns the encapsulation type of the listener (e.g. TCP, UDP, HICN)
+ *
+ * @param [in] ops Pointer to this structure
+ *
+ * @return the listener encapsulation type
+ */
+ EncapType (*getEncapType)(const ListenerOps *ops);
+
+ /**
+ * Returns the underlying socket associated with the listener
+ *
+ * Not all listeners are capable of returning a useful socket. In those
+ * cases, this function pointer is NULL.
+ *
+ * TCP does not support this operation (function is NULL). UDP returns its
+ * local socket.
+ *
+ * The caller should never close this socket, the listener will do that when
+ * its destroy method is called.
+ *
+ * @param [in] ops Pointer to this structure
+ *
+ * @retval integer The socket descriptor
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ int (*getSocket)(const ListenerOps *ops);
+};
+#endif // listener_h
diff --git a/hicn-light/src/io/listenerSet.c b/hicn-light/src/io/listenerSet.c
new file mode 100755
index 000000000..a890cd5b8
--- /dev/null
+++ b/hicn-light/src/io/listenerSet.c
@@ -0,0 +1,132 @@
+/*
+ * 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/io/listenerSet.h>
+
+struct listener_set {
+ PARCArrayList *listOfListeners;
+};
+
+static void listenerSet_DestroyListenerOps(void **opsPtr) {
+ ListenerOps *ops = *((ListenerOps **)opsPtr);
+ ops->destroy(&ops);
+}
+
+ListenerSet *listenerSet_Create() {
+ ListenerSet *set = parcMemory_AllocateAndClear(sizeof(ListenerSet));
+ parcAssertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerSet));
+ set->listOfListeners = parcArrayList_Create(listenerSet_DestroyListenerOps);
+
+ return set;
+}
+
+void listenerSet_Destroy(ListenerSet **setPtr) {
+ parcAssertNotNull(setPtr, "Parameter must be non-null double pointer");
+ parcAssertNotNull(*setPtr, "Parameter must dereference to non-null pointer");
+
+ ListenerSet *set = *setPtr;
+ parcArrayList_Destroy(&set->listOfListeners);
+ parcMemory_Deallocate((void **)&set);
+ *setPtr = NULL;
+}
+
+/**
+ * @function listenerSet_Add
+ * @abstract Adds the listener to the set
+ * @discussion
+ * Unique set based on pair (EncapType, localAddress)
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+bool listenerSet_Add(ListenerSet *set, ListenerOps *ops) {
+ parcAssertNotNull(set, "Parameter set must be non-null");
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+
+ int opsEncap = ops->getEncapType(ops);
+ const Address *opsAddress = ops->getListenAddress(ops);
+
+ // make sure its not in the set
+ size_t length = parcArrayList_Size(set->listOfListeners);
+ for (size_t i = 0; i < length; i++) {
+ ListenerOps *entry = parcArrayList_Get(set->listOfListeners, i);
+
+ int entryEncap = entry->getEncapType(entry);
+ const Address *entryAddress = entry->getListenAddress(entry);
+
+ if (opsEncap == entryEncap && addressEquals(opsAddress, entryAddress)) {
+ // duplicate
+ return false;
+ }
+ }
+
+ parcArrayList_Add(set->listOfListeners, ops);
+ return true;
+}
+
+size_t listenerSet_Length(const ListenerSet *set) {
+ parcAssertNotNull(set, "Parameter set must be non-null");
+ return parcArrayList_Size(set->listOfListeners);
+}
+
+/**
+ * Returns the listener at the given index
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] set An allocated listener set
+ * @param [in] index The index position (0 <= index < listenerSet_Count)
+ *
+ * @retval non-null The listener at index
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index) {
+ parcAssertNotNull(set, "Parameter set must be non-null");
+ return parcArrayList_Get(set->listOfListeners, index);
+}
+
+ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType,
+ const Address *localAddress) {
+ parcAssertNotNull(set, "Parameter set must be non-null");
+ parcAssertNotNull(localAddress, "Parameter localAddress must be non-null");
+
+ ListenerOps *match = NULL;
+
+ for (size_t i = 0; i < parcArrayList_Size(set->listOfListeners) && !match;
+ i++) {
+ ListenerOps *ops = parcArrayList_Get(set->listOfListeners, i);
+ parcAssertNotNull(ops, "Got null listener ops at index %zu", i);
+
+ if (ops->getEncapType(ops) == encapType) {
+ if (addressEquals(localAddress, ops->getListenAddress(ops))) {
+ match = ops;
+ }
+ }
+ }
+
+ return match;
+}
diff --git a/hicn-light/src/io/listenerSet.h b/hicn-light/src/io/listenerSet.h
new file mode 100755
index 000000000..671e68479
--- /dev/null
+++ b/hicn-light/src/io/listenerSet.h
@@ -0,0 +1,137 @@
+/*
+ * 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 listenerSet.h
+ * @brief A listener set is unique on (EncapType, localAddress)
+ *
+ * Keeps track of all the running listeners. The set is unique on the
+ * encapsulation type and the local address. For example, with TCP
+ * encapsulation and local address 127.0.0.1 or Ethernet encapsulation and MAC
+ * address 00:11:22:33:44:55.
+ *
+ * NOTE: This does not allow multiple EtherType on the same interface because
+ * the Address for a LINK address does not include an EtherType.
+ *
+ */
+
+#ifndef listenerSet_h
+#define listenerSet_h
+
+#include <src/io/listener.h>
+
+struct listener_set;
+typedef struct listener_set ListenerSet;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @retval <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ListenerSet *listenerSet_Create(void);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @retval <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void listenerSet_Destroy(ListenerSet **setPtr);
+
+/**
+ * @function listenerSet_Add
+ * @abstract Adds the listener to the set
+ * @discussion
+ * Unique set based on pair (EncapType, localAddress).
+ * Takes ownership of the ops memory if added.
+ *
+ * @param <#param1#>
+ * @return true if added, false if not
+ */
+bool listenerSet_Add(ListenerSet *set, ListenerOps *ops);
+
+/**
+ * The number of listeners in the set
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] set An allocated listener set
+ *
+ * @retval <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t listenerSet_Length(const ListenerSet *set);
+size_t listenerSet_Length(const ListenerSet *set);
+
+/**
+ * Returns the listener at the given index
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] set An allocated listener set
+ * @param [in] index The index position (0 <= index < listenerSet_Lenght)
+ *
+ * @retval non-null The listener at index
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ListenerOps *listenerSet_Get(const ListenerSet *set, size_t index);
+
+/**
+ * Looks up a listener by its key (EncapType, LocalAddress)
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] set An allocated listener set
+ * @param [in] encapType the listener type
+ * @param [in] localAddress The local bind address (e.g. MAC address or TCP
+ * socket)
+ *
+ * @retval non-null The listener matching the query
+ * @retval null Does not exist
+ *
+ * Example:
+ * @code
+ *
+ * @endcode
+ */
+ListenerOps *listenerSet_Find(const ListenerSet *set, EncapType encapType,
+ const Address *localAddress);
+#endif
diff --git a/hicn-light/src/io/streamConnection.c b/hicn-light/src/io/streamConnection.c
new file mode 100755
index 000000000..fedbb157a
--- /dev/null
+++ b/hicn-light/src/io/streamConnection.c
@@ -0,0 +1,714 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common activity for STREAM based listeners.
+ */
+
+#include <errno.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+#include <src/io/streamConnection.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/messageHandler.h>
+
+#include <src/utils/commands.h>
+
+#include <hicn/hicn.h>
+// 128 KB output queue
+#define OUTPUT_QUEUE_BYTES (128 * 1024)
+
+static void _conn_readcb(PARCEventQueue *bufferEventVector, PARCEventType type,
+ void *ioOpsVoid);
+
+static void _conn_eventcb(PARCEventQueue *bufferEventVector,
+ PARCEventQueueEventType events, void *ioOpsVoid);
+
+typedef struct stream_state {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ int fd;
+
+ AddressPair *addressPair;
+ PARCEventQueue *bufferEventVector;
+
+ bool isLocal;
+ bool isUp;
+ bool isClosed;
+ unsigned id;
+
+ size_t nextMessageLength;
+} _StreamState;
+
+// Prototypes
+static bool _streamConnection_Send(IoOperations *ops, const Address *nexthop,
+ Message *message);
+static const Address *_streamConnection_GetRemoteAddress(
+ const IoOperations *ops);
+static const AddressPair *_streamConnection_GetAddressPair(
+ const IoOperations *ops);
+static unsigned _streamConnection_GetConnectionId(const IoOperations *ops);
+static bool _streamConnection_IsUp(const IoOperations *ops);
+static bool _streamConnection_IsLocal(const IoOperations *ops);
+static void _streamConnection_DestroyOperations(IoOperations **opsPtr);
+
+static void _setConnectionState(_StreamState *stream, bool isUp);
+static list_connections_type _streamConnection_GetConnectionType(
+ const IoOperations *ops);
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message);
+
+// REMINDER: when a new_command is added, the following array has to be updated
+// with the sizeof(new_command). It allows to allocate the buffer for receiving
+// the payload of the CONTROLLER REQUEST after the header has beed read. Each
+// command identifier (typedef enum command_id) corresponds to a position in the
+// following array.
+static int payloadLengthDaemon[LAST_COMMAND_VALUE] = {
+ sizeof(add_listener_command),
+ sizeof(add_connection_command),
+ 0, // list connections: payload always 0 when get controller request
+ sizeof(add_route_command),
+ 0, // list routes: payload always 0 when get controller request
+ sizeof(remove_connection_command),
+ sizeof(remove_route_command),
+ sizeof(cache_store_command),
+ sizeof(cache_serve_command),
+ 0, // cache clear
+ sizeof(set_strategy_command),
+ sizeof(set_wldr_command),
+ sizeof(add_punting_command),
+ 0, // list listeners: payload always 0 when get controller request
+ sizeof(mapme_activator_command),
+ sizeof(mapme_activator_command),
+ sizeof(mapme_timing_command),
+ sizeof(mapme_timing_command)};
+
+/*
+ * This assigns a unique pointer to the void * which we use
+ * as a GUID for this class.
+ */
+static const void *_ioOperationsGuid = __FILE__;
+
+/*
+ * Return our GUID
+ */
+static const void *_streamConnection_Class(const IoOperations *ops) {
+ return _ioOperationsGuid;
+}
+
+static IoOperations _template = {
+ .closure = NULL,
+ .send = &_streamConnection_Send,
+ .getRemoteAddress = &_streamConnection_GetRemoteAddress,
+ .getAddressPair = &_streamConnection_GetAddressPair,
+ .getConnectionId = &_streamConnection_GetConnectionId,
+ .isUp = &_streamConnection_IsUp,
+ .isLocal = &_streamConnection_IsLocal,
+ .destroy = &_streamConnection_DestroyOperations,
+ .class = &_streamConnection_Class,
+ .getConnectionType = &_streamConnection_GetConnectionType,
+ .sendProbe = &_sendProbe,
+};
+
+IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd,
+ AddressPair *pair,
+ bool isLocal) {
+ _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState));
+ parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_StreamState));
+
+ Dispatcher *dispatcher = forwarder_GetDispatcher(forwarder);
+ PARCEventScheduler *eventBase = dispatcher_GetEventScheduler(dispatcher);
+ stream->bufferEventVector = parcEventQueue_Create(
+ eventBase, fd,
+ PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks);
+
+ stream->forwarder = forwarder;
+ stream->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+ stream->fd = fd;
+ stream->id = forwarder_GetNextConnectionId(forwarder);
+ stream->addressPair = pair;
+ stream->isClosed = false;
+
+ // allocate a connection
+ IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations));
+ parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(IoOperations));
+ memcpy(io_ops, &_template, sizeof(IoOperations));
+ io_ops->closure = stream;
+ stream->isLocal = isLocal;
+
+ parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL,
+ _conn_eventcb, (void *)io_ops);
+ parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read);
+
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionCreate, stream->id));
+
+ // As we are acceting a connection, we begin in the UP state
+ _setConnectionState(stream, true);
+
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ char *pair_str = addressPair_ToString(pair);
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "StreamConnection %p accept for address pair %s", (void *)stream,
+ pair_str);
+ free(pair_str);
+ }
+
+ return io_ops;
+}
+
+IoOperations *streamConnection_OpenConnection(Forwarder *forwarder,
+ AddressPair *pair, bool isLocal) {
+ parcAssertNotNull(forwarder, "Parameter hicn-light must be non-null");
+ parcAssertNotNull(pair, "Parameter pair must be non-null");
+
+ // if there's an error on the bind or connect, will return NULL
+ PARCEventQueue *bufferEventVector =
+ dispatcher_StreamBufferConnect(forwarder_GetDispatcher(forwarder), pair);
+ if (bufferEventVector == NULL) {
+ // error opening connection
+ return NULL;
+ }
+
+ _StreamState *stream = parcMemory_AllocateAndClear(sizeof(_StreamState));
+ parcAssertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_StreamState));
+
+ stream->forwarder = forwarder;
+ stream->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+ stream->fd = parcEventQueue_GetFileDescriptor(bufferEventVector);
+ stream->bufferEventVector = bufferEventVector;
+ stream->id = forwarder_GetNextConnectionId(forwarder);
+ stream->addressPair = pair;
+ stream->isClosed = false;
+
+ // allocate a connection
+ IoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations));
+ parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(IoOperations));
+ memcpy(io_ops, &_template, sizeof(IoOperations));
+ io_ops->closure = stream;
+ stream->isLocal = isLocal;
+
+ parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL,
+ _conn_eventcb, (void *)io_ops);
+ parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read);
+
+ // we start in DOWN state, until remote side answers
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionCreate, stream->id));
+ _setConnectionState(stream, false);
+
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) {
+ char *pair_str = addressPair_ToString(pair);
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__,
+ "StreamConnection %p connect for address pair %s",
+ (void *)stream, pair_str);
+ free(pair_str);
+ }
+
+ return io_ops;
+}
+
+static void _streamConnection_DestroyOperations(IoOperations **opsPtr) {
+ parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer");
+ parcAssertNotNull(*opsPtr,
+ "Parameter opsPtr must dereference to non-null pointer");
+
+ IoOperations *ops = *opsPtr;
+ parcAssertNotNull(ioOperations_GetClosure(ops),
+ "ops->context must not be null");
+
+ _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops);
+
+ parcEventQueue_Destroy(&stream->bufferEventVector);
+
+ addressPair_Release(&stream->addressPair);
+
+ if (!stream->isClosed) {
+ stream->isClosed = true;
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionClosed, stream->id));
+ }
+
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionDestroyed, stream->id));
+
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO, PARCLogLevel_Info)) {
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__,
+ "StreamConnection %p destroyed", (void *)stream);
+ }
+
+ logger_Release(&stream->logger);
+ parcMemory_Deallocate((void **)&stream);
+ parcMemory_Deallocate((void **)&ops);
+
+ *opsPtr = NULL;
+}
+
+static bool _streamConnection_IsUp(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _StreamState *stream =
+ (const _StreamState *)ioOperations_GetClosure(ops);
+ return stream->isUp;
+}
+
+static bool _streamConnection_IsLocal(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _StreamState *stream =
+ (const _StreamState *)ioOperations_GetClosure(ops);
+ return stream->isLocal;
+}
+
+static const Address *_streamConnection_GetRemoteAddress(
+ const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _StreamState *stream =
+ (const _StreamState *)ioOperations_GetClosure(ops);
+ return addressPair_GetRemote(stream->addressPair);
+}
+
+static const AddressPair *_streamConnection_GetAddressPair(
+ const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _StreamState *stream =
+ (const _StreamState *)ioOperations_GetClosure(ops);
+ return stream->addressPair;
+}
+
+static unsigned _streamConnection_GetConnectionId(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _StreamState *stream =
+ (const _StreamState *)ioOperations_GetClosure(ops);
+ return stream->id;
+}
+
+bool streamState_SendCommandResponse(IoOperations *ops,
+ struct iovec *response) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ parcAssertNotNull(response, "Parameter message must be non-null");
+ _StreamState *conn = (_StreamState *)ioOperations_GetClosure(ops);
+
+ bool success = false;
+ if (conn->isUp) {
+ PARCEventBuffer *buffer =
+ parcEventBuffer_GetQueueBufferOutput(conn->bufferEventVector);
+ size_t buffer_backlog = parcEventBuffer_GetLength(buffer);
+ parcEventBuffer_Destroy(&buffer);
+
+ if (buffer_backlog < OUTPUT_QUEUE_BYTES) {
+ if (logger_IsLoggable(conn->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(
+ conn->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "connid %u Writing %zu bytes to buffer with backlog %zu bytes",
+ conn->id,
+ (response[0].iov_len +
+ response[1].iov_len), // NEW: take total lenght
+ buffer_backlog);
+ }
+
+ // NEW: write directly ino the parcEventQueue without passing through
+ // message
+ int failure =
+ parcEventQueue_Write(conn->bufferEventVector, response[0].iov_base,
+ response[0].iov_len) +
+ parcEventQueue_Write(conn->bufferEventVector, response[1].iov_base,
+ response[1].iov_len);
+
+ if (failure == 0) {
+ success = true;
+ }
+ } else {
+ if (logger_IsLoggable(conn->logger, LoggerFacility_IO,
+ PARCLogLevel_Warning)) {
+ logger_Log(conn->logger, LoggerFacility_IO, PARCLogLevel_Warning,
+ __func__,
+ "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE",
+ conn->id, buffer_backlog);
+ }
+ }
+ } else {
+ if (logger_IsLoggable(conn->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ logger_Log(
+ conn->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "connid %u tried to send to down connection (isUp %d isClosed %d)",
+ conn->id, conn->isUp, conn->isClosed);
+ }
+ }
+
+ return success;
+}
+
+/**
+ * @function streamConnection_Send
+ * @abstract Non-destructive send of the message.
+ * @discussion
+ * Send uses message_CopyToStreamBuffer, which is a non-destructive write.
+ * The send may fail if there's no buffer space in the output queue.
+ *
+ * @param dummy is ignored. A stream has only one peer.
+ * @return <#return#>
+ */
+static bool _streamConnection_Send(IoOperations *ops, const Address *dummy,
+ Message *message) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ parcAssertNotNull(message, "Parameter message must be non-null");
+ _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops);
+
+ bool success = false;
+ if (stream->isUp) {
+ PARCEventBuffer *buffer =
+ parcEventBuffer_GetQueueBufferOutput(stream->bufferEventVector);
+ size_t buffer_backlog = parcEventBuffer_GetLength(buffer);
+ parcEventBuffer_Destroy(&buffer);
+
+ if (buffer_backlog < OUTPUT_QUEUE_BYTES) {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Debug)) {
+ logger_Log(
+ stream->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "connid %u Writing %zu bytes to buffer with backlog %zu bytes",
+ stream->id, message_Length(message), buffer_backlog);
+ }
+
+ int failure = message_Write(stream->bufferEventVector, message);
+ if (failure == 0) {
+ success = true;
+ }
+ } else {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Warning)) {
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Warning,
+ __func__,
+ "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE",
+ stream->id, buffer_backlog);
+ }
+ }
+ } else {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ logger_Log(
+ stream->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "connid %u tried to send to down connection (isUp %d isClosed %d)",
+ stream->id, stream->isUp, stream->isClosed);
+ }
+ }
+
+ return success;
+}
+
+list_connections_type _streamConnection_GetConnectionType(
+ const IoOperations *ops) {
+ return CONN_TCP;
+}
+
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message) {
+ // we don't need to implemet this here, it is a local connection
+ return 0;
+}
+
+// =================================================================
+// the actual I/O functions
+
+int _isACommand(PARCEventBuffer *input) {
+ size_t bytesAvailable = parcEventBuffer_GetLength(input);
+ parcAssertTrue(bytesAvailable >= sizeof(header_control_message),
+ "Called with too short an input: %zu", bytesAvailable);
+
+ uint8_t *msg = parcEventBuffer_Pullup(input, bytesAvailable);
+ // read first byte of the header
+
+ // first byte: must be a REQUEST_LIGHT
+ if (msg[0] != 100) {
+ return LAST_COMMAND_VALUE;
+ }
+
+ // second byte: must be a command_id
+ if (msg[1] < 0 || msg[1] >= LAST_COMMAND_VALUE) {
+ return LAST_COMMAND_VALUE;
+ }
+
+ return msg[1];
+}
+
+PARCEventBuffer *_tryReadControlMessage(_StreamState *stream,
+ PARCEventBuffer *input,
+ command_id command,
+ struct iovec **request) {
+ size_t bytesAvailable = parcEventBuffer_GetLength(input);
+
+ if (stream->nextMessageLength == 0) {
+ stream->nextMessageLength =
+ sizeof(header_control_message) +
+ payloadLengthDaemon[command]; // consider the whole packet.
+ }
+
+ if (bytesAvailable >= stream->nextMessageLength) {
+ PARCEventBuffer *message = parcEventBuffer_Create();
+ int bytesRead = parcEventBuffer_ReadIntoBuffer(input, message,
+ stream->nextMessageLength);
+ parcAssertTrue(bytesRead == stream->nextMessageLength,
+ "Partial read, expected %zu got %d",
+ stream->nextMessageLength, bytesRead);
+
+ uint8_t *control =
+ parcEventBuffer_Pullup(message, stream->nextMessageLength);
+ if (!(*request = (struct iovec *)parcMemory_AllocateAndClear(
+ sizeof(struct iovec) * 2))) {
+ return NULL;
+ }
+ (*request)[0].iov_base = control; // header
+ (*request)[0].iov_len = sizeof(header_control_message);
+ if (payloadLengthDaemon[command] > 0) {
+ (*request)[1].iov_base =
+ control + sizeof(header_control_message); // payload
+ } else {
+ (*request)[1].iov_base = NULL;
+ }
+ (*request)[1].iov_len = payloadLengthDaemon[command];
+ // now reset message length for next packet
+
+ stream->nextMessageLength = 0;
+
+ return message;
+ }
+
+ return NULL;
+}
+
+static bool _isAnHIcnPacket(PARCEventBuffer *input) {
+ size_t bytesAvailable = parcEventBuffer_GetLength(input);
+ parcAssertTrue(bytesAvailable >= sizeof(header_control_message),
+ "Called with too short an input: %zu", bytesAvailable);
+
+ uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message));
+ return messageHandler_IsValidHIcnPacket(fh);
+}
+
+static Message *_readMessage(_StreamState *stream, Ticks time,
+ PARCEventBuffer *input) {
+ Message *message = message_CreateFromEventBuffer(
+ input, stream->nextMessageLength, stream->id, time, stream->logger);
+
+ return message;
+}
+
+static void _startNewMessage(_StreamState *stream, PARCEventBuffer *input,
+ size_t inputBytesAvailable) {
+ parcAssertTrue(stream->nextMessageLength == 0,
+ "Invalid state, nextMessageLength not zero: %zu",
+ stream->nextMessageLength);
+ parcAssertTrue(inputBytesAvailable >= sizeof(header_control_message),
+ "read_length not a whole fixed header!: %zd",
+ inputBytesAvailable);
+
+ // this linearizes the first messageHandler_GetIPv6HeaderLength() bytes of the
+ // input buffer's iovecs and returns a pointer to it.
+ uint8_t *fh = parcEventBuffer_Pullup(input, sizeof(header_control_message));
+
+ // Calculate the total message size based on the fixed header
+ stream->nextMessageLength = messageHandler_GetTotalPacketLength(fh);
+}
+
+static Message *_tryReadMessage(PARCEventBuffer *input, _StreamState *stream) {
+ size_t bytesAvailable = parcEventBuffer_GetLength(input);
+ parcAssertTrue(bytesAvailable >= sizeof(header_control_message),
+ "Called with too short an input: %zu", bytesAvailable);
+
+ if (stream->nextMessageLength == 0) {
+ _startNewMessage(stream, input, bytesAvailable);
+ }
+
+ // This is not an ELSE statement. We can both start a new message then
+ // check if there's enough bytes to read the whole thing.
+
+ if (bytesAvailable >= stream->nextMessageLength) {
+ Message *message =
+ _readMessage(stream, forwarder_GetTicks(stream->forwarder), input);
+
+ // now reset message length for next packet
+ stream->nextMessageLength = 0;
+
+ return message;
+ }
+
+ return NULL;
+}
+
+/**
+ * @function conn_readcb
+ * @abstract Event callback for reads
+ * @discussion
+ * Will read messages off the input. Continues reading as long as we
+ * can get a header to determine the next message length or as long as we
+ * can read a complete message.
+ *
+ * This function manipulates the read low water mark. (1) read a fixed header
+ * plus complete message, then set the low water mark to FIXED_HEADER_LEN. (2)
+ * read a fixed header, but not a complete message, then set low water mark to
+ * the total mesage length. Using the low water mark like this means the buffer
+ * event will only trigger on meaningful byte boundaries when we can get actual
+ * work done.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+static void _conn_readcb(PARCEventQueue *event, PARCEventType type,
+ void *ioOpsVoid) {
+ command_id command;
+ IoOperations *ops = (IoOperations *)ioOpsVoid;
+ _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops);
+
+ PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(event);
+
+ // drain the input buffer
+
+ // notice that we always try to read at least 8 bytes
+ // (sizeof(header_control_message)). This is enough to read the length of all
+ // kind of packets
+ while (parcEventBuffer_GetLength(input) >= sizeof(header_control_message) &&
+ parcEventBuffer_GetLength(input) >= stream->nextMessageLength) {
+ if ((command = _isACommand(input)) != LAST_COMMAND_VALUE) {
+ struct iovec *rx;
+ // Get message from the stream and set the stream->nextMessageLength
+ PARCEventBuffer *message =
+ _tryReadControlMessage(stream, input, command, &rx);
+ // If received correctly the whole message, send to dispatcher
+ if (message) {
+ forwarder_ReceiveCommand(stream->forwarder, command, rx, stream->id);
+ parcEventBuffer_Destroy(&message);
+ }
+
+ } else if (_isAnHIcnPacket(input)) {
+ // this is an HIcn packet (here we should distinguish between IPv4 and
+ // IPv6 tryReadMessage may set nextMessageLength
+ Message *message = _tryReadMessage(input, stream);
+
+ if (message) {
+ forwarder_Receive(stream->forwarder, message);
+ }
+
+ } else {
+ parcAssertTrue(false,
+ "(Local stream connection) malformend packet received");
+ }
+ }
+
+ if (stream->nextMessageLength == 0) {
+ // we don't have the next header, so set it to the header length
+ streamBuffer_SetWatermark(event, true, false,
+ sizeof(header_control_message), 0);
+ } else {
+ // set it to the packet length
+ streamBuffer_SetWatermark(event, true, false, stream->nextMessageLength, 0);
+ }
+ parcEventBuffer_Destroy(&input);
+}
+
+static void _setConnectionState(_StreamState *stream, bool isUp) {
+ parcAssertNotNull(stream, "Parameter stream must be non-null");
+
+ Messenger *messenger = forwarder_GetMessenger(stream->forwarder);
+
+ bool oldStateIsUp = stream->isUp;
+ stream->isUp = isUp;
+
+ if (oldStateIsUp && !isUp) {
+ // bring connection DOWN
+ Missive *missive = missive_Create(MissiveType_ConnectionDown, stream->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+
+ if (!oldStateIsUp && isUp) {
+ // bring connection UP
+ Missive *missive = missive_Create(MissiveType_ConnectionUp, stream->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+}
+
+static void _conn_eventcb(PARCEventQueue *event, PARCEventQueueEventType events,
+ void *ioOpsVoid) {
+ IoOperations *ops = (IoOperations *)ioOpsVoid;
+ _StreamState *stream = (_StreamState *)ioOperations_GetClosure(ops);
+
+ if (events & PARCEventQueueEventType_Connected) {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__,
+ "Connection %u is connected", stream->id);
+ }
+
+ // if the stream was closed, do not transition to an UP state
+ if (!stream->isClosed) {
+ _setConnectionState(stream, true);
+ }
+ } else if (events & PARCEventQueueEventType_EOF) {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Info, __func__,
+ "connid %u closed.", stream->id);
+ }
+
+ parcEventQueue_Disable(stream->bufferEventVector, PARCEventType_Read);
+
+ _setConnectionState(stream, false);
+
+ if (!stream->isClosed) {
+ stream->isClosed = true;
+ // this will cause the connection manager to destroy the connection later
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionClosed, stream->id));
+ }
+ } else if (events & PARCEventQueueEventType_Error) {
+ if (logger_IsLoggable(stream->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ logger_Log(stream->logger, LoggerFacility_IO, PARCLogLevel_Error,
+ __func__, "Got an error on the connection %u: %s", stream->id,
+ strerror(errno));
+ }
+
+ parcEventQueue_Disable(stream->bufferEventVector,
+ PARCEventType_Read | PARCEventType_Write);
+
+ _setConnectionState(stream, false);
+
+ if (!stream->isClosed) {
+ stream->isClosed = true;
+ // this will cause the connection manager to destroy the connection later
+ messenger_Send(forwarder_GetMessenger(stream->forwarder),
+ missive_Create(MissiveType_ConnectionClosed, stream->id));
+ }
+ }
+ /* None of the other events can happen here, since we haven't enabled
+ * timeouts */
+}
diff --git a/hicn-light/src/io/streamConnection.h b/hicn-light/src/io/streamConnection.h
new file mode 100755
index 000000000..8eb63a094
--- /dev/null
+++ b/hicn-light/src/io/streamConnection.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+
+/**
+ * Methods common to TCP and PF_LOCAL stream-based listeners
+ */
+
+#ifndef streamConnection_h
+#define streamConnection_h
+
+#include <src/core/forwarder.h>
+#include <src/core/messagePacketType.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * @function streamConnection_AcceptConnection
+ * @abstract Receive a connection from a remote peer
+ * @discussion
+ * We are the "server side" of the stream connection, so we need to accept the
+ * client connection and setup state for her.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+IoOperations *streamConnection_AcceptConnection(Forwarder *forwarder, int fd,
+ AddressPair *pair,
+ bool isLocal);
+
+/**
+ * @function streamConnection_OpenConnection
+ * @abstract Initiate a connection to a remote peer
+ * @discussion
+ * We are the "client side" of the stream connection. We'll create state for
+ * the peer, but it will be in the "down" state until the connection
+ * establishes.
+ *
+ * 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().
+ *
+ * AF_UNIX is not yet supported
+ *
+ * If there's an error binding to the specified address or connecting to the
+ * remote address, will return NULL.
+ *
+ * @param pair (takes ownership of this) is the complete socket pair of
+ * (address, port) for each end, if INET or INET6.
+ * @return NULL on error, otherwise the connections IO operations.
+ */
+IoOperations *streamConnection_OpenConnection(Forwarder *forwarder,
+ AddressPair *pair, bool isLocal);
+
+bool streamState_SendCommandResponse(IoOperations *ops, struct iovec *response);
+
+#endif // streamConnection_h
diff --git a/hicn-light/src/io/tcpListener.c b/hicn-light/src/io/tcpListener.c
new file mode 100755
index 000000000..6f0477f5b
--- /dev/null
+++ b/hicn-light/src/io/tcpListener.c
@@ -0,0 +1,233 @@
+/*
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <src/core/connectionTable.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <src/io/streamConnection.h>
+#include <src/io/tcpListener.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Network.h>
+#include <parc/assert/parc_Assert.h>
+
+typedef struct tcp_listener {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ PARCEventSocket *listener;
+
+ Address *localAddress;
+
+ unsigned id;
+
+ // is the localAddress as 127.0.0.0 address?
+ bool isLocalAddressLocal;
+} _TcpListener;
+
+static void _tcpListener_Destroy(_TcpListener **listenerPtr);
+
+static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr);
+static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops);
+static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops);
+static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops);
+
+static ListenerOps _tcpTemplate = {
+ .context = NULL,
+ .destroy = &_tcpListener_OpsDestroy,
+ .getInterfaceIndex = &_tcpListener_OpsGetInterfaceIndex,
+ .getListenAddress = &_tcpListener_OpsGetListenAddress,
+ .getEncapType = &_tcpListener_OpsGetEncapType,
+ .getSocket = NULL};
+
+// STREAM daemon listener callback
+static void _tcpListener_Listen(int, struct sockaddr *, int socklen,
+ void *tcpVoid);
+
+ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder,
+ struct sockaddr_in6 sin6) {
+ _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener));
+ parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_TcpListener));
+
+ tcp->forwarder = forwarder;
+ tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+
+ tcp->listener = dispatcher_CreateListener(
+ forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1,
+ (struct sockaddr *)&sin6, sizeof(sin6));
+
+ if (tcp->listener == NULL) {
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "dispatcher_CreateListener failed to create listener (%d) %s",
+ errno, strerror(errno));
+ logger_Release(&tcp->logger);
+ parcMemory_Deallocate((void **)&tcp);
+ return NULL;
+ }
+
+ tcp->localAddress = addressCreateFromInet6(&sin6);
+ tcp->id = forwarder_GetNextConnectionId(forwarder);
+ tcp->isLocalAddressLocal =
+ parcNetwork_IsSocketLocal((struct sockaddr *)&sin6);
+
+ ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+
+ memcpy(ops, &_tcpTemplate, sizeof(ListenerOps));
+ ops->context = tcp;
+
+ if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ char *str = addressToString(tcp->localAddress);
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "TcpListener %p created for address %s (isLocal %d)",
+ (void *)tcp, str, tcp->isLocalAddressLocal);
+ parcMemory_Deallocate((void **)&str);
+ }
+
+ return ops;
+}
+
+ListenerOps *tcpListener_CreateInet(Forwarder *forwarder,
+ struct sockaddr_in sin) {
+ _TcpListener *tcp = parcMemory_AllocateAndClear(sizeof(_TcpListener));
+ parcAssertNotNull(tcp, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_TcpListener));
+
+ tcp->forwarder = forwarder;
+ tcp->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+ tcp->listener = dispatcher_CreateListener(
+ forwarder_GetDispatcher(forwarder), _tcpListener_Listen, (void *)tcp, -1,
+ (struct sockaddr *)&sin, sizeof(sin));
+
+ if (tcp->listener == NULL) {
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "dispatcher_CreateListener failed to create listener (%d) %s",
+ errno, strerror(errno));
+
+ logger_Release(&tcp->logger);
+ parcMemory_Deallocate((void **)&tcp);
+ return NULL;
+ }
+
+ tcp->localAddress = addressCreateFromInet(&sin);
+ tcp->id = forwarder_GetNextConnectionId(forwarder);
+ tcp->isLocalAddressLocal = parcNetwork_IsSocketLocal((struct sockaddr *)&sin);
+
+ ListenerOps *ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+
+ memcpy(ops, &_tcpTemplate, sizeof(ListenerOps));
+ ops->context = tcp;
+
+ if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ char *str = addressToString(tcp->localAddress);
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "TcpListener %p created for address %s (isLocal %d)",
+ (void *)tcp, str, tcp->isLocalAddressLocal);
+ parcMemory_Deallocate((void **)&str);
+ }
+
+ return ops;
+}
+
+static void _tcpListener_Destroy(_TcpListener **listenerPtr) {
+ parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer");
+ parcAssertNotNull(*listenerPtr,
+ "Parameter must derefernce to non-null pointer");
+ _TcpListener *tcp = *listenerPtr;
+
+ if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ char *str = addressToString(tcp->localAddress);
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "TcpListener %p destroyed", (void *)tcp);
+ parcMemory_Deallocate((void **)&str);
+ }
+
+ logger_Release(&tcp->logger);
+ dispatcher_DestroyListener(forwarder_GetDispatcher(tcp->forwarder),
+ &tcp->listener);
+ addressDestroy(&tcp->localAddress);
+ parcMemory_Deallocate((void **)&tcp);
+ *listenerPtr = NULL;
+}
+
+// ==================================================
+
+static void _tcpListener_Listen(int fd, struct sockaddr *sa, int socklen,
+ void *tcpVoid) {
+ _TcpListener *tcp = (_TcpListener *)tcpVoid;
+
+ Address *remote;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ remote = addressCreateFromInet((struct sockaddr_in *)sa);
+ break;
+
+ case AF_INET6:
+ remote = addressCreateFromInet6((struct sockaddr_in6 *)sa);
+ break;
+
+ default:
+ parcTrapIllegalValue(sa, "Expected INET or INET6, got %d", sa->sa_family);
+ abort();
+ }
+
+ AddressPair *pair = addressPair_Create(tcp->localAddress, remote);
+
+ IoOperations *ops = streamConnection_AcceptConnection(
+ tcp->forwarder, fd, pair, tcp->isLocalAddressLocal);
+ Connection *conn = connection_Create(ops);
+
+ connectionTable_Add(forwarder_GetConnectionTable(tcp->forwarder), conn);
+
+ if (logger_IsLoggable(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(tcp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "TcpListener %p listen started", (void *)tcp);
+ }
+
+ addressDestroy(&remote);
+}
+
+static void _tcpListener_OpsDestroy(ListenerOps **listenerOpsPtr) {
+ ListenerOps *ops = *listenerOpsPtr;
+ _TcpListener *tcp = (_TcpListener *)ops->context;
+ _tcpListener_Destroy(&tcp);
+ parcMemory_Deallocate((void **)&ops);
+ *listenerOpsPtr = NULL;
+}
+
+static unsigned _tcpListener_OpsGetInterfaceIndex(const ListenerOps *ops) {
+ _TcpListener *tcp = (_TcpListener *)ops->context;
+ return tcp->id;
+}
+
+static const Address *_tcpListener_OpsGetListenAddress(const ListenerOps *ops) {
+ _TcpListener *tcp = (_TcpListener *)ops->context;
+ return tcp->localAddress;
+}
+
+static EncapType _tcpListener_OpsGetEncapType(const ListenerOps *ops) {
+ return ENCAP_TCP;
+}
diff --git a/hicn-light/src/io/tcpListener.h b/hicn-light/src/io/tcpListener.h
new file mode 100755
index 000000000..c5d1e33af
--- /dev/null
+++ b/hicn-light/src/io/tcpListener.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 tcpListener.h
+ * @brief Listens for in coming TCP connections
+ *
+ * This is the "server socket" of hicn-light for TCP connections. The actual
+ * I/O is handled by {@link StreamConnection}.
+ *
+ */
+
+#ifndef tcpListener_h
+#define tcpListener_h
+
+#include <netinet/in.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+ListenerOps *tcpListener_CreateInet6(Forwarder *forwarder,
+ struct sockaddr_in6 sin6);
+ListenerOps *tcpListener_CreateInet(Forwarder *forwarder,
+ struct sockaddr_in sin);
+#endif // tcpListener_h
diff --git a/hicn-light/src/io/tcpTunnel.c b/hicn-light/src/io/tcpTunnel.c
new file mode 100755
index 000000000..a2bf2bd30
--- /dev/null
+++ b/hicn-light/src/io/tcpTunnel.c
@@ -0,0 +1,43 @@
+/*
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/streamConnection.h>
+#include <src/io/tcpListener.h>
+#include <src/io/tcpTunnel.h>
+
+IoOperations *tcpTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress) {
+ IoOperations *ops = NULL;
+
+ address_type localType = addressGetType(localAddress);
+ address_type remoteType = addressGetType(remoteAddress);
+
+ if (localType == remoteType) {
+ AddressPair *pair = addressPair_Create(localAddress, remoteAddress);
+ bool isLocal = false;
+
+ ops = streamConnection_OpenConnection(forwarder, pair, isLocal);
+ }
+
+ return ops;
+}
diff --git a/hicn-light/src/io/tcpTunnel.h b/hicn-light/src/io/tcpTunnel.h
new file mode 100755
index 000000000..4daa7d032
--- /dev/null
+++ b/hicn-light/src/io/tcpTunnel.h
@@ -0,0 +1,42 @@
+/*
+ * 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 tcpTunnel.h
+ * @brief Establish a tunnel to a remote system
+ *
+ */
+
+#ifndef tcpTunnel_h
+#define tcpTunnel_h
+
+#include <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ */
+// IoOperations *tcpTunnel_CreateOnListener(Forwarder *forwarder,
+// ListenerOps *localListener,
+// const Address *remoteAddress);
+
+/**
+ */
+IoOperations *tcpTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress);
+
+#endif // tcpTunnel_h
diff --git a/hicn-light/src/io/udpConnection.c b/hicn-light/src/io/udpConnection.c
new file mode 100755
index 000000000..2aa6edc51
--- /dev/null
+++ b/hicn-light/src/io/udpConnection.c
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+/**
+ * Embodies the reader/writer for a UDP connection
+ *
+ * NB The Send() function may overflow the output buffer
+ *
+ */
+
+#include <errno.h>
+#include <src/config.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <src/core/messageHandler.h>
+#include <src/io/udpConnection.h>
+
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/message.h>
+
+typedef struct udp_state {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ // the udp listener socket we receive packets on
+ int udpListenerSocket;
+
+ AddressPair *addressPair;
+
+ // We need to access this all the time, so grab it out
+ // of the addressPair;
+ struct sockaddr *peerAddress;
+ socklen_t peerAddressLength;
+
+ bool isLocal;
+ bool isUp;
+ unsigned id;
+
+ unsigned delay;
+} _UdpState;
+
+// Prototypes
+static bool _send(IoOperations *ops, const Address *nexthop, Message *message);
+static const Address *_getRemoteAddress(const IoOperations *ops);
+static const AddressPair *_getAddressPair(const IoOperations *ops);
+static unsigned _getConnectionId(const IoOperations *ops);
+static bool _isUp(const IoOperations *ops);
+static bool _isLocal(const IoOperations *ops);
+static void _destroy(IoOperations **opsPtr);
+static list_connections_type _getConnectionType(const IoOperations *ops);
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message);
+/*
+ * This assigns a unique pointer to the void * which we use
+ * as a GUID for this class.
+ */
+static const void *_IoOperationsGuid = __FILE__;
+
+/*
+ * Return our GUID
+ */
+static const void *_streamConnection_Class(const IoOperations *ops) {
+ return _IoOperationsGuid;
+}
+
+static IoOperations _template = {.closure = NULL,
+ .send = &_send,
+ .getRemoteAddress = &_getRemoteAddress,
+ .getAddressPair = &_getAddressPair,
+ .getConnectionId = &_getConnectionId,
+ .isUp = &_isUp,
+ .isLocal = &_isLocal,
+ .destroy = &_destroy,
+ .class = &_streamConnection_Class,
+ .getConnectionType = &_getConnectionType,
+ .sendProbe = &_sendProbe};
+
+// =================================================================
+
+static void _setConnectionState(_UdpState *Udp, bool isUp);
+static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair);
+
+IoOperations *udpConnection_Create(Forwarder *forwarder, int fd,
+ const AddressPair *pair, bool isLocal) {
+ IoOperations *io_ops = NULL;
+
+ _UdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_UdpState));
+ parcAssertNotNull(udpConnState,
+ "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(_UdpState));
+
+ udpConnState->forwarder = forwarder;
+ udpConnState->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+
+ bool saved = _saveSockaddr(udpConnState, pair);
+ if (saved) {
+ udpConnState->udpListenerSocket = fd;
+ udpConnState->id = forwarder_GetNextConnectionId(forwarder);
+ udpConnState->addressPair = addressPair_Acquire(pair);
+ udpConnState->isLocal = isLocal;
+
+ // allocate a connection
+ io_ops = parcMemory_AllocateAndClear(sizeof(IoOperations));
+ parcAssertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(IoOperations));
+ memcpy(io_ops, &_template, sizeof(IoOperations));
+ io_ops->closure = udpConnState;
+
+ _setConnectionState(udpConnState, true);
+
+ if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ char *str = addressPair_ToString(udpConnState->addressPair);
+ logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info,
+ __func__,
+ "UdpConnection %p created for address %s (isLocal %d)",
+ (void *)udpConnState, str, udpConnState->isLocal);
+ free(str);
+ }
+
+ messenger_Send(
+ forwarder_GetMessenger(forwarder),
+ missive_Create(MissiveType_ConnectionCreate, udpConnState->id));
+ messenger_Send(forwarder_GetMessenger(forwarder),
+ missive_Create(MissiveType_ConnectionUp, udpConnState->id));
+ } else {
+ // _saveSockaddr will already log an error, no need for extra log message
+ // here
+ logger_Release(&udpConnState->logger);
+ parcMemory_Deallocate((void **)&udpConnState);
+ }
+
+ return io_ops;
+}
+
+// =================================================================
+// I/O Operations implementation
+
+static void _destroy(IoOperations **opsPtr) {
+ parcAssertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer");
+ parcAssertNotNull(*opsPtr,
+ "Parameter opsPtr must dereference to non-null pointer");
+
+ IoOperations *ops = *opsPtr;
+ parcAssertNotNull(ioOperations_GetClosure(ops),
+ "ops->context must not be null");
+
+ _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops);
+ addressPair_Release(&udpConnState->addressPair);
+ parcMemory_Deallocate((void **)&(udpConnState->peerAddress));
+
+ messenger_Send(
+ forwarder_GetMessenger(udpConnState->forwarder),
+ missive_Create(MissiveType_ConnectionDestroyed, udpConnState->id));
+
+ if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Info)) {
+ logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Info,
+ __func__, "UdpConnection %p destroyed", (void *)udpConnState);
+ }
+
+ // do not close udp->udpListenerSocket, the listener will close
+ // that when its done
+
+ logger_Release(&udpConnState->logger);
+ parcMemory_Deallocate((void **)&udpConnState);
+ parcMemory_Deallocate((void **)&ops);
+
+ *opsPtr = NULL;
+}
+
+static bool _isUp(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _UdpState *udpConnState =
+ (const _UdpState *)ioOperations_GetClosure(ops);
+ return udpConnState->isUp;
+}
+
+static bool _isLocal(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _UdpState *udpConnState =
+ (const _UdpState *)ioOperations_GetClosure(ops);
+ return udpConnState->isLocal;
+}
+
+static const Address *_getRemoteAddress(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _UdpState *udpConnState =
+ (const _UdpState *)ioOperations_GetClosure(ops);
+ return addressPair_GetRemote(udpConnState->addressPair);
+}
+
+static const AddressPair *_getAddressPair(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _UdpState *udpConnState =
+ (const _UdpState *)ioOperations_GetClosure(ops);
+ return udpConnState->addressPair;
+}
+
+static unsigned _getConnectionId(const IoOperations *ops) {
+ parcAssertNotNull(ops, "Parameter must be non-null");
+ const _UdpState *udpConnState =
+ (const _UdpState *)ioOperations_GetClosure(ops);
+ return udpConnState->id;
+}
+
+/**
+ * @function metisUdpConnection_Send
+ * @abstract Non-destructive send of the message.
+ * @discussion
+ * sends a message to the peer.
+ *
+ * @param dummy is ignored. A udp connection has only one peer.
+ * @return <#return#>
+ */
+static bool _send(IoOperations *ops, const Address *dummy, Message *message) {
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ parcAssertNotNull(message, "Parameter message must be non-null");
+ _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops);
+
+ // NAT for HICN
+ // in this particular connection we don't need natting beacause we send the
+ // packet to the next hop using upd connection
+
+#if 0
+ if((hicnConnState->peerAddressLength == sizeof(struct sockaddr_in)) || (hicnConnState->localAddressLength == sizeof(struct sockaddr_in)))
+ return false;
+
+ if(message_GetType(message) = MessagePacketType_ContentObject){
+ //this is a data packet. We need to put the remote address in the destination field
+ messageHandler_SetDestination_IPv6((uint8_t *) message_FixedHeader(message),
+ &((struct sockaddr_in6 *) hicnConnState->peerAddress)->sin6_addr);
+
+ } else if (message_GetType(message) == MessagePacketType_Interest) {
+ //this si an interest packet. We need to put the local address in the source field
+ messageHandler_SetSource_IPv6((uint8_t *) message_FixedHeader(message),
+ &((struct sockaddr_in6 *) hicnConnState->localAddress)->sin6_addr);
+
+ //only in this case we may need to set the probeDestAddress
+ if(hicnConnState->refreshProbeDestAddress){
+ _refreshProbeDestAddress(hicnConnState, message_FixedHeader(message));
+ }
+
+ } else if (message_GetType(message) == MessagePacketType_WldrNotification) {
+ //here we don't need to do anything for now
+ }else{
+ //unkown packet
+ if (logger_IsLoggable(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(hicnConnState->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "connid %u can't parse the message",
+ hicnConnState->id);
+ }
+ return false;
+ }
+#endif
+
+ ssize_t writeLength =
+ sendto(udpConnState->udpListenerSocket, message_FixedHeader(message),
+ message_Length(message), 0, udpConnState->peerAddress,
+ udpConnState->peerAddressLength);
+
+ if (writeLength < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return false;
+ } else {
+ // this print is for debugging
+ printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength,
+ message_Length(message), errno, strerror(errno));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static list_connections_type _getConnectionType(const IoOperations *ops) {
+ return CONN_UDP;
+}
+
+static Ticks _sendProbe(IoOperations *ops, unsigned probeType,
+ uint8_t *message) {
+#if 0
+ parcAssertNotNull(ops, "Parameter ops must be non-null");
+ _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops);
+
+
+ uint8_t *pkt;
+ size_t pkt_size = 8;
+ pkt = (uint8_t *) malloc(sizeof(uint8_t) * pkt_size);
+ for (unsigned i = 0; i < pkt_size; i++) {
+ pkt[i] = 0;
+ }
+ pkt[0] = 1; //type
+ pkt[1] = probeType; //packet type
+ pkt[6] = 8; //header len (16bit, network order)
+
+ ssize_t writeLen = sendto(udpConnState->udpListenerSocket, pkt, pkt_size, 0, udpConnState->peerAddress, udpConnState->peerAddressLength);
+
+ if (writeLen < 0) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ free(pkt);
+ return 0;
+ } else {
+ //this print is for debugging
+ printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLen, pkt_size, errno, strerror(errno));
+ free(pkt);
+ return 0;
+ }
+ }
+
+ free(pkt);
+ return metisForwarder_GetTicks(udpConnState->metis);
+#endif
+ return 0;
+}
+
+// =================================================================
+// Internal API
+
+static bool _saveSockaddr(_UdpState *udpConnState, const AddressPair *pair) {
+ bool success = false;
+ const Address *remoteAddress = addressPair_GetRemote(pair);
+
+ switch (addressGetType(remoteAddress)) {
+ case ADDR_INET: {
+ size_t bytes = sizeof(struct sockaddr_in);
+ udpConnState->peerAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(udpConnState->peerAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet(remoteAddress,
+ (struct sockaddr_in *)udpConnState->peerAddress);
+ udpConnState->peerAddressLength = (socklen_t)bytes;
+
+ success = true;
+ break;
+ }
+
+ case ADDR_INET6: {
+ size_t bytes = sizeof(struct sockaddr_in6);
+ udpConnState->peerAddress = parcMemory_Allocate(bytes);
+ parcAssertNotNull(udpConnState->peerAddress,
+ "parcMemory_Allocate(%zu) returned NULL", bytes);
+
+ addressGetInet6(remoteAddress,
+ (struct sockaddr_in6 *)udpConnState->peerAddress);
+ udpConnState->peerAddressLength = (socklen_t)bytes;
+
+ success = true;
+ break;
+ }
+
+ default:
+ if (logger_IsLoggable(udpConnState->logger, LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ char *str = addressToString(remoteAddress);
+ logger_Log(udpConnState->logger, LoggerFacility_IO, PARCLogLevel_Error,
+ __func__, "Remote address is not INET or INET6: %s", str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ break;
+ }
+ return success;
+}
+
+static void _setConnectionState(_UdpState *udpConnState, bool isUp) {
+ parcAssertNotNull(udpConnState, "Parameter Udp must be non-null");
+
+ Messenger *messenger = forwarder_GetMessenger(udpConnState->forwarder);
+
+ bool oldStateIsUp = udpConnState->isUp;
+ udpConnState->isUp = isUp;
+
+ if (oldStateIsUp && !isUp) {
+ // bring connection DOWN
+ Missive *missive =
+ missive_Create(MissiveType_ConnectionDown, udpConnState->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+
+ if (!oldStateIsUp && isUp) {
+ // bring connection UP
+ Missive *missive =
+ missive_Create(MissiveType_ConnectionUp, udpConnState->id);
+ messenger_Send(messenger, missive);
+ return;
+ }
+}
diff --git a/hicn-light/src/io/udpConnection.h b/hicn-light/src/io/udpConnection.h
new file mode 100755
index 000000000..122f332d5
--- /dev/null
+++ b/hicn-light/src/io/udpConnection.h
@@ -0,0 +1,53 @@
+/*
+ * 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 udpConnection.h
+ * @brief Represents a UDP connection (socket) for the connection table
+ *
+ * <#Detailed Description#>
+ *
+ */
+
+#ifndef udpConnection_h
+#define udpConnection_h
+
+#include <src/core/forwarder.h>
+#include <src/io/addressPair.h>
+#include <src/io/ioOperations.h>
+#include <src/utils/address.h>
+
+/**
+ * Creates a UDP connection that can send to the remote address
+ *
+ * The address pair must both be same type (i.e. INET or INET6).
+ *
+ * @param [in] metis An allocated MetisForwarder (saves reference)
+ * @param [in] fd The socket to use
+ * @param [in] pair An allocated address pair for the connection (saves
+ * reference)
+ * @param [in] isLocal determines if the remote address is on the current system
+ *
+ * @retval non-null An allocated Io operations
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+IoOperations *udpConnection_Create(Forwarder *forwarder, int fd,
+ const AddressPair *pair, bool isLocal);
+#endif // udpConnection_h
diff --git a/hicn-light/src/io/udpListener.c b/hicn-light/src/io/udpListener.c
new file mode 100755
index 000000000..31c0e673b
--- /dev/null
+++ b/hicn-light/src/io/udpListener.c
@@ -0,0 +1,533 @@
+/*
+ * 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 <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <src/config.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <src/core/messageHandler.h>
+
+#include <src/io/udpConnection.h>
+#include <src/io/udpListener.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/assert/parc_Assert.h>
+#include <src/core/connection.h>
+#include <src/core/forwarder.h>
+#include <src/core/messagePacketType.h>
+
+#ifdef WITH_MAPME
+#include <src/core/mapMe.h>
+#endif /* WITH_MAPME */
+
+#define IPv4 4
+#define IPv6 6
+
+struct udp_listener {
+ Forwarder *forwarder;
+ Logger *logger;
+
+ PARCEvent *udp_event;
+ SocketType udp_socket;
+ uint16_t port;
+
+ unsigned id;
+ Address *localAddress;
+};
+
+static void _destroy(ListenerOps **listenerOpsPtr);
+static unsigned _getInterfaceIndex(const ListenerOps *ops);
+static const Address *_getListenAddress(const ListenerOps *ops);
+static EncapType _getEncapType(const ListenerOps *ops);
+static int _getSocket(const ListenerOps *ops);
+
+static ListenerOps udpTemplate = {.context = NULL,
+ .destroy = &_destroy,
+ .getInterfaceIndex = &_getInterfaceIndex,
+ .getListenAddress = &_getListenAddress,
+ .getEncapType = &_getEncapType,
+ .getSocket = &_getSocket};
+
+static void _readcb(int fd, PARCEventType what, void *udpVoid);
+
+ListenerOps *udpListener_CreateInet6(Forwarder *forwarder,
+ struct sockaddr_in6 sin6) {
+ ListenerOps *ops = NULL;
+
+ UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener));
+ parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(UdpListener));
+ udp->forwarder = forwarder;
+ udp->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+ udp->localAddress = addressCreateFromInet6(&sin6);
+ udp->id = forwarder_GetNextConnectionId(forwarder);
+
+ udp->udp_socket = socket(AF_INET6, SOCK_DGRAM, 0);
+ parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s",
+ errno, strerror(errno));
+
+ // Set non-blocking flag
+ int flags = fcntl(udp->udp_socket, F_GETFL, NULL);
+ parcAssertTrue(flags != -1,
+ "fcntl failed to obtain file descriptor flags (%d)", errno);
+ int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK);
+ parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)",
+ errno);
+
+ int one = 1;
+ // don't hang onto address after listener has closed
+ failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one,
+ (socklen_t)sizeof(one));
+ parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno);
+
+ failure = bind(udp->udp_socket, (struct sockaddr *)&sin6, sizeof(sin6));
+ if (failure == 0) {
+ udp->udp_event =
+ dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true,
+ _readcb, (void *)udp, udp->udp_socket);
+ dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder),
+ udp->udp_event);
+
+ ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+ memcpy(ops, &udpTemplate, sizeof(ListenerOps));
+ ops->context = udp;
+
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ char *str = addressToString(udp->localAddress);
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "UdpListener %p created for address %s", (void *)udp, str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ } else {
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ int myerrno = errno;
+ char *str = addressToString(udp->localAddress);
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Error binding UDP socket to address %s: (%d) %s", str,
+ myerrno, strerror(myerrno));
+ parcMemory_Deallocate((void **)&str);
+ }
+
+ close(udp->udp_socket);
+ addressDestroy(&udp->localAddress);
+ logger_Release(&udp->logger);
+ parcMemory_Deallocate((void **)&udp);
+ }
+
+ return ops;
+}
+
+ListenerOps *udpListener_CreateInet(Forwarder *forwarder,
+ struct sockaddr_in sin) {
+ ListenerOps *ops = NULL;
+
+ UdpListener *udp = parcMemory_AllocateAndClear(sizeof(UdpListener));
+ parcAssertNotNull(udp, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(UdpListener));
+ udp->forwarder = forwarder;
+ udp->logger = logger_Acquire(forwarder_GetLogger(forwarder));
+ udp->localAddress = addressCreateFromInet(&sin);
+ udp->id = forwarder_GetNextConnectionId(forwarder);
+
+ udp->udp_socket = socket(AF_INET, SOCK_DGRAM, 0);
+ parcAssertFalse(udp->udp_socket < 0, "Error opening UDP socket: (%d) %s",
+ errno, strerror(errno));
+
+ // Set non-blocking flag
+ int flags = fcntl(udp->udp_socket, F_GETFL, NULL);
+ parcAssertTrue(flags != -1,
+ "fcntl failed to obtain file descriptor flags (%d)", errno);
+ int failure = fcntl(udp->udp_socket, F_SETFL, flags | O_NONBLOCK);
+ parcAssertFalse(failure, "fcntl failed to set file descriptor flags (%d)",
+ errno);
+
+ int one = 1;
+ // don't hang onto address after listener has closed
+ failure = setsockopt(udp->udp_socket, SOL_SOCKET, SO_REUSEADDR, (void *)&one,
+ (socklen_t)sizeof(one));
+ parcAssertFalse(failure, "failed to set REUSEADDR on socket(%d)", errno);
+
+ failure = bind(udp->udp_socket, (struct sockaddr *)&sin, sizeof(sin));
+ if (failure == 0) {
+ udp->udp_event =
+ dispatcher_CreateNetworkEvent(forwarder_GetDispatcher(forwarder), true,
+ _readcb, (void *)udp, udp->udp_socket);
+ dispatcher_StartNetworkEvent(forwarder_GetDispatcher(forwarder),
+ udp->udp_event);
+
+ ops = parcMemory_AllocateAndClear(sizeof(ListenerOps));
+ parcAssertNotNull(ops, "parcMemory_AllocateAndClear(%zu) returned NULL",
+ sizeof(ListenerOps));
+ memcpy(ops, &udpTemplate, sizeof(ListenerOps));
+ ops->context = udp;
+
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ char *str = addressToString(udp->localAddress);
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "UdpListener %p created for address %s", (void *)udp, str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ } else {
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ int myerrno = errno;
+ char *str = addressToString(udp->localAddress);
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Error binding UDP socket to address %s: (%d) %s", str,
+ myerrno, strerror(myerrno));
+ parcMemory_Deallocate((void **)&str);
+ }
+
+ close(udp->udp_socket);
+ addressDestroy(&udp->localAddress);
+ logger_Release(&udp->logger);
+ parcMemory_Deallocate((void **)&udp);
+ }
+
+ return ops;
+}
+
+static void udpListener_Destroy(UdpListener **listenerPtr) {
+ parcAssertNotNull(listenerPtr, "Parameter must be non-null double pointer");
+ parcAssertNotNull(*listenerPtr,
+ "Parameter must derefernce to non-null pointer");
+
+ UdpListener *udp = *listenerPtr;
+
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "UdpListener %p destroyed", (void *)udp);
+ }
+
+ close(udp->udp_socket);
+ addressDestroy(&udp->localAddress);
+ dispatcher_DestroyNetworkEvent(forwarder_GetDispatcher(udp->forwarder),
+ &udp->udp_event);
+ logger_Release(&udp->logger);
+ parcMemory_Deallocate((void **)&udp);
+ *listenerPtr = NULL;
+}
+
+static void _destroy(ListenerOps **listenerOpsPtr) {
+ ListenerOps *ops = *listenerOpsPtr;
+ UdpListener *udp = (UdpListener *)ops->context;
+ udpListener_Destroy(&udp);
+ parcMemory_Deallocate((void **)&ops);
+ *listenerOpsPtr = NULL;
+}
+
+static unsigned _getInterfaceIndex(const ListenerOps *ops) {
+ UdpListener *udp = (UdpListener *)ops->context;
+ return udp->id;
+}
+
+static const Address *_getListenAddress(const ListenerOps *ops) {
+ UdpListener *udp = (UdpListener *)ops->context;
+ return udp->localAddress;
+}
+
+static EncapType _getEncapType(const ListenerOps *ops) { return ENCAP_UDP; }
+
+static int _getSocket(const ListenerOps *ops) {
+ UdpListener *udp = (UdpListener *)ops->context;
+ return (int)udp->udp_socket;
+}
+
+// void
+// udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type)
+//{
+// return;
+//}
+
+// =====================================================================
+
+/**
+ * @function peekMesageLength
+ * @abstract Peek at the next packet to learn its length by reading the fixed
+ * header
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ */
+static size_t _peekMessageLength(UdpListener *udp, int fd,
+ struct sockaddr *peerIpAddress,
+ socklen_t *peerIpAddressLengthPtr) {
+ // to be fast I try to use just ipv6, this needs to be validated for ipv4
+
+ size_t packetLength = 0;
+
+ uint8_t fixedHeader[messageHandler_GetIPHeaderLength(IPv6)];
+
+ // peek at the UDP packet and read in the fixed header.
+ // Also returns the socket information for the remote peer
+
+ ssize_t res = recvfrom(
+ fd, fixedHeader, messageHandler_GetIPHeaderLength(IPv6), MSG_PEEK,
+ (struct sockaddr *)peerIpAddress, peerIpAddressLengthPtr);
+
+ if (res == messageHandler_GetIPHeaderLength(IPv6)) {
+ packetLength =
+ messageHandler_GetTotalPacketLength((const uint8_t *)&fixedHeader);
+ } else {
+ if (res < 0) {
+ printf("error while readin packet\n");
+ }
+ }
+
+ return packetLength;
+}
+
+/**
+ * @function _constructAddressPair
+ * @abstract Creates the address pair that uniquely identifies the connection
+ * @discussion
+ * The peerIpAddress must be of AF_INET or AF_INET6 family.
+ *
+ * @param <#param1#>
+ * @return Allocated MetisAddressPair, must be destroyed
+ */
+static AddressPair *_constructAddressPair(UdpListener *udp,
+ struct sockaddr *peerIpAddress,
+ socklen_t peerIpAddressLength) {
+ Address *remoteAddress;
+
+ switch (peerIpAddress->sa_family) {
+ case AF_INET:
+ remoteAddress =
+ addressCreateFromInet((struct sockaddr_in *)peerIpAddress);
+ break;
+
+ case AF_INET6:
+ remoteAddress =
+ addressCreateFromInet6((struct sockaddr_in6 *)peerIpAddress);
+ break;
+
+ default:
+ parcTrapIllegalValue(peerIpAddress,
+ "Peer address unrecognized family for IP: %d",
+ peerIpAddress->sa_family);
+ }
+
+ AddressPair *pair = addressPair_Create(udp->localAddress, remoteAddress);
+ addressDestroy(&remoteAddress);
+
+ return pair;
+}
+
+/**
+ * @function _lookupConnectionId
+ * @abstract Lookup a connection in the connection table
+ * @discussion
+ * Looks up the connection in the connection table and returns the connection
+ * id if it exists.
+ *
+ * @param outputConnectionIdPtr is the output parameter
+ * @return true if connection found and outputConnectionIdPtr set
+ */
+static bool _lookupConnectionId(UdpListener *udp, AddressPair *pair,
+ unsigned *outputConnectionIdPtr) {
+ ConnectionTable *connTable = forwarder_GetConnectionTable(udp->forwarder);
+
+ const Connection *conn = connectionTable_FindByAddressPair(connTable, pair);
+ if (conn) {
+ *outputConnectionIdPtr = connection_GetConnectionId(conn);
+ return true;
+ } else {
+ *outputConnectionIdPtr = 0;
+ return false;
+ }
+}
+
+/**
+ * @function _createNewConnection
+ * @abstract Creates a new Metis connection for the peer
+ * @discussion
+ * PRECONDITION: you know there's not an existing connection with the address
+ * pair
+ *
+ * Creates a new connection and adds it to the connection table.
+ *
+ * @param <#param1#>
+ * @return The connection id for the new connection
+ */
+
+static unsigned _createNewConnection(UdpListener *udp, int fd,
+ const AddressPair *pair) {
+ bool isLocal = false;
+
+ // metisUdpConnection_Create takes ownership of the pair
+ IoOperations *ops = udpConnection_Create(udp->forwarder, fd, pair, isLocal);
+ Connection *conn = connection_Create(ops);
+ // connection_AllowWldrAutoStart(conn);
+
+ connectionTable_Add(forwarder_GetConnectionTable(udp->forwarder), conn);
+ unsigned connid = ioOperations_GetConnectionId(ops);
+
+ return connid;
+}
+
+static void _handleProbeMessage(UdpListener *udp, uint8_t *msgBuffer) {
+ // TODO
+ parcMemory_Deallocate((void **)&msgBuffer);
+}
+
+static void _handleWldrNotification(UdpListener *udp, unsigned connId,
+ uint8_t *msgBuffer) {
+ const Connection *conn = connectionTable_FindById(
+ forwarder_GetConnectionTable(udp->forwarder), connId);
+ if (conn == NULL) {
+ return;
+ }
+
+ Message *message = message_CreateFromByteArray(
+ connId, msgBuffer, MessagePacketType_WldrNotification,
+ forwarder_GetTicks(udp->forwarder), forwarder_GetLogger(udp->forwarder));
+
+ connection_HandleWldrNotification((Connection *)conn, message);
+
+ message_Release(&message);
+}
+
+static Message *_readMessage(UdpListener *udp, int fd, size_t packetLength,
+ AddressPair *pair) {
+ uint8_t *msgBuffer = parcMemory_AllocateAndClear(packetLength);
+
+ ssize_t readLength = read(fd, msgBuffer, packetLength);
+
+ Message *message = NULL;
+
+ if (readLength < 0) {
+ printf("read failed %d: (%d) %s\n", fd, errno, strerror(errno));
+ return message;
+ }
+
+ unsigned connid = 0;
+ bool foundConnection = _lookupConnectionId(udp, pair, &connid);
+
+ if (readLength == packetLength) {
+ // we need to check if it is a valid packet
+ if (messageHandler_IsTCP(msgBuffer)) {
+ MessagePacketType pktType;
+
+ if (messageHandler_IsData(msgBuffer)) {
+ pktType = MessagePacketType_ContentObject;
+ if (!foundConnection) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return message;
+ }
+ } else if (messageHandler_IsInterest(msgBuffer)) {
+ pktType = MessagePacketType_Interest;
+ if (!foundConnection) {
+ connid = _createNewConnection(udp, fd, pair);
+ }
+ } else {
+ printf("Got a packet that is not a data nor an interest, drop it!\n");
+ parcMemory_Deallocate((void **)&msgBuffer);
+ return message;
+ }
+
+ message = message_CreateFromByteArray(
+ connid, msgBuffer, pktType, forwarder_GetTicks(udp->forwarder),
+ forwarder_GetLogger(udp->forwarder));
+
+ if (message == NULL) {
+ parcMemory_Deallocate((void **)&msgBuffer);
+ }
+ } else if (messageHandler_IsWldrNotification(msgBuffer)) {
+ _handleWldrNotification(udp, connid, msgBuffer);
+ } else if (messageHandler_IsLoadBalancerProbe(msgBuffer)) {
+ _handleProbeMessage(udp, msgBuffer);
+ }
+#ifdef WITH_MAPME
+ else if (mapMe_isMapMe(msgBuffer)) {
+ forwarder_ProcessMapMe(udp->forwarder, msgBuffer, connid);
+ }
+#endif /* WITH_MAPME */
+ }
+
+ return message;
+}
+
+static void _receivePacket(UdpListener *udp, int fd, size_t packetLength,
+ struct sockaddr_storage *peerIpAddress,
+ socklen_t peerIpAddressLength) {
+ AddressPair *pair = _constructAddressPair(
+ udp, (struct sockaddr *)peerIpAddress, peerIpAddressLength);
+
+ Message *message = _readMessage(udp, fd, packetLength, pair);
+ addressPair_Release(&pair);
+
+ if (message) {
+ forwarder_Receive(udp->forwarder, message);
+ } else {
+ return;
+ }
+}
+
+static void _readFrameToDiscard(UdpListener *udp, int fd) {
+ // we need to discard the frame. Read 1 byte. This will clear it off the
+ // stack.
+ uint8_t buffer;
+ ssize_t nread = read(fd, &buffer, 1);
+
+ if (nread == 1) {
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "Discarded frame from fd %d", fd);
+ }
+ } else if (nread < 0) {
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Error trying to discard frame from fd %d: (%d) %s", fd, errno,
+ strerror(errno));
+ }
+ }
+}
+
+static void _readcb(int fd, PARCEventType what, void *udpVoid) {
+ UdpListener *udp = (UdpListener *)udpVoid;
+
+ if (logger_IsLoggable(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug)) {
+ logger_Log(udp->logger, LoggerFacility_IO, PARCLogLevel_Debug, __func__,
+ "%s socket %d what %s%s%s%s data %p", __func__, fd,
+ (what & PARCEventType_Timeout) ? " timeout" : "",
+ (what & PARCEventType_Read) ? " read" : "",
+ (what & PARCEventType_Write) ? " write" : "",
+ (what & PARCEventType_Signal) ? " signal" : "", udpVoid);
+ }
+
+ if (what & PARCEventType_Read) {
+ struct sockaddr_storage peerIpAddress;
+ socklen_t peerIpAddressLength = sizeof(peerIpAddress);
+
+ size_t packetLength = _peekMessageLength(
+ udp, fd, (struct sockaddr *)&peerIpAddress, &peerIpAddressLength);
+
+ if (packetLength > 0) {
+ _receivePacket(udp, fd, packetLength, &peerIpAddress,
+ peerIpAddressLength);
+ } else {
+ _readFrameToDiscard(udp, fd);
+ }
+ }
+}
diff --git a/hicn-light/src/io/udpListener.h b/hicn-light/src/io/udpListener.h
new file mode 100755
index 000000000..1cf3bd887
--- /dev/null
+++ b/hicn-light/src/io/udpListener.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.
+ */
+
+#ifndef udpListener_h
+#define udpListener_h
+
+#include <netinet/in.h>
+#include <src/core/forwarder.h>
+#include <src/io/listener.h>
+#include <stdlib.h>
+
+struct udp_listener;
+typedef struct udp_listener UdpListener;
+
+ListenerOps *udpListener_CreateInet6(Forwarder *forwarder,
+ struct sockaddr_in6 sin6);
+ListenerOps *udpListener_CreateInet(Forwarder *forwarder,
+ struct sockaddr_in sin);
+// void udpListener_SetPacketType(ListenerOps *ops, MessagePacketType type);
+#endif // udpListener_h
diff --git a/hicn-light/src/io/udpTunnel.c b/hicn-light/src/io/udpTunnel.c
new file mode 100755
index 000000000..d06a35ce6
--- /dev/null
+++ b/hicn-light/src/io/udpTunnel.c
@@ -0,0 +1,91 @@
+/*
+ * 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 <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/assert/parc_Assert.h>
+#include <src/io/udpConnection.h>
+#include <src/io/udpListener.h>
+#include <src/io/udpTunnel.h>
+
+IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder,
+ ListenerOps *localListener,
+ const Address *remoteAddress) {
+ parcAssertNotNull(forwarder, "Parameter metis must be non-null");
+ parcAssertNotNull(localListener, "Parameter localListener must be non-null");
+ parcAssertNotNull(remoteAddress, "Parameter remoteAddress must be non-null");
+
+ Logger *logger = forwarder_GetLogger(forwarder);
+
+ IoOperations *ops = NULL;
+ if (localListener->getEncapType(localListener) == ENCAP_UDP) {
+ const Address *localAddress =
+ localListener->getListenAddress(localListener);
+ address_type localType = addressGetType(localAddress);
+ address_type remoteType = addressGetType(remoteAddress);
+
+ if (localType == remoteType) {
+ AddressPair *pair = addressPair_Create(localAddress, remoteAddress);
+ bool isLocal = false;
+ int fd = localListener->getSocket(localListener);
+ // udpListener_SetPacketType(localListener,
+ // MessagePacketType_ContentObject);
+ ops = udpConnection_Create(forwarder, fd, pair, isLocal);
+
+ addressPair_Release(&pair);
+ } else {
+ if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Local listener of type %s and remote type %s, cannot "
+ "establish tunnel",
+ addressTypeToString(localType),
+ addressTypeToString(remoteType));
+ }
+ }
+ } else {
+ if (logger_IsLoggable(logger, LoggerFacility_IO, PARCLogLevel_Error)) {
+ logger_Log(logger, LoggerFacility_IO, PARCLogLevel_Error, __func__,
+ "Local listener %p is not type UDP, cannot establish tunnel",
+ (void *)localListener);
+ }
+ }
+
+ return ops;
+}
+
+IoOperations *udpTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress) {
+ ListenerSet *set = forwarder_GetListenerSet(forwarder);
+ ListenerOps *listener = listenerSet_Find(set, ENCAP_UDP, localAddress);
+ IoOperations *ops = NULL;
+ if (listener) {
+ ops = udpTunnel_CreateOnListener(forwarder, listener, remoteAddress);
+ } else {
+ if (logger_IsLoggable(forwarder_GetLogger(forwarder), LoggerFacility_IO,
+ PARCLogLevel_Error)) {
+ char *str = addressToString(localAddress);
+ logger_Log(forwarder_GetLogger(forwarder), LoggerFacility_IO,
+ PARCLogLevel_Error, __func__,
+ "Could not find listener to match address %s", str);
+ parcMemory_Deallocate((void **)&str);
+ }
+ }
+ return ops;
+}
diff --git a/hicn-light/src/io/udpTunnel.h b/hicn-light/src/io/udpTunnel.h
new file mode 100755
index 000000000..a79ca4a4e
--- /dev/null
+++ b/hicn-light/src/io/udpTunnel.h
@@ -0,0 +1,42 @@
+/*
+ * 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 udpTunnel.h
+ * @brief Establish a tunnel to a remote system
+ *
+ */
+
+#ifndef udpTunnel_h
+#define udpTunnel_h
+
+#include <src/core/forwarder.h>
+#include <src/io/ioOperations.h>
+#include <src/io/listener.h>
+#include <src/utils/address.h>
+
+/**
+ */
+IoOperations *udpTunnel_CreateOnListener(Forwarder *forwarder,
+ ListenerOps *localListener,
+ const Address *remoteAddress);
+
+/**
+ */
+IoOperations *udpTunnel_Create(Forwarder *forwarder,
+ const Address *localAddress,
+ const Address *remoteAddress);
+
+#endif // udpTunnel_h