static CCNxMetaMessage * _TemplateReceive(AthenaTransportLink *athenaTransportLink) { struct _TemplateLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); CCNxMetaMessage *ccnxMetaMessage = NULL; PARCBuffer *wireFormatBuffer = _internalRECEIVE(linkData); // On error, just return and retry. if (wireFormatBuffer == NULL) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "read error (%s)", strerror(errno)); return NULL; } // Construct, and return a ccnxMetaMessage from the wire format buffer. ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer); if (ccnxMetaMessage == NULL) { linkData->_stats.receive_DecodeFailed++; parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet."); } parcBuffer_Release(&wireFormatBuffer); if (parcDeque_Size(linkData->queue) > 0) { // if there's another message, mark an event. athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive); } return ccnxMetaMessage; }
static void _queueMessage(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); parcDeque_Append(linkData->queue, ccnxMetaMessage); athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive); }
static void _TemplateClose(AthenaTransportLink *athenaTransportLink) { parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink), "link %s closed", athenaTransportLink_GetName(athenaTransportLink)); _TemplateLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); _TemplateLinkData_Destroy(&linkData); }
static int _UDPPoll(AthenaTransportLink *athenaTransportLink, int timeout) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); if (linkData->queue) { return (int) parcDeque_Size(linkData->queue); } return 0; }
static bool _linkIsEOF(AthenaTransportLink *athenaTransportLink) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); // If poll indicates there's a read event and a subsequent read returns zero our peer has hungup. struct pollfd pollfd = { .fd = linkData->fd, .events = POLLIN }; int events = poll(&pollfd, 1, 0); if (events == -1) { parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "poll error (%s)", strerror(errno)); return true; // poll error, close the link } else if (events == 0) { // there are no pending events, was truly a zero read return false; } if (pollfd.revents & POLLIN) { char peekBuffer; ssize_t readCount = recv(linkData->fd, (void *) &peekBuffer, 1, MSG_PEEK); if (readCount == -1) { if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // read blocked linkData->_stats.receive_ReadWouldBlock++; return false; } return true; // read error } if (readCount == 0) { // EOF return true; } } return false; } static void _UDPClose(AthenaTransportLink *athenaTransportLink) { parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink), "link %s closed", athenaTransportLink_GetName(athenaTransportLink)); _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); close(linkData->fd); _UDPLinkData_Destroy(&linkData); }
// // Return a message which has been queued on this link from a UDP listener // static CCNxMetaMessage * _UDPReceiveProxy(AthenaTransportLink *athenaTransportLink) { CCNxMetaMessage *ccnxMetaMessage = NULL; struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); if (parcDeque_Size(linkData->queue) > 0) { ccnxMetaMessage = parcDeque_RemoveFirst(linkData->queue); if (parcDeque_Size(linkData->queue) > 0) { // if there's another message, post an event. athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive); } } return ccnxMetaMessage; }
// // Receive a message from the specified link. // static CCNxMetaMessage * _ETHReceiveMessage(AthenaTransportLink *athenaTransportLink, struct ether_addr *peerAddress, socklen_t *peerAddressLength) { struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); CCNxMetaMessage *ccnxMetaMessage = NULL; AthenaTransportLinkEvent events = 0; PARCBuffer *message = athenaEthernet_Receive(linkData->athenaEthernet, -1, &events); if (message == NULL) { return NULL; } // Mark any pending events if (events) { athenaTransportLink_SetEvent(athenaTransportLink, events); } // Map the header struct ether_header *header = parcBuffer_Overlay(message, sizeof(struct ether_header)); // If the destination does not match my address, drop the message if (memcmp(&linkData->link.myAddress, header->ether_dhost, ETHER_ADDR_LEN * sizeof(uint8_t)) != 0) { linkData->_stats.receive_NoLinkDestination++; parcBuffer_Release(&message); return NULL; } assertTrue(header->ether_type == htons(CCNX_ETHERTYPE), "Unexpected ether type %x", header->ether_type); // Set peerAddress from header source address *peerAddressLength = ETHER_ADDR_LEN * sizeof(uint8_t); memcpy(peerAddress, header->ether_shost, *peerAddressLength); parcBuffer_SetPosition(message, sizeof(struct ether_header)); PARCBuffer *wireFormatBuffer = parcBuffer_Slice(message); parcBuffer_Release(&message); parcBuffer_SetPosition(wireFormatBuffer, 0); // Construct, and return a ccnxMetaMessage from the wire format buffer. ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer); if (ccnxMetaMessage == NULL) { linkData->_stats.receive_DecodeFailed++; parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet."); } else if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "received deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage)); } parcBuffer_Release(&wireFormatBuffer); return ccnxMetaMessage; }
static int _TemplateSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage) { struct _TemplateLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); PARCBuffer *wireFormatBuffer = athenaTransportLinkModule_GetMessageBuffer(ccnxMetaMessage); int result = _internalSEND(linkData, wireFormatBuffer); parcBuffer_Release(&wireFormatBuffer); // Flag there's a message to pickup athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive); return result; }
// // Receive a message from a point to point link. // static CCNxMetaMessage * _ETHReceive(AthenaTransportLink *athenaTransportLink) { struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); struct ether_addr peerAddress; socklen_t peerAddressLength; CCNxMetaMessage *ccnxMetaMessage = _ETHReceiveMessage(athenaTransportLink, &peerAddress, &peerAddressLength); // If the souce does not match my configured link peer address, drop the message if (memcmp(&linkData->link.peerAddress, &peerAddress, peerAddressLength) != 0) { linkData->_stats.receive_PeerNotConfigured++; ccnxMetaMessage_Release(&ccnxMetaMessage); return NULL; } return ccnxMetaMessage; }
static void _demuxDelivery(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage, struct sockaddr_in *peerAddress, socklen_t peerAddressLength) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); AthenaTransportLink *demuxLink = parcHashCodeTable_Get(linkData->multiplexTable, (void *) _hashAddress(peerAddress)); // If it's an unknown peer, try to create a new link if (demuxLink == NULL) { _UDPLinkData *newLinkData = _UDPLinkData_Create(); // We use our parents fd to send, and receive demux'd messages from our parent on our queue newLinkData->fd = dup(linkData->fd); newLinkData->queue = parcDeque_Create(); assertNotNull(newLinkData->queue, "Could not create data queue for new link"); newLinkData->link.myAddressLength = linkData->link.myAddressLength; memcpy(&newLinkData->link.myAddress, &linkData->link.myAddress, linkData->link.myAddressLength); newLinkData->link.peerAddressLength = peerAddressLength; memcpy(&newLinkData->link.peerAddress, peerAddress, peerAddressLength); demuxLink = _newLink(athenaTransportLink, newLinkData); if (demuxLink) { parcHashCodeTable_Add(linkData->multiplexTable, (void *) _hashAddress(peerAddress), demuxLink); } } // If there's no existing link and a new one can't be created, drop the message if (demuxLink == NULL) { ccnxMetaMessage_Release(&ccnxMetaMessage); return; } _queueMessage(demuxLink, ccnxMetaMessage); }
// // Receive a message from the specified link. // static CCNxMetaMessage * _UDPReceiveMessage(AthenaTransportLink *athenaTransportLink, struct sockaddr_in *peerAddress, socklen_t *peerAddressLength) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); CCNxMetaMessage *ccnxMetaMessage = NULL; size_t messageLength; // If an MTU has been set, allocate a buffer of that size to avoid having to peek at the message, // othersize derive the link from the header and allocate a buffer based on the message size. if ((messageLength = linkData->link.mtu) == 0) { messageLength = _messageLengthFromHeader(athenaTransportLink, linkData); if (messageLength <= 0) { return NULL; } } PARCBuffer *wireFormatBuffer = parcBuffer_Allocate(messageLength); char *buffer = parcBuffer_Overlay(wireFormatBuffer, 0); *peerAddressLength = (socklen_t) sizeof(struct sockaddr_in); ssize_t readCount = recvfrom(linkData->fd, buffer, messageLength, 0, (struct sockaddr *) peerAddress, peerAddressLength); // On error, just return and retry. if (readCount == -1) { linkData->_stats.receive_ReadError++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "read error (%s)", strerror(errno)); parcBuffer_Release(&wireFormatBuffer); return NULL; } // A zero read means either no more data is currently available or our peer hungup. // Just return to retry as we'll detect EOF when we come back at the top of UDPReceive if (readCount == 0) { parcBuffer_Release(&wireFormatBuffer); return NULL; } // If it was it a short read just return to retry later. while (readCount < messageLength) { linkData->_stats.receive_ShortRead++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short read error (%s)", strerror(errno)); parcBuffer_Release(&wireFormatBuffer); return NULL; } parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "received message (size=%d)", readCount); parcBuffer_SetPosition(wireFormatBuffer, parcBuffer_Position(wireFormatBuffer) + readCount); parcBuffer_Flip(wireFormatBuffer); // Construct, and return a ccnxMetaMessage from the wire format buffer. ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer); if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "received deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage)); } if (ccnxMetaMessage == NULL) { linkData->_stats.receive_DecodeFailed++; parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet."); } parcBuffer_Release(&wireFormatBuffer); return ccnxMetaMessage; }
static int _UDPSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage)); } // Get a wire format buffer and write it out. PARCBuffer *wireFormatBuffer = ccnxWireFormatMessage_GetWireFormatBuffer(ccnxMetaMessage); if (wireFormatBuffer == NULL) { CCNxCodecNetworkBufferIoVec *iovec = ccnxWireFormatMessage_GetIoVec(ccnxMetaMessage); assertNotNull(iovec, "Null io vector"); size_t iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec); const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec); // If it's a single vector wrap it in a buffer to avoid a copy if (iovcnt == 1) { wireFormatBuffer = parcBuffer_Wrap(array[0].iov_base, array[0].iov_len, 0, array[0].iov_len); } else { size_t totalbytes = 0; for (int i = 0; i < iovcnt; i++) { totalbytes += array[i].iov_len; } wireFormatBuffer = parcBuffer_Allocate(totalbytes); for (int i = 0; i < iovcnt; i++) { parcBuffer_PutArray(wireFormatBuffer, array[i].iov_len, array[i].iov_base); } parcBuffer_Flip(wireFormatBuffer); } } else { wireFormatBuffer = parcBuffer_Acquire(wireFormatBuffer); } size_t length = parcBuffer_Limit(wireFormatBuffer); char *buffer = parcBuffer_Overlay(wireFormatBuffer, length); if (linkData->link.mtu) { if (length > linkData->link.mtu) { errno = EMSGSIZE; parcBuffer_Release(&wireFormatBuffer); return -1; } } parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending message (size=%d)", length); ssize_t writeCount = 0; #ifdef LINUX_IGNORESIGPIPE writeCount = sendto(linkData->fd, buffer, length, MSG_NOSIGNAL, (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength); #else writeCount = sendto(linkData->fd, buffer, length, 0, (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength); #endif // on error close the link, else return to retry a zero write if (writeCount == -1) { if (errno == EPIPE) { athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error); } parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "send error (%s)", strerror(errno)); parcBuffer_Release(&wireFormatBuffer); return -1; } // Short write if (writeCount != length) { linkData->_stats.receive_ShortWrite++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write"); parcBuffer_Release(&wireFormatBuffer); return -1; } parcBuffer_Release(&wireFormatBuffer); return 0; }
static int _UDPSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage) { struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage)); } // Get a wire format buffer and write it out. PARCBuffer *wireFormatBuffer = athenaTransportLinkModule_GetMessageBuffer(ccnxMetaMessage); parcBuffer_SetPosition(wireFormatBuffer, 0); size_t length = parcBuffer_Limit(wireFormatBuffer); char *buffer = parcBuffer_Overlay(wireFormatBuffer, length); if (linkData->link.mtu) { if (length > linkData->link.mtu) { errno = EMSGSIZE; parcBuffer_Release(&wireFormatBuffer); return -1; } } parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending message (size=%d)", length); ssize_t writeCount = 0; #ifdef LINUX_IGNORESIGPIPE writeCount = sendto(linkData->fd, buffer, length, MSG_NOSIGNAL, (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength); #else writeCount = sendto(linkData->fd, buffer, length, 0, (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength); #endif // on error close the link, else return to retry a zero write if (writeCount == -1) { if ((errno == EAGAIN) || (errno == EINTR)) { linkData->_stats.send_SendRetry++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "send retry (%s)", strerror(errno)); } else { athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error); parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "send error (%s)", strerror(errno)); } parcBuffer_Release(&wireFormatBuffer); return -1; } // Short write if (writeCount != length) { linkData->_stats.send_ShortWrite++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write"); parcBuffer_Release(&wireFormatBuffer); return -1; } parcBuffer_Release(&wireFormatBuffer); return 0; }
static int _ETHSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage) { struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink); if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) { parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage)); } // Construct our header to prepend struct ether_header header; memcpy(header.ether_shost, &(linkData->link.myAddress), ETHER_ADDR_LEN * sizeof(uint8_t)); memcpy(header.ether_dhost, &(linkData->link.peerAddress), ETHER_ADDR_LEN * sizeof(uint8_t)); header.ether_type = htons(athenaEthernet_GetEtherType(linkData->athenaEthernet)); // An iovec to contain the header and packet data struct iovec iov[2]; struct iovec *array = iov; int iovcnt = 2; size_t messageLength = 0; // If the iovec we're prepending to has more than one element, allocatedIovec holds the // allocated IO vector of the right size that we must deallocate before returning. struct iovec *allocatedIovec = NULL; // Attach the header and populate the iovec CCNxCodecNetworkBufferIoVec *iovec = athenaTransportLinkModule_GetMessageIoVector(ccnxMetaMessage); iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec); const struct iovec *networkBufferIovec = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec); // Trivial case, single iovec element. if (iovcnt == 1) { // Header array[0].iov_len = sizeof(struct ether_header); array[0].iov_base = &header; // Message content array[1].iov_len = networkBufferIovec->iov_len; array[1].iov_base = networkBufferIovec->iov_base; messageLength = array[0].iov_len + array[1].iov_len; } else { // Allocate a new iovec if more than one vector allocatedIovec = parcMemory_Allocate(sizeof(struct iovec) * (iovcnt + 1)); array = allocatedIovec; // Header array[0].iov_len = sizeof(struct ether_header); array[0].iov_base = &header; messageLength = array[0].iov_len; // Append message content for (int i = 0; i < iovcnt; i++) { array[i + 1].iov_len = networkBufferIovec[i].iov_len; array[i + 1].iov_base = networkBufferIovec[i].iov_base; messageLength += array[i + 1].iov_len; } } iovcnt++; // increment for the header if (linkData->link.mtu) { if (messageLength > linkData->link.mtu) { if (allocatedIovec != NULL) { parcMemory_Deallocate(&allocatedIovec); } ccnxCodecNetworkBufferIoVec_Release(&iovec); errno = EMSGSIZE; return -1; } } parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "sending message (size=%d)", messageLength); ssize_t writeCount = 0; writeCount = athenaEthernet_Send(linkData->athenaEthernet, array, iovcnt); ccnxCodecNetworkBufferIoVec_Release(&iovec); // Free up any storage allocated for a non-singular iovec if (allocatedIovec != NULL) { parcMemory_Deallocate(&allocatedIovec); array = NULL; } // on error close the link, else return to retry a zero write if (writeCount == -1) { if ((errno == EAGAIN) || (errno == EINTR)) { parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink), "send retry"); linkData->_stats.send_Retry++; return -1; } athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error); parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "send error (%s)", strerror(errno)); linkData->_stats.send_Error++; return -1; } // Short write if (writeCount != messageLength) { linkData->_stats.send_ShortWrite++; parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write"); return -1; } return 0; }