diff options
Diffstat (limited to 'metis/ccnx/forwarder/metis/io/metis_UdpConnection.c')
-rw-r--r-- | metis/ccnx/forwarder/metis/io/metis_UdpConnection.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/io/metis_UdpConnection.c b/metis/ccnx/forwarder/metis/io/metis_UdpConnection.c new file mode 100644 index 00000000..aecd68fa --- /dev/null +++ b/metis/ccnx/forwarder/metis/io/metis_UdpConnection.c @@ -0,0 +1,396 @@ +/* + * Copyright (c) 2017 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 <config.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#include <ccnx/forwarder/metis/io/metis_UdpConnection.h> + +#include <ccnx/forwarder/metis/core/metis_Forwarder.h> +#include <ccnx/forwarder/metis/core/metis_Connection.h> +#include <ccnx/forwarder/metis/core/metis_Message.h> +#include <parc/algol/parc_Hash.h> + +#include <LongBow/runtime.h> +#include <parc/algol/parc_Memory.h> +#include <ccnx/forwarder/metis/tlv/metis_Tlv.h> + +typedef struct metis_udp_state { + MetisForwarder *metis; + MetisLogger *logger; + + // the udp listener socket we receive packets on + int udpListenerSocket; + + MetisAddressPair *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; +} _MetisUdpState; + +// Prototypes +static bool _send(MetisIoOperations *ops, const CPIAddress *nexthop, MetisMessage *message); +static const CPIAddress *_getRemoteAddress(const MetisIoOperations *ops); +static const MetisAddressPair *_getAddressPair(const MetisIoOperations *ops); +static unsigned _getConnectionId(const MetisIoOperations *ops); +static bool _isUp(const MetisIoOperations *ops); +static bool _isLocal(const MetisIoOperations *ops); +static void _destroy(MetisIoOperations **opsPtr); +static CPIConnectionType _getConnectionType(const MetisIoOperations *ops); +static MetisTicks _sendProbe(MetisIoOperations *ops, unsigned probeType); +/* + * This assigns a unique pointer to the void * which we use + * as a GUID for this class. + */ +static const void *_metisIoOperationsGuid = __FILE__; + +/* + * Return our GUID + */ +static const void * +_metisStreamConnection_Class(const MetisIoOperations *ops) +{ + return _metisIoOperationsGuid; +} + +static MetisIoOperations _template = { + .closure = NULL, + .send = &_send, + .getRemoteAddress = &_getRemoteAddress, + .getAddressPair = &_getAddressPair, + .getConnectionId = &_getConnectionId, + .isUp = &_isUp, + .isLocal = &_isLocal, + .destroy = &_destroy, + .class = &_metisStreamConnection_Class, + .getConnectionType = &_getConnectionType, + .sendProbe = &_sendProbe +}; + +// ================================================================= + +static void _setConnectionState(_MetisUdpState *Udp, bool isUp); +static bool _saveSockaddr(_MetisUdpState *udpConnState, const MetisAddressPair *pair); + +MetisIoOperations * +metisUdpConnection_Create(MetisForwarder *metis, int fd, const MetisAddressPair *pair, bool isLocal) +{ + MetisIoOperations *io_ops = NULL; + + _MetisUdpState *udpConnState = parcMemory_AllocateAndClear(sizeof(_MetisUdpState)); + assertNotNull(udpConnState, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisUdpState)); + + udpConnState->metis = metis; + udpConnState->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis)); + + bool saved = _saveSockaddr(udpConnState, pair); + if (saved) { + udpConnState->udpListenerSocket = fd; + udpConnState->id = metisForwarder_GetNextConnectionId(metis); + udpConnState->addressPair = metisAddressPair_Acquire(pair); + udpConnState->isLocal = isLocal; + + // allocate a connection + io_ops = parcMemory_AllocateAndClear(sizeof(MetisIoOperations)); + assertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisIoOperations)); + memcpy(io_ops, &_template, sizeof(MetisIoOperations)); + io_ops->closure = udpConnState; + + _setConnectionState(udpConnState, true); + + if (metisLogger_IsLoggable(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) { + char *str = metisAddressPair_ToString(udpConnState->addressPair); + metisLogger_Log(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__, + "UdpConnection %p created for address %s (isLocal %d)", + (void *) udpConnState, str, udpConnState->isLocal); + free(str); + } + + metisMessenger_Send(metisForwarder_GetMessenger(metis), metisMissive_Create(MetisMissiveType_ConnectionCreate, udpConnState->id)); + metisMessenger_Send(metisForwarder_GetMessenger(metis), metisMissive_Create(MetisMissiveType_ConnectionUp, udpConnState->id)); + } else { + // _saveSockaddr will already log an error, no need for extra log message here + metisLogger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **) &udpConnState); + } + + return io_ops; +} + +// ================================================================= +// I/O Operations implementation + +static void +_destroy(MetisIoOperations **opsPtr) +{ + assertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer"); + assertNotNull(*opsPtr, "Parameter opsPtr must dereference to non-null pointer"); + + MetisIoOperations *ops = *opsPtr; + assertNotNull(metisIoOperations_GetClosure(ops), "ops->context must not be null"); + + _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops); + metisAddressPair_Release(&udpConnState->addressPair); + parcMemory_Deallocate((void **) &(udpConnState->peerAddress)); + + metisMessenger_Send(metisForwarder_GetMessenger(udpConnState->metis), metisMissive_Create(MetisMissiveType_ConnectionDestroyed, udpConnState->id)); + + if (metisLogger_IsLoggable(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) { + metisLogger_Log(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__, + "UdpConnection %p destroyed", + (void *) udpConnState); + } + + // do not close udp->udpListenerSocket, the listener will close + // that when its done + + metisLogger_Release(&udpConnState->logger); + parcMemory_Deallocate((void **) &udpConnState); + parcMemory_Deallocate((void **) &ops); + + *opsPtr = NULL; +} + +static bool +_isUp(const MetisIoOperations *ops) +{ + assertNotNull(ops, "Parameter must be non-null"); + const _MetisUdpState *udpConnState = (const _MetisUdpState *) metisIoOperations_GetClosure(ops); + return udpConnState->isUp; +} + +static bool +_isLocal(const MetisIoOperations *ops) +{ + assertNotNull(ops, "Parameter must be non-null"); + const _MetisUdpState *udpConnState = (const _MetisUdpState *) metisIoOperations_GetClosure(ops); + return udpConnState->isLocal; +} + +static const CPIAddress * +_getRemoteAddress(const MetisIoOperations *ops) +{ + assertNotNull(ops, "Parameter must be non-null"); + const _MetisUdpState *udpConnState = (const _MetisUdpState *) metisIoOperations_GetClosure(ops); + return metisAddressPair_GetRemote(udpConnState->addressPair); +} + +static const MetisAddressPair * +_getAddressPair(const MetisIoOperations *ops) +{ + assertNotNull(ops, "Parameter must be non-null"); + const _MetisUdpState *udpConnState = (const _MetisUdpState *) metisIoOperations_GetClosure(ops); + return udpConnState->addressPair; +} + +static unsigned +_getConnectionId(const MetisIoOperations *ops) +{ + assertNotNull(ops, "Parameter must be non-null"); + const _MetisUdpState *udpConnState = (const _MetisUdpState *) metisIoOperations_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(MetisIoOperations *ops, const CPIAddress *dummy, MetisMessage *message) +{ + assertNotNull(ops, "Parameter ops must be non-null"); + assertNotNull(message, "Parameter message must be non-null"); + _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops); + + PARCEventBuffer *writeBuffer = parcEventBuffer_Create(); + metisMessage_Append(writeBuffer, message); + + const uint8_t *buffer = parcEventBuffer_Pullup(writeBuffer, -1); + size_t bufferLength = parcEventBuffer_GetLength(writeBuffer); + + ssize_t writeLength = sendto(udpConnState->udpListenerSocket, buffer, bufferLength, 0, udpConnState->peerAddress, udpConnState->peerAddressLength); + + if (writeLength < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + parcEventBuffer_Destroy(&writeBuffer); + return false; + } else { + //this print is for debugging + printf("Incorrect write length %zd, expected %zd: (%d) %s\n", writeLength, bufferLength, errno, strerror(errno)); + parcEventBuffer_Destroy(&writeBuffer); + return false; + } + } + + parcEventBuffer_Destroy(&writeBuffer); + return true; +} + +static CPIConnectionType +_getConnectionType(const MetisIoOperations *ops) +{ + return cpiConnection_UDP; +} + +static MetisTicks +_sendProbe(MetisIoOperations *ops, unsigned probeType) +{ + assertNotNull(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; //tlv 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); +} + +/*static MetisTicks + * _handleProbe(MetisIoOperations *ops, MetisTicks last_sent, uint8_t *pkt) + * { + * assertNotNull(ops, "Parameter ops must be non-null"); + * assertNotNull(pkt, "Parameter pkt must be non-null"); + * + * _MetisUdpState *udpConnState = (_MetisUdpState *) metisIoOperations_GetClosure(ops); + * + * MetisTicks delay = 0; + * + * if(pkt[1] == METIS_PACKET_TYPE_PROBE_REQUEST){ + * _sendProbeType(ops, METIS_PACKET_TYPE_PROBE_REPLY); + * } else if (pkt[1] == METIS_PACKET_TYPE_PROBE_REPLY) { + * MetisTicks now = metisForwarder_GetTicks(udpConnState->metis); + * delay = now - last_sent; + * } else { + * printf("receivde unkwon probe type\n"); + * } + * + * return delay; + * }*/ + +// ================================================================= +// Internal API + +static bool +_saveSockaddr(_MetisUdpState *udpConnState, const MetisAddressPair *pair) +{ + bool success = false; + const CPIAddress *remoteAddress = metisAddressPair_GetRemote(pair); + + switch (cpiAddress_GetType(remoteAddress)) { + case cpiAddressType_INET: { + size_t bytes = sizeof(struct sockaddr_in); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + assertNotNull(udpConnState->peerAddress, "parcMemory_Allocate(%zu) returned NULL", bytes); + + cpiAddress_GetInet(remoteAddress, (struct sockaddr_in *) udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t) bytes; + + success = true; + break; + } + + case cpiAddressType_INET6: { + size_t bytes = sizeof(struct sockaddr_in6); + udpConnState->peerAddress = parcMemory_Allocate(bytes); + assertNotNull(udpConnState->peerAddress, "parcMemory_Allocate(%zu) returned NULL", bytes); + + cpiAddress_GetInet6(remoteAddress, (struct sockaddr_in6 *) udpConnState->peerAddress); + udpConnState->peerAddressLength = (socklen_t) bytes; + + success = true; + break; + } + + default: + if (metisLogger_IsLoggable(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Error)) { + char *str = cpiAddress_ToString(remoteAddress); + metisLogger_Log(udpConnState->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__, + "Remote address is not INET or INET6: %s", str); + parcMemory_Deallocate((void **) &str); + } + break; + } + return success; +} + +static void +_setConnectionState(_MetisUdpState *udpConnState, bool isUp) +{ + assertNotNull(udpConnState, "Parameter Udp must be non-null"); + + MetisMessenger *messenger = metisForwarder_GetMessenger(udpConnState->metis); + + bool oldStateIsUp = udpConnState->isUp; + udpConnState->isUp = isUp; + + if (oldStateIsUp && !isUp) { + // bring connection DOWN + MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionDown, udpConnState->id); + metisMessenger_Send(messenger, missive); + return; + } + + + if (!oldStateIsUp && isUp) { + // bring connection UP + MetisMissive *missive = metisMissive_Create(MetisMissiveType_ConnectionUp, udpConnState->id); + metisMessenger_Send(messenger, missive); + return; + } +} |