aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/tlv
diff options
context:
space:
mode:
Diffstat (limited to 'metis/ccnx/forwarder/metis/tlv')
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_Tlv.c176
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_Tlv.h215
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.c30
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.h89
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvName.c319
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvName.h300
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.c64
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.h65
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvOps.h216
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.c419
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h51
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.c567
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h49
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.c484
-rw-r--r--metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h770
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/.gitignore8
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/CMakeLists.txt19
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_Tlv.c249
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvExtent.c90
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvName.c470
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvNameCodec.c172
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV0.c342
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV1.c518
-rw-r--r--metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSkeleton.c860
24 files changed, 6542 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_Tlv.c b/metis/ccnx/forwarder/metis/tlv/metis_Tlv.c
new file mode 100644
index 00000000..f1afe34e
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_Tlv.c
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+
+#include <LongBow/runtime.h>
+
+#include <stdio.h>
+
+
+#include <parc/algol/parc_Memory.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+
+// a reasonably large enough number that we capture name parsing without
+// needing to re-alloc. Not a big deal if this is wrong, it just means
+// we have to do one pass to count and another pass to fillin, and waste
+// one malloc and free.
+static const size_t _initialLengthForNameExtents = 24;
+
+
+// -----------------------------
+
+size_t
+metisTlv_FixedHeaderLength(void)
+{
+ // at some point this will no longer be true and we will have to refactor
+ return 8;
+}
+
+
+size_t
+metisTlv_TotalHeaderLength(const uint8_t *packet)
+{
+ size_t length = 0;
+ uint8_t version = packet[0];
+ switch (version) {
+ case 0:
+ length = MetisTlvSchemaV0_Ops.totalHeaderLength(packet); // Deprecated
+ break;
+
+ case 1:
+ length = MetisTlvSchemaV1_Ops.totalHeaderLength(packet);
+ break;
+
+ default:
+ break;
+ }
+ return length;
+}
+
+size_t
+metisTlv_TotalPacketLength(const uint8_t *packet)
+{
+ size_t length = 0;
+ uint8_t version = packet[0];
+ switch (version) {
+ case 0:
+ length = MetisTlvSchemaV0_Ops.totalPacketLength(packet); // Deprecated
+ break;
+
+ case 1:
+ length = MetisTlvSchemaV1_Ops.totalPacketLength(packet);
+ break;
+
+ default:
+ break;
+ }
+ return length;
+}
+
+PARCBuffer *
+metisTlv_EncodeControlPlaneInformation(const CCNxControl *cpiControlMessage)
+{
+ PARCBuffer *encoded = NULL;
+ CCNxTlvDictionary_SchemaVersion version = ccnxTlvDictionary_GetSchemaVersion(cpiControlMessage);
+ switch (version) {
+ case CCNxTlvDictionary_SchemaVersion_V0:
+ encoded = MetisTlvSchemaV0_Ops.encodeControlPlaneInformation(cpiControlMessage);
+ break;
+
+ case CCNxTlvDictionary_SchemaVersion_V1:
+ encoded = MetisTlvSchemaV1_Ops.encodeControlPlaneInformation(cpiControlMessage);
+ break;
+
+ default:
+ break;
+ }
+ return encoded;
+}
+
+/**
+ * @function metisTlv_ParseName
+ * @abstract Parse a name into the provided output array, ensuring it does not exceed outputLength
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param outputArray may be NULL to count the number of name elements.
+ * @para outputLength is the maximum number of name segments to parse in to outputArray
+ * @return The number of name elements parsed
+ */
+static size_t
+_metisTlv_ParseName(uint8_t *name, size_t nameLength, MetisTlvExtent *outputArray, size_t outputLength)
+{
+ size_t offset = 0;
+ size_t count = 0;
+ const size_t tl_length = 4;
+ while (offset < nameLength) {
+ MetisTlvType *tlv = (MetisTlvType *) (name + offset);
+ uint16_t v_length = htons(tlv->length);
+
+ if (count < outputLength) {
+ outputArray[count].offset = offset;
+ outputArray[count].length = tl_length + v_length;
+ }
+
+ // skip past the TL and V
+ offset += tl_length + v_length;
+ count++;
+ }
+ return count;
+}
+
+void
+metisTlv_NameSegments(uint8_t *name, size_t nameLength, MetisTlvExtent **outputArrayPtr, size_t *outputLengthPtr)
+{
+ // allocate an array that's kind of big. if name does not fit, we'll need to re-alloc.
+ MetisTlvExtent *output = parcMemory_Allocate(_initialLengthForNameExtents * sizeof(MetisTlvExtent));
+ assertNotNull(output, "parcMemory_Allocate(%zu) returned NULL", _initialLengthForNameExtents * sizeof(MetisTlvExtent));
+
+ size_t actualLength = _metisTlv_ParseName(name, nameLength, output, _initialLengthForNameExtents);
+ if (actualLength > _initialLengthForNameExtents) {
+ // Oops, do over
+ parcMemory_Deallocate((void **) &output);
+ output = parcMemory_Allocate(actualLength * sizeof(MetisTlvExtent));
+ assertNotNull(output, "parcMemory_Allocate(%zu) returned NULL", actualLength * sizeof(MetisTlvExtent));
+ _metisTlv_ParseName(name, nameLength, output, actualLength);
+ }
+
+ *outputArrayPtr = output;
+ *outputLengthPtr = actualLength;
+}
+
+bool
+metisTlv_ExtentToVarInt(const uint8_t *packet, const MetisTlvExtent *extent, uint64_t *output)
+{
+ assertNotNull(packet, "Parameter buffer must be non-null");
+ assertNotNull(extent, "Parameter output must be non-null");
+
+ bool success = false;
+ if (extent->length >= 1 && extent->length <= 8) {
+ uint64_t value = 0;
+ for (int i = 0; i < extent->length; i++) {
+ value = value << 8 | packet[extent->offset + i];
+ }
+ *output = value;
+ success = true;
+ }
+ return success;
+}
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_Tlv.h b/metis/ccnx/forwarder/metis/tlv/metis_Tlv.h
new file mode 100644
index 00000000..1cd04631
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_Tlv.h
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_Tlv.h
+ * @brief The generic Tlv utilities.
+ *
+ * Provides generaic Tlv utilities, particularly for packets that have not been
+ * decoded in to their skeleton. Once packets are in the skeleton format, one should
+ * use functions in metis_TlvSkeleton.
+ *
+ */
+
+#ifndef Metis_metis_Tlv_h
+#define Metis_metis_Tlv_h
+
+#include <stdlib.h>
+#include <parc/security/parc_CryptoHash.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+#include <parc/algol/parc_ByteArray.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvExtent.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h>
+
+/**
+ * The TLV format
+ *
+ * Mapping of the TLV format to a structure. Remember to use
+ * <code>htons()</code> or <code>ntohs()</code> on the values
+ * if working in host byte order.
+ *
+ * The 'length' is the length of the 'value', it does not include
+ * the T and L of the TLV. A length of "0" is acceptable.
+ *
+ * @param type is in network byte order
+ * @param length is in network byte order
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // packet received from network
+ * size_t offset = // where you are in parsing the packet
+ *
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * }
+ * @endcode
+ */
+typedef struct __attribute__ ((packed)) metis_tlv_type {
+ uint16_t type;
+ uint16_t length;
+} MetisTlvType;
+
+
+/**
+ * Returns the length of the fixed header
+ *
+ * This is assumed to be the same for all versions. At some point this will no longer be true
+ * and metis will need to be re-factored. This function works for V0 and V1 packets.
+ *
+ * @return positive The bytes of the fixed header.
+ *
+ * Example:
+ * @code
+ * {
+ * if (parcEventBuffer_GetLength(input) >= metisTlv_FixedHeaderLength()) {
+ * uint8_t fixedheader[metisTlv_FixedHeaderLength()];
+ * read(fd, &fixedheader, metisTlv_FixedHeaderLength());
+ * // process fixed header
+ * }
+ * }
+ * @endcode
+ */
+size_t metisTlv_FixedHeaderLength(void);
+
+/**
+ * Returns the length of all headers, which is the offset where the CCNx message starts
+ *
+ * Includes both the fixed header and the per hop headers.
+ * Will return "0" for unknown packet version
+ *
+ * @param [in] packet Pointer to bytes 0 of the fixed header
+ *
+ * @retval positive The total header length (minimum 8)
+ * @retval 0 Unsupported packet version or other error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t metisTlv_TotalHeaderLength(const uint8_t *packet);
+
+/**
+ * The total packet length based on the fixed header
+ *
+ * Parses the fixed header and returns the total packet length
+ * Will return "0" for unknown packet version
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval number Total packet length
+ *
+ * Example:
+ * @code
+ * {
+ * // in an example packet parser (does not handle any error conditions or partial reads)
+ * if (parcEventBuffer_GetLength(input) >= metisTlv_FixedHeaderLength()) {
+ * uint8_t fixedheader[metisTlv_FixedHeaderLength()];
+ * read(fd, &fixedheader, metisTlv_FixedHeaderLength());
+ *
+ * size_t remainingBytes = metisTlv_TotalPacketLength(&fixedheader) - metisTlv_FixedHeaderLength();
+ * if (parcEventBuffer_GetLength(input) >= remainingBytes) {
+ * uint8_t *packet = parcMemory_Allocate(metisTlv_TotalPacketLength(&fixedheader));
+ * read(fd, packet + metisTlv_FixedHeaderLength(), remainingBytes);
+ * }
+ * }
+ * }
+ * @endcode
+ */
+size_t metisTlv_TotalPacketLength(const uint8_t *packet);
+
+/**
+ * @function metisTlv_NameSegments
+ * @abstract Treats the input as a TLV-encoded name, generating an output array of name segment extents
+ * @discussion
+ * The outputArray is an ordered list of extents, giving the offset and length of each path segment.
+ * The lengths include the path segment type, length, and value.
+ *
+ * Example: Lets represent the name as a set of of tuples of T, L, and V:
+ * (t=1, len=4, value="help"), (t=1, len=2, value="me"), (t=7, len=10, value="understand")
+ * This name as 3 path segments The first segment is of type 1, length 4, and value "help".
+ * The total length of that segment is 4 + 4 = 8, including the T and L.
+ * The outputArray would be { {.offset=0, .length=8}, {.offset=8, .length=6}, {.offset=14, .length=14} }.
+ * The outputLenght would be 3, because there are 3 elements in the array.
+ *
+ * @param name is a TLV-encoded name, not including the container name TLV
+ * @param nameLength is the length of the name
+ * @param outputArrayPtr is an allocated array of ordered extents, must be freed with <code>parcMemory_Deallocate()</code>
+ * @param outputLengthPtr is the number of elements allocated in the array.
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvExtent *extentArray;
+ * size_t arrayLength;
+ * uint8_t encodedName[] = "\x00\x01\x00\x05" "apple" "\x00\x01\x00\x03" "pie";
+ * metisTlv_NameSegments(encodedName, sizeof(encodedName), &extentArray, &arrayLength);
+ * // arrrayLength = 2
+ * // extentArray[1].offset = 4
+ * // extentArray[1].length = 5
+ * // etc.
+ * parcMemory_Deallocate(&extentArray);
+ * }
+ * @endcode
+ */
+void metisTlv_NameSegments(uint8_t *name, size_t nameLength, MetisTlvExtent **outputArrayPtr, size_t *outputLengthPtr);
+
+/**
+ * Given a CCNxControl packet, encode it in the proper schema
+ *
+ * Based on the dictionary schema version, will encode the control packet with the
+ * correct encoder.
+ *
+ * @param [in] cpiControlMessage A an allocated control message
+ *
+ * @retval non-null An allocation wire format buffer
+ * @retval null An error (likely unsupported schema version)
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCBuffer *metisTlv_EncodeControlPlaneInformation(const CCNxControl *cpiControlMessage);
+
+/**
+ * Parse an extent as a VarInt
+ *
+ * The extent must be 1 to 8 bytes.
+ *
+ * @param [in] packet The packet memory pointer
+ * @param [in] extent The byte extent of the varint buffer
+ * @param [out] output The VarInt value
+ *
+ * @retval true The buffer was correctly parsed
+ * @retval false The buffer was not parsed (likely extent length is 0 or greater than 8)
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t packet[] = { 0x00, 0x03, 0x00, 0x03, 0xa0, 0xa1, 0xa3 };
+ * MetisTlvExtent extent = { .offset = 4, .length = 3 };
+ * uint64_t output;
+ * metisTlv_ExtentToVarInt(packet, &extent, &output);
+ * // output = 0xa0a1a3
+ * }
+ * @endcode
+ */
+bool metisTlv_ExtentToVarInt(const uint8_t *packet, const MetisTlvExtent *extent, uint64_t *output);
+
+#endif // Metis_metis_Tlv_h
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.c
new file mode 100644
index 00000000..6529b710
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.c
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include "metis_TlvExtent.h"
+
+const MetisTlvExtent metisTlvExtent_NotFound = { 0x0, 0x0 };
+
+bool
+metisTlvExtent_Equals(const MetisTlvExtent *a, const MetisTlvExtent *b)
+{
+ if (a->offset == b->offset && a->length == b->length) {
+ return true;
+ }
+ return false;
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.h
new file mode 100644
index 00000000..056ad34d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvExtent.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvExtent.h
+ * @brief Defines the extent structure used to map a TLV packet
+ *
+ * In MetisTlvSkeleton, all the pertinent fields used by Metis are stored by their extent range in the
+ * received packet buffer. An extent is (offset, length).
+ *
+ */
+#ifndef Metis_metis_tlv_Extent_h
+#define Metis_metis_tlv_Extent_h
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * Stores the location of a field within a buffer.
+ * The values are usually in host byte order.
+ */
+typedef struct metis_tlv_extent {
+ uint16_t offset;
+ uint16_t length;
+} MetisTlvExtent;
+
+/**
+ * Used to detect a "not found" or "not present" condition.
+ * Equal to { 0x0, 0x0 }, which is an invalid extent value.
+ */
+extern const MetisTlvExtent metisTlvExtent_NotFound;
+
+
+/**
+ * Determine if two MetisTlvExtent instances are equal.
+ *
+ * The following equivalence relations on non-null `MetisTlvExtent` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `MetisTlvExtent_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `metisTlvExtent_Equals(x, y)` must return true if and only if
+ * `metisTlvExtent_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `metisTlvExtent_Equals(x, y)` returns true and
+ * `metisTlvExtent_Equals(y, z)` returns true,
+ * then `metisTlvExtent_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `metisTlvExtent_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `metisTlvExtent_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `MetisTlvExtent` instance.
+ * @param b A pointer to a `MetisTlvExtent` instance.
+ * @return true if the two `MetisTlvExtent` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvExtent *a = { .offset = 5, .length = 7 };
+ * MetisTlvExtent *b = { .offset = 5, .length = 8 };
+ *
+ * if (metisTlvExtent_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false (this is the block executed)
+ * }
+ * }
+ * @endcode
+ */
+bool metisTlvExtent_Equals(const MetisTlvExtent *a, const MetisTlvExtent *b);
+#endif // Metis_metis_tlv_Extent_h
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvName.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvName.c
new file mode 100644
index 00000000..852978c5
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvName.c
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+
+/**
+ * A name built around the TLV representation.
+ *
+ * A common operation is to get a sub-string of the name, specifically prefixes. Use
+ * metisTlvName_Slice() for that.
+ *
+ * To be efficient about Slice(), the reference counters are pointers, so every allocated
+ * copy shares the reference counter and all the allocated memory. Each Slice() will do
+ * one malloc for the new shell and do a shallow memcpy of the struct. Destroy will always
+ * free the shell, but will only free the guts when the shared reference count goes to zero.
+ *
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_BufferComposer.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvName.h>
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvNameCodec.h>
+
+#include <LongBow/runtime.h>
+
+struct metis_tlv_name {
+ uint8_t *memory;
+ size_t memoryLength;
+
+ // the refcount is shared between all copies
+ unsigned *refCountPtr;
+
+ // the memory extents of each path segment
+ MetisTlvExtent *segmentArray;
+ size_t segmentArrayLength;
+
+ // hashes of the name through different prefix lengths
+ // It is allocated out to the limit (same assegmentArrayLength),
+ // but only computed so far through segmentCumulativeHashArrayLength
+ // This is to avoid computing the hash over unnecessary suffix segments.
+ size_t segmentCumulativeHashArrayLimit;
+
+ // the cumulative hash array length is shared between all copies, so if
+ // one copy extends the array, all copies see it
+ size_t *segmentCumulativeHashArrayLengthPtr;
+ uint32_t *segmentCumulativeHashArray;
+};
+
+// =====================================================
+
+static unsigned
+_getRefCount(const MetisTlvName *name)
+{
+ return *name->refCountPtr;
+}
+
+static void
+_incrementRefCount(MetisTlvName *name)
+{
+ assertTrue(*name->refCountPtr > 0, "Illegal State: Trying to increment a 0 refcount!");
+ (*name->refCountPtr)++;
+}
+
+static void
+_decrementRefCount(MetisTlvName *name)
+{
+ assertTrue(*name->refCountPtr > 0, "Illegal State: Trying to decrement a 0 refcount!");
+ (*name->refCountPtr)--;
+}
+
+// ============================================================================
+
+/**
+ * Common parts of setting up a MetisTlvName after the backing memory has been allocated and copied in to.
+ *
+ * PRECONDITIONS: name->memory and name->memoryLength set
+ */
+static void
+_setup(MetisTlvName *name)
+{
+ name->refCountPtr = parcMemory_Allocate(sizeof(unsigned));
+ assertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", sizeof(unsigned));
+ *name->refCountPtr = 1;
+
+ metisTlv_NameSegments(name->memory, name->memoryLength, &name->segmentArray, &name->segmentArrayLength);
+
+ name->segmentCumulativeHashArray = parcMemory_Allocate(name->segmentArrayLength * sizeof(uint32_t));
+ assertNotNull(name->segmentCumulativeHashArray, "parcMemory_Allocate(%zu) returned NULL", name->segmentArrayLength * sizeof(uint32_t));
+
+ name->segmentCumulativeHashArrayLengthPtr = parcMemory_Allocate(sizeof(size_t));
+ assertNotNull(name->segmentCumulativeHashArrayLengthPtr, "parcMemory_Allocate(%zu) returned NULL", sizeof(size_t));
+
+ *name->segmentCumulativeHashArrayLengthPtr = 1;
+ name->segmentCumulativeHashArrayLimit = name->segmentArrayLength;
+
+
+ if (name->segmentArrayLength > 0) {
+ // always hash the 1st name component. This is needed as the initial case
+ // to do the cumulative hashes in metisTlvName_HashCode
+ name->segmentCumulativeHashArray[0] = parcHash32_Data(&name->memory[name->segmentArray[0].offset], name->segmentArray[0].length);
+ }
+}
+
+MetisTlvName *
+metisTlvName_Create(const uint8_t *memory, size_t memoryLength)
+{
+ MetisTlvName *name = parcMemory_AllocateAndClear(sizeof(MetisTlvName));
+ assertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisTlvName));
+
+ name->memory = parcMemory_Allocate(memoryLength);
+ assertNotNull(name->memory, "parcMemory_Allocate(%zu) returned NULL", memoryLength);
+
+ memcpy(name->memory, memory, memoryLength);
+ name->memoryLength = memoryLength;
+
+ _setup(name);
+
+ return name;
+}
+
+MetisTlvName *
+metisTlvName_CreateFromCCNxName(const CCNxName *ccnxName)
+{
+ // to avoid reallocs, calculate the exact size we need
+ size_t memoryLength = 0;
+ for (size_t i = 0; i < ccnxName_GetSegmentCount(ccnxName); i++) {
+ CCNxNameSegment *segment = ccnxName_GetSegment(ccnxName, i);
+ memoryLength += 4 + ccnxNameSegment_Length(segment);
+ }
+
+ MetisTlvName *name = parcMemory_AllocateAndClear(sizeof(MetisTlvName));
+ assertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisTlvName));
+
+ name->memoryLength = memoryLength;
+ name->memory = parcMemory_Allocate(memoryLength);
+ assertNotNull(name->memory, "parcMemory_Allocate(%zu) returned NULL", memoryLength);
+
+ uint8_t *p = name->memory;
+ uint8_t *end = p + memoryLength;
+ for (size_t i = 0; i < ccnxName_GetSegmentCount(ccnxName); i++) {
+ CCNxNameSegment *segment = ccnxName_GetSegment(ccnxName, i);
+ uint16_t type = ccnxNameSegment_GetType(segment);
+ uint16_t length = ccnxNameSegment_Length(segment);
+
+ *(uint16_t *) p = htons(type);
+ p += 2;
+
+ *(uint16_t *) p = htons(length);
+ p += 2;
+
+ if (length >0) {
+ PARCBuffer *buffer = ccnxNameSegment_GetValue(segment);
+ uint8_t *overlay = parcBuffer_Overlay(buffer, 0);
+ memcpy(p, overlay, length);
+
+ p += length;
+ }
+
+ // sanity check
+ assertTrue(p <= end, "Wrote past the end of buffer, now at %p end at %p", p, end);
+ }
+
+ _setup(name);
+ return name;
+}
+
+void
+metisTlvName_Release(MetisTlvName **namePtr)
+{
+ assertNotNull(namePtr, "Parameter must be non-null double pointer");
+ assertNotNull(*namePtr, "Parameter must dereference to non-null pointer");
+
+ MetisTlvName *name = *namePtr;
+ _decrementRefCount(name);
+ if (_getRefCount(name) == 0) {
+ parcMemory_Deallocate((void **) &(name->refCountPtr));
+ parcMemory_Deallocate((void **) &(name->segmentArray));
+ parcMemory_Deallocate((void **) &(name->segmentCumulativeHashArray));
+ parcMemory_Deallocate((void **) &(name->segmentCumulativeHashArrayLengthPtr));
+ parcMemory_Deallocate((void **) &(name->memory));
+ }
+ parcMemory_Deallocate((void **) &name);
+ *namePtr = NULL;
+}
+
+MetisTlvName *
+metisTlvName_Acquire(const MetisTlvName *original)
+{
+ return metisTlvName_Slice(original, UINT_MAX);
+}
+
+MetisTlvName *
+metisTlvName_Slice(const MetisTlvName *original, size_t segmentCount)
+{
+ assertNotNull(original, "Parameter must be non-null");
+ MetisTlvName *copy = parcMemory_AllocateAndClear(sizeof(MetisTlvName));
+ assertNotNull(copy, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisTlvName));
+
+ memcpy(copy, original, sizeof(MetisTlvName));
+ _incrementRefCount(copy);
+
+ copy->segmentArrayLength = (copy->segmentArrayLength < segmentCount) ? copy->segmentArrayLength : segmentCount;
+
+ // for equality to work, we need to shorten the MemoryLength to the amount
+ // actually used by the number of segments.
+ size_t startOfLastSegment = copy->segmentArray[ copy->segmentArrayLength - 1 ].offset;
+ size_t lengthOfLastSegment = copy->segmentArray[ copy->segmentArrayLength - 1 ].length;
+
+ copy->memoryLength = startOfLastSegment + lengthOfLastSegment;
+
+ return copy;
+}
+
+uint32_t
+metisTlvName_HashCode(const MetisTlvName *name)
+{
+ if ((name == NULL) || (name->segmentArrayLength == 0)) {
+ return 0;
+ }
+
+ size_t lastSegment = name->segmentArrayLength - 1;
+
+ if (lastSegment >= *name->segmentCumulativeHashArrayLengthPtr) {
+ // we have not yet computed this, so lets do it now!
+ // Note that we go up to and including lastSegment in the for loop. lastSegment is not a "length", it is
+ // the actual array index.
+ for (size_t i = (*name->segmentCumulativeHashArrayLengthPtr); i <= lastSegment; i++) {
+ // the -1 is ok in the 3rd argument, because segmentCumulativeHashArrayLength always has length at least 1
+ // if there are any name components to hash.
+ name->segmentCumulativeHashArray[i] = parcHash32_Data_Cumulative(&name->memory[ name->segmentArray[i].offset ],
+ name->segmentArray[i].length,
+ name->segmentCumulativeHashArray[i - 1]);
+ }
+ *name->segmentCumulativeHashArrayLengthPtr = lastSegment + 1;
+ }
+
+ return name->segmentCumulativeHashArray[lastSegment];
+}
+
+bool
+metisTlvName_Equals(const MetisTlvName *a, const MetisTlvName *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a->memoryLength == b->memoryLength) {
+ return (memcmp(a->memory, b->memory, a->memoryLength) == 0);
+ }
+ return false;
+}
+
+int
+metisTlvName_Compare(const MetisTlvName *a, const MetisTlvName *b)
+{
+ if (a == NULL && b == NULL) {
+ return 0;
+ }
+ if (a == NULL) {
+ return -1;
+ }
+ if (b == NULL) {
+ return +1;
+ }
+
+ if (a->memoryLength < b->memoryLength) {
+ return -1;
+ }
+
+ if (a->memoryLength > b->memoryLength) {
+ return +1;
+ }
+
+ return memcmp(a->memory, b->memory, a->memoryLength);
+}
+
+bool
+metisTlvName_StartsWith(const MetisTlvName *name, const MetisTlvName *prefix)
+{
+ assertNotNull(name, "Parameter name must be non-null");
+ assertNotNull(prefix, "Parameter prefix must be non-null");
+
+ if (prefix->memoryLength <= name->memoryLength) {
+ return (memcmp(prefix->memory, name->memory, prefix->memoryLength) == 0);
+ }
+
+ return false;
+}
+
+size_t
+metisTlvName_SegmentCount(const MetisTlvName *name)
+{
+ assertNotNull(name, "Parameter name must be non-null");
+ return name->segmentArrayLength;
+}
+
+CCNxName *
+metisTlvName_ToCCNxName(const MetisTlvName *name)
+{
+ return metisTlvNameCodec_Decode(name->memory, 0, name->memoryLength);
+}
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvName.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvName.h
new file mode 100644
index 00000000..b63f4273
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvName.h
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvName.h
+ * @brief Representation of the name
+ *
+ * It is stored as an array of TLV extents, one for each name segment. This allows longest matching
+ * prefix comparisons as well as equality comparisons.
+ *
+ */
+
+#ifndef Metis_metis_tlv_Name_h
+#define Metis_metis_tlv_Name_h
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <ccnx/common/ccnx_Name.h>
+
+struct metis_tlv_name;
+typedef struct metis_tlv_name MetisTlvName;
+
+/**
+ * Creates a name from packet memory
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] memory A pointer to the beginning of the Name TLV "value".
+ * @param [in] The length of the "value"
+ *
+ * @retval non-null An allocated MetisTlvName
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t encodedName[] = {0x00, 0x01, 0x00, 0x05, 'a', 'p', 'p', 'l', 'e', 0x00, 0x01, 0x00, 0x03, 'p', 'i', 'e'};
+ * MetisTlvName *name = metisTlvName_Create(encodedName, sizeof(encodedName));
+ * metisTlvName_Release(&name);
+ * }
+ * @endcode
+ */
+MetisTlvName *metisTlvName_Create(const uint8_t *memory, size_t length);
+
+/**
+ * Creates a Metis-sytle name from a CCNxName
+ *
+ * Converts a CCNxName to a Metis Name. The Metis name has its own backing memory,
+ * so it is independent of the CCNxName.
+ *
+ * @param [in] ccnxName An allocated CCNxName
+ *
+ * @retval non-null An allocated MetisTlvName
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+MetisTlvName *metisTlvName_CreateFromCCNxName(const CCNxName *ccnxName);
+
+/**
+ * Releases one reference count, and frees memory after last reference
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in,out] namePtr pointer to the name to free, *namePtr will be NULL'd
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t encodedName[] = {0x00, 0x01, 0x00, 0x05, 'a', 'p', 'p', 'l', 'e', 0x00, 0x01, 0x00, 0x03, 'p', 'i', 'e'};
+ * MetisTlvName *name = metisTlvName_Create(encodedName, sizeof(encodedName));
+ * metisTlvName_Release(&name);
+ * }
+ * @endcode
+ */
+void metisTlvName_Release(MetisTlvName **namePtr);
+
+/**
+ * Acquires a reference to the name
+ *
+ * The MetisTlvName wrapper is allocated but the underlying name memory is shared.
+ * The returned pointer will not be the same as the original.
+ *
+ * @param [in] original The name to acquire a reference to
+ *
+ * @retval non-null A reference counted copy
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t encodedName[] = {0x00, 0x01, 0x00, 0x05, 'a', 'p', 'p', 'l', 'e', 0x00, 0x01, 0x00, 0x03, 'p', 'i', 'e'};
+ * MetisTlvName *name = metisTlvName_Create(encodedName, sizeof(encodedName));
+ * MetisTlvName *copy = metisTlvName_Acquire(name);
+ * metisTlvName_Release(&name);
+ * metisTlvName_Release(&copy);
+ * }
+ * @endcode
+ */
+MetisTlvName *metisTlvName_Acquire(const MetisTlvName *original);
+
+/**
+ * Acquire a reference to the first name, but only use first 'segmentCount' name segments
+ *
+ * A reference to the underlying name segments is increased but a new MetisTlvName wrapper is
+ * put around them. This wrapper will only have 'segmentCount' name segments -- any name segments
+ * after that are ignored.
+ *
+ * If segmentCount is longer than the name (e.g. UINT_MAX), it will be the same as the orignal
+ * name, but have a different wrapper. You sould probalby use metisTlvName_Acquire() in this case,
+ * as it will avoid re-calculating the name's hash.
+ *
+ * This is a reference counted way to shorten a name, such as to store it as a shorter FIB entry.
+ *
+ * @param [in] original The name to acquire and slice
+ * @param [in] segmentCount The number of segments, may be longer than the name
+ *
+ * @retval non-null A new slice
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t encodedName[] = {0x00, 0x01, 0x00, 0x05, 'a', 'p', 'p', 'l', 'e', 0x00, 0x01, 0x00, 0x03, 'p', 'i', 'e'};
+ * MetisTlvName *name = metisTlvName_Create(encodedName, sizeof(encodedName));
+ * MetisTlvName *slice = metisTlvName_Slice(name, 1);
+ * // slice is only lci:/apple
+ * metisTlvName_Release(&name);
+ * metisTlvName_Release(&slice);
+ * }
+ * @endcode
+ */
+MetisTlvName *metisTlvName_Slice(const MetisTlvName *original, size_t segmentCount);
+
+/**
+ * A hash value for use in hash tables
+ *
+ * Will only be calculated once, then cached inside the MetisTlvName.
+ *
+ * @param [in] name The name to hash
+ *
+ * @retval number A hash value for use in hash tables
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+uint32_t metisTlvName_HashCode(const MetisTlvName *name);
+
+/**
+ * Determine if two MetisTlvName instances are equal.
+ *
+ * Two MetisTlvName instances are equal if, and only if,
+ * both objects have a name and they are equal.
+ *
+ * The following equivalence relations on non-null `MetisTlvName` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `MetisTlvName_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `metisTlvName_Equals(x, y)` must return true if and only if
+ * `metisTlvName_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `metisTlvName_Equals(x, y)` returns true and
+ * `metisTlvName_Equals(y, z)` returns true,
+ * then `metisTlvName_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `metisTlvName_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `metisTlvName_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `MetisTlvName` instance.
+ * @param b A pointer to a `MetisTlvName` instance.
+ * @return true if the two `MetisTlvName` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvName *a = metisTlvName_Create();
+ * MetisTlvName *b = metisTlvName_Create();
+ *
+ * if (metisTlvName_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool metisTlvName_Equals(const MetisTlvName *a, const MetisTlvName *b);
+
+/**
+ * Compares two names and returns their ordering
+ *
+ * If |A| < |B| or |A|=|B| & A < B, return -1
+ * If A = B, return 0
+ * If |A| > |B| or |A|=|B| & A > B, return +1
+ *
+ * @param [in] a The first name
+ * @param [in] b The second name
+ *
+ * @retval negative If |A| < |B| or |A|=|B| & A < B
+ * @retval zero If A = B, return 0
+ * @retval positive If |A| > |B| or |A|=|B| & A > B
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+int metisTlvName_Compare(const MetisTlvName *a, const MetisTlvName *b);
+
+/**
+ * @function metsName_StartsWith
+ * @abstract Tests if name starts with prefix
+ * @discussion
+ * Byte-by-byte prefix comparison
+ *
+ * @return True if the name is equal to or begins with prefix
+ */
+
+/**
+ * Determines if name begins with prefix
+ *
+ * Returns true if the given name begins or equals the given prefix.
+ *
+ * @param [in] name The name to test (must be non-null)
+ * @param [in] prefix The prefix to check the name against (must be non-null)
+ *
+ * @retval true name is equal to or prefixed by prefix
+ * @retval false prefix is unreleated to name
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvName_StartsWith(const MetisTlvName *name, const MetisTlvName *prefix);
+
+/**
+ * The number of name segments in the name
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] name An allocated MetisTlvName
+ *
+ * @retval number The number of name segments
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t encodedName[] = {0x00, 0x01, 0x00, 0x05, 'a', 'p', 'p', 'l', 'e', 0x00, 0x01, 0x00, 0x03, 'p', 'i', 'e'};
+ * MetisTlvName *name = metisTlvName_Create(encodedName, sizeof(encodedName));
+ * size_t count = metisTlvName_SegmentCount(name);
+ * // count = 2
+ * metisTlvName_Release(&name);
+ * }
+ * @endcode
+ */
+size_t metisTlvName_SegmentCount(const MetisTlvName *name);
+
+/**
+ * Converts a MetisTlvName to a CCNxName
+ *
+ * The new name will use its own memory unrelated to the MetisTlvName.
+ * You must release the reference to the CCNxName when done with it.
+ *
+ * @param [in] name An allocated MetisTlvName
+ *
+ * @retval non-null An allocated CCNxName
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxName *metisTlvName_ToCCNxName(const MetisTlvName *name);
+#endif // Metis_metis_tlv_Name_h
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.c
new file mode 100644
index 00000000..f3847e6a
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.c
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include <arpa/inet.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <LongBow/runtime.h>
+
+#include "metis_TlvNameCodec.h"
+#include "metis_Tlv.h"
+
+CCNxName *
+metisTlvNameCodec_Decode(uint8_t *buffer, size_t offset, size_t end)
+{
+ assertNotNull(buffer, "Parameter buffer must be non-null");
+ assertTrue(end >= offset, "Buffer must be at least 4 bytes");
+
+ CCNxName *ccnxName = ccnxName_Create();
+
+ while (offset < end) {
+ trapIllegalValueIf(end < offset + 4, "Buffer must be at least 4 bytes")
+ {
+ ccnxName_Release(&ccnxName);
+ }
+
+ MetisTlvType *tlv = (MetisTlvType *) (buffer + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t length = htons(tlv->length);
+
+ offset += sizeof(MetisTlvType);
+
+ trapIllegalValueIf(offset + length > end, "name component extends beyond end of name")
+ {
+ ccnxName_Release(&ccnxName);
+ }
+
+ PARCBuffer *nameValue = parcBuffer_Wrap(&buffer[offset], length, 0, length);
+ CCNxNameSegment *segment = ccnxNameSegment_CreateTypeValue(type, nameValue);
+ parcBuffer_Release(&nameValue);
+
+ ccnxName_Append(ccnxName, segment);
+ ccnxNameSegment_Release(&segment);
+
+ offset += length;
+ }
+
+ return ccnxName;
+}
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.h
new file mode 100644
index 00000000..c047f50d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvNameCodec.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file tlv_NameCodec.h
+ * @brief Encode/Decode a Name tlv
+ *
+ * Encodes a CCNxName to a Name TLV container plus one NameComponent TLV container
+ * per name segment.
+ *
+ * Decodes a buffer as a Name TLV that contains one NameComponent TLV per name segment.
+ *
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+#ifndef Metis_metis_tlv_NameCodec_h
+#define Metis_metis_tlv_NameCodec_h
+
+#include <ccnx/common/ccnx_Name.h>
+
+/**
+ * Decodes a byte array as the segments of a Name.
+ *
+ * The (buffer + offset) should point to the beginning of the first NameSegment.
+ *
+ * The length (end - offset) may be 0 length, in which case an empty name is returned.
+ * Otherwise, it must be at least 4 bytes long.
+ *
+ * @param [in] buffer The byte array
+ * @param [in] offset The starting location of the Name
+ * @param [in] end The location just past the end of the name
+ *
+ * @return non-null the Name
+ *
+ * Example:
+ * @code
+ * {
+ * // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ * |-- type --|-- length --|-- type --|-- length --| ----- value -----|
+ * uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x04, 'a', 'b', 'c', 'd', 0xFF };
+ *
+ * // skip the two 0xFF bytes
+ * // name = "lci:/%02=abcd"
+ * CCNxName * name = tlvName_Decode(buffer, 5, 13);
+ * }
+ * @endcode
+ *
+ */
+CCNxName *metisTlvNameCodec_Decode(uint8_t *buffer, size_t offset, size_t end);
+#endif // Metis_metis_tlv_NameCodec_h
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvOps.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvOps.h
new file mode 100644
index 00000000..35dae490
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvOps.h
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvOps.h
+ * @brief The API for TLV schemas
+ *
+ * Each TLV schema must implement this API
+ *
+ */
+
+#ifndef Metis_metis_TlvOps_h
+#define Metis_metis_TlvOps_h
+
+#include <stdbool.h>
+#include <parc/algol/parc_Buffer.h>
+#include <parc/security/parc_CryptoHash.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+typedef struct metis_tlv_ops {
+ /**
+ * Fills in the packet TLV skeleton
+ *
+ * The skeleton must have been initialized with the correct parser and packet buffer.
+ *
+ * @param [in] skeleton An allocated MetisTlvSkeleton to fill in
+ *
+ * @retval true Good parse
+ * @retval false Error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*parse)(MetisTlvSkeleton *skeleton);
+
+ /**
+ * Computes the ContentObjectHash over a packet
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of the fixed header
+ *
+ * @return non-null The sha256 hash
+ * @return null An error (or not a content object)
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+ PARCCryptoHash * (*computeContentObjectHash)(const uint8_t *packet);
+
+ /**
+ * @function metisTlv_EncodeCPI
+ * @abstract Encodes a CPI control message in TLV format
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated message, must call <code>metisMessage_Destroy()</code> on it.
+ */
+ PARCBuffer *(*encodeControlPlaneInformation)(const CCNxControl *cpiControlMessage);
+
+ /**
+ * Returns the total header length based on the Fixed Header
+ *
+ * The length may be 0 for an unsupported FixedHeader version or other error.
+ *
+ * @param [in] packet Packet memory pointing to byte 0 of the Fixed Header
+ *
+ * @retval number Total header length
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ size_t (*totalHeaderLength)(const uint8_t *packet);
+
+ /**
+ * Returns the total packet length based on the Fixed Header
+ *
+ * The length may be 0 for an unsupported FixedHeader version or other error.
+ *
+ * @param [in] packet Packet memory pointing to byte 0 of the Fixed Header
+ *
+ * @retval number Total packet length
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ size_t (*totalPacketLength)(const uint8_t *packet);
+
+ /**
+ * Returns the length of the fixed header
+ *
+ * The length may be 0 for an unsupported FixedHeader version or other error.
+ *
+ * @param [in] packet Packet memory pointing to byte 0 of the Fixed Header
+ *
+ * @retval number Total packet length
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ size_t (*fixedHeaderLength)(const uint8_t *packet);
+
+ /**
+ * Determines if the FixedHeader PacketType is Intereest
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of fixed header
+ *
+ * @retval true PacketType is Interest
+ * @retval false PacketType is not Interest
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*isPacketTypeInterest)(const uint8_t *packet);
+
+ /**
+ * Determines if the FixedHeader PacketType is ContentObject
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of fixed header
+ *
+ * @retval true PacketType is ContentObject
+ * @retval false PacketType is not ContentObject
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*isPacketTypeContentObject)(const uint8_t *packet);
+
+ /**
+ * Determines if the FixedHeader PacketType is InterestReturn
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of fixed header
+ *
+ * @retval true PacketType is InterestReturn
+ * @retval false PacketType is not InterestReturn
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*isPacketTypeInterestReturn)(const uint8_t *packet);
+
+ /**
+ * Determines if the FixedHeader PacketType is Control
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of fixed header
+ *
+ * @retval true PacketType is Control
+ * @retval false PacketType is not Control
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*isPacketTypeControl)(const uint8_t *packet);
+
+ /**
+ * Determines if the FixedHeader PacketType is Hop By Hop Fragment
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of fixed header
+ *
+ * @retval true PacketType is Hop By Hop Fragment
+ * @retval false PacketType is not Hop By Hop Fragment
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+ bool (*isPacketTypeHopByHopFragment)(const uint8_t *packet);
+} MetisTlvOps;
+
+
+#endif // Metis_metis_TlvOps_h
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.c
new file mode 100644
index 00000000..e4bba4c8
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.c
@@ -0,0 +1,419 @@
+/*
+ * 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.
+ */
+
+/**
+ */
+
+/**
+ * THIS IS A DEPRECATED CLASS. V0 IS NO LONGER IN USE.
+ */
+
+#include <config.h>
+
+#include <LongBow/runtime.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvExtent.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/security/parc_CryptoHasher.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h>
+
+typedef struct __attribute__ ((__packed__)) metis_tlv_fixed_header {
+ uint8_t version;
+ uint8_t packetType;
+ uint16_t payloadLength;
+ uint16_t reserved;
+ uint16_t headerLength;
+} _MetisTlvFixedHeaderV0;
+
+#define FIXED_HEADER_LEN 8
+
+
+#define TotalPacketLength(fhPtr) (htons((fhPtr)->payloadLength) + htons((fhPtr)->headerLength) + FIXED_HEADER_LEN)
+
+
+#define METIS_PACKET_TYPE_INTEREST 0x01
+#define METIS_PACKET_TYPE_CONTENT 0x02
+
+// The message type for a Metis control packet
+#define METIS_PACKET_TYPE_CONTROL 0xA4
+
+// -----------------------------
+// in host byte order
+
+#define T_NAME 0x0000
+
+#define T_HOPLIMIT 0x0002
+#define T_INTFRAG 0x0003
+#define T_OBJFRAG 0x0004
+
+// inside interest
+#define T_KEYID 0x0001
+#define T_OBJHASH 0x0002
+#define T_SCOPE 0x0003
+#define T_INTLIFE 0x0005
+
+// inside an object
+#define T_NAMEAUTH 0x0002
+#define T_CONTENTS 0x0004
+#define T_SIGBLOCK 0x0005
+#define T_SIGBITS 0x000E
+
+// inside a CPI
+#define T_CPI 0xBEEF
+
+// -----------------------------
+// Internal API
+
+static void
+_parsePerHopV0(const uint8_t *packet, size_t offset, size_t endHeaders, MetisTlvSkeleton *skeleton)
+{
+ int foundCount = 0;
+ const size_t tl_length = 4;
+
+ // we only parse to the end of the per-hop headers or until we've found
+ // the 1 header we want is hoplimit. Others ignored.
+ while (offset < endHeaders && foundCount < 1) {
+ MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += tl_length;
+
+ switch (type) {
+ case T_HOPLIMIT:
+ metisTlvSkeleton_SetHopLimit(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+
+ offset += v_length;
+ }
+}
+
+static void
+_parseNameAuth(const uint8_t *packet, size_t offset, size_t endSection, MetisTlvSkeleton *skeleton)
+{
+ const size_t tl_length = 4;
+
+ while (offset < endSection) {
+ MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += tl_length;
+
+ switch (type) {
+ case T_KEYID:
+ metisTlvSkeleton_SetKeyId(skeleton, offset, v_length);
+ return;
+
+ default:
+ break;
+ }
+
+ offset += v_length;
+ }
+}
+
+static void
+_parseObjectV0(const uint8_t *packet, size_t offset, size_t endMessage, MetisTlvSkeleton *skeleton)
+{
+ int foundCount = 0;
+ const size_t tl_length = 4;
+
+ // skip the opending content object TLV
+ offset += 4;
+
+ // parse to the end or until we find the two things we need (name, keyid)
+ while (offset < endMessage && foundCount < 2) {
+ MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += tl_length;
+
+ switch (type) {
+ case T_NAME:
+ metisTlvSkeleton_SetName(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_NAMEAUTH:
+ _parseNameAuth(packet, offset, offset + v_length, skeleton);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+
+ offset += v_length;
+ }
+}
+
+static void
+_parseInterestV0(const uint8_t *packet, size_t offset, size_t endMessage, MetisTlvSkeleton *skeleton)
+{
+ int foundCount = 0;
+ const size_t tl_length = sizeof(MetisTlvType);
+
+ // skip the Interest wrapper
+ offset += 4;
+
+ // parse to the end or until we find all 5 things (name, keyid, objecthash, scope, interest lifetime)
+ while (offset < endMessage && foundCount < 5) {
+ MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t v_length = htons(tlv->length);
+
+ // skip past the TLV header
+ offset += tl_length;
+
+ switch (type) {
+ case T_NAME:
+ metisTlvSkeleton_SetName(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_KEYID:
+ metisTlvSkeleton_SetKeyId(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_OBJHASH:
+ metisTlvSkeleton_SetObjectHash(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_INTLIFE:
+ metisTlvSkeleton_SetInterestLifetime(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+
+ offset += v_length;
+ }
+}
+
+
+static void
+_parseControlPlaneInterface(const uint8_t *packet, size_t offset, size_t endMessage, MetisTlvSkeleton *skeleton)
+{
+ int foundCount = 0;
+ const size_t tl_length = 4;
+
+ // parse to the end or until we find all 5 things (name, keyid, objecthash, scope, interest lifetime)
+ while (offset < endMessage && foundCount < 1) {
+ MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ uint16_t type = htons(tlv->type);
+ uint16_t v_length = htons(tlv->length);
+
+ // skip past the TLV header
+ offset += tl_length;
+
+ switch (type) {
+ case T_CPI:
+ metisTlvSkeleton_SetCPI(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+
+ offset += v_length;
+ }
+}
+
+static PARCCryptoHash *
+_computeHash(const uint8_t *packet, size_t offset, size_t endMessage)
+{
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBytes(hasher, packet + offset, endMessage - offset);
+ PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher);
+ parcCryptoHasher_Release(&hasher);
+ return hash;
+}
+
+// ==================
+// TlvOps functions
+
+static PARCBuffer *
+_encodeControlPlaneInformation(const CCNxControl *cpiControlMessage)
+{
+ PARCJSON *json = ccnxControl_GetJson(cpiControlMessage);
+ char *str = parcJSON_ToCompactString(json);
+
+ // include +1 because we need the NULL byte
+ size_t len = strlen(str) + 1;
+
+ size_t packetLength = sizeof(_MetisTlvFixedHeaderV0) + sizeof(MetisTlvType) + len;
+ PARCBuffer *packet = parcBuffer_Allocate(packetLength);
+
+ _MetisTlvFixedHeaderV0 hdr;
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.version = 0;
+ hdr.packetType = METIS_PACKET_TYPE_CONTROL;
+ hdr.payloadLength = htons(len + sizeof(MetisTlvType));
+
+ parcBuffer_PutArray(packet, sizeof(hdr), (uint8_t *) &hdr);
+
+ MetisTlvType tlv = { .type = htons(T_CPI), .length = htons(len) };
+ parcBuffer_PutArray(packet, sizeof(tlv), (uint8_t *) &tlv);
+
+ parcBuffer_PutArray(packet, len, (uint8_t *) str);
+
+ parcMemory_Deallocate((void **) &str);
+ return parcBuffer_Flip(packet);
+}
+
+
+static PARCCryptoHash *
+_computeContentObjectHash(const uint8_t *packet)
+{
+ assertNotNull(packet, "Parameter packet must be non-null");
+
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+ if (hdr->packetType == METIS_PACKET_TYPE_CONTENT) {
+ size_t headerLength = htons(hdr->headerLength);
+ size_t endHeaders = FIXED_HEADER_LEN + headerLength;
+ size_t endPacket = TotalPacketLength(hdr);
+
+ return _computeHash(packet, endHeaders, endPacket);
+ }
+
+ return NULL;
+}
+
+static bool
+_isPacketTypeInterest(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_INTEREST);
+}
+
+static bool
+_isPacketTypeInterestReturn(const uint8_t *packet)
+{
+ return false;
+}
+
+static bool
+_isPacketTypeContentObject(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_CONTENT);
+}
+
+static bool
+_isPacketTypeControl(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_CONTROL);
+}
+
+static bool
+_isPacketTypeHopByHopFragment(const uint8_t *packet)
+{
+ // does not exist for version 0 packets
+ return false;
+}
+
+static size_t
+_fixedHeaderLength(const uint8_t *packet)
+{
+ return sizeof(_MetisTlvFixedHeaderV0);
+}
+
+static size_t
+_totalHeaderLength(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+
+ return htons(hdr->headerLength) + sizeof(_MetisTlvFixedHeaderV0);
+}
+
+static size_t
+_totalPacketLength(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) packet;
+
+ return TotalPacketLength(hdr);
+}
+
+static bool
+_parse(MetisTlvSkeleton *skeleton)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) metisTlvSkeleton_GetPacket(skeleton);
+ size_t headerLength = htons(hdr->headerLength);
+
+ size_t endHeaders = FIXED_HEADER_LEN + headerLength;
+ size_t endPacket = TotalPacketLength(hdr);
+
+ trapUnexpectedStateIf(hdr->version != 0, "Version not 0");
+
+ switch (hdr->packetType) {
+ case METIS_PACKET_TYPE_INTEREST:
+ _parsePerHopV0(metisTlvSkeleton_GetPacket(skeleton), FIXED_HEADER_LEN, endHeaders, skeleton);
+ _parseInterestV0(metisTlvSkeleton_GetPacket(skeleton), endHeaders, endPacket, skeleton);
+ break;
+
+ case METIS_PACKET_TYPE_CONTENT:
+ _parsePerHopV0(metisTlvSkeleton_GetPacket(skeleton), FIXED_HEADER_LEN, endHeaders, skeleton);
+ _parseObjectV0(metisTlvSkeleton_GetPacket(skeleton), endHeaders, endPacket, skeleton);
+ break;
+
+ case METIS_PACKET_TYPE_CONTROL:
+ _parseControlPlaneInterface(metisTlvSkeleton_GetPacket(skeleton), endHeaders, endPacket, skeleton);
+ break;
+
+ default:
+ break;
+ }
+
+ return true;
+}
+
+const MetisTlvOps MetisTlvSchemaV0_Ops = {
+ .parse = _parse,
+ .computeContentObjectHash = _computeContentObjectHash,
+ .encodeControlPlaneInformation = _encodeControlPlaneInformation,
+ .fixedHeaderLength = _fixedHeaderLength,
+ .totalHeaderLength = _totalHeaderLength,
+ .totalPacketLength = _totalPacketLength,
+ .isPacketTypeInterest = _isPacketTypeInterest,
+ .isPacketTypeContentObject = _isPacketTypeContentObject,
+ .isPacketTypeInterestReturn = _isPacketTypeInterestReturn,
+ .isPacketTypeControl = _isPacketTypeControl,
+ .isPacketTypeHopByHopFragment = _isPacketTypeHopByHopFragment,
+};
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h
new file mode 100644
index 00000000..cbbbbe83
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvSchemaV0.h
+ * @brief Define the TLV Operations for the V0 schema
+ *
+ * Defines the operations for parsing a V0 schema name
+ *
+ * THIS IS A DEPRECATED CLASS. V0 IS NO LONGER IN USE.
+ *
+ *
+ */
+
+#ifndef Metis_metis_TlvSchemaV0
+#define Metis_metis_TlvSchemaV0
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvOps.h>
+
+/**
+ * Defines the TLV Operations for the V0 Schema
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read a packet from the network
+ * MetisTlvSkeleton skeleton;
+ * bool success = MetisTlvSchemaV0_Ops.parseSkeleton(packet, &skeleton);
+ * if (success) {
+ * if (MetisTlvSchemaV0_Ops.isPacketTypeInterest(packet)) {
+ * // parse interest
+ * }
+ * }
+ * @endcode
+ */
+extern const MetisTlvOps MetisTlvSchemaV0_Ops;
+
+#endif // Metis_metis_TlvSchemaV0
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.c
new file mode 100644
index 00000000..bf3695f2
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.c
@@ -0,0 +1,567 @@
+/*
+ * 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.
+ */
+
+
+#include <config.h>
+
+#include <LongBow/runtime.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <arpa/inet.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_Tlv.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvExtent.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/security/parc_CryptoHasher.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h>
+
+typedef struct __attribute__ ((__packed__)) metis_tlv_fixed_header {
+ uint8_t version;
+ uint8_t packetType;
+ uint16_t packetLength;
+ uint8_t interestHopLimit;
+ uint8_t returnCode;
+ uint8_t flags;
+ uint8_t headerLength;
+} _MetisTlvFixedHeaderV1;
+
+#define METIS_PACKET_TYPE_INTEREST 0
+#define METIS_PACKET_TYPE_CONTENT 1
+#define METIS_PACKET_TYPE_INTERESTRETURN 2
+#define METIS_PACKET_TYPE_HOPFRAG 4
+#define METIS_PACKET_TYPE_CONTROL 0xA4
+
+// -----------------------------
+// in host byte order
+
+#define T_NAME 0x0000
+
+// perhop headers
+#define T_INTLIFE 0x0001
+#define T_CACHETIME 0x0002
+#define T_PATHLABEL 0x0003
+#define T_FLOW 0x0005
+
+// Top-level TLVs
+#define T_INTEREST 0x0001
+#define T_OBJECT 0x0002
+#define T_VALALG 0x0003
+#define T_VALPAYLOAD 0x0004
+#define T_HOPFRAG_PAYLOAD 0x0005
+#define T_MANIFEST 0x0006
+
+// inside interest
+#define T_KEYIDRES 0x0002
+#define T_OBJHASHRES 0x0003
+
+// inside a content object
+#define T_EXPIRYTIME 0x0006
+
+// ValidationAlg
+
+// these are the algorithms we need a KEYID for
+#define T_RSA_SHA256 0x0006
+#define T_EC_SECP_256K1 0x0007
+#define T_EC_SECP_384R1 0x0008
+
+#define T_KEYID 0x0009
+#define T_PUBLICKEY 0x000B
+#define T_CERT 0x000C
+
+// inside a CPI
+#define T_CPI 0xBEEF
+
+
+// -----------------------------
+// Internal API
+
+/**
+ * Parse the per-hop headers.
+ *
+ * Will return the absolute offset of the next byte to parse (i.e. 'endHeaders')
+ *
+ * @param [in] packet The packet buffer
+ * @param [in] offset The first byte to begin parsing at
+ * @param [in] endMessage The ceiling of bytes to parse
+ * @param [in] skeleton The structure to fill in
+ */
+static void
+_parsePerHopV1(const uint8_t *packet, size_t offset, size_t endHeaders, MetisTlvSkeleton *skeleton)
+{
+ const size_t tl_length = sizeof(MetisTlvType);
+
+ // we only parse to the end of the per-hop headers or until we've found
+ // the 2 headers we want (hoplimit, fragmentation header)
+ while (offset + sizeof(MetisTlvType) < endHeaders) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += tl_length;
+
+ size_t endSubSection = offset + v_length;
+ if (endSubSection <= endHeaders) {
+ switch (type) {
+ case T_INTLIFE:
+ metisTlvSkeleton_SetInterestLifetime(skeleton, offset, v_length);
+ break;
+
+ // should verify that we dont have both INTFRAG and OBJFRAG
+ case T_CACHETIME:
+ metisTlvSkeleton_SetCacheTimeHeader(skeleton, offset, v_length);
+ break;
+
+ case T_PATHLABEL:
+ metisTlvSkeleton_SetPathLabel(skeleton, offset, v_length);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ offset = endSubSection;
+ }
+}
+
+static void
+_parseSignatureParameters(const uint8_t *packet, size_t offset, size_t endSection, struct tlv_skeleton *skeleton)
+{
+ // Scan the section for KeyId, and optional Certificate or PublicKey.
+ while (offset + sizeof(MetisTlvType) < endSection) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += sizeof(MetisTlvType);
+
+ size_t endSubSection = offset + v_length;
+ if (endSubSection <= endSection) {
+ switch (type) {
+ case T_KEYID:
+ metisTlvSkeleton_SetKeyId(skeleton, offset, v_length);
+ break;
+
+ case T_CERT:
+ metisTlvSkeleton_SetCertificate(skeleton, offset, v_length);
+ break;
+
+ case T_PUBLICKEY:
+ metisTlvSkeleton_SetPublicKey(skeleton, offset, v_length);
+
+ default:
+ break;
+ }
+ }
+ offset += v_length;
+ }
+}
+
+static void
+_parseValidationType(const uint8_t *packet, size_t offset, size_t endSection, struct tlv_skeleton *skeleton)
+{
+ if (offset + sizeof(MetisTlvType) < endSection) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += sizeof(MetisTlvType);
+
+ size_t endSubSection = offset + v_length;
+ if (endSubSection <= endSection) {
+ switch (type) {
+ // These are the Validation Algorithms that have a usable KeyId
+ case T_EC_SECP_256K1: // fallthrough
+ case T_EC_SECP_384R1: // fallthrough
+ case T_RSA_SHA256:
+ _parseSignatureParameters(packet, offset, endSubSection, skeleton);
+ return;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+static size_t
+_parseValidationAlg(const uint8_t *packet, size_t offset, size_t endMessage, struct tlv_skeleton *skeleton)
+{
+ size_t endSection = endMessage;
+
+ if (offset + sizeof(MetisTlvType) < endMessage) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ offset += sizeof(MetisTlvType);
+ endSection = offset + v_length;
+
+ // make sure we don't have container overrun
+ if (endSection <= endMessage && type == T_VALALG) {
+ _parseValidationType(packet, offset, endSection, skeleton);
+ }
+ }
+
+ return endSection;
+}
+
+/**
+ * Parse the "value" of a T_OBJECT
+ *
+ * 'offset' should point to the first byte of the "value" of the T_OBJECT container
+ *
+ * @param [in] packet The packet buffer
+ * @param [in] offset The first byte to begin parsing at
+ * @param [in] endSection The ceiling of bytes to parse
+ * @param [in] skeleton The structure to fill in
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+static void
+_parseObjectV1(const uint8_t *packet, size_t offset, size_t endSection, MetisTlvSkeleton *skeleton)
+{
+ int foundCount = 0;
+
+ // parse to the end or until we find the two things we need (name, keyid)
+ while (offset < endSection && foundCount < 2) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ // move past the TL header
+ offset += sizeof(MetisTlvType);
+ size_t endSubSection = offset + v_length;
+ if (endSubSection <= endSection) {
+ switch (type) {
+ case T_NAME:
+ metisTlvSkeleton_SetName(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_EXPIRYTIME:
+ metisTlvSkeleton_SetExpiryTime(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ offset = endSubSection;
+ }
+}
+
+/**
+ * Parse the "value" of a T_INTEREST
+ *
+ * 'offset' should point to the first byte of the "value" of the T_INTEREST container
+ *
+ * @param [in] packet The packet buffer
+ * @param [in] offset The first byte to begin parsing at
+ * @param [in] endSection The ceiling of bytes to parse
+ * @param [in] skeleton The structure to fill in
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+static void
+_parseInterestV1(const uint8_t *packet, size_t offset, size_t endSection, struct tlv_skeleton *skeleton)
+{
+ int foundCount = 0;
+
+ // parse to the end or until we find all 3 things (name, keyid, objecthash)
+ while (offset < endSection && foundCount < 3) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ // skip past the TLV header
+ offset += sizeof(MetisTlvType);
+ size_t endSubSection = offset + v_length;
+ if (endSubSection <= endSection) {
+ switch (type) {
+ case T_NAME:
+ metisTlvSkeleton_SetName(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_KEYIDRES:
+ metisTlvSkeleton_SetKeyId(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ case T_OBJHASHRES:
+ metisTlvSkeleton_SetObjectHash(skeleton, offset, v_length);
+ foundCount++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ offset = endSubSection;
+ }
+}
+
+/**
+ * Parses the message body
+ *
+ * 'offset' should point to the first byte of the T_INTEREST, T_CONTENTOBJECT, etc.
+ *
+ * @param [<#in#> | <#out#> | <#in,out#>] <#name#> <#description#>
+ *
+ * @return number The absolute byte offset of the next location to parse
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+static size_t
+_parseMessage(const uint8_t *packet, size_t offset, size_t endMessage, struct tlv_skeleton *skeleton)
+{
+ size_t endSection = endMessage;
+
+ if (offset + sizeof(MetisTlvType) < endMessage) {
+ const MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ const uint16_t type = htons(tlv->type);
+ const uint16_t v_length = htons(tlv->length);
+
+ offset += sizeof(MetisTlvType);
+ size_t endSubSection = offset + v_length;
+
+ // make sure we don't have container overrun
+ if (endSubSection <= endMessage) {
+ switch (type) {
+ case T_INTEREST:
+ _parseInterestV1(packet, offset, endSubSection, skeleton);
+ break;
+
+ case T_MANIFEST:
+ case T_OBJECT:
+ _parseObjectV1(packet, offset, endSubSection, skeleton);
+ break;
+
+ case T_CPI:
+ // There is nothing nested here, its just the value
+ metisTlvSkeleton_SetCPI(skeleton, offset, v_length);
+ break;
+
+ case T_HOPFRAG_PAYLOAD:
+ // There is nothing nested here, its just the value
+ metisTlvSkeleton_SetFragmentPayload(skeleton, offset, v_length);
+ break;
+
+ default:
+ break;
+ }
+
+ endSection = endSubSection;
+ }
+ }
+ return endSection;
+}
+
+static PARCCryptoHash *
+_computeHash(const uint8_t *packet, size_t offset, size_t endMessage)
+{
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBytes(hasher, packet + offset, endMessage - offset);
+ PARCCryptoHash *hash = parcCryptoHasher_Finalize(hasher);
+ parcCryptoHasher_Release(&hasher);
+ return hash;
+}
+
+// ==================
+// TlvOps functions
+
+static PARCBuffer *
+_encodeControlPlaneInformation(const CCNxControl *cpiControlMessage)
+{
+ PARCJSON *json = ccnxControl_GetJson(cpiControlMessage);
+ char *str = parcJSON_ToCompactString(json);
+
+ // include +1 because we need the NULL byte
+ size_t len = strlen(str) + 1;
+
+ size_t packetLength = sizeof(_MetisTlvFixedHeaderV1) + sizeof(MetisTlvType) + len;
+ PARCBuffer *packet = parcBuffer_Allocate(packetLength);
+
+ _MetisTlvFixedHeaderV1 hdr;
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.version = 1;
+ hdr.packetType = METIS_PACKET_TYPE_CONTROL;
+ hdr.packetLength = htons(packetLength);
+ hdr.headerLength = 8;
+
+ parcBuffer_PutArray(packet, sizeof(hdr), (uint8_t *) &hdr);
+
+ MetisTlvType tlv = { .type = htons(T_CPI), .length = htons(len) };
+ parcBuffer_PutArray(packet, sizeof(tlv), (uint8_t *) &tlv);
+
+ parcBuffer_PutArray(packet, len, (uint8_t *) str);
+
+ parcMemory_Deallocate((void **) &str);
+ return parcBuffer_Flip(packet);
+}
+
+
+static bool
+_isPacketTypeInterest(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_INTEREST);
+}
+
+static bool
+_isPacketTypeInterestReturn(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_INTERESTRETURN);
+}
+
+static bool
+_isPacketTypeContentObject(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_CONTENT);
+}
+
+static bool
+_isPacketTypeControl(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_CONTROL);
+}
+
+static bool
+_isPacketTypeHopByHopFragment(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return (hdr->packetType == METIS_PACKET_TYPE_HOPFRAG);
+}
+
+static size_t
+_fixedHeaderLength(const uint8_t *packet)
+{
+ return sizeof(_MetisTlvFixedHeaderV1);
+}
+
+static size_t
+_totalHeaderLength(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return hdr->headerLength;
+}
+
+static size_t
+_totalPacketLength(const uint8_t *packet)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ return htons(hdr->packetLength);
+}
+
+static PARCCryptoHash *
+_computeContentObjectHash(const uint8_t *packet)
+{
+ assertNotNull(packet, "Parameter packet must be non-null");
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) packet;
+ if (hdr->packetType == METIS_PACKET_TYPE_CONTENT) {
+ const size_t endHeaders = _totalHeaderLength(packet);
+ const size_t endPacket = _totalPacketLength(packet);
+ return _computeHash(packet, endHeaders, endPacket);
+ }
+
+ return NULL;
+}
+
+
+static bool
+_goodPacketType(uint8_t packetType)
+{
+ bool goodType = false;
+ if (packetType == METIS_PACKET_TYPE_INTEREST || packetType == METIS_PACKET_TYPE_CONTENT ||
+ packetType == METIS_PACKET_TYPE_CONTROL || packetType == METIS_PACKET_TYPE_INTERESTRETURN ||
+ packetType == METIS_PACKET_TYPE_HOPFRAG) {
+ goodType = true;
+ }
+ return goodType;
+}
+
+static bool
+_parse(MetisTlvSkeleton *skeleton)
+{
+ bool success = false;
+
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) metisTlvSkeleton_GetPacket(skeleton);
+
+ // this function should only be called for version 1 packets
+ trapUnexpectedStateIf(hdr->version != 1, "Version not 1");
+
+ if (_goodPacketType(hdr->packetType) && hdr->headerLength >= sizeof(_MetisTlvFixedHeaderV1)) {
+ size_t endHeaders = hdr->headerLength;
+ size_t endPacket = htons(hdr->packetLength);
+
+ if (endPacket >= endHeaders) {
+ if (_isPacketTypeInterest((uint8_t *) hdr)) {
+ metisTlvSkeleton_SetHopLimit(skeleton, 4, 1);
+ }
+
+ _parsePerHopV1(metisTlvSkeleton_GetPacket(skeleton), sizeof(_MetisTlvFixedHeaderV1), endHeaders, skeleton);
+ size_t offset = _parseMessage(metisTlvSkeleton_GetPacket(skeleton), endHeaders, endPacket, skeleton);
+ _parseValidationAlg(metisTlvSkeleton_GetPacket(skeleton), offset, endPacket, skeleton);
+ success = true;
+ }
+ }
+
+ return success;
+}
+
+const MetisTlvOps MetisTlvSchemaV1_Ops = {
+ .parse = _parse,
+ .computeContentObjectHash = _computeContentObjectHash,
+ .encodeControlPlaneInformation = _encodeControlPlaneInformation,
+ .fixedHeaderLength = _fixedHeaderLength,
+ .totalHeaderLength = _totalHeaderLength,
+ .totalPacketLength = _totalPacketLength,
+ .isPacketTypeInterest = _isPacketTypeInterest,
+ .isPacketTypeContentObject = _isPacketTypeContentObject,
+ .isPacketTypeInterestReturn = _isPacketTypeInterestReturn,
+ .isPacketTypeHopByHopFragment = _isPacketTypeHopByHopFragment,
+ .isPacketTypeControl = _isPacketTypeControl,
+};
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h
new file mode 100644
index 00000000..628bc556
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvSchemaV1.h
+ * @brief API to handle the v1 packet format
+ *
+ * Defines the operations for parsing a V1 schema name
+ *
+ */
+
+#ifndef Metis_metis_TlvSchemaV1
+#define Metis_metis_TlvSchemaV1
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvOps.h>
+
+/**
+ * Defines the TLV Operations for the V1 Schema
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read a packet from the network
+ * MetisTlvSkeleton skeleton;
+ * bool success = MetisTlvSchemaV1_Ops.parseSkeleton(packet, &skeleton);
+ * if (success) {
+ * if (MetisTlvSchemaV1_Ops.isPacketTypeInterest(packet)) {
+ * // parse interest
+ * }
+ * }
+ * @endcode
+ */
+extern const MetisTlvOps MetisTlvSchemaV1_Ops;
+
+
+#endif // Metis_metis_TlvSchemaV1
+
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.c b/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.c
new file mode 100644
index 00000000..ce479ce5
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.c
@@ -0,0 +1,484 @@
+/*
+ * 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.
+ */
+
+
+#include <stdio.h>
+#include <config.h>
+#include <string.h>
+#include <LongBow/runtime.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h>
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV0.h>
+#include <ccnx/forwarder/metis/tlv/metis_TlvSchemaV1.h>
+
+#define INDEX_NAME 0
+#define INDEX_KEYID 1
+#define INDEX_OBJHASH 2
+#define INDEX_HOPLIMIT 3
+#define INDEX_INTLIFETIME 4
+#define INDEX_CACHETIME 5
+#define INDEX_EXPIRYTIME 6
+#define INDEX_CPI 7
+#define INDEX_FRAGMENTPAYLOAD 8
+#define INDEX_CERTIFICATE 9
+#define INDEX_PUBKEY 10
+#define INDEX_PATHLABEL 11
+
+/**
+ * The non-opaque representation of the MetisTlvSkeleton.
+ *
+ * IMPORTANT: if you change this structure, you must make sure the corresponding
+ * opaque structure in metis_TlvSkeleton.h has at least that much memory in it.
+ */
+typedef struct internal_skeleton {
+ const struct metis_tlv_ops *tlvOps;
+ uint8_t *packet;
+ MetisLogger *logger;
+
+ MetisTlvExtent array[MetisTlvSkeleton_ArrayLength];
+} _InternalSkeleton;
+
+static void
+_assertInvariants(const _InternalSkeleton *skeleton)
+{
+ assertNotNull(skeleton->tlvOps, "Invalid skeleton, does not have a schema ops");
+ assertNotNull(skeleton->packet, "Invalid skeleton, does not have a packet buffer");
+}
+
+/**
+ * Initialize the skeleton memory
+ *
+ * Clears all the extents to {0, 0} and sets the tlvOps and packet members for further parsing.
+ *
+ * @param [in] skeleton The skeleton to initialize
+ * @param [in] tlvOps The parser operations to use
+ * @param [in] packet the packet buffer (points to byte "0" of the fixed header)
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvSkeleton skeleton;
+ * _initialize(&skeleton, &MetisTlvSchemaV0_Ops, packet);
+ * }
+ * @endcode
+ */
+static void
+_initialize(_InternalSkeleton *skeleton, const struct metis_tlv_ops *tlvOps, uint8_t *packet, MetisLogger *logger)
+{
+ memset(skeleton, 0, sizeof(MetisTlvSkeleton));
+ skeleton->packet = packet;
+ skeleton->tlvOps = tlvOps;
+ skeleton->logger = logger;
+ _assertInvariants(skeleton);
+}
+
+
+bool
+metisTlvSkeleton_Parse(MetisTlvSkeleton *opaque, uint8_t *packet, MetisLogger *logger)
+{
+ // do not assert invariants here. Parse will setup the invariants.
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ uint8_t version = packet[0];
+
+ switch (version) {
+ case 0:
+ _initialize(skeleton, &MetisTlvSchemaV0_Ops, packet, logger);
+ return MetisTlvSchemaV0_Ops.parse(opaque);
+
+ case 1:
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ return MetisTlvSchemaV1_Ops.parse(opaque);
+
+ default:
+ if (metisLogger_IsLoggable(logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
+ metisLogger_Log(logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
+ "Parsing unknown packet version %u", version);
+ }
+ break;
+ }
+ return false;
+}
+
+// ==========================================================
+// Setters
+
+void
+metisTlvSkeleton_SetName(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_NAME].offset = offset;
+ skeleton->array[INDEX_NAME].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set name extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetKeyId(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_KEYID].offset = offset;
+ skeleton->array[INDEX_KEYID].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set keyid extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetCertificate(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_CERTIFICATE].offset = offset;
+ skeleton->array[INDEX_CERTIFICATE].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set certificate extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetPublicKey(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_PUBKEY].offset = offset;
+ skeleton->array[INDEX_PUBKEY].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set public key extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetObjectHash(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_OBJHASH].offset = offset;
+ skeleton->array[INDEX_OBJHASH].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set objhash extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetHopLimit(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_HOPLIMIT].offset = offset;
+ skeleton->array[INDEX_HOPLIMIT].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set hoplimit extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetInterestLifetime(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_INTLIFETIME].offset = offset;
+ skeleton->array[INDEX_INTLIFETIME].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set int lifetime extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetPathLabel(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_PATHLABEL].offset = offset;
+ skeleton->array[INDEX_PATHLABEL].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set path label extent {%u, %u}", offset, length);
+ }
+}
+
+
+void
+metisTlvSkeleton_SetCacheTimeHeader(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_CACHETIME].offset = offset;
+ skeleton->array[INDEX_CACHETIME].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set cachetime extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetExpiryTime(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_EXPIRYTIME].offset = offset;
+ skeleton->array[INDEX_EXPIRYTIME].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set expirytime extent {%u, %u}", offset, length);
+ }
+}
+
+void
+metisTlvSkeleton_SetCPI(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_CPI].offset = offset;
+ skeleton->array[INDEX_CPI].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set cpi extent {%u, %u}", offset, length);
+ }
+}
+
+bool
+metisTlvSkeleton_UpdateHopLimit(MetisTlvSkeleton *opaque, uint8_t hoplimit)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ bool updated = false;
+ if (!metisTlvExtent_Equals(&skeleton->array[INDEX_HOPLIMIT], &metisTlvExtent_NotFound)) {
+ if (skeleton->array[INDEX_HOPLIMIT].length == 1) {
+ updated = true;
+ uint8_t *value = skeleton->packet + skeleton->array[INDEX_HOPLIMIT].offset;
+ *value = hoplimit;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "set hoplimit %u", hoplimit);
+ }
+ }
+ }
+ return updated;
+}
+
+bool
+metisTlvSkeleton_UpdatePathLabel(MetisTlvSkeleton *opaque, uint8_t outFace)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ bool updated = false;
+ if (!metisTlvExtent_Equals(&skeleton->array[INDEX_PATHLABEL], &metisTlvExtent_NotFound)) {
+ if (skeleton->array[INDEX_PATHLABEL].length == 1) {
+ updated = true;
+ uint8_t *value = skeleton->packet + skeleton->array[INDEX_PATHLABEL].offset;
+ uint8_t oldPathLabel = (uint8_t) (*value);
+ uint8_t tmp = (oldPathLabel << 1) | (oldPathLabel >> 7);
+ uint8_t newPathLabel = (tmp ^ outFace);
+ *value = newPathLabel;
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "set pathlabel %u", newPathLabel);
+ }
+ }
+ }
+ return updated;
+}
+
+bool
+metisTlvSkeleton_ResetPathLabel(MetisTlvSkeleton *opaque)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ bool updated = false;
+ if (!metisTlvExtent_Equals(&skeleton->array[INDEX_PATHLABEL], &metisTlvExtent_NotFound)) {
+ if (skeleton->array[INDEX_PATHLABEL].length == 1) {
+ updated = true;
+ uint8_t *value = skeleton->packet + skeleton->array[INDEX_PATHLABEL].offset;
+ *value = 0;
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "reset pathlabel 0");
+ }
+ }
+ }
+ return updated;
+}
+
+void
+metisTlvSkeleton_SetFragmentPayload(MetisTlvSkeleton *opaque, size_t offset, size_t length)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ skeleton->array[INDEX_FRAGMENTPAYLOAD].offset = offset;
+ skeleton->array[INDEX_FRAGMENTPAYLOAD].length = length;
+
+ if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
+ metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
+ "Set fragment payload extent {%u, %u}", offset, length);
+ }
+}
+
+
+
+// ==========================================================
+
+MetisTlvExtent
+metisTlvSkeleton_GetName(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_NAME];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetKeyId(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_KEYID];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetCertificate(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_CERTIFICATE];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetPublicKey(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_PUBKEY];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetObjectHash(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_OBJHASH];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetHopLimit(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_HOPLIMIT];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetInterestLifetime(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_INTLIFETIME];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetPathLabel(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_PATHLABEL];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetCacheTimeHeader(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_CACHETIME];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetExpiryTime(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_EXPIRYTIME];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetCPI(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_CPI];
+}
+
+MetisTlvExtent
+metisTlvSkeleton_GetFragmentPayload(const MetisTlvSkeleton *skeleton)
+{
+ return skeleton->array[INDEX_FRAGMENTPAYLOAD];
+}
+
+
+const uint8_t *
+metisTlvSkeleton_GetPacket(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->packet;
+}
+
+PARCCryptoHash *
+metisTlvSkeleton_ComputeContentObjectHash(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->computeContentObjectHash(skeleton->packet);
+}
+
+size_t
+metisTlvSkeleton_TotalPacketLength(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->totalPacketLength(skeleton->packet);
+}
+
+bool
+metisTlvSkeleton_IsPacketTypeInterest(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->isPacketTypeInterest(skeleton->packet);
+}
+
+bool
+metisTlvSkeleton_IsPacketTypeContentObject(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->isPacketTypeContentObject(skeleton->packet);
+}
+
+bool
+metisTlvSkeleton_IsPacketTypeControl(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->isPacketTypeControl(skeleton->packet);
+}
+
+bool
+metisTlvSkeleton_IsPacketTypeInterestReturn(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->isPacketTypeInterestReturn(skeleton->packet);
+}
+
+bool
+metisTlvSkeleton_IsPacketTypeHopByHopFragment(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->tlvOps->isPacketTypeHopByHopFragment(skeleton->packet);
+}
+
+MetisLogger *
+metisTlvSkeleton_GetLogger(const MetisTlvSkeleton *opaque)
+{
+ const _InternalSkeleton *skeleton = (const _InternalSkeleton *) opaque;
+ _assertInvariants(skeleton);
+ return skeleton->logger;
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h b/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h
new file mode 100644
index 00000000..7e4eb603
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/metis_TlvSkeleton.h
@@ -0,0 +1,770 @@
+/*
+ * 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.
+ */
+
+/**
+ * @file metis_TlvSkeleton.h
+ * @brief The structure used to store the set of fields used by the forwarder
+ *
+ * The TLV skeleton is the parsed form of the packet. It contains the TLV extents of each
+ * field relevant to the forwarder.
+ *
+ * To use MetisTlvSkeleton, you first parse a packet in to the skeleton and can then access the various
+ * TlvExtents from the getters. The Tlv parsers use the Setters.
+ *
+ * The TlvSkeleton is not allocated memory (in general). It is defined as a sized opaque struct so one
+ * can use it as a member of another struct without deep allocations. The general use is as shown in
+ * the example below.
+ *
+ * @code
+ * typedef struct my_data_s {
+ * unsigned id;
+ * MetisTlvSkeleton skeleton;
+ * } MyData;
+ *
+ * void foo(uint8_t *packetBuffer)
+ * {
+ * MyData *mydata = parcMemory_Allocate(sizeof(MyData));
+ * mydata->id = _global_id++;
+ * metisTlvSkeleton_Parse(&mydata->skeleton, packetBuffer);
+ * // now forward the packet using the data in the skeleton
+ * parcMemory_Deallocate(&mydata);
+ * }
+ *
+ */
+
+#ifndef Metis_metis_TlvSkeleton_h
+#define Metis_metis_TlvSkeleton_h
+
+#include <ccnx/forwarder/metis/tlv/metis_TlvExtent.h>
+#include <ccnx/forwarder/metis/core/metis_Logger.h>
+
+#define MetisTlvSkeleton_ArrayLength 12
+
+/**
+ * The MetisTlvSkeleton is an opaque object defined in the header so it
+ * can be pre-allocated as part of another data structure. The user should have
+ * no direct access to any of the fields.
+ */
+typedef struct tlv_skeleton {
+ void *field1;
+ void *field2;
+ void *field3;
+
+ MetisTlvExtent array[MetisTlvSkeleton_ArrayLength];
+} MetisTlvSkeleton;
+
+// ===================================
+// Setters
+
+/**
+ * Fills in the packet TLV skeleton
+ *
+ * Sets the skeleton's tlv operations to the correct value and sets the packet buffer.
+ * Will call metisTlvSkeleton_Initialize().
+ *
+ * Because the MetisTlvSkeleton is meant to be allocated as part of a larger object, it does
+ * not store its own reference to the logger, as there is no _Destroy or _Release method. Therefore,
+ * the caller must ensure the logger stays allocated for the lifetime of the skeleton.
+ *
+ * @param [in] packet Packet memory, pointing to byte 0 of the fixed header
+ * @param [in] skeleton An allocated MetisTlvSkeleton to fill in
+ * @param [in] logger The logger to use
+ *
+ * @retval true Good parse
+ * @retval false Error
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvSkeleton skeleton;
+ * bool success = metisTlvSkeleton_Parse(&skeleton, packet);
+ * }
+ * @endcode
+ */
+bool metisTlvSkeleton_Parse(MetisTlvSkeleton *skeleton, uint8_t *packet, MetisLogger *logger);
+
+/**
+ * Sets the Name extent
+ *
+ * Sets the name extent to the specified offset and length.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserNameType) {
+ * metisTlvSkeleton_SetName(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetName(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the KeyId extent
+ *
+ * For an Interest, the KeyId extent is the KeyIdRestriction. For a ContentObject, it is
+ * the KeyId in the Validation Algorithm.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserKeyIdType) {
+ * metisTlvSkeleton_SetKeyId(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetKeyId(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/*
+ * Sets the Certificate extent.
+ *
+ * In a ContentObject, in the Validation dependent data may contain a certificate.
+ * Use this to set the extents of the certificate.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the certificate.
+ * @param [in] length The byte length of the certificate.
+ */
+void metisTlvSkeleton_SetCertificate(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/*
+ * Sets the Public Key extent.
+ *
+ * In a ContentObject, in the Validation dependent data may contain a public key.
+ * Use this to set the extents of the certificate.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the public key.
+ * @param [in] length The byte length of the public key.
+ */
+void metisTlvSkeleton_SetPublicKey(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Content Object Hash extent
+ *
+ * For an Interest, this is the Content Object hash restriction
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserKeyIdType) {
+ * metisTlvSkeleton_SetObjectHash(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetObjectHash(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Hop Limit extent
+ *
+ * For an Interest, the hoplimit is found in the fixed header (v1) or in a perhop header (v0)
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * // For a version 1 packet, point it to the field in the fixed header
+ * metisTlvSkeleton_SetHopLimit(skeleton, 4, 1);
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetHopLimit(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Interest Lifetime extent
+ *
+ * Sets the extent for the Interest lifetime
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserInterestLifetimeType) {
+ * metisTlvSkeleton_SetInterestLifetime(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetInterestLifetime(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Recommended Cache Time extent
+ *
+ * Sets the extent for the Recommended Cache Time for a Content Object
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserRecommendedCacheTimeType) {
+ * metisTlvSkeleton_SetCacheTimeHeader(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetCacheTimeHeader(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the ExpiryTime extent
+ *
+ * Sets the extent for the Expiry Time for a Content Object
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserExpiryTimeType) {
+ * metisTlvSkeleton_SetExpiryTime(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetExpiryTime(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Control Plane Interface (CPI) extent
+ *
+ * Sets the extent for the CPI value.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserControlType) {
+ * metisTlvSkeleton_SetCPI(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetCPI(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Sets the Fragment Payload extent
+ *
+ * This is the payload of the fragment, i.e. part of the original packet.
+ *
+ * @param [in] skeleton A MetisTlvSkeleton structure
+ * @param [in] offset The byte offset of the beginning of the 'value'
+ * @param [in] length The byte length of the 'value'
+ *
+ * Example:
+ * @code
+ * {
+ * MetisTlvType *tlv = (MetisTlvType *) (packet + offset);
+ * uint16_t type = htons(tlv->type);
+ * uint16_t v_length = htons(tlv->length);
+ * offset += sizeof(MetisTlvType);
+ *
+ * if (type == _ParserFragmentPayloadType) {
+ * metisTlvSkeleton_SetFragmentPayload(skeleton, offset, v_length);
+ * }
+ * }
+ * @endcode
+ */
+void metisTlvSkeleton_SetFragmentPayload(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+
+/**
+ * Updates the HopLimit inside the packet buffer
+ *
+ * If the HopLimit extent is not metisTlvExtent_NotFound, it will update the specified location
+ * with the new hoplimit. The call will fail if the HopLimit extent is not exactly 1 byte.
+ *
+ * @param [in] skeleton A parsed MetisTlvSkeleton
+ * @param [in] hoplimit The new value
+ *
+ * @retval true Updated
+ * @retval false Not updated (likely was metisTlvExtent_NotFound)
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_UpdateHopLimit(MetisTlvSkeleton *skeleton, uint8_t hoplimit);
+
+void metisTlvSkeleton_SetPathLabel(MetisTlvSkeleton *skeleton, size_t offset, size_t length);
+bool metisTlvSkeleton_UpdatePathLabel(MetisTlvSkeleton *opaque, uint8_t outFace);
+bool metisTlvSkeleton_ResetPathLabel(MetisTlvSkeleton *opaque);
+
+// ====================================
+// Getters
+
+
+MetisTlvExtent metisTlvSkeleton_GetPathLabel(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Name
+ *
+ * Returns the previously set Name extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetName(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetName(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the KeyId
+ *
+ * Returns the previously set KeyId extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetKeyId(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetKeyId(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Public Key.
+ *
+ * Returns the previously set Public Key extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetPublicKey(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetPublicKey(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Certificate.
+ *
+ * Returns the previously set Certificate extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetCertificate(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetCertificate(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Object Hash
+ *
+ * Returns the previously set Object hash extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetObjectHash(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetObjectHash(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Hop Limit
+ *
+ * Returns the previously set Hop Limit extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetObjectHash(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetHopLimit(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Interest Lifetime
+ *
+ * Returns the previously set Interest Lifetime extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetInterestLifetime(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetInterestLifetime(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Recommended Cache Time
+ *
+ * Returns the previously set Recommended Cache Time extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetCacheTimeHeader(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetCacheTimeHeader(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the Expiry Time
+ *
+ * Returns the previously set Expiry Time extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetExpiryTime(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetExpiryTime(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the CPI payload
+ *
+ * Returns the previously set CPI payload extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetCPI(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetCPI(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the extent of the fragment payload
+ *
+ * Returns the previously set fragment payload extent or metisTlvExtent_NotFound if not set.
+ *
+ * @param [in] skeleton An initialized and parsed skeleton
+ *
+ * @retval {0,0} Not Present
+ * @retval other The set value of the extent
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t *packet = // read from network
+ * MetisTlvSkeleton skeleton;
+ * if (metisTlvSkeleton_Parse(&skeleton, packet)) {
+ * MetisTlvExtent extent = metisTlvSkeleton_GetFragmentPayload(&skeleton);
+ * }
+ * }
+ * @endcode
+ */
+MetisTlvExtent metisTlvSkeleton_GetFragmentPayload(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the pointer to the packet buffer
+ *
+ * Returns pointer to byte 0 of the fixed header
+ *
+ * @param [in] skeleton A parsed MetisTlvSkeleton
+ *
+ * @retval non-null The packet buffer
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const uint8_t *metisTlvSkeleton_GetPacket(const MetisTlvSkeleton *skeleton);
+
+/**
+ * The total packet length based on the fixed header
+ *
+ * Parses the fixed header and returns the total packet length
+ * Will return "0" for unknown packet version
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval number Total packet length
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t metisTlvSkeleton_TotalPacket(const MetisTlvSkeleton *skeleton);
+
+/**
+ * @function metisTlv_ComputeContentObjectHash
+ * @abstract Computes the ContentObjectHash over a packet
+ * @discussion
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return NULL if not a content object, otherwise the SHA256 hash
+ */
+PARCCryptoHash *metisTlvSkeleton_ComputeContentObjectHash(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Determines if the packet type is Interest
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval true It is an Interest
+ * @retval false Not an Interest
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_IsPacketTypeInterest(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Determines if the packet type is Content Object
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval true It is a ContentObject
+ * @retval false Not a ContentObject
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_IsPacketTypeContentObject(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Determines if the packet type is Control
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval true It is a Control
+ * @retval false Not a Control
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_IsPacketTypeControl(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Determines if the packet type is InterestReturn
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval true It is a InterestReturn
+ * @retval false Not a InterestReturn
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_IsPacketTypeInterestReturn(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Determines if the packet type is HopByHop Fragment
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] packet Packet memory pointer
+ *
+ * @retval true It is a InterestReturn
+ * @retval false Not a InterestReturn
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool metisTlvSkeleton_IsPacketTypeHopByHopFragment(const MetisTlvSkeleton *skeleton);
+
+/**
+ * Returns the logger associated with the skeleton
+ *
+ * Returns the logger the user passed to the skeleton, which may be NULL
+ *
+ * @param [in] skeleton An initialized skeleton
+ *
+ * @retval non-null An allocated logger
+ * @retval null No logger associated with skeleton
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+MetisLogger *metisTlvSkeleton_GetLogger(const MetisTlvSkeleton *skeleton);
+#endif // Metis_metis_TlvSkeleton_h
diff --git a/metis/ccnx/forwarder/metis/tlv/test/.gitignore b/metis/ccnx/forwarder/metis/tlv/test/.gitignore
new file mode 100644
index 00000000..5cba902c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/.gitignore
@@ -0,0 +1,8 @@
+test_metis_Tlv
+test_metis_TlvExtent
+test_metis_TlvName
+test_metis_TlvNameCodec
+test_metis_TlvSchemaV0
+test_metis_TlvSchemaV1
+test_metis_TlvSkeleton
+
diff --git a/metis/ccnx/forwarder/metis/tlv/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/tlv/test/CMakeLists.txt
new file mode 100644
index 00000000..73f1d34d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_metis_Tlv
+ test_metis_TlvExtent
+ test_metis_TlvName
+ test_metis_TlvNameCodec
+ test_metis_TlvSchemaV0
+ test_metis_TlvSchemaV1
+ test_metis_TlvSkeleton
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_Tlv.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_Tlv.c
new file mode 100644
index 00000000..79a28ed3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_Tlv.c
@@ -0,0 +1,249 @@
+/*
+ * 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.
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_Tlv.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+LONGBOW_TEST_RUNNER(metis_Tlv)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_Tlv)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_Tlv)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_NameSegments);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_NameSegments_Realloc);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_ExtentToVarInt);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_FixedHeaderLength);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_TotalHeaderLength_V0);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_TotalHeaderLength_V1);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_TotalPacketLength_V0);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlv_TotalPacketLength_V1);
+
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, metisTlv_EncodeControlPlaneInformation_V0);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, metisTlv_EncodeControlPlaneInformation_V1);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+
+LONGBOW_TEST_CASE(Global, metisTlv_NameSegments)
+{
+ uint8_t name[] = {
+ 0x00, 0x02, 0x00, 0x05, // type = binary, length = 5
+ 'h', 'e', 'l', 'l',
+ 'o', // "hello"
+ 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4
+ 'o', 'u', 'c', 'h'
+ };
+
+ MetisTlvExtent *nameExtents;
+ size_t nameExtentsLength;
+
+ MetisTlvExtent truthExtents[] = { { .offset = 0, .length = 9 }, { .offset = 9, .length = 8 } };
+ size_t truthExtentsLength = 2;
+
+ metisTlv_NameSegments(name, sizeof(name), &nameExtents, &nameExtentsLength);
+
+ assertTrue(nameExtentsLength == truthExtentsLength, "nameExtentsLength wrong, expected %zu got %zu", truthExtentsLength, nameExtentsLength);
+ for (int i = 0; i < nameExtentsLength; i++) {
+ assertTrue(truthExtents[i].offset == nameExtents[i].offset,
+ "nameExtents[%d].offset wrong, expected %u got %u",
+ i,
+ truthExtents[i].offset,
+ nameExtents[i].offset);
+
+ assertTrue(truthExtents[i].length == nameExtents[i].length,
+ "nameExtents[%d].offset wrong, expected %u got %u",
+ i,
+ truthExtents[i].length,
+ nameExtents[i].length);
+ }
+
+ parcMemory_Deallocate((void **) &nameExtents);
+}
+
+/**
+ * Create a name with enough name components to cause a re-alloc in the parser
+ */
+LONGBOW_TEST_CASE(Global, metisTlv_NameSegments_Realloc)
+{
+ uint8_t oneSegment[] = {
+ 0x00, 0x02, 0x00, 0x04, // type = binary, length = 4
+ 'h', 'e', 'l', 'l'
+ };
+
+ // build a name with neededComponents copies of oneSegment such that it will
+ // exceed the initialLengthForNameExtents allocation in the parser
+
+ size_t neededComponents = _initialLengthForNameExtents + 2;
+ size_t nameBufferLength = neededComponents * sizeof(oneSegment);
+
+ uint8_t *nameBuffer = parcMemory_Allocate(nameBufferLength);
+ assertNotNull(nameBuffer, "parcMemory_Allocate(%zu) returned NULL", nameBufferLength);
+ for (int i = 0; i < neededComponents; i++) {
+ memcpy(nameBuffer + i * sizeof(oneSegment), oneSegment, sizeof(oneSegment));
+ }
+
+ MetisTlvExtent *nameExtents;
+ size_t nameExtentsLength;
+
+ metisTlv_NameSegments(nameBuffer, nameBufferLength, &nameExtents, &nameExtentsLength);
+
+ assertTrue(nameExtentsLength == neededComponents,
+ "metisTlv_NameSegments returned wrong number of segments, expected %zu got %zu",
+ neededComponents,
+ nameExtentsLength);
+
+
+ parcMemory_Deallocate((void **) &nameExtents);
+ parcMemory_Deallocate((void **) &nameBuffer);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_ExtentToVarInt)
+{
+ uint8_t packet[] = { 0xff, 0xff, 0x00, 0x01, 0x02, 0xff, 0xff };
+ MetisTlvExtent extent = { 2, 3 };
+ uint64_t truth = 0x0102;
+ uint64_t test = 0;
+
+ bool success = metisTlv_ExtentToVarInt(packet, &extent, &test);
+ assertTrue(success, "Failed to parse a good extent");
+ assertTrue(truth == test, "Wrong value, expected %#" PRIx64 "got %#" PRIx64, truth, test);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_FixedHeaderLength)
+{
+ size_t test = metisTlv_FixedHeaderLength();
+ assertTrue(test == 8, "Wrong fixed header length, got %zu", test);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_TotalHeaderLength_V0)
+{
+ size_t test = metisTlv_TotalHeaderLength(metisTestDataV0_EncodedInterest);
+ assertTrue(test == 29, "Wrong total header length, expected 29 got %zu", test);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_TotalHeaderLength_V1)
+{
+ size_t test = metisTlv_TotalHeaderLength(metisTestDataV1_Interest_AllFields);
+ assertTrue(test == 14, "Wrong total header length, expected 14 got %zu", test);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_TotalPacketLength_V0)
+{
+ size_t test = metisTlv_TotalPacketLength(metisTestDataV0_EncodedInterest);
+ assertTrue(test == sizeof(metisTestDataV0_EncodedInterest), "Wrong total header length, expected %zu got %zu", sizeof(metisTestDataV0_EncodedInterest), test);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlv_TotalPacketLength_V1)
+{
+ size_t test = metisTlv_TotalPacketLength(metisTestDataV1_Interest_AllFields);
+ assertTrue(test == sizeof(metisTestDataV1_Interest_AllFields), "Wrong total header length, expected %zu got %zu", sizeof(metisTestDataV1_Interest_AllFields), test);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, metisTlv_EncodeControlPlaneInformation_V0)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+
+ PARCBuffer *buffer = metisTlv_EncodeControlPlaneInformation(control);
+ ccnxControl_Release(&control);
+
+ assertNotNull(buffer, "Got null encoding buffer");
+ uint8_t *overlay = parcBuffer_Overlay(buffer, 0);
+
+ assertTrue(overlay[1] == 0xA4, "PacketType is not Control");
+ parcBuffer_Release(&buffer);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, metisTlv_EncodeControlPlaneInformation_V1)
+{
+ // there's no easy way to test this right now, cannot contruct a v1 CCNxControl
+}
+
+
+// ===================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ // computeHash and parseName Auth are tested called through by other tests
+
+ LONGBOW_RUN_TEST_CASE(Local, _metisTlv_ParseName);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, _metisTlv_ParseName)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_Tlv);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvExtent.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvExtent.c
new file mode 100644
index 00000000..f61904f9
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvExtent.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvExtent.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(tlv_Extent)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(tlv_Extent)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(tlv_Extent)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, tlvExtent_Equals_IsEqual);
+ LONGBOW_RUN_TEST_CASE(Global, tlvExtent_Equals_IsNotEqual);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, tlvExtent_Equals_IsEqual)
+{
+ MetisTlvExtent a = { .offset = 5, .length = 7 };
+ MetisTlvExtent b = { .offset = 5, .length = 7 };
+ MetisTlvExtent c = { .offset = 5, .length = 7 };
+
+ // transitivity testing too
+ assertTrue(metisTlvExtent_Equals(&a, &b), "Two equal extents did not compare equal");
+ assertTrue(metisTlvExtent_Equals(&b, &c), "Two equal extents did not compare equal");
+ assertTrue(metisTlvExtent_Equals(&c, &a), "Two equal extents did not compare equal");
+}
+
+LONGBOW_TEST_CASE(Global, tlvExtent_Equals_IsNotEqual)
+{
+ MetisTlvExtent a = { .offset = 5, .length = 7 };
+ MetisTlvExtent b = { .offset = 3, .length = 7 };
+
+ assertFalse(metisTlvExtent_Equals(&a, &b), "Two unequal extents compare equal");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(tlv_Extent);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvName.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvName.c
new file mode 100644
index 00000000..5fba7669
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvName.c
@@ -0,0 +1,470 @@
+/*
+ * 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.
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvName.c"
+#include <LongBow/unit-test.h>
+
+#include <stdint.h>
+#include <limits.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+uint8_t encoded_name[] = {
+ 0x00, 0x02, 0x00, 0x05, // type = binary, length = 5
+ 'h', 'e', 'l', 'l',
+ 'o', // "hello"
+ 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4
+ 'o', 'u', 'c', 'h', // value = "ouch"
+ 0xF0, 0x01, 0x00, 0x02, // type = app, length = 2
+ 0x01, 0xFF // value = 0x01FF
+};
+
+uint8_t second_name[] = {
+ 0x00, 0x02, 0x00, 0x05, // type = binary, length = 5
+ 'h', 'e', 'l', 'l',
+ 'o', // "hello"
+ 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4
+ 'o', 'u', 'c', 'h', // value = "ouch"
+ 0xF0, 0x01, 0x00, 0x02, // type = app, length = 2
+ 0xFF, 0xFF // value = 0xFFFF
+};
+
+uint8_t prefixOf_name[] = {
+ 0x00, 0x02, 0x00, 0x05, // type = binary, length = 5
+ 'h', 'e', 'l', 'l',
+ 'o', // "hello"
+ 0xF0, 0x00, 0x00, 0x04, // type = app, length = 4
+ 'o', 'u', 'c', 'h', // value = "ouch"
+};
+
+uint8_t default_route_name[] = {
+ 0x00, 0x01, 0x00, 0x00, // type = name, length = 0
+};
+
+LONGBOW_TEST_RUNNER(metis_TlvName)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_TlvName)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TlvName)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Acquire);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost0);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost1);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost2);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMostAll);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_CreateFromCCNxName);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_CreateFromCCNxName_DefaultRoute);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Equals_IsEqual);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Equals_SameCountDifferentBytes);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Equals_DifferentCount);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Compare);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Compare_DefaultRoute);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_Compare_DefaultRoute_Binary);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_HashCode);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_SegmentCount);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_StartsWith_SelfPrefix);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_StartsWith_IsPrefix);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_StartsWith_PrefixTooLong);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvName_StartsWith_IsNotPrefix);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Acquire)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+
+ MetisTlvName *copy = metisTlvName_Acquire(name);
+ assertTrue(_getRefCount(name) == 2, "Name created with wrong refcount, expected %u got %u", 2, _getRefCount(name));
+
+ metisTlvName_Release(&copy);
+ assertTrue(_getRefCount(name) == 1, "Name created with wrong refcount, expected %u got %u", 1, _getRefCount(name));
+
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Create_Destroy)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ assertTrue(_getRefCount(name) == 1, "Name created with wrong refcount, expected %u got %u", 1, _getRefCount(name));
+ metisTlvName_Release(&name);
+
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance after create/destroy: %u", parcMemory_Outstanding());
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_CreateFromCCNxName)
+{
+ char uri[] = "lci:/2=hello/0xF000=ouch/0xF001=%01%FF";
+ CCNxName *ccnxName = ccnxName_CreateFromCString(uri);
+
+ MetisTlvName *truth = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *name = metisTlvName_CreateFromCCNxName(ccnxName);
+
+ assertTrue(metisTlvName_Equals(truth, name), "MetisTlvName from ccnxName did not equal expected");
+ metisTlvName_Release(&name);
+ metisTlvName_Release(&truth);
+ ccnxName_Release(&ccnxName);
+
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance after create/destroy: %u", parcMemory_Outstanding());
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_CreateFromCCNxName_DefaultRoute)
+{
+ char uri[] = "lci:/";
+ CCNxName *ccnxName = ccnxName_CreateFromCString(uri);
+
+ MetisTlvName *truth = metisTlvName_Create(default_route_name, sizeof(default_route_name));
+ MetisTlvName *name = metisTlvName_CreateFromCCNxName(ccnxName);
+
+ assertTrue(metisTlvName_Equals(truth, name), "MetisTlvName from ccnxName did not equal expected");
+ metisTlvName_Release(&name);
+ metisTlvName_Release(&truth);
+ ccnxName_Release(&ccnxName);
+
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance after create/destroy: %u", parcMemory_Outstanding());
+}
+
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Equals_IsEqual)
+{
+ MetisTlvName *a = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *b = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+
+ assertTrue(metisTlvName_Equals(a, b), "Two equal names did not compare");
+ metisTlvName_Release(&a);
+ metisTlvName_Release(&b);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Equals_SameCountDifferentBytes)
+{
+ MetisTlvName *a = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *b = metisTlvName_Create(second_name, sizeof(second_name));
+
+ assertFalse(metisTlvName_Equals(a, b), "Two names with same # component but differnet bytes compared the same.");
+ metisTlvName_Release(&a);
+ metisTlvName_Release(&b);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Equals_DifferentCount)
+{
+ MetisTlvName *a = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *b = metisTlvName_Create(prefixOf_name, sizeof(prefixOf_name));
+
+ assertFalse(metisTlvName_Equals(a, b), "Two names with different # component compared the same.");
+ metisTlvName_Release(&a);
+ metisTlvName_Release(&b);
+}
+
+int
+compareWrapper(void *a, void *b)
+{
+ return metisTlvName_Compare((MetisTlvName *) a, (MetisTlvName *) b);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Compare)
+{
+ CCNxName *basename = ccnxName_CreateFromCString("lci:/middle/of/6=the");
+ CCNxName *equal_1 = ccnxName_CreateFromCString("lci:/middle/of/6=the");
+ CCNxName *defaultRoute = ccnxName_CreateFromCString("lci:/");
+ CCNxName *lesser_by_count = ccnxName_CreateFromCString("lci:/middle/of");
+ CCNxName *lesser_by_value = ccnxName_CreateFromCString("lci:/middle/of/6=th");
+ CCNxName *lesser_by_type_2 = ccnxName_CreateFromCString("lci:/middle/of/2=the");
+ CCNxName *greater_by_count = ccnxName_CreateFromCString("lci:/middle/of/the/road");
+ CCNxName *greater_by_type = ccnxName_CreateFromCString("lci:/middle/of/7=the");
+ CCNxName *greater_by_value = ccnxName_CreateFromCString("lci:/middle/of/the/town");
+ CCNxName *greater_2 = ccnxName_CreateFromCString("lci:/nox/arcana/occulta");
+
+ void *equivalent[] = { equal_1, NULL };
+ void *lesser[] = { defaultRoute, lesser_by_count, lesser_by_type_2, lesser_by_value, NULL };
+ void *greater[] = { greater_by_count, greater_by_type, greater_by_value, greater_2, NULL };
+
+ MetisTlvName *tlv_basename = metisTlvName_CreateFromCCNxName(basename);
+ void **tlv_equivalent = parcMemory_AllocateAndClear(sizeof(equivalent) * sizeof(void *));
+ assertNotNull(tlv_equivalent, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(equivalent) * sizeof(void *));
+ void **tlv_lesser = parcMemory_AllocateAndClear(sizeof(lesser) * sizeof(void *));
+ assertNotNull(tlv_lesser, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(lesser) * sizeof(void *));
+ void **tlv_greater = parcMemory_AllocateAndClear(sizeof(greater) * sizeof(void *));
+ assertNotNull(tlv_greater, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(greater) * sizeof(void *));
+
+ for (int i = 0; equivalent[i] != NULL; i++) {
+ tlv_equivalent[i] = metisTlvName_CreateFromCCNxName(equivalent[i]);
+ }
+
+ for (int i = 0; lesser[i] != NULL; i++) {
+ tlv_lesser[i] = metisTlvName_CreateFromCCNxName(lesser[i]);
+ }
+
+ for (int i = 0; greater[i] != NULL; i++) {
+ tlv_greater[i] = metisTlvName_CreateFromCCNxName(greater[i]);
+ }
+
+ // Use this:
+ assertCompareToContract(compareWrapper, tlv_basename, tlv_equivalent, tlv_lesser, tlv_greater);
+ // Not this, unless you provide the necessary casts to avoid warnings.
+ // longbow_AssertCompareToContract(compareWrapper, tlv_basename, tlv_equivalent, tlv_lesser, tlv_greater);
+
+ for (int i = 0; equivalent[i] != NULL; i++) {
+ ccnxName_Release((CCNxName **) &equivalent[i]);
+ metisTlvName_Release((MetisTlvName **) &tlv_equivalent[i]);
+ }
+
+ for (int i = 0; lesser[i] != NULL; i++) {
+ ccnxName_Release((CCNxName **) &lesser[i]);
+ metisTlvName_Release((MetisTlvName **) &tlv_lesser[i]);
+ }
+
+ for (int i = 0; greater[i] != NULL; i++) {
+ ccnxName_Release((CCNxName **) &greater[i]);
+ metisTlvName_Release((MetisTlvName **) &tlv_greater[i]);
+ }
+
+ ccnxName_Release(&basename);
+ metisTlvName_Release(&tlv_basename);
+ parcMemory_Deallocate((void **) &tlv_equivalent);
+ parcMemory_Deallocate((void **) &tlv_greater);
+ parcMemory_Deallocate((void **) &tlv_lesser);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Compare_DefaultRoute)
+{
+ CCNxName *defaultRoute = ccnxName_CreateFromCString("lci:/");
+ MetisTlvName *metisDefaultRoute = metisTlvName_CreateFromCCNxName(defaultRoute);
+
+ // THis name cannot be constructed via CCNxName, so do it as a byte array
+ // Empty name with "0" type
+ uint8_t shortest[] = { 0x00, 0x00, 0x00, 4,
+ 0x00, 0x00, 0x00, 0 };
+
+ MetisTlvName *metisShortest = metisTlvName_Create(shortest, sizeof(shortest));
+
+ int compare = metisTlvName_Compare(metisDefaultRoute, metisShortest);
+ assertTrue(compare < 0, "Default route should have compared less than shortest name, compared = %d", compare);
+
+ metisTlvName_Release(&metisShortest);
+ metisTlvName_Release(&metisDefaultRoute);
+ ccnxName_Release(&defaultRoute);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Compare_DefaultRoute_Binary)
+{
+ // The empty name (default route)
+ uint8_t defaultRoute[] = { 0x00, 0x00, 0x00, 0};
+ MetisTlvName *metisDefaultRoute = metisTlvName_Create(defaultRoute, sizeof(defaultRoute));
+
+ // THis name cannot be constructed via CCNxName, so do it as a byte array
+ // Empty name with "0" type
+ uint8_t shortest[] = { 0x00, 0x00, 0x00, 4,
+ 0x00, 0x00, 0x00, 0 };
+
+ MetisTlvName *metisShortest = metisTlvName_Create(shortest, sizeof(shortest));
+
+ int compare = metisTlvName_Compare(metisDefaultRoute, metisShortest);
+ assertTrue(compare < 0, "Default route should have compared less than shortest name, compared = %d", compare);
+
+ metisTlvName_Release(&metisShortest);
+ metisTlvName_Release(&metisDefaultRoute);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_HashCode)
+{
+ // first, compute the hashes of the name
+ uint32_t hash_0 = parcHash32_Data(&encoded_name[0], 9);
+ uint32_t hash_1 = parcHash32_Data_Cumulative(&encoded_name[ 9], 8, hash_0);
+ uint32_t hash_2 = parcHash32_Data_Cumulative(&encoded_name[17], 6, hash_1);
+
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+
+ uint32_t test_hash;
+ test_hash = metisTlvName_HashCode(name);
+ assertTrue(test_hash == hash_2, "Incorrect hash for segment %d, expected %08X got %08X", 2, hash_2, test_hash);
+
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost0)
+{
+ unsigned copyLength = 0;
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *copy = metisTlvName_Slice(name, copyLength);
+
+ // hash of a 0-length name is 0
+ uint32_t hash_0 = 0;
+
+ assertTrue(_getRefCount(name) == 2, "Wrong refcount in name, expected %u got %u", 2, _getRefCount(name));
+ assertTrue(_getRefCount(copy) == 2, "Wrong refcount in copy, expected %u got %u", 2, _getRefCount(copy));
+ assertTrue(copy->segmentArrayLength == copyLength, "Wrong array length, expected %u got %zu", copyLength, copy->segmentArrayLength);
+ uint32_t test_hash = metisTlvName_HashCode(copy);
+ assertTrue(test_hash == hash_0, "Incorrect hash for segment %d, expected %08X got %08X", 0, hash_0, test_hash);
+
+ metisTlvName_Release(&copy);
+ assertTrue(_getRefCount(name) == 1, "Wrong refcount in name, expected %u got %u", 1, _getRefCount(name));
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost1)
+{
+ unsigned copyLength = 1;
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *copy = metisTlvName_Slice(name, copyLength);
+ uint32_t hash_0 = parcHash32_Data(&encoded_name[0], 9);
+
+ assertTrue(_getRefCount(name) == 2, "Wrong refcount in name, expected %u got %u", 2, _getRefCount(name));
+ assertTrue(_getRefCount(copy) == 2, "Wrong refcount in copy, expected %u got %u", 2, _getRefCount(copy));
+ assertTrue(copy->segmentArrayLength == copyLength, "Wrong array length, expected %u got %zu", copyLength, copy->segmentArrayLength);
+ uint32_t test_hash = metisTlvName_HashCode(copy);
+ assertTrue(test_hash == hash_0, "Incorrect hash for segment %d, expected %08X got %08X", 0, hash_0, test_hash);
+
+ metisTlvName_Release(&copy);
+ assertTrue(_getRefCount(name) == 1, "Wrong refcount in name, expected %u got %u", 1, _getRefCount(name));
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMost2)
+{
+ unsigned copyLength = 2;
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *copy = metisTlvName_Slice(name, copyLength);
+ uint32_t hash_0 = parcHash32_Data(&encoded_name[0], 9);
+ uint32_t hash_1 = parcHash32_Data_Cumulative(&encoded_name[ 9], 8, hash_0);
+
+ assertTrue(_getRefCount(name) == 2, "Wrong refcount in name, expected %u got %u", 2, _getRefCount(name));
+ assertTrue(_getRefCount(copy) == 2, "Wrong refcount in copy, expected %u got %u", 2, _getRefCount(copy));
+ assertTrue(copy->segmentArrayLength == copyLength, "Wrong array length, expected %u got %zu", copyLength, copy->segmentArrayLength);
+ uint32_t test_hash = metisTlvName_HashCode(copy);
+ assertTrue(test_hash == hash_1, "Incorrect hash for segment %d, expected %08X got %08X", 1, hash_1, test_hash);
+
+ metisTlvName_Release(&copy);
+ assertTrue(_getRefCount(name) == 1, "Wrong refcount in name, expected %u got %u", 1, _getRefCount(name));
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_Acquire_CopyAtMostAll)
+{
+ unsigned copyLength = 3;
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *copy = metisTlvName_Slice(name, UINT_MAX);
+ uint32_t hash_0 = parcHash32_Data(&encoded_name[0], 9);
+ uint32_t hash_1 = parcHash32_Data_Cumulative(&encoded_name[ 9], 8, hash_0);
+ uint32_t hash_2 = parcHash32_Data_Cumulative(&encoded_name[17], 6, hash_1);
+
+ assertTrue(_getRefCount(name) == 2, "Wrong refcount in name, expected %u got %u", 2, _getRefCount(name));
+ assertTrue(_getRefCount(copy) == 2, "Wrong refcount in copy, expected %u got %u", 2, _getRefCount(copy));
+ assertTrue(copy->segmentArrayLength == copyLength, "Wrong array length, expected %u got %zu", copyLength, copy->segmentArrayLength);
+ uint32_t test_hash = metisTlvName_HashCode(copy);
+ assertTrue(test_hash == hash_2, "Incorrect hash for segment %d, expected %08X got %08X", 2, hash_2, test_hash);
+
+ metisTlvName_Release(&copy);
+ assertTrue(_getRefCount(name) == 1, "Wrong refcount in name, expected %u got %u", 1, _getRefCount(name));
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_SegmentCount)
+{
+ MetisTlvName *a = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+
+ size_t count = metisTlvName_SegmentCount(a);
+ assertTrue(count == 3, "Incorrect segment count, expected %u got %zu", 3, count);
+
+ metisTlvName_Release(&a);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_StartsWith_SelfPrefix)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+
+ // a name is always a prefix of itself
+ bool success = metisTlvName_StartsWith(name, name);
+ assertTrue(success, "Name is not prefix of self in metisTlvName_StartsWith");
+ metisTlvName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_StartsWith_IsPrefix)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *prefix = metisTlvName_Create(prefixOf_name, sizeof(prefixOf_name));
+
+ bool success = metisTlvName_StartsWith(name, prefix);
+ assertTrue(success, "Valid prefix did not test true in metisTlvName_StartsWith");
+ metisTlvName_Release(&name);
+ metisTlvName_Release(&prefix);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_StartsWith_PrefixTooLong)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *prefix = metisTlvName_Create(prefixOf_name, sizeof(prefixOf_name));
+
+ // we just reversed the prefix and name from the test metisTlvName_StartsWith_IsPrefix,
+ // so the prefix is longer than the name
+ bool success = metisTlvName_StartsWith(prefix, name);
+ assertFalse(success, "Invalid prefix tested true in metisTlvName_StartsWith");
+ metisTlvName_Release(&name);
+ metisTlvName_Release(&prefix);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvName_StartsWith_IsNotPrefix)
+{
+ MetisTlvName *name = metisTlvName_Create(encoded_name, sizeof(encoded_name));
+ MetisTlvName *other = metisTlvName_Create(second_name, sizeof(second_name));
+
+ // we just reversed the prefix and name from the test metisTlvName_StartsWith_IsPrefix
+ bool success = metisTlvName_StartsWith(other, name);
+ assertFalse(success, "Invalid prefix tested true in metisTlvName_StartsWith");
+ metisTlvName_Release(&name);
+ metisTlvName_Release(&other);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TlvName);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvNameCodec.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvNameCodec.c
new file mode 100644
index 00000000..121adf29
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvNameCodec.c
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvNameCodec.c"
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(tlv_NameCodec)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(tlv_NameCodec)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(tlv_NameCodec)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, tlvName_Decode_0_Length_Name);
+ LONGBOW_RUN_TEST_CASE(Global, tlvName_Decode_0_Length_Segment);
+ LONGBOW_RUN_TEST_CASE(Global, tlvName_Decode_Good);
+ LONGBOW_RUN_TEST_CASE(Global, tlvName_Decode_Overflow);
+ LONGBOW_RUN_TEST_CASE(Global, tlvName_Decode_UnderRun);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/**
+ * Buffer is 1 .. 3 bytes
+ */
+LONGBOW_TEST_CASE_EXPECTS(Global, tlvName_Decode_UnderRun, .event = &LongBowTrapIllegalValue)
+{
+ // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // |-- type --|-- length --||
+ uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x04, 0xFF };
+
+ // This will assert
+ // CCNxName *name =
+ metisTlvNameCodec_Decode(buffer, 5, 6);
+}
+
+/**
+ * Buffer exactly 0 bytes
+ */
+LONGBOW_TEST_CASE(Global, tlvName_Decode_0_Length_Name)
+{
+ // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // |-- type --|-- length --||
+ uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x04, 0xFF };
+
+ // skip the two 0xFF bytes
+ // name = "lci:/%02=abcd"
+ CCNxName *test = metisTlvNameCodec_Decode(buffer, 5, 5);
+ CCNxName *truth = ccnxName_Create();
+ char *nameString = ccnxName_ToString(test);
+
+ assertTrue(ccnxName_Equals(truth, test), "Names not equal, got %s", nameString);
+
+ parcMemory_Deallocate((void **) &nameString);
+ ccnxName_Release(&truth);
+ ccnxName_Release(&test);
+}
+
+/**
+ * Buffer exactly 4 bytes
+ */
+LONGBOW_TEST_CASE(Global, tlvName_Decode_0_Length_Segment)
+{
+ // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // |-- type --|-- length --|-- type --|-- length --||
+ uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0xFF };
+
+ // skip the two 0xFF bytes
+ // name = "lci:/%02=abcd"
+ CCNxName *test = metisTlvNameCodec_Decode(buffer, 5, 9);
+// CCNxName *truth = ccnxName_CreateFromCString("lci:/%02=");
+ CCNxName *truth = ccnxName_CreateFromCString("lci:/2=");
+ char *nameString = ccnxName_ToString(test);
+
+ assertTrue(ccnxName_Equals(truth, test), "Names not equal, got %s", nameString);
+
+ parcMemory_Deallocate((void **) &nameString);
+ ccnxName_Release(&truth);
+ ccnxName_Release(&test);
+}
+
+/**
+ * A good, normal name
+ */
+LONGBOW_TEST_CASE(Global, tlvName_Decode_Good)
+{
+ // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // |-- type --|-- length --|-- type --|-- length --| ----- value -----|
+ uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x04, 'a', 'b', 'c', 'd', 0xFF };
+
+ // skip the two 0xFF bytes
+ // name = "lci:/%02=abcd"
+ CCNxName *test = metisTlvNameCodec_Decode(buffer, 5, 13);
+
+// CCNxName *truth = ccnxName_CreateFromCString("lci:/%02=abcd");
+ CCNxName *truth = ccnxName_CreateFromCString("lci:/2=abcd");
+ char *nameString = ccnxName_ToString(test);
+
+ assertTrue(ccnxName_Equals(truth, test), "Names not equal, got %s", nameString);
+
+ parcMemory_Deallocate((void **) &nameString);
+ ccnxName_Release(&truth);
+ ccnxName_Release(&test);
+}
+
+/**
+ * The name component length shoots beyond the end of the buffer. Byte 8 is "5" instead of "4".
+ */
+LONGBOW_TEST_CASE_EXPECTS(Global, tlvName_Decode_Overflow, .event = &LongBowTrapIllegalValue)
+{
+ // offset 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // |-- type --|-- length --|-- type --|-- length --| ----- value -----|
+ uint8_t buffer[] = { 0xFF, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x05, 'a', 'b', 'c', 'd', 0xFF };
+
+ // This will trap because the length 5 will go beyond 12
+ metisTlvNameCodec_Decode(buffer, 5, 13);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(tlv_NameCodec);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV0.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV0.c
new file mode 100644
index 00000000..bfb5a47c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV0.c
@@ -0,0 +1,342 @@
+/*
+ * 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.
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvSchemaV0.c"
+#include "../metis_TlvSkeleton.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+static void
+verifyInterestPerHop(MetisTlvSkeleton *skeleton)
+{
+ MetisTlvExtent extent = metisTlvSkeleton_GetHopLimit(skeleton);
+ assertTrue(extent.offset == 12, "Incorrect hopLimit offset, expected %u got %u", 12, extent.offset);
+ assertTrue(extent.length == 1, "Incorrect hopLimit length, expected %u got %u", 1, extent.length);
+}
+
+static void
+verifyInterestSkeleton(MetisTlvSkeleton *skeleton)
+{
+ MetisTlvExtent nameExtent = metisTlvSkeleton_GetName(skeleton);
+ assertTrue(nameExtent.offset == 37, "Incorrect name offset, expected %u got %u", 37, nameExtent.offset);
+ assertTrue(nameExtent.length == 17, "Incorrect name length, expected %u got %u", 17, nameExtent.length);
+
+ MetisTlvExtent keyidExtent = metisTlvSkeleton_GetKeyId(skeleton);
+ assertTrue(keyidExtent.offset == 58, "Incorrect keyId offset, expected %u got %u", 58, keyidExtent.offset);
+ assertTrue(keyidExtent.length == 4, "Incorrect keyId length, expected %u got %u", 4, keyidExtent.length);
+
+ MetisTlvExtent objHashExtent = metisTlvSkeleton_GetObjectHash(skeleton);
+ assertTrue(objHashExtent.offset == 66, "Incorrect objectHash offset, expected %u got %u", 66, objHashExtent.offset);
+ assertTrue(objHashExtent.length == 6, "Incorrect objectHash length, expected %u got %u", 6, objHashExtent.length);
+
+ MetisTlvExtent lifetimeExtent = metisTlvSkeleton_GetInterestLifetime(skeleton);
+ assertTrue(lifetimeExtent.offset == 81, "Incorrect interestLifetime offset, expected %u got %u", 81, lifetimeExtent.offset);
+ assertTrue(lifetimeExtent.length == 2, "Incorrect interestLifetime length, expected %u got %u", 2, lifetimeExtent.length);
+}
+
+static void
+verifyObjectPerHop(MetisTlvSkeleton *skeleton)
+{
+ MetisTlvExtent hoplimitExtent = metisTlvSkeleton_GetHopLimit(skeleton);
+ assertTrue(hoplimitExtent.offset == 12, "Incorrect hopLimit offset, expected %u got %u", 12, hoplimitExtent.offset);
+ assertTrue(hoplimitExtent.length == 1, "Incorrect hopLimit length, expected %u got %u", 1, hoplimitExtent.length);
+}
+
+static void
+verifyObjectSkeleton(MetisTlvSkeleton *skeleton)
+{
+ MetisTlvExtent nameExtent = metisTlvSkeleton_GetName(skeleton);
+ assertTrue(nameExtent.offset == metisTestDataV0_EncodedObject_name.offset, "Incorrect name offset, expected %u got %u", metisTestDataV0_EncodedObject_name.offset, nameExtent.offset);
+ assertTrue(nameExtent.length == metisTestDataV0_EncodedObject_name.length, "Incorrect name length, expected %u got %u", metisTestDataV0_EncodedObject_name.length, nameExtent.length);
+
+ MetisTlvExtent keyidExtent = metisTlvSkeleton_GetKeyId(skeleton);
+ assertTrue(keyidExtent.offset == metisTestDataV0_EncodedObject_keyid.offset, "Incorrect keyId offset, expected %u got %u", metisTestDataV0_EncodedObject_keyid.offset, keyidExtent.offset);
+ assertTrue(keyidExtent.length == metisTestDataV0_EncodedObject_keyid.length, "Incorrect keyId length, expected %u got %u", metisTestDataV0_EncodedObject_keyid.length, keyidExtent.length);
+}
+
+LONGBOW_TEST_RUNNER(metis_TlvSchemaV0)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_TlvSchemaV0)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TlvSchemaV0)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_ComputeContentObjectHash);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Interest);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Object);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Control);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeInterest_True);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeContentObject_True);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeInterest_False);
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeContentObject_False);
+
+ LONGBOW_RUN_TEST_CASE(Global, metisTlvSchemaV0_EncodeControlPlaneInformation);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_ComputeContentObjectHash)
+{
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) metisTestDataV0_EncodedObject;
+ size_t headerLength = htons(hdr->headerLength);
+ size_t endHeaders = FIXED_HEADER_LEN + headerLength;
+ size_t endPacket = metisTlv_TotalPacketLength((uint8_t *) hdr);
+
+ uint8_t *start = &metisTestDataV0_EncodedObject[endHeaders];
+ size_t length = endPacket - endHeaders;
+
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBytes(hasher, start, length);
+
+ PARCCryptoHash *hash_truth = parcCryptoHasher_Finalize(hasher);
+
+ PARCCryptoHash *hash_test = _computeContentObjectHash(metisTestDataV0_EncodedObject);
+
+ assertTrue(parcCryptoHash_Equals(hash_truth, hash_test),
+ "Content object digests did not match: truth %s test %s",
+ parcBuffer_ToString(parcCryptoHash_GetDigest(hash_truth)),
+ parcBuffer_ToString(parcCryptoHash_GetDigest(hash_test)));
+
+ parcCryptoHash_Release(&hash_truth);
+ parcCryptoHash_Release(&hash_test);
+ parcCryptoHasher_Release(&hasher);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Interest)
+{
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedInterest, logger);
+ _parse(&opaque);
+ verifyInterestPerHop(&opaque);
+ verifyInterestSkeleton(&opaque);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Object)
+{
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedObject, logger);
+ _parse(&opaque);
+ verifyObjectPerHop(&opaque);
+ verifyObjectSkeleton(&opaque);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_Skeleton_Control)
+{
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_CPIMessage, logger);
+ _parse(&opaque);
+
+ MetisTlvExtent cpiExtent = metisTlvSkeleton_GetCPI(&opaque);
+ assertTrue(cpiExtent.offset == 12, "cpi offset wrong, got %u expected %u", cpiExtent.offset, 12);
+ assertTrue(cpiExtent.length == 47, "cpi length wrong, got %u expected %u", cpiExtent.length, 47);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeInterest_True)
+{
+ bool result = _isPacketTypeInterest(metisTestDataV0_EncodedInterest);
+ assertTrue(result, "Interest packet type did not return true for IsInterest test");
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeContentObject_True)
+{
+ bool result = _isPacketTypeContentObject(metisTestDataV0_EncodedObject);
+ assertTrue(result, "ContentObject packet type did not return true for IsContentObject test");
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeInterest_False)
+{
+ bool result = _isPacketTypeInterest(metisTestDataV0_EncodedObject);
+ assertFalse(result, "ContentObject packet type did not return false for IsInterest test");
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_IsPacketTypeContentObject_False)
+{
+ bool result = _isPacketTypeContentObject(metisTestDataV0_EncodedInterest);
+ assertFalse(result, "Interest packet type did not return false for IsContentObject test");
+}
+
+LONGBOW_TEST_CASE(Global, metisTlvSchemaV0_EncodeControlPlaneInformation)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ PARCBuffer *buffer = _encodeControlPlaneInformation(control);
+ PARCBuffer *truth = parcBuffer_Flip(parcBuffer_PutArray(parcBuffer_Allocate(sizeof(metisTestDataV0_CPIMessage)), sizeof(metisTestDataV0_CPIMessage), metisTestDataV0_CPIMessage));
+
+ assertTrue(parcBuffer_Equals(truth, buffer), "Buffers not equal")
+ {
+ printf("expected:\n");
+ parcBuffer_Display(truth, 3);
+ printf("got:\n");
+ parcBuffer_Display(buffer, 3);
+ }
+ ccnxControl_Release(&control);
+ parcBuffer_Release(&truth);
+ parcBuffer_Release(&buffer);
+}
+
+
+// ======================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ // computeHash and parseName Auth are tested called through by other tests
+
+ LONGBOW_RUN_TEST_CASE(Local, _parseInterestV0);
+ LONGBOW_RUN_TEST_CASE(Local, _parseObjectV0);
+ LONGBOW_RUN_TEST_CASE(Local, _parsePerHopV0_Interest);
+ LONGBOW_RUN_TEST_CASE(Local, _parsePerHopV0_Object);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, _parseInterestV0)
+{
+ MetisTlvSkeleton skeleton;
+ memset(&skeleton, 0, sizeof(skeleton));
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedInterest, logger);
+
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) metisTestDataV0_EncodedInterest;
+ size_t headerLength = htons(hdr->headerLength);
+ size_t endHeaders = FIXED_HEADER_LEN + headerLength;
+ size_t endPacket = metisTlv_TotalPacketLength((uint8_t *) hdr);
+
+ _parseInterestV0(metisTestDataV0_EncodedInterest, endHeaders, endPacket, &skeleton);
+ verifyInterestSkeleton(&skeleton);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseObjectV0)
+{
+ MetisTlvSkeleton skeleton;
+ memset(&skeleton, 0, sizeof(skeleton));
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedInterest, logger);
+
+ _MetisTlvFixedHeaderV0 *hdr = (_MetisTlvFixedHeaderV0 *) metisTestDataV0_EncodedObject;
+ size_t headerLength = htons(hdr->headerLength);
+ size_t endHeaders = FIXED_HEADER_LEN + headerLength;
+ size_t endPacket = metisTlv_TotalPacketLength((uint8_t *) hdr);
+
+ _parseObjectV0(metisTestDataV0_EncodedObject, endHeaders, endPacket, &skeleton);
+ verifyObjectSkeleton(&skeleton);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parsePerHopV0_Interest)
+{
+ MetisTlvSkeleton skeleton;
+ memset(&skeleton, 0, sizeof(skeleton));
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedInterest, logger);
+
+ _parsePerHopV0(metisTestDataV0_EncodedInterest, 8, 29, &skeleton);
+ verifyInterestPerHop(&skeleton);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parsePerHopV0_Object)
+{
+ MetisTlvSkeleton skeleton;
+ memset(&skeleton, 0, sizeof(skeleton));
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV0_Ops, metisTestDataV0_EncodedInterest, logger);
+
+ _parsePerHopV0(metisTestDataV0_EncodedObject, 8, 29, &skeleton);
+ verifyObjectPerHop(&skeleton);
+ metisLogger_Release(&logger);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TlvSchemaV0);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV1.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV1.c
new file mode 100644
index 00000000..75dcd68d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSchemaV1.c
@@ -0,0 +1,518 @@
+/*
+ * 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.
+ */
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvSchemaV1.c"
+#include "../metis_TlvSkeleton.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+
+LONGBOW_TEST_RUNNER(metis_TlvSchemaV1)
+{
+ LONGBOW_RUN_TEST_FIXTURE(TlvOpsFunctions);
+ LONGBOW_RUN_TEST_FIXTURE(Local);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_TlvSchemaV1)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TlvSchemaV1)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ======================================================
+
+LONGBOW_TEST_FIXTURE(TlvOpsFunctions)
+{
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_Interest);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_ContentObject);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_Control);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_InterestReturn);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_Unknown);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _parse_HopByHopFragment);
+
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _computeContentObjectHash);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _encodeControlPlaneInformation);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _fixedHeaderLength);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _totalHeaderLength);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _totalPacketLength);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _isPacketTypeInterest);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _isPacketTypeContentObject);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _isPacketTypeInterestReturn);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _isPacketTypeControl);
+ LONGBOW_RUN_TEST_CASE(TlvOpsFunctions, _isPacketTypeHopByHopFragment);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(TlvOpsFunctions)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(TlvOpsFunctions)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_Interest)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, metisTestDataV1_Interest_AllFields, logger);
+ bool success = _parse(&skeleton);
+ assertTrue(success, "_parse(Interest) did not succeed");
+
+ // spot check
+ {
+ MetisTlvExtent trueExtent = { .offset = 4, .length = 1 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetHopLimit(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong hoplimit extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+
+ {
+ MetisTlvExtent trueExtent = { .offset = 12, .length = 2 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetInterestLifetime(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong interest lifetime extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+
+ {
+ MetisTlvExtent trueExtent = { .offset = 22, .length = 8 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetName(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong name extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+
+ {
+ MetisTlvExtent trueExtent = { .offset = 34, .length = 16 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetKeyId(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong keyid extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+
+ {
+ MetisTlvExtent trueExtent = { .offset = 54, .length = 32 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetObjectHash(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong objhash extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_ContentObject)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, metisTestDataV1_ContentObject_NameA_Crc32c, logger);
+ bool success = _parse(&skeleton);
+ assertTrue(success, "_parse(ContentObject) did not succeed");
+
+ // spot check
+ {
+ MetisTlvExtent trueExtent = { .offset = 36, .length = 8 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetCacheTimeHeader(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong cache time extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+
+ {
+ MetisTlvExtent trueExtent = { .offset = 52, .length = 17 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetName(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong name extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_Control)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, metisTestDataV1_CPI_AddRoute_Crc32c, logger);
+ bool success = _parse(&skeleton);
+ assertTrue(success, "_parse(Control) did not succeed");
+
+ // spot check
+ {
+ MetisTlvExtent trueExtent = { .offset = 12, .length = 154 };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetCPI(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong CPI extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_HopByHopFragment)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, metisTestDataV1_HopByHopFrag_BeginEnd, logger);
+ bool success = _parse(&skeleton);
+ assertTrue(success, "_parse(Control) did not succeed");
+
+ // spot check
+ {
+ MetisTlvExtent trueExtent = { .offset = 12, .length = sizeof(metisTestDataV1_HopByHopFrag_BeginEnd_Fragment) };
+ MetisTlvExtent testExtent = metisTlvSkeleton_GetFragmentPayload(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&trueExtent, &testExtent), "Wrong fragment payload extent, expected {%u, %u} got {%u, %u}",
+ trueExtent.offset, trueExtent.length, testExtent.offset, testExtent.length);
+ }
+ metisLogger_Release(&logger);
+}
+
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_InterestReturn)
+{
+ // not implemented yet
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _parse_Unknown)
+{
+ uint8_t unknown[] = { 0x01, 0x77, 0x00, 8, 0x00, 0x00, 0x00, 8 };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, unknown, logger);
+ bool success = _parse(&skeleton);
+ assertFalse(success, "_parse(Unknown) should have failed");
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _computeContentObjectHash)
+{
+ _MetisTlvFixedHeaderV1 *hdr = (_MetisTlvFixedHeaderV1 *) metisTestDataV1_ContentObject_NameA_Crc32c;
+ size_t endHeaders = _totalHeaderLength(metisTestDataV1_ContentObject_NameA_Crc32c);
+ size_t endPacket = _totalPacketLength((uint8_t *) hdr);
+
+ uint8_t *start = &metisTestDataV1_ContentObject_NameA_Crc32c[endHeaders];
+ size_t length = endPacket - endHeaders;
+
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBytes(hasher, start, length);
+
+ PARCCryptoHash *hash_truth = parcCryptoHasher_Finalize(hasher);
+
+ PARCCryptoHash *hash_test = _computeContentObjectHash(metisTestDataV1_ContentObject_NameA_Crc32c);
+
+ assertTrue(parcCryptoHash_Equals(hash_truth, hash_test),
+ "Content object digests did not match")
+ {
+ parcBuffer_Display(parcCryptoHash_GetDigest(hash_truth), 3),
+ parcBuffer_Display(parcCryptoHash_GetDigest(hash_test), 3);
+ }
+
+ parcCryptoHash_Release(&hash_truth);
+ parcCryptoHash_Release(&hash_test);
+ parcCryptoHasher_Release(&hasher);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _encodeControlPlaneInformation)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+
+ PARCBuffer *buffer = _encodeControlPlaneInformation(control);
+ ccnxControl_Release(&control);
+
+ assertNotNull(buffer, "Got null encoding buffer");
+ uint8_t *overlay = parcBuffer_Overlay(buffer, 0);
+
+ assertTrue(_isPacketTypeControl(overlay), "PacketType is not Control");
+ parcBuffer_Release(&buffer);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _fixedHeaderLength)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5 };
+ size_t test = _fixedHeaderLength(packet);
+ assertTrue(test == sizeof(_MetisTlvFixedHeaderV1), "wrong fixed header lenght, expected %zu got %zu", sizeof(_MetisTlvFixedHeaderV1), test);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _totalHeaderLength)
+{
+ size_t test = _totalHeaderLength(metisTestDataV1_ContentObject_NameA_Crc32c);
+ assertTrue(test == metisTestDataV1_ContentObject_NameA_Crc32c[7], "Wrong header length, expected %u got %zu", metisTestDataV1_ContentObject_NameA_Crc32c[7], test);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _totalPacketLength)
+{
+ size_t test = _totalPacketLength(metisTestDataV1_ContentObject_NameA_Crc32c);
+ assertTrue(test == sizeof(metisTestDataV1_ContentObject_NameA_Crc32c), "Wrong packet length, expected %zu got %zu", sizeof(metisTestDataV1_ContentObject_NameA_Crc32c), test);
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _isPacketTypeInterest)
+{
+ bool match = _isPacketTypeInterest(metisTestDataV1_Interest_AllFields);
+ assertTrue(match, "Interest did not match");
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _isPacketTypeContentObject)
+{
+ bool match = _isPacketTypeContentObject(metisTestDataV1_ContentObject_NameA_Crc32c);
+ assertTrue(match, "Content object did not match");
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _isPacketTypeInterestReturn)
+{
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _isPacketTypeControl)
+{
+ bool match = _isPacketTypeControl(metisTestDataV1_CPI_AddRoute_Crc32c);
+ assertTrue(match, "Control did not match");
+}
+
+LONGBOW_TEST_CASE(TlvOpsFunctions, _isPacketTypeHopByHopFragment)
+{
+ bool match = _isPacketTypeHopByHopFragment(metisTestDataV1_HopByHopFrag_Begin);
+ assertTrue(match, "HopByHop Fragment did not match");
+}
+
+
+// ======================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _parsePerHopV1);
+ LONGBOW_RUN_TEST_CASE(Local, _parseSignatureParameters);
+ LONGBOW_RUN_TEST_CASE(Local, _parseSignatureParameters_NoKeyid);
+ LONGBOW_RUN_TEST_CASE(Local, _parseValidationType);
+ LONGBOW_RUN_TEST_CASE(Local, _parseValidationType_NotSignature);
+ LONGBOW_RUN_TEST_CASE(Local, _parseValidationAlg);
+ LONGBOW_RUN_TEST_CASE(Local, _parseObjectV1);
+ LONGBOW_RUN_TEST_CASE(Local, _parseInterestV1);
+ LONGBOW_RUN_TEST_CASE(Local, _parseMessage);
+ LONGBOW_RUN_TEST_CASE(Local, _computeHash);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Local)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Local)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Local, _parsePerHopV1)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _parseSignatureParameters)
+{
+ uint8_t encoded[] = {
+ 0x00, T_KEYID, 0x00, 6,
+ 0xa0, 0xa1, 0xa2, 0xa3,0xa4,0xa5,
+
+ 0x00, T_PUBLICKEY, 0x00, 8,
+ 0xb1, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8,
+
+ 0x00, T_CERT, 0x00, 8,
+ 0xc1, 0xc2, 0xc3, 0xc4,
+ 0xc5, 0xc6, 0xc7, 0xc8,
+
+ 0x00, 0xFF, 0x00, 2,
+ 0xb0, 0xb1
+ };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, encoded, logger);
+ _parseSignatureParameters(encoded, 0, sizeof(encoded), &skeleton);
+
+ MetisTlvExtent truth = { .offset = 4, .length = 6 };
+ MetisTlvExtent keyid = metisTlvSkeleton_GetKeyId(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&truth, &keyid), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ truth.offset, truth.length, keyid.offset, keyid.length);
+
+ // Check the public key was found.
+ MetisTlvExtent pubKeyTruth = { .offset = 14, .length = 8 };
+ MetisTlvExtent pubKey = metisTlvSkeleton_GetPublicKey(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&pubKeyTruth, &pubKey), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ pubKeyTruth.offset, pubKeyTruth.length, pubKey.offset, pubKey.length);
+
+ // Check that the cert was found.
+ MetisTlvExtent certTruth = { .offset = 26, .length = 8 };
+ MetisTlvExtent cert = metisTlvSkeleton_GetCertificate(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&certTruth, &cert), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ certTruth.offset, certTruth.length, cert.offset, cert.length);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseSignatureParameters_NoKeyid)
+{
+ uint8_t encoded[] = {
+ 0x00, 0xFF, 0x00, 2,
+ 0xb0, 0xb1
+ };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, encoded, logger);
+ _parseSignatureParameters(encoded, 0, sizeof(encoded), &skeleton);
+
+ MetisTlvExtent keyid = metisTlvSkeleton_GetKeyId(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&metisTlvExtent_NotFound, &keyid), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ metisTlvExtent_NotFound.offset, metisTlvExtent_NotFound.length, keyid.offset, keyid.length);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseValidationType)
+{
+ uint8_t encoded[] = {
+ 0x00, T_RSA_SHA256, 0x00, 10,
+ 0x00, T_KEYID, 0x00, 6,
+ 0xa0, 0xa1, 0xa2, 0xa3,0xa4,0xa5,
+ 0x00, 0xFF, 0x00, 2,
+ 0xb0, 0xb1
+ };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, encoded, logger);
+ _parseValidationType(encoded, 0, sizeof(encoded), &skeleton);
+
+ MetisTlvExtent truth = { .offset = 8, .length = 6 };
+ MetisTlvExtent keyid = metisTlvSkeleton_GetKeyId(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&truth, &keyid), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ truth.offset, truth.length, keyid.offset, keyid.length);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseValidationType_NotSignature)
+{
+ uint8_t encoded[] = {
+ 0x00, 0xFF, 0x00, 10,
+ 0x00, T_KEYID, 0x00, 6,
+ 0xa0, 0xa1, 0xa2, 0xa3,0xa4,0xa5,
+ 0x00, 0xFF, 0x00, 2,
+ 0xb0, 0xb1
+ };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, encoded, logger);
+ _parseValidationType(encoded, 0, sizeof(encoded), &skeleton);
+
+ MetisTlvExtent keyid = metisTlvSkeleton_GetKeyId(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&metisTlvExtent_NotFound, &keyid), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ metisTlvExtent_NotFound.offset, metisTlvExtent_NotFound.length, keyid.offset, keyid.length);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseValidationAlg)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _parseObjectV1)
+{
+ uint8_t encoded[] = {
+ 0x00, 0x00, 0x00, 8, // type = name, length = 8
+ 0x00, 0x02, 0x00, 4, // type = binary, length = 4
+ 'c', 'o', 'o', 'l', // "cool"
+
+ 0x00, T_EXPIRYTIME, 0x00, 2, // type = name, length = 2
+ 0xa0, 0xa1
+ };
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize((_InternalSkeleton *) &skeleton, &MetisTlvSchemaV1_Ops, encoded, logger);
+ _parseObjectV1(encoded, 0, sizeof(encoded), &skeleton);
+
+ MetisTlvExtent truth = { .offset = 16, .length = 2 };
+ MetisTlvExtent expiryTime = metisTlvSkeleton_GetExpiryTime(&skeleton);
+ assertTrue(metisTlvExtent_Equals(&truth, &expiryTime), "Wrong extent, expected {%u, %u} got {%u, %u}",
+ truth.offset, truth.length, expiryTime.offset, expiryTime.length);
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Local, _parseInterestV1)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _parseMessage)
+{
+}
+
+LONGBOW_TEST_CASE(Local, _computeHash)
+{
+}
+
+// ======================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TlvSchemaV1);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
+
+
+
+// ====================================
+
+
+
diff --git a/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSkeleton.c b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSkeleton.c
new file mode 100644
index 00000000..243b28f3
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/tlv/test/test_metis_TlvSkeleton.c
@@ -0,0 +1,860 @@
+/*
+ * 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.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../metis_TlvSkeleton.c"
+#include <stdio.h>
+
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/logging/parc_LogReporterTextStdout.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+LONGBOW_TEST_RUNNER(metis_TlvSkeleton)
+{
+ // The following Test Fixtures will run their corresponding Test Cases.
+ // Test Fixtures are run in the order specified, but all tests should be idempotent.
+ // Never rely on the execution order of tests or share state between them.
+
+ LONGBOW_RUN_TEST_FIXTURE(SchemaV1);
+ LONGBOW_RUN_TEST_FIXTURE(Setters);
+ LONGBOW_RUN_TEST_FIXTURE(Getters);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(metis_TlvSkeleton)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(metis_TlvSkeleton)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ===================================================
+
+LONGBOW_TEST_FIXTURE(SchemaV1)
+{
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_ComputeContentObjectHash);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_Skeleton_Interest);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_Skeleton_Object);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeInterest);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeContentObject);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeControl);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeInterestReturn);
+ LONGBOW_RUN_TEST_CASE(SchemaV1, metisTlvSkeleton_TotalPacketLength);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(SchemaV1)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(SchemaV1)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+static void
+_schemaV1_verifyInterestPerHop(MetisTlvSkeleton *opaque)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ assertTrue(skeleton->array[INDEX_HOPLIMIT].offset == 4, "Incorrect hopLimit offset, expected %u got %u", 4, skeleton->array[INDEX_HOPLIMIT].offset);
+ assertTrue(skeleton->array[INDEX_HOPLIMIT].length == 1, "Incorrect hopLimit length, expected %u got %u", 1, skeleton->array[INDEX_HOPLIMIT].length);
+}
+
+static void
+_schemaV1_verifyInterestSkeleton(MetisTlvSkeleton *opaque)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ assertTrue(skeleton->array[INDEX_NAME].offset == 22, "Incorrect name offset, expected %u got %u", 2, skeleton->array[INDEX_NAME].offset);
+ assertTrue(skeleton->array[INDEX_NAME].length == 8, "Incorrect name length, expected %u got %u", 8, skeleton->array[INDEX_NAME].length);
+
+ assertTrue(skeleton->array[INDEX_KEYID].offset == 34, "Incorrect keyId offset, expected %u got %u", 34, skeleton->array[INDEX_KEYID].offset);
+ assertTrue(skeleton->array[INDEX_KEYID].length == 16, "Incorrect keyId length, expected %u got %u", 16, skeleton->array[INDEX_KEYID].length);
+
+ assertTrue(skeleton->array[INDEX_OBJHASH].offset == 54, "Incorrect objectHash offset, expected %u got %u", 54, skeleton->array[INDEX_OBJHASH].offset);
+ assertTrue(skeleton->array[INDEX_OBJHASH].length == 32, "Incorrect objectHash length, expected %u got %u", 32, skeleton->array[INDEX_OBJHASH].length);
+
+ assertTrue(skeleton->array[INDEX_INTLIFETIME].offset == 12, "Incorrect interestLifetime offset, expected %u got %u", 12, skeleton->array[INDEX_INTLIFETIME].offset);
+ assertTrue(skeleton->array[INDEX_INTLIFETIME].length == 2, "Incorrect interestLifetime length, expected %u got %u", 2, skeleton->array[INDEX_INTLIFETIME].length);
+}
+
+static void
+_schemaV1_verifyObjectSkeleton(MetisTlvSkeleton *opaque)
+{
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
+ assertTrue(skeleton->array[INDEX_NAME].offset == 40, "Incorrect name offset, expected %u got %u", 40, skeleton->array[INDEX_NAME].offset);
+ assertTrue(skeleton->array[INDEX_NAME].length == 17, "Incorrect name length, expected %u got %u", 17, skeleton->array[INDEX_NAME].length);
+
+ assertTrue(skeleton->array[INDEX_KEYID].offset == 106, "Incorrect keyId offset, expected %u got %u", 106, skeleton->array[INDEX_KEYID].offset);
+ assertTrue(skeleton->array[INDEX_KEYID].length == 32, "Incorrect keyId length, expected %u got %u", 32, skeleton->array[INDEX_KEYID].length);
+}
+
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_ComputeContentObjectHash)
+{
+ size_t endHeaders = metisTlv_TotalHeaderLength(metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256);
+ size_t endPacket = metisTlv_TotalPacketLength(metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256);
+ uint8_t *start = &metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256[endHeaders];
+
+
+ size_t length = endPacket - endHeaders;
+
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBytes(hasher, start, length);
+
+ PARCCryptoHash *hash_truth = parcCryptoHasher_Finalize(hasher);
+
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256, logger);
+ metisLogger_Release(&logger);
+
+ PARCCryptoHash *hash_test = metisTlvSkeleton_ComputeContentObjectHash(&skeleton);
+
+ assertTrue(parcCryptoHash_Equals(hash_truth, hash_test),
+ "Content object digests did not match")
+ {
+ printf("Expected:\n");
+ parcBuffer_Display(parcCryptoHash_GetDigest(hash_truth), 3);
+ printf("Got:\n");
+ parcBuffer_Display(parcCryptoHash_GetDigest(hash_test), 3);
+ }
+
+ parcCryptoHash_Release(&hash_truth);
+ parcCryptoHash_Release(&hash_test);
+ parcCryptoHasher_Release(&hasher);
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_Skeleton_Interest)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_Interest_AllFields, logger);
+ metisLogger_Release(&logger);
+ _schemaV1_verifyInterestPerHop(&skeleton);
+ _schemaV1_verifyInterestSkeleton(&skeleton);
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_Skeleton_Object)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256, logger);
+ metisLogger_Release(&logger);
+ _schemaV1_verifyObjectSkeleton(&skeleton);
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeInterest)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_Interest_AllFields, logger);
+ metisLogger_Release(&logger);
+ bool match = metisTlvSkeleton_IsPacketTypeInterest(&skeleton);
+ assertTrue(match, "Packet should have tested true as Interest");
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeContentObject)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_ContentObject_NameA_KeyId1_RsaSha256, logger);
+ metisLogger_Release(&logger);
+ bool match = metisTlvSkeleton_IsPacketTypeContentObject(&skeleton);
+ assertTrue(match, "Packet should have tested true as Content Object");
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeControl)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_CPI_AddRoute_Crc32c, logger);
+ metisLogger_Release(&logger);
+ bool match = metisTlvSkeleton_IsPacketTypeControl(&skeleton);
+ assertTrue(match, "Packet should have tested true as Control");
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_IsPacketTypeInterestReturn)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_CPI_AddRoute_Crc32c, logger);
+ metisLogger_Release(&logger);
+ bool match = metisTlvSkeleton_IsPacketTypeInterestReturn(&skeleton);
+ assertFalse(match, "Packet should have tested false as Interest Return");
+}
+
+LONGBOW_TEST_CASE(SchemaV1, metisTlvSkeleton_TotalPacketLength)
+{
+ MetisTlvSkeleton skeleton;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ metisTlvSkeleton_Parse(&skeleton, metisTestDataV1_Interest_AllFields, logger);
+ metisLogger_Release(&logger);
+ size_t truth = sizeof(metisTestDataV1_Interest_AllFields);
+ size_t test = metisTlvSkeleton_TotalPacketLength(&skeleton);
+
+ assertTrue(truth == test, "Wrong value, expected %zu got %zu", truth, test);
+}
+
+// ======================================================
+
+LONGBOW_TEST_FIXTURE(Setters)
+{
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetName);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetKeyId);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetObjectHash);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetHopLimit);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetInterestLifetime);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetCacheTimeHeader);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetExpiryTime);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetCPI);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetFragmentPayload);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_UpdateHopLimit);
+
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetKeyId);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetCertificate);
+ LONGBOW_RUN_TEST_CASE(Setters, metisTlvSkeleton_SetPublicKey);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Setters)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Setters)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetName)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_NAME;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetName(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetKeyId)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_KEYID;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetKeyId(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetObjectHash)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_OBJHASH;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetObjectHash(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetHopLimit)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 1;
+ int element = INDEX_HOPLIMIT;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetHopLimit(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetInterestLifetime)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_INTLIFETIME;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetInterestLifetime(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetCacheTimeHeader)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_CACHETIME;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetCacheTimeHeader(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetExpiryTime)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_EXPIRYTIME;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetExpiryTime(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetCPI)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_CPI;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetCPI(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetFragmentPayload)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 4;
+ int element = INDEX_FRAGMENTPAYLOAD;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetFragmentPayload(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_UpdateHopLimit)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 2;
+ size_t length = 1;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetHopLimit(&opaque, offset, length);
+
+ metisTlvSkeleton_UpdateHopLimit(&opaque, 77);
+
+ assertTrue(packet[offset] == 77, "Wrong hop limit, expected 77 got %u", packet[offset]);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetCertificate)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 6;
+ size_t length = 2;
+ int element = INDEX_CERTIFICATE;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetCertificate(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u",
+ element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u",
+ element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Setters, metisTlvSkeleton_SetPublicKey)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ size_t offset = 5;
+ size_t length = 3;
+ int element = INDEX_PUBKEY;
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ metisLogger_SetLogLevel(logger, MetisLoggerFacility_Message, PARCLogLevel_Debug);
+
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetPublicKey(&opaque, offset, length);
+
+ assertTrue(skeleton->array[element].offset == offset, "Wrong offset for index %d, expected %zu got %u", element, offset, skeleton->array[element].offset);
+ assertTrue(skeleton->array[element].length == length, "Wrong length for index %d, expected %zu got %u", element, length, skeleton->array[element].length);
+ metisLogger_Release(&logger);
+}
+
+// ======================================================
+
+LONGBOW_TEST_FIXTURE(Getters)
+{
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetName);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetKeyId);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetObjectHash);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetHopLimit);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetInterestLifetime);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetCacheTimeHeader);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetExpiryTime);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetCPI);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetFragmentPayload);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetPacket);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetLogger);
+
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetPublicKey);
+ LONGBOW_RUN_TEST_CASE(Getters, metisTlvSkeleton_GetCertificate);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Getters)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Getters)
+{
+ uint32_t outstandingAllocations = parcSafeMemory_ReportAllocation(STDERR_FILENO);
+ if (outstandingAllocations != 0) {
+ printf("%s leaks memory by %d allocations\n", longBowTestCase_GetName(testCase), outstandingAllocations);
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetName)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetName(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetName(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetKeyId)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetKeyId(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetKeyId(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetObjectHash)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetObjectHash(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetObjectHash(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetHopLimit)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 1 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetHopLimit(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetHopLimit(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetInterestLifetime)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetInterestLifetime(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetInterestLifetime(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetCacheTimeHeader)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetCacheTimeHeader(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetCacheTimeHeader(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetExpiryTime)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetExpiryTime(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetExpiryTime(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetCPI)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetCPI(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetCPI(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetFragmentPayload)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 3, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+ metisTlvSkeleton_SetFragmentPayload(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetFragmentPayload(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetPacket)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+
+ const uint8_t *test = metisTlvSkeleton_GetPacket(&opaque);
+
+ assertTrue(packet == test, "Wrong packet pointer, expected %p, got %p",
+ (void *) packet, (void *) test);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetLogger)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+
+ MetisLogger *test = metisTlvSkeleton_GetLogger(&opaque);
+ assertNotNull(test, "Got null logger from skeleton");
+
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetPublicKey)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 5, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+
+ metisTlvSkeleton_SetPublicKey(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetPublicKey(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+LONGBOW_TEST_CASE(Getters, metisTlvSkeleton_GetCertificate)
+{
+ uint8_t packet[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+ MetisTlvExtent extent = { .offset = 5, .length = 2 };
+
+ MetisTlvSkeleton opaque;
+ _InternalSkeleton *skeleton = (_InternalSkeleton *) &opaque;
+ PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
+ MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
+ parcLogReporter_Release(&reporter);
+ _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
+
+ metisTlvSkeleton_SetCertificate(&opaque, extent.offset, extent.length);
+
+ MetisTlvExtent test = metisTlvSkeleton_GetCertificate(&opaque);
+
+ assertTrue(metisTlvExtent_Equals(&extent, &test), "Wrong extent, expected {%u, %u}, got {%u, %u}",
+ extent.offset, extent.length, test.offset, test.length);
+ metisLogger_Release(&logger);
+}
+
+
+
+// ======================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(metis_TlvSkeleton);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
+
+
+
+// ====================================
+
+