aboutsummaryrefslogtreecommitdiffstats
path: root/hicn-light/src/io/hicnConnection.c
diff options
context:
space:
mode:
Diffstat (limited to 'hicn-light/src/io/hicnConnection.c')
-rwxr-xr-xhicn-light/src/io/hicnConnection.c517
1 files changed, 517 insertions, 0 deletions
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;
+ }
+}