diff options
Diffstat (limited to 'metis/ccnx/forwarder/metis/tlv')
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(©); + * } + * @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(©); + 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(©); + 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(©); + 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(©); + 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(©); + 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); +} + + + +// ==================================== + + |