aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/core/metis_Message.c
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/core/metis_Message.c')
-rw-r--r--metis/ccnx/forwarder/metis/core/metis_Message.c992
1 files changed, 992 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/core/metis_Message.c b/metis/ccnx/forwarder/metis/core/metis_Message.c
new file mode 100644
index 00000000..77295476
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/core/metis_Message.c
@@ -0,0 +1,992 @@
+/*
+ * 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.
+ */
+
+/**
+ * The implementation of metisMessage_Slice() copies data, it needs to do this by reference.
+ *
+ */
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include <ccnx/forwarder/metis/core/metis_Message.h>
+#include <ccnx/forwarder/metis/core/metis_StreamBuffer.h>
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+
+#include <ccnx/forwarder/metis/core/metis_Wldr.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Hash.h>
+
+#include <LongBow/runtime.h>
+
+#include <parc/algol/parc_EventBuffer.h>
+
+struct metis_message {
+ MetisLogger *logger;
+
+ MetisTicks receiveTime;
+ unsigned ingressConnectionId;
+
+ PARCEventBuffer *messageBytes;
+ uint8_t *messageHead;
+
+ unsigned refcount;
+
+ struct tlv_skeleton skeleton;
+
+ bool hasKeyId;
+ uint32_t keyIdHash;
+ bool isKeyIdVerified;
+
+ bool hasContentObjectHash;
+ // may be null, even if hasContentObjectHash true due to lazy calculation
+ PARCBuffer *contentObjectHash;
+
+ PARCBuffer *certificate;
+
+ PARCBuffer *publicKey;
+
+ bool hasInterestLifetime;
+ uint64_t interestLifetimeTicks;
+
+ bool hasExpiryTimeTicks;
+ uint64_t expiryTimeTicks;
+
+ bool hasRecommendedCacheTimeTicks;
+ uint64_t recommendedCacheTimeTicks;
+
+ bool hasName;
+ MetisTlvName *name;
+
+ bool hasFragmentPayload;
+
+ MetisMessagePacketType packetType;
+
+
+ bool hasPathLabel;
+ uint64_t pathLabel;
+
+ bool hasWldr;
+ //the following fields are valid only if hasWldr is true
+ uint8_t wldrType;
+ uint16_t wldrLbl; //if wldrType == WLDR_LBL this indicates the message label
+ //if wldrType == WLDR_NOTIFICATION this indicates the expected message label
+ uint16_t wldrLastReceived; //this field is valid only when wldrType == WLDR_NOTIFICATION. In this case,
+ //all the messages between wldrLbl (included) and wldrLastReceived (excluded)
+ //are considered lost
+};
+
+static void
+_setupWldr(MetisMessage *message)
+{
+ uint8_t wldr_header = 0;
+ parcEventBuffer_copyOut(message->messageBytes, &wldr_header, 1);
+ if (wldr_header == WLDR_HEADER) {
+ message->hasWldr = true;
+ parcEventBuffer_Read(message->messageBytes, NULL, 1);
+ parcEventBuffer_Read(message->messageBytes, &(message->wldrType), 1);
+ if (message->wldrType == WLDR_LBL) {
+ parcEventBuffer_Read(message->messageBytes, &(message->wldrLbl), 2);
+ parcEventBuffer_Read(message->messageBytes, NULL, 2);
+ } else if (message->wldrType == WLDR_NOTIFICATION) {
+ parcEventBuffer_Read(message->messageBytes, &(message->wldrLbl), 2);
+ parcEventBuffer_Read(message->messageBytes, &(message->wldrLastReceived), 2);
+ } else {
+ //find a better way to exit (look into longBow)
+ printf("Error, Unknown WLDR Type\n");
+ exit(0);
+ }
+ message->messageHead = parcEventBuffer_Pullup(message->messageBytes, -1);
+ } else {
+ message->hasWldr = false;
+ }
+}
+
+static void
+_setupName(MetisMessage *message)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetName(&message->skeleton);
+ if (extent.offset > 0) {
+ message->hasName = true;
+ message->name = metisTlvName_Create(&message->messageHead[extent.offset], extent.length);
+ } else {
+ message->hasName = false;
+ message->name = NULL;
+ }
+}
+
+static void
+_setupValidationParams(MetisMessage *message)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetKeyId(&message->skeleton);
+ if (extent.offset > 0) {
+ message->hasKeyId = true;
+ message->keyIdHash = parcHash32_Data(&message->messageHead[extent.offset], extent.length);
+ } else {
+ message->hasKeyId = false;
+ message->keyIdHash = 0;
+ }
+ message->isKeyIdVerified = false;
+
+ extent = metisTlvSkeleton_GetCertificate(&message->skeleton);
+ if (extent.offset > 0) {
+ message->certificate = parcBuffer_Flip(parcBuffer_CreateFromArray(&message->messageHead[extent.offset], extent.length));
+ } else {
+ message->certificate = NULL;
+ }
+
+ extent = metisTlvSkeleton_GetPublicKey(&message->skeleton);
+ if (extent.offset > 0) {
+ message->publicKey = parcBuffer_Flip(parcBuffer_CreateFromArray(&message->messageHead[extent.offset], extent.length));
+ } else {
+ message->publicKey = NULL;
+ }
+}
+
+static void
+_setupContentObjectHash(MetisMessage *message)
+{
+ if (metisTlvSkeleton_IsPacketTypeInterest(&message->skeleton)) {
+ MetisTlvExtent extent = metisTlvSkeleton_GetObjectHash(&message->skeleton);
+ // pre-compute it for an interest
+ if (extent.offset > 0) {
+ message->hasContentObjectHash = true;
+ message->contentObjectHash =
+ parcBuffer_Flip(parcBuffer_CreateFromArray(&message->messageHead[extent.offset], extent.length));
+ } else {
+ message->hasContentObjectHash = false;
+ }
+ } else if (metisTlvSkeleton_IsPacketTypeContentObject(&message->skeleton)) {
+ // we will compute this on demand
+ message->hasContentObjectHash = true;
+ message->contentObjectHash = NULL;
+ } else {
+ message->hasContentObjectHash = false;
+ }
+}
+
+static void
+_setupInterestLifetime(MetisMessage *message)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetInterestLifetime(&message->skeleton);
+ message->hasInterestLifetime = false;
+ if (metisTlvSkeleton_IsPacketTypeInterest(&message->skeleton) && extent.offset > 0) {
+ message->hasInterestLifetime = true;
+ uint64_t lifetimeMilliseconds;
+ metisTlv_ExtentToVarInt(message->messageHead, &extent, &lifetimeMilliseconds);
+ message->interestLifetimeTicks = metisForwarder_NanosToTicks(lifetimeMilliseconds * 1000000);
+ }
+}
+
+static void
+_setupPathLabel(MetisMessage *message)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetPathLabel(&message->skeleton);
+ message->hasPathLabel = false;
+ if (metisTlvSkeleton_IsPacketTypeContentObject(&message->skeleton) && extent.offset > 0) {
+ message->hasPathLabel = true;
+ uint64_t pathLabel;
+ metisTlv_ExtentToVarInt(message->messageHead, &extent, &pathLabel);
+ message->pathLabel = pathLabel;
+ }
+}
+
+
+static void
+_setupFragmentPayload(MetisMessage *message)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetFragmentPayload(&message->skeleton);
+ if (extent.offset > 0) {
+ message->hasFragmentPayload = true;
+ } else {
+ message->hasFragmentPayload = true;
+ }
+}
+
+static void
+_setupExpiryTime(MetisMessage *message)
+{
+ MetisTlvExtent expiryTimeExtent = metisTlvSkeleton_GetExpiryTime(&message->skeleton);
+
+ message->hasExpiryTimeTicks = false;
+
+ if (metisTlvSkeleton_IsPacketTypeContentObject(&message->skeleton)) {
+ if (!metisTlvExtent_Equals(&expiryTimeExtent, &metisTlvExtent_NotFound)) {
+ uint64_t expiryTimeUTC = 0;
+ if (metisTlv_ExtentToVarInt(metisTlvSkeleton_GetPacket(&message->skeleton), &expiryTimeExtent, &expiryTimeUTC)) {
+ message->hasExpiryTimeTicks = true;
+
+ // Convert it to ticks that we can use for expiration checking.
+ uint64_t metisWallClockTime = parcClock_GetTime(parcClock_Wallclock());
+ uint64_t currentTimeInTicks = parcClock_GetTime(parcClock_Monotonic());
+
+ message->expiryTimeTicks = expiryTimeUTC - metisWallClockTime + currentTimeInTicks;
+ }
+ }
+ }
+}
+
+static void
+_setupRecommendedCacheTime(MetisMessage *message)
+{
+ MetisTlvExtent cacheTimeExtent = metisTlvSkeleton_GetCacheTimeHeader(&message->skeleton);
+
+ message->hasRecommendedCacheTimeTicks = false;
+
+ if (metisTlvSkeleton_IsPacketTypeContentObject(&message->skeleton)) {
+ if (!metisTlvExtent_Equals(&cacheTimeExtent, &metisTlvExtent_NotFound)) {
+ uint64_t recommendedCacheTime = 0;
+ if (metisTlv_ExtentToVarInt(metisTlvSkeleton_GetPacket(&message->skeleton), &cacheTimeExtent, &recommendedCacheTime)) {
+ message->hasRecommendedCacheTimeTicks = true;
+
+ // Convert it to ticks that we can use for expiration checking.
+ uint64_t metisWallClockTime = parcClock_GetTime(parcClock_Wallclock());
+ uint64_t currentTimeInTicks = parcClock_GetTime(parcClock_Monotonic());
+
+ message->recommendedCacheTimeTicks = recommendedCacheTime - metisWallClockTime + currentTimeInTicks;
+ }
+ }
+ }
+}
+
+/**
+ * Parse the TLV skeleton and setup message pointers
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] message An allocated messae with the message->messagaeHead pointer setup.
+ *
+ * @retval false Error parsing message
+ * @retval true Good parse
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+static bool
+_setupInternalData(MetisMessage *message)
+{
+ // -1 means linearize the whole buffer
+ message->messageHead = parcEventBuffer_Pullup(message->messageBytes, -1);
+ message->packetType = MetisMessagePacketType_Unknown;
+
+ _setupWldr(message);
+ if (message->hasWldr == true && message->wldrType == WLDR_NOTIFICATION) {
+ //this is a WLDR notification message, all the other fields are meaningless because the
+ //packet will be dropped immmedialtly after the retransmissions. For this reason we can
+ //immediatly retrun and avoid the parsing of the message
+ return true;
+ }
+ //now the WLDR header is removed from the packet and the parsing shuould work as usual
+
+ bool goodSkeleton = metisTlvSkeleton_Parse(&message->skeleton, message->messageHead, message->logger);
+
+ if (goodSkeleton) {
+ _setupName(message);
+ _setupValidationParams(message);
+ _setupContentObjectHash(message);
+ _setupInterestLifetime(message);
+ _setupPathLabel(message);
+ _setupFragmentPayload(message);
+ _setupExpiryTime(message);
+ _setupRecommendedCacheTime(message);
+
+ // set the packet type
+ bool requiresName = false;
+ if (metisTlvSkeleton_IsPacketTypeInterest(&message->skeleton)) {
+ message->packetType = MetisMessagePacketType_Interest;
+ requiresName = true;
+ } else if (metisTlvSkeleton_IsPacketTypeContentObject(&message->skeleton)) {
+ message->packetType = MetisMessagePacketType_ContentObject;
+ requiresName = true;
+ } else if (metisTlvSkeleton_IsPacketTypeHopByHopFragment(&message->skeleton)) {
+ message->packetType = MetisMessagePacketType_HopByHopFrag;
+ } else if (metisTlvSkeleton_IsPacketTypeControl(&message->skeleton)) {
+ message->packetType = MetisMessagePacketType_Control;
+ } else if (metisTlvSkeleton_IsPacketTypeInterestReturn(&message->skeleton)) {
+ message->packetType = MetisMessagePacketType_InterestReturn;
+ }
+
+ if (requiresName && !metisMessage_HasName(message)) {
+ goodSkeleton = false;
+ }
+ }
+
+ return goodSkeleton;
+}
+
+MetisMessage *
+metisMessage_Acquire(const MetisMessage *message)
+{
+ MetisMessage *copy = (MetisMessage *) message;
+ copy->refcount++;
+ return copy;
+}
+
+MetisMessage *
+metisMessage_CreateFromParcBuffer(PARCBuffer *buffer, unsigned ingressConnectionId, MetisTicks receiveTime, MetisLogger *logger)
+{
+ MetisMessage *message = parcMemory_AllocateAndClear(sizeof(MetisMessage));
+ assertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessage));
+ message->receiveTime = receiveTime;
+ message->ingressConnectionId = ingressConnectionId;
+ message->messageBytes = parcEventBuffer_Create();
+ message->refcount = 1;
+ message->logger = metisLogger_Acquire(logger);
+
+ // this copies the data
+ int failure = parcEventBuffer_Append(message->messageBytes, parcBuffer_Overlay(buffer, 0), parcBuffer_Remaining(buffer));
+ assertFalse(failure, "Got failure copying data into buffer: (%d) %s", errno, strerror(errno));
+
+ bool goodSkeleton = _setupInternalData(message);
+ if (goodSkeleton) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p created ingress %u",
+ (void *) message, ingressConnectionId);
+ }
+ } else {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Error setting up skeleton for buffer %p ingress %u",
+ (void *) buffer, ingressConnectionId);
+ }
+
+ metisMessage_Release(&message);
+ }
+ return message;
+}
+
+MetisMessage *
+metisMessage_CreateFromArray(const uint8_t *data, size_t dataLength, unsigned ingressConnectionId, MetisTicks receiveTime, MetisLogger *logger)
+{
+ MetisMessage *message = parcMemory_AllocateAndClear(sizeof(MetisMessage));
+ assertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessage));
+ message->receiveTime = receiveTime;
+ message->ingressConnectionId = ingressConnectionId;
+ message->messageBytes = parcEventBuffer_Create();
+ message->refcount = 1;
+ message->logger = metisLogger_Acquire(logger);
+
+ // this copies the data
+ int failure = parcEventBuffer_Append(message->messageBytes, (void *) data, dataLength);
+ assertFalse(failure, "Got failure copying data into PARCEventBuffer: (%d) %s", errno, strerror(errno));
+
+ bool goodSkeleton = _setupInternalData(message);
+ if (goodSkeleton) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p created ingress %u",
+ (void *) message, ingressConnectionId);
+ }
+ } else {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Error setting up skeleton for array %p ingress %u",
+ (void *) data, ingressConnectionId);
+ }
+
+ metisMessage_Release(&message);
+ }
+
+ return message;
+}
+
+MetisMessage *
+metisMessage_ReadFromBuffer(unsigned ingressConnectionId, MetisTicks receiveTime, PARCEventBuffer *input, size_t bytesToRead, MetisLogger *logger)
+{
+ MetisMessage *message = parcMemory_AllocateAndClear(sizeof(MetisMessage));
+ assertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessage));
+ message->receiveTime = receiveTime;
+ message->ingressConnectionId = ingressConnectionId;
+ message->messageBytes = parcEventBuffer_Create();
+ message->refcount = 1;
+ message->logger = metisLogger_Acquire(logger);
+
+ // dequeue into packet buffer. This is a near-zero-copy operation from
+ // one buffer to another. It only copies if the message falls across iovec
+ // boundaries.
+ int bytesRead = parcEventBuffer_ReadIntoBuffer(input, message->messageBytes, bytesToRead);
+ assertTrue(bytesRead == bytesToRead, "Partial read, expected %zu got %d", bytesToRead, bytesRead);
+
+ bool goodSkeleton = _setupInternalData(message);
+ if (goodSkeleton) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p created ingress %u",
+ (void *) message, ingressConnectionId);
+ }
+ } else {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Error setting up skeleton for buffer %p ingress %u",
+ (void *) input, ingressConnectionId);
+ }
+
+ metisMessage_Release(&message);
+ }
+ return message;
+}
+
+MetisMessage *
+metisMessage_CreateFromBuffer(unsigned ingressConnectionId, MetisTicks receiveTime, PARCEventBuffer *input, MetisLogger *logger)
+{
+ assertNotNull(input, "Parameter input must be non-null");
+ MetisMessage *message = parcMemory_AllocateAndClear(sizeof(MetisMessage));
+ assertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessage));
+ message->receiveTime = receiveTime;
+ message->ingressConnectionId = ingressConnectionId;
+ message->messageBytes = input;
+ message->refcount = 1;
+ message->logger = metisLogger_Acquire(logger);
+
+ bool goodSkeleton = _setupInternalData(message);
+ if (goodSkeleton) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p created ingress %u",
+ (void *) message, ingressConnectionId);
+ }
+ } else {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Error setting up skeleton for buffer %p ingress %u",
+ (void *) input, ingressConnectionId);
+ }
+
+ metisMessage_Release(&message);
+ }
+ return message;
+}
+
+void
+metisMessage_Release(MetisMessage **messagePtr)
+{
+ assertNotNull(messagePtr, "Parameter must be non-null double pointer");
+ assertNotNull(*messagePtr, "Parameter must dereference to non-null pointer");
+
+ MetisMessage *message = *messagePtr;
+ assertTrue(message->refcount > 0, "Invalid state: metisMessage_Release called on message with 0 references %p", (void *) message);
+
+ message->refcount--;
+ if (message->refcount == 0) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p destroyed",
+ (void *) message);
+ }
+
+ if (message->contentObjectHash) {
+ parcBuffer_Release(&message->contentObjectHash);
+ }
+
+ if (message->name) {
+ metisTlvName_Release(&message->name);
+ }
+
+ if (message->publicKey) {
+ parcBuffer_Release(&message->publicKey);
+ }
+
+ if (message->certificate) {
+ parcBuffer_Release(&message->certificate);
+ }
+
+ metisLogger_Release(&message->logger);
+ parcEventBuffer_Destroy(&(message->messageBytes));
+ parcMemory_Deallocate((void **) &message);
+ }
+ *messagePtr = NULL;
+}
+
+bool
+metisMessage_Write(PARCEventQueue *parcEventQueue, const MetisMessage *message)
+{
+ assertNotNull(message, "Message parameter must be non-null");
+ assertNotNull(parcEventQueue, "Buffer parameter must be non-null");
+
+ return parcEventQueue_Write(parcEventQueue, message->messageHead, parcEventBuffer_GetLength(message->messageBytes));
+}
+
+bool
+metisMessage_Append(PARCEventBuffer *writeBuffer, const MetisMessage *message)
+{
+ assertNotNull(message, "Message parameter must be non-null");
+ assertNotNull(writeBuffer, "Buffer parameter must be non-null");
+
+ if (message->messageBytes == NULL) {
+ //this is an error that we need to handle (just dropping the packet?)
+ //right now we log the event, drop the packet and return false
+ //should I release the message as well?
+ if (message->logger != NULL && metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p has a null message buffer inside!",
+ (void *) message);
+ }
+ return false;
+ }
+
+ return parcEventBuffer_Append(writeBuffer, message->messageHead, metisMessage_Length(message));
+}
+
+size_t
+metisMessage_Length(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ return parcEventBuffer_GetLength(message->messageBytes);
+}
+
+bool
+metisMessage_HasWldr(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ return message->hasWldr;
+}
+
+unsigned
+metisMessage_GetWldrType(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ assertTrue(message->hasWldr == true, "This message does not contains a WLDR header");
+ return message->wldrType;
+}
+
+unsigned
+metisMessage_GetWldrLabel(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ assertTrue(message->hasWldr == true, "This message does not contains a WLDR header");
+ return message->wldrLbl;
+}
+
+unsigned
+metisMessage_GetWldrLastReceived(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ assertTrue(message->hasWldr == true, "This message does not contains a WLDR header");
+ return message->wldrLastReceived;
+}
+
+static void
+_removeOldWldrHeader(MetisMessage *message)
+{
+ uint8_t wldr_header = 0;
+ parcEventBuffer_copyOut(message->messageBytes, &wldr_header, 1);
+ if (wldr_header == WLDR_HEADER) {
+ parcEventBuffer_Read(message->messageBytes, NULL, WLDR_HEADER_SIZE);
+ }
+}
+
+void
+metisMessage_SetWldrLabel(MetisMessage *message, uint16_t label)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ _removeOldWldrHeader(message);
+ message->hasWldr = true;
+ message->wldrType = WLDR_LBL;
+ message->wldrLbl = label;
+ uint8_t wldr_header[6];
+ wldr_header[0] = WLDR_HEADER;
+ wldr_header[1] = WLDR_LBL;
+ wldr_header[2] = label & 0xFF;
+ wldr_header[3] = (label >> 8UL) & 0xFF;
+ wldr_header[4] = 0;
+ wldr_header[5] = 0;
+ parcEventBuffer_Prepend(message->messageBytes, &wldr_header, sizeof(wldr_header));
+ uint8_t *newMessage = parcEventBuffer_Pullup(message->messageBytes, -1);
+ bool goodSkeleton = metisTlvSkeleton_Parse(&message->skeleton, newMessage + WLDR_HEADER_SIZE, message->logger);
+ if (goodSkeleton) {
+ message->messageHead = newMessage;
+ } else {
+ trapNotImplemented("[metis_Message.c] message parsing after WLDR header insertion failed");
+ }
+}
+
+void
+metisMessage_SetWldrNotification(MetisMessage *message, uint16_t expected, uint16_t lastReceived)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ _removeOldWldrHeader(message);
+ message->hasWldr = true;
+ message->wldrType = WLDR_NOTIFICATION;
+ message->wldrLbl = expected;
+ message->wldrLastReceived = lastReceived;
+ uint8_t wldr_header[6];
+ wldr_header[0] = WLDR_HEADER;
+ wldr_header[1] = WLDR_NOTIFICATION;
+ wldr_header[2] = expected & 0xFF;
+ wldr_header[3] = (expected >> 8U) & 0xFF;
+ wldr_header[4] = lastReceived & 0xFF;
+ wldr_header[5] = (lastReceived >> 8U) & 0xFF;
+ parcEventBuffer_Prepend(message->messageBytes, &wldr_header, sizeof(wldr_header));
+ message->messageHead = parcEventBuffer_Pullup(message->messageBytes, -1);
+}
+
+unsigned
+metisMessage_GetIngressConnectionId(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ return message->ingressConnectionId;
+}
+
+MetisTicks
+metisMessage_GetReceiveTime(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ return message->receiveTime;
+}
+
+bool
+metisMessage_HasHopLimit(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ MetisTlvExtent extent = metisTlvSkeleton_GetHopLimit(&message->skeleton);
+
+ if (extent.offset > 0) {
+ return true;
+ }
+ return false;
+}
+
+uint8_t
+metisMessage_GetHopLimit(const MetisMessage *message)
+{
+ bool hasHopLimit = metisMessage_HasHopLimit(message);
+ assertTrue(hasHopLimit, "Message does not have a HopLimit field");
+
+ MetisTlvExtent extent = metisTlvSkeleton_GetHopLimit(&message->skeleton);
+ uint8_t hopLimit = message->messageHead[extent.offset];
+ return hopLimit;
+}
+
+void
+metisMessage_SetHopLimit(MetisMessage *message, uint8_t hoplimit)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ metisTlvSkeleton_UpdateHopLimit(&message->skeleton, hoplimit);
+}
+
+void
+metisMessage_UpdatePathLabel(MetisMessage *message, uint8_t outFace)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ metisTlvSkeleton_UpdatePathLabel(&message->skeleton, outFace);
+}
+void
+metisMessage_ResetPathLabel(MetisMessage *message)
+{
+ assertNotNull(message, "Parameter must be non-null");
+ metisTlvSkeleton_ResetPathLabel(&message->skeleton);
+}
+
+MetisMessagePacketType
+metisMessage_GetType(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->packetType;
+}
+
+MetisTlvName *
+metisMessage_GetName(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->name;
+}
+
+bool
+metisMessage_GetKeyIdHash(const MetisMessage *message, uint32_t *hashOutput)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ if (message->hasKeyId) {
+ *hashOutput = message->keyIdHash;
+ return true;
+ }
+ return false;
+}
+
+PARCBuffer *
+metisMessage_GetCertificate(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->certificate;
+}
+
+PARCBuffer *
+metisMessage_GetPublicKey(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->publicKey;
+}
+
+bool
+metisMessage_KeyIdEquals(const MetisMessage *a, const MetisMessage *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a->hasKeyId && b->hasKeyId) {
+ MetisTlvExtent ae = metisTlvSkeleton_GetKeyId(&a->skeleton);
+ MetisTlvExtent be = metisTlvSkeleton_GetKeyId(&b->skeleton);
+
+ if (ae.length == be.length) {
+ return memcmp(&a->messageHead[ae.offset], &b->messageHead[be.offset], ae.length) == 0;
+ }
+ }
+ return false;
+}
+
+bool
+metisMessage_ObjectHashEquals(MetisMessage *a, MetisMessage *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a->hasContentObjectHash && b->hasContentObjectHash) {
+ if (a->contentObjectHash == NULL) {
+ PARCCryptoHash *hash = metisTlvSkeleton_ComputeContentObjectHash(&a->skeleton);
+ a->contentObjectHash = parcBuffer_Acquire(parcCryptoHash_GetDigest(hash));
+ parcCryptoHash_Release(&hash);
+ }
+
+ if (b->contentObjectHash == NULL) {
+ PARCCryptoHash *hash = metisTlvSkeleton_ComputeContentObjectHash(&b->skeleton);
+ b->contentObjectHash = parcBuffer_Acquire(parcCryptoHash_GetDigest(hash));
+ parcCryptoHash_Release(&hash);
+ }
+
+ return parcBuffer_Equals(a->contentObjectHash, b->contentObjectHash);
+ }
+
+ return false;
+}
+
+bool
+metisMessage_GetContentObjectHashHash(MetisMessage *message, uint32_t *hashOutput)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ assertNotNull(hashOutput, "Parameter hashOutput must be non-null");
+
+ if (message->hasContentObjectHash) {
+ if (message->contentObjectHash == NULL) {
+ PARCCryptoHash *hash = metisTlvSkeleton_ComputeContentObjectHash(&message->skeleton);
+ message->contentObjectHash = parcBuffer_Acquire(parcCryptoHash_GetDigest(hash));
+ parcCryptoHash_Release(&hash);
+ }
+
+ *hashOutput = (uint32_t) parcBuffer_HashCode(message->contentObjectHash);
+ return true;
+ }
+ return false;
+}
+
+bool
+metisMessage_HasPublicKey(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return (message->publicKey != NULL);
+}
+
+bool
+metisMessage_HasCertificate(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return (message->certificate != NULL);
+}
+
+bool
+metisMessage_HasName(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasName;
+}
+
+bool
+metisMessage_HasKeyId(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasKeyId;
+}
+
+bool
+metisMessage_IsKeyIdVerified(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->isKeyIdVerified;
+}
+
+bool
+metisMessage_HasContentObjectHash(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasContentObjectHash;
+}
+
+CCNxControl *
+metisMessage_CreateControlMessage(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ assertTrue(metisMessage_GetType(message) == MetisMessagePacketType_Control,
+ "Wrong type of message, expected %02X got %02X",
+ MetisMessagePacketType_Control,
+ metisMessage_GetType(message));
+
+ MetisTlvExtent extent = metisTlvSkeleton_GetCPI(&message->skeleton);
+ assertTrue(extent.offset > 0, "Message does not have a CPI TLV field!");
+
+ PARCJSON *json = parcJSON_ParseString((char *) &message->messageHead[ extent.offset ]);
+ CCNxControl *control = ccnxControl_CreateCPIRequest(json);
+ parcJSON_Release(&json);
+ return control;
+}
+
+bool
+metisMessage_HasInterestLifetime(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasInterestLifetime;
+}
+
+uint64_t
+metisMessage_GetInterestLifetimeTicks(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->interestLifetimeTicks;
+}
+
+bool
+metisMessage_HasFragmentPayload(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasFragmentPayload;
+}
+
+size_t
+metisMessage_AppendFragmentPayload(const MetisMessage *message, PARCEventBuffer *buffer)
+{
+ size_t bytesAppended = 0;
+ if (message->hasFragmentPayload) {
+ MetisTlvExtent extent = metisTlvSkeleton_GetFragmentPayload(&message->skeleton);
+ parcEventBuffer_Append(buffer, message->messageHead + extent.offset, extent.length);
+ bytesAppended = extent.length;
+ }
+ return bytesAppended;
+}
+
+const uint8_t *
+metisMessage_FixedHeader(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->messageHead;
+}
+
+MetisMessage *
+metisMessage_Slice(const MetisMessage *original, size_t offset, size_t length, size_t headerLength, const uint8_t header[headerLength])
+{
+ assertNotNull(original, "Parameter original must be non-null");
+ assertTrue(length > 0, "Parameter length must be positive");
+ assertTrue(offset + length <= parcEventBuffer_GetLength(original->messageBytes),
+ "Slice extends beyond end, maximum %zu got %zu",
+ parcEventBuffer_GetLength(original->messageBytes),
+ offset + length);
+
+ MetisMessage *message = parcMemory_AllocateAndClear(sizeof(MetisMessage));
+ assertNotNull(message, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisMessage));
+ message->receiveTime = original->receiveTime;
+ message->ingressConnectionId = original->ingressConnectionId;
+ message->messageBytes = parcEventBuffer_Create();
+ message->refcount = 1;
+ message->logger = metisLogger_Acquire(original->logger);
+
+ if (headerLength > 0) {
+ assertNotNull(header, "Cannot have a positive headerLength and NULL header");
+
+ // this copies the data
+ int failure = parcEventBuffer_Append(message->messageBytes, (void *) header, headerLength);
+ assertFalse(failure, "Got failure adding header data into PARCEventBuffer: (%d) %s", errno, strerror(errno));
+ }
+
+ // this copies the data
+ int failure = parcEventBuffer_Append(message->messageBytes, (uint8_t *) original->messageHead + offset, length);
+ assertFalse(failure, "Got failure adding slice data into PARCEventBuffer: (%d) %s", errno, strerror(errno));
+
+ bool goodSkeleton = _setupInternalData(message);
+ if (goodSkeleton) {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Message %p created slice(%p, %zu, %zu)",
+ (void *) message, (void *) original, offset, length);
+ }
+ } else {
+ if (metisLogger_IsLoggable(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(message->logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Error setting up skeleton for original %p and header %p",
+ (void *) original, (void *) header);
+ }
+
+ metisMessage_Release(&message);
+ }
+
+ return message;
+}
+
+bool
+metisMessage_HasRecommendedCacheTime(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasRecommendedCacheTimeTicks;
+}
+
+uint64_t
+metisMessage_GetRecommendedCacheTimeTicks(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ assertTrue(message->hasRecommendedCacheTimeTicks, "MetisMessage does not have a RecommendedCacheTime. Call metisMessage_HasRecommendedCacheTime() first.");
+ return message->recommendedCacheTimeTicks;
+}
+
+void
+metisMessage_SetRecommendedCacheTimeTicks(MetisMessage *message, uint64_t recommendedCacheTimeTicks)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ message->recommendedCacheTimeTicks = recommendedCacheTimeTicks;
+ message->hasRecommendedCacheTimeTicks = true;
+}
+
+bool
+metisMessage_HasExpiryTime(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ return message->hasExpiryTimeTicks;
+}
+
+uint64_t
+metisMessage_GetExpiryTimeTicks(const MetisMessage *message)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ assertTrue(message->hasExpiryTimeTicks, "MetisMessage does not have an ExpiryTime. Call metisMessage_HasExpiryTime() first.");
+ return message->expiryTimeTicks;
+}
+
+void
+metisMessage_SetExpiryTimeTicks(MetisMessage *message, uint64_t expiryTimeTicks)
+{
+ assertNotNull(message, "Parameter message must be non-null");
+ message->expiryTimeTicks = expiryTimeTicks;
+ message->hasExpiryTimeTicks = true;
+}