diff options
author | Luca Muscariello <lumuscar+fdio@cisco.com> | 2019-03-23 14:13:53 +0100 |
---|---|---|
committer | Angelo Mantellini <manangel@cisco.com> | 2019-03-24 14:11:17 +0100 |
commit | eb323e056e747d71867cf965434811c1de925de2 (patch) | |
tree | 23a6096823d795efe448559cf0409a18ab47932d /hicn-light/src/hicn/io/udpConnection.c | |
parent | 43562f9f02d35e5d540ab4028a0326c0c7cd4898 (diff) |
[HICN-141] Definition of a C API for hicn-light
Change-Id: Id861f0abe58b1e3c9ba8cc76701da0f9c6801748
Signed-off-by: Luca Muscariello <lumuscar+fdio@cisco.com>
Signed-off-by: Angelo Mantellini <manangel@cisco.com>
Diffstat (limited to 'hicn-light/src/hicn/io/udpConnection.c')
-rw-r--r-- | hicn-light/src/hicn/io/udpConnection.c | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/hicn-light/src/hicn/io/udpConnection.c b/hicn-light/src/hicn/io/udpConnection.c new file mode 100644 index 000000000..b057f03ff --- /dev/null +++ b/hicn-light/src/hicn/io/udpConnection.c @@ -0,0 +1,391 @@ +/* + * 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 + * + */ + +#ifndef _WIN32 +#include <sys/uio.h> +#endif +#include <errno.h> +#include <src/hicn/config.h> +#include <stdio.h> +#include <string.h> + +#include <hicn/core/messageHandler.h> +#include <hicn/io/udpConnection.h> + +#include <parc/algol/parc_Hash.h> +#include <parc/algol/parc_Memory.h> +#include <parc/assert/parc_Assert.h> +#include <hicn/core/connection.h> +#include <hicn/core/forwarder.h> +#include <hicn/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 bool _sendCommandResponse(IoOperations *ops, struct iovec *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, + .sendCommandResponse = &_sendCommandResponse, + .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 + + ssize_t writeLength = + sendto(udpConnState->udpListenerSocket, message_FixedHeader(message), + (int)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 bool _sendCommandResponse(IoOperations *ops, struct iovec *message){ + parcAssertNotNull(ops, "Parameter ops must be non-null"); + parcAssertNotNull(message, "Parameter message must be non-null"); + _UdpState *udpConnState = (_UdpState *)ioOperations_GetClosure(ops); + +#ifndef _WIN32 + // Perform connect before to establish association between this peer and + // the remote peer. This is required to use writev. + // Connection association can be changed at any time. + connect(udpConnState->udpListenerSocket, + udpConnState->peerAddress, + udpConnState->peerAddressLength); + + ssize_t writeLength = writev(udpConnState->udpListenerSocket, message, 2); + + struct sockaddr any_address = {0}; + any_address.sa_family = AF_UNSPEC; + connect(udpConnState->udpListenerSocket, + &any_address, (socklen_t)sizeof(any_address)); + + if (writeLength < 0) { + return false; + } +#else + WSABUF dataBuf[2]; + DWORD BytesSent = 0; + + for (int i = 0; i < 2; i++) { + dataBuf[i].buf = message[i].iov_base; + dataBuf[i].len = (ULONG)message[i].iov_len; + } + + int rc = WSASendTo(udpConnState->udpListenerSocket, dataBuf, 2, + &BytesSent, 0, (SOCKADDR *)udpConnState->peerAddress, + udpConnState->peerAddressLength, NULL, NULL); + + if (rc == SOCKET_ERROR) { + return false; + } +#endif + return true; +} + +static list_connections_type _getConnectionType(const IoOperations *ops) { + return CONN_UDP; +} + +static Ticks _sendProbe(IoOperations *ops, unsigned probeType, + uint8_t *message) { + //TODO + 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; + } +} |