diff options
Diffstat (limited to 'hicn-light/src/io')
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 |